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