1 #include "mupdf/fitz.h"
2
3 #include "context-imp.h"
4 #include "color-imp.h"
5 #include "draw-imp.h"
6 #include "glyph-imp.h"
7 #include "pixmap-imp.h"
8
9 #include <string.h>
10 #include <assert.h>
11 #include <math.h>
12 #include <float.h>
13
14 #define STACK_SIZE 96
15
16 /* Enable the following to attempt to support knockout and/or isolated
17 * blending groups. */
18 #define ATTEMPT_KNOCKOUT_AND_ISOLATED
19
20 /* Enable the following to help debug group blending. */
21 #undef DUMP_GROUP_BLENDS
22
23 /* Enable the following to help debug graphics stack pushes/pops */
24 #undef DUMP_STACK_CHANGES
25
26 enum {
27 FZ_DRAWDEV_FLAGS_TYPE3 = 1,
28 };
29
30 typedef struct {
31 fz_irect scissor;
32 fz_pixmap *dest;
33 fz_pixmap *mask;
34 fz_pixmap *shape;
35 fz_pixmap *group_alpha;
36 int blendmode;
37 int id, encache;
38 float alpha;
39 fz_matrix ctm;
40 float xstep, ystep;
41 fz_irect area;
42 } fz_draw_state;
43
44 typedef struct fz_draw_device
45 {
46 fz_device super;
47 fz_matrix transform;
48 fz_rasterizer *rast;
49 fz_default_colorspaces *default_cs;
50 fz_colorspace *proof_cs;
51 int flags;
52 int resolve_spots;
53 int top;
54 fz_scale_cache *cache_x;
55 fz_scale_cache *cache_y;
56 fz_draw_state *stack;
57 int stack_cap;
58 fz_draw_state init_stack[STACK_SIZE];
59 } fz_draw_device;
60
61 #ifdef DUMP_GROUP_BLENDS
62
63 #include <stdio.h>
64
65 static int group_dump_count = 0;
66
fz_dump_blend(fz_context * ctx,const char * s,fz_pixmap * pix)67 static void fz_dump_blend(fz_context *ctx, const char *s, fz_pixmap *pix)
68 {
69 char name[80];
70 int psd = 0;
71
72 if (!pix)
73 return;
74
75 if (pix->s || fz_colorspace_is_subtractive(ctx, pix->colorspace))
76 psd = 1;
77
78 fz_snprintf(name, sizeof(name), "dump%02d.%s", group_dump_count, psd ? "psd" : "png");
79 printf("%s%02d%s(%p)", s ? s : "", group_dump_count++, psd ? "(PSD)" : "", pix);
80 if (psd)
81 fz_save_pixmap_as_psd(ctx, pix, name);
82 else
83 fz_save_pixmap_as_png(ctx, pix, name);
84 }
85
dump_spaces(int x,const char * s)86 static void dump_spaces(int x, const char *s)
87 {
88 int i;
89 for (i = 0; i < x; i++)
90 printf(" ");
91 printf("%s", s);
92 }
93
94 #endif
95
96 #ifdef DUMP_STACK_CHANGES
97 #define STACK_PUSHED(A) stack_change(ctx, dev, '>', A)
98 #define STACK_POPPED(A) stack_change(ctx, dev, '<', A)
99 #define STACK_CONVERT(A) stack_change(ctx, dev, '=', A)
stack_change(fz_context * ctx,fz_draw_device * dev,int c,const char * s)100 static void stack_change(fz_context *ctx, fz_draw_device *dev, int c, const char *s)
101 {
102 int n, depth = dev->top;
103 if (c != '<')
104 depth--;
105 n = depth;
106 while (n-- > 0)
107 fputc('\t', stderr);
108 fprintf(stderr, "%c%s (%d)\n", c, s, depth);
109 }
110 #else
111 #define STACK_PUSHED(A) do {} while (0)
112 #define STACK_POPPED(A) do {} while (0)
113 #define STACK_CONVERT(A) do {} while (0)
114 #endif
115
116 /* Logic below assumes that default cs is set to color context cs if there
117 * was not a default in the document for that particular cs
118 */
fz_default_colorspace(fz_context * ctx,fz_default_colorspaces * default_cs,fz_colorspace * cs)119 static fz_colorspace *fz_default_colorspace(fz_context *ctx, fz_default_colorspaces *default_cs, fz_colorspace *cs)
120 {
121 if (cs == NULL)
122 return NULL;
123 if (default_cs == NULL)
124 return cs;
125
126 switch (fz_colorspace_type(ctx, cs))
127 {
128 case FZ_COLORSPACE_GRAY:
129 if (cs == fz_device_gray(ctx))
130 return fz_default_gray(ctx, default_cs);
131 break;
132 case FZ_COLORSPACE_RGB:
133 if (cs == fz_device_rgb(ctx))
134 return fz_default_rgb(ctx, default_cs);
135 break;
136 case FZ_COLORSPACE_CMYK:
137 if (cs == fz_device_cmyk(ctx))
138 return fz_default_cmyk(ctx, default_cs);
139 break;
140 default:
141 break;
142 }
143 return cs;
144 }
145
grow_stack(fz_context * ctx,fz_draw_device * dev)146 static void grow_stack(fz_context *ctx, fz_draw_device *dev)
147 {
148 int max = dev->stack_cap * 2;
149 fz_draw_state *stack;
150 if (dev->stack == &dev->init_stack[0])
151 {
152 stack = fz_malloc_array(ctx, max, fz_draw_state);
153 memcpy(stack, dev->stack, sizeof(*stack) * dev->stack_cap);
154 }
155 else
156 {
157 stack = fz_realloc_array(ctx, dev->stack, max, fz_draw_state);
158 }
159 dev->stack = Memento_label(stack, "draw_stack");
160 dev->stack_cap = max;
161 }
162
163 /* 'Push' the stack. Returns a pointer to the current state, with state[1]
164 * already having been initialised to contain the same thing. Simply
165 * change any contents of state[1] that you want to and continue. */
push_stack(fz_context * ctx,fz_draw_device * dev,const char * message)166 static fz_draw_state *push_stack(fz_context *ctx, fz_draw_device *dev, const char *message)
167 {
168 fz_draw_state *state;
169 if (dev->top == dev->stack_cap-1)
170 grow_stack(ctx, dev);
171 state = &dev->stack[dev->top];
172 dev->top++;
173 memcpy(&state[1], state, sizeof(*state));
174 STACK_PUSHED(message);
175 return state;
176 }
177
pop_stack(fz_context * ctx,fz_draw_device * dev,const char * message)178 static fz_draw_state *pop_stack(fz_context *ctx, fz_draw_device *dev, const char *message)
179 {
180 fz_draw_state *state = &dev->stack[--dev->top];
181 STACK_POPPED(message);
182 return state;
183 }
184
convert_stack(fz_context * ctx,fz_draw_device * dev,const char * message)185 static fz_draw_state *convert_stack(fz_context *ctx, fz_draw_device *dev, const char *message)
186 {
187 fz_draw_state *state = &dev->stack[dev->top-1];
188 STACK_CONVERT(message);
189 return state;
190 }
191
192 static fz_draw_state *
fz_knockout_begin(fz_context * ctx,fz_draw_device * dev)193 fz_knockout_begin(fz_context *ctx, fz_draw_device *dev)
194 {
195 fz_irect bbox, ga_bbox;
196 fz_draw_state *state = &dev->stack[dev->top];
197 int isolated = state->blendmode & FZ_BLEND_ISOLATED;
198
199 if ((state->blendmode & FZ_BLEND_KNOCKOUT) == 0)
200 return state;
201
202 state = push_stack(ctx, dev, "knockout");
203
204 bbox = fz_pixmap_bbox(ctx, state->dest);
205 bbox = fz_intersect_irect(bbox, state->scissor);
206 state[1].dest = fz_new_pixmap_with_bbox(ctx, state->dest->colorspace, bbox, state->dest->seps, state->dest->alpha);
207 if (state[0].group_alpha)
208 {
209 ga_bbox = fz_pixmap_bbox(ctx, state->group_alpha);
210 ga_bbox = fz_intersect_irect(ga_bbox, state->scissor);
211 state[1].group_alpha = fz_new_pixmap_with_bbox(ctx, state->group_alpha->colorspace, ga_bbox, state->group_alpha->seps, state->group_alpha->alpha);
212 }
213
214 if (isolated)
215 {
216 fz_clear_pixmap(ctx, state[1].dest);
217 if (state[1].group_alpha)
218 fz_clear_pixmap(ctx, state[1].group_alpha);
219 }
220 else
221 {
222 /* Find the last but one destination to copy */
223 int i = dev->top-1; /* i = the one on entry (i.e. the last one) */
224 fz_draw_state *prev = state;
225 while (i > 0)
226 {
227 prev = &dev->stack[--i];
228 if (prev->dest != state->dest)
229 break;
230 }
231 if (prev->dest)
232 {
233 fz_copy_pixmap_rect(ctx, state[1].dest, prev->dest, bbox, dev->default_cs);
234 if (state[1].group_alpha)
235 {
236 if (prev->group_alpha)
237 fz_copy_pixmap_rect(ctx, state[1].group_alpha, prev->group_alpha, ga_bbox, dev->default_cs);
238 else
239 fz_clear_pixmap(ctx, state[1].group_alpha);
240 }
241 }
242 else
243 {
244 fz_clear_pixmap(ctx, state[1].dest);
245 if (state[1].group_alpha)
246 fz_clear_pixmap(ctx, state[1].group_alpha);
247 }
248 }
249
250 /* Knockout groups (and only knockout groups) rely on shape */
251 state[1].shape = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
252 fz_clear_pixmap(ctx, state[1].shape);
253
254 #ifdef DUMP_GROUP_BLENDS
255 dump_spaces(dev->top-1, "");
256 fz_dump_blend(ctx, "Knockout begin: background is ", state[1].dest);
257 if (state[1].shape)
258 fz_dump_blend(ctx, "/S=", state[1].shape);
259 if (state[1].group_alpha)
260 fz_dump_blend(ctx, "/GA=", state[1].group_alpha);
261 printf("\n");
262 #endif
263
264 state[1].scissor = bbox;
265 state[1].blendmode &= ~(FZ_BLEND_MODEMASK | FZ_BLEND_ISOLATED);
266
267 return &state[1];
268 }
269
fz_knockout_end(fz_context * ctx,fz_draw_device * dev)270 static void fz_knockout_end(fz_context *ctx, fz_draw_device *dev)
271 {
272 fz_draw_state *state;
273
274 if (dev->top == 0)
275 fz_throw(ctx, FZ_ERROR_GENERIC, "unexpected knockout end");
276
277 state = pop_stack(ctx, dev, "knockout");
278 if ((state[0].blendmode & FZ_BLEND_KNOCKOUT) == 0)
279 return;
280
281 assert((state[1].blendmode & FZ_BLEND_ISOLATED) == 0);
282 assert((state[1].blendmode & FZ_BLEND_MODEMASK) == 0);
283 assert(state[1].shape);
284
285 #ifdef DUMP_GROUP_BLENDS
286 dump_spaces(dev->top, "");
287 fz_dump_blend(ctx, "Knockout end: blending ", state[1].dest);
288 if (state[1].shape)
289 fz_dump_blend(ctx, "/S=", state[1].shape);
290 if (state[1].group_alpha)
291 fz_dump_blend(ctx, "/GA=", state[1].group_alpha);
292 fz_dump_blend(ctx, " onto ", state[0].dest);
293 if (state[0].shape)
294 fz_dump_blend(ctx, "/S=", state[0].shape);
295 if (state[0].group_alpha)
296 fz_dump_blend(ctx, "/GA=", state[0].group_alpha);
297 if ((state->blendmode & FZ_BLEND_MODEMASK) != 0)
298 printf(" (blend %d)", state->blendmode & FZ_BLEND_MODEMASK);
299 if ((state->blendmode & FZ_BLEND_ISOLATED) != 0)
300 printf(" (isolated)");
301 printf(" (knockout)");
302 #endif
303
304 fz_blend_pixmap_knockout(ctx, state[0].dest, state[1].dest, state[1].shape);
305 fz_drop_pixmap(ctx, state[1].dest);
306 state[1].dest = NULL;
307
308 if (state[1].group_alpha && state[0].group_alpha != state[1].group_alpha)
309 {
310 if (state[0].group_alpha)
311 fz_blend_pixmap_knockout(ctx, state[0].group_alpha, state[1].group_alpha, state[1].shape);
312 fz_drop_pixmap(ctx, state[1].group_alpha);
313 state[1].group_alpha = NULL;
314 }
315
316 if (state[0].shape != state[1].shape)
317 {
318 if (state[0].shape)
319 fz_paint_pixmap(state[0].shape, state[1].shape, 255);
320 fz_drop_pixmap(ctx, state[1].shape);
321 state[1].shape = NULL;
322 }
323
324 #ifdef DUMP_GROUP_BLENDS
325 fz_dump_blend(ctx, " to get ", state[0].dest);
326 if (state[0].shape)
327 fz_dump_blend(ctx, "/S=", state[0].shape);
328 if (state[0].group_alpha)
329 fz_dump_blend(ctx, "/GA=", state[0].group_alpha);
330 printf("\n");
331 #endif
332 }
333
334 static int
colors_supported(fz_context * ctx,fz_colorspace * cs,fz_pixmap * dest)335 colors_supported(fz_context *ctx, fz_colorspace *cs, fz_pixmap *dest)
336 {
337 /* Even if we support separations in the destination, if the color space has CMY or K as one of
338 * its colorants and we are in RGB or Gray we will want to do the tint transform */
339 if (!fz_colorspace_is_subtractive(ctx, dest->colorspace) && fz_colorspace_device_n_has_cmyk(ctx, cs))
340 return 0;
341
342 /* If we have separations then we should support it */
343 if (dest->seps)
344 return 1;
345
346 /* If our destination is CMYK and the source color space is only C, M, Y or K we support it
347 * even if we have no seps */
348 if (fz_colorspace_is_subtractive(ctx, dest->colorspace))
349 {
350 int i, n;
351 if (fz_colorspace_device_n_has_only_cmyk(ctx, cs))
352 return 1;
353
354 n = fz_colorspace_n(ctx, cs);
355 for (i = 0; i < n; i++)
356 {
357 const char *name = fz_colorspace_colorant(ctx, cs, i);
358
359 if (!name)
360 return 0;
361 if (!strcmp(name, "All"))
362 continue;
363 if (!strcmp(name, "Cyan"))
364 continue;
365 if (!strcmp(name, "Magenta"))
366 continue;
367 if (!strcmp(name, "Yellow"))
368 continue;
369 if (!strcmp(name, "Black"))
370 continue;
371 if (!strcmp(name, "None"))
372 continue;
373 return 0;
374 }
375 return 1;
376 }
377
378 return 0;
379 }
380
381 static fz_overprint *
set_op_from_spaces(fz_context * ctx,fz_overprint * op,const fz_pixmap * dest,fz_colorspace * src,int opm)382 set_op_from_spaces(fz_context *ctx, fz_overprint *op, const fz_pixmap *dest, fz_colorspace *src, int opm)
383 {
384 int dn, sn, i, j, dc;
385
386 if (!op)
387 return NULL;
388
389 if (!fz_colorspace_is_subtractive(ctx, src) || !fz_colorspace_is_subtractive(ctx, dest->colorspace))
390 return NULL;
391
392 sn = fz_colorspace_n(ctx, src);
393 dn = dest->n - dest->alpha;
394 dc = dn - dest->s;
395
396 /* If a source colorant is not mentioned in the destination
397 * colorants (either process or spots), then it will be mapped
398 * to process colorants. In this case, the process colorants
399 * can never be protected.
400 */
401 for (j = 0; j < sn; j++)
402 {
403 /* Run through the colorants looking for one that isn't mentioned.
404 * i.e. continue if we we find one, break if not. */
405 const char *sname = fz_colorspace_colorant(ctx, src, j);
406 if (!sname)
407 break;
408 if (!strcmp(sname, "All") || !strcmp(sname, "None"))
409 continue;
410 for (i = 0; i < dc; i++)
411 {
412 const char *name = fz_colorspace_colorant(ctx, dest->colorspace, i);
413 if (!name)
414 continue;
415 if (!strcmp(name, sname))
416 break;
417 }
418 if (i != dc)
419 continue;
420 for (; i < dn; i++)
421 {
422 const char *name = fz_separation_name(ctx, dest->seps, i - dc);
423 if (!name)
424 continue;
425 if (!strcmp(name, sname))
426 break;
427 }
428 if (i == dn)
429 {
430 /* This source colorant wasn't mentioned */
431 break;
432 }
433 }
434 if (j == sn)
435 {
436 /* We did not find any source colorants that weren't mentioned, so
437 * process colorants might not be touched... */
438 for (i = 0; i < dc; i++)
439 {
440 const char *name = fz_colorspace_colorant(ctx, dest->colorspace, i);
441
442 for (j = 0; j < sn; j++)
443 {
444 const char *sname = fz_colorspace_colorant(ctx, src, j);
445 if (!name || !sname)
446 continue;
447 if (!strcmp(name, sname))
448 break;
449 if (!strcmp(sname, "All"))
450 break;
451 }
452 if (j == sn)
453 fz_set_overprint(op, i);
454 }
455 }
456 for (i = dc; i < dn; i++)
457 {
458 const char *name = fz_separation_name(ctx, dest->seps, i - dc);
459
460 for (j = 0; j < sn; j++)
461 {
462 const char *sname = fz_colorspace_colorant(ctx, src, j);
463 if (!name || !sname)
464 continue;
465 if (!strcmp(name, sname))
466 break;
467 if (!strcmp(sname, "All"))
468 break;
469 }
470 if (j == sn)
471 fz_set_overprint(op, i);
472 }
473
474 return op;
475 }
476
477 static fz_overprint *
resolve_color(fz_context * ctx,fz_overprint * op,const float * color,fz_colorspace * colorspace,float alpha,fz_color_params color_params,unsigned char * colorbv,fz_pixmap * dest)478 resolve_color(fz_context *ctx,
479 fz_overprint *op,
480 const float *color,
481 fz_colorspace *colorspace,
482 float alpha,
483 fz_color_params color_params,
484 unsigned char *colorbv,
485 fz_pixmap *dest)
486 {
487 float colorfv[FZ_MAX_COLORS];
488 int i;
489 int n = dest->n - dest->alpha;
490 fz_colorspace *model = dest->colorspace;
491 int devn, devgray;
492 int effective_opm;
493
494 if (colorspace == NULL && model != NULL)
495 fz_throw(ctx, FZ_ERROR_GENERIC, "color destination requires source color");
496
497 effective_opm = color_params.opm;
498 devn = fz_colorspace_is_device_n(ctx, colorspace);
499 devgray = fz_colorspace_is_device_gray(ctx, colorspace);
500
501 /* We can only overprint when enabled, and when we are in a subtractive colorspace */
502 if (color_params.op == 0 || !fz_colorspace_is_subtractive(ctx, dest->colorspace))
503 op = NULL;
504
505 else if (devgray)
506 {
507 /* Device Gray is additive, but seems to still be
508 * counted for overprint (see
509 * Ghent_V3.0/030_Gray_K_black_OP_x1a.pdf 030.pdf). */
510 }
511
512 /* If we are in a CMYK space (i.e. not a devn one, given we know we are subtractive at this point),
513 * then we only adhere to overprint mode if it's the same space as the destination. */
514 /* FIXME: Possibly we need a better equivalency test here. */
515 else if (!devn && colorspace != dest->colorspace)
516 {
517 effective_opm = 0;
518 }
519
520 if (n == 0)
521 i = 0;
522 else if (devn && colors_supported(ctx, colorspace, dest))
523 {
524 fz_convert_separation_colors(ctx, colorspace, color, dest->seps, dest->colorspace, colorfv, color_params);
525 for (i = 0; i < n; i++)
526 colorbv[i] = colorfv[i] * 255;
527 op = set_op_from_spaces(ctx, op, dest, colorspace, effective_opm);
528 }
529 else
530 {
531 int c = n - dest->s;
532 fz_convert_color(ctx, colorspace, color, dest->colorspace, colorfv, NULL, color_params);
533 for (i = 0; i < c; i++)
534 colorbv[i] = colorfv[i] * 255;
535 for (; i < n; i++)
536 {
537 colorfv[i] = 0;
538 colorbv[i] = 0;
539 }
540 }
541 colorbv[i] = alpha * 255;
542
543 /* op && !devn => overprinting in cmyk or devicegray. */
544 if (op && !devn)
545 {
546 /* We are overprinting, so protect all spots. */
547 for (i = 4; i < n; i++)
548 fz_set_overprint(op, i);
549 /* If OPM, then protect all components for which the color values are zero.
550 * (but only if we're in devicecmyk). */
551 if (effective_opm == 1 && colorspace != fz_device_gray(ctx))
552 for (i = 0; i < n; i++)
553 if (colorfv[i] == 0)
554 fz_set_overprint(op, i);
555 }
556
557 return op;
558 }
559
560 static fz_draw_state *
push_group_for_separations(fz_context * ctx,fz_draw_device * dev,fz_color_params color_params,fz_default_colorspaces * default_cs)561 push_group_for_separations(fz_context *ctx, fz_draw_device *dev, fz_color_params color_params, fz_default_colorspaces *default_cs)
562 {
563 fz_separations *clone = fz_clone_separations_for_overprint(ctx, dev->stack[0].dest->seps);
564 fz_colorspace *oi = fz_default_output_intent(ctx, default_cs);
565 fz_colorspace *dcs = fz_device_cmyk(ctx);
566
567 /* Pick sep target CMYK based upon proof and output intent settings. Priority
568 * is oi, proof, devicecmyk. */
569 if (dev->proof_cs)
570 {
571 dcs = dev->proof_cs;
572 }
573
574 if (oi)
575 {
576 dcs = oi;
577 }
578
579 /* Not needed if dest has the seps, and we are not using a proof or the target is the same as the proof and we don't have an oi or the target is the same as the oi */
580 if ((clone == dev->stack[0].dest->seps) && (dev->proof_cs == NULL || dev->proof_cs == dev->stack[0].dest->colorspace) && (oi == NULL || oi == dev->stack[0].dest->colorspace))
581 {
582 fz_drop_separations(ctx, clone);
583 dev->resolve_spots = 0;
584 return &dev->stack[0];
585 }
586
587 /* Make a new pixmap to render to. */
588 fz_try(ctx)
589 {
590 push_stack(ctx, dev, "separations");
591 dev->stack[1].dest = fz_clone_pixmap_area_with_different_seps(ctx, dev->stack[0].dest, &dev->stack[0].scissor, dcs, clone, color_params, default_cs);
592 }
593 fz_always(ctx)
594 fz_drop_separations(ctx, clone);
595 fz_catch(ctx)
596 fz_rethrow(ctx);
597
598 return &dev->stack[1];
599 }
600
601 static void
fz_draw_fill_path(fz_context * ctx,fz_device * devp,const fz_path * path,int even_odd,fz_matrix in_ctm,fz_colorspace * colorspace_in,const float * color,float alpha,fz_color_params color_params)602 fz_draw_fill_path(fz_context *ctx, fz_device *devp, const fz_path *path, int even_odd, fz_matrix in_ctm,
603 fz_colorspace *colorspace_in, const float *color, float alpha, fz_color_params color_params)
604 {
605 fz_draw_device *dev = (fz_draw_device*)devp;
606 fz_matrix ctm = fz_concat(in_ctm, dev->transform);
607 fz_rasterizer *rast = dev->rast;
608 fz_colorspace *colorspace = fz_default_colorspace(ctx, dev->default_cs, colorspace_in);
609 float expansion = fz_matrix_expansion(ctm);
610 float flatness;
611 unsigned char colorbv[FZ_MAX_COLORS + 1];
612 fz_irect bbox;
613 fz_draw_state *state = &dev->stack[dev->top];
614 fz_overprint op = { { 0 } };
615 fz_overprint *eop;
616
617 if (dev->top == 0 && dev->resolve_spots)
618 state = push_group_for_separations(ctx, dev, color_params, dev->default_cs);
619
620 if (expansion < FLT_EPSILON)
621 expansion = 1;
622 flatness = 0.3f / expansion;
623 if (flatness < 0.001f)
624 flatness = 0.001f;
625
626 bbox = fz_intersect_irect(fz_pixmap_bbox(ctx, state->dest), state->scissor);
627 if (fz_flatten_fill_path(ctx, rast, path, ctm, flatness, &bbox, &bbox))
628 return;
629
630 if (state->blendmode & FZ_BLEND_KNOCKOUT)
631 state = fz_knockout_begin(ctx, dev);
632
633 eop = resolve_color(ctx, &op, color, colorspace, alpha, color_params, colorbv, state->dest);
634
635 fz_convert_rasterizer(ctx, rast, even_odd, state->dest, colorbv, eop);
636 if (state->shape)
637 {
638 if (!rast->fns.reusable)
639 fz_flatten_fill_path(ctx, rast, path, ctm, flatness, &bbox, NULL);
640
641 colorbv[0] = 255;
642 fz_convert_rasterizer(ctx, rast, even_odd, state->shape, colorbv, 0);
643 }
644 if (state->group_alpha)
645 {
646 if (!rast->fns.reusable)
647 fz_flatten_fill_path(ctx, rast, path, ctm, flatness, &bbox, NULL);
648
649 colorbv[0] = alpha * 255;
650 fz_convert_rasterizer(ctx, rast, even_odd, state->group_alpha, colorbv, 0);
651 }
652
653 if (state->blendmode & FZ_BLEND_KNOCKOUT)
654 fz_knockout_end(ctx, dev);
655 }
656
657 static void
fz_draw_stroke_path(fz_context * ctx,fz_device * devp,const fz_path * path,const fz_stroke_state * stroke,fz_matrix in_ctm,fz_colorspace * colorspace_in,const float * color,float alpha,fz_color_params color_params)658 fz_draw_stroke_path(fz_context *ctx, fz_device *devp, const fz_path *path, const fz_stroke_state *stroke, fz_matrix in_ctm,
659 fz_colorspace *colorspace_in, const float *color, float alpha, fz_color_params color_params)
660 {
661 fz_draw_device *dev = (fz_draw_device*)devp;
662 fz_matrix ctm = fz_concat(in_ctm, dev->transform);
663 fz_rasterizer *rast = dev->rast;
664 fz_colorspace *colorspace = fz_default_colorspace(ctx, dev->default_cs, colorspace_in);
665 float expansion = fz_matrix_expansion(ctm);
666 float flatness;
667 float linewidth = stroke->linewidth;
668 unsigned char colorbv[FZ_MAX_COLORS + 1];
669 fz_irect bbox;
670 float aa_level = 2.0f/(fz_rasterizer_graphics_aa_level(rast)+2);
671 fz_draw_state *state = &dev->stack[dev->top];
672 float mlw = fz_rasterizer_graphics_min_line_width(rast);
673 fz_overprint op = { { 0 } };
674 fz_overprint *eop;
675
676 if (dev->top == 0 && dev->resolve_spots)
677 state = push_group_for_separations(ctx, dev, color_params, dev->default_cs);
678
679 if (mlw > aa_level)
680 aa_level = mlw;
681 if (expansion < FLT_EPSILON)
682 expansion = 1;
683 if (linewidth * expansion < aa_level)
684 linewidth = aa_level / expansion;
685 flatness = 0.3f / expansion;
686 if (flatness < 0.001f)
687 flatness = 0.001f;
688
689 bbox = fz_intersect_irect(fz_pixmap_bbox_no_ctx(state->dest), state->scissor);
690 if (fz_flatten_stroke_path(ctx, rast, path, stroke, ctm, flatness, linewidth, &bbox, &bbox))
691 return;
692
693 if (state->blendmode & FZ_BLEND_KNOCKOUT)
694 state = fz_knockout_begin(ctx, dev);
695
696 eop = resolve_color(ctx, &op, color, colorspace, alpha, color_params, colorbv, state->dest);
697
698 #ifdef DUMP_GROUP_BLENDS
699 dump_spaces(dev->top, "");
700 fz_dump_blend(ctx, "Before stroke ", state->dest);
701 if (state->shape)
702 fz_dump_blend(ctx, "/S=", state->shape);
703 if (state->group_alpha)
704 fz_dump_blend(ctx, "/GA=", state->group_alpha);
705 printf("\n");
706 #endif
707 fz_convert_rasterizer(ctx, rast, 0, state->dest, colorbv, eop);
708 if (state->shape)
709 {
710 if (!rast->fns.reusable)
711 (void)fz_flatten_stroke_path(ctx, rast, path, stroke, ctm, flatness, linewidth, &bbox, NULL);
712
713 colorbv[0] = 255;
714 fz_convert_rasterizer(ctx, rast, 0, state->shape, colorbv, 0);
715 }
716 if (state->group_alpha)
717 {
718 if (!rast->fns.reusable)
719 (void)fz_flatten_stroke_path(ctx, rast, path, stroke, ctm, flatness, linewidth, &bbox, NULL);
720
721 colorbv[0] = 255 * alpha;
722 fz_convert_rasterizer(ctx, rast, 0, state->group_alpha, colorbv, 0);
723 }
724
725 #ifdef DUMP_GROUP_BLENDS
726 dump_spaces(dev->top, "");
727 fz_dump_blend(ctx, "After stroke ", state->dest);
728 if (state->shape)
729 fz_dump_blend(ctx, "/S=", state->shape);
730 if (state->group_alpha)
731 fz_dump_blend(ctx, "/GA=", state->group_alpha);
732 printf("\n");
733 #endif
734
735 if (state->blendmode & FZ_BLEND_KNOCKOUT)
736 fz_knockout_end(ctx, dev);
737 }
738
739 static void
fz_draw_clip_path(fz_context * ctx,fz_device * devp,const fz_path * path,int even_odd,fz_matrix in_ctm,fz_rect scissor)740 fz_draw_clip_path(fz_context *ctx, fz_device *devp, const fz_path *path, int even_odd, fz_matrix in_ctm, fz_rect scissor)
741 {
742 fz_draw_device *dev = (fz_draw_device*)devp;
743 fz_matrix ctm = fz_concat(in_ctm, dev->transform);
744 fz_rasterizer *rast = dev->rast;
745
746 float expansion = fz_matrix_expansion(ctm);
747 float flatness;
748 fz_irect bbox;
749 fz_draw_state *state = &dev->stack[dev->top];
750 fz_colorspace *model;
751
752 if (dev->top == 0 && dev->resolve_spots)
753 state = push_group_for_separations(ctx, dev, fz_default_color_params /* FIXME */, dev->default_cs);
754
755 if (expansion < FLT_EPSILON)
756 expansion = 1;
757 flatness = 0.3f / expansion;
758 if (flatness < 0.001f)
759 flatness = 0.001f;
760
761 state = push_stack(ctx, dev, "clip path");
762
763 model = state->dest->colorspace;
764
765 if (!fz_is_infinite_rect(scissor))
766 {
767 bbox = fz_irect_from_rect(fz_transform_rect(scissor, dev->transform));
768 bbox = fz_intersect_irect(bbox, fz_pixmap_bbox(ctx, state->dest));
769 bbox = fz_intersect_irect(bbox, state->scissor);
770 }
771 else
772 {
773 bbox = fz_intersect_irect(fz_pixmap_bbox(ctx, state->dest), state->scissor);
774 }
775
776 if (fz_flatten_fill_path(ctx, rast, path, ctm, flatness, &bbox, &bbox) || fz_is_rect_rasterizer(ctx, rast))
777 {
778 state[1].scissor = bbox;
779 state[1].mask = NULL;
780 #ifdef DUMP_GROUP_BLENDS
781 dump_spaces(dev->top-1, "Clip (rectangular) begin\n");
782 #endif
783 return;
784 }
785
786 state[1].mask = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
787 fz_clear_pixmap(ctx, state[1].mask);
788 state[1].dest = fz_new_pixmap_with_bbox(ctx, model, bbox, state[0].dest->seps, state[0].dest->alpha);
789 fz_copy_pixmap_rect(ctx, state[1].dest, state[0].dest, bbox, dev->default_cs);
790 if (state[1].shape)
791 {
792 state[1].shape = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
793 fz_clear_pixmap(ctx, state[1].shape);
794 }
795 if (state[1].group_alpha)
796 {
797 state[1].group_alpha = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
798 fz_clear_pixmap(ctx, state[1].group_alpha);
799 }
800
801 fz_convert_rasterizer(ctx, rast, even_odd, state[1].mask, NULL, 0);
802
803 state[1].scissor = bbox;
804
805 #ifdef DUMP_GROUP_BLENDS
806 dump_spaces(dev->top-1, "Clip (non-rectangular) begin\n");
807 #endif
808 }
809
810 static void
fz_draw_clip_stroke_path(fz_context * ctx,fz_device * devp,const fz_path * path,const fz_stroke_state * stroke,fz_matrix in_ctm,fz_rect scissor)811 fz_draw_clip_stroke_path(fz_context *ctx, fz_device *devp, const fz_path *path, const fz_stroke_state *stroke, fz_matrix in_ctm, fz_rect scissor)
812 {
813 fz_draw_device *dev = (fz_draw_device*)devp;
814 fz_matrix ctm = fz_concat(in_ctm, dev->transform);
815 fz_rasterizer *rast = dev->rast;
816
817 float expansion = fz_matrix_expansion(ctm);
818 float flatness;
819 float linewidth = stroke->linewidth;
820 fz_irect bbox;
821 fz_draw_state *state = &dev->stack[dev->top];
822 fz_colorspace *model;
823 float aa_level = 2.0f/(fz_rasterizer_graphics_aa_level(rast)+2);
824 float mlw = fz_rasterizer_graphics_min_line_width(rast);
825
826 if (dev->top == 0 && dev->resolve_spots)
827 state = push_group_for_separations(ctx, dev, fz_default_color_params /* FIXME */, dev->default_cs);
828
829 if (mlw > aa_level)
830 aa_level = mlw;
831 if (expansion < FLT_EPSILON)
832 expansion = 1;
833 if (linewidth * expansion < aa_level)
834 linewidth = aa_level / expansion;
835 flatness = 0.3f / expansion;
836 if (flatness < 0.001f)
837 flatness = 0.001f;
838
839 state = push_stack(ctx, dev, "clip stroke");
840
841 model = state->dest->colorspace;
842
843 if (!fz_is_infinite_rect(scissor))
844 {
845 bbox = fz_irect_from_rect(fz_transform_rect(scissor, dev->transform));
846 bbox = fz_intersect_irect(bbox, fz_pixmap_bbox(ctx, state->dest));
847 bbox = fz_intersect_irect(bbox, state->scissor);
848 }
849 else
850 {
851 bbox = fz_intersect_irect(fz_pixmap_bbox(ctx, state->dest), state->scissor);
852 }
853
854 if (fz_flatten_stroke_path(ctx, rast, path, stroke, ctm, flatness, linewidth, &bbox, &bbox))
855 {
856 state[1].scissor = bbox;
857 state[1].mask = NULL;
858 #ifdef DUMP_GROUP_BLENDS
859 dump_spaces(dev->top-1, "Clip (stroke, empty) begin\n");
860 #endif
861 return;
862 }
863
864 state[1].mask = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
865 fz_clear_pixmap(ctx, state[1].mask);
866 /* When there is no alpha in the current destination (state[0].dest->alpha == 0)
867 * we have a choice. We can either create the new destination WITH alpha, or
868 * we can copy the old pixmap contents in. We opt for the latter here, but
869 * may want to revisit this decision in the future. */
870 state[1].dest = fz_new_pixmap_with_bbox(ctx, model, bbox, state[0].dest->seps, state[0].dest->alpha);
871 if (state[0].dest->alpha)
872 fz_clear_pixmap(ctx, state[1].dest);
873 else
874 fz_copy_pixmap_rect(ctx, state[1].dest, state[0].dest, bbox, dev->default_cs);
875 if (state->shape)
876 {
877 state[1].shape = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
878 fz_clear_pixmap(ctx, state[1].shape);
879 }
880 if (state->group_alpha)
881 {
882 state[1].group_alpha = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
883 fz_clear_pixmap(ctx, state[1].group_alpha);
884 }
885
886 fz_convert_rasterizer(ctx, rast, 0, state[1].mask, NULL, 0);
887
888 state[1].blendmode |= FZ_BLEND_ISOLATED;
889 state[1].scissor = bbox;
890
891 #ifdef DUMP_GROUP_BLENDS
892 dump_spaces(dev->top-1, "Clip (stroke) begin\n");
893 #endif
894 }
895
896 static void
draw_glyph(unsigned char * colorbv,fz_pixmap * dst,fz_glyph * glyph,int xorig,int yorig,const fz_irect * scissor,fz_overprint * eop)897 draw_glyph(unsigned char *colorbv, fz_pixmap *dst, fz_glyph *glyph,
898 int xorig, int yorig, const fz_irect *scissor, fz_overprint *eop)
899 {
900 unsigned char *dp;
901 fz_irect bbox;
902 int x, y, w, h;
903 int skip_x, skip_y;
904 fz_pixmap *msk;
905
906 bbox = fz_glyph_bbox_no_ctx(glyph);
907 bbox = fz_translate_irect(bbox, xorig, yorig);
908 bbox = fz_intersect_irect(bbox, *scissor); /* scissor < dst */
909 bbox = fz_intersect_irect(bbox, fz_pixmap_bbox_no_ctx(dst));
910
911 if (fz_is_empty_irect(bbox))
912 return;
913
914 x = bbox.x0;
915 y = bbox.y0;
916 w = bbox.x1 - bbox.x0;
917 h = bbox.y1 - bbox.y0;
918
919 skip_x = x - glyph->x - xorig;
920 skip_y = y - glyph->y - yorig;
921
922 msk = glyph->pixmap;
923 dp = dst->samples + (unsigned int)((y - dst->y) * dst->stride + (x - dst->x) * dst->n);
924 if (msk == NULL)
925 {
926 fz_paint_glyph(colorbv, dst, dp, glyph, w, h, skip_x, skip_y, eop);
927 }
928 else
929 {
930 unsigned char *mp = msk->samples + skip_y * msk->stride + skip_x;
931 int da = dst->alpha;
932
933 if (dst->colorspace)
934 {
935 fz_span_color_painter_t *fn;
936
937 fn = fz_get_span_color_painter(dst->n, da, colorbv, eop);
938 assert(fn);
939 if (fn == NULL)
940 return;
941 while (h--)
942 {
943 (*fn)(dp, mp, dst->n, w, colorbv, da, eop);
944 dp += dst->stride;
945 mp += msk->stride;
946 }
947 }
948 else
949 {
950 fz_span_painter_t *fn;
951
952 fn = fz_get_span_painter(da, 1, 0, 255, eop);
953 assert(fn);
954 if (fn == NULL)
955 return;
956 while (h--)
957 {
958 (*fn)(dp, da, mp, 1, 0, w, 255, eop);
959 dp += dst->stride;
960 mp += msk->stride;
961 }
962 }
963 }
964 }
965
966 static void
fz_draw_fill_text(fz_context * ctx,fz_device * devp,const fz_text * text,fz_matrix in_ctm,fz_colorspace * colorspace_in,const float * color,float alpha,fz_color_params color_params)967 fz_draw_fill_text(fz_context *ctx, fz_device *devp, const fz_text *text, fz_matrix in_ctm,
968 fz_colorspace *colorspace_in, const float *color, float alpha, fz_color_params color_params)
969 {
970 fz_draw_device *dev = (fz_draw_device*)devp;
971 fz_matrix ctm = fz_concat(in_ctm, dev->transform);
972 fz_draw_state *state = &dev->stack[dev->top];
973 fz_colorspace *model = state->dest->colorspace;
974 unsigned char colorbv[FZ_MAX_COLORS + 1];
975 unsigned char shapebv, shapebva;
976 fz_text_span *span;
977 int i;
978 fz_colorspace *colorspace = NULL;
979 fz_rasterizer *rast = dev->rast;
980 fz_overprint op = { { 0 } };
981 fz_overprint *eop;
982
983 if (dev->top == 0 && dev->resolve_spots)
984 state = push_group_for_separations(ctx, dev, color_params, dev->default_cs);
985
986 if (colorspace_in)
987 colorspace = fz_default_colorspace(ctx, dev->default_cs, colorspace_in);
988
989 if (colorspace == NULL && model != NULL)
990 fz_throw(ctx, FZ_ERROR_GENERIC, "color destination requires source color");
991
992 if (state->blendmode & FZ_BLEND_KNOCKOUT)
993 state = fz_knockout_begin(ctx, dev);
994
995 eop = resolve_color(ctx, &op, color, colorspace, alpha, color_params, colorbv, state->dest);
996 shapebv = 255;
997 shapebva = 255 * alpha;
998
999 for (span = text->head; span; span = span->next)
1000 {
1001 fz_matrix tm, trm;
1002 fz_glyph *glyph;
1003 int gid;
1004
1005 tm = span->trm;
1006
1007 for (i = 0; i < span->len; i++)
1008 {
1009 gid = span->items[i].gid;
1010 if (gid < 0)
1011 continue;
1012
1013 tm.e = span->items[i].x;
1014 tm.f = span->items[i].y;
1015 trm = fz_concat(tm, ctm);
1016
1017 glyph = fz_render_glyph(ctx, span->font, gid, &trm, model, &state->scissor, state->dest->alpha, fz_rasterizer_text_aa_level(rast));
1018 if (glyph)
1019 {
1020 fz_pixmap *pixmap = glyph->pixmap;
1021 int x = floorf(trm.e);
1022 int y = floorf(trm.f);
1023 if (pixmap == NULL || pixmap->n == 1)
1024 {
1025 draw_glyph(colorbv, state->dest, glyph, x, y, &state->scissor, eop);
1026 if (state->shape)
1027 draw_glyph(&shapebv, state->shape, glyph, x, y, &state->scissor, 0);
1028 if (state->group_alpha)
1029 draw_glyph(&shapebva, state->group_alpha, glyph, x, y, &state->scissor, 0);
1030 }
1031 else
1032 {
1033 fz_matrix mat;
1034 mat.a = pixmap->w; mat.b = mat.c = 0; mat.d = pixmap->h;
1035 mat.e = x + pixmap->x; mat.f = y + pixmap->y;
1036 fz_paint_image(ctx, state->dest, &state->scissor, state->shape, state->group_alpha, pixmap, mat, alpha * 255, !(devp->hints & FZ_DONT_INTERPOLATE_IMAGES), devp->flags & FZ_DEVFLAG_GRIDFIT_AS_TILED, eop);
1037 }
1038 fz_drop_glyph(ctx, glyph);
1039 }
1040 else
1041 {
1042 fz_path *path = fz_outline_glyph(ctx, span->font, gid, tm);
1043 if (path)
1044 {
1045 fz_draw_fill_path(ctx, devp, path, 0, in_ctm, colorspace, color, alpha, color_params);
1046 fz_drop_path(ctx, path);
1047 }
1048 else
1049 {
1050 fz_warn(ctx, "cannot render glyph");
1051 }
1052 }
1053 }
1054 }
1055
1056 if (state->blendmode & FZ_BLEND_KNOCKOUT)
1057 fz_knockout_end(ctx, dev);
1058 }
1059
1060 static void
fz_draw_stroke_text(fz_context * ctx,fz_device * devp,const fz_text * text,const fz_stroke_state * stroke,fz_matrix in_ctm,fz_colorspace * colorspace_in,const float * color,float alpha,fz_color_params color_params)1061 fz_draw_stroke_text(fz_context *ctx, fz_device *devp, const fz_text *text, const fz_stroke_state *stroke,
1062 fz_matrix in_ctm, fz_colorspace *colorspace_in, const float *color, float alpha, fz_color_params color_params)
1063 {
1064 fz_draw_device *dev = (fz_draw_device*)devp;
1065 fz_matrix ctm = fz_concat(in_ctm, dev->transform);
1066 fz_draw_state *state = &dev->stack[dev->top];
1067 unsigned char colorbv[FZ_MAX_COLORS + 1];
1068 unsigned char solid = 255;
1069 unsigned char alpha_byte = alpha * 255;
1070 fz_text_span *span;
1071 int i;
1072 fz_colorspace *colorspace = NULL;
1073 int aa = fz_rasterizer_text_aa_level(dev->rast);
1074 fz_overprint op = { { 0 } };
1075 fz_overprint *eop;
1076
1077 if (dev->top == 0 && dev->resolve_spots)
1078 state = push_group_for_separations(ctx, dev, color_params, dev->default_cs);
1079
1080 if (colorspace_in)
1081 colorspace = fz_default_colorspace(ctx, dev->default_cs, colorspace_in);
1082
1083 if (state->blendmode & FZ_BLEND_KNOCKOUT)
1084 state = fz_knockout_begin(ctx, dev);
1085
1086 eop = resolve_color(ctx, &op, color, colorspace, alpha, color_params, colorbv, state->dest);
1087
1088 for (span = text->head; span; span = span->next)
1089 {
1090 fz_matrix tm, trm;
1091 fz_glyph *glyph;
1092 int gid;
1093
1094 tm = span->trm;
1095
1096 for (i = 0; i < span->len; i++)
1097 {
1098 gid = span->items[i].gid;
1099 if (gid < 0)
1100 continue;
1101
1102 tm.e = span->items[i].x;
1103 tm.f = span->items[i].y;
1104 trm = fz_concat(tm, ctm);
1105
1106 glyph = fz_render_stroked_glyph(ctx, span->font, gid, &trm, ctm, stroke, &state->scissor, aa);
1107 if (glyph)
1108 {
1109 fz_pixmap *pixmap = glyph->pixmap;
1110 int x = (int)trm.e;
1111 int y = (int)trm.f;
1112 if (pixmap == NULL || pixmap->n == 1)
1113 {
1114 draw_glyph(colorbv, state->dest, glyph, x, y, &state->scissor, eop);
1115 if (state->shape)
1116 draw_glyph(&solid, state->shape, glyph, x, y, &state->scissor, 0);
1117 if (state->group_alpha)
1118 draw_glyph(&alpha_byte, state->group_alpha, glyph, x, y, &state->scissor, 0);
1119 }
1120 else
1121 {
1122 fz_matrix mat;
1123 mat.a = pixmap->w; mat.b = mat.c = 0; mat.d = pixmap->h;
1124 mat.e = x + pixmap->x; mat.f = y + pixmap->y;
1125 fz_paint_image(ctx, state->dest, &state->scissor, state->shape, state->group_alpha, pixmap, mat, alpha * 255, !(devp->hints & FZ_DONT_INTERPOLATE_IMAGES), devp->flags & FZ_DEVFLAG_GRIDFIT_AS_TILED, eop);
1126 }
1127 fz_drop_glyph(ctx, glyph);
1128 }
1129 else
1130 {
1131 fz_path *path = fz_outline_glyph(ctx, span->font, gid, tm);
1132 if (path)
1133 {
1134 fz_draw_stroke_path(ctx, devp, path, stroke, in_ctm, colorspace, color, alpha, color_params);
1135 fz_drop_path(ctx, path);
1136 }
1137 else
1138 {
1139 fz_warn(ctx, "cannot render glyph");
1140 }
1141 }
1142 }
1143 }
1144
1145 if (state->blendmode & FZ_BLEND_KNOCKOUT)
1146 fz_knockout_end(ctx, dev);
1147 }
1148
1149 static void
fz_draw_clip_text(fz_context * ctx,fz_device * devp,const fz_text * text,fz_matrix in_ctm,fz_rect scissor)1150 fz_draw_clip_text(fz_context *ctx, fz_device *devp, const fz_text *text, fz_matrix in_ctm, fz_rect scissor)
1151 {
1152 fz_draw_device *dev = (fz_draw_device*)devp;
1153 fz_matrix ctm = fz_concat(in_ctm, dev->transform);
1154 fz_irect bbox;
1155 fz_matrix tm, trm;
1156 fz_glyph *glyph;
1157 int i, gid;
1158 fz_draw_state *state;
1159 fz_colorspace *model;
1160 fz_text_span *span;
1161 fz_rasterizer *rast = dev->rast;
1162
1163 if (dev->top == 0 && dev->resolve_spots)
1164 state = push_group_for_separations(ctx, dev, fz_default_color_params /* FIXME */, dev->default_cs);
1165
1166 state = push_stack(ctx, dev, "clip text");
1167
1168 model = state->dest->colorspace;
1169
1170 /* make the mask the exact size needed */
1171 bbox = fz_irect_from_rect(fz_bound_text(ctx, text, NULL, ctm));
1172 bbox = fz_intersect_irect(bbox, state->scissor);
1173 if (!fz_is_infinite_rect(scissor))
1174 {
1175 fz_rect tscissor = fz_transform_rect(scissor, dev->transform);
1176 bbox = fz_intersect_irect(bbox, fz_irect_from_rect(tscissor));
1177 }
1178
1179 state[1].mask = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
1180 fz_clear_pixmap(ctx, state[1].mask);
1181 /* When there is no alpha in the current destination (state[0].dest->alpha == 0)
1182 * we have a choice. We can either create the new destination WITH alpha, or
1183 * we can copy the old pixmap contents in. We opt for the latter here, but
1184 * may want to revisit this decision in the future. */
1185 state[1].dest = fz_new_pixmap_with_bbox(ctx, model, bbox, state[0].dest->seps, state[0].dest->alpha);
1186 if (state[0].dest->alpha)
1187 fz_clear_pixmap(ctx, state[1].dest);
1188 else
1189 fz_copy_pixmap_rect(ctx, state[1].dest, state[0].dest, bbox, dev->default_cs);
1190 if (state->shape)
1191 {
1192 state[1].shape = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
1193 fz_clear_pixmap(ctx, state[1].shape);
1194 }
1195 else
1196 state[1].shape = NULL;
1197 if (state->group_alpha)
1198 {
1199 state[1].group_alpha = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
1200 fz_clear_pixmap(ctx, state[1].group_alpha);
1201 }
1202 else
1203 state[1].group_alpha = NULL;
1204
1205 state[1].blendmode |= FZ_BLEND_ISOLATED;
1206 state[1].scissor = bbox;
1207
1208 #ifdef DUMP_GROUP_BLENDS
1209 dump_spaces(dev->top-1, "Clip (text) begin\n");
1210 #endif
1211
1212 if (!fz_is_empty_irect(bbox) && state[1].mask)
1213 {
1214 for (span = text->head; span; span = span->next)
1215 {
1216 tm = span->trm;
1217
1218 for (i = 0; i < span->len; i++)
1219 {
1220 gid = span->items[i].gid;
1221 if (gid < 0)
1222 continue;
1223
1224 tm.e = span->items[i].x;
1225 tm.f = span->items[i].y;
1226 trm = fz_concat(tm, ctm);
1227
1228 glyph = fz_render_glyph(ctx, span->font, gid, &trm, model, &state->scissor, state[1].dest->alpha, fz_rasterizer_text_aa_level(rast));
1229 if (glyph)
1230 {
1231 int x = (int)trm.e;
1232 int y = (int)trm.f;
1233 draw_glyph(NULL, state[1].mask, glyph, x, y, &bbox, 0);
1234 if (state[1].shape)
1235 draw_glyph(NULL, state[1].shape, glyph, x, y, &bbox, 0);
1236 if (state[1].group_alpha)
1237 draw_glyph(NULL, state[1].group_alpha, glyph, x, y, &bbox, 0);
1238 fz_drop_glyph(ctx, glyph);
1239 }
1240 else
1241 {
1242 fz_path *path = fz_outline_glyph(ctx, span->font, gid, tm);
1243 if (path)
1244 {
1245 fz_pixmap *old_dest;
1246 float white = 1;
1247
1248 old_dest = state[1].dest;
1249 state[1].dest = state[1].mask;
1250 state[1].mask = NULL;
1251 fz_try(ctx)
1252 {
1253 fz_draw_fill_path(ctx, devp, path, 0, in_ctm, fz_device_gray(ctx), &white, 1, fz_default_color_params);
1254 }
1255 fz_always(ctx)
1256 {
1257 state[1].mask = state[1].dest;
1258 state[1].dest = old_dest;
1259 fz_drop_path(ctx, path);
1260 }
1261 fz_catch(ctx)
1262 {
1263 fz_rethrow(ctx);
1264 }
1265 }
1266 else
1267 {
1268 fz_warn(ctx, "cannot render glyph for clipping");
1269 }
1270 }
1271 }
1272 }
1273 }
1274 }
1275
1276 static void
fz_draw_clip_stroke_text(fz_context * ctx,fz_device * devp,const fz_text * text,const fz_stroke_state * stroke,fz_matrix in_ctm,fz_rect scissor)1277 fz_draw_clip_stroke_text(fz_context *ctx, fz_device *devp, const fz_text *text, const fz_stroke_state *stroke, fz_matrix in_ctm, fz_rect scissor)
1278 {
1279 fz_draw_device *dev = (fz_draw_device*)devp;
1280 fz_matrix ctm = fz_concat(in_ctm, dev->transform);
1281 fz_irect bbox;
1282 fz_pixmap *mask, *dest, *shape, *group_alpha;
1283 fz_matrix tm, trm;
1284 fz_glyph *glyph;
1285 int i, gid;
1286 fz_draw_state *state = push_stack(ctx, dev, "clip stroke text");
1287 fz_colorspace *model = state->dest->colorspace;
1288 fz_text_span *span;
1289 int aa = fz_rasterizer_text_aa_level(dev->rast);
1290
1291 if (dev->top == 0 && dev->resolve_spots)
1292 state = push_group_for_separations(ctx, dev, fz_default_color_params /* FIXME */, dev->default_cs);
1293
1294 /* make the mask the exact size needed */
1295 bbox = fz_irect_from_rect(fz_bound_text(ctx, text, stroke, ctm));
1296 bbox = fz_intersect_irect(bbox, state->scissor);
1297 if (!fz_is_infinite_rect(scissor))
1298 {
1299 fz_rect tscissor = fz_transform_rect(scissor, dev->transform);
1300 bbox = fz_intersect_irect(bbox, fz_irect_from_rect(tscissor));
1301 }
1302
1303 state[1].mask = mask = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
1304 fz_clear_pixmap(ctx, mask);
1305 /* When there is no alpha in the current destination (state[0].dest->alpha == 0)
1306 * we have a choice. We can either create the new destination WITH alpha, or
1307 * we can copy the old pixmap contents in. We opt for the latter here, but
1308 * may want to revisit this decision in the future. */
1309 state[1].dest = dest = fz_new_pixmap_with_bbox(ctx, model, bbox, state[0].dest->seps, state[0].dest->alpha);
1310 if (state[0].dest->alpha)
1311 fz_clear_pixmap(ctx, state[1].dest);
1312 else
1313 fz_copy_pixmap_rect(ctx, state[1].dest, state[0].dest, bbox, dev->default_cs);
1314 if (state->shape)
1315 {
1316 state[1].shape = shape = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
1317 fz_clear_pixmap(ctx, shape);
1318 }
1319 else
1320 shape = state->shape;
1321 if (state->group_alpha)
1322 {
1323 state[1].group_alpha = group_alpha = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
1324 fz_clear_pixmap(ctx, group_alpha);
1325 }
1326 else
1327 group_alpha = NULL;
1328
1329 state[1].blendmode |= FZ_BLEND_ISOLATED;
1330 state[1].scissor = bbox;
1331
1332 #ifdef DUMP_GROUP_BLENDS
1333 dump_spaces(dev->top-1, "Clip (stroke text) begin\n");
1334 #endif
1335
1336 if (!fz_is_empty_irect(bbox))
1337 {
1338 for (span = text->head; span; span = span->next)
1339 {
1340 tm = span->trm;
1341
1342 for (i = 0; i < span->len; i++)
1343 {
1344 gid = span->items[i].gid;
1345 if (gid < 0)
1346 continue;
1347
1348 tm.e = span->items[i].x;
1349 tm.f = span->items[i].y;
1350 trm = fz_concat(tm, ctm);
1351
1352 glyph = fz_render_stroked_glyph(ctx, span->font, gid, &trm, ctm, stroke, &state->scissor, aa);
1353 if (glyph)
1354 {
1355 int x = (int)trm.e;
1356 int y = (int)trm.f;
1357 draw_glyph(NULL, mask, glyph, x, y, &bbox, 0);
1358 if (shape)
1359 draw_glyph(NULL, shape, glyph, x, y, &bbox, 0);
1360 if (group_alpha)
1361 draw_glyph(NULL, group_alpha, glyph, x, y, &bbox, 0);
1362 fz_drop_glyph(ctx, glyph);
1363 }
1364 else
1365 {
1366 fz_path *path = fz_outline_glyph(ctx, span->font, gid, tm);
1367 if (path)
1368 {
1369 fz_pixmap *old_dest;
1370 float white = 1;
1371
1372 state = &dev->stack[dev->top];
1373 old_dest = state[0].dest;
1374 state[0].dest = state[0].mask;
1375 state[0].mask = NULL;
1376 fz_try(ctx)
1377 {
1378 fz_draw_stroke_path(ctx, devp, path, stroke, in_ctm, fz_device_gray(ctx), &white, 1, fz_default_color_params);
1379 }
1380 fz_always(ctx)
1381 {
1382 state[0].mask = state[0].dest;
1383 state[0].dest = old_dest;
1384 fz_drop_path(ctx, path);
1385 }
1386 fz_catch(ctx)
1387 {
1388 fz_rethrow(ctx);
1389 }
1390 }
1391 else
1392 {
1393 fz_warn(ctx, "cannot render glyph for stroked clipping");
1394 }
1395 }
1396 }
1397 }
1398 }
1399 }
1400
1401 static void
fz_draw_ignore_text(fz_context * ctx,fz_device * dev,const fz_text * text,fz_matrix ctm)1402 fz_draw_ignore_text(fz_context *ctx, fz_device *dev, const fz_text *text, fz_matrix ctm)
1403 {
1404 }
1405
1406 static void
fz_draw_fill_shade(fz_context * ctx,fz_device * devp,fz_shade * shade,fz_matrix in_ctm,float alpha,fz_color_params color_params)1407 fz_draw_fill_shade(fz_context *ctx, fz_device *devp, fz_shade *shade, fz_matrix in_ctm, float alpha, fz_color_params color_params)
1408 {
1409 fz_draw_device *dev = (fz_draw_device*)devp;
1410 fz_matrix ctm = fz_concat(in_ctm, dev->transform);
1411 fz_irect bbox, scissor;
1412 fz_pixmap *dest, *shape, *group_alpha;
1413 unsigned char colorbv[FZ_MAX_COLORS + 1];
1414 unsigned char alpha_byte = 255 * alpha;
1415 fz_draw_state *state = &dev->stack[dev->top];
1416 fz_overprint op = { { 0 } };
1417 fz_overprint *eop;
1418 fz_colorspace *colorspace = fz_default_colorspace(ctx, dev->default_cs, shade->colorspace);
1419
1420 if (dev->top == 0 && dev->resolve_spots)
1421 state = push_group_for_separations(ctx, dev, color_params, dev->default_cs);
1422
1423 scissor = state->scissor;
1424 bbox = fz_irect_from_rect(fz_bound_shade(ctx, shade, ctm));
1425 bbox = fz_intersect_irect(bbox, scissor);
1426
1427 if (fz_is_empty_irect(bbox))
1428 return;
1429
1430 if (state->blendmode & FZ_BLEND_KNOCKOUT)
1431 state = fz_knockout_begin(ctx, dev);
1432
1433 fz_var(dest);
1434 fz_var(shape);
1435 fz_var(group_alpha);
1436
1437 dest = state->dest;
1438 shape = state->shape;
1439 group_alpha = state->group_alpha;
1440
1441 fz_try(ctx)
1442 {
1443 if (alpha < 1)
1444 {
1445 dest = fz_new_pixmap_with_bbox(ctx, state->dest->colorspace, bbox, state->dest->seps, state->dest->alpha);
1446 if (state->dest->alpha)
1447 fz_clear_pixmap(ctx, dest);
1448 else
1449 fz_copy_pixmap_rect(ctx, dest, state[0].dest, bbox, dev->default_cs);
1450 if (shape)
1451 {
1452 shape = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
1453 fz_clear_pixmap(ctx, shape);
1454 }
1455 if (group_alpha)
1456 {
1457 group_alpha = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
1458 fz_clear_pixmap(ctx, group_alpha);
1459 }
1460 }
1461
1462 if (shade->use_background)
1463 {
1464 unsigned char *s;
1465 int x, y, n, i;
1466
1467 /* Disable OPM */
1468 color_params.opm = 0;
1469
1470 eop = resolve_color(ctx, &op, shade->background, colorspace, alpha, color_params, colorbv, state->dest);
1471
1472 n = dest->n;
1473 if (fz_overprint_required(eop))
1474 {
1475 for (y = scissor.y0; y < scissor.y1; y++)
1476 {
1477 s = dest->samples + (unsigned int)((y - dest->y) * dest->stride + (scissor.x0 - dest->x) * n);
1478 for (x = scissor.x0; x < scissor.x1; x++)
1479 {
1480 for (i = 0; i < n; i++)
1481 if (fz_overprint_component(eop, i))
1482 *s++ = colorbv[i];
1483 }
1484 }
1485 }
1486 else
1487 {
1488 for (y = scissor.y0; y < scissor.y1; y++)
1489 {
1490 s = dest->samples + (unsigned int)((y - dest->y) * dest->stride + (scissor.x0 - dest->x) * n);
1491 for (x = scissor.x0; x < scissor.x1; x++)
1492 {
1493 for (i = 0; i < n; i++)
1494 *s++ = colorbv[i];
1495 }
1496 }
1497 }
1498 if (shape)
1499 {
1500 for (y = scissor.y0; y < scissor.y1; y++)
1501 {
1502 s = shape->samples + (unsigned int)((y - shape->y) * shape->stride + (scissor.x0 - shape->x));
1503 for (x = scissor.x0; x < scissor.x1; x++)
1504 {
1505 *s++ = 255;
1506 }
1507 }
1508 }
1509 if (group_alpha)
1510 {
1511 for (y = scissor.y0; y < scissor.y1; y++)
1512 {
1513 s = group_alpha->samples + (unsigned int)((y - group_alpha->y) * group_alpha->stride + (scissor.x0 - group_alpha->x));
1514 for (x = scissor.x0; x < scissor.x1; x++)
1515 {
1516 *s++ = alpha_byte;
1517 }
1518 }
1519 }
1520 }
1521
1522 if (color_params.op)
1523 eop = set_op_from_spaces(ctx, &op, dest, colorspace, 0);
1524 else
1525 eop = NULL;
1526
1527 fz_paint_shade(ctx, shade, colorspace, ctm, dest, color_params, bbox, eop);
1528 if (shape)
1529 fz_clear_pixmap_rect_with_value(ctx, shape, 255, bbox);
1530 if (group_alpha)
1531 fz_clear_pixmap_rect_with_value(ctx, group_alpha, 255, bbox);
1532
1533 #ifdef DUMP_GROUP_BLENDS
1534 dump_spaces(dev->top, "");
1535 fz_dump_blend(ctx, "Shade ", dest);
1536 if (shape)
1537 fz_dump_blend(ctx, "/S=", shape);
1538 if (group_alpha)
1539 fz_dump_blend(ctx, "/GA=", group_alpha);
1540 printf("\n");
1541 #endif
1542
1543 if (alpha < 1)
1544 {
1545 /* FIXME: eop */
1546 fz_paint_pixmap(state->dest, dest, alpha * 255);
1547 fz_drop_pixmap(ctx, dest);
1548 dest = NULL;
1549
1550 if (shape)
1551 {
1552 fz_paint_pixmap(state->shape, shape, 255);
1553 fz_drop_pixmap(ctx, shape);
1554 shape = NULL;
1555 }
1556
1557 if (group_alpha)
1558 {
1559 fz_paint_pixmap(state->group_alpha, group_alpha, alpha * 255);
1560 fz_drop_pixmap(ctx, group_alpha);
1561 group_alpha = NULL;
1562 }
1563 }
1564
1565 if (state->blendmode & FZ_BLEND_KNOCKOUT)
1566 fz_knockout_end(ctx, dev);
1567 }
1568 fz_catch(ctx)
1569 {
1570 if (dest != state[0].dest) fz_drop_pixmap(ctx, dest);
1571 if (shape != state[0].shape) fz_drop_pixmap(ctx, shape);
1572 if (group_alpha != state[0].group_alpha) fz_drop_pixmap(ctx, group_alpha);
1573 fz_rethrow(ctx);
1574 }
1575 }
1576
1577 static fz_pixmap *
fz_transform_pixmap(fz_context * ctx,fz_draw_device * dev,const fz_pixmap * image,fz_matrix * ctm,int x,int y,int dx,int dy,int gridfit,const fz_irect * clip)1578 fz_transform_pixmap(fz_context *ctx, fz_draw_device *dev, const fz_pixmap *image, fz_matrix *ctm, int x, int y, int dx, int dy, int gridfit, const fz_irect *clip)
1579 {
1580 fz_pixmap *scaled;
1581
1582 if (ctm->a != 0 && ctm->b == 0 && ctm->c == 0 && ctm->d != 0)
1583 {
1584 /* Unrotated or X-flip or Y-flip or XY-flip */
1585 fz_matrix m = *ctm;
1586 if (gridfit)
1587 {
1588 m = fz_gridfit_matrix(dev->flags & FZ_DEVFLAG_GRIDFIT_AS_TILED, m);
1589 }
1590 scaled = fz_scale_pixmap_cached(ctx, image, m.e, m.f, m.a, m.d, clip, dev->cache_x, dev->cache_y);
1591 if (!scaled)
1592 return NULL;
1593 ctm->a = scaled->w;
1594 ctm->d = scaled->h;
1595 ctm->e = scaled->x;
1596 ctm->f = scaled->y;
1597 return scaled;
1598 }
1599
1600 if (ctm->a == 0 && ctm->b != 0 && ctm->c != 0 && ctm->d == 0)
1601 {
1602 /* Other orthogonal flip/rotation cases */
1603 fz_matrix m = *ctm;
1604 fz_irect rclip;
1605 if (gridfit)
1606 m = fz_gridfit_matrix(dev->flags & FZ_DEVFLAG_GRIDFIT_AS_TILED, m);
1607 if (clip)
1608 {
1609 rclip.x0 = clip->y0;
1610 rclip.y0 = clip->x0;
1611 rclip.x1 = clip->y1;
1612 rclip.y1 = clip->x1;
1613 }
1614 scaled = fz_scale_pixmap_cached(ctx, image, m.f, m.e, m.b, m.c, (clip ? &rclip : NULL), dev->cache_x, dev->cache_y);
1615 if (!scaled)
1616 return NULL;
1617 ctm->b = scaled->w;
1618 ctm->c = scaled->h;
1619 ctm->f = scaled->x;
1620 ctm->e = scaled->y;
1621 return scaled;
1622 }
1623
1624 /* Downscale, non rectilinear case */
1625 if (dx > 0 && dy > 0)
1626 {
1627 scaled = fz_scale_pixmap_cached(ctx, image, 0, 0, dx, dy, NULL, dev->cache_x, dev->cache_y);
1628 return scaled;
1629 }
1630
1631 return NULL;
1632 }
1633
1634 int
fz_default_image_scale(void * arg,int dst_w,int dst_h,int src_w,int src_h)1635 fz_default_image_scale(void *arg, int dst_w, int dst_h, int src_w, int src_h)
1636 {
1637 (void)arg;
1638 return dst_w < src_w && dst_h < src_h;
1639 }
1640
1641 static fz_pixmap *
convert_pixmap_for_painting(fz_context * ctx,fz_pixmap * pixmap,fz_colorspace * model,fz_colorspace * src_cs,fz_pixmap * dest,fz_color_params color_params,fz_draw_device * dev,fz_overprint ** eop)1642 convert_pixmap_for_painting(fz_context *ctx, fz_pixmap *pixmap, fz_colorspace *model, fz_colorspace *src_cs, fz_pixmap *dest, fz_color_params color_params, fz_draw_device *dev, fz_overprint **eop)
1643 {
1644 fz_pixmap *converted;
1645
1646 if (fz_colorspace_is_device_n(ctx, src_cs) && dest->seps)
1647 {
1648 converted = fz_clone_pixmap_area_with_different_seps(ctx, pixmap, NULL, model, dest->seps, color_params, dev->default_cs);
1649 *eop = set_op_from_spaces(ctx, *eop, dest, src_cs, 0);
1650 }
1651 else
1652 {
1653 converted = fz_convert_pixmap(ctx, pixmap, model, NULL, dev->default_cs, color_params, 1);
1654 if (*eop)
1655 {
1656 if (fz_colorspace_type(ctx, model) != FZ_COLORSPACE_CMYK)
1657 {
1658 /* Can only overprint to CMYK based spaces */
1659 *eop = NULL;
1660 }
1661 else if (!fz_colorspace_is_device_n(ctx, pixmap->colorspace))
1662 {
1663 int i;
1664 int n = dest->n - dest->alpha;
1665 for (i = 4; i < n; i++)
1666 fz_set_overprint(*eop, i);
1667 }
1668 else
1669 {
1670 *eop = set_op_from_spaces(ctx, *eop, dest, src_cs, 0);
1671 }
1672 }
1673 }
1674 fz_drop_pixmap(ctx, pixmap);
1675
1676 return converted;
1677 }
1678
1679 static void
fz_draw_fill_image(fz_context * ctx,fz_device * devp,fz_image * image,fz_matrix in_ctm,float alpha,fz_color_params color_params)1680 fz_draw_fill_image(fz_context *ctx, fz_device *devp, fz_image *image, fz_matrix in_ctm, float alpha, fz_color_params color_params)
1681 {
1682 fz_draw_device *dev = (fz_draw_device*)devp;
1683 fz_matrix local_ctm = fz_concat(in_ctm, dev->transform);
1684 fz_pixmap *pixmap;
1685 int after;
1686 int dx, dy;
1687 fz_draw_state *state = &dev->stack[dev->top];
1688 fz_colorspace *model;
1689 fz_irect clip;
1690 fz_matrix inverse;
1691 fz_irect src_area;
1692 fz_colorspace *src_cs;
1693 fz_overprint op = { { 0 } };
1694 fz_overprint *eop = &op;
1695
1696 if (alpha == 0)
1697 return;
1698
1699 if (dev->top == 0 && dev->resolve_spots)
1700 state = push_group_for_separations(ctx, dev, color_params, dev->default_cs);
1701 model = state->dest->colorspace;
1702
1703 clip = fz_intersect_irect(fz_pixmap_bbox(ctx, state->dest), state->scissor);
1704
1705 if (image->w == 0 || image->h == 0)
1706 return;
1707
1708 if (color_params.op == 0)
1709 eop = NULL;
1710
1711 /* ctm maps the image (expressed as the unit square) onto the
1712 * destination device. Reverse that to get a mapping from
1713 * the destination device to the source pixels. */
1714 if (fz_try_invert_matrix(&inverse, local_ctm))
1715 {
1716 /* Not invertible. Could just bail? Use the whole image
1717 * for now. */
1718 src_area.x0 = 0;
1719 src_area.x1 = image->w;
1720 src_area.y0 = 0;
1721 src_area.y1 = image->h;
1722 }
1723 else
1724 {
1725 float exp;
1726 fz_rect rect;
1727 fz_irect sane;
1728 /* We want to scale from image coords, not from unit square */
1729 inverse = fz_post_scale(inverse, image->w, image->h);
1730 /* Are we scaling up or down? exp < 1 means scaling down. */
1731 exp = fz_matrix_max_expansion(inverse);
1732 rect = fz_rect_from_irect(clip);
1733 rect = fz_transform_rect(rect, inverse);
1734 /* Allow for support requirements for scalers. */
1735 rect = fz_expand_rect(rect, fz_max(exp, 1) * 4);
1736 src_area = fz_irect_from_rect(rect);
1737 sane.x0 = 0;
1738 sane.y0 = 0;
1739 sane.x1 = image->w;
1740 sane.y1 = image->h;
1741 src_area = fz_intersect_irect(src_area, sane);
1742 if (fz_is_empty_irect(src_area))
1743 return;
1744 }
1745
1746 pixmap = fz_get_pixmap_from_image(ctx, image, &src_area, &local_ctm, &dx, &dy);
1747 src_cs = fz_default_colorspace(ctx, dev->default_cs, pixmap->colorspace);
1748
1749 /* convert images with more components (cmyk->rgb) before scaling */
1750 /* convert images with fewer components (gray->rgb) after scaling */
1751 /* convert images with expensive colorspace transforms after scaling */
1752
1753 fz_var(pixmap);
1754
1755 fz_try(ctx)
1756 {
1757 int conversion_required = (src_cs != model || state->dest->seps);
1758
1759 if (state->blendmode & FZ_BLEND_KNOCKOUT)
1760 state = fz_knockout_begin(ctx, dev);
1761
1762 switch (fz_colorspace_type(ctx, src_cs))
1763 {
1764 case FZ_COLORSPACE_GRAY:
1765 after = 1;
1766 break;
1767 case FZ_COLORSPACE_INDEXED:
1768 after = 0;
1769 break;
1770 default:
1771 if (fz_colorspace_n(ctx, src_cs) <= fz_colorspace_n(ctx, model))
1772 after = 1;
1773 else
1774 after = 0;
1775 break;
1776 }
1777
1778 if (conversion_required && !after)
1779 pixmap = convert_pixmap_for_painting(ctx, pixmap, model, src_cs, state->dest, color_params, dev, &eop);
1780
1781 if (!(devp->hints & FZ_DONT_INTERPOLATE_IMAGES) && ctx->tuning->image_scale(ctx->tuning->image_scale_arg, dx, dy, pixmap->w, pixmap->h))
1782 {
1783 int gridfit = alpha == 1.0f && !(dev->flags & FZ_DRAWDEV_FLAGS_TYPE3);
1784 fz_pixmap *scaled = fz_transform_pixmap(ctx, dev, pixmap, &local_ctm, state->dest->x, state->dest->y, dx, dy, gridfit, &clip);
1785 if (!scaled)
1786 {
1787 if (dx < 1)
1788 dx = 1;
1789 if (dy < 1)
1790 dy = 1;
1791 scaled = fz_scale_pixmap_cached(ctx, pixmap, pixmap->x, pixmap->y, dx, dy, NULL, dev->cache_x, dev->cache_y);
1792 }
1793 if (scaled)
1794 {
1795 fz_drop_pixmap(ctx, pixmap);
1796 pixmap = scaled;
1797 }
1798 }
1799
1800 if (conversion_required && after)
1801 {
1802 #if FZ_PLOTTERS_RGB
1803 if (state->dest->seps == NULL &&
1804 ((src_cs == fz_device_gray(ctx) && model == fz_device_rgb(ctx)) ||
1805 (src_cs == fz_device_gray(ctx) && model == fz_device_bgr(ctx))))
1806 {
1807 /* We have special case rendering code for gray -> rgb/bgr */
1808 }
1809 else
1810 #endif
1811 pixmap = convert_pixmap_for_painting(ctx, pixmap, model, src_cs, state->dest, color_params, dev, &eop);
1812 }
1813
1814 fz_paint_image(ctx, state->dest, &state->scissor, state->shape, state->group_alpha, pixmap, local_ctm, alpha * 255, !(devp->hints & FZ_DONT_INTERPOLATE_IMAGES), devp->flags & FZ_DEVFLAG_GRIDFIT_AS_TILED, eop);
1815
1816 if (state->blendmode & FZ_BLEND_KNOCKOUT)
1817 fz_knockout_end(ctx, dev);
1818 }
1819 fz_always(ctx)
1820 fz_drop_pixmap(ctx, pixmap);
1821 fz_catch(ctx)
1822 fz_rethrow(ctx);
1823 }
1824
1825 static void
fz_draw_fill_image_mask(fz_context * ctx,fz_device * devp,fz_image * image,fz_matrix in_ctm,fz_colorspace * colorspace_in,const float * color,float alpha,fz_color_params color_params)1826 fz_draw_fill_image_mask(fz_context *ctx, fz_device *devp, fz_image *image, fz_matrix in_ctm,
1827 fz_colorspace *colorspace_in, const float *color, float alpha, fz_color_params color_params)
1828 {
1829 fz_draw_device *dev = (fz_draw_device*)devp;
1830 fz_matrix local_ctm = fz_concat(in_ctm, dev->transform);
1831 unsigned char colorbv[FZ_MAX_COLORS + 1];
1832 fz_pixmap *scaled = NULL;
1833 fz_pixmap *pixmap;
1834 int dx, dy;
1835 fz_draw_state *state = &dev->stack[dev->top];
1836 fz_irect clip;
1837 fz_matrix inverse;
1838 fz_irect src_area;
1839 fz_colorspace *colorspace = NULL;
1840 fz_overprint op = { { 0 } };
1841 fz_overprint *eop;
1842
1843 if (alpha == 0)
1844 return;
1845
1846 if (dev->top == 0 && dev->resolve_spots)
1847 state = push_group_for_separations(ctx, dev, color_params, dev->default_cs);
1848
1849 if (colorspace_in)
1850 colorspace = fz_default_colorspace(ctx, dev->default_cs, colorspace_in);
1851
1852 clip = fz_pixmap_bbox(ctx, state->dest);
1853 clip = fz_intersect_irect(clip, state->scissor);
1854
1855 if (image->w == 0 || image->h == 0)
1856 return;
1857
1858 /* ctm maps the image (expressed as the unit square) onto the
1859 * destination device. Reverse that to get a mapping from
1860 * the destination device to the source pixels. */
1861 if (fz_try_invert_matrix(&inverse, local_ctm))
1862 {
1863 /* Not invertible. Could just bail? Use the whole image
1864 * for now. */
1865 src_area.x0 = 0;
1866 src_area.x1 = image->w;
1867 src_area.y0 = 0;
1868 src_area.y1 = image->h;
1869 }
1870 else
1871 {
1872 float exp;
1873 fz_rect rect;
1874 fz_irect sane;
1875 /* We want to scale from image coords, not from unit square */
1876 inverse = fz_post_scale(inverse, image->w, image->h);
1877 /* Are we scaling up or down? exp < 1 means scaling down. */
1878 exp = fz_matrix_max_expansion(inverse);
1879 rect = fz_rect_from_irect(clip);
1880 rect = fz_transform_rect(rect, inverse);
1881 /* Allow for support requirements for scalers. */
1882 rect = fz_expand_rect(rect, fz_max(exp, 1) * 4);
1883 src_area = fz_irect_from_rect(rect);
1884 sane.x0 = 0;
1885 sane.y0 = 0;
1886 sane.x1 = image->w;
1887 sane.y1 = image->h;
1888 src_area = fz_intersect_irect(src_area, sane);
1889 if (fz_is_empty_irect(src_area))
1890 return;
1891 }
1892
1893 pixmap = fz_get_pixmap_from_image(ctx, image, &src_area, &local_ctm, &dx, &dy);
1894
1895 fz_var(pixmap);
1896
1897 fz_try(ctx)
1898 {
1899 if (state->blendmode & FZ_BLEND_KNOCKOUT)
1900 state = fz_knockout_begin(ctx, dev);
1901
1902 if (!(devp->hints & FZ_DONT_INTERPOLATE_IMAGES) && ctx->tuning->image_scale(ctx->tuning->image_scale_arg, dx, dy, pixmap->w, pixmap->h))
1903 {
1904 int gridfit = alpha == 1.0f && !(dev->flags & FZ_DRAWDEV_FLAGS_TYPE3);
1905 scaled = fz_transform_pixmap(ctx, dev, pixmap, &local_ctm, state->dest->x, state->dest->y, dx, dy, gridfit, &clip);
1906 if (!scaled)
1907 {
1908 if (dx < 1)
1909 dx = 1;
1910 if (dy < 1)
1911 dy = 1;
1912 scaled = fz_scale_pixmap_cached(ctx, pixmap, pixmap->x, pixmap->y, dx, dy, NULL, dev->cache_x, dev->cache_y);
1913 }
1914 if (scaled)
1915 {
1916 fz_drop_pixmap(ctx, pixmap);
1917 pixmap = scaled;
1918 }
1919 }
1920
1921 eop = resolve_color(ctx, &op, color, colorspace, alpha, color_params, colorbv, state->dest);
1922
1923 fz_paint_image_with_color(ctx, state->dest, &state->scissor, state->shape, state->group_alpha, pixmap, local_ctm, colorbv, !(devp->hints & FZ_DONT_INTERPOLATE_IMAGES), devp->flags & FZ_DEVFLAG_GRIDFIT_AS_TILED, eop);
1924
1925 if (state->blendmode & FZ_BLEND_KNOCKOUT)
1926 fz_knockout_end(ctx, dev);
1927 }
1928 fz_always(ctx)
1929 fz_drop_pixmap(ctx, pixmap);
1930 fz_catch(ctx)
1931 fz_rethrow(ctx);
1932 }
1933
1934 static void
fz_draw_clip_image_mask(fz_context * ctx,fz_device * devp,fz_image * image,fz_matrix in_ctm,fz_rect scissor)1935 fz_draw_clip_image_mask(fz_context *ctx, fz_device *devp, fz_image *image, fz_matrix in_ctm, fz_rect scissor)
1936 {
1937 fz_draw_device *dev = (fz_draw_device*)devp;
1938 fz_matrix local_ctm = fz_concat(in_ctm, dev->transform);
1939 fz_irect bbox;
1940 fz_pixmap *scaled = NULL;
1941 fz_pixmap *pixmap = NULL;
1942 int dx, dy;
1943 fz_draw_state *state = push_stack(ctx, dev, "clip image mask");
1944 fz_colorspace *model = state->dest->colorspace;
1945 fz_irect clip;
1946
1947 fz_var(pixmap);
1948
1949 if (dev->top == 0 && dev->resolve_spots)
1950 state = push_group_for_separations(ctx, dev, fz_default_color_params /* FIXME */, dev->default_cs);
1951
1952 clip = fz_pixmap_bbox(ctx, state->dest);
1953 clip = fz_intersect_irect(clip, state->scissor);
1954
1955 if (image->w == 0 || image->h == 0)
1956 {
1957 #ifdef DUMP_GROUP_BLENDS
1958 dump_spaces(dev->top-1, "Clip (image mask) (empty) begin\n");
1959 #endif
1960 state[1].scissor = fz_empty_irect;
1961 state[1].mask = NULL;
1962 return;
1963 }
1964
1965 #ifdef DUMP_GROUP_BLENDS
1966 dump_spaces(dev->top-1, "Clip (image mask) begin\n");
1967 #endif
1968
1969 bbox = fz_irect_from_rect(fz_transform_rect(fz_unit_rect, local_ctm));
1970 bbox = fz_intersect_irect(bbox, state->scissor);
1971 if (!fz_is_infinite_rect(scissor))
1972 {
1973 fz_rect tscissor = fz_transform_rect(scissor, dev->transform);
1974 bbox = fz_intersect_irect(bbox, fz_irect_from_rect(tscissor));
1975 }
1976
1977 fz_try(ctx)
1978 {
1979 pixmap = fz_get_pixmap_from_image(ctx, image, NULL, &local_ctm, &dx, &dy);
1980
1981 state[1].mask = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
1982 fz_clear_pixmap(ctx, state[1].mask);
1983
1984 state[1].dest = fz_new_pixmap_with_bbox(ctx, model, bbox, state[0].dest->seps, state[0].dest->alpha);
1985 fz_copy_pixmap_rect(ctx, state[1].dest, state[0].dest, bbox, dev->default_cs);
1986 if (state[0].shape)
1987 {
1988 state[1].shape = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
1989 fz_clear_pixmap(ctx, state[1].shape);
1990 }
1991 if (state[0].group_alpha)
1992 {
1993 state[1].group_alpha = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
1994 fz_clear_pixmap(ctx, state[1].group_alpha);
1995 }
1996
1997 state[1].blendmode |= FZ_BLEND_ISOLATED;
1998 state[1].scissor = bbox;
1999
2000 if (!(devp->hints & FZ_DONT_INTERPOLATE_IMAGES) && ctx->tuning->image_scale(ctx->tuning->image_scale_arg, dx, dy, pixmap->w, pixmap->h))
2001 {
2002 int gridfit = !(dev->flags & FZ_DRAWDEV_FLAGS_TYPE3);
2003 scaled = fz_transform_pixmap(ctx, dev, pixmap, &local_ctm, state->dest->x, state->dest->y, dx, dy, gridfit, &clip);
2004 if (!scaled)
2005 {
2006 if (dx < 1)
2007 dx = 1;
2008 if (dy < 1)
2009 dy = 1;
2010 scaled = fz_scale_pixmap_cached(ctx, pixmap, pixmap->x, pixmap->y, dx, dy, NULL, dev->cache_x, dev->cache_y);
2011 }
2012 if (scaled)
2013 {
2014 fz_drop_pixmap(ctx, pixmap);
2015 pixmap = scaled;
2016 }
2017 }
2018
2019 #ifdef DUMP_GROUP_BLENDS
2020 dump_spaces(dev->top, "");
2021 fz_dump_blend(ctx, "Creating imagemask: plotting ", pixmap);
2022 fz_dump_blend(ctx, " onto ", state[1].mask);
2023 if (state[1].shape)
2024 fz_dump_blend(ctx, "/S=", state[1].shape);
2025 if (state[1].group_alpha)
2026 fz_dump_blend(ctx, "/GA=", state[1].group_alpha);
2027 #endif
2028
2029 fz_paint_image(ctx, state[1].mask, &bbox, state[1].shape, state[1].group_alpha, pixmap, local_ctm, 255, !(devp->hints & FZ_DONT_INTERPOLATE_IMAGES), devp->flags & FZ_DEVFLAG_GRIDFIT_AS_TILED, 0);
2030
2031 #ifdef DUMP_GROUP_BLENDS
2032 fz_dump_blend(ctx, " to get ", state[1].mask);
2033 if (state[1].shape)
2034 fz_dump_blend(ctx, "/S=", state[1].shape);
2035 if (state[1].group_alpha)
2036 fz_dump_blend(ctx, "/GA=", state[1].group_alpha);
2037 printf("\n");
2038 #endif
2039 }
2040 fz_always(ctx)
2041 fz_drop_pixmap(ctx, pixmap);
2042 fz_catch(ctx)
2043 fz_rethrow(ctx);
2044 }
2045
2046 static void
fz_draw_pop_clip(fz_context * ctx,fz_device * devp)2047 fz_draw_pop_clip(fz_context *ctx, fz_device *devp)
2048 {
2049 fz_draw_device *dev = (fz_draw_device*)devp;
2050 fz_draw_state *state;
2051
2052 if (dev->top == 0)
2053 fz_throw(ctx, FZ_ERROR_GENERIC, "unexpected pop clip");
2054
2055 state = pop_stack(ctx, dev, "clip");
2056
2057 /* We can get here with state[1].mask == NULL if the clipping actually
2058 * resolved to a rectangle earlier.
2059 */
2060 if (state[1].mask)
2061 {
2062 #ifdef DUMP_GROUP_BLENDS
2063 dump_spaces(dev->top, "");
2064 fz_dump_blend(ctx, "Clipping ", state[1].dest);
2065 if (state[1].shape)
2066 fz_dump_blend(ctx, "/S=", state[1].shape);
2067 if (state[1].group_alpha)
2068 fz_dump_blend(ctx, "/GA=", state[1].group_alpha);
2069 fz_dump_blend(ctx, " onto ", state[0].dest);
2070 if (state[0].shape)
2071 fz_dump_blend(ctx, "/S=", state[0].shape);
2072 if (state[0].group_alpha)
2073 fz_dump_blend(ctx, "/GA=", state[0].group_alpha);
2074 fz_dump_blend(ctx, " with ", state[1].mask);
2075 #endif
2076
2077 fz_paint_pixmap_with_mask(state[0].dest, state[1].dest, state[1].mask);
2078 if (state[0].shape != state[1].shape)
2079 {
2080 fz_paint_pixmap_with_mask(state[0].shape, state[1].shape, state[1].mask);
2081 fz_drop_pixmap(ctx, state[1].shape);
2082 state[1].shape = NULL;
2083 }
2084 if (state[0].group_alpha != state[1].group_alpha)
2085 {
2086 fz_paint_pixmap_with_mask(state[0].group_alpha, state[1].group_alpha, state[1].mask);
2087 fz_drop_pixmap(ctx, state[1].group_alpha);
2088 state[1].group_alpha = NULL;
2089 }
2090 fz_drop_pixmap(ctx, state[1].mask);
2091 state[1].mask = NULL;
2092 fz_drop_pixmap(ctx, state[1].dest);
2093 state[1].dest = NULL;
2094
2095 #ifdef DUMP_GROUP_BLENDS
2096 fz_dump_blend(ctx, " to get ", state[0].dest);
2097 if (state[0].shape)
2098 fz_dump_blend(ctx, "/S=", state[0].shape);
2099 if (state[0].group_alpha)
2100 fz_dump_blend(ctx, "/GA=", state[0].group_alpha);
2101 printf("\n");
2102 #endif
2103 }
2104 else
2105 {
2106 #ifdef DUMP_GROUP_BLENDS
2107 dump_spaces(dev->top, "Clip end\n");
2108 #endif
2109 }
2110 }
2111
2112 static void
fz_draw_begin_mask(fz_context * ctx,fz_device * devp,fz_rect area,int luminosity,fz_colorspace * colorspace_in,const float * colorfv,fz_color_params color_params)2113 fz_draw_begin_mask(fz_context *ctx, fz_device *devp, fz_rect area, int luminosity, fz_colorspace *colorspace_in, const float *colorfv, fz_color_params color_params)
2114 {
2115 fz_draw_device *dev = (fz_draw_device*)devp;
2116 fz_pixmap *dest;
2117 fz_irect bbox;
2118 fz_draw_state *state = push_stack(ctx, dev, "mask");
2119 fz_pixmap *shape = state->shape;
2120 fz_pixmap *group_alpha = state->group_alpha;
2121 fz_rect trect;
2122 fz_colorspace *colorspace = NULL;
2123
2124 if (dev->top == 0 && dev->resolve_spots)
2125 state = push_group_for_separations(ctx, dev, color_params, dev->default_cs);
2126
2127 if (colorspace_in)
2128 colorspace = fz_default_colorspace(ctx, dev->default_cs, colorspace_in);
2129
2130 trect = fz_transform_rect(area, dev->transform);
2131 bbox = fz_intersect_irect(fz_irect_from_rect(trect), state->scissor);
2132
2133 /* Reset the blendmode for the mask rendering. In particular,
2134 * don't carry forward knockout or isolated. */
2135 state[1].blendmode = 0;
2136
2137 /* If luminosity, then we generate a mask from the greyscale value of the shapes.
2138 * If !luminosity, then we generate a mask from the alpha value of the shapes.
2139 */
2140 if (luminosity)
2141 state[1].dest = dest = fz_new_pixmap_with_bbox(ctx, fz_device_gray(ctx), bbox, NULL, 0);
2142 else
2143 state[1].dest = dest = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
2144 if (state->shape)
2145 {
2146 /* FIXME: If we ever want to support AIS true, then
2147 * we probably want to create a shape pixmap here,
2148 * using: shape = fz_new_pixmap_with_bbox(NULL, bbox);
2149 * then, in the end_mask code, we create the mask
2150 * from this rather than dest.
2151 */
2152 state[1].shape = shape = NULL;
2153 }
2154 if (state->group_alpha)
2155 {
2156 state[1].group_alpha = group_alpha = NULL;
2157 }
2158
2159 if (luminosity)
2160 {
2161 float bc;
2162 if (!colorspace)
2163 colorspace = fz_device_gray(ctx);
2164 fz_convert_color(ctx, colorspace, colorfv, fz_device_gray(ctx), &bc, NULL, color_params);
2165 fz_clear_pixmap_with_value(ctx, dest, bc * 255);
2166 if (shape)
2167 fz_clear_pixmap_with_value(ctx, shape, 255);
2168 if (group_alpha)
2169 fz_clear_pixmap_with_value(ctx, group_alpha, 255);
2170 }
2171 else
2172 {
2173 fz_clear_pixmap(ctx, dest);
2174 if (shape)
2175 fz_clear_pixmap(ctx, shape);
2176 if (group_alpha)
2177 fz_clear_pixmap(ctx, group_alpha);
2178 }
2179
2180 #ifdef DUMP_GROUP_BLENDS
2181 dump_spaces(dev->top-1, "Mask begin\n");
2182 #endif
2183 state[1].scissor = bbox;
2184 }
2185
2186 static void
fz_draw_end_mask(fz_context * ctx,fz_device * devp)2187 fz_draw_end_mask(fz_context *ctx, fz_device *devp)
2188 {
2189 fz_draw_device *dev = (fz_draw_device*)devp;
2190 fz_pixmap *temp, *dest;
2191 fz_irect bbox;
2192 fz_draw_state *state;
2193
2194 if (dev->top == 0)
2195 fz_throw(ctx, FZ_ERROR_GENERIC, "unexpected end mask");
2196
2197 state = convert_stack(ctx, dev, "mask");
2198
2199 #ifdef DUMP_GROUP_BLENDS
2200 dump_spaces(dev->top-1, "Mask -> Clip: ");
2201 fz_dump_blend(ctx, "Mask ", state[1].dest);
2202 if (state[1].shape)
2203 fz_dump_blend(ctx, "/S=", state[1].shape);
2204 if (state[1].group_alpha)
2205 fz_dump_blend(ctx, "/GA=", state[1].group_alpha);
2206 #endif
2207 {
2208 /* convert to alpha mask */
2209 temp = fz_alpha_from_gray(ctx, state[1].dest);
2210 if (state[1].mask != state[0].mask)
2211 fz_drop_pixmap(ctx, state[1].mask);
2212 state[1].mask = temp;
2213 if (state[1].dest != state[0].dest)
2214 fz_drop_pixmap(ctx, state[1].dest);
2215 state[1].dest = NULL;
2216 if (state[1].shape != state[0].shape)
2217 fz_drop_pixmap(ctx, state[1].shape);
2218 state[1].shape = NULL;
2219 if (state[1].group_alpha != state[0].group_alpha)
2220 fz_drop_pixmap(ctx, state[1].group_alpha);
2221 state[1].group_alpha = NULL;
2222
2223 #ifdef DUMP_GROUP_BLENDS
2224 fz_dump_blend(ctx, "-> Clip ", temp);
2225 printf("\n");
2226 #endif
2227
2228 /* create new dest scratch buffer */
2229 bbox = fz_pixmap_bbox(ctx, temp);
2230 dest = fz_new_pixmap_with_bbox(ctx, state->dest->colorspace, bbox, state->dest->seps, state->dest->alpha);
2231 fz_copy_pixmap_rect(ctx, dest, state->dest, bbox, dev->default_cs);
2232
2233 /* push soft mask as clip mask */
2234 state[1].dest = dest;
2235 state[1].blendmode |= FZ_BLEND_ISOLATED;
2236 /* If we have a shape, then it'll need to be masked with the
2237 * clip mask when we pop. So create a new shape now. */
2238 if (state[0].shape)
2239 {
2240 state[1].shape = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
2241 fz_clear_pixmap(ctx, state[1].shape);
2242 }
2243 if (state[0].group_alpha)
2244 {
2245 state[1].group_alpha = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
2246 fz_clear_pixmap(ctx, state[1].group_alpha);
2247 }
2248 state[1].scissor = bbox;
2249 }
2250 }
2251
2252 static void
fz_draw_begin_group(fz_context * ctx,fz_device * devp,fz_rect area,fz_colorspace * cs,int isolated,int knockout,int blendmode,float alpha)2253 fz_draw_begin_group(fz_context *ctx, fz_device *devp, fz_rect area, fz_colorspace *cs, int isolated, int knockout, int blendmode, float alpha)
2254 {
2255 fz_draw_device *dev = (fz_draw_device*)devp;
2256 fz_irect bbox;
2257 fz_pixmap *dest;
2258 fz_draw_state *state = &dev->stack[dev->top];
2259 fz_colorspace *model = state->dest->colorspace;
2260 fz_rect trect;
2261
2262 if (dev->top == 0 && dev->resolve_spots)
2263 state = push_group_for_separations(ctx, dev, fz_default_color_params /* FIXME */, dev->default_cs);
2264
2265 if (cs != NULL)
2266 model = fz_default_colorspace(ctx, dev->default_cs, cs);
2267
2268 if (state->blendmode & FZ_BLEND_KNOCKOUT)
2269 fz_knockout_begin(ctx, dev);
2270
2271 state = push_stack(ctx, dev, "group");
2272
2273 trect = fz_transform_rect(area, dev->transform);
2274 bbox = fz_intersect_irect(fz_irect_from_rect(trect), state->scissor);
2275
2276 #ifndef ATTEMPT_KNOCKOUT_AND_ISOLATED
2277 knockout = 0;
2278 isolated = 1;
2279 #endif
2280
2281 state[1].dest = dest = fz_new_pixmap_with_bbox(ctx, model, bbox, state[0].dest->seps, state[0].dest->alpha || isolated);
2282
2283 if (isolated)
2284 {
2285 fz_clear_pixmap(ctx, dest);
2286 state[1].group_alpha = NULL;
2287 }
2288 else
2289 {
2290 fz_copy_pixmap_rect(ctx, dest, state[0].dest, bbox, dev->default_cs);
2291 state[1].group_alpha = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
2292 fz_clear_pixmap(ctx, state[1].group_alpha);
2293 }
2294
2295 /* shape is inherited from the previous group */
2296 state[1].alpha = alpha;
2297
2298 #ifdef DUMP_GROUP_BLENDS
2299 dump_spaces(dev->top-1, "");
2300 {
2301 char text[240];
2302 char atext[80];
2303 char btext[80];
2304 if (alpha != 1)
2305 sprintf(atext, " (alpha %g)", alpha);
2306 else
2307 atext[0] = 0;
2308 if (blendmode != 0)
2309 sprintf(btext, " (blend %d)", blendmode);
2310 else
2311 btext[0] = 0;
2312 sprintf(text, "Group begin%s%s%s%s: background is ", isolated ? " (isolated)" : "", knockout ? " (knockout)" : "", atext, btext);
2313 fz_dump_blend(ctx, text, state[1].dest);
2314 }
2315 if (state[1].shape)
2316 fz_dump_blend(ctx, "/S=", state[1].shape);
2317 if (state[1].group_alpha)
2318 fz_dump_blend(ctx, "/GA=", state[1].group_alpha);
2319 printf("\n");
2320 #endif
2321
2322 state[1].scissor = bbox;
2323 state[1].blendmode = blendmode | (isolated ? FZ_BLEND_ISOLATED : 0) | (knockout ? FZ_BLEND_KNOCKOUT : 0);
2324 }
2325
2326 static void
fz_draw_end_group(fz_context * ctx,fz_device * devp)2327 fz_draw_end_group(fz_context *ctx, fz_device *devp)
2328 {
2329 fz_draw_device *dev = (fz_draw_device*)devp;
2330 int blendmode;
2331 int isolated;
2332 float alpha;
2333 fz_draw_state *state;
2334
2335 if (dev->top == 0)
2336 fz_throw(ctx, FZ_ERROR_GENERIC, "unexpected end group");
2337
2338 state = pop_stack(ctx, dev, "group");
2339
2340 alpha = state[1].alpha;
2341 blendmode = state[1].blendmode & FZ_BLEND_MODEMASK;
2342 isolated = state[1].blendmode & FZ_BLEND_ISOLATED;
2343
2344 #ifdef DUMP_GROUP_BLENDS
2345 dump_spaces(dev->top, "");
2346 fz_dump_blend(ctx, "Group end: blending ", state[1].dest);
2347 if (state[1].shape)
2348 fz_dump_blend(ctx, "/S=", state[1].shape);
2349 if (state[1].group_alpha)
2350 fz_dump_blend(ctx, "/GA=", state[1].group_alpha);
2351 fz_dump_blend(ctx, " onto ", state[0].dest);
2352 if (state[0].shape)
2353 fz_dump_blend(ctx, "/S=", state[0].shape);
2354 if (state[0].group_alpha)
2355 fz_dump_blend(ctx, "/GA=", state[0].group_alpha);
2356 if (alpha != 1.0f)
2357 printf(" (alpha %g)", alpha);
2358 if (blendmode != 0)
2359 printf(" (blend %d)", blendmode);
2360 if (isolated != 0)
2361 printf(" (isolated)");
2362 if (state[1].blendmode & FZ_BLEND_KNOCKOUT)
2363 printf(" (knockout)");
2364 #endif
2365
2366 if (state[0].dest->colorspace != state[1].dest->colorspace)
2367 {
2368 fz_pixmap *converted = fz_convert_pixmap(ctx, state[1].dest, state[0].dest->colorspace, NULL, dev->default_cs, fz_default_color_params, 1);
2369 fz_drop_pixmap(ctx, state[1].dest);
2370 state[1].dest = converted;
2371 }
2372
2373 if ((blendmode == 0) && (state[0].shape == state[1].shape) && (state[0].group_alpha == state[1].group_alpha))
2374 fz_paint_pixmap(state[0].dest, state[1].dest, alpha * 255);
2375 else
2376 fz_blend_pixmap(ctx, state[0].dest, state[1].dest, alpha * 255, blendmode, isolated, state[1].group_alpha);
2377
2378 if (state[0].shape != state[1].shape)
2379 {
2380 /* The 'D' on page 7 of Altona_Technical_v20_x4.pdf goes wrong if this
2381 * isn't alpha * 255, as the blend back fails to take account of alpha. */
2382 if (state[0].shape)
2383 {
2384 if (state[1].shape)
2385 fz_paint_pixmap(state[0].shape, state[1].shape, alpha * 255);
2386 else
2387 fz_paint_pixmap_alpha(state[0].shape, state[1].dest, alpha * 255);
2388 }
2389 }
2390 assert(state[0].group_alpha == NULL || state[0].group_alpha != state[1].group_alpha);
2391 if (state[0].group_alpha && state[0].group_alpha != state[1].group_alpha)
2392 {
2393 /* The 'D' on page 7 of Altona_Technical_v20_x4.pdf uses an isolated group,
2394 * and goes wrong if this is 255 * alpha, as an alpha effectively gets
2395 * applied twice. CATX5233 page 7 uses a non-isolated group, and goes wrong
2396 * if alpha isn't applied here. */
2397 if (state[1].group_alpha)
2398 fz_paint_pixmap(state[0].group_alpha, state[1].group_alpha, isolated ? 255 : alpha * 255);
2399 else
2400 fz_paint_pixmap_alpha(state[0].group_alpha, state[1].dest, isolated ? 255 : alpha * 255);
2401 }
2402
2403 assert(state[0].dest != state[1].dest);
2404
2405 #ifdef DUMP_GROUP_BLENDS
2406 fz_dump_blend(ctx, " to get ", state[0].dest);
2407 if (state[0].shape)
2408 fz_dump_blend(ctx, "/S=", state[0].shape);
2409 if (state[0].group_alpha)
2410 fz_dump_blend(ctx, "/GA=", state[0].group_alpha);
2411 printf("\n");
2412 #endif
2413
2414 if (state[0].shape != state[1].shape)
2415 {
2416 fz_drop_pixmap(ctx, state[1].shape);
2417 state[1].shape = NULL;
2418 }
2419 fz_drop_pixmap(ctx, state[1].group_alpha);
2420 state[1].group_alpha = NULL;
2421 fz_drop_pixmap(ctx, state[1].dest);
2422 state[1].dest = NULL;
2423
2424 if (state[0].blendmode & FZ_BLEND_KNOCKOUT)
2425 fz_knockout_end(ctx, dev);
2426 }
2427
2428 typedef struct
2429 {
2430 int refs;
2431 float ctm[4];
2432 int id;
2433 char has_shape;
2434 char has_group_alpha;
2435 fz_colorspace *cs;
2436 } tile_key;
2437
2438 typedef struct
2439 {
2440 fz_storable storable;
2441 fz_pixmap *dest;
2442 fz_pixmap *shape;
2443 fz_pixmap *group_alpha;
2444 } tile_record;
2445
2446 static int
fz_make_hash_tile_key(fz_context * ctx,fz_store_hash * hash,void * key_)2447 fz_make_hash_tile_key(fz_context *ctx, fz_store_hash *hash, void *key_)
2448 {
2449 tile_key *key = key_;
2450
2451 hash->u.im.id = key->id;
2452 hash->u.im.has_shape = key->has_shape;
2453 hash->u.im.has_group_alpha = key->has_group_alpha;
2454 hash->u.im.m[0] = key->ctm[0];
2455 hash->u.im.m[1] = key->ctm[1];
2456 hash->u.im.m[2] = key->ctm[2];
2457 hash->u.im.m[3] = key->ctm[3];
2458 hash->u.im.ptr = key->cs;
2459 return 1;
2460 }
2461
2462 static void *
fz_keep_tile_key(fz_context * ctx,void * key_)2463 fz_keep_tile_key(fz_context *ctx, void *key_)
2464 {
2465 tile_key *key = key_;
2466 return fz_keep_imp(ctx, key, &key->refs);
2467 }
2468
2469 static void
fz_drop_tile_key(fz_context * ctx,void * key_)2470 fz_drop_tile_key(fz_context *ctx, void *key_)
2471 {
2472 tile_key *key = key_;
2473 if (fz_drop_imp(ctx, key, &key->refs))
2474 {
2475 fz_drop_colorspace_store_key(ctx, key->cs);
2476 fz_free(ctx, key);
2477 }
2478 }
2479
2480 static int
fz_cmp_tile_key(fz_context * ctx,void * k0_,void * k1_)2481 fz_cmp_tile_key(fz_context *ctx, void *k0_, void *k1_)
2482 {
2483 tile_key *k0 = k0_;
2484 tile_key *k1 = k1_;
2485 return k0->id == k1->id &&
2486 k0->has_shape == k1->has_shape &&
2487 k0->has_group_alpha == k1->has_group_alpha &&
2488 k0->ctm[0] == k1->ctm[0] &&
2489 k0->ctm[1] == k1->ctm[1] &&
2490 k0->ctm[2] == k1->ctm[2] &&
2491 k0->ctm[3] == k1->ctm[3] &&
2492 k0->cs == k1->cs;
2493 }
2494
2495 static void
fz_format_tile_key(fz_context * ctx,char * s,size_t n,void * key_)2496 fz_format_tile_key(fz_context *ctx, char *s, size_t n, void *key_)
2497 {
2498 tile_key *key = (tile_key *)key_;
2499 fz_snprintf(s, n, "(tile id=%x, ctm=%g %g %g %g, cs=%x, shape=%d, ga=%d)",
2500 key->id, key->ctm[0], key->ctm[1], key->ctm[2], key->ctm[3], key->cs,
2501 key->has_shape, key->has_group_alpha);
2502 }
2503
2504 static const fz_store_type fz_tile_store_type =
2505 {
2506 "struct tile_record",
2507 fz_make_hash_tile_key,
2508 fz_keep_tile_key,
2509 fz_drop_tile_key,
2510 fz_cmp_tile_key,
2511 fz_format_tile_key,
2512 NULL
2513 };
2514
2515 static void
fz_drop_tile_record_imp(fz_context * ctx,fz_storable * storable)2516 fz_drop_tile_record_imp(fz_context *ctx, fz_storable *storable)
2517 {
2518 tile_record *tr = (tile_record *)storable;
2519 fz_drop_pixmap(ctx, tr->dest);
2520 fz_drop_pixmap(ctx, tr->shape);
2521 fz_drop_pixmap(ctx, tr->group_alpha);
2522 fz_free(ctx, tr);
2523 }
2524
2525 static void
fz_drop_tile_record(fz_context * ctx,tile_record * tile)2526 fz_drop_tile_record(fz_context *ctx, tile_record *tile)
2527 {
2528 fz_drop_storable(ctx, &tile->storable);
2529 }
2530
2531 static tile_record *
fz_new_tile_record(fz_context * ctx,fz_pixmap * dest,fz_pixmap * shape,fz_pixmap * group_alpha)2532 fz_new_tile_record(fz_context *ctx, fz_pixmap *dest, fz_pixmap *shape, fz_pixmap *group_alpha)
2533 {
2534 tile_record *tile = fz_malloc_struct(ctx, tile_record);
2535 FZ_INIT_STORABLE(tile, 1, fz_drop_tile_record_imp);
2536 tile->dest = fz_keep_pixmap(ctx, dest);
2537 tile->shape = fz_keep_pixmap(ctx, shape);
2538 tile->group_alpha = fz_keep_pixmap(ctx, group_alpha);
2539 return tile;
2540 }
2541
2542 size_t
fz_tile_size(fz_context * ctx,tile_record * tile)2543 fz_tile_size(fz_context *ctx, tile_record *tile)
2544 {
2545 if (!tile)
2546 return 0;
2547 return sizeof(*tile) + fz_pixmap_size(ctx, tile->dest) + fz_pixmap_size(ctx, tile->shape) + fz_pixmap_size(ctx, tile->group_alpha);
2548 }
2549
2550 static int
fz_draw_begin_tile(fz_context * ctx,fz_device * devp,fz_rect area,fz_rect view,float xstep,float ystep,fz_matrix in_ctm,int id)2551 fz_draw_begin_tile(fz_context *ctx, fz_device *devp, fz_rect area, fz_rect view, float xstep, float ystep, fz_matrix in_ctm, int id)
2552 {
2553 fz_draw_device *dev = (fz_draw_device*)devp;
2554 fz_matrix ctm = fz_concat(in_ctm, dev->transform);
2555 fz_pixmap *dest = NULL;
2556 fz_pixmap *shape, *group_alpha;
2557 fz_irect bbox;
2558 fz_draw_state *state = &dev->stack[dev->top];
2559 fz_colorspace *model = state->dest->colorspace;
2560 fz_rect local_view;
2561
2562 if (dev->top == 0 && dev->resolve_spots)
2563 state = push_group_for_separations(ctx, dev, fz_default_color_params /* FIXME */, dev->default_cs);
2564
2565 /* area, view, xstep, ystep are in pattern space */
2566 /* ctm maps from pattern space to device space */
2567
2568 if (state->blendmode & FZ_BLEND_KNOCKOUT)
2569 fz_knockout_begin(ctx, dev);
2570
2571 state = push_stack(ctx, dev, "tile");
2572
2573 local_view = fz_transform_rect(view, ctm);
2574 bbox = fz_irect_from_rect(local_view);
2575 /* We should never have a bbox that entirely covers our destination.
2576 * If we do, then the check for only 1 tile being visible above has
2577 * failed. Actually, this *can* fail due to the round_rect, at extreme
2578 * resolutions, so disable this assert.
2579 * assert(bbox.x0 > state->dest->x || bbox.x1 < state->dest->x + state->dest->w ||
2580 * bbox.y0 > state->dest->y || bbox.y1 < state->dest->y + state->dest->h);
2581 */
2582
2583 /* Check to see if we have one cached */
2584 if (id)
2585 {
2586 tile_key tk;
2587 tile_record *tile;
2588 tk.ctm[0] = ctm.a;
2589 tk.ctm[1] = ctm.b;
2590 tk.ctm[2] = ctm.c;
2591 tk.ctm[3] = ctm.d;
2592 tk.id = id;
2593 tk.cs = state[1].dest->colorspace;
2594 tk.has_shape = (state[1].shape != NULL);
2595 tk.has_group_alpha = (state[1].group_alpha != NULL);
2596
2597 tile = fz_find_item(ctx, fz_drop_tile_record_imp, &tk, &fz_tile_store_type);
2598 if (tile)
2599 {
2600 state[1].dest = fz_keep_pixmap(ctx, tile->dest);
2601 state[1].shape = fz_keep_pixmap(ctx, tile->shape);
2602 state[1].group_alpha = fz_keep_pixmap(ctx, tile->group_alpha);
2603 state[1].blendmode |= FZ_BLEND_ISOLATED;
2604 state[1].xstep = xstep;
2605 state[1].ystep = ystep;
2606 state[1].id = id;
2607 state[1].encache = 0;
2608 state[1].area = fz_irect_from_rect(area);
2609 state[1].ctm = ctm;
2610 state[1].scissor = bbox;
2611
2612 #ifdef DUMP_GROUP_BLENDS
2613 dump_spaces(dev->top-1, "Tile begin (cached)\n");
2614 #endif
2615
2616 fz_drop_tile_record(ctx, tile);
2617 return 1;
2618 }
2619 }
2620
2621 /* Patterns can be transparent, so we need to have an alpha here. */
2622 state[1].dest = dest = fz_new_pixmap_with_bbox(ctx, model, bbox, state[0].dest->seps, 1);
2623 fz_clear_pixmap(ctx, dest);
2624 shape = state[0].shape;
2625 if (shape)
2626 {
2627 state[1].shape = shape = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
2628 fz_clear_pixmap(ctx, shape);
2629 }
2630 group_alpha = state[0].group_alpha;
2631 if (group_alpha)
2632 {
2633 state[1].group_alpha = group_alpha = fz_new_pixmap_with_bbox(ctx, NULL, bbox, NULL, 1);
2634 fz_clear_pixmap(ctx, group_alpha);
2635 }
2636
2637 state[1].blendmode |= FZ_BLEND_ISOLATED;
2638 state[1].xstep = xstep;
2639 state[1].ystep = ystep;
2640 state[1].id = id;
2641 state[1].encache = 1;
2642 state[1].area = fz_irect_from_rect(area);
2643 state[1].ctm = ctm;
2644 state[1].scissor = bbox;
2645
2646 #ifdef DUMP_GROUP_BLENDS
2647 dump_spaces(dev->top-1, "Tile begin\n");
2648 #endif
2649
2650 return 0;
2651 }
2652
2653 static void
fz_draw_end_tile(fz_context * ctx,fz_device * devp)2654 fz_draw_end_tile(fz_context *ctx, fz_device *devp)
2655 {
2656 fz_draw_device *dev = (fz_draw_device*)devp;
2657 float xstep, ystep;
2658 fz_matrix ttm, ctm, shapectm, gactm;
2659 fz_irect area, scissor, tile_bbox;
2660 fz_rect scissor_tmp, tile_tmp;
2661 int x0, y0, x1, y1, x, y, extra_x, extra_y;
2662 fz_draw_state *state;
2663 fz_pixmap *dest = NULL;
2664 fz_pixmap *shape = NULL;
2665 fz_pixmap *group_alpha = NULL;
2666
2667 if (dev->top == 0)
2668 fz_throw(ctx, FZ_ERROR_GENERIC, "unexpected end tile");
2669
2670 state = pop_stack(ctx, dev, "tile");
2671
2672 xstep = state[1].xstep;
2673 ystep = state[1].ystep;
2674 area = state[1].area;
2675 ctm = state[1].ctm;
2676
2677 /* Fudge the scissor bbox a little to allow for inaccuracies in the
2678 * matrix inversion. */
2679 ttm = fz_invert_matrix(ctm);
2680 scissor_tmp = fz_rect_from_irect(state[0].scissor);
2681 scissor_tmp = fz_expand_rect(scissor_tmp, 1);
2682 scissor_tmp = fz_transform_rect(scissor_tmp, ttm);
2683 scissor = fz_irect_from_rect(scissor_tmp);
2684 area = fz_intersect_irect(area, scissor);
2685
2686 tile_bbox.x0 = state[1].dest->x;
2687 tile_bbox.y0 = state[1].dest->y;
2688 tile_bbox.x1 = state[1].dest->w + tile_bbox.x0;
2689 tile_bbox.y1 = state[1].dest->h + tile_bbox.y0;
2690 tile_tmp = fz_rect_from_irect(tile_bbox);
2691 tile_tmp = fz_expand_rect(tile_tmp, 1);
2692 tile_tmp = fz_transform_rect(tile_tmp, ttm);
2693
2694 /* FIXME: area is a bbox, so FP not appropriate here */
2695 /* In PDF files xstep/ystep can be smaller than view (the area of a
2696 * single tile) (see fts_15_1506.pdf for an example). This means that
2697 * we have to bias the left hand/bottom edge calculations by the
2698 * difference between the step and the width/height of the tile. */
2699 /* scissor, xstep and area are all in pattern space. */
2700 extra_x = tile_tmp.x1 - tile_tmp.x0 - xstep;
2701 if (extra_x < 0)
2702 extra_x = 0;
2703 extra_y = tile_tmp.y1 - tile_tmp.y0 - ystep;
2704 if (extra_y < 0)
2705 extra_y = 0;
2706 x0 = floorf((area.x0 - tile_tmp.x0 - extra_x) / xstep);
2707 y0 = floorf((area.y0 - tile_tmp.y0 - extra_y) / ystep);
2708 x1 = ceilf((area.x1 - tile_tmp.x0 + extra_x) / xstep);
2709 y1 = ceilf((area.y1 - tile_tmp.y0 + extra_y) / ystep);
2710
2711 ctm.e = state[1].dest->x;
2712 ctm.f = state[1].dest->y;
2713 if (state[1].shape)
2714 {
2715 shapectm = ctm;
2716 shapectm.e = state[1].shape->x;
2717 shapectm.f = state[1].shape->y;
2718 }
2719 if (state[1].group_alpha)
2720 {
2721 gactm = ctm;
2722 gactm.e = state[1].group_alpha->x;
2723 gactm.f = state[1].group_alpha->y;
2724 }
2725
2726 #ifdef DUMP_GROUP_BLENDS
2727 dump_spaces(dev->top, "");
2728 fz_dump_blend(ctx, "Tiling ", state[1].dest);
2729 if (state[1].shape)
2730 fz_dump_blend(ctx, "/S=", state[1].shape);
2731 if (state[1].group_alpha)
2732 fz_dump_blend(ctx, "/GA=", state[1].group_alpha);
2733 fz_dump_blend(ctx, " onto ", state[0].dest);
2734 if (state[0].shape)
2735 fz_dump_blend(ctx, "/S=", state[0].shape);
2736 if (state[0].group_alpha)
2737 fz_dump_blend(ctx, "/GA=", state[0].group_alpha);
2738 #endif
2739 fz_var(dest);
2740 fz_var(shape);
2741 fz_var(group_alpha);
2742
2743 fz_try(ctx)
2744 {
2745 dest = fz_new_pixmap_from_pixmap(ctx, state[1].dest, NULL);
2746
2747 shape = fz_new_pixmap_from_pixmap(ctx, state[1].shape, NULL);
2748 group_alpha = fz_new_pixmap_from_pixmap(ctx, state[1].group_alpha, NULL);
2749
2750 for (y = y0; y < y1; y++)
2751 {
2752 for (x = x0; x < x1; x++)
2753 {
2754 ttm = fz_pre_translate(ctm, x * xstep, y * ystep);
2755 dest->x = ttm.e;
2756 dest->y = ttm.f;
2757 /* Check for overflow due to float -> int conversions */
2758 if (dest->x > 0 && dest->x + dest->w < 0)
2759 continue;
2760 if (dest->y > 0 && dest->y + dest->h < 0)
2761 continue;
2762 fz_paint_pixmap_with_bbox(state[0].dest, dest, 255, state[0].scissor);
2763 if (shape)
2764 {
2765 ttm = fz_pre_translate(shapectm, x * xstep, y * ystep);
2766 shape->x = ttm.e;
2767 shape->y = ttm.f;
2768 fz_paint_pixmap_with_bbox(state[0].shape, shape, 255, state[0].scissor);
2769 }
2770 if (group_alpha)
2771 {
2772 ttm = fz_pre_translate(gactm, x * xstep, y * ystep);
2773 group_alpha->x = ttm.e;
2774 group_alpha->y = ttm.f;
2775 fz_paint_pixmap_with_bbox(state[0].group_alpha, group_alpha, 255, state[0].scissor);
2776 }
2777 }
2778 }
2779
2780 /* Now we try to cache the tiles. Any failure here will just result in us not caching. */
2781 if (state[1].encache && state[1].id != 0)
2782 {
2783 tile_record *tile = NULL;
2784 tile_key *key = NULL;
2785 fz_var(tile);
2786 fz_var(key);
2787 fz_try(ctx)
2788 {
2789 tile_record *existing_tile;
2790
2791 tile = fz_new_tile_record(ctx, state[1].dest, state[1].shape, state[1].group_alpha);
2792
2793 key = fz_malloc_struct(ctx, tile_key);
2794 key->refs = 1;
2795 key->id = state[1].id;
2796 key->ctm[0] = ctm.a;
2797 key->ctm[1] = ctm.b;
2798 key->ctm[2] = ctm.c;
2799 key->ctm[3] = ctm.d;
2800 key->cs = fz_keep_colorspace_store_key(ctx, state[1].dest->colorspace);
2801 key->has_shape = (state[1].shape != NULL);
2802 key->has_group_alpha = (state[1].group_alpha != NULL);
2803 existing_tile = fz_store_item(ctx, key, tile, fz_tile_size(ctx, tile), &fz_tile_store_type);
2804 if (existing_tile)
2805 {
2806 /* We already have a tile. This will either have been
2807 * produced by a racing thread, or there is already
2808 * an entry for this one in the store. */
2809 fz_drop_tile_record(ctx, tile);
2810 tile = existing_tile;
2811 }
2812 }
2813 fz_always(ctx)
2814 {
2815 fz_drop_tile_key(ctx, key);
2816 fz_drop_tile_record(ctx, tile);
2817 }
2818 fz_catch(ctx)
2819 {
2820 /* Do nothing */
2821 }
2822 }
2823 }
2824 fz_always(ctx)
2825 {
2826 fz_drop_pixmap(ctx, dest);
2827 fz_drop_pixmap(ctx, shape);
2828 fz_drop_pixmap(ctx, group_alpha);
2829 fz_drop_pixmap(ctx, state[1].dest);
2830 state[1].dest = NULL;
2831 fz_drop_pixmap(ctx, state[1].shape);
2832 state[1].shape = NULL;
2833 fz_drop_pixmap(ctx, state[1].group_alpha);
2834 state[1].group_alpha = NULL;
2835 }
2836 fz_catch(ctx)
2837 fz_rethrow(ctx);
2838
2839 #ifdef DUMP_GROUP_BLENDS
2840 fz_dump_blend(ctx, " to get ", state[0].dest);
2841 if (state[0].shape)
2842 fz_dump_blend(ctx, "/S=", state[0].shape);
2843 if (state[0].group_alpha)
2844 fz_dump_blend(ctx, "/GA=", state[0].group_alpha);
2845 printf("\n");
2846 #endif
2847
2848 if (state->blendmode & FZ_BLEND_KNOCKOUT)
2849 fz_knockout_end(ctx, dev);
2850 }
2851
2852 static void
fz_draw_render_flags(fz_context * ctx,fz_device * devp,int set,int clear)2853 fz_draw_render_flags(fz_context *ctx, fz_device *devp, int set, int clear)
2854 {
2855 fz_draw_device *dev = (fz_draw_device*)devp;
2856 dev->flags = (dev->flags | set ) & ~clear;
2857 }
2858
2859 static void
fz_draw_set_default_colorspaces(fz_context * ctx,fz_device * devp,fz_default_colorspaces * default_cs)2860 fz_draw_set_default_colorspaces(fz_context *ctx, fz_device *devp, fz_default_colorspaces *default_cs)
2861 {
2862 fz_draw_device *dev = (fz_draw_device*)devp;
2863 fz_drop_default_colorspaces(ctx, dev->default_cs);
2864 dev->default_cs = fz_keep_default_colorspaces(ctx, default_cs);
2865 }
2866
2867 static void
fz_draw_close_device(fz_context * ctx,fz_device * devp)2868 fz_draw_close_device(fz_context *ctx, fz_device *devp)
2869 {
2870 fz_draw_device *dev = (fz_draw_device*)devp;
2871
2872 /* pop and free the stacks */
2873 if (dev->top > dev->resolve_spots)
2874 fz_throw(ctx, FZ_ERROR_GENERIC, "items left on stack in draw device: %d", dev->top);
2875
2876 if (dev->resolve_spots && dev->top)
2877 {
2878 fz_draw_state *state = &dev->stack[--dev->top];
2879 fz_try(ctx)
2880 {
2881 fz_copy_pixmap_area_converting_seps(ctx, state[1].dest, state[0].dest, dev->proof_cs, fz_default_color_params, dev->default_cs);
2882 assert(state[1].mask == NULL);
2883 assert(state[1].shape == NULL);
2884 assert(state[1].group_alpha == NULL);
2885 }
2886 fz_always(ctx)
2887 {
2888 fz_drop_pixmap(ctx, state[1].dest);
2889 state[1].dest = NULL;
2890 }
2891 fz_catch(ctx)
2892 fz_rethrow(ctx);
2893 }
2894 }
2895
2896 static void
fz_draw_drop_device(fz_context * ctx,fz_device * devp)2897 fz_draw_drop_device(fz_context *ctx, fz_device *devp)
2898 {
2899 fz_draw_device *dev = (fz_draw_device*)devp;
2900 fz_rasterizer *rast = dev->rast;
2901
2902 fz_drop_default_colorspaces(ctx, dev->default_cs);
2903 fz_drop_colorspace(ctx, dev->proof_cs);
2904
2905 /* pop and free the stacks */
2906 for (; dev->top > 0; dev->top--)
2907 {
2908 fz_draw_state *state = &dev->stack[dev->top - 1];
2909 if (state[1].mask != state[0].mask)
2910 fz_drop_pixmap(ctx, state[1].mask);
2911 if (state[1].dest != state[0].dest)
2912 fz_drop_pixmap(ctx, state[1].dest);
2913 if (state[1].shape != state[0].shape)
2914 fz_drop_pixmap(ctx, state[1].shape);
2915 if (state[1].group_alpha != state[0].group_alpha)
2916 fz_drop_pixmap(ctx, state[1].group_alpha);
2917 }
2918
2919 /* We never free the dest/mask/shape at level 0, as:
2920 * 1) dest is passed in and ownership remains with the caller.
2921 * 2) shape and mask are NULL at level 0.
2922 */
2923
2924 if (dev->stack != &dev->init_stack[0])
2925 fz_free(ctx, dev->stack);
2926 fz_drop_scale_cache(ctx, dev->cache_x);
2927 fz_drop_scale_cache(ctx, dev->cache_y);
2928 fz_drop_rasterizer(ctx, rast);
2929 }
2930
2931 static fz_device *
new_draw_device(fz_context * ctx,fz_matrix transform,fz_pixmap * dest,const fz_aa_context * aa,const fz_irect * clip,fz_colorspace * proof_cs)2932 new_draw_device(fz_context *ctx, fz_matrix transform, fz_pixmap *dest, const fz_aa_context *aa, const fz_irect *clip, fz_colorspace *proof_cs)
2933 {
2934 fz_draw_device *dev = fz_new_derived_device(ctx, fz_draw_device);
2935
2936 dev->super.drop_device = fz_draw_drop_device;
2937 dev->super.close_device = fz_draw_close_device;
2938
2939 dev->super.fill_path = fz_draw_fill_path;
2940 dev->super.stroke_path = fz_draw_stroke_path;
2941 dev->super.clip_path = fz_draw_clip_path;
2942 dev->super.clip_stroke_path = fz_draw_clip_stroke_path;
2943
2944 dev->super.fill_text = fz_draw_fill_text;
2945 dev->super.stroke_text = fz_draw_stroke_text;
2946 dev->super.clip_text = fz_draw_clip_text;
2947 dev->super.clip_stroke_text = fz_draw_clip_stroke_text;
2948 dev->super.ignore_text = fz_draw_ignore_text;
2949
2950 dev->super.fill_image_mask = fz_draw_fill_image_mask;
2951 dev->super.clip_image_mask = fz_draw_clip_image_mask;
2952 dev->super.fill_image = fz_draw_fill_image;
2953 dev->super.fill_shade = fz_draw_fill_shade;
2954
2955 dev->super.pop_clip = fz_draw_pop_clip;
2956
2957 dev->super.begin_mask = fz_draw_begin_mask;
2958 dev->super.end_mask = fz_draw_end_mask;
2959 dev->super.begin_group = fz_draw_begin_group;
2960 dev->super.end_group = fz_draw_end_group;
2961
2962 dev->super.begin_tile = fz_draw_begin_tile;
2963 dev->super.end_tile = fz_draw_end_tile;
2964
2965 dev->super.render_flags = fz_draw_render_flags;
2966 dev->super.set_default_colorspaces = fz_draw_set_default_colorspaces;
2967
2968 dev->proof_cs = fz_keep_colorspace(ctx, proof_cs);
2969 dev->transform = transform;
2970 dev->flags = 0;
2971 dev->resolve_spots = 0;
2972 dev->top = 0;
2973 dev->stack = &dev->init_stack[0];
2974 dev->stack_cap = STACK_SIZE;
2975 dev->stack[0].dest = dest;
2976 dev->stack[0].shape = NULL;
2977 dev->stack[0].group_alpha = NULL;
2978 dev->stack[0].mask = NULL;
2979 dev->stack[0].blendmode = 0;
2980 dev->stack[0].scissor.x0 = dest->x;
2981 dev->stack[0].scissor.y0 = dest->y;
2982 dev->stack[0].scissor.x1 = dest->x + dest->w;
2983 dev->stack[0].scissor.y1 = dest->y + dest->h;
2984
2985 if (clip)
2986 {
2987 if (clip->x0 > dev->stack[0].scissor.x0)
2988 dev->stack[0].scissor.x0 = clip->x0;
2989 if (clip->x1 < dev->stack[0].scissor.x1)
2990 dev->stack[0].scissor.x1 = clip->x1;
2991 if (clip->y0 > dev->stack[0].scissor.y0)
2992 dev->stack[0].scissor.y0 = clip->y0;
2993 if (clip->y1 < dev->stack[0].scissor.y1)
2994 dev->stack[0].scissor.y1 = clip->y1;
2995 }
2996
2997 /* If we have no separations structure at all, then we want a
2998 * simple composite rendering (with no overprint simulation).
2999 * If we do have a separations structure, so: 1) Any
3000 * 'disabled' separations are ignored. 2) Any 'composite'
3001 * separations means we will need to do an overprint
3002 * simulation.
3003 *
3004 * The supplied pixmaps 's' will match the number of
3005 * 'spots' separations. If we have any 'composite'
3006 * separations therefore, we'll need to make a new pixmap
3007 * with a new (completely 'spots') separations structure,
3008 * render to that, and then map down at the end.
3009 *
3010 * Unfortunately we can't produce this until we know what
3011 * the default_colorspaces etc are, so set a flag for us
3012 * to trigger on later.
3013 */
3014 if (dest->seps || dev->proof_cs != NULL)
3015 #if FZ_ENABLE_SPOT_RENDERING
3016 dev->resolve_spots = 1;
3017 #else
3018 fz_throw(ctx, FZ_ERROR_GENERIC, "Spot rendering (and overprint/overprint simulation) not available in this build");
3019 #endif
3020
3021 fz_try(ctx)
3022 {
3023 dev->rast = fz_new_rasterizer(ctx, aa);
3024 dev->cache_x = fz_new_scale_cache(ctx);
3025 dev->cache_y = fz_new_scale_cache(ctx);
3026 }
3027 fz_catch(ctx)
3028 {
3029 fz_drop_device(ctx, (fz_device*)dev);
3030 fz_rethrow(ctx);
3031 }
3032
3033 return (fz_device*)dev;
3034 }
3035
3036 fz_device *
fz_new_draw_device(fz_context * ctx,fz_matrix transform,fz_pixmap * dest)3037 fz_new_draw_device(fz_context *ctx, fz_matrix transform, fz_pixmap *dest)
3038 {
3039 return new_draw_device(ctx, transform, dest, NULL, NULL, NULL);
3040 }
3041
3042 fz_device *
fz_new_draw_device_with_bbox(fz_context * ctx,fz_matrix transform,fz_pixmap * dest,const fz_irect * clip)3043 fz_new_draw_device_with_bbox(fz_context *ctx, fz_matrix transform, fz_pixmap *dest, const fz_irect *clip)
3044 {
3045 return new_draw_device(ctx, transform, dest, NULL, clip, NULL);
3046 }
3047
3048 fz_device *
fz_new_draw_device_with_proof(fz_context * ctx,fz_matrix transform,fz_pixmap * dest,fz_colorspace * cs)3049 fz_new_draw_device_with_proof(fz_context *ctx, fz_matrix transform, fz_pixmap *dest, fz_colorspace *cs)
3050 {
3051 return new_draw_device(ctx, transform, dest, NULL, NULL, cs);
3052 }
3053
3054 fz_device *
fz_new_draw_device_with_bbox_proof(fz_context * ctx,fz_matrix transform,fz_pixmap * dest,const fz_irect * clip,fz_colorspace * cs)3055 fz_new_draw_device_with_bbox_proof(fz_context *ctx, fz_matrix transform, fz_pixmap *dest, const fz_irect *clip, fz_colorspace *cs)
3056 {
3057 return new_draw_device(ctx, transform, dest, NULL, clip, cs);
3058 }
3059
3060 fz_device *
fz_new_draw_device_type3(fz_context * ctx,fz_matrix transform,fz_pixmap * dest)3061 fz_new_draw_device_type3(fz_context *ctx, fz_matrix transform, fz_pixmap *dest)
3062 {
3063 fz_draw_device *dev = (fz_draw_device*)fz_new_draw_device(ctx, transform, dest);
3064 dev->flags |= FZ_DRAWDEV_FLAGS_TYPE3;
3065 return (fz_device*)dev;
3066 }
3067
3068 fz_irect *
fz_bound_path_accurate(fz_context * ctx,fz_irect * bbox,const fz_irect * scissor,const fz_path * path,const fz_stroke_state * stroke,fz_matrix ctm,float flatness,float linewidth)3069 fz_bound_path_accurate(fz_context *ctx, fz_irect *bbox, const fz_irect *scissor, const fz_path *path, const fz_stroke_state *stroke, fz_matrix ctm, float flatness, float linewidth)
3070 {
3071 fz_rasterizer *rast = fz_new_rasterizer(ctx, NULL);
3072
3073 fz_try(ctx)
3074 {
3075 if (stroke)
3076 (void)fz_flatten_stroke_path(ctx, rast, path, stroke, ctm, flatness, linewidth, scissor, bbox);
3077 else
3078 (void)fz_flatten_fill_path(ctx, rast, path, ctm, flatness, scissor, bbox);
3079 }
3080 fz_always(ctx)
3081 fz_drop_rasterizer(ctx, rast);
3082 fz_catch(ctx)
3083 fz_rethrow(ctx);
3084
3085 return bbox;
3086 }
3087
3088 const char *fz_draw_options_usage =
3089 "Raster output options:\n"
3090 "\trotate=N: rotate rendered pages N degrees counterclockwise\n"
3091 "\tresolution=N: set both X and Y resolution in pixels per inch\n"
3092 "\tx-resolution=N: X resolution of rendered pages in pixels per inch\n"
3093 "\ty-resolution=N: Y resolution of rendered pages in pixels per inch\n"
3094 "\twidth=N: render pages to fit N pixels wide (ignore resolution option)\n"
3095 "\theight=N: render pages to fit N pixels tall (ignore resolution option)\n"
3096 "\tcolorspace=(gray|rgb|cmyk): render using specified colorspace\n"
3097 "\talpha: render pages with alpha channel and transparent background\n"
3098 "\tgraphics=(aaN|cop|app): set the rasterizer to use\n"
3099 "\ttext=(aaN|cop|app): set the rasterizer to use for text\n"
3100 "\t\taaN=antialias with N bits (0 to 8)\n"
3101 "\t\tcop=center of pixel\n"
3102 "\t\tapp=any part of pixel\n"
3103 "\n";
3104
parse_aa_opts(const char * val)3105 static int parse_aa_opts(const char *val)
3106 {
3107 if (fz_option_eq(val, "cop"))
3108 return 9;
3109 if (fz_option_eq(val, "app"))
3110 return 10;
3111 if (val[0] == 'a' && val[1] == 'a' && val[2] >= '0' && val[2] <= '9')
3112 return fz_clampi(fz_atoi(&val[2]), 0, 8);
3113 return 8;
3114 }
3115
3116 fz_draw_options *
fz_parse_draw_options(fz_context * ctx,fz_draw_options * opts,const char * args)3117 fz_parse_draw_options(fz_context *ctx, fz_draw_options *opts, const char *args)
3118 {
3119 const char *val;
3120
3121 memset(opts, 0, sizeof *opts);
3122
3123 opts->x_resolution = 96;
3124 opts->y_resolution = 96;
3125 opts->rotate = 0;
3126 opts->width = 0;
3127 opts->height = 0;
3128 opts->colorspace = fz_device_rgb(ctx);
3129 opts->alpha = 0;
3130 opts->graphics = fz_aa_level(ctx);
3131 opts->text = fz_text_aa_level(ctx);
3132
3133 if (fz_has_option(ctx, args, "rotate", &val))
3134 opts->rotate = fz_atoi(val);
3135 if (fz_has_option(ctx, args, "resolution", &val))
3136 opts->x_resolution = opts->y_resolution = fz_atoi(val);
3137 if (fz_has_option(ctx, args, "x-resolution", &val))
3138 opts->x_resolution = fz_atoi(val);
3139 if (fz_has_option(ctx, args, "y-resolution", &val))
3140 opts->y_resolution = fz_atoi(val);
3141 if (fz_has_option(ctx, args, "width", &val))
3142 opts->width = fz_atoi(val);
3143 if (fz_has_option(ctx, args, "height", &val))
3144 opts->height = fz_atoi(val);
3145 if (fz_has_option(ctx, args, "colorspace", &val))
3146 {
3147 if (fz_option_eq(val, "gray") || fz_option_eq(val, "grey") || fz_option_eq(val, "mono"))
3148 opts->colorspace = fz_device_gray(ctx);
3149 else if (fz_option_eq(val, "rgb"))
3150 opts->colorspace = fz_device_rgb(ctx);
3151 else if (fz_option_eq(val, "cmyk"))
3152 opts->colorspace = fz_device_cmyk(ctx);
3153 else
3154 fz_throw(ctx, FZ_ERROR_GENERIC, "unknown colorspace in options");
3155 }
3156 if (fz_has_option(ctx, args, "alpha", &val))
3157 opts->alpha = fz_option_eq(val, "yes");
3158 if (fz_has_option(ctx, args, "graphics", &val))
3159 opts->text = opts->graphics = parse_aa_opts(val);
3160 if (fz_has_option(ctx, args, "text", &val))
3161 opts->text = parse_aa_opts(val);
3162
3163 /* Sanity check values */
3164 if (opts->x_resolution <= 0) opts->x_resolution = 96;
3165 if (opts->y_resolution <= 0) opts->y_resolution = 96;
3166 if (opts->width < 0) opts->width = 0;
3167 if (opts->height < 0) opts->height = 0;
3168
3169 return opts;
3170 }
3171
3172 fz_device *
fz_new_draw_device_with_options(fz_context * ctx,const fz_draw_options * opts,fz_rect mediabox,fz_pixmap ** pixmap)3173 fz_new_draw_device_with_options(fz_context *ctx, const fz_draw_options *opts, fz_rect mediabox, fz_pixmap **pixmap)
3174 {
3175 fz_aa_context aa = ctx->aa;
3176 float x_zoom = opts->x_resolution / 72.0f;
3177 float y_zoom = opts->y_resolution / 72.0f;
3178 float page_w = mediabox.x1 - mediabox.x0;
3179 float page_h = mediabox.y1 - mediabox.y0;
3180 float w = opts->width;
3181 float h = opts->height;
3182 float x_scale, y_scale;
3183 fz_matrix transform;
3184 fz_irect bbox;
3185 fz_device *dev;
3186
3187 fz_set_rasterizer_graphics_aa_level(ctx, &aa, opts->graphics);
3188 fz_set_rasterizer_text_aa_level(ctx, &aa, opts->text);
3189
3190 if (w > 0)
3191 {
3192 x_scale = w / page_w;
3193 if (h > 0)
3194 y_scale = h / page_h;
3195 else
3196 y_scale = floorf(page_h * x_scale + 0.5f) / page_h;
3197 }
3198 else if (h > 0)
3199 {
3200 y_scale = h / page_h;
3201 x_scale = floorf(page_w * y_scale + 0.5f) / page_w;
3202 }
3203 else
3204 {
3205 x_scale = floorf(page_w * x_zoom + 0.5f) / page_w;
3206 y_scale = floorf(page_h * y_zoom + 0.5f) / page_h;
3207 }
3208
3209 transform = fz_pre_rotate(fz_scale(x_scale, y_scale), opts->rotate);
3210 bbox = fz_irect_from_rect(fz_transform_rect(mediabox, transform));
3211
3212 *pixmap = fz_new_pixmap_with_bbox(ctx, opts->colorspace, bbox, NULL, opts->alpha);
3213 fz_try(ctx)
3214 {
3215 fz_set_pixmap_resolution(ctx, *pixmap, opts->x_resolution, opts->y_resolution);
3216 if (opts->alpha)
3217 fz_clear_pixmap(ctx, *pixmap);
3218 else
3219 fz_clear_pixmap_with_value(ctx, *pixmap, 255);
3220
3221 dev = new_draw_device(ctx, transform, *pixmap, &aa, NULL, NULL);
3222 }
3223 fz_catch(ctx)
3224 {
3225 fz_drop_pixmap(ctx, *pixmap);
3226 *pixmap = NULL;
3227 fz_rethrow(ctx);
3228 }
3229 return dev;
3230 }
3231