Coverage Report

Created: 2025-01-28 06:17

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