Coverage Report

Created: 2026-06-30 07:15

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