/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 | } |