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