1 /*
2 % Copyright (C) 2003 - 2019 GraphicsMagick Group
3 % Copyright (C) 2003 ImageMagick Studio
4 % Copyright 1991-1999 E. I. du Pont de Nemours and Company
5 %
6 % This program is covered by multiple licenses, which are described in
7 % Copyright.txt. You should have received a copy of Copyright.txt with this
8 % package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
9 %
10 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11 %                                                                             %
12 %                                                                             %
13 %                  Methods to transform the image colorspace                  %
14 %                                                                             %
15 %                                                                             %
16 %                              Software Design                                %
17 %                                John Cristy                                  %
18 %                                 July 1992                                   %
19 %                              Bob Friesenhahn                                %
20 %                                 March 2003                                  %
21 %                                                                             %
22 %                                                                             %
23 %                                                                             %
24 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
25 %
26 %
27 */
28 
29 /*
30   Include declarations.
31 */
32 #include "magick/studio.h"
33 #include "magick/attribute.h"
34 #include "magick/color.h"
35 #include "magick/colorspace.h"
36 #include "magick/enum_strings.h"
37 #include "magick/gem.h"
38 #include "magick/log.h"
39 #include "magick/monitor.h"
40 #include "magick/pixel_iterator.h"
41 #include "magick/utility.h"
42 
43 /*
44 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
45 %                                                                             %
46 %                                                                             %
47 %                                                                             %
48 +     R G B T r a n s f o r m I m a g e                                       %
49 %                                                                             %
50 %                                                                             %
51 %                                                                             %
52 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
53 %
54 %  Method RGBTransformImage converts the reference image from RGB to
55 %  an alternate colorspace.  The transformation matrices are not the standard
56 %  ones: the weights are rescaled to normalize the range of the transformed
57 %  values to be [0..MaxRGB].
58 %
59 %  The format of the RGBTransformImage method is:
60 %
61 %      unsigned int RGBTransformImage(Image *image,
62 %        const ColorspaceType colorspace)
63 %
64 %  A description of each parameter follows:
65 %
66 %    o image: the image
67 %
68 %    o colorspace: the colorspace to transform the image to.
69 %
70 %
71 */
72 
73 static MagickPassFail
RGBToCMYKTransform(void * mutable_data,const void * immutable_data,Image * restrict image,PixelPacket * restrict pixels,IndexPacket * restrict indexes,const long npixels,ExceptionInfo * exception)74 RGBToCMYKTransform(void *mutable_data,          /* User provided mutable data */
75                    const void *immutable_data,  /* User provided immutable data */
76                    Image * restrict image,                /* Modify image */
77                    PixelPacket * restrict pixels,         /* Pixel row */
78                    IndexPacket * restrict indexes,        /* Pixel row indexes */
79                    const long npixels,          /* Number of pixels in row */
80                    ExceptionInfo *exception)    /* Exception report */
81 {
82   /*
83     Transform RGB to CMYK(A) pixels.
84   */
85   register long
86     i;
87 
88   Quantum
89     black,
90     cyan,
91     magenta,
92     yellow;
93 
94   ARG_NOT_USED(mutable_data);
95   ARG_NOT_USED(immutable_data);
96   ARG_NOT_USED(image);
97   ARG_NOT_USED(exception);
98 
99   for (i=0; i < npixels; i++)
100     {
101       cyan=(Quantum) (MaxRGB-pixels[i].red);
102       magenta=(Quantum) (MaxRGB-pixels[i].green);
103       yellow=(Quantum) (MaxRGB-pixels[i].blue);
104       black=(cyan < magenta ? Min(cyan,yellow) : Min(magenta,yellow));
105       pixels[i].red=cyan;
106       pixels[i].green=magenta;
107       pixels[i].blue=yellow;
108       indexes[i]=pixels[i].opacity;
109       pixels[i].opacity=black;
110     }
111 
112   return MagickPass;
113 }
114 
115 static MagickPassFail
RGBToCineonLogTransform(void * mutable_data,const void * immutable_data,Image * restrict image,PixelPacket * restrict pixels,IndexPacket * restrict indexes,const long npixels,ExceptionInfo * exception)116 RGBToCineonLogTransform(void *mutable_data,          /* User provided mutable data */
117                         const void *immutable_data,  /* User provided immutable data */
118                         Image * restrict image,                /* Modify image */
119                         PixelPacket * restrict pixels,         /* Pixel row */
120                         IndexPacket * restrict indexes,        /* Pixel row indexes */
121                         const long npixels,          /* Number of pixels in row */
122                         ExceptionInfo *exception)    /* Exception report */
123 {
124   /*
125     Transform RGB pixels to CineonLog based on an existing lookup
126     table.
127   */
128   const unsigned int
129     *logmap = (const unsigned int *) immutable_data;
130 
131   register long
132     i;
133 
134   ARG_NOT_USED(mutable_data);
135   ARG_NOT_USED(image);
136   ARG_NOT_USED(indexes);
137   ARG_NOT_USED(exception);
138 
139   for (i=0; i < npixels; i++)
140     {
141       pixels[i].red   = logmap[ScaleQuantumToMap(pixels[i].red)];
142       pixels[i].green = logmap[ScaleQuantumToMap(pixels[i].green)];
143       pixels[i].blue  = logmap[ScaleQuantumToMap(pixels[i].blue)];
144     }
145 
146   return MagickPass;
147 }
148 
149 static MagickPassFail
RGBToHSLTransform(void * mutable_data,const void * immutable_data,Image * restrict image,PixelPacket * restrict pixels,IndexPacket * restrict indexes,const long npixels,ExceptionInfo * exception)150 RGBToHSLTransform(void *mutable_data,          /* User provided mutable data */
151                   const void *immutable_data,  /* User provided immutable data */
152                   Image * restrict image,                /* Modify image */
153                   PixelPacket * restrict pixels,         /* Pixel row */
154                   IndexPacket * restrict indexes,        /* Pixel row indexes */
155                   const long npixels,          /* Number of pixels in row */
156                   ExceptionInfo *exception)    /* Exception report */
157 {
158   /*
159     Transform pixels from RGB space to HSL space.
160   */
161   double
162     h,
163     s,
164     l;
165 
166   register long
167     i;
168 
169   ARG_NOT_USED(mutable_data);
170   ARG_NOT_USED(immutable_data);
171   ARG_NOT_USED(image);
172   ARG_NOT_USED(indexes);
173   ARG_NOT_USED(exception);
174 
175   for (i=0; i < npixels; i++)
176     {
177       TransformHSL(pixels[i].red,pixels[i].green,pixels[i].blue,&h,&s,&l);
178       h *= MaxRGB;
179       s *= MaxRGB;
180       l *= MaxRGB;
181       pixels[i].red=RoundDoubleToQuantum(h);
182       pixels[i].green=RoundDoubleToQuantum(s);
183       pixels[i].blue=RoundDoubleToQuantum(l);
184     }
185 
186   return MagickPass;
187 }
188 
189 static MagickPassFail
RGBToHWBTransform(void * mutable_data,const void * immutable_data,Image * restrict image,PixelPacket * restrict pixels,IndexPacket * restrict indexes,const long npixels,ExceptionInfo * exception)190 RGBToHWBTransform(void *mutable_data,          /* User provided mutable data */
191                   const void *immutable_data,  /* User provided immutable data */
192                   Image * restrict image,                /* Modify image */
193                   PixelPacket * restrict pixels,         /* Pixel row */
194                   IndexPacket * restrict indexes,        /* Pixel row indexes */
195                   const long npixels,          /* Number of pixels in row */
196                   ExceptionInfo *exception)    /* Exception report */
197 {
198   /*
199     Transform pixels from RGB space to HWB space.
200   */
201   double
202     h,
203     w,
204     b;
205 
206   register long
207     i;
208 
209   ARG_NOT_USED(mutable_data);
210   ARG_NOT_USED(immutable_data);
211   ARG_NOT_USED(image);
212   ARG_NOT_USED(indexes);
213   ARG_NOT_USED(exception);
214 
215   for (i=0; i < npixels; i++)
216     {
217       TransformHWB(pixels[i].red,pixels[i].green,pixels[i].blue,&h,&w,&b);
218       h *= MaxRGB;
219       w *= MaxRGB;
220       b *= MaxRGB;
221       pixels[i].red=RoundDoubleToQuantum(h);
222       pixels[i].green=RoundDoubleToQuantum(w);
223       pixels[i].blue=RoundDoubleToQuantum(b);
224     }
225 
226   return MagickPass;
227 }
228 
229 typedef struct _XYZColorTransformPacket
230 {
231   float
232     x,
233     y,
234     z;
235 } XYZColorTransformPacket;
236 
237 typedef struct _XYZColorTransformInfo_t
238 {
239   XYZColorTransformPacket *x;
240   XYZColorTransformPacket *y;
241   XYZColorTransformPacket *z;
242   XYZColorTransformPacket primary_info;
243 } XYZColorTransformInfo_t;
244 
245 static const size_t
246   XYZMapAllocSize=(MaxMap+1)*sizeof(XYZColorTransformPacket);
247 
248 
249 static MagickPassFail
XYZTransformPackets(void * mutable_data,const void * immutable_data,Image * restrict image,PixelPacket * restrict pixels,IndexPacket * restrict indexes,const long npixels,ExceptionInfo * exception)250 XYZTransformPackets(void *mutable_data,          /* User provided mutable data */
251                     const void *immutable_data,  /* User provided immutable data */
252                     Image * restrict image,                /* Modify image */
253                     PixelPacket * restrict pixels,         /* Pixel row */
254                     IndexPacket * restrict indexes,        /* Pixel row indexes */
255                     const long npixels,          /* Number of pixels in row */
256                     ExceptionInfo *exception)    /* Exception report */
257 {
258   /*
259     3D transform pixels from RGB to alternate colorspace.
260   */
261   float
262     b,
263     g,
264     r;
265 
266   const XYZColorTransformInfo_t
267     *xform = (const XYZColorTransformInfo_t *) immutable_data;
268 
269   register long
270     i;
271 
272   ARG_NOT_USED(mutable_data);
273   ARG_NOT_USED(image);
274   ARG_NOT_USED(indexes);
275   ARG_NOT_USED(exception);
276 
277   for (i=0; i < npixels; i++)
278     {
279       register unsigned int
280         x_index,
281         y_index,
282         z_index;
283 
284       x_index = ScaleQuantumToMap(pixels[i].red);
285       y_index = ScaleQuantumToMap(pixels[i].green);
286       z_index = ScaleQuantumToMap(pixels[i].blue);
287 
288       r = (xform->x[x_index].x + xform->y[y_index].x + xform->z[z_index].x + xform->primary_info.x);
289       g = (xform->x[x_index].y + xform->y[y_index].y + xform->z[z_index].y + xform->primary_info.y);
290       b = (xform->x[x_index].z + xform->y[y_index].z + xform->z[z_index].z + xform->primary_info.z);
291 
292       r = r < 0.0f ? 0.0f : r > MaxMapFloat ? MaxMapFloat : (r + 0.5f);
293       g = g < 0.0f ? 0.0f : g > MaxMapFloat ? MaxMapFloat : (g + 0.5f);
294       b = b < 0.0f ? 0.0f : b > MaxMapFloat ? MaxMapFloat : (b + 0.5f);
295 
296       pixels[i].red   = ScaleMapToQuantum((Quantum) r);
297       pixels[i].green = ScaleMapToQuantum((Quantum) g);
298       pixels[i].blue  = ScaleMapToQuantum((Quantum) b);
299     }
300 
301   return MagickPass;
302 }
303 
RGBTransformImage(Image * image,const ColorspaceType colorspace)304 MagickExport MagickPassFail RGBTransformImage(Image *image,
305                                               const ColorspaceType colorspace)
306 {
307   char
308     progress_message[MaxTextExtent];
309 
310   register long
311     i;
312 
313   MagickPassFail
314     status=MagickPass;
315 
316   assert(image != (Image *) NULL);
317   assert(image->signature == MagickSignature);
318 
319   /* Detect bogus request to convert to RGB */
320   assert(colorspace != RGBColorspace);
321   assert(colorspace != TransparentColorspace);
322   assert(colorspace != UndefinedColorspace);
323 
324   /*
325     Ensure that image is an RGB-compatible colorspace prior to
326     transforming to an alternate colorspace.
327   */
328   if (!IsRGBColorspace(image->colorspace))
329     (void) TransformRGBImage(image,image->colorspace);
330 
331   /*
332     Log colorspace transform event
333   */
334   (void) LogMagickEvent(TransformEvent,GetMagickModule(),
335                         "Transform colorspace from %s to %s",
336                         ColorspaceTypeToString(image->colorspace),
337                         ColorspaceTypeToString(colorspace));
338   FormatString(progress_message,"[%%s] Transform colorspace from %s to %s...",
339                ColorspaceTypeToString(image->colorspace),
340                ColorspaceTypeToString(colorspace));
341 
342   /*
343     Store colorspace in image.
344   */
345   image->colorspace=colorspace;
346 
347   if (colorspace == CMYKColorspace)
348     {
349       /*
350         Transform RGB to CMYK(A) pixels.
351       */
352       if (image->storage_class == PseudoClass)
353         {
354 
355           status=SyncImage(image);
356           image->storage_class=DirectClass;
357         }
358       if (status != MagickFail)
359         status=PixelIterateMonoModify(RGBToCMYKTransform,
360                                       NULL,
361                                       progress_message,
362                                       NULL,NULL,
363                                       0,0,image->columns,image->rows,
364                                       image,
365                                       &image->exception);
366       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
367                             "Colorspace transform completed");
368       return(status);
369     }
370 
371   if (colorspace == CineonLogRGBColorspace)
372     {
373       /*
374         Transform from linear RGB to Cineon Log RGB.
375       */
376       double
377         DisplayGamma,
378         Gain,
379         MaxLinearValue,
380         NegativeFilmGamma,
381         Offset,
382         ReferenceBlack,
383         ReferenceWhite;
384 
385       unsigned int
386         *logmap,
387         scale_to_short;
388 
389       /*
390         Establish defaults.
391       */
392       MaxLinearValue=MaxRGB; /* Maximum linear value output */
393       ReferenceWhite=685;    /* 90% white card (default 685) */
394       ReferenceBlack=95;     /* 1% black card  (default 95) */
395       DisplayGamma=1.7;      /* Typical display gamma (Kodak recommends 1.7) */
396       NegativeFilmGamma=0.6; /* Typical gamma for a film negative */
397 
398       /*
399         Allow image attributes to override defaults.
400       */
401       MagickAttributeToDouble(image,"reference-white",ReferenceWhite);
402       MagickAttributeToDouble(image,"reference-black",ReferenceBlack);
403       MagickAttributeToDouble(image,"display-gamma",DisplayGamma);
404       MagickAttributeToDouble(image,"film-gamma",NegativeFilmGamma);
405 
406       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
407                             "Log Transform: ReferenceWhite=%g ReferenceBlack=%g DisplayGamma=%g FilmGamma=%g",
408                             ReferenceWhite,ReferenceBlack,DisplayGamma,NegativeFilmGamma);
409 
410       /*
411         FIXME: Math seems to be producing data with gamma 1.0 rather than 1.7.
412       */
413 
414 #if 1
415       Gain=MaxLinearValue/(1.0 - pow(pow(10,((ReferenceBlack-ReferenceWhite)
416                                              *0.002/NegativeFilmGamma)),
417                                      (DisplayGamma/1.7)));
418 #else
419       Gain=MaxLinearValue/(1.0 - pow(pow(10,((ReferenceBlack-ReferenceWhite)
420                                              *0.002/NegativeFilmGamma)),
421                                      (1.0/DisplayGamma)));
422 #endif
423       Offset=Gain-MaxLinearValue;
424 
425       /*
426         Build LUT
427       */
428       logmap=MagickAllocateMemory(unsigned int *,(MaxMap+1)*sizeof(unsigned int));
429       if (logmap == 0)
430         ThrowBinaryException3(ResourceLimitError,MemoryAllocationFailed,
431                               UnableToTransformColorspace);
432       scale_to_short=(65535U / (65535U >> (16-10)));
433       for (i=0; i <= (long) MaxMap; i++)
434         {
435           double
436             linearval,
437             logval;
438 
439           linearval=i*(double) MaxRGB/MaxMap;
440 
441           /*
442             FIXME: Math seems to be expecting data with gamma 1.0 rather than 1.7.
443             Also, quantizing to 64K levels does not do justice to the Q32 build at all.
444           */
445           logval=685+log10(pow((((double) linearval+Offset)/Gain),
446                                (1.7/DisplayGamma)))/(0.002/NegativeFilmGamma);
447 
448           /*           logval=685+log10(pow((((double) linearval+Offset)/Gain), */
449           /*                                (DisplayGamma/1.0)))/(0.002/NegativeFilmGamma); */
450 
451           logval *= scale_to_short;
452           logmap[i]=ScaleShortToQuantum((unsigned int) (logval + 0.5));
453           /* printf("logmap[%u]=%u\n",i,(unsigned int) logmap[i]); */
454         }
455       /*
456         Transform pixels.
457       */
458       if (image->storage_class == PseudoClass)
459         {
460           /*
461             Convert PseudoClass image colormap.
462           */
463           (void) RGBToCineonLogTransform(0,
464                                          logmap,
465                                          image,
466                                          image->colormap,
467                                          (IndexPacket *) NULL,
468                                          image->colors,
469                                          &image->exception);
470           status=SyncImage(image);
471         }
472       else
473         {
474           /*
475             Convert DirectClass image.
476           */
477           status=PixelIterateMonoModify(RGBToCineonLogTransform,
478                                         NULL,
479                                         progress_message,
480                                         NULL,logmap,
481                                         0,0,image->columns,image->rows,
482                                         image,
483                                         &image->exception);
484         }
485 
486       MagickFreeMemory(logmap);
487       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
488                             "Transform to colorspace %s completed",
489                             ColorspaceTypeToString(colorspace));
490       return(status);
491     }
492 
493 
494   if (colorspace == HSLColorspace)
495     {
496       if (image->storage_class == PseudoClass)
497         {
498           /*
499             Convert PseudoClass image colormap.
500           */
501           RGBToHSLTransform(NULL,
502                             NULL,
503                             image,
504                             image->colormap,
505                             (IndexPacket *) NULL,
506                             image->colors,
507                             &image->exception);
508           status=SyncImage(image);
509         }
510       else
511         {
512           /*
513             Convert DirectClass image.
514           */
515           status=PixelIterateMonoModify(RGBToHSLTransform,
516                                         NULL,
517                                         progress_message,
518                                         NULL,NULL,
519                                         0,0,image->columns,image->rows,
520                                         image,
521                                         &image->exception);
522         }
523       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
524                             "Colorspace transform completed");
525       return(status);
526     }
527 
528   if (colorspace == HWBColorspace)
529     {
530       if (image->storage_class == PseudoClass)
531         {
532           /*
533             Convert PseudoClass image colormap.
534           */
535           RGBToHWBTransform(NULL,
536                             NULL,
537                             image,
538                             image->colormap,
539                             (IndexPacket *) NULL,
540                             image->colors,
541                             &image->exception);
542           status=SyncImage(image);
543         }
544       else
545         {
546           status=PixelIterateMonoModify(RGBToHWBTransform,
547                                         NULL,
548                                         progress_message,
549                                         NULL,NULL,
550                                         0,0,image->columns,image->rows,
551                                         image,
552                                         &image->exception);
553         }
554       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
555                             "Colorspace transform completed");
556       return(status);
557     }
558 
559   {
560     /*
561       3D Transform.
562     */
563 
564     XYZColorTransformInfo_t
565       xform;
566 
567     /*
568       Allocate the tables.
569     */
570     xform.x=MagickAllocateMemory(XYZColorTransformPacket *,XYZMapAllocSize);
571     xform.y=MagickAllocateMemory(XYZColorTransformPacket *,XYZMapAllocSize);
572     xform.z=MagickAllocateMemory(XYZColorTransformPacket *,XYZMapAllocSize);
573     if ((xform.x == 0) || (xform.y == 0) || (xform.z == 0))
574       {
575         MagickFreeMemory(xform.x);
576         MagickFreeMemory(xform.y);
577         MagickFreeMemory(xform.z);
578         ThrowBinaryException3(ResourceLimitError,MemoryAllocationFailed,
579                               UnableToTransformColorspace);
580       }
581     xform.primary_info.x=xform.primary_info.y=xform.primary_info.z=0;
582     switch (colorspace)
583       {
584       case GRAYColorspace:
585       case Rec601LumaColorspace:
586         {
587           /*
588             Initialize Rec. 601 Luma tables:
589 
590             G = 0.29900*R+0.58700*G+0.11400*B
591           */
592 #if MaxMap > 255
593 #  if defined(HAVE_OPENMP)
594 #    pragma omp parallel for schedule(static,64)
595 #  endif
596 #endif
597           for (i=0; i <= (long) MaxMap; i++)
598             {
599               xform.x[i].x=xform.x[i].y=xform.x[i].z=(0.299f*(float) i);
600               xform.y[i].x=xform.y[i].y=xform.y[i].z=(0.587f*(float) i);
601               xform.z[i].x=xform.z[i].y=xform.z[i].z=(0.114f*(float) i);
602             }
603           break;
604         }
605       case Rec709LumaColorspace:
606         {
607           /*
608             Initialize Rec. 709 Luma tables:
609 
610             G = 0.2126*R+0.7152*G+0.0722*B
611           */
612 #if MaxMap > 255
613 #  if defined(HAVE_OPENMP)
614 #    pragma omp parallel for schedule(static,64)
615 #  endif
616 #endif
617           for (i=0; i <= (long) MaxMap; i++)
618             {
619               xform.x[i].x=xform.x[i].y=xform.x[i].z=(0.2126f*(float) i);
620               xform.y[i].x=xform.y[i].y=xform.y[i].z=(0.7152f*(float) i);
621               xform.z[i].x=xform.z[i].y=xform.z[i].z=(0.0722f*(float) i);
622             }
623           break;
624         }
625       case OHTAColorspace:
626         {
627           /*
628             Initialize OHTA tables:
629 
630             I1 = 0.33333*R+0.33334*G+0.33333*B
631             I2 = 0.50000*R+0.00000*G-0.50000*B
632             I3 =-0.25000*R+0.50000*G-0.25000*B
633 
634             I and Q, normally -0.5 through 0.5, are normalized to the range 0
635             through MaxRGB.
636           */
637           xform.primary_info.y=((MaxMap+1)/2);
638           xform.primary_info.z=((MaxMap+1)/2);
639 #if MaxMap > 255
640 #  if defined(HAVE_OPENMP)
641 #    pragma omp parallel for schedule(static,64)
642 #  endif
643 #endif
644           for (i=0; i <= (long) MaxMap; i++)
645             {
646               xform.x[i].x=(0.33333f*(float) i);
647               xform.x[i].y=(0.5f*(float) i);
648               xform.x[i].z=((-0.25f)*(float) i);
649               xform.y[i].x=(0.33334f*(float) i);
650               xform.y[i].y=(0.0f);
651               xform.y[i].z=(0.5f*(float) i);
652               xform.z[i].x=(0.33333f*(float) i);
653               xform.z[i].y=((-0.5f)*(float) i);
654               xform.z[i].z=((-0.25f)*(float) i);
655             }
656           break;
657         }
658       case sRGBColorspace:
659         {
660           /*
661             Kodak PhotoYCC Color Space
662 
663             Initialize sRGB tables:
664 
665             Y =  0.29900*R+0.58700*G+0.11400*B
666             C1= -0.29900*R-0.58700*G+0.88600*B
667             C2=  0.70100*R-0.58700*G-0.11400*B
668 
669             sRGB is scaled by 1.3584.  C1 zero is 156 and C2 is at 137.
670           */
671           /* FIXME! The scaling factors for this transform look bizarre,
672              and in fact, the results are not correct. */
673           xform.primary_info.y=(ScaleCharToMap(156));
674           xform.primary_info.z=(ScaleCharToMap(137));
675           for (i=0; i <= (long) (0.018*MaxMap); i++)
676             {
677               xform.x[i].x=(0.003962014134275617*i);
678               xform.x[i].y=((-0.002426619775463276)*i);
679               xform.x[i].z=(0.006927257754597858*i);
680               xform.y[i].x=(0.007778268551236748*i);
681               xform.y[i].y=((-0.004763965913702149)*i);
682               xform.y[i].z=((-0.005800713697502058)*i);
683               xform.z[i].x=(0.001510600706713781*i);
684               xform.z[i].y=(0.007190585689165425*i);
685               xform.z[i].z=((-0.0011265440570958)*i);
686             }
687           for ( ; i <= (long) MaxMap; i++)
688             {
689               xform.x[i].x=(0.2201118963486454*(1.099*i-0.099));
690               xform.x[i].y=((-0.1348122097479598)*(1.099*i-0.099));
691               xform.x[i].z=(0.3848476530332144*(1.099*i-0.099));
692               xform.y[i].x=(0.4321260306242638*(1.099*i-0.099));
693               xform.y[i].y=((-0.2646647729834528)*(1.099*i-0.099));
694               xform.y[i].z=((-0.3222618720834477)*(1.099*i-0.099));
695               xform.z[i].x=(0.08392226148409894*(1.099*i-0.099));
696               xform.z[i].y=(0.3994769827314126*(1.099*i-0.099));
697               xform.z[i].z=((-0.06258578094976668)*(1.099*i-0.099));
698             }
699           break;
700         }
701       case XYZColorspace:
702         {
703           /*
704             Initialize CIE XYZ tables (from ITU-R 709 RGB):
705 
706             X = 0.412453*X+0.357580*Y+0.180423*Z
707             Y = 0.212671*X+0.715160*Y+0.072169*Z
708             Z = 0.019334*X+0.119193*Y+0.950227*Z
709           */
710 #if MaxMap > 255
711 #  if defined(HAVE_OPENMP)
712 #    pragma omp parallel for schedule(static,64)
713 #  endif
714 #endif
715           for (i=0; i <= (long) MaxMap; i++)
716             {
717               xform.x[i].x=(0.412453f*(float) i);
718               xform.x[i].y=(0.212671f*(float) i);
719               xform.x[i].z=(0.019334f*(float) i);
720               xform.y[i].x=(0.35758f*(float) i);
721               xform.y[i].y=(0.71516f*(float) i);
722               xform.y[i].z=(0.119193f*(float) i);
723               xform.z[i].x=(0.180423f*(float) i);
724               xform.z[i].y=(0.072169f*(float) i);
725               xform.z[i].z=(0.950227f*(float) i);
726             }
727           break;
728         }
729       case Rec601YCbCrColorspace:
730         {
731           /*
732             Initialize YCbCr tables (using ITU-R BT.601 luma):
733 
734             Y =  0.299000*R+0.587000*G+0.114000*B
735             Cb= -0.168736*R-0.331264*G+0.500000*B
736             Cr=  0.500000*R-0.418688*G-0.081312*B
737 
738             Cb and Cr, normally -0.5 through 0.5, are normalized to the range 0
739             through MaxRGB.
740           */
741           xform.primary_info.y=((MaxMap+1)/2);
742           xform.primary_info.z=((MaxMap+1)/2);
743 #if MaxMap > 255
744 #  if defined(HAVE_OPENMP)
745 #    pragma omp parallel for schedule(static,64)
746 #  endif
747 #endif
748           for (i=0; i <= (long) MaxMap; i++)
749             {
750               xform.x[i].x=(0.299f*(float) i); /* Red */
751               xform.x[i].y=((-0.16873f)*(float) i); /* Green */
752               xform.x[i].z=(0.500000f*(float) i); /* Blue */
753               xform.y[i].x=(0.587f*(float) i); /* Red */
754               xform.y[i].y=((-0.331264f)*(float) i); /* Green */
755               xform.y[i].z=((-0.418688f)*(float) i); /* Blue */
756               xform.z[i].x=(0.114f*(float) i); /* Red */
757               xform.z[i].y=(0.500000f*(float) i); /* Green */
758               xform.z[i].z=((-0.081312f)*(float) i); /* Blue */
759             }
760           break;
761         }
762       case Rec709YCbCrColorspace:
763         {
764           /*
765             Initialize YCbCr tables (using ITU-R BT.709 luma):
766 
767             Y =  0.212600*R+0.715200*G+0.072200*B
768             Cb= -0.114572*R-0.385428*G+0.500000*B
769             Cr=  0.500000*R-0.454153*G-0.045847*B
770 
771             Cb and Cr, normally -0.5 through 0.5, are normalized to the range 0
772             through MaxRGB.
773           */
774           xform.primary_info.y=((MaxMap+1)/2);
775           xform.primary_info.z=((MaxMap+1)/2);
776 #if MaxMap > 255
777 #  if defined(HAVE_OPENMP)
778 #    pragma omp parallel for schedule(static,64)
779 #  endif
780 #endif
781           for (i=0; i <= (long) MaxMap; i++)
782             {
783               xform.x[i].x=(0.212600f*(float) i); /* Red */
784               xform.x[i].y=((-0.114572f)*(float) i); /* Green */
785               xform.x[i].z=(0.500000f*(float) i); /* Blue */
786               xform.y[i].x=(0.715200f*(float) i); /* Red */
787               xform.y[i].y=((-0.385428f)*(float) i); /* Green */
788               xform.y[i].z=((-0.454153f)*(float) i); /* Blue */
789               xform.z[i].x=(0.072200f*(float) i); /* Red */
790               xform.z[i].y=(0.500000f*(float) i); /* Green */
791               xform.z[i].z=((-0.045847f)*(float) i); /* Blue */
792             }
793           break;
794         }
795       case YCCColorspace:
796         {
797           /*
798             Kodak PhotoYCC Color Space.
799 
800             Initialize YCC tables:
801 
802             Y =  0.29900*R+0.58700*G+0.11400*B
803             C1= -0.29900*R-0.58700*G+0.88600*B
804             C2=  0.70100*R-0.58700*G-0.11400*B
805 
806             YCC is scaled by 1.3584.  C1 zero is 156 and C2 is at 137.
807           */
808           /* FIXME! The scaling factors for this transform look bizarre,
809              and in fact, the results are not correct. */
810           xform.primary_info.y=(ScaleCharToMap(156));
811           xform.primary_info.z=(ScaleCharToMap(137));
812           for (i=0; i <= (long) (0.018*MaxMap); i++)
813             {
814               xform.x[i].x=(0.003962014134275617*i);
815               xform.x[i].y=((-0.002426619775463276)*i);
816               xform.x[i].z=(0.006927257754597858*i);
817               xform.y[i].x=(0.007778268551236748*i);
818               xform.y[i].y=((-0.004763965913702149)*i);
819               xform.y[i].z=((-0.005800713697502058)*i);
820               xform.z[i].x=(0.001510600706713781*i);
821               xform.z[i].y=(0.007190585689165425*i);
822               xform.z[i].z=((-0.0011265440570958)*i);
823             }
824           for ( ; i <= (long) MaxMap; i++)
825             {
826               xform.x[i].x=(0.2201118963486454*(1.099*i-0.099));
827               xform.x[i].y=((-0.1348122097479598)*(1.099*i-0.099));
828               xform.x[i].z=(0.3848476530332144*(1.099*i-0.099));
829               xform.y[i].x=(0.4321260306242638*(1.099*i-0.099));
830               xform.y[i].y=((-0.2646647729834528)*(1.099*i-0.099));
831               xform.y[i].z=((-0.3222618720834477)*(1.099*i-0.099));
832               xform.z[i].x=(0.08392226148409894*(1.099*i-0.099));
833               xform.z[i].y=(0.3994769827314126*(1.099*i-0.099));
834               xform.z[i].z=((-0.06258578094976668)*(1.099*i-0.099));
835             }
836           break;
837         }
838       case YIQColorspace:
839         {
840           /*
841             Initialize YIQ tables:
842 
843             Y = 0.29900*R+0.58700*G+0.11400*B
844             I = 0.59600*R-0.27400*G-0.32200*B
845             Q = 0.21100*R-0.52300*G+0.31200*B
846 
847             I and Q, normally -0.5 through 0.5, are normalized to the range 0
848             through MaxRGB.
849           */
850           xform.primary_info.y=((MaxMap+1)/2);
851           xform.primary_info.z=((MaxMap+1)/2);
852 #if MaxMap > 255
853 #  if defined(HAVE_OPENMP)
854 #    pragma omp parallel for schedule(static,64)
855 #  endif
856 #endif
857           for (i=0; i <= (long) MaxMap; i++)
858             {
859               xform.x[i].x=(0.299f*(float) i);
860               xform.x[i].y=(0.596f*(float) i);
861               xform.x[i].z=(0.211f*(float) i);
862               xform.y[i].x=(0.587f*(float) i);
863               xform.y[i].y=((-0.274f)*(float) i);
864               xform.y[i].z=((-0.523f)*(float) i);
865               xform.z[i].x=(0.114f*(float) i);
866               xform.z[i].y=((-0.322f)*(float) i);
867               xform.z[i].z=(0.312f*(float) i);
868             }
869           break;
870         }
871       case YPbPrColorspace:
872         {
873           /*
874             Initialize YPbPr tables (according to ITU-R BT.601):
875 
876             Y =  0.299000*R+0.587000*G+0.114000*B
877             Pb= -0.168736*R-0.331264*G+0.500000*B
878             Pr=  0.500000*R-0.418688*G-0.081312*B
879 
880             Pb and Pr, normally -0.5 through 0.5, are normalized to the range 0
881             through MaxRGB.
882           */
883           xform.primary_info.y=((MaxMap+1)/2);
884           xform.primary_info.z=((MaxMap+1)/2);
885 #if MaxMap > 255
886 #  if defined(HAVE_OPENMP)
887 #    pragma omp parallel for schedule(static,64)
888 #  endif
889 #endif
890           for (i=0; i <= (long) MaxMap; i++)
891             {
892               xform.x[i].x=(0.299f*(float) i);
893               xform.x[i].y=((-0.168736f)*(float) i);
894               xform.x[i].z=(0.5f*(float) i);
895               xform.y[i].x=(0.587f*(float) i);
896               xform.y[i].y=((-0.331264f)*(float) i);
897               xform.y[i].z=((-0.418688f)*(float) i);
898               xform.z[i].x=(0.114f*(float) i);
899               xform.z[i].y=(0.5f*(float) i);
900               xform.z[i].z=((-0.081312f)*(float) i);
901             }
902           break;
903         }
904       case YUVColorspace:
905       default:
906         {
907           /*
908             Initialize YUV tables:
909 
910             Y =  0.29900*R+0.58700*G+0.11400*B
911             U = -0.14740*R-0.28950*G+0.43690*B
912             V =  0.61500*R-0.51500*G-0.10000*B
913 
914             U and V, normally -0.5 through 0.5, are normalized to the range 0
915             through MaxRGB.  Note that U = 0.493*(B-Y), V = 0.877*(R-Y).
916           */
917           xform.primary_info.y=((MaxMap+1)/2);
918           xform.primary_info.z=((MaxMap+1)/2);
919 #if MaxMap > 255
920 #  if defined(HAVE_OPENMP)
921 #    pragma omp parallel for schedule(static,64)
922 #  endif
923 #endif
924           for (i=0; i <= (long) MaxMap; i++)
925             {
926               xform.x[i].x=(0.299f*(float) i);
927               xform.x[i].y=((-0.1474f)*(float) i);
928               xform.x[i].z=(0.615f*(float) i);
929               xform.y[i].x=(0.587f*(float) i);
930               xform.y[i].y=((-0.2895f)*(float) i);
931               xform.y[i].z=((-0.515f)*(float) i);
932               xform.z[i].x=(0.114f*(float) i);
933               xform.z[i].y=(0.4369f*(float) i);
934               xform.z[i].z=((-0.1f)*(float) i);
935             }
936           break;
937         }
938       }
939 
940 #if 0
941     /*
942       Dump tables
943     */
944     for (i=0; i <= (long) MaxMap; i++)
945       {
946         printf("%5ld: xform.x(%g,%g,%g) xform.y(%g,%g,%g) xform.z(%g,%g,%g)\n",
947                i,
948                ((xform.x[i].x)),
949                ((xform.x[i].y)),
950                ((xform.x[i].z)),
951 
952                ((xform.y[i].x)),
953                ((xform.y[i].y)),
954                ((xform.y[i].z)),
955 
956                ((xform.z[i].x)),
957                ((xform.z[i].y)),
958                ((xform.z[i].z)));
959       }
960 #endif
961 
962     /*
963       Convert from RGB.
964     */
965     if (image->storage_class == PseudoClass)
966       {
967         /*
968           Convert PseudoClass image colormap.
969         */
970         (void) XYZTransformPackets(NULL,
971                                    &xform,
972                                    image,
973                                    image->colormap,
974                                    (IndexPacket *) NULL,
975                                    image->colors,
976                                    &image->exception);
977         status=SyncImage(image);
978       }
979     else
980       {
981         /*
982           Convert DirectClass image.
983         */
984         status=PixelIterateMonoModify(XYZTransformPackets,
985                                       NULL,
986                                       progress_message,
987                                       NULL,&xform,
988                                       0,0,image->columns,image->rows,
989                                       image,
990                                       &image->exception);
991       }
992 
993     /*
994       Free allocated memory.
995     */
996     MagickFreeMemory(xform.x);
997     MagickFreeMemory(xform.y);
998     MagickFreeMemory(xform.z);
999   }
1000 
1001   image->is_grayscale=IsGrayColorspace(colorspace);
1002   (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1003                         "Transform to colorspace %s completed",
1004                         ColorspaceTypeToString(colorspace));
1005   return(status);
1006 }
1007 
1008 /*
1009 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1010 %                                                                             %
1011 %                                                                             %
1012 %                                                                             %
1013 +     T r a n s f o r m C o l o r s p a c e                                   %
1014 %                                                                             %
1015 %                                                                             %
1016 %                                                                             %
1017 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1018 %
1019 %  Method (void) TransformColorspace converts the image to a specified colorspace.
1020 %  If the image is already in the requested colorspace, no work is performed.
1021 %  Note that the current colorspace is stored in the image colorspace member.
1022 %  The transformation matrices are not necessarily the standard ones: the
1023 %  weights are rescaled to normalize the range of the transformed values to
1024 %  be [0..MaxRGB].
1025 %
1026 %  The format of the (void) TransformColorspace method is:
1027 %
1028 %      unsigned int (void) TransformColorspace(Image *image,
1029 %        const ColorspaceType colorspace)
1030 %
1031 %  A description of each parameter follows:
1032 %
1033 %    o image: the image to transform
1034 %
1035 %    o colorspace: the desired colorspace.
1036 %
1037 */
TransformColorspace(Image * image,const ColorspaceType colorspace)1038 MagickExport MagickPassFail TransformColorspace(Image *image,
1039   const ColorspaceType colorspace)
1040 {
1041   MagickPassFail
1042     status=MagickPass;
1043 
1044   assert(image != (Image *) NULL);
1045   assert(colorspace != UndefinedColorspace);
1046   assert(image->colorspace != UndefinedColorspace);
1047   /*
1048     If the image colorspace is the same as requested, do nothing.
1049   */
1050   if (image->colorspace == colorspace)
1051      return (status);
1052 
1053   /*
1054     If the requested colorspace is RGB or Transparent, then convert
1055     via TransformRGBImage.
1056   */
1057   if ((colorspace == RGBColorspace) ||
1058       (colorspace == TransparentColorspace))
1059       {
1060         status &= TransformRGBImage(image,image->colorspace);
1061         image->colorspace=colorspace;
1062         return  (status);
1063       }
1064 
1065   /*
1066     If the image is not already in an RGB-compatible colorspace, then
1067     convert it to RGB via TransformRGBImage, and then to the target
1068     colorspace via RGBTransformImage, otherwise just convert to the
1069     target colorspace via RGBTransformImage.
1070   */
1071   if (!IsRGBColorspace(image->colorspace))
1072     status=TransformRGBImage(image,image->colorspace);
1073 
1074   status &= RGBTransformImage(image,colorspace);
1075   return (status);
1076 }
1077 
1078 /*
1079 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1080 %                                                                             %
1081 %                                                                             %
1082 %                                                                             %
1083 +     T r a n s f o r m R G B I m a g e                                       %
1084 %                                                                             %
1085 %                                                                             %
1086 %                                                                             %
1087 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1088 %
1089 %  Method TransformRGBImage converts the reference image from an alternate
1090 %  colorspace to RGB.  The transformation matrices are not the standard ones:
1091 %  the weights are rescaled to normalize the range of the transformed values
1092 %  to be [0..MaxRGB].
1093 %
1094 %  The format of the TransformRGBImage method is:
1095 %
1096 %      unsigned int TransformRGBImage(Image *image,
1097 %        const ColorspaceType colorspace)
1098 %
1099 %  A description of each parameter follows:
1100 %
1101 %    o image: the image
1102 %
1103 %    o colorspace: the colorspace to transform the image to.
1104 %
1105 */
1106 static MagickPassFail
CMYKToRGBTransform(void * mutable_data,const void * immutable_data,Image * restrict image,PixelPacket * restrict pixels,IndexPacket * restrict indexes,const long npixels,ExceptionInfo * exception)1107 CMYKToRGBTransform(void *mutable_data,          /* User provided mutable data */
1108                    const void *immutable_data,  /* User provided immutable data */
1109                    Image * restrict image,                /* Modify image */
1110                    PixelPacket * restrict pixels,         /* Pixel row */
1111                    IndexPacket * restrict indexes,        /* Pixel row indexes */
1112                    const long npixels,          /* Number of pixels in row */
1113                    ExceptionInfo *exception)    /* Exception report */
1114 {
1115   /*
1116     Transform CMYK(A) pixels to RGB.
1117   */
1118   register long
1119     i;
1120 
1121   ARG_NOT_USED(mutable_data);
1122   ARG_NOT_USED(immutable_data);
1123   ARG_NOT_USED(exception);
1124 
1125   for (i=0; i < npixels; i++)
1126     {
1127       double
1128         black_factor;
1129 
1130       black_factor=MaxRGBDouble-GetBlackSample(&pixels[i]);
1131       SetRedSample(&pixels[i],
1132                    (Quantum) (((MaxRGBDouble-GetCyanSample(&pixels[i]))*
1133                                black_factor)/MaxRGBDouble+0.5));
1134       SetGreenSample(&pixels[i],
1135                      (Quantum) (((MaxRGBDouble-GetMagentaSample(&pixels[i]))*
1136                                  black_factor)/MaxRGBDouble+0.5));
1137       SetBlueSample(&pixels[i],
1138                     (Quantum) (((MaxRGBDouble-GetYellowSample(&pixels[i]))*
1139                                 black_factor)/ MaxRGBDouble+0.5));
1140       SetOpacitySample(&pixels[i],(image->matte ? indexes[i] : OpaqueOpacity));
1141     }
1142 
1143   return MagickPass;
1144 }
1145 
1146 static MagickPassFail
CineonLogToRGBTransform(void * mutable_data,const void * immutable_data,Image * restrict image,PixelPacket * restrict pixels,IndexPacket * restrict indexes,const long npixels,ExceptionInfo * exception)1147 CineonLogToRGBTransform(void *mutable_data,         /* User provided mutable data */
1148                         const void *immutable_data, /* User provided immutable data */
1149                         Image * restrict image,               /* Modify image */
1150                         PixelPacket * restrict pixels,        /* Pixel row */
1151                         IndexPacket * restrict indexes,       /* Pixel row indexes */
1152                         const long npixels,         /* Number of pixels in row */
1153                         ExceptionInfo *exception)   /* Exception report */
1154 {
1155   /*
1156     Transform CineonLog pixels to RGB based on an existing lookup
1157     table.
1158   */
1159   const Quantum
1160     *linearmap = (const Quantum *) immutable_data;
1161 
1162   register long
1163     i;
1164 
1165   ARG_NOT_USED(mutable_data);
1166   ARG_NOT_USED(image);
1167   ARG_NOT_USED(indexes);
1168   ARG_NOT_USED(exception);
1169 
1170   for (i=0; i < npixels; i++)
1171     {
1172       pixels[i].red   = linearmap[ScaleQuantumToShort(pixels[i].red)/64U];
1173       pixels[i].green = linearmap[ScaleQuantumToShort(pixels[i].green)/64U];
1174       pixels[i].blue  = linearmap[ScaleQuantumToShort(pixels[i].blue)/64U];
1175     }
1176 
1177   return MagickPass;
1178 }
1179 
1180 static MagickPassFail
HSLToRGBTransform(void * mutable_data,const void * immutable_data,Image * image,PixelPacket * restrict pixels,IndexPacket * restrict indexes,const long npixels,ExceptionInfo * exception)1181 HSLToRGBTransform(void *mutable_data,         /* User provided mutable data */
1182                   const void *immutable_data, /* User provided immutable data */
1183                   Image *image,               /* Modify image */
1184                   PixelPacket * restrict pixels,        /* Pixel row */
1185                   IndexPacket * restrict indexes,       /* Pixel row indexes */
1186                   const long npixels,         /* Number of pixels in row */
1187                   ExceptionInfo *exception)   /* Exception report */
1188 {
1189   /*
1190     Transform pixels from HSL space to RGB space.
1191   */
1192   register long
1193     i;
1194 
1195   ARG_NOT_USED(mutable_data);
1196   ARG_NOT_USED(immutable_data);
1197   ARG_NOT_USED(image);
1198   ARG_NOT_USED(indexes);
1199   ARG_NOT_USED(exception);
1200 
1201   for (i=0; i < npixels; i++)
1202     {
1203       HSLTransform((double) pixels[i].red/MaxRGB,(double) pixels[i].green/MaxRGB,
1204                    (double) pixels[i].blue/MaxRGB,&pixels[i].red,&pixels[i].green,&pixels[i].blue);
1205     }
1206 
1207   return MagickPass;
1208 }
1209 
1210 static MagickPassFail
HWBToRGBTransform(void * mutable_data,const void * immutable_data,Image * restrict image,PixelPacket * restrict pixels,IndexPacket * restrict indexes,const long npixels,ExceptionInfo * exception)1211 HWBToRGBTransform(void *mutable_data,         /* User provided mutable data */
1212                   const void *immutable_data, /* User provided immutable data */
1213                   Image * restrict image,               /* Modify image */
1214                   PixelPacket * restrict pixels,        /* Pixel row */
1215                   IndexPacket * restrict indexes,       /* Pixel row indexes */
1216                   const long npixels,         /* Number of pixels in row */
1217                   ExceptionInfo *exception)   /* Exception report */
1218 {
1219   /*
1220     Transform pixels from HWB space to RGB space.
1221   */
1222   register long
1223     i;
1224 
1225   ARG_NOT_USED(mutable_data);
1226   ARG_NOT_USED(immutable_data);
1227   ARG_NOT_USED(image);
1228   ARG_NOT_USED(indexes);
1229   ARG_NOT_USED(exception);
1230 
1231   for (i=0; i < npixels; i++)
1232     {
1233       HWBTransform((double) pixels[i].red/MaxRGB,(double) pixels[i].green/MaxRGB,
1234                    (double) pixels[i].blue/MaxRGB,&pixels[i].red,&pixels[i].green,&pixels[i].blue);
1235     }
1236 
1237   return MagickPass;
1238 }
1239 
1240 typedef struct _RGBColorTransformPacket
1241 {
1242   float
1243     r,
1244     g,
1245     b;
1246 } RGBColorTransformPacket;
1247 
1248 typedef struct _RGBTransformInfo_t
1249 {
1250   RGBColorTransformPacket *r;
1251   RGBColorTransformPacket *g;
1252   RGBColorTransformPacket *b;
1253   const unsigned char *rgb_map;
1254   unsigned int rgb_map_max_index;
1255 } RGBTransformInfo_t;
1256 
1257 static MagickPassFail
RGBTransformPackets(void * mutable_data,const void * immutable_data,Image * restrict image,PixelPacket * restrict pixels,IndexPacket * restrict indexes,const long npixels,ExceptionInfo * exception)1258 RGBTransformPackets(void *mutable_data,         /* User provided mutable data */
1259                     const void *immutable_data, /* User provided immutable data */
1260                     Image * restrict image,               /* Modify image */
1261                     PixelPacket * restrict pixels,        /* Pixel row */
1262                     IndexPacket * restrict indexes,       /* Pixel row indexes */
1263                     const long npixels,         /* Number of pixels in row */
1264                     ExceptionInfo *exception)   /* Exception report */
1265 {
1266   /*
1267     3D transform pixels to RGB.
1268   */
1269   float
1270     b,
1271     g,
1272     r;
1273 
1274   const RGBTransformInfo_t
1275     *xform = (const RGBTransformInfo_t *) immutable_data;
1276 
1277   register long
1278     i;
1279 
1280   unsigned int
1281     r_index,
1282     g_index,
1283     b_index;
1284 
1285   ARG_NOT_USED(mutable_data);
1286   ARG_NOT_USED(image);
1287   ARG_NOT_USED(indexes);
1288   ARG_NOT_USED(exception);
1289 
1290   for (i=0; i < npixels; i++)
1291     {
1292       r_index = ScaleQuantumToMap(pixels[i].red);
1293       g_index = ScaleQuantumToMap(pixels[i].green);
1294       b_index = ScaleQuantumToMap(pixels[i].blue);
1295 
1296       r = (xform->r[r_index].r + xform->g[g_index].r + xform->b[b_index].r);
1297       g = (xform->r[r_index].g + xform->g[g_index].g + xform->b[b_index].g);
1298       b = (xform->r[r_index].b + xform->g[g_index].b + xform->b[b_index].b);
1299 
1300       r = r < 0.0f ? 0.0f : r > MaxMapFloat ? MaxMapFloat : (r + 0.5f);
1301       g = g < 0.0f ? 0.0f : g > MaxMapFloat ? MaxMapFloat : (g + 0.5f);
1302       b = b < 0.0f ? 0.0f : b > MaxMapFloat ? MaxMapFloat : (b + 0.5f);
1303 
1304       if ( xform->rgb_map != 0 )
1305         {
1306           r_index = ScaleMapToChar(r);
1307           g_index = ScaleMapToChar(g);
1308           b_index = ScaleMapToChar(b);
1309 
1310           if (r_index > xform->rgb_map_max_index) r_index = xform->rgb_map_max_index;
1311           if (g_index > xform->rgb_map_max_index) g_index = xform->rgb_map_max_index;
1312           if (b_index > xform->rgb_map_max_index) b_index = xform->rgb_map_max_index;
1313 
1314           pixels[i].red   = ScaleCharToQuantum(xform->rgb_map[r_index]);
1315           pixels[i].green = ScaleCharToQuantum(xform->rgb_map[g_index]);
1316           pixels[i].blue  = ScaleCharToQuantum(xform->rgb_map[b_index]);
1317         }
1318       else
1319         {
1320           pixels[i].red   = ScaleMapToQuantum(r);
1321           pixels[i].green = ScaleMapToQuantum(g);
1322           pixels[i].blue  = ScaleMapToQuantum(b);
1323         }
1324     }
1325 
1326   return MagickPass;
1327 }
1328 
TransformRGBImage(Image * image,const ColorspaceType colorspace)1329 MagickExport MagickPassFail TransformRGBImage(Image *image,
1330                                               const ColorspaceType colorspace)
1331 {
1332   static const unsigned char
1333     sRGBMap[351] =
1334     {
1335       0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
1336       19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 29, 30, 31, 32, 33,
1337       34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
1338       50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 65, 66,
1339       67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82,
1340       83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 95, 96, 97, 98, 99,
1341       100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
1342       114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126,
1343       127, 128, 129, 130, 131, 132, 133, 135, 136, 137, 138, 139, 140,
1344       141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153,
1345       154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166,
1346       167, 168, 169, 170, 171, 172, 173, 174, 175, 175, 176, 177, 178,
1347       179, 180, 181, 182, 183, 184, 185, 186, 187, 187, 188, 189, 190,
1348       191, 192, 193, 194, 194, 195, 196, 197, 198, 199, 199, 200, 201,
1349       202, 203, 203, 204, 205, 206, 207, 207, 208, 209, 210, 210, 211,
1350       212, 213, 213, 214, 215, 215, 216, 217, 218, 218, 219, 220, 220,
1351       221, 222, 222, 223, 223, 224, 225, 225, 226, 227, 227, 228, 228,
1352       229, 229, 230, 230, 231, 232, 232, 233, 233, 234, 234, 235, 235,
1353       235, 236, 236, 237, 237, 238, 238, 238, 239, 239, 240, 240, 240,
1354       241, 241, 242, 242, 242, 243, 243, 243, 243, 244, 244, 244, 245,
1355       245, 245, 245, 246, 246, 246, 247, 247, 247, 247, 247, 248, 248,
1356       248, 248, 249, 249, 249, 249, 249, 249, 250, 250, 250, 250, 250,
1357       250, 251, 251, 251, 251, 251, 251, 252, 252, 252, 252, 252, 252,
1358       252, 252, 252, 253, 253, 253, 253, 253, 253, 253, 253, 253, 254,
1359       254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 255, 255,
1360       255, 255, 255, 255, 255
1361     };
1362 
1363   static const unsigned char
1364     YCCMap[351] =  /* Photo CD information beyond 100% white, Gamma 2.2 */
1365     {
1366       0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
1367       19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 32, 33, 34, 35,
1368       36, 37, 38, 39, 40, 41, 42, 43, 45, 46, 47, 48, 49, 50, 51, 52,
1369       53, 54, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68, 69, 70,
1370       71, 72, 73, 74, 76, 77, 78, 79, 80, 81, 82, 83, 84, 86, 87, 88,
1371       89, 90, 91, 92, 93, 94, 95, 97, 98, 99, 100, 101, 102, 103, 104,
1372       105, 106, 107, 108, 110, 111, 112, 113, 114, 115, 116, 117, 118,
1373       119, 120, 121, 122, 123, 124, 125, 126, 127, 129, 130, 131, 132,
1374       133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145,
1375       146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158,
1376       159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171,
1377       172, 173, 174, 175, 176, 176, 177, 178, 179, 180, 181, 182, 183,
1378       184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 193, 194, 195,
1379       196, 197, 198, 199, 200, 201, 201, 202, 203, 204, 205, 206, 207,
1380       207, 208, 209, 210, 211, 211, 212, 213, 214, 215, 215, 216, 217,
1381       218, 218, 219, 220, 221, 221, 222, 223, 224, 224, 225, 226, 226,
1382       227, 228, 228, 229, 230, 230, 231, 232, 232, 233, 234, 234, 235,
1383       236, 236, 237, 237, 238, 238, 239, 240, 240, 241, 241, 242, 242,
1384       243, 243, 244, 244, 245, 245, 245, 246, 246, 247, 247, 247, 248,
1385       248, 248, 249, 249, 249, 249, 250, 250, 250, 250, 251, 251, 251,
1386       251, 251, 252, 252, 252, 252, 252, 253, 253, 253, 253, 253, 253,
1387       253, 253, 253, 253, 253, 253, 253, 254, 254, 254, 254, 254, 254,
1388       254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254,
1389       255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
1390       255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
1391       255, 255, 255, 255, 255, 255
1392     };
1393 
1394   char
1395     progress_message[MaxTextExtent];
1396 
1397   register long
1398     i;
1399 
1400   unsigned int
1401     is_grayscale;
1402 
1403   MagickPassFail
1404     status=MagickPass;
1405 
1406   assert(image != (Image *) NULL);
1407   assert(image->signature == MagickSignature);
1408   assert(image->colorspace != UndefinedColorspace);
1409 
1410   is_grayscale=((image->is_grayscale) || IsGrayColorspace(image->colorspace));
1411 
1412   /*
1413     If colorspace is already an RGB type then simply return.
1414   */
1415   if (IsRGBColorspace(image->colorspace))
1416     return(status);
1417 
1418   (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1419                         "Transform colorspace from %s to RGB",
1420                         ColorspaceTypeToString(image->colorspace));
1421 
1422   FormatString(progress_message,"[%%s] Transform colorspace from %s to RGB...",
1423                ColorspaceTypeToString(image->colorspace));
1424 
1425   if (image->colorspace == CMYKColorspace)
1426     {
1427       if (image->storage_class == PseudoClass)
1428         {
1429           status=SyncImage(image);
1430           image->storage_class=DirectClass;
1431         }
1432       if (status != MagickFail)
1433         status=PixelIterateMonoModify(CMYKToRGBTransform,
1434                                       NULL,
1435                                       progress_message,
1436                                       NULL,NULL,
1437                                       0,0,image->columns,image->rows,
1438                                       image,
1439                                       &image->exception);
1440       image->colorspace=RGBColorspace;
1441       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1442                             "Colorspace transform completed");
1443       return(status);
1444     }
1445 
1446   if (colorspace == CineonLogRGBColorspace)
1447     {
1448       /*
1449         Transform from Cineon Log RGB to Linear RGB
1450       */
1451       double
1452         BreakPoint,
1453         DisplayGamma,
1454         Gain,
1455         KneeGain,
1456         KneeOffset,
1457         MaxLinearValue,
1458         NegativeFilmGamma,
1459         Offset,
1460         ReferenceBlack,
1461         ReferenceWhite,
1462         SoftClip;
1463 
1464       Quantum
1465         *linearmap;
1466 
1467       /*
1468         Establish defaults.
1469       */
1470       MaxLinearValue=MaxRGB; /* Maximum linear value output */
1471       ReferenceWhite=685.0;  /* 90% white card (default 685) */
1472       ReferenceBlack=95.0;   /* 1% black card  (default 95) */
1473       DisplayGamma=1.7;      /* Typical display gamma (Kodak recommended 1.7) */
1474       NegativeFilmGamma=0.6; /* Typical gamma for a film negative */
1475       SoftClip=0.0;          /* Soft clip offset */
1476 
1477       /*
1478         Allow image attributes to override defaults.
1479       */
1480       MagickAttributeToDouble(image,"reference-white",ReferenceWhite);
1481       MagickAttributeToDouble(image,"reference-black",ReferenceBlack);
1482       MagickAttributeToDouble(image,"display-gamma",DisplayGamma);
1483       MagickAttributeToDouble(image,"film-gamma",NegativeFilmGamma);
1484       MagickAttributeToDouble(image,"soft-clip-offset",SoftClip);
1485 
1486       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1487                             "Log Transform: ReferenceWhite=%g ReferenceBlack=%g DisplayGamma=%g FilmGamma=%g SoftClip=%g",
1488                             ReferenceWhite,ReferenceBlack,DisplayGamma,NegativeFilmGamma,SoftClip);
1489 
1490       BreakPoint=ReferenceWhite-SoftClip;
1491       Gain=MaxLinearValue/(1.0 - pow(pow(10,((ReferenceBlack-ReferenceWhite)
1492                                              *0.002/NegativeFilmGamma)),
1493                                      (DisplayGamma/1.7)));
1494       Offset=Gain-MaxLinearValue;
1495       KneeOffset=pow(pow(10,((BreakPoint-ReferenceWhite)*0.002/NegativeFilmGamma)),
1496                      (DisplayGamma/1.7))*Gain-Offset;
1497       KneeGain=(MaxLinearValue-KneeOffset)/pow((5*SoftClip),(SoftClip/100));
1498 
1499 
1500       linearmap=MagickAllocateMemory(Quantum *,1024*sizeof(Quantum));
1501       if (linearmap == 0)
1502         ThrowBinaryException3(ResourceLimitError,MemoryAllocationFailed,
1503                               UnableToTransformColorspace);
1504 
1505       for (i=0; i < 1023; i++)
1506         {
1507           double
1508             linearval,
1509             logval;
1510 
1511           logval=i;
1512           if (logval < ReferenceBlack)
1513             {
1514               /* Values below reference black are clipped to zero */
1515               linearval=0.0;
1516             }
1517           else if (logval > BreakPoint)
1518             {
1519               /* Values above the breakpoint are soft-clipped. */
1520               linearval=pow((logval-BreakPoint),(SoftClip/100))*KneeGain+KneeOffset;
1521             }
1522           else
1523             {
1524               /* Otherwise, normal values */
1525               linearval=pow(pow(10,((logval-ReferenceWhite)*0.002/NegativeFilmGamma)),
1526                             (DisplayGamma/1.7))*Gain-Offset;
1527             }
1528 
1529           linearmap[i]=(unsigned int) (linearval + 0.5);
1530         }
1531       /*
1532         Transform pixels.
1533       */
1534       if (image->storage_class == PseudoClass)
1535         {
1536           /*
1537             Convert PseudoClass image colormap.
1538           */
1539           CineonLogToRGBTransform(NULL,
1540                                   linearmap,
1541                                   image,
1542                                   image->colormap,
1543                                   (IndexPacket *) NULL,
1544                                   image->colors,
1545                                   &image->exception);
1546           status=SyncImage(image);
1547         }
1548       else
1549         {
1550           /*
1551             Convert DirectClass image.
1552           */
1553           status=PixelIterateMonoModify(CineonLogToRGBTransform,
1554                                         NULL,
1555                                         progress_message,
1556                                         NULL,linearmap,
1557                                         0,0,image->columns,image->rows,
1558                                         image,
1559                                         &image->exception);
1560         }
1561       MagickFreeMemory(linearmap);
1562       image->colorspace=RGBColorspace;
1563       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1564                             "Transform to colorspace %s completed",
1565                             ColorspaceTypeToString(colorspace));
1566       return(status);
1567     }
1568 
1569   if (image->colorspace == HSLColorspace)
1570     {
1571       if (image->storage_class == PseudoClass)
1572         {
1573           /*
1574             Convert PseudoClass image colormap.
1575           */
1576           HSLToRGBTransform(NULL,
1577                             NULL,
1578                             image,
1579                             image->colormap,
1580                             (IndexPacket *) NULL,
1581                             image->colors,
1582                             &image->exception);
1583           status=SyncImage(image);
1584         }
1585       else
1586         {
1587           status=PixelIterateMonoModify(HSLToRGBTransform,
1588                                         NULL,
1589                                         progress_message,
1590                                         NULL,NULL,
1591                                         0,0,image->columns,image->rows,
1592                                         image,
1593                                         &image->exception);
1594         }
1595       image->colorspace=RGBColorspace;
1596       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1597                             "Colorspace transform completed");
1598       return(status);
1599     }
1600 
1601   if (image->colorspace == HWBColorspace)
1602     {
1603       if (image->storage_class == PseudoClass)
1604         {
1605           /*
1606             Convert PseudoClass image colormap.
1607           */
1608           HWBToRGBTransform(NULL,
1609                             NULL,
1610                             image,
1611                             image->colormap,
1612                             (IndexPacket *) NULL,
1613                             image->colors,
1614                             &image->exception);
1615           status=SyncImage(image);
1616         }
1617       else
1618         {
1619           status=PixelIterateMonoModify(HWBToRGBTransform,
1620                                         NULL,
1621                                         progress_message,
1622                                         NULL,NULL,
1623                                         0,0,image->columns,image->rows,
1624                                         image,
1625                                         &image->exception);
1626         }
1627       image->colorspace=RGBColorspace;
1628       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1629                             "Colorspace transform completed");
1630       return(status);
1631     }
1632 
1633   {
1634     /*
1635       3D Transform.
1636     */
1637 
1638     RGBTransformInfo_t
1639       xform;
1640 
1641     /*
1642       Allocate the tables.
1643     */
1644     xform.rgb_map = (const unsigned char *) NULL;
1645     xform.rgb_map_max_index = 0;
1646     xform.r=MagickAllocateMemory(RGBColorTransformPacket *,
1647                                  (MaxMap+1)*sizeof(RGBColorTransformPacket));
1648     xform.g=MagickAllocateMemory(RGBColorTransformPacket *,
1649                                  (MaxMap+1)*sizeof(RGBColorTransformPacket));
1650     xform.b=MagickAllocateMemory(RGBColorTransformPacket *,
1651                                  (MaxMap+1)*sizeof(RGBColorTransformPacket));
1652     if ((xform.r == (RGBColorTransformPacket *) NULL) ||
1653         (xform.g == (RGBColorTransformPacket *) NULL) ||
1654         (xform.b == (RGBColorTransformPacket *) NULL))
1655       {
1656         MagickFreeMemory(xform.r);
1657         MagickFreeMemory(xform.g);
1658         MagickFreeMemory(xform.b);
1659         ThrowBinaryException3(ResourceLimitError,MemoryAllocationFailed,
1660                               UnableToTransformColorspace);
1661       }
1662 
1663     switch (image->colorspace)
1664       {
1665       case OHTAColorspace:
1666         {
1667           /*
1668             Initialize OHTA tables:
1669 
1670             R = I1+1.00000*I2-0.66668*I3
1671             G = I1+0.00000*I2+1.33333*I3
1672             B = I1-1.00000*I2-0.66668*I3
1673 
1674             I and Q, normally -0.5 through 0.5, must be normalized to the range 0
1675             through MaxMap.
1676           */
1677 #if MaxMap > 255
1678 #  if defined(HAVE_OPENMP)
1679 #    pragma omp parallel for schedule(static,64)
1680 #  endif
1681 #endif
1682           for (i=0; i <= (long) MaxMap; i++)
1683             {
1684               xform.b[i].b=((-0.33334f)*(2.0f*(float) i-MaxMapFloat));
1685               xform.b[i].g=(0.666665f*(2.0f*(float) i-MaxMapFloat));
1686               xform.b[i].r=((-0.33334f)*(2.0f*(float) i-MaxMapFloat));
1687               xform.g[i].b=((-0.5f)*(2.0f*(float) i-MaxMapFloat));
1688               xform.g[i].g=(0.0f);
1689               xform.g[i].r=(0.5f*(2.0f*(float) i-MaxMapFloat));
1690               xform.r[i].b=((float) i);
1691               xform.r[i].g=((float) i);
1692               xform.r[i].r=((float) i);
1693             }
1694           break;
1695         }
1696       case sRGBColorspace:
1697         {
1698           /*
1699             Initialize sRGB tables:
1700 
1701             R = Y            +1.032096*C2
1702             G = Y-0.326904*C1-0.704445*C2
1703             B = Y+1.685070*C1
1704 
1705             sRGB is scaled by 1.3584.  C1 zero is 156 and C2 is at 137.
1706           */
1707           xform.rgb_map=sRGBMap;
1708           xform.rgb_map_max_index=350;
1709 #if MaxMap > 255
1710 #  if defined(HAVE_OPENMP)
1711 #    pragma omp parallel for schedule(static,64)
1712 #  endif
1713 #endif
1714           for (i=0; i <= (long) MaxMap; i++)
1715             {
1716               xform.b[i].b=(0.0f);
1717               xform.b[i].g=((-0.95692f)*((float) i-ScaleCharToMap(137)));
1718               xform.b[i].r=(1.88000f*((float) i-ScaleCharToMap(137)));
1719               xform.g[i].b=(2.28900f*((float) i-ScaleCharToMap(156)));
1720               xform.g[i].g=((-0.444066f)*((float) i-ScaleCharToMap(156)));
1721               xform.g[i].r=(0.0f);
1722               xform.r[i].b=(1.40200f*(float) i);
1723               xform.r[i].g=(1.40200f*(float) i);
1724               xform.r[i].r=(1.40200f*(float) i);
1725             }
1726           break;
1727         }
1728       case XYZColorspace:
1729         {
1730           /*
1731             Initialize CIE XYZ tables (to ITU R-709 RGB):
1732 
1733             R =  3.240479*R-1.537150*G-0.498535*B
1734             G = -0.969256*R+1.875992*G+0.041556*B
1735             B =  0.055648*R-0.204043*G+1.057311*B
1736           */
1737 #if MaxMap > 255
1738 #  if defined(HAVE_OPENMP)
1739 #    pragma omp parallel for schedule(static,64)
1740 #  endif
1741 #endif
1742           for (i=0; i <= (long) MaxMap; i++)
1743             {
1744               xform.b[i].b=(1.057311f*(float) i);
1745               xform.b[i].g=(0.041556f*(float) i);
1746               xform.b[i].r=((-0.498535f)*(float) i);
1747               xform.g[i].b=((-0.204043f)*(float) i);
1748               xform.g[i].g=(1.875992f*(float) i);
1749               xform.g[i].r=((-1.537150f)*(float) i);
1750               xform.r[i].b=(0.055648f*(float) i);
1751               xform.r[i].g=((-0.969256f)*(float) i);
1752               xform.r[i].r=(3.240479f*(float) i);
1753             }
1754           break;
1755         }
1756       case Rec601YCbCrColorspace:
1757         {
1758           /*
1759             Y'CbCr based on ITU-R 601 Luma
1760 
1761             Initialize Y'CbCr tables:
1762 
1763             R' = Y'            +1.402000*Cr
1764             G' = Y'-0.344136*Cb-0.714136*Cr
1765             B' = Y'+1.772000*Cb
1766 
1767             Cb and Cr, normally -0.5 through 0.5, must be normalized to the range 0
1768             through MaxMap.
1769           */
1770 #if MaxMap > 255
1771 #  if defined(HAVE_OPENMP)
1772 #    pragma omp parallel for schedule(static,64)
1773 #  endif
1774 #endif
1775           for (i=0; i <= (long) MaxMap; i++)
1776             {
1777               xform.b[i].b=(0.0f); /* Pr */
1778               xform.b[i].g=((-0.714136f*0.5f)*(2.0f*(float) i-MaxMapFloat)); /* Pb */
1779               xform.b[i].r=((1.402000f*0.5f)*(2.0f*(float) i-MaxMapFloat)); /* Y */
1780               xform.g[i].b=((1.772000f*0.5f)*(2.0f*(float) i-MaxMapFloat)); /* Pr */
1781               xform.g[i].g=((-0.344136f*0.5f)*(2.0f*(float) i-MaxMapFloat)); /* Pb */
1782               xform.g[i].r=(0.0f); /* Y */
1783               xform.r[i].b=((float) i); /* Pr */
1784               xform.r[i].g=((float) i); /* Pb */
1785               xform.r[i].r=((float) i); /* Y */
1786             }
1787           break;
1788         }
1789       case Rec709YCbCrColorspace:
1790         {
1791           /*
1792             Y'CbCr based on ITU-R 709 Luma.
1793 
1794             R' = Y'            +1.574800*Cr
1795             G' = Y'-0.187324*Cb-0.468124*Cr
1796             B' = Y'+1.855600*Cb
1797 
1798             Cb and Cr, normally -0.5 through 0.5, must be normalized to the range 0
1799             through MaxMap.
1800           */
1801 #if MaxMap > 255
1802 #  if defined(HAVE_OPENMP)
1803 #    pragma omp parallel for schedule(static,64)
1804 #  endif
1805 #endif
1806           for (i=0; i <= (long) MaxMap; i++)
1807             {
1808               xform.b[i].b=(0.0f); /* Pr */
1809               xform.b[i].g=((-0.468124f*0.5f)*(2.0f*(float) i-MaxMapFloat)); /* Pb */
1810               xform.b[i].r=((1.5748f*0.5f)*(2.0f*(float) i-MaxMapFloat)); /* Y */
1811               xform.g[i].b=((1.8556f*0.5f)*(2.0f*(float) i- MaxMapFloat)); /* Pr */
1812               xform.g[i].g=((-0.187324f*0.5f)*(2.0f*(float) i-MaxMapFloat)); /* Pb */
1813               xform.g[i].r=(0.0f); /* Y */
1814               xform.r[i].b=((float) i); /* Pr */
1815               xform.r[i].g=((float) i); /* Pb */
1816               xform.r[i].r=((float) i); /* Y */
1817             }
1818           break;
1819         }
1820       case YCCColorspace:
1821         {
1822           /*
1823             Kodak PhotoYCC Color Space.
1824 
1825             Initialize YCC tables:
1826 
1827             R = Y            +1.340762*C2
1828             G = Y-0.317038*C1-0.682243*C2
1829             B = Y+1.632639*C1
1830 
1831             YCC is scaled by 1.3584.  C1 zero is 156 and C2 is at 137.
1832           */
1833           xform.rgb_map=YCCMap;
1834           xform.rgb_map_max_index=350;
1835 #if MaxMap > 255
1836 #  if defined(HAVE_OPENMP)
1837 #    pragma omp parallel for schedule(static,64)
1838 #  endif
1839 #endif
1840           for (i=0; i <= (long) MaxMap; i++)
1841             {
1842               xform.b[i].b=(0.0f);
1843               xform.b[i].g=((-0.9271435f)*((float) i-ScaleCharToMap(137)));
1844               xform.b[i].r=(1.8215f*((float) i-ScaleCharToMap(137)));
1845               xform.g[i].b=(2.2179f*((float) i-ScaleCharToMap(156)));
1846               xform.g[i].g=((-0.4302726f)*((float) i-ScaleCharToMap(156)));
1847               xform.g[i].r=(0.0f);
1848               xform.r[i].b=(1.3584f*i);
1849               xform.r[i].g=(1.3584f*(float) i);
1850               xform.r[i].r=(1.3584f*(float) i);
1851             }
1852           break;
1853         }
1854       case YIQColorspace:
1855         {
1856           /*
1857             Initialize YIQ tables:
1858 
1859             R = Y+0.95620*I+0.62140*Q
1860             G = Y-0.27270*I-0.64680*Q
1861             B = Y-1.10370*I+1.70060*Q
1862 
1863             I and Q, normally -0.5 through 0.5, must be normalized to the range 0
1864             through MaxMap.
1865           */
1866 #if MaxMap > 255
1867 #  if defined(HAVE_OPENMP)
1868 #    pragma omp parallel for schedule(static,64)
1869 #  endif
1870 #endif
1871           for (i=0; i <= (long) MaxMap; i++)
1872             {
1873               xform.b[i].b=(0.8503f*(2.0f*(float) i-MaxMapFloat));
1874               xform.b[i].g=((-0.3234f)*(2.0f*(float) i-MaxMapFloat));
1875               xform.b[i].r=(0.3107f*(2.0f*(float) i-MaxMapFloat));
1876               xform.g[i].b=((-0.55185f)*(2.0f*(float) i-MaxMapFloat));
1877               xform.g[i].g=((-0.13635f)*(2.0f*(float) i-MaxMapFloat));
1878               xform.g[i].r=(0.4781f*(2.0f*(float) i-MaxMapFloat));
1879               xform.r[i].b=((float) i);
1880               xform.r[i].g=((float) i);
1881               xform.r[i].r=((float) i);
1882             }
1883           break;
1884         }
1885       case YPbPrColorspace:
1886         {
1887           /*
1888             Initialize Y'PbPr tables using ITU-R 601 luma:
1889 
1890             R = Y            +1.402000*C2
1891             G = Y-0.344136*C1+0.714136*C2
1892             B = Y+1.772000*C1
1893 
1894             Pb and Pr, normally -0.5 through 0.5, must be normalized to the range 0
1895             through MaxMap.
1896           */
1897 #if MaxMap > 255
1898 #  if defined(HAVE_OPENMP)
1899 #    pragma omp parallel for schedule(static,64)
1900 #  endif
1901 #endif
1902           for (i=0; i <= (long) MaxMap; i++)
1903             {
1904               xform.b[i].b=(0.0f);
1905               xform.b[i].g=(0.357068f*(2.0f*(float) i-MaxMapFloat));
1906               xform.b[i].r=(0.701f*(2.0f*(float) i-MaxMapFloat));
1907               xform.g[i].b=(0.886f*(2.0f*(float) i-MaxMapFloat));
1908               xform.g[i].g=((-0.172068f)*(2.0f*(float) i-MaxMapFloat));
1909               xform.g[i].r=(0.0f);
1910               xform.r[i].b=((float) i);
1911               xform.r[i].g=((float) i);
1912               xform.r[i].r=((float) i);
1913             }
1914           break;
1915         }
1916       case YUVColorspace:
1917       default:
1918         {
1919           /*
1920             Initialize YUV tables:
1921 
1922             R = Y          +1.13980*V
1923             G = Y-0.39380*U-0.58050*V
1924             B = Y+2.02790*U
1925 
1926             U and V, normally -0.5 through 0.5, must be normalized to the range 0
1927             through MaxMap.
1928           */
1929 #if MaxMap > 255
1930 #  if defined(HAVE_OPENMP)
1931 #    pragma omp parallel for schedule(static,64)
1932 #  endif
1933 #endif
1934           for (i=0; i <= (long) MaxMap; i++)
1935             {
1936               xform.b[i].b=(0.0f);
1937               xform.b[i].g=((-0.29025f)*(2.0f*(float) i-MaxMapFloat));
1938               xform.b[i].r=(0.5699f*(2.0f*(float) i-MaxMapFloat));
1939               xform.g[i].b=(1.01395f*(2.0f*(float) i-MaxMapFloat));
1940               xform.g[i].g=((-0.1969f)*(2.0f*(float) i-MaxMapFloat));
1941               xform.g[i].r=(0.0f);
1942               xform.r[i].b=((float) i);
1943               xform.r[i].g=((float) i);
1944               xform.r[i].r=((float) i);
1945             }
1946           break;
1947         }
1948       }
1949 
1950 #if 0
1951     /*
1952       Dump tables
1953     */
1954     for (i=0; i <= (long) MaxMap; i++)
1955       {
1956         printf("%5ld: xform.r(%g,%g,%g) xform.g(%g,%g,%g) xform.b(%g,%g,%g)\n",
1957                i,
1958                ((xform.r[i].r)),
1959                ((xform.r[i].g)),
1960                ((xform.r[i].b)),
1961 
1962                ((xform.g[i].r)),
1963                ((xform.g[i].g)),
1964                ((xform.g[i].b)),
1965 
1966                ((xform.b[i].r)),
1967                ((xform.b[i].g)),
1968                ((xform.b[i].b)));
1969       }
1970 #endif
1971 
1972     /*
1973       Convert to RGB.
1974     */
1975     if (image->storage_class == PseudoClass)
1976       {
1977         /*
1978           Convert PseudoClass image colormap.
1979         */
1980         (void) RGBTransformPackets(NULL,
1981                                    &xform,
1982                                    image,
1983                                    image->colormap,
1984                                    (IndexPacket *) NULL,
1985                                    image->colors,
1986                                    &image->exception);
1987         status=SyncImage(image);
1988       }
1989     else
1990       {
1991         /*
1992           Convert DirectClass image.
1993         */
1994         status=PixelIterateMonoModify(RGBTransformPackets,
1995                                       NULL,
1996                                       progress_message,
1997                                       NULL,&xform,
1998                                       0,0,image->columns,image->rows,
1999                                       image,
2000                                       &image->exception);
2001       }
2002 
2003 
2004     /*
2005       Free allocated memory.
2006     */
2007     MagickFreeMemory(xform.b);
2008     MagickFreeMemory(xform.g);
2009     MagickFreeMemory(xform.r);
2010   }
2011   image->is_grayscale=is_grayscale;
2012   image->colorspace=RGBColorspace;
2013   (void) LogMagickEvent(TransformEvent,GetMagickModule(),
2014                         "Colorspace transform completed");
2015   return(status);
2016 }
2017