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