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

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 // Copyright (C) 2004-2025 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
27 #include <assert.h>
28 #include <math.h>
29 #include <string.h>
30
31 #if FZ_ENABLE_ICC
32
33 #include "icc/gray.icc.h"
34 #include "icc/rgb.icc.h"
35 #include "icc/cmyk.icc.h"
36 #include "icc/lab.icc.h"
37
38 void fz_new_colorspace_context(fz_context *ctx)
39 {
40 fz_colorspace_context *cct;
41
42 fz_buffer *gray = NULL;
43 fz_buffer *rgb = NULL;
44 fz_buffer *cmyk = NULL;
45 fz_buffer *lab = NULL;
46
47 fz_var(gray);
48 fz_var(rgb);
49 fz_var(cmyk);
50 fz_var(lab);
51
52 cct = ctx->colorspace = fz_malloc_struct(ctx, fz_colorspace_context);
53 cct->ctx_refs = 1;
54
55 fz_new_icc_context(ctx);
56
57 ctx->icc_enabled = 1;
58
59 fz_try(ctx)
60 {
61 gray = fz_new_buffer_from_shared_data(ctx, resources_icc_gray_icc, resources_icc_gray_icc_len);
62 rgb = fz_new_buffer_from_shared_data(ctx, resources_icc_rgb_icc, resources_icc_rgb_icc_len);
63 cmyk = fz_new_buffer_from_shared_data(ctx, resources_icc_cmyk_icc, resources_icc_cmyk_icc_len);
64 lab = fz_new_buffer_from_shared_data(ctx, resources_icc_lab_icc, resources_icc_lab_icc_len);
65 cct->gray = fz_new_icc_colorspace(ctx, FZ_COLORSPACE_GRAY, FZ_COLORSPACE_IS_DEVICE, "DeviceGray", gray);
66 cct->rgb = fz_new_icc_colorspace(ctx, FZ_COLORSPACE_RGB, FZ_COLORSPACE_IS_DEVICE, "DeviceRGB", rgb);
67 cct->bgr = fz_new_icc_colorspace(ctx, FZ_COLORSPACE_BGR, FZ_COLORSPACE_IS_DEVICE, "DeviceBGR", rgb);
68 cct->cmyk = fz_new_icc_colorspace(ctx, FZ_COLORSPACE_CMYK, FZ_COLORSPACE_IS_DEVICE, "DeviceCMYK", cmyk);
69 cct->lab = fz_new_icc_colorspace(ctx, FZ_COLORSPACE_LAB, FZ_COLORSPACE_IS_DEVICE, "Lab", lab);
70 }
71 fz_always(ctx)
72 {
73 fz_drop_buffer(ctx, gray);
74 fz_drop_buffer(ctx, rgb);
75 fz_drop_buffer(ctx, cmyk);
76 fz_drop_buffer(ctx, lab);
77 }
78 fz_catch(ctx)
79 {
80 fz_rethrow(ctx);
81 }
82 }
83
84 void fz_enable_icc(fz_context *ctx)
85 {
86 ctx->icc_enabled = 1;
87 }
88
89 void fz_disable_icc(fz_context *ctx)
90 {
91 ctx->icc_enabled = 0;
92 }
93
94 #else
95
96 void fz_new_colorspace_context(fz_context *ctx)
97 {
98 fz_colorspace_context *cct;
99
100 cct = ctx->colorspace = fz_malloc_struct(ctx, fz_colorspace_context);
101 cct->ctx_refs = 1;
102
103 cct->gray = fz_new_colorspace(ctx, FZ_COLORSPACE_GRAY, FZ_COLORSPACE_IS_DEVICE, 1, "DeviceGray");
104 cct->rgb = fz_new_colorspace(ctx, FZ_COLORSPACE_RGB, FZ_COLORSPACE_IS_DEVICE, 3, "DeviceRGB");
105 cct->bgr = fz_new_colorspace(ctx, FZ_COLORSPACE_BGR, FZ_COLORSPACE_IS_DEVICE, 3, "DeviceBGR");
106 cct->cmyk = fz_new_colorspace(ctx, FZ_COLORSPACE_CMYK, FZ_COLORSPACE_IS_DEVICE, 4, "DeviceCMYK");
107 cct->lab = fz_new_colorspace(ctx, FZ_COLORSPACE_LAB, FZ_COLORSPACE_IS_DEVICE, 3, "Lab");
108 }
109
110 void fz_enable_icc(fz_context *ctx)
111 {
112 fz_warn(ctx, "ICC support is not available");
113 }
114
115 void fz_disable_icc(fz_context *ctx)
116 {
117 }
118
119 #endif
120
121 fz_colorspace_context *fz_keep_colorspace_context(fz_context *ctx)
122 {
123 fz_keep_imp(ctx, ctx->colorspace, &ctx->colorspace->ctx_refs);
124 return ctx->colorspace;
125 }
126
127 void fz_drop_colorspace_context(fz_context *ctx)
128 {
129 if (fz_drop_imp(ctx, ctx->colorspace, &ctx->colorspace->ctx_refs))
130 {
131 fz_drop_colorspace(ctx, ctx->colorspace->gray);
132 fz_drop_colorspace(ctx, ctx->colorspace->rgb);
133 fz_drop_colorspace(ctx, ctx->colorspace->bgr);
134 fz_drop_colorspace(ctx, ctx->colorspace->cmyk);
135 fz_drop_colorspace(ctx, ctx->colorspace->lab);
136 #if FZ_ENABLE_ICC
137 fz_drop_icc_context(ctx);
138 #endif
139 fz_free(ctx, ctx->colorspace);
140 ctx->colorspace = NULL;
141 }
142 }
143
144 fz_colorspace *fz_device_gray(fz_context *ctx)
145 {
146 return ctx->colorspace->gray;
147 }
148
149 fz_colorspace *fz_device_rgb(fz_context *ctx)
150 {
151 return ctx->colorspace->rgb;
152 }
153
154 fz_colorspace *fz_device_bgr(fz_context *ctx)
155 {
156 return ctx->colorspace->bgr;
157 }
158
159 fz_colorspace *fz_device_cmyk(fz_context *ctx)
160 {
161 return ctx->colorspace->cmyk;
162 }
163
164 fz_colorspace *fz_device_lab(fz_context *ctx)
165 {
166 return ctx->colorspace->lab;
167 }
168
169 /* Same order as needed by LCMS */
170 static const char *fz_intent_names[] =
171 {
172 "Perceptual",
173 "RelativeColorimetric",
174 "Saturation",
175 "AbsoluteColorimetric",
176 };
177
178 int fz_lookup_rendering_intent(const char *name)
179 {
180 int i;
181 for (i = 0; i < (int)nelem(fz_intent_names); i++)
182 if (!strcmp(name, fz_intent_names[i]))
183 return i;
184 return FZ_RI_RELATIVE_COLORIMETRIC;
185 }
186
187 const char *fz_rendering_intent_name(int ri)
188 {
189 if (ri >= 0 && ri < (int)nelem(fz_intent_names))
190 return fz_intent_names[ri];
191 return "RelativeColorimetric";
192 }
193
194 /* Colorspace feature tests */
195
196 const char *fz_colorspace_name(fz_context *ctx, fz_colorspace *cs)
197 {
198 return cs ? cs->name : "None";
199 }
200
201 enum fz_colorspace_type fz_colorspace_type(fz_context *ctx, fz_colorspace *cs)
202 {
203 return cs ? cs->type : FZ_COLORSPACE_NONE;
204 }
205
206 int fz_colorspace_n(fz_context *ctx, fz_colorspace *cs)
207 {
208 return cs ? cs->n : 0;
209 }
210
211 int fz_colorspace_is_gray(fz_context *ctx, fz_colorspace *cs)
212 {
213 return cs && cs->type == FZ_COLORSPACE_GRAY;
214 }
215
216 int fz_colorspace_is_rgb(fz_context *ctx, fz_colorspace *cs)
217 {
218 return cs && cs->type == FZ_COLORSPACE_RGB;
219 }
220
221 int fz_colorspace_is_cmyk(fz_context *ctx, fz_colorspace *cs)
222 {
223 return cs && cs->type == FZ_COLORSPACE_CMYK;
224 }
225
226 int fz_colorspace_is_lab(fz_context *ctx, fz_colorspace *cs)
227 {
228 return cs && cs->type == FZ_COLORSPACE_LAB;
229 }
230
231 int fz_colorspace_is_indexed(fz_context *ctx, fz_colorspace *cs)
232 {
233 return cs && (cs->type == FZ_COLORSPACE_INDEXED);
234 }
235
236 int fz_colorspace_is_device_n(fz_context *ctx, fz_colorspace *cs)
237 {
238 return cs && (cs->type == FZ_COLORSPACE_SEPARATION);
239 }
240
241 int fz_colorspace_is_subtractive(fz_context *ctx, fz_colorspace *cs)
242 {
243 return cs && (cs->type == FZ_COLORSPACE_CMYK || cs->type == FZ_COLORSPACE_SEPARATION);
244 }
245
246 int fz_colorspace_is_device(fz_context *ctx, fz_colorspace *cs)
247 {
248 return cs && (cs->flags & FZ_COLORSPACE_IS_DEVICE);
249 }
250
251 int fz_colorspace_is_icc(fz_context *ctx, fz_colorspace *cs)
252 {
253 return cs && (cs->flags & FZ_COLORSPACE_IS_ICC);
254 }
255
256 int fz_colorspace_is_lab_icc(fz_context *ctx, fz_colorspace *cs)
257 {
258 return cs && (cs->type == FZ_COLORSPACE_LAB) && (cs->flags & FZ_COLORSPACE_IS_ICC);
259 }
260
261 int fz_colorspace_is_device_gray(fz_context *ctx, fz_colorspace *cs)
262 {
263 return fz_colorspace_is_device(ctx, cs) && fz_colorspace_is_gray(ctx, cs);
264 }
265
266 int fz_colorspace_is_device_cmyk(fz_context *ctx, fz_colorspace *cs)
267 {
268 return fz_colorspace_is_device(ctx, cs) && fz_colorspace_is_cmyk(ctx, cs);
269 }
270
271 int fz_colorspace_device_n_has_only_cmyk(fz_context *ctx, fz_colorspace *cs)
272 {
273 return cs && ((cs->flags & FZ_COLORSPACE_HAS_CMYK_AND_SPOTS) == FZ_COLORSPACE_HAS_CMYK);
274 }
275
276 int fz_colorspace_device_n_has_cmyk(fz_context *ctx, fz_colorspace *cs)
277 {
278 return cs && (cs->flags & FZ_COLORSPACE_HAS_CMYK);
279 }
280
281 int fz_is_valid_blend_colorspace(fz_context *ctx, fz_colorspace *cs)
282 {
283 return cs == NULL ||
284 cs->type == FZ_COLORSPACE_GRAY ||
285 cs->type == FZ_COLORSPACE_RGB ||
286 cs->type == FZ_COLORSPACE_CMYK;
287 }
288
289 fz_colorspace *fz_base_colorspace(fz_context *ctx, fz_colorspace *cs)
290 {
291 if (cs == NULL)
292 return NULL;
293 if (cs->type == FZ_COLORSPACE_INDEXED)
294 return cs->u.indexed.base;
295 return cs;
296 }
297
298 fz_colorspace *
299 fz_keep_colorspace(fz_context *ctx, fz_colorspace *cs)
300 {
301 return fz_keep_key_storable(ctx, &cs->key_storable);
302 }
303
304 void
305 fz_drop_colorspace(fz_context *ctx, fz_colorspace *cs)
306 {
307 fz_drop_key_storable(ctx, &cs->key_storable);
308 }
309
310 fz_colorspace *
311 fz_keep_colorspace_store_key(fz_context *ctx, fz_colorspace *cs)
312 {
313 return fz_keep_key_storable_key(ctx, &cs->key_storable);
314 }
315
316 void
317 fz_drop_colorspace_store_key(fz_context *ctx, fz_colorspace *cs)
318 {
319 fz_drop_key_storable_key(ctx, &cs->key_storable);
320 }
321
322 void
323 fz_drop_colorspace_imp(fz_context *ctx, fz_storable *cs_)
324 {
325 fz_colorspace *cs = (fz_colorspace *)cs_;
326 int i;
327
328 if (cs->type == FZ_COLORSPACE_INDEXED)
329 {
330 fz_drop_colorspace(ctx, cs->u.indexed.base);
331 fz_free(ctx, cs->u.indexed.lookup);
332 }
333 if (cs->type == FZ_COLORSPACE_SEPARATION)
334 {
335 fz_drop_colorspace(ctx, cs->u.separation.base);
336 cs->u.separation.drop(ctx, cs->u.separation.tint);
337 for (i = 0; i < FZ_MAX_COLORS; i++)
338 fz_free(ctx, cs->u.separation.colorant[i]);
339 }
340 #if FZ_ENABLE_ICC
341 if (cs->flags & FZ_COLORSPACE_IS_ICC)
342 {
343 fz_drop_icc_profile(ctx, cs->u.icc.profile);
344 fz_drop_buffer(ctx, cs->u.icc.buffer);
345 }
346 #endif
347
348 fz_free(ctx, cs->name);
349 fz_free(ctx, cs);
350 }
351
352 fz_colorspace *
353 fz_new_colorspace(fz_context *ctx, enum fz_colorspace_type type, int flags, int n, const char *name)
354 {
355 fz_colorspace *cs = fz_malloc_struct(ctx, fz_colorspace);
356 FZ_INIT_KEY_STORABLE(cs, 1, fz_drop_colorspace_imp);
357
358 if (n > FZ_MAX_COLORS)
359 fz_throw(ctx, FZ_ERROR_ARGUMENT, "too many color components (%d > %d)", n, FZ_MAX_COLORS);
360 if (n < 1)
361 fz_throw(ctx, FZ_ERROR_ARGUMENT, "too few color components (%d < 1)", n);
362
363 fz_try(ctx)
364 {
365 cs->type = type;
366 cs->flags = flags;
367 cs->n = n;
368 cs->name = Memento_label(fz_strdup(ctx, name ? name : "UNKNOWN"), "cs_name");
369 }
370 fz_catch(ctx)
371 {
372 fz_free(ctx, cs);
373 fz_rethrow(ctx);
374 }
375
376 return cs;
377 }
378
379 fz_colorspace *
380 fz_new_indexed_colorspace(fz_context *ctx, fz_colorspace *base, int high, unsigned char *lookup)
381 {
382 fz_colorspace *cs;
383 char name[100];
384 if (high < 0 || high > 255)
385 fz_throw(ctx, FZ_ERROR_SYNTAX, "invalid maximum value in indexed colorspace");
386 fz_snprintf(name, sizeof name, "Indexed(%d,%s)", high, base->name);
387 cs = fz_new_colorspace(ctx, FZ_COLORSPACE_INDEXED, 0, 1, name);
388 cs->u.indexed.base = fz_keep_colorspace(ctx, base);
389 cs->u.indexed.high = high;
390 cs->u.indexed.lookup = lookup;
391 return cs;
392 }
393
394 fz_colorspace *
395 fz_new_icc_colorspace(fz_context *ctx, enum fz_colorspace_type type, int flags, const char *name, fz_buffer *buf)
396 {
397 #if FZ_ENABLE_ICC
398 fz_icc_profile *profile = NULL;
399 fz_colorspace *cs = NULL;
400 unsigned char *data;
401 char name_buf[100];
402 size_t size;
403 int n;
404
405 fz_var(profile);
406 fz_var(cs);
407 fz_var(type);
408
409 fz_try(ctx)
410 {
411 size = fz_buffer_storage(ctx, buf, &data);
412 profile = fz_new_icc_profile(ctx, data, size);
413 n = fz_icc_profile_components(ctx, profile);
414 switch (type)
415 {
416 default:
417 fz_throw(ctx, FZ_ERROR_SYNTAX, "invalid colorspace type for ICC profile");
418 case FZ_COLORSPACE_NONE:
419 switch (n)
420 {
421 default:
422 fz_throw(ctx, FZ_ERROR_SYNTAX, "ICC profile has unexpected number of channels: %d", n);
423 case 1:
424 type = FZ_COLORSPACE_GRAY;
425 break;
426 case 3:
427 if (fz_icc_profile_is_lab(ctx, profile))
428 type = FZ_COLORSPACE_LAB;
429 else
430 type = FZ_COLORSPACE_RGB;
431 break;
432 case 4:
433 type = FZ_COLORSPACE_CMYK;
434 break;
435 }
436 break;
437 case FZ_COLORSPACE_GRAY:
438 if (n != 1)
439 fz_throw(ctx, FZ_ERROR_SYNTAX, "ICC profile (N=%d) is not Gray", n);
440 break;
441 case FZ_COLORSPACE_RGB:
442 case FZ_COLORSPACE_BGR:
443 if (n != 3 || fz_icc_profile_is_lab(ctx, profile))
444 fz_throw(ctx, FZ_ERROR_SYNTAX, "ICC profile (N=%d) is not RGB", n);
445 break;
446 case FZ_COLORSPACE_LAB:
447 if (n != 3 || !fz_icc_profile_is_lab(ctx, profile))
448 fz_throw(ctx, FZ_ERROR_SYNTAX, "ICC profile (N=%d) is not Lab", n);
449 break;
450 case FZ_COLORSPACE_CMYK:
451 if (n != 4)
452 fz_throw(ctx, FZ_ERROR_SYNTAX, "ICC profile (N=%d) is not CMYK", n);
453 break;
454 }
455
456 if (!name)
457 {
458 char cmm_name[100];
459 fz_icc_profile_name(ctx, profile, cmm_name, sizeof cmm_name);
460 switch (type)
461 {
462 default: fz_snprintf(name_buf, sizeof name_buf, "ICCBased(%d,%s)", n, cmm_name); break;
463 case FZ_COLORSPACE_GRAY: fz_snprintf(name_buf, sizeof name_buf, "ICCBased(Gray,%s)", cmm_name); break;
464 case FZ_COLORSPACE_RGB: fz_snprintf(name_buf, sizeof name_buf, "ICCBased(RGB,%s)", cmm_name); break;
465 case FZ_COLORSPACE_BGR: fz_snprintf(name_buf, sizeof name_buf, "ICCBased(BGR,%s)", cmm_name); break;
466 case FZ_COLORSPACE_CMYK: fz_snprintf(name_buf, sizeof name_buf, "ICCBased(CMYK,%s)", cmm_name); break;
467 case FZ_COLORSPACE_LAB: fz_snprintf(name_buf, sizeof name_buf, "ICCBased(Lab,%s)", cmm_name); break;
468 }
469 name = name_buf;
470 }
471
472 cs = fz_new_colorspace(ctx, type, flags | FZ_COLORSPACE_IS_ICC, n, name);
473 cs->u.icc.buffer = fz_keep_buffer(ctx, buf);
474 cs->u.icc.profile = profile;
475 fz_md5_buffer(ctx, buf, cs->u.icc.md5);
476 }
477 fz_catch(ctx)
478 {
479 fz_drop_icc_profile(ctx, profile);
480 fz_drop_colorspace(ctx, cs);
481 fz_rethrow(ctx);
482 }
483 return cs;
484 #else
485 switch (type)
486 {
487 default: fz_throw(ctx, FZ_ERROR_SYNTAX, "unknown colorspace type");
488 case FZ_COLORSPACE_GRAY: return fz_keep_colorspace(ctx, fz_device_gray(ctx));
489 case FZ_COLORSPACE_RGB: return fz_keep_colorspace(ctx, fz_device_rgb(ctx));
490 case FZ_COLORSPACE_BGR: return fz_keep_colorspace(ctx, fz_device_bgr(ctx));
491 case FZ_COLORSPACE_CMYK: return fz_keep_colorspace(ctx, fz_device_cmyk(ctx));
492 case FZ_COLORSPACE_LAB: return fz_keep_colorspace(ctx, fz_device_lab(ctx));
493 }
494 #endif
495 }
496
497 fz_colorspace *fz_new_cal_gray_colorspace(fz_context *ctx, float wp[3], float bp[3], float gamma)
498 {
499 #if FZ_ENABLE_ICC
500 fz_buffer *buf = fz_new_icc_data_from_cal(ctx, wp, bp, &gamma, NULL, 1);
501 fz_colorspace *cs;
502 fz_try(ctx)
503 cs = fz_new_icc_colorspace(ctx, FZ_COLORSPACE_GRAY, 0, "CalGray", buf);
504 fz_always(ctx)
505 fz_drop_buffer(ctx, buf);
506 fz_catch(ctx)
507 fz_rethrow(ctx);
508 return cs;
509 #else
510 return fz_keep_colorspace(ctx, fz_device_gray(ctx));
511 #endif
512 }
513
514 fz_colorspace *fz_new_cal_rgb_colorspace(fz_context *ctx, float wp[3], float bp[3], float gamma[3], float matrix[9])
515 {
516 #if FZ_ENABLE_ICC
517 fz_buffer *buf = fz_new_icc_data_from_cal(ctx, wp, bp, gamma, matrix, 3);
518 fz_colorspace *cs;
519 fz_try(ctx)
520 cs = fz_new_icc_colorspace(ctx, FZ_COLORSPACE_RGB, 0, "CalRGB", buf);
521 fz_always(ctx)
522 fz_drop_buffer(ctx, buf);
523 fz_catch(ctx)
524 fz_rethrow(ctx);
525 return cs;
526 #else
527 return fz_keep_colorspace(ctx, fz_device_rgb(ctx));
528 #endif
529 }
530
531 void fz_colorspace_name_colorant(fz_context *ctx, fz_colorspace *cs, int i, const char *name)
532 {
533 if (i < 0 || i >= cs->n)
534 fz_throw(ctx, FZ_ERROR_ARGUMENT, "Attempt to name out of range colorant");
535 if (cs->type != FZ_COLORSPACE_SEPARATION)
536 fz_throw(ctx, FZ_ERROR_ARGUMENT, "Attempt to name colorant for non-separation colorspace");
537
538 fz_free(ctx, cs->u.separation.colorant[i]);
539 cs->u.separation.colorant[i] = NULL;
540 cs->u.separation.colorant[i] = fz_strdup(ctx, name);
541
542 if (!strcmp(name, "Cyan") || !strcmp(name, "Magenta") || !strcmp(name, "Yellow") || !strcmp(name, "Black"))
543 cs->flags |= FZ_COLORSPACE_HAS_CMYK;
544 else
545 cs->flags |= FZ_COLORSPACE_HAS_SPOTS;
546 }
547
548 const char *fz_colorspace_colorant(fz_context *ctx, fz_colorspace *cs, int i)
549 {
550 if (!cs || i < 0 || i >= cs->n)
551 fz_throw(ctx, FZ_ERROR_ARGUMENT, "Colorant out of range");
552 switch (cs->type)
553 {
554 case FZ_COLORSPACE_NONE:
555 return "None";
556 case FZ_COLORSPACE_GRAY:
557 return "Gray";
558 case FZ_COLORSPACE_RGB:
559 if (i == 0) return "Red";
560 if (i == 1) return "Green";
561 if (i == 2) return "Blue";
562 break;
563 case FZ_COLORSPACE_BGR:
564 if (i == 0) return "Blue";
565 if (i == 1) return "Green";
566 if (i == 2) return "Red";
567 break;
568 case FZ_COLORSPACE_CMYK:
569 if (i == 0) return "Cyan";
570 if (i == 1) return "Magenta";
571 if (i == 2) return "Yellow";
572 if (i == 3) return "Black";
573 break;
574 case FZ_COLORSPACE_LAB:
575 if (i == 0) return "L*";
576 if (i == 1) return "a*";
577 if (i == 2) return "b*";
578 break;
579 case FZ_COLORSPACE_INDEXED:
580 return "Index";
581 case FZ_COLORSPACE_SEPARATION:
582 return cs->u.separation.colorant[i];
583 }
584 return "None";
585 }
586
587 void
588 fz_clamp_color(fz_context *ctx, fz_colorspace *cs, const float *in, float *out)
589 {
590 if (cs->type == FZ_COLORSPACE_LAB)
591 {
592 out[0] = fz_clamp(in[0], 0, 100);
593 out[1] = fz_clamp(in[1], -128, 127);
594 out[2] = fz_clamp(in[2], -128, 127);
595 }
596 else if (cs->type == FZ_COLORSPACE_INDEXED)
597 {
598 /* round color index to integer before rescaling to hival */
599 out[0] = fz_clamp((int)(in[0]+0.5), 0, cs->u.indexed.high) / 255.0f;
600 }
601 else
602 {
603 int i, n = cs->n;
604 for (i = 0; i < n; ++i)
605 out[i] = fz_clamp(in[i], 0, 1);
606 }
607 }
608
609 const fz_color_params fz_default_color_params = { FZ_RI_RELATIVE_COLORIMETRIC, 1, 0, 0 };
610
611 fz_default_colorspaces *fz_new_default_colorspaces(fz_context *ctx)
612 {
613 fz_default_colorspaces *default_cs = fz_malloc_struct(ctx, fz_default_colorspaces);
614 default_cs->refs = 1;
615 default_cs->gray = fz_keep_colorspace(ctx, fz_device_gray(ctx));
616 default_cs->rgb = fz_keep_colorspace(ctx, fz_device_rgb(ctx));
617 default_cs->cmyk = fz_keep_colorspace(ctx, fz_device_cmyk(ctx));
618 default_cs->oi = NULL;
619 return default_cs;
620 }
621
622 fz_default_colorspaces *fz_clone_default_colorspaces(fz_context *ctx, fz_default_colorspaces *base)
623 {
624 fz_default_colorspaces *default_cs = fz_malloc_struct(ctx, fz_default_colorspaces);
625 default_cs->refs = 1;
626 if (base)
627 {
628 default_cs->gray = fz_keep_colorspace(ctx, base->gray);
629 default_cs->rgb = fz_keep_colorspace(ctx, base->rgb);
630 default_cs->cmyk = fz_keep_colorspace(ctx, base->cmyk);
631 default_cs->oi = fz_keep_colorspace(ctx, base->oi);
632 }
633 return default_cs;
634 }
635
636 fz_default_colorspaces *fz_keep_default_colorspaces(fz_context *ctx, fz_default_colorspaces *default_cs)
637 {
638 return fz_keep_imp(ctx, default_cs, &default_cs->refs);
639 }
640
641 void
642 fz_drop_default_colorspaces(fz_context *ctx, fz_default_colorspaces *default_cs)
643 {
644 if (fz_drop_imp(ctx, default_cs, &default_cs->refs))
645 {
646 fz_drop_colorspace(ctx, default_cs->gray);
647 fz_drop_colorspace(ctx, default_cs->rgb);
648 fz_drop_colorspace(ctx, default_cs->cmyk);
649 fz_drop_colorspace(ctx, default_cs->oi);
650 fz_free(ctx, default_cs);
651 }
652 }
653
654 fz_colorspace *fz_default_gray(fz_context *ctx, const fz_default_colorspaces *default_cs)
655 {
656 return (default_cs && default_cs->gray) ? default_cs->gray : fz_device_gray(ctx);
657 }
658
659 fz_colorspace *fz_default_rgb(fz_context *ctx, const fz_default_colorspaces *default_cs)
660 {
661 return (default_cs && default_cs->rgb) ? default_cs->rgb : fz_device_rgb(ctx);
662 }
663
664 fz_colorspace *fz_default_cmyk(fz_context *ctx, const fz_default_colorspaces *default_cs)
665 {
666 return (default_cs && default_cs->cmyk) ? default_cs->cmyk : fz_device_cmyk(ctx);
667 }
668
669 fz_colorspace *fz_default_output_intent(fz_context *ctx, const fz_default_colorspaces *default_cs)
670 {
671 return default_cs ? default_cs->oi : NULL;
672 }
673
674 void fz_set_default_gray(fz_context *ctx, fz_default_colorspaces *default_cs, fz_colorspace *cs)
675 {
676 if (cs->type == FZ_COLORSPACE_GRAY && cs->n == 1)
677 {
678 fz_drop_colorspace(ctx, default_cs->gray);
679 default_cs->gray = fz_keep_colorspace(ctx, cs);
680 }
681 }
682
683 void fz_set_default_rgb(fz_context *ctx, fz_default_colorspaces *default_cs, fz_colorspace *cs)
684 {
685 if (cs->type == FZ_COLORSPACE_RGB && cs->n == 3)
686 {
687 fz_drop_colorspace(ctx, default_cs->rgb);
688 default_cs->rgb = fz_keep_colorspace(ctx, cs);
689 }
690 }
691
692 void fz_set_default_cmyk(fz_context *ctx, fz_default_colorspaces *default_cs, fz_colorspace *cs)
693 {
694 if (cs->type == FZ_COLORSPACE_CMYK && cs->n == 4)
695 {
696 fz_drop_colorspace(ctx, default_cs->cmyk);
697 default_cs->cmyk = fz_keep_colorspace(ctx, cs);
698 }
699 }
700
701 void fz_set_default_output_intent(fz_context *ctx, fz_default_colorspaces *default_cs, fz_colorspace *cs)
702 {
703 fz_drop_colorspace(ctx, default_cs->oi);
704 default_cs->oi = NULL;
705
706 /* FIXME: Why do we set DefaultXXX along with the output intent?! */
707 switch (cs->type)
708 {
709 default:
710 fz_warn(ctx, "Ignoring incompatible output intent: %s.", cs->name);
711 break;
712 case FZ_COLORSPACE_GRAY:
713 default_cs->oi = fz_keep_colorspace(ctx, cs);
714 if (default_cs->gray == fz_device_gray(ctx))
715 fz_set_default_gray(ctx, default_cs, cs);
716 break;
717 case FZ_COLORSPACE_RGB:
718 default_cs->oi = fz_keep_colorspace(ctx, cs);
719 if (default_cs->rgb == fz_device_rgb(ctx))
720 fz_set_default_rgb(ctx, default_cs, cs);
721 break;
722 case FZ_COLORSPACE_CMYK:
723 default_cs->oi = fz_keep_colorspace(ctx, cs);
724 if (default_cs->cmyk == fz_device_cmyk(ctx))
725 fz_set_default_cmyk(ctx, default_cs, cs);
726 break;
727 }
728 }
729
730 /* Link cache */
731
732 #if FZ_ENABLE_ICC
733
734 typedef struct {
735 int refs;
736 unsigned char src_md5[16];
737 unsigned char dst_md5[16];
738 fz_color_params rend;
739 unsigned char src_extras;
740 unsigned char dst_extras;
741 unsigned char copy_spots;
742 unsigned char format;
743 unsigned char proof;
744 unsigned char bgr;
745 } fz_link_key;
746
747 static void *
748 fz_keep_link_key(fz_context *ctx, void *key_)
749 {
750 fz_link_key *key = (fz_link_key *)key_;
751 return fz_keep_imp(ctx, key, &key->refs);
752 }
753
754 static void
755 fz_drop_link_key(fz_context *ctx, void *key_)
756 {
757 fz_link_key *key = (fz_link_key *)key_;
758 if (fz_drop_imp(ctx, key, &key->refs))
759 fz_free(ctx, key);
760 }
761
762 static int
763 fz_cmp_link_key(fz_context *ctx, void *k0_, void *k1_)
764 {
765 fz_link_key *k0 = (fz_link_key *)k0_;
766 fz_link_key *k1 = (fz_link_key *)k1_;
767 return
768 memcmp(k0->src_md5, k1->src_md5, 16) == 0 &&
769 memcmp(k0->dst_md5, k1->dst_md5, 16) == 0 &&
770 k0->src_extras == k1->src_extras &&
771 k0->dst_extras == k1->dst_extras &&
772 k0->rend.bp == k1->rend.bp &&
773 k0->rend.ri == k1->rend.ri &&
774 k0->copy_spots == k1->copy_spots &&
775 k0->format == k1->format &&
776 k0->proof == k1->proof &&
777 k0->bgr == k1->bgr;
778 }
779
780 static void
781 fz_format_link_key(fz_context *ctx, char *s, size_t n, void *key_)
782 {
783 static const char *hex = "0123456789abcdef";
784 fz_link_key *key = (fz_link_key *)key_;
785 char sm[33], dm[33];
786 int i;
787 for (i = 0; i < 16; ++i)
788 {
789 sm[i*2+0] = hex[key->src_md5[i]>>4];
790 sm[i*2+1] = hex[key->src_md5[i]&15];
791 dm[i*2+0] = hex[key->dst_md5[i]>>4];
792 dm[i*2+1] = hex[key->dst_md5[i]&15];
793 }
794 sm[32] = 0;
795 dm[32] = 0;
796 fz_snprintf(s, n, "(link src_md5=%s dst_md5=%s)", sm, dm);
797 }
798
799 static int
800 fz_make_hash_link_key(fz_context *ctx, fz_store_hash *hash, void *key_)
801 {
802 fz_link_key *key = (fz_link_key *)key_;
803 memcpy(hash->u.link.dst_md5, key->dst_md5, 16);
804 memcpy(hash->u.link.src_md5, key->src_md5, 16);
805 hash->u.link.ri = key->rend.ri;
806 hash->u.link.bp = key->rend.bp;
807 hash->u.link.src_extras = key->src_extras;
808 hash->u.link.dst_extras = key->dst_extras;
809 hash->u.link.format = key->format;
810 hash->u.link.proof = key->proof;
811 hash->u.link.copy_spots = key->copy_spots;
812 hash->u.link.bgr = key->bgr;
813 return 1;
814 }
815
816 static fz_store_type fz_link_store_type =
817 {
818 "fz_icc_link",
819 fz_make_hash_link_key,
820 fz_keep_link_key,
821 fz_drop_link_key,
822 fz_cmp_link_key,
823 fz_format_link_key,
824 NULL
825 };
826
827 fz_icc_link *
828 fz_find_icc_link(fz_context *ctx,
829 fz_colorspace *src, int src_extras,
830 fz_colorspace *dst, int dst_extras,
831 fz_colorspace *prf,
832 fz_color_params rend,
833 int format,
834 int copy_spots,
835 int premult)
836 {
837 fz_icc_link *link, *old_link;
838 fz_link_key key, *new_key;
839
840 fz_var(link);
841
842 /* Check the storable to see if we have a copy. */
843 key.refs = 1;
844 memcpy(&key.src_md5, src->u.icc.md5, 16);
845 memcpy(&key.dst_md5, dst->u.icc.md5, 16);
846 key.rend = rend;
847 key.src_extras = src_extras;
848 key.dst_extras = dst_extras;
849 key.copy_spots = copy_spots;
850 key.format = (format & 1) | (premult*2);
851 key.proof = (prf != NULL);
852 key.bgr = (dst->type == FZ_COLORSPACE_BGR);
853
854 link = fz_find_item(ctx, fz_drop_icc_link_imp, &key, &fz_link_store_type);
855 if (!link)
856 {
857 new_key = fz_malloc_struct(ctx, fz_link_key);
858 memcpy(new_key, &key, sizeof (fz_link_key));
859 fz_try(ctx)
860 {
861 link = fz_new_icc_link(ctx, src, src_extras, dst, dst_extras, prf, rend, format, copy_spots, premult);
862 old_link = fz_store_item(ctx, new_key, link, 1000, &fz_link_store_type);
863 if (old_link)
864 {
865 /* Found one while adding! Perhaps from another thread? */
866 fz_drop_icc_link(ctx, link);
867 link = old_link;
868 }
869 }
870 fz_always(ctx)
871 {
872 fz_drop_link_key(ctx, new_key);
873 }
874 fz_catch(ctx)
875 {
876 fz_drop_icc_link(ctx, link);
877 fz_rethrow(ctx);
878 }
879 }
880 return link;
881 }
882
883 #endif
884
885 /* Color conversions */
886
887 static void indexed_via_base(fz_context *ctx, fz_color_converter *cc, const float *src, float *dst)
888 {
889 fz_colorspace *ss = cc->ss_via;
890 const unsigned char *lookup = ss->u.indexed.lookup;
891 int high = ss->u.indexed.high;
892 int n = ss->u.indexed.base->n;
893 float base[4];
894 int i, k;
895
896 i = src[0] * 255;
897 i = fz_clampi(i, 0, high);
898 if (ss->u.indexed.base->type == FZ_COLORSPACE_LAB)
899 {
900 base[0] = lookup[i * 3 + 0] * 100 / 255.0f;
901 base[1] = lookup[i * 3 + 1] - 128;
902 base[2] = lookup[i * 3 + 2] - 128;
903 }
904 else
905 {
906 for (k = 0; k < n; ++k)
907 base[k] = lookup[i * n + k] / 255.0f;
908 }
909
910 cc->convert_via(ctx, cc, base, dst);
911 }
912
913 static void separation_via_base(fz_context *ctx, fz_color_converter *cc, const float *src, float *dst)
914 {
915 fz_colorspace *ss = cc->ss_via;
916 float base[4];
917 ss->u.separation.eval(ctx, ss->u.separation.tint, src, ss->n, base, ss->u.separation.base->n);
918 cc->convert_via(ctx, cc, base, dst);
919 }
920
921 static void indexed_via_separation_via_base(fz_context *ctx, fz_color_converter *cc, const float *src, float *dst)
922 {
923 fz_colorspace *ss = cc->ss_via;
924 fz_colorspace *ssep = cc->ss_via->u.indexed.base;
925 const unsigned char *lookup = ss->u.indexed.lookup;
926 int high = ss->u.indexed.high;
927 int n = ss->u.indexed.base->n;
928 float base[4], mid[FZ_MAX_COLORS];
929 int i, k;
930
931 /* First map through the index. */
932 i = src[0] * 255;
933 i = fz_clampi(i, 0, high);
934 for (k = 0; k < n; ++k)
935 mid[k] = lookup[i * n + k] / 255.0f;
936
937 /* Then map through the separation. */
938 ssep->u.separation.eval(ctx, ssep->u.separation.tint, mid, ssep->n, base, ssep->u.separation.base->n);
939
940 /* Then convert in the base. */
941 cc->convert_via(ctx, cc, base, dst);
942 }
943
944 static void
945 fz_init_process_color_converter(fz_context *ctx, fz_color_converter *cc, fz_colorspace *ss, fz_colorspace *ds, fz_colorspace *is, fz_color_params params)
946 {
947 if (ss->type == FZ_COLORSPACE_INDEXED)
948 fz_throw(ctx, FZ_ERROR_ARGUMENT, "base colorspace must not be indexed");
949 if (ss->type == FZ_COLORSPACE_SEPARATION)
950 fz_throw(ctx, FZ_ERROR_ARGUMENT, "base colorspace must not be separation");
951
952 #if FZ_ENABLE_ICC
953 if (ctx->icc_enabled)
954 {
955 /* Handle identity case. */
956 if (ss == ds || (!memcmp(ss->u.icc.md5, ds->u.icc.md5, 16)))
957 {
958 cc->convert = fz_lookup_fast_color_converter(ctx, ss, ds);
959 return;
960 }
961
962 /* Handle DeviceGray to CMYK as K only. See note in Section 6.3 of PDF spec 1.7. */
963 if (ss->type == FZ_COLORSPACE_GRAY && (ss->flags & FZ_COLORSPACE_IS_DEVICE))
964 {
965 if (ds->type == FZ_COLORSPACE_CMYK)
966 {
967 cc->convert = fz_lookup_fast_color_converter(ctx, ss, ds);
968 return;
969 }
970 }
971
972 fz_try(ctx)
973 {
974 cc->link = fz_find_icc_link(ctx, ss, 0, ds, 0, is, params, 1, 0, 0);
975 cc->convert = fz_icc_transform_color;
976 }
977 fz_catch(ctx)
978 {
979 fz_rethrow_if(ctx, FZ_ERROR_SYSTEM);
980 fz_report_error(ctx);
981 fz_warn(ctx, "cannot create ICC link, falling back to fast color conversion");
982 cc->convert = fz_lookup_fast_color_converter(ctx, ss, ds);
983 }
984 }
985 else
986 {
987 cc->convert = fz_lookup_fast_color_converter(ctx, ss, ds);
988 }
989 #else
990 cc->convert = fz_lookup_fast_color_converter(ctx, ss, ds);
991 #endif
992 }
993
994 void
995 fz_find_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)
996 {
997 cc->ds = ds;
998 cc->dseps = NULL;
999 cc->dst_n = ds->n;
1000 #if FZ_ENABLE_ICC
1001 cc->link = NULL;
1002 #endif
1003
1004 if (ds->type == FZ_COLORSPACE_INDEXED)
1005 fz_throw(ctx, FZ_ERROR_ARGUMENT, "Cannot convert into Indexed colorspace.");
1006 if (ds->type == FZ_COLORSPACE_SEPARATION)
1007 fz_throw(ctx, FZ_ERROR_ARGUMENT, "Cannot convert into Separation colorspace.");
1008
1009 if (ss->type == FZ_COLORSPACE_INDEXED)
1010 {
1011 if (ss->u.indexed.base->type == FZ_COLORSPACE_SEPARATION)
1012 {
1013 cc->ss = ss->u.indexed.base->u.separation.base;
1014 cc->ss_via = ss;
1015 fz_init_process_color_converter(ctx, cc, cc->ss, ds, is, params);
1016 cc->convert_via = cc->convert;
1017 cc->convert = indexed_via_separation_via_base;
1018 }
1019 else
1020 {
1021 cc->ss = ss->u.indexed.base;
1022 cc->ss_via = ss;
1023 fz_init_process_color_converter(ctx, cc, cc->ss, ds, is, params);
1024 cc->convert_via = cc->convert;
1025 cc->convert = indexed_via_base;
1026 }
1027 }
1028 else if (ss->type == FZ_COLORSPACE_SEPARATION)
1029 {
1030 if (dseps &&
1031 fz_init_separation_copy_color_converter(ctx, cc, ss, ds, dseps, is, params))
1032 {
1033 /* We can just copy separations from ss to dseps */
1034 cc->dseps = dseps;
1035 cc->dst_n += fz_count_separations(ctx, dseps);
1036 }
1037 else
1038 {
1039 cc->ss = ss->u.separation.base;
1040 cc->ss_via = ss;
1041 fz_init_process_color_converter(ctx, cc, cc->ss, ds, is, params);
1042 cc->convert_via = cc->convert;
1043 cc->convert = separation_via_base;
1044 }
1045 }
1046 else
1047 {
1048 cc->ss = ss;
1049 fz_init_process_color_converter(ctx, cc, ss, ds, is, params);
1050 }
1051 }
1052
1053 void
1054 fz_drop_color_converter(fz_context *ctx, fz_color_converter *cc)
1055 {
1056 #if FZ_ENABLE_ICC
1057 if (cc->link)
1058 {
1059 fz_drop_icc_link(ctx, cc->link);
1060 cc->link = NULL;
1061 }
1062 #endif
1063 }
1064
1065 void
1066 fz_convert_color(fz_context *ctx, fz_colorspace *ss, const float *sv, fz_colorspace *ds, float *dv, fz_colorspace *is, fz_color_params params)
1067 {
1068 fz_color_converter cc;
1069 fz_find_color_converter(ctx, &cc, ss, ds, NULL, is, params);
1070 cc.convert(ctx, &cc, sv, dv);
1071 fz_drop_color_converter(ctx, &cc);
1072 }
1073
1074 /* Cached color converter using hash table. */
1075
1076 typedef struct fz_cached_color_converter
1077 {
1078 fz_color_converter base;
1079 fz_hash_table *hash;
1080 } fz_cached_color_converter;
1081
1082 static void fz_cached_color_convert(fz_context *ctx, fz_color_converter *cc_, const float *ss, float *ds)
1083 {
1084 fz_cached_color_converter *cc = cc_->opaque;
1085 if (cc->hash)
1086 {
1087 float *val = fz_hash_find(ctx, cc->hash, ss);
1088 int n = cc->base.dst_n * sizeof(float);
1089
1090 if (val)
1091 {
1092 memcpy(ds, val, n);
1093 return;
1094 }
1095
1096 cc->base.convert(ctx, &cc->base, ss, ds);
1097
1098 val = Memento_label(fz_malloc_array(ctx, cc->base.dst_n, float), "cached_color_convert");
1099 memcpy(val, ds, n);
1100 fz_try(ctx)
1101 fz_hash_insert(ctx, cc->hash, ss, val);
1102 fz_catch(ctx)
1103 {
1104 fz_rethrow_if(ctx, FZ_ERROR_SYSTEM);
1105 fz_report_error(ctx);
1106 fz_free(ctx, val);
1107 }
1108 }
1109 else
1110 {
1111 cc->base.convert(ctx, &cc->base, ss, ds);
1112 }
1113 }
1114
1115 void fz_init_cached_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)
1116 {
1117 int n = ss->n;
1118 fz_cached_color_converter *cached = fz_malloc_struct(ctx, fz_cached_color_converter);
1119
1120 cc->opaque = cached;
1121 cc->convert = fz_cached_color_convert;
1122 cc->ss = ss;
1123 cc->ds = ds;
1124 #if FZ_ENABLE_ICC
1125 cc->link = NULL;
1126 #endif
1127
1128 fz_try(ctx)
1129 {
1130 fz_find_color_converter(ctx, &cached->base, ss, ds, dseps, is, params);
1131 if (n * sizeof(float) <= FZ_HASH_TABLE_KEY_LENGTH)
1132 cached->hash = fz_new_hash_table(ctx, 256, n * sizeof(float), -1, fz_free);
1133 else
1134 fz_warn(ctx, "colorspace has too many components to be cached");
1135 }
1136 fz_catch(ctx)
1137 {
1138 fz_drop_color_converter(ctx, &cached->base);
1139 fz_drop_hash_table(ctx, cached->hash);
1140 fz_free(ctx, cached);
1141 cc->opaque = NULL;
1142 fz_rethrow(ctx);
1143 }
1144 }
1145
1146 void fz_fin_cached_color_converter(fz_context *ctx, fz_color_converter *cc_)
1147 {
1148 fz_cached_color_converter *cc;
1149 if (cc_ == NULL)
1150 return;
1151 cc = cc_->opaque;
1152 if (cc == NULL)
1153 return;
1154 cc_->opaque = NULL;
1155 fz_drop_hash_table(ctx, cc->hash);
1156 fz_drop_color_converter(ctx, &cc->base);
1157 fz_free(ctx, cc);
1158 }
1159
1160 /* Pixmap color conversion */
1161
1162 static inline void
1163 template_convert_lab(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params, int sa, int da, int spots)
1164 {
1165 float srcv[FZ_MAX_COLORS];
1166 float dstv[FZ_MAX_COLORS];
1167 size_t w = src->w;
1168 int h = src->h;
1169 fz_colorspace *src_cs = src->colorspace;
1170 fz_colorspace *dst_cs = dst->colorspace;
1171
1172 unsigned char *s = src->samples;
1173 unsigned char *d = dst->samples;
1174
1175 int src_n = spots ? src->n : 3+sa;
1176 int dst_c = dst->n - (spots ? dst->s : 0) - da;
1177 int dst_n = dst->n;
1178
1179 fz_color_converter cc;
1180 int alpha = 255;
1181 ptrdiff_t d_line_inc = dst->stride - w * dst->n;
1182 ptrdiff_t s_line_inc = src->stride - w * src->n;
1183
1184 int k;
1185
1186 fz_find_color_converter(ctx, &cc, src_cs, dst_cs, NULL, is, params);
1187 while (h--)
1188 {
1189 size_t ww = w;
1190 while (ww--)
1191 {
1192 if (sa)
1193 {
1194 alpha = s[4];
1195 srcv[0] = fz_div255(s[0], alpha) / 255.0f * 100;
1196 srcv[1] = fz_div255(s[1], alpha) - 128;
1197 srcv[2] = fz_div255(s[2], alpha) - 128;
1198 }
1199 else
1200 {
1201 srcv[0] = s[0] / 255.0f * 100;
1202 srcv[1] = s[1] - 128;
1203 srcv[2] = s[2] - 128;
1204 }
1205 s += src_n;
1206
1207 cc.convert(ctx, &cc, srcv, dstv);
1208
1209 if (da)
1210 {
1211 for (k = 0; k < dst_c; k++)
1212 *d++ = fz_mul255(dstv[k] * 255, alpha);
1213 /* Just fill in spots as empty */
1214 if (spots)
1215 for (; k < dst_n; k++)
1216 *d++ = 0;
1217 *d++ = alpha;
1218 }
1219 else
1220 {
1221 for (k = 0; k < dst_c; k++)
1222 *d++ = dstv[k] * 255;
1223 if (spots)
1224 for (; k < dst_n; k++)
1225 *d++ = 0;
1226 }
1227 }
1228 d += d_line_inc;
1229 s += s_line_inc;
1230 }
1231 fz_drop_color_converter(ctx, &cc);
1232 }
1233
1234 static void convert_lab(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params)
1235 {
1236 template_convert_lab(ctx, src, dst, is, params, 0, 0, 0);
1237 }
1238
1239 static void convert_lab_sa(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params)
1240 {
1241 template_convert_lab(ctx, src, dst, is, params, 1, 0, 0);
1242 }
1243
1244 static void convert_lab_da(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params)
1245 {
1246 template_convert_lab(ctx, src, dst, is, params, 0, 1, 0);
1247 }
1248
1249 static void convert_lab_sa_da(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params)
1250 {
1251 template_convert_lab(ctx, src, dst, is, params, 1, 1, 0);
1252 }
1253
1254 static void convert_lab_spots(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params)
1255 {
1256 template_convert_lab(ctx, src, dst, is, params, 0, 0, 1);
1257 }
1258
1259 static void convert_lab_sa_spots(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params)
1260 {
1261 template_convert_lab(ctx, src, dst, is, params, 1, 0, 1);
1262 }
1263
1264 static void convert_lab_da_spots(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params)
1265 {
1266 template_convert_lab(ctx, src, dst, is, params, 0, 1, 1);
1267 }
1268
1269 static void convert_lab_sa_da_spots(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params)
1270 {
1271 template_convert_lab(ctx, src, dst, is, params, 1, 1, 1);
1272 }
1273
1274 static inline void
1275 template_brute_force(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params, int sa, int da, int spots)
1276 {
1277 float srcv[FZ_MAX_COLORS];
1278 float dstv[FZ_MAX_COLORS];
1279 size_t w = src->w;
1280 int h = src->h;
1281 fz_colorspace *src_cs = src->colorspace;
1282 fz_colorspace *dst_cs = dst->colorspace;
1283
1284 unsigned char *s = src->samples;
1285 unsigned char *d = dst->samples;
1286
1287 int src_c = src->n - (spots ? src->s : 0) - sa;
1288 int src_n = src->n;
1289 int dst_c = dst->n - (spots ? dst->s : 0) - da;
1290 int dst_n = dst->n;
1291
1292 fz_color_converter cc;
1293 int alpha = 255;
1294 ptrdiff_t d_line_inc = dst->stride - w * dst->n;
1295 ptrdiff_t s_line_inc = src->stride - w * src->n;
1296
1297 int k;
1298
1299 fz_find_color_converter(ctx, &cc, src_cs, dst_cs, NULL, is, params);
1300 while (h--)
1301 {
1302 size_t ww = w;
1303 while (ww--)
1304 {
1305 if (sa)
1306 {
1307 alpha = s[src_n];
1308 for (k = 0; k < src_c; k++)
1309 srcv[k] = fz_div255(s[k], alpha) / 255.0f;
1310 }
1311 else
1312 {
1313 for (k = 0; k < src_c; k++)
1314 srcv[k] = s[k] / 255.0f;
1315 }
1316 s += src_n;
1317
1318 cc.convert(ctx, &cc, srcv, dstv);
1319
1320 if (da)
1321 {
1322 for (k = 0; k < dst_c; k++)
1323 *d++ = fz_mul255(dstv[k] * 255, alpha);
1324 if (spots)
1325 for (; k < dst_n; k++)
1326 *d++ = 0;
1327 *d++ = alpha;
1328 }
1329 else
1330 {
1331 for (k = 0; k < dst_c; k++)
1332 *d++ = dstv[k] * 255;
1333 if (spots)
1334 for (; k < dst_n; k++)
1335 *d++ = 0;
1336 }
1337 }
1338 d += d_line_inc;
1339 s += s_line_inc;
1340 }
1341 fz_drop_color_converter(ctx, &cc);
1342 }
1343
1344 static void brute_force(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params)
1345 {
1346 template_brute_force(ctx, src, dst, is, params, 0, 0, 0);
1347 }
1348
1349 static void brute_force_sa(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params)
1350 {
1351 template_brute_force(ctx, src, dst, is, params, 1, 0, 0);
1352 }
1353
1354 static void brute_force_da(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params)
1355 {
1356 template_brute_force(ctx, src, dst, is, params, 0, 1, 0);
1357 }
1358
1359 static void brute_force_sa_da(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params)
1360 {
1361 template_brute_force(ctx, src, dst, is, params, 1, 1, 0);
1362 }
1363
1364 static void brute_force_spots(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params)
1365 {
1366 template_brute_force(ctx, src, dst, is, params, 0, 0, 1);
1367 }
1368
1369 static void brute_force_sa_spots(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params)
1370 {
1371 template_brute_force(ctx, src, dst, is, params, 1, 0, 1);
1372 }
1373
1374 static void brute_force_da_spots(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params)
1375 {
1376 template_brute_force(ctx, src, dst, is, params, 0, 1, 1);
1377 }
1378
1379 static void brute_force_sa_da_spots(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params)
1380 {
1381 template_brute_force(ctx, src, dst, is, params, 1, 1, 1);
1382 }
1383
1384 static void
1385 lookup_1d(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params)
1386 {
1387 float srcv[FZ_MAX_COLORS];
1388 float dstv[FZ_MAX_COLORS];
1389 size_t w = src->w;
1390 int h = src->h;
1391 fz_colorspace *src_cs = src->colorspace;
1392 fz_colorspace *dst_cs = dst->colorspace;
1393
1394 unsigned char *s = src->samples;
1395 unsigned char *d = dst->samples;
1396
1397 int sa = src->alpha;
1398 int da = dst->alpha;
1399 int dst_s = dst->s;
1400 int dst_n = dst->n;
1401 int dst_c = dst_n - dst_s - da;
1402
1403 fz_color_converter cc;
1404 int alpha = 255;
1405 ptrdiff_t d_line_inc = dst->stride - w * dst->n;
1406 ptrdiff_t s_line_inc = src->stride - w * src->n;
1407
1408 int i, k;
1409
1410 unsigned char lookup[FZ_MAX_COLORS * 256];
1411
1412 fz_find_color_converter(ctx, &cc, src_cs, dst_cs, NULL, is, params);
1413 for (i = 0; i < 256; i++)
1414 {
1415 srcv[0] = i / 255.0f;
1416 cc.convert(ctx, &cc, srcv, dstv);
1417 for (k = 0; k < dst_c; k++)
1418 lookup[i * dst_c + k] = dstv[k] * 255;
1419 }
1420 fz_drop_color_converter(ctx, &cc);
1421
1422 while (h--)
1423 {
1424 size_t ww = w;
1425 while (ww--)
1426 {
1427 if (sa)
1428 {
1429 alpha = s[1];
1430 i = fz_div255(s[0], alpha);
1431 s += 2;
1432 }
1433 else
1434 {
1435 i = *s++;
1436 }
1437
1438 if (da)
1439 {
1440 for (k = 0; k < dst_c; k++)
1441 *d++ = fz_mul255(lookup[i * dst_c + k], alpha);
1442 for (; k < dst_n; k++)
1443 *d++ = 0;
1444 *d++ = alpha;
1445 }
1446 else
1447 {
1448 for (k = 0; k < dst_c; k++)
1449 *d++ = lookup[i * dst_c + k];
1450 for (; k < dst_n; k++)
1451 *d++ = 0;
1452 }
1453 }
1454 d += d_line_inc;
1455 s += s_line_inc;
1456 }
1457 }
1458
1459 static void
1460 memoize_nospots(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params)
1461 {
1462 float srcv[FZ_MAX_COLORS];
1463 float dstv[FZ_MAX_COLORS];
1464 size_t w = src->w;
1465 int h = src->h;
1466 fz_colorspace *src_cs = src->colorspace;
1467 fz_colorspace *dst_cs = dst->colorspace;
1468
1469 unsigned char *s = src->samples;
1470 unsigned char *d = dst->samples;
1471
1472 int sa = src->alpha;
1473 int src_s = src->s;
1474 int src_n = src->n;
1475 int src_c = src_n - src_s - sa;
1476 int da = dst->alpha;
1477 int dst_s = dst->s;
1478 int dst_n = dst->n;
1479 int dst_c = dst_n - dst_s - da;
1480
1481 ptrdiff_t d_line_inc = dst->stride - w * dst->n;
1482 ptrdiff_t s_line_inc = src->stride - w * src->n;
1483
1484 int k;
1485
1486 fz_hash_table *lookup;
1487 unsigned char *color;
1488 unsigned char dummy = s[0] ^ 255;
1489 unsigned char *sold = &dummy;
1490 unsigned char *dold;
1491 fz_color_converter cc;
1492 int alpha = 255;
1493
1494 lookup = fz_new_hash_table(ctx, 509, src_n, -1, NULL);
1495 fz_find_color_converter(ctx, &cc, src_cs, dst_cs, NULL, is, params);
1496
1497 fz_try(ctx)
1498 {
1499 while (h--)
1500 {
1501 size_t ww = w;
1502 while (ww--)
1503 {
1504 if (*s == *sold && memcmp(sold, s, src_n) == 0)
1505 {
1506 sold = s;
1507 memcpy(d, dold, dst_n);
1508 }
1509 else
1510 {
1511 sold = s;
1512 dold = d;
1513 color = fz_hash_find(ctx, lookup, s);
1514 if (color)
1515 {
1516 memcpy(d, color, dst_n);
1517 }
1518 else
1519 {
1520 if (sa)
1521 {
1522 alpha = s[src_c];
1523 for (k = 0; k < src_c; k++)
1524 srcv[k] = fz_div255(s[k], alpha) / 255.0f;
1525 }
1526 else
1527 {
1528 for (k = 0; k < src_c; k++)
1529 srcv[k] = s[k] / 255.0f;
1530 }
1531
1532 cc.convert(ctx, &cc, srcv, dstv);
1533
1534 if (da)
1535 {
1536 for (k = 0; k < dst_c; k++)
1537 d[k] = fz_mul255(dstv[k] * 255, alpha);
1538 d[k] = alpha;
1539 }
1540 else
1541 {
1542 for (k = 0; k < dst_c; k++)
1543 d[k] = dstv[k] * 255;
1544 }
1545
1546 fz_hash_insert(ctx, lookup, s, d);
1547 }
1548 }
1549 s += src_n;
1550 d += dst_n;
1551 }
1552 d += d_line_inc;
1553 s += s_line_inc;
1554 }
1555 }
1556 fz_always(ctx)
1557 {
1558 fz_drop_color_converter(ctx, &cc);
1559 fz_drop_hash_table(ctx, lookup);
1560 }
1561 fz_catch(ctx)
1562 fz_rethrow(ctx);
1563 }
1564
1565 static void
1566 memoize_spots(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params)
1567 {
1568 float srcv[FZ_MAX_COLORS];
1569 float dstv[FZ_MAX_COLORS];
1570 size_t w = src->w;
1571 int h = src->h;
1572 fz_colorspace *src_cs = src->colorspace;
1573 fz_colorspace *dst_cs = dst->colorspace;
1574
1575 unsigned char *s = src->samples;
1576 unsigned char *d = dst->samples;
1577
1578 int sa = src->alpha;
1579 int src_s = src->s;
1580 int src_n = src->n;
1581 int src_c = src_n - src_s - sa;
1582 int src_m = src_c + sa;
1583 int da = dst->alpha;
1584 int dst_s = dst->s;
1585 int dst_n = dst->n;
1586 int dst_c = dst_n - dst_s - da;
1587
1588 ptrdiff_t d_line_inc = dst->stride - w * dst->n;
1589 ptrdiff_t s_line_inc = src->stride - w * src->n;
1590
1591 int k;
1592
1593 fz_hash_table *lookup;
1594 unsigned char *color;
1595 unsigned char sold[FZ_MAX_COLORS];
1596 unsigned char dold[FZ_MAX_COLORS];
1597 fz_color_converter cc;
1598 int alpha = 255;
1599
1600 sold[0] = s[0] ^ 255;
1601
1602 lookup = fz_new_hash_table(ctx, 509, src_m, -1, NULL);
1603 fz_find_color_converter(ctx, &cc, src_cs, dst_cs, NULL, is, params);
1604
1605 fz_try(ctx)
1606 {
1607 while (h--)
1608 {
1609 size_t ww = w;
1610 while (ww--)
1611 {
1612 if (*s == *sold && memcmp(sold, s, src_m) == 0)
1613 {
1614 memcpy(d, dold, dst_c);
1615 if (dst_s)
1616 memset(d + dst_c, 0, dst_s);
1617 if (da)
1618 d[dst_n-1] = sold[src_m];
1619 }
1620 else
1621 {
1622 memcpy(sold, s, src_m);
1623 if (sa)
1624 sold[src_m] = s[src_n-1];
1625 color = fz_hash_find(ctx, lookup, sold);
1626 if (color)
1627 {
1628 memcpy(d, color, dst_n);
1629 }
1630 else
1631 {
1632 if (sa)
1633 {
1634 alpha = s[src_c];
1635 for (k = 0; k < src_c; k++)
1636 srcv[k] = fz_div255(s[k], alpha) / 255.0f;
1637 }
1638 else
1639 {
1640 for (k = 0; k < src_c; k++)
1641 srcv[k] = s[k] / 255.0f;
1642 }
1643
1644 cc.convert(ctx, &cc, srcv, dstv);
1645
1646 if (da)
1647 {
1648 for (k = 0; k < dst_c; k++)
1649 d[k] = fz_mul255(dstv[k] * 255, alpha);
1650 if (dst_s)
1651 memset(d + dst_c, 0, dst_s);
1652 dold[dst_c] = d[dst_n-1] = alpha;
1653 }
1654 else
1655 {
1656 for (k = 0; k < dst_c; k++)
1657 d[k] = dstv[k] * 255;
1658 if (dst_s)
1659 memset(d + dst_c, 0, dst_s);
1660 }
1661 memcpy(dold, d, dst_c);
1662
1663 fz_hash_insert(ctx, lookup, sold, dold);
1664 }
1665 }
1666 s += src_n;
1667 d += dst_n;
1668 }
1669 d += d_line_inc;
1670 s += s_line_inc;
1671 }
1672 }
1673 fz_always(ctx)
1674 {
1675 fz_drop_color_converter(ctx, &cc);
1676 fz_drop_hash_table(ctx, lookup);
1677 }
1678 fz_catch(ctx)
1679 fz_rethrow(ctx);
1680 }
1681
1682 void
1683 fz_convert_slow_pixmap_samples(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst, fz_colorspace *is, fz_color_params params, int copy_spots)
1684 {
1685 int sa = src->alpha;
1686 int src_s = src->s;
1687 int src_c = src->n - src_s - sa;
1688 int da = dst->alpha;
1689 int dst_s = dst->s;
1690 size_t w = src->w;
1691 int h = src->h;
1692 ptrdiff_t d_line_inc = dst->stride - w * dst->n;
1693 ptrdiff_t s_line_inc = src->stride - w * src->n;
1694
1695 fz_colorspace *ss = src->colorspace;
1696
1697 if ((int)w < 0 || h < 0)
1698 return;
1699
1700 assert(src->w == dst->w && src->h == dst->h);
1701
1702 if (d_line_inc == 0 && s_line_inc == 0)
1703 {
1704 w *= h;
1705 h = 1;
1706 }
1707
1708 if (src_s != 0 || dst_s != 0)
1709 {
1710 fz_warn(ctx, "Spots dropped during pixmap conversion");
1711 }
1712
1713 /* Special case for Lab colorspace (scaling of components to float) */
1714 if (ss->type == FZ_COLORSPACE_LAB)
1715 {
1716 if (src_s == 0 && dst_s == 0)
1717 {
1718 if (sa)
1719 {
1720 if (da)
1721 convert_lab_sa_da(ctx, src, dst, is, params);
1722 else
1723 convert_lab_sa(ctx, src, dst, is, params);
1724 }
1725 else
1726 {
1727 if (da)
1728 convert_lab_da(ctx, src, dst, is, params);
1729 else
1730 convert_lab(ctx, src, dst, is, params);
1731 }
1732 }
1733 else
1734 {
1735 if (sa)
1736 {
1737 if (da)
1738 convert_lab_sa_da_spots(ctx, src, dst, is, params);
1739 else
1740 convert_lab_sa_spots(ctx, src, dst, is, params);
1741 }
1742 else
1743 {
1744 if (da)
1745 convert_lab_da_spots(ctx, src, dst, is, params);
1746 else
1747 convert_lab_spots(ctx, src, dst, is, params);
1748 }
1749 }
1750 }
1751
1752 /* Brute-force for small images */
1753 else if (w*h < 256)
1754 {
1755 if (src_s == 0 && dst_s == 0)
1756 {
1757 if (sa)
1758 {
1759 if (da)
1760 brute_force_sa_da(ctx, src, dst, is, params);
1761 else
1762 brute_force_sa(ctx, src, dst, is, params);
1763 }
1764 else
1765 {
1766 if (da)
1767 brute_force_da(ctx, src, dst, is, params);
1768 else
1769 brute_force(ctx, src, dst, is, params);
1770 }
1771 }
1772 else
1773 {
1774 if (sa)
1775 {
1776 if (da)
1777 brute_force_sa_da_spots(ctx, src, dst, is, params);
1778 else
1779 brute_force_sa_spots(ctx, src, dst, is, params);
1780 }
1781 else
1782 {
1783 if (da)
1784 brute_force_da_spots(ctx, src, dst, is, params);
1785 else
1786 brute_force_spots(ctx, src, dst, is, params);
1787 }
1788 }
1789 }
1790
1791 /* 1-d lookup table for single channel colorspaces */
1792 else if (src_c == 1)
1793 {
1794 lookup_1d(ctx, src, dst, is, params);
1795 }
1796
1797 /* Memoize colors using a hash table for the general case */
1798 else
1799 {
1800 if (src_s == 0 && dst_s == 0)
1801 memoize_nospots(ctx, src, dst, is, params);
1802 else
1803 memoize_spots(ctx, src, dst, is, params);
1804 }
1805 }
1806
1807 void
1808 fz_convert_pixmap_samples(fz_context *ctx, const fz_pixmap *src, fz_pixmap *dst,
1809 fz_colorspace *prf,
1810 const fz_default_colorspaces *default_cs,
1811 fz_color_params params,
1812 int copy_spots)
1813 {
1814 #if FZ_ENABLE_ICC
1815 fz_colorspace *ss = src->colorspace;
1816 fz_colorspace *ds = dst->colorspace;
1817 fz_pixmap *base_idx = NULL;
1818 fz_pixmap *base_sep = NULL;
1819 fz_icc_link *link = NULL;
1820
1821 fz_var(link);
1822 fz_var(base_idx);
1823 fz_var(base_sep);
1824
1825 if (!ds)
1826 {
1827 fz_fast_any_to_alpha(ctx, src, dst, copy_spots);
1828 return;
1829 }
1830
1831 fz_try(ctx)
1832 {
1833 /* Treat any alpha-only pixmap as being device gray here. */
1834 if (!ss)
1835 ss = fz_device_gray(ctx);
1836
1837 /* Convert indexed into base colorspace. */
1838 if (ss->type == FZ_COLORSPACE_INDEXED)
1839 {
1840 src = base_idx = fz_convert_indexed_pixmap_to_base(ctx, src);
1841 ss = src->colorspace;
1842 }
1843
1844 /* Convert separation into base colorspace. */
1845 if (ss->type == FZ_COLORSPACE_SEPARATION)
1846 {
1847 src = base_sep = fz_convert_separation_pixmap_to_base(ctx, src);
1848 ss = src->colorspace;
1849 }
1850
1851 /* Substitute Device colorspace with page Default colorspace: */
1852 if (ss->flags & FZ_COLORSPACE_IS_DEVICE)
1853 {
1854 switch (ss->type)
1855 {
1856 default: break;
1857 case FZ_COLORSPACE_GRAY: ss = fz_default_gray(ctx, default_cs); break;
1858 case FZ_COLORSPACE_RGB: ss = fz_default_rgb(ctx, default_cs); break;
1859 case FZ_COLORSPACE_CMYK: ss = fz_default_cmyk(ctx, default_cs); break;
1860 }
1861 }
1862
1863 if (!ctx->icc_enabled)
1864 {
1865 fz_convert_fast_pixmap_samples(ctx, src, dst, copy_spots);
1866 }
1867
1868 /* Handle identity case. */
1869 else if (ss == ds || (!memcmp(ss->u.icc.md5, ds->u.icc.md5, 16)))
1870 {
1871 fz_convert_fast_pixmap_samples(ctx, src, dst, copy_spots);
1872 }
1873
1874 /* Handle DeviceGray to CMYK as K only. See note in Section 6.3 of PDF spec 1.7. */
1875 else if ((ss->flags & FZ_COLORSPACE_IS_DEVICE) &&
1876 (ss->type == FZ_COLORSPACE_GRAY) &&
1877 (ds->type == FZ_COLORSPACE_CMYK))
1878 {
1879 fz_convert_fast_pixmap_samples(ctx, src, dst, copy_spots);
1880 }
1881
1882 /* Use slow conversion path for indexed. */
1883 else if (ss->type == FZ_COLORSPACE_INDEXED)
1884 {
1885 fz_convert_slow_pixmap_samples(ctx, src, dst, prf, params, copy_spots);
1886 }
1887
1888 /* Use slow conversion path for separation. */
1889 else if (ss->type == FZ_COLORSPACE_SEPARATION)
1890 {
1891 fz_convert_slow_pixmap_samples(ctx, src, dst, prf, params, copy_spots);
1892 }
1893
1894 else
1895 {
1896 fz_try(ctx)
1897 {
1898 int sx = src->s + src->alpha;
1899 int dx = dst->s + dst->alpha;
1900 /* If there are no spots to copy, we might as well copy spots! */
1901 int effectively_copying_spots = copy_spots || (src->s == 0 && dst->s == 0);
1902 /* If we have alpha, we're preserving spots and we have the same number
1903 * of 'extra' (non process, spots+alpha) channels (i.e. sx == dx), then
1904 * we get lcms2 to do the premultiplication handling for us. If not,
1905 * fz_icc_transform_pixmap will have to do it by steam. */
1906 int premult = src->alpha && (sx == dx) && effectively_copying_spots;
1907 link = fz_find_icc_link(ctx, ss, sx, ds, dx, prf, params, 0, effectively_copying_spots, premult);
1908 fz_icc_transform_pixmap(ctx, link, src, dst, effectively_copying_spots);
1909 }
1910 fz_catch(ctx)
1911 {
1912 fz_rethrow_if(ctx, FZ_ERROR_SYSTEM);
1913 fz_report_error(ctx);
1914 fz_warn(ctx, "falling back to fast color conversion");
1915 fz_convert_fast_pixmap_samples(ctx, src, dst, copy_spots);
1916 }
1917 }
1918 }
1919 fz_always(ctx)
1920 {
1921 fz_drop_icc_link(ctx, link);
1922 fz_drop_pixmap(ctx, base_sep);
1923 fz_drop_pixmap(ctx, base_idx);
1924 }
1925 fz_catch(ctx)
1926 fz_rethrow(ctx);
1927 #else
1928 fz_convert_fast_pixmap_samples(ctx, src, dst, copy_spots);
1929 #endif
1930 }
1931
1932 void fz_colorspace_digest(fz_context *ctx, fz_colorspace *cs, unsigned char digest[16])
1933 {
1934 #if FZ_ENABLE_ICC
1935 if (!fz_colorspace_is_icc(ctx, cs))
1936 fz_throw(ctx, FZ_ERROR_ARGUMENT, "must have icc profile for colorspace digest");
1937 memcpy(digest, cs->u.icc.md5, 16);
1938 #else
1939 fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "ICC support disabled");
1940 #endif
1941 }