root / ImageMagick / trunk / magick / colorspace.c

Revision 12706, 63.5 kB (checked in by cristy, 2 days ago)
Line 
1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3%                                                                             %
4%                                                                             %
5%                                                                             %
6%     CCCC   OOO   L       OOO   RRRR   SSSSS  PPPP    AAA    CCCC  EEEEE     %
7%    C      O   O  L      O   O  R   R  SS     P   P  A   A  C      E         %
8%    C      O   O  L      O   O  RRRR    SSS   PPPP   AAAAA  C      EEE       %
9%    C      O   O  L      O   O  R R       SS  P      A   A  C      E         %
10%     CCCC   OOO   LLLLL   OOO   R  R   SSSSS  P      A   A   CCCC  EEEEE     %
11%                                                                             %
12%                                                                             %
13%                     MagickCore Image Colorspace Methods                     %
14%                                                                             %
15%                              Software Design                                %
16%                                John Cristy                                  %
17%                                 July 1992                                   %
18%                                                                             %
19%                                                                             %
20%  Copyright 1999-2008 ImageMagick Studio LLC, a non-profit organization      %
21%  dedicated to making software imaging solutions freely available.           %
22%                                                                             %
23%  You may not use this file except in compliance with the License.  You may  %
24%  obtain a copy of the License at                                            %
25%                                                                             %
26%    http://www.imagemagick.org/script/license.php                            %
27%                                                                             %
28%  Unless required by applicable law or agreed to in writing, software        %
29%  distributed under the License is distributed on an "AS IS" BASIS,          %
30%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31%  See the License for the specific language governing permissions and        %
32%  limitations under the License.                                             %
33%                                                                             %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40  Include declarations.
41*/
42#include "magick/studio.h"
43#include "magick/property.h"
44#include "magick/cache.h"
45#include "magick/cache-view.h"
46#include "magick/color.h"
47#include "magick/color-private.h"
48#include "magick/colorspace.h"
49#include "magick/colorspace-private.h"
50#include "magick/exception.h"
51#include "magick/exception-private.h"
52#include "magick/image.h"
53#include "magick/image-private.h"
54#include "magick/gem.h"
55#include "magick/memory_.h"
56#include "magick/monitor.h"
57#include "magick/monitor-private.h"
58#include "magick/pixel-private.h"
59#include "magick/quantize.h"
60#include "magick/quantum.h"
61#include "magick/string_.h"
62#include "magick/utility.h"
63
64/*
65%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
66%                                                                             %
67%                                                                             %
68%                                                                             %
69+     R G B T r a n s f o r m I m a g e                                       %
70%                                                                             %
71%                                                                             %
72%                                                                             %
73%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
74%
75%  RGBTransformImage() converts the reference image from RGB to an alternate
76%  colorspace.  The transformation matrices are not the standard ones: the
77%  weights are rescaled to normalized the range of the transformed values to
78%  be [0..QuantumRange].
79%
80%  The format of the RGBTransformImage method is:
81%
82%      MagickBooleanType RGBTransformImage(Image *image,
83%        const ColorspaceType colorspace)
84%
85%  A description of each parameter follows:
86%
87%    o image: the image.
88%
89%    o colorspace: the colorspace to transform the image to.
90%
91*/
92
93static inline void ConvertRGBToXYZ(const Quantum red,const Quantum green,
94  const Quantum blue,double *X,double *Y,double *Z)
95{
96  double
97    b,
98    g,
99    r;
100
101  assert(X != (double *) NULL);
102  assert(Y != (double *) NULL);
103  assert(Z != (double *) NULL);
104  r=QuantumScale*red;
105  g=QuantumScale*green;
106  b=QuantumScale*blue;
107  *X=0.4124240*r+0.3575790*g+0.1804640*b;
108  *Y=0.2126560*r+0.7151580*g+0.0721856*b;
109  *Z=0.0193324*r+0.1191930*g+0.9504440*b;
110}
111
112static inline void ConvertXYZToLab(const double X,const double Y,const double Z,
113  double *L,double *a,double *b)
114{
115  double
116    x,
117    y,
118    z;
119
120  assert(L != (double *) NULL);
121  assert(a != (double *) NULL);
122  assert(b != (double *) NULL);
123  x=X/0.9504559271;
124  if (x > (216/24389.0))
125    x=pow(x,1.0/3.0);
126  else 
127    x=(7.787*x)+(16.0/116.0);
128  y=Y/1.00000;
129  if (y > (216/24389.0))
130    y=pow(y,1.0/3.0);
131  else
132    y=(7.787*y)+(16.0/116.0);
133  z=Z/1.0890577508;
134  if (z > (216/24389.0))
135    z=pow(z,1.0/3.0);
136  else
137    z=(7.787*z)+(16.0/116.0);
138  *L=0.5*((1.160*y)-0.160+1.0);
139  *a=0.5*(5.000*(x-y)+1.0);
140  *b=0.5*(2.000*(y-z)+1.0);
141}
142
143MagickExport MagickBooleanType RGBTransformImage(Image *image,
144  const ColorspaceType colorspace)
145{
146#define RGBTransformImageTag  "RGBTransform/Image"
147
148  long
149    y;
150
151  MagickBooleanType
152    proceed;
153
154  PrimaryInfo
155    primary_info,
156    *x_map,
157    *y_map,
158    *z_map;
159
160  register long
161    i;
162
163  ViewInfo
164    **image_view;
165
166  volatile MagickBooleanType
167    status;
168
169  assert(image != (Image *) NULL);
170  assert(image->signature == MagickSignature);
171  if (image->debug != MagickFalse)
172    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
173  assert(colorspace != RGBColorspace);
174  assert(colorspace != TransparentColorspace);
175  assert(colorspace != UndefinedColorspace);
176  switch (image->colorspace)
177  {
178    case GRAYColorspace:
179    case Rec601LumaColorspace:
180    case Rec709LumaColorspace:
181    case RGBColorspace:
182    case TransparentColorspace:
183      break;
184    default:
185    {
186      (void) SetImageColorspace(image,image->colorspace);
187      break;
188    }
189  }
190  image->colorspace=colorspace;
191  status=MagickTrue;
192  switch (colorspace)
193  {
194    case CMYColorspace:
195    {
196      /*
197        Convert RGB to CMY colorspace.
198      */
199      if (image->storage_class == PseudoClass)
200        {
201          if (SyncImage(image) == MagickFalse)
202            return(MagickFalse);
203          if (SetImageStorageClass(image,DirectClass) == MagickFalse)
204            return(MagickFalse);
205        }
206      image_view=AcquireCacheViewThreadSet(image);
207      #pragma omp parallel for schedule(static,64)
208      for (y=0; y < (long) image->rows; y++)
209      {
210        register long
211          id,
212          x;
213
214        register PixelPacket
215          *q;
216
217        if (status == MagickFalse)
218          continue;
219        id=GetCacheViewThreadId();
220        q=GetCacheViewPixels(image_view[id],0,y,image->columns,1);
221        if (q == (PixelPacket *) NULL)
222          {
223            status=MagickFalse;
224            continue;
225          }
226        for (x=0; x < (long) image->columns; x++)
227        {
228          q->red=RoundToQuantum((MagickRealType) (QuantumRange-q->red));
229          q->green=RoundToQuantum((MagickRealType) (QuantumRange-q->green));
230          q->blue=RoundToQuantum((MagickRealType) (QuantumRange-q->blue));
231          q++;
232        }
233        if (SyncCacheViewPixels(image_view[id]) == MagickFalse)
234          status=MagickFalse;
235      }
236      image_view=DestroyCacheViewThreadSet(image_view);
237      image->type=image->matte == MagickFalse ? ColorSeparationType :
238        ColorSeparationMatteType;
239      return(status);
240    }
241    case CMYKColorspace:
242    {
243      MagickPixelPacket
244        zero;
245
246      /*
247        Convert RGB to CMYK colorspace.
248      */
249      if (image->storage_class == PseudoClass)
250        {
251          if (SyncImage(image) == MagickFalse)
252            return(MagickFalse);
253          if (SetImageStorageClass(image,DirectClass) == MagickFalse)
254            return(MagickFalse);
255        }
256      GetMagickPixelPacket(image,&zero);
257      image_view=AcquireCacheViewThreadSet(image);
258      #pragma omp parallel for schedule(static,64)
259      for (y=0; y < (long) image->rows; y++)
260      {
261        MagickPixelPacket
262          pixel;
263
264        register IndexPacket
265          *indexes;
266
267        register long
268          id,
269          x;
270
271        register PixelPacket
272          *q;
273
274        if (status == MagickFalse)
275          continue;
276        id=GetCacheViewThreadId();
277        q=GetCacheViewPixels(image_view[id],0,y,image->columns,1);
278        if (q == (PixelPacket *) NULL)
279          {
280            status=MagickFalse;
281            continue;
282          }
283        indexes=GetCacheViewIndexes(image_view[id]);
284        pixel=zero;
285        for (x=0; x < (long) image->columns; x++)
286        {
287          SetMagickPixelPacket(image,q,indexes+x,&pixel);
288          ConvertRGBToCMYK(&pixel);
289          SetPixelPacket(image,&pixel,q,indexes+x);
290          q++;
291        }
292        if (SyncCacheViewPixels(image_view[id]) == MagickFalse)
293          status=MagickFalse;
294      }
295      image_view=DestroyCacheViewThreadSet(image_view);
296      image->type=image->matte == MagickFalse ? ColorSeparationType :
297        ColorSeparationMatteType;
298      return(status);
299    }
300    case HSBColorspace:
301    {
302      /*
303        Transform image from RGB to HSB.
304      */
305      if (image->storage_class == PseudoClass)
306        {
307          if (SyncImage(image) == MagickFalse)
308            return(MagickFalse);
309          if (SetImageStorageClass(image,DirectClass) == MagickFalse)
310            return(MagickFalse);
311        }
312      image_view=AcquireCacheViewThreadSet(image);
313      #pragma omp parallel for schedule(static,64)
314      for (y=0; y < (long) image->rows; y++)
315      {
316        double
317          brightness,
318          hue,
319          saturation;
320
321        register long
322          id,
323          x;
324
325        register PixelPacket
326          *q;
327
328        if (status == MagickFalse)
329          continue;
330        id=GetCacheViewThreadId();
331        q=GetCacheViewPixels(image_view[id],0,y,image->columns,1);
332        if (q == (PixelPacket *) NULL)
333          {
334            status=MagickFalse;
335            continue;
336          }
337        hue=0.0;
338        saturation=0.0;
339        brightness=0.0;
340        for (x=0; x < (long) image->columns; x++)
341        {
342          ConvertRGBToHSB(q->red,q->green,q->blue,&hue,&saturation,&brightness);
343          q->red=RoundToQuantum((MagickRealType) QuantumRange*hue);
344          q->green=RoundToQuantum((MagickRealType) QuantumRange*saturation);
345          q->blue=RoundToQuantum((MagickRealType) QuantumRange*brightness);
346          q++;
347        }
348        if (SyncCacheViewPixels(image_view[id]) == MagickFalse)
349          status=MagickFalse;
350      }
351      image_view=DestroyCacheViewThreadSet(image_view);
352      return(status);
353    }
354    case HSLColorspace:
355    {
356      /*
357        Transform image from RGB to HSL.
358      */
359      if (image->storage_class == PseudoClass)
360        {
361          if (SyncImage(image) == MagickFalse)
362            return(MagickFalse);
363          if (SetImageStorageClass(image,DirectClass) == MagickFalse)
364            return(MagickFalse);
365        }
366      image_view=AcquireCacheViewThreadSet(image);
367      #pragma omp parallel for schedule(static,64)
368      for (y=0; y < (long) image->rows; y++)
369      {
370        double
371          hue,
372          lightness,
373          saturation;
374
375        register long
376          id,
377          x;
378
379        register PixelPacket
380          *q;
381
382        if (status == MagickFalse)
383          continue;
384        id=GetCacheViewThreadId();
385        q=GetCacheViewPixels(image_view[id],0,y,image->columns,1);
386        if (q == (PixelPacket *) NULL)
387          {
388            status=MagickFalse;
389            continue;
390          }
391        hue=0.0;
392        saturation=0.0;
393        lightness=0.0;
394        for (x=0; x < (long) image->columns; x++)
395        {
396          ConvertRGBToHSL(q->red,q->green,q->blue,&hue,&saturation,&lightness);
397          q->red=RoundToQuantum((MagickRealType) QuantumRange*hue);
398          q->green=RoundToQuantum((MagickRealType) QuantumRange*saturation);
399          q->blue=RoundToQuantum((MagickRealType) QuantumRange*lightness);
400          q++;
401        }
402        if (SyncCacheViewPixels(image_view[id]) == MagickFalse)
403          status=MagickFalse;
404      }
405      image_view=DestroyCacheViewThreadSet(image_view);
406      return(status);
407    }
408    case HWBColorspace:
409    {
410      /*
411        Transform image from RGB to HWB.
412      */
413      if (image->storage_class == PseudoClass)
414        {
415          if (SyncImage(image) == MagickFalse)
416            return(MagickFalse);
417          if (SetImageStorageClass(image,DirectClass) == MagickFalse)
418            return(MagickFalse);
419        }
420      image_view=AcquireCacheViewThreadSet(image);
421      #pragma omp parallel for schedule(static,64)
422      for (y=0; y < (long) image->rows; y++)
423      {
424        double
425          blackness,
426          hue,
427          whiteness;
428
429        register long
430          id,
431          x;
432
433        register PixelPacket
434          *q;
435
436        if (status == MagickFalse)
437          continue;
438        id=GetCacheViewThreadId();
439        q=GetCacheViewPixels(image_view[id],0,y,image->columns,1);
440        if (q == (PixelPacket *) NULL)
441          {
442            status=MagickFalse;
443            continue;
444          }
445        hue=0.0;
446        whiteness=0.0;
447        blackness=0.0;
448        for (x=0; x < (long) image->columns; x++)
449        {
450          ConvertRGBToHWB(q->red,q->green,q->blue,&hue,&whiteness,&blackness);
451          q->red=RoundToQuantum((MagickRealType) QuantumRange*hue);
452          q->green=RoundToQuantum((MagickRealType) QuantumRange*whiteness);
453          q->blue=RoundToQuantum((MagickRealType) QuantumRange*blackness);
454          q++;
455        }
456        if (SyncCacheViewPixels(image_view[id]) == MagickFalse)
457          status=MagickFalse;
458      }
459      image_view=DestroyCacheViewThreadSet(image_view);
460      return(status);
461    }
462    case LabColorspace:
463    {
464      /*
465        Transform image from RGB to Lab.
466      */
467      if (image->storage_class == PseudoClass)
468        {
469          if (SyncImage(image) == MagickFalse)
470            return(MagickFalse);
471          if (SetImageStorageClass(image,DirectClass) == MagickFalse)
472            return(MagickFalse);
473        }
474      image_view=AcquireCacheViewThreadSet(image);
475      #pragma omp parallel for schedule(static,64)
476      for (y=0; y < (long) image->rows; y++)
477      {
478        double
479          a,
480          b,
481          L,
482          X,
483          Y,
484          Z;
485
486        register long
487          id,
488          x;
489
490        register PixelPacket
491          *q;
492
493        if (status == MagickFalse)
494          continue;
495        id=GetCacheViewThreadId();
496        q=GetCacheViewPixels(image_view[id],0,y,image->columns,1);
497        if (q == (PixelPacket *) NULL)
498          {
499            status=MagickFalse;
500            continue;
501          }
502        L=0.0;
503        a=0.0;
504        b=0.0;
505        X=0.0;
506        Y=0.0;
507        Z=0.0;
508        for (x=0; x < (long) image->columns; x++)
509        {
510          ConvertRGBToXYZ(q->red,q->green,q->blue,&X,&Y,&Z);
511          ConvertXYZToLab(X,Y,Z,&L,&a,&b);
512          q->red=RoundToQuantum((MagickRealType) QuantumRange*L);
513          q->green=RoundToQuantum((MagickRealType) QuantumRange*a);
514          q->blue=RoundToQuantum((MagickRealType) QuantumRange*b);
515          q++;
516        }
517        if (SyncCacheViewPixels(image_view[id]) == MagickFalse)
518          status=MagickFalse;
519      }
520      image_view=DestroyCacheViewThreadSet(image_view);
521      return(status);
522    }
523    case LogColorspace:
524    {
525#define ReferenceBlack  95.0
526#define ReferenceWhite  685.0
527#define DisplayGamma  (1.0/1.7)
528
529      const char
530        *value;
531
532      double
533        black,
534        density,
535        gamma,
536        reference_black,
537        reference_white;
538
539      Quantum
540        *logmap;
541
542      /*
543        Transform RGB to Log colorspace.
544      */
545      density=2.03728;
546      gamma=DisplayGamma;
547      value=GetImageProperty(image,"gamma");
548      if (value != (const char *) NULL)
549        gamma=1.0/atof(value) != 0.0 ? atof(value) : 1.0;
550      reference_black=ReferenceBlack;
551      value=GetImageProperty(image,"reference-black");
552      if (value != (const char *) NULL)
553        reference_black=atof(value);
554      reference_white=ReferenceWhite;
555      value=GetImageProperty(image,"reference-white");
556      if (value != (const char *) NULL)
557        reference_white=atof(value);
558      logmap=(Quantum *) AcquireQuantumMemory((size_t) MaxMap+1UL,
559        sizeof(*logmap));
560      if (logmap == (Quantum *) NULL)
561        ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
562          image->filename);
563      black=pow(10.0,(reference_black-reference_white)*(gamma/density)*
564        0.002/0.6);
565      for (i=0; i <= (long) MaxMap; i++)
566        logmap[i]=ScaleMapToQuantum((MagickRealType) (MaxMap*(reference_white+
567          log10(black+((double) i/MaxMap)*(1.0-black))/((gamma/density)*
568          0.002/0.6))/1024.0+0.5));
569      image_view=AcquireCacheViewThreadSet(image);
570      #pragma omp parallel for schedule(static,64)
571      for (y=0; y < (long) image->rows; y++)
572      {
573        register long
574          id,
575          x;
576
577        register PixelPacket
578          *q;
579
580        if (status == MagickFalse)
581          continue;
582        id=GetCacheViewThreadId();
583        q=GetCacheViewPixels(image_view[id],0,y,image->columns,1);
584        if (q == (PixelPacket *) NULL)
585          {
586            status=MagickFalse;
587            continue;
588          }
589        for (x=(long) image->columns; x != 0; x--)
590        {
591          q->red=logmap[ScaleQuantumToMap(q->red)];
592          q->green=logmap[ScaleQuantumToMap(q->green)];
593          q->blue=logmap[ScaleQuantumToMap(q->blue)];
594          q++;
595        }
596        if (SyncCacheViewPixels(image_view[id]) == MagickFalse)
597          status=MagickFalse;
598      }
599      image_view=DestroyCacheViewThreadSet(image_view);
600      logmap=(Quantum *) RelinquishMagickMemory(logmap);
601      return(status);
602    }
603    default:
604      break;
605  }
606  /*
607    Allocate the tables.
608  */
609  x_map=(PrimaryInfo *) AcquireQuantumMemory((size_t) MaxMap+1UL,
610    sizeof(*x_map));
611  y_map=(PrimaryInfo *) AcquireQuantumMemory((size_t) MaxMap+1UL,
612    sizeof(*y_map));
613  z_map=(PrimaryInfo *) AcquireQuantumMemory((size_t) MaxMap+1UL,
614    sizeof(*z_map));
615  if ((x_map == (PrimaryInfo *) NULL) || (y_map == (PrimaryInfo *) NULL) ||
616      (z_map == (PrimaryInfo *) NULL))
617    ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
618      image->filename);
619  (void) ResetMagickMemory(&primary_info,0,sizeof(primary_info));
620  switch (colorspace)
621  {
622    case OHTAColorspace:
623    {
624      /*
625        Initialize OHTA tables:
626
627          I1 = 0.33333*R+0.33334*G+0.33333*B
628          I2 = 0.50000*R+0.00000*G-0.50000*B
629          I3 =-0.25000*R+0.50000*G-0.25000*B
630
631        I and Q, normally -0.5 through 0.5, are normalized to the range 0
632        through QuantumRange.
633      */
634      primary_info.y=(double) (MaxMap+1.0)/2.0;
635      primary_info.z=(double) (MaxMap+1.0)/2.0;
636      for (i=0; i <= (long) MaxMap; i++)
637      {
638        x_map[i].x=0.33333*i;
639        y_map[i].x=0.33334*i;
640        z_map[i].x=0.33333*i;
641        x_map[i].y=0.50000*i;
642        y_map[i].y=0.00000*i;
643        z_map[i].y=(-0.50000)*i;
644        x_map[i].z=(-0.25000)*i;
645        y_map[i].z=0.50000*i;
646        z_map[i].z=(-0.25000)*i;
647      }
648      break;
649    }
650    case Rec601LumaColorspace:
651    case GRAYColorspace:
652    {
653      /*
654        Initialize Rec601 luma tables:
655
656          G = 0.29900*R+0.58700*G+0.11400*B
657      */
658      for (i=0; i <= (long) MaxMap; i++)
659      {
660        x_map[i].x=0.29900*i;
661        y_map[i].x=0.58700*i;
662        z_map[i].x=0.11400*i;
663        x_map[i].y=0.29900*i;
664        y_map[i].y=0.58700*i;
665        z_map[i].y=0.11400*i;
666        x_map[i].z=0.29900*i;
667        y_map[i].z=0.58700*i;
668        z_map[i].z=0.11400*i;
669      }
670      image->type=GrayscaleType;
671      break;
672    }
673    case Rec601YCbCrColorspace:
674    case YCbCrColorspace:
675    {
676      /*
677        Initialize YCbCr tables (ITU-R BT.601):
678
679          Y =  0.299000*R+0.587000*G+0.114000*B
680          Cb= -0.168736*R-0.331264*G+0.500000*B
681          Cr=  0.500000*R-0.418688*G-0.081312*B
682
683        Cb and Cr, normally -0.5 through 0.5, are normalized to the range 0
684        through QuantumRange.
685      */
686      primary_info.y=(double) (MaxMap+1.0)/2.0;
687      primary_info.z=(double) (MaxMap+1.0)/2.0;
688      for (i=0; i <= (long) MaxMap; i++)
689      {
690        x_map[i].x=0.299000*i;
691        y_map[i].x=0.587000*i;
692        z_map[i].x=0.114000*i;
693        x_map[i].y=(-0.168730)*i;
694        y_map[i].y=(-0.331264)*i;
695        z_map[i].y=0.500000*i;
696        x_map[i].z=0.500000*i;
697        y_map[i].z=(-0.418688)*i;
698        z_map[i].z=(-0.081312)*i;
699      }
700      break;
701    }
702    case Rec709LumaColorspace:
703    {
704      /*
705        Initialize Rec709 luma tables:
706
707          G = 0.21260*R+0.71520*G+0.07220*B
708      */
709      for (i=0; i <= (long) MaxMap; i++)
710      {
711        x_map[i].x=0.21260*i;
712        y_map[i].x=0.71520*i;
713        z_map[i].x=0.07220*i;
714        x_map[i].y=0.21260*i;
715        y_map[i].y=0.71520*i;
716        z_map[i].y=0.07220*i;
717        x_map[i].z=0.21260*i;
718        y_map[i].z=0.71520*i;
719        z_map[i].z=0.07220*i;
720      }
721      break;
722    }
723    case Rec709YCbCrColorspace:
724    {
725      /*
726        Initialize YCbCr tables (ITU-R BT.709):
727
728          Y =  0.212600*R+0.715200*G+0.072200*B
729          Cb= -0.114572*R-0.385428*G+0.500000*B
730          Cr=  0.500000*R-0.454153*G-0.045847*B
731
732        Cb and Cr, normally -0.5 through 0.5, are normalized to the range 0
733        through QuantumRange.
734      */
735      primary_info.y=(double) (MaxMap+1.0)/2.0;
736      primary_info.z=(double) (MaxMap+1.0)/2.0;
737      for (i=0; i <= (long) MaxMap; i++)
738      {
739        x_map[i].x=0.212600*i;
740        y_map[i].x=0.715200*i;
741        z_map[i].x=0.072200*i;
742        x_map[i].y=(-0.114572)*i;
743        y_map[i].y=(-0.385428)*i;
744        z_map[i].y=0.500000*i;
745        x_map[i].z=0.500000*i;
746        y_map[i].z=(-0.454153)*i;
747        z_map[i].z=(-0.045847)*i;
748      }
749      break;
750    }
751    case sRGBColorspace:
752    {
753      /*
754        Linear RGB to nonlinear sRGB (http://www.w3.org/Graphics/Color/sRGB):
755
756          R = 1.0*R+0.0*G+0.0*B
757          G = 0.0*R+0.1*G+0.0*B
758          B = 0.0*R+0.0*G+1.0*B
759      */
760      for (i=0; i <= (long) MaxMap; i++)
761      {
762        double
763          v;
764
765        v=(double) i/MaxMap;
766        if (((double) i/MaxMap) <= 0.03928)
767          v/=12.92;
768        else
769          v=(double) MaxMap*pow((((double) i/MaxMap)+0.055)/1.055,2.4);
770        x_map[i].x=1.0*v;
771        y_map[i].x=0.0*v;
772        z_map[i].x=0.0*v;
773        x_map[i].y=0.0*v;
774        y_map[i].y=1.0*v;
775        z_map[i].y=0.0*v;
776        x_map[i].z=0.0*v;
777        y_map[i].z=0.0*v;
778        z_map[i].z=1.0*v;
779      }
780      break;
781    }
782    case XYZColorspace:
783    {
784      /*
785        Initialize CIE XYZ tables (ITU-R 709 RGB):
786
787          X = 0.4124240*R+0.3575790*G+0.1804640*B
788          Y = 0.2126560*R+0.7151580*G+0.0721856*B
789          Z = 0.0193324*R+0.1191930*G+0.9504440*B
790      */
791      for (i=0; i <= (long) MaxMap; i++)
792      {
793        x_map[i].x=0.4124240*i;
794        y_map[i].x=0.3575790*i;
795        z_map[i].x=0.1804640*i;
796        x_map[i].y=0.2126560*i;
797        y_map[i].y=0.7151580*i;
798        z_map[i].y=0.0721856*i;
799        x_map[i].z=0.0193324*i;
800        y_map[i].z=0.1191930*i;
801        z_map[i].z=0.9504440*i;
802      }
803      break;
804    }
805    case YCCColorspace:
806    {
807      /*
808        Initialize YCC tables:
809
810          Y =  0.29900*R+0.58700*G+0.11400*B
811          C1= -0.29900*R-0.58700*G+0.88600*B
812          C2=  0.70100*R-0.58700*G-0.11400*B
813
814        YCC is scaled by 1.3584.  C1 zero is 156 and C2 is at 137.
815      */
816      primary_info.y=(double) ScaleQuantumToMap(ScaleCharToQuantum(156));
817      primary_info.z=(double) ScaleQuantumToMap(ScaleCharToQuantum(137));
818      for (i=0; i <= (long) (0.018*MaxMap); i++)
819      {
820        x_map[i].x=0.003962014134275617*i;
821        y_map[i].x=0.007778268551236748*i;
822        z_map[i].x=0.001510600706713781*i;
823        x_map[i].y=(-0.002426619775463276)*i;
824        y_map[i].y=(-0.004763965913702149)*i;
825        z_map[i].y=0.007190585689165425*i;
826        x_map[i].z=0.006927257754597858*i;
827        y_map[i].z=(-0.005800713697502058)*i;
828        z_map[i].z=(-0.0011265440570958)*i;
829      }
830      for ( ; i <= (long) MaxMap; i++)
831      {
832        x_map[i].x=0.2201118963486454*(1.099*i-0.099);
833        y_map[i].x=0.4321260306242638*(1.099*i-0.099);
834        z_map[i].x=0.08392226148409894*(1.099*i-0.099);
835        x_map[i].y=(-0.1348122097479598)*(1.099*i-0.099);
836        y_map[i].y=(-0.2646647729834528)*(1.099*i-0.099);
837        z_map[i].y=0.3994769827314126*(1.099*i-0.099);
838        x_map[i].z=0.3848476530332144*(1.099*i-0.099);
839        y_map[i].z=(-0.3222618720834477)*(1.099*i-0.099);
840        z_map[i].z=(-0.06258578094976668)*(1.099*i-0.099);
841      }
842      break;
843    }
844    case YIQColorspace:
845    {
846      /*
847        Initialize YIQ tables:
848
849          Y = 0.29900*R+0.58700*G+0.11400*B
850          I = 0.59600*R-0.27400*G-0.32200*B
851          Q = 0.21100*R-0.52300*G+0.31200*B
852
853        I and Q, normally -0.5 through 0.5, are normalized to the range 0
854        through QuantumRange.
855      */
856      primary_info.y=(double) (MaxMap+1.0)/2.0;
857      primary_info.z=(double) (MaxMap+1.0)/2.0;
858      for (i=0; i <= (long) MaxMap; i++)
859      {
860        x_map[i].x=0.29900*i;
861        y_map[i].x=0.58700*i;
862        z_map[i].x=0.11400*i;
863        x_map[i].y=0.59600*i;
864        y_map[i].y=(-0.27400)*i;
865        z_map[i].y=(-0.32200)*i;
866        x_map[i].z=0.21100*i;
867        y_map[i].z=(-0.52300)*i;
868        z_map[i].z=0.31200*i;
869      }
870      break;
871    }
872    case YPbPrColorspace:
873    {
874      /*
875        Initialize YPbPr tables (ITU-R BT.601):
876
877          Y =  0.299000*R+0.587000*G+0.114000*B
878          Pb= -0.168736*R-0.331264*G+0.500000*B
879          Pr=  0.500000*R-0.418688*G-0.081312*B
880
881        Pb and Pr, normally -0.5 through 0.5, are normalized to the range 0
882        through QuantumRange.
883      */
884      primary_info.y=(double) (MaxMap+1.0)/2.0;
885      primary_info.z=(double) (MaxMap+1.0)/2.0;
886      for (i=0; i <= (long) MaxMap; i++)
887      {
888        x_map[i].x=0.299000*i;
889        y_map[i].x=0.587000*i;
890        z_map[i].x=0.114000*i;
891        x_map[i].y=(-0.168736)*i;
892        y_map[i].y=(-0.331264)*i;
893        z_map[i].y=0.500000*i;
894        x_map[i].z=0.500000*i;
895        y_map[i].z=(-0.418688)*i;
896        z_map[i].z=(-0.081312)*i;
897      }
898      break;
899    }
900    case YUVColorspace:
901    default:
902    {
903      /*
904        Initialize YUV tables:
905
906          Y =  0.29900*R+0.58700*G+0.11400*B
907          U = -0.14740*R-0.28950*G+0.43690*B
908          V =  0.61500*R-0.51500*G-0.10000*B
909
910        U and V, normally -0.5 through 0.5, are normalized to the range 0
911        through QuantumRange.  Note that U = 0.493*(B-Y), V = 0.877*(R-Y).
912      */
913      primary_info.y=(double) (MaxMap+1.0)/2.0;
914      primary_info.z=(double) (MaxMap+1.0)/2.0;
915      for (i=0; i <= (long) MaxMap; i++)
916      {
917        x_map[i].x=0.29900*i;
918        y_map[i].x=0.58700*i;
919        z_map[i].x=0.11400*i;
920        x_map[i].y=(-0.14740)*i;
921        y_map[i].y=(-0.28950)*i;
922        z_map[i].y=0.43690*i;
923        x_map[i].z=0.61500*i;
924        y_map[i].z=(-0.51500)*i;
925        z_map[i].z=(-0.10000)*i;
926      }
927      break;
928    }
929  }
930  /*
931    Convert from RGB.
932  */
933  switch (image->storage_class)
934  {
935    case DirectClass:
936    default:
937    {
938      MagickPixelPacket
939        zero;
940
941      /*
942        Convert DirectClass image.
943      */
944      GetMagickPixelPacket(image,&zero);
945      image_view=AcquireCacheViewThreadSet(image);
946      #pragma omp parallel for schedule(static,64)
947      for (y=0; y < (long) image->rows; y++)
948      {
949        MagickPixelPacket
950          pixel;
951
952        register long
953          id,
954          x;
955
956        register PixelPacket
957          *q;
958
959        if (status == MagickFalse)
960          continue;
961        id=GetCacheViewThreadId();
962        q=GetCacheViewPixels(image_view[id],0,y,image->columns,1);
963        if (q == (PixelPacket *) NULL)
964          {
965            status=MagickFalse;
966            continue;
967          }
968        pixel=zero;
969        for (x=0; x < (long) image->columns; x++)
970        {
971          pixel.red=x_map[ScaleQuantumToMap(q->red)].x+
972            y_map[ScaleQuantumToMap(q->green)].x+
973            z_map[ScaleQuantumToMap(q->blue)].x+primary_info.x;
974          pixel.green=x_map[ScaleQuantumToMap(q->red)].y+
975            y_map[ScaleQuantumToMap(q->green)].y+
976            z_map[ScaleQuantumToMap(q->blue)].y+primary_info.y;
977          pixel.blue=x_map[ScaleQuantumToMap(q->red)].z+
978            y_map[ScaleQuantumToMap(q->green)].z+
979            z_map[ScaleQuantumToMap(q->blue)].z+primary_info.z;
980          q->red=ScaleMapToQuantum(pixel.red);
981          q->green=ScaleMapToQuantum(pixel.green);
982          q->blue=ScaleMapToQuantum(pixel.blue);
983          q++;
984        }
985        if (SyncCacheViewPixels(image_view[id]) == MagickFalse)
986          status=MagickFalse;
987        proceed=SetImageProgress(image,RGBTransformImageTag,y,image->rows);
988        if (proceed == MagickFalse)
989          status=MagickFalse;
990      }
991      image_view=DestroyCacheViewThreadSet(image_view);
992      break;
993    }
994    case PseudoClass:
995    {
996      MagickPixelPacket
997        zero;
998
999      /*
1000        Convert PseudoClass image.
1001      */
1002      GetMagickPixelPacket(image,&zero);
1003      image_view=AcquireCacheViewThreadSet(image);
1004      for (i=0; i < (long) image->colors; i++)
1005      {
1006        MagickPixelPacket
1007          pixel;
1008
1009        pixel=zero;
1010        pixel.red=x_map[ScaleQuantumToMap(image->colormap[i].red)].x+
1011          y_map[ScaleQuantumToMap(image->colormap[i].green)].x+
1012          z_map[ScaleQuantumToMap(image->colormap[i].blue)].x+primary_info.x;
1013        pixel.green=x_map[ScaleQuantumToMap(image->colormap[i].red)].y+
1014          y_map[ScaleQuantumToMap(image->colormap[i].green)].y+
1015          z_map[ScaleQuantumToMap(image->colormap[i].blue)].y+primary_info.y;
1016        pixel.blue=x_map[ScaleQuantumToMap(image->colormap[i].red)].z+
1017          y_map[ScaleQuantumToMap(image->colormap[i].green)].z+
1018          z_map[ScaleQuantumToMap(image->colormap[i].blue)].z+primary_info.z;
1019        image->colormap[i].red=ScaleMapToQuantum(pixel.red);
1020        image->colormap[i].green=ScaleMapToQuantum(pixel.green);
1021        image->colormap[i].blue=ScaleMapToQuantum(pixel.blue);
1022      }
1023      image_view=DestroyCacheViewThreadSet(image_view);
1024      (void) SyncImage(image);
1025      break;
1026    }
1027  }
1028  /*
1029    Relinquish resources.
1030  */
1031  z_map=(PrimaryInfo *) RelinquishMagickMemory(z_map);
1032  y_map=(PrimaryInfo *) RelinquishMagickMemory(y_map);
1033  x_map=(PrimaryInfo *) RelinquishMagickMemory(x_map);
1034  return(status);
1035}
1036
1037/*
1038%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1039%                                                                             %
1040%                                                                             %
1041%                                                                             %
1042%   S e t I m a g e C o l o r s p a c e                                       %
1043%                                                                             %
1044%                                                                             %
1045%                                                                             %
1046%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1047%
1048%  SetImageColorspace() sets the colorspace member of the Image structure.
1049%
1050%  The format of the SetImageColorspace method is:
1051%
1052%      MagickBooleanType SetImageColorspace(Image *image,
1053%        const ColorspaceType colorspace)
1054%
1055%  A description of each parameter follows:
1056%
1057%    o image: the image.
1058%
1059%    o colorspace: the colorspace.
1060%
1061*/
1062MagickExport MagickBooleanType SetImageColorspace(Image *image,
1063  const ColorspaceType colorspace)
1064{
1065  MagickBooleanType
1066    status;
1067
1068  assert(image != (Image *) NULL);