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