Coverage Report

Created: 2026-06-16 07:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}