Coverage Report

Created: 2026-03-31 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mupdf/source/fitz/separation.c
Line
Count
Source
1
// Copyright (C) 2004-2026 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
5
{
62
5
  return fz_keep_imp(ctx, sep, &sep->refs);
63
5
}
64
65
void fz_drop_separations(fz_context *ctx, fz_separations *sep)
66
5
{
67
5
  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
5
}
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
0
{
178
0
  if (!sep)
179
0
    return 0;
180
0
  return sep->num_separations;
181
0
}
182
183
int fz_count_active_separations(fz_context *ctx, const fz_separations *sep)
184
10
{
185
10
  int i, n, c;
186
187
10
  if (!sep)
188
10
    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
10
}
196
197
int fz_compare_separations(fz_context *ctx, const fz_separations *sep1, const fz_separations *sep2)
198
0
{
199
0
  int i, n1, n2;
200
201
0
  if (sep1 == sep2)
202
0
    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
0
{
286
0
  fz_irect local_bbox;
287
0
  fz_pixmap *dst, *pix;
288
0
  int drop_src = 0;
289
290
0
  if (bbox == NULL)
291
0
  {
292
0
    local_bbox.x0 = src->x;
293
0
    local_bbox.y0 = src->y;
294
0
    local_bbox.x1 = src->x + src->w;
295
0
    local_bbox.y1 = src->y + src->h;
296
0
    bbox = &local_bbox;
297
0
  }
298
299
0
  dst = fz_new_pixmap_with_bbox(ctx, dcs, *bbox, dseps, src->alpha);
300
0
  if (src->flags & FZ_PIXMAP_FLAG_INTERPOLATE)
301
0
    dst->flags |= FZ_PIXMAP_FLAG_INTERPOLATE;
302
0
  else
303
0
    dst->flags &= ~FZ_PIXMAP_FLAG_INTERPOLATE;
304
305
0
  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
0
  fz_try(ctx)
312
0
    pix = fz_copy_pixmap_area_converting_seps(ctx, src, dst, NULL, color_params, default_cs);
313
0
  fz_always(ctx)
314
0
    if (drop_src)
315
0
      fz_drop_pixmap(ctx, src);
316
0
  fz_catch(ctx)
317
0
  {
318
0
    fz_drop_pixmap(ctx, dst);
319
0
    fz_rethrow(ctx);
320
0
  }
321
322
0
  return pix;
323
0
}
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
0
{
328
0
  int dw = dst->w;
329
0
  int dh = dst->h;
330
0
  fz_separations *sseps = src->seps;
331
0
  fz_separations *dseps = dst->seps;
332
0
  int sseps_n = sseps ? sseps->num_separations : 0;
333
0
  int dseps_n = dseps ? dseps->num_separations : 0;
334
0
  int sstride = src->stride;
335
0
  int dstride = dst->stride;
336
0
  int sn = src->n;
337
0
  int dn = dst->n;
338
0
  int sa = src->alpha;
339
0
  int da = dst->alpha;
340
0
  int ss = src->s;
341
0
  int ds = dst->s;
342
0
  int sc = sn - ss - sa;
343
0
  int dc = dn - ds - da;
344
0
  const unsigned char *sdata = src->samples + sstride * (dst->y - src->y) + (dst->x - src->x) * sn;
345
0
  unsigned char *ddata = dst->samples;
346
0
  int x, y, i, j, k, n;
347
0
  unsigned char mapped[FZ_MAX_COLORS];
348
0
  int unmapped = sseps_n;
349
0
  int src_is_device_n = fz_colorspace_is_device_n(ctx, src->colorspace);
350
0
  fz_colorspace *proof_cs = (prf == src->colorspace ? NULL : prf);
351
352
0
  assert(da == sa);
353
0
  assert(ss == fz_count_active_separations(ctx, sseps));
354
0
  assert(ds == fz_count_active_separations(ctx, dseps));
355
356
0
  dstride -= dn * dw;
357
0
  sstride -= sn * dw;
358
359
0
  if (dst->x < src->x || dst->x + dst->w > src->x + src->w ||
360
0
    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
0
  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
0
  else if (src_is_device_n)
385
0
  {
386
0
    fz_color_converter cc;
387
388
    /* Init the target pixmap. */
389
0
    if (!da)
390
0
    {
391
      /* No alpha to worry about, just clear it. */
392
0
      fz_clear_pixmap(ctx, dst);
393
0
    }
394
0
    else if (fz_colorspace_is_subtractive(ctx, dst->colorspace))
395
0
    {
396
      /* Subtractive space, so copy the alpha, and set process and spot colors to 0. */
397
0
      unsigned char *dd = ddata;
398
0
      const unsigned char *sd = sdata;
399
0
      int dcs = dc + ds;
400
0
      for (y = dh; y > 0; y--)
401
0
      {
402
0
        for (x = dw; x > 0; x--)
403
0
        {
404
0
          for (i = 0; i < dcs; i++)
405
0
            dd[i] = 0;
406
0
          dd += dn;
407
0
          sd += sn;
408
0
          dd[-1] = sd[-1];
409
0
        }
410
0
        dd += dstride;
411
0
        sd += sstride;
412
0
      }
413
0
    }
414
0
    else
415
0
    {
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
0
      unsigned char *dd = ddata;
421
0
      const unsigned char *sd = sdata + sn - 1;
422
0
      int dcs = dc + ds;
423
0
      for (y = dh; y > 0; y--)
424
0
      {
425
0
        for (x = dw; x > 0; x--)
426
0
        {
427
0
          int a = *sd;
428
0
          for (i = 0; i < dc; i++)
429
0
            dd[i] = a;
430
0
          for (; i < dcs; i++)
431
0
            dd[i] = 0;
432
0
          dd[i] = a;
433
0
          dd += dn;
434
0
          sd += sn;
435
0
        }
436
0
        dd += dstride;
437
0
        sd += sstride;
438
0
      }
439
0
    }
440
441
    /* Now map the colorants down. */
442
0
    n = fz_colorspace_n(ctx, src->colorspace);
443
444
0
    fz_find_color_converter(ctx, &cc, src->colorspace, dst->colorspace, NULL, proof_cs, color_params);
445
446
0
    fz_try(ctx)
447
0
    {
448
0
      unmapped = 0;
449
0
      for (i = 0; i < n; i++)
450
0
      {
451
0
        const char *name = fz_colorspace_colorant(ctx, src->colorspace, i);
452
453
0
        mapped[i] = 1;
454
455
0
        if (name)
456
0
        {
457
0
          if (!strcmp(name, "None")) {
458
0
            mapped[i] = 0;
459
0
            continue;
460
0
          }
461
0
          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
0
          for (j = 0; j < dc; j++)
483
0
          {
484
0
            const char *dname = fz_colorspace_colorant(ctx, dst->colorspace, j);
485
0
            if (dname && !strcmp(name, dname))
486
0
              goto map_device_n_spot;
487
0
          }
488
0
          for (k = 0; k < dseps_n; k++)
489
0
          {
490
0
            const char *dname = dseps->name[k];
491
0
            int state = sep_state(dseps, k);
492
0
            if (state != FZ_SEPARATION_SPOT)
493
0
              continue;
494
0
            if (dname && !strcmp(name, dname))
495
0
              goto map_device_n_spot;
496
0
            j++;
497
0
          }
498
0
        }
499
0
        if (0)
500
0
        {
501
0
          unsigned char *dd;
502
0
          const unsigned char *sd;
503
0
  map_device_n_spot:
504
          /* Directly map a devicen colorant to a
505
           * component (either process or spot)
506
           * in the destination. */
507
0
          dd = ddata + j;
508
0
          sd = sdata + i;
509
510
0
          for (y = dh; y > 0; y--)
511
0
          {
512
0
            for (x = dw; x > 0; x--)
513
0
            {
514
0
              *dd = *sd;
515
0
              dd += dn;
516
0
              sd += sn;
517
0
            }
518
0
            dd += dstride;
519
0
            sd += sstride;
520
0
          }
521
0
        }
522
0
        else
523
0
        {
524
0
          unmapped = 1;
525
0
          mapped[i] = 0;
526
0
        }
527
0
      }
528
0
      if (unmapped)
529
0
      {
530
/* The standard spot mapping algorithm assumes that it's reasonable
531
 * to treat the components of deviceN spaces as being orthogonal,
532
 * and to add them together at the end. This avoids a color lookup
533
 * per pixel. The alternative mapping algorithm looks up each
534
 * pixel at a time, and is hence slower. */
535
0
#define ALTERNATIVE_SPOT_MAP
536
#ifndef ALTERNATIVE_SPOT_MAP
537
        for (i = 0; i < n; i++)
538
        {
539
          unsigned char *dd = ddata;
540
          const unsigned char *sd = sdata;
541
          float convert[FZ_MAX_COLORS];
542
          float colors[FZ_MAX_COLORS];
543
544
          if (mapped[i])
545
            continue;
546
547
          /* Src component i is not mapped. We need to convert that down. */
548
          memset(colors, 0, sizeof(float) * n);
549
          colors[i] = 1;
550
          cc.convert(ctx, &cc, colors, convert);
551
552
          if (fz_colorspace_is_subtractive(ctx, dst->colorspace))
553
          {
554
            if (sa)
555
            {
556
              for (y = dh; y > 0; y--)
557
              {
558
                for (x = dw; x > 0; x--)
559
                {
560
                  unsigned char v = sd[i];
561
                  sd += sn;
562
                  if (v != 0)
563
                  {
564
                    int a = dd[-1];
565
                    for (j = 0; j < dc; j++)
566
                      dd[j] = fz_clampi(dd[j] + v * convert[j], 0, a);
567
                  }
568
                  dd += dn;
569
                }
570
                dd += dstride;
571
                sd += sstride;
572
              }
573
            }
574
            else
575
            {
576
              for (y = dh; y > 0; y--)
577
              {
578
                for (x = dw; x > 0; x--)
579
                {
580
                  unsigned char v = sd[i];
581
                  if (v != 0)
582
                  {
583
                    for (j = 0; j < dc; j++)
584
                      dd[j] = fz_clampi(dd[j] + v * convert[j], 0, 255);
585
                  }
586
                  dd += dn;
587
                  sd += sn;
588
                }
589
                dd += dstride;
590
                sd += sstride;
591
              }
592
            }
593
          }
594
          else
595
          {
596
            if (sa)
597
            {
598
              for (y = dh; y > 0; y--)
599
              {
600
                for (x = dw; x > 0; x--)
601
                {
602
                  unsigned char v = sd[i];
603
                  sd += sn;
604
                  if (v != 0)
605
                  {
606
                    int a = sd[-1];
607
                    for (j = 0; j < dc; j++)
608
                      dd[j] = fz_clampi(dd[j] - v * (1-convert[j]), 0, a);
609
                  }
610
                  dd += dn;
611
                }
612
                dd += dstride;
613
                sd += sstride;
614
              }
615
            }
616
            else
617
            {
618
              for (y = dh; y > 0; y--)
619
              {
620
                for (x = dw; x > 0; x--)
621
                {
622
                  unsigned char v = sd[i];
623
                  if (v != 0)
624
                  {
625
                    for (j = 0; j < dc; j++)
626
                      dd[j] = fz_clampi(dd[j] - v * (1-convert[j]), 0, 255);
627
                  }
628
                  dd += dn;
629
                  sd += sn;
630
                }
631
                dd += dstride;
632
                sd += sstride;
633
              }
634
            }
635
          }
636
        }
637
#else
638
/* If space is subtractive then treat spots like Adobe does in Photoshop.
639
 * Which is to just use an equivalent CMYK value.  If we are in an additive
640
 * color space we will need to convert on a pixel-by-pixel basis.
641
 */
642
0
        float convert[FZ_MAX_COLORS];
643
0
        float colors[FZ_MAX_COLORS];
644
645
0
        if (fz_colorspace_is_subtractive(ctx, dst->colorspace))
646
0
        {
647
0
          for (i = 0; i < n; i++)
648
0
          {
649
0
            unsigned char *dd = ddata;
650
0
            const unsigned char *sd = sdata;
651
652
0
            if (mapped[i])
653
0
              continue;
654
655
0
            memset(colors, 0, sizeof(float) * n);
656
0
            colors[i] = 1;
657
0
            cc.convert(ctx, &cc, colors, convert);
658
659
0
            if (sa)
660
0
            {
661
0
              for (y = dh; y > 0; y--)
662
0
              {
663
0
                for (x = dw; x > 0; x--)
664
0
                {
665
0
                  unsigned char v = sd[i];
666
0
                  if (v != 0)
667
0
                  {
668
0
                    unsigned char a = sd[sc];
669
0
                    for (j = 0; j < dc; j++)
670
0
                      dd[j] = fz_clampi(dd[j] + v * convert[j], 0, a);
671
0
                  }
672
0
                  dd += dn;
673
0
                  sd += sn;
674
0
                }
675
0
                dd += dstride;
676
0
                sd += sstride;
677
0
              }
678
0
            }
679
0
            else
680
0
            {
681
0
              for (y = dh; y > 0; y--)
682
0
              {
683
0
                for (x = dw; x > 0; x--)
684
0
                {
685
0
                  unsigned char v = sd[i];
686
0
                  if (v != 0)
687
0
                    for (j = 0; j < dc; j++)
688
0
                      dd[j] = fz_clampi(dd[j] + v * convert[j], 0, 255);
689
0
                  dd += dn;
690
0
                  sd += sn;
691
0
                }
692
0
                dd += dstride;
693
0
                sd += sstride;
694
0
              }
695
0
            }
696
0
          }
697
0
        }
698
0
        else
699
0
        {
700
0
          unsigned char *dd = ddata;
701
0
          const unsigned char *sd = sdata;
702
0
          if (!sa)
703
0
          {
704
0
            for (y = dh; y > 0; y--)
705
0
            {
706
0
              for (x = dw; x > 0; x--)
707
0
              {
708
0
                for (j = 0; j < n; j++)
709
0
                  colors[j] = mapped[j] ? 0 : sd[j] / 255.0f;
710
0
                cc.convert(ctx, &cc, colors, convert);
711
712
0
                for (j = 0; j < dc; j++)
713
0
                  dd[j] = fz_clampi(255 * convert[j], 0, 255);
714
0
                dd += dn;
715
0
                sd += sn;
716
0
              }
717
0
              dd += dstride;
718
0
              sd += sstride;
719
0
            }
720
0
          }
721
0
          else
722
0
          {
723
0
            for (y = dh; y > 0; y--)
724
0
            {
725
0
              for (x = dw; x > 0; x--)
726
0
              {
727
0
                unsigned char a = sd[sc];
728
0
                if (a == 0)
729
0
                  memset(dd, 0, dc);
730
0
                else
731
0
                {
732
0
                  float inva = 1.0f/a;
733
0
                  for (j = 0; j < n; j++)
734
0
                    colors[j] = mapped[j] ? 0 : sd[j] * inva;
735
0
                  cc.convert(ctx, &cc, colors, convert);
736
737
0
                  for (j = 0; j < dc; j++)
738
0
                    dd[j] = fz_clampi(a * convert[j], 0, a);
739
0
                }
740
0
                dd += dn;
741
0
                sd += sn;
742
0
              }
743
0
              dd += dstride;
744
0
              sd += sstride;
745
0
            }
746
0
          }
747
0
        }
748
0
#endif
749
0
      }
750
0
    }
751
0
    fz_always(ctx)
752
0
      fz_drop_color_converter(ctx, &cc);
753
0
    fz_catch(ctx)
754
0
      fz_rethrow(ctx);
755
0
  }
756
0
  else
757
0
  {
758
0
    signed char map[FZ_MAX_COLORS];
759
760
    /* We have a special case here. Converting from CMYK + Spots
761
     * to RGB with less spots, involves folding (at least some of)
762
     * the spots down via their equivalent colors. Merging a spot's
763
     * equivalent colour (generally expressed in CMYK) with an RGB
764
     * one works badly, (presumably because RGB colors have
765
     * different linearity to CMYK ones). For best results we want
766
     * to merge the spots into the CMYK color, and then convert
767
     * that into RGB.  We handle that case here. */
768
0
    if (fz_colorspace_is_subtractive(ctx, src->colorspace) &&
769
0
      !fz_colorspace_is_subtractive(ctx, dst->colorspace) &&
770
0
      src->seps > 0 &&
771
0
      fz_compare_separations(ctx, dst->seps, src->seps))
772
0
    {
773
      /* Converting from CMYK + Spots -> RGB with a change in spots. */
774
0
      fz_pixmap *temp = fz_new_pixmap(ctx, src->colorspace, src->w, src->h, dst->seps, dst->alpha);
775
776
      /* Match the regions exactly (this matters in particular when we are
777
       * using rotation, and the src region is not origined at 0,0 - see bug
778
       * 704726. */
779
0
      temp->x = src->x;
780
0
      temp->y = src->y;
781
782
0
      fz_var(temp);
783
784
0
      fz_try(ctx)
785
0
      {
786
0
        temp = fz_copy_pixmap_area_converting_seps(ctx, src, temp, prf, color_params, default_cs);
787
0
        dst =  fz_copy_pixmap_area_converting_seps(ctx, temp, dst, NULL, color_params, default_cs);
788
0
      }
789
0
      fz_always(ctx)
790
0
        fz_drop_pixmap(ctx, temp);
791
0
      fz_catch(ctx)
792
0
        fz_rethrow(ctx);
793
794
0
      return dst;
795
0
    }
796
797
    /* Use a standard pixmap converter to convert the process + alpha. */
798
0
    fz_convert_pixmap_samples(ctx, src, dst, proof_cs, default_cs, fz_default_color_params, 0);
799
800
    /* And handle the spots ourselves. First make a map of what spots go where. */
801
    /* We want to set it up so that:
802
     *    For each source spot, i, mapped[i] != 0 implies that it maps directly to a dest spot.
803
     *    For each dest spot, j, map[j] = the source spot that goes there (or -1 if none).
804
     */
805
0
    for (i = 0; i < sseps_n; i++)
806
0
      mapped[i] = 0;
807
808
0
    for (i = 0; i < dseps_n; i++)
809
0
    {
810
0
      const char *name;
811
0
      int state = sep_state(dseps, i);
812
813
0
      map[i] = -1;
814
0
      if (state != FZ_SEPARATION_SPOT)
815
0
        continue;
816
0
      name = dseps->name[i];
817
0
      if (name == NULL)
818
0
        continue;
819
0
      for (j = 0; j < sseps_n; j++)
820
0
      {
821
0
        const char *sname;
822
0
        if (mapped[j])
823
0
          continue;
824
0
        if (sep_state(sseps, j) != FZ_SEPARATION_SPOT)
825
0
          continue;
826
0
        sname = sseps->name[j];
827
0
        if (sname && !strcmp(name, sname))
828
0
        {
829
0
          map[i] = j;
830
0
          unmapped--;
831
0
          mapped[j] = 1;
832
0
          break;
833
0
        }
834
0
      }
835
0
    }
836
0
    if (sa)
837
0
      map[i] = sseps_n;
838
    /* map[i] is now defined for all 0 <= i < dseps_n+sa */
839
840
    /* Now we need to make d[i] = map[i] < 0 : 0 ? s[map[i]] */
841
0
    if (ds)
842
0
    {
843
0
      unsigned char *dd = ddata + dc;
844
0
      const unsigned char *sd = sdata + sc;
845
0
      for (y = dh; y > 0; y--)
846
0
      {
847
0
        for (x = dw; x > 0; x--)
848
0
        {
849
0
          for (i = 0; i < ds; i++)
850
0
            dd[i] = map[i] < 0 ? 0 : sd[map[i]];
851
0
          dd += dn;
852
0
          sd += sn;
853
0
        }
854
0
        dd += dstride;
855
0
        sd += sstride;
856
0
      }
857
0
    }
858
859
    /* So that's all the process colors, the alpha, and the
860
     * directly mapped spots done. Now, are there any that
861
     * remain unmapped? */
862
0
    if (unmapped)
863
0
    {
864
0
      int m;
865
      /* Still need to handle mapping 'lost' spots down to process colors */
866
0
      for (i = -1, m = 0; m < sseps_n; m++)
867
0
      {
868
0
        float convert[FZ_MAX_COLORS];
869
870
0
        if (mapped[m])
871
0
          continue;
872
0
        if (fz_separation_current_behavior(ctx, sseps, m) != FZ_SEPARATION_SPOT)
873
0
          continue;
874
0
        i++;
875
        /* Src spot m (the i'th one) is not mapped. We need to convert that down. */
876
0
        fz_separation_equivalent(ctx, sseps, m, dst->colorspace, convert, proof_cs, color_params);
877
878
0
        if (fz_colorspace_is_subtractive(ctx, dst->colorspace))
879
0
        {
880
0
          if (fz_colorspace_is_subtractive(ctx, src->colorspace))
881
0
          {
882
0
            unsigned char *dd = ddata;
883
0
            const unsigned char *sd = sdata + sc;
884
885
0
            if (sa)
886
0
            {
887
0
              for (y = dh; y > 0; y--)
888
0
              {
889
0
                for (x = dw; x > 0; x--)
890
0
                {
891
0
                  unsigned char v = sd[i];
892
0
                  if (v != 0)
893
0
                  {
894
0
                    unsigned char a = sd[ss];
895
0
                    for (k = 0; k < dc; k++)
896
0
                      dd[k] = fz_clampi(dd[k] + v * convert[k], 0, a);
897
0
                  }
898
0
                  dd += dn;
899
0
                  sd += sn;
900
0
                }
901
0
                dd += dstride;
902
0
                sd += sstride;
903
0
              }
904
0
            }
905
0
            else
906
0
            {
907
              /* This case is exercised by: -o out%d.pgm -r72 -D -F pgm -stm ../perf-testing-gpdl/pdf/Ad_InDesign.pdf */
908
0
              for (y = dh; y > 0; y--)
909
0
              {
910
0
                for (x = dw; x > 0; x--)
911
0
                {
912
0
                  unsigned char v = sd[i];
913
0
                  if (v != 0)
914
0
                    for (k = 0; k < dc; k++)
915
0
                      dd[k] = fz_clampi(dd[k] + v * convert[k], 0, 255);
916
0
                  dd += dn;
917
0
                  sd += sn;
918
0
                }
919
0
                dd += dstride;
920
0
                sd += sstride;
921
0
              }
922
0
            }
923
0
          }
924
0
          else
925
0
          {
926
0
            unsigned char *dd = ddata;
927
0
            const unsigned char *sd = sdata + sc;
928
929
0
            if (sa)
930
0
            {
931
0
              for (y = dh; y > 0; y--)
932
0
              {
933
0
                for (x = dw; x > 0; x--)
934
0
                {
935
0
                  unsigned char v = sd[i];
936
0
                  if (v != 0)
937
0
                  {
938
0
                    unsigned char a = sd[ss];
939
0
                    for (k = 0; k < dc; k++)
940
0
                      dd[k] = fz_clampi(dd[k] + v * convert[k], 0, a);
941
0
                  }
942
0
                  dd += dn;
943
0
                  sd += sn;
944
0
                }
945
0
                dd += dstride;
946
0
                sd += sstride;
947
0
              }
948
0
            }
949
0
            else
950
0
            {
951
              /* This case is exercised by: -o out.pkm -r72 -D ../MyTests/Bug704778.pdf 1 */
952
0
              for (y = dh; y > 0; y--)
953
0
              {
954
0
                for (x = dw; x > 0; x--)
955
0
                {
956
0
                  unsigned char v = sd[i];
957
0
                  if (v != 0)
958
0
                    for (k = 0; k < dc; k++)
959
0
                      dd[k] = fz_clampi(dd[k] + v * convert[k], 0, 255);
960
0
                  dd += dn;
961
0
                  sd += sn;
962
0
                }
963
0
                dd += dstride;
964
0
                sd += sstride;
965
0
              }
966
0
            }
967
0
          }
968
0
        }
969
0
        else
970
0
        {
971
0
          for (k = 0; k < dc; k++)
972
0
            convert[k] = 1-convert[k];
973
0
          if (fz_colorspace_is_subtractive(ctx, src->colorspace))
974
0
          {
975
0
            unsigned char *dd = ddata;
976
0
            const unsigned char *sd = sdata + sc;
977
978
0
            if (sa)
979
0
            {
980
0
              for (y = dh; y > 0; y--)
981
0
              {
982
0
                for (x = dw; x > 0; x--)
983
0
                {
984
0
                  unsigned char v = sd[i];
985
0
                  if (v != 0)
986
0
                  {
987
0
                    unsigned char a = sd[ss];
988
0
                    for (k = 0; k < dc; k++)
989
0
                      dd[k] = fz_clampi(dd[k] - v * convert[k], 0, a);
990
0
                  }
991
0
                  dd += dn;
992
0
                  sd += sn;
993
0
                }
994
0
                dd += dstride;
995
0
                sd += sstride;
996
0
              }
997
0
            }
998
0
            else
999
0
            {
1000
              /* Nothing in the cluster tests this case. */
1001
0
              for (y = dh; y > 0; y--)
1002
0
              {
1003
0
                for (x = dw; x > 0; x--)
1004
0
                {
1005
0
                  unsigned char v = sd[i];
1006
0
                  if (v != 0)
1007
0
                    for (k = 0; k < dc; k++)
1008
0
                      dd[k] = fz_clampi(dd[k] - v * convert[k], 0, 255);
1009
0
                  dd += dn;
1010
0
                  sd += sn;
1011
0
                }
1012
0
                dd += dstride;
1013
0
                sd += sstride;
1014
0
              }
1015
0
            }
1016
0
          }
1017
0
          else
1018
0
          {
1019
0
            unsigned char *dd = ddata;
1020
0
            const unsigned char *sd = sdata + sc;
1021
1022
0
            if (sa)
1023
0
            {
1024
0
              for (y = dh; y > 0; y--)
1025
0
              {
1026
0
                for (x = dw; x > 0; x--)
1027
0
                {
1028
0
                  unsigned char v = sd[i];
1029
0
                  if (v != 0)
1030
0
                  {
1031
0
                    unsigned char a = sd[ss];
1032
0
                    for (k = 0; k < dc; k++)
1033
0
                      dd[k] = fz_clampi(dd[k] - v * convert[k], 0, a);
1034
0
                  }
1035
0
                  dd += dn;
1036
0
                  sd += sn;
1037
0
                }
1038
0
                dd += dstride;
1039
0
                sd += sstride;
1040
0
              }
1041
0
            }
1042
0
            else
1043
0
            {
1044
              /* This case is exercised by: -o out.png -r72 -D ../MyTests/Bug704778.pdf 1 */
1045
0
              for (y = dh; y > 0; y--)
1046
0
              {
1047
0
                for (x = dw; x > 0; x--)
1048
0
                {
1049
0
                  unsigned char v = sd[i];
1050
0
                  if (v != 0)
1051
0
                    for (k = 0; k < dc; k++)
1052
0
                      dd[k] = fz_clampi(dd[k] - v * convert[k], 0, 255);
1053
0
                  dd += dn;
1054
0
                  sd += sn;
1055
0
                }
1056
0
                dd += dstride;
1057
0
                sd += sstride;
1058
0
              }
1059
0
            }
1060
0
          }
1061
0
        }
1062
0
      }
1063
0
    }
1064
0
  }
1065
1066
0
  return dst;
1067
0
}
1068
1069
void
1070
fz_convert_separation_colors(fz_context *ctx,
1071
  fz_colorspace *src_cs, const float *src_color,
1072
  fz_separations *dst_seps, fz_colorspace *dst_cs, float *dst_color,
1073
  fz_color_params color_params)
1074
0
{
1075
0
  int i, j, k, n, dc, ds, dn;
1076
0
  float remainders[FZ_MAX_COLORS];
1077
0
  int remaining = 0;
1078
1079
0
  assert(dst_cs && src_cs && dst_color && src_color);
1080
0
  assert(fz_colorspace_is_device_n(ctx, src_cs));
1081
1082
0
  dc = fz_colorspace_n(ctx, dst_cs);
1083
0
  ds = (dst_seps == NULL ? 0: dst_seps->num_separations);
1084
0
  dn = dc + ds;
1085
1086
0
  i = 0;
1087
0
  if (!fz_colorspace_is_subtractive(ctx, dst_cs))
1088
0
    for (; i < dc; i++)
1089
0
      dst_color[i] = 1;
1090
0
  for (; i < dn; i++)
1091
0
    dst_color[i] = 0;
1092
1093
0
  n = fz_colorspace_n(ctx, src_cs);
1094
0
  for (i = 0; i < n; i++)
1095
0
  {
1096
0
    const char *name = fz_colorspace_colorant(ctx, src_cs, i);
1097
1098
0
    if (name == NULL)
1099
0
      continue;
1100
0
    if (i == 0 && !strcmp(name, "All"))
1101
0
    {
1102
      /* This is only supposed to happen in separation spaces, not DeviceN */
1103
0
      if (n != 1)
1104
0
        fz_warn(ctx, "All found in DeviceN space");
1105
0
      for (i = 0; i < dn; i++)
1106
0
        dst_color[i] = src_color[0];
1107
0
      break;
1108
0
    }
1109
0
    if (!strcmp(name, "None"))
1110
0
      continue;
1111
1112
0
    k = 0;
1113
0
    for (j = 0; j < ds; j++)
1114
0
    {
1115
0
      fz_separation_behavior beh = fz_separation_current_behavior(ctx, dst_seps, j);
1116
0
      const char *dname = dst_seps->name[j];
1117
0
      if (dname && !strcmp(name, dname))
1118
0
      {
1119
0
        if (beh == FZ_SEPARATION_DISABLED)
1120
0
          goto found_disabled;
1121
0
        goto found_sep;
1122
0
      }
1123
0
      if (beh != FZ_SEPARATION_SPOT)
1124
0
        continue;
1125
0
      k++;
1126
0
    }
1127
0
    for (j = 0; j < dc; j++)
1128
0
    {
1129
0
      const char *dname = fz_colorspace_colorant(ctx, dst_cs, j);
1130
0
      if (dname && !strcmp(name, dname))
1131
0
        goto found_process;
1132
0
    }
1133
0
    if (0) {
1134
0
found_sep:
1135
0
      dst_color[k+dc] = src_color[i];
1136
0
    }
1137
0
    else if (0)
1138
0
    {
1139
0
found_process:
1140
0
      dst_color[j] += src_color[i];
1141
0
    }
1142
0
    else if (0)
1143
0
    {
1144
0
found_disabled:
1145
      /* Don't do anything with this value */
1146
0
      {}
1147
0
    }
1148
0
    else
1149
0
    {
1150
0
      if (remaining == 0)
1151
0
      {
1152
0
        memset(remainders, 0, sizeof(float) * n);
1153
0
        remaining = 1;
1154
0
      }
1155
0
      remainders[i] = src_color[i];
1156
0
    }
1157
0
  }
1158
1159
0
  if (remaining)
1160
0
  {
1161
    /* There were some spots that didn't copy over */
1162
0
    float converted[FZ_MAX_COLORS];
1163
0
    fz_convert_color(ctx, src_cs, remainders, dst_cs, converted, NULL, color_params);
1164
0
    for (i = 0; i < dc; i++)
1165
0
      dst_color[i] += converted[i];
1166
0
  }
1167
0
}
1168
1169
void
1170
fz_separation_equivalent(fz_context *ctx,
1171
  const fz_separations *seps,
1172
  int i,
1173
  fz_colorspace *dst_cs, float *convert,
1174
  fz_colorspace *prf,
1175
  fz_color_params color_params)
1176
0
{
1177
0
  float colors[FZ_MAX_COLORS];
1178
1179
0
  if (!seps->cs[i])
1180
0
  {
1181
0
    switch (fz_colorspace_n(ctx, dst_cs))
1182
0
    {
1183
0
    case 3:
1184
0
      convert[0] = (seps->rgba[i] & 0xff)/ 255.0f;
1185
0
      convert[1] = ((seps->rgba[i]>>8) & 0xff)/ 255.0f;
1186
0
      convert[2] = ((seps->rgba[i]>>16) & 0xff)/ 255.0f;
1187
0
      convert[3] = ((seps->rgba[i]>>24) & 0xff)/ 255.0f;
1188
0
      return;
1189
0
    case 4:
1190
0
      convert[0] = (seps->cmyk[i] & 0xff)/ 255.0f;
1191
0
      convert[1] = ((seps->cmyk[i]>>8) & 0xff)/ 255.0f;
1192
0
      convert[2] = ((seps->cmyk[i]>>16) & 0xff)/ 255.0f;
1193
0
      convert[3] = ((seps->cmyk[i]>>24) & 0xff)/ 255.0f;
1194
0
      return;
1195
0
    default:
1196
0
      fz_throw(ctx, FZ_ERROR_ARGUMENT, "Cannot return equivalent in this colorspace");
1197
0
    }
1198
0
  }
1199
1200
0
  memset(colors, 0, sizeof(float) * fz_colorspace_n(ctx, seps->cs[i]));
1201
0
  colors[seps->cs_pos[i]] = 1;
1202
0
  fz_convert_color(ctx, seps->cs[i], colors, dst_cs, convert, prf, color_params);
1203
0
}
1204
1205
static void
1206
convert_by_copying_separations(fz_context *ctx, fz_color_converter *cc, const float *src, float *dst)
1207
0
{
1208
0
  int i, o;
1209
0
  int n = cc->dst_n;
1210
0
  fz_separations *dseps = (fz_separations *)cc->opaque;
1211
1212
0
  for (i = 0; i < n; i++)
1213
0
    dst[i] = 0;
1214
1215
0
  n = dseps->num_separations;
1216
0
  o = cc->ds->n;
1217
0
  for (i = 0; i < n; i++)
1218
0
    if (dseps->cs[i] == cc->ss)
1219
0
      dst[o+i] = src[dseps->cs_pos[i]];
1220
0
}
1221
1222
int
1223
fz_init_separation_copy_color_converter(fz_context *ctx, fz_color_converter *cc, fz_colorspace *ss, fz_colorspace *ds, fz_separations *dseps, fz_colorspace *is, fz_color_params params)
1224
0
{
1225
0
  int i, n;
1226
1227
  /* No idea how to cope with intermediate space here. Bale. */
1228
0
  if (is != NULL && is != ss)
1229
0
    return 0;
1230
1231
  /* If all the separations for ss are catered for in dseps, we can just copy the values. */
1232
0
  n = 0;
1233
0
  for (i = 0; i < dseps->num_separations; i++)
1234
0
  {
1235
0
    if (dseps->cs[i] == ss)
1236
0
      n++;
1237
0
  }
1238
1239
  /* If all of the components of ss were found, we're happy. (We assume the destination space
1240
   * doesn't have any component twice.) */
1241
0
  if (n != ss->n)
1242
0
    return 0;
1243
1244
0
  cc->ss = ss;
1245
0
  cc->ss_via = NULL;
1246
0
  cc->ds = ds;
1247
0
  cc->opaque = dseps;
1248
0
  cc->convert = convert_by_copying_separations;
1249
1250
0
  return 1;
1251
0
}