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