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