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