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