1 /*
2 * "$Id: image-colorspace.c 7720 2008-07-11 22:46:21Z mike $"
3 *
4 * Colorspace conversions for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 2007-2008 by Apple Inc.
7 * Copyright 1993-2006 by Easy Software Products.
8 *
9 * The color saturation/hue matrix stuff is provided thanks to Mr. Paul
10 * Haeberli at "http://www.sgi.com/grafica/matrix/index.html".
11 *
12 * These coded instructions, statements, and computer programs are the
13 * property of Apple Inc. and are protected by Federal copyright
14 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
15 * which should have been included with this file. If this file is
16 * file is missing or damaged, see the license at "http://www.cups.org/".
17 *
18 * This file is subject to the Apple OS-Developed Software exception.
19 *
20 * Contents:
21 *
22 * cupsImageCMYKToBlack() - Convert CMYK data to black.
23 * cupsImageCMYKToCMY() - Convert CMYK colors to CMY.
24 * cupsImageCMYKToCMYK() - Convert CMYK colors to CMYK.
25 * cupsImageCMYKToRGB() - Convert CMYK colors to device-dependent
26 * RGB.
27 * cupsImageCMYKToWhite() - Convert CMYK colors to luminance.
28 * cupsImageLut() - Adjust all pixel values with the given
29 * LUT.
30 * cupsImageRGBAdjust() - Adjust the hue and saturation of the
31 * given RGB colors.
32 * cupsImageRGBToBlack() - Convert RGB data to black.
33 * cupsImageRGBToCMY() - Convert RGB colors to CMY.
34 * cupsImageRGBToCMYK() - Convert RGB colors to CMYK.
35 * cupsImageRGBToRGB() - Convert RGB colors to device-dependent
36 * RGB.
37 * cupsImageRGBToWhite() - Convert RGB colors to luminance.
38 * cupsImageSetProfile() - Set the device color profile.
39 * cupsImageSetRasterColorSpace() - Set the destination colorspace.
40 * cupsImageWhiteToBlack() - Convert luminance colors to black.
41 * cupsImageWhiteToCMY() - Convert luminance colors to CMY.
42 * cupsImageWhiteToCMYK() - Convert luminance colors to CMYK.
43 * cupsImageWhiteToRGB() - Convert luminance data to RGB.
44 * cupsImageWhiteToWhite() - Convert luminance colors to device-
45 * dependent luminance.
46 * cielab() - Map CIE Lab transformation...
47 * huerotate() - Rotate the hue, maintaining luminance.
48 * ident() - Make an identity matrix.
49 * mult() - Multiply two matrices.
50 * rgb_to_lab() - Convert an RGB color to CIE Lab.
51 * rgb_to_xyz() - Convert an RGB color to CIE XYZ.
52 * saturate() - Make a saturation matrix.
53 * xform() - Transform a 3D point using a matrix...
54 * xrotate() - Rotate about the x (red) axis...
55 * yrotate() - Rotate about the y (green) axis...
56 * zrotate() - Rotate about the z (blue) axis...
57 * zshear() - Shear z using x and y...
58 */
59
60 /*
61 * Include necessary headers...
62 */
63
64 #include "image-private.h"
65
66 #if WIN32
67 #define cbrt(arg) pow(arg, 1.0/3)
68 #endif
69
70 /*
71 * Define some math constants that are required...
72 */
73
74 #ifndef M_PI
75 # define M_PI 3.14159265358979323846
76 #endif /* !M_PI */
77
78 #ifndef M_SQRT2
79 # define M_SQRT2 1.41421356237309504880
80 #endif /* !M_SQRT2 */
81
82 #ifndef M_SQRT1_2
83 # define M_SQRT1_2 0.70710678118654752440
84 #endif /* !M_SQRT1_2 */
85
86 /*
87 * CIE XYZ whitepoint...
88 */
89
90 #define D65_X (0.412453 + 0.357580 + 0.180423)
91 #define D65_Y (0.212671 + 0.715160 + 0.072169)
92 #define D65_Z (0.019334 + 0.119193 + 0.950227)
93
94
95 /*
96 * Lookup table structure...
97 */
98
99 typedef int cups_clut_t[3][256];
100
101
102 /*
103 * Local globals...
104 */
105
106 static int cupsImageHaveProfile = 0;
107 /* Do we have a color profile? */
108 static int *cupsImageDensity;
109 /* Ink/marker density LUT */
110 static cups_clut_t *cupsImageMatrix;
111 /* Color transform matrix LUT */
112 static cups_cspace_t cupsImageColorSpace = CUPS_CSPACE_RGB;
113 /* Destination colorspace */
114
115
116 /*
117 * Local functions...
118 */
119
120 static float cielab(float x, float xn);
121 static void huerotate(float [3][3], float);
122 static void ident(float [3][3]);
123 static void mult(float [3][3], float [3][3], float [3][3]);
124 static void rgb_to_lab(cups_ib_t *val);
125 static void rgb_to_xyz(cups_ib_t *val);
126 static void saturate(float [3][3], float);
127 static void xform(float [3][3], float, float, float, float *, float *, float *);
128 static void xrotate(float [3][3], float, float);
129 static void yrotate(float [3][3], float, float);
130 static void zrotate(float [3][3], float, float);
131 static void zshear(float [3][3], float, float);
132
133
134 /*
135 * 'cupsImageCMYKToBlack()' - Convert CMYK data to black.
136 */
137
138 void
cupsImageCMYKToBlack(const cups_ib_t * in,cups_ib_t * out,int count)139 cupsImageCMYKToBlack(
140 const cups_ib_t *in, /* I - Input pixels */
141 cups_ib_t *out, /* I - Output pixels */
142 int count) /* I - Number of pixels */
143 {
144 int k; /* Black value */
145
146
147 if (cupsImageHaveProfile)
148 while (count > 0)
149 {
150 k = (31 * in[0] + 61 * in[1] + 8 * in[2]) / 100 + in[3];
151
152 if (k < 255)
153 *out++ = cupsImageDensity[k];
154 else
155 *out++ = cupsImageDensity[255];
156
157 in += 4;
158 count --;
159 }
160 else
161 while (count > 0)
162 {
163 k = (31 * in[0] + 61 * in[1] + 8 * in[2]) / 100 + in[3];
164
165 if (k < 255)
166 *out++ = k;
167 else
168 *out++ = 255;
169
170 in += 4;
171 count --;
172 }
173 }
174
175
176 /*
177 * 'cupsImageCMYKToCMY()' - Convert CMYK colors to CMY.
178 */
179
180 void
cupsImageCMYKToCMY(const cups_ib_t * in,cups_ib_t * out,int count)181 cupsImageCMYKToCMY(
182 const cups_ib_t *in, /* I - Input pixels */
183 cups_ib_t *out, /* I - Output pixels */
184 int count) /* I - Number of pixels */
185 {
186 int c, m, y, k; /* CMYK values */
187 int cc, cm, cy; /* Calibrated CMY values */
188
189
190 if (cupsImageHaveProfile)
191 while (count > 0)
192 {
193 c = *in++;
194 m = *in++;
195 y = *in++;
196 k = *in++;
197
198 cc = cupsImageMatrix[0][0][c] +
199 cupsImageMatrix[0][1][m] +
200 cupsImageMatrix[0][2][y] + k;
201 cm = cupsImageMatrix[1][0][c] +
202 cupsImageMatrix[1][1][m] +
203 cupsImageMatrix[1][2][y] + k;
204 cy = cupsImageMatrix[2][0][c] +
205 cupsImageMatrix[2][1][m] +
206 cupsImageMatrix[2][2][y] + k;
207
208 if (cc < 0)
209 *out++ = 0;
210 else if (cc > 255)
211 *out++ = cupsImageDensity[255];
212 else
213 *out++ = cupsImageDensity[cc];
214
215 if (cm < 0)
216 *out++ = 0;
217 else if (cm > 255)
218 *out++ = cupsImageDensity[255];
219 else
220 *out++ = cupsImageDensity[cm];
221
222 if (cy < 0)
223 *out++ = 0;
224 else if (cy > 255)
225 *out++ = cupsImageDensity[255];
226 else
227 *out++ = cupsImageDensity[cy];
228
229 count --;
230 }
231 else
232 while (count > 0)
233 {
234 c = *in++;
235 m = *in++;
236 y = *in++;
237 k = *in++;
238
239 c += k;
240 m += k;
241 y += k;
242
243 if (c < 255)
244 *out++ = c;
245 else
246 *out++ = 255;
247
248 if (m < 255)
249 *out++ = y;
250 else
251 *out++ = 255;
252
253 if (y < 255)
254 *out++ = y;
255 else
256 *out++ = 255;
257
258 count --;
259 }
260 }
261
262
263 /*
264 * 'cupsImageCMYKToCMYK()' - Convert CMYK colors to CMYK.
265 */
266
267 void
cupsImageCMYKToCMYK(const cups_ib_t * in,cups_ib_t * out,int count)268 cupsImageCMYKToCMYK(
269 const cups_ib_t *in, /* I - Input pixels */
270 cups_ib_t *out, /* I - Output pixels */
271 int count) /* I - Number of pixels */
272 {
273 int c, m, y, k; /* CMYK values */
274 int cc, cm, cy; /* Calibrated CMY values */
275
276
277 if (cupsImageHaveProfile)
278 while (count > 0)
279 {
280 c = *in++;
281 m = *in++;
282 y = *in++;
283 k = *in++;
284
285 cc = (cupsImageMatrix[0][0][c] +
286 cupsImageMatrix[0][1][m] +
287 cupsImageMatrix[0][2][y]);
288 cm = (cupsImageMatrix[1][0][c] +
289 cupsImageMatrix[1][1][m] +
290 cupsImageMatrix[1][2][y]);
291 cy = (cupsImageMatrix[2][0][c] +
292 cupsImageMatrix[2][1][m] +
293 cupsImageMatrix[2][2][y]);
294
295 if (cc < 0)
296 *out++ = 0;
297 else if (cc > 255)
298 *out++ = cupsImageDensity[255];
299 else
300 *out++ = cupsImageDensity[cc];
301
302 if (cm < 0)
303 *out++ = 0;
304 else if (cm > 255)
305 *out++ = cupsImageDensity[255];
306 else
307 *out++ = cupsImageDensity[cm];
308
309 if (cy < 0)
310 *out++ = 0;
311 else if (cy > 255)
312 *out++ = cupsImageDensity[255];
313 else
314 *out++ = cupsImageDensity[cy];
315
316 *out++ = cupsImageDensity[k];
317
318 count --;
319 }
320 else if (in != out)
321 {
322 while (count > 0)
323 {
324 *out++ = *in++;
325 *out++ = *in++;
326 *out++ = *in++;
327 *out++ = *in++;
328
329 count --;
330 }
331 }
332 }
333
334
335 /*
336 * 'cupsImageCMYKToRGB()' - Convert CMYK colors to device-dependent RGB.
337 */
338
339 void
cupsImageCMYKToRGB(const cups_ib_t * in,cups_ib_t * out,int count)340 cupsImageCMYKToRGB(
341 const cups_ib_t *in, /* I - Input pixels */
342 cups_ib_t *out, /* I - Output pixels */
343 int count) /* I - Number of pixels */
344 {
345 int c, m, y, k; /* CMYK values */
346 int cr, cg, cb; /* Calibrated RGB values */
347
348
349 if (cupsImageHaveProfile)
350 {
351 while (count > 0)
352 {
353 c = *in++;
354 m = *in++;
355 y = *in++;
356 k = *in++;
357
358 cr = cupsImageMatrix[0][0][c] +
359 cupsImageMatrix[0][1][m] +
360 cupsImageMatrix[0][2][y] + k;
361 cg = cupsImageMatrix[1][0][c] +
362 cupsImageMatrix[1][1][m] +
363 cupsImageMatrix[1][2][y] + k;
364 cb = cupsImageMatrix[2][0][c] +
365 cupsImageMatrix[2][1][m] +
366 cupsImageMatrix[2][2][y] + k;
367
368 if (cr < 0)
369 *out++ = 255;
370 else if (cr > 255)
371 *out++ = 255 - cupsImageDensity[255];
372 else
373 *out++ = 255 - cupsImageDensity[cr];
374
375 if (cg < 0)
376 *out++ = 255;
377 else if (cg > 255)
378 *out++ = 255 - cupsImageDensity[255];
379 else
380 *out++ = 255 - cupsImageDensity[cg];
381
382 if (cb < 0)
383 *out++ = 255;
384 else if (cb > 255)
385 *out++ = 255 - cupsImageDensity[255];
386 else
387 *out++ = 255 - cupsImageDensity[cb];
388
389 count --;
390 }
391 }
392 else
393 {
394 while (count > 0)
395 {
396 c = 255 - *in++;
397 m = 255 - *in++;
398 y = 255 - *in++;
399 k = *in++;
400
401 c -= k;
402 m -= k;
403 y -= k;
404
405 if (c > 0)
406 *out++ = c;
407 else
408 *out++ = 0;
409
410 if (m > 0)
411 *out++ = m;
412 else
413 *out++ = 0;
414
415 if (y > 0)
416 *out++ = y;
417 else
418 *out++ = 0;
419
420 if (cupsImageColorSpace == CUPS_CSPACE_CIELab ||
421 cupsImageColorSpace >= CUPS_CSPACE_ICC1)
422 rgb_to_lab(out - 3);
423 else if (cupsImageColorSpace == CUPS_CSPACE_CIEXYZ)
424 rgb_to_xyz(out - 3);
425
426 count --;
427 }
428 }
429 }
430
431
432 /*
433 * 'cupsImageCMYKToWhite()' - Convert CMYK colors to luminance.
434 */
435
436 void
cupsImageCMYKToWhite(const cups_ib_t * in,cups_ib_t * out,int count)437 cupsImageCMYKToWhite(
438 const cups_ib_t *in, /* I - Input pixels */
439 cups_ib_t *out, /* I - Output pixels */
440 int count) /* I - Number of pixels */
441 {
442 int w; /* White value */
443
444
445 if (cupsImageHaveProfile)
446 {
447 while (count > 0)
448 {
449 w = 255 - (31 * in[0] + 61 * in[1] + 8 * in[2]) / 100 - in[3];
450
451 if (w > 0)
452 *out++ = cupsImageDensity[w];
453 else
454 *out++ = cupsImageDensity[0];
455
456 in += 4;
457 count --;
458 }
459 }
460 else
461 {
462 while (count > 0)
463 {
464 w = 255 - (31 * in[0] + 61 * in[1] + 8 * in[2]) / 100 - in[3];
465
466 if (w > 0)
467 *out++ = w;
468 else
469 *out++ = 0;
470
471 in += 4;
472 count --;
473 }
474 }
475 }
476
477
478 /*
479 * 'cupsImageLut()' - Adjust all pixel values with the given LUT.
480 */
481
482 void
cupsImageLut(cups_ib_t * pixels,int count,const cups_ib_t * lut)483 cupsImageLut(cups_ib_t *pixels, /* IO - Input/output pixels */
484 int count, /* I - Number of pixels/bytes to adjust */
485 const cups_ib_t *lut) /* I - Lookup table */
486 {
487 while (count > 0)
488 {
489 *pixels = lut[*pixels];
490 pixels ++;
491 count --;
492 }
493 }
494
495
496 /*
497 * 'cupsImageRGBAdjust()' - Adjust the hue and saturation of the given RGB colors.
498 */
499
500 void
cupsImageRGBAdjust(cups_ib_t * pixels,int count,int saturation,int hue)501 cupsImageRGBAdjust(cups_ib_t *pixels, /* IO - Input/output pixels */
502 int count, /* I - Number of pixels to adjust */
503 int saturation,/* I - Color saturation (%) */
504 int hue) /* I - Color hue (degrees) */
505 {
506 int i, j, k; /* Looping vars */
507 float mat[3][3]; /* Color adjustment matrix */
508 static int last_sat = 100, /* Last saturation used */
509 last_hue = 0; /* Last hue used */
510 static cups_clut_t *lut = NULL; /* Lookup table for matrix */
511
512
513 if (saturation != last_sat || hue != last_hue || !lut)
514 {
515 /*
516 * Build the color adjustment matrix...
517 */
518
519 ident(mat);
520 saturate(mat, saturation * 0.01);
521 huerotate(mat, (float)hue);
522
523 /*
524 * Allocate memory for the lookup table...
525 */
526
527 if (lut == NULL)
528 lut = calloc(3, sizeof(cups_clut_t));
529
530 if (lut == NULL)
531 return;
532
533 /*
534 * Convert the matrix into a 3x3 array of lookup tables...
535 */
536
537 for (i = 0; i < 3; i ++)
538 for (j = 0; j < 3; j ++)
539 for (k = 0; k < 256; k ++)
540 lut[i][j][k] = mat[i][j] * k + 0.5;
541
542 /*
543 * Save the saturation and hue to compare later...
544 */
545
546 last_sat = saturation;
547 last_hue = hue;
548 }
549
550 /*
551 * Adjust each pixel in the given buffer.
552 */
553
554 while (count > 0)
555 {
556 i = lut[0][0][pixels[0]] +
557 lut[1][0][pixels[1]] +
558 lut[2][0][pixels[2]];
559 if (i < 0)
560 pixels[0] = 0;
561 else if (i > 255)
562 pixels[0] = 255;
563 else
564 pixels[0] = i;
565
566 i = lut[0][1][pixels[0]] +
567 lut[1][1][pixels[1]] +
568 lut[2][1][pixels[2]];
569 if (i < 0)
570 pixels[1] = 0;
571 else if (i > 255)
572 pixels[1] = 255;
573 else
574 pixels[1] = i;
575
576 i = lut[0][2][pixels[0]] +
577 lut[1][2][pixels[1]] +
578 lut[2][2][pixels[2]];
579 if (i < 0)
580 pixels[2] = 0;
581 else if (i > 255)
582 pixels[2] = 255;
583 else
584 pixels[2] = i;
585
586 count --;
587 pixels += 3;
588 }
589 }
590
591
592 /*
593 * 'cupsImageRGBToBlack()' - Convert RGB data to black.
594 */
595
596 void
cupsImageRGBToBlack(const cups_ib_t * in,cups_ib_t * out,int count)597 cupsImageRGBToBlack(
598 const cups_ib_t *in, /* I - Input pixels */
599 cups_ib_t *out, /* I - Output pixels */
600 int count) /* I - Number of pixels */
601 {
602 if (cupsImageHaveProfile)
603 while (count > 0)
604 {
605 *out++ = cupsImageDensity[255 - (31 * in[0] + 61 * in[1] + 8 * in[2]) / 100];
606 in += 3;
607 count --;
608 }
609 else
610 while (count > 0)
611 {
612 *out++ = 255 - (31 * in[0] + 61 * in[1] + 8 * in[2]) / 100;
613 in += 3;
614 count --;
615 }
616 }
617
618
619 /*
620 * 'cupsImageRGBToCMY()' - Convert RGB colors to CMY.
621 */
622
623 void
cupsImageRGBToCMY(const cups_ib_t * in,cups_ib_t * out,int count)624 cupsImageRGBToCMY(
625 const cups_ib_t *in, /* I - Input pixels */
626 cups_ib_t *out, /* I - Output pixels */
627 int count) /* I - Number of pixels */
628 {
629 int c, m, y, k; /* CMYK values */
630 int cc, cm, cy; /* Calibrated CMY values */
631
632
633 if (cupsImageHaveProfile)
634 while (count > 0)
635 {
636 c = 255 - *in++;
637 m = 255 - *in++;
638 y = 255 - *in++;
639 k = min(c, min(m, y));
640 c -= k;
641 m -= k;
642 y -= k;
643
644 cc = cupsImageMatrix[0][0][c] +
645 cupsImageMatrix[0][1][m] +
646 cupsImageMatrix[0][2][y] + k;
647 cm = cupsImageMatrix[1][0][c] +
648 cupsImageMatrix[1][1][m] +
649 cupsImageMatrix[1][2][y] + k;
650 cy = cupsImageMatrix[2][0][c] +
651 cupsImageMatrix[2][1][m] +
652 cupsImageMatrix[2][2][y] + k;
653
654 if (cc < 0)
655 *out++ = 0;
656 else if (cc > 255)
657 *out++ = cupsImageDensity[255];
658 else
659 *out++ = cupsImageDensity[cc];
660
661 if (cm < 0)
662 *out++ = 0;
663 else if (cm > 255)
664 *out++ = cupsImageDensity[255];
665 else
666 *out++ = cupsImageDensity[cm];
667
668 if (cy < 0)
669 *out++ = 0;
670 else if (cy > 255)
671 *out++ = cupsImageDensity[255];
672 else
673 *out++ = cupsImageDensity[cy];
674
675 count --;
676 }
677 else
678 while (count > 0)
679 {
680 c = 255 - in[0];
681 m = 255 - in[1];
682 y = 255 - in[2];
683 k = min(c, min(m, y));
684
685 *out++ = (255 - in[1] / 4) * (c - k) / 255 + k;
686 *out++ = (255 - in[2] / 4) * (m - k) / 255 + k;
687 *out++ = (255 - in[0] / 4) * (y - k) / 255 + k;
688 in += 3;
689 count --;
690 }
691 }
692
693
694 /*
695 * 'cupsImageRGBToCMYK()' - Convert RGB colors to CMYK.
696 */
697
698 void
cupsImageRGBToCMYK(const cups_ib_t * in,cups_ib_t * out,int count)699 cupsImageRGBToCMYK(
700 const cups_ib_t *in, /* I - Input pixels */
701 cups_ib_t *out, /* I - Output pixels */
702 int count) /* I - Number of pixels */
703 {
704 int c, m, y, k, /* CMYK values */
705 km; /* Maximum K value */
706 int cc, cm, cy; /* Calibrated CMY values */
707
708
709 if (cupsImageHaveProfile)
710 while (count > 0)
711 {
712 c = 255 - *in++;
713 m = 255 - *in++;
714 y = 255 - *in++;
715 k = min(c, min(m, y));
716
717 if ((km = max(c, max(m, y))) > k)
718 k = k * k * k / (km * km);
719
720 c -= k;
721 m -= k;
722 y -= k;
723
724 cc = (cupsImageMatrix[0][0][c] +
725 cupsImageMatrix[0][1][m] +
726 cupsImageMatrix[0][2][y]);
727 cm = (cupsImageMatrix[1][0][c] +
728 cupsImageMatrix[1][1][m] +
729 cupsImageMatrix[1][2][y]);
730 cy = (cupsImageMatrix[2][0][c] +
731 cupsImageMatrix[2][1][m] +
732 cupsImageMatrix[2][2][y]);
733
734 if (cc < 0)
735 *out++ = 0;
736 else if (cc > 255)
737 *out++ = cupsImageDensity[255];
738 else
739 *out++ = cupsImageDensity[cc];
740
741 if (cm < 0)
742 *out++ = 0;
743 else if (cm > 255)
744 *out++ = cupsImageDensity[255];
745 else
746 *out++ = cupsImageDensity[cm];
747
748 if (cy < 0)
749 *out++ = 0;
750 else if (cy > 255)
751 *out++ = cupsImageDensity[255];
752 else
753 *out++ = cupsImageDensity[cy];
754
755 *out++ = cupsImageDensity[k];
756
757 count --;
758 }
759 else
760 while (count > 0)
761 {
762 c = 255 - *in++;
763 m = 255 - *in++;
764 y = 255 - *in++;
765 k = min(c, min(m, y));
766
767 if ((km = max(c, max(m, y))) > k)
768 k = k * k * k / (km * km);
769
770 c -= k;
771 m -= k;
772 y -= k;
773
774 *out++ = c;
775 *out++ = m;
776 *out++ = y;
777 *out++ = k;
778
779 count --;
780 }
781 }
782
783
784 /*
785 * 'cupsImageRGBToRGB()' - Convert RGB colors to device-dependent RGB.
786 */
787
788 void
cupsImageRGBToRGB(const cups_ib_t * in,cups_ib_t * out,int count)789 cupsImageRGBToRGB(
790 const cups_ib_t *in, /* I - Input pixels */
791 cups_ib_t *out, /* I - Output pixels */
792 int count) /* I - Number of pixels */
793 {
794 int c, m, y, k; /* CMYK values */
795 int cr, cg, cb; /* Calibrated RGB values */
796
797
798 if (cupsImageHaveProfile)
799 {
800 while (count > 0)
801 {
802 c = 255 - *in++;
803 m = 255 - *in++;
804 y = 255 - *in++;
805 k = min(c, min(m, y));
806 c -= k;
807 m -= k;
808 y -= k;
809
810 cr = cupsImageMatrix[0][0][c] +
811 cupsImageMatrix[0][1][m] +
812 cupsImageMatrix[0][2][y] + k;
813 cg = cupsImageMatrix[1][0][c] +
814 cupsImageMatrix[1][1][m] +
815 cupsImageMatrix[1][2][y] + k;
816 cb = cupsImageMatrix[2][0][c] +
817 cupsImageMatrix[2][1][m] +
818 cupsImageMatrix[2][2][y] + k;
819
820 if (cr < 0)
821 *out++ = 255;
822 else if (cr > 255)
823 *out++ = 255 - cupsImageDensity[255];
824 else
825 *out++ = 255 - cupsImageDensity[cr];
826
827 if (cg < 0)
828 *out++ = 255;
829 else if (cg > 255)
830 *out++ = 255 - cupsImageDensity[255];
831 else
832 *out++ = 255 - cupsImageDensity[cg];
833
834 if (cb < 0)
835 *out++ = 255;
836 else if (cb > 255)
837 *out++ = 255 - cupsImageDensity[255];
838 else
839 *out++ = 255 - cupsImageDensity[cb];
840
841 count --;
842 }
843 }
844 else
845 {
846 if (in != out)
847 memcpy(out, in, count * 3);
848
849 if (cupsImageColorSpace == CUPS_CSPACE_CIELab ||
850 cupsImageColorSpace >= CUPS_CSPACE_ICC1)
851 {
852 while (count > 0)
853 {
854 rgb_to_lab(out);
855
856 out += 3;
857 count --;
858 }
859 }
860 else if (cupsImageColorSpace == CUPS_CSPACE_CIEXYZ)
861 {
862 while (count > 0)
863 {
864 rgb_to_xyz(out);
865
866 out += 3;
867 count --;
868 }
869 }
870 }
871 }
872
873
874 /*
875 * 'cupsImageRGBToWhite()' - Convert RGB colors to luminance.
876 */
877
878 void
cupsImageRGBToWhite(const cups_ib_t * in,cups_ib_t * out,int count)879 cupsImageRGBToWhite(
880 const cups_ib_t *in, /* I - Input pixels */
881 cups_ib_t *out, /* I - Output pixels */
882 int count) /* I - Number of pixels */
883 {
884 if (cupsImageHaveProfile)
885 {
886 while (count > 0)
887 {
888 *out++ = 255 - cupsImageDensity[255 - (31 * in[0] + 61 * in[1] + 8 * in[2]) / 100];
889 in += 3;
890 count --;
891 }
892 }
893 else
894 {
895 while (count > 0)
896 {
897 *out++ = (31 * in[0] + 61 * in[1] + 8 * in[2]) / 100;
898 in += 3;
899 count --;
900 }
901 }
902 }
903
904
905 /*
906 * 'cupsImageSetProfile()' - Set the device color profile.
907 */
908
909 void
cupsImageSetProfile(float d,float g,float matrix[3][3])910 cupsImageSetProfile(float d, /* I - Ink/marker density */
911 float g, /* I - Ink/marker gamma */
912 float matrix[3][3]) /* I - Color transform matrix */
913 {
914 int i, j, k; /* Looping vars */
915 float m; /* Current matrix value */
916 int *im; /* Pointer into cupsImageMatrix */
917
918
919 /*
920 * Allocate memory for the profile data...
921 */
922
923 if (cupsImageMatrix == NULL)
924 cupsImageMatrix = calloc(3, sizeof(cups_clut_t));
925
926 if (cupsImageMatrix == NULL)
927 return;
928
929 if (cupsImageDensity == NULL)
930 cupsImageDensity = calloc(256, sizeof(int));
931
932 if (cupsImageDensity == NULL)
933 return;
934
935 /*
936 * Populate the profile lookup tables...
937 */
938
939 cupsImageHaveProfile = 1;
940
941 for (i = 0, im = cupsImageMatrix[0][0]; i < 3; i ++)
942 for (j = 0; j < 3; j ++)
943 for (k = 0, m = matrix[i][j]; k < 256; k ++)
944 *im++ = (int)(k * m + 0.5);
945
946 for (k = 0, im = cupsImageDensity; k < 256; k ++)
947 *im++ = 255.0 * d * pow((float)k / 255.0, g) + 0.5;
948 }
949
950
951 /*
952 * 'cupsImageSetRasterColorSpace()' - Set the destination colorspace.
953 */
954
955 void
cupsImageSetRasterColorSpace(cups_cspace_t cs)956 cupsImageSetRasterColorSpace(
957 cups_cspace_t cs) /* I - Destination colorspace */
958 {
959 /*
960 * Set the destination colorspace...
961 */
962
963 cupsImageColorSpace = cs;
964
965 /*
966 * Don't use color profiles in colorimetric colorspaces...
967 */
968
969 if (cs == CUPS_CSPACE_CIEXYZ ||
970 cs == CUPS_CSPACE_CIELab ||
971 cs >= CUPS_CSPACE_ICC1)
972 cupsImageHaveProfile = 0;
973 }
974
975
976 /*
977 * 'cupsImageWhiteToBlack()' - Convert luminance colors to black.
978 */
979
980 void
cupsImageWhiteToBlack(const cups_ib_t * in,cups_ib_t * out,int count)981 cupsImageWhiteToBlack(
982 const cups_ib_t *in, /* I - Input pixels */
983 cups_ib_t *out, /* I - Output pixels */
984 int count) /* I - Number of pixels */
985 {
986 if (cupsImageHaveProfile)
987 while (count > 0)
988 {
989 *out++ = cupsImageDensity[255 - *in++];
990 count --;
991 }
992 else
993 while (count > 0)
994 {
995 *out++ = 255 - *in++;
996 count --;
997 }
998 }
999
1000
1001 /*
1002 * 'cupsImageWhiteToCMY()' - Convert luminance colors to CMY.
1003 */
1004
1005 void
cupsImageWhiteToCMY(const cups_ib_t * in,cups_ib_t * out,int count)1006 cupsImageWhiteToCMY(
1007 const cups_ib_t *in, /* I - Input pixels */
1008 cups_ib_t *out, /* I - Output pixels */
1009 int count) /* I - Number of pixels */
1010 {
1011 if (cupsImageHaveProfile)
1012 while (count > 0)
1013 {
1014 out[0] = cupsImageDensity[255 - *in++];
1015 out[1] = out[0];
1016 out[2] = out[0];
1017 out += 3;
1018 count --;
1019 }
1020 else
1021 while (count > 0)
1022 {
1023 *out++ = 255 - *in;
1024 *out++ = 255 - *in;
1025 *out++ = 255 - *in++;
1026 count --;
1027 }
1028 }
1029
1030
1031 /*
1032 * 'cupsImageWhiteToCMYK()' - Convert luminance colors to CMYK.
1033 */
1034
1035 void
cupsImageWhiteToCMYK(const cups_ib_t * in,cups_ib_t * out,int count)1036 cupsImageWhiteToCMYK(
1037 const cups_ib_t *in, /* I - Input pixels */
1038 cups_ib_t *out, /* I - Output pixels */
1039 int count) /* I - Number of pixels */
1040 {
1041 if (cupsImageHaveProfile)
1042 while (count > 0)
1043 {
1044 *out++ = 0;
1045 *out++ = 0;
1046 *out++ = 0;
1047 *out++ = cupsImageDensity[255 - *in++];
1048 count --;
1049 }
1050 else
1051 while (count > 0)
1052 {
1053 *out++ = 0;
1054 *out++ = 0;
1055 *out++ = 0;
1056 *out++ = 255 - *in++;
1057 count --;
1058 }
1059 }
1060
1061
1062 /*
1063 * 'cupsImageWhiteToRGB()' - Convert luminance data to RGB.
1064 */
1065
1066 void
cupsImageWhiteToRGB(const cups_ib_t * in,cups_ib_t * out,int count)1067 cupsImageWhiteToRGB(
1068 const cups_ib_t *in, /* I - Input pixels */
1069 cups_ib_t *out, /* I - Output pixels */
1070 int count) /* I - Number of pixels */
1071 {
1072 if (cupsImageHaveProfile)
1073 {
1074 while (count > 0)
1075 {
1076 out[0] = 255 - cupsImageDensity[255 - *in++];
1077 out[1] = out[0];
1078 out[2] = out[0];
1079 out += 3;
1080 count --;
1081 }
1082 }
1083 else
1084 {
1085 while (count > 0)
1086 {
1087 *out++ = *in;
1088 *out++ = *in;
1089 *out++ = *in++;
1090
1091 if (cupsImageColorSpace == CUPS_CSPACE_CIELab ||
1092 cupsImageColorSpace >= CUPS_CSPACE_ICC1)
1093 rgb_to_lab(out - 3);
1094 else if (cupsImageColorSpace == CUPS_CSPACE_CIEXYZ)
1095 rgb_to_xyz(out - 3);
1096
1097 count --;
1098 }
1099 }
1100 }
1101
1102
1103 /*
1104 * 'cupsImageWhiteToWhite()' - Convert luminance colors to device-dependent
1105 * luminance.
1106 */
1107
1108 void
cupsImageWhiteToWhite(const cups_ib_t * in,cups_ib_t * out,int count)1109 cupsImageWhiteToWhite(
1110 const cups_ib_t *in, /* I - Input pixels */
1111 cups_ib_t *out, /* I - Output pixels */
1112 int count) /* I - Number of pixels */
1113 {
1114 if (cupsImageHaveProfile)
1115 while (count > 0)
1116 {
1117 *out++ = 255 - cupsImageDensity[255 - *in++];
1118 count --;
1119 }
1120 else if (in != out)
1121 memcpy(out, in, count);
1122 }
1123
1124
1125 /*
1126 * 'cielab()' - Map CIE Lab transformation...
1127 */
1128
1129 static float /* O - Adjusted color value */
cielab(float x,float xn)1130 cielab(float x, /* I - Raw color value */
1131 float xn) /* I - Whitepoint color value */
1132 {
1133 float x_xn; /* Fraction of whitepoint */
1134
1135
1136 x_xn = x / xn;
1137
1138 if (x_xn > 0.008856)
1139 return (cbrt(x_xn));
1140 else
1141 return (7.787 * x_xn + 16.0 / 116.0);
1142 }
1143
1144
1145 /*
1146 * 'huerotate()' - Rotate the hue, maintaining luminance.
1147 */
1148
1149 static void
huerotate(float mat[3][3],float rot)1150 huerotate(float mat[3][3], /* I - Matrix to append to */
1151 float rot) /* I - Hue rotation in degrees */
1152 {
1153 float hmat[3][3]; /* Hue matrix */
1154 float lx, ly, lz; /* Luminance vector */
1155 float xrs, xrc; /* X rotation sine/cosine */
1156 float yrs, yrc; /* Y rotation sine/cosine */
1157 float zrs, zrc; /* Z rotation sine/cosine */
1158 float zsx, zsy; /* Z shear x/y */
1159
1160
1161 /*
1162 * Load the identity matrix...
1163 */
1164
1165 ident(hmat);
1166
1167 /*
1168 * Rotate the grey vector into positive Z...
1169 */
1170
1171 xrs = M_SQRT1_2;
1172 xrc = M_SQRT1_2;
1173 xrotate(hmat,xrs,xrc);
1174
1175 yrs = -1.0 / sqrt(3.0);
1176 yrc = -M_SQRT2 * yrs;
1177 yrotate(hmat,yrs,yrc);
1178
1179 /*
1180 * Shear the space to make the luminance plane horizontal...
1181 */
1182
1183 xform(hmat, 0.3086, 0.6094, 0.0820, &lx, &ly, &lz);
1184 zsx = lx / lz;
1185 zsy = ly / lz;
1186 zshear(hmat, zsx, zsy);
1187
1188 /*
1189 * Rotate the hue...
1190 */
1191
1192 zrs = sin(rot * M_PI / 180.0);
1193 zrc = cos(rot * M_PI / 180.0);
1194
1195 zrotate(hmat, zrs, zrc);
1196
1197 /*
1198 * Unshear the space to put the luminance plane back...
1199 */
1200
1201 zshear(hmat, -zsx, -zsy);
1202
1203 /*
1204 * Rotate the grey vector back into place...
1205 */
1206
1207 yrotate(hmat, -yrs, yrc);
1208 xrotate(hmat, -xrs, xrc);
1209
1210 /*
1211 * Append it to the current matrix...
1212 */
1213
1214 mult(hmat, mat, mat);
1215 }
1216
1217
1218 /*
1219 * 'ident()' - Make an identity matrix.
1220 */
1221
1222 static void
ident(float mat[3][3])1223 ident(float mat[3][3]) /* I - Matrix to identify */
1224 {
1225 mat[0][0] = 1.0;
1226 mat[0][1] = 0.0;
1227 mat[0][2] = 0.0;
1228 mat[1][0] = 0.0;
1229 mat[1][1] = 1.0;
1230 mat[1][2] = 0.0;
1231 mat[2][0] = 0.0;
1232 mat[2][1] = 0.0;
1233 mat[2][2] = 1.0;
1234 }
1235
1236
1237 /*
1238 * 'mult()' - Multiply two matrices.
1239 */
1240
1241 static void
mult(float a[3][3],float b[3][3],float c[3][3])1242 mult(float a[3][3], /* I - First matrix */
1243 float b[3][3], /* I - Second matrix */
1244 float c[3][3]) /* I - Destination matrix */
1245 {
1246 int x, y; /* Looping vars */
1247 float temp[3][3]; /* Temporary matrix */
1248
1249
1250 /*
1251 * Multiply a and b, putting the result in temp...
1252 */
1253
1254 for (y = 0; y < 3; y ++)
1255 for (x = 0; x < 3; x ++)
1256 temp[y][x] = b[y][0] * a[0][x] +
1257 b[y][1] * a[1][x] +
1258 b[y][2] * a[2][x];
1259
1260 /*
1261 * Copy temp to c (that way c can be a pointer to a or b).
1262 */
1263
1264 memcpy(c, temp, sizeof(temp));
1265 }
1266
1267
1268 /*
1269 * 'rgb_to_lab()' - Convert an RGB color to CIE Lab.
1270 */
1271
1272 static void
rgb_to_lab(cups_ib_t * val)1273 rgb_to_lab(cups_ib_t *val) /* IO - Color value */
1274 {
1275 float r, /* Red value */
1276 g, /* Green value */
1277 b, /* Blue value */
1278 ciex, /* CIE X value */
1279 ciey, /* CIE Y value */
1280 ciez, /* CIE Z value */
1281 ciey_yn, /* Normalized luminance */
1282 ciel, /* CIE L value */
1283 ciea, /* CIE a value */
1284 cieb; /* CIE b value */
1285
1286
1287 /*
1288 * Convert sRGB to linear RGB...
1289 */
1290
1291 r = pow((val[0] / 255.0 + 0.055) / 1.055, 2.4);
1292 g = pow((val[1] / 255.0 + 0.055) / 1.055, 2.4);
1293 b = pow((val[2] / 255.0 + 0.055) / 1.055, 2.4);
1294
1295 /*
1296 * Convert to CIE XYZ...
1297 */
1298
1299 ciex = 0.412453 * r + 0.357580 * g + 0.180423 * b;
1300 ciey = 0.212671 * r + 0.715160 * g + 0.072169 * b;
1301 ciez = 0.019334 * r + 0.119193 * g + 0.950227 * b;
1302
1303 /*
1304 * Normalize and convert to CIE Lab...
1305 */
1306
1307 ciey_yn = ciey / D65_Y;
1308
1309 if (ciey_yn > 0.008856)
1310 ciel = 116 * cbrt(ciey_yn) - 16;
1311 else
1312 ciel = 903.3 * ciey_yn;
1313
1314 ciel = ciel;
1315 ciea = 500 * (cielab(ciex, D65_X) - cielab(ciey, D65_Y));
1316 cieb = 200 * (cielab(ciey, D65_Y) - cielab(ciez, D65_Z));
1317
1318 /*
1319 * Scale the L value and bias the a and b values by 128 so that all
1320 * numbers are from 0 to 255.
1321 */
1322
1323 ciel = ciel * 2.55 + 0.5;
1324 ciea += 128.5;
1325 cieb += 128.5;
1326
1327 /*
1328 * Output 8-bit values...
1329 */
1330
1331 if (ciel < 0.0)
1332 val[0] = 0;
1333 else if (ciel < 255.0)
1334 val[0] = (int)ciel;
1335 else
1336 val[0] = 255;
1337
1338 if (ciea < 0.0)
1339 val[1] = 0;
1340 else if (ciea < 255.0)
1341 val[1] = (int)ciea;
1342 else
1343 val[1] = 255;
1344
1345 if (cieb < 0.0)
1346 val[2] = 0;
1347 else if (cieb < 255.0)
1348 val[2] = (int)cieb;
1349 else
1350 val[2] = 255;
1351 }
1352
1353
1354 /*
1355 * 'rgb_to_xyz()' - Convert an RGB color to CIE XYZ.
1356 */
1357
1358 static void
rgb_to_xyz(cups_ib_t * val)1359 rgb_to_xyz(cups_ib_t *val) /* IO - Color value */
1360 {
1361 float r, /* Red value */
1362 g, /* Green value */
1363 b, /* Blue value */
1364 ciex, /* CIE X value */
1365 ciey, /* CIE Y value */
1366 ciez; /* CIE Z value */
1367
1368
1369 /*
1370 * Convert sRGB to linear RGB...
1371 */
1372
1373 r = pow((val[0] / 255.0 + 0.055) / 1.055, 2.4);
1374 g = pow((val[1] / 255.0 + 0.055) / 1.055, 2.4);
1375 b = pow((val[2] / 255.0 + 0.055) / 1.055, 2.4);
1376
1377 /*
1378 * Convert to CIE XYZ...
1379 */
1380
1381 ciex = 0.412453 * r + 0.357580 * g + 0.180423 * b;
1382 ciey = 0.212671 * r + 0.715160 * g + 0.072169 * b;
1383 ciez = 0.019334 * r + 0.119193 * g + 0.950227 * b;
1384
1385 /*
1386 * Encode as 8-bit XYZ...
1387 */
1388
1389 if (ciex < 0.0f)
1390 val[0] = 0;
1391 else if (ciex < 1.1f)
1392 val[0] = (int)(231.8181f * ciex + 0.5);
1393 else
1394 val[0] = 255;
1395
1396 if (ciey < 0.0f)
1397 val[1] = 0;
1398 else if (ciey < 1.1f)
1399 val[1] = (int)(231.8181f * ciey + 0.5);
1400 else
1401 val[1] = 255;
1402
1403 if (ciez < 0.0f)
1404 val[2] = 0;
1405 else if (ciez < 1.1f)
1406 val[2] = (int)(231.8181f * ciez + 0.5);
1407 else
1408 val[2] = 255;
1409 }
1410
1411
1412 /*
1413 * 'saturate()' - Make a saturation matrix.
1414 */
1415
1416 static void
saturate(float mat[3][3],float sat)1417 saturate(float mat[3][3], /* I - Matrix to append to */
1418 float sat) /* I - Desired color saturation */
1419 {
1420 float smat[3][3]; /* Saturation matrix */
1421
1422
1423 smat[0][0] = (1.0 - sat) * 0.3086 + sat;
1424 smat[0][1] = (1.0 - sat) * 0.3086;
1425 smat[0][2] = (1.0 - sat) * 0.3086;
1426 smat[1][0] = (1.0 - sat) * 0.6094;
1427 smat[1][1] = (1.0 - sat) * 0.6094 + sat;
1428 smat[1][2] = (1.0 - sat) * 0.6094;
1429 smat[2][0] = (1.0 - sat) * 0.0820;
1430 smat[2][1] = (1.0 - sat) * 0.0820;
1431 smat[2][2] = (1.0 - sat) * 0.0820 + sat;
1432
1433 mult(smat, mat, mat);
1434 }
1435
1436
1437 /*
1438 * 'xform()' - Transform a 3D point using a matrix...
1439 */
1440
1441 static void
xform(float mat[3][3],float x,float y,float z,float * tx,float * ty,float * tz)1442 xform(float mat[3][3], /* I - Matrix */
1443 float x, /* I - Input X coordinate */
1444 float y, /* I - Input Y coordinate */
1445 float z, /* I - Input Z coordinate */
1446 float *tx, /* O - Output X coordinate */
1447 float *ty, /* O - Output Y coordinate */
1448 float *tz) /* O - Output Z coordinate */
1449 {
1450 *tx = x * mat[0][0] + y * mat[1][0] + z * mat[2][0];
1451 *ty = x * mat[0][1] + y * mat[1][1] + z * mat[2][1];
1452 *tz = x * mat[0][2] + y * mat[1][2] + z * mat[2][2];
1453 }
1454
1455
1456 /*
1457 * 'xrotate()' - Rotate about the x (red) axis...
1458 */
1459
1460 static void
xrotate(float mat[3][3],float rs,float rc)1461 xrotate(float mat[3][3], /* I - Matrix */
1462 float rs, /* I - Rotation angle sine */
1463 float rc) /* I - Rotation angle cosine */
1464 {
1465 float rmat[3][3]; /* I - Rotation matrix */
1466
1467
1468 rmat[0][0] = 1.0;
1469 rmat[0][1] = 0.0;
1470 rmat[0][2] = 0.0;
1471
1472 rmat[1][0] = 0.0;
1473 rmat[1][1] = rc;
1474 rmat[1][2] = rs;
1475
1476 rmat[2][0] = 0.0;
1477 rmat[2][1] = -rs;
1478 rmat[2][2] = rc;
1479
1480 mult(rmat, mat, mat);
1481 }
1482
1483
1484 /*
1485 * 'yrotate()' - Rotate about the y (green) axis...
1486 */
1487
1488 static void
yrotate(float mat[3][3],float rs,float rc)1489 yrotate(float mat[3][3], /* I - Matrix */
1490 float rs, /* I - Rotation angle sine */
1491 float rc) /* I - Rotation angle cosine */
1492 {
1493 float rmat[3][3]; /* I - Rotation matrix */
1494
1495
1496 rmat[0][0] = rc;
1497 rmat[0][1] = 0.0;
1498 rmat[0][2] = -rs;
1499
1500 rmat[1][0] = 0.0;
1501 rmat[1][1] = 1.0;
1502 rmat[1][2] = 0.0;
1503
1504 rmat[2][0] = rs;
1505 rmat[2][1] = 0.0;
1506 rmat[2][2] = rc;
1507
1508 mult(rmat,mat,mat);
1509 }
1510
1511
1512 /*
1513 * 'zrotate()' - Rotate about the z (blue) axis...
1514 */
1515
1516 static void
zrotate(float mat[3][3],float rs,float rc)1517 zrotate(float mat[3][3], /* I - Matrix */
1518 float rs, /* I - Rotation angle sine */
1519 float rc) /* I - Rotation angle cosine */
1520 {
1521 float rmat[3][3]; /* I - Rotation matrix */
1522
1523
1524 rmat[0][0] = rc;
1525 rmat[0][1] = rs;
1526 rmat[0][2] = 0.0;
1527
1528 rmat[1][0] = -rs;
1529 rmat[1][1] = rc;
1530 rmat[1][2] = 0.0;
1531
1532 rmat[2][0] = 0.0;
1533 rmat[2][1] = 0.0;
1534 rmat[2][2] = 1.0;
1535
1536 mult(rmat,mat,mat);
1537 }
1538
1539
1540 /*
1541 * 'zshear()' - Shear z using x and y...
1542 */
1543
1544 static void
zshear(float mat[3][3],float dx,float dy)1545 zshear(float mat[3][3], /* I - Matrix */
1546 float dx, /* I - X shear */
1547 float dy) /* I - Y shear */
1548 {
1549 float smat[3][3]; /* Shear matrix */
1550
1551
1552 smat[0][0] = 1.0;
1553 smat[0][1] = 0.0;
1554 smat[0][2] = dx;
1555
1556 smat[1][0] = 0.0;
1557 smat[1][1] = 1.0;
1558 smat[1][2] = dy;
1559
1560 smat[2][0] = 0.0;
1561 smat[2][1] = 0.0;
1562 smat[2][2] = 1.0;
1563
1564 mult(smat, mat, mat);
1565 }
1566
1567
1568 /*
1569 * End of "$Id: image-colorspace.c 7720 2008-07-11 22:46:21Z mike $".
1570 */
1571