blob: 8f6a6ced0b35024db948ed28209188f1eead2f64 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% GGGG EEEEE M M %
7% G E MM MM %
8% G GG EEE M M M %
9% G G E M M %
10% GGGG EEEEE M M %
11% %
12% %
13% Graphic Gems - Graphic Support Methods %
14% %
15% Software Design %
16% John Cristy %
17% August 1996 %
18% %
19% %
cristy1454be72011-12-19 01:52:48 +000020% Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% 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/*
41 Include declarations.
42*/
cristy4c08aed2011-07-01 19:47:50 +000043#include "MagickCore/studio.h"
44#include "MagickCore/color-private.h"
45#include "MagickCore/draw.h"
46#include "MagickCore/gem.h"
cristyd1dd6e42011-09-04 01:46:08 +000047#include "MagickCore/gem-private.h"
cristy4c08aed2011-07-01 19:47:50 +000048#include "MagickCore/image.h"
49#include "MagickCore/image-private.h"
50#include "MagickCore/log.h"
51#include "MagickCore/memory_.h"
52#include "MagickCore/pixel-accessor.h"
cristy35f15302012-06-07 14:59:02 +000053#include "MagickCore/pixel-private.h"
cristy4c08aed2011-07-01 19:47:50 +000054#include "MagickCore/quantum.h"
55#include "MagickCore/quantum-private.h"
56#include "MagickCore/random_.h"
57#include "MagickCore/resize.h"
58#include "MagickCore/transform.h"
59#include "MagickCore/signature-private.h"
cristy3ed852e2009-09-05 21:47:34 +000060
61/*
62%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
63% %
64% %
65% %
cristybd0e5482012-06-14 11:51:00 +000066% C o n v e r t H S B T o s R G B %
cristy3ed852e2009-09-05 21:47:34 +000067% %
68% %
69% %
70%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
71%
cristycf12ee42012-06-14 11:02:23 +000072% ConvertHSBTosRGB() transforms a (hue, saturation, brightness) to a (red,
cristy3ed852e2009-09-05 21:47:34 +000073% green, blue) triple.
74%
cristycf12ee42012-06-14 11:02:23 +000075% The format of the ConvertHSBTosRGBImage method is:
cristy3ed852e2009-09-05 21:47:34 +000076%
cristycf12ee42012-06-14 11:02:23 +000077% void ConvertHSBTosRGB(const double hue,const double saturation,
cristy3094b7f2011-10-01 23:18:02 +000078% const double brightness,double *red,double *green,double *blue)
cristy3ed852e2009-09-05 21:47:34 +000079%
80% A description of each parameter follows:
81%
82% o hue, saturation, brightness: A double value representing a
83% component of the HSB color space.
84%
85% o red, green, blue: A pointer to a pixel component of type Quantum.
86%
87*/
cristycf12ee42012-06-14 11:02:23 +000088MagickPrivate void ConvertHSBTosRGB(const double hue,const double saturation,
cristy3094b7f2011-10-01 23:18:02 +000089 const double brightness,double *red,double *green,double *blue)
cristy3ed852e2009-09-05 21:47:34 +000090{
cristycaf45802012-06-16 18:28:54 +000091 double
cristy3ed852e2009-09-05 21:47:34 +000092 f,
93 h,
94 p,
95 q,
96 t;
97
98 /*
99 Convert HSB to RGB colorspace.
100 */
cristy3094b7f2011-10-01 23:18:02 +0000101 assert(red != (double *) NULL);
102 assert(green != (double *) NULL);
103 assert(blue != (double *) NULL);
cristy98a65d52010-04-14 02:12:38 +0000104 if (saturation == 0.0)
cristy3ed852e2009-09-05 21:47:34 +0000105 {
cristycaf45802012-06-16 18:28:54 +0000106 *red=QuantumRange*brightness;
cristy3ed852e2009-09-05 21:47:34 +0000107 *green=(*red);
108 *blue=(*red);
109 return;
110 }
cristy98a65d52010-04-14 02:12:38 +0000111 h=6.0*(hue-floor(hue));
112 f=h-floor((double) h);
113 p=brightness*(1.0-saturation);
114 q=brightness*(1.0-saturation*f);
115 t=brightness*(1.0-(saturation*(1.0-f)));
cristy3ed852e2009-09-05 21:47:34 +0000116 switch ((int) h)
117 {
118 case 0:
119 default:
120 {
cristycaf45802012-06-16 18:28:54 +0000121 *red=QuantumRange*sRGBCompanding(brightness);
122 *green=QuantumRange*sRGBCompanding(t);
123 *blue=QuantumRange*sRGBCompanding(p);
cristy3ed852e2009-09-05 21:47:34 +0000124 break;
125 }
126 case 1:
127 {
cristycaf45802012-06-16 18:28:54 +0000128 *red=QuantumRange*sRGBCompanding(q);
129 *green=QuantumRange*sRGBCompanding(brightness);
130 *blue=QuantumRange*sRGBCompanding(p);
cristy3ed852e2009-09-05 21:47:34 +0000131 break;
132 }
133 case 2:
134 {
cristycaf45802012-06-16 18:28:54 +0000135 *red=QuantumRange*sRGBCompanding(p);
136 *green=QuantumRange*sRGBCompanding(brightness);
137 *blue=QuantumRange*sRGBCompanding(t);
cristy3ed852e2009-09-05 21:47:34 +0000138 break;
139 }
140 case 3:
141 {
cristycaf45802012-06-16 18:28:54 +0000142 *red=QuantumRange*sRGBCompanding(p);
143 *green=QuantumRange*sRGBCompanding(q);
144 *blue=QuantumRange*sRGBCompanding(brightness);
cristy3ed852e2009-09-05 21:47:34 +0000145 break;
146 }
147 case 4:
148 {
cristycaf45802012-06-16 18:28:54 +0000149 *red=QuantumRange*sRGBCompanding(t);
150 *green=QuantumRange*sRGBCompanding(p);
151 *blue=QuantumRange*sRGBCompanding(brightness);
cristy3ed852e2009-09-05 21:47:34 +0000152 break;
153 }
154 case 5:
155 {
cristycaf45802012-06-16 18:28:54 +0000156 *red=QuantumRange*sRGBCompanding(brightness);
157 *green=QuantumRange*sRGBCompanding(p);
158 *blue=QuantumRange*sRGBCompanding(q);
cristy3ed852e2009-09-05 21:47:34 +0000159 break;
160 }
161 }
162}
163
164/*
165%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
166% %
167% %
168% %
cristybd0e5482012-06-14 11:51:00 +0000169% C o n v e r t H S L T o s R G B %
cristy3ed852e2009-09-05 21:47:34 +0000170% %
171% %
172% %
173%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
174%
cristycf12ee42012-06-14 11:02:23 +0000175% ConvertHSLTosRGB() transforms a (hue, saturation, lightness) to a (red,
cristy3ed852e2009-09-05 21:47:34 +0000176% green, blue) triple.
177%
cristycf12ee42012-06-14 11:02:23 +0000178% The format of the ConvertHSLTosRGBImage method is:
cristy3ed852e2009-09-05 21:47:34 +0000179%
cristycf12ee42012-06-14 11:02:23 +0000180% void ConvertHSLTosRGB(const double hue,const double saturation,
cristy3094b7f2011-10-01 23:18:02 +0000181% const double lightness,double *red,double *green,double *blue)
cristy3ed852e2009-09-05 21:47:34 +0000182%
183% A description of each parameter follows:
184%
185% o hue, saturation, lightness: A double value representing a
186% component of the HSL color space.
187%
188% o red, green, blue: A pointer to a pixel component of type Quantum.
189%
190*/
191
cristycaf45802012-06-16 18:28:54 +0000192static inline double ConvertHueTosRGB(double m1,double m2,double hue)
cristy3ed852e2009-09-05 21:47:34 +0000193{
194 if (hue < 0.0)
195 hue+=1.0;
196 if (hue > 1.0)
197 hue-=1.0;
198 if ((6.0*hue) < 1.0)
cristya163b0c2010-04-13 01:04:49 +0000199 return(m1+6.0*(m2-m1)*hue);
200 if ((2.0*hue) < 1.0)
201 return(m2);
202 if ((3.0*hue) < 2.0)
203 return(m1+6.0*(m2-m1)*(2.0/3.0-hue));
204 return(m1);
cristy3ed852e2009-09-05 21:47:34 +0000205}
206
cristycf12ee42012-06-14 11:02:23 +0000207MagickExport void ConvertHSLTosRGB(const double hue,const double saturation,
cristy3094b7f2011-10-01 23:18:02 +0000208 const double lightness,double *red,double *green,double *blue)
cristy3ed852e2009-09-05 21:47:34 +0000209{
cristycaf45802012-06-16 18:28:54 +0000210 double
cristy3ed852e2009-09-05 21:47:34 +0000211 b,
212 g,
213 r,
214 m1,
215 m2;
216
217 /*
218 Convert HSL to RGB colorspace.
219 */
cristy3094b7f2011-10-01 23:18:02 +0000220 assert(red != (double *) NULL);
221 assert(green != (double *) NULL);
222 assert(blue != (double *) NULL);
cristy98a65d52010-04-14 02:12:38 +0000223 if (saturation == 0)
cristy3ed852e2009-09-05 21:47:34 +0000224 {
cristycaf45802012-06-16 18:28:54 +0000225 *red=QuantumRange*lightness;
cristy3ed852e2009-09-05 21:47:34 +0000226 *green=(*red);
227 *blue=(*red);
228 return;
229 }
cristy27cabb82010-04-14 11:33:57 +0000230 if (lightness < 0.5)
cristy98a65d52010-04-14 02:12:38 +0000231 m2=lightness*(saturation+1.0);
232 else
233 m2=(lightness+saturation)-(lightness*saturation);
234 m1=2.0*lightness-m2;
cristycf12ee42012-06-14 11:02:23 +0000235 r=ConvertHueTosRGB(m1,m2,hue+1.0/3.0);
236 g=ConvertHueTosRGB(m1,m2,hue);
237 b=ConvertHueTosRGB(m1,m2,hue-1.0/3.0);
cristycaf45802012-06-16 18:28:54 +0000238 *red=QuantumRange*sRGBCompanding(r);
239 *green=QuantumRange*sRGBCompanding(g);
240 *blue=QuantumRange*sRGBCompanding(b);
cristy3ed852e2009-09-05 21:47:34 +0000241}
242
243/*
244%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
245% %
246% %
247% %
cristybd0e5482012-06-14 11:51:00 +0000248% C o n v e r t H W B T o s R G B %
cristy3ed852e2009-09-05 21:47:34 +0000249% %
250% %
251% %
252%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
253%
cristycf12ee42012-06-14 11:02:23 +0000254% ConvertHWBTosRGB() transforms a (hue, whiteness, blackness) to a (red, green,
cristy3ed852e2009-09-05 21:47:34 +0000255% blue) triple.
256%
cristycf12ee42012-06-14 11:02:23 +0000257% The format of the ConvertHWBTosRGBImage method is:
cristy3ed852e2009-09-05 21:47:34 +0000258%
cristycf12ee42012-06-14 11:02:23 +0000259% void ConvertHWBTosRGB(const double hue,const double whiteness,
cristy3094b7f2011-10-01 23:18:02 +0000260% const double blackness,double *red,double *green,double *blue)
cristy3ed852e2009-09-05 21:47:34 +0000261%
262% A description of each parameter follows:
263%
264% o hue, whiteness, blackness: A double value representing a
265% component of the HWB color space.
266%
267% o red, green, blue: A pointer to a pixel component of type Quantum.
268%
269*/
cristycf12ee42012-06-14 11:02:23 +0000270MagickPrivate void ConvertHWBTosRGB(const double hue,const double whiteness,
cristy3094b7f2011-10-01 23:18:02 +0000271 const double blackness,double *red,double *green,double *blue)
cristy3ed852e2009-09-05 21:47:34 +0000272{
cristycaf45802012-06-16 18:28:54 +0000273 double
cristy3ed852e2009-09-05 21:47:34 +0000274 b,
275 f,
276 g,
277 n,
278 r,
279 v;
280
cristybb503372010-05-27 20:51:26 +0000281 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000282 i;
283
284 /*
285 Convert HWB to RGB colorspace.
286 */
cristy3094b7f2011-10-01 23:18:02 +0000287 assert(red != (double *) NULL);
288 assert(green != (double *) NULL);
289 assert(blue != (double *) NULL);
cristy3ed852e2009-09-05 21:47:34 +0000290 v=1.0-blackness;
cristyaf10b112012-04-18 13:25:37 +0000291 if (hue == -1.0)
cristy3ed852e2009-09-05 21:47:34 +0000292 {
cristycaf45802012-06-16 18:28:54 +0000293 *red=QuantumRange*sRGBCompanding(v);
294 *green=QuantumRange*sRGBCompanding(v);
295 *blue=QuantumRange*sRGBCompanding(v);
cristy3ed852e2009-09-05 21:47:34 +0000296 return;
297 }
cristybb503372010-05-27 20:51:26 +0000298 i=(ssize_t) floor(6.0*hue);
cristy3ed852e2009-09-05 21:47:34 +0000299 f=6.0*hue-i;
300 if ((i & 0x01) != 0)
301 f=1.0-f;
302 n=whiteness+f*(v-whiteness); /* linear interpolation */
303 switch (i)
304 {
305 default:
306 case 6:
307 case 0: r=v; g=n; b=whiteness; break;
308 case 1: r=n; g=v; b=whiteness; break;
309 case 2: r=whiteness; g=v; b=n; break;
310 case 3: r=whiteness; g=n; b=v; break;
311 case 4: r=n; g=whiteness; b=v; break;
312 case 5: r=v; g=whiteness; b=n; break;
313 }
cristycaf45802012-06-16 18:28:54 +0000314 *red=QuantumRange*sRGBCompanding(r);
315 *green=QuantumRange*sRGBCompanding(g);
316 *blue=QuantumRange*sRGBCompanding(b);
cristy3ed852e2009-09-05 21:47:34 +0000317}
318
319/*
320%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
321% %
322% %
323% %
cristybd0e5482012-06-14 11:51:00 +0000324% C o n v e r t s R G B T o H S B %
cristy3ed852e2009-09-05 21:47:34 +0000325% %
326% %
327% %
328%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
329%
cristycf12ee42012-06-14 11:02:23 +0000330% ConvertsRGBToHSB() transforms a (red, green, blue) to a (hue, saturation,
cristy3ed852e2009-09-05 21:47:34 +0000331% brightness) triple.
332%
cristycf12ee42012-06-14 11:02:23 +0000333% The format of the ConvertsRGBToHSB method is:
cristy3ed852e2009-09-05 21:47:34 +0000334%
cristycf12ee42012-06-14 11:02:23 +0000335% void ConvertsRGBToHSB(const double red,const double green,
cristy3094b7f2011-10-01 23:18:02 +0000336% const double blue,double *hue,double *saturation,double *brightness)
cristy3ed852e2009-09-05 21:47:34 +0000337%
338% A description of each parameter follows:
339%
340% o red, green, blue: A Quantum value representing the red, green, and
341% blue component of a pixel..
342%
343% o hue, saturation, brightness: A pointer to a double value representing a
344% component of the HSB color space.
345%
346*/
cristycf12ee42012-06-14 11:02:23 +0000347MagickPrivate void ConvertsRGBToHSB(const double red,const double green,
cristy3094b7f2011-10-01 23:18:02 +0000348 const double blue,double *hue,double *saturation,double *brightness)
cristy3ed852e2009-09-05 21:47:34 +0000349{
cristycaf45802012-06-16 18:28:54 +0000350 double
351 b,
cristy3ed852e2009-09-05 21:47:34 +0000352 delta,
cristycaf45802012-06-16 18:28:54 +0000353 g,
cristy3ed852e2009-09-05 21:47:34 +0000354 max,
cristycaf45802012-06-16 18:28:54 +0000355 min,
356 r;
cristy3ed852e2009-09-05 21:47:34 +0000357
358 /*
359 Convert RGB to HSB colorspace.
360 */
361 assert(hue != (double *) NULL);
362 assert(saturation != (double *) NULL);
363 assert(brightness != (double *) NULL);
364 *hue=0.0;
365 *saturation=0.0;
366 *brightness=0.0;
cristycaf45802012-06-16 18:28:54 +0000367 r=QuantumRange*sRGBDecompanding(QuantumScale*red);
368 g=QuantumRange*sRGBDecompanding(QuantumScale*green);
369 b=QuantumRange*sRGBDecompanding(QuantumScale*blue);
370 min=r < g ? r : g;
371 if (b < min)
372 min=b;
373 max=r > g ? r : g;
374 if (b > max)
375 max=b;
cristy3ed852e2009-09-05 21:47:34 +0000376 if (max == 0.0)
377 return;
378 delta=max-min;
cristycaf45802012-06-16 18:28:54 +0000379 *saturation=delta/max;
380 *brightness=QuantumScale*max;
cristy3ed852e2009-09-05 21:47:34 +0000381 if (delta == 0.0)
382 return;
cristycaf45802012-06-16 18:28:54 +0000383 if (r == max)
384 *hue=(g-b)/delta;
cristy3ed852e2009-09-05 21:47:34 +0000385 else
cristycaf45802012-06-16 18:28:54 +0000386 if (g == max)
387 *hue=2.0+(b-r)/delta;
cristy3ed852e2009-09-05 21:47:34 +0000388 else
cristycaf45802012-06-16 18:28:54 +0000389 *hue=4.0+(r-g)/delta;
cristy18b17442009-10-25 18:36:48 +0000390 *hue/=6.0;
cristy3ed852e2009-09-05 21:47:34 +0000391 if (*hue < 0.0)
392 *hue+=1.0;
393}
394
395/*
396%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
397% %
398% %
399% %
cristybd0e5482012-06-14 11:51:00 +0000400% C o n v e r t s R G B T o H S L %
cristy3ed852e2009-09-05 21:47:34 +0000401% %
402% %
403% %
404%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
405%
cristycf12ee42012-06-14 11:02:23 +0000406% ConvertsRGBToHSL() transforms a (red, green, blue) to a (hue, saturation,
cristy3ed852e2009-09-05 21:47:34 +0000407% lightness) triple.
408%
cristycf12ee42012-06-14 11:02:23 +0000409% The format of the ConvertsRGBToHSL method is:
cristy3ed852e2009-09-05 21:47:34 +0000410%
cristycf12ee42012-06-14 11:02:23 +0000411% void ConvertsRGBToHSL(const double red,const double green,
cristy3094b7f2011-10-01 23:18:02 +0000412% const double blue,double *hue,double *saturation,double *lightness)
cristy3ed852e2009-09-05 21:47:34 +0000413%
414% A description of each parameter follows:
415%
416% o red, green, blue: A Quantum value representing the red, green, and
417% blue component of a pixel..
418%
419% o hue, saturation, lightness: A pointer to a double value representing a
420% component of the HSL color space.
421%
422*/
423
424static inline double MagickMax(const double x,const double y)
425{
426 if (x > y)
427 return(x);
428 return(y);
429}
430
431static inline double MagickMin(const double x,const double y)
432{
433 if (x < y)
434 return(x);
435 return(y);
436}
437
cristycf12ee42012-06-14 11:02:23 +0000438MagickExport void ConvertsRGBToHSL(const double red,const double green,
cristy3094b7f2011-10-01 23:18:02 +0000439 const double blue,double *hue,double *saturation,double *lightness)
cristy3ed852e2009-09-05 21:47:34 +0000440{
cristycaf45802012-06-16 18:28:54 +0000441 double
cristy3ed852e2009-09-05 21:47:34 +0000442 b,
443 delta,
444 g,
445 max,
446 min,
447 r;
448
449 /*
450 Convert RGB to HSL colorspace.
451 */
452 assert(hue != (double *) NULL);
453 assert(saturation != (double *) NULL);
454 assert(lightness != (double *) NULL);
cristycaf45802012-06-16 18:28:54 +0000455 r=sRGBDecompanding(QuantumScale*red);
456 g=sRGBDecompanding(QuantumScale*green);
457 b=sRGBDecompanding(QuantumScale*blue);
cristy3ed852e2009-09-05 21:47:34 +0000458 max=MagickMax(r,MagickMax(g,b));
459 min=MagickMin(r,MagickMin(g,b));
460 *lightness=(double) ((min+max)/2.0);
461 delta=max-min;
462 if (delta == 0.0)
463 {
464 *hue=0.0;
465 *saturation=0.0;
466 return;
467 }
468 if (*lightness < 0.5)
469 *saturation=(double) (delta/(min+max));
470 else
471 *saturation=(double) (delta/(2.0-max-min));
472 if (r == max)
473 *hue=((((max-b)/6.0)+(delta/2.0))-(((max-g)/6.0)+(delta/2.0)))/delta;
474 else
475 if (g == max)
476 *hue=(1.0/3.0)+((((max-r)/6.0)+(delta/2.0))-(((max-b)/6.0)+(delta/2.0)))/
477 delta;
478 else
479 if (b == max)
480 *hue=(2.0/3.0)+((((max-g)/6.0)+(delta/2.0))-(((max-r)/6.0)+
481 (delta/2.0)))/delta;
482 if (*hue < 0.0)
483 *hue+=1.0;
484 if (*hue > 1.0)
485 *hue-=1.0;
486}
487
488/*
489%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
490% %
491% %
492% %
cristybd0e5482012-06-14 11:51:00 +0000493% C o n v e r t s R G B T o H W B %
cristy3ed852e2009-09-05 21:47:34 +0000494% %
495% %
496% %
497%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
498%
cristycf12ee42012-06-14 11:02:23 +0000499% ConvertsRGBToHWB() transforms a (red, green, blue) to a (hue, whiteness,
cristy3ed852e2009-09-05 21:47:34 +0000500% blackness) triple.
501%
cristycf12ee42012-06-14 11:02:23 +0000502% The format of the ConvertsRGBToHWB method is:
cristy3ed852e2009-09-05 21:47:34 +0000503%
cristycf12ee42012-06-14 11:02:23 +0000504% void ConvertsRGBToHWB(const double red,const double green,
cristy3094b7f2011-10-01 23:18:02 +0000505% const double blue,double *hue,double *whiteness,double *blackness)
cristy3ed852e2009-09-05 21:47:34 +0000506%
507% A description of each parameter follows:
508%
509% o red, green, blue: A Quantum value representing the red, green, and
510% blue component of a pixel.
511%
512% o hue, whiteness, blackness: A pointer to a double value representing a
513% component of the HWB color space.
514%
515*/
cristycf12ee42012-06-14 11:02:23 +0000516MagickPrivate void ConvertsRGBToHWB(const double red,const double green,
cristy3094b7f2011-10-01 23:18:02 +0000517 const double blue,double *hue,double *whiteness,double *blackness)
cristy3ed852e2009-09-05 21:47:34 +0000518{
cristycaf45802012-06-16 18:28:54 +0000519 double
520 b,
cristy3ed852e2009-09-05 21:47:34 +0000521 f,
cristycaf45802012-06-16 18:28:54 +0000522 g,
523 p,
524 r,
cristy3ed852e2009-09-05 21:47:34 +0000525 v,
526 w;
527
cristy3ed852e2009-09-05 21:47:34 +0000528 /*
529 Convert RGB to HWB colorspace.
530 */
531 assert(hue != (double *) NULL);
532 assert(whiteness != (double *) NULL);
533 assert(blackness != (double *) NULL);
cristycaf45802012-06-16 18:28:54 +0000534 r=QuantumRange*sRGBDecompanding(QuantumScale*red);
535 g=QuantumRange*sRGBDecompanding(QuantumScale*green);
536 b=QuantumRange*sRGBDecompanding(QuantumScale*blue);
537 w=MagickMin(r,MagickMin(g,b));
538 v=MagickMax(r,MagickMax(g,b));
cristy3ed852e2009-09-05 21:47:34 +0000539 *blackness=1.0-QuantumScale*v;
540 *whiteness=QuantumScale*w;
541 if (v == w)
542 {
cristyaf10b112012-04-18 13:25:37 +0000543 *hue=(-1.0);
cristy3ed852e2009-09-05 21:47:34 +0000544 return;
545 }
cristycaf45802012-06-16 18:28:54 +0000546 f=(r == w) ? g-b : ((g == w) ? b-r : r-g);
547 p=(r == w) ? 3.0 : ((g == w) ? 5.0 : 1.0);
548 *hue=(p-f/(v-1.0*w))/6.0;
cristy3ed852e2009-09-05 21:47:34 +0000549}
550
551/*
552%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
553% %
554% %
555% %
556% E x p a n d A f f i n e %
557% %
558% %
559% %
560%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
561%
562% ExpandAffine() computes the affine's expansion factor, i.e. the square root
563% of the factor by which the affine transform affects area. In an affine
564% transform composed of scaling, rotation, shearing, and translation, returns
565% the amount of scaling.
566%
567% The format of the ExpandAffine method is:
568%
569% double ExpandAffine(const AffineMatrix *affine)
570%
571% A description of each parameter follows:
572%
573% o expansion: Method ExpandAffine returns the affine's expansion factor.
574%
575% o affine: A pointer the affine transform of type AffineMatrix.
576%
577*/
578MagickExport double ExpandAffine(const AffineMatrix *affine)
579{
580 assert(affine != (const AffineMatrix *) NULL);
581 return(sqrt(fabs(affine->sx*affine->sy-affine->rx*affine->ry)));
582}
583
584/*
585%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
586% %
587% %
588% %
589% G e n e r a t e D i f f e r e n t i a l N o i s e %
590% %
591% %
592% %
593%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
594%
cristy82b15832009-10-06 19:17:37 +0000595% GenerateDifferentialNoise() generates differentual noise.
cristy3ed852e2009-09-05 21:47:34 +0000596%
597% The format of the GenerateDifferentialNoise method is:
598%
599% double GenerateDifferentialNoise(RandomInfo *random_info,
cristy9ed1f812011-10-08 02:00:08 +0000600% const Quantum pixel,const NoiseType noise_type,const double attenuate)
cristy3ed852e2009-09-05 21:47:34 +0000601%
602% A description of each parameter follows:
603%
604% o random_info: the random info.
605%
606% o pixel: noise is relative to this pixel value.
607%
608% o noise_type: the type of noise.
609%
610% o attenuate: attenuate the noise.
611%
612*/
cristy8ea81222011-09-04 10:33:32 +0000613MagickPrivate double GenerateDifferentialNoise(RandomInfo *random_info,
cristy9ed1f812011-10-08 02:00:08 +0000614 const Quantum pixel,const NoiseType noise_type,const double attenuate)
cristy3ed852e2009-09-05 21:47:34 +0000615{
cristy7118edf2011-10-08 13:33:25 +0000616#define SigmaUniform (attenuate*0.015625)
617#define SigmaGaussian (attenuate*0.015625)
618#define SigmaImpulse (attenuate*0.1)
619#define SigmaLaplacian (attenuate*0.0390625)
620#define SigmaMultiplicativeGaussian (attenuate*0.5)
cristy4ce9df62011-10-12 12:06:02 +0000621#define SigmaPoisson (attenuate*12.5)
cristy7118edf2011-10-08 13:33:25 +0000622#define TauGaussian (attenuate*0.078125)
cristy3ed852e2009-09-05 21:47:34 +0000623
cristyadb41ca2009-10-22 15:02:28 +0000624 double
cristy3ed852e2009-09-05 21:47:34 +0000625 alpha,
626 beta,
627 noise,
628 sigma;
629
630 alpha=GetPseudoRandomValue(random_info);
cristy3ed852e2009-09-05 21:47:34 +0000631 switch (noise_type)
632 {
633 case UniformNoise:
634 default:
635 {
cristy6bbabe62011-10-09 13:54:18 +0000636 noise=(double) (pixel+QuantumRange*SigmaUniform*(alpha-0.5));
cristy3ed852e2009-09-05 21:47:34 +0000637 break;
638 }
639 case GaussianNoise:
640 {
cristyadb41ca2009-10-22 15:02:28 +0000641 double
cristy62faa602010-02-20 03:36:17 +0000642 gamma,
cristy3ed852e2009-09-05 21:47:34 +0000643 tau;
644
cristyadb41ca2009-10-22 15:02:28 +0000645 if (alpha == 0.0)
646 alpha=1.0;
cristy3ed852e2009-09-05 21:47:34 +0000647 beta=GetPseudoRandomValue(random_info);
cristy62faa602010-02-20 03:36:17 +0000648 gamma=sqrt(-2.0*log(alpha));
cristy55a91cd2010-12-01 00:57:40 +0000649 sigma=gamma*cos((double) (2.0*MagickPI*beta));
650 tau=gamma*sin((double) (2.0*MagickPI*beta));
cristy6bbabe62011-10-09 13:54:18 +0000651 noise=(double) (pixel+sqrt((double) pixel)*SigmaGaussian*sigma+
652 QuantumRange*TauGaussian*tau);
cristy3ed852e2009-09-05 21:47:34 +0000653 break;
654 }
655 case ImpulseNoise:
656 {
657 if (alpha < (SigmaImpulse/2.0))
658 noise=0.0;
cristy37c24072011-10-08 01:26:00 +0000659 else
660 if (alpha >= (1.0-(SigmaImpulse/2.0)))
661 noise=(double) QuantumRange;
662 else
663 noise=(double) pixel;
cristy3ed852e2009-09-05 21:47:34 +0000664 break;
665 }
666 case LaplacianNoise:
667 {
668 if (alpha <= 0.5)
669 {
cristy7118edf2011-10-08 13:33:25 +0000670 if (alpha <= MagickEpsilon)
cristy6bbabe62011-10-09 13:54:18 +0000671 noise=(double) (pixel-QuantumRange);
cristy3ed852e2009-09-05 21:47:34 +0000672 else
cristy6bbabe62011-10-09 13:54:18 +0000673 noise=(double) (pixel+QuantumRange*SigmaLaplacian*
674 log(2.0*alpha)+0.5);
cristy3ed852e2009-09-05 21:47:34 +0000675 break;
676 }
677 beta=1.0-alpha;
cristy7118edf2011-10-08 13:33:25 +0000678 if (beta <= (0.5*MagickEpsilon))
cristyadb41ca2009-10-22 15:02:28 +0000679 noise=(double) (pixel+QuantumRange);
cristy3ed852e2009-09-05 21:47:34 +0000680 else
cristy6bbabe62011-10-09 13:54:18 +0000681 noise=(double) (pixel-QuantumRange*SigmaLaplacian*log(2.0*beta)+0.5);
cristy7118edf2011-10-08 13:33:25 +0000682 break;
683 }
684 case MultiplicativeGaussianNoise:
685 {
686 sigma=1.0;
687 if (alpha > MagickEpsilon)
688 sigma=sqrt(-2.0*log(alpha));
689 beta=GetPseudoRandomValue(random_info);
cristy6bbabe62011-10-09 13:54:18 +0000690 noise=(double) (pixel+pixel*SigmaMultiplicativeGaussian*sigma*
691 cos((double) (2.0*MagickPI*beta))/2.0);
cristy3ed852e2009-09-05 21:47:34 +0000692 break;
693 }
694 case PoissonNoise:
695 {
cristyadb41ca2009-10-22 15:02:28 +0000696 double
cristy3ed852e2009-09-05 21:47:34 +0000697 poisson;
698
cristybb503372010-05-27 20:51:26 +0000699 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000700 i;
701
cristy7118edf2011-10-08 13:33:25 +0000702 poisson=exp(-SigmaPoisson*QuantumScale*pixel);
cristy3ed852e2009-09-05 21:47:34 +0000703 for (i=0; alpha > poisson; i++)
704 {
705 beta=GetPseudoRandomValue(random_info);
706 alpha*=beta;
707 }
cristy6bbabe62011-10-09 13:54:18 +0000708 noise=(double) (QuantumRange*i/SigmaPoisson);
cristy3ed852e2009-09-05 21:47:34 +0000709 break;
710 }
711 case RandomNoise:
712 {
cristy6bbabe62011-10-09 13:54:18 +0000713 noise=(double) (QuantumRange*alpha);
cristy3ed852e2009-09-05 21:47:34 +0000714 break;
715 }
716 }
717 return(noise);
718}
719
720/*
721%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
722% %
723% %
724% %
725% G e t O p t i m a l K e r n e l W i d t h %
726% %
727% %
728% %
729%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
730%
731% GetOptimalKernelWidth() computes the optimal kernel radius for a convolution
732% filter. Start with the minimum value of 3 pixels and walk out until we drop
733% below the threshold of one pixel numerical accuracy.
734%
735% The format of the GetOptimalKernelWidth method is:
736%
cristybb503372010-05-27 20:51:26 +0000737% size_t GetOptimalKernelWidth(const double radius,
cristy3ed852e2009-09-05 21:47:34 +0000738% const double sigma)
739%
740% A description of each parameter follows:
741%
742% o width: Method GetOptimalKernelWidth returns the optimal width of
743% a convolution kernel.
744%
745% o radius: the radius of the Gaussian, in pixels, not counting the center
746% pixel.
747%
748% o sigma: the standard deviation of the Gaussian, in pixels.
749%
750*/
cristy8ea81222011-09-04 10:33:32 +0000751MagickPrivate size_t GetOptimalKernelWidth1D(const double radius,
cristye96405a2010-05-19 02:24:31 +0000752 const double sigma)
cristy3ed852e2009-09-05 21:47:34 +0000753{
cristye96405a2010-05-19 02:24:31 +0000754 double
755 alpha,
756 beta,
757 gamma,
cristy3ed852e2009-09-05 21:47:34 +0000758 normalize,
cristye96405a2010-05-19 02:24:31 +0000759 value;
cristy3ed852e2009-09-05 21:47:34 +0000760
cristybb503372010-05-27 20:51:26 +0000761 register ssize_t
cristy47e00502009-12-17 19:19:57 +0000762 i;
763
cristybb503372010-05-27 20:51:26 +0000764 size_t
cristy47e00502009-12-17 19:19:57 +0000765 width;
cristy3ed852e2009-09-05 21:47:34 +0000766
cristy9d314ff2011-03-09 01:30:28 +0000767 ssize_t
768 j;
769
cristy3ed852e2009-09-05 21:47:34 +0000770 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristy47e00502009-12-17 19:19:57 +0000771 if (radius > MagickEpsilon)
cristybb503372010-05-27 20:51:26 +0000772 return((size_t) (2.0*ceil(radius)+1.0));
cristye96405a2010-05-19 02:24:31 +0000773 gamma=fabs(sigma);
774 if (gamma <= MagickEpsilon)
anthonyc1061722010-05-14 06:23:49 +0000775 return(3UL);
cristy35f15302012-06-07 14:59:02 +0000776 alpha=MagickEpsilonReciprocal(2.0*gamma*gamma);
cristycaf45802012-06-16 18:28:54 +0000777 beta=(double) MagickEpsilonReciprocal((MagickRealType) MagickSQ2PI*gamma);
cristy3ed852e2009-09-05 21:47:34 +0000778 for (width=5; ; )
779 {
780 normalize=0.0;
cristybb503372010-05-27 20:51:26 +0000781 j=(ssize_t) width/2;
cristy47e00502009-12-17 19:19:57 +0000782 for (i=(-j); i <= j; i++)
cristye96405a2010-05-19 02:24:31 +0000783 normalize+=exp(-((double) (i*i))*alpha)*beta;
784 value=exp(-((double) (j*j))*alpha)*beta/normalize;
cristy20908da2009-12-02 14:34:11 +0000785 if ((value < QuantumScale) || (value < MagickEpsilon))
cristy3ed852e2009-09-05 21:47:34 +0000786 break;
787 width+=2;
788 }
cristybb503372010-05-27 20:51:26 +0000789 return((size_t) (width-2));
cristy3ed852e2009-09-05 21:47:34 +0000790}
791
cristy8ea81222011-09-04 10:33:32 +0000792MagickPrivate size_t GetOptimalKernelWidth2D(const double radius,
cristye96405a2010-05-19 02:24:31 +0000793 const double sigma)
cristy3ed852e2009-09-05 21:47:34 +0000794{
cristy47e00502009-12-17 19:19:57 +0000795 double
cristye96405a2010-05-19 02:24:31 +0000796 alpha,
797 beta,
798 gamma,
cristy3ed852e2009-09-05 21:47:34 +0000799 normalize,
cristye96405a2010-05-19 02:24:31 +0000800 value;
cristy3ed852e2009-09-05 21:47:34 +0000801
cristy9d314ff2011-03-09 01:30:28 +0000802 size_t
803 width;
804
cristybb503372010-05-27 20:51:26 +0000805 ssize_t
cristy47e00502009-12-17 19:19:57 +0000806 j,
cristy3ed852e2009-09-05 21:47:34 +0000807 u,
808 v;
809
810 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristy47e00502009-12-17 19:19:57 +0000811 if (radius > MagickEpsilon)
cristybb503372010-05-27 20:51:26 +0000812 return((size_t) (2.0*ceil(radius)+1.0));
cristye96405a2010-05-19 02:24:31 +0000813 gamma=fabs(sigma);
814 if (gamma <= MagickEpsilon)
anthonyc1061722010-05-14 06:23:49 +0000815 return(3UL);
cristy35f15302012-06-07 14:59:02 +0000816 alpha=MagickEpsilonReciprocal(2.0*gamma*gamma);
cristycaf45802012-06-16 18:28:54 +0000817 beta=(double) MagickEpsilonReciprocal((MagickRealType) Magick2PI*gamma*gamma);
cristy3ed852e2009-09-05 21:47:34 +0000818 for (width=5; ; )
819 {
820 normalize=0.0;
cristybb503372010-05-27 20:51:26 +0000821 j=(ssize_t) width/2;
cristy47e00502009-12-17 19:19:57 +0000822 for (v=(-j); v <= j; v++)
cristy47e00502009-12-17 19:19:57 +0000823 for (u=(-j); u <= j; u++)
cristye96405a2010-05-19 02:24:31 +0000824 normalize+=exp(-((double) (u*u+v*v))*alpha)*beta;
825 value=exp(-((double) (j*j))*alpha)*beta/normalize;
cristy20908da2009-12-02 14:34:11 +0000826 if ((value < QuantumScale) || (value < MagickEpsilon))
cristy3ed852e2009-09-05 21:47:34 +0000827 break;
828 width+=2;
829 }
cristybb503372010-05-27 20:51:26 +0000830 return((size_t) (width-2));
cristy3ed852e2009-09-05 21:47:34 +0000831}
832
cristy8ea81222011-09-04 10:33:32 +0000833MagickPrivate size_t GetOptimalKernelWidth(const double radius,
cristy3ed852e2009-09-05 21:47:34 +0000834 const double sigma)
835{
836 return(GetOptimalKernelWidth1D(radius,sigma));
837}