comparison mupdf-source/source/fitz/draw-device.c @ 2:b50eed0cc0ef upstream

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