blob: 8d0e258701d0c355e6eb0299dad20093c3ee08a4 [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% %
cristy722fc0c2012-08-04 23:15:43 +000066% C o n v e r t H C L T o R G B %
67% %
68% %
69% %
70%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
71%
72% ConvertHCLToRGB() transforms a (hue, chroma, luma) to a (red, green,
73% blue) triple.
74%
75% The format of the ConvertHCLToRGBImage method is:
76%
77% void ConvertHCLToRGB(const double hue,const double chroma,
78% const double luma,double *red,double *green,double *blue)
79%
80% A description of each parameter follows:
81%
82% o hue, chroma, luma: A double value representing a
83% component of the HCL color space.
84%
85% o red, green, blue: A pointer to a pixel component of type Quantum.
86%
87*/
88MagickPrivate void ConvertHCLToRGB(const double hue,const double chroma,
89 const double luma,double *red,double *green,double *blue)
90{
91 double
92 b,
93 c,
94 g,
95 h,
96 m,
97 r,
98 x;
99
100 /*
101 Convert HCL to RGB colorspace.
102 */
103 assert(red != (double *) NULL);
104 assert(green != (double *) NULL);
105 assert(blue != (double *) NULL);
106 h=6.0*hue;
107 c=chroma;
108 x=c*(1.0-fabs(fmod(h,2.0)-1.0));
109 r=0.0;
110 g=0.0;
111 b=0.0;
112 if ((0.0 <= h) && (h < 1.0))
113 {
114 r=c;
115 g=x;
116 }
117 else
118 if ((1.0 <= h) && (h < 2.0))
119 {
120 r=x;
121 g=c;
122 }
123 else
124 if ((2.0 <= h) && (h < 3.0))
125 {
126 g=c;
127 b=x;
128 }
129 else
130 if ((3.0 <= h) && (h < 4.0))
131 {
132 g=x;
133 b=c;
134 }
135 else
136 if ((4.0 <= h) && (h < 5.0))
137 {
138 r=x;
139 b=c;
140 }
141 else
142 if ((5.0 <= h) && (h < 6.0))
143 {
144 r=c;
145 b=x;
146 }
147 m=luma-0.298839*r+0.586811*g+0.114350*b;
148 *red=QuantumRange*(r+m);
149 *green=QuantumRange*(g+m);
150 *blue=QuantumRange*(b+m);
151}
152
153/*
154%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
155% %
156% %
157% %
cristy0a39a5c2012-06-27 12:51:45 +0000158% C o n v e r t H S B T o R G B %
cristy3ed852e2009-09-05 21:47:34 +0000159% %
160% %
161% %
162%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
163%
cristy0a39a5c2012-06-27 12:51:45 +0000164% ConvertHSBToRGB() transforms a (hue, saturation, brightness) to a (red,
cristy3ed852e2009-09-05 21:47:34 +0000165% green, blue) triple.
166%
cristy0a39a5c2012-06-27 12:51:45 +0000167% The format of the ConvertHSBToRGBImage method is:
cristy3ed852e2009-09-05 21:47:34 +0000168%
cristy0a39a5c2012-06-27 12:51:45 +0000169% void ConvertHSBToRGB(const double hue,const double saturation,
cristy3094b7f2011-10-01 23:18:02 +0000170% const double brightness,double *red,double *green,double *blue)
cristy3ed852e2009-09-05 21:47:34 +0000171%
172% A description of each parameter follows:
173%
174% o hue, saturation, brightness: A double value representing a
175% component of the HSB color space.
176%
177% o red, green, blue: A pointer to a pixel component of type Quantum.
178%
179*/
cristy0a39a5c2012-06-27 12:51:45 +0000180MagickPrivate void ConvertHSBToRGB(const double hue,const double saturation,
cristy3094b7f2011-10-01 23:18:02 +0000181 const double brightness,double *red,double *green,double *blue)
cristy3ed852e2009-09-05 21:47:34 +0000182{
cristycaf45802012-06-16 18:28:54 +0000183 double
cristy3ed852e2009-09-05 21:47:34 +0000184 f,
185 h,
186 p,
187 q,
188 t;
189
190 /*
191 Convert HSB to RGB colorspace.
192 */
cristy3094b7f2011-10-01 23:18:02 +0000193 assert(red != (double *) NULL);
194 assert(green != (double *) NULL);
195 assert(blue != (double *) NULL);
cristy98a65d52010-04-14 02:12:38 +0000196 if (saturation == 0.0)
cristy3ed852e2009-09-05 21:47:34 +0000197 {
cristycaf45802012-06-16 18:28:54 +0000198 *red=QuantumRange*brightness;
cristy3ed852e2009-09-05 21:47:34 +0000199 *green=(*red);
200 *blue=(*red);
201 return;
202 }
cristy98a65d52010-04-14 02:12:38 +0000203 h=6.0*(hue-floor(hue));
204 f=h-floor((double) h);
205 p=brightness*(1.0-saturation);
206 q=brightness*(1.0-saturation*f);
207 t=brightness*(1.0-(saturation*(1.0-f)));
cristy3ed852e2009-09-05 21:47:34 +0000208 switch ((int) h)
209 {
210 case 0:
211 default:
212 {
cristy0a39a5c2012-06-27 12:51:45 +0000213 *red=QuantumRange*brightness;
214 *green=QuantumRange*t;
215 *blue=QuantumRange*p;
cristy3ed852e2009-09-05 21:47:34 +0000216 break;
217 }
218 case 1:
219 {
cristy0a39a5c2012-06-27 12:51:45 +0000220 *red=QuantumRange*q;
221 *green=QuantumRange*brightness;
222 *blue=QuantumRange*p;
cristy3ed852e2009-09-05 21:47:34 +0000223 break;
224 }
225 case 2:
226 {
cristy0a39a5c2012-06-27 12:51:45 +0000227 *red=QuantumRange*p;
228 *green=QuantumRange*brightness;
229 *blue=QuantumRange*t;
cristy3ed852e2009-09-05 21:47:34 +0000230 break;
231 }
232 case 3:
233 {
cristy0a39a5c2012-06-27 12:51:45 +0000234 *red=QuantumRange*p;
235 *green=QuantumRange*q;
236 *blue=QuantumRange*brightness;
cristy3ed852e2009-09-05 21:47:34 +0000237 break;
238 }
239 case 4:
240 {
cristy0a39a5c2012-06-27 12:51:45 +0000241 *red=QuantumRange*t;
242 *green=QuantumRange*p;
243 *blue=QuantumRange*brightness;
cristy3ed852e2009-09-05 21:47:34 +0000244 break;
245 }
246 case 5:
247 {
cristy0a39a5c2012-06-27 12:51:45 +0000248 *red=QuantumRange*brightness;
249 *green=QuantumRange*p;
250 *blue=QuantumRange*q;
cristy3ed852e2009-09-05 21:47:34 +0000251 break;
252 }
253 }
254}
255
256/*
257%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
258% %
259% %
260% %
cristy0a39a5c2012-06-27 12:51:45 +0000261% C o n v e r t H S L T o R G B %
cristy3ed852e2009-09-05 21:47:34 +0000262% %
263% %
264% %
265%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
266%
cristy0a39a5c2012-06-27 12:51:45 +0000267% ConvertHSLToRGB() transforms a (hue, saturation, lightness) to a (red,
cristy3ed852e2009-09-05 21:47:34 +0000268% green, blue) triple.
269%
cristy0a39a5c2012-06-27 12:51:45 +0000270% The format of the ConvertHSLToRGBImage method is:
cristy3ed852e2009-09-05 21:47:34 +0000271%
cristy0a39a5c2012-06-27 12:51:45 +0000272% void ConvertHSLToRGB(const double hue,const double saturation,
cristy3094b7f2011-10-01 23:18:02 +0000273% const double lightness,double *red,double *green,double *blue)
cristy3ed852e2009-09-05 21:47:34 +0000274%
275% A description of each parameter follows:
276%
277% o hue, saturation, lightness: A double value representing a
278% component of the HSL color space.
279%
280% o red, green, blue: A pointer to a pixel component of type Quantum.
281%
282*/
283
cristy0a39a5c2012-06-27 12:51:45 +0000284static inline double ConvertHueToRGB(double m1,double m2,double hue)
cristy3ed852e2009-09-05 21:47:34 +0000285{
286 if (hue < 0.0)
287 hue+=1.0;
288 if (hue > 1.0)
289 hue-=1.0;
290 if ((6.0*hue) < 1.0)
cristya163b0c2010-04-13 01:04:49 +0000291 return(m1+6.0*(m2-m1)*hue);
292 if ((2.0*hue) < 1.0)
293 return(m2);
294 if ((3.0*hue) < 2.0)
295 return(m1+6.0*(m2-m1)*(2.0/3.0-hue));
296 return(m1);
cristy3ed852e2009-09-05 21:47:34 +0000297}
298
cristy0a39a5c2012-06-27 12:51:45 +0000299MagickExport void ConvertHSLToRGB(const double hue,const double saturation,
cristy3094b7f2011-10-01 23:18:02 +0000300 const double lightness,double *red,double *green,double *blue)
cristy3ed852e2009-09-05 21:47:34 +0000301{
cristycaf45802012-06-16 18:28:54 +0000302 double
cristy3ed852e2009-09-05 21:47:34 +0000303 b,
304 g,
305 r,
306 m1,
307 m2;
308
309 /*
310 Convert HSL to RGB colorspace.
311 */
cristy3094b7f2011-10-01 23:18:02 +0000312 assert(red != (double *) NULL);
313 assert(green != (double *) NULL);
314 assert(blue != (double *) NULL);
cristy98a65d52010-04-14 02:12:38 +0000315 if (saturation == 0)
cristy3ed852e2009-09-05 21:47:34 +0000316 {
cristycaf45802012-06-16 18:28:54 +0000317 *red=QuantumRange*lightness;
cristy3ed852e2009-09-05 21:47:34 +0000318 *green=(*red);
319 *blue=(*red);
320 return;
321 }
cristy27cabb82010-04-14 11:33:57 +0000322 if (lightness < 0.5)
cristy98a65d52010-04-14 02:12:38 +0000323 m2=lightness*(saturation+1.0);
324 else
325 m2=(lightness+saturation)-(lightness*saturation);
326 m1=2.0*lightness-m2;
cristy0a39a5c2012-06-27 12:51:45 +0000327 r=ConvertHueToRGB(m1,m2,hue+1.0/3.0);
328 g=ConvertHueToRGB(m1,m2,hue);
329 b=ConvertHueToRGB(m1,m2,hue-1.0/3.0);
330 *red=QuantumRange*r;
331 *green=QuantumRange*g;
332 *blue=QuantumRange*b;
cristy3ed852e2009-09-05 21:47:34 +0000333}
334
335/*
336%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
337% %
338% %
339% %
cristy0a39a5c2012-06-27 12:51:45 +0000340% C o n v e r t H W B T o R G B %
cristy3ed852e2009-09-05 21:47:34 +0000341% %
342% %
343% %
344%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
345%
cristy0a39a5c2012-06-27 12:51:45 +0000346% ConvertHWBToRGB() transforms a (hue, whiteness, blackness) to a (red, green,
cristy3ed852e2009-09-05 21:47:34 +0000347% blue) triple.
348%
cristy0a39a5c2012-06-27 12:51:45 +0000349% The format of the ConvertHWBToRGBImage method is:
cristy3ed852e2009-09-05 21:47:34 +0000350%
cristy0a39a5c2012-06-27 12:51:45 +0000351% void ConvertHWBToRGB(const double hue,const double whiteness,
cristy3094b7f2011-10-01 23:18:02 +0000352% const double blackness,double *red,double *green,double *blue)
cristy3ed852e2009-09-05 21:47:34 +0000353%
354% A description of each parameter follows:
355%
356% o hue, whiteness, blackness: A double value representing a
357% component of the HWB color space.
358%
359% o red, green, blue: A pointer to a pixel component of type Quantum.
360%
361*/
cristy0a39a5c2012-06-27 12:51:45 +0000362MagickPrivate void ConvertHWBToRGB(const double hue,const double whiteness,
cristy3094b7f2011-10-01 23:18:02 +0000363 const double blackness,double *red,double *green,double *blue)
cristy3ed852e2009-09-05 21:47:34 +0000364{
cristycaf45802012-06-16 18:28:54 +0000365 double
cristy3ed852e2009-09-05 21:47:34 +0000366 b,
367 f,
368 g,
369 n,
370 r,
371 v;
372
cristybb503372010-05-27 20:51:26 +0000373 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000374 i;
375
376 /*
377 Convert HWB to RGB colorspace.
378 */
cristy3094b7f2011-10-01 23:18:02 +0000379 assert(red != (double *) NULL);
380 assert(green != (double *) NULL);
381 assert(blue != (double *) NULL);
cristy3ed852e2009-09-05 21:47:34 +0000382 v=1.0-blackness;
cristyaf10b112012-04-18 13:25:37 +0000383 if (hue == -1.0)
cristy3ed852e2009-09-05 21:47:34 +0000384 {
cristy0a39a5c2012-06-27 12:51:45 +0000385 *red=QuantumRange*v;
386 *green=QuantumRange*v;
387 *blue=QuantumRange*v;
cristy3ed852e2009-09-05 21:47:34 +0000388 return;
389 }
cristybb503372010-05-27 20:51:26 +0000390 i=(ssize_t) floor(6.0*hue);
cristy3ed852e2009-09-05 21:47:34 +0000391 f=6.0*hue-i;
392 if ((i & 0x01) != 0)
393 f=1.0-f;
394 n=whiteness+f*(v-whiteness); /* linear interpolation */
395 switch (i)
396 {
397 default:
398 case 6:
399 case 0: r=v; g=n; b=whiteness; break;
400 case 1: r=n; g=v; b=whiteness; break;
401 case 2: r=whiteness; g=v; b=n; break;
402 case 3: r=whiteness; g=n; b=v; break;
403 case 4: r=n; g=whiteness; b=v; break;
404 case 5: r=v; g=whiteness; b=n; break;
405 }
cristy0a39a5c2012-06-27 12:51:45 +0000406 *red=QuantumRange*r;
407 *green=QuantumRange*g;
408 *blue=QuantumRange*b;
cristy3ed852e2009-09-05 21:47:34 +0000409}
410
411/*
412%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
413% %
414% %
415% %
cristy722fc0c2012-08-04 23:15:43 +0000416% C o n v e r t R G B T o H C L %
417% %
418% %
419% %
420%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
421%
422% ConvertRGBToHCL() transforms a (red, green, blue) to a (hue, chroma,
423% luma) triple.
424%
425% The format of the ConvertRGBToHCL method is:
426%
427% void ConvertRGBToHCL(const double red,const double green,
428% const double blue,double *hue,double *chroma,double *luma)
429%
430% A description of each parameter follows:
431%
432% o red, green, blue: A Quantum value representing the red, green, and
433% blue component of a pixel.
434%
435% o hue, chroma, luma: A pointer to a double value representing a
436% component of the HCL color space.
437%
438*/
439MagickPrivate void ConvertRGBToHCL(const double red,const double green,
440 const double blue,double *hue,double *chroma,double *luma)
441{
442 double
443 b,
444 c,
445 g,
446 h,
447 max,
448 r;
449
450 /*
451 Convert RGB to HCL colorspace.
452 */
453 assert(hue != (double *) NULL);
454 assert(chroma != (double *) NULL);
455 assert(luma != (double *) NULL);
456 r=red;
457 g=green;
458 b=blue;
459 max=MagickMax(r,MagickMax(g,b));
460 c=max-(double) MagickMin(r,MagickMin(g,b));
461 h=0.0;
462 if (c == 0)
463 h=0.0;
464 else
465 if (red == max)
466 h=fmod((g-b)/c,6.0);
467 else
468 if (green == max)
469 h=((b-r)/c)+2.0;
470 else
471 if (blue == max)
472 h=((r-g)/c)+4.0;
473 *hue=(h/6.0);
474 *chroma=QuantumScale*c;
475 *luma=QuantumScale*(0.298839*r+0.586811*g+0.114350*b);
476}
477
478/*
479%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
480% %
481% %
482% %
cristy0a39a5c2012-06-27 12:51:45 +0000483% C o n v e r t R G B T o H S B %
cristy3ed852e2009-09-05 21:47:34 +0000484% %
485% %
486% %
487%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
488%
cristy0a39a5c2012-06-27 12:51:45 +0000489% ConvertRGBToHSB() transforms a (red, green, blue) to a (hue, saturation,
cristy3ed852e2009-09-05 21:47:34 +0000490% brightness) triple.
491%
cristy0a39a5c2012-06-27 12:51:45 +0000492% The format of the ConvertRGBToHSB method is:
cristy3ed852e2009-09-05 21:47:34 +0000493%
cristy0a39a5c2012-06-27 12:51:45 +0000494% void ConvertRGBToHSB(const double red,const double green,
cristy3094b7f2011-10-01 23:18:02 +0000495% const double blue,double *hue,double *saturation,double *brightness)
cristy3ed852e2009-09-05 21:47:34 +0000496%
497% A description of each parameter follows:
498%
499% o red, green, blue: A Quantum value representing the red, green, and
500% blue component of a pixel..
501%
502% o hue, saturation, brightness: A pointer to a double value representing a
503% component of the HSB color space.
504%
505*/
cristy0a39a5c2012-06-27 12:51:45 +0000506MagickPrivate void ConvertRGBToHSB(const double red,const double green,
cristy3094b7f2011-10-01 23:18:02 +0000507 const double blue,double *hue,double *saturation,double *brightness)
cristy3ed852e2009-09-05 21:47:34 +0000508{
cristycaf45802012-06-16 18:28:54 +0000509 double
510 b,
cristy3ed852e2009-09-05 21:47:34 +0000511 delta,
cristycaf45802012-06-16 18:28:54 +0000512 g,
cristy3ed852e2009-09-05 21:47:34 +0000513 max,
cristycaf45802012-06-16 18:28:54 +0000514 min,
515 r;
cristy3ed852e2009-09-05 21:47:34 +0000516
517 /*
518 Convert RGB to HSB colorspace.
519 */
520 assert(hue != (double *) NULL);
521 assert(saturation != (double *) NULL);
522 assert(brightness != (double *) NULL);
523 *hue=0.0;
524 *saturation=0.0;
525 *brightness=0.0;
cristy0a39a5c2012-06-27 12:51:45 +0000526 r=red;
527 g=green;
528 b=blue;
cristycaf45802012-06-16 18:28:54 +0000529 min=r < g ? r : g;
530 if (b < min)
531 min=b;
532 max=r > g ? r : g;
533 if (b > max)
534 max=b;
cristy3ed852e2009-09-05 21:47:34 +0000535 if (max == 0.0)
536 return;
537 delta=max-min;
cristycaf45802012-06-16 18:28:54 +0000538 *saturation=delta/max;
539 *brightness=QuantumScale*max;
cristy3ed852e2009-09-05 21:47:34 +0000540 if (delta == 0.0)
541 return;
cristycaf45802012-06-16 18:28:54 +0000542 if (r == max)
543 *hue=(g-b)/delta;
cristy3ed852e2009-09-05 21:47:34 +0000544 else
cristycaf45802012-06-16 18:28:54 +0000545 if (g == max)
546 *hue=2.0+(b-r)/delta;
cristy3ed852e2009-09-05 21:47:34 +0000547 else
cristycaf45802012-06-16 18:28:54 +0000548 *hue=4.0+(r-g)/delta;
cristy18b17442009-10-25 18:36:48 +0000549 *hue/=6.0;
cristy3ed852e2009-09-05 21:47:34 +0000550 if (*hue < 0.0)
551 *hue+=1.0;
552}
553
554/*
555%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
556% %
557% %
558% %
cristy0a39a5c2012-06-27 12:51:45 +0000559% C o n v e r t R G B T o H S L %
cristy3ed852e2009-09-05 21:47:34 +0000560% %
561% %
562% %
563%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
564%
cristy0a39a5c2012-06-27 12:51:45 +0000565% ConvertRGBToHSL() transforms a (red, green, blue) to a (hue, saturation,
cristy3ed852e2009-09-05 21:47:34 +0000566% lightness) triple.
567%
cristy0a39a5c2012-06-27 12:51:45 +0000568% The format of the ConvertRGBToHSL method is:
cristy3ed852e2009-09-05 21:47:34 +0000569%
cristy0a39a5c2012-06-27 12:51:45 +0000570% void ConvertRGBToHSL(const double red,const double green,
cristy3094b7f2011-10-01 23:18:02 +0000571% const double blue,double *hue,double *saturation,double *lightness)
cristy3ed852e2009-09-05 21:47:34 +0000572%
573% A description of each parameter follows:
574%
575% o red, green, blue: A Quantum value representing the red, green, and
576% blue component of a pixel..
577%
578% o hue, saturation, lightness: A pointer to a double value representing a
579% component of the HSL color space.
580%
581*/
582
583static inline double MagickMax(const double x,const double y)
584{
585 if (x > y)
586 return(x);
587 return(y);
588}
589
590static inline double MagickMin(const double x,const double y)
591{
592 if (x < y)
593 return(x);
594 return(y);
595}
596
cristy0a39a5c2012-06-27 12:51:45 +0000597MagickExport void ConvertRGBToHSL(const double red,const double green,
cristy3094b7f2011-10-01 23:18:02 +0000598 const double blue,double *hue,double *saturation,double *lightness)
cristy3ed852e2009-09-05 21:47:34 +0000599{
cristycaf45802012-06-16 18:28:54 +0000600 double
cristy3ed852e2009-09-05 21:47:34 +0000601 b,
602 delta,
603 g,
604 max,
605 min,
606 r;
607
608 /*
609 Convert RGB to HSL colorspace.
610 */
611 assert(hue != (double *) NULL);
612 assert(saturation != (double *) NULL);
613 assert(lightness != (double *) NULL);
cristy0a39a5c2012-06-27 12:51:45 +0000614 r=QuantumScale*red;
615 g=QuantumScale*green;
616 b=QuantumScale*blue;
cristy3ed852e2009-09-05 21:47:34 +0000617 max=MagickMax(r,MagickMax(g,b));
618 min=MagickMin(r,MagickMin(g,b));
619 *lightness=(double) ((min+max)/2.0);
620 delta=max-min;
621 if (delta == 0.0)
622 {
623 *hue=0.0;
624 *saturation=0.0;
625 return;
626 }
627 if (*lightness < 0.5)
628 *saturation=(double) (delta/(min+max));
629 else
630 *saturation=(double) (delta/(2.0-max-min));
631 if (r == max)
632 *hue=((((max-b)/6.0)+(delta/2.0))-(((max-g)/6.0)+(delta/2.0)))/delta;
633 else
634 if (g == max)
635 *hue=(1.0/3.0)+((((max-r)/6.0)+(delta/2.0))-(((max-b)/6.0)+(delta/2.0)))/
636 delta;
637 else
638 if (b == max)
639 *hue=(2.0/3.0)+((((max-g)/6.0)+(delta/2.0))-(((max-r)/6.0)+
640 (delta/2.0)))/delta;
641 if (*hue < 0.0)
642 *hue+=1.0;
643 if (*hue > 1.0)
644 *hue-=1.0;
645}
646
647/*
648%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
649% %
650% %
651% %
cristy0a39a5c2012-06-27 12:51:45 +0000652% C o n v e r t R G B T o H W B %
cristy3ed852e2009-09-05 21:47:34 +0000653% %
654% %
655% %
656%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
657%
cristy0a39a5c2012-06-27 12:51:45 +0000658% ConvertRGBToHWB() transforms a (red, green, blue) to a (hue, whiteness,
cristy3ed852e2009-09-05 21:47:34 +0000659% blackness) triple.
660%
cristy0a39a5c2012-06-27 12:51:45 +0000661% The format of the ConvertRGBToHWB method is:
cristy3ed852e2009-09-05 21:47:34 +0000662%
cristy0a39a5c2012-06-27 12:51:45 +0000663% void ConvertRGBToHWB(const double red,const double green,
cristy3094b7f2011-10-01 23:18:02 +0000664% const double blue,double *hue,double *whiteness,double *blackness)
cristy3ed852e2009-09-05 21:47:34 +0000665%
666% A description of each parameter follows:
667%
668% o red, green, blue: A Quantum value representing the red, green, and
669% blue component of a pixel.
670%
671% o hue, whiteness, blackness: A pointer to a double value representing a
672% component of the HWB color space.
673%
674*/
cristy0a39a5c2012-06-27 12:51:45 +0000675MagickPrivate void ConvertRGBToHWB(const double red,const double green,
cristy3094b7f2011-10-01 23:18:02 +0000676 const double blue,double *hue,double *whiteness,double *blackness)
cristy3ed852e2009-09-05 21:47:34 +0000677{
cristycaf45802012-06-16 18:28:54 +0000678 double
679 b,
cristy3ed852e2009-09-05 21:47:34 +0000680 f,
cristycaf45802012-06-16 18:28:54 +0000681 g,
682 p,
683 r,
cristy3ed852e2009-09-05 21:47:34 +0000684 v,
685 w;
686
cristy3ed852e2009-09-05 21:47:34 +0000687 /*
688 Convert RGB to HWB colorspace.
689 */
690 assert(hue != (double *) NULL);
691 assert(whiteness != (double *) NULL);
692 assert(blackness != (double *) NULL);
cristy0a39a5c2012-06-27 12:51:45 +0000693 r=red;
694 g=green;
695 b=blue;
cristycaf45802012-06-16 18:28:54 +0000696 w=MagickMin(r,MagickMin(g,b));
697 v=MagickMax(r,MagickMax(g,b));
cristy3ed852e2009-09-05 21:47:34 +0000698 *blackness=1.0-QuantumScale*v;
699 *whiteness=QuantumScale*w;
700 if (v == w)
701 {
cristyaf10b112012-04-18 13:25:37 +0000702 *hue=(-1.0);
cristy3ed852e2009-09-05 21:47:34 +0000703 return;
704 }
cristycaf45802012-06-16 18:28:54 +0000705 f=(r == w) ? g-b : ((g == w) ? b-r : r-g);
706 p=(r == w) ? 3.0 : ((g == w) ? 5.0 : 1.0);
707 *hue=(p-f/(v-1.0*w))/6.0;
cristy3ed852e2009-09-05 21:47:34 +0000708}
709
710/*
711%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
712% %
713% %
714% %
715% E x p a n d A f f i n e %
716% %
717% %
718% %
719%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
720%
721% ExpandAffine() computes the affine's expansion factor, i.e. the square root
722% of the factor by which the affine transform affects area. In an affine
723% transform composed of scaling, rotation, shearing, and translation, returns
724% the amount of scaling.
725%
726% The format of the ExpandAffine method is:
727%
728% double ExpandAffine(const AffineMatrix *affine)
729%
730% A description of each parameter follows:
731%
732% o expansion: Method ExpandAffine returns the affine's expansion factor.
733%
734% o affine: A pointer the affine transform of type AffineMatrix.
735%
736*/
737MagickExport double ExpandAffine(const AffineMatrix *affine)
738{
739 assert(affine != (const AffineMatrix *) NULL);
740 return(sqrt(fabs(affine->sx*affine->sy-affine->rx*affine->ry)));
741}
742
743/*
744%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
745% %
746% %
747% %
748% 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 %
749% %
750% %
751% %
752%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
753%
cristy82b15832009-10-06 19:17:37 +0000754% GenerateDifferentialNoise() generates differentual noise.
cristy3ed852e2009-09-05 21:47:34 +0000755%
756% The format of the GenerateDifferentialNoise method is:
757%
758% double GenerateDifferentialNoise(RandomInfo *random_info,
cristy9ed1f812011-10-08 02:00:08 +0000759% const Quantum pixel,const NoiseType noise_type,const double attenuate)
cristy3ed852e2009-09-05 21:47:34 +0000760%
761% A description of each parameter follows:
762%
763% o random_info: the random info.
764%
765% o pixel: noise is relative to this pixel value.
766%
767% o noise_type: the type of noise.
768%
769% o attenuate: attenuate the noise.
770%
771*/
cristy8ea81222011-09-04 10:33:32 +0000772MagickPrivate double GenerateDifferentialNoise(RandomInfo *random_info,
cristy9ed1f812011-10-08 02:00:08 +0000773 const Quantum pixel,const NoiseType noise_type,const double attenuate)
cristy3ed852e2009-09-05 21:47:34 +0000774{
cristy7118edf2011-10-08 13:33:25 +0000775#define SigmaUniform (attenuate*0.015625)
776#define SigmaGaussian (attenuate*0.015625)
777#define SigmaImpulse (attenuate*0.1)
778#define SigmaLaplacian (attenuate*0.0390625)
779#define SigmaMultiplicativeGaussian (attenuate*0.5)
cristy4ce9df62011-10-12 12:06:02 +0000780#define SigmaPoisson (attenuate*12.5)
cristy785eb592012-07-08 22:12:15 +0000781#define SigmaRandom (attenuate)
cristy7118edf2011-10-08 13:33:25 +0000782#define TauGaussian (attenuate*0.078125)
cristy3ed852e2009-09-05 21:47:34 +0000783
cristyadb41ca2009-10-22 15:02:28 +0000784 double
cristy3ed852e2009-09-05 21:47:34 +0000785 alpha,
786 beta,
787 noise,
788 sigma;
789
790 alpha=GetPseudoRandomValue(random_info);
cristy3ed852e2009-09-05 21:47:34 +0000791 switch (noise_type)
792 {
793 case UniformNoise:
794 default:
795 {
cristy6bbabe62011-10-09 13:54:18 +0000796 noise=(double) (pixel+QuantumRange*SigmaUniform*(alpha-0.5));
cristy3ed852e2009-09-05 21:47:34 +0000797 break;
798 }
799 case GaussianNoise:
800 {
cristyadb41ca2009-10-22 15:02:28 +0000801 double
cristy62faa602010-02-20 03:36:17 +0000802 gamma,
cristy3ed852e2009-09-05 21:47:34 +0000803 tau;
804
cristyadb41ca2009-10-22 15:02:28 +0000805 if (alpha == 0.0)
806 alpha=1.0;
cristy3ed852e2009-09-05 21:47:34 +0000807 beta=GetPseudoRandomValue(random_info);
cristy62faa602010-02-20 03:36:17 +0000808 gamma=sqrt(-2.0*log(alpha));
cristy55a91cd2010-12-01 00:57:40 +0000809 sigma=gamma*cos((double) (2.0*MagickPI*beta));
810 tau=gamma*sin((double) (2.0*MagickPI*beta));
cristy6bbabe62011-10-09 13:54:18 +0000811 noise=(double) (pixel+sqrt((double) pixel)*SigmaGaussian*sigma+
812 QuantumRange*TauGaussian*tau);
cristy3ed852e2009-09-05 21:47:34 +0000813 break;
814 }
815 case ImpulseNoise:
816 {
817 if (alpha < (SigmaImpulse/2.0))
818 noise=0.0;
cristy37c24072011-10-08 01:26:00 +0000819 else
820 if (alpha >= (1.0-(SigmaImpulse/2.0)))
821 noise=(double) QuantumRange;
822 else
823 noise=(double) pixel;
cristy3ed852e2009-09-05 21:47:34 +0000824 break;
825 }
826 case LaplacianNoise:
827 {
828 if (alpha <= 0.5)
829 {
cristy7118edf2011-10-08 13:33:25 +0000830 if (alpha <= MagickEpsilon)
cristy6bbabe62011-10-09 13:54:18 +0000831 noise=(double) (pixel-QuantumRange);
cristy3ed852e2009-09-05 21:47:34 +0000832 else
cristy785eb592012-07-08 22:12:15 +0000833 noise=(double) (pixel+QuantumRange*SigmaLaplacian*log(2.0*alpha)+
834 0.5);
cristy3ed852e2009-09-05 21:47:34 +0000835 break;
836 }
837 beta=1.0-alpha;
cristy7118edf2011-10-08 13:33:25 +0000838 if (beta <= (0.5*MagickEpsilon))
cristyadb41ca2009-10-22 15:02:28 +0000839 noise=(double) (pixel+QuantumRange);
cristy3ed852e2009-09-05 21:47:34 +0000840 else
cristy6bbabe62011-10-09 13:54:18 +0000841 noise=(double) (pixel-QuantumRange*SigmaLaplacian*log(2.0*beta)+0.5);
cristy7118edf2011-10-08 13:33:25 +0000842 break;
843 }
844 case MultiplicativeGaussianNoise:
845 {
846 sigma=1.0;
847 if (alpha > MagickEpsilon)
848 sigma=sqrt(-2.0*log(alpha));
849 beta=GetPseudoRandomValue(random_info);
cristy6bbabe62011-10-09 13:54:18 +0000850 noise=(double) (pixel+pixel*SigmaMultiplicativeGaussian*sigma*
851 cos((double) (2.0*MagickPI*beta))/2.0);
cristy3ed852e2009-09-05 21:47:34 +0000852 break;
853 }
854 case PoissonNoise:
855 {
cristyadb41ca2009-10-22 15:02:28 +0000856 double
cristy3ed852e2009-09-05 21:47:34 +0000857 poisson;
858
cristybb503372010-05-27 20:51:26 +0000859 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000860 i;
861
cristy7118edf2011-10-08 13:33:25 +0000862 poisson=exp(-SigmaPoisson*QuantumScale*pixel);
cristy3ed852e2009-09-05 21:47:34 +0000863 for (i=0; alpha > poisson; i++)
864 {
865 beta=GetPseudoRandomValue(random_info);
866 alpha*=beta;
867 }
cristy6bbabe62011-10-09 13:54:18 +0000868 noise=(double) (QuantumRange*i/SigmaPoisson);
cristy3ed852e2009-09-05 21:47:34 +0000869 break;
870 }
871 case RandomNoise:
872 {
cristy785eb592012-07-08 22:12:15 +0000873 noise=(double) (QuantumRange*SigmaRandom*alpha);
cristy3ed852e2009-09-05 21:47:34 +0000874 break;
875 }
876 }
877 return(noise);
878}
879
880/*
881%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
882% %
883% %
884% %
885% G e t O p t i m a l K e r n e l W i d t h %
886% %
887% %
888% %
889%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
890%
891% GetOptimalKernelWidth() computes the optimal kernel radius for a convolution
892% filter. Start with the minimum value of 3 pixels and walk out until we drop
893% below the threshold of one pixel numerical accuracy.
894%
895% The format of the GetOptimalKernelWidth method is:
896%
cristybb503372010-05-27 20:51:26 +0000897% size_t GetOptimalKernelWidth(const double radius,
cristy3ed852e2009-09-05 21:47:34 +0000898% const double sigma)
899%
900% A description of each parameter follows:
901%
902% o width: Method GetOptimalKernelWidth returns the optimal width of
903% a convolution kernel.
904%
905% o radius: the radius of the Gaussian, in pixels, not counting the center
906% pixel.
907%
908% o sigma: the standard deviation of the Gaussian, in pixels.
909%
910*/
cristy8ea81222011-09-04 10:33:32 +0000911MagickPrivate size_t GetOptimalKernelWidth1D(const double radius,
cristye96405a2010-05-19 02:24:31 +0000912 const double sigma)
cristy3ed852e2009-09-05 21:47:34 +0000913{
cristye96405a2010-05-19 02:24:31 +0000914 double
915 alpha,
916 beta,
917 gamma,
cristy3ed852e2009-09-05 21:47:34 +0000918 normalize,
cristye96405a2010-05-19 02:24:31 +0000919 value;
cristy3ed852e2009-09-05 21:47:34 +0000920
cristybb503372010-05-27 20:51:26 +0000921 register ssize_t
cristy47e00502009-12-17 19:19:57 +0000922 i;
923
cristybb503372010-05-27 20:51:26 +0000924 size_t
cristy47e00502009-12-17 19:19:57 +0000925 width;
cristy3ed852e2009-09-05 21:47:34 +0000926
cristy9d314ff2011-03-09 01:30:28 +0000927 ssize_t
928 j;
929
cristy3ed852e2009-09-05 21:47:34 +0000930 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristy47e00502009-12-17 19:19:57 +0000931 if (radius > MagickEpsilon)
cristybb503372010-05-27 20:51:26 +0000932 return((size_t) (2.0*ceil(radius)+1.0));
cristye96405a2010-05-19 02:24:31 +0000933 gamma=fabs(sigma);
934 if (gamma <= MagickEpsilon)
anthonyc1061722010-05-14 06:23:49 +0000935 return(3UL);
cristy35f15302012-06-07 14:59:02 +0000936 alpha=MagickEpsilonReciprocal(2.0*gamma*gamma);
cristycaf45802012-06-16 18:28:54 +0000937 beta=(double) MagickEpsilonReciprocal((MagickRealType) MagickSQ2PI*gamma);
cristy3ed852e2009-09-05 21:47:34 +0000938 for (width=5; ; )
939 {
940 normalize=0.0;
cristybb503372010-05-27 20:51:26 +0000941 j=(ssize_t) width/2;
cristy47e00502009-12-17 19:19:57 +0000942 for (i=(-j); i <= j; i++)
cristye96405a2010-05-19 02:24:31 +0000943 normalize+=exp(-((double) (i*i))*alpha)*beta;
944 value=exp(-((double) (j*j))*alpha)*beta/normalize;
cristy20908da2009-12-02 14:34:11 +0000945 if ((value < QuantumScale) || (value < MagickEpsilon))
cristy3ed852e2009-09-05 21:47:34 +0000946 break;
947 width+=2;
948 }
cristybb503372010-05-27 20:51:26 +0000949 return((size_t) (width-2));
cristy3ed852e2009-09-05 21:47:34 +0000950}
951
cristy8ea81222011-09-04 10:33:32 +0000952MagickPrivate size_t GetOptimalKernelWidth2D(const double radius,
cristye96405a2010-05-19 02:24:31 +0000953 const double sigma)
cristy3ed852e2009-09-05 21:47:34 +0000954{
cristy47e00502009-12-17 19:19:57 +0000955 double
cristye96405a2010-05-19 02:24:31 +0000956 alpha,
957 beta,
958 gamma,
cristy3ed852e2009-09-05 21:47:34 +0000959 normalize,
cristye96405a2010-05-19 02:24:31 +0000960 value;
cristy3ed852e2009-09-05 21:47:34 +0000961
cristy9d314ff2011-03-09 01:30:28 +0000962 size_t
963 width;
964
cristybb503372010-05-27 20:51:26 +0000965 ssize_t
cristy47e00502009-12-17 19:19:57 +0000966 j,
cristy3ed852e2009-09-05 21:47:34 +0000967 u,
968 v;
969
970 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristy47e00502009-12-17 19:19:57 +0000971 if (radius > MagickEpsilon)
cristybb503372010-05-27 20:51:26 +0000972 return((size_t) (2.0*ceil(radius)+1.0));
cristye96405a2010-05-19 02:24:31 +0000973 gamma=fabs(sigma);
974 if (gamma <= MagickEpsilon)
anthonyc1061722010-05-14 06:23:49 +0000975 return(3UL);
cristy35f15302012-06-07 14:59:02 +0000976 alpha=MagickEpsilonReciprocal(2.0*gamma*gamma);
cristycaf45802012-06-16 18:28:54 +0000977 beta=(double) MagickEpsilonReciprocal((MagickRealType) Magick2PI*gamma*gamma);
cristy3ed852e2009-09-05 21:47:34 +0000978 for (width=5; ; )
979 {
980 normalize=0.0;
cristybb503372010-05-27 20:51:26 +0000981 j=(ssize_t) width/2;
cristy47e00502009-12-17 19:19:57 +0000982 for (v=(-j); v <= j; v++)
cristy47e00502009-12-17 19:19:57 +0000983 for (u=(-j); u <= j; u++)
cristye96405a2010-05-19 02:24:31 +0000984 normalize+=exp(-((double) (u*u+v*v))*alpha)*beta;
985 value=exp(-((double) (j*j))*alpha)*beta/normalize;
cristy20908da2009-12-02 14:34:11 +0000986 if ((value < QuantumScale) || (value < MagickEpsilon))
cristy3ed852e2009-09-05 21:47:34 +0000987 break;
988 width+=2;
989 }
cristybb503372010-05-27 20:51:26 +0000990 return((size_t) (width-2));
cristy3ed852e2009-09-05 21:47:34 +0000991}
992
cristy8ea81222011-09-04 10:33:32 +0000993MagickPrivate size_t GetOptimalKernelWidth(const double radius,
cristy3ed852e2009-09-05 21:47:34 +0000994 const double sigma)
995{
996 return(GetOptimalKernelWidth1D(radius,sigma));
997}