1 #include "mupdf/fitz.h"
2 
3 #include <assert.h>
4 #include <string.h>
5 
6 #define STACK_SIZE 96
7 
8 typedef enum
9 {
10 	FZ_CMD_FILL_PATH,
11 	FZ_CMD_STROKE_PATH,
12 	FZ_CMD_CLIP_PATH,
13 	FZ_CMD_CLIP_STROKE_PATH,
14 	FZ_CMD_FILL_TEXT,
15 	FZ_CMD_STROKE_TEXT,
16 	FZ_CMD_CLIP_TEXT,
17 	FZ_CMD_CLIP_STROKE_TEXT,
18 	FZ_CMD_IGNORE_TEXT,
19 	FZ_CMD_FILL_SHADE,
20 	FZ_CMD_FILL_IMAGE,
21 	FZ_CMD_FILL_IMAGE_MASK,
22 	FZ_CMD_CLIP_IMAGE_MASK,
23 	FZ_CMD_POP_CLIP,
24 	FZ_CMD_BEGIN_MASK,
25 	FZ_CMD_END_MASK,
26 	FZ_CMD_BEGIN_GROUP,
27 	FZ_CMD_END_GROUP,
28 	FZ_CMD_BEGIN_TILE,
29 	FZ_CMD_END_TILE,
30 	FZ_CMD_RENDER_FLAGS,
31 	FZ_CMD_DEFAULT_COLORSPACES,
32 	FZ_CMD_BEGIN_LAYER,
33 	FZ_CMD_END_LAYER
34 } fz_display_command;
35 
36 /* The display list is a list of nodes.
37  * Each node is a structure consisting of a bitfield (that packs into a
38  * 32 bit word).
39  * The different fields in the bitfield identify what information is
40  * present in the node.
41  *
42  *	cmd:	What type of node this is.
43  *
44  *	size:	The number of sizeof(fz_display_node) bytes that this node's
45  *		data occupies. (i.e. &node[node->size] = the next node in the
46  *		chain; 0 for end of list).
47  *
48  *	rect:	0 for unchanged, 1 for present.
49  *
50  *	path:	0 for unchanged, 1 for present.
51  *
52  *	cs:	0 for unchanged
53  *		1 for devicegray (color defaults to 0)
54  *		2 for devicegray (color defaults to 1)
55  *		3 for devicergb (color defaults to 0,0,0)
56  *		4 for devicergb (color defaults to 1,1,1)
57  *		5 for devicecmyk (color defaults to 0,0,0,0)
58  *		6 for devicecmyk (color defaults to 0,0,0,1)
59  *		7 for present (color defaults to 0)
60  *
61  *	color:	0 for unchanged color, 1 for present.
62  *
63  *	alpha:	0 for unchanged, 1 for solid, 2 for transparent, 3
64  *		for alpha value present.
65  *
66  *	ctm:	0 for unchanged,
67  *		1 for change ad
68  *		2 for change bc
69  *		4 for change ef.
70  *
71  *	stroke:	0 for unchanged, 1 for present.
72  *
73  *	flags:	Flags (node specific meanings)
74  *
75  * Nodes are packed in the order:
76  * header, rect, colorspace, color, alpha, ctm, stroke_state, path, private data.
77  */
78 typedef struct
79 {
80 	unsigned int cmd    : 5;
81 	unsigned int size   : 9;
82 	unsigned int rect   : 1;
83 	unsigned int path   : 1;
84 	unsigned int cs     : 3;
85 	unsigned int color  : 1;
86 	unsigned int alpha  : 2;
87 	unsigned int ctm    : 3;
88 	unsigned int stroke : 1;
89 	unsigned int flags  : 6;
90 } fz_display_node;
91 
92 enum {
93 	CS_UNCHANGED = 0,
94 	CS_GRAY_0    = 1,
95 	CS_GRAY_1    = 2,
96 	CS_RGB_0     = 3,
97 	CS_RGB_1     = 4,
98 	CS_CMYK_0    = 5,
99 	CS_CMYK_1    = 6,
100 	CS_OTHER_0   = 7,
101 
102 	ALPHA_UNCHANGED = 0,
103 	ALPHA_1         = 1,
104 	ALPHA_0         = 2,
105 	ALPHA_PRESENT   = 3,
106 
107 	CTM_UNCHANGED = 0,
108 	CTM_CHANGE_AD = 1,
109 	CTM_CHANGE_BC = 2,
110 	CTM_CHANGE_EF = 4,
111 
112 	MAX_NODE_SIZE = (1<<9)-sizeof(fz_display_node)
113 };
114 
115 struct fz_display_list
116 {
117 	fz_storable storable;
118 	fz_display_node *list;
119 	fz_rect mediabox;
120 	size_t max;
121 	size_t len;
122 };
123 
124 typedef struct
125 {
126 	fz_device super;
127 
128 	fz_display_list *list;
129 
130 	fz_path *path;
131 	float alpha;
132 	fz_matrix ctm;
133 	fz_stroke_state *stroke;
134 	fz_colorspace *colorspace;
135 	fz_color_params *color_params;
136 	float color[FZ_MAX_COLORS];
137 	fz_rect rect;
138 
139 	int top;
140 	struct {
141 		fz_rect *update;
142 		fz_rect rect;
143 	} stack[STACK_SIZE];
144 	int tiled;
145 } fz_list_device;
146 
147 enum { ISOLATED = 1, KNOCKOUT = 2 };
148 enum { OPM = 1, OP = 2, BP = 3, RI = 4};
149 
150 #define SIZE_IN_NODES(t) \
151 	((t + sizeof(fz_display_node) - 1) / sizeof(fz_display_node))
152 
153 static void
fz_append_display_node(fz_context * ctx,fz_device * dev,fz_display_command cmd,int flags,const fz_rect * rect,const fz_path * path,const float * color,fz_colorspace * colorspace,const float * alpha,const fz_matrix * ctm,const fz_stroke_state * stroke,const void * private_data,size_t private_data_len)154 fz_append_display_node(
155 	fz_context *ctx,
156 	fz_device *dev,
157 	fz_display_command cmd,
158 	int flags,
159 	const fz_rect *rect,
160 	const fz_path *path,
161 	const float *color,
162 	fz_colorspace *colorspace,
163 	const float *alpha,
164 	const fz_matrix *ctm,
165 	const fz_stroke_state *stroke,
166 	const void *private_data,
167 	size_t private_data_len)
168 {
169 	fz_display_node node = { 0 };
170 	fz_display_node *node_ptr;
171 	fz_list_device *writer = (fz_list_device *)dev;
172 	fz_display_list *list = writer->list;
173 	size_t size;
174 	size_t rect_off = 0;
175 	size_t path_off = 0;
176 	size_t color_off = 0;
177 	size_t colorspace_off = 0;
178 	size_t alpha_off = 0;
179 	size_t ctm_off = 0;
180 	size_t stroke_off = 0;
181 	int rect_for_updates = 0;
182 	size_t private_off = 0;
183 	fz_path *my_path = NULL;
184 	fz_stroke_state *my_stroke = NULL;
185 	fz_rect local_rect;
186 	size_t path_size = 0;
187 
188 	switch (cmd)
189 	{
190 	case FZ_CMD_CLIP_PATH:
191 	case FZ_CMD_CLIP_STROKE_PATH:
192 	case FZ_CMD_CLIP_TEXT:
193 	case FZ_CMD_CLIP_STROKE_TEXT:
194 	case FZ_CMD_CLIP_IMAGE_MASK:
195 		if (writer->top < STACK_SIZE)
196 		{
197 			rect_for_updates = 1;
198 			writer->stack[writer->top].rect = fz_empty_rect;
199 		}
200 		writer->top++;
201 		break;
202 	case FZ_CMD_END_MASK:
203 		if (writer->top < STACK_SIZE)
204 		{
205 			writer->stack[writer->top].update = NULL;
206 			writer->stack[writer->top].rect = fz_empty_rect;
207 		}
208 		writer->top++;
209 		break;
210 	case FZ_CMD_BEGIN_TILE:
211 		writer->tiled++;
212 		if (writer->top > 0 && writer->top <= STACK_SIZE)
213 		{
214 			writer->stack[writer->top-1].rect = fz_infinite_rect;
215 		}
216 		break;
217 	case FZ_CMD_END_TILE:
218 		writer->tiled--;
219 		break;
220 	case FZ_CMD_END_GROUP:
221 		break;
222 	case FZ_CMD_POP_CLIP:
223 		if (writer->top > STACK_SIZE)
224 		{
225 			writer->top--;
226 			rect = &fz_infinite_rect;
227 		}
228 		else if (writer->top > 0)
229 		{
230 			fz_rect *update;
231 			writer->top--;
232 			update = writer->stack[writer->top].update;
233 			if (writer->tiled == 0)
234 			{
235 				if (update)
236 				{
237 					*update = fz_intersect_rect(*update, writer->stack[writer->top].rect);
238 					local_rect = *update;
239 					rect = &local_rect;
240 				}
241 				else
242 					rect = &writer->stack[writer->top].rect;
243 			}
244 			else
245 				rect = &fz_infinite_rect;
246 		}
247 		/* fallthrough */
248 	default:
249 		if (writer->top > 0 && writer->tiled == 0 && writer->top <= STACK_SIZE && rect)
250 			writer->stack[writer->top-1].rect = fz_union_rect(writer->stack[writer->top-1].rect, *rect);
251 		break;
252 	}
253 
254 	size = 1; /* 1 for the fz_display_node */
255 	node.cmd = cmd;
256 
257 	/* Figure out what we need to write, and the offsets at which we will
258 	 * write it. */
259 	if (rect_for_updates || (rect != NULL && (writer->rect.x0 != rect->x0 || writer->rect.y0 != rect->y0 || writer->rect.x1 != rect->x1 || writer->rect.y1 != rect->y1)))
260 	{
261 		node.rect = 1;
262 		rect_off = size;
263 		size += SIZE_IN_NODES(sizeof(fz_rect));
264 	}
265 	if (color || colorspace)
266 	{
267 		if (colorspace != writer->colorspace)
268 		{
269 			assert(color);
270 			if (colorspace == fz_device_gray(ctx))
271 			{
272 				if (color[0] == 0.0f)
273 					node.cs = CS_GRAY_0, color = NULL;
274 				else
275 				{
276 					node.cs = CS_GRAY_1;
277 					if (color[0] == 1.0f)
278 						color = NULL;
279 				}
280 			}
281 			else if (colorspace == fz_device_rgb(ctx))
282 			{
283 				if (color[0] == 0.0f && color[1] == 0.0f && color[2] == 0.0f)
284 					node.cs = CS_RGB_0, color = NULL;
285 				else
286 				{
287 					node.cs = CS_RGB_1;
288 					if (color[0] == 1.0f && color[1] == 1.0f && color[2] == 1.0f)
289 						color = NULL;
290 				}
291 			}
292 			else if (colorspace == fz_device_cmyk(ctx))
293 			{
294 				node.cs = CS_CMYK_0;
295 				if (color[0] == 0.0f && color[1] == 0.0f && color[2] == 0.0f)
296 				{
297 					if (color[3] == 0.0f)
298 						color = NULL;
299 					else
300 					{
301 						node.cs = CS_CMYK_1;
302 						if (color[3] == 1.0f)
303 							color = NULL;
304 					}
305 				}
306 			}
307 			else
308 			{
309 				int i;
310 				int n = fz_colorspace_n(ctx, colorspace);
311 
312 				colorspace_off = size;
313 				size += SIZE_IN_NODES(sizeof(fz_colorspace *));
314 				node.cs = CS_OTHER_0;
315 				for (i = 0; i < n; i++)
316 					if (color[i] != 0.0f)
317 						break;
318 				if (i == n)
319 					color = NULL;
320 				memset(writer->color, 0, sizeof(float)*n);
321 			}
322 		}
323 		else
324 		{
325 			/* Colorspace is unchanged, but color may have changed
326 			 * to something best coded as a colorspace change */
327 			if (colorspace == fz_device_gray(ctx))
328 			{
329 				if (writer->color[0] != color[0])
330 				{
331 					if (color[0] == 0.0f)
332 					{
333 						node.cs = CS_GRAY_0;
334 						color = NULL;
335 					}
336 					else if (color[0] == 1.0f)
337 					{
338 						node.cs = CS_GRAY_1;
339 						color = NULL;
340 					}
341 				}
342 			}
343 			else if (colorspace == fz_device_rgb(ctx))
344 			{
345 				if (writer->color[0] != color[0] || writer->color[1] != color[1] || writer->color[2] != color[2])
346 				{
347 					if (color[0] == 0.0f && color[1] == 0.0f && color[2] == 0.0f)
348 					{
349 						node.cs = CS_RGB_0;
350 						color = NULL;
351 					}
352 					else if (color[0] == 1.0f && color[1] == 1.0f && color[2] == 1.0f)
353 					{
354 						node.cs = CS_RGB_1;
355 						color = NULL;
356 					}
357 				}
358 			}
359 			else if (colorspace == fz_device_cmyk(ctx))
360 			{
361 				if (writer->color[0] != color[0] || writer->color[1] != color[1] || writer->color[2] != color[2] || writer->color[3] != color[3])
362 				{
363 					if (color[0] == 0.0f && color[1] == 0.0f && color[2] == 0.0f)
364 					{
365 						if (color[3] == 0.0f)
366 						{
367 							node.cs = CS_CMYK_0;
368 							color = NULL;
369 						}
370 						else if (color[3] == 1.0f)
371 						{
372 							node.cs = CS_CMYK_1;
373 							color = NULL;
374 						}
375 					}
376 				}
377 			}
378 			else
379 			{
380 				int i;
381 				int n = fz_colorspace_n(ctx, colorspace);
382 				for (i=0; i < n; i++)
383 					if (color[i] != 0.0f)
384 						break;
385 				if (i == n)
386 				{
387 					node.cs = CS_OTHER_0;
388 					colorspace_off = size;
389 					size += SIZE_IN_NODES(sizeof(fz_colorspace *));
390 					color = NULL;
391 				}
392 			}
393 		}
394 	}
395 	if (color)
396 	{
397 		int i, n;
398 		const float *wc = &writer->color[0];
399 
400 		assert(colorspace != NULL);
401 		n = fz_colorspace_n(ctx, colorspace);
402 		i = 0;
403 		/* Only check colors if the colorspace is unchanged. If the
404 		 * colorspace *has* changed and the colors are implicit then
405 		 * this will have been caught above. */
406 		if (colorspace == writer->colorspace)
407 			for (; i < n; i++)
408 				if (color[i] != wc[i])
409 					break;
410 
411 		if (i != n)
412 		{
413 			node.color = 1;
414 			color_off = size;
415 			size += n * SIZE_IN_NODES(sizeof(float));
416 		}
417 	}
418 	if (alpha && (*alpha != writer->alpha))
419 	{
420 		if (*alpha >= 1.0f)
421 			node.alpha = ALPHA_1;
422 		else if (*alpha <= 0.0f)
423 			node.alpha = ALPHA_0;
424 		else
425 		{
426 			alpha_off = size;
427 			size += SIZE_IN_NODES(sizeof(float));
428 			node.alpha = ALPHA_PRESENT;
429 		}
430 	}
431 	if (ctm && (ctm->a != writer->ctm.a || ctm->b != writer->ctm.b || ctm->c != writer->ctm.c || ctm->d != writer->ctm.d || ctm->e != writer->ctm.e || ctm->f != writer->ctm.f))
432 	{
433 		int ctm_flags;
434 
435 		ctm_off = size;
436 		ctm_flags = CTM_UNCHANGED;
437 		if (ctm->a != writer->ctm.a || ctm->d != writer->ctm.d)
438 			ctm_flags = CTM_CHANGE_AD, size += SIZE_IN_NODES(2*sizeof(float));
439 		if (ctm->b != writer->ctm.b || ctm->c != writer->ctm.c)
440 			ctm_flags |= CTM_CHANGE_BC, size += SIZE_IN_NODES(2*sizeof(float));
441 		if (ctm->e != writer->ctm.e || ctm->f != writer->ctm.f)
442 			ctm_flags |= CTM_CHANGE_EF, size += SIZE_IN_NODES(2*sizeof(float));
443 		node.ctm = ctm_flags;
444 	}
445 	if (stroke && (writer->stroke == NULL || stroke != writer->stroke))
446 	{
447 		stroke_off = size;
448 		size += SIZE_IN_NODES(sizeof(fz_stroke_state *));
449 		node.stroke = 1;
450 	}
451 	if (path && (writer->path == NULL || path != writer->path))
452 	{
453 		size_t max = SIZE_IN_NODES(MAX_NODE_SIZE) - size - SIZE_IN_NODES(private_data_len);
454 		path_size = SIZE_IN_NODES(fz_pack_path(ctx, NULL, max, path));
455 		node.path = 1;
456 		path_off = size;
457 
458 		size += path_size;
459 	}
460 	if (private_data != NULL)
461 	{
462 		size_t max = SIZE_IN_NODES(MAX_NODE_SIZE) - size;
463 		if (SIZE_IN_NODES(private_data_len) > max)
464 			fz_throw(ctx, FZ_ERROR_GENERIC, "Private data too large to pack into display list node");
465 		private_off = size;
466 		size += SIZE_IN_NODES(private_data_len);
467 	}
468 
469 	while (list->len + size > list->max)
470 	{
471 		size_t newsize = list->max * 2;
472 		fz_display_node *old = list->list;
473 		ptrdiff_t diff;
474 		int i, n;
475 
476 		if (newsize < 256)
477 			newsize = 256;
478 		list->list = fz_realloc_array(ctx, list->list, newsize, fz_display_node);
479 		list->max = newsize;
480 		diff = (char *)(list->list) - (char *)old;
481 		n = (writer->top < STACK_SIZE ? writer->top : STACK_SIZE);
482 		for (i = 0; i < n; i++)
483 		{
484 			if (writer->stack[i].update != NULL)
485 				writer->stack[i].update = (fz_rect *)(((char *)writer->stack[i].update) + diff);
486 		}
487 		if (writer->path)
488 			writer->path = (fz_path *)(((char *)writer->path) + diff);
489 	}
490 
491 	if ((unsigned int)size != size)
492 		fz_throw(ctx, FZ_ERROR_GENERIC, "Display list node too large");
493 
494 	/* Write the node to the list */
495 	node.size = (unsigned int)size;
496 	node.flags = flags;
497 	assert(size < (1<<9));
498 	node_ptr = &list->list[list->len];
499 	*node_ptr = node;
500 
501 	/* Path is the most frequent one, so try to avoid the try/catch in
502 	 * this case */
503 	if (path_off)
504 	{
505 		my_path = (void *)(&node_ptr[path_off]);
506 		(void)fz_pack_path(ctx, (void *)my_path, path_size * sizeof(fz_display_node), path);
507 	}
508 
509 	if (stroke_off)
510 	{
511 		fz_try(ctx)
512 		{
513 			my_stroke = fz_keep_stroke_state(ctx, stroke);
514 		}
515 		fz_catch(ctx)
516 		{
517 			fz_drop_path(ctx, my_path);
518 			fz_rethrow(ctx);
519 		}
520 	}
521 
522 	if (rect_off)
523 	{
524 		fz_rect *out_rect = (fz_rect *)(void *)(&node_ptr[rect_off]);
525 		writer->rect = *rect;
526 		*out_rect = *rect;
527 		if (rect_for_updates)
528 			writer->stack[writer->top-1].update = out_rect;
529 	}
530 	if (path_off)
531 	{
532 		fz_drop_path(ctx, writer->path);
533 		writer->path = fz_keep_path(ctx, my_path); /* Can never fail */
534 	}
535 	if (node.cs)
536 	{
537 		fz_drop_colorspace(ctx, writer->colorspace);
538 		switch(node.cs)
539 		{
540 		case CS_GRAY_0:
541 			writer->colorspace = fz_keep_colorspace(ctx, fz_device_gray(ctx));
542 			writer->color[0] = 0;
543 			break;
544 		case CS_GRAY_1:
545 			writer->colorspace = fz_keep_colorspace(ctx, fz_device_gray(ctx));
546 			writer->color[0] = 1;
547 			break;
548 		case CS_RGB_0:
549 			writer->color[0] = 0;
550 			writer->color[1] = 0;
551 			writer->color[2] = 0;
552 			writer->colorspace = fz_keep_colorspace(ctx, fz_device_rgb(ctx));
553 			break;
554 		case CS_RGB_1:
555 			writer->color[0] = 1;
556 			writer->color[1] = 1;
557 			writer->color[2] = 1;
558 			writer->colorspace = fz_keep_colorspace(ctx, fz_device_rgb(ctx));
559 			break;
560 		case CS_CMYK_0:
561 			writer->color[0] = 0;
562 			writer->color[1] = 0;
563 			writer->color[2] = 0;
564 			writer->color[3] = 0;
565 			writer->colorspace = fz_keep_colorspace(ctx, fz_device_cmyk(ctx));
566 			break;
567 		case CS_CMYK_1:
568 			writer->color[0] = 0;
569 			writer->color[1] = 0;
570 			writer->color[2] = 0;
571 			writer->color[3] = 1;
572 			writer->colorspace = fz_keep_colorspace(ctx, fz_device_cmyk(ctx));
573 			break;
574 		default:
575 		{
576 			fz_colorspace **out_colorspace = (fz_colorspace **)(void *)(&node_ptr[colorspace_off]);
577 			int i, n;
578 			n = fz_colorspace_n(ctx, colorspace);
579 			*out_colorspace = fz_keep_colorspace(ctx, colorspace);
580 
581 			writer->colorspace = fz_keep_colorspace(ctx, colorspace);
582 			for (i = 0; i < n; i++)
583 				writer->color[i] = 0;
584 			break;
585 		}
586 		}
587 	}
588 	if (color_off)
589 	{
590 		int n = fz_colorspace_n(ctx, colorspace);
591 		float *out_color = (float *)(void *)(&node_ptr[color_off]);
592 		memcpy(writer->color, color, n * sizeof(float));
593 		memcpy(out_color, color, n * sizeof(float));
594 	}
595 	if (node.alpha)
596 	{
597 		writer->alpha = *alpha;
598 		if (alpha_off)
599 		{
600 			float *out_alpha = (float *)(void *)(&node_ptr[alpha_off]);
601 			*out_alpha = *alpha;
602 		}
603 	}
604 	if (ctm_off)
605 	{
606 		float *out_ctm = (float *)(void *)(&node_ptr[ctm_off]);
607 		if (node.ctm & CTM_CHANGE_AD)
608 		{
609 			writer->ctm.a = *out_ctm++ = ctm->a;
610 			writer->ctm.d = *out_ctm++ = ctm->d;
611 		}
612 		if (node.ctm & CTM_CHANGE_BC)
613 		{
614 			writer->ctm.b = *out_ctm++ = ctm->b;
615 			writer->ctm.c = *out_ctm++ = ctm->c;
616 		}
617 		if (node.ctm & CTM_CHANGE_EF)
618 		{
619 			writer->ctm.e = *out_ctm++ = ctm->e;
620 			writer->ctm.f = *out_ctm = ctm->f;
621 		}
622 	}
623 	if (stroke_off)
624 	{
625 		fz_stroke_state **out_stroke = (fz_stroke_state **)(void *)(&node_ptr[stroke_off]);
626 		*out_stroke = my_stroke;
627 		fz_drop_stroke_state(ctx, writer->stroke);
628 		/* Can never fail as my_stroke was kept above */
629 		writer->stroke = fz_keep_stroke_state(ctx, my_stroke);
630 	}
631 	if (private_off)
632 	{
633 		char *out_private = (char *)(void *)(&node_ptr[private_off]);
634 		memcpy(out_private, private_data, private_data_len);
635 	}
636 	list->len += size;
637 }
638 
639 /* Pack ri, op, opm, bp into flags upper bits, even/odd in lower bit */
640 static int
fz_pack_color_params(fz_color_params color_params)641 fz_pack_color_params(fz_color_params color_params)
642 {
643 	int flags = 0;
644 	flags |= color_params.ri << RI; /* 2 bits */
645 	flags |= color_params.bp << BP;
646 	flags |= color_params.op << OP;
647 	flags |= color_params.opm << OPM;
648 	return flags;
649 }
650 
651 /* unpack ri, op, opm, bp from flags, even/odd in lower bit */
652 static void
fz_unpack_color_params(fz_color_params * color_params,int flags)653 fz_unpack_color_params(fz_color_params *color_params, int flags)
654 {
655 	color_params->ri = (flags >> RI) & 3;
656 	color_params->bp = (flags >> BP) & 1;
657 	color_params->op = (flags >> OP) & 1;
658 	color_params->opm = (flags >> OPM) & 1;
659 }
660 
661 static void
fz_list_fill_path(fz_context * ctx,fz_device * dev,const fz_path * path,int even_odd,fz_matrix ctm,fz_colorspace * colorspace,const float * color,float alpha,fz_color_params color_params)662 fz_list_fill_path(fz_context *ctx, fz_device *dev, const fz_path *path, int even_odd, fz_matrix ctm,
663 	fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params)
664 {
665 	fz_rect rect = fz_bound_path(ctx, path, NULL, ctm);
666 	fz_append_display_node(
667 		ctx,
668 		dev,
669 		FZ_CMD_FILL_PATH,
670 		even_odd | fz_pack_color_params(color_params), /* flags */
671 		&rect,
672 		path, /* path */
673 		color,
674 		colorspace,
675 		&alpha, /* alpha */
676 		&ctm,
677 		NULL, /* stroke_state */
678 		NULL, /* private_data */
679 		0); /* private_data_len */
680 }
681 
682 static void
fz_list_stroke_path(fz_context * ctx,fz_device * dev,const fz_path * path,const fz_stroke_state * stroke,fz_matrix ctm,fz_colorspace * colorspace,const float * color,float alpha,fz_color_params color_params)683 fz_list_stroke_path(fz_context *ctx, fz_device *dev, const fz_path *path, const fz_stroke_state *stroke,
684 	fz_matrix ctm, fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params)
685 {
686 	fz_rect rect = fz_bound_path(ctx, path, stroke, ctm);
687 	fz_append_display_node(
688 		ctx,
689 		dev,
690 		FZ_CMD_STROKE_PATH,
691 		fz_pack_color_params(color_params), /* flags */
692 		&rect,
693 		path, /* path */
694 		color,
695 		colorspace,
696 		&alpha, /* alpha */
697 		&ctm, /* ctm */
698 		stroke,
699 		NULL, /* private_data */
700 		0); /* private_data_len */
701 }
702 
703 static void
fz_list_clip_path(fz_context * ctx,fz_device * dev,const fz_path * path,int even_odd,fz_matrix ctm,fz_rect scissor)704 fz_list_clip_path(fz_context *ctx, fz_device *dev, const fz_path *path, int even_odd, fz_matrix ctm, fz_rect scissor)
705 {
706 	fz_rect rect = fz_bound_path(ctx, path, NULL, ctm);
707 	rect = fz_intersect_rect(rect, scissor);
708 	fz_append_display_node(
709 		ctx,
710 		dev,
711 		FZ_CMD_CLIP_PATH,
712 		even_odd, /* flags */
713 		&rect,
714 		path, /* path */
715 		NULL, /* color */
716 		NULL, /* colorspace */
717 		NULL, /* alpha */
718 		&ctm, /* ctm */
719 		NULL, /* stroke */
720 		NULL, /* private_data */
721 		0); /* private_data_len */
722 }
723 
724 static void
fz_list_clip_stroke_path(fz_context * ctx,fz_device * dev,const fz_path * path,const fz_stroke_state * stroke,fz_matrix ctm,fz_rect scissor)725 fz_list_clip_stroke_path(fz_context *ctx, fz_device *dev, const fz_path *path, const fz_stroke_state *stroke, fz_matrix ctm, fz_rect scissor)
726 {
727 	fz_rect rect = fz_bound_path(ctx, path, stroke, ctm);
728 	rect = fz_intersect_rect(rect, scissor);
729 	fz_append_display_node(
730 		ctx,
731 		dev,
732 		FZ_CMD_CLIP_STROKE_PATH,
733 		0, /* flags */
734 		&rect,
735 		path, /* path */
736 		NULL, /* color */
737 		NULL, /* colorspace */
738 		NULL, /* alpha */
739 		&ctm, /* ctm */
740 		stroke, /* stroke */
741 		NULL, /* private_data */
742 		0); /* private_data_len */
743 }
744 
745 static void
fz_list_fill_text(fz_context * ctx,fz_device * dev,const fz_text * text,fz_matrix ctm,fz_colorspace * colorspace,const float * color,float alpha,fz_color_params color_params)746 fz_list_fill_text(fz_context *ctx, fz_device *dev, const fz_text *text, fz_matrix ctm,
747 	fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params)
748 {
749 	fz_text *cloned_text = fz_keep_text(ctx, text);
750 	fz_try(ctx)
751 	{
752 		fz_rect rect = fz_bound_text(ctx, text, NULL, ctm);
753 		fz_append_display_node(
754 			ctx,
755 			dev,
756 			FZ_CMD_FILL_TEXT,
757 			fz_pack_color_params(color_params), /* flags */
758 			&rect,
759 			NULL, /* path */
760 			color, /* color */
761 			colorspace, /* colorspace */
762 			&alpha, /* alpha */
763 			&ctm, /* ctm */
764 			NULL, /* stroke */
765 			&cloned_text, /* private_data */
766 			sizeof(cloned_text)); /* private_data_len */
767 	}
768 	fz_catch(ctx)
769 	{
770 		fz_drop_text(ctx, cloned_text);
771 		fz_rethrow(ctx);
772 	}
773 }
774 
775 static void
fz_list_stroke_text(fz_context * ctx,fz_device * dev,const fz_text * text,const fz_stroke_state * stroke,fz_matrix ctm,fz_colorspace * colorspace,const float * color,float alpha,fz_color_params color_params)776 fz_list_stroke_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz_stroke_state *stroke, fz_matrix ctm,
777 	fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params)
778 {
779 	fz_text *cloned_text = fz_keep_text(ctx, text);
780 	fz_try(ctx)
781 	{
782 		fz_rect rect = fz_bound_text(ctx, text, stroke, ctm);
783 		fz_append_display_node(
784 			ctx,
785 			dev,
786 			FZ_CMD_STROKE_TEXT,
787 			fz_pack_color_params(color_params), /* flags */
788 			&rect,
789 			NULL, /* path */
790 			color, /* color */
791 			colorspace, /* colorspace */
792 			&alpha, /* alpha */
793 			&ctm, /* ctm */
794 			stroke,
795 			&cloned_text, /* private_data */
796 			sizeof(cloned_text)); /* private_data_len */
797 	}
798 	fz_catch(ctx)
799 	{
800 		fz_drop_text(ctx, cloned_text);
801 		fz_rethrow(ctx);
802 	}
803 }
804 
805 static void
fz_list_clip_text(fz_context * ctx,fz_device * dev,const fz_text * text,fz_matrix ctm,fz_rect scissor)806 fz_list_clip_text(fz_context *ctx, fz_device *dev, const fz_text *text, fz_matrix ctm, fz_rect scissor)
807 {
808 	fz_text *cloned_text = fz_keep_text(ctx, text);
809 	fz_try(ctx)
810 	{
811 		fz_rect rect = fz_bound_text(ctx, text, NULL, ctm);
812 		rect = fz_intersect_rect(rect, scissor);
813 		fz_append_display_node(
814 			ctx,
815 			dev,
816 			FZ_CMD_CLIP_TEXT,
817 			0, /* flags */
818 			&rect,
819 			NULL, /* path */
820 			NULL, /* color */
821 			NULL, /* colorspace */
822 			NULL, /* alpha */
823 			&ctm, /* ctm */
824 			NULL, /* stroke */
825 			&cloned_text, /* private_data */
826 			sizeof(cloned_text)); /* private_data_len */
827 	}
828 	fz_catch(ctx)
829 	{
830 		fz_drop_text(ctx, cloned_text);
831 		fz_rethrow(ctx);
832 	}
833 }
834 
835 static void
fz_list_clip_stroke_text(fz_context * ctx,fz_device * dev,const fz_text * text,const fz_stroke_state * stroke,fz_matrix ctm,fz_rect scissor)836 fz_list_clip_stroke_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz_stroke_state *stroke, fz_matrix ctm, fz_rect scissor)
837 {
838 	fz_text *cloned_text = fz_keep_text(ctx, text);
839 	fz_try(ctx)
840 	{
841 		fz_rect rect = fz_bound_text(ctx, text, stroke, ctm);
842 		rect = fz_intersect_rect(rect, scissor);
843 		fz_append_display_node(
844 			ctx,
845 			dev,
846 			FZ_CMD_CLIP_STROKE_TEXT,
847 			0, /* flags */
848 			&rect,
849 			NULL, /* path */
850 			NULL, /* color */
851 			NULL, /* colorspace */
852 			NULL, /* alpha */
853 			&ctm, /* ctm */
854 			stroke, /* stroke */
855 			&cloned_text, /* private_data */
856 			sizeof(cloned_text)); /* private_data_len */
857 	}
858 	fz_catch(ctx)
859 	{
860 		fz_drop_text(ctx, cloned_text);
861 		fz_rethrow(ctx);
862 	}
863 }
864 
865 static void
fz_list_ignore_text(fz_context * ctx,fz_device * dev,const fz_text * text,fz_matrix ctm)866 fz_list_ignore_text(fz_context *ctx, fz_device *dev, const fz_text *text, fz_matrix ctm)
867 {
868 	fz_text *cloned_text = fz_keep_text(ctx, text);
869 	fz_try(ctx)
870 	{
871 		fz_rect rect = fz_bound_text(ctx, text, NULL, ctm);
872 		fz_append_display_node(
873 			ctx,
874 			dev,
875 			FZ_CMD_IGNORE_TEXT,
876 			0, /* flags */
877 			&rect,
878 			NULL, /* path */
879 			NULL, /* color */
880 			NULL, /* colorspace */
881 			NULL, /* alpha */
882 			&ctm, /* ctm */
883 			NULL, /* stroke */
884 			&cloned_text, /* private_data */
885 			sizeof(cloned_text)); /* private_data_len */
886 	}
887 	fz_catch(ctx)
888 	{
889 		fz_drop_text(ctx, cloned_text);
890 		fz_rethrow(ctx);
891 	}
892 }
893 
894 static void
fz_list_pop_clip(fz_context * ctx,fz_device * dev)895 fz_list_pop_clip(fz_context *ctx, fz_device *dev)
896 {
897 	fz_append_display_node(
898 		ctx,
899 		dev,
900 		FZ_CMD_POP_CLIP,
901 		0, /* flags */
902 		NULL, /* rect */
903 		NULL, /* path */
904 		NULL, /* color */
905 		NULL, /* colorspace */
906 		NULL, /* alpha */
907 		NULL, /* ctm */
908 		NULL, /* stroke */
909 		NULL, /* private_data */
910 		0); /* private_data_len */
911 }
912 
913 static void
fz_list_fill_shade(fz_context * ctx,fz_device * dev,fz_shade * shade,fz_matrix ctm,float alpha,fz_color_params color_params)914 fz_list_fill_shade(fz_context *ctx, fz_device *dev, fz_shade *shade, fz_matrix ctm, float alpha, fz_color_params color_params)
915 {
916 	fz_shade *shade2 = fz_keep_shade(ctx, shade);
917 	fz_try(ctx)
918 	{
919 		fz_rect rect = fz_bound_shade(ctx, shade, ctm);
920 		fz_append_display_node(
921 			ctx,
922 			dev,
923 			FZ_CMD_FILL_SHADE,
924 			fz_pack_color_params(color_params), /* flags */
925 			&rect,
926 			NULL, /* path */
927 			NULL, /* color */
928 			NULL, /* colorspace */
929 			&alpha, /* alpha */
930 			&ctm, /* ctm */
931 			NULL, /* stroke */
932 			&shade2, /* private_data */
933 			sizeof(shade2)); /* private_data_len */
934 	}
935 	fz_catch(ctx)
936 	{
937 		fz_drop_shade(ctx, shade2);
938 		fz_rethrow(ctx);
939 	}
940 }
941 
942 static void
fz_list_fill_image(fz_context * ctx,fz_device * dev,fz_image * image,fz_matrix ctm,float alpha,fz_color_params color_params)943 fz_list_fill_image(fz_context *ctx, fz_device *dev, fz_image *image, fz_matrix ctm, float alpha, fz_color_params color_params)
944 {
945 	fz_image *image2 = fz_keep_image(ctx, image);
946 	fz_try(ctx)
947 	{
948 		fz_rect rect = fz_transform_rect(fz_unit_rect, ctm);
949 		fz_append_display_node(
950 			ctx,
951 			dev,
952 			FZ_CMD_FILL_IMAGE,
953 			fz_pack_color_params(color_params), /* flags */
954 			&rect,
955 			NULL, /* path */
956 			NULL, /* color */
957 			NULL, /* colorspace */
958 			&alpha, /* alpha */
959 			&ctm, /* ctm */
960 			NULL, /* stroke */
961 			&image2, /* private_data */
962 			sizeof(image2)); /* private_data_len */
963 	}
964 	fz_catch(ctx)
965 	{
966 		fz_drop_image(ctx, image2);
967 		fz_rethrow(ctx);
968 	}
969 }
970 
971 static void
fz_list_fill_image_mask(fz_context * ctx,fz_device * dev,fz_image * image,fz_matrix ctm,fz_colorspace * colorspace,const float * color,float alpha,fz_color_params color_params)972 fz_list_fill_image_mask(fz_context *ctx, fz_device *dev, fz_image *image, fz_matrix ctm,
973 	fz_colorspace *colorspace, const float *color, float alpha, fz_color_params color_params)
974 {
975 	fz_image *image2 = fz_keep_image(ctx, image);
976 
977 	fz_try(ctx)
978 	{
979 		fz_rect rect = fz_transform_rect(fz_unit_rect, ctm);
980 		fz_append_display_node(
981 			ctx,
982 			dev,
983 			FZ_CMD_FILL_IMAGE_MASK,
984 			fz_pack_color_params(color_params), /* flags */
985 			&rect,
986 			NULL, /* path */
987 			color,
988 			colorspace,
989 			&alpha, /* alpha */
990 			&ctm, /* ctm */
991 			NULL, /* stroke */
992 			&image2, /* private_data */
993 			sizeof(image2)); /* private_data_len */
994 	}
995 	fz_catch(ctx)
996 	{
997 		fz_drop_image(ctx, image2);
998 		fz_rethrow(ctx);
999 	}
1000 }
1001 
1002 static void
fz_list_clip_image_mask(fz_context * ctx,fz_device * dev,fz_image * image,fz_matrix ctm,fz_rect scissor)1003 fz_list_clip_image_mask(fz_context *ctx, fz_device *dev, fz_image *image, fz_matrix ctm, fz_rect scissor)
1004 {
1005 	fz_image *image2 = fz_keep_image(ctx, image);
1006 	fz_try(ctx)
1007 	{
1008 		fz_rect rect = fz_transform_rect(fz_unit_rect, ctm);
1009 		rect = fz_intersect_rect(rect, scissor);
1010 		fz_append_display_node(
1011 			ctx,
1012 			dev,
1013 			FZ_CMD_CLIP_IMAGE_MASK,
1014 			0, /* flags */
1015 			&rect,
1016 			NULL, /* path */
1017 			NULL, /* color */
1018 			NULL, /* colorspace */
1019 			NULL, /* alpha */
1020 			&ctm, /* ctm */
1021 			NULL, /* stroke */
1022 			&image2, /* private_data */
1023 			sizeof(image2)); /* private_data_len */
1024 	}
1025 	fz_catch(ctx)
1026 	{
1027 		fz_drop_image(ctx, image2);
1028 		fz_rethrow(ctx);
1029 	}
1030 }
1031 
1032 static void
fz_list_begin_mask(fz_context * ctx,fz_device * dev,fz_rect rect,int luminosity,fz_colorspace * colorspace,const float * color,fz_color_params color_params)1033 fz_list_begin_mask(fz_context *ctx, fz_device *dev, fz_rect rect, int luminosity, fz_colorspace *colorspace, const float *color, fz_color_params color_params)
1034 {
1035 	fz_append_display_node(
1036 		ctx,
1037 		dev,
1038 		FZ_CMD_BEGIN_MASK,
1039 		(!!luminosity) | fz_pack_color_params(color_params), /* flags */
1040 		&rect,
1041 		NULL, /* path */
1042 		color,
1043 		colorspace,
1044 		NULL, /* alpha */
1045 		NULL, /* ctm */
1046 		NULL, /* stroke */
1047 		NULL, /* private_data */
1048 		0); /* private_data_len */
1049 }
1050 
1051 static void
fz_list_end_mask(fz_context * ctx,fz_device * dev)1052 fz_list_end_mask(fz_context *ctx, fz_device *dev)
1053 {
1054 	fz_append_display_node(
1055 		ctx,
1056 		dev,
1057 		FZ_CMD_END_MASK,
1058 		0, /* flags */
1059 		NULL, /* rect */
1060 		NULL, /* path */
1061 		NULL, /* color */
1062 		NULL, /* colorspace */
1063 		NULL, /* alpha */
1064 		NULL, /* ctm */
1065 		NULL, /* stroke */
1066 		NULL, /* private_data */
1067 		0); /* private_data_len */
1068 }
1069 
1070 static void
fz_list_begin_group(fz_context * ctx,fz_device * dev,fz_rect rect,fz_colorspace * colorspace,int isolated,int knockout,int blendmode,float alpha)1071 fz_list_begin_group(fz_context *ctx, fz_device *dev, fz_rect rect, fz_colorspace *colorspace, int isolated, int knockout, int blendmode, float alpha)
1072 {
1073 	int flags;
1074 
1075 	colorspace = fz_keep_colorspace(ctx, colorspace);
1076 
1077 	flags = (blendmode<<2);
1078 	if (isolated)
1079 		flags |= ISOLATED;
1080 	if (knockout)
1081 		flags |= KNOCKOUT;
1082 
1083 	fz_try(ctx)
1084 	{
1085 		fz_append_display_node(
1086 			ctx,
1087 			dev,
1088 			FZ_CMD_BEGIN_GROUP,
1089 			flags,
1090 			&rect,
1091 			NULL, /* path */
1092 			NULL, /* color */
1093 			NULL, /* colorspace */
1094 			&alpha, /* alpha */
1095 			NULL, /* ctm */
1096 			NULL, /* stroke */
1097 			&colorspace, /* private_data */
1098 			sizeof(colorspace)); /* private_data_len */
1099 	}
1100 	fz_catch(ctx)
1101 	{
1102 		fz_drop_colorspace(ctx, colorspace);
1103 		fz_rethrow(ctx);
1104 	}
1105 }
1106 
1107 static void
fz_list_end_group(fz_context * ctx,fz_device * dev)1108 fz_list_end_group(fz_context *ctx, fz_device *dev)
1109 {
1110 	fz_append_display_node(
1111 		ctx,
1112 		dev,
1113 		FZ_CMD_END_GROUP,
1114 		0, /* flags */
1115 		NULL, /* rect */
1116 		NULL, /* path */
1117 		NULL, /* color */
1118 		NULL, /* colorspace */
1119 		NULL, /* alpha */
1120 		NULL, /* ctm */
1121 		NULL, /* stroke */
1122 		NULL, /* private_data */
1123 		0); /* private_data_len */
1124 }
1125 
1126 typedef struct
1127 {
1128 	float xstep;
1129 	float ystep;
1130 	fz_rect view;
1131 	int id;
1132 } fz_list_tile_data;
1133 
1134 static int
fz_list_begin_tile(fz_context * ctx,fz_device * dev,fz_rect area,fz_rect view,float xstep,float ystep,fz_matrix ctm,int id)1135 fz_list_begin_tile(fz_context *ctx, fz_device *dev, fz_rect area, fz_rect view, float xstep, float ystep, fz_matrix ctm, int id)
1136 {
1137 	fz_list_tile_data tile;
1138 
1139 	tile.xstep = xstep;
1140 	tile.ystep = ystep;
1141 	tile.view = view;
1142 	tile.id = id;
1143 	fz_append_display_node(
1144 		ctx,
1145 		dev,
1146 		FZ_CMD_BEGIN_TILE,
1147 		0, /* flags */
1148 		&area,
1149 		NULL, /* path */
1150 		NULL, /* color */
1151 		NULL, /* colorspace */
1152 		NULL, /* alpha */
1153 		&ctm, /* ctm */
1154 		NULL, /* stroke */
1155 		&tile, /* private_data */
1156 		sizeof(tile)); /* private_data_len */
1157 
1158 	return 0;
1159 }
1160 
1161 static void
fz_list_end_tile(fz_context * ctx,fz_device * dev)1162 fz_list_end_tile(fz_context *ctx, fz_device *dev)
1163 {
1164 	fz_append_display_node(
1165 		ctx,
1166 		dev,
1167 		FZ_CMD_END_TILE,
1168 		0, /* flags */
1169 		NULL,
1170 		NULL, /* path */
1171 		NULL, /* color */
1172 		NULL, /* colorspace */
1173 		NULL, /* alpha */
1174 		NULL, /* ctm */
1175 		NULL, /* stroke */
1176 		NULL, /* private_data */
1177 		0); /* private_data_len */
1178 }
1179 
1180 static void
fz_list_render_flags(fz_context * ctx,fz_device * dev,int set,int clear)1181 fz_list_render_flags(fz_context *ctx, fz_device *dev, int set, int clear)
1182 {
1183 	int flags;
1184 
1185 	/* Pack the options down */
1186 	if (set == FZ_DEVFLAG_GRIDFIT_AS_TILED && clear == 0)
1187 		flags = 1;
1188 	else if (set == 0 && clear == FZ_DEVFLAG_GRIDFIT_AS_TILED)
1189 		flags = 0;
1190 	else
1191 	{
1192 		assert("Unsupported flags combination" == NULL);
1193 		return;
1194 	}
1195 	fz_append_display_node(
1196 		ctx,
1197 		dev,
1198 		FZ_CMD_RENDER_FLAGS,
1199 		flags, /* flags */
1200 		NULL,
1201 		NULL, /* path */
1202 		NULL, /* color */
1203 		NULL, /* colorspace */
1204 		NULL, /* alpha */
1205 		NULL, /* ctm */
1206 		NULL, /* stroke */
1207 		NULL, /* private_data */
1208 		0); /* private_data_len */
1209 }
1210 
1211 static void
fz_list_set_default_colorspaces(fz_context * ctx,fz_device * dev,fz_default_colorspaces * default_cs)1212 fz_list_set_default_colorspaces(fz_context *ctx, fz_device *dev, fz_default_colorspaces *default_cs)
1213 {
1214 	fz_default_colorspaces *default_cs2 = fz_keep_default_colorspaces(ctx, default_cs);
1215 
1216 	fz_try(ctx)
1217 	{
1218 		fz_append_display_node(
1219 			ctx,
1220 			dev,
1221 			FZ_CMD_DEFAULT_COLORSPACES,
1222 			0, /* flags */
1223 			NULL,
1224 			NULL, /* path */
1225 			NULL, /* color */
1226 			NULL, /* colorspace */
1227 			NULL, /* alpha */
1228 			NULL, /* ctm */
1229 			NULL, /* stroke */
1230 			&default_cs2, /* private_data */
1231 			sizeof(default_cs2)); /* private_data_len */
1232 	}
1233 	fz_catch(ctx)
1234 	{
1235 		fz_drop_default_colorspaces(ctx, default_cs2);
1236 		fz_rethrow(ctx);
1237 	}
1238 }
1239 
1240 static void
fz_list_begin_layer(fz_context * ctx,fz_device * dev,const char * layer_name)1241 fz_list_begin_layer(fz_context *ctx, fz_device *dev, const char *layer_name)
1242 {
1243 	fz_append_display_node(
1244 		ctx,
1245 		dev,
1246 		FZ_CMD_BEGIN_LAYER,
1247 		0, /* flags */
1248 		NULL,
1249 		NULL, /* path */
1250 		NULL, /* color */
1251 		NULL, /* colorspace */
1252 		NULL, /* alpha */
1253 		NULL,
1254 		NULL, /* stroke */
1255 		layer_name, /* private_data */
1256 		1+strlen(layer_name)); /* private_data_len */
1257 }
1258 
1259 static void
fz_list_end_layer(fz_context * ctx,fz_device * dev)1260 fz_list_end_layer(fz_context *ctx, fz_device *dev)
1261 {
1262 	fz_append_display_node(
1263 		ctx,
1264 		dev,
1265 		FZ_CMD_END_LAYER,
1266 		0, /* flags */
1267 		NULL,
1268 		NULL, /* path */
1269 		NULL, /* color */
1270 		NULL, /* colorspace */
1271 		NULL, /* alpha */
1272 		NULL, /* ctm */
1273 		NULL, /* stroke */
1274 		NULL, /* private_data */
1275 		0); /* private_data_len */
1276 }
1277 
1278 static void
fz_list_drop_device(fz_context * ctx,fz_device * dev)1279 fz_list_drop_device(fz_context *ctx, fz_device *dev)
1280 {
1281 	fz_list_device *writer = (fz_list_device *)dev;
1282 
1283 	fz_drop_colorspace(ctx, writer->colorspace);
1284 	fz_drop_stroke_state(ctx, writer->stroke);
1285 	fz_drop_path(ctx, writer->path);
1286 	fz_drop_display_list(ctx, writer->list);
1287 }
1288 
1289 fz_device *
fz_new_list_device(fz_context * ctx,fz_display_list * list)1290 fz_new_list_device(fz_context *ctx, fz_display_list *list)
1291 {
1292 	fz_list_device *dev;
1293 
1294 	dev = fz_new_derived_device(ctx, fz_list_device);
1295 
1296 	dev->super.fill_path = fz_list_fill_path;
1297 	dev->super.stroke_path = fz_list_stroke_path;
1298 	dev->super.clip_path = fz_list_clip_path;
1299 	dev->super.clip_stroke_path = fz_list_clip_stroke_path;
1300 
1301 	dev->super.fill_text = fz_list_fill_text;
1302 	dev->super.stroke_text = fz_list_stroke_text;
1303 	dev->super.clip_text = fz_list_clip_text;
1304 	dev->super.clip_stroke_text = fz_list_clip_stroke_text;
1305 	dev->super.ignore_text = fz_list_ignore_text;
1306 
1307 	dev->super.fill_shade = fz_list_fill_shade;
1308 	dev->super.fill_image = fz_list_fill_image;
1309 	dev->super.fill_image_mask = fz_list_fill_image_mask;
1310 	dev->super.clip_image_mask = fz_list_clip_image_mask;
1311 
1312 	dev->super.pop_clip = fz_list_pop_clip;
1313 
1314 	dev->super.begin_mask = fz_list_begin_mask;
1315 	dev->super.end_mask = fz_list_end_mask;
1316 	dev->super.begin_group = fz_list_begin_group;
1317 	dev->super.end_group = fz_list_end_group;
1318 
1319 	dev->super.begin_tile = fz_list_begin_tile;
1320 	dev->super.end_tile = fz_list_end_tile;
1321 
1322 	dev->super.render_flags = fz_list_render_flags;
1323 	dev->super.set_default_colorspaces = fz_list_set_default_colorspaces;
1324 
1325 	dev->super.begin_layer = fz_list_begin_layer;
1326 	dev->super.end_layer = fz_list_end_layer;
1327 
1328 	dev->super.drop_device = fz_list_drop_device;
1329 
1330 	dev->list = fz_keep_display_list(ctx, list);
1331 	dev->path = NULL;
1332 	dev->alpha = 1.0f;
1333 	dev->ctm = fz_identity;
1334 	dev->stroke = NULL;
1335 	dev->colorspace = fz_keep_colorspace(ctx, fz_device_gray(ctx));
1336 	memset(dev->color, 0, sizeof(float)*FZ_MAX_COLORS);
1337 	dev->top = 0;
1338 	dev->tiled = 0;
1339 
1340 	return &dev->super;
1341 }
1342 
1343 static void
fz_drop_display_list_imp(fz_context * ctx,fz_storable * list_)1344 fz_drop_display_list_imp(fz_context *ctx, fz_storable *list_)
1345 {
1346 	fz_display_list *list = (fz_display_list *)list_;
1347 	fz_display_node *node = list->list;
1348 	fz_display_node *node_end = list->list + list->len;
1349 	int cs_n = 1;
1350 	fz_colorspace *cs;
1351 
1352 	while (node != node_end)
1353 	{
1354 		fz_display_node n = *node;
1355 		fz_display_node *next = node + n.size;
1356 
1357 		node++;
1358 		if (n.rect)
1359 		{
1360 			node += SIZE_IN_NODES(sizeof(fz_rect));
1361 		}
1362 		switch (n.cs)
1363 		{
1364 		default:
1365 		case CS_UNCHANGED:
1366 			break;
1367 		case CS_GRAY_0:
1368 		case CS_GRAY_1:
1369 			cs_n = 1;
1370 			break;
1371 		case CS_RGB_0:
1372 		case CS_RGB_1:
1373 			cs_n = 3;
1374 			break;
1375 		case CS_CMYK_0:
1376 		case CS_CMYK_1:
1377 			cs_n = 4;
1378 			break;
1379 		case CS_OTHER_0:
1380 			cs = *(fz_colorspace **)node;
1381 			cs_n = fz_colorspace_n(ctx, cs);
1382 			fz_drop_colorspace(ctx, cs);
1383 			node += SIZE_IN_NODES(sizeof(fz_colorspace *));
1384 			break;
1385 		}
1386 		if (n.color)
1387 		{
1388 			node += SIZE_IN_NODES(cs_n * sizeof(float));
1389 		}
1390 		if (n.alpha == ALPHA_PRESENT)
1391 		{
1392 			node += SIZE_IN_NODES(sizeof(float));
1393 		}
1394 		if (n.ctm & CTM_CHANGE_AD)
1395 			node += SIZE_IN_NODES(2*sizeof(float));
1396 		if (n.ctm & CTM_CHANGE_BC)
1397 			node += SIZE_IN_NODES(2*sizeof(float));
1398 		if (n.ctm & CTM_CHANGE_EF)
1399 			node += SIZE_IN_NODES(2*sizeof(float));
1400 		if (n.stroke)
1401 		{
1402 			fz_drop_stroke_state(ctx, *(fz_stroke_state **)node);
1403 			node += SIZE_IN_NODES(sizeof(fz_stroke_state *));
1404 		}
1405 		if (n.path)
1406 		{
1407 			int path_size = fz_packed_path_size((fz_path *)node);
1408 			fz_drop_path(ctx, (fz_path *)node);
1409 			node += SIZE_IN_NODES(path_size);
1410 		}
1411 		switch(n.cmd)
1412 		{
1413 		case FZ_CMD_FILL_TEXT:
1414 		case FZ_CMD_STROKE_TEXT:
1415 		case FZ_CMD_CLIP_TEXT:
1416 		case FZ_CMD_CLIP_STROKE_TEXT:
1417 		case FZ_CMD_IGNORE_TEXT:
1418 			fz_drop_text(ctx, *(fz_text **)node);
1419 			break;
1420 		case FZ_CMD_FILL_SHADE:
1421 			fz_drop_shade(ctx, *(fz_shade **)node);
1422 			break;
1423 		case FZ_CMD_FILL_IMAGE:
1424 		case FZ_CMD_FILL_IMAGE_MASK:
1425 		case FZ_CMD_CLIP_IMAGE_MASK:
1426 			fz_drop_image(ctx, *(fz_image **)node);
1427 			break;
1428 		case FZ_CMD_BEGIN_GROUP:
1429 			fz_drop_colorspace(ctx, *(fz_colorspace **)node);
1430 			break;
1431 		case FZ_CMD_DEFAULT_COLORSPACES:
1432 			fz_drop_default_colorspaces(ctx, *(fz_default_colorspaces **)node);
1433 			break;
1434 		}
1435 		node = next;
1436 	}
1437 	fz_free(ctx, list->list);
1438 	fz_free(ctx, list);
1439 }
1440 
1441 fz_display_list *
fz_new_display_list(fz_context * ctx,fz_rect mediabox)1442 fz_new_display_list(fz_context *ctx, fz_rect mediabox)
1443 {
1444 	fz_display_list *list = fz_malloc_struct(ctx, fz_display_list);
1445 	FZ_INIT_STORABLE(list, 1, fz_drop_display_list_imp);
1446 	list->list = NULL;
1447 	list->mediabox = mediabox;
1448 	list->max = 0;
1449 	list->len = 0;
1450 	return list;
1451 }
1452 
1453 fz_display_list *
fz_keep_display_list(fz_context * ctx,fz_display_list * list)1454 fz_keep_display_list(fz_context *ctx, fz_display_list *list)
1455 {
1456 	return fz_keep_storable(ctx, &list->storable);
1457 }
1458 
1459 void
fz_drop_display_list(fz_context * ctx,fz_display_list * list)1460 fz_drop_display_list(fz_context *ctx, fz_display_list *list)
1461 {
1462 	fz_defer_reap_start(ctx);
1463 	fz_drop_storable(ctx, &list->storable);
1464 	fz_defer_reap_end(ctx);
1465 }
1466 
1467 fz_rect
fz_bound_display_list(fz_context * ctx,fz_display_list * list)1468 fz_bound_display_list(fz_context *ctx, fz_display_list *list)
1469 {
1470 	return list->mediabox;
1471 }
1472 
fz_display_list_is_empty(fz_context * ctx,const fz_display_list * list)1473 int fz_display_list_is_empty(fz_context *ctx, const fz_display_list *list)
1474 {
1475 	return !list || list->len == 0;
1476 }
1477 
1478 void
fz_run_display_list(fz_context * ctx,fz_display_list * list,fz_device * dev,fz_matrix top_ctm,fz_rect scissor,fz_cookie * cookie)1479 fz_run_display_list(fz_context *ctx, fz_display_list *list, fz_device *dev, fz_matrix top_ctm, fz_rect scissor, fz_cookie *cookie)
1480 {
1481 	fz_display_node *node;
1482 	fz_display_node *node_end;
1483 	fz_display_node *next_node;
1484 	int clipped = 0;
1485 	int tiled = 0;
1486 	int progress = 0;
1487 
1488 	/* Current graphics state as unpacked from list */
1489 	fz_path *path = NULL;
1490 	float alpha = 1.0f;
1491 	fz_matrix ctm = fz_identity;
1492 	fz_stroke_state *stroke = NULL;
1493 	float color[FZ_MAX_COLORS] = { 0 };
1494 	fz_colorspace *colorspace = fz_keep_colorspace(ctx, fz_device_gray(ctx));
1495 	fz_color_params color_params;
1496 	fz_rect rect = { 0 };
1497 
1498 	/* Transformed versions of graphic state entries */
1499 	fz_rect trans_rect;
1500 	fz_matrix trans_ctm;
1501 	int tile_skip_depth = 0;
1502 
1503 	if (cookie)
1504 	{
1505 		cookie->progress_max = list->len;
1506 		cookie->progress = 0;
1507 	}
1508 
1509 	color_params = fz_default_color_params;
1510 
1511 	node = list->list;
1512 	node_end = &list->list[list->len];
1513 	for (; node != node_end ; node = next_node)
1514 	{
1515 		int empty;
1516 		fz_display_node n = *node;
1517 
1518 		next_node = node + n.size;
1519 
1520 		/* Check the cookie for aborting */
1521 		if (cookie)
1522 		{
1523 			if (cookie->abort)
1524 				break;
1525 			cookie->progress = progress;
1526 			progress += n.size;
1527 		}
1528 
1529 		node++;
1530 		if (n.rect)
1531 		{
1532 			rect = *(fz_rect *)node;
1533 			node += SIZE_IN_NODES(sizeof(fz_rect));
1534 		}
1535 		if (n.cs)
1536 		{
1537 			int i, en;
1538 
1539 			fz_drop_colorspace(ctx, colorspace);
1540 			switch (n.cs)
1541 			{
1542 			default:
1543 			case CS_GRAY_0:
1544 				colorspace = fz_keep_colorspace(ctx, fz_device_gray(ctx));
1545 				color[0] = 0.0f;
1546 				break;
1547 			case CS_GRAY_1:
1548 				colorspace = fz_keep_colorspace(ctx, fz_device_gray(ctx));
1549 				color[0] = 1.0f;
1550 				break;
1551 			case CS_RGB_0:
1552 				colorspace = fz_keep_colorspace(ctx, fz_device_rgb(ctx));
1553 				color[0] = 0.0f;
1554 				color[1] = 0.0f;
1555 				color[2] = 0.0f;
1556 				break;
1557 			case CS_RGB_1:
1558 				colorspace = fz_keep_colorspace(ctx, fz_device_rgb(ctx));
1559 				color[0] = 1.0f;
1560 				color[1] = 1.0f;
1561 				color[2] = 1.0f;
1562 				break;
1563 			case CS_CMYK_0:
1564 				colorspace = fz_keep_colorspace(ctx, fz_device_cmyk(ctx));
1565 				color[0] = 0.0f;
1566 				color[1] = 0.0f;
1567 				color[2] = 0.0f;
1568 				color[3] = 0.0f;
1569 				break;
1570 			case CS_CMYK_1:
1571 				colorspace = fz_keep_colorspace(ctx, fz_device_cmyk(ctx));
1572 				color[0] = 0.0f;
1573 				color[1] = 0.0f;
1574 				color[2] = 0.0f;
1575 				color[3] = 1.0f;
1576 				break;
1577 			case CS_OTHER_0:
1578 				colorspace = fz_keep_colorspace(ctx, *(fz_colorspace **)(node));
1579 				node += SIZE_IN_NODES(sizeof(fz_colorspace *));
1580 				en = fz_colorspace_n(ctx, colorspace);
1581 				for (i = 0; i < en; i++)
1582 					color[i] = 0.0f;
1583 				break;
1584 			}
1585 		}
1586 		if (n.color)
1587 		{
1588 			int nc = fz_colorspace_n(ctx, colorspace);
1589 			memcpy(color, (float *)node, nc * sizeof(float));
1590 			node += SIZE_IN_NODES(nc * sizeof(float));
1591 		}
1592 		if (n.alpha)
1593 		{
1594 			switch(n.alpha)
1595 			{
1596 			default:
1597 			case ALPHA_0:
1598 				alpha = 0.0f;
1599 				break;
1600 			case ALPHA_1:
1601 				alpha = 1.0f;
1602 				break;
1603 			case ALPHA_PRESENT:
1604 				alpha = *(float *)node;
1605 				node += SIZE_IN_NODES(sizeof(float));
1606 				break;
1607 			}
1608 		}
1609 		if (n.ctm != 0)
1610 		{
1611 			float *packed_ctm = (float *)node;
1612 			if (n.ctm & CTM_CHANGE_AD)
1613 			{
1614 				ctm.a = *packed_ctm++;
1615 				ctm.d = *packed_ctm++;
1616 				node += SIZE_IN_NODES(2*sizeof(float));
1617 			}
1618 			if (n.ctm & CTM_CHANGE_BC)
1619 			{
1620 				ctm.b = *packed_ctm++;
1621 				ctm.c = *packed_ctm++;
1622 				node += SIZE_IN_NODES(2*sizeof(float));
1623 			}
1624 			if (n.ctm & CTM_CHANGE_EF)
1625 			{
1626 				ctm.e = *packed_ctm++;
1627 				ctm.f = *packed_ctm;
1628 				node += SIZE_IN_NODES(2*sizeof(float));
1629 			}
1630 		}
1631 		if (n.stroke)
1632 		{
1633 			fz_drop_stroke_state(ctx, stroke);
1634 			stroke = fz_keep_stroke_state(ctx, *(fz_stroke_state **)node);
1635 			node += SIZE_IN_NODES(sizeof(fz_stroke_state *));
1636 		}
1637 		if (n.path)
1638 		{
1639 			fz_drop_path(ctx, path);
1640 			path = fz_keep_path(ctx, (fz_path *)node);
1641 			node += SIZE_IN_NODES(fz_packed_path_size(path));
1642 		}
1643 
1644 		if (tile_skip_depth > 0)
1645 		{
1646 			if (n.cmd == FZ_CMD_BEGIN_TILE)
1647 				tile_skip_depth++;
1648 			else if (n.cmd == FZ_CMD_END_TILE)
1649 				tile_skip_depth--;
1650 			if (tile_skip_depth > 0)
1651 				continue;
1652 		}
1653 
1654 		trans_rect = fz_transform_rect(rect, top_ctm);
1655 
1656 		/* cull objects to draw using a quick visibility test */
1657 
1658 		if (tiled ||
1659 			n.cmd == FZ_CMD_BEGIN_TILE || n.cmd == FZ_CMD_END_TILE ||
1660 			n.cmd == FZ_CMD_RENDER_FLAGS || n.cmd == FZ_CMD_DEFAULT_COLORSPACES ||
1661 			n.cmd == FZ_CMD_BEGIN_LAYER || n.cmd == FZ_CMD_END_LAYER)
1662 		{
1663 			empty = 0;
1664 		}
1665 		else
1666 		{
1667 			empty = fz_is_empty_rect(fz_intersect_rect(trans_rect, scissor));
1668 		}
1669 
1670 		if (clipped || empty)
1671 		{
1672 			switch (n.cmd)
1673 			{
1674 			case FZ_CMD_CLIP_PATH:
1675 			case FZ_CMD_CLIP_STROKE_PATH:
1676 			case FZ_CMD_CLIP_TEXT:
1677 			case FZ_CMD_CLIP_STROKE_TEXT:
1678 			case FZ_CMD_CLIP_IMAGE_MASK:
1679 			case FZ_CMD_BEGIN_MASK:
1680 			case FZ_CMD_BEGIN_GROUP:
1681 				clipped++;
1682 				continue;
1683 			case FZ_CMD_POP_CLIP:
1684 			case FZ_CMD_END_GROUP:
1685 				if (!clipped)
1686 					goto visible;
1687 				clipped--;
1688 				continue;
1689 			case FZ_CMD_END_MASK:
1690 				if (!clipped)
1691 					goto visible;
1692 				continue;
1693 			default:
1694 				continue;
1695 			}
1696 		}
1697 
1698 visible:
1699 		trans_ctm = fz_concat(ctm, top_ctm);
1700 
1701 		fz_try(ctx)
1702 		{
1703 			switch (n.cmd)
1704 			{
1705 			case FZ_CMD_FILL_PATH:
1706 				fz_unpack_color_params(&color_params, n.flags);
1707 				fz_fill_path(ctx, dev, path, n.flags & 1, trans_ctm, colorspace, color, alpha, color_params);
1708 				break;
1709 			case FZ_CMD_STROKE_PATH:
1710 				fz_unpack_color_params(&color_params, n.flags);
1711 				fz_stroke_path(ctx, dev, path, stroke, trans_ctm, colorspace, color, alpha, color_params);
1712 				break;
1713 			case FZ_CMD_CLIP_PATH:
1714 				fz_clip_path(ctx, dev, path, n.flags, trans_ctm, trans_rect);
1715 				break;
1716 			case FZ_CMD_CLIP_STROKE_PATH:
1717 				fz_clip_stroke_path(ctx, dev, path, stroke, trans_ctm, trans_rect);
1718 				break;
1719 			case FZ_CMD_FILL_TEXT:
1720 				fz_unpack_color_params(&color_params, n.flags);
1721 				fz_fill_text(ctx, dev, *(fz_text **)node, trans_ctm, colorspace, color, alpha, color_params);
1722 				break;
1723 			case FZ_CMD_STROKE_TEXT:
1724 				fz_unpack_color_params(&color_params, n.flags);
1725 				fz_stroke_text(ctx, dev, *(fz_text **)node, stroke, trans_ctm, colorspace, color, alpha, color_params);
1726 				break;
1727 			case FZ_CMD_CLIP_TEXT:
1728 				fz_clip_text(ctx, dev, *(fz_text **)node, trans_ctm, trans_rect);
1729 				break;
1730 			case FZ_CMD_CLIP_STROKE_TEXT:
1731 				fz_clip_stroke_text(ctx, dev, *(fz_text **)node, stroke, trans_ctm, trans_rect);
1732 				break;
1733 			case FZ_CMD_IGNORE_TEXT:
1734 				fz_ignore_text(ctx, dev, *(fz_text **)node, trans_ctm);
1735 				break;
1736 			case FZ_CMD_FILL_SHADE:
1737 				fz_unpack_color_params(&color_params, n.flags);
1738 				fz_fill_shade(ctx, dev, *(fz_shade **)node, trans_ctm, alpha, color_params);
1739 				break;
1740 			case FZ_CMD_FILL_IMAGE:
1741 				fz_unpack_color_params(&color_params, n.flags);
1742 				fz_fill_image(ctx, dev, *(fz_image **)node, trans_ctm, alpha, color_params);
1743 				break;
1744 			case FZ_CMD_FILL_IMAGE_MASK:
1745 				fz_unpack_color_params(&color_params, n.flags);
1746 				fz_fill_image_mask(ctx, dev, *(fz_image **)node, trans_ctm, colorspace, color, alpha, color_params);
1747 				break;
1748 			case FZ_CMD_CLIP_IMAGE_MASK:
1749 				fz_clip_image_mask(ctx, dev, *(fz_image **)node, trans_ctm, trans_rect);
1750 				break;
1751 			case FZ_CMD_POP_CLIP:
1752 				fz_pop_clip(ctx, dev);
1753 				break;
1754 			case FZ_CMD_BEGIN_MASK:
1755 				fz_unpack_color_params(&color_params, n.flags);
1756 				fz_begin_mask(ctx, dev, trans_rect, n.flags & 1, colorspace, color, color_params);
1757 				break;
1758 			case FZ_CMD_END_MASK:
1759 				fz_end_mask(ctx, dev);
1760 				break;
1761 			case FZ_CMD_BEGIN_GROUP:
1762 				fz_begin_group(ctx, dev, trans_rect, *(fz_colorspace **)node, (n.flags & ISOLATED) != 0, (n.flags & KNOCKOUT) != 0, (n.flags>>2), alpha);
1763 				break;
1764 			case FZ_CMD_END_GROUP:
1765 				fz_end_group(ctx, dev);
1766 				break;
1767 			case FZ_CMD_BEGIN_TILE:
1768 			{
1769 				int cached;
1770 				fz_list_tile_data *data = (fz_list_tile_data *)node;
1771 				fz_rect tile_rect;
1772 				tiled++;
1773 				tile_rect = data->view;
1774 				cached = fz_begin_tile_id(ctx, dev, rect, tile_rect, data->xstep, data->ystep, trans_ctm, data->id);
1775 				if (cached)
1776 					tile_skip_depth = 1;
1777 				break;
1778 			}
1779 			case FZ_CMD_END_TILE:
1780 				tiled--;
1781 				fz_end_tile(ctx, dev);
1782 				break;
1783 			case FZ_CMD_RENDER_FLAGS:
1784 				if (n.flags == 0)
1785 					fz_render_flags(ctx, dev, 0, FZ_DEVFLAG_GRIDFIT_AS_TILED);
1786 				else if (n.flags == 1)
1787 					fz_render_flags(ctx, dev, FZ_DEVFLAG_GRIDFIT_AS_TILED, 0);
1788 				break;
1789 			case FZ_CMD_DEFAULT_COLORSPACES:
1790 				fz_set_default_colorspaces(ctx, dev, *(fz_default_colorspaces **)node);
1791 				break;
1792 			case FZ_CMD_BEGIN_LAYER:
1793 				fz_begin_layer(ctx, dev, (const char *)node);
1794 				break;
1795 			case FZ_CMD_END_LAYER:
1796 				fz_end_layer(ctx, dev);
1797 				break;
1798 			}
1799 		}
1800 		fz_catch(ctx)
1801 		{
1802 			/* Swallow the error */
1803 			if (cookie)
1804 				cookie->errors++;
1805 			if (fz_caught(ctx) == FZ_ERROR_ABORT)
1806 				break;
1807 			fz_warn(ctx, "Ignoring error during interpretation");
1808 		}
1809 	}
1810 	fz_drop_colorspace(ctx, colorspace);
1811 	fz_drop_stroke_state(ctx, stroke);
1812 	fz_drop_path(ctx, path);
1813 	if (cookie)
1814 		cookie->progress = progress;
1815 }
1816