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
21using BPrivate::PicturePlayer;
22
23typedef void (*fnc)(void*);
24typedef void (*fnc_BPoint)(void*, BPoint);
25typedef void (*fnc_BPointBPoint)(void*, BPoint, BPoint);
26typedef void (*fnc_BRect)(void*, BRect);
27typedef void (*fnc_BRectBPoint)(void*, BRect, BPoint);
28typedef void (*fnc_PBPoint)(void*, const BPoint*);
29typedef void (*fnc_i)(void*, int32);
30typedef void (*fnc_iPBPointb)(void*, int32, const BPoint*, bool);
31typedef void (*fnc_iPBPoint)(void*, int32, const BPoint*);
32typedef void (*fnc_Pc)(void*, const char*);
33typedef void (*fnc_Pcff)(void*, const char*, float, float);
34typedef void (*fnc_BPointBPointff)(void*, BPoint, BPoint, float, float);
35typedef void (*fnc_s)(void*, int16);
36typedef void (*fnc_ssf)(void*, int16, int16, float);
37typedef void (*fnc_f)(void*, float);
38typedef void (*fnc_Color)(void*, rgb_color);
39typedef void (*fnc_Pattern)(void*, pattern);
40typedef void (*fnc_ss)(void *, int16, int16);
41typedef void (*fnc_PBRecti)(void*, const BRect*, uint32);
42typedef void (*fnc_DrawPixels)(void *, BRect, BRect, int32, int32, int32,
43							   int32, int32, const void *);
44typedef void (*fnc_DrawPicture)(void *, BPoint, int32);
45typedef void (*fnc_BShape)(void*, BShape*);
46
47
48static void
49nop()
50{
51}
52
53
54#if DEBUG > 1
55static const char *
56PictureOpToString(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
116PicturePlayer::PicturePlayer(const void *data, size_t size, BList *pictures)
117	:	fData(data),
118		fSize(size),
119		fPictures(pictures)
120{
121}
122
123
124PicturePlayer::~PicturePlayer()
125{
126}
127
128
129status_t
130PicturePlayer::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 %" B_PRId64 " usecs\n", system_time()
566			- startOpTime);
567#endif
568#endif
569		// TODO: what if too much was read, should we return B_ERROR?
570	}
571
572#if DEBUG
573	fprintf(file, "Done! %" B_PRId32 " ops, rendering completed in %"
574		B_PRId64 " usecs.\n", numOps, system_time() - startTime);
575	fclose(file);
576#endif
577	return B_OK;
578}
579