Coverage Report

Created: 2024-07-05 06:13

/src/mupdf/source/fitz/separation.c
Line
Count
Source (jump to first uncovered line)
1
// Copyright (C) 2004-2021 Artifex Software, Inc.
2
//
3
// This file is part of MuPDF.
4
//
5
// MuPDF is free software: you can redistribute it and/or modify it under the
6
// terms of the GNU Affero General Public License as published by the Free
7
// Software Foundation, either version 3 of the License, or (at your option)
8
// any later version.
9
//
10
// MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
11
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12
// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13
// details.
14
//
15
// You should have received a copy of the GNU Affero General Public License
16
// along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
17
//
18
// Alternative licensing terms are available from the licensor.
19
// For commercial licensing, see <https://www.artifex.com/> or contact
20
// Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
21
// CA 94129, USA, for further information.
22
23
#include "mupdf/fitz.h"
24
25
#include "color-imp.h"
26
#include "pixmap-imp.h"
27
28
#include <assert.h>
29
#include <string.h>
30
31
enum
32
{
33
  FZ_SEPARATION_DISABLED_RENDER = 3
34
};
35
36
struct fz_separations
37
{
38
  int refs;
39
  int num_separations;
40
  int controllable;
41
  uint32_t state[(2*FZ_MAX_SEPARATIONS + 31) / 32];
42
  fz_colorspace *cs[FZ_MAX_SEPARATIONS];
43
  uint8_t cs_pos[FZ_MAX_SEPARATIONS];
44
  uint32_t rgba[FZ_MAX_SEPARATIONS];
45
  uint32_t cmyk[FZ_MAX_SEPARATIONS];
46
  char *name[FZ_MAX_SEPARATIONS];
47
};
48
49
fz_separations *fz_new_separations(fz_context *ctx, int controllable)
50
0
{
51
0
  fz_separations *sep;
52
53
0
  sep = fz_malloc_struct(ctx, fz_separations);
54
0
  sep->refs = 1;
55
0
  sep->controllable = controllable;
56
57
0
  return sep;
58
0
}
59
60
fz_separations *fz_keep_separations(fz_context *ctx, fz_separations *sep)
61
667k
{
62
667k
  return fz_keep_imp(ctx, sep, &sep->refs);
63
667k
}
64
65
void fz_drop_separations(fz_context *ctx, fz_separations *sep)
66
667k
{
67
667k
  if (fz_drop_imp(ctx, sep, &sep->refs))
68
0
  {
69
0
    int i;
70
0
    for (i = 0; i < sep->num_separations; i++)
71
0
    {
72
0
      fz_free(ctx, sep->name[i]);
73
0
      fz_drop_colorspace(ctx, sep->cs[i]);
74
0
    }
75
0
    fz_free(ctx, sep);
76
0
  }
77
667k
}
78
79
void fz_add_separation(fz_context *ctx, fz_separations *sep, const char *name, fz_colorspace *cs, int colorant)
80
0
{
81
0
  int n;
82
83
0
  if (!sep)
84
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "can't add to non-existent separations");
85
86
0
  n = sep->num_separations;
87
0
  if (n == FZ_MAX_SEPARATIONS)
88
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "too many separations");
89
90
0
  sep->name[n] = fz_strdup(ctx, name);
91
0
  sep->cs[n] = fz_keep_colorspace(ctx, cs);
92
0
  sep->cs_pos[n] = colorant;
93
94
0
  sep->num_separations++;
95
0
}
96
97
void fz_add_separation_equivalents(fz_context *ctx, fz_separations *sep, uint32_t rgba, uint32_t cmyk, const char *name)
98
0
{
99
0
  int n;
100
101
0
  if (!sep)
102
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "can't add to non-existent separations");
103
104
0
  n = sep->num_separations;
105
0
  if (n == FZ_MAX_SEPARATIONS)
106
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "too many separations");
107
108
0
  sep->name[n] = fz_strdup(ctx, name);
109
0
  sep->rgba[n] = rgba;
110
0
  sep->cmyk[n] = cmyk;
111
112
0
  sep->num_separations++;
113
0
}
114
115
void fz_set_separation_behavior(fz_context *ctx, fz_separations *sep, int separation, fz_separation_behavior beh)
116
0
{
117
0
  int shift;
118
0
  fz_separation_behavior old;
119
120
0
  if (!sep || separation < 0 || separation >= sep->num_separations)
121
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "can't control non-existent separation");
122
123
0
  if (beh == FZ_SEPARATION_DISABLED && !sep->controllable)
124
0
    beh = FZ_SEPARATION_DISABLED_RENDER;
125
126
0
  shift = ((2*separation) & 31);
127
0
  separation >>= 4;
128
129
0
  old = (sep->state[separation]>>shift) & 3;
130
131
0
  if (old == (fz_separation_behavior)FZ_SEPARATION_DISABLED_RENDER)
132
0
    old = FZ_SEPARATION_DISABLED;
133
134
  /* If no change, great */
135
0
  if (old == beh)
136
0
    return;
137
138
0
  sep->state[separation] = (sep->state[separation] & ~(3<<shift)) | (beh<<shift);
139
140
  /* FIXME: Could only empty images from the store, or maybe only
141
   * images that depend on separations. */
142
0
  fz_empty_store(ctx);
143
0
}
144
145
static inline fz_separation_behavior
146
sep_state(const fz_separations *sep, int i)
147
0
{
148
0
  return (fz_separation_behavior)((sep->state[i>>5]>>((2*i) & 31)) & 3);
149
0
}
150
151
fz_separation_behavior fz_separation_current_behavior_internal(fz_context *ctx, const fz_separations *sep, int separation)
152
0
{
153
0
  if (!sep || separation < 0 || separation >= sep->num_separations)
154
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "can't disable non-existent separation");
155
156
0
  return sep_state(sep, separation);
157
0
}
158
159
fz_separation_behavior fz_separation_current_behavior(fz_context *ctx, const fz_separations *sep, int separation)
160
0
{
161
0
  int beh = fz_separation_current_behavior_internal(ctx, sep, separation);
162
163
0
  if (beh == FZ_SEPARATION_DISABLED_RENDER)
164
0
    return FZ_SEPARATION_DISABLED;
165
0
  return beh;
166
0
}
167
168
const char *fz_separation_name(fz_context *ctx, const fz_separations *sep, int separation)
169
0
{
170
0
  if (!sep || separation < 0 || separation >= sep->num_separations)
171
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "can't access non-existent separation");
172
173
0
  return sep->name[separation];
174
0
}
175
176
int fz_count_separations(fz_context *ctx, const fz_separations *sep)
177
50
{
178
50
  if (!sep)
179
50
    return 0;
180
0
  return sep->num_separations;
181
50
}
182
183
int fz_count_active_separations(fz_context *ctx, const fz_separations *sep)
184
1.38M
{
185
1.38M
  int i, n, c;
186
187
1.38M
  if (!sep)
188
1.38M
    return 0;
189
0
  n = sep->num_separations;
190
0
  c = 0;
191
0
  for (i = 0; i < n; i++)
192
0
    if (sep_state(sep, i) == FZ_SEPARATION_SPOT)
193
0
      c++;
194
0
  return c;
195
1.38M
}
196
197
int fz_compare_separations(fz_context *ctx, const fz_separations *sep1, const fz_separations *sep2)
198
5.07k
{
199
5.07k
  int i, n1, n2;
200
201
5.07k
  if (sep1 == sep2)
202
5.07k
    return 0; /* Match */
203
0
  if (sep1 == NULL || sep2 == NULL)
204
0
    return 1; /* No match */
205
0
  n1 = sep1->num_separations;
206
0
  n2 = sep2->num_separations;
207
0
  if (n1 != n2)
208
0
    return 1; /* No match */
209
0
  if (sep1->controllable != sep2->controllable)
210
0
    return 1; /* No match */
211
0
  for (i = 0; i < n1; i++)
212
0
  {
213
0
    if (sep_state(sep1, i) != sep_state(sep2, i))
214
0
      return 1; /* No match */
215
0
    if (sep1->name[i] == NULL && sep2->name[i] == NULL)
216
0
    { /* Two unnamed separations match */ }
217
0
    else if (sep1->name[i] == NULL || sep2->name[i] == NULL || strcmp(sep1->name[i], sep2->name[i]))
218
0
      return 1; /* No match */
219
0
    if (sep1->cs[i] != sep2->cs[i] ||
220
0
      sep1->cs_pos[i] != sep2->cs_pos[i] ||
221
0
      sep1->rgba[i] != sep2->rgba[i] ||
222
0
      sep1->cmyk[i] != sep2->cmyk[i])
223
0
      return 1; /* No match */
224
0
  }
225
0
  return 0;
226
0
}
227
228
fz_separations *fz_clone_separations_for_overprint(fz_context *ctx, fz_separations *sep)
229
0
{
230
0
  int i, j, n, c;
231
0
  fz_separations *clone;
232
233
0
  if (!sep)
234
0
    return NULL;
235
236
0
  n = sep->num_separations;
237
0
  if (n == 0)
238
0
    return NULL;
239
0
  c = 0;
240
0
  for (i = 0; i < n; i++)
241
0
  {
242
0
    fz_separation_behavior state = sep_state(sep, i);
243
0
    if (state == FZ_SEPARATION_COMPOSITE)
244
0
      c++;
245
0
  }
246
247
  /* If no composites, then we don't need to create a new seps object
248
   * with the composite ones enabled, so just reuse our current object. */
249
0
  if (c == 0)
250
0
    return fz_keep_separations(ctx, sep);
251
252
  /* We need to clone us a separation structure, with all
253
   * the composite separations marked as enabled. */
254
0
  clone = fz_malloc_struct(ctx, fz_separations);
255
0
  clone->refs = 1;
256
0
  clone->controllable = 0;
257
258
0
  fz_try(ctx)
259
0
  {
260
0
    for (i = 0; i < n; i++)
261
0
    {
262
0
      fz_separation_behavior beh = sep_state(sep, i);
263
0
      if (beh == FZ_SEPARATION_DISABLED)
264
0
        continue;
265
0
      j = clone->num_separations++;
266
0
      if (beh == FZ_SEPARATION_COMPOSITE)
267
0
        beh = FZ_SEPARATION_SPOT;
268
0
      fz_set_separation_behavior(ctx, clone, j, beh);
269
0
      clone->name[j] = sep->name[i] ? fz_strdup(ctx, sep->name[i]) : NULL;
270
0
      clone->cs[j] = fz_keep_colorspace(ctx, sep->cs[i]);
271
0
      clone->cs_pos[j] = sep->cs_pos[i];
272
0
    }
273
0
  }
274
0
  fz_catch(ctx)
275
0
  {
276
0
    fz_drop_separations(ctx, clone);
277
0
    fz_rethrow(ctx);
278
0
  }
279
280
0
  return clone;
281
0
}
282
283
fz_pixmap *
284
fz_clone_pixmap_area_with_different_seps(fz_context *ctx, fz_pixmap *src, const fz_irect *bbox, fz_colorspace *dcs, fz_separations *dseps, fz_color_params color_params, fz_default_colorspaces *default_cs)
285
26.9k
{
286
26.9k
  fz_irect local_bbox;
287
26.9k
  fz_pixmap *dst, *pix;
288
26.9k
  int drop_src = 0;
289
290
26.9k
  if (bbox == NULL)
291
26.9k
  {
292
26.9k
    local_bbox.x0 = src->x;
293
26.9k
    local_bbox.y0 = src->y;
294
26.9k
    local_bbox.x1 = src->x + src->w;
295
26.9k
    local_bbox.y1 = src->y + src->h;
296
26.9k
    bbox = &local_bbox;
297
26.9k
  }
298
299
26.9k
  dst = fz_new_pixmap_with_bbox(ctx, dcs, *bbox, dseps, src->alpha);
300
26.9k
  if (src->flags & FZ_PIXMAP_FLAG_INTERPOLATE)
301
26.9k
    dst->flags |= FZ_PIXMAP_FLAG_INTERPOLATE;
302
0
  else
303
0
    dst->flags &= ~FZ_PIXMAP_FLAG_INTERPOLATE;
304
305
26.9k
  if (fz_colorspace_is_indexed(ctx, src->colorspace))
306
0
  {
307
0
    src = fz_convert_indexed_pixmap_to_base(ctx, src);
308
0
    drop_src = 1;
309
0
  }
310
311
53.9k
  fz_try(ctx)
312
53.9k
    pix = fz_copy_pixmap_area_converting_seps(ctx, src, dst, NULL, color_params, default_cs);
313
53.9k
  fz_always(ctx)
314
26.9k
    if (drop_src)
315
0
      fz_drop_pixmap(ctx, src);
316
26.9k
  fz_catch(ctx)
317
0
  {
318
0
    fz_drop_pixmap(ctx, dst);
319
0
    fz_rethrow(ctx);
320
0
  }
321
322
26.9k
  return pix;
323
26.9k
}
324
325
fz_pixmap *
326
fz_copy_pixmap_area_converting_seps(fz_context *ctx, fz_pixmap *src, fz_pixmap *dst, fz_colorspace *prf, fz_color_params color_params, fz_default_colorspaces *default_cs)
327
26.9k
{
328
26.9k
  int dw = dst->w;
329
26.9k
  int dh = dst->h;
330
26.9k
  fz_separations *sseps = src->seps;
331
26.9k
  fz_separations *dseps = dst->seps;
332
26.9k
  int sseps_n = sseps ? sseps->num_separations : 0;
333
26.9k
  int dseps_n = dseps ? dseps->num_separations : 0;
334
26.9k
  int sstride = src->stride;
335
26.9k
  int dstride = dst->stride;
336
26.9k
  int sn = src->n;
337
26.9k
  int dn = dst->n;
338
26.9k
  int sa = src->alpha;
339
26.9k
  int da = dst->alpha;
340
26.9k
  int ss = src->s;
341
26.9k
  int ds = dst->s;
342
26.9k
  int sc = sn - ss - sa;
343
26.9k
  int dc = dn - ds - da;
344
26.9k
  const unsigned char *sdata = src->samples + sstride * (dst->y - src->y) + (dst->x - src->x) * sn;
345
26.9k
  unsigned char *ddata = dst->samples;
346
26.9k
  int x, y, i, j, k, n;
347
26.9k
  unsigned char mapped[FZ_MAX_COLORS];
348
26.9k
  int unmapped = sseps_n;
349
26.9k
  int src_is_device_n = fz_colorspace_is_device_n(ctx, src->colorspace);
350
26.9k
  fz_colorspace *proof_cs = (prf == src->colorspace ? NULL : prf);
351
352
26.9k
  assert(da == sa);
353
26.9k
  assert(ss == fz_count_active_separations(ctx, sseps));
354
26.9k
  assert(ds == fz_count_active_separations(ctx, dseps));
355
356
26.9k
  dstride -= dn * dw;
357
26.9k
  sstride -= sn * dw;
358
359
26.9k
  if (dst->x < src->x || dst->x + dst->w > src->x + src->w ||
360
26.9k
    dst->y < src->y || dst->y + dst->h > src->y + src-> h)
361
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "Cannot convert pixmap where dst is not within src!");
362
363
  /* Process colorants (and alpha) first */
364
26.9k
  if (dst->colorspace == src->colorspace && proof_cs == NULL && dst->s == 0 && src->s == 0)
365
0
  {
366
    /* Simple copy - no spots to worry about. */
367
0
    unsigned char *dd = ddata;
368
0
    const unsigned char *sd = sdata;
369
0
    for (y = dh; y > 0; y--)
370
0
    {
371
0
      for (x = dw; x > 0; x--)
372
0
      {
373
0
        for (i = 0; i < dc; i++)
374
0
          dd[i] = sd[i];
375
0
        dd += dn;
376
0
        sd += sn;
377
0
        if (da)
378
0
          dd[-1] = sd[-1];
379
0
      }
380
0
      dd += dstride;
381
0
      sd += sstride;
382
0
    }
383
0
  }
384
26.9k
  else if (src_is_device_n)
385
26.9k
  {
386
26.9k
    fz_color_converter cc;
387
388
    /* Init the target pixmap. */
389
26.9k
    if (!da)
390
0
    {
391
      /* No alpha to worry about, just clear it. */
392
0
      fz_clear_pixmap(ctx, dst);
393
0
    }
394
26.9k
    else if (fz_colorspace_is_subtractive(ctx, dst->colorspace))
395
26.9k
    {
396
      /* Subtractive space, so copy the alpha, and set process and spot colors to 0. */
397
26.9k
      unsigned char *dd = ddata;
398
26.9k
      const unsigned char *sd = sdata;
399
26.9k
      int dcs = dc + ds;
400
112k
      for (y = dh; y > 0; y--)
401
85.4k
      {
402
16.5M
        for (x = dw; x > 0; x--)
403
16.4M
        {
404
82.2M
          for (i = 0; i < dcs; i++)
405
65.8M
            dd[i] = 0;
406
16.4M
          dd += dn;
407
16.4M
          sd += sn;
408
16.4M
          dd[-1] = sd[-1];
409
16.4M
        }
410
85.4k
        dd += dstride;
411
85.4k
        sd += sstride;
412
85.4k
      }
413
26.9k
    }
414
21
    else
415
21
    {
416
      /* Additive space; tricky case. We need to copy the alpha, and
417
       * init the process colors "full", and the spots to 0. Because
418
       * we are in an additive space, and premultiplied, this means
419
       * setting the process colors to alpha. */
420
21
      unsigned char *dd = ddata;
421
21
      const unsigned char *sd = sdata + sn - 1;
422
21
      int dcs = dc + ds;
423
1.47k
      for (y = dh; y > 0; y--)
424
1.45k
      {
425
1.07M
        for (x = dw; x > 0; x--)
426
1.07M
        {
427
1.07M
          int a = *sd;
428
4.29M
          for (i = 0; i < dc; i++)
429
3.21M
            dd[i] = a;
430
1.07M
          for (; i < dcs; i++)
431
0
            dd[i] = 0;
432
1.07M
          dd[i] = a;
433
1.07M
          dd += dn;
434
1.07M
          sd += sn;
435
1.07M
        }
436
1.45k
        dd += dstride;
437
1.45k
        sd += sstride;
438
1.45k
      }
439
21
    }
440
441
    /* Now map the colorants down. */
442
26.9k
    n = fz_colorspace_n(ctx, src->colorspace);
443
444
26.9k
    fz_find_color_converter(ctx, &cc, src->colorspace, dst->colorspace, proof_cs, color_params);
445
446
53.9k
    fz_try(ctx)
447
53.9k
    {
448
26.9k
      unmapped = 0;
449
55.1k
      for (i = 0; i < n; i++)
450
28.1k
      {
451
28.1k
        const char *name = fz_colorspace_colorant(ctx, src->colorspace, i);
452
453
28.1k
        mapped[i] = 1;
454
455
28.1k
        if (name)
456
28.1k
        {
457
28.1k
          if (!strcmp(name, "None")) {
458
0
            mapped[i] = 0;
459
0
            continue;
460
0
          }
461
28.1k
          if (!strcmp(name, "All"))
462
0
          {
463
0
            int n1 = dn - da;
464
0
            unsigned char *dd = ddata;
465
0
            const unsigned char *sd = sdata + i;
466
467
0
            for (y = dh; y > 0; y--)
468
0
            {
469
0
              for (x = dw; x > 0; x--)
470
0
              {
471
0
                unsigned char v = *sd;
472
0
                sd += sn;
473
0
                for (k = 0; k < n1; k++)
474
0
                  dd[k] = v;
475
0
                dd += dn;
476
0
              }
477
0
              dd += dstride;
478
0
              sd += sstride;
479
0
            }
480
0
            continue;
481
0
          }
482
110k
          for (j = 0; j < dc; j++)
483
110k
          {
484
110k
            const char *dname = fz_colorspace_colorant(ctx, dst->colorspace, j);
485
110k
            if (dname && !strcmp(name, dname))
486
28.0k
              goto map_device_n_spot;
487
110k
          }
488
34
          for (j = 0; j < dseps_n; j++)
489
0
          {
490
0
            const char *dname = dseps->name[j];
491
0
            if (dname && !strcmp(name, dname))
492
0
            {
493
0
              j += dc;
494
0
              goto map_device_n_spot;
495
0
            }
496
0
          }
497
34
        }
498
34
        if (0)
499
0
        {
500
0
          unsigned char *dd;
501
0
          const unsigned char *sd;
502
28.0k
  map_device_n_spot:
503
          /* Directly map a devicen colorant to a
504
           * component (either process or spot)
505
           * in the destination. */
506
28.0k
          dd = ddata + j;
507
28.0k
          sd = sdata + i;
508
509
120k
          for (y = dh; y > 0; y--)
510
92.3k
          {
511
17.9M
            for (x = dw; x > 0; x--)
512
17.8M
            {
513
17.8M
              *dd = *sd;
514
17.8M
              dd += dn;
515
17.8M
              sd += sn;
516
17.8M
            }
517
92.3k
            dd += dstride;
518
92.3k
            sd += sstride;
519
92.3k
          }
520
28.0k
        }
521
34
        else
522
34
        {
523
34
          unmapped = 1;
524
34
          mapped[i] = 0;
525
34
        }
526
34
      }
527
26.9k
      if (unmapped)
528
21
      {
529
/* The standard spot mapping algorithm assumes that it's reasonable
530
 * to treat the components of deviceN spaces as being orthogonal,
531
 * and to add them together at the end. This avoids a color lookup
532
 * per pixel. The alternative mapping algorithm looks up each
533
 * pixel at a time, and is hence slower. */
534
21
#define ALTERNATIVE_SPOT_MAP
535
#ifndef ALTERNATIVE_SPOT_MAP
536
        for (i = 0; i < n; i++)
537
        {
538
          unsigned char *dd = ddata;
539
          const unsigned char *sd = sdata;
540
          float convert[FZ_MAX_COLORS];
541
          float colors[FZ_MAX_COLORS];
542
543
          if (mapped[i])
544
            continue;
545
546
          /* Src component i is not mapped. We need to convert that down. */
547
          memset(colors, 0, sizeof(float) * n);
548
          colors[i] = 1;
549
          cc.convert(ctx, &cc, colors, convert);
550
551
          if (fz_colorspace_is_subtractive(ctx, dst->colorspace))
552
          {
553
            if (sa)
554
            {
555
              for (y = dh; y > 0; y--)
556
              {
557
                for (x = dw; x > 0; x--)
558
                {
559
                  unsigned char v = sd[i];
560
                  sd += sn;
561
                  if (v != 0)
562
                  {
563
                    int a = dd[-1];
564
                    for (j = 0; j < dc; j++)
565
                      dd[j] = fz_clampi(dd[j] + v * convert[j], 0, a);
566
                  }
567
                  dd += dn;
568
                }
569
                dd += dstride;
570
                sd += sstride;
571
              }
572
            }
573
            else
574
            {
575
              for (y = dh; y > 0; y--)
576
              {
577
                for (x = dw; x > 0; x--)
578
                {
579
                  unsigned char v = sd[i];
580
                  if (v != 0)
581
                  {
582
                    for (j = 0; j < dc; j++)
583
                      dd[j] = fz_clampi(dd[j] + v * convert[j], 0, 255);
584
                  }
585
                  dd += dn;
586
                  sd += sn;
587
                }
588
                dd += dstride;
589
                sd += sstride;
590
              }
591
            }
592
          }
593
          else
594
          {
595
            if (sa)
596
            {
597
              for (y = dh; y > 0; y--)
598
              {
599
                for (x = dw; x > 0; x--)
600
                {
601
                  unsigned char v = sd[i];
602
                  sd += sn;
603
                  if (v != 0)
604
                  {
605
                    int a = sd[-1];
606
                    for (j = 0; j < dc; j++)
607
                      dd[j] = fz_clampi(dd[j] - v * (1-convert[j]), 0, a);
608
                  }
609
                  dd += dn;
610
                }
611
                dd += dstride;
612
                sd += sstride;
613
              }
614
            }
615
            else
616
            {
617
              for (y = dh; y > 0; y--)
618
              {
619
                for (x = dw; x > 0; x--)
620
                {
621
                  unsigned char v = sd[i];
622
                  if (v != 0)
623
                  {
624
                    for (j = 0; j < dc; j++)
625
                      dd[j] = fz_clampi(dd[j] - v * (1-convert[j]), 0, 255);
626
                  }
627
                  dd += dn;
628
                  sd += sn;
629
                }
630
                dd += dstride;
631
                sd += sstride;
632
              }
633
            }
634
          }
635
        }
636
#else
637
/* If space is subtractive then treat spots like Adobe does in Photoshop.
638
 * Which is to just use an equivalent CMYK value.  If we are in an additive
639
 * color space we will need to convert on a pixel-by-pixel basis.
640
 */
641
21
        float convert[FZ_MAX_COLORS];
642
21
        float colors[FZ_MAX_COLORS];
643
644
21
        if (fz_colorspace_is_subtractive(ctx, dst->colorspace))
645
0
        {
646
0
          for (i = 0; i < n; i++)
647
0
          {
648
0
            unsigned char *dd = ddata;
649
0
            const unsigned char *sd = sdata;
650
651
0
            if (mapped[i])
652
0
              continue;
653
654
0
            memset(colors, 0, sizeof(float) * n);
655
0
            colors[i] = 1;
656
0
            cc.convert(ctx, &cc, colors, convert);
657
658
0
            if (sa)
659
0
            {
660
0
              for (y = dh; y > 0; y--)
661
0
              {
662
0
                for (x = dw; x > 0; x--)
663
0
                {
664
0
                  unsigned char v = sd[i];
665
0
                  if (v != 0)
666
0
                  {
667
0
                    unsigned char a = sd[sc];
668
0
                    for (j = 0; j < dc; j++)
669
0
                      dd[j] = fz_clampi(dd[j] + v * convert[j], 0, a);
670
0
                  }
671
0
                  dd += dn;
672
0
                  sd += sn;
673
0
                }
674
0
                dd += dstride;
675
0
                sd += sstride;
676
0
              }
677
0
            }
678
0
            else
679
0
            {
680
0
              for (y = dh; y > 0; y--)
681
0
              {
682
0
                for (x = dw; x > 0; x--)
683
0
                {
684
0
                  unsigned char v = sd[i];
685
0
                  if (v != 0)
686
0
                    for (j = 0; j < dc; j++)
687
0
                      dd[j] = fz_clampi(dd[j] + v * convert[j], 0, 255);
688
0
                  dd += dn;
689
0
                  sd += sn;
690
0
                }
691
0
                dd += dstride;
692
0
                sd += sstride;
693
0
              }
694
0
            }
695
0
          }
696
0
        }
697
21
        else
698
21
        {
699
21
          unsigned char *dd = ddata;
700
21
          const unsigned char *sd = sdata;
701
21
          if (!sa)
702
0
          {
703
0
            for (y = dh; y > 0; y--)
704
0
            {
705
0
              for (x = dw; x > 0; x--)
706
0
              {
707
0
                for (j = 0; j < n; j++)
708
0
                  colors[j] = mapped[j] ? 0 : sd[j] / 255.0f;
709
0
                cc.convert(ctx, &cc, colors, convert);
710
711
0
                for (j = 0; j < dc; j++)
712
0
                  dd[j] = fz_clampi(255 * convert[j], 0, 255);
713
0
                dd += dn;
714
0
                sd += sn;
715
0
              }
716
0
              dd += dstride;
717
0
              sd += sstride;
718
0
            }
719
0
          }
720
21
          else
721
21
          {
722
1.47k
            for (y = dh; y > 0; y--)
723
1.45k
            {
724
1.07M
              for (x = dw; x > 0; x--)
725
1.07M
              {
726
1.07M
                unsigned char a = sd[sc];
727
1.07M
                if (a == 0)
728
0
                  memset(dd, 0, dc);
729
1.07M
                else
730
1.07M
                {
731
1.07M
                  float inva = 1.0f/a;
732
2.24M
                  for (j = 0; j < n; j++)
733
1.17M
                    colors[j] = mapped[j] ? 0 : sd[j] * inva;
734
1.07M
                  cc.convert(ctx, &cc, colors, convert);
735
736
4.29M
                  for (j = 0; j < dc; j++)
737
3.21M
                    dd[j] = fz_clampi(a * convert[j], 0, a);
738
1.07M
                }
739
1.07M
                dd += dn;
740
1.07M
                sd += sn;
741
1.07M
              }
742
1.45k
              dd += dstride;
743
1.45k
              sd += sstride;
744
1.45k
            }
745
21
          }
746
21
        }
747
21
#endif
748
21
      }
749
26.9k
    }
750
53.9k
    fz_always(ctx)
751
26.9k
      fz_drop_color_converter(ctx, &cc);
752
26.9k
    fz_catch(ctx)
753
0
      fz_rethrow(ctx);
754
26.9k
  }
755
0
  else
756
0
  {
757
0
    signed char map[FZ_MAX_COLORS];
758
759
    /* We have a special case here. Converting from CMYK + Spots
760
     * to RGB with less spots, involves folding (at least some of)
761
     * the spots down via their equivalent colors. Merging a spot's
762
     * equivalent colour (generally expressed in CMYK) with an RGB
763
     * one works badly, (presumably because RGB colors have
764
     * different linearity to CMYK ones). For best results we want
765
     * to merge the spots into the CMYK color, and then convert
766
     * that into RGB.  We handle that case here. */
767
0
    if (fz_colorspace_is_subtractive(ctx, src->colorspace) &&
768
0
      !fz_colorspace_is_subtractive(ctx, dst->colorspace) &&
769
0
      src->seps > 0 &&
770
0
      fz_compare_separations(ctx, dst->seps, src->seps))
771
0
    {
772
      /* Converting from CMYK + Spots -> RGB with a change in spots. */
773
0
      fz_pixmap *temp = fz_new_pixmap(ctx, src->colorspace, src->w, src->h, dst->seps, dst->alpha);
774
775
      /* Match the regions exactly (this matters in particular when we are
776
       * using rotation, and the src region is not origined at 0,0 - see bug
777
       * 704726. */
778
0
      temp->x = src->x;
779
0
      temp->y = src->y;
780
781
0
      fz_try(ctx)
782
0
      {
783
0
        temp = fz_copy_pixmap_area_converting_seps(ctx, src, temp, prf, color_params, default_cs);
784
0
        dst =  fz_copy_pixmap_area_converting_seps(ctx, temp, dst, NULL, color_params, default_cs);
785
0
      }
786
0
      fz_always(ctx)
787
0
        fz_drop_pixmap(ctx, temp);
788
0
      fz_catch(ctx)
789
0
        fz_rethrow(ctx);
790
791
0
      return dst;
792
0
    }
793
794
    /* Use a standard pixmap converter to convert the process + alpha. */
795
0
    fz_convert_pixmap_samples(ctx, src, dst, proof_cs, default_cs, fz_default_color_params, 0);
796
797
    /* And handle the spots ourselves. First make a map of what spots go where. */
798
    /* We want to set it up so that:
799
     *    For each source spot, i, mapped[i] != 0 implies that it maps directly to a dest spot.
800
     *    For each dest spot, j, map[j] = the source spot that goes there (or -1 if none).
801
     */
802
0
    for (i = 0; i < sseps_n; i++)
803
0
      mapped[i] = 0;
804
805
0
    for (i = 0; i < dseps_n; i++)
806
0
    {
807
0
      const char *name;
808
0
      int state = sep_state(dseps, i);
809
810
0
      map[i] = -1;
811
0
      if (state != FZ_SEPARATION_SPOT)
812
0
        continue;
813
0
      name = dseps->name[i];
814
0
      if (name == NULL)
815
0
        continue;
816
0
      for (j = 0; j < sseps_n; j++)
817
0
      {
818
0
        const char *sname;
819
0
        if (mapped[j])
820
0
          continue;
821
0
        if (sep_state(sseps, j) != FZ_SEPARATION_SPOT)
822
0
          continue;
823
0
        sname = sseps->name[j];
824
0
        if (sname && !strcmp(name, sname))
825
0
        {
826
0
          map[i] = j;
827
0
          unmapped--;
828
0
          mapped[j] = 1;
829
0
          break;
830
0
        }
831
0
      }
832
0
    }
833
0
    if (sa)
834
0
      map[i] = sseps_n;
835
    /* map[i] is now defined for all 0 <= i < dseps_n+sa */
836
837
    /* Now we need to make d[i] = map[i] < 0 : 0 ? s[map[i]] */
838
0
    if (ds)
839
0
    {
840
0
      unsigned char *dd = ddata + dc;
841
0
      const unsigned char *sd = sdata + sc;
842
0
      for (y = dh; y > 0; y--)
843
0
      {
844
0
        for (x = dw; x > 0; x--)
845
0
        {
846
0
          for (i = 0; i < ds; i++)
847
0
            dd[i] = map[i] < 0 ? 0 : sd[map[i]];
848
0
          dd += dn;
849
0
          sd += sn;
850
0
        }
851
0
        dd += dstride;
852
0
        sd += sstride;
853
0
      }
854
0
    }
855
856
    /* So that's all the process colors, the alpha, and the
857
     * directly mapped spots done. Now, are there any that
858
     * remain unmapped? */
859
0
    if (unmapped)
860
0
    {
861
0
      int m;
862
      /* Still need to handle mapping 'lost' spots down to process colors */
863
0
      for (i = -1, m = 0; m < sseps_n; m++)
864
0
      {
865
0
        float convert[FZ_MAX_COLORS];
866
867
0
        if (mapped[m])
868
0
          continue;
869
0
        if (fz_separation_current_behavior(ctx, sseps, m) != FZ_SEPARATION_SPOT)
870
0
          continue;
871
0
        i++;
872
        /* Src spot m (the i'th one) is not mapped. We need to convert that down. */
873
0
        fz_separation_equivalent(ctx, sseps, m, dst->colorspace, convert, proof_cs, color_params);
874
875
0
        if (fz_colorspace_is_subtractive(ctx, dst->colorspace))
876
0
        {
877
0
          if (fz_colorspace_is_subtractive(ctx, src->colorspace))
878
0
          {
879
0
            unsigned char *dd = ddata;
880
0
            const unsigned char *sd = sdata + sc;
881
882
0
            if (sa)
883
0
            {
884
0
              for (y = dh; y > 0; y--)
885
0
              {
886
0
                for (x = dw; x > 0; x--)
887
0
                {
888
0
                  unsigned char v = sd[i];
889
0
                  if (v != 0)
890
0
                  {
891
0
                    unsigned char a = sd[ss];
892
0
                    for (k = 0; k < dc; k++)
893
0
                      dd[k] = fz_clampi(dd[k] + v * convert[k], 0, a);
894
0
                  }
895
0
                  dd += dn;
896
0
                  sd += sn;
897
0
                }
898
0
                dd += dstride;
899
0
                sd += sstride;
900
0
              }
901
0
            }
902
0
            else
903
0
            {
904
              /* This case is exercised by: -o out%d.pgm -r72 -D -F pgm -stm ../perf-testing-gpdl/pdf/Ad_InDesign.pdf */
905
0
              for (y = dh; y > 0; y--)
906
0
              {
907
0
                for (x = dw; x > 0; x--)
908
0
                {
909
0
                  unsigned char v = sd[i];
910
0
                  if (v != 0)
911
0
                    for (k = 0; k < dc; k++)
912
0
                      dd[k] = fz_clampi(dd[k] + v * convert[k], 0, 255);
913
0
                  dd += dn;
914
0
                  sd += sn;
915
0
                }
916
0
                dd += dstride;
917
0
                sd += sstride;
918
0
              }
919
0
            }
920
0
          }
921
0
          else
922
0
          {
923
0
            unsigned char *dd = ddata;
924
0
            const unsigned char *sd = sdata + sc;
925
926
0
            if (sa)
927
0
            {
928
0
              for (y = dh; y > 0; y--)
929
0
              {
930
0
                for (x = dw; x > 0; x--)
931
0
                {
932
0
                  unsigned char v = sd[i];
933
0
                  if (v != 0)
934
0
                  {
935
0
                    unsigned char a = sd[ss];
936
0
                    for (k = 0; k < dc; k++)
937
0
                      dd[k] = fz_clampi(dd[k] + v * convert[k], 0, a);
938
0
                  }
939
0
                  dd += dn;
940
0
                  sd += sn;
941
0
                }
942
0
                dd += dstride;
943
0
                sd += sstride;
944
0
              }
945
0
            }
946
0
            else
947
0
            {
948
              /* This case is exercised by: -o out.pkm -r72 -D ../MyTests/Bug704778.pdf 1 */
949
0
              for (y = dh; y > 0; y--)
950
0
              {
951
0
                for (x = dw; x > 0; x--)
952
0
                {
953
0
                  unsigned char v = sd[i];
954
0
                  if (v != 0)
955
0
                    for (k = 0; k < dc; k++)
956
0
                      dd[k] = fz_clampi(dd[k] + v * convert[k], 0, 255);
957
0
                  dd += dn;
958
0
                  sd += sn;
959
0
                }
960
0
                dd += dstride;
961
0
                sd += sstride;
962
0
              }
963
0
            }
964
0
          }
965
0
        }
966
0
        else
967
0
        {
968
0
          for (k = 0; k < dc; k++)
969
0
            convert[k] = 1-convert[k];
970
0
          if (fz_colorspace_is_subtractive(ctx, src->colorspace))
971
0
          {
972
0
            unsigned char *dd = ddata;
973
0
            const unsigned char *sd = sdata + sc;
974
975
0
            if (sa)
976
0
            {
977
0
              for (y = dh; y > 0; y--)
978
0
              {
979
0
                for (x = dw; x > 0; x--)
980
0
                {
981
0
                  unsigned char v = sd[i];
982
0
                  if (v != 0)
983
0
                  {
984
0
                    unsigned char a = sd[ss];
985
0
                    for (k = 0; k < dc; k++)
986
0
                      dd[k] = fz_clampi(dd[k] - v * convert[k], 0, a);
987
0
                  }
988
0
                  dd += dn;
989
0
                  sd += sn;
990
0
                }
991
0
                dd += dstride;
992
0
                sd += sstride;
993
0
              }
994
0
            }
995
0
            else
996
0
            {
997
              /* Nothing in the cluster tests this case. */
998
0
              for (y = dh; y > 0; y--)
999
0
              {
1000
0
                for (x = dw; x > 0; x--)
1001
0
                {
1002
0
                  unsigned char v = sd[i];
1003
0
                  if (v != 0)
1004
0
                    for (k = 0; k < dc; k++)
1005
0
                      dd[k] = fz_clampi(dd[k] - v * convert[k], 0, 255);
1006
0
                  dd += dn;
1007
0
                  sd += sn;
1008
0
                }
1009
0
                dd += dstride;
1010
0
                sd += sstride;
1011
0
              }
1012
0
            }
1013
0
          }
1014
0
          else
1015
0
          {
1016
0
            unsigned char *dd = ddata;
1017
0
            const unsigned char *sd = sdata + sc;
1018
1019
0
            if (sa)
1020
0
            {
1021
0
              for (y = dh; y > 0; y--)
1022
0
              {
1023
0
                for (x = dw; x > 0; x--)
1024
0
                {
1025
0
                  unsigned char v = sd[i];
1026
0
                  if (v != 0)
1027
0
                  {
1028
0
                    unsigned char a = sd[ss];
1029
0
                    for (k = 0; k < dc; k++)
1030
0
                      dd[k] = fz_clampi(dd[k] - v * convert[k], 0, a);
1031
0
                  }
1032
0
                  dd += dn;
1033
0
                  sd += sn;
1034
0
                }
1035
0
                dd += dstride;
1036
0
                sd += sstride;
1037
0
              }
1038
0
            }
1039
0
            else
1040
0
            {
1041
              /* This case is exercised by: -o out.png -r72 -D ../MyTests/Bug704778.pdf 1 */
1042
0
              for (y = dh; y > 0; y--)
1043
0
              {
1044
0
                for (x = dw; x > 0; x--)
1045
0
                {
1046
0
                  unsigned char v = sd[i];
1047
0
                  if (v != 0)
1048
0
                    for (k = 0; k < dc; k++)
1049
0
                      dd[k] = fz_clampi(dd[k] - v * convert[k], 0, 255);
1050
0
                  dd += dn;
1051
0
                  sd += sn;
1052
0
                }
1053
0
                dd += dstride;
1054
0
                sd += sstride;
1055
0
              }
1056
0
            }
1057
0
          }
1058
0
        }
1059
0
      }
1060
0
    }
1061
0
  }
1062
1063
26.9k
  return dst;
1064
26.9k
}
1065
1066
void
1067
fz_convert_separation_colors(fz_context *ctx,
1068
  fz_colorspace *src_cs, const float *src_color,
1069
  fz_separations *dst_seps, fz_colorspace *dst_cs, float *dst_color,
1070
  fz_color_params color_params)
1071
0
{
1072
0
  int i, j, n, dc, ds, dn, pred;
1073
0
  float remainders[FZ_MAX_COLORS];
1074
0
  int remaining = 0;
1075
1076
0
  assert(dst_cs && src_cs && dst_color && src_color);
1077
0
  assert(fz_colorspace_is_device_n(ctx, src_cs));
1078
1079
0
  dc = fz_colorspace_n(ctx, dst_cs);
1080
0
  ds = (dst_seps == NULL ? 0: dst_seps->num_separations);
1081
0
  dn = dc + ds;
1082
1083
0
  i = 0;
1084
0
  if (!fz_colorspace_is_subtractive(ctx, dst_cs))
1085
0
    for (; i < dc; i++)
1086
0
      dst_color[i] = 1;
1087
0
  for (; i < dn; i++)
1088
0
    dst_color[i] = 0;
1089
1090
0
  n = fz_colorspace_n(ctx, src_cs);
1091
0
  pred = 0;
1092
0
  for (i = 0; i < n; i++)
1093
0
  {
1094
0
    const char *name = fz_colorspace_colorant(ctx, src_cs, i);
1095
1096
0
    if (name == NULL)
1097
0
      continue;
1098
0
    if (i == 0 && !strcmp(name, "All"))
1099
0
    {
1100
      /* This is only supposed to happen in separation spaces, not DeviceN */
1101
0
      if (n != 1)
1102
0
        fz_warn(ctx, "All found in DeviceN space");
1103
0
      for (i = 0; i < dn; i++)
1104
0
        dst_color[i] = src_color[0];
1105
0
      break;
1106
0
    }
1107
0
    if (!strcmp(name, "None"))
1108
0
      continue;
1109
1110
    /* The most common case is that the colorant we match is the
1111
     * one after the one we matched before, so optimise for that. */
1112
0
    for (j = pred; j < ds; j++)
1113
0
    {
1114
0
      const char *dname = dst_seps->name[j];
1115
0
      if (dname && !strcmp(name, dname))
1116
0
        goto found_sep;
1117
0
    }
1118
0
    for (j = 0; j < pred; j++)
1119
0
    {
1120
0
      const char *dname = dst_seps->name[j];
1121
0
      if (dname && !strcmp(name, dname))
1122
0
        goto found_sep;
1123
0
    }
1124
0
    for (j = 0; j < dc; j++)
1125
0
    {
1126
0
      const char *dname = fz_colorspace_colorant(ctx, dst_cs, j);
1127
0
      if (dname && !strcmp(name, dname))
1128
0
        goto found_process;
1129
0
    }
1130
0
    if (0) {
1131
0
found_sep:
1132
0
      dst_color[j+dc] = src_color[i];
1133
0
      pred = j+1;
1134
0
    }
1135
0
    else if (0)
1136
0
    {
1137
0
found_process:
1138
0
      dst_color[j] += src_color[i];
1139
0
    }
1140
0
    else
1141
0
    {
1142
0
      if (remaining == 0)
1143
0
      {
1144
0
        memset(remainders, 0, sizeof(float) * n);
1145
0
        remaining = 1;
1146
0
      }
1147
0
      remainders[i] = src_color[i];
1148
0
    }
1149
0
  }
1150
1151
0
  if (remaining)
1152
0
  {
1153
    /* There were some spots that didn't copy over */
1154
0
    float converted[FZ_MAX_COLORS];
1155
0
    fz_convert_color(ctx, src_cs, remainders, dst_cs, converted, NULL, color_params);
1156
0
    for (i = 0; i < dc; i++)
1157
0
      dst_color[i] += converted[i];
1158
0
  }
1159
0
}
1160
1161
void
1162
fz_separation_equivalent(fz_context *ctx,
1163
  const fz_separations *seps,
1164
  int i,
1165
  fz_colorspace *dst_cs, float *convert,
1166
  fz_colorspace *prf,
1167
  fz_color_params color_params)
1168
0
{
1169
0
  float colors[FZ_MAX_COLORS];
1170
1171
0
  if (!seps->cs[i])
1172
0
  {
1173
0
    switch (fz_colorspace_n(ctx, dst_cs))
1174
0
    {
1175
0
    case 3:
1176
0
      convert[0] = (seps->rgba[i] & 0xff)/ 255.0f;
1177
0
      convert[1] = ((seps->rgba[i]>>8) & 0xff)/ 255.0f;
1178
0
      convert[2] = ((seps->rgba[i]>>16) & 0xff)/ 255.0f;
1179
0
      convert[3] = ((seps->rgba[i]>>24) & 0xff)/ 255.0f;
1180
0
      return;
1181
0
    case 4:
1182
0
      convert[0] = (seps->cmyk[i] & 0xff)/ 255.0f;
1183
0
      convert[1] = ((seps->cmyk[i]>>8) & 0xff)/ 255.0f;
1184
0
      convert[2] = ((seps->cmyk[i]>>16) & 0xff)/ 255.0f;
1185
0
      convert[3] = ((seps->cmyk[i]>>24) & 0xff)/ 255.0f;
1186
0
      return;
1187
0
    default:
1188
0
      fz_throw(ctx, FZ_ERROR_ARGUMENT, "Cannot return equivalent in this colorspace");
1189
0
    }
1190
0
  }
1191
1192
0
  memset(colors, 0, sizeof(float) * fz_colorspace_n(ctx, seps->cs[i]));
1193
0
  colors[seps->cs_pos[i]] = 1;
1194
0
  fz_convert_color(ctx, seps->cs[i], colors, dst_cs, convert, prf, color_params);
1195
0
}