Coverage Report

Created: 2023-11-19 06:58

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