1#include "mupdf/fitz.h"
2
3#include <assert.h>
4#include <string.h>
5
6typedef struct fz_display_node_s fz_display_node;
7typedef struct fz_list_device_s fz_list_device;
8
9#define STACK_SIZE 96
10
11typedef enum fz_display_command_e
12{
13 FZ_CMD_FILL_PATH,
14 FZ_CMD_STROKE_PATH,
15 FZ_CMD_CLIP_PATH,
16 FZ_CMD_CLIP_STROKE_PATH,
17 FZ_CMD_FILL_TEXT,
18 FZ_CMD_STROKE_TEXT,
19 FZ_CMD_CLIP_TEXT,
20 FZ_CMD_CLIP_STROKE_TEXT,
21 FZ_CMD_IGNORE_TEXT,
22 FZ_CMD_FILL_SHADE,
23 FZ_CMD_FILL_IMAGE,
24 FZ_CMD_FILL_IMAGE_MASK,
25 FZ_CMD_CLIP_IMAGE_MASK,
26 FZ_CMD_POP_CLIP,
27 FZ_CMD_BEGIN_MASK,
28 FZ_CMD_END_MASK,
29 FZ_CMD_BEGIN_GROUP,
30 FZ_CMD_END_GROUP,
31 FZ_CMD_BEGIN_TILE,
32 FZ_CMD_END_TILE,
33 FZ_CMD_RENDER_FLAGS,
34 FZ_CMD_DEFAULT_COLORSPACES,
35 FZ_CMD_BEGIN_LAYER,
36 FZ_CMD_END_LAYER
37} fz_display_command;
38
39/* The display list is a list of nodes.
40 * Each node is a structure consisting of a bitfield (that packs into a
41 * 32 bit word).
42 * The different fields in the bitfield identify what information is
43 * present in the node.
44 *
45 * cmd: What type of node this is.
46 *
47 * size: The number of sizeof(fz_display_node) bytes that this node's
48 * data occupies. (i.e. &node[node->size] = the next node in the
49 * chain; 0 for end of list).
50 *
51 * rect: 0 for unchanged, 1 for present.
52 *
53 * path: 0 for unchanged, 1 for present.
54 *
55 * cs: 0 for unchanged
56 * 1 for devicegray (color defaults to 0)
57 * 2 for devicegray (color defaults to 1)
58 * 3 for devicergb (color defaults to 0,0,0)
59 * 4 for devicergb (color defaults to 1,1,1)
60 * 5 for devicecmyk (color defaults to 0,0,0,0)
61 * 6 for devicecmyk (color defaults to 0,0,0,1)
62 * 7 for present (color defaults to 0)
63 *
64 * color: 0 for unchanged color, 1 for present.
65 *
66 * alpha: 0 for unchanged, 1 for solid, 2 for transparent, 3
67 * for alpha value present.
68 *
69 * ctm: 0 for unchanged,
70 * 1 for change ad
71 * 2 for change bc
72 * 4 for change ef.
73 *
74 * stroke: 0 for unchanged, 1 for present.
75 *
76 * flags: Flags (node specific meanings)
77 *
78 * Nodes are packed in the order:
79 * header, rect, colorspace, color, alpha, ctm, stroke_state, path, private data.
80 */
81struct fz_display_node_s
82{
83 unsigned int cmd : 5;
84 unsigned int size : 9;
85 unsigned int rect : 1;
86 unsigned int path : 1;
87 unsigned int cs : 3;
88 unsigned int color : 1;
89 unsigned int alpha : 2;
90 unsigned int ctm : 3;
91 unsigned int stroke : 1;
92 unsigned int flags : 6;
93};
94
95enum {
96 CS_UNCHANGED = 0,
97 CS_GRAY_0 = 1,
98 CS_GRAY_1 = 2,
99 CS_RGB_0 = 3,
100 CS_RGB_1 = 4,
101 CS_CMYK_0 = 5,
102 CS_CMYK_1 = 6,
103 CS_OTHER_0 = 7,
104
105 ALPHA_UNCHANGED = 0,
106 ALPHA_1 = 1,
107 ALPHA_0 = 2,
108 ALPHA_PRESENT = 3,
109
110 CTM_UNCHANGED = 0,
111 CTM_CHANGE_AD = 1,
112 CTM_CHANGE_BC = 2,
113 CTM_CHANGE_EF = 4,
114
115 MAX_NODE_SIZE = (1<<9)-sizeof(fz_display_node)
116};
117
118struct fz_display_list_s
119{
120 fz_storable storable;
121 fz_display_node *list;
122 fz_rect mediabox;
123 int max;
124 int len;
125};
126
127struct fz_list_device_s
128{
129 fz_device super;
130
131 fz_display_list *list;
132
133 fz_path *path;
134 float alpha;
135 fz_matrix ctm;
136 fz_stroke_state *stroke;
137 fz_colorspace *colorspace;
138 fz_color_params *color_params;
139 float color[FZ_MAX_COLORS];
140 fz_rect rect;
141
142 int top;
143 struct {
144 fz_rect *update;
145 fz_rect rect;
146 } stack[STACK_SIZE];
147 int tiled;
148};
149
150enum { ISOLATED = 1, KNOCKOUT = 2 };
151enum { OPM = 1, OP = 2, BP = 3, RI = 4};
152
153#define SIZE_IN_NODES(t) \
154 ((t + sizeof(fz_display_node) - 1) / sizeof(fz_display_node))
155
156static void
157fz_append_display_node(
158 fz_context *ctx,
159 fz_device *dev,
160 fz_display_command cmd,
161 int flags,
162 const fz_rect *rect,
163 const fz_path *path,
164 const float *color,
165 fz_colorspace *colorspace,
166 const float *alpha,
167 const fz_matrix *ctm,
168 const fz_stroke_state *stroke,
169 const void *private_data,
170 int private_data_len)
171{
172 fz_display_node node = { 0 };
173 fz_display_node *node_ptr;
174 fz_list_device *writer = (fz_list_device *)dev;
175 fz_display_list *list = writer->list;
176 int size;
177 int rect_off = 0;
178 int path_off = 0;
179 int color_off = 0;
180 int colorspace_off = 0;
181 int alpha_off = 0;
182 int ctm_off = 0;
183 int stroke_off = 0;
184 int rect_for_updates = 0;
185 int private_off = 0;
186 fz_path *my_path = NULL;
187 fz_stroke_state *my_stroke = NULL;
188 fz_rect local_rect;
189 int path_size = 0;
190
191 switch (cmd)
192 {
193 case FZ_CMD_CLIP_PATH:
194 case FZ_CMD_CLIP_STROKE_PATH:
195 case FZ_CMD_CLIP_TEXT:
196 case FZ_CMD_CLIP_STROKE_TEXT:
197 case FZ_CMD_CLIP_IMAGE_MASK:
198 if (writer->top < STACK_SIZE)
199 {
200 rect_for_updates = 1;
201 writer->stack[writer->top].rect = fz_empty_rect;
202 }
203 writer->top++;
204 break;
205 case FZ_CMD_END_MASK:
206 if (writer->top < STACK_SIZE)
207 {
208 writer->stack[writer->top].update = NULL;
209 writer->stack[writer->top].rect = fz_empty_rect;
210 }
211 writer->top++;
212 break;
213 case FZ_CMD_BEGIN_TILE:
214 writer->tiled++;
215 if (writer->top > 0 && writer->top <= STACK_SIZE)
216 {
217 writer->stack[writer->top-1].rect = fz_infinite_rect;
218 }
219 break;
220 case FZ_CMD_END_TILE:
221 writer->tiled--;
222 break;
223 case FZ_CMD_END_GROUP:
224 break;
225 case FZ_CMD_POP_CLIP:
226 if (writer->top > STACK_SIZE)
227 {
228 writer->top--;
229 rect = &fz_infinite_rect;
230 }
231 else if (writer->top > 0)
232 {
233 fz_rect *update;
234 writer->top--;
235 update = writer->stack[writer->top].update;
236 if (writer->tiled == 0)
237 {
238 if (update)
239 {
240 *update = fz_intersect_rect(*update, writer->stack[writer->top].rect);
241 local_rect = *update;
242 rect = &local_rect;
243 }
244 else
245 rect = &writer->stack[writer->top].rect;
246 }
247 else
248 rect = &fz_infinite_rect;
249 }
250 /* fallthrough */
251 default:
252 if (writer->top > 0 && writer->tiled == 0 && writer->top <= STACK_SIZE && rect)
253 writer->stack[writer->top-1].rect = fz_union_rect(writer->stack[writer->top-1].rect, *rect);
254 break;
255 }
256
257 size = 1; /* 1 for the fz_display_node */
258 node.cmd = cmd;
259
260 /* Figure out what we need to write, and the offsets at which we will
261 * write it. */
262 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)))
263 {
264 node.rect = 1;
265 rect_off = size;
266 size += SIZE_IN_NODES(sizeof(fz_rect));
267 }
268 if (color || colorspace)
269 {
270 if (colorspace != writer->colorspace)
271 {
272 assert(color);
273 if (colorspace == fz_device_gray(ctx))
274 {
275 if (color[0] == 0.0f)
276 node.cs = CS_GRAY_0, color = NULL;
277 else
278 {
279 node.cs = CS_GRAY_1;
280 if (color[0] == 1.0f)
281 color = NULL;
282 }
283 }
284 else if (colorspace == fz_device_rgb(ctx))
285 {
286 if (color[0] == 0.0f && color[1] == 0.0f && color[2] == 0.0f)
287 node.cs = CS_RGB_0, color = NULL;
288 else
289 {
290 node.cs = CS_RGB_1;
291 if (color[0] == 1.0f && color[1] == 1.0f && color[2] == 1.0f)
292 color = NULL;
293 }
294 }
295 else if (colorspace == fz_device_cmyk(ctx))
296 {
297 node.cs = CS_CMYK_0;
298 if (color[0] == 0.0f && color[1] == 0.0f && color[2] == 0.0f)
299 {
300 if (color[3] == 0.0f)
301 color = NULL;
302 else
303 {
304 node.cs = CS_CMYK_1;
305 if (color[3] == 1.0f)
306 color = NULL;
307 }
308 }
309 }
310 else
311 {
312 int i;
313 int n = fz_colorspace_n(ctx, colorspace);
314
315 colorspace_off = size;
316 size += SIZE_IN_NODES(sizeof(fz_colorspace *));
317 node.cs = CS_OTHER_0;
318 for (i = 0; i < n; i++)
319 if (color[i] != 0.0f)
320 break;
321 if (i == n)
322 color = NULL;
323 memset(writer->color, 0, sizeof(float)*n);
324 }
325 }
326 else
327 {
328 /* Colorspace is unchanged, but color may have changed
329 * to something best coded as a colorspace change */
330 if (colorspace == fz_device_gray(ctx))
331 {
332 if (writer->color[0] != color[0])
333 {
334 if (color[0] == 0.0f)
335 {
336 node.cs = CS_GRAY_0;
337 color = NULL;
338 }
339 else if (color[0] == 1.0f)
340 {
341 node.cs = CS_GRAY_1;
342 color = NULL;
343 }
344 }
345 }
346 else if (colorspace == fz_device_rgb(ctx))
347 {
348 if (writer->color[0] != color[0] || writer->color[1] != color[1] || writer->color[2] != color[2])
349 {
350 if (color[0] == 0.0f && color[1] == 0.0f && color[2] == 0.0f)
351 {
352 node.cs = CS_RGB_0;
353 color = NULL;
354 }
355 else if (color[0] == 1.0f && color[1] == 1.0f && color[2] == 1.0f)
356 {
357 node.cs = CS_RGB_1;
358 color = NULL;
359 }
360 }
361 }
362 else if (colorspace == fz_device_cmyk(ctx))
363 {
364 if (writer->color[0] != color[0] || writer->color[1] != color[1] || writer->color[2] != color[2] || writer->color[3] != color[3])
365 {
366 if (color[0] == 0.0f && color[1] == 0.0f && color[2] == 0.0f)
367 {
368 if (color[3] == 0.0f)
369 {
370 node.cs = CS_CMYK_0;
371 color = NULL;
372 }
373 else if (color[3] == 1.0f)
374 {
375 node.cs = CS_CMYK_1;
376 color = NULL;
377 }
378 }
379 }
380 }
381 else
382 {
383 int i;
384 int n = fz_colorspace_n(ctx, colorspace);
385 for (i=0; i < n; i++)
386 if (color[i] != 0.0f)
387 break;
388 if (i == n)
389 {
390 node.cs = CS_OTHER_0;
391 colorspace_off = size;
392 size += SIZE_IN_NODES(sizeof(fz_colorspace *));
393 color = NULL;
394 }
395 }
396 }
397 }
398 if (color)
399 {
400 int i, n;
401 const float *wc = &writer->color[0];
402
403 assert(colorspace != NULL);
404 n = fz_colorspace_n(ctx, colorspace);
405 i = 0;
406 /* Only check colors if the colorspace is unchanged. If the
407 * colorspace *has* changed and the colors are implicit then
408 * this will have been caught above. */
409 if (colorspace == writer->colorspace)
410 for (; i < n; i++)
411 if (color[i] != wc[i])
412 break;
413
414 if (i != n)
415 {
416 node.color = 1;
417 color_off = size;
418 size += n * SIZE_IN_NODES(sizeof(float));
419 }
420 }
421 if (alpha && (*alpha != writer->alpha))
422 {
423 if (*alpha >= 1.0f)
424 node.alpha = ALPHA_1;
425 else if (*alpha <= 0.0f)
426 node.alpha = ALPHA_0;
427 else
428 {
429 alpha_off = size;
430 size += SIZE_IN_NODES(sizeof(float));
431 node.alpha = ALPHA_PRESENT;
432 }
433 }
434 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))
435 {
436 int ctm_flags;
437
438 ctm_off = size;
439 ctm_flags = CTM_UNCHANGED;
440 if (ctm->a != writer->ctm.a || ctm->d != writer->ctm.d)
441 ctm_flags = CTM_CHANGE_AD, size += SIZE_IN_NODES(2*sizeof(float));
442 if (ctm->b != writer->ctm.b || ctm->c != writer->ctm.c)
443 ctm_flags |= CTM_CHANGE_BC, size += SIZE_IN_NODES(2*sizeof(float));
444 if (ctm->e != writer->ctm.e || ctm->f != writer->ctm.f)
445 ctm_flags |= CTM_CHANGE_EF, size += SIZE_IN_NODES(2*sizeof(float));
446 node.ctm = ctm_flags;
447 }
448 if (stroke && (writer->stroke == NULL || stroke != writer->stroke))
449 {
450 stroke_off = size;
451 size += SIZE_IN_NODES(sizeof(fz_stroke_state *));
452 node.stroke = 1;
453 }
454 if (path && (writer->path == NULL || path != writer->path))
455 {
456 int max = SIZE_IN_NODES(MAX_NODE_SIZE) - size - SIZE_IN_NODES(private_data_len);
457 path_size = SIZE_IN_NODES(fz_pack_path(ctx, NULL, max, path));
458 node.path = 1;
459 path_off = size;
460
461 size += path_size;
462 }
463 if (private_data != NULL)
464 {
465 int max = SIZE_IN_NODES(MAX_NODE_SIZE) - size;
466 if (SIZE_IN_NODES(private_data_len) > max)
467 fz_throw(ctx, FZ_ERROR_GENERIC, "Private data too large to pack into display list node");
468 private_off = size;
469 size += SIZE_IN_NODES(private_data_len);
470 }
471
472 while (list->len + size > list->max)
473 {
474 int newsize = list->max * 2;
475 fz_display_node *old = list->list;
476 ptrdiff_t diff;
477 int i, n;
478
479 if (newsize < 256)
480 newsize = 256;
481 list->list = fz_realloc_array(ctx, list->list, newsize, fz_display_node);
482 list->max = newsize;
483 diff = (char *)(list->list) - (char *)old;
484 n = (writer->top < STACK_SIZE ? writer->top : STACK_SIZE);
485 for (i = 0; i < n; i++)
486 {
487 if (writer->stack[i].update != NULL)
488 writer->stack[i].update = (fz_rect *)(((char *)writer->stack[i].update) + diff);
489 }
490 if (writer->path)
491 writer->path = (fz_path *)(((char *)writer->path) + diff);
492 }
493
494 /* Write the node to the list */
495 node.size = 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 */
640static int
641fz_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 */
652static void
653fz_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
661static void
662fz_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
682static void
683fz_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
703static void
704fz_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
724static void
725fz_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
745static void
746fz_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
775static void
776fz_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
805static void
806fz_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
835static void
836fz_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
865static void
866fz_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
894static void
895fz_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
913static void
914fz_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
942static void
943fz_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
971static void
972fz_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
1002static void
1003fz_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
1032static void
1033fz_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
1051static void
1052fz_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
1070static void
1071fz_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
1107static void
1108fz_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
1126typedef struct fz_list_tile_data_s fz_list_tile_data;
1127
1128struct fz_list_tile_data_s
1129{
1130 float xstep;
1131 float ystep;
1132 fz_rect view;
1133 int id;
1134};
1135
1136static int
1137fz_list_begin_tile(fz_context *ctx, fz_device *dev, fz_rect area, fz_rect view, float xstep, float ystep, fz_matrix ctm, int id)
1138{
1139 fz_list_tile_data tile;
1140
1141 tile.xstep = xstep;
1142 tile.ystep = ystep;
1143 tile.view = view;
1144 tile.id = id;
1145 fz_append_display_node(
1146 ctx,
1147 dev,
1148 FZ_CMD_BEGIN_TILE,
1149 0, /* flags */
1150 &area,
1151 NULL, /* path */
1152 NULL, /* color */
1153 NULL, /* colorspace */
1154 NULL, /* alpha */
1155 &ctm, /* ctm */
1156 NULL, /* stroke */
1157 &tile, /* private_data */
1158 sizeof(tile)); /* private_data_len */
1159
1160 return 0;
1161}
1162
1163static void
1164fz_list_end_tile(fz_context *ctx, fz_device *dev)
1165{
1166 fz_append_display_node(
1167 ctx,
1168 dev,
1169 FZ_CMD_END_TILE,
1170 0, /* flags */
1171 NULL,
1172 NULL, /* path */
1173 NULL, /* color */
1174 NULL, /* colorspace */
1175 NULL, /* alpha */
1176 NULL, /* ctm */
1177 NULL, /* stroke */
1178 NULL, /* private_data */
1179 0); /* private_data_len */
1180}
1181
1182static void
1183fz_list_render_flags(fz_context *ctx, fz_device *dev, int set, int clear)
1184{
1185 int flags;
1186
1187 /* Pack the options down */
1188 if (set == FZ_DEVFLAG_GRIDFIT_AS_TILED && clear == 0)
1189 flags = 1;
1190 else if (set == 0 && clear == FZ_DEVFLAG_GRIDFIT_AS_TILED)
1191 flags = 0;
1192 else
1193 {
1194 assert("Unsupported flags combination" == NULL);
1195 return;
1196 }
1197 fz_append_display_node(
1198 ctx,
1199 dev,
1200 FZ_CMD_RENDER_FLAGS,
1201 flags, /* flags */
1202 NULL,
1203 NULL, /* path */
1204 NULL, /* color */
1205 NULL, /* colorspace */
1206 NULL, /* alpha */
1207 NULL, /* ctm */
1208 NULL, /* stroke */
1209 NULL, /* private_data */
1210 0); /* private_data_len */
1211}
1212
1213static void
1214fz_list_set_default_colorspaces(fz_context *ctx, fz_device *dev, fz_default_colorspaces *default_cs)
1215{
1216 fz_default_colorspaces *default_cs2 = fz_keep_default_colorspaces(ctx, default_cs);
1217
1218 fz_try(ctx)
1219 {
1220 fz_append_display_node(
1221 ctx,
1222 dev,
1223 FZ_CMD_DEFAULT_COLORSPACES,
1224 0, /* flags */
1225 NULL,
1226 NULL, /* path */
1227 NULL, /* color */
1228 NULL, /* colorspace */
1229 NULL, /* alpha */
1230 NULL, /* ctm */
1231 NULL, /* stroke */
1232 &default_cs2, /* private_data */
1233 sizeof(default_cs2)); /* private_data_len */
1234 }
1235 fz_catch(ctx)
1236 {
1237 fz_drop_default_colorspaces(ctx, default_cs2);
1238 fz_rethrow(ctx);
1239 }
1240}
1241
1242static void
1243fz_list_begin_layer(fz_context *ctx, fz_device *dev, const char *layer_name)
1244{
1245 fz_append_display_node(
1246 ctx,
1247 dev,
1248 FZ_CMD_BEGIN_LAYER,
1249 0, /* flags */
1250 NULL,
1251 NULL, /* path */
1252 NULL, /* color */
1253 NULL, /* colorspace */
1254 NULL, /* alpha */
1255 NULL,
1256 NULL, /* stroke */
1257 layer_name, /* private_data */
1258 1+strlen(layer_name)); /* private_data_len */
1259}
1260
1261static void
1262fz_list_end_layer(fz_context *ctx, fz_device *dev)
1263{
1264 fz_append_display_node(
1265 ctx,
1266 dev,
1267 FZ_CMD_END_LAYER,
1268 0, /* flags */
1269 NULL,
1270 NULL, /* path */
1271 NULL, /* color */
1272 NULL, /* colorspace */
1273 NULL, /* alpha */
1274 NULL, /* ctm */
1275 NULL, /* stroke */
1276 NULL, /* private_data */
1277 0); /* private_data_len */
1278}
1279
1280static void
1281fz_list_drop_device(fz_context *ctx, fz_device *dev)
1282{
1283 fz_list_device *writer = (fz_list_device *)dev;
1284
1285 fz_drop_colorspace(ctx, writer->colorspace);
1286 fz_drop_stroke_state(ctx, writer->stroke);
1287 fz_drop_path(ctx, writer->path);
1288}
1289
1290/*
1291 Create a rendering device for a display list.
1292
1293 When the device is rendering a page it will populate the
1294 display list with drawing commands (text, images, etc.). The
1295 display list can later be reused to render a page many times
1296 without having to re-interpret the page from the document file
1297 for each rendering. Once the device is no longer needed, free
1298 it with fz_drop_device.
1299
1300 list: A display list that the list device takes ownership of.
1301*/
1302fz_device *
1303fz_new_list_device(fz_context *ctx, fz_display_list *list)
1304{
1305 fz_list_device *dev;
1306
1307 dev = fz_new_derived_device(ctx, fz_list_device);
1308
1309 dev->super.fill_path = fz_list_fill_path;
1310 dev->super.stroke_path = fz_list_stroke_path;
1311 dev->super.clip_path = fz_list_clip_path;
1312 dev->super.clip_stroke_path = fz_list_clip_stroke_path;
1313
1314 dev->super.fill_text = fz_list_fill_text;
1315 dev->super.stroke_text = fz_list_stroke_text;
1316 dev->super.clip_text = fz_list_clip_text;
1317 dev->super.clip_stroke_text = fz_list_clip_stroke_text;
1318 dev->super.ignore_text = fz_list_ignore_text;
1319
1320 dev->super.fill_shade = fz_list_fill_shade;
1321 dev->super.fill_image = fz_list_fill_image;
1322 dev->super.fill_image_mask = fz_list_fill_image_mask;
1323 dev->super.clip_image_mask = fz_list_clip_image_mask;
1324
1325 dev->super.pop_clip = fz_list_pop_clip;
1326
1327 dev->super.begin_mask = fz_list_begin_mask;
1328 dev->super.end_mask = fz_list_end_mask;
1329 dev->super.begin_group = fz_list_begin_group;
1330 dev->super.end_group = fz_list_end_group;
1331
1332 dev->super.begin_tile = fz_list_begin_tile;
1333 dev->super.end_tile = fz_list_end_tile;
1334
1335 dev->super.render_flags = fz_list_render_flags;
1336 dev->super.set_default_colorspaces = fz_list_set_default_colorspaces;
1337
1338 dev->super.begin_layer = fz_list_begin_layer;
1339 dev->super.end_layer = fz_list_end_layer;
1340
1341 dev->super.drop_device = fz_list_drop_device;
1342
1343 dev->list = list;
1344 dev->path = NULL;
1345 dev->alpha = 1.0f;
1346 dev->ctm = fz_identity;
1347 dev->stroke = NULL;
1348 dev->colorspace = fz_keep_colorspace(ctx, fz_device_gray(ctx));
1349 memset(dev->color, 0, sizeof(float)*FZ_MAX_COLORS);
1350 dev->top = 0;
1351 dev->tiled = 0;
1352
1353 return &dev->super;
1354}
1355
1356static void
1357fz_drop_display_list_imp(fz_context *ctx, fz_storable *list_)
1358{
1359 fz_display_list *list = (fz_display_list *)list_;
1360 fz_display_node *node = list->list;
1361 fz_display_node *node_end = list->list + list->len;
1362 int cs_n = 1;
1363 fz_colorspace *cs;
1364
1365 while (node != node_end)
1366 {
1367 fz_display_node n = *node;
1368 fz_display_node *next = node + n.size;
1369
1370 node++;
1371 if (n.rect)
1372 {
1373 node += SIZE_IN_NODES(sizeof(fz_rect));
1374 }
1375 switch (n.cs)
1376 {
1377 default:
1378 case CS_UNCHANGED:
1379 break;
1380 case CS_GRAY_0:
1381 case CS_GRAY_1:
1382 cs_n = 1;
1383 break;
1384 case CS_RGB_0:
1385 case CS_RGB_1:
1386 cs_n = 3;
1387 break;
1388 case CS_CMYK_0:
1389 case CS_CMYK_1:
1390 cs_n = 4;
1391 break;
1392 case CS_OTHER_0:
1393 cs = *(fz_colorspace **)node;
1394 cs_n = fz_colorspace_n(ctx, cs);
1395 fz_drop_colorspace(ctx, cs);
1396 node += SIZE_IN_NODES(sizeof(fz_colorspace *));
1397 break;
1398 }
1399 if (n.color)
1400 {
1401 node += SIZE_IN_NODES(cs_n * sizeof(float));
1402 }
1403 if (n.alpha == ALPHA_PRESENT)
1404 {
1405 node += SIZE_IN_NODES(sizeof(float));
1406 }
1407 if (n.ctm & CTM_CHANGE_AD)
1408 node += SIZE_IN_NODES(2*sizeof(float));
1409 if (n.ctm & CTM_CHANGE_BC)
1410 node += SIZE_IN_NODES(2*sizeof(float));
1411 if (n.ctm & CTM_CHANGE_EF)
1412 node += SIZE_IN_NODES(2*sizeof(float));
1413 if (n.stroke)
1414 {
1415 fz_drop_stroke_state(ctx, *(fz_stroke_state **)node);
1416 node += SIZE_IN_NODES(sizeof(fz_stroke_state *));
1417 }
1418 if (n.path)
1419 {
1420 int path_size = fz_packed_path_size((fz_path *)node);
1421 fz_drop_path(ctx, (fz_path *)node);
1422 node += SIZE_IN_NODES(path_size);
1423 }
1424 switch(n.cmd)
1425 {
1426 case FZ_CMD_FILL_TEXT:
1427 case FZ_CMD_STROKE_TEXT:
1428 case FZ_CMD_CLIP_TEXT:
1429 case FZ_CMD_CLIP_STROKE_TEXT:
1430 case FZ_CMD_IGNORE_TEXT:
1431 fz_drop_text(ctx, *(fz_text **)node);
1432 break;
1433 case FZ_CMD_FILL_SHADE:
1434 fz_drop_shade(ctx, *(fz_shade **)node);
1435 break;
1436 case FZ_CMD_FILL_IMAGE:
1437 case FZ_CMD_FILL_IMAGE_MASK:
1438 case FZ_CMD_CLIP_IMAGE_MASK:
1439 fz_drop_image(ctx, *(fz_image **)node);
1440 break;
1441 case FZ_CMD_BEGIN_GROUP:
1442 fz_drop_colorspace(ctx, *(fz_colorspace **)node);
1443 break;
1444 case FZ_CMD_DEFAULT_COLORSPACES:
1445 fz_drop_default_colorspaces(ctx, *(fz_default_colorspaces **)node);
1446 break;
1447 }
1448 node = next;
1449 }
1450 fz_free(ctx, list->list);
1451 fz_free(ctx, list);
1452}
1453
1454/*
1455 Create an empty display list.
1456
1457 A display list contains drawing commands (text, images, etc.).
1458 Use fz_new_list_device for populating the list.
1459
1460 mediabox: Bounds of the page (in points) represented by the display list.
1461*/
1462fz_display_list *
1463fz_new_display_list(fz_context *ctx, fz_rect mediabox)
1464{
1465 fz_display_list *list = fz_malloc_struct(ctx, fz_display_list);
1466 FZ_INIT_STORABLE(list, 1, fz_drop_display_list_imp);
1467 list->list = NULL;
1468 list->mediabox = mediabox;
1469 list->max = 0;
1470 list->len = 0;
1471 return list;
1472}
1473
1474fz_display_list *
1475fz_keep_display_list(fz_context *ctx, fz_display_list *list)
1476{
1477 return fz_keep_storable(ctx, &list->storable);
1478}
1479
1480void
1481fz_drop_display_list(fz_context *ctx, fz_display_list *list)
1482{
1483 fz_defer_reap_start(ctx);
1484 fz_drop_storable(ctx, &list->storable);
1485 fz_defer_reap_end(ctx);
1486}
1487
1488/*
1489 Return the bounding box of the page recorded in a display list.
1490*/
1491fz_rect
1492fz_bound_display_list(fz_context *ctx, fz_display_list *list)
1493{
1494 return list->mediabox;
1495}
1496
1497/*
1498 Check for a display list being empty
1499
1500 list: The list to check.
1501
1502 Returns true if empty, false otherwise.
1503*/
1504int fz_display_list_is_empty(fz_context *ctx, const fz_display_list *list)
1505{
1506 return !list || list->len == 0;
1507}
1508
1509/*
1510 (Re)-run a display list through a device.
1511
1512 list: A display list, created by fz_new_display_list and
1513 populated with objects from a page by running fz_run_page on a
1514 device obtained from fz_new_list_device.
1515
1516 ctm: Transform to apply to display list contents. May include
1517 for example scaling and rotation, see fz_scale, fz_rotate and
1518 fz_concat. Set to fz_identity if no transformation is desired.
1519
1520 scissor: Only the part of the contents of the display list
1521 visible within this area will be considered when the list is
1522 run through the device. This does not imply for tile objects
1523 contained in the display list.
1524
1525 cookie: Communication mechanism between caller and library
1526 running the page. Intended for multi-threaded applications,
1527 while single-threaded applications set cookie to NULL. The
1528 caller may abort an ongoing page run. Cookie also communicates
1529 progress information back to the caller. The fields inside
1530 cookie are continually updated while the page is being run.
1531*/
1532void
1533fz_run_display_list(fz_context *ctx, fz_display_list *list, fz_device *dev, fz_matrix top_ctm, fz_rect scissor, fz_cookie *cookie)
1534{
1535 fz_display_node *node;
1536 fz_display_node *node_end;
1537 fz_display_node *next_node;
1538 int clipped = 0;
1539 int tiled = 0;
1540 int progress = 0;
1541
1542 /* Current graphics state as unpacked from list */
1543 fz_path *path = NULL;
1544 float alpha = 1.0f;
1545 fz_matrix ctm = fz_identity;
1546 fz_stroke_state *stroke = NULL;
1547 float color[FZ_MAX_COLORS] = { 0 };
1548 fz_colorspace *colorspace = fz_keep_colorspace(ctx, fz_device_gray(ctx));
1549 fz_color_params color_params;
1550 fz_rect rect = { 0 };
1551
1552 /* Transformed versions of graphic state entries */
1553 fz_rect trans_rect;
1554 fz_matrix trans_ctm;
1555 int tile_skip_depth = 0;
1556
1557 if (cookie)
1558 {
1559 cookie->progress_max = list->len;
1560 cookie->progress = 0;
1561 }
1562
1563 color_params = fz_default_color_params;
1564
1565 node = list->list;
1566 node_end = &list->list[list->len];
1567 for (; node != node_end ; node = next_node)
1568 {
1569 int empty;
1570 fz_display_node n = *node;
1571
1572 next_node = node + n.size;
1573
1574 /* Check the cookie for aborting */
1575 if (cookie)
1576 {
1577 if (cookie->abort)
1578 break;
1579 cookie->progress = progress;
1580 progress += n.size;
1581 }
1582
1583 node++;
1584 if (n.rect)
1585 {
1586 rect = *(fz_rect *)node;
1587 node += SIZE_IN_NODES(sizeof(fz_rect));
1588 }
1589 if (n.cs)
1590 {
1591 int i, en;
1592
1593 fz_drop_colorspace(ctx, colorspace);
1594 switch (n.cs)
1595 {
1596 default:
1597 case CS_GRAY_0:
1598 colorspace = fz_keep_colorspace(ctx, fz_device_gray(ctx));
1599 color[0] = 0.0f;
1600 break;
1601 case CS_GRAY_1:
1602 colorspace = fz_keep_colorspace(ctx, fz_device_gray(ctx));
1603 color[0] = 1.0f;
1604 break;
1605 case CS_RGB_0:
1606 colorspace = fz_keep_colorspace(ctx, fz_device_rgb(ctx));
1607 color[0] = 0.0f;
1608 color[1] = 0.0f;
1609 color[2] = 0.0f;
1610 break;
1611 case CS_RGB_1:
1612 colorspace = fz_keep_colorspace(ctx, fz_device_rgb(ctx));
1613 color[0] = 1.0f;
1614 color[1] = 1.0f;
1615 color[2] = 1.0f;
1616 break;
1617 case CS_CMYK_0:
1618 colorspace = fz_keep_colorspace(ctx, fz_device_cmyk(ctx));
1619 color[0] = 0.0f;
1620 color[1] = 0.0f;
1621 color[2] = 0.0f;
1622 color[3] = 0.0f;
1623 break;
1624 case CS_CMYK_1:
1625 colorspace = fz_keep_colorspace(ctx, fz_device_cmyk(ctx));
1626 color[0] = 0.0f;
1627 color[1] = 0.0f;
1628 color[2] = 0.0f;
1629 color[3] = 1.0f;
1630 break;
1631 case CS_OTHER_0:
1632 colorspace = fz_keep_colorspace(ctx, *(fz_colorspace **)(node));
1633 node += SIZE_IN_NODES(sizeof(fz_colorspace *));
1634 en = fz_colorspace_n(ctx, colorspace);
1635 for (i = 0; i < en; i++)
1636 color[i] = 0.0f;
1637 break;
1638 }
1639 }
1640 if (n.color)
1641 {
1642 int nc = fz_colorspace_n(ctx, colorspace);
1643 memcpy(color, (float *)node, nc * sizeof(float));
1644 node += SIZE_IN_NODES(nc * sizeof(float));
1645 }
1646 if (n.alpha)
1647 {
1648 switch(n.alpha)
1649 {
1650 default:
1651 case ALPHA_0:
1652 alpha = 0.0f;
1653 break;
1654 case ALPHA_1:
1655 alpha = 1.0f;
1656 break;
1657 case ALPHA_PRESENT:
1658 alpha = *(float *)node;
1659 node += SIZE_IN_NODES(sizeof(float));
1660 break;
1661 }
1662 }
1663 if (n.ctm != 0)
1664 {
1665 float *packed_ctm = (float *)node;
1666 if (n.ctm & CTM_CHANGE_AD)
1667 {
1668 ctm.a = *packed_ctm++;
1669 ctm.d = *packed_ctm++;
1670 node += SIZE_IN_NODES(2*sizeof(float));
1671 }
1672 if (n.ctm & CTM_CHANGE_BC)
1673 {
1674 ctm.b = *packed_ctm++;
1675 ctm.c = *packed_ctm++;
1676 node += SIZE_IN_NODES(2*sizeof(float));
1677 }
1678 if (n.ctm & CTM_CHANGE_EF)
1679 {
1680 ctm.e = *packed_ctm++;
1681 ctm.f = *packed_ctm;
1682 node += SIZE_IN_NODES(2*sizeof(float));
1683 }
1684 }
1685 if (n.stroke)
1686 {
1687 fz_drop_stroke_state(ctx, stroke);
1688 stroke = fz_keep_stroke_state(ctx, *(fz_stroke_state **)node);
1689 node += SIZE_IN_NODES(sizeof(fz_stroke_state *));
1690 }
1691 if (n.path)
1692 {
1693 fz_drop_path(ctx, path);
1694 path = fz_keep_path(ctx, (fz_path *)node);
1695 node += SIZE_IN_NODES(fz_packed_path_size(path));
1696 }
1697
1698 if (tile_skip_depth > 0)
1699 {
1700 if (n.cmd == FZ_CMD_BEGIN_TILE)
1701 tile_skip_depth++;
1702 else if (n.cmd == FZ_CMD_END_TILE)
1703 tile_skip_depth--;
1704 if (tile_skip_depth > 0)
1705 continue;
1706 }
1707
1708 trans_rect = fz_transform_rect(rect, top_ctm);
1709
1710 /* cull objects to draw using a quick visibility test */
1711
1712 if (tiled ||
1713 n.cmd == FZ_CMD_BEGIN_TILE || n.cmd == FZ_CMD_END_TILE ||
1714 n.cmd == FZ_CMD_RENDER_FLAGS || n.cmd == FZ_CMD_DEFAULT_COLORSPACES ||
1715 n.cmd == FZ_CMD_BEGIN_LAYER || n.cmd == FZ_CMD_END_LAYER)
1716 {
1717 empty = 0;
1718 }
1719 else
1720 {
1721 empty = fz_is_empty_rect(fz_intersect_rect(trans_rect, scissor));
1722 }
1723
1724 if (clipped || empty)
1725 {
1726 switch (n.cmd)
1727 {
1728 case FZ_CMD_CLIP_PATH:
1729 case FZ_CMD_CLIP_STROKE_PATH:
1730 case FZ_CMD_CLIP_TEXT:
1731 case FZ_CMD_CLIP_STROKE_TEXT:
1732 case FZ_CMD_CLIP_IMAGE_MASK:
1733 case FZ_CMD_BEGIN_MASK:
1734 case FZ_CMD_BEGIN_GROUP:
1735 clipped++;
1736 continue;
1737 case FZ_CMD_POP_CLIP:
1738 case FZ_CMD_END_GROUP:
1739 if (!clipped)
1740 goto visible;
1741 clipped--;
1742 continue;
1743 case FZ_CMD_END_MASK:
1744 if (!clipped)
1745 goto visible;
1746 continue;
1747 default:
1748 continue;
1749 }
1750 }
1751
1752visible:
1753 trans_ctm = fz_concat(ctm, top_ctm);
1754
1755 fz_try(ctx)
1756 {
1757 switch (n.cmd)
1758 {
1759 case FZ_CMD_FILL_PATH:
1760 fz_unpack_color_params(&color_params, n.flags);
1761 fz_fill_path(ctx, dev, path, n.flags & 1, trans_ctm, colorspace, color, alpha, color_params);
1762 break;
1763 case FZ_CMD_STROKE_PATH:
1764 fz_unpack_color_params(&color_params, n.flags);
1765 fz_stroke_path(ctx, dev, path, stroke, trans_ctm, colorspace, color, alpha, color_params);
1766 break;
1767 case FZ_CMD_CLIP_PATH:
1768 fz_clip_path(ctx, dev, path, n.flags, trans_ctm, trans_rect);
1769 break;
1770 case FZ_CMD_CLIP_STROKE_PATH:
1771 fz_clip_stroke_path(ctx, dev, path, stroke, trans_ctm, trans_rect);
1772 break;
1773 case FZ_CMD_FILL_TEXT:
1774 fz_unpack_color_params(&color_params, n.flags);
1775 fz_fill_text(ctx, dev, *(fz_text **)node, trans_ctm, colorspace, color, alpha, color_params);
1776 break;
1777 case FZ_CMD_STROKE_TEXT:
1778 fz_unpack_color_params(&color_params, n.flags);
1779 fz_stroke_text(ctx, dev, *(fz_text **)node, stroke, trans_ctm, colorspace, color, alpha, color_params);
1780 break;
1781 case FZ_CMD_CLIP_TEXT:
1782 fz_clip_text(ctx, dev, *(fz_text **)node, trans_ctm, trans_rect);
1783 break;
1784 case FZ_CMD_CLIP_STROKE_TEXT:
1785 fz_clip_stroke_text(ctx, dev, *(fz_text **)node, stroke, trans_ctm, trans_rect);
1786 break;
1787 case FZ_CMD_IGNORE_TEXT:
1788 fz_ignore_text(ctx, dev, *(fz_text **)node, trans_ctm);
1789 break;
1790 case FZ_CMD_FILL_SHADE:
1791 fz_unpack_color_params(&color_params, n.flags);
1792 fz_fill_shade(ctx, dev, *(fz_shade **)node, trans_ctm, alpha, color_params);
1793 break;
1794 case FZ_CMD_FILL_IMAGE:
1795 fz_unpack_color_params(&color_params, n.flags);
1796 fz_fill_image(ctx, dev, *(fz_image **)node, trans_ctm, alpha, color_params);
1797 break;
1798 case FZ_CMD_FILL_IMAGE_MASK:
1799 fz_unpack_color_params(&color_params, n.flags);
1800 fz_fill_image_mask(ctx, dev, *(fz_image **)node, trans_ctm, colorspace, color, alpha, color_params);
1801 break;
1802 case FZ_CMD_CLIP_IMAGE_MASK:
1803 fz_clip_image_mask(ctx, dev, *(fz_image **)node, trans_ctm, trans_rect);
1804 break;
1805 case FZ_CMD_POP_CLIP:
1806 fz_pop_clip(ctx, dev);
1807 break;
1808 case FZ_CMD_BEGIN_MASK:
1809 fz_unpack_color_params(&color_params, n.flags);
1810 fz_begin_mask(ctx, dev, trans_rect, n.flags & 1, colorspace, color, color_params);
1811 break;
1812 case FZ_CMD_END_MASK:
1813 fz_end_mask(ctx, dev);
1814 break;
1815 case FZ_CMD_BEGIN_GROUP:
1816 fz_begin_group(ctx, dev, trans_rect, *(fz_colorspace **)node, (n.flags & ISOLATED) != 0, (n.flags & KNOCKOUT) != 0, (n.flags>>2), alpha);
1817 break;
1818 case FZ_CMD_END_GROUP:
1819 fz_end_group(ctx, dev);
1820 break;
1821 case FZ_CMD_BEGIN_TILE:
1822 {
1823 int cached;
1824 fz_list_tile_data *data = (fz_list_tile_data *)node;
1825 fz_rect tile_rect;
1826 tiled++;
1827 tile_rect = data->view;
1828 cached = fz_begin_tile_id(ctx, dev, rect, tile_rect, data->xstep, data->ystep, trans_ctm, data->id);
1829 if (cached)
1830 tile_skip_depth = 1;
1831 break;
1832 }
1833 case FZ_CMD_END_TILE:
1834 tiled--;
1835 fz_end_tile(ctx, dev);
1836 break;
1837 case FZ_CMD_RENDER_FLAGS:
1838 if (n.flags == 0)
1839 fz_render_flags(ctx, dev, 0, FZ_DEVFLAG_GRIDFIT_AS_TILED);
1840 else if (n.flags == 1)
1841 fz_render_flags(ctx, dev, FZ_DEVFLAG_GRIDFIT_AS_TILED, 0);
1842 break;
1843 case FZ_CMD_DEFAULT_COLORSPACES:
1844 fz_set_default_colorspaces(ctx, dev, *(fz_default_colorspaces **)node);
1845 break;
1846 case FZ_CMD_BEGIN_LAYER:
1847 fz_begin_layer(ctx, dev, (const char *)node);
1848 break;
1849 case FZ_CMD_END_LAYER:
1850 fz_end_layer(ctx, dev);
1851 break;
1852 }
1853 }
1854 fz_catch(ctx)
1855 {
1856 /* Swallow the error */
1857 if (cookie)
1858 cookie->errors++;
1859 if (fz_caught(ctx) == FZ_ERROR_ABORT)
1860 break;
1861 fz_warn(ctx, "Ignoring error during interpretation");
1862 }
1863 }
1864 fz_drop_colorspace(ctx, colorspace);
1865 fz_drop_stroke_state(ctx, stroke);
1866 fz_drop_path(ctx, path);
1867 if (cookie)
1868 cookie->progress = progress;
1869}
1870