Coverage Report

Created: 2023-06-07 06:20

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