/src/graphicsmagick/magick/gem.c
Line | Count | Source |
1 | | /* |
2 | | % Copyright (C) 2003-2023 GraphicsMagick Group |
3 | | % Copyright (C) 2002 ImageMagick Studio |
4 | | % |
5 | | % This program is covered by multiple licenses, which are described in |
6 | | % Copyright.txt. You should have received a copy of Copyright.txt with this |
7 | | % package; otherwise see http://www.graphicsmagick.org/www/Copyright.html. |
8 | | % |
9 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
10 | | % % |
11 | | % % |
12 | | % % |
13 | | % GGGG EEEEE M M % |
14 | | % G E MM MM % |
15 | | % G GG EEE M M M % |
16 | | % G G E M M % |
17 | | % GGGG EEEEE M M % |
18 | | % % |
19 | | % % |
20 | | % Graphic Gems - Graphic Support Methods % |
21 | | % % |
22 | | % % |
23 | | % Software Design % |
24 | | % John Cristy % |
25 | | % August 1996 % |
26 | | % % |
27 | | % % |
28 | | % % |
29 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
30 | | % |
31 | | % |
32 | | % |
33 | | */ |
34 | | |
35 | | /* |
36 | | Include declarations. |
37 | | */ |
38 | | #include "magick/studio.h" |
39 | | #include "magick/gem.h" |
40 | | #include "magick/random.h" |
41 | | #include "magick/utility.h" |
42 | | |
43 | | /* |
44 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
45 | | % % |
46 | | % % |
47 | | % % |
48 | | % C o n s t r a s t % |
49 | | % % |
50 | | % % |
51 | | % % |
52 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
53 | | % |
54 | | % Method Contrast enhances the intensity differences between the lighter |
55 | | % and darker elements of the image. |
56 | | % |
57 | | % The format of the Contrast method is: |
58 | | % |
59 | | % void Contrast(const int sign,Quantum *red,Quantum *green,Quantum *blue) |
60 | | % |
61 | | % A description of each parameter follows: |
62 | | % |
63 | | % o sign: A positive value enhances the contrast otherwise it is reduced. |
64 | | % |
65 | | % o red, green, blue: A pointer to a pixel component of type Quantum. |
66 | | % |
67 | | % |
68 | | */ |
69 | | MagickExport void Contrast(const int sign,Quantum *red,Quantum *green, |
70 | | Quantum *blue) |
71 | 0 | { |
72 | 0 | static const double |
73 | 0 | alpha=0.50000000000099997787827987849595956504344940185546875; /* 0.5+MagickEpsilon */ |
74 | |
|
75 | 0 | double |
76 | 0 | brightness, |
77 | 0 | hue, |
78 | 0 | saturation; |
79 | | |
80 | | /* |
81 | | Enhance contrast: dark color become darker, light color become lighter. |
82 | | */ |
83 | 0 | assert(red != (Quantum *) NULL); |
84 | 0 | assert(green != (Quantum *) NULL); |
85 | 0 | assert(blue != (Quantum *) NULL); |
86 | 0 | TransformHSL(*red,*green,*blue,&hue,&saturation,&brightness); |
87 | 0 | brightness+= |
88 | 0 | alpha*sign*(alpha*(sin(MagickPI*(brightness-alpha))+1.0)-brightness); |
89 | 0 | if (brightness > 1.0) |
90 | 0 | brightness=1.0; |
91 | 0 | if (brightness < 0.0) |
92 | 0 | brightness=0.0; |
93 | 0 | HSLTransform(hue,saturation,brightness,red,green,blue); |
94 | 0 | } |
95 | | |
96 | | /* |
97 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
98 | | % % |
99 | | % % |
100 | | % % |
101 | | % E x p a n d A f f i n e % |
102 | | % % |
103 | | % % |
104 | | % % |
105 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
106 | | % |
107 | | % Method ExpandAffine computes the affine's expansion factor, i.e. the |
108 | | % square root of the factor by which the affine transform affects area. In an |
109 | | % affine transform composed of scaling, rotation, shearing, and translation, |
110 | | % returns the amount of scaling. |
111 | | % |
112 | | % The format of the ExpandAffine method is: |
113 | | % |
114 | | % double ExpandAffine(const AffineMatrix *affine) |
115 | | % |
116 | | % A description of each parameter follows: |
117 | | % |
118 | | % o expansion: Method ExpandAffine returns the affine's expansion factor. |
119 | | % |
120 | | % o affine: A pointer the the affine transform of type AffineMatrix. |
121 | | % |
122 | | % |
123 | | */ |
124 | | MagickExport double ExpandAffine(const AffineMatrix *affine) |
125 | 6.58M | { |
126 | 6.58M | double |
127 | 6.58M | expand; |
128 | | |
129 | 6.58M | assert(affine != (const AffineMatrix *) NULL); |
130 | 6.58M | expand=fabs(affine->sx*affine->sy)-fabs(affine->rx*affine->ry); |
131 | 6.58M | if (fabs(expand) < MagickEpsilon) |
132 | 323k | return(1.0); |
133 | 6.25M | return(sqrt(fabs(expand))); |
134 | 6.58M | } |
135 | | |
136 | | /* |
137 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
138 | | % % |
139 | | % % |
140 | | % % |
141 | | % 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 % |
142 | | % % |
143 | | % % |
144 | | % % |
145 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
146 | | % |
147 | | % Method GenerateDifferentialNoise generates a differential floating-point |
148 | | % noise value which will produce the final result when added to the |
149 | | % original pixel. The floating point differential value is useful since |
150 | | % it allows scaling without loss of precision and avoids clipping. |
151 | | % |
152 | | % The format of the GenerateDifferentialNoise method is: |
153 | | % |
154 | | % double GenerateDifferentialNoise(const Quantum pixel, |
155 | | % const NoiseType noise_type, |
156 | | % MagickRandomKernel *kernel) |
157 | | % |
158 | | % A description of each parameter follows: |
159 | | % |
160 | | % o pixel: A structure of type Quantum. |
161 | | % |
162 | | % o noise_type: The type of noise: Uniform, gaussian, |
163 | | % multiplicative Gaussian, impulse, laplacian, or Poisson. |
164 | | % |
165 | | % o kernel: Kernel for random number generator. |
166 | | % |
167 | | */ |
168 | 0 | #define NoiseEpsilon 1.0e-5 |
169 | 0 | #define SigmaUniform 4.0 |
170 | 0 | #define SigmaGaussian 4.0 |
171 | 0 | #define SigmaImpulse 0.10 |
172 | 0 | #define SigmaLaplacian 10.0 |
173 | 0 | #define SigmaMultiplicativeGaussian 0.5 |
174 | 0 | #define SigmaPoisson 0.05 |
175 | 0 | #define TauGaussian 20.0 |
176 | | |
177 | | #if defined(HAVE_LOGF) |
178 | 0 | # define NOISE_FLT_T float |
179 | | #else |
180 | | # define NOISE_FLT_T double |
181 | | #endif |
182 | | |
183 | | MagickExport double GenerateDifferentialNoise(const Quantum quantum_pixel, |
184 | | const NoiseType noise_type, |
185 | | MagickRandomKernel *kernel) |
186 | 0 | { |
187 | 0 | NOISE_FLT_T |
188 | 0 | alpha, |
189 | 0 | beta, |
190 | 0 | pixel, |
191 | 0 | sigma; |
192 | |
|
193 | 0 | double |
194 | 0 | value; |
195 | |
|
196 | 0 | pixel=(NOISE_FLT_T) quantum_pixel; |
197 | |
|
198 | 0 | #if QuantumDepth > 8 |
199 | 0 | pixel /= (NOISE_FLT_T) (MaxRGBDouble/255.0); |
200 | 0 | #endif |
201 | |
|
202 | 0 | alpha=(NOISE_FLT_T) MagickRandomRealInlined(kernel); |
203 | 0 | if (alpha == 0.0) |
204 | 0 | alpha=1.0; |
205 | 0 | switch (noise_type) |
206 | 0 | { |
207 | 0 | case UniformNoise: |
208 | 0 | default: |
209 | 0 | { |
210 | 0 | value=SigmaUniform*(alpha-0.5); |
211 | 0 | break; |
212 | 0 | } |
213 | 0 | case GaussianNoise: |
214 | 0 | { |
215 | 0 | float |
216 | 0 | tau; |
217 | |
|
218 | 0 | beta=(NOISE_FLT_T) MagickRandomRealInlined(kernel); |
219 | 0 | sigma=SQRTF(-2.0*LOGF(alpha))*COSF(2.0*MagickPI*beta); |
220 | 0 | tau=SQRTF(-2.0*LOGF(alpha))*SINF(2.0*MagickPI*beta); |
221 | 0 | value=SQRTF(pixel)*SigmaGaussian*sigma+TauGaussian*tau; |
222 | 0 | break; |
223 | 0 | } |
224 | 0 | case MultiplicativeGaussianNoise: |
225 | 0 | { |
226 | 0 | if (alpha <= NoiseEpsilon) |
227 | 0 | sigma=255.0; |
228 | 0 | else |
229 | 0 | sigma=SQRTF(-2.0*LOGF(alpha)); |
230 | 0 | beta=(NOISE_FLT_T) MagickRandomRealInlined(kernel); |
231 | 0 | value=pixel*SigmaMultiplicativeGaussian*sigma*COSF(2.0*MagickPI*beta); |
232 | 0 | break; |
233 | 0 | } |
234 | 0 | case ImpulseNoise: |
235 | 0 | { |
236 | 0 | if (alpha < (SigmaImpulse/2.0)) |
237 | 0 | value=-pixel; |
238 | 0 | else |
239 | 0 | if (alpha >= (1.0-(SigmaImpulse/2.0))) |
240 | 0 | value=255.0-pixel; |
241 | 0 | else |
242 | 0 | value=0.0; |
243 | 0 | break; |
244 | 0 | } |
245 | 0 | case LaplacianNoise: |
246 | 0 | { |
247 | 0 | if (alpha <= 0.5) |
248 | 0 | { |
249 | 0 | if (alpha <= NoiseEpsilon) |
250 | 0 | value=-255.0; |
251 | 0 | else |
252 | 0 | value=SigmaLaplacian*LOGF(2.0*alpha); |
253 | 0 | break; |
254 | 0 | } |
255 | 0 | beta=(NOISE_FLT_T) 1.0-alpha; |
256 | 0 | if (beta <= (0.5*NoiseEpsilon)) |
257 | 0 | value=255.0; |
258 | 0 | else |
259 | 0 | value=-(SigmaLaplacian*LOGF(2.0*beta)); |
260 | 0 | break; |
261 | 0 | } |
262 | 0 | case PoissonNoise: |
263 | 0 | { |
264 | 0 | double |
265 | 0 | limit; |
266 | |
|
267 | 0 | register long |
268 | 0 | i; |
269 | |
|
270 | 0 | limit=exp(-SigmaPoisson*(double) pixel); |
271 | 0 | for (i=0; alpha > limit; i++) |
272 | 0 | { |
273 | 0 | beta=(NOISE_FLT_T) MagickRandomRealInlined(kernel); |
274 | 0 | alpha=alpha*beta; |
275 | 0 | } |
276 | 0 | value=pixel-((double) i/SigmaPoisson); |
277 | 0 | break; |
278 | 0 | } |
279 | 0 | case RandomNoise: |
280 | 0 | { |
281 | | /* Range is approximately -MaxRGB/2.0 to +MaxRGB/2.0 */ |
282 | 0 | value=257.0*(0.5-MagickRandomRealInlined(kernel)); |
283 | 0 | break; |
284 | 0 | } |
285 | 0 | } |
286 | | /* printf("value = %g\n",value); */ |
287 | | |
288 | 0 | #if QuantumDepth > 8 |
289 | 0 | value *= (MaxRGBFloat/255.0); |
290 | 0 | #endif |
291 | |
|
292 | 0 | return value; |
293 | 0 | } |
294 | | |
295 | | /* |
296 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
297 | | % % |
298 | | % % |
299 | | % % |
300 | | % G e n e r a t e N o i s e % |
301 | | % % |
302 | | % % |
303 | | % % |
304 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
305 | | % |
306 | | % Method GenerateNoise adds noise to a pixel. |
307 | | % |
308 | | % The format of the GenerateNoise method is: |
309 | | % |
310 | | % Quantum GenerateNoise(const Quantum pixel,const NoiseType noise_type) |
311 | | % |
312 | | % A description of each parameter follows: |
313 | | % |
314 | | % o pixel: A structure of type Quantum. |
315 | | % |
316 | | % o noise_type: The type of noise: Uniform, gaussian, |
317 | | % multiplicative Gaussian, impulse, laplacian, or Poisson. |
318 | | % |
319 | | % |
320 | | */ |
321 | | MagickExport Quantum GenerateNoise(const Quantum pixel, |
322 | | const NoiseType noise_type) |
323 | 0 | { |
324 | 0 | double |
325 | 0 | value; |
326 | |
|
327 | 0 | value=(double) pixel+GenerateDifferentialNoise(pixel,noise_type, |
328 | 0 | AcquireMagickRandomKernel()); |
329 | 0 | return (RoundDoubleToQuantum(value)); |
330 | 0 | } |
331 | | |
332 | | /* |
333 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
334 | | % % |
335 | | % % |
336 | | % % |
337 | | % G e t O p t i m a l K e r n e l W i d t h % |
338 | | % % |
339 | | % % |
340 | | % % |
341 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
342 | | % |
343 | | % Method GetOptimalKernelWidth computes the optimal kernel radius for a |
344 | | % convolution filter. Start with the minimum value of 3 pixels and walk out |
345 | | % until we drop below the threshold of one pixel numerical accuracy, |
346 | | % |
347 | | % The format of the GetOptimalKernelWidth method is: |
348 | | % |
349 | | % int GetOptimalKernelWidth(const double radius,const double sigma) |
350 | | % |
351 | | % A description of each parameter follows: |
352 | | % |
353 | | % o width: Method GetOptimalKernelWidth returns the optimal width of |
354 | | % a convolution kernel. |
355 | | % |
356 | | % o radius: The radius of the Gaussian, in pixels, not counting the center |
357 | | % pixel. |
358 | | % |
359 | | % o sigma: The standard deviation of the Gaussian, in pixels. |
360 | | % |
361 | | % |
362 | | */ |
363 | | |
364 | | MagickExport int GetOptimalKernelWidth1D(const double radius,const double sigma) |
365 | 0 | { |
366 | 0 | double |
367 | 0 | epsilon, |
368 | 0 | normalize, |
369 | 0 | value; |
370 | |
|
371 | 0 | long |
372 | 0 | width; |
373 | |
|
374 | 0 | register long |
375 | 0 | u; |
376 | |
|
377 | 0 | if (radius > 0.0) |
378 | 0 | return((int) (2.0*ceil(radius)+1.0)); |
379 | 0 | epsilon=1.0/MaxRGBDouble; |
380 | 0 | if (epsilon < MagickEpsilon) |
381 | 0 | epsilon=MagickEpsilon; |
382 | 0 | for (width=5; ;) |
383 | 0 | { |
384 | 0 | normalize=0.0; |
385 | 0 | for (u=(-width/2); u <= (width/2); u++) |
386 | 0 | normalize+=exp(-((double) u*u)/(2.0*sigma*sigma))/(MagickSQ2PI*sigma); |
387 | 0 | u=width/2; |
388 | 0 | value=exp(-((double) u*u)/(2.0*sigma*sigma))/(MagickSQ2PI*sigma)/normalize; |
389 | 0 | if (value < epsilon) |
390 | 0 | break; |
391 | 0 | width+=2; |
392 | 0 | } |
393 | 0 | return(width-2); |
394 | 0 | } |
395 | | |
396 | | MagickExport int GetOptimalKernelWidth2D(const double radius,const double sigma) |
397 | 0 | { |
398 | 0 | double |
399 | 0 | alpha, |
400 | 0 | epsilon, |
401 | 0 | normalize, |
402 | 0 | value; |
403 | |
|
404 | 0 | long |
405 | 0 | width; |
406 | |
|
407 | 0 | register long |
408 | 0 | u, |
409 | 0 | v; |
410 | |
|
411 | 0 | if (radius > 0.0) |
412 | 0 | return((int) (2.0*ceil(radius)+1.0)); |
413 | 0 | epsilon=1.0/MaxRGBDouble; |
414 | 0 | if (epsilon < MagickEpsilon) |
415 | 0 | epsilon=MagickEpsilon; |
416 | 0 | for (width=5; ;) |
417 | 0 | { |
418 | 0 | normalize=0.0; |
419 | 0 | for (v=(-width/2); v <= (width/2); v++) |
420 | 0 | { |
421 | 0 | for (u=(-width/2); u <= (width/2); u++) |
422 | 0 | { |
423 | 0 | alpha=exp(-((double) u*u+(double) v*v)/(2.0*sigma*sigma)); |
424 | 0 | normalize+=alpha/(2.0*MagickPI*sigma*sigma); |
425 | 0 | } |
426 | 0 | } |
427 | 0 | v=width/2; |
428 | 0 | value=exp(-((double) v*v)/(2.0*sigma*sigma))/(MagickSQ2PI*sigma)/normalize; |
429 | 0 | if (value < epsilon) |
430 | 0 | break; |
431 | 0 | width+=2; |
432 | 0 | } |
433 | 0 | return(width-2); |
434 | 0 | } |
435 | | |
436 | | MagickExport int GetOptimalKernelWidth(const double radius,const double sigma) |
437 | 0 | { |
438 | 0 | return(GetOptimalKernelWidth1D(radius,sigma)); |
439 | 0 | } |
440 | | |
441 | | /* |
442 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
443 | | % % |
444 | | % % |
445 | | % % |
446 | | % H S L T r a n s f o r m % |
447 | | % % |
448 | | % % |
449 | | % % |
450 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
451 | | % |
452 | | % Method HSLTransform converts a floating point (hue, saturation, |
453 | | % luminosity) with range 0.0 to 1.0 to a (red, green, blue) triple |
454 | | % with range 0 to MaxRGB. |
455 | | % |
456 | | % The format of the HSLTransformImage method is: |
457 | | % |
458 | | % void HSLTransform(const double hue,const double saturation, |
459 | | % const double luminosity,Quantum *red,Quantum *green,Quantum *blue) |
460 | | % |
461 | | % A description of each parameter follows: |
462 | | % |
463 | | % o hue, saturation, luminosity: A double value representing a |
464 | | % component of the HSL color space. |
465 | | % |
466 | | % o red, green, blue: A pointer to a pixel component of type Quantum. |
467 | | % |
468 | | % |
469 | | */ |
470 | | MagickExport void HSLTransform(const double hue,const double saturation, |
471 | | const double luminosity,Quantum *red,Quantum *green,Quantum *blue) |
472 | 541k | { |
473 | | /* |
474 | | Convert HSL to RGB colorspace. |
475 | | */ |
476 | 541k | assert(red != (Quantum *) NULL); |
477 | 541k | assert(green != (Quantum *) NULL); |
478 | 541k | assert(blue != (Quantum *) NULL); |
479 | 541k | if (saturation == 0.0) |
480 | 3.97k | { |
481 | 3.97k | double l = MaxRGBDouble*luminosity; |
482 | 3.97k | *red=*green=*blue= RoundDoubleToQuantum(l); |
483 | 3.97k | } |
484 | 537k | else |
485 | 537k | { |
486 | 537k | double |
487 | 537k | b, |
488 | 537k | g, |
489 | 537k | r, |
490 | 537k | v, |
491 | 537k | x, |
492 | 537k | y, |
493 | 537k | z, |
494 | 537k | hue_times_six, |
495 | 537k | hue_fract, |
496 | 537k | vsf; |
497 | | |
498 | 537k | int |
499 | 537k | sextant; |
500 | | |
501 | 537k | v=(luminosity <= 0.5) ? (luminosity*(1.0+saturation)) : |
502 | 537k | (luminosity+saturation-luminosity*saturation); |
503 | | |
504 | 537k | hue_times_six=6.0*hue; |
505 | 537k | sextant=(int) hue_times_six; |
506 | 537k | hue_fract=hue_times_six-(double) sextant; |
507 | | |
508 | 537k | y=luminosity+luminosity-v; |
509 | 537k | vsf=(v-y)*hue_fract; |
510 | 537k | x=y+vsf; |
511 | 537k | z=v-vsf; |
512 | | |
513 | 537k | switch (sextant) |
514 | 537k | { |
515 | 206k | case 0: r=v; g=x; b=y; break; |
516 | 105k | case 1: r=z; g=v; b=y; break; |
517 | 78.9k | case 2: r=y; g=v; b=x; break; |
518 | 51.7k | case 3: r=y; g=z; b=v; break; |
519 | 56.1k | case 4: r=x; g=y; b=v; break; |
520 | 37.4k | case 5: r=v; g=y; b=z; break; |
521 | 968 | default: r=v; g=x; b=y; break; |
522 | 537k | } |
523 | 537k | r *= MaxRGBDouble; |
524 | 537k | *red=RoundDoubleToQuantum(r); |
525 | 537k | g *= MaxRGBDouble; |
526 | 537k | *green=RoundDoubleToQuantum(g); |
527 | 537k | b *= MaxRGBDouble; |
528 | 537k | *blue=RoundDoubleToQuantum(b); |
529 | 537k | } |
530 | 541k | } |
531 | | |
532 | | /* |
533 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
534 | | % % |
535 | | % % |
536 | | % % |
537 | | % H W B T r a n s f o r m % |
538 | | % % |
539 | | % % |
540 | | % % |
541 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
542 | | % |
543 | | % Method HWBTransform converts a (hue, whiteness, blackness) to a |
544 | | % (red, green, blue) triple. |
545 | | % |
546 | | % Algorithm derived from "HWB: A more intuitive hue-based color model." |
547 | | % Alvy Ray Smith and Eric Ray Lyons, Journal of Graphics Tools, Volume 1 |
548 | | % Number 1, 1996. http://www.acm.org/jgt/papers/SmithLyons96/ |
549 | | % |
550 | | % The format of the HWBTransformImage method is: |
551 | | % |
552 | | % void HWBTransform(const double hue,const double whiteness, |
553 | | % const double blackness,Quantum *red,Quantum *green,Quantum *blue) |
554 | | % |
555 | | % A description of each parameter follows: |
556 | | % |
557 | | % o hue, whiteness, blackness: A double value representing a |
558 | | % component of the HWB color space. |
559 | | % |
560 | | % o red, green, blue: A pointer to a pixel component of type Quantum. |
561 | | % |
562 | | % |
563 | | */ |
564 | | MagickExport void HWBTransform(const double hue,const double whiteness, |
565 | | const double blackness,Quantum *red,Quantum *green,Quantum *blue) |
566 | 441k | { |
567 | 441k | double |
568 | 441k | b, |
569 | 441k | f, |
570 | 441k | g, |
571 | 441k | n, |
572 | 441k | r, |
573 | 441k | v; |
574 | | |
575 | 441k | register unsigned int |
576 | 441k | i; |
577 | | |
578 | | /* |
579 | | Convert HWB to RGB colorspace. |
580 | | */ |
581 | 441k | assert(red != (Quantum *) NULL); |
582 | 441k | assert(green != (Quantum *) NULL); |
583 | 441k | assert(blue != (Quantum *) NULL); |
584 | 441k | v=1.0-blackness; |
585 | 441k | if (hue == 0.0) |
586 | 228 | { |
587 | 228 | v *= MaxRGBDouble; |
588 | 228 | *red=*green=*blue=RoundDoubleToQuantum(v); |
589 | 228 | return; |
590 | 228 | } |
591 | 441k | i=(unsigned int) (6.0*hue); |
592 | 441k | f=6.0*hue-i; |
593 | 441k | if (i & 0x01) |
594 | 72.5k | f=1.0-f; |
595 | 441k | n=whiteness+f*(v-whiteness); /* linear interpolation */ |
596 | 441k | switch (i) |
597 | 441k | { |
598 | 0 | default: |
599 | 362 | case 6: |
600 | 344k | case 0: r=v; g=n; b=whiteness; break; |
601 | 31.0k | case 1: r=n; g=v; b=whiteness; break; |
602 | 18.1k | case 2: r=whiteness; g=v; b=n; break; |
603 | 21.6k | case 3: r=whiteness; g=n; b=v; break; |
604 | 5.99k | case 4: r=n; g=whiteness; b=v; break; |
605 | 19.8k | case 5: r=v; g=whiteness; b=n; break; |
606 | 441k | } |
607 | 441k | r *= MaxRGBDouble; |
608 | 441k | g *= MaxRGBDouble; |
609 | 441k | b *= MaxRGBDouble; |
610 | 441k | *red=RoundDoubleToQuantum(r); |
611 | 441k | *green=RoundDoubleToQuantum(g); |
612 | 441k | *blue=RoundDoubleToQuantum(b); |
613 | 441k | } |
614 | | |
615 | | /* |
616 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
617 | | % % |
618 | | % % |
619 | | % % |
620 | | % H u l l % |
621 | | % % |
622 | | % % |
623 | | % % |
624 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
625 | | % |
626 | | % Method Hull implements the eight hull algorithm described in Applied |
627 | | % Optics, Vol. 24, No. 10, 15 May 1985, "Geometric filter for Speckle |
628 | | % Reduction", by Thomas R Crimmins. Each pixel in the image is replaced by |
629 | | % one of its eight of its surrounding pixels using a polarity and negative |
630 | | % hull function. |
631 | | % |
632 | | % The format of the Hull method is: |
633 | | % |
634 | | % void Hull(const long x_offset,const long y_offset, |
635 | | % const unsigned long columns,const unsigned long rows,Quantum *f, |
636 | | % Quantum *g,const int polarity) |
637 | | % |
638 | | % A description of each parameter follows: |
639 | | % |
640 | | % o x_offset, y_offset: An integer value representing the offset of the |
641 | | % current pixel within the image. |
642 | | % |
643 | | % o columns, rows: Specifies the number of rows and columns in the image. |
644 | | % |
645 | | % o polarity: An integer value declaring the polarity (+,-). |
646 | | % |
647 | | % o f, g: A pointer to an image pixel and one of it's neighbor. |
648 | | % |
649 | | % |
650 | | */ |
651 | | |
652 | | MagickExport void Hull(const long x_offset,const long y_offset, |
653 | | const unsigned long columns,const unsigned long rows,Quantum *f,Quantum *g, |
654 | | const int polarity) |
655 | 0 | { |
656 | | #if QuantumDepth > 16 |
657 | | typedef double SignedQuantum; |
658 | | #else |
659 | 0 | typedef int SignedQuantum; |
660 | 0 | #endif |
661 | |
|
662 | 0 | long |
663 | 0 | y; |
664 | |
|
665 | 0 | Quantum |
666 | 0 | *p, |
667 | 0 | *q, |
668 | 0 | *r, |
669 | 0 | *s; |
670 | |
|
671 | 0 | assert(f != (Quantum *) NULL); |
672 | 0 | assert(g != (Quantum *) NULL); |
673 | 0 | assert(!((size_t)columns > (size_t)columns+2)); |
674 | |
|
675 | 0 | p=f+((size_t)columns+2); |
676 | 0 | q=g+((size_t)columns+2); |
677 | |
|
678 | 0 | assert(!((p < f) || (q < g))); |
679 | |
|
680 | 0 | r=p+((ptrdiff_t)y_offset*((ptrdiff_t)columns+2)+(ptrdiff_t)x_offset); |
681 | |
|
682 | | #if defined(HAVE_OPENMP) |
683 | | # if defined(TUNE_OPENMP) |
684 | | # pragma omp parallel for schedule(runtime) |
685 | | # else |
686 | | # if defined(USE_STATIC_SCHEDULING_ONLY) |
687 | | # pragma omp parallel for schedule(static) |
688 | | # else |
689 | | # pragma omp parallel for schedule(guided) |
690 | | # endif |
691 | | # endif |
692 | | #endif |
693 | 0 | for (y=0; y < (long) rows; y++) |
694 | 0 | { |
695 | 0 | SignedQuantum |
696 | 0 | v; |
697 | |
|
698 | 0 | unsigned long |
699 | 0 | x; |
700 | |
|
701 | 0 | unsigned int |
702 | 0 | index; |
703 | |
|
704 | 0 | index=(2*y+1)+y*columns; |
705 | 0 | if (polarity > 0) |
706 | 0 | { |
707 | 0 | for (x=columns ; x != 0; x--) |
708 | 0 | { |
709 | 0 | v=(p[index]); |
710 | 0 | if (r[index] >= (v+ScaleCharToQuantum(2))) |
711 | 0 | v+=ScaleCharToQuantum(1); |
712 | 0 | q[index]=(Quantum) v; |
713 | 0 | index++; |
714 | 0 | } |
715 | 0 | } |
716 | 0 | else |
717 | 0 | { |
718 | 0 | for (x=columns ; x != 0; x--) |
719 | 0 | { |
720 | 0 | v=(p[index]); |
721 | 0 | if (r[index] <= (v-(long) ScaleCharToQuantum(2))) |
722 | 0 | v-=(long) ScaleCharToQuantum(1); |
723 | 0 | q[index]=(Quantum) v; |
724 | 0 | index++; |
725 | 0 | } |
726 | 0 | } |
727 | 0 | } |
728 | 0 | p=f+((size_t) columns+2); |
729 | 0 | q=g+((size_t) columns+2); |
730 | 0 | r=q+(y_offset*((ptrdiff_t) columns+2)+x_offset); |
731 | 0 | s=q-(y_offset*((ptrdiff_t) columns+2)+x_offset); |
732 | | #if defined(HAVE_OPENMP) |
733 | | # if defined(TUNE_OPENMP) |
734 | | # pragma omp parallel for schedule(runtime) |
735 | | # else |
736 | | # if defined(USE_STATIC_SCHEDULING_ONLY) |
737 | | # pragma omp parallel for schedule(static) |
738 | | # else |
739 | | # pragma omp parallel for schedule(guided) |
740 | | # endif |
741 | | # endif |
742 | | #endif |
743 | 0 | for (y=0; y < (long) rows; y++) |
744 | 0 | { |
745 | 0 | SignedQuantum |
746 | 0 | v; |
747 | |
|
748 | 0 | unsigned long |
749 | 0 | x; |
750 | |
|
751 | 0 | unsigned int |
752 | 0 | index; |
753 | |
|
754 | 0 | index=(2*y+1)+y*columns; |
755 | 0 | if (polarity > 0) |
756 | 0 | { |
757 | 0 | for (x=columns ; x != 0; x--) |
758 | 0 | { |
759 | 0 | v=(q[index]); |
760 | 0 | if ((s[index] >= (v+ScaleCharToQuantum(2))) && (r[index] > v)) |
761 | 0 | v+=ScaleCharToQuantum(1); |
762 | 0 | p[index]=(Quantum) v; |
763 | 0 | index++; |
764 | 0 | } |
765 | 0 | } |
766 | 0 | else |
767 | 0 | { |
768 | 0 | for (x=columns ; x != 0; x--) |
769 | 0 | { |
770 | 0 | v=(q[index]); |
771 | 0 | if ((s[index] <= (v-(long) ScaleCharToQuantum(2))) && (r[index] < v)) |
772 | 0 | v-=(long) ScaleCharToQuantum(1); |
773 | 0 | p[index]=(Quantum) v; |
774 | 0 | index++; |
775 | 0 | } |
776 | 0 | } |
777 | 0 | } |
778 | 0 | } |
779 | | |
780 | | /* |
781 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
782 | | % % |
783 | | % % |
784 | | % % |
785 | | % I d e n t i t y A f f i n e % |
786 | | % % |
787 | | % % |
788 | | % % |
789 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
790 | | % |
791 | | % Method IdentityAffine initializes the affine transform to the identity |
792 | | % matrix. |
793 | | % |
794 | | % The format of the IdentityAffine method is: |
795 | | % |
796 | | % IdentityAffine(AffineMatrix *affine) |
797 | | % |
798 | | % A description of each parameter follows: |
799 | | % |
800 | | % o affine: A pointer the the affine transform of type AffineMatrix. |
801 | | % |
802 | | % |
803 | | */ |
804 | | MagickExport void IdentityAffine(AffineMatrix *affine) |
805 | 6.54M | { |
806 | 6.54M | assert(affine != (AffineMatrix *) NULL); |
807 | 6.54M | (void) memset(affine,0,sizeof(AffineMatrix)); |
808 | 6.54M | affine->sx=1.0; |
809 | 6.54M | affine->sy=1.0; |
810 | 6.54M | } |
811 | | |
812 | | /* |
813 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
814 | | % % |
815 | | % % |
816 | | % % |
817 | | % M o d u l a t e % |
818 | | % % |
819 | | % % |
820 | | % % |
821 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
822 | | % |
823 | | % Method Modulate modulates the hue, saturation, and brightness of an |
824 | | % image. Brightness and saturation are expressed as a ratio of the |
825 | | % existing value. Hue is expressed as a ratio of rotation from the current |
826 | | % position in that 1.0 results in the existing position, 0.5 results in a |
827 | | % counter-clockwise rotation of 90 degrees, 1.5 results in a clockwise |
828 | | % rotation of 90 degrees, and 0 and 2.0 obtain a 180 degree rotation. |
829 | | % |
830 | | % The format of the Modulate method is: |
831 | | % |
832 | | % void Modulate(const double percent_hue,const double percent_saturation, |
833 | | % const double percent_brightness,Quantum *red,Quantum *green, |
834 | | % Quantum *blue) |
835 | | % |
836 | | % A description of each parameter follows: |
837 | | % |
838 | | % o percent_hue, percent_saturation, percent_brightness: A double value |
839 | | % representing the percent change in a component of the HSL color space. |
840 | | % |
841 | | % o red, green, blue: A pointer to a pixel component of type Quantum. |
842 | | % |
843 | | % |
844 | | */ |
845 | | MagickExport void Modulate(const double percent_hue, |
846 | | const double percent_saturation,const double percent_brightness, |
847 | | Quantum *red,Quantum *green,Quantum *blue) |
848 | 0 | { |
849 | 0 | double |
850 | 0 | brightness, |
851 | 0 | hue, |
852 | 0 | saturation; |
853 | | |
854 | | /* |
855 | | Increase or decrease color brightness, saturation, or hue. |
856 | | */ |
857 | 0 | assert(red != (Quantum *) NULL); |
858 | 0 | assert(green != (Quantum *) NULL); |
859 | 0 | assert(blue != (Quantum *) NULL); |
860 | 0 | TransformHSL(*red,*green,*blue,&hue,&saturation,&brightness); |
861 | 0 | brightness*=(0.01+MagickEpsilon)*percent_brightness; |
862 | 0 | if (brightness > 1.0) |
863 | 0 | brightness=1.0; |
864 | 0 | saturation*=(0.01+MagickEpsilon)*percent_saturation; |
865 | 0 | if (saturation > 1.0) |
866 | 0 | saturation=1.0; |
867 | |
|
868 | 0 | hue += (percent_hue/200.0 - 0.5); |
869 | 0 | while (hue < 0.0) |
870 | 0 | hue += 1.0; |
871 | 0 | while (hue > 1.0) |
872 | 0 | hue -= 1.0; |
873 | 0 | HSLTransform(hue,saturation,brightness,red,green,blue); |
874 | 0 | } |
875 | | |
876 | | /* |
877 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
878 | | % % |
879 | | % % |
880 | | % % |
881 | | % T r a n s f o r m H S L % |
882 | | % % |
883 | | % % |
884 | | % % |
885 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
886 | | % |
887 | | % Method TransformHSL converts a (red, green, blue) to a (hue, saturation, |
888 | | % luminosity) triple with values in range 0.0 to 1.0. |
889 | | % |
890 | | % The format of the TransformHSL method is: |
891 | | % |
892 | | % void TransformHSL(const Quantum red,const Quantum green, |
893 | | % const Quantum blue,double *hue,double *saturation,double *luminosity) |
894 | | % |
895 | | % A description of each parameter follows: |
896 | | % |
897 | | % o red, green, blue: A Quantum value representing the red, green, and |
898 | | % blue component of a pixel.. |
899 | | % |
900 | | % o hue, saturation, luminosity: A pointer to a double value representing a |
901 | | % component of the HSL color space. |
902 | | % |
903 | | % |
904 | | */ |
905 | | MagickExport void TransformHSL(const Quantum red,const Quantum green, |
906 | | const Quantum blue,double *hue_result,double *saturation_result,double *luminosity_result) |
907 | 0 | { |
908 | 0 | double |
909 | 0 | hue, |
910 | 0 | saturation, |
911 | 0 | luminosity, |
912 | 0 | b, |
913 | 0 | delta, |
914 | 0 | g, |
915 | 0 | max, |
916 | 0 | min, |
917 | 0 | r; |
918 | | |
919 | | /* |
920 | | Convert RGB to HSL colorspace. |
921 | | */ |
922 | 0 | assert(hue_result != (double *) NULL); |
923 | 0 | assert(saturation_result != (double *) NULL); |
924 | 0 | assert(luminosity_result != (double *) NULL); |
925 | |
|
926 | 0 | r=(double) red/MaxRGBDouble; |
927 | 0 | g=(double) green/MaxRGBDouble; |
928 | 0 | b=(double) blue/MaxRGBDouble; |
929 | 0 | max=Max(r,Max(g,b)); |
930 | 0 | min=Min(r,Min(g,b)); |
931 | 0 | hue=0.0; |
932 | 0 | saturation=0.0; |
933 | 0 | luminosity=(min+max)/2.0; |
934 | 0 | delta=max-min; |
935 | 0 | if (delta != 0.0) |
936 | 0 | { |
937 | 0 | saturation=delta/((luminosity <= 0.5) ? (min+max) : (2.0-max-min)); |
938 | 0 | if (r == max) |
939 | 0 | hue=(g == min ? 5.0+(max-b)/delta : 1.0-(max-g)/delta); |
940 | 0 | else |
941 | 0 | if (g == max) |
942 | 0 | hue=(b == min ? 1.0+(max-r)/delta : 3.0-(max-b)/delta); |
943 | 0 | else |
944 | 0 | hue=(r == min ? 3.0+(max-g)/delta : 5.0-(max-r)/delta); |
945 | 0 | hue/=6.0; |
946 | 0 | } |
947 | |
|
948 | 0 | *hue_result=ConstrainToRange(0.0,1.0,hue); |
949 | 0 | *saturation_result=ConstrainToRange(0.0,1.0,saturation); |
950 | 0 | *luminosity_result=ConstrainToRange(0.0,1.0,luminosity); |
951 | 0 | } |
952 | | |
953 | | /* |
954 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
955 | | % % |
956 | | % % |
957 | | % % |
958 | | % T r a n s f o r m H W B % |
959 | | % % |
960 | | % % |
961 | | % % |
962 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
963 | | % |
964 | | % Method TransformHWB converts a (red, green, blue) to a |
965 | | % (hue, whiteness, blackness) triple. |
966 | | % |
967 | | % Algorithm derived from "HWB: A more intuitive hue-based color model." |
968 | | % Alvy Ray Smith and Eric Ray Lyons, Journal of Graphics Tools, Volume 1 |
969 | | % Number 1, 1996. http://www.acm.org/jgt/papers/SmithLyons96/ |
970 | | % |
971 | | % The format of the TransformHWB method is: |
972 | | % |
973 | | % void TransformHWB(const Quantum red,const Quantum green, |
974 | | % const Quantum blue,double *hue,double *whiteness,double *blackness) |
975 | | % |
976 | | % A description of each parameter follows: |
977 | | % |
978 | | % o red, green, blue: A Quantum value representing the red, green, and |
979 | | % blue component of a pixel. |
980 | | % |
981 | | % o hue, whiteness, blackness: A pointer to a double value representing a |
982 | | % component of the HWB color space. |
983 | | % |
984 | | % |
985 | | */ |
986 | | MagickExport void TransformHWB(const Quantum red,const Quantum green, const Quantum blue, |
987 | | double *hue,double *whiteness,double *blackness) |
988 | 0 | { |
989 | 0 | double |
990 | 0 | f, |
991 | 0 | v, |
992 | 0 | w; |
993 | |
|
994 | 0 | register long |
995 | 0 | i; |
996 | | |
997 | | /* |
998 | | Convert RGB to HWB colorspace. |
999 | | */ |
1000 | 0 | assert(hue != (double *) NULL); |
1001 | 0 | assert(whiteness != (double *) NULL); |
1002 | 0 | assert(blackness != (double *) NULL); |
1003 | 0 | w=(double) Min(red,Min(green,blue)); |
1004 | 0 | v=(double) Max(red,Max(green,blue)); |
1005 | 0 | *blackness=((double) MaxRGBDouble-v)/MaxRGBDouble; |
1006 | 0 | if (v == w) |
1007 | 0 | { |
1008 | 0 | *hue=0.0; |
1009 | 0 | *whiteness=1.0-(*blackness); |
1010 | 0 | } |
1011 | 0 | else |
1012 | 0 | { |
1013 | 0 | f=(red == w) ? (double) green-blue : |
1014 | 0 | ((green == w) ? (double) blue-red : |
1015 | 0 | (double) red-green); |
1016 | 0 | i=(red == w) ? 3 : ((green == w) ? 5 : 1); |
1017 | 0 | *hue=((double) i-f/(v-w))/6.0; |
1018 | 0 | *whiteness=((double) w/MaxRGBDouble); |
1019 | 0 | } |
1020 | 0 | } |