xref: /haiku/src/kits/interface/PicturePlayer.cpp (revision 959ff00ddee8411dabb09211f3bfbd52d87229da)
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 <stdio.h>
14 #include <string.h>
15 
16 #include <PicturePlayer.h>
17 #include <PictureProtocol.h>
18 
19 #include <Shape.h>
20 
21 using BPrivate::PicturePlayer;
22 
23 typedef void (*fnc)(void*);
24 typedef void (*fnc_BPoint)(void*, BPoint);
25 typedef void (*fnc_BPointBPoint)(void*, BPoint, BPoint);
26 typedef void (*fnc_BRect)(void*, BRect);
27 typedef void (*fnc_BRectBPoint)(void*, BRect, BPoint);
28 typedef void (*fnc_PBPoint)(void*, const BPoint*);
29 typedef void (*fnc_i)(void*, int32);
30 typedef void (*fnc_iPBPointb)(void*, int32, const BPoint*, bool);
31 typedef void (*fnc_iPBPoint)(void*, int32, const BPoint*);
32 typedef void (*fnc_Pc)(void*, const char*);
33 typedef void (*fnc_Pcff)(void*, const char*, float, float);
34 typedef void (*fnc_BPointBPointff)(void*, BPoint, BPoint, float, float);
35 typedef void (*fnc_s)(void*, int16);
36 typedef void (*fnc_ssf)(void*, int16, int16, float);
37 typedef void (*fnc_f)(void*, float);
38 typedef void (*fnc_Color)(void*, rgb_color);
39 typedef void (*fnc_Pattern)(void*, pattern);
40 typedef void (*fnc_ss)(void *, int16, int16);
41 typedef void (*fnc_PBRecti)(void*, const BRect*, uint32);
42 typedef void (*fnc_DrawPixels)(void *, BRect, BRect, int32, int32, int32,
43 							   int32, int32, const void *);
44 typedef void (*fnc_DrawPicture)(void *, BPoint, int32);
45 typedef void (*fnc_BShape)(void*, BShape*);
46 
47 
48 static void
49 nop()
50 {
51 }
52 
53 
54 #if DEBUG
55 static const char *
56 PictureOpToString(int op)
57 {
58 	#define RETURN_STRING(x) case x: return #x
59 
60 	switch(op) {
61 		RETURN_STRING(B_PIC_MOVE_PEN_BY);
62 		RETURN_STRING(B_PIC_STROKE_LINE);
63 		RETURN_STRING(B_PIC_STROKE_RECT);
64 		RETURN_STRING(B_PIC_FILL_RECT);
65 		RETURN_STRING(B_PIC_STROKE_ROUND_RECT);
66 		RETURN_STRING(B_PIC_FILL_ROUND_RECT);
67 		RETURN_STRING(B_PIC_STROKE_BEZIER);
68 		RETURN_STRING(B_PIC_FILL_BEZIER);
69 		RETURN_STRING(B_PIC_STROKE_POLYGON);
70 		RETURN_STRING(B_PIC_FILL_POLYGON);
71 		RETURN_STRING(B_PIC_STROKE_SHAPE);
72 		RETURN_STRING(B_PIC_FILL_SHAPE);
73 		RETURN_STRING(B_PIC_DRAW_STRING);
74 		RETURN_STRING(B_PIC_DRAW_PIXELS);
75 		RETURN_STRING(B_PIC_DRAW_PICTURE);
76 		RETURN_STRING(B_PIC_STROKE_ARC);
77 		RETURN_STRING(B_PIC_FILL_ARC);
78 		RETURN_STRING(B_PIC_STROKE_ELLIPSE);
79 		RETURN_STRING(B_PIC_FILL_ELLIPSE);
80 
81 		RETURN_STRING(B_PIC_ENTER_STATE_CHANGE);
82 		RETURN_STRING(B_PIC_SET_CLIPPING_RECTS);
83 		RETURN_STRING(B_PIC_CLIP_TO_PICTURE);
84 		RETURN_STRING(B_PIC_PUSH_STATE);
85 		RETURN_STRING(B_PIC_POP_STATE);
86 		RETURN_STRING(B_PIC_CLEAR_CLIPPING_RECTS);
87 
88 		RETURN_STRING(B_PIC_SET_ORIGIN);
89 		RETURN_STRING(B_PIC_SET_PEN_LOCATION);
90 		RETURN_STRING(B_PIC_SET_DRAWING_MODE);
91 		RETURN_STRING(B_PIC_SET_LINE_MODE);
92 		RETURN_STRING(B_PIC_SET_PEN_SIZE);
93 		RETURN_STRING(B_PIC_SET_SCALE);
94 		RETURN_STRING(B_PIC_SET_FORE_COLOR);
95 		RETURN_STRING(B_PIC_SET_BACK_COLOR);
96 		RETURN_STRING(B_PIC_SET_STIPLE_PATTERN);
97 		RETURN_STRING(B_PIC_ENTER_FONT_STATE);
98 		RETURN_STRING(B_PIC_SET_BLENDING_MODE);
99 		RETURN_STRING(B_PIC_SET_FONT_FAMILY);
100 		RETURN_STRING(B_PIC_SET_FONT_STYLE);
101 		RETURN_STRING(B_PIC_SET_FONT_SPACING);
102 		RETURN_STRING(B_PIC_SET_FONT_ENCODING);
103 		RETURN_STRING(B_PIC_SET_FONT_FLAGS);
104 		RETURN_STRING(B_PIC_SET_FONT_SIZE);
105 		RETURN_STRING(B_PIC_SET_FONT_ROTATE);
106 		RETURN_STRING(B_PIC_SET_FONT_SHEAR);
107 		RETURN_STRING(B_PIC_SET_FONT_BPP);
108 		RETURN_STRING(B_PIC_SET_FONT_FACE);
109 		default: return "Unknown op";
110 	}
111 	#undef RETURN_STRING
112 }
113 #endif
114 
115 
116 PicturePlayer::PicturePlayer(const void *data, size_t size, BList *pictures)
117 	:	fData(data),
118 		fSize(size),
119 		fPictures(pictures)
120 {
121 }
122 
123 
124 PicturePlayer::~PicturePlayer()
125 {
126 }
127 
128 
129 status_t
130 PicturePlayer::Play(void **callBackTable, int32 tableEntries, void *userData)
131 {
132 	// We don't check if the functions in the table are NULL, but we
133 	// check the tableEntries to see if the table is big enough.
134 	// If an application supplies the wrong size or an invalid pointer,
135 	// it's its own fault.
136 #if DEBUG
137 	FILE *file = fopen("/var/log/PicturePlayer.log", "a");
138 	fprintf(file, "Start rendering BPicture...\n");
139 	bigtime_t startTime = system_time();
140 	int32 numOps = 0;
141 #endif
142 	// If the caller supplied a function table smaller than needed,
143 	// we use our dummy table, and copy the supported ops from the supplied one.
144 	void **functionTable = callBackTable;
145 	void *dummyTable[kOpsTableSize] = {
146 		(void *)nop, (void *)nop, (void *)nop, (void *)nop,
147 		(void *)nop, (void *)nop, (void *)nop, (void *)nop,
148 		(void *)nop, (void *)nop, (void *)nop, (void *)nop,
149 		(void *)nop, (void *)nop, (void *)nop, (void *)nop,
150 		(void *)nop, (void *)nop, (void *)nop, (void *)nop,
151 		(void *)nop, (void *)nop, (void *)nop, (void *)nop,
152 		(void *)nop, (void *)nop, (void *)nop, (void *)nop,
153 		(void *)nop, (void *)nop, (void *)nop, (void *)nop,
154 		(void *)nop, (void *)nop, (void *)nop, (void *)nop,
155 		(void *)nop, (void *)nop, (void *)nop, (void *)nop,
156 		(void *)nop, (void *)nop, (void *)nop, (void *)nop,
157 		(void *)nop, (void *)nop, (void *)nop, (void *)nop
158 	};
159 
160 	if ((uint32)tableEntries < kOpsTableSize) {
161 #if DEBUG
162 		fprintf(file, "PicturePlayer: A smaller than needed function table was supplied.\n");
163 #endif
164 		functionTable = dummyTable;
165 		memcpy(functionTable, callBackTable, tableEntries * sizeof(void *));
166 	}
167 
168 	const char *data = reinterpret_cast<const char *>(fData);
169 	size_t pos = 0;
170 
171 	int32 fontStateBlockSize = -1;
172 	int32 stateBlockSize = -1;
173 
174 	while ((pos + 6) <= fSize) {
175 		int16 op = *reinterpret_cast<const int16 *>(data);
176 		int32 size = *reinterpret_cast<const int32 *>(data + 2);
177 		pos += 6;
178 		data += 6;
179 
180 		if (pos + size > fSize)
181 			debugger("PicturePlayer::Play: buffer overrun\n");
182 
183 #if DEBUG > 1
184 		bigtime_t startOpTime = system_time();
185 		fprintf(file, "Op %s ", PictureOpToString(op));
186 #endif
187 		switch (op) {
188 			case B_PIC_MOVE_PEN_BY:
189 			{
190 				((fnc_BPoint)functionTable[1])(userData,
191 					*reinterpret_cast<const BPoint *>(data)); /* where */
192 				break;
193 			}
194 
195 			case B_PIC_STROKE_LINE:
196 			{
197 				((fnc_BPointBPoint)functionTable[2])(userData,
198 					*reinterpret_cast<const BPoint *>(data), /* start */
199 					*reinterpret_cast<const BPoint *>(data + sizeof(BPoint))); /* end */
200 				break;
201 			}
202 
203 			case B_PIC_STROKE_RECT:
204 			{
205 				((fnc_BRect)functionTable[3])(userData,
206 					*reinterpret_cast<const BRect *>(data)); /* rect */
207 				break;
208 			}
209 
210 			case B_PIC_FILL_RECT:
211 			{
212 				((fnc_BRect)functionTable[4])(userData,
213 					*reinterpret_cast<const BRect *>(data)); /* rect */
214 				break;
215 			}
216 
217 			case B_PIC_STROKE_ROUND_RECT:
218 			{
219 				((fnc_BRectBPoint)functionTable[5])(userData,
220 					*reinterpret_cast<const BRect *>(data), /* rect */
221 					*reinterpret_cast<const BPoint *>(data + sizeof(BRect))); /* radii */
222 				break;
223 			}
224 
225 			case B_PIC_FILL_ROUND_RECT:
226 			{
227 				((fnc_BRectBPoint)functionTable[6])(userData,
228 					*reinterpret_cast<const BRect *>(data), /* rect */
229 					*reinterpret_cast<const BPoint *>(data + sizeof(BRect))); /* radii */
230 				break;
231 			}
232 
233 			case B_PIC_STROKE_BEZIER:
234 			{
235 				((fnc_PBPoint)functionTable[7])(userData,
236 					reinterpret_cast<const BPoint *>(data));
237 				break;
238 			}
239 
240 			case B_PIC_FILL_BEZIER:
241 			{
242 				((fnc_PBPoint)functionTable[8])(userData,
243 					reinterpret_cast<const BPoint *>(data));
244 				break;
245 			}
246 
247 			case B_PIC_STROKE_ARC:
248 			{
249 				((fnc_BPointBPointff)functionTable[9])(userData,
250 					*reinterpret_cast<const BPoint *>(data), /* center */
251 					*reinterpret_cast<const BPoint *>(data + sizeof(BPoint)), /* radii */
252 					*reinterpret_cast<const float *>(data + 2 * sizeof(BPoint)), /* startTheta */
253 					*reinterpret_cast<const float *>(data + 2 * sizeof(BPoint) + sizeof(float))); /* arcTheta */
254 				break;
255 			}
256 
257 			case B_PIC_FILL_ARC:
258 			{
259 				((fnc_BPointBPointff)functionTable[10])(userData,
260 					*reinterpret_cast<const BPoint *>(data), /* center */
261 					*reinterpret_cast<const BPoint *>(data + sizeof(BPoint)), /* radii */
262 					*reinterpret_cast<const float *>(data + 2 * sizeof(BPoint)), /* startTheta */
263 					*reinterpret_cast<const float *>(data + 2 * sizeof(BPoint) + sizeof(float))); /* arcTheta */
264 				break;
265 			}
266 
267 			case B_PIC_STROKE_ELLIPSE:
268 			{
269 				const BRect *rect = reinterpret_cast<const BRect *>(data);
270 				BPoint radii((rect->Width() + 1) / 2.0f, (rect->Height() + 1) / 2.0f);
271 				BPoint center = rect->LeftTop() + radii;
272 				((fnc_BPointBPoint)functionTable[11])(userData, center, radii);
273 				break;
274 			}
275 
276 			case B_PIC_FILL_ELLIPSE:
277 			{
278 				const BRect *rect = reinterpret_cast<const BRect *>(data);
279 				BPoint radii((rect->Width() + 1) / 2.0f, (rect->Height() + 1) / 2.0f);
280 				BPoint center = rect->LeftTop() + radii;
281 				((fnc_BPointBPoint)functionTable[12])(userData, center, radii);
282 				break;
283 			}
284 
285 			case B_PIC_STROKE_POLYGON:
286 			{
287 				int32 numPoints = *reinterpret_cast<const int32 *>(data);
288 				((fnc_iPBPointb)functionTable[13])(userData,
289 					numPoints,
290 					reinterpret_cast<const BPoint *>(data + sizeof(int32)), /* points */
291 					*reinterpret_cast<const uint8 *>(data + sizeof(int32) + numPoints * sizeof(BPoint))); /* is-closed */
292 				break;
293 			}
294 
295 			case B_PIC_FILL_POLYGON:
296 			{
297 				((fnc_iPBPoint)functionTable[14])(userData,
298 					*reinterpret_cast<const int32 *>(data), /* numPoints */
299 					reinterpret_cast<const BPoint *>(data + sizeof(int32))); /* points */
300 				break;
301 			}
302 
303 			case B_PIC_STROKE_SHAPE:
304 			case B_PIC_FILL_SHAPE:
305 			{
306 				const bool stroke = (op == B_PIC_STROKE_SHAPE);
307 				int32 opCount = *reinterpret_cast<const int32 *>(data);
308 				int32 ptCount = *reinterpret_cast<const int32 *>(data + sizeof(int32));
309 				const uint32 *opList = reinterpret_cast<const uint32 *>(data + 2 * sizeof(int32));
310 				const BPoint *ptList = reinterpret_cast<const BPoint *>(data + 2 * sizeof(int32) + opCount * sizeof(uint32));
311 
312 				// TODO: remove BShape data copying
313 				BShape shape;
314 				shape.SetData(opCount, ptCount, opList, ptList);
315 
316 				const int32 tableIndex = stroke ? 15 : 16;
317 				((fnc_BShape)functionTable[tableIndex])(userData, &shape);
318 				break;
319 			}
320 
321 			case B_PIC_DRAW_STRING:
322 			{
323 				((fnc_Pcff)functionTable[17])(userData,
324 					reinterpret_cast<const char *>(data + 2 * sizeof(float)), /* string */
325 					*reinterpret_cast<const float *>(data), /* escapement.space */
326 					*reinterpret_cast<const float *>(data + sizeof(float))); /* escapement.nonspace */
327 				break;
328 			}
329 
330 			case B_PIC_DRAW_PIXELS:
331 			{
332 				((fnc_DrawPixels)functionTable[18])(userData,
333 					*reinterpret_cast<const BRect *>(data), /* src */
334 					*reinterpret_cast<const BRect *>(data + 1 * sizeof(BRect)), /* dst */
335 					*reinterpret_cast<const int32 *>(data + 2 * sizeof(BRect)), /* width */
336 					*reinterpret_cast<const int32 *>(data + 2 * sizeof(BRect) + 1 * sizeof(int32)), /* height */
337 					*reinterpret_cast<const int32 *>(data + 2 * sizeof(BRect) + 2 * sizeof(int32)), /* bytesPerRow */
338 					*reinterpret_cast<const int32 *>(data + 2 * sizeof(BRect) + 3 * sizeof(int32)), /* pixelFormat */
339 					*reinterpret_cast<const int32 *>(data + 2 * sizeof(BRect) + 4 * sizeof(int32)), /* flags */
340 					reinterpret_cast<const void *>(data + 2 * sizeof(BRect) + 5 * sizeof(int32))); /* data */
341 				break;
342 			}
343 
344 			case B_PIC_DRAW_PICTURE:
345 			{
346 				((fnc_DrawPicture)functionTable[19])(userData,
347 					*reinterpret_cast<const BPoint *>(data),
348 					*reinterpret_cast<const int32 *>(data + sizeof(BPoint)));
349 				break;
350 			}
351 
352 			case B_PIC_SET_CLIPPING_RECTS:
353 			{
354 				// TODO: Not sure if it's compatible with R5's BPicture version
355 				const uint32 numRects = *reinterpret_cast<const uint32 *>(data);
356 				const BRect *rects = reinterpret_cast<const BRect *>(data + sizeof(uint32));
357 				((fnc_PBRecti)functionTable[20])(userData, rects, numRects);
358 
359 				break;
360 			}
361 
362 			case B_PIC_CLEAR_CLIPPING_RECTS:
363 			{
364 				((fnc_PBRecti)functionTable[20])(userData, NULL, 0);
365 				break;
366 			}
367 
368 			case B_PIC_CLIP_TO_PICTURE:
369 			{
370 				// TODO: Implement
371 				break;
372 			}
373 
374 			case B_PIC_PUSH_STATE:
375 			{
376 				((fnc)functionTable[22])(userData);
377 				break;
378 			}
379 
380 			case B_PIC_POP_STATE:
381 			{
382 				((fnc)functionTable[23])(userData);
383 				break;
384 			}
385 
386 			case B_PIC_ENTER_STATE_CHANGE:
387 			{
388 				((fnc)functionTable[24])(userData);
389 				stateBlockSize = size;
390 				break;
391 			}
392 
393 			case B_PIC_ENTER_FONT_STATE:
394 			{
395 				((fnc)functionTable[26])(userData);
396 				fontStateBlockSize = size;
397 				break;
398 			}
399 
400 			case B_PIC_SET_ORIGIN:
401 			{
402 				((fnc_BPoint)functionTable[28])(userData,
403 					*reinterpret_cast<const BPoint *>(data)); /* origin */
404 				break;
405 			}
406 
407 			case B_PIC_SET_PEN_LOCATION:
408 			{
409 				((fnc_BPoint)functionTable[29])(userData,
410 					*reinterpret_cast<const BPoint *>(data)); /* location */
411 				break;
412 			}
413 
414 			case B_PIC_SET_DRAWING_MODE:
415 			{
416 				((fnc_s)functionTable[30])(userData,
417 					*reinterpret_cast<const int16 *>(data)); /* mode */
418 				break;
419 			}
420 
421 			case B_PIC_SET_LINE_MODE:
422 			{
423 				((fnc_ssf)functionTable[31])(userData,
424 					*reinterpret_cast<const int16 *>(data), /* cap-mode */
425 					*reinterpret_cast<const int16 *>(data + 1 * sizeof(int16)), /* join-mode */
426 					*reinterpret_cast<const float *>(data + 2 * sizeof(int16))); /* miter-limit */
427 				break;
428 			}
429 
430 			case B_PIC_SET_PEN_SIZE:
431 			{
432 				((fnc_f)functionTable[32])(userData,
433 					*reinterpret_cast<const float *>(data)); /* size */
434 				break;
435 			}
436 
437 			case B_PIC_SET_FORE_COLOR:
438 			{
439 				((fnc_Color)functionTable[33])(userData,
440 					*reinterpret_cast<const rgb_color *>(data)); /* color */
441 				break;
442 			}
443 
444 			case B_PIC_SET_BACK_COLOR:
445 			{
446 				((fnc_Color)functionTable[34])(userData,
447 					*reinterpret_cast<const rgb_color *>(data)); /* color */
448 				break;
449 			}
450 
451 			case B_PIC_SET_STIPLE_PATTERN:
452 			{
453 				((fnc_Pattern)functionTable[35])(userData,
454 					*reinterpret_cast<const pattern *>(data)); /* pattern */
455 				break;
456 			}
457 
458 			case B_PIC_SET_SCALE:
459 			{
460 				((fnc_f)functionTable[36])(userData,
461 					*reinterpret_cast<const float *>(data)); /* scale */
462 				break;
463 			}
464 
465 			case B_PIC_SET_FONT_FAMILY:
466 			{
467 				((fnc_Pc)functionTable[37])(userData,
468 					reinterpret_cast<const char *>(data)); /* string */
469 				break;
470 			}
471 
472 			case B_PIC_SET_FONT_STYLE:
473 			{
474 				((fnc_Pc)functionTable[38])(userData,
475 					reinterpret_cast<const char *>(data)); /* string */
476 				break;
477 			}
478 
479 			case B_PIC_SET_FONT_SPACING:
480 			{
481 				((fnc_i)functionTable[39])(userData,
482 					*reinterpret_cast<const int32 *>(data)); /* spacing */
483 				break;
484 			}
485 
486 			case B_PIC_SET_FONT_SIZE:
487 			{
488 				((fnc_f)functionTable[40])(userData,
489 					*reinterpret_cast<const float *>(data)); /* size */
490 				break;
491 			}
492 
493 			case B_PIC_SET_FONT_ROTATE:
494 			{
495 				((fnc_f)functionTable[41])(userData,
496 					*reinterpret_cast<const float *>(data)); /* rotation */
497 				break;
498 			}
499 
500 			case B_PIC_SET_FONT_ENCODING:
501 			{
502 				((fnc_i)functionTable[42])(userData,
503 					*reinterpret_cast<const int32 *>(data)); /* encoding */
504 				break;
505 			}
506 
507 			case B_PIC_SET_FONT_FLAGS:
508 			{
509 				((fnc_i)functionTable[43])(userData,
510 					*reinterpret_cast<const int32 *>(data)); /* flags */
511 				break;
512 			}
513 
514 			case B_PIC_SET_FONT_SHEAR:
515 			{
516 				((fnc_f)functionTable[44])(userData,
517 					*reinterpret_cast<const float *>(data)); /* shear */
518 				break;
519 			}
520 
521 			case B_PIC_SET_FONT_FACE:
522 			{
523 				((fnc_i)functionTable[46])(userData,
524 					*reinterpret_cast<const int32 *>(data)); /* flags */
525 				break;
526 			}
527 
528 			case B_PIC_SET_BLENDING_MODE:
529 			{
530 				((fnc_ss)functionTable[47])(userData,
531 					*reinterpret_cast<const int16 *>(data), /* alphaSrcMode */
532 					*reinterpret_cast<const int16 *>(data + sizeof(int16))); /* alphaFncMode */
533 				break;
534 			}
535 
536 			default:
537 				break;
538 		}
539 
540 		// Skip the already handled block unless it's one of these two,
541 		// since they can contain other nested ops.
542 		if (op != B_PIC_ENTER_STATE_CHANGE && op != B_PIC_ENTER_FONT_STATE) {
543 			pos += size;
544 			data += size;
545 			if (stateBlockSize > 0)
546 				stateBlockSize -= size + 6;
547 			if (fontStateBlockSize > 0)
548 				fontStateBlockSize -= size + 6;
549 		}
550 
551 		// call the exit_state_change hook if needed
552 		if (stateBlockSize == 0) {
553 			((fnc)functionTable[25])(userData);
554 			stateBlockSize = -1;
555 		}
556 
557 		// call the exit_font_state hook if needed
558 		if (fontStateBlockSize == 0) {
559 			((fnc)functionTable[27])(userData);
560 			fontStateBlockSize = -1;
561 		}
562 #if DEBUG
563 		numOps++;
564 #if DEBUG > 1
565 		fprintf(file, "executed in %lld usecs\n", system_time() - startOpTime);
566 #endif
567 #endif
568 		// TODO: what if too much was read, should we return B_ERROR?
569 	}
570 
571 #if DEBUG
572 	fprintf(file, "Done! %ld ops, rendering completed in %lld usecs.\n", numOps, system_time() - startTime);
573 	fclose(file);
574 #endif
575 	return B_OK;
576 }
577