1/*
2 * Copyright 2001-2018, Haiku Inc.
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 *		Stephan A��mus <superstippi@gmx.de>
10 */
11
12/**	PicturePlayer is used to play picture data. */
13
14#include <PicturePlayer.h>
15
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19
20#include <AffineTransform.h>
21#include <PictureProtocol.h>
22#include <Shape.h>
23
24
25using BPrivate::PicturePlayer;
26
27
28struct adapter_context {
29	void* user_data;
30	void** function_table;
31};
32
33
34static void
35nop()
36{
37}
38
39
40static void
41move_pen_by(void* _context, const BPoint& delta)
42{
43	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
44	((void (*)(void*, BPoint))context->function_table[1])(context->user_data,
45		delta);
46}
47
48
49static void
50stroke_line(void* _context, const BPoint& start, const BPoint& end)
51{
52	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
53	((void (*)(void*, BPoint, BPoint))context->function_table[2])(
54		context->user_data, start, end);
55}
56
57
58static void
59draw_rect(void* _context, const BRect& rect, bool fill)
60{
61	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
62	((void (*)(void*, BRect))context->function_table[fill ? 4 : 3])(
63		context->user_data, rect);
64}
65
66
67static void
68draw_round_rect(void* _context, const BRect& rect, const BPoint& radii,
69	bool fill)
70{
71	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
72	((void (*)(void*, BRect, BPoint))context->function_table[fill ? 6 : 5])(
73		context->user_data, rect, radii);
74}
75
76
77static void
78draw_bezier(void* _context, size_t numPoints, const BPoint _points[], bool fill)
79{
80	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
81	if (numPoints != 4)
82		return;
83
84	BPoint points[4] = { _points[0], _points[1], _points[2], _points[3] };
85	((void (*)(void*, BPoint*))context->function_table[fill ? 8 : 7])(
86		context->user_data, points);
87}
88
89
90static void
91draw_arc(void* _context, const BPoint& center, const BPoint& radii,
92	float startTheta, float arcTheta, bool fill)
93{
94	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
95	((void (*)(void*, BPoint, BPoint, float, float))
96		context->function_table[fill ? 10 : 9])(context->user_data, center,
97			radii, startTheta, arcTheta);
98}
99
100
101static void
102draw_ellipse(void* _context, const BRect& rect, bool fill)
103{
104	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
105	BPoint radii((rect.Width() + 1) / 2.0f, (rect.Height() + 1) / 2.0f);
106	BPoint center = rect.LeftTop() + radii;
107	((void (*)(void*, BPoint, BPoint))
108		context->function_table[fill ? 12 : 11])(context->user_data, center,
109			radii);
110}
111
112
113static void
114draw_polygon(void* _context, size_t numPoints, const BPoint _points[],
115	bool isClosed, bool fill)
116{
117	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
118
119	// This is rather ugly but works for such a trivial class.
120	const size_t kMaxStackCount = 200;
121	char stackData[kMaxStackCount * sizeof(BPoint)];
122	BPoint* points = (BPoint*)stackData;
123	if (numPoints > kMaxStackCount) {
124		points = (BPoint*)malloc(numPoints * sizeof(BPoint));
125		if (points == NULL)
126			return;
127	}
128
129	memcpy((void*)points, _points, numPoints * sizeof(BPoint));
130
131	((void (*)(void*, int32, BPoint*, bool))
132		context->function_table[fill ? 14 : 13])(context->user_data, numPoints,
133			points, isClosed);
134
135	if (numPoints > kMaxStackCount)
136		free(points);
137}
138
139
140static void
141draw_shape(void* _context, const BShape& shape, bool fill)
142{
143	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
144	((void (*)(void*, BShape))context->function_table[fill ? 16 : 15])(
145		context->user_data, shape);
146}
147
148
149static void
150draw_string(void* _context, const char* _string, size_t length,
151	float deltaSpace, float deltaNonSpace)
152{
153	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
154	char* string = strndup(_string, length);
155
156	((void (*)(void*, char*, float, float))
157		context->function_table[17])(context->user_data, string, deltaSpace,
158			deltaNonSpace);
159
160	free(string);
161}
162
163
164static void
165draw_pixels(void* _context, const BRect& src, const BRect& dest, uint32 width,
166	uint32 height, size_t bytesPerRow, color_space pixelFormat, uint32 options,
167	const void* _data, size_t length)
168{
169	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
170	void* data = malloc(length);
171	if (data == NULL)
172		return;
173
174	memcpy(data, _data, length);
175
176	((void (*)(void*, BRect, BRect, int32, int32, int32, int32, int32, void*))
177		context->function_table[18])(context->user_data, src, dest, width,
178			height, bytesPerRow, pixelFormat, options, data);
179
180	free(data);
181}
182
183
184static void
185draw_picture(void* _context, const BPoint& where, int32 token)
186{
187	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
188	((void (*)(void*, BPoint, int32))context->function_table[19])(
189		context->user_data, where, token);
190}
191
192
193static void
194set_clipping_rects(void* _context, size_t numRects, const BRect _rects[])
195{
196	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
197
198	// This is rather ugly but works for such a trivial class.
199	const size_t kMaxStackCount = 100;
200	char stackData[kMaxStackCount * sizeof(BRect)];
201	BRect* rects = (BRect*)stackData;
202	if (numRects > kMaxStackCount) {
203		rects = (BRect*)malloc(numRects * sizeof(BRect));
204		if (rects == NULL)
205			return;
206	}
207
208	memcpy((void*)rects, _rects, numRects * sizeof(BRect));
209
210	((void (*)(void*, BRect*, uint32))context->function_table[20])(
211		context->user_data, rects, numRects);
212
213	if (numRects > kMaxStackCount)
214		free(rects);
215}
216
217
218static void
219clip_to_picture(void* _context, int32 token, const BPoint& origin,
220	bool clipToInverse)
221{
222	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
223	((void (*)(void*, int32, BPoint, bool))context->function_table[21])(
224			context->user_data, token, origin, clipToInverse);
225}
226
227
228static void
229push_state(void* _context)
230{
231	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
232	((void (*)(void*))context->function_table[22])(context->user_data);
233}
234
235
236static void
237pop_state(void* _context)
238{
239	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
240	((void (*)(void*))context->function_table[23])(context->user_data);
241}
242
243
244static void
245enter_state_change(void* _context)
246{
247	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
248	((void (*)(void*))context->function_table[24])(context->user_data);
249}
250
251
252static void
253exit_state_change(void* _context)
254{
255	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
256	((void (*)(void*))context->function_table[25])(context->user_data);
257}
258
259
260static void
261enter_font_state(void* _context)
262{
263	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
264	((void (*)(void*))context->function_table[26])(context->user_data);
265}
266
267
268static void
269exit_font_state(void* _context)
270{
271	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
272	((void (*)(void*))context->function_table[27])(context->user_data);
273}
274
275
276static void
277set_origin(void* _context, const BPoint& origin)
278{
279	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
280	((void (*)(void*, BPoint))context->function_table[28])(context->user_data,
281		origin);
282}
283
284
285static void
286set_pen_location(void* _context, const BPoint& penLocation)
287{
288	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
289	((void (*)(void*, BPoint))context->function_table[29])(context->user_data,
290		penLocation);
291}
292
293
294static void
295set_drawing_mode(void* _context, drawing_mode mode)
296{
297	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
298	((void (*)(void*, drawing_mode))context->function_table[30])(
299		context->user_data, mode);
300}
301
302
303static void
304set_line_mode(void* _context, cap_mode capMode, join_mode joinMode,
305	float miterLimit)
306{
307	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
308	((void (*)(void*, cap_mode, join_mode, float))context->function_table[31])(
309		context->user_data, capMode, joinMode, miterLimit);
310}
311
312
313static void
314set_pen_size(void* _context, float size)
315{
316	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
317	((void (*)(void*, float))context->function_table[32])(context->user_data,
318		size);
319}
320
321
322static void
323set_fore_color(void* _context, const rgb_color& color)
324{
325	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
326	((void (*)(void*, rgb_color))context->function_table[33])(
327		context->user_data, color);
328}
329
330
331static void
332set_back_color(void* _context, const rgb_color& color)
333{
334	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
335	((void (*)(void*, rgb_color))context->function_table[34])(
336		context->user_data, color);
337}
338
339
340static void
341set_stipple_pattern(void* _context, const pattern& stipplePattern)
342{
343	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
344	((void (*)(void*, pattern))context->function_table[35])(context->user_data,
345		stipplePattern);
346}
347
348
349static void
350set_scale(void* _context, float scale)
351{
352	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
353	((void (*)(void*, float))context->function_table[36])(context->user_data,
354		scale);
355}
356
357
358static void
359set_font_family(void* _context, const char* _family, size_t length)
360{
361	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
362	char* family = strndup(_family, length);
363
364	((void (*)(void*, char*))context->function_table[37])(context->user_data,
365		family);
366
367	free(family);
368}
369
370
371static void
372set_font_style(void* _context, const char* _style, size_t length)
373{
374	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
375	char* style = strndup(_style, length);
376
377	((void (*)(void*, char*))context->function_table[38])(context->user_data,
378		style);
379
380	free(style);
381}
382
383
384static void
385set_font_spacing(void* _context, uint8 spacing)
386{
387	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
388	((void (*)(void*, int32))context->function_table[39])(context->user_data,
389		spacing);
390}
391
392
393static void
394set_font_size(void* _context, float size)
395{
396	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
397	((void (*)(void*, float))context->function_table[40])(context->user_data,
398		size);
399}
400
401
402static void
403set_font_rotation(void* _context, float rotation)
404{
405	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
406	((void (*)(void*, float))context->function_table[41])(context->user_data,
407		rotation);
408}
409
410
411static void
412set_font_encoding(void* _context, uint8 encoding)
413{
414	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
415	((void (*)(void*, int32))context->function_table[42])(context->user_data,
416		encoding);
417}
418
419
420static void
421set_font_flags(void* _context, uint32 flags)
422{
423	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
424	((void (*)(void*, int32))context->function_table[43])(context->user_data,
425		flags);
426}
427
428
429static void
430set_font_shear(void* _context, float shear)
431{
432	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
433	((void (*)(void*, float))context->function_table[44])(context->user_data,
434		shear);
435}
436
437
438static void
439set_font_face(void* _context, uint16 face)
440{
441	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
442	((void (*)(void*, int32))context->function_table[46])(context->user_data,
443		face);
444}
445
446
447static void
448set_blending_mode(void* _context, source_alpha alphaSrcMode,
449	alpha_function alphaFncMode)
450{
451	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
452	((void (*)(void*, source_alpha, alpha_function))
453		context->function_table[47])(context->user_data, alphaSrcMode,
454			alphaFncMode);
455}
456
457
458static void
459set_transform(void* _context, const BAffineTransform& transform)
460{
461	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
462	((void (*)(void*, const BAffineTransform&))
463		context->function_table[48])(context->user_data, transform);
464}
465
466
467static void
468translate_by(void* _context, double x, double y)
469{
470	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
471	((void (*)(void*, double, double))
472		context->function_table[49])(context->user_data, x, y);
473}
474
475
476static void
477scale_by(void* _context, double x, double y)
478{
479	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
480	((void (*)(void*, double, double))
481		context->function_table[50])(context->user_data, x, y);
482}
483
484
485static void
486rotate_by(void* _context, double angleRadians)
487{
488	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
489	((void (*)(void*, double))
490		context->function_table[51])(context->user_data, angleRadians);
491}
492
493
494static void
495blend_layer(void* _context, Layer* layer)
496{
497	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
498	((void (*)(void*, Layer*))
499		context->function_table[52])(context->user_data, layer);
500}
501
502
503static void
504clip_to_rect(void* _context, const BRect& rect, bool inverse)
505{
506	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
507	((void (*)(void*, const BRect&, bool))
508		context->function_table[53])(context->user_data, rect, inverse);
509}
510
511
512static void
513clip_to_shape(void* _context, int32 opCount, const uint32 opList[],
514	int32 ptCount, const BPoint ptList[], bool inverse)
515{
516	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
517	((void (*)(void*, int32, const uint32*, int32, const BPoint*, bool))
518		context->function_table[54])(context->user_data, opCount, opList,
519			ptCount, ptList, inverse);
520}
521
522
523static void
524draw_string_locations(void* _context, const char* _string, size_t length,
525	const BPoint* locations, size_t locationCount)
526{
527	adapter_context* context = reinterpret_cast<adapter_context*>(_context);
528	char* string = strndup(_string, length);
529
530	((void (*)(void*, char*, const BPoint*, size_t))
531		context->function_table[55])(context->user_data, string, locations,
532			locationCount);
533
534	free(string);
535}
536
537
538
539#if DEBUG > 1
540static const char *
541PictureOpToString(int op)
542{
543	#define RETURN_STRING(x) case x: return #x
544
545	switch(op) {
546		RETURN_STRING(B_PIC_MOVE_PEN_BY);
547		RETURN_STRING(B_PIC_STROKE_LINE);
548		RETURN_STRING(B_PIC_STROKE_RECT);
549		RETURN_STRING(B_PIC_FILL_RECT);
550		RETURN_STRING(B_PIC_STROKE_ROUND_RECT);
551		RETURN_STRING(B_PIC_FILL_ROUND_RECT);
552		RETURN_STRING(B_PIC_STROKE_BEZIER);
553		RETURN_STRING(B_PIC_FILL_BEZIER);
554		RETURN_STRING(B_PIC_STROKE_POLYGON);
555		RETURN_STRING(B_PIC_FILL_POLYGON);
556		RETURN_STRING(B_PIC_STROKE_SHAPE);
557		RETURN_STRING(B_PIC_FILL_SHAPE);
558		RETURN_STRING(B_PIC_DRAW_STRING);
559		RETURN_STRING(B_PIC_DRAW_STRING_LOCATIONS);
560		RETURN_STRING(B_PIC_DRAW_PIXELS);
561		RETURN_STRING(B_PIC_DRAW_PICTURE);
562		RETURN_STRING(B_PIC_STROKE_ARC);
563		RETURN_STRING(B_PIC_FILL_ARC);
564		RETURN_STRING(B_PIC_STROKE_ELLIPSE);
565		RETURN_STRING(B_PIC_FILL_ELLIPSE);
566
567		RETURN_STRING(B_PIC_ENTER_STATE_CHANGE);
568		RETURN_STRING(B_PIC_SET_CLIPPING_RECTS);
569		RETURN_STRING(B_PIC_CLIP_TO_PICTURE);
570		RETURN_STRING(B_PIC_PUSH_STATE);
571		RETURN_STRING(B_PIC_POP_STATE);
572		RETURN_STRING(B_PIC_CLEAR_CLIPPING_RECTS);
573
574		RETURN_STRING(B_PIC_SET_ORIGIN);
575		RETURN_STRING(B_PIC_SET_PEN_LOCATION);
576		RETURN_STRING(B_PIC_SET_DRAWING_MODE);
577		RETURN_STRING(B_PIC_SET_LINE_MODE);
578		RETURN_STRING(B_PIC_SET_PEN_SIZE);
579		RETURN_STRING(B_PIC_SET_SCALE);
580		RETURN_STRING(B_PIC_SET_TRANSFORM);
581		RETURN_STRING(B_PIC_SET_FORE_COLOR);
582		RETURN_STRING(B_PIC_SET_BACK_COLOR);
583		RETURN_STRING(B_PIC_SET_STIPLE_PATTERN);
584		RETURN_STRING(B_PIC_ENTER_FONT_STATE);
585		RETURN_STRING(B_PIC_SET_BLENDING_MODE);
586		RETURN_STRING(B_PIC_SET_FONT_FAMILY);
587		RETURN_STRING(B_PIC_SET_FONT_STYLE);
588		RETURN_STRING(B_PIC_SET_FONT_SPACING);
589		RETURN_STRING(B_PIC_SET_FONT_ENCODING);
590		RETURN_STRING(B_PIC_SET_FONT_FLAGS);
591		RETURN_STRING(B_PIC_SET_FONT_SIZE);
592		RETURN_STRING(B_PIC_SET_FONT_ROTATE);
593		RETURN_STRING(B_PIC_SET_FONT_SHEAR);
594		RETURN_STRING(B_PIC_SET_FONT_BPP);
595		RETURN_STRING(B_PIC_SET_FONT_FACE);
596
597		RETURN_STRING(B_PIC_AFFINE_TRANSLATE);
598		RETURN_STRING(B_PIC_AFFINE_SCALE);
599		RETURN_STRING(B_PIC_AFFINE_ROTATE);
600
601		RETURN_STRING(B_PIC_BLEND_LAYER);
602
603		default: return "Unknown op";
604	}
605	#undef RETURN_STRING
606}
607#endif
608
609
610PicturePlayer::PicturePlayer(const void *data, size_t size, BList *pictures)
611	:	fData(data),
612		fSize(size),
613		fPictures(pictures)
614{
615}
616
617
618PicturePlayer::~PicturePlayer()
619{
620}
621
622
623status_t
624PicturePlayer::Play(void** callBackTable, int32 tableEntries, void* userData)
625{
626	const BPrivate::picture_player_callbacks kAdapterCallbacks = {
627		move_pen_by,
628		stroke_line,
629		draw_rect,
630		draw_round_rect,
631		draw_bezier,
632		draw_arc,
633		draw_ellipse,
634		draw_polygon,
635		draw_shape,
636		draw_string,
637		draw_pixels,
638		draw_picture,
639		set_clipping_rects,
640		clip_to_picture,
641		push_state,
642		pop_state,
643		enter_state_change,
644		exit_state_change,
645		enter_font_state,
646		exit_font_state,
647		set_origin,
648		set_pen_location,
649		set_drawing_mode,
650		set_line_mode,
651		set_pen_size,
652		set_fore_color,
653		set_back_color,
654		set_stipple_pattern,
655		set_scale,
656		set_font_family,
657		set_font_style,
658		set_font_spacing,
659		set_font_size,
660		set_font_rotation,
661		set_font_encoding,
662		set_font_flags,
663		set_font_shear,
664		set_font_face,
665		set_blending_mode,
666		set_transform,
667		translate_by,
668		scale_by,
669		rotate_by,
670		blend_layer,
671		clip_to_rect,
672		clip_to_shape,
673		draw_string_locations
674	};
675
676	// We don't check if the functions in the table are NULL, but we
677	// check the tableEntries to see if the table is big enough.
678	// If an application supplies the wrong size or an invalid pointer,
679	// it's its own fault.
680
681	// If the caller supplied a function table smaller than needed,
682	// we use our dummy table, and copy the supported ops from the supplied one.
683	void *dummyTable[kOpsTableSize];
684
685	adapter_context adapterContext;
686	adapterContext.user_data = userData;
687	adapterContext.function_table = callBackTable;
688
689	if ((size_t)tableEntries < kOpsTableSize) {
690		memcpy(dummyTable, callBackTable, tableEntries * sizeof(void*));
691		for (size_t i = (size_t)tableEntries; i < kOpsTableSize; i++)
692			dummyTable[i] = (void*)nop;
693
694		adapterContext.function_table = dummyTable;
695	}
696
697	return _Play(kAdapterCallbacks, &adapterContext, fData, fSize, 0);
698}
699
700
701status_t
702PicturePlayer::Play(const picture_player_callbacks& callbacks,
703	size_t callbacksSize, void* userData)
704{
705	return _Play(callbacks, userData, fData, fSize, 0);
706}
707
708
709class DataReader {
710public:
711		DataReader(const void* buffer, size_t length)
712			:
713			fBuffer((const uint8*)buffer),
714			fRemaining(length)
715		{
716		}
717
718		size_t
719		Remaining() const
720		{
721			return fRemaining;
722		}
723
724		template<typename T>
725		bool
726		Get(const T*& typed, size_t count = 1)
727		{
728			if (fRemaining < sizeof(T) * count)
729				return false;
730
731			typed = reinterpret_cast<const T *>(fBuffer);
732			fRemaining -= sizeof(T) * count;
733			fBuffer += sizeof(T) * count;
734			return true;
735		}
736
737		template<typename T>
738		bool
739		GetRemaining(const T*& buffer, size_t& size)
740		{
741			if (fRemaining == 0)
742				return false;
743
744			buffer = reinterpret_cast<const T*>(fBuffer);
745			size = fRemaining;
746			fRemaining = 0;
747			return true;
748		}
749
750private:
751		const uint8*	fBuffer;
752		size_t			fRemaining;
753};
754
755
756struct picture_data_entry_header {
757	uint16 op;
758	uint32 size;
759} _PACKED;
760
761
762status_t
763PicturePlayer::_Play(const picture_player_callbacks& callbacks, void* userData,
764	const void* buffer, size_t length, uint16 parentOp)
765{
766#if DEBUG
767	printf("Start rendering %sBPicture...\n", parentOp != 0 ? "sub " : "");
768	bigtime_t startTime = system_time();
769	int32 numOps = 0;
770#endif
771
772	DataReader pictureReader(buffer, length);
773
774	while (pictureReader.Remaining() > 0) {
775		const picture_data_entry_header* header;
776		const uint8* opData = NULL;
777		if (!pictureReader.Get(header)
778			|| !pictureReader.Get(opData, header->size)) {
779			return B_BAD_DATA;
780		}
781
782		DataReader reader(opData, header->size);
783
784		// Disallow ops that don't fit the parent.
785		switch (parentOp) {
786			case 0:
787				// No parent op, no restrictions.
788				break;
789
790			case B_PIC_ENTER_STATE_CHANGE:
791				if (header->op <= B_PIC_ENTER_STATE_CHANGE
792					|| header->op > B_PIC_SET_TRANSFORM) {
793					return B_BAD_DATA;
794				}
795				break;
796
797			case B_PIC_ENTER_FONT_STATE:
798				if (header->op < B_PIC_SET_FONT_FAMILY
799					|| header->op > B_PIC_SET_FONT_FACE) {
800					return B_BAD_DATA;
801					}
802				break;
803
804			default:
805				return B_BAD_DATA;
806		}
807
808#if DEBUG > 1
809		bigtime_t startOpTime = system_time();
810		printf("Op %s ", PictureOpToString(header->op));
811#endif
812		switch (header->op) {
813			case B_PIC_MOVE_PEN_BY:
814			{
815				const BPoint* where;
816				if (callbacks.move_pen_by == NULL || !reader.Get(where))
817					break;
818
819				callbacks.move_pen_by(userData, *where);
820				break;
821			}
822
823			case B_PIC_STROKE_LINE:
824			{
825				const BPoint* start;
826				const BPoint* end;
827				if (callbacks.stroke_line == NULL || !reader.Get(start)
828					|| !reader.Get(end)) {
829					break;
830				}
831
832				callbacks.stroke_line(userData, *start, *end);
833				break;
834			}
835
836			case B_PIC_STROKE_RECT:
837			case B_PIC_FILL_RECT:
838			{
839				const BRect* rect;
840				if (callbacks.draw_rect == NULL || !reader.Get(rect))
841					break;
842
843				callbacks.draw_rect(userData, *rect,
844					header->op == B_PIC_FILL_RECT);
845				break;
846			}
847
848			case B_PIC_STROKE_ROUND_RECT:
849			case B_PIC_FILL_ROUND_RECT:
850			{
851				const BRect* rect;
852				const BPoint* radii;
853				if (callbacks.draw_round_rect == NULL || !reader.Get(rect)
854					|| !reader.Get(radii)) {
855					break;
856				}
857
858				callbacks.draw_round_rect(userData, *rect, *radii,
859					header->op == B_PIC_FILL_ROUND_RECT);
860				break;
861			}
862
863			case B_PIC_STROKE_BEZIER:
864			case B_PIC_FILL_BEZIER:
865			{
866				const size_t kNumControlPoints = 4;
867				const BPoint* controlPoints;
868				if (callbacks.draw_bezier == NULL
869					|| !reader.Get(controlPoints, kNumControlPoints)) {
870					break;
871				}
872
873				callbacks.draw_bezier(userData, kNumControlPoints,
874					controlPoints, header->op == B_PIC_FILL_BEZIER);
875				break;
876			}
877
878			case B_PIC_STROKE_ARC:
879			case B_PIC_FILL_ARC:
880			{
881				const BPoint* center;
882				const BPoint* radii;
883				const float* startTheta;
884				const float* arcTheta;
885				if (callbacks.draw_arc == NULL || !reader.Get(center)
886					|| !reader.Get(radii) || !reader.Get(startTheta)
887					|| !reader.Get(arcTheta)) {
888					break;
889				}
890
891				callbacks.draw_arc(userData, *center, *radii, *startTheta,
892					*arcTheta, header->op == B_PIC_FILL_ARC);
893				break;
894			}
895
896			case B_PIC_STROKE_ELLIPSE:
897			case B_PIC_FILL_ELLIPSE:
898			{
899				const BRect* rect;
900				if (callbacks.draw_ellipse == NULL || !reader.Get(rect))
901					break;
902
903				callbacks.draw_ellipse(userData, *rect,
904					header->op == B_PIC_FILL_ELLIPSE);
905				break;
906			}
907
908			case B_PIC_STROKE_POLYGON:
909			case B_PIC_FILL_POLYGON:
910			{
911				const uint32* numPoints;
912				const BPoint* points;
913				if (callbacks.draw_polygon == NULL || !reader.Get(numPoints)
914					|| !reader.Get(points, *numPoints)) {
915					break;
916				}
917
918				bool isClosed = true;
919				const bool* closedPointer;
920				if (header->op != B_PIC_FILL_POLYGON) {
921					if (!reader.Get(closedPointer))
922						break;
923
924					isClosed = *closedPointer;
925				}
926
927				callbacks.draw_polygon(userData, *numPoints, points, isClosed,
928					header->op == B_PIC_FILL_POLYGON);
929				break;
930			}
931
932			case B_PIC_STROKE_SHAPE:
933			case B_PIC_FILL_SHAPE:
934			{
935				const uint32* opCount;
936				const uint32* pointCount;
937				const uint32* opList;
938				const BPoint* pointList;
939				if (callbacks.draw_shape == NULL || !reader.Get(opCount)
940					|| !reader.Get(pointCount) || !reader.Get(opList, *opCount)
941					|| !reader.Get(pointList, *pointCount)) {
942					break;
943				}
944
945				// TODO: remove BShape data copying
946				BShape shape;
947				shape.SetData(*opCount, *pointCount, opList, pointList);
948
949				callbacks.draw_shape(userData, shape,
950					header->op == B_PIC_FILL_SHAPE);
951				break;
952			}
953
954			case B_PIC_DRAW_STRING:
955			{
956				const float* escapementSpace;
957				const float* escapementNonSpace;
958				const char* string;
959				size_t length;
960				if (callbacks.draw_string == NULL
961					|| !reader.Get(escapementSpace)
962					|| !reader.Get(escapementNonSpace)
963					|| !reader.GetRemaining(string, length)) {
964					break;
965				}
966
967				callbacks.draw_string(userData, string, length,
968					*escapementSpace, *escapementNonSpace);
969				break;
970			}
971
972			case B_PIC_DRAW_STRING_LOCATIONS:
973			{
974				const uint32* pointCount;
975				const BPoint* pointList;
976				const char* string;
977				size_t length;
978				if (callbacks.draw_string_locations == NULL
979					|| !reader.Get(pointCount)
980					|| !reader.Get(pointList, *pointCount)
981					|| !reader.GetRemaining(string, length)) {
982					break;
983				}
984
985				callbacks.draw_string_locations(userData, string, length,
986					pointList, *pointCount);
987				break;
988			}
989
990			case B_PIC_DRAW_PIXELS:
991			{
992				const BRect* sourceRect;
993				const BRect* destinationRect;
994				const uint32* width;
995				const uint32* height;
996				const uint32* bytesPerRow;
997				const uint32* colorSpace;
998				const uint32* flags;
999				const void* data;
1000				size_t length;
1001				if (callbacks.draw_pixels == NULL || !reader.Get(sourceRect)
1002					|| !reader.Get(destinationRect) || !reader.Get(width)
1003					|| !reader.Get(height) || !reader.Get(bytesPerRow)
1004					|| !reader.Get(colorSpace) || !reader.Get(flags)
1005					|| !reader.GetRemaining(data, length)) {
1006					break;
1007				}
1008
1009				callbacks.draw_pixels(userData, *sourceRect, *destinationRect,
1010					*width, *height, *bytesPerRow, (color_space)*colorSpace,
1011					*flags, data, length);
1012				break;
1013			}
1014
1015			case B_PIC_DRAW_PICTURE:
1016			{
1017				const BPoint* where;
1018				const int32* token;
1019				if (callbacks.draw_picture == NULL || !reader.Get(where)
1020					|| !reader.Get(token)) {
1021					break;
1022				}
1023
1024				callbacks.draw_picture(userData, *where, *token);
1025				break;
1026			}
1027
1028			case B_PIC_SET_CLIPPING_RECTS:
1029			{
1030				const uint32* numRects;
1031				const BRect* rects;
1032				if (callbacks.set_clipping_rects == NULL
1033					|| !reader.Get(numRects) || !reader.Get(rects, *numRects)) {
1034					break;
1035				}
1036
1037				callbacks.set_clipping_rects(userData, *numRects, rects);
1038				break;
1039			}
1040
1041			case B_PIC_CLEAR_CLIPPING_RECTS:
1042			{
1043				if (callbacks.set_clipping_rects == NULL)
1044					break;
1045
1046				callbacks.set_clipping_rects(userData, 0, NULL);
1047				break;
1048			}
1049
1050			case B_PIC_CLIP_TO_PICTURE:
1051			{
1052				const int32* token;
1053				const BPoint* where;
1054				const bool* inverse;
1055				if (callbacks.clip_to_picture == NULL || !reader.Get(token)
1056					|| !reader.Get(where) || !reader.Get(inverse))
1057					break;
1058
1059				callbacks.clip_to_picture(userData, *token, *where, *inverse);
1060				break;
1061			}
1062
1063			case B_PIC_PUSH_STATE:
1064			{
1065				if (callbacks.push_state == NULL)
1066					break;
1067
1068				callbacks.push_state(userData);
1069				break;
1070			}
1071
1072			case B_PIC_POP_STATE:
1073			{
1074				if (callbacks.pop_state == NULL)
1075					break;
1076
1077				callbacks.pop_state(userData);
1078				break;
1079			}
1080
1081			case B_PIC_ENTER_STATE_CHANGE:
1082			case B_PIC_ENTER_FONT_STATE:
1083			{
1084				const void* data;
1085				size_t length;
1086				if (!reader.GetRemaining(data, length))
1087					break;
1088
1089				if (header->op == B_PIC_ENTER_STATE_CHANGE) {
1090					if (callbacks.enter_state_change != NULL)
1091						callbacks.enter_state_change(userData);
1092				} else if (callbacks.enter_font_state != NULL)
1093					callbacks.enter_font_state(userData);
1094
1095				status_t result = _Play(callbacks, userData, data, length,
1096					header->op);
1097				if (result != B_OK)
1098					return result;
1099
1100				if (header->op == B_PIC_ENTER_STATE_CHANGE) {
1101					if (callbacks.exit_state_change != NULL)
1102						callbacks.exit_state_change(userData);
1103				} else if (callbacks.exit_font_state != NULL)
1104					callbacks.exit_font_state(userData);
1105
1106				break;
1107			}
1108
1109			case B_PIC_SET_ORIGIN:
1110			{
1111				const BPoint* origin;
1112				if (callbacks.set_origin == NULL || !reader.Get(origin))
1113					break;
1114
1115				callbacks.set_origin(userData, *origin);
1116				break;
1117			}
1118
1119			case B_PIC_SET_PEN_LOCATION:
1120			{
1121				const BPoint* location;
1122				if (callbacks.set_pen_location == NULL || !reader.Get(location))
1123					break;
1124
1125				callbacks.set_pen_location(userData, *location);
1126				break;
1127			}
1128
1129			case B_PIC_SET_DRAWING_MODE:
1130			{
1131				const uint16* mode;
1132				if (callbacks.set_drawing_mode == NULL || !reader.Get(mode))
1133					break;
1134
1135				callbacks.set_drawing_mode(userData, (drawing_mode)*mode);
1136				break;
1137			}
1138
1139			case B_PIC_SET_LINE_MODE:
1140			{
1141				const uint16* capMode;
1142				const uint16* joinMode;
1143				const float* miterLimit;
1144				if (callbacks.set_line_mode == NULL || !reader.Get(capMode)
1145					|| !reader.Get(joinMode) || !reader.Get(miterLimit)) {
1146					break;
1147				}
1148
1149				callbacks.set_line_mode(userData, (cap_mode)*capMode,
1150					(join_mode)*joinMode, *miterLimit);
1151				break;
1152			}
1153
1154			case B_PIC_SET_PEN_SIZE:
1155			{
1156				const float* penSize;
1157				if (callbacks.set_pen_size == NULL || !reader.Get(penSize))
1158					break;
1159
1160				callbacks.set_pen_size(userData, *penSize);
1161				break;
1162			}
1163
1164			case B_PIC_SET_FORE_COLOR:
1165			{
1166				const rgb_color* color;
1167				if (callbacks.set_fore_color == NULL || !reader.Get(color))
1168					break;
1169
1170				callbacks.set_fore_color(userData, *color);
1171				break;
1172			}
1173
1174			case B_PIC_SET_BACK_COLOR:
1175			{
1176				const rgb_color* color;
1177				if (callbacks.set_back_color == NULL || !reader.Get(color))
1178					break;
1179
1180				callbacks.set_back_color(userData, *color);
1181				break;
1182			}
1183
1184			case B_PIC_SET_STIPLE_PATTERN:
1185			{
1186				const pattern* stipplePattern;
1187				if (callbacks.set_stipple_pattern == NULL
1188					|| !reader.Get(stipplePattern)) {
1189					break;
1190				}
1191
1192				callbacks.set_stipple_pattern(userData, *stipplePattern);
1193				break;
1194			}
1195
1196			case B_PIC_SET_SCALE:
1197			{
1198				const float* scale;
1199				if (callbacks.set_scale == NULL || !reader.Get(scale))
1200					break;
1201
1202				callbacks.set_scale(userData, *scale);
1203				break;
1204			}
1205
1206			case B_PIC_SET_FONT_FAMILY:
1207			{
1208				const char* family;
1209				size_t length;
1210				if (callbacks.set_font_family == NULL
1211					|| !reader.GetRemaining(family, length)) {
1212					break;
1213				}
1214
1215				callbacks.set_font_family(userData, family, length);
1216				break;
1217			}
1218
1219			case B_PIC_SET_FONT_STYLE:
1220			{
1221				const char* style;
1222				size_t length;
1223				if (callbacks.set_font_style == NULL
1224					|| !reader.GetRemaining(style, length)) {
1225					break;
1226				}
1227
1228				callbacks.set_font_style(userData, style, length);
1229				break;
1230			}
1231
1232			case B_PIC_SET_FONT_SPACING:
1233			{
1234				const uint32* spacing;
1235				if (callbacks.set_font_spacing == NULL || !reader.Get(spacing))
1236					break;
1237
1238				callbacks.set_font_spacing(userData, *spacing);
1239				break;
1240			}
1241
1242			case B_PIC_SET_FONT_SIZE:
1243			{
1244				const float* size;
1245				if (callbacks.set_font_size == NULL || !reader.Get(size))
1246					break;
1247
1248				callbacks.set_font_size(userData, *size);
1249				break;
1250			}
1251
1252			case B_PIC_SET_FONT_ROTATE:
1253			{
1254				const float* rotation;
1255				if (callbacks.set_font_rotation == NULL
1256					|| !reader.Get(rotation)) {
1257					break;
1258				}
1259
1260				callbacks.set_font_rotation(userData, *rotation);
1261				break;
1262			}
1263
1264			case B_PIC_SET_FONT_ENCODING:
1265			{
1266				const uint32* encoding;
1267				if (callbacks.set_font_encoding == NULL
1268					|| !reader.Get(encoding)) {
1269					break;
1270				}
1271
1272				callbacks.set_font_encoding(userData, *encoding);
1273				break;
1274			}
1275
1276			case B_PIC_SET_FONT_FLAGS:
1277			{
1278				const uint32* flags;
1279				if (callbacks.set_font_flags == NULL || !reader.Get(flags))
1280					break;
1281
1282				callbacks.set_font_flags(userData, *flags);
1283				break;
1284			}
1285
1286			case B_PIC_SET_FONT_SHEAR:
1287			{
1288				const float* shear;
1289				if (callbacks.set_font_shear == NULL || !reader.Get(shear))
1290					break;
1291
1292				callbacks.set_font_shear(userData, *shear);
1293				break;
1294			}
1295
1296			case B_PIC_SET_FONT_FACE:
1297			{
1298				const uint32* face;
1299				if (callbacks.set_font_face == NULL || !reader.Get(face))
1300					break;
1301
1302				callbacks.set_font_face(userData, *face);
1303				break;
1304			}
1305
1306			case B_PIC_SET_BLENDING_MODE:
1307			{
1308				const uint16* alphaSourceMode;
1309				const uint16* alphaFunctionMode;
1310				if (callbacks.set_blending_mode == NULL
1311					|| !reader.Get(alphaSourceMode)
1312					|| !reader.Get(alphaFunctionMode)) {
1313					break;
1314				}
1315
1316				callbacks.set_blending_mode(userData,
1317					(source_alpha)*alphaSourceMode,
1318					(alpha_function)*alphaFunctionMode);
1319				break;
1320			}
1321
1322			case B_PIC_SET_TRANSFORM:
1323			{
1324				const BAffineTransform* transform;
1325				if (callbacks.set_transform == NULL || !reader.Get(transform))
1326					break;
1327
1328				callbacks.set_transform(userData, *transform);
1329				break;
1330			}
1331
1332			case B_PIC_AFFINE_TRANSLATE:
1333			{
1334				const double* x;
1335				const double* y;
1336				if (callbacks.translate_by == NULL || !reader.Get(x)
1337					|| !reader.Get(y)) {
1338					break;
1339				}
1340
1341				callbacks.translate_by(userData, *x, *y);
1342				break;
1343			}
1344
1345			case B_PIC_AFFINE_SCALE:
1346			{
1347				const double* x;
1348				const double* y;
1349				if (callbacks.scale_by == NULL || !reader.Get(x)
1350					|| !reader.Get(y)) {
1351					break;
1352				}
1353
1354				callbacks.scale_by(userData, *x, *y);
1355				break;
1356			}
1357
1358			case B_PIC_AFFINE_ROTATE:
1359			{
1360				const double* angleRadians;
1361				if (callbacks.rotate_by == NULL || !reader.Get(angleRadians))
1362					break;
1363
1364				callbacks.rotate_by(userData, *angleRadians);
1365				break;
1366			}
1367
1368			case B_PIC_BLEND_LAYER:
1369			{
1370				Layer* const* layer;
1371				if (callbacks.blend_layer == NULL || !reader.Get<Layer*>(layer))
1372					break;
1373
1374				callbacks.blend_layer(userData, *layer);
1375				break;
1376			}
1377
1378			case B_PIC_CLIP_TO_RECT:
1379			{
1380				const bool* inverse;
1381				const BRect* rect;
1382
1383				if (callbacks.clip_to_rect == NULL || !reader.Get(inverse)
1384					|| !reader.Get(rect)) {
1385					break;
1386				}
1387
1388				callbacks.clip_to_rect(userData, *rect, *inverse);
1389				break;
1390			}
1391
1392			case B_PIC_CLIP_TO_SHAPE:
1393			{
1394				const bool* inverse;
1395				const uint32* opCount;
1396				const uint32* pointCount;
1397				const uint32* opList;
1398				const BPoint* pointList;
1399				if (callbacks.clip_to_shape == NULL || !reader.Get(inverse)
1400					|| !reader.Get(opCount) || !reader.Get(pointCount)
1401					|| !reader.Get(opList, *opCount)
1402					|| !reader.Get(pointList, *pointCount)) {
1403					break;
1404				}
1405
1406				callbacks.clip_to_shape(userData, *opCount, opList,
1407					*pointCount, pointList, *inverse);
1408				break;
1409			}
1410
1411			default:
1412				break;
1413		}
1414
1415#if DEBUG
1416		numOps++;
1417#if DEBUG > 1
1418		printf("executed in %" B_PRId64 " usecs\n", system_time()
1419			- startOpTime);
1420#endif
1421#endif
1422	}
1423
1424#if DEBUG
1425	printf("Done! %" B_PRId32 " ops, rendering completed in %" B_PRId64
1426		" usecs.\n", numOps, system_time() - startTime);
1427#endif
1428	return B_OK;
1429}
1430