1/*
2 * Copyright 2001-2015, Haiku.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Marc Flerackers (mflerackers@androme.be)
7 *		Stefano Ceccherini (stefano.ceccherini@gmail.com)
8 *		Marcus Overhagen <marcus@overhagen.de>
9 *      Julian Harnath <julian.harnath@rwth-aachen.de>
10 */
11
12#include "PictureBoundingBoxPlayer.h"
13
14#include <new>
15#include <stdio.h>
16
17#include "DrawState.h"
18#include "FontManager.h"
19#include "Layer.h"
20#include "ServerApp.h"
21#include "ServerBitmap.h"
22#include "ServerFont.h"
23#include "ServerPicture.h"
24#include "ServerTokenSpace.h"
25#include "View.h"
26#include "Window.h"
27
28#include <Bitmap.h>
29#include <Debug.h>
30#include <List.h>
31#include <ObjectListPrivate.h>
32#include <PicturePlayer.h>
33#include <PictureProtocol.h>
34#include <Shape.h>
35
36
37//#define DEBUG_TRACE_BB
38#ifdef DEBUG_TRACE_BB
39#	define TRACE_BB(text, ...) debug_printf("PBBP: " text, ##__VA_ARGS__)
40#else
41#	define TRACE_BB(text, ...)
42#endif
43
44
45typedef PictureBoundingBoxPlayer::State BoundingBoxState;
46
47
48// #pragma mark - PictureBoundingBoxPlayer::State
49
50
51class PictureBoundingBoxPlayer::State {
52public:
53	State(const DrawState* drawState, BRect* boundingBox)
54		:
55		fDrawState(drawState->Squash()),
56		fBoundingBox(boundingBox)
57	{
58		fBoundingBox->Set(INT_MAX, INT_MAX, INT_MIN, INT_MIN);
59	}
60
61	~State()
62	{
63		delete fDrawState;
64	}
65
66	DrawState* GetDrawState()
67	{
68		return fDrawState;
69	}
70
71	void PushDrawState()
72	{
73		DrawState* nextState = fDrawState->PushState();
74		if (nextState != NULL)
75			fDrawState = nextState;
76	}
77
78	void PopDrawState()
79	{
80		if (fDrawState->PreviousState() != NULL)
81			fDrawState = fDrawState->PopState();
82	}
83
84	SimpleTransform PenToLocalTransform() const
85	{
86		SimpleTransform transform;
87		fDrawState->Transform(transform);
88		return transform;
89	}
90
91	void IncludeRect(BRect& rect)
92	{
93		_AffineTransformRect(rect);
94		*fBoundingBox = (*fBoundingBox) | rect;
95	}
96
97private:
98	void _AffineTransformRect(BRect& rect)
99	{
100		BAffineTransform transform = fDrawState->CombinedTransform();
101		if (transform.IsIdentity())
102			return;
103
104		BPoint transformedShape[4];
105		transformedShape[0] = rect.LeftTop();
106		transformedShape[1] = rect.LeftBottom();
107		transformedShape[2] = rect.RightTop();
108		transformedShape[3] = rect.RightBottom();
109
110		transform.Apply(&transformedShape[0], 4);
111
112		float minX = INT_MAX;
113		float minY = INT_MAX;
114		float maxX = INT_MIN;
115		float maxY = INT_MIN;
116
117		for (uint32 i = 0; i < 4; i++) {
118			if (transformedShape[i].x < minX)
119				minX = transformedShape[i].x;
120			else if (transformedShape[i].x > maxX)
121				maxX = transformedShape[i].x;
122			if (transformedShape[i].y < minY)
123				minY = transformedShape[i].y;
124			else if (transformedShape[i].y > maxY)
125				maxY = transformedShape[i].y;
126		}
127
128		rect.Set(minX, minY, maxX, maxY);
129	}
130
131
132private:
133	DrawState*	fDrawState;
134	BRect*		fBoundingBox;
135};
136
137
138// #pragma mark - Picture playback hooks
139
140
141static void
142get_polygon_frame(const BPoint* points, int32 numPoints, BRect* frame)
143{
144	ASSERT(numPoints > 0);
145
146	float left = points->x;
147	float top = points->y;
148	float right = left;
149	float bottom = top;
150
151	points++;
152	numPoints--;
153
154	while (numPoints--) {
155		if (points->x < left)
156			left = points->x;
157		if (points->x > right)
158			right = points->x;
159		if (points->y < top)
160			top = points->y;
161		if (points->y > bottom)
162			bottom = points->y;
163		points++;
164	}
165
166	frame->Set(left, top, right, bottom);
167}
168
169
170template<class RectType>
171static void
172expand_rect_for_pen_size(BoundingBoxState* state, RectType& rect)
173{
174	float penInset = -((state->GetDrawState()->PenSize() / 2.0f) + 1.0f);
175	rect.InsetBy(penInset, penInset);
176}
177
178
179static void
180move_pen_by(void* _state, const BPoint& delta)
181{
182	TRACE_BB("%p move pen by %.2f %.2f\n", _state, delta.x, delta.y);
183	BoundingBoxState* const state =
184		reinterpret_cast<BoundingBoxState*>(_state);
185
186	state->GetDrawState()->SetPenLocation(
187		state->GetDrawState()->PenLocation() + delta);
188}
189
190
191static void
192determine_bounds_stroke_line(void* _state, const BPoint& _start,
193	const BPoint& _end)
194{
195	TRACE_BB("%p stroke line %.2f %.2f -> %.2f %.2f\n", _state,
196		_start.x, _start.y, _end.x, _end.y);
197	BoundingBoxState* const state =
198		reinterpret_cast<BoundingBoxState*>(_state);
199
200	BPoint start = _start;
201	BPoint end = _end;
202
203	const SimpleTransform transform = state->PenToLocalTransform();
204	transform.Apply(&start);
205	transform.Apply(&end);
206
207	BRect rect;
208	if (start.x <= end.x) {
209		rect.left = start.x;
210		rect.right = end.x;
211	} else {
212		rect.left = end.x;
213		rect.right = start.x;
214	}
215	if (start.y <= end.y) {
216		rect.top = start.y;
217		rect.bottom = end.y;
218	} else {
219		rect.top = end.y;
220		rect.bottom = start.y;
221	}
222
223	expand_rect_for_pen_size(state, rect);
224	state->IncludeRect(rect);
225
226	state->GetDrawState()->SetPenLocation(_end);
227}
228
229
230static void
231determine_bounds_draw_rect(void* _state, const BRect& _rect, bool fill)
232{
233	TRACE_BB("%p draw rect fill=%d %.2f %.2f %.2f %.2f\n", _state, fill,
234		_rect.left, _rect.top, _rect.right, _rect.bottom);
235	BoundingBoxState* const state =
236		reinterpret_cast<BoundingBoxState*>(_state);
237
238	BRect rect = _rect;
239	state->PenToLocalTransform().Apply(&rect);
240	if (!fill)
241		expand_rect_for_pen_size(state, rect);
242	state->IncludeRect(rect);
243}
244
245
246static void
247determine_bounds_draw_round_rect(void* _state, const BRect& _rect,
248	const BPoint&, bool fill)
249{
250	determine_bounds_draw_rect(_state, _rect, fill);
251}
252
253
254static void
255determine_bounds_bezier(BoundingBoxState* state, const BPoint* viewPoints,
256	BRect& outRect)
257{
258	// Note: this is an approximation which results in a rectangle which
259	// encloses all four control points. That will always enclose the curve,
260	// although not necessarily tightly, but it's good enough for the purpose.
261	// The exact bounding box of a bezier curve is not trivial to determine,
262	// (need to calculate derivative of the curve) and we're going for
263	// performance here.
264	BPoint points[4];
265	state->PenToLocalTransform().Apply(points, viewPoints, 4);
266	BPoint topLeft = points[0];
267	BPoint bottomRight = points[0];
268	for (uint32 index = 1; index < 4; index++) {
269		if (points[index].x < topLeft.x || points[index].y < topLeft.y)
270			topLeft = points[index];
271		if (points[index].x > topLeft.x || points[index].y > topLeft.y)
272			bottomRight = points[index];
273	}
274	outRect.SetLeftTop(topLeft);
275	outRect.SetRightBottom(bottomRight);
276}
277
278
279static void
280determine_bounds_draw_bezier(void* _state, size_t numPoints,
281	const BPoint viewPoints[], bool fill)
282{
283	TRACE_BB("%p draw bezier fill=%d (%.2f %.2f) (%.2f %.2f) "
284		"(%.2f %.2f) (%.2f %.2f)\n",
285		_state,
286		fill,
287		viewPoints[0].x, viewPoints[0].y,
288		viewPoints[1].x, viewPoints[1].y,
289		viewPoints[2].x, viewPoints[2].y,
290		viewPoints[3].x, viewPoints[3].y);
291	BoundingBoxState* const state =
292		reinterpret_cast<BoundingBoxState*>(_state);
293
294	const size_t kSupportedPoints = 4;
295	if (numPoints != kSupportedPoints)
296		return;
297
298	BRect rect;
299	determine_bounds_bezier(state, viewPoints, rect);
300	if (!fill)
301		expand_rect_for_pen_size(state, rect);
302	state->IncludeRect(rect);
303}
304
305
306static void
307determine_bounds_draw_ellipse(void* _state, const BRect& _rect, bool fill)
308{
309	TRACE_BB("%p draw ellipse fill=%d (%.2f %.2f) (%.2f %.2f)\n", _state, fill,
310		_rect.left, _rect.top, _rect.right, _rect.bottom);
311	BoundingBoxState* const state =
312		reinterpret_cast<BoundingBoxState*>(_state);
313
314	BRect rect = _rect;
315	state->PenToLocalTransform().Apply(&rect);
316	if (!fill)
317		expand_rect_for_pen_size(state, rect);
318	state->IncludeRect(rect);
319}
320
321
322static void
323determine_bounds_draw_arc(void* _state, const BPoint& center,
324	const BPoint& radii, float, float, bool fill)
325{
326	BRect rect(center.x - radii.x, center.y - radii.y,
327		center.x + radii.x - 1, center.y + radii.y - 1);
328	determine_bounds_draw_ellipse(_state, rect, fill);
329}
330
331
332static void
333determine_bounds_polygon(BoundingBoxState* state, int32 numPoints,
334	const BPoint* viewPoints, BRect& outRect)
335{
336	if (numPoints <= 0)
337		return;
338
339	if (numPoints <= 200) {
340		// fast path: no malloc/free, also avoid
341		// constructor/destructor calls
342		char data[200 * sizeof(BPoint)];
343		BPoint* points = (BPoint*)data;
344
345		state->PenToLocalTransform().Apply(points, viewPoints, numPoints);
346		get_polygon_frame(points, numPoints, &outRect);
347
348	} else {
349		 // avoid constructor/destructor calls by
350		 // using malloc instead of new []
351		BPoint* points = (BPoint*)malloc(numPoints * sizeof(BPoint));
352		if (points == NULL)
353			return;
354
355		state->PenToLocalTransform().Apply(points, viewPoints, numPoints);
356		get_polygon_frame(points, numPoints, &outRect);
357
358		free(points);
359	}
360}
361
362
363void
364determine_bounds_draw_polygon(void* _state, size_t numPoints,
365	const BPoint viewPoints[], bool, bool fill)
366{
367	TRACE_BB("%p draw polygon fill=%d (%ld points)\n", _state, fill, numPoints);
368	BoundingBoxState* const state =
369		reinterpret_cast<BoundingBoxState*>(_state);
370
371	BRect rect;
372	determine_bounds_polygon(state, numPoints, viewPoints, rect);
373	if (!fill)
374		expand_rect_for_pen_size(state, rect);
375	state->IncludeRect(rect);
376}
377
378
379static void
380determine_bounds_draw_shape(void* _state, const BShape& shape, bool fill)
381{
382	BRect rect = shape.Bounds();
383
384	TRACE_BB("%p stroke shape (bounds %.2f %.2f %.2f %.2f)\n", _state,
385		rect.left, rect.top, rect.right, rect.bottom);
386	BoundingBoxState* const state =
387		reinterpret_cast<BoundingBoxState*>(_state);
388
389	state->PenToLocalTransform().Apply(&rect);
390	if (!fill)
391		expand_rect_for_pen_size(state, rect);
392	state->IncludeRect(rect);
393}
394
395
396static void
397determine_bounds_draw_string(void* _state, const char* string, size_t length,
398	float deltaSpace, float deltaNonSpace)
399{
400	TRACE_BB("%p string '%s'\n", _state, string);
401	BoundingBoxState* const state =
402		reinterpret_cast<BoundingBoxState*>(_state);
403
404	ServerFont font = state->GetDrawState()->Font();
405
406	escapement_delta delta = { deltaSpace, deltaNonSpace };
407	BRect rect;
408	font.GetBoundingBoxesForStrings((char**)&string, &length, 1, &rect,
409		B_SCREEN_METRIC, &delta);
410
411	BPoint location = state->GetDrawState()->PenLocation();
412
413	state->PenToLocalTransform().Apply(&location);
414	rect.OffsetBy(location);
415	state->IncludeRect(rect);
416
417	state->PenToLocalTransform().Apply(&location);
418	state->GetDrawState()->SetPenLocation(location);
419}
420
421
422static void
423determine_bounds_draw_pixels(void* _state, const BRect&, const BRect& _dest,
424	uint32, uint32, size_t, color_space, uint32, const void*, size_t)
425{
426	TRACE_BB("%p pixels (dest %.2f %.2f %.2f %.2f)\n", _state,
427		_dest.left, _dest.top, _dest.right, _dest.bottom);
428	BoundingBoxState* const state =
429		reinterpret_cast<BoundingBoxState*>(_state);
430
431	BRect dest = _dest;
432	state->PenToLocalTransform().Apply(&dest);
433	state->IncludeRect(dest);
434}
435
436
437static void
438draw_picture(void* _state, const BPoint& where, int32 token)
439{
440	TRACE_BB("%p picture (unimplemented)\n", _state);
441
442	// TODO
443	(void)_state;
444	(void)where;
445	(void)token;
446}
447
448
449static void
450set_clipping_rects(void* _state, size_t numRects, const BRect rects[])
451{
452	TRACE_BB("%p cliping rects (%ld rects)\n", _state, numRects);
453
454	// TODO
455	(void)_state;
456	(void)rects;
457	(void)numRects;
458}
459
460
461static void
462clip_to_picture(void* _state, int32 pictureToken, const BPoint& where,
463	bool clipToInverse)
464{
465	TRACE_BB("%p clip to picture (unimplemented)\n", _state);
466
467	// TODO
468}
469
470
471static void
472push_state(void* _state)
473{
474	TRACE_BB("%p push state\n", _state);
475	BoundingBoxState* const state =
476		reinterpret_cast<BoundingBoxState*>(_state);
477
478	state->PushDrawState();
479}
480
481
482static void
483pop_state(void* _state)
484{
485	TRACE_BB("%p pop state\n", _state);
486	BoundingBoxState* const state =
487		reinterpret_cast<BoundingBoxState*>(_state);
488
489	state->PopDrawState();
490}
491
492
493static void
494enter_state_change(void*)
495{
496}
497
498
499static void
500exit_state_change(void*)
501{
502}
503
504
505static void
506enter_font_state(void*)
507{
508}
509
510
511static void
512exit_font_state(void*)
513{
514}
515
516
517static void
518set_origin(void* _state, const BPoint& pt)
519{
520	TRACE_BB("%p set origin %.2f %.2f\n", _state, pt.x, pt.y);
521	BoundingBoxState* const state =
522		reinterpret_cast<BoundingBoxState*>(_state);
523	state->GetDrawState()->SetOrigin(pt);
524}
525
526
527static void
528set_pen_location(void* _state, const BPoint& pt)
529{
530	TRACE_BB("%p set pen location %.2f %.2f\n", _state, pt.x, pt.y);
531	BoundingBoxState* const state =
532		reinterpret_cast<BoundingBoxState*>(_state);
533	state->GetDrawState()->SetPenLocation(pt);
534}
535
536
537static void
538set_drawing_mode(void*, drawing_mode)
539{
540}
541
542
543static void
544set_line_mode(void* _state, cap_mode capMode, join_mode joinMode,
545	float miterLimit)
546{
547	BoundingBoxState* const state =
548		reinterpret_cast<BoundingBoxState*>(_state);
549
550	DrawState* drawState = state->GetDrawState();
551	drawState->SetLineCapMode(capMode);
552	drawState->SetLineJoinMode(joinMode);
553	drawState->SetMiterLimit(miterLimit);
554}
555
556
557static void
558set_pen_size(void* _state, float size)
559{
560	TRACE_BB("%p set pen size %.2f\n", _state, size);
561	BoundingBoxState* const state =
562		reinterpret_cast<BoundingBoxState*>(_state);
563
564	state->GetDrawState()->SetPenSize(size);
565}
566
567
568static void
569set_fore_color(void* _state, const rgb_color& color)
570{
571	BoundingBoxState* const state =
572		reinterpret_cast<BoundingBoxState*>(_state);
573
574	state->GetDrawState()->SetHighColor(color);
575}
576
577
578static void
579set_back_color(void* _state, const rgb_color& color)
580{
581	BoundingBoxState* const state =
582		reinterpret_cast<BoundingBoxState*>(_state);
583
584	state->GetDrawState()->SetLowColor(color);
585}
586
587
588static void
589set_stipple_pattern(void* _state, const pattern& _pattern)
590{
591	BoundingBoxState* const state =
592		reinterpret_cast<BoundingBoxState*>(_state);
593
594	state->GetDrawState()->SetPattern(Pattern(_pattern));
595}
596
597
598static void
599set_scale(void* _state, float scale)
600{
601	BoundingBoxState* const state =
602		reinterpret_cast<BoundingBoxState*>(_state);
603
604	state->GetDrawState()->SetScale(scale);
605}
606
607
608static void
609set_font_family(void* _state, const char* _family, size_t length)
610{
611	BoundingBoxState* const state =
612		reinterpret_cast<BoundingBoxState*>(_state);
613
614	BString family(_family, length);
615	FontStyle* fontStyle = gFontManager->GetStyleByIndex(family, 0);
616	ServerFont font;
617	font.SetStyle(fontStyle);
618	state->GetDrawState()->SetFont(font, B_FONT_FAMILY_AND_STYLE);
619}
620
621
622static void
623set_font_style(void* _state, const char* _style, size_t length)
624{
625	BoundingBoxState* const state =
626		reinterpret_cast<BoundingBoxState*>(_state);
627
628	BString style(_style, length);
629	ServerFont font(state->GetDrawState()->Font());
630	FontStyle* fontStyle = gFontManager->GetStyle(font.Family(), style);
631	font.SetStyle(fontStyle);
632	state->GetDrawState()->SetFont(font, B_FONT_FAMILY_AND_STYLE);
633}
634
635
636static void
637set_font_spacing(void* _state, uint8 spacing)
638{
639	BoundingBoxState* const state =
640		reinterpret_cast<BoundingBoxState*>(_state);
641
642	ServerFont font;
643	font.SetSpacing(spacing);
644	state->GetDrawState()->SetFont(font, B_FONT_SPACING);
645}
646
647
648static void
649set_font_size(void* _state, float size)
650{
651	BoundingBoxState* const state =
652		reinterpret_cast<BoundingBoxState*>(_state);
653
654	ServerFont font;
655	font.SetSize(size);
656	state->GetDrawState()->SetFont(font, B_FONT_SIZE);
657}
658
659
660static void
661set_font_rotate(void* _state, float rotation)
662{
663	BoundingBoxState* const state =
664		reinterpret_cast<BoundingBoxState*>(_state);
665
666	ServerFont font;
667	font.SetRotation(rotation);
668	state->GetDrawState()->SetFont(font, B_FONT_ROTATION);
669}
670
671
672static void
673set_font_encoding(void* _state, uint8 encoding)
674{
675	BoundingBoxState* const state =
676		reinterpret_cast<BoundingBoxState*>(_state);
677
678	ServerFont font;
679	font.SetEncoding(encoding);
680	state->GetDrawState()->SetFont(font, B_FONT_ENCODING);
681}
682
683
684static void
685set_font_flags(void* _state, uint32 flags)
686{
687	BoundingBoxState* const state =
688		reinterpret_cast<BoundingBoxState*>(_state);
689
690	ServerFont font;
691	font.SetFlags(flags);
692	state->GetDrawState()->SetFont(font, B_FONT_FLAGS);
693}
694
695
696static void
697set_font_shear(void* _state, float shear)
698{
699	BoundingBoxState* const state =
700		reinterpret_cast<BoundingBoxState*>(_state);
701
702	ServerFont font;
703	font.SetShear(shear);
704	state->GetDrawState()->SetFont(font, B_FONT_SHEAR);
705}
706
707
708static void
709set_font_face(void* _state, uint16 face)
710{
711	BoundingBoxState* const state =
712		reinterpret_cast<BoundingBoxState*>(_state);
713
714	ServerFont font;
715	font.SetFace(face);
716	state->GetDrawState()->SetFont(font, B_FONT_FACE);
717}
718
719
720static void
721set_blending_mode(void*, source_alpha, alpha_function)
722{
723}
724
725
726static void
727set_transform(void* _state, const BAffineTransform& transform)
728{
729	TRACE_BB("%p transform\n", _state);
730	BoundingBoxState* const state =
731		reinterpret_cast<BoundingBoxState*>(_state);
732	state->GetDrawState()->SetTransform(transform);
733}
734
735
736static void
737translate_by(void* _state, double x, double y)
738{
739	TRACE_BB("%p translate\n", _state);
740	BoundingBoxState* const state =
741		reinterpret_cast<BoundingBoxState*>(_state);
742	BAffineTransform transform = state->GetDrawState()->Transform();
743	transform.PreTranslateBy(x, y);
744	state->GetDrawState()->SetTransform(transform);
745}
746
747
748static void
749scale_by(void* _state, double x, double y)
750{
751	TRACE_BB("%p scale\n", _state);
752	BoundingBoxState* const state =
753		reinterpret_cast<BoundingBoxState*>(_state);
754	BAffineTransform transform = state->GetDrawState()->Transform();
755	transform.PreScaleBy(x, y);
756	state->GetDrawState()->SetTransform(transform);
757}
758
759
760static void
761rotate_by(void* _state, double angleRadians)
762{
763	TRACE_BB("%p rotate\n", _state);
764	BoundingBoxState* const state =
765		reinterpret_cast<BoundingBoxState*>(_state);
766	BAffineTransform transform = state->GetDrawState()->Transform();
767	transform.PreRotateBy(angleRadians);
768	state->GetDrawState()->SetTransform(transform);
769}
770
771
772static void
773determine_bounds_nested_layer(void* _state, Layer* layer)
774{
775	TRACE_BB("%p nested layer\n", _state);
776	BoundingBoxState* const state =
777		reinterpret_cast<BoundingBoxState*>(_state);
778
779	BRect boundingBox;
780	PictureBoundingBoxPlayer::Play(layer, state->GetDrawState(), &boundingBox);
781	if (boundingBox.IsValid())
782		state->IncludeRect(boundingBox);
783}
784
785
786static const BPrivate::picture_player_callbacks
787	kPictureBoundingBoxPlayerCallbacks = {
788	move_pen_by,
789	determine_bounds_stroke_line,
790	determine_bounds_draw_rect,
791	determine_bounds_draw_round_rect,
792	determine_bounds_draw_bezier,
793	determine_bounds_draw_arc,
794	determine_bounds_draw_ellipse,
795	determine_bounds_draw_polygon,
796	determine_bounds_draw_shape,
797	determine_bounds_draw_string,
798	determine_bounds_draw_pixels,
799	draw_picture,
800	set_clipping_rects,
801	clip_to_picture,
802	push_state,
803	pop_state,
804	enter_state_change,
805	exit_state_change,
806	enter_font_state,
807	exit_font_state,
808	set_origin,
809	set_pen_location,
810	set_drawing_mode,
811	set_line_mode,
812	set_pen_size,
813	set_fore_color,
814	set_back_color,
815	set_stipple_pattern,
816	set_scale,
817	set_font_family,
818	set_font_style,
819	set_font_spacing,
820	set_font_size,
821	set_font_rotate,
822	set_font_encoding,
823	set_font_flags,
824	set_font_shear,
825	set_font_face,
826	set_blending_mode,
827	set_transform,
828	translate_by,
829	scale_by,
830	rotate_by,
831	determine_bounds_nested_layer
832};
833
834
835// #pragma mark - PictureBoundingBoxPlayer
836
837
838/* static */ void
839PictureBoundingBoxPlayer::Play(ServerPicture* picture,
840	const DrawState* drawState, BRect* outBoundingBox)
841{
842	State state(drawState, outBoundingBox);
843
844	BMallocIO* mallocIO = dynamic_cast<BMallocIO*>(picture->fData);
845	if (mallocIO == NULL)
846		return;
847
848	BPrivate::PicturePlayer player(mallocIO->Buffer(),
849		mallocIO->BufferLength(), ServerPicture::PictureList::Private(
850			picture->fPictures).AsBList());
851	player.Play(kPictureBoundingBoxPlayerCallbacks,
852		sizeof(kPictureBoundingBoxPlayerCallbacks), &state);
853}
854