blob: dd7a0b0e1d111d32e900affe5a44ae0fc521cd49 [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% %
cristy7e41fe82010-12-04 23:12:08 +000020% Copyright 1999-2011 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"
53#include "MagickCore/quantum.h"
54#include "MagickCore/quantum-private.h"
55#include "MagickCore/random_.h"
56#include "MagickCore/resize.h"
57#include "MagickCore/transform.h"
58#include "MagickCore/signature-private.h"
cristy3ed852e2009-09-05 21:47:34 +000059
60/*
61%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
62% %
63% %
64% %
65% C o n v e r t H S B T o R G B %
66% %
67% %
68% %
69%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
70%
71% ConvertHSBToRGB() transforms a (hue, saturation, brightness) to a (red,
72% green, blue) triple.
73%
74% The format of the ConvertHSBToRGBImage method is:
75%
76% void ConvertHSBToRGB(const double hue,const double saturation,
cristy3094b7f2011-10-01 23:18:02 +000077% const double brightness,double *red,double *green,double *blue)
cristy3ed852e2009-09-05 21:47:34 +000078%
79% A description of each parameter follows:
80%
81% o hue, saturation, brightness: A double value representing a
82% component of the HSB color space.
83%
84% o red, green, blue: A pointer to a pixel component of type Quantum.
85%
86*/
cristyd1dd6e42011-09-04 01:46:08 +000087MagickPrivate void ConvertHSBToRGB(const double hue,const double saturation,
cristy3094b7f2011-10-01 23:18:02 +000088 const double brightness,double *red,double *green,double *blue)
cristy3ed852e2009-09-05 21:47:34 +000089{
90 MagickRealType
91 f,
92 h,
93 p,
94 q,
95 t;
96
97 /*
98 Convert HSB to RGB colorspace.
99 */
cristy3094b7f2011-10-01 23:18:02 +0000100 assert(red != (double *) NULL);
101 assert(green != (double *) NULL);
102 assert(blue != (double *) NULL);
cristy98a65d52010-04-14 02:12:38 +0000103 if (saturation == 0.0)
cristy3ed852e2009-09-05 21:47:34 +0000104 {
cristybcfb0432010-05-06 01:45:33 +0000105 *red=ClampToQuantum((MagickRealType) QuantumRange*brightness);
cristy3ed852e2009-09-05 21:47:34 +0000106 *green=(*red);
107 *blue=(*red);
108 return;
109 }
cristy98a65d52010-04-14 02:12:38 +0000110 h=6.0*(hue-floor(hue));
111 f=h-floor((double) h);
112 p=brightness*(1.0-saturation);
113 q=brightness*(1.0-saturation*f);
114 t=brightness*(1.0-(saturation*(1.0-f)));
cristy3ed852e2009-09-05 21:47:34 +0000115 switch ((int) h)
116 {
117 case 0:
118 default:
119 {
cristybcfb0432010-05-06 01:45:33 +0000120 *red=ClampToQuantum((MagickRealType) QuantumRange*brightness);
121 *green=ClampToQuantum((MagickRealType) QuantumRange*t);
122 *blue=ClampToQuantum((MagickRealType) QuantumRange*p);
cristy3ed852e2009-09-05 21:47:34 +0000123 break;
124 }
125 case 1:
126 {
cristybcfb0432010-05-06 01:45:33 +0000127 *red=ClampToQuantum((MagickRealType) QuantumRange*q);
128 *green=ClampToQuantum((MagickRealType) QuantumRange*brightness);
129 *blue=ClampToQuantum((MagickRealType) QuantumRange*p);
cristy3ed852e2009-09-05 21:47:34 +0000130 break;
131 }
132 case 2:
133 {
cristybcfb0432010-05-06 01:45:33 +0000134 *red=ClampToQuantum((MagickRealType) QuantumRange*p);
135 *green=ClampToQuantum((MagickRealType) QuantumRange*brightness);
136 *blue=ClampToQuantum((MagickRealType) QuantumRange*t);
cristy3ed852e2009-09-05 21:47:34 +0000137 break;
138 }
139 case 3:
140 {
cristybcfb0432010-05-06 01:45:33 +0000141 *red=ClampToQuantum((MagickRealType) QuantumRange*p);
142 *green=ClampToQuantum((MagickRealType) QuantumRange*q);
143 *blue=ClampToQuantum((MagickRealType) QuantumRange*brightness);
cristy3ed852e2009-09-05 21:47:34 +0000144 break;
145 }
146 case 4:
147 {
cristybcfb0432010-05-06 01:45:33 +0000148 *red=ClampToQuantum((MagickRealType) QuantumRange*t);
149 *green=ClampToQuantum((MagickRealType) QuantumRange*p);
150 *blue=ClampToQuantum((MagickRealType) QuantumRange*brightness);
cristy3ed852e2009-09-05 21:47:34 +0000151 break;
152 }
153 case 5:
154 {
cristybcfb0432010-05-06 01:45:33 +0000155 *red=ClampToQuantum((MagickRealType) QuantumRange*brightness);
156 *green=ClampToQuantum((MagickRealType) QuantumRange*p);
157 *blue=ClampToQuantum((MagickRealType) QuantumRange*q);
cristy3ed852e2009-09-05 21:47:34 +0000158 break;
159 }
160 }
161}
162
163/*
164%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
165% %
166% %
167% %
168% C o n v e r t H S L T o R G B %
169% %
170% %
171% %
172%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
173%
174% ConvertHSLToRGB() transforms a (hue, saturation, lightness) to a (red,
175% green, blue) triple.
176%
177% The format of the ConvertHSLToRGBImage method is:
178%
179% void ConvertHSLToRGB(const double hue,const double saturation,
cristy3094b7f2011-10-01 23:18:02 +0000180% const double lightness,double *red,double *green,double *blue)
cristy3ed852e2009-09-05 21:47:34 +0000181%
182% A description of each parameter follows:
183%
184% o hue, saturation, lightness: A double value representing a
185% component of the HSL color space.
186%
187% o red, green, blue: A pointer to a pixel component of type Quantum.
188%
189*/
190
191static inline MagickRealType ConvertHueToRGB(MagickRealType m1,
192 MagickRealType m2,MagickRealType hue)
193{
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
207MagickExport void ConvertHSLToRGB(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{
210 MagickRealType
211 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 {
cristybcfb0432010-05-06 01:45:33 +0000225 *red=ClampToQuantum((MagickRealType) 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;
cristy3ed852e2009-09-05 21:47:34 +0000235 r=ConvertHueToRGB(m1,m2,hue+1.0/3.0);
236 g=ConvertHueToRGB(m1,m2,hue);
237 b=ConvertHueToRGB(m1,m2,hue-1.0/3.0);
cristybcfb0432010-05-06 01:45:33 +0000238 *red=ClampToQuantum((MagickRealType) QuantumRange*r);
239 *green=ClampToQuantum((MagickRealType) QuantumRange*g);
240 *blue=ClampToQuantum((MagickRealType) QuantumRange*b);
cristy3ed852e2009-09-05 21:47:34 +0000241}
242
243/*
244%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
245% %
246% %
247% %
248% C o n v e r t H W B T o R G B %
249% %
250% %
251% %
252%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
253%
254% ConvertHWBToRGB() transforms a (hue, whiteness, blackness) to a (red, green,
255% blue) triple.
256%
257% The format of the ConvertHWBToRGBImage method is:
258%
259% void ConvertHWBToRGB(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*/
cristyd1dd6e42011-09-04 01:46:08 +0000270MagickPrivate void ConvertHWBToRGB(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{
273 MagickRealType
274 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;
291 if (hue == 0.0)
292 {
cristybcfb0432010-05-06 01:45:33 +0000293 *red=ClampToQuantum((MagickRealType) QuantumRange*v);
294 *green=ClampToQuantum((MagickRealType) QuantumRange*v);
295 *blue=ClampToQuantum((MagickRealType) QuantumRange*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 }
cristybcfb0432010-05-06 01:45:33 +0000314 *red=ClampToQuantum((MagickRealType) QuantumRange*r);
315 *green=ClampToQuantum((MagickRealType) QuantumRange*g);
316 *blue=ClampToQuantum((MagickRealType) QuantumRange*b);
cristy3ed852e2009-09-05 21:47:34 +0000317}
318
319/*
320%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
321% %
322% %
323% %
324% C o n v e r t R G B T o H S B %
325% %
326% %
327% %
328%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
329%
330% ConvertRGBToHSB() transforms a (red, green, blue) to a (hue, saturation,
331% brightness) triple.
332%
333% The format of the ConvertRGBToHSB method is:
334%
cristy3094b7f2011-10-01 23:18:02 +0000335% void ConvertRGBToHSB(const double red,const double green,
336% 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*/
cristy3094b7f2011-10-01 23:18:02 +0000347MagickPrivate void ConvertRGBToHSB(const double red,const double green,
348 const double blue,double *hue,double *saturation,double *brightness)
cristy3ed852e2009-09-05 21:47:34 +0000349{
350 MagickRealType
351 delta,
352 max,
353 min;
354
355 /*
356 Convert RGB to HSB colorspace.
357 */
358 assert(hue != (double *) NULL);
359 assert(saturation != (double *) NULL);
360 assert(brightness != (double *) NULL);
361 *hue=0.0;
362 *saturation=0.0;
363 *brightness=0.0;
364 min=(MagickRealType) (red < green ? red : green);
365 if ((MagickRealType) blue < min)
366 min=(MagickRealType) blue;
367 max=(MagickRealType) (red > green ? red : green);
368 if ((MagickRealType) blue > max)
369 max=(MagickRealType) blue;
370 if (max == 0.0)
371 return;
372 delta=max-min;
373 *saturation=(double) (delta/max);
374 *brightness=(double) (QuantumScale*max);
375 if (delta == 0.0)
376 return;
cristy18b17442009-10-25 18:36:48 +0000377 if ((MagickRealType) red == max)
378 *hue=(double) ((green-(MagickRealType) blue)/delta);
cristy3ed852e2009-09-05 21:47:34 +0000379 else
cristy18b17442009-10-25 18:36:48 +0000380 if ((MagickRealType) green == max)
381 *hue=(double) (2.0+(blue-(MagickRealType) red)/delta);
cristy3ed852e2009-09-05 21:47:34 +0000382 else
cristy18b17442009-10-25 18:36:48 +0000383 *hue=(double) (4.0+(red-(MagickRealType) green)/delta);
384 *hue/=6.0;
cristy3ed852e2009-09-05 21:47:34 +0000385 if (*hue < 0.0)
386 *hue+=1.0;
387}
388
389/*
390%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
391% %
392% %
393% %
394% C o n v e r t R G B T o H S L %
395% %
396% %
397% %
398%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
399%
400% ConvertRGBToHSL() transforms a (red, green, blue) to a (hue, saturation,
401% lightness) triple.
402%
403% The format of the ConvertRGBToHSL method is:
404%
cristy3094b7f2011-10-01 23:18:02 +0000405% void ConvertRGBToHSL(const double red,const double green,
406% const double blue,double *hue,double *saturation,double *lightness)
cristy3ed852e2009-09-05 21:47:34 +0000407%
408% A description of each parameter follows:
409%
410% o red, green, blue: A Quantum value representing the red, green, and
411% blue component of a pixel..
412%
413% o hue, saturation, lightness: A pointer to a double value representing a
414% component of the HSL color space.
415%
416*/
417
418static inline double MagickMax(const double x,const double y)
419{
420 if (x > y)
421 return(x);
422 return(y);
423}
424
425static inline double MagickMin(const double x,const double y)
426{
427 if (x < y)
428 return(x);
429 return(y);
430}
431
cristy3094b7f2011-10-01 23:18:02 +0000432MagickExport void ConvertRGBToHSL(const double red,const double green,
433 const double blue,double *hue,double *saturation,double *lightness)
cristy3ed852e2009-09-05 21:47:34 +0000434{
435 MagickRealType
436 b,
437 delta,
438 g,
439 max,
440 min,
441 r;
442
443 /*
444 Convert RGB to HSL colorspace.
445 */
446 assert(hue != (double *) NULL);
447 assert(saturation != (double *) NULL);
448 assert(lightness != (double *) NULL);
449 r=QuantumScale*red;
450 g=QuantumScale*green;
451 b=QuantumScale*blue;
452 max=MagickMax(r,MagickMax(g,b));
453 min=MagickMin(r,MagickMin(g,b));
454 *lightness=(double) ((min+max)/2.0);
455 delta=max-min;
456 if (delta == 0.0)
457 {
458 *hue=0.0;
459 *saturation=0.0;
460 return;
461 }
462 if (*lightness < 0.5)
463 *saturation=(double) (delta/(min+max));
464 else
465 *saturation=(double) (delta/(2.0-max-min));
466 if (r == max)
467 *hue=((((max-b)/6.0)+(delta/2.0))-(((max-g)/6.0)+(delta/2.0)))/delta;
468 else
469 if (g == max)
470 *hue=(1.0/3.0)+((((max-r)/6.0)+(delta/2.0))-(((max-b)/6.0)+(delta/2.0)))/
471 delta;
472 else
473 if (b == max)
474 *hue=(2.0/3.0)+((((max-g)/6.0)+(delta/2.0))-(((max-r)/6.0)+
475 (delta/2.0)))/delta;
476 if (*hue < 0.0)
477 *hue+=1.0;
478 if (*hue > 1.0)
479 *hue-=1.0;
480}
481
482/*
483%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
484% %
485% %
486% %
487% C o n v e r t R G B T o H W B %
488% %
489% %
490% %
491%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
492%
493% ConvertRGBToHWB() transforms a (red, green, blue) to a (hue, whiteness,
494% blackness) triple.
495%
496% The format of the ConvertRGBToHWB method is:
497%
cristy3094b7f2011-10-01 23:18:02 +0000498% void ConvertRGBToHWB(const double red,const double green,
499% const double blue,double *hue,double *whiteness,double *blackness)
cristy3ed852e2009-09-05 21:47:34 +0000500%
501% A description of each parameter follows:
502%
503% o red, green, blue: A Quantum value representing the red, green, and
504% blue component of a pixel.
505%
506% o hue, whiteness, blackness: A pointer to a double value representing a
507% component of the HWB color space.
508%
509*/
cristy3094b7f2011-10-01 23:18:02 +0000510MagickPrivate void ConvertRGBToHWB(const double red,const double green,
511 const double blue,double *hue,double *whiteness,double *blackness)
cristy3ed852e2009-09-05 21:47:34 +0000512{
cristybb503372010-05-27 20:51:26 +0000513 long
514 i;
515
cristy3ed852e2009-09-05 21:47:34 +0000516 MagickRealType
517 f,
518 v,
519 w;
520
cristy3ed852e2009-09-05 21:47:34 +0000521 /*
522 Convert RGB to HWB colorspace.
523 */
524 assert(hue != (double *) NULL);
525 assert(whiteness != (double *) NULL);
526 assert(blackness != (double *) NULL);
527 w=(MagickRealType) MagickMin((double) red,MagickMin((double) green,(double)
528 blue));
529 v=(MagickRealType) MagickMax((double) red,MagickMax((double) green,(double)
530 blue));
531 *blackness=1.0-QuantumScale*v;
532 *whiteness=QuantumScale*w;
533 if (v == w)
534 {
535 *hue=0.0;
536 return;
537 }
538 f=((MagickRealType) red == w) ? green-(MagickRealType) blue :
539 (((MagickRealType) green == w) ? blue-(MagickRealType) red : red-
540 (MagickRealType) green);
541 i=((MagickRealType) red == w) ? 3 : (((MagickRealType) green == w) ? 5 : 1);
542 *hue=((double) i-f/(v-1.0*w))/6.0;
543}
544
545/*
546%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
547% %
548% %
549% %
550% E x p a n d A f f i n e %
551% %
552% %
553% %
554%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
555%
556% ExpandAffine() computes the affine's expansion factor, i.e. the square root
557% of the factor by which the affine transform affects area. In an affine
558% transform composed of scaling, rotation, shearing, and translation, returns
559% the amount of scaling.
560%
561% The format of the ExpandAffine method is:
562%
563% double ExpandAffine(const AffineMatrix *affine)
564%
565% A description of each parameter follows:
566%
567% o expansion: Method ExpandAffine returns the affine's expansion factor.
568%
569% o affine: A pointer the affine transform of type AffineMatrix.
570%
571*/
572MagickExport double ExpandAffine(const AffineMatrix *affine)
573{
574 assert(affine != (const AffineMatrix *) NULL);
575 return(sqrt(fabs(affine->sx*affine->sy-affine->rx*affine->ry)));
576}
577
578/*
579%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
580% %
581% %
582% %
583% 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 %
584% %
585% %
586% %
587%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
588%
cristy82b15832009-10-06 19:17:37 +0000589% GenerateDifferentialNoise() generates differentual noise.
cristy3ed852e2009-09-05 21:47:34 +0000590%
591% The format of the GenerateDifferentialNoise method is:
592%
593% double GenerateDifferentialNoise(RandomInfo *random_info,
594% const Quantum pixel,const NoiseType noise_type,
595% const MagickRealType attenuate)
596%
597% A description of each parameter follows:
598%
599% o random_info: the random info.
600%
601% o pixel: noise is relative to this pixel value.
602%
603% o noise_type: the type of noise.
604%
605% o attenuate: attenuate the noise.
606%
607*/
cristy8ea81222011-09-04 10:33:32 +0000608MagickPrivate double GenerateDifferentialNoise(RandomInfo *random_info,
cristy3ed852e2009-09-05 21:47:34 +0000609 const Quantum pixel,const NoiseType noise_type,const MagickRealType attenuate)
610{
611#define NoiseEpsilon (attenuate*1.0e-5)
cristy20a777c2009-10-23 13:03:15 +0000612#define SigmaUniform (attenuate*4.0)
613#define SigmaGaussian (attenuate*4.0)
cristy3ed852e2009-09-05 21:47:34 +0000614#define SigmaImpulse (attenuate*0.10)
cristy20a777c2009-10-23 13:03:15 +0000615#define SigmaLaplacian (attenuate*10.0)
616#define SigmaMultiplicativeGaussian (attenuate*1.0)
cristy3ed852e2009-09-05 21:47:34 +0000617#define SigmaPoisson (attenuate*0.05)
cristy20a777c2009-10-23 13:03:15 +0000618#define TauGaussian (attenuate*20.0)
cristy3ed852e2009-09-05 21:47:34 +0000619
cristyadb41ca2009-10-22 15:02:28 +0000620 double
cristy3ed852e2009-09-05 21:47:34 +0000621 alpha,
622 beta,
623 noise,
624 sigma;
625
626 alpha=GetPseudoRandomValue(random_info);
cristy3ed852e2009-09-05 21:47:34 +0000627 switch (noise_type)
628 {
629 case UniformNoise:
630 default:
631 {
cristy37c24072011-10-08 01:26:00 +0000632 noise=(double) pixel+QuantumRange*SigmaUniform*(alpha-0.5)/255.0;
cristy3ed852e2009-09-05 21:47:34 +0000633 break;
634 }
635 case GaussianNoise:
636 {
cristyadb41ca2009-10-22 15:02:28 +0000637 double
cristy62faa602010-02-20 03:36:17 +0000638 gamma,
cristy3ed852e2009-09-05 21:47:34 +0000639 tau;
640
cristyadb41ca2009-10-22 15:02:28 +0000641 if (alpha == 0.0)
642 alpha=1.0;
cristy3ed852e2009-09-05 21:47:34 +0000643 beta=GetPseudoRandomValue(random_info);
cristy62faa602010-02-20 03:36:17 +0000644 gamma=sqrt(-2.0*log(alpha));
cristy55a91cd2010-12-01 00:57:40 +0000645 sigma=gamma*cos((double) (2.0*MagickPI*beta));
646 tau=gamma*sin((double) (2.0*MagickPI*beta));
cristy37c24072011-10-08 01:26:00 +0000647 noise=(double) pixel+sqrt((double) pixel)*SigmaGaussian*sigma/255.0+
648 QuantumRange*TauGaussian*tau/255.0;
cristy3ed852e2009-09-05 21:47:34 +0000649 break;
650 }
651 case MultiplicativeGaussianNoise:
652 {
cristy37c24072011-10-08 01:26:00 +0000653 sigma=1.0;
654 if (alpha > NoiseEpsilon)
cristyadb41ca2009-10-22 15:02:28 +0000655 sigma=sqrt(-2.0*log(alpha));
cristy3ed852e2009-09-05 21:47:34 +0000656 beta=GetPseudoRandomValue(random_info);
cristy37c24072011-10-08 01:26:00 +0000657 noise=(double) pixel+pixel*SigmaMultiplicativeGaussian*sigma*
658 cos((double) (2.0*MagickPI*beta))/2.0/255.0;
cristy3ed852e2009-09-05 21:47:34 +0000659 break;
660 }
661 case ImpulseNoise:
662 {
663 if (alpha < (SigmaImpulse/2.0))
664 noise=0.0;
cristy37c24072011-10-08 01:26:00 +0000665 else
666 if (alpha >= (1.0-(SigmaImpulse/2.0)))
667 noise=(double) QuantumRange;
668 else
669 noise=(double) pixel;
cristy3ed852e2009-09-05 21:47:34 +0000670 break;
671 }
672 case LaplacianNoise:
673 {
674 if (alpha <= 0.5)
675 {
676 if (alpha <= NoiseEpsilon)
cristyadb41ca2009-10-22 15:02:28 +0000677 noise=(double) pixel-(double) QuantumRange;
cristy3ed852e2009-09-05 21:47:34 +0000678 else
cristy37c24072011-10-08 01:26:00 +0000679 noise=(double) pixel+QuantumRange*SigmaLaplacian*
680 log(2.0*alpha)/255.0+0.5;
cristy3ed852e2009-09-05 21:47:34 +0000681 break;
682 }
683 beta=1.0-alpha;
684 if (beta <= (0.5*NoiseEpsilon))
cristyadb41ca2009-10-22 15:02:28 +0000685 noise=(double) (pixel+QuantumRange);
cristy3ed852e2009-09-05 21:47:34 +0000686 else
cristy37c24072011-10-08 01:26:00 +0000687 noise=(double) pixel-QuantumRange*SigmaLaplacian*log(2.0*beta)/255.0+
688 0.5;
cristy3ed852e2009-09-05 21:47:34 +0000689 break;
690 }
691 case PoissonNoise:
692 {
cristyadb41ca2009-10-22 15:02:28 +0000693 double
cristy3ed852e2009-09-05 21:47:34 +0000694 poisson;
695
cristybb503372010-05-27 20:51:26 +0000696 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000697 i;
698
cristy37c24072011-10-08 01:26:00 +0000699 poisson=exp(-SigmaPoisson*255.0*QuantumScale*pixel);
cristy3ed852e2009-09-05 21:47:34 +0000700 for (i=0; alpha > poisson; i++)
701 {
702 beta=GetPseudoRandomValue(random_info);
703 alpha*=beta;
704 }
cristy37c24072011-10-08 01:26:00 +0000705 noise=(double) QuantumRange*i/SigmaPoisson/255.0;
cristy3ed852e2009-09-05 21:47:34 +0000706 break;
707 }
708 case RandomNoise:
709 {
cristy2a42b1b2010-02-20 23:21:13 +0000710 noise=(double) QuantumRange*alpha;
cristy3ed852e2009-09-05 21:47:34 +0000711 break;
712 }
713 }
714 return(noise);
715}
716
717/*
718%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
719% %
720% %
721% %
722% G e t O p t i m a l K e r n e l W i d t h %
723% %
724% %
725% %
726%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
727%
728% GetOptimalKernelWidth() computes the optimal kernel radius for a convolution
729% filter. Start with the minimum value of 3 pixels and walk out until we drop
730% below the threshold of one pixel numerical accuracy.
731%
732% The format of the GetOptimalKernelWidth method is:
733%
cristybb503372010-05-27 20:51:26 +0000734% size_t GetOptimalKernelWidth(const double radius,
cristy3ed852e2009-09-05 21:47:34 +0000735% const double sigma)
736%
737% A description of each parameter follows:
738%
739% o width: Method GetOptimalKernelWidth returns the optimal width of
740% a convolution kernel.
741%
742% o radius: the radius of the Gaussian, in pixels, not counting the center
743% pixel.
744%
745% o sigma: the standard deviation of the Gaussian, in pixels.
746%
747*/
cristy8ea81222011-09-04 10:33:32 +0000748MagickPrivate size_t GetOptimalKernelWidth1D(const double radius,
cristye96405a2010-05-19 02:24:31 +0000749 const double sigma)
cristy3ed852e2009-09-05 21:47:34 +0000750{
cristye96405a2010-05-19 02:24:31 +0000751 double
752 alpha,
753 beta,
754 gamma,
cristy3ed852e2009-09-05 21:47:34 +0000755 normalize,
cristye96405a2010-05-19 02:24:31 +0000756 value;
cristy3ed852e2009-09-05 21:47:34 +0000757
cristybb503372010-05-27 20:51:26 +0000758 register ssize_t
cristy47e00502009-12-17 19:19:57 +0000759 i;
760
cristybb503372010-05-27 20:51:26 +0000761 size_t
cristy47e00502009-12-17 19:19:57 +0000762 width;
cristy3ed852e2009-09-05 21:47:34 +0000763
cristy9d314ff2011-03-09 01:30:28 +0000764 ssize_t
765 j;
766
cristy3ed852e2009-09-05 21:47:34 +0000767 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristy47e00502009-12-17 19:19:57 +0000768 if (radius > MagickEpsilon)
cristybb503372010-05-27 20:51:26 +0000769 return((size_t) (2.0*ceil(radius)+1.0));
cristye96405a2010-05-19 02:24:31 +0000770 gamma=fabs(sigma);
771 if (gamma <= MagickEpsilon)
anthonyc1061722010-05-14 06:23:49 +0000772 return(3UL);
cristye96405a2010-05-19 02:24:31 +0000773 alpha=1.0/(2.0*gamma*gamma);
cristy55a91cd2010-12-01 00:57:40 +0000774 beta=(double) (1.0/(MagickSQ2PI*gamma));
cristy3ed852e2009-09-05 21:47:34 +0000775 for (width=5; ; )
776 {
777 normalize=0.0;
cristybb503372010-05-27 20:51:26 +0000778 j=(ssize_t) width/2;
cristy47e00502009-12-17 19:19:57 +0000779 for (i=(-j); i <= j; i++)
cristye96405a2010-05-19 02:24:31 +0000780 normalize+=exp(-((double) (i*i))*alpha)*beta;
781 value=exp(-((double) (j*j))*alpha)*beta/normalize;
cristy20908da2009-12-02 14:34:11 +0000782 if ((value < QuantumScale) || (value < MagickEpsilon))
cristy3ed852e2009-09-05 21:47:34 +0000783 break;
784 width+=2;
785 }
cristybb503372010-05-27 20:51:26 +0000786 return((size_t) (width-2));
cristy3ed852e2009-09-05 21:47:34 +0000787}
788
cristy8ea81222011-09-04 10:33:32 +0000789MagickPrivate size_t GetOptimalKernelWidth2D(const double radius,
cristye96405a2010-05-19 02:24:31 +0000790 const double sigma)
cristy3ed852e2009-09-05 21:47:34 +0000791{
cristy47e00502009-12-17 19:19:57 +0000792 double
cristye96405a2010-05-19 02:24:31 +0000793 alpha,
794 beta,
795 gamma,
cristy3ed852e2009-09-05 21:47:34 +0000796 normalize,
cristye96405a2010-05-19 02:24:31 +0000797 value;
cristy3ed852e2009-09-05 21:47:34 +0000798
cristy9d314ff2011-03-09 01:30:28 +0000799 size_t
800 width;
801
cristybb503372010-05-27 20:51:26 +0000802 ssize_t
cristy47e00502009-12-17 19:19:57 +0000803 j,
cristy3ed852e2009-09-05 21:47:34 +0000804 u,
805 v;
806
807 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristy47e00502009-12-17 19:19:57 +0000808 if (radius > MagickEpsilon)
cristybb503372010-05-27 20:51:26 +0000809 return((size_t) (2.0*ceil(radius)+1.0));
cristye96405a2010-05-19 02:24:31 +0000810 gamma=fabs(sigma);
811 if (gamma <= MagickEpsilon)
anthonyc1061722010-05-14 06:23:49 +0000812 return(3UL);
cristye96405a2010-05-19 02:24:31 +0000813 alpha=1.0/(2.0*gamma*gamma);
cristy55a91cd2010-12-01 00:57:40 +0000814 beta=(double) (1.0/(Magick2PI*gamma*gamma));
cristy3ed852e2009-09-05 21:47:34 +0000815 for (width=5; ; )
816 {
817 normalize=0.0;
cristybb503372010-05-27 20:51:26 +0000818 j=(ssize_t) width/2;
cristy47e00502009-12-17 19:19:57 +0000819 for (v=(-j); v <= j; v++)
cristy47e00502009-12-17 19:19:57 +0000820 for (u=(-j); u <= j; u++)
cristye96405a2010-05-19 02:24:31 +0000821 normalize+=exp(-((double) (u*u+v*v))*alpha)*beta;
822 value=exp(-((double) (j*j))*alpha)*beta/normalize;
cristy20908da2009-12-02 14:34:11 +0000823 if ((value < QuantumScale) || (value < MagickEpsilon))
cristy3ed852e2009-09-05 21:47:34 +0000824 break;
825 width+=2;
826 }
cristybb503372010-05-27 20:51:26 +0000827 return((size_t) (width-2));
cristy3ed852e2009-09-05 21:47:34 +0000828}
829
cristy8ea81222011-09-04 10:33:32 +0000830MagickPrivate size_t GetOptimalKernelWidth(const double radius,
cristy3ed852e2009-09-05 21:47:34 +0000831 const double sigma)
832{
833 return(GetOptimalKernelWidth1D(radius,sigma));
834}