comparison mupdf-source/source/fitz/separation.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
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 {
51 fz_separations *sep;
52
53 sep = fz_malloc_struct(ctx, fz_separations);
54 sep->refs = 1;
55 sep->controllable = controllable;
56
57 return sep;
58 }
59
60 fz_separations *fz_keep_separations(fz_context *ctx, fz_separations *sep)
61 {
62 return fz_keep_imp(ctx, sep, &sep->refs);
63 }
64
65 void fz_drop_separations(fz_context *ctx, fz_separations *sep)
66 {
67 if (fz_drop_imp(ctx, sep, &sep->refs))
68 {
69 int i;
70 for (i = 0; i < sep->num_separations; i++)
71 {
72 fz_free(ctx, sep->name[i]);
73 fz_drop_colorspace(ctx, sep->cs[i]);
74 }
75 fz_free(ctx, sep);
76 }
77 }
78
79 void fz_add_separation(fz_context *ctx, fz_separations *sep, const char *name, fz_colorspace *cs, int colorant)
80 {
81 int n;
82
83 if (!sep)
84 fz_throw(ctx, FZ_ERROR_ARGUMENT, "can't add to non-existent separations");
85
86 n = sep->num_separations;
87 if (n == FZ_MAX_SEPARATIONS)
88 fz_throw(ctx, FZ_ERROR_ARGUMENT, "too many separations");
89
90 sep->name[n] = fz_strdup(ctx, name);
91 sep->cs[n] = fz_keep_colorspace(ctx, cs);
92 sep->cs_pos[n] = colorant;
93
94 sep->num_separations++;
95 }
96
97 void fz_add_separation_equivalents(fz_context *ctx, fz_separations *sep, uint32_t rgba, uint32_t cmyk, const char *name)
98 {
99 int n;
100
101 if (!sep)
102 fz_throw(ctx, FZ_ERROR_ARGUMENT, "can't add to non-existent separations");
103
104 n = sep->num_separations;
105 if (n == FZ_MAX_SEPARATIONS)
106 fz_throw(ctx, FZ_ERROR_ARGUMENT, "too many separations");
107
108 sep->name[n] = fz_strdup(ctx, name);
109 sep->rgba[n] = rgba;
110 sep->cmyk[n] = cmyk;
111
112 sep->num_separations++;
113 }
114
115 void fz_set_separation_behavior(fz_context *ctx, fz_separations *sep, int separation, fz_separation_behavior beh)
116 {
117 int shift;
118 fz_separation_behavior old;
119
120 if (!sep || separation < 0 || separation >= sep->num_separations)
121 fz_throw(ctx, FZ_ERROR_ARGUMENT, "can't control non-existent separation");
122
123 if (beh == FZ_SEPARATION_DISABLED && !sep->controllable)
124 beh = FZ_SEPARATION_DISABLED_RENDER;
125
126 shift = ((2*separation) & 31);
127 separation >>= 4;
128
129 old = (sep->state[separation]>>shift) & 3;
130
131 if (old == (fz_separation_behavior)FZ_SEPARATION_DISABLED_RENDER)
132 old = FZ_SEPARATION_DISABLED;
133
134 /* If no change, great */
135 if (old == beh)
136 return;
137
138 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 fz_empty_store(ctx);
143 }
144
145 static inline fz_separation_behavior
146 sep_state(const fz_separations *sep, int i)
147 {
148 return (fz_separation_behavior)((sep->state[i>>5]>>((2*i) & 31)) & 3);
149 }
150
151 fz_separation_behavior fz_separation_current_behavior_internal(fz_context *ctx, const fz_separations *sep, int separation)
152 {
153 if (!sep || separation < 0 || separation >= sep->num_separations)
154 fz_throw(ctx, FZ_ERROR_ARGUMENT, "can't disable non-existent separation");
155
156 return sep_state(sep, separation);
157 }
158
159 fz_separation_behavior fz_separation_current_behavior(fz_context *ctx, const fz_separations *sep, int separation)
160 {
161 int beh = fz_separation_current_behavior_internal(ctx, sep, separation);
162
163 if (beh == FZ_SEPARATION_DISABLED_RENDER)
164 return FZ_SEPARATION_DISABLED;
165 return beh;
166 }
167
168 const char *fz_separation_name(fz_context *ctx, const fz_separations *sep, int separation)
169 {
170 if (!sep || separation < 0 || separation >= sep->num_separations)
171 fz_throw(ctx, FZ_ERROR_ARGUMENT, "can't access non-existent separation");
172
173 return sep->name[separation];
174 }
175
176 int fz_count_separations(fz_context *ctx, const fz_separations *sep)
177 {
178 if (!sep)
179 return 0;
180 return sep->num_separations;
181 }
182
183 int fz_count_active_separations(fz_context *ctx, const fz_separations *sep)
184 {
185 int i, n, c;
186
187 if (!sep)
188 return 0;
189 n = sep->num_separations;
190 c = 0;
191 for (i = 0; i < n; i++)
192 if (sep_state(sep, i) == FZ_SEPARATION_SPOT)
193 c++;
194 return c;
195 }
196
197 int fz_compare_separations(fz_context *ctx, const fz_separations *sep1, const fz_separations *sep2)
198 {
199 int i, n1, n2;
200
201 if (sep1 == sep2)
202 return 0; /* Match */
203 if (sep1 == NULL || sep2 == NULL)
204 return 1; /* No match */
205 n1 = sep1->num_separations;
206 n2 = sep2->num_separations;
207 if (n1 != n2)
208 return 1; /* No match */
209 if (sep1->controllable != sep2->controllable)
210 return 1; /* No match */
211 for (i = 0; i < n1; i++)
212 {
213 if (sep_state(sep1, i) != sep_state(sep2, i))
214 return 1; /* No match */
215 if (sep1->name[i] == NULL && sep2->name[i] == NULL)
216 { /* Two unnamed separations match */ }
217 else if (sep1->name[i] == NULL || sep2->name[i] == NULL || strcmp(sep1->name[i], sep2->name[i]))
218 return 1; /* No match */
219 if (sep1->cs[i] != sep2->cs[i] ||
220 sep1->cs_pos[i] != sep2->cs_pos[i] ||
221 sep1->rgba[i] != sep2->rgba[i] ||
222 sep1->cmyk[i] != sep2->cmyk[i])
223 return 1; /* No match */
224 }
225 return 0;
226 }
227
228 fz_separations *fz_clone_separations_for_overprint(fz_context *ctx, fz_separations *sep)
229 {
230 int i, j, n, c;
231 fz_separations *clone;
232
233 if (!sep)
234 return NULL;
235
236 n = sep->num_separations;
237 if (n == 0)
238 return NULL;
239 c = 0;
240 for (i = 0; i < n; i++)
241 {
242 fz_separation_behavior state = sep_state(sep, i);
243 if (state == FZ_SEPARATION_COMPOSITE)
244 c++;
245 }
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 if (c == 0)
250 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 clone = fz_malloc_struct(ctx, fz_separations);
255 clone->refs = 1;
256 clone->controllable = 0;
257
258 fz_try(ctx)
259 {
260 for (i = 0; i < n; i++)
261 {
262 fz_separation_behavior beh = sep_state(sep, i);
263 if (beh == FZ_SEPARATION_DISABLED)
264 continue;
265 j = clone->num_separations++;
266 if (beh == FZ_SEPARATION_COMPOSITE)
267 beh = FZ_SEPARATION_SPOT;
268 fz_set_separation_behavior(ctx, clone, j, beh);
269 clone->name[j] = sep->name[i] ? fz_strdup(ctx, sep->name[i]) : NULL;
270 clone->cs[j] = fz_keep_colorspace(ctx, sep->cs[i]);
271 clone->cs_pos[j] = sep->cs_pos[i];
272 }
273 }
274 fz_catch(ctx)
275 {
276 fz_drop_separations(ctx, clone);
277 fz_rethrow(ctx);
278 }
279
280 return clone;
281 }
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 {
286 fz_irect local_bbox;
287 fz_pixmap *dst, *pix;
288 int drop_src = 0;
289
290 if (bbox == NULL)
291 {
292 local_bbox.x0 = src->x;
293 local_bbox.y0 = src->y;
294 local_bbox.x1 = src->x + src->w;
295 local_bbox.y1 = src->y + src->h;
296 bbox = &local_bbox;
297 }
298
299 dst = fz_new_pixmap_with_bbox(ctx, dcs, *bbox, dseps, src->alpha);
300 if (src->flags & FZ_PIXMAP_FLAG_INTERPOLATE)
301 dst->flags |= FZ_PIXMAP_FLAG_INTERPOLATE;
302 else
303 dst->flags &= ~FZ_PIXMAP_FLAG_INTERPOLATE;
304
305 if (fz_colorspace_is_indexed(ctx, src->colorspace))
306 {
307 src = fz_convert_indexed_pixmap_to_base(ctx, src);
308 drop_src = 1;
309 }
310
311 fz_try(ctx)
312 pix = fz_copy_pixmap_area_converting_seps(ctx, src, dst, NULL, color_params, default_cs);
313 fz_always(ctx)
314 if (drop_src)
315 fz_drop_pixmap(ctx, src);
316 fz_catch(ctx)
317 {
318 fz_drop_pixmap(ctx, dst);
319 fz_rethrow(ctx);
320 }
321
322 return pix;
323 }
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 {
328 int dw = dst->w;
329 int dh = dst->h;
330 fz_separations *sseps = src->seps;
331 fz_separations *dseps = dst->seps;
332 int sseps_n = sseps ? sseps->num_separations : 0;
333 int dseps_n = dseps ? dseps->num_separations : 0;
334 int sstride = src->stride;
335 int dstride = dst->stride;
336 int sn = src->n;
337 int dn = dst->n;
338 int sa = src->alpha;
339 int da = dst->alpha;
340 int ss = src->s;
341 int ds = dst->s;
342 int sc = sn - ss - sa;
343 int dc = dn - ds - da;
344 const unsigned char *sdata = src->samples + sstride * (dst->y - src->y) + (dst->x - src->x) * sn;
345 unsigned char *ddata = dst->samples;
346 int x, y, i, j, k, n;
347 unsigned char mapped[FZ_MAX_COLORS];
348 int unmapped = sseps_n;
349 int src_is_device_n = fz_colorspace_is_device_n(ctx, src->colorspace);
350 fz_colorspace *proof_cs = (prf == src->colorspace ? NULL : prf);
351
352 assert(da == sa);
353 assert(ss == fz_count_active_separations(ctx, sseps));
354 assert(ds == fz_count_active_separations(ctx, dseps));
355
356 dstride -= dn * dw;
357 sstride -= sn * dw;
358
359 if (dst->x < src->x || dst->x + dst->w > src->x + src->w ||
360 dst->y < src->y || dst->y + dst->h > src->y + src-> h)
361 fz_throw(ctx, FZ_ERROR_ARGUMENT, "Cannot convert pixmap where dst is not within src!");
362
363 /* Process colorants (and alpha) first */
364 if (dst->colorspace == src->colorspace && proof_cs == NULL && dst->s == 0 && src->s == 0)
365 {
366 /* Simple copy - no spots to worry about. */
367 unsigned char *dd = ddata;
368 const unsigned char *sd = sdata;
369 for (y = dh; y > 0; y--)
370 {
371 for (x = dw; x > 0; x--)
372 {
373 for (i = 0; i < dc; i++)
374 dd[i] = sd[i];
375 dd += dn;
376 sd += sn;
377 if (da)
378 dd[-1] = sd[-1];
379 }
380 dd += dstride;
381 sd += sstride;
382 }
383 }
384 else if (src_is_device_n)
385 {
386 fz_color_converter cc;
387
388 /* Init the target pixmap. */
389 if (!da)
390 {
391 /* No alpha to worry about, just clear it. */
392 fz_clear_pixmap(ctx, dst);
393 }
394 else if (fz_colorspace_is_subtractive(ctx, dst->colorspace))
395 {
396 /* Subtractive space, so copy the alpha, and set process and spot colors to 0. */
397 unsigned char *dd = ddata;
398 const unsigned char *sd = sdata;
399 int dcs = dc + ds;
400 for (y = dh; y > 0; y--)
401 {
402 for (x = dw; x > 0; x--)
403 {
404 for (i = 0; i < dcs; i++)
405 dd[i] = 0;
406 dd += dn;
407 sd += sn;
408 dd[-1] = sd[-1];
409 }
410 dd += dstride;
411 sd += sstride;
412 }
413 }
414 else
415 {
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 unsigned char *dd = ddata;
421 const unsigned char *sd = sdata + sn - 1;
422 int dcs = dc + ds;
423 for (y = dh; y > 0; y--)
424 {
425 for (x = dw; x > 0; x--)
426 {
427 int a = *sd;
428 for (i = 0; i < dc; i++)
429 dd[i] = a;
430 for (; i < dcs; i++)
431 dd[i] = 0;
432 dd[i] = a;
433 dd += dn;
434 sd += sn;
435 }
436 dd += dstride;
437 sd += sstride;
438 }
439 }
440
441 /* Now map the colorants down. */
442 n = fz_colorspace_n(ctx, src->colorspace);
443
444 fz_find_color_converter(ctx, &cc, src->colorspace, dst->colorspace, NULL, proof_cs, color_params);
445
446 fz_try(ctx)
447 {
448 unmapped = 0;
449 for (i = 0; i < n; i++)
450 {
451 const char *name = fz_colorspace_colorant(ctx, src->colorspace, i);
452
453 mapped[i] = 1;
454
455 if (name)
456 {
457 if (!strcmp(name, "None")) {
458 mapped[i] = 0;
459 continue;
460 }
461 if (!strcmp(name, "All"))
462 {
463 int n1 = dn - da;
464 unsigned char *dd = ddata;
465 const unsigned char *sd = sdata + i;
466
467 for (y = dh; y > 0; y--)
468 {
469 for (x = dw; x > 0; x--)
470 {
471 unsigned char v = *sd;
472 sd += sn;
473 for (k = 0; k < n1; k++)
474 dd[k] = v;
475 dd += dn;
476 }
477 dd += dstride;
478 sd += sstride;
479 }
480 continue;
481 }
482 for (j = 0; j < dc; j++)
483 {
484 const char *dname = fz_colorspace_colorant(ctx, dst->colorspace, j);
485 if (dname && !strcmp(name, dname))
486 goto map_device_n_spot;
487 }
488 for (j = 0; j < dseps_n; j++)
489 {
490 const char *dname = dseps->name[j];
491 if (dname && !strcmp(name, dname))
492 {
493 j += dc;
494 goto map_device_n_spot;
495 }
496 }
497 }
498 if (0)
499 {
500 unsigned char *dd;
501 const unsigned char *sd;
502 map_device_n_spot:
503 /* Directly map a devicen colorant to a
504 * component (either process or spot)
505 * in the destination. */
506 dd = ddata + j;
507 sd = sdata + i;
508
509 for (y = dh; y > 0; y--)
510 {
511 for (x = dw; x > 0; x--)
512 {
513 *dd = *sd;
514 dd += dn;
515 sd += sn;
516 }
517 dd += dstride;
518 sd += sstride;
519 }
520 }
521 else
522 {
523 unmapped = 1;
524 mapped[i] = 0;
525 }
526 }
527 if (unmapped)
528 {
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 #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 float convert[FZ_MAX_COLORS];
642 float colors[FZ_MAX_COLORS];
643
644 if (fz_colorspace_is_subtractive(ctx, dst->colorspace))
645 {
646 for (i = 0; i < n; i++)
647 {
648 unsigned char *dd = ddata;
649 const unsigned char *sd = sdata;
650
651 if (mapped[i])
652 continue;
653
654 memset(colors, 0, sizeof(float) * n);
655 colors[i] = 1;
656 cc.convert(ctx, &cc, colors, convert);
657
658 if (sa)
659 {
660 for (y = dh; y > 0; y--)
661 {
662 for (x = dw; x > 0; x--)
663 {
664 unsigned char v = sd[i];
665 if (v != 0)
666 {
667 unsigned char a = sd[sc];
668 for (j = 0; j < dc; j++)
669 dd[j] = fz_clampi(dd[j] + v * convert[j], 0, a);
670 }
671 dd += dn;
672 sd += sn;
673 }
674 dd += dstride;
675 sd += sstride;
676 }
677 }
678 else
679 {
680 for (y = dh; y > 0; y--)
681 {
682 for (x = dw; x > 0; x--)
683 {
684 unsigned char v = sd[i];
685 if (v != 0)
686 for (j = 0; j < dc; j++)
687 dd[j] = fz_clampi(dd[j] + v * convert[j], 0, 255);
688 dd += dn;
689 sd += sn;
690 }
691 dd += dstride;
692 sd += sstride;
693 }
694 }
695 }
696 }
697 else
698 {
699 unsigned char *dd = ddata;
700 const unsigned char *sd = sdata;
701 if (!sa)
702 {
703 for (y = dh; y > 0; y--)
704 {
705 for (x = dw; x > 0; x--)
706 {
707 for (j = 0; j < n; j++)
708 colors[j] = mapped[j] ? 0 : sd[j] / 255.0f;
709 cc.convert(ctx, &cc, colors, convert);
710
711 for (j = 0; j < dc; j++)
712 dd[j] = fz_clampi(255 * convert[j], 0, 255);
713 dd += dn;
714 sd += sn;
715 }
716 dd += dstride;
717 sd += sstride;
718 }
719 }
720 else
721 {
722 for (y = dh; y > 0; y--)
723 {
724 for (x = dw; x > 0; x--)
725 {
726 unsigned char a = sd[sc];
727 if (a == 0)
728 memset(dd, 0, dc);
729 else
730 {
731 float inva = 1.0f/a;
732 for (j = 0; j < n; j++)
733 colors[j] = mapped[j] ? 0 : sd[j] * inva;
734 cc.convert(ctx, &cc, colors, convert);
735
736 for (j = 0; j < dc; j++)
737 dd[j] = fz_clampi(a * convert[j], 0, a);
738 }
739 dd += dn;
740 sd += sn;
741 }
742 dd += dstride;
743 sd += sstride;
744 }
745 }
746 }
747 #endif
748 }
749 }
750 fz_always(ctx)
751 fz_drop_color_converter(ctx, &cc);
752 fz_catch(ctx)
753 fz_rethrow(ctx);
754 }
755 else
756 {
757 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 if (fz_colorspace_is_subtractive(ctx, src->colorspace) &&
768 !fz_colorspace_is_subtractive(ctx, dst->colorspace) &&
769 src->seps > 0 &&
770 fz_compare_separations(ctx, dst->seps, src->seps))
771 {
772 /* Converting from CMYK + Spots -> RGB with a change in spots. */
773 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 temp->x = src->x;
779 temp->y = src->y;
780
781 fz_try(ctx)
782 {
783 temp = fz_copy_pixmap_area_converting_seps(ctx, src, temp, prf, color_params, default_cs);
784 dst = fz_copy_pixmap_area_converting_seps(ctx, temp, dst, NULL, color_params, default_cs);
785 }
786 fz_always(ctx)
787 fz_drop_pixmap(ctx, temp);
788 fz_catch(ctx)
789 fz_rethrow(ctx);
790
791 return dst;
792 }
793
794 /* Use a standard pixmap converter to convert the process + alpha. */
795 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 for (i = 0; i < sseps_n; i++)
803 mapped[i] = 0;
804
805 for (i = 0; i < dseps_n; i++)
806 {
807 const char *name;
808 int state = sep_state(dseps, i);
809
810 map[i] = -1;
811 if (state != FZ_SEPARATION_SPOT)
812 continue;
813 name = dseps->name[i];
814 if (name == NULL)
815 continue;
816 for (j = 0; j < sseps_n; j++)
817 {
818 const char *sname;
819 if (mapped[j])
820 continue;
821 if (sep_state(sseps, j) != FZ_SEPARATION_SPOT)
822 continue;
823 sname = sseps->name[j];
824 if (sname && !strcmp(name, sname))
825 {
826 map[i] = j;
827 unmapped--;
828 mapped[j] = 1;
829 break;
830 }
831 }
832 }
833 if (sa)
834 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 if (ds)
839 {
840 unsigned char *dd = ddata + dc;
841 const unsigned char *sd = sdata + sc;
842 for (y = dh; y > 0; y--)
843 {
844 for (x = dw; x > 0; x--)
845 {
846 for (i = 0; i < ds; i++)
847 dd[i] = map[i] < 0 ? 0 : sd[map[i]];
848 dd += dn;
849 sd += sn;
850 }
851 dd += dstride;
852 sd += sstride;
853 }
854 }
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 if (unmapped)
860 {
861 int m;
862 /* Still need to handle mapping 'lost' spots down to process colors */
863 for (i = -1, m = 0; m < sseps_n; m++)
864 {
865 float convert[FZ_MAX_COLORS];
866
867 if (mapped[m])
868 continue;
869 if (fz_separation_current_behavior(ctx, sseps, m) != FZ_SEPARATION_SPOT)
870 continue;
871 i++;
872 /* Src spot m (the i'th one) is not mapped. We need to convert that down. */
873 fz_separation_equivalent(ctx, sseps, m, dst->colorspace, convert, proof_cs, color_params);
874
875 if (fz_colorspace_is_subtractive(ctx, dst->colorspace))
876 {
877 if (fz_colorspace_is_subtractive(ctx, src->colorspace))
878 {
879 unsigned char *dd = ddata;
880 const unsigned char *sd = sdata + sc;
881
882 if (sa)
883 {
884 for (y = dh; y > 0; y--)
885 {
886 for (x = dw; x > 0; x--)
887 {
888 unsigned char v = sd[i];
889 if (v != 0)
890 {
891 unsigned char a = sd[ss];
892 for (k = 0; k < dc; k++)
893 dd[k] = fz_clampi(dd[k] + v * convert[k], 0, a);
894 }
895 dd += dn;
896 sd += sn;
897 }
898 dd += dstride;
899 sd += sstride;
900 }
901 }
902 else
903 {
904 /* This case is exercised by: -o out%d.pgm -r72 -D -F pgm -stm ../perf-testing-gpdl/pdf/Ad_InDesign.pdf */
905 for (y = dh; y > 0; y--)
906 {
907 for (x = dw; x > 0; x--)
908 {
909 unsigned char v = sd[i];
910 if (v != 0)
911 for (k = 0; k < dc; k++)
912 dd[k] = fz_clampi(dd[k] + v * convert[k], 0, 255);
913 dd += dn;
914 sd += sn;
915 }
916 dd += dstride;
917 sd += sstride;
918 }
919 }
920 }
921 else
922 {
923 unsigned char *dd = ddata;
924 const unsigned char *sd = sdata + sc;
925
926 if (sa)
927 {
928 for (y = dh; y > 0; y--)
929 {
930 for (x = dw; x > 0; x--)
931 {
932 unsigned char v = sd[i];
933 if (v != 0)
934 {
935 unsigned char a = sd[ss];
936 for (k = 0; k < dc; k++)
937 dd[k] = fz_clampi(dd[k] + v * convert[k], 0, a);
938 }
939 dd += dn;
940 sd += sn;
941 }
942 dd += dstride;
943 sd += sstride;
944 }
945 }
946 else
947 {
948 /* This case is exercised by: -o out.pkm -r72 -D ../MyTests/Bug704778.pdf 1 */
949 for (y = dh; y > 0; y--)
950 {
951 for (x = dw; x > 0; x--)
952 {
953 unsigned char v = sd[i];
954 if (v != 0)
955 for (k = 0; k < dc; k++)
956 dd[k] = fz_clampi(dd[k] + v * convert[k], 0, 255);
957 dd += dn;
958 sd += sn;
959 }
960 dd += dstride;
961 sd += sstride;
962 }
963 }
964 }
965 }
966 else
967 {
968 for (k = 0; k < dc; k++)
969 convert[k] = 1-convert[k];
970 if (fz_colorspace_is_subtractive(ctx, src->colorspace))
971 {
972 unsigned char *dd = ddata;
973 const unsigned char *sd = sdata + sc;
974
975 if (sa)
976 {
977 for (y = dh; y > 0; y--)
978 {
979 for (x = dw; x > 0; x--)
980 {
981 unsigned char v = sd[i];
982 if (v != 0)
983 {
984 unsigned char a = sd[ss];
985 for (k = 0; k < dc; k++)
986 dd[k] = fz_clampi(dd[k] - v * convert[k], 0, a);
987 }
988 dd += dn;
989 sd += sn;
990 }
991 dd += dstride;
992 sd += sstride;
993 }
994 }
995 else
996 {
997 /* Nothing in the cluster tests this case. */
998 for (y = dh; y > 0; y--)
999 {
1000 for (x = dw; x > 0; x--)
1001 {
1002 unsigned char v = sd[i];
1003 if (v != 0)
1004 for (k = 0; k < dc; k++)
1005 dd[k] = fz_clampi(dd[k] - v * convert[k], 0, 255);
1006 dd += dn;
1007 sd += sn;
1008 }
1009 dd += dstride;
1010 sd += sstride;
1011 }
1012 }
1013 }
1014 else
1015 {
1016 unsigned char *dd = ddata;
1017 const unsigned char *sd = sdata + sc;
1018
1019 if (sa)
1020 {
1021 for (y = dh; y > 0; y--)
1022 {
1023 for (x = dw; x > 0; x--)
1024 {
1025 unsigned char v = sd[i];
1026 if (v != 0)
1027 {
1028 unsigned char a = sd[ss];
1029 for (k = 0; k < dc; k++)
1030 dd[k] = fz_clampi(dd[k] - v * convert[k], 0, a);
1031 }
1032 dd += dn;
1033 sd += sn;
1034 }
1035 dd += dstride;
1036 sd += sstride;
1037 }
1038 }
1039 else
1040 {
1041 /* This case is exercised by: -o out.png -r72 -D ../MyTests/Bug704778.pdf 1 */
1042 for (y = dh; y > 0; y--)
1043 {
1044 for (x = dw; x > 0; x--)
1045 {
1046 unsigned char v = sd[i];
1047 if (v != 0)
1048 for (k = 0; k < dc; k++)
1049 dd[k] = fz_clampi(dd[k] - v * convert[k], 0, 255);
1050 dd += dn;
1051 sd += sn;
1052 }
1053 dd += dstride;
1054 sd += sstride;
1055 }
1056 }
1057 }
1058 }
1059 }
1060 }
1061 }
1062
1063 return dst;
1064 }
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 {
1072 int i, j, n, dc, ds, dn, pred;
1073 float remainders[FZ_MAX_COLORS];
1074 int remaining = 0;
1075
1076 assert(dst_cs && src_cs && dst_color && src_color);
1077 assert(fz_colorspace_is_device_n(ctx, src_cs));
1078
1079 dc = fz_colorspace_n(ctx, dst_cs);
1080 ds = (dst_seps == NULL ? 0: dst_seps->num_separations);
1081 dn = dc + ds;
1082
1083 i = 0;
1084 if (!fz_colorspace_is_subtractive(ctx, dst_cs))
1085 for (; i < dc; i++)
1086 dst_color[i] = 1;
1087 for (; i < dn; i++)
1088 dst_color[i] = 0;
1089
1090 n = fz_colorspace_n(ctx, src_cs);
1091 pred = 0;
1092 for (i = 0; i < n; i++)
1093 {
1094 const char *name = fz_colorspace_colorant(ctx, src_cs, i);
1095
1096 if (name == NULL)
1097 continue;
1098 if (i == 0 && !strcmp(name, "All"))
1099 {
1100 /* This is only supposed to happen in separation spaces, not DeviceN */
1101 if (n != 1)
1102 fz_warn(ctx, "All found in DeviceN space");
1103 for (i = 0; i < dn; i++)
1104 dst_color[i] = src_color[0];
1105 break;
1106 }
1107 if (!strcmp(name, "None"))
1108 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 for (j = pred; j < ds; j++)
1113 {
1114 const char *dname = dst_seps->name[j];
1115 if (dname && !strcmp(name, dname))
1116 goto found_sep;
1117 }
1118 for (j = 0; j < pred; j++)
1119 {
1120 const char *dname = dst_seps->name[j];
1121 if (dname && !strcmp(name, dname))
1122 goto found_sep;
1123 }
1124 for (j = 0; j < dc; j++)
1125 {
1126 const char *dname = fz_colorspace_colorant(ctx, dst_cs, j);
1127 if (dname && !strcmp(name, dname))
1128 goto found_process;
1129 }
1130 if (0) {
1131 found_sep:
1132 dst_color[j+dc] = src_color[i];
1133 pred = j+1;
1134 }
1135 else if (0)
1136 {
1137 found_process:
1138 dst_color[j] += src_color[i];
1139 }
1140 else
1141 {
1142 if (remaining == 0)
1143 {
1144 memset(remainders, 0, sizeof(float) * n);
1145 remaining = 1;
1146 }
1147 remainders[i] = src_color[i];
1148 }
1149 }
1150
1151 if (remaining)
1152 {
1153 /* There were some spots that didn't copy over */
1154 float converted[FZ_MAX_COLORS];
1155 fz_convert_color(ctx, src_cs, remainders, dst_cs, converted, NULL, color_params);
1156 for (i = 0; i < dc; i++)
1157 dst_color[i] += converted[i];
1158 }
1159 }
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 {
1169 float colors[FZ_MAX_COLORS];
1170
1171 if (!seps->cs[i])
1172 {
1173 switch (fz_colorspace_n(ctx, dst_cs))
1174 {
1175 case 3:
1176 convert[0] = (seps->rgba[i] & 0xff)/ 255.0f;
1177 convert[1] = ((seps->rgba[i]>>8) & 0xff)/ 255.0f;
1178 convert[2] = ((seps->rgba[i]>>16) & 0xff)/ 255.0f;
1179 convert[3] = ((seps->rgba[i]>>24) & 0xff)/ 255.0f;
1180 return;
1181 case 4:
1182 convert[0] = (seps->cmyk[i] & 0xff)/ 255.0f;
1183 convert[1] = ((seps->cmyk[i]>>8) & 0xff)/ 255.0f;
1184 convert[2] = ((seps->cmyk[i]>>16) & 0xff)/ 255.0f;
1185 convert[3] = ((seps->cmyk[i]>>24) & 0xff)/ 255.0f;
1186 return;
1187 default:
1188 fz_throw(ctx, FZ_ERROR_ARGUMENT, "Cannot return equivalent in this colorspace");
1189 }
1190 }
1191
1192 memset(colors, 0, sizeof(float) * fz_colorspace_n(ctx, seps->cs[i]));
1193 colors[seps->cs_pos[i]] = 1;
1194 fz_convert_color(ctx, seps->cs[i], colors, dst_cs, convert, prf, color_params);
1195 }
1196
1197 static void
1198 convert_by_copying_separations(fz_context *ctx, fz_color_converter *cc, const float *src, float *dst)
1199 {
1200 int i, o;
1201 int n = cc->dst_n;
1202 fz_separations *dseps = (fz_separations *)cc->opaque;
1203
1204 for (i = 0; i < n; i++)
1205 dst[i] = 0;
1206
1207 n = dseps->num_separations;
1208 o = cc->ds->n;
1209 for (i = 0; i < n; i++)
1210 if (dseps->cs[i] == cc->ss)
1211 dst[o+i] = src[dseps->cs_pos[i]];
1212 }
1213
1214 int
1215 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)
1216 {
1217 int i, n;
1218
1219 /* No idea how to cope with intermediate space here. Bale. */
1220 if (is != NULL && is != ss)
1221 return 0;
1222
1223 /* If all the separations for ss are catered for in dseps, we can just copy the values. */
1224 n = 0;
1225 for (i = 0; i < dseps->num_separations; i++)
1226 {
1227 if (dseps->cs[i] == ss)
1228 n++;
1229 }
1230
1231 /* If all of the components of ss were found, we're happy. (We assume the destination space
1232 * doesn't have any component twice.) */
1233 if (n != ss->n)
1234 return 0;
1235
1236 cc->ss = ss;
1237 cc->ss_via = NULL;
1238 cc->ds = ds;
1239 cc->opaque = dseps;
1240 cc->convert = convert_by_copying_separations;
1241
1242 return 1;
1243 }