• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                   EEEEE  FFFFF  FFFFF  EEEEE  CCCC  TTTTT                   %
7 %                   E      F      F      E     C        T                     %
8 %                   EEE    FFF    FFF    EEE   C        T                     %
9 %                   E      F      F      E     C        T                     %
10 %                   EEEEE  F      F      EEEEE  CCCC    T                     %
11 %                                                                             %
12 %                                                                             %
13 %                       MagickCore Image Effects Methods                      %
14 %                                                                             %
15 %                               Software Design                               %
16 %                                    Cristy                                   %
17 %                                 October 1996                                %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2019 ImageMagick Studio LLC, a non-profit organization      %
21 %  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 %    https://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 */
43 #include "MagickCore/studio.h"
44 #include "MagickCore/accelerate-private.h"
45 #include "MagickCore/blob.h"
46 #include "MagickCore/cache-view.h"
47 #include "MagickCore/color.h"
48 #include "MagickCore/color-private.h"
49 #include "MagickCore/colorspace.h"
50 #include "MagickCore/constitute.h"
51 #include "MagickCore/decorate.h"
52 #include "MagickCore/distort.h"
53 #include "MagickCore/draw.h"
54 #include "MagickCore/enhance.h"
55 #include "MagickCore/exception.h"
56 #include "MagickCore/exception-private.h"
57 #include "MagickCore/effect.h"
58 #include "MagickCore/fx.h"
59 #include "MagickCore/gem.h"
60 #include "MagickCore/gem-private.h"
61 #include "MagickCore/geometry.h"
62 #include "MagickCore/image-private.h"
63 #include "MagickCore/list.h"
64 #include "MagickCore/log.h"
65 #include "MagickCore/matrix.h"
66 #include "MagickCore/memory_.h"
67 #include "MagickCore/memory-private.h"
68 #include "MagickCore/monitor.h"
69 #include "MagickCore/monitor-private.h"
70 #include "MagickCore/montage.h"
71 #include "MagickCore/morphology.h"
72 #include "MagickCore/morphology-private.h"
73 #include "MagickCore/paint.h"
74 #include "MagickCore/pixel-accessor.h"
75 #include "MagickCore/pixel-private.h"
76 #include "MagickCore/property.h"
77 #include "MagickCore/quantize.h"
78 #include "MagickCore/quantum.h"
79 #include "MagickCore/quantum-private.h"
80 #include "MagickCore/random_.h"
81 #include "MagickCore/random-private.h"
82 #include "MagickCore/resample.h"
83 #include "MagickCore/resample-private.h"
84 #include "MagickCore/resize.h"
85 #include "MagickCore/resource_.h"
86 #include "MagickCore/segment.h"
87 #include "MagickCore/shear.h"
88 #include "MagickCore/signature-private.h"
89 #include "MagickCore/statistic.h"
90 #include "MagickCore/string_.h"
91 #include "MagickCore/thread-private.h"
92 #include "MagickCore/transform.h"
93 #include "MagickCore/threshold.h"
94 
95 /*
96 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
97 %                                                                             %
98 %                                                                             %
99 %                                                                             %
100 %     A d a p t i v e B l u r I m a g e                                       %
101 %                                                                             %
102 %                                                                             %
103 %                                                                             %
104 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
105 %
106 %  AdaptiveBlurImage() adaptively blurs the image by blurring less
107 %  intensely near image edges and more intensely far from edges.  We blur the
108 %  image with a Gaussian operator of the given radius and standard deviation
109 %  (sigma).  For reasonable results, radius should be larger than sigma.  Use a
110 %  radius of 0 and AdaptiveBlurImage() selects a suitable radius for you.
111 %
112 %  The format of the AdaptiveBlurImage method is:
113 %
114 %      Image *AdaptiveBlurImage(const Image *image,const double radius,
115 %        const double sigma,ExceptionInfo *exception)
116 %
117 %  A description of each parameter follows:
118 %
119 %    o image: the image.
120 %
121 %    o radius: the radius of the Gaussian, in pixels, not counting the center
122 %      pixel.
123 %
124 %    o sigma: the standard deviation of the Laplacian, in pixels.
125 %
126 %    o exception: return any errors or warnings in this structure.
127 %
128 */
AdaptiveBlurImage(const Image * image,const double radius,const double sigma,ExceptionInfo * exception)129 MagickExport Image *AdaptiveBlurImage(const Image *image,const double radius,
130   const double sigma,ExceptionInfo *exception)
131 {
132 #define AdaptiveBlurImageTag  "Convolve/Image"
133 #define MagickSigma  (fabs(sigma) < MagickEpsilon ? MagickEpsilon : sigma)
134 
135   CacheView
136     *blur_view,
137     *edge_view,
138     *image_view;
139 
140   double
141     normalize,
142     **kernel;
143 
144   Image
145     *blur_image,
146     *edge_image,
147     *gaussian_image;
148 
149   MagickBooleanType
150     status;
151 
152   MagickOffsetType
153     progress;
154 
155   register ssize_t
156     i;
157 
158   size_t
159     width;
160 
161   ssize_t
162     j,
163     k,
164     u,
165     v,
166     y;
167 
168   assert(image != (const Image *) NULL);
169   assert(image->signature == MagickCoreSignature);
170   if (image->debug != MagickFalse)
171     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
172   assert(exception != (ExceptionInfo *) NULL);
173   assert(exception->signature == MagickCoreSignature);
174   blur_image=CloneImage(image,0,0,MagickTrue,exception);
175   if (blur_image == (Image *) NULL)
176     return((Image *) NULL);
177   if (fabs(sigma) < MagickEpsilon)
178     return(blur_image);
179   if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
180     {
181       blur_image=DestroyImage(blur_image);
182       return((Image *) NULL);
183     }
184   /*
185     Edge detect the image brightness channel, level, blur, and level again.
186   */
187   edge_image=EdgeImage(image,radius,exception);
188   if (edge_image == (Image *) NULL)
189     {
190       blur_image=DestroyImage(blur_image);
191       return((Image *) NULL);
192     }
193   (void) AutoLevelImage(edge_image,exception);
194   gaussian_image=BlurImage(edge_image,radius,sigma,exception);
195   if (gaussian_image != (Image *) NULL)
196     {
197       edge_image=DestroyImage(edge_image);
198       edge_image=gaussian_image;
199     }
200   (void) AutoLevelImage(edge_image,exception);
201   /*
202     Create a set of kernels from maximum (radius,sigma) to minimum.
203   */
204   width=GetOptimalKernelWidth2D(radius,sigma);
205   kernel=(double **) MagickAssumeAligned(AcquireAlignedMemory((size_t) width,
206     sizeof(*kernel)));
207   if (kernel == (double **) NULL)
208     {
209       edge_image=DestroyImage(edge_image);
210       blur_image=DestroyImage(blur_image);
211       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
212     }
213   (void) memset(kernel,0,(size_t) width*sizeof(*kernel));
214   for (i=0; i < (ssize_t) width; i+=2)
215   {
216     kernel[i]=(double *) MagickAssumeAligned(AcquireAlignedMemory(
217       (size_t) (width-i),(width-i)*sizeof(**kernel)));
218     if (kernel[i] == (double *) NULL)
219       break;
220     normalize=0.0;
221     j=(ssize_t) (width-i-1)/2;
222     k=0;
223     for (v=(-j); v <= j; v++)
224     {
225       for (u=(-j); u <= j; u++)
226       {
227         kernel[i][k]=(double) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
228           MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
229         normalize+=kernel[i][k];
230         k++;
231       }
232     }
233     kernel[i][(k-1)/2]+=(double) (1.0-normalize);
234     if (sigma < MagickEpsilon)
235       kernel[i][(k-1)/2]=1.0;
236   }
237   if (i < (ssize_t) width)
238     {
239       for (i-=2; i >= 0; i-=2)
240         kernel[i]=(double *) RelinquishAlignedMemory(kernel[i]);
241       kernel=(double **) RelinquishAlignedMemory(kernel);
242       edge_image=DestroyImage(edge_image);
243       blur_image=DestroyImage(blur_image);
244       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
245     }
246   /*
247     Adaptively blur image.
248   */
249   status=MagickTrue;
250   progress=0;
251   image_view=AcquireVirtualCacheView(image,exception);
252   edge_view=AcquireVirtualCacheView(edge_image,exception);
253   blur_view=AcquireAuthenticCacheView(blur_image,exception);
254 #if defined(MAGICKCORE_OPENMP_SUPPORT)
255   #pragma omp parallel for schedule(static) shared(progress,status) \
256     magick_number_threads(image,blur_image,blur_image->rows,1)
257 #endif
258   for (y=0; y < (ssize_t) blur_image->rows; y++)
259   {
260     register const Quantum
261       *magick_restrict r;
262 
263     register Quantum
264       *magick_restrict q;
265 
266     register ssize_t
267       x;
268 
269     if (status == MagickFalse)
270       continue;
271     r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
272     q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
273       exception);
274     if ((r == (const Quantum *) NULL) || (q == (Quantum *) NULL))
275       {
276         status=MagickFalse;
277         continue;
278       }
279     for (x=0; x < (ssize_t) blur_image->columns; x++)
280     {
281       register const Quantum
282         *magick_restrict p;
283 
284       register ssize_t
285         i;
286 
287       ssize_t
288         center,
289         j;
290 
291       j=(ssize_t) ceil((double) width*(1.0-QuantumScale*
292         GetPixelIntensity(edge_image,r))-0.5);
293       if (j < 0)
294         j=0;
295       else
296         if (j > (ssize_t) width)
297           j=(ssize_t) width;
298       if ((j & 0x01) != 0)
299         j--;
300       p=GetCacheViewVirtualPixels(image_view,x-((ssize_t) (width-j)/2L),y-
301         (ssize_t) ((width-j)/2L),width-j,width-j,exception);
302       if (p == (const Quantum *) NULL)
303         break;
304       center=(ssize_t) GetPixelChannels(image)*(width-j)*((width-j)/2L)+
305         GetPixelChannels(image)*((width-j)/2);
306       for (i=0; i < (ssize_t) GetPixelChannels(blur_image); i++)
307       {
308         double
309           alpha,
310           gamma,
311           pixel;
312 
313         PixelChannel
314           channel;
315 
316         PixelTrait
317           blur_traits,
318           traits;
319 
320         register const double
321           *magick_restrict k;
322 
323         register const Quantum
324           *magick_restrict pixels;
325 
326         register ssize_t
327           u;
328 
329         ssize_t
330           v;
331 
332         channel=GetPixelChannelChannel(image,i);
333         traits=GetPixelChannelTraits(image,channel);
334         blur_traits=GetPixelChannelTraits(blur_image,channel);
335         if ((traits == UndefinedPixelTrait) ||
336             (blur_traits == UndefinedPixelTrait))
337           continue;
338         if ((blur_traits & CopyPixelTrait) != 0)
339           {
340             SetPixelChannel(blur_image,channel,p[center+i],q);
341             continue;
342           }
343         k=kernel[j];
344         pixels=p;
345         pixel=0.0;
346         gamma=0.0;
347         if ((blur_traits & BlendPixelTrait) == 0)
348           {
349             /*
350               No alpha blending.
351             */
352             for (v=0; v < (ssize_t) (width-j); v++)
353             {
354               for (u=0; u < (ssize_t) (width-j); u++)
355               {
356                 pixel+=(*k)*pixels[i];
357                 gamma+=(*k);
358                 k++;
359                 pixels+=GetPixelChannels(image);
360               }
361             }
362             gamma=PerceptibleReciprocal(gamma);
363             SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
364             continue;
365           }
366         /*
367           Alpha blending.
368         */
369         for (v=0; v < (ssize_t) (width-j); v++)
370         {
371           for (u=0; u < (ssize_t) (width-j); u++)
372           {
373             alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
374             pixel+=(*k)*alpha*pixels[i];
375             gamma+=(*k)*alpha;
376             k++;
377             pixels+=GetPixelChannels(image);
378           }
379         }
380         gamma=PerceptibleReciprocal(gamma);
381         SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
382       }
383       q+=GetPixelChannels(blur_image);
384       r+=GetPixelChannels(edge_image);
385     }
386     if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
387       status=MagickFalse;
388     if (image->progress_monitor != (MagickProgressMonitor) NULL)
389       {
390         MagickBooleanType
391           proceed;
392 
393 #if defined(MAGICKCORE_OPENMP_SUPPORT)
394         #pragma omp atomic
395 #endif
396         progress++;
397         proceed=SetImageProgress(image,AdaptiveBlurImageTag,progress,
398           image->rows);
399         if (proceed == MagickFalse)
400           status=MagickFalse;
401       }
402   }
403   blur_image->type=image->type;
404   blur_view=DestroyCacheView(blur_view);
405   edge_view=DestroyCacheView(edge_view);
406   image_view=DestroyCacheView(image_view);
407   edge_image=DestroyImage(edge_image);
408   for (i=0; i < (ssize_t) width; i+=2)
409     kernel[i]=(double *) RelinquishAlignedMemory(kernel[i]);
410   kernel=(double **) RelinquishAlignedMemory(kernel);
411   if (status == MagickFalse)
412     blur_image=DestroyImage(blur_image);
413   return(blur_image);
414 }
415 
416 /*
417 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
418 %                                                                             %
419 %                                                                             %
420 %                                                                             %
421 %     A d a p t i v e S h a r p e n I m a g e                                 %
422 %                                                                             %
423 %                                                                             %
424 %                                                                             %
425 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
426 %
427 %  AdaptiveSharpenImage() adaptively sharpens the image by sharpening more
428 %  intensely near image edges and less intensely far from edges. We sharpen the
429 %  image with a Gaussian operator of the given radius and standard deviation
430 %  (sigma).  For reasonable results, radius should be larger than sigma.  Use a
431 %  radius of 0 and AdaptiveSharpenImage() selects a suitable radius for you.
432 %
433 %  The format of the AdaptiveSharpenImage method is:
434 %
435 %      Image *AdaptiveSharpenImage(const Image *image,const double radius,
436 %        const double sigma,ExceptionInfo *exception)
437 %
438 %  A description of each parameter follows:
439 %
440 %    o image: the image.
441 %
442 %    o radius: the radius of the Gaussian, in pixels, not counting the center
443 %      pixel.
444 %
445 %    o sigma: the standard deviation of the Laplacian, in pixels.
446 %
447 %    o exception: return any errors or warnings in this structure.
448 %
449 */
AdaptiveSharpenImage(const Image * image,const double radius,const double sigma,ExceptionInfo * exception)450 MagickExport Image *AdaptiveSharpenImage(const Image *image,const double radius,
451   const double sigma,ExceptionInfo *exception)
452 {
453 #define AdaptiveSharpenImageTag  "Convolve/Image"
454 #define MagickSigma  (fabs(sigma) < MagickEpsilon ? MagickEpsilon : sigma)
455 
456   CacheView
457     *sharp_view,
458     *edge_view,
459     *image_view;
460 
461   double
462     normalize,
463     **kernel;
464 
465   Image
466     *sharp_image,
467     *edge_image,
468     *gaussian_image;
469 
470   MagickBooleanType
471     status;
472 
473   MagickOffsetType
474     progress;
475 
476   register ssize_t
477     i;
478 
479   size_t
480     width;
481 
482   ssize_t
483     j,
484     k,
485     u,
486     v,
487     y;
488 
489   assert(image != (const Image *) NULL);
490   assert(image->signature == MagickCoreSignature);
491   if (image->debug != MagickFalse)
492     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
493   assert(exception != (ExceptionInfo *) NULL);
494   assert(exception->signature == MagickCoreSignature);
495   sharp_image=CloneImage(image,0,0,MagickTrue,exception);
496   if (sharp_image == (Image *) NULL)
497     return((Image *) NULL);
498   if (fabs(sigma) < MagickEpsilon)
499     return(sharp_image);
500   if (SetImageStorageClass(sharp_image,DirectClass,exception) == MagickFalse)
501     {
502       sharp_image=DestroyImage(sharp_image);
503       return((Image *) NULL);
504     }
505   /*
506     Edge detect the image brightness channel, level, sharp, and level again.
507   */
508   edge_image=EdgeImage(image,radius,exception);
509   if (edge_image == (Image *) NULL)
510     {
511       sharp_image=DestroyImage(sharp_image);
512       return((Image *) NULL);
513     }
514   (void) AutoLevelImage(edge_image,exception);
515   gaussian_image=BlurImage(edge_image,radius,sigma,exception);
516   if (gaussian_image != (Image *) NULL)
517     {
518       edge_image=DestroyImage(edge_image);
519       edge_image=gaussian_image;
520     }
521   (void) AutoLevelImage(edge_image,exception);
522   /*
523     Create a set of kernels from maximum (radius,sigma) to minimum.
524   */
525   width=GetOptimalKernelWidth2D(radius,sigma);
526   kernel=(double **) MagickAssumeAligned(AcquireAlignedMemory((size_t)
527     width,sizeof(*kernel)));
528   if (kernel == (double **) NULL)
529     {
530       edge_image=DestroyImage(edge_image);
531       sharp_image=DestroyImage(sharp_image);
532       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
533     }
534   (void) memset(kernel,0,(size_t) width*sizeof(*kernel));
535   for (i=0; i < (ssize_t) width; i+=2)
536   {
537     kernel[i]=(double *) MagickAssumeAligned(AcquireAlignedMemory((size_t)
538       (width-i),(width-i)*sizeof(**kernel)));
539     if (kernel[i] == (double *) NULL)
540       break;
541     normalize=0.0;
542     j=(ssize_t) (width-i-1)/2;
543     k=0;
544     for (v=(-j); v <= j; v++)
545     {
546       for (u=(-j); u <= j; u++)
547       {
548         kernel[i][k]=(double) (-exp(-((double) u*u+v*v)/(2.0*MagickSigma*
549           MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
550         normalize+=kernel[i][k];
551         k++;
552       }
553     }
554     kernel[i][(k-1)/2]=(double) ((-2.0)*normalize);
555     if (sigma < MagickEpsilon)
556       kernel[i][(k-1)/2]=1.0;
557   }
558   if (i < (ssize_t) width)
559     {
560       for (i-=2; i >= 0; i-=2)
561         kernel[i]=(double *) RelinquishAlignedMemory(kernel[i]);
562       kernel=(double **) RelinquishAlignedMemory(kernel);
563       edge_image=DestroyImage(edge_image);
564       sharp_image=DestroyImage(sharp_image);
565       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
566     }
567   /*
568     Adaptively sharpen image.
569   */
570   status=MagickTrue;
571   progress=0;
572   image_view=AcquireVirtualCacheView(image,exception);
573   edge_view=AcquireVirtualCacheView(edge_image,exception);
574   sharp_view=AcquireAuthenticCacheView(sharp_image,exception);
575 #if defined(MAGICKCORE_OPENMP_SUPPORT)
576   #pragma omp parallel for schedule(static) shared(progress,status) \
577     magick_number_threads(image,sharp_image,sharp_image->rows,1)
578 #endif
579   for (y=0; y < (ssize_t) sharp_image->rows; y++)
580   {
581     register const Quantum
582       *magick_restrict r;
583 
584     register Quantum
585       *magick_restrict q;
586 
587     register ssize_t
588       x;
589 
590     if (status == MagickFalse)
591       continue;
592     r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
593     q=QueueCacheViewAuthenticPixels(sharp_view,0,y,sharp_image->columns,1,
594       exception);
595     if ((r == (const Quantum *) NULL) || (q == (Quantum *) NULL))
596       {
597         status=MagickFalse;
598         continue;
599       }
600     for (x=0; x < (ssize_t) sharp_image->columns; x++)
601     {
602       register const Quantum
603         *magick_restrict p;
604 
605       register ssize_t
606         i;
607 
608       ssize_t
609         center,
610         j;
611 
612       j=(ssize_t) ceil((double) width*(1.0-QuantumScale*
613         GetPixelIntensity(edge_image,r))-0.5);
614       if (j < 0)
615         j=0;
616       else
617         if (j > (ssize_t) width)
618           j=(ssize_t) width;
619       if ((j & 0x01) != 0)
620         j--;
621       p=GetCacheViewVirtualPixels(image_view,x-((ssize_t) (width-j)/2L),y-
622         (ssize_t) ((width-j)/2L),width-j,width-j,exception);
623       if (p == (const Quantum *) NULL)
624         break;
625       center=(ssize_t) GetPixelChannels(image)*(width-j)*((width-j)/2L)+
626         GetPixelChannels(image)*((width-j)/2);
627       for (i=0; i < (ssize_t) GetPixelChannels(sharp_image); i++)
628       {
629         double
630           alpha,
631           gamma,
632           pixel;
633 
634         PixelChannel
635           channel;
636 
637         PixelTrait
638           sharp_traits,
639           traits;
640 
641         register const double
642           *magick_restrict k;
643 
644         register const Quantum
645           *magick_restrict pixels;
646 
647         register ssize_t
648           u;
649 
650         ssize_t
651           v;
652 
653         channel=GetPixelChannelChannel(image,i);
654         traits=GetPixelChannelTraits(image,channel);
655         sharp_traits=GetPixelChannelTraits(sharp_image,channel);
656         if ((traits == UndefinedPixelTrait) ||
657             (sharp_traits == UndefinedPixelTrait))
658           continue;
659         if ((sharp_traits & CopyPixelTrait) != 0)
660           {
661             SetPixelChannel(sharp_image,channel,p[center+i],q);
662             continue;
663           }
664         k=kernel[j];
665         pixels=p;
666         pixel=0.0;
667         gamma=0.0;
668         if ((sharp_traits & BlendPixelTrait) == 0)
669           {
670             /*
671               No alpha blending.
672             */
673             for (v=0; v < (ssize_t) (width-j); v++)
674             {
675               for (u=0; u < (ssize_t) (width-j); u++)
676               {
677                 pixel+=(*k)*pixels[i];
678                 gamma+=(*k);
679                 k++;
680                 pixels+=GetPixelChannels(image);
681               }
682             }
683             gamma=PerceptibleReciprocal(gamma);
684             SetPixelChannel(sharp_image,channel,ClampToQuantum(gamma*pixel),q);
685             continue;
686           }
687         /*
688           Alpha blending.
689         */
690         for (v=0; v < (ssize_t) (width-j); v++)
691         {
692           for (u=0; u < (ssize_t) (width-j); u++)
693           {
694             alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
695             pixel+=(*k)*alpha*pixels[i];
696             gamma+=(*k)*alpha;
697             k++;
698             pixels+=GetPixelChannels(image);
699           }
700         }
701         gamma=PerceptibleReciprocal(gamma);
702         SetPixelChannel(sharp_image,channel,ClampToQuantum(gamma*pixel),q);
703       }
704       q+=GetPixelChannels(sharp_image);
705       r+=GetPixelChannels(edge_image);
706     }
707     if (SyncCacheViewAuthenticPixels(sharp_view,exception) == MagickFalse)
708       status=MagickFalse;
709     if (image->progress_monitor != (MagickProgressMonitor) NULL)
710       {
711         MagickBooleanType
712           proceed;
713 
714 #if defined(MAGICKCORE_OPENMP_SUPPORT)
715         #pragma omp atomic
716 #endif
717         progress++;
718         proceed=SetImageProgress(image,AdaptiveSharpenImageTag,progress,
719           image->rows);
720         if (proceed == MagickFalse)
721           status=MagickFalse;
722       }
723   }
724   sharp_image->type=image->type;
725   sharp_view=DestroyCacheView(sharp_view);
726   edge_view=DestroyCacheView(edge_view);
727   image_view=DestroyCacheView(image_view);
728   edge_image=DestroyImage(edge_image);
729   for (i=0; i < (ssize_t) width; i+=2)
730     kernel[i]=(double *) RelinquishAlignedMemory(kernel[i]);
731   kernel=(double **) RelinquishAlignedMemory(kernel);
732   if (status == MagickFalse)
733     sharp_image=DestroyImage(sharp_image);
734   return(sharp_image);
735 }
736 
737 /*
738 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
739 %                                                                             %
740 %                                                                             %
741 %                                                                             %
742 %     B l u r I m a g e                                                       %
743 %                                                                             %
744 %                                                                             %
745 %                                                                             %
746 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
747 %
748 %  BlurImage() blurs an image.  We convolve the image with a Gaussian operator
749 %  of the given radius and standard deviation (sigma).  For reasonable results,
750 %  the radius should be larger than sigma.  Use a radius of 0 and BlurImage()
751 %  selects a suitable radius for you.
752 %
753 %  The format of the BlurImage method is:
754 %
755 %      Image *BlurImage(const Image *image,const double radius,
756 %        const double sigma,ExceptionInfo *exception)
757 %
758 %  A description of each parameter follows:
759 %
760 %    o image: the image.
761 %
762 %    o radius: the radius of the Gaussian, in pixels, not counting the center
763 %      pixel.
764 %
765 %    o sigma: the standard deviation of the Gaussian, in pixels.
766 %
767 %    o exception: return any errors or warnings in this structure.
768 %
769 */
BlurImage(const Image * image,const double radius,const double sigma,ExceptionInfo * exception)770 MagickExport Image *BlurImage(const Image *image,const double radius,
771   const double sigma,ExceptionInfo *exception)
772 {
773   char
774     geometry[MagickPathExtent];
775 
776   KernelInfo
777     *kernel_info;
778 
779   Image
780     *blur_image;
781 
782   assert(image != (const Image *) NULL);
783   assert(image->signature == MagickCoreSignature);
784   if (image->debug != MagickFalse)
785     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
786   assert(exception != (ExceptionInfo *) NULL);
787   assert(exception->signature == MagickCoreSignature);
788 #if defined(MAGICKCORE_OPENCL_SUPPORT)
789   blur_image=AccelerateBlurImage(image,radius,sigma,exception);
790   if (blur_image != (Image *) NULL)
791     return(blur_image);
792 #endif
793   (void) FormatLocaleString(geometry,MagickPathExtent,
794     "blur:%.20gx%.20g;blur:%.20gx%.20g+90",radius,sigma,radius,sigma);
795   kernel_info=AcquireKernelInfo(geometry,exception);
796   if (kernel_info == (KernelInfo *) NULL)
797     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
798   blur_image=ConvolveImage(image,kernel_info,exception);
799   kernel_info=DestroyKernelInfo(kernel_info);
800   return(blur_image);
801 }
802 
803 /*
804 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
805 %                                                                             %
806 %                                                                             %
807 %                                                                             %
808 %     C o n v o l v e I m a g e                                               %
809 %                                                                             %
810 %                                                                             %
811 %                                                                             %
812 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
813 %
814 %  ConvolveImage() applies a custom convolution kernel to the image.
815 %
816 %  The format of the ConvolveImage method is:
817 %
818 %      Image *ConvolveImage(const Image *image,const KernelInfo *kernel,
819 %        ExceptionInfo *exception)
820 %
821 %  A description of each parameter follows:
822 %
823 %    o image: the image.
824 %
825 %    o kernel: the filtering kernel.
826 %
827 %    o exception: return any errors or warnings in this structure.
828 %
829 */
ConvolveImage(const Image * image,const KernelInfo * kernel_info,ExceptionInfo * exception)830 MagickExport Image *ConvolveImage(const Image *image,
831   const KernelInfo *kernel_info,ExceptionInfo *exception)
832 {
833   Image
834     *convolve_image;
835 
836 #if defined(MAGICKCORE_OPENCL_SUPPORT)
837   convolve_image=AccelerateConvolveImage(image,kernel_info,exception);
838   if (convolve_image != (Image *) NULL)
839     return(convolve_image);
840 #endif
841 
842   convolve_image=MorphologyImage(image,ConvolveMorphology,1,kernel_info,
843     exception);
844   return(convolve_image);
845 }
846 
847 /*
848 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
849 %                                                                             %
850 %                                                                             %
851 %                                                                             %
852 %     D e s p e c k l e I m a g e                                             %
853 %                                                                             %
854 %                                                                             %
855 %                                                                             %
856 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
857 %
858 %  DespeckleImage() reduces the speckle noise in an image while perserving the
859 %  edges of the original image.  A speckle removing filter uses a complementary
860 %  hulling technique (raising pixels that are darker than their surrounding
861 %  neighbors, then complementarily lowering pixels that are brighter than their
862 %  surrounding neighbors) to reduce the speckle index of that image (reference
863 %  Crimmins speckle removal).
864 %
865 %  The format of the DespeckleImage method is:
866 %
867 %      Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
868 %
869 %  A description of each parameter follows:
870 %
871 %    o image: the image.
872 %
873 %    o exception: return any errors or warnings in this structure.
874 %
875 */
876 
Hull(const Image * image,const ssize_t x_offset,const ssize_t y_offset,const size_t columns,const size_t rows,const int polarity,Quantum * magick_restrict f,Quantum * magick_restrict g)877 static void Hull(const Image *image,const ssize_t x_offset,
878   const ssize_t y_offset,const size_t columns,const size_t rows,
879   const int polarity,Quantum *magick_restrict f,Quantum *magick_restrict g)
880 {
881   register Quantum
882     *p,
883     *q,
884     *r,
885     *s;
886 
887   ssize_t
888     y;
889 
890   assert(image != (const Image *) NULL);
891   assert(image->signature == MagickCoreSignature);
892   if (image->debug != MagickFalse)
893     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
894   assert(f != (Quantum *) NULL);
895   assert(g != (Quantum *) NULL);
896   p=f+(columns+2);
897   q=g+(columns+2);
898   r=p+(y_offset*(columns+2)+x_offset);
899 #if defined(MAGICKCORE_OPENMP_SUPPORT)
900   #pragma omp parallel for schedule(static) \
901     magick_number_threads(image,image,rows,1)
902 #endif
903   for (y=0; y < (ssize_t) rows; y++)
904   {
905     MagickRealType
906       v;
907 
908     register ssize_t
909       i,
910       x;
911 
912     i=(2*y+1)+y*columns;
913     if (polarity > 0)
914       for (x=0; x < (ssize_t) columns; x++)
915       {
916         v=(MagickRealType) p[i];
917         if ((MagickRealType) r[i] >= (v+ScaleCharToQuantum(2)))
918           v+=ScaleCharToQuantum(1);
919         q[i]=(Quantum) v;
920         i++;
921       }
922     else
923       for (x=0; x < (ssize_t) columns; x++)
924       {
925         v=(MagickRealType) p[i];
926         if ((MagickRealType) r[i] <= (v-ScaleCharToQuantum(2)))
927           v-=ScaleCharToQuantum(1);
928         q[i]=(Quantum) v;
929         i++;
930       }
931   }
932   p=f+(columns+2);
933   q=g+(columns+2);
934   r=q+(y_offset*(columns+2)+x_offset);
935   s=q-(y_offset*(columns+2)+x_offset);
936 #if defined(MAGICKCORE_OPENMP_SUPPORT)
937   #pragma omp parallel for schedule(static) \
938     magick_number_threads(image,image,rows,1)
939 #endif
940   for (y=0; y < (ssize_t) rows; y++)
941   {
942     register ssize_t
943       i,
944       x;
945 
946     MagickRealType
947       v;
948 
949     i=(2*y+1)+y*columns;
950     if (polarity > 0)
951       for (x=0; x < (ssize_t) columns; x++)
952       {
953         v=(MagickRealType) q[i];
954         if (((MagickRealType) s[i] >= (v+ScaleCharToQuantum(2))) &&
955             ((MagickRealType) r[i] > v))
956           v+=ScaleCharToQuantum(1);
957         p[i]=(Quantum) v;
958         i++;
959       }
960     else
961       for (x=0; x < (ssize_t) columns; x++)
962       {
963         v=(MagickRealType) q[i];
964         if (((MagickRealType) s[i] <= (v-ScaleCharToQuantum(2))) &&
965             ((MagickRealType) r[i] < v))
966           v-=ScaleCharToQuantum(1);
967         p[i]=(Quantum) v;
968         i++;
969       }
970   }
971 }
972 
DespeckleImage(const Image * image,ExceptionInfo * exception)973 MagickExport Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
974 {
975 #define DespeckleImageTag  "Despeckle/Image"
976 
977   CacheView
978     *despeckle_view,
979     *image_view;
980 
981   Image
982     *despeckle_image;
983 
984   MagickBooleanType
985     status;
986 
987   MemoryInfo
988     *buffer_info,
989     *pixel_info;
990 
991   Quantum
992     *magick_restrict buffer,
993     *magick_restrict pixels;
994 
995   register ssize_t
996     i;
997 
998   size_t
999     length;
1000 
1001   static const ssize_t
1002     X[4] = {0, 1, 1,-1},
1003     Y[4] = {1, 0, 1, 1};
1004 
1005   /*
1006     Allocate despeckled image.
1007   */
1008   assert(image != (const Image *) NULL);
1009   assert(image->signature == MagickCoreSignature);
1010   if (image->debug != MagickFalse)
1011     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1012   assert(exception != (ExceptionInfo *) NULL);
1013   assert(exception->signature == MagickCoreSignature);
1014 #if defined(MAGICKCORE_OPENCL_SUPPORT)
1015   despeckle_image=AccelerateDespeckleImage(image,exception);
1016   if (despeckle_image != (Image *) NULL)
1017     return(despeckle_image);
1018 #endif
1019   despeckle_image=CloneImage(image,0,0,MagickTrue,exception);
1020   if (despeckle_image == (Image *) NULL)
1021     return((Image *) NULL);
1022   status=SetImageStorageClass(despeckle_image,DirectClass,exception);
1023   if (status == MagickFalse)
1024     {
1025       despeckle_image=DestroyImage(despeckle_image);
1026       return((Image *) NULL);
1027     }
1028   /*
1029     Allocate image buffer.
1030   */
1031   length=(size_t) ((image->columns+2)*(image->rows+2));
1032   pixel_info=AcquireVirtualMemory(length,sizeof(*pixels));
1033   buffer_info=AcquireVirtualMemory(length,sizeof(*buffer));
1034   if ((pixel_info == (MemoryInfo *) NULL) ||
1035       (buffer_info == (MemoryInfo *) NULL))
1036     {
1037       if (buffer_info != (MemoryInfo *) NULL)
1038         buffer_info=RelinquishVirtualMemory(buffer_info);
1039       if (pixel_info != (MemoryInfo *) NULL)
1040         pixel_info=RelinquishVirtualMemory(pixel_info);
1041       despeckle_image=DestroyImage(despeckle_image);
1042       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1043     }
1044   pixels=(Quantum *) GetVirtualMemoryBlob(pixel_info);
1045   buffer=(Quantum *) GetVirtualMemoryBlob(buffer_info);
1046   /*
1047     Reduce speckle in the image.
1048   */
1049   status=MagickTrue;
1050   image_view=AcquireVirtualCacheView(image,exception);
1051   despeckle_view=AcquireAuthenticCacheView(despeckle_image,exception);
1052   for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1053   {
1054     PixelChannel
1055        channel;
1056 
1057     PixelTrait
1058       despeckle_traits,
1059       traits;
1060 
1061     register ssize_t
1062       k,
1063       x;
1064 
1065     ssize_t
1066       j,
1067       y;
1068 
1069     if (status == MagickFalse)
1070       continue;
1071     channel=GetPixelChannelChannel(image,i);
1072     traits=GetPixelChannelTraits(image,channel);
1073     despeckle_traits=GetPixelChannelTraits(despeckle_image,channel);
1074     if ((traits == UndefinedPixelTrait) ||
1075         (despeckle_traits == UndefinedPixelTrait))
1076       continue;
1077     if ((despeckle_traits & CopyPixelTrait) != 0)
1078       continue;
1079     (void) memset(pixels,0,length*sizeof(*pixels));
1080     j=(ssize_t) image->columns+2;
1081     for (y=0; y < (ssize_t) image->rows; y++)
1082     {
1083       register const Quantum
1084         *magick_restrict p;
1085 
1086       p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1087       if (p == (const Quantum *) NULL)
1088         {
1089           status=MagickFalse;
1090           continue;
1091         }
1092       j++;
1093       for (x=0; x < (ssize_t) image->columns; x++)
1094       {
1095         pixels[j++]=p[i];
1096         p+=GetPixelChannels(image);
1097       }
1098       j++;
1099     }
1100     (void) memset(buffer,0,length*sizeof(*buffer));
1101     for (k=0; k < 4; k++)
1102     {
1103       Hull(image,X[k],Y[k],image->columns,image->rows,1,pixels,buffer);
1104       Hull(image,-X[k],-Y[k],image->columns,image->rows,1,pixels,buffer);
1105       Hull(image,-X[k],-Y[k],image->columns,image->rows,-1,pixels,buffer);
1106       Hull(image,X[k],Y[k],image->columns,image->rows,-1,pixels,buffer);
1107     }
1108     j=(ssize_t) image->columns+2;
1109     for (y=0; y < (ssize_t) image->rows; y++)
1110     {
1111       MagickBooleanType
1112         sync;
1113 
1114       register Quantum
1115         *magick_restrict q;
1116 
1117       q=GetCacheViewAuthenticPixels(despeckle_view,0,y,despeckle_image->columns,
1118         1,exception);
1119       if (q == (Quantum *) NULL)
1120         {
1121           status=MagickFalse;
1122           continue;
1123         }
1124       j++;
1125       for (x=0; x < (ssize_t) image->columns; x++)
1126       {
1127         SetPixelChannel(despeckle_image,channel,pixels[j++],q);
1128         q+=GetPixelChannels(despeckle_image);
1129       }
1130       sync=SyncCacheViewAuthenticPixels(despeckle_view,exception);
1131       if (sync == MagickFalse)
1132         status=MagickFalse;
1133       j++;
1134     }
1135     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1136       {
1137         MagickBooleanType
1138           proceed;
1139 
1140         proceed=SetImageProgress(image,DespeckleImageTag,(MagickOffsetType) i,
1141           GetPixelChannels(image));
1142         if (proceed == MagickFalse)
1143           status=MagickFalse;
1144       }
1145   }
1146   despeckle_view=DestroyCacheView(despeckle_view);
1147   image_view=DestroyCacheView(image_view);
1148   buffer_info=RelinquishVirtualMemory(buffer_info);
1149   pixel_info=RelinquishVirtualMemory(pixel_info);
1150   despeckle_image->type=image->type;
1151   if (status == MagickFalse)
1152     despeckle_image=DestroyImage(despeckle_image);
1153   return(despeckle_image);
1154 }
1155 
1156 /*
1157 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1158 %                                                                             %
1159 %                                                                             %
1160 %                                                                             %
1161 %     E d g e I m a g e                                                       %
1162 %                                                                             %
1163 %                                                                             %
1164 %                                                                             %
1165 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1166 %
1167 %  EdgeImage() finds edges in an image.  Radius defines the radius of the
1168 %  convolution filter.  Use a radius of 0 and EdgeImage() selects a suitable
1169 %  radius for you.
1170 %
1171 %  The format of the EdgeImage method is:
1172 %
1173 %      Image *EdgeImage(const Image *image,const double radius,
1174 %        ExceptionInfo *exception)
1175 %
1176 %  A description of each parameter follows:
1177 %
1178 %    o image: the image.
1179 %
1180 %    o radius: the radius of the pixel neighborhood.
1181 %
1182 %    o exception: return any errors or warnings in this structure.
1183 %
1184 */
EdgeImage(const Image * image,const double radius,ExceptionInfo * exception)1185 MagickExport Image *EdgeImage(const Image *image,const double radius,
1186   ExceptionInfo *exception)
1187 {
1188   Image
1189     *edge_image;
1190 
1191   KernelInfo
1192     *kernel_info;
1193 
1194   register ssize_t
1195     i;
1196 
1197   size_t
1198     width;
1199 
1200   assert(image != (const Image *) NULL);
1201   assert(image->signature == MagickCoreSignature);
1202   if (image->debug != MagickFalse)
1203     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1204   assert(exception != (ExceptionInfo *) NULL);
1205   assert(exception->signature == MagickCoreSignature);
1206   width=GetOptimalKernelWidth1D(radius,0.5);
1207   kernel_info=AcquireKernelInfo((const char *) NULL,exception);
1208   if (kernel_info == (KernelInfo *) NULL)
1209     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1210   (void) memset(kernel_info,0,sizeof(*kernel_info));
1211   kernel_info->width=width;
1212   kernel_info->height=width;
1213   kernel_info->x=(ssize_t) (kernel_info->width-1)/2;
1214   kernel_info->y=(ssize_t) (kernel_info->height-1)/2;
1215   kernel_info->signature=MagickCoreSignature;
1216   kernel_info->values=(MagickRealType *) MagickAssumeAligned(
1217     AcquireAlignedMemory(kernel_info->width,kernel_info->height*
1218     sizeof(*kernel_info->values)));
1219   if (kernel_info->values == (MagickRealType *) NULL)
1220     {
1221       kernel_info=DestroyKernelInfo(kernel_info);
1222       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1223     }
1224   for (i=0; i < (ssize_t) (kernel_info->width*kernel_info->height); i++)
1225     kernel_info->values[i]=(-1.0);
1226   kernel_info->values[i/2]=(double) kernel_info->width*kernel_info->height-1.0;
1227   edge_image=ConvolveImage(image,kernel_info,exception);
1228   kernel_info=DestroyKernelInfo(kernel_info);
1229   return(edge_image);
1230 }
1231 
1232 /*
1233 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1234 %                                                                             %
1235 %                                                                             %
1236 %                                                                             %
1237 %     E m b o s s I m a g e                                                   %
1238 %                                                                             %
1239 %                                                                             %
1240 %                                                                             %
1241 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1242 %
1243 %  EmbossImage() returns a grayscale image with a three-dimensional effect.
1244 %  We convolve the image with a Gaussian operator of the given radius and
1245 %  standard deviation (sigma).  For reasonable results, radius should be
1246 %  larger than sigma.  Use a radius of 0 and Emboss() selects a suitable
1247 %  radius for you.
1248 %
1249 %  The format of the EmbossImage method is:
1250 %
1251 %      Image *EmbossImage(const Image *image,const double radius,
1252 %        const double sigma,ExceptionInfo *exception)
1253 %
1254 %  A description of each parameter follows:
1255 %
1256 %    o image: the image.
1257 %
1258 %    o radius: the radius of the pixel neighborhood.
1259 %
1260 %    o sigma: the standard deviation of the Gaussian, in pixels.
1261 %
1262 %    o exception: return any errors or warnings in this structure.
1263 %
1264 */
EmbossImage(const Image * image,const double radius,const double sigma,ExceptionInfo * exception)1265 MagickExport Image *EmbossImage(const Image *image,const double radius,
1266   const double sigma,ExceptionInfo *exception)
1267 {
1268   double
1269     gamma,
1270     normalize;
1271 
1272   Image
1273     *emboss_image;
1274 
1275   KernelInfo
1276     *kernel_info;
1277 
1278   register ssize_t
1279     i;
1280 
1281   size_t
1282     width;
1283 
1284   ssize_t
1285     j,
1286     k,
1287     u,
1288     v;
1289 
1290   assert(image != (const Image *) NULL);
1291   assert(image->signature == MagickCoreSignature);
1292   if (image->debug != MagickFalse)
1293     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1294   assert(exception != (ExceptionInfo *) NULL);
1295   assert(exception->signature == MagickCoreSignature);
1296   width=GetOptimalKernelWidth1D(radius,sigma);
1297   kernel_info=AcquireKernelInfo((const char *) NULL,exception);
1298   if (kernel_info == (KernelInfo *) NULL)
1299     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1300   kernel_info->width=width;
1301   kernel_info->height=width;
1302   kernel_info->x=(ssize_t) (width-1)/2;
1303   kernel_info->y=(ssize_t) (width-1)/2;
1304   kernel_info->values=(MagickRealType *) MagickAssumeAligned(
1305     AcquireAlignedMemory(kernel_info->width,kernel_info->width*
1306     sizeof(*kernel_info->values)));
1307   if (kernel_info->values == (MagickRealType *) NULL)
1308     {
1309       kernel_info=DestroyKernelInfo(kernel_info);
1310       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1311     }
1312   j=(ssize_t) (kernel_info->width-1)/2;
1313   k=j;
1314   i=0;
1315   for (v=(-j); v <= j; v++)
1316   {
1317     for (u=(-j); u <= j; u++)
1318     {
1319       kernel_info->values[i]=(MagickRealType) (((u < 0) || (v < 0) ? -8.0 :
1320         8.0)*exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
1321         (2.0*MagickPI*MagickSigma*MagickSigma));
1322       if (u != k)
1323         kernel_info->values[i]=0.0;
1324       i++;
1325     }
1326     k--;
1327   }
1328   normalize=0.0;
1329   for (i=0; i < (ssize_t) (kernel_info->width*kernel_info->height); i++)
1330     normalize+=kernel_info->values[i];
1331   gamma=PerceptibleReciprocal(normalize);
1332   for (i=0; i < (ssize_t) (kernel_info->width*kernel_info->height); i++)
1333     kernel_info->values[i]*=gamma;
1334   emboss_image=ConvolveImage(image,kernel_info,exception);
1335   kernel_info=DestroyKernelInfo(kernel_info);
1336   if (emboss_image != (Image *) NULL)
1337     (void) EqualizeImage(emboss_image,exception);
1338   return(emboss_image);
1339 }
1340 
1341 /*
1342 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1343 %                                                                             %
1344 %                                                                             %
1345 %                                                                             %
1346 %     G a u s s i a n B l u r I m a g e                                       %
1347 %                                                                             %
1348 %                                                                             %
1349 %                                                                             %
1350 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1351 %
1352 %  GaussianBlurImage() blurs an image.  We convolve the image with a
1353 %  Gaussian operator of the given radius and standard deviation (sigma).
1354 %  For reasonable results, the radius should be larger than sigma.  Use a
1355 %  radius of 0 and GaussianBlurImage() selects a suitable radius for you
1356 %
1357 %  The format of the GaussianBlurImage method is:
1358 %
1359 %      Image *GaussianBlurImage(const Image *image,onst double radius,
1360 %        const double sigma,ExceptionInfo *exception)
1361 %
1362 %  A description of each parameter follows:
1363 %
1364 %    o image: the image.
1365 %
1366 %    o radius: the radius of the Gaussian, in pixels, not counting the center
1367 %      pixel.
1368 %
1369 %    o sigma: the standard deviation of the Gaussian, in pixels.
1370 %
1371 %    o exception: return any errors or warnings in this structure.
1372 %
1373 */
GaussianBlurImage(const Image * image,const double radius,const double sigma,ExceptionInfo * exception)1374 MagickExport Image *GaussianBlurImage(const Image *image,const double radius,
1375   const double sigma,ExceptionInfo *exception)
1376 {
1377   char
1378     geometry[MagickPathExtent];
1379 
1380   KernelInfo
1381     *kernel_info;
1382 
1383   Image
1384     *blur_image;
1385 
1386   assert(image != (const Image *) NULL);
1387   assert(image->signature == MagickCoreSignature);
1388   if (image->debug != MagickFalse)
1389     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1390   assert(exception != (ExceptionInfo *) NULL);
1391   assert(exception->signature == MagickCoreSignature);
1392   (void) FormatLocaleString(geometry,MagickPathExtent,"gaussian:%.20gx%.20g",
1393     radius,sigma);
1394   kernel_info=AcquireKernelInfo(geometry,exception);
1395   if (kernel_info == (KernelInfo *) NULL)
1396     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1397   blur_image=ConvolveImage(image,kernel_info,exception);
1398   kernel_info=DestroyKernelInfo(kernel_info);
1399   return(blur_image);
1400 }
1401 
1402 /*
1403 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1404 %                                                                             %
1405 %                                                                             %
1406 %                                                                             %
1407 %     K u w a h a r a I m a g e                                               %
1408 %                                                                             %
1409 %                                                                             %
1410 %                                                                             %
1411 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1412 %
1413 %  KuwaharaImage() is an edge preserving noise reduction filter.
1414 %
1415 %  The format of the KuwaharaImage method is:
1416 %
1417 %      Image *KuwaharaImage(const Image *image,const double radius,
1418 %        const double sigma,ExceptionInfo *exception)
1419 %
1420 %  A description of each parameter follows:
1421 %
1422 %    o image: the image.
1423 %
1424 %    o radius: the square window radius.
1425 %
1426 %    o sigma: the standard deviation of the Gaussian, in pixels.
1427 %
1428 %    o exception: return any errors or warnings in this structure.
1429 %
1430 */
1431 
GetMeanLuma(const Image * magick_restrict image,const double * magick_restrict pixel)1432 static inline MagickRealType GetMeanLuma(const Image *magick_restrict image,
1433   const double *magick_restrict pixel)
1434 {
1435   return(0.212656f*pixel[image->channel_map[RedPixelChannel].offset]+
1436     0.715158f*pixel[image->channel_map[GreenPixelChannel].offset]+
1437     0.072186f*pixel[image->channel_map[BluePixelChannel].offset]);  /* Rec709 */
1438 }
1439 
KuwaharaImage(const Image * image,const double radius,const double sigma,ExceptionInfo * exception)1440 MagickExport Image *KuwaharaImage(const Image *image,const double radius,
1441   const double sigma,ExceptionInfo *exception)
1442 {
1443 #define KuwaharaImageTag  "Kuwahara/Image"
1444 
1445   CacheView
1446     *image_view,
1447     *kuwahara_view;
1448 
1449   Image
1450     *gaussian_image,
1451     *kuwahara_image;
1452 
1453   MagickBooleanType
1454     status;
1455 
1456   MagickOffsetType
1457     progress;
1458 
1459   size_t
1460     width;
1461 
1462   ssize_t
1463     y;
1464 
1465   /*
1466     Initialize Kuwahara image attributes.
1467   */
1468   assert(image != (Image *) NULL);
1469   assert(image->signature == MagickCoreSignature);
1470   if (image->debug != MagickFalse)
1471     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1472   assert(exception != (ExceptionInfo *) NULL);
1473   assert(exception->signature == MagickCoreSignature);
1474   width=(size_t) radius+1;
1475   gaussian_image=BlurImage(image,radius,sigma,exception);
1476   if (gaussian_image == (Image *) NULL)
1477     return((Image *) NULL);
1478   kuwahara_image=CloneImage(image,0,0,MagickTrue,exception);
1479   if (kuwahara_image == (Image *) NULL)
1480     {
1481       gaussian_image=DestroyImage(gaussian_image);
1482       return((Image *) NULL);
1483     }
1484   if (SetImageStorageClass(kuwahara_image,DirectClass,exception) == MagickFalse)
1485     {
1486       gaussian_image=DestroyImage(gaussian_image);
1487       kuwahara_image=DestroyImage(kuwahara_image);
1488       return((Image *) NULL);
1489     }
1490   /*
1491     Edge preserving noise reduction filter.
1492   */
1493   status=MagickTrue;
1494   progress=0;
1495   image_view=AcquireVirtualCacheView(gaussian_image,exception);
1496   kuwahara_view=AcquireAuthenticCacheView(kuwahara_image,exception);
1497 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1498   #pragma omp parallel for schedule(static) shared(progress,status) \
1499     magick_number_threads(image,kuwahara_image,gaussian_image->rows,1)
1500 #endif
1501   for (y=0; y < (ssize_t) gaussian_image->rows; y++)
1502   {
1503     register Quantum
1504       *magick_restrict q;
1505 
1506     register ssize_t
1507       x;
1508 
1509     if (status == MagickFalse)
1510       continue;
1511     q=QueueCacheViewAuthenticPixels(kuwahara_view,0,y,kuwahara_image->columns,1,
1512       exception);
1513     if (q == (Quantum *) NULL)
1514       {
1515         status=MagickFalse;
1516         continue;
1517       }
1518     for (x=0; x < (ssize_t) gaussian_image->columns; x++)
1519     {
1520       const Quantum
1521         *magick_restrict p;
1522 
1523       double
1524         min_variance;
1525 
1526       RectangleInfo
1527         quadrant,
1528         target;
1529 
1530       register size_t
1531         i;
1532 
1533       min_variance=MagickMaximumValue;
1534       SetGeometry(gaussian_image,&target);
1535       quadrant.width=width;
1536       quadrant.height=width;
1537       for (i=0; i < 4; i++)
1538       {
1539         const Quantum
1540           *magick_restrict k;
1541 
1542         double
1543           mean[MaxPixelChannels],
1544           variance;
1545 
1546         register ssize_t
1547           n;
1548 
1549         ssize_t
1550           j;
1551 
1552         quadrant.x=x;
1553         quadrant.y=y;
1554         switch (i)
1555         {
1556           case 0:
1557           {
1558             quadrant.x=x-(ssize_t) (width-1);
1559             quadrant.y=y-(ssize_t) (width-1);
1560             break;
1561           }
1562           case 1:
1563           {
1564             quadrant.y=y-(ssize_t) (width-1);
1565             break;
1566           }
1567           case 2:
1568           {
1569             quadrant.x=x-(ssize_t) (width-1);
1570             break;
1571           }
1572           case 3:
1573           default:
1574             break;
1575         }
1576         p=GetCacheViewVirtualPixels(image_view,quadrant.x,quadrant.y,
1577           quadrant.width,quadrant.height,exception);
1578         if (p == (const Quantum *) NULL)
1579           break;
1580         for (j=0; j < (ssize_t) GetPixelChannels(gaussian_image); j++)
1581           mean[j]=0.0;
1582         k=p;
1583         for (n=0; n < (ssize_t) (width*width); n++)
1584         {
1585           for (j=0; j < (ssize_t) GetPixelChannels(gaussian_image); j++)
1586             mean[j]+=(double) k[j];
1587           k+=GetPixelChannels(gaussian_image);
1588         }
1589         for (j=0; j < (ssize_t) GetPixelChannels(gaussian_image); j++)
1590           mean[j]/=(double) (width*width);
1591         k=p;
1592         variance=0.0;
1593         for (n=0; n < (ssize_t) (width*width); n++)
1594         {
1595           double
1596             luma;
1597 
1598           luma=GetPixelLuma(gaussian_image,k);
1599           variance+=(luma-GetMeanLuma(gaussian_image,mean))*
1600             (luma-GetMeanLuma(gaussian_image,mean));
1601           k+=GetPixelChannels(gaussian_image);
1602         }
1603         if (variance < min_variance)
1604           {
1605             min_variance=variance;
1606             target=quadrant;
1607           }
1608       }
1609       if (i < 4)
1610         {
1611           status=MagickFalse;
1612           break;
1613         }
1614       status=InterpolatePixelChannels(gaussian_image,image_view,kuwahara_image,
1615         UndefinedInterpolatePixel,(double) target.x+target.width/2.0,(double)
1616         target.y+target.height/2.0,q,exception);
1617       if (status == MagickFalse)
1618         break;
1619       q+=GetPixelChannels(kuwahara_image);
1620     }
1621     if (SyncCacheViewAuthenticPixels(kuwahara_view,exception) == MagickFalse)
1622       status=MagickFalse;
1623     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1624       {
1625         MagickBooleanType
1626           proceed;
1627 
1628 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1629         #pragma omp atomic
1630 #endif
1631         progress++;
1632         proceed=SetImageProgress(image,KuwaharaImageTag,progress,image->rows);
1633         if (proceed == MagickFalse)
1634           status=MagickFalse;
1635       }
1636   }
1637   kuwahara_view=DestroyCacheView(kuwahara_view);
1638   image_view=DestroyCacheView(image_view);
1639   gaussian_image=DestroyImage(gaussian_image);
1640   if (status == MagickFalse)
1641     kuwahara_image=DestroyImage(kuwahara_image);
1642   return(kuwahara_image);
1643 }
1644 
1645 /*
1646 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1647 %                                                                             %
1648 %                                                                             %
1649 %                                                                             %
1650 %     L o c a l C o n t r a s t I m a g e                                     %
1651 %                                                                             %
1652 %                                                                             %
1653 %                                                                             %
1654 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1655 %
1656 %  LocalContrastImage() attempts to increase the appearance of large-scale
1657 %  light-dark transitions. Local contrast enhancement works similarly to
1658 %  sharpening with an unsharp mask, however the mask is instead created using
1659 %  an image with a greater blur distance.
1660 %
1661 %  The format of the LocalContrastImage method is:
1662 %
1663 %      Image *LocalContrastImage(const Image *image, const double radius,
1664 %        const double strength,ExceptionInfo *exception)
1665 %
1666 %  A description of each parameter follows:
1667 %
1668 %    o image: the image.
1669 %
1670 %    o radius: the radius of the Gaussian blur, in percentage with 100%
1671 %      resulting in a blur radius of 20% of largest dimension.
1672 %
1673 %    o strength: the strength of the blur mask in percentage.
1674 %
1675 %    o exception: return any errors or warnings in this structure.
1676 %
1677 */
LocalContrastImage(const Image * image,const double radius,const double strength,ExceptionInfo * exception)1678 MagickExport Image *LocalContrastImage(const Image *image,const double radius,
1679   const double strength,ExceptionInfo *exception)
1680 {
1681 #define LocalContrastImageTag  "LocalContrast/Image"
1682 
1683   CacheView
1684     *image_view,
1685     *contrast_view;
1686 
1687   float
1688     *interImage,
1689     *scanLinePixels,
1690     totalWeight;
1691 
1692   Image
1693     *contrast_image;
1694 
1695   MagickBooleanType
1696     status;
1697 
1698   MemoryInfo
1699     *scanLinePixels_info,
1700     *interImage_info;
1701 
1702   ssize_t
1703     scanLineSize,
1704     width;
1705 
1706   /*
1707     Initialize contrast image attributes.
1708   */
1709   assert(image != (const Image *) NULL);
1710   assert(image->signature == MagickCoreSignature);
1711   if (image->debug != MagickFalse)
1712     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1713   assert(exception != (ExceptionInfo *) NULL);
1714   assert(exception->signature == MagickCoreSignature);
1715 #if defined(MAGICKCORE_OPENCL_SUPPORT)
1716   contrast_image=AccelerateLocalContrastImage(image,radius,strength,exception);
1717   if (contrast_image != (Image *) NULL)
1718     return(contrast_image);
1719 #endif
1720   contrast_image=CloneImage(image,0,0,MagickTrue,exception);
1721   if (contrast_image == (Image *) NULL)
1722     return((Image *) NULL);
1723   if (SetImageStorageClass(contrast_image,DirectClass,exception) == MagickFalse)
1724     {
1725       contrast_image=DestroyImage(contrast_image);
1726       return((Image *) NULL);
1727     }
1728   image_view=AcquireVirtualCacheView(image,exception);
1729   contrast_view=AcquireAuthenticCacheView(contrast_image,exception);
1730   scanLineSize=(ssize_t) MagickMax(image->columns,image->rows);
1731   width=(ssize_t) scanLineSize*0.002f*fabs(radius);
1732   scanLineSize+=(2*width);
1733   scanLinePixels_info=AcquireVirtualMemory((size_t) GetOpenMPMaximumThreads()*
1734     scanLineSize,sizeof(*scanLinePixels));
1735   if (scanLinePixels_info == (MemoryInfo *) NULL)
1736     {
1737       contrast_view=DestroyCacheView(contrast_view);
1738       image_view=DestroyCacheView(image_view);
1739       contrast_image=DestroyImage(contrast_image);
1740       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1741     }
1742   scanLinePixels=(float *) GetVirtualMemoryBlob(scanLinePixels_info);
1743   /*
1744     Create intermediate buffer.
1745   */
1746   interImage_info=AcquireVirtualMemory(image->rows*(image->columns+(2*width)),
1747     sizeof(*interImage));
1748   if (interImage_info == (MemoryInfo *) NULL)
1749     {
1750       scanLinePixels_info=RelinquishVirtualMemory(scanLinePixels_info);
1751       contrast_view=DestroyCacheView(contrast_view);
1752       image_view=DestroyCacheView(image_view);
1753       contrast_image=DestroyImage(contrast_image);
1754       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1755     }
1756   interImage=(float *) GetVirtualMemoryBlob(interImage_info);
1757   totalWeight=(float) ((width+1)*(width+1));
1758   /*
1759     Vertical pass.
1760   */
1761   status=MagickTrue;
1762   {
1763     ssize_t
1764       x;
1765 
1766 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1767 #pragma omp parallel for schedule(static) \
1768     magick_number_threads(image,image,image->columns,1)
1769 #endif
1770     for (x=0; x < (ssize_t) image->columns; x++)
1771     {
1772       const int
1773         id = GetOpenMPThreadId();
1774 
1775       const Quantum
1776         *magick_restrict p;
1777 
1778       float
1779         *out,
1780         *pix,
1781         *pixels;
1782 
1783       register ssize_t
1784         y;
1785 
1786       ssize_t
1787         i;
1788 
1789       if (status == MagickFalse)
1790         continue;
1791       pixels=scanLinePixels;
1792       pixels+=id*scanLineSize;
1793       pix=pixels;
1794       p=GetCacheViewVirtualPixels(image_view,x,-width,1,image->rows+(2*width),
1795         exception);
1796       if (p == (const Quantum *) NULL)
1797         {
1798           status=MagickFalse;
1799           continue;
1800         }
1801       for (y=0; y < (ssize_t) image->rows+(2*width); y++)
1802       {
1803         *pix++=(float)GetPixelLuma(image,p);
1804         p+=image->number_channels;
1805       }
1806       out=interImage+x+width;
1807       for (y=0; y < (ssize_t) image->rows; y++)
1808       {
1809         float
1810           sum,
1811           weight;
1812 
1813         weight=1.0f;
1814         sum=0;
1815         pix=pixels+y;
1816         for (i=0; i < width; i++)
1817         {
1818           sum+=weight*(*pix++);
1819           weight+=1.0f;
1820         }
1821         for (i=width+1; i < (2*width); i++)
1822         {
1823           sum+=weight*(*pix++);
1824           weight-=1.0f;
1825         }
1826         /* write to output */
1827         *out=sum/totalWeight;
1828         /* mirror into padding */
1829         if (x <= width && x != 0)
1830           *(out-(x*2))=*out;
1831         if ((x > (ssize_t) image->columns-width-2) &&
1832             (x != (ssize_t) image->columns-1))
1833           *(out+((image->columns-x-1)*2))=*out;
1834         out+=image->columns+(width*2);
1835       }
1836     }
1837   }
1838   /*
1839     Horizontal pass.
1840   */
1841   {
1842     ssize_t
1843       y;
1844 
1845 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1846 #pragma omp parallel for schedule(static) \
1847     magick_number_threads(image,image,image->rows,1)
1848 #endif
1849     for (y=0; y < (ssize_t) image->rows; y++)
1850     {
1851       const int
1852         id = GetOpenMPThreadId();
1853 
1854       const Quantum
1855         *magick_restrict p;
1856 
1857       float
1858         *pix,
1859         *pixels;
1860 
1861       register Quantum
1862         *magick_restrict q;
1863 
1864       register ssize_t
1865         x;
1866 
1867       ssize_t
1868         i;
1869 
1870       if (status == MagickFalse)
1871         continue;
1872       pixels=scanLinePixels;
1873       pixels+=id*scanLineSize;
1874       p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1875       q=GetCacheViewAuthenticPixels(contrast_view,0,y,image->columns,1,
1876         exception);
1877       if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1878         {
1879           status=MagickFalse;
1880           continue;
1881         }
1882       memcpy(pixels,interImage+(y*(image->columns+(2*width))),(image->columns+
1883         (2*width))*sizeof(float));
1884       for (x=0; x < (ssize_t) image->columns; x++)
1885       {
1886         float
1887           mult,
1888           srcVal,
1889           sum,
1890           weight;
1891 
1892         PixelTrait
1893           traits;
1894 
1895         weight=1.0f;
1896         sum=0;
1897         pix=pixels+x;
1898         for (i=0; i < width; i++)
1899         {
1900           sum+=weight*(*pix++);
1901           weight+=1.0f;
1902         }
1903         for (i=width+1; i < (2*width); i++)
1904         {
1905           sum+=weight*(*pix++);
1906           weight-=1.0f;
1907         }
1908         /* Apply and write */
1909         srcVal=(float) GetPixelLuma(image,p);
1910         mult=(srcVal-(sum/totalWeight))*(strength/100.0f);
1911         mult=(srcVal+mult)/srcVal;
1912         traits=GetPixelChannelTraits(image,RedPixelChannel);
1913         if ((traits & UpdatePixelTrait) != 0)
1914           SetPixelRed(contrast_image,ClampToQuantum(GetPixelRed(image,p)*mult),
1915             q);
1916         traits=GetPixelChannelTraits(image,GreenPixelChannel);
1917         if ((traits & UpdatePixelTrait) != 0)
1918           SetPixelGreen(contrast_image,ClampToQuantum(GetPixelGreen(image,p)*
1919             mult),q);
1920         traits=GetPixelChannelTraits(image,BluePixelChannel);
1921         if ((traits & UpdatePixelTrait) != 0)
1922           SetPixelBlue(contrast_image,ClampToQuantum(GetPixelBlue(image,p)*
1923             mult),q);
1924         p+=image->number_channels;
1925         q+=contrast_image->number_channels;
1926       }
1927       if (SyncCacheViewAuthenticPixels(contrast_view,exception) == MagickFalse)
1928         status=MagickFalse;
1929     }
1930   }
1931   scanLinePixels_info=RelinquishVirtualMemory(scanLinePixels_info);
1932   interImage_info=RelinquishVirtualMemory(interImage_info);
1933   contrast_view=DestroyCacheView(contrast_view);
1934   image_view=DestroyCacheView(image_view);
1935   if (status == MagickFalse)
1936     contrast_image=DestroyImage(contrast_image);
1937   return(contrast_image);
1938 }
1939 
1940 /*
1941 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1942 %                                                                             %
1943 %                                                                             %
1944 %                                                                             %
1945 %     M o t i o n B l u r I m a g e                                           %
1946 %                                                                             %
1947 %                                                                             %
1948 %                                                                             %
1949 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1950 %
1951 %  MotionBlurImage() simulates motion blur.  We convolve the image with a
1952 %  Gaussian operator of the given radius and standard deviation (sigma).
1953 %  For reasonable results, radius should be larger than sigma.  Use a
1954 %  radius of 0 and MotionBlurImage() selects a suitable radius for you.
1955 %  Angle gives the angle of the blurring motion.
1956 %
1957 %  Andrew Protano contributed this effect.
1958 %
1959 %  The format of the MotionBlurImage method is:
1960 %
1961 %    Image *MotionBlurImage(const Image *image,const double radius,
1962 %      const double sigma,const double angle,ExceptionInfo *exception)
1963 %
1964 %  A description of each parameter follows:
1965 %
1966 %    o image: the image.
1967 %
1968 %    o radius: the radius of the Gaussian, in pixels, not counting
1969 %      the center pixel.
1970 %
1971 %    o sigma: the standard deviation of the Gaussian, in pixels.
1972 %
1973 %    o angle: Apply the effect along this angle.
1974 %
1975 %    o exception: return any errors or warnings in this structure.
1976 %
1977 */
1978 
GetMotionBlurKernel(const size_t width,const double sigma)1979 static MagickRealType *GetMotionBlurKernel(const size_t width,
1980   const double sigma)
1981 {
1982   MagickRealType
1983     *kernel,
1984     normalize;
1985 
1986   register ssize_t
1987     i;
1988 
1989   /*
1990    Generate a 1-D convolution kernel.
1991   */
1992   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1993   kernel=(MagickRealType *) MagickAssumeAligned(AcquireAlignedMemory((size_t)
1994     width,sizeof(*kernel)));
1995   if (kernel == (MagickRealType *) NULL)
1996     return(kernel);
1997   normalize=0.0;
1998   for (i=0; i < (ssize_t) width; i++)
1999   {
2000     kernel[i]=(MagickRealType) (exp((-((double) i*i)/(double) (2.0*MagickSigma*
2001       MagickSigma)))/(MagickSQ2PI*MagickSigma));
2002     normalize+=kernel[i];
2003   }
2004   for (i=0; i < (ssize_t) width; i++)
2005     kernel[i]/=normalize;
2006   return(kernel);
2007 }
2008 
MotionBlurImage(const Image * image,const double radius,const double sigma,const double angle,ExceptionInfo * exception)2009 MagickExport Image *MotionBlurImage(const Image *image,const double radius,
2010   const double sigma,const double angle,ExceptionInfo *exception)
2011 {
2012 #define BlurImageTag  "Blur/Image"
2013 
2014   CacheView
2015     *blur_view,
2016     *image_view,
2017     *motion_view;
2018 
2019   Image
2020     *blur_image;
2021 
2022   MagickBooleanType
2023     status;
2024 
2025   MagickOffsetType
2026     progress;
2027 
2028   MagickRealType
2029     *kernel;
2030 
2031   OffsetInfo
2032     *offset;
2033 
2034   PointInfo
2035     point;
2036 
2037   register ssize_t
2038     i;
2039 
2040   size_t
2041     width;
2042 
2043   ssize_t
2044     y;
2045 
2046   assert(image != (Image *) NULL);
2047   assert(image->signature == MagickCoreSignature);
2048   if (image->debug != MagickFalse)
2049     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2050   assert(exception != (ExceptionInfo *) NULL);
2051   width=GetOptimalKernelWidth1D(radius,sigma);
2052   kernel=GetMotionBlurKernel(width,sigma);
2053   if (kernel == (MagickRealType *) NULL)
2054     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2055   offset=(OffsetInfo *) AcquireQuantumMemory(width,sizeof(*offset));
2056   if (offset == (OffsetInfo *) NULL)
2057     {
2058       kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
2059       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2060     }
2061   point.x=(double) width*sin(DegreesToRadians(angle));
2062   point.y=(double) width*cos(DegreesToRadians(angle));
2063   for (i=0; i < (ssize_t) width; i++)
2064   {
2065     offset[i].x=(ssize_t) ceil((double) (i*point.y)/hypot(point.x,point.y)-0.5);
2066     offset[i].y=(ssize_t) ceil((double) (i*point.x)/hypot(point.x,point.y)-0.5);
2067   }
2068   /*
2069     Motion blur image.
2070   */
2071 #if defined(MAGICKCORE_OPENCL_SUPPORT)
2072   blur_image=AccelerateMotionBlurImage(image,kernel,width,offset,exception);
2073   if (blur_image != (Image *) NULL)
2074     {
2075       kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
2076       offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2077       return(blur_image);
2078     }
2079 #endif
2080   blur_image=CloneImage(image,0,0,MagickTrue,exception);
2081   if (blur_image == (Image *) NULL)
2082     {
2083       kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
2084       offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2085       return((Image *) NULL);
2086     }
2087   if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
2088     {
2089       kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
2090       offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2091       blur_image=DestroyImage(blur_image);
2092       return((Image *) NULL);
2093     }
2094   status=MagickTrue;
2095   progress=0;
2096   image_view=AcquireVirtualCacheView(image,exception);
2097   motion_view=AcquireVirtualCacheView(image,exception);
2098   blur_view=AcquireAuthenticCacheView(blur_image,exception);
2099 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2100   #pragma omp parallel for schedule(static) shared(progress,status) \
2101     magick_number_threads(image,blur_image,image->rows,1)
2102 #endif
2103   for (y=0; y < (ssize_t) image->rows; y++)
2104   {
2105     register const Quantum
2106       *magick_restrict p;
2107 
2108     register Quantum
2109       *magick_restrict q;
2110 
2111     register ssize_t
2112       x;
2113 
2114     if (status == MagickFalse)
2115       continue;
2116     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2117     q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
2118       exception);
2119     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2120       {
2121         status=MagickFalse;
2122         continue;
2123       }
2124     for (x=0; x < (ssize_t) image->columns; x++)
2125     {
2126       register ssize_t
2127         i;
2128 
2129       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2130       {
2131         double
2132           alpha,
2133           gamma,
2134           pixel;
2135 
2136         PixelChannel
2137           channel;
2138 
2139         PixelTrait
2140           blur_traits,
2141           traits;
2142 
2143         register const Quantum
2144           *magick_restrict r;
2145 
2146         register MagickRealType
2147           *magick_restrict k;
2148 
2149         register ssize_t
2150           j;
2151 
2152         channel=GetPixelChannelChannel(image,i);
2153         traits=GetPixelChannelTraits(image,channel);
2154         blur_traits=GetPixelChannelTraits(blur_image,channel);
2155         if ((traits == UndefinedPixelTrait) ||
2156             (blur_traits == UndefinedPixelTrait))
2157           continue;
2158         if ((blur_traits & CopyPixelTrait) != 0)
2159           {
2160             SetPixelChannel(blur_image,channel,p[i],q);
2161             continue;
2162           }
2163         k=kernel;
2164         pixel=0.0;
2165         if ((blur_traits & BlendPixelTrait) == 0)
2166           {
2167             for (j=0; j < (ssize_t) width; j++)
2168             {
2169               r=GetCacheViewVirtualPixels(motion_view,x+offset[j].x,y+
2170                 offset[j].y,1,1,exception);
2171               if (r == (const Quantum *) NULL)
2172                 {
2173                   status=MagickFalse;
2174                   continue;
2175                 }
2176               pixel+=(*k)*r[i];
2177               k++;
2178             }
2179             SetPixelChannel(blur_image,channel,ClampToQuantum(pixel),q);
2180             continue;
2181           }
2182         alpha=0.0;
2183         gamma=0.0;
2184         for (j=0; j < (ssize_t) width; j++)
2185         {
2186           r=GetCacheViewVirtualPixels(motion_view,x+offset[j].x,y+offset[j].y,1,
2187             1,exception);
2188           if (r == (const Quantum *) NULL)
2189             {
2190               status=MagickFalse;
2191               continue;
2192             }
2193           alpha=(double) (QuantumScale*GetPixelAlpha(image,r));
2194           pixel+=(*k)*alpha*r[i];
2195           gamma+=(*k)*alpha;
2196           k++;
2197         }
2198         gamma=PerceptibleReciprocal(gamma);
2199         SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
2200       }
2201       p+=GetPixelChannels(image);
2202       q+=GetPixelChannels(blur_image);
2203     }
2204     if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
2205       status=MagickFalse;
2206     if (image->progress_monitor != (MagickProgressMonitor) NULL)
2207       {
2208         MagickBooleanType
2209           proceed;
2210 
2211 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2212         #pragma omp atomic
2213 #endif
2214         progress++;
2215         proceed=SetImageProgress(image,BlurImageTag,progress,image->rows);
2216         if (proceed == MagickFalse)
2217           status=MagickFalse;
2218       }
2219   }
2220   blur_view=DestroyCacheView(blur_view);
2221   motion_view=DestroyCacheView(motion_view);
2222   image_view=DestroyCacheView(image_view);
2223   kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
2224   offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2225   if (status == MagickFalse)
2226     blur_image=DestroyImage(blur_image);
2227   return(blur_image);
2228 }
2229 
2230 /*
2231 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2232 %                                                                             %
2233 %                                                                             %
2234 %                                                                             %
2235 %     P r e v i e w I m a g e                                                 %
2236 %                                                                             %
2237 %                                                                             %
2238 %                                                                             %
2239 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2240 %
2241 %  PreviewImage() tiles 9 thumbnails of the specified image with an image
2242 %  processing operation applied with varying parameters.  This may be helpful
2243 %  pin-pointing an appropriate parameter for a particular image processing
2244 %  operation.
2245 %
2246 %  The format of the PreviewImages method is:
2247 %
2248 %      Image *PreviewImages(const Image *image,const PreviewType preview,
2249 %        ExceptionInfo *exception)
2250 %
2251 %  A description of each parameter follows:
2252 %
2253 %    o image: the image.
2254 %
2255 %    o preview: the image processing operation.
2256 %
2257 %    o exception: return any errors or warnings in this structure.
2258 %
2259 */
PreviewImage(const Image * image,const PreviewType preview,ExceptionInfo * exception)2260 MagickExport Image *PreviewImage(const Image *image,const PreviewType preview,
2261   ExceptionInfo *exception)
2262 {
2263 #define NumberTiles  9
2264 #define PreviewImageTag  "Preview/Image"
2265 #define DefaultPreviewGeometry  "204x204+10+10"
2266 
2267   char
2268     factor[MagickPathExtent],
2269     label[MagickPathExtent];
2270 
2271   double
2272     degrees,
2273     gamma,
2274     percentage,
2275     radius,
2276     sigma,
2277     threshold;
2278 
2279   extern const char
2280     DefaultTileFrame[];
2281 
2282   Image
2283     *images,
2284     *montage_image,
2285     *preview_image,
2286     *thumbnail;
2287 
2288   ImageInfo
2289     *preview_info;
2290 
2291   MagickBooleanType
2292     proceed;
2293 
2294   MontageInfo
2295     *montage_info;
2296 
2297   QuantizeInfo
2298     quantize_info;
2299 
2300   RectangleInfo
2301     geometry;
2302 
2303   register ssize_t
2304     i,
2305     x;
2306 
2307   size_t
2308     colors;
2309 
2310   ssize_t
2311     y;
2312 
2313   /*
2314     Open output image file.
2315   */
2316   assert(image != (Image *) NULL);
2317   assert(image->signature == MagickCoreSignature);
2318   if (image->debug != MagickFalse)
2319     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2320   colors=2;
2321   degrees=0.0;
2322   gamma=(-0.2f);
2323   preview_info=AcquireImageInfo();
2324   SetGeometry(image,&geometry);
2325   (void) ParseMetaGeometry(DefaultPreviewGeometry,&geometry.x,&geometry.y,
2326     &geometry.width,&geometry.height);
2327   images=NewImageList();
2328   percentage=12.5;
2329   GetQuantizeInfo(&quantize_info);
2330   radius=0.0;
2331   sigma=1.0;
2332   threshold=0.0;
2333   x=0;
2334   y=0;
2335   for (i=0; i < NumberTiles; i++)
2336   {
2337     thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
2338     if (thumbnail == (Image *) NULL)
2339       break;
2340     (void) SetImageProgressMonitor(thumbnail,(MagickProgressMonitor) NULL,
2341       (void *) NULL);
2342     (void) SetImageProperty(thumbnail,"label",DefaultTileLabel,exception);
2343     if (i == (NumberTiles/2))
2344       {
2345         (void) QueryColorCompliance("#dfdfdf",AllCompliance,
2346           &thumbnail->matte_color,exception);
2347         AppendImageToList(&images,thumbnail);
2348         continue;
2349       }
2350     switch (preview)
2351     {
2352       case RotatePreview:
2353       {
2354         degrees+=45.0;
2355         preview_image=RotateImage(thumbnail,degrees,exception);
2356         (void) FormatLocaleString(label,MagickPathExtent,"rotate %g",degrees);
2357         break;
2358       }
2359       case ShearPreview:
2360       {
2361         degrees+=5.0;
2362         preview_image=ShearImage(thumbnail,degrees,degrees,exception);
2363         (void) FormatLocaleString(label,MagickPathExtent,"shear %gx%g",degrees,
2364           2.0*degrees);
2365         break;
2366       }
2367       case RollPreview:
2368       {
2369         x=(ssize_t) ((i+1)*thumbnail->columns)/NumberTiles;
2370         y=(ssize_t) ((i+1)*thumbnail->rows)/NumberTiles;
2371         preview_image=RollImage(thumbnail,x,y,exception);
2372         (void) FormatLocaleString(label,MagickPathExtent,"roll %+.20gx%+.20g",
2373           (double) x,(double) y);
2374         break;
2375       }
2376       case HuePreview:
2377       {
2378         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2379         if (preview_image == (Image *) NULL)
2380           break;
2381         (void) FormatLocaleString(factor,MagickPathExtent,"100,100,%g",2.0*
2382           percentage);
2383         (void) ModulateImage(preview_image,factor,exception);
2384         (void) FormatLocaleString(label,MagickPathExtent,"modulate %s",factor);
2385         break;
2386       }
2387       case SaturationPreview:
2388       {
2389         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2390         if (preview_image == (Image *) NULL)
2391           break;
2392         (void) FormatLocaleString(factor,MagickPathExtent,"100,%g",2.0*
2393           percentage);
2394         (void) ModulateImage(preview_image,factor,exception);
2395         (void) FormatLocaleString(label,MagickPathExtent,"modulate %s",factor);
2396         break;
2397       }
2398       case BrightnessPreview:
2399       {
2400         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2401         if (preview_image == (Image *) NULL)
2402           break;
2403         (void) FormatLocaleString(factor,MagickPathExtent,"%g",2.0*percentage);
2404         (void) ModulateImage(preview_image,factor,exception);
2405         (void) FormatLocaleString(label,MagickPathExtent,"modulate %s",factor);
2406         break;
2407       }
2408       case GammaPreview:
2409       default:
2410       {
2411         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2412         if (preview_image == (Image *) NULL)
2413           break;
2414         gamma+=0.4f;
2415         (void) GammaImage(preview_image,gamma,exception);
2416         (void) FormatLocaleString(label,MagickPathExtent,"gamma %g",gamma);
2417         break;
2418       }
2419       case SpiffPreview:
2420       {
2421         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2422         if (preview_image != (Image *) NULL)
2423           for (x=0; x < i; x++)
2424             (void) ContrastImage(preview_image,MagickTrue,exception);
2425         (void) FormatLocaleString(label,MagickPathExtent,"contrast (%.20g)",
2426           (double) i+1);
2427         break;
2428       }
2429       case DullPreview:
2430       {
2431         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2432         if (preview_image == (Image *) NULL)
2433           break;
2434         for (x=0; x < i; x++)
2435           (void) ContrastImage(preview_image,MagickFalse,exception);
2436         (void) FormatLocaleString(label,MagickPathExtent,"+contrast (%.20g)",
2437           (double) i+1);
2438         break;
2439       }
2440       case GrayscalePreview:
2441       {
2442         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2443         if (preview_image == (Image *) NULL)
2444           break;
2445         colors<<=1;
2446         quantize_info.number_colors=colors;
2447         quantize_info.colorspace=GRAYColorspace;
2448         (void) QuantizeImage(&quantize_info,preview_image,exception);
2449         (void) FormatLocaleString(label,MagickPathExtent,
2450           "-colorspace gray -colors %.20g",(double) colors);
2451         break;
2452       }
2453       case QuantizePreview:
2454       {
2455         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2456         if (preview_image == (Image *) NULL)
2457           break;
2458         colors<<=1;
2459         quantize_info.number_colors=colors;
2460         (void) QuantizeImage(&quantize_info,preview_image,exception);
2461         (void) FormatLocaleString(label,MagickPathExtent,"colors %.20g",
2462           (double) colors);
2463         break;
2464       }
2465       case DespecklePreview:
2466       {
2467         for (x=0; x < (i-1); x++)
2468         {
2469           preview_image=DespeckleImage(thumbnail,exception);
2470           if (preview_image == (Image *) NULL)
2471             break;
2472           thumbnail=DestroyImage(thumbnail);
2473           thumbnail=preview_image;
2474         }
2475         preview_image=DespeckleImage(thumbnail,exception);
2476         if (preview_image == (Image *) NULL)
2477           break;
2478         (void) FormatLocaleString(label,MagickPathExtent,"despeckle (%.20g)",
2479           (double) i+1);
2480         break;
2481       }
2482       case ReduceNoisePreview:
2483       {
2484         preview_image=StatisticImage(thumbnail,NonpeakStatistic,(size_t)
2485           radius,(size_t) radius,exception);
2486         (void) FormatLocaleString(label,MagickPathExtent,"noise %g",radius);
2487         break;
2488       }
2489       case AddNoisePreview:
2490       {
2491         switch ((int) i)
2492         {
2493           case 0:
2494           {
2495             (void) CopyMagickString(factor,"uniform",MagickPathExtent);
2496             break;
2497           }
2498           case 1:
2499           {
2500             (void) CopyMagickString(factor,"gaussian",MagickPathExtent);
2501             break;
2502           }
2503           case 2:
2504           {
2505             (void) CopyMagickString(factor,"multiplicative",MagickPathExtent);
2506             break;
2507           }
2508           case 3:
2509           {
2510             (void) CopyMagickString(factor,"impulse",MagickPathExtent);
2511             break;
2512           }
2513           case 5:
2514           {
2515             (void) CopyMagickString(factor,"laplacian",MagickPathExtent);
2516             break;
2517           }
2518           case 6:
2519           {
2520             (void) CopyMagickString(factor,"Poisson",MagickPathExtent);
2521             break;
2522           }
2523           default:
2524           {
2525             (void) CopyMagickString(thumbnail->magick,"NULL",MagickPathExtent);
2526             break;
2527           }
2528         }
2529         preview_image=StatisticImage(thumbnail,NonpeakStatistic,(size_t) i,
2530           (size_t) i,exception);
2531         (void) FormatLocaleString(label,MagickPathExtent,"+noise %s",factor);
2532         break;
2533       }
2534       case SharpenPreview:
2535       {
2536         preview_image=SharpenImage(thumbnail,radius,sigma,exception);
2537         (void) FormatLocaleString(label,MagickPathExtent,"sharpen %gx%g",
2538           radius,sigma);
2539         break;
2540       }
2541       case BlurPreview:
2542       {
2543         preview_image=BlurImage(thumbnail,radius,sigma,exception);
2544         (void) FormatLocaleString(label,MagickPathExtent,"blur %gx%g",radius,
2545           sigma);
2546         break;
2547       }
2548       case ThresholdPreview:
2549       {
2550         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2551         if (preview_image == (Image *) NULL)
2552           break;
2553         (void) BilevelImage(thumbnail,(double) (percentage*((double)
2554           QuantumRange+1.0))/100.0,exception);
2555         (void) FormatLocaleString(label,MagickPathExtent,"threshold %g",
2556           (double) (percentage*((double) QuantumRange+1.0))/100.0);
2557         break;
2558       }
2559       case EdgeDetectPreview:
2560       {
2561         preview_image=EdgeImage(thumbnail,radius,exception);
2562         (void) FormatLocaleString(label,MagickPathExtent,"edge %g",radius);
2563         break;
2564       }
2565       case SpreadPreview:
2566       {
2567         preview_image=SpreadImage(thumbnail,image->interpolate,radius,
2568           exception);
2569         (void) FormatLocaleString(label,MagickPathExtent,"spread %g",
2570           radius+0.5);
2571         break;
2572       }
2573       case SolarizePreview:
2574       {
2575         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2576         if (preview_image == (Image *) NULL)
2577           break;
2578         (void) SolarizeImage(preview_image,(double) QuantumRange*percentage/
2579           100.0,exception);
2580         (void) FormatLocaleString(label,MagickPathExtent,"solarize %g",
2581           (QuantumRange*percentage)/100.0);
2582         break;
2583       }
2584       case ShadePreview:
2585       {
2586         degrees+=10.0;
2587         preview_image=ShadeImage(thumbnail,MagickTrue,degrees,degrees,
2588           exception);
2589         (void) FormatLocaleString(label,MagickPathExtent,"shade %gx%g",degrees,
2590           degrees);
2591         break;
2592       }
2593       case RaisePreview:
2594       {
2595         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2596         if (preview_image == (Image *) NULL)
2597           break;
2598         geometry.width=(size_t) (2*i+2);
2599         geometry.height=(size_t) (2*i+2);
2600         geometry.x=(i-1)/2;
2601         geometry.y=(i-1)/2;
2602         (void) RaiseImage(preview_image,&geometry,MagickTrue,exception);
2603         (void) FormatLocaleString(label,MagickPathExtent,
2604           "raise %.20gx%.20g%+.20g%+.20g",(double) geometry.width,(double)
2605           geometry.height,(double) geometry.x,(double) geometry.y);
2606         break;
2607       }
2608       case SegmentPreview:
2609       {
2610         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2611         if (preview_image == (Image *) NULL)
2612           break;
2613         threshold+=0.4f;
2614         (void) SegmentImage(preview_image,sRGBColorspace,MagickFalse,threshold,
2615           threshold,exception);
2616         (void) FormatLocaleString(label,MagickPathExtent,"segment %gx%g",
2617           threshold,threshold);
2618         break;
2619       }
2620       case SwirlPreview:
2621       {
2622         preview_image=SwirlImage(thumbnail,degrees,image->interpolate,
2623           exception);
2624         (void) FormatLocaleString(label,MagickPathExtent,"swirl %g",degrees);
2625         degrees+=45.0;
2626         break;
2627       }
2628       case ImplodePreview:
2629       {
2630         degrees+=0.1f;
2631         preview_image=ImplodeImage(thumbnail,degrees,image->interpolate,
2632           exception);
2633         (void) FormatLocaleString(label,MagickPathExtent,"implode %g",degrees);
2634         break;
2635       }
2636       case WavePreview:
2637       {
2638         degrees+=5.0f;
2639         preview_image=WaveImage(thumbnail,0.5*degrees,2.0*degrees,
2640           image->interpolate,exception);
2641         (void) FormatLocaleString(label,MagickPathExtent,"wave %gx%g",0.5*
2642           degrees,2.0*degrees);
2643         break;
2644       }
2645       case OilPaintPreview:
2646       {
2647         preview_image=OilPaintImage(thumbnail,(double) radius,(double) sigma,
2648           exception);
2649         (void) FormatLocaleString(label,MagickPathExtent,"charcoal %gx%g",
2650           radius,sigma);
2651         break;
2652       }
2653       case CharcoalDrawingPreview:
2654       {
2655         preview_image=CharcoalImage(thumbnail,(double) radius,(double) sigma,
2656           exception);
2657         (void) FormatLocaleString(label,MagickPathExtent,"charcoal %gx%g",
2658           radius,sigma);
2659         break;
2660       }
2661       case JPEGPreview:
2662       {
2663         char
2664           filename[MagickPathExtent];
2665 
2666         int
2667           file;
2668 
2669         MagickBooleanType
2670           status;
2671 
2672         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2673         if (preview_image == (Image *) NULL)
2674           break;
2675         preview_info->quality=(size_t) percentage;
2676         (void) FormatLocaleString(factor,MagickPathExtent,"%.20g",(double)
2677           preview_info->quality);
2678         file=AcquireUniqueFileResource(filename);
2679         if (file != -1)
2680           file=close(file)-1;
2681         (void) FormatLocaleString(preview_image->filename,MagickPathExtent,
2682           "jpeg:%s",filename);
2683         status=WriteImage(preview_info,preview_image,exception);
2684         if (status != MagickFalse)
2685           {
2686             Image
2687               *quality_image;
2688 
2689             (void) CopyMagickString(preview_info->filename,
2690               preview_image->filename,MagickPathExtent);
2691             quality_image=ReadImage(preview_info,exception);
2692             if (quality_image != (Image *) NULL)
2693               {
2694                 preview_image=DestroyImage(preview_image);
2695                 preview_image=quality_image;
2696               }
2697           }
2698         (void) RelinquishUniqueFileResource(preview_image->filename);
2699         if ((GetBlobSize(preview_image)/1024) >= 1024)
2700           (void) FormatLocaleString(label,MagickPathExtent,"quality %s\n%gmb ",
2701             factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
2702             1024.0/1024.0);
2703         else
2704           if (GetBlobSize(preview_image) >= 1024)
2705             (void) FormatLocaleString(label,MagickPathExtent,
2706               "quality %s\n%gkb ",factor,(double) ((MagickOffsetType)
2707               GetBlobSize(preview_image))/1024.0);
2708           else
2709             (void) FormatLocaleString(label,MagickPathExtent,
2710               "quality %s\n%.20gb ",factor,(double) ((MagickOffsetType)
2711               GetBlobSize(thumbnail)));
2712         break;
2713       }
2714     }
2715     thumbnail=DestroyImage(thumbnail);
2716     percentage+=12.5;
2717     radius+=0.5;
2718     sigma+=0.25;
2719     if (preview_image == (Image *) NULL)
2720       break;
2721     (void) DeleteImageProperty(preview_image,"label");
2722     (void) SetImageProperty(preview_image,"label",label,exception);
2723     AppendImageToList(&images,preview_image);
2724     proceed=SetImageProgress(image,PreviewImageTag,(MagickOffsetType) i,
2725       NumberTiles);
2726     if (proceed == MagickFalse)
2727       break;
2728   }
2729   if (images == (Image *) NULL)
2730     {
2731       preview_info=DestroyImageInfo(preview_info);
2732       return((Image *) NULL);
2733     }
2734   /*
2735     Create the montage.
2736   */
2737   montage_info=CloneMontageInfo(preview_info,(MontageInfo *) NULL);
2738   (void) CopyMagickString(montage_info->filename,image->filename,
2739     MagickPathExtent);
2740   montage_info->shadow=MagickTrue;
2741   (void) CloneString(&montage_info->tile,"3x3");
2742   (void) CloneString(&montage_info->geometry,DefaultPreviewGeometry);
2743   (void) CloneString(&montage_info->frame,DefaultTileFrame);
2744   montage_image=MontageImages(images,montage_info,exception);
2745   montage_info=DestroyMontageInfo(montage_info);
2746   images=DestroyImageList(images);
2747   if (montage_image == (Image *) NULL)
2748     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2749   if (montage_image->montage != (char *) NULL)
2750     {
2751       /*
2752         Free image directory.
2753       */
2754       montage_image->montage=(char *) RelinquishMagickMemory(
2755         montage_image->montage);
2756       if (image->directory != (char *) NULL)
2757         montage_image->directory=(char *) RelinquishMagickMemory(
2758           montage_image->directory);
2759     }
2760   preview_info=DestroyImageInfo(preview_info);
2761   return(montage_image);
2762 }
2763 
2764 /*
2765 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2766 %                                                                             %
2767 %                                                                             %
2768 %                                                                             %
2769 %     R o t a t i o n a l B l u r I m a g e                                   %
2770 %                                                                             %
2771 %                                                                             %
2772 %                                                                             %
2773 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2774 %
2775 %  RotationalBlurImage() applies a radial blur to the image.
2776 %
2777 %  Andrew Protano contributed this effect.
2778 %
2779 %  The format of the RotationalBlurImage method is:
2780 %
2781 %    Image *RotationalBlurImage(const Image *image,const double angle,
2782 %      ExceptionInfo *exception)
2783 %
2784 %  A description of each parameter follows:
2785 %
2786 %    o image: the image.
2787 %
2788 %    o angle: the angle of the radial blur.
2789 %
2790 %    o blur: the blur.
2791 %
2792 %    o exception: return any errors or warnings in this structure.
2793 %
2794 */
RotationalBlurImage(const Image * image,const double angle,ExceptionInfo * exception)2795 MagickExport Image *RotationalBlurImage(const Image *image,const double angle,
2796   ExceptionInfo *exception)
2797 {
2798   CacheView
2799     *blur_view,
2800     *image_view,
2801     *radial_view;
2802 
2803   double
2804     blur_radius,
2805     *cos_theta,
2806     offset,
2807     *sin_theta,
2808     theta;
2809 
2810   Image
2811     *blur_image;
2812 
2813   MagickBooleanType
2814     status;
2815 
2816   MagickOffsetType
2817     progress;
2818 
2819   PointInfo
2820     blur_center;
2821 
2822   register ssize_t
2823     i;
2824 
2825   size_t
2826     n;
2827 
2828   ssize_t
2829     y;
2830 
2831   /*
2832     Allocate blur image.
2833   */
2834   assert(image != (Image *) NULL);
2835   assert(image->signature == MagickCoreSignature);
2836   if (image->debug != MagickFalse)
2837     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2838   assert(exception != (ExceptionInfo *) NULL);
2839   assert(exception->signature == MagickCoreSignature);
2840 #if defined(MAGICKCORE_OPENCL_SUPPORT)
2841   blur_image=AccelerateRotationalBlurImage(image,angle,exception);
2842   if (blur_image != (Image *) NULL)
2843     return(blur_image);
2844 #endif
2845   blur_image=CloneImage(image,0,0,MagickTrue,exception);
2846   if (blur_image == (Image *) NULL)
2847     return((Image *) NULL);
2848   if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
2849     {
2850       blur_image=DestroyImage(blur_image);
2851       return((Image *) NULL);
2852     }
2853   blur_center.x=(double) (image->columns-1)/2.0;
2854   blur_center.y=(double) (image->rows-1)/2.0;
2855   blur_radius=hypot(blur_center.x,blur_center.y);
2856   n=(size_t) fabs(4.0*DegreesToRadians(angle)*sqrt((double) blur_radius)+2UL);
2857   theta=DegreesToRadians(angle)/(double) (n-1);
2858   cos_theta=(double *) AcquireQuantumMemory((size_t) n,
2859     sizeof(*cos_theta));
2860   sin_theta=(double *) AcquireQuantumMemory((size_t) n,
2861     sizeof(*sin_theta));
2862   if ((cos_theta == (double *) NULL) ||
2863       (sin_theta == (double *) NULL))
2864     {
2865       if (cos_theta != (double *) NULL)
2866         cos_theta=(double *) RelinquishMagickMemory(cos_theta);
2867       if (sin_theta != (double *) NULL)
2868         sin_theta=(double *) RelinquishMagickMemory(sin_theta);
2869       blur_image=DestroyImage(blur_image);
2870       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2871     }
2872   offset=theta*(double) (n-1)/2.0;
2873   for (i=0; i < (ssize_t) n; i++)
2874   {
2875     cos_theta[i]=cos((double) (theta*i-offset));
2876     sin_theta[i]=sin((double) (theta*i-offset));
2877   }
2878   /*
2879     Radial blur image.
2880   */
2881   status=MagickTrue;
2882   progress=0;
2883   image_view=AcquireVirtualCacheView(image,exception);
2884   radial_view=AcquireVirtualCacheView(image,exception);
2885   blur_view=AcquireAuthenticCacheView(blur_image,exception);
2886 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2887   #pragma omp parallel for schedule(static) shared(progress,status) \
2888     magick_number_threads(image,blur_image,image->rows,1)
2889 #endif
2890   for (y=0; y < (ssize_t) image->rows; y++)
2891   {
2892     register const Quantum
2893       *magick_restrict p;
2894 
2895     register Quantum
2896       *magick_restrict q;
2897 
2898     register ssize_t
2899       x;
2900 
2901     if (status == MagickFalse)
2902       continue;
2903     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2904     q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
2905       exception);
2906     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2907       {
2908         status=MagickFalse;
2909         continue;
2910       }
2911     for (x=0; x < (ssize_t) image->columns; x++)
2912     {
2913       double
2914         radius;
2915 
2916       PointInfo
2917         center;
2918 
2919       register ssize_t
2920         i;
2921 
2922       size_t
2923         step;
2924 
2925       center.x=(double) x-blur_center.x;
2926       center.y=(double) y-blur_center.y;
2927       radius=hypot((double) center.x,center.y);
2928       if (radius == 0)
2929         step=1;
2930       else
2931         {
2932           step=(size_t) (blur_radius/radius);
2933           if (step == 0)
2934             step=1;
2935           else
2936             if (step >= n)
2937               step=n-1;
2938         }
2939       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2940       {
2941         double
2942           gamma,
2943           pixel;
2944 
2945         PixelChannel
2946           channel;
2947 
2948         PixelTrait
2949           blur_traits,
2950           traits;
2951 
2952         register const Quantum
2953           *magick_restrict r;
2954 
2955         register ssize_t
2956           j;
2957 
2958         channel=GetPixelChannelChannel(image,i);
2959         traits=GetPixelChannelTraits(image,channel);
2960         blur_traits=GetPixelChannelTraits(blur_image,channel);
2961         if ((traits == UndefinedPixelTrait) ||
2962             (blur_traits == UndefinedPixelTrait))
2963           continue;
2964         if ((blur_traits & CopyPixelTrait) != 0)
2965           {
2966             SetPixelChannel(blur_image,channel,p[i],q);
2967             continue;
2968           }
2969         gamma=0.0;
2970         pixel=0.0;
2971         if ((GetPixelChannelTraits(image,AlphaPixelChannel) == UndefinedPixelTrait) ||
2972             (channel == AlphaPixelChannel))
2973           {
2974             for (j=0; j < (ssize_t) n; j+=(ssize_t) step)
2975             {
2976               r=GetCacheViewVirtualPixels(radial_view, (ssize_t) (blur_center.x+
2977                 center.x*cos_theta[j]-center.y*sin_theta[j]+0.5),(ssize_t)
2978                 (blur_center.y+center.x*sin_theta[j]+center.y*cos_theta[j]+0.5),
2979                 1,1,exception);
2980               if (r == (const Quantum *) NULL)
2981                 {
2982                   status=MagickFalse;
2983                   continue;
2984                 }
2985               pixel+=r[i];
2986               gamma++;
2987             }
2988             gamma=PerceptibleReciprocal(gamma);
2989             SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
2990             continue;
2991           }
2992         for (j=0; j < (ssize_t) n; j+=(ssize_t) step)
2993         {
2994           double
2995             alpha;
2996 
2997           r=GetCacheViewVirtualPixels(radial_view, (ssize_t) (blur_center.x+
2998             center.x*cos_theta[j]-center.y*sin_theta[j]+0.5),(ssize_t)
2999             (blur_center.y+center.x*sin_theta[j]+center.y*cos_theta[j]+0.5),
3000             1,1,exception);
3001           if (r == (const Quantum *) NULL)
3002             {
3003               status=MagickFalse;
3004               continue;
3005             }
3006           alpha=(double) QuantumScale*GetPixelAlpha(image,r);
3007           pixel+=alpha*r[i];
3008           gamma+=alpha;
3009         }
3010         gamma=PerceptibleReciprocal(gamma);
3011         SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
3012       }
3013       p+=GetPixelChannels(image);
3014       q+=GetPixelChannels(blur_image);
3015     }
3016     if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
3017       status=MagickFalse;
3018     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3019       {
3020         MagickBooleanType
3021           proceed;
3022 
3023 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3024         #pragma omp atomic
3025 #endif
3026         progress++;
3027         proceed=SetImageProgress(image,BlurImageTag,progress,image->rows);
3028         if (proceed == MagickFalse)
3029           status=MagickFalse;
3030       }
3031   }
3032   blur_view=DestroyCacheView(blur_view);
3033   radial_view=DestroyCacheView(radial_view);
3034   image_view=DestroyCacheView(image_view);
3035   cos_theta=(double *) RelinquishMagickMemory(cos_theta);
3036   sin_theta=(double *) RelinquishMagickMemory(sin_theta);
3037   if (status == MagickFalse)
3038     blur_image=DestroyImage(blur_image);
3039   return(blur_image);
3040 }
3041 
3042 /*
3043 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3044 %                                                                             %
3045 %                                                                             %
3046 %                                                                             %
3047 %     S e l e c t i v e B l u r I m a g e                                     %
3048 %                                                                             %
3049 %                                                                             %
3050 %                                                                             %
3051 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3052 %
3053 %  SelectiveBlurImage() selectively blur pixels within a contrast threshold.
3054 %  It is similar to the unsharpen mask that sharpens everything with contrast
3055 %  above a certain threshold.
3056 %
3057 %  The format of the SelectiveBlurImage method is:
3058 %
3059 %      Image *SelectiveBlurImage(const Image *image,const double radius,
3060 %        const double sigma,const double threshold,ExceptionInfo *exception)
3061 %
3062 %  A description of each parameter follows:
3063 %
3064 %    o image: the image.
3065 %
3066 %    o radius: the radius of the Gaussian, in pixels, not counting the center
3067 %      pixel.
3068 %
3069 %    o sigma: the standard deviation of the Gaussian, in pixels.
3070 %
3071 %    o threshold: only pixels within this contrast threshold are included
3072 %      in the blur operation.
3073 %
3074 %    o exception: return any errors or warnings in this structure.
3075 %
3076 */
SelectiveBlurImage(const Image * image,const double radius,const double sigma,const double threshold,ExceptionInfo * exception)3077 MagickExport Image *SelectiveBlurImage(const Image *image,const double radius,
3078   const double sigma,const double threshold,ExceptionInfo *exception)
3079 {
3080 #define SelectiveBlurImageTag  "SelectiveBlur/Image"
3081 
3082   CacheView
3083     *blur_view,
3084     *image_view,
3085     *luminance_view;
3086 
3087   Image
3088     *blur_image,
3089     *luminance_image;
3090 
3091   MagickBooleanType
3092     status;
3093 
3094   MagickOffsetType
3095     progress;
3096 
3097   MagickRealType
3098     *kernel;
3099 
3100   register ssize_t
3101     i;
3102 
3103   size_t
3104     width;
3105 
3106   ssize_t
3107     center,
3108     j,
3109     u,
3110     v,
3111     y;
3112 
3113   /*
3114     Initialize blur image attributes.
3115   */
3116   assert(image != (Image *) NULL);
3117   assert(image->signature == MagickCoreSignature);
3118   if (image->debug != MagickFalse)
3119     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3120   assert(exception != (ExceptionInfo *) NULL);
3121   assert(exception->signature == MagickCoreSignature);
3122   width=GetOptimalKernelWidth1D(radius,sigma);
3123   kernel=(MagickRealType *) MagickAssumeAligned(AcquireAlignedMemory((size_t)
3124     width,width*sizeof(*kernel)));
3125   if (kernel == (MagickRealType *) NULL)
3126     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3127   j=(ssize_t) (width-1)/2;
3128   i=0;
3129   for (v=(-j); v <= j; v++)
3130   {
3131     for (u=(-j); u <= j; u++)
3132       kernel[i++]=(MagickRealType) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
3133         MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
3134   }
3135   if (image->debug != MagickFalse)
3136     {
3137       char
3138         format[MagickPathExtent],
3139         *message;
3140 
3141       register const MagickRealType
3142         *k;
3143 
3144       ssize_t
3145         u,
3146         v;
3147 
3148       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
3149         "  SelectiveBlurImage with %.20gx%.20g kernel:",(double) width,(double)
3150         width);
3151       message=AcquireString("");
3152       k=kernel;
3153       for (v=0; v < (ssize_t) width; v++)
3154       {
3155         *message='\0';
3156         (void) FormatLocaleString(format,MagickPathExtent,"%.20g: ",(double) v);
3157         (void) ConcatenateString(&message,format);
3158         for (u=0; u < (ssize_t) width; u++)
3159         {
3160           (void) FormatLocaleString(format,MagickPathExtent,"%+f ",(double)
3161             *k++);
3162           (void) ConcatenateString(&message,format);
3163         }
3164         (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
3165       }
3166       message=DestroyString(message);
3167     }
3168   blur_image=CloneImage(image,0,0,MagickTrue,exception);
3169   if (blur_image == (Image *) NULL)
3170     return((Image *) NULL);
3171   if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
3172     {
3173       blur_image=DestroyImage(blur_image);
3174       kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
3175       return((Image *) NULL);
3176     }
3177   luminance_image=CloneImage(image,0,0,MagickTrue,exception);
3178   if (luminance_image == (Image *) NULL)
3179     {
3180       blur_image=DestroyImage(blur_image);
3181       kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
3182       return((Image *) NULL);
3183     }
3184   status=TransformImageColorspace(luminance_image,GRAYColorspace,exception);
3185   if (status == MagickFalse)
3186     {
3187       luminance_image=DestroyImage(luminance_image);
3188       blur_image=DestroyImage(blur_image);
3189       kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
3190       return((Image *) NULL);
3191     }
3192   /*
3193     Threshold blur image.
3194   */
3195   status=MagickTrue;
3196   progress=0;
3197   center=(ssize_t) (GetPixelChannels(image)*(image->columns+width)*
3198     ((width-1)/2L)+GetPixelChannels(image)*((width-1)/2L));
3199   image_view=AcquireVirtualCacheView(image,exception);
3200   luminance_view=AcquireVirtualCacheView(luminance_image,exception);
3201   blur_view=AcquireAuthenticCacheView(blur_image,exception);
3202 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3203   #pragma omp parallel for schedule(static) shared(progress,status) \
3204     magick_number_threads(image,blur_image,image->rows,1)
3205 #endif
3206   for (y=0; y < (ssize_t) image->rows; y++)
3207   {
3208     double
3209       contrast;
3210 
3211     MagickBooleanType
3212       sync;
3213 
3214     register const Quantum
3215       *magick_restrict l,
3216       *magick_restrict p;
3217 
3218     register Quantum
3219       *magick_restrict q;
3220 
3221     register ssize_t
3222       x;
3223 
3224     if (status == MagickFalse)
3225       continue;
3226     p=GetCacheViewVirtualPixels(image_view,-((ssize_t) (width-1)/2L),y-(ssize_t)
3227       ((width-1)/2L),image->columns+width,width,exception);
3228     l=GetCacheViewVirtualPixels(luminance_view,-((ssize_t) (width-1)/2L),y-
3229       (ssize_t) ((width-1)/2L),luminance_image->columns+width,width,exception);
3230     q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
3231       exception);
3232     if ((p == (const Quantum *) NULL) || (l == (const Quantum *) NULL) ||
3233         (q == (Quantum *) NULL))
3234       {
3235         status=MagickFalse;
3236         continue;
3237       }
3238     for (x=0; x < (ssize_t) image->columns; x++)
3239     {
3240       double
3241         intensity;
3242 
3243       register ssize_t
3244         i;
3245 
3246       intensity=GetPixelIntensity(image,p+center);
3247       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3248       {
3249         double
3250           alpha,
3251           gamma,
3252           pixel;
3253 
3254         PixelChannel
3255           channel;
3256 
3257         PixelTrait
3258           blur_traits,
3259           traits;
3260 
3261         register const MagickRealType
3262           *magick_restrict k;
3263 
3264         register const Quantum
3265           *magick_restrict luminance_pixels,
3266           *magick_restrict pixels;
3267 
3268         register ssize_t
3269           u;
3270 
3271         ssize_t
3272           v;
3273 
3274         channel=GetPixelChannelChannel(image,i);
3275         traits=GetPixelChannelTraits(image,channel);
3276         blur_traits=GetPixelChannelTraits(blur_image,channel);
3277         if ((traits == UndefinedPixelTrait) ||
3278             (blur_traits == UndefinedPixelTrait))
3279           continue;
3280         if ((blur_traits & CopyPixelTrait) != 0)
3281           {
3282             SetPixelChannel(blur_image,channel,p[center+i],q);
3283             continue;
3284           }
3285         k=kernel;
3286         pixel=0.0;
3287         pixels=p;
3288         luminance_pixels=l;
3289         gamma=0.0;
3290         if ((blur_traits & BlendPixelTrait) == 0)
3291           {
3292             for (v=0; v < (ssize_t) width; v++)
3293             {
3294               for (u=0; u < (ssize_t) width; u++)
3295               {
3296                 contrast=GetPixelIntensity(luminance_image,luminance_pixels)-
3297                   intensity;
3298                 if (fabs(contrast) < threshold)
3299                   {
3300                     pixel+=(*k)*pixels[i];
3301                     gamma+=(*k);
3302                   }
3303                 k++;
3304                 pixels+=GetPixelChannels(image);
3305                 luminance_pixels+=GetPixelChannels(luminance_image);
3306               }
3307               pixels+=GetPixelChannels(image)*image->columns;
3308               luminance_pixels+=GetPixelChannels(luminance_image)*
3309                 luminance_image->columns;
3310             }
3311             if (fabs((double) gamma) < MagickEpsilon)
3312               {
3313                 SetPixelChannel(blur_image,channel,p[center+i],q);
3314                 continue;
3315               }
3316             gamma=PerceptibleReciprocal(gamma);
3317             SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
3318             continue;
3319           }
3320         for (v=0; v < (ssize_t) width; v++)
3321         {
3322           for (u=0; u < (ssize_t) width; u++)
3323           {
3324             contrast=GetPixelIntensity(image,pixels)-intensity;
3325             if (fabs(contrast) < threshold)
3326               {
3327                 alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
3328                 pixel+=(*k)*alpha*pixels[i];
3329                 gamma+=(*k)*alpha;
3330               }
3331             k++;
3332             pixels+=GetPixelChannels(image);
3333             luminance_pixels+=GetPixelChannels(luminance_image);
3334           }
3335           pixels+=GetPixelChannels(image)*image->columns;
3336           luminance_pixels+=GetPixelChannels(luminance_image)*
3337             luminance_image->columns;
3338         }
3339         if (fabs((double) gamma) < MagickEpsilon)
3340           {
3341             SetPixelChannel(blur_image,channel,p[center+i],q);
3342             continue;
3343           }
3344         gamma=PerceptibleReciprocal(gamma);
3345         SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
3346       }
3347       p+=GetPixelChannels(image);
3348       l+=GetPixelChannels(luminance_image);
3349       q+=GetPixelChannels(blur_image);
3350     }
3351     sync=SyncCacheViewAuthenticPixels(blur_view,exception);
3352     if (sync == MagickFalse)
3353       status=MagickFalse;
3354     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3355       {
3356         MagickBooleanType
3357           proceed;
3358 
3359 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3360         #pragma omp atomic
3361 #endif
3362         progress++;
3363         proceed=SetImageProgress(image,SelectiveBlurImageTag,progress,
3364           image->rows);
3365         if (proceed == MagickFalse)
3366           status=MagickFalse;
3367       }
3368   }
3369   blur_image->type=image->type;
3370   blur_view=DestroyCacheView(blur_view);
3371   image_view=DestroyCacheView(image_view);
3372   luminance_image=DestroyImage(luminance_image);
3373   kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
3374   if (status == MagickFalse)
3375     blur_image=DestroyImage(blur_image);
3376   return(blur_image);
3377 }
3378 
3379 /*
3380 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3381 %                                                                             %
3382 %                                                                             %
3383 %                                                                             %
3384 %     S h a d e I m a g e                                                     %
3385 %                                                                             %
3386 %                                                                             %
3387 %                                                                             %
3388 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3389 %
3390 %  ShadeImage() shines a distant light on an image to create a
3391 %  three-dimensional effect. You control the positioning of the light with
3392 %  azimuth and elevation; azimuth is measured in degrees off the x axis
3393 %  and elevation is measured in pixels above the Z axis.
3394 %
3395 %  The format of the ShadeImage method is:
3396 %
3397 %      Image *ShadeImage(const Image *image,const MagickBooleanType gray,
3398 %        const double azimuth,const double elevation,ExceptionInfo *exception)
3399 %
3400 %  A description of each parameter follows:
3401 %
3402 %    o image: the image.
3403 %
3404 %    o gray: A value other than zero shades the intensity of each pixel.
3405 %
3406 %    o azimuth, elevation:  Define the light source direction.
3407 %
3408 %    o exception: return any errors or warnings in this structure.
3409 %
3410 */
ShadeImage(const Image * image,const MagickBooleanType gray,const double azimuth,const double elevation,ExceptionInfo * exception)3411 MagickExport Image *ShadeImage(const Image *image,const MagickBooleanType gray,
3412   const double azimuth,const double elevation,ExceptionInfo *exception)
3413 {
3414 #define GetShadeIntensity(image,pixel) \
3415   ClampPixel(GetPixelIntensity((image),(pixel)))
3416 #define ShadeImageTag  "Shade/Image"
3417 
3418   CacheView
3419     *image_view,
3420     *shade_view;
3421 
3422   Image
3423     *linear_image,
3424     *shade_image;
3425 
3426   MagickBooleanType
3427     status;
3428 
3429   MagickOffsetType
3430     progress;
3431 
3432   PrimaryInfo
3433     light;
3434 
3435   ssize_t
3436     y;
3437 
3438   /*
3439     Initialize shaded image attributes.
3440   */
3441   assert(image != (const Image *) NULL);
3442   assert(image->signature == MagickCoreSignature);
3443   if (image->debug != MagickFalse)
3444     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3445   assert(exception != (ExceptionInfo *) NULL);
3446   assert(exception->signature == MagickCoreSignature);
3447   linear_image=CloneImage(image,0,0,MagickTrue,exception);
3448   shade_image=CloneImage(image,0,0,MagickTrue,exception);
3449   if ((linear_image == (Image *) NULL) || (shade_image == (Image *) NULL))
3450     {
3451       if (linear_image != (Image *) NULL)
3452         linear_image=DestroyImage(linear_image);
3453       if (shade_image != (Image *) NULL)
3454         shade_image=DestroyImage(shade_image);
3455       return((Image *) NULL);
3456     }
3457   if (SetImageStorageClass(shade_image,DirectClass,exception) == MagickFalse)
3458     {
3459       linear_image=DestroyImage(linear_image);
3460       shade_image=DestroyImage(shade_image);
3461       return((Image *) NULL);
3462     }
3463   /*
3464     Compute the light vector.
3465   */
3466   light.x=(double) QuantumRange*cos(DegreesToRadians(azimuth))*
3467     cos(DegreesToRadians(elevation));
3468   light.y=(double) QuantumRange*sin(DegreesToRadians(azimuth))*
3469     cos(DegreesToRadians(elevation));
3470   light.z=(double) QuantumRange*sin(DegreesToRadians(elevation));
3471   /*
3472     Shade image.
3473   */
3474   status=MagickTrue;
3475   progress=0;
3476   image_view=AcquireVirtualCacheView(linear_image,exception);
3477   shade_view=AcquireAuthenticCacheView(shade_image,exception);
3478 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3479   #pragma omp parallel for schedule(static) shared(progress,status) \
3480     magick_number_threads(linear_image,shade_image,linear_image->rows,1)
3481 #endif
3482   for (y=0; y < (ssize_t) linear_image->rows; y++)
3483   {
3484     double
3485       distance,
3486       normal_distance,
3487       shade;
3488 
3489     PrimaryInfo
3490       normal;
3491 
3492     register const Quantum
3493       *magick_restrict center,
3494       *magick_restrict p,
3495       *magick_restrict post,
3496       *magick_restrict pre;
3497 
3498     register Quantum
3499       *magick_restrict q;
3500 
3501     register ssize_t
3502       x;
3503 
3504     if (status == MagickFalse)
3505       continue;
3506     p=GetCacheViewVirtualPixels(image_view,-1,y-1,linear_image->columns+2,3,
3507       exception);
3508     q=QueueCacheViewAuthenticPixels(shade_view,0,y,shade_image->columns,1,
3509       exception);
3510     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3511       {
3512         status=MagickFalse;
3513         continue;
3514       }
3515     /*
3516       Shade this row of pixels.
3517     */
3518     normal.z=2.0*(double) QuantumRange;  /* constant Z of surface normal */
3519     for (x=0; x < (ssize_t) linear_image->columns; x++)
3520     {
3521       register ssize_t
3522         i;
3523 
3524       /*
3525         Determine the surface normal and compute shading.
3526       */
3527       pre=p+GetPixelChannels(linear_image);
3528       center=pre+(linear_image->columns+2)*GetPixelChannels(linear_image);
3529       post=center+(linear_image->columns+2)*GetPixelChannels(linear_image);
3530       normal.x=(double) (
3531         GetShadeIntensity(linear_image,pre-GetPixelChannels(linear_image))+
3532         GetShadeIntensity(linear_image,center-GetPixelChannels(linear_image))+
3533         GetShadeIntensity(linear_image,post-GetPixelChannels(linear_image))-
3534         GetShadeIntensity(linear_image,pre+GetPixelChannels(linear_image))-
3535         GetShadeIntensity(linear_image,center+GetPixelChannels(linear_image))-
3536         GetShadeIntensity(linear_image,post+GetPixelChannels(linear_image)));
3537       normal.y=(double) (
3538         GetShadeIntensity(linear_image,post-GetPixelChannels(linear_image))+
3539         GetShadeIntensity(linear_image,post)+
3540         GetShadeIntensity(linear_image,post+GetPixelChannels(linear_image))-
3541         GetShadeIntensity(linear_image,pre-GetPixelChannels(linear_image))-
3542         GetShadeIntensity(linear_image,pre)-
3543         GetShadeIntensity(linear_image,pre+GetPixelChannels(linear_image)));
3544       if ((fabs(normal.x) <= MagickEpsilon) &&
3545           (fabs(normal.y) <= MagickEpsilon))
3546         shade=light.z;
3547       else
3548         {
3549           shade=0.0;
3550           distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
3551           if (distance > MagickEpsilon)
3552             {
3553               normal_distance=normal.x*normal.x+normal.y*normal.y+
3554                 normal.z*normal.z;
3555               if (normal_distance > (MagickEpsilon*MagickEpsilon))
3556                 shade=distance/sqrt((double) normal_distance);
3557             }
3558         }
3559       for (i=0; i < (ssize_t) GetPixelChannels(linear_image); i++)
3560       {
3561         PixelChannel
3562           channel;
3563 
3564         PixelTrait
3565           shade_traits,
3566           traits;
3567 
3568         channel=GetPixelChannelChannel(linear_image,i);
3569         traits=GetPixelChannelTraits(linear_image,channel);
3570         shade_traits=GetPixelChannelTraits(shade_image,channel);
3571         if ((traits == UndefinedPixelTrait) ||
3572             (shade_traits == UndefinedPixelTrait))
3573           continue;
3574         if ((shade_traits & CopyPixelTrait) != 0)
3575           {
3576             SetPixelChannel(shade_image,channel,center[i],q);
3577             continue;
3578           }
3579         if ((traits & UpdatePixelTrait) == 0)
3580           {
3581             SetPixelChannel(shade_image,channel,center[i],q);
3582             continue;
3583           }
3584         if (gray != MagickFalse)
3585           {
3586             SetPixelChannel(shade_image,channel,ClampToQuantum(shade),q);
3587             continue;
3588           }
3589         SetPixelChannel(shade_image,channel,ClampToQuantum(QuantumScale*shade*
3590           center[i]),q);
3591       }
3592       p+=GetPixelChannels(linear_image);
3593       q+=GetPixelChannels(shade_image);
3594     }
3595     if (SyncCacheViewAuthenticPixels(shade_view,exception) == MagickFalse)
3596       status=MagickFalse;
3597     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3598       {
3599         MagickBooleanType
3600           proceed;
3601 
3602 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3603         #pragma omp atomic
3604 #endif
3605         progress++;
3606         proceed=SetImageProgress(image,ShadeImageTag,progress,image->rows);
3607         if (proceed == MagickFalse)
3608           status=MagickFalse;
3609       }
3610   }
3611   shade_view=DestroyCacheView(shade_view);
3612   image_view=DestroyCacheView(image_view);
3613   linear_image=DestroyImage(linear_image);
3614   if (status == MagickFalse)
3615     shade_image=DestroyImage(shade_image);
3616   return(shade_image);
3617 }
3618 
3619 /*
3620 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3621 %                                                                             %
3622 %                                                                             %
3623 %                                                                             %
3624 %     S h a r p e n I m a g e                                                 %
3625 %                                                                             %
3626 %                                                                             %
3627 %                                                                             %
3628 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3629 %
3630 %  SharpenImage() sharpens the image.  We convolve the image with a Gaussian
3631 %  operator of the given radius and standard deviation (sigma).  For
3632 %  reasonable results, radius should be larger than sigma.  Use a radius of 0
3633 %  and SharpenImage() selects a suitable radius for you.
3634 %
3635 %  Using a separable kernel would be faster, but the negative weights cancel
3636 %  out on the corners of the kernel producing often undesirable ringing in the
3637 %  filtered result; this can be avoided by using a 2D gaussian shaped image
3638 %  sharpening kernel instead.
3639 %
3640 %  The format of the SharpenImage method is:
3641 %
3642 %    Image *SharpenImage(const Image *image,const double radius,
3643 %      const double sigma,ExceptionInfo *exception)
3644 %
3645 %  A description of each parameter follows:
3646 %
3647 %    o image: the image.
3648 %
3649 %    o radius: the radius of the Gaussian, in pixels, not counting the center
3650 %      pixel.
3651 %
3652 %    o sigma: the standard deviation of the Laplacian, in pixels.
3653 %
3654 %    o exception: return any errors or warnings in this structure.
3655 %
3656 */
SharpenImage(const Image * image,const double radius,const double sigma,ExceptionInfo * exception)3657 MagickExport Image *SharpenImage(const Image *image,const double radius,
3658   const double sigma,ExceptionInfo *exception)
3659 {
3660   double
3661     gamma,
3662     normalize;
3663 
3664   Image
3665     *sharp_image;
3666 
3667   KernelInfo
3668     *kernel_info;
3669 
3670   register ssize_t
3671     i;
3672 
3673   size_t
3674     width;
3675 
3676   ssize_t
3677     j,
3678     u,
3679     v;
3680 
3681   assert(image != (const Image *) NULL);
3682   assert(image->signature == MagickCoreSignature);
3683   if (image->debug != MagickFalse)
3684     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3685   assert(exception != (ExceptionInfo *) NULL);
3686   assert(exception->signature == MagickCoreSignature);
3687   width=GetOptimalKernelWidth2D(radius,sigma);
3688   kernel_info=AcquireKernelInfo((const char *) NULL,exception);
3689   if (kernel_info == (KernelInfo *) NULL)
3690     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3691   (void) memset(kernel_info,0,sizeof(*kernel_info));
3692   kernel_info->width=width;
3693   kernel_info->height=width;
3694   kernel_info->x=(ssize_t) (width-1)/2;
3695   kernel_info->y=(ssize_t) (width-1)/2;
3696   kernel_info->signature=MagickCoreSignature;
3697   kernel_info->values=(MagickRealType *) MagickAssumeAligned(
3698     AcquireAlignedMemory(kernel_info->width,kernel_info->height*
3699     sizeof(*kernel_info->values)));
3700   if (kernel_info->values == (MagickRealType *) NULL)
3701     {
3702       kernel_info=DestroyKernelInfo(kernel_info);
3703       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3704     }
3705   normalize=0.0;
3706   j=(ssize_t) (kernel_info->width-1)/2;
3707   i=0;
3708   for (v=(-j); v <= j; v++)
3709   {
3710     for (u=(-j); u <= j; u++)
3711     {
3712       kernel_info->values[i]=(MagickRealType) (-exp(-((double) u*u+v*v)/(2.0*
3713         MagickSigma*MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
3714       normalize+=kernel_info->values[i];
3715       i++;
3716     }
3717   }
3718   kernel_info->values[i/2]=(double) ((-2.0)*normalize);
3719   normalize=0.0;
3720   for (i=0; i < (ssize_t) (kernel_info->width*kernel_info->height); i++)
3721     normalize+=kernel_info->values[i];
3722   gamma=PerceptibleReciprocal(normalize);
3723   for (i=0; i < (ssize_t) (kernel_info->width*kernel_info->height); i++)
3724     kernel_info->values[i]*=gamma;
3725   sharp_image=ConvolveImage(image,kernel_info,exception);
3726   kernel_info=DestroyKernelInfo(kernel_info);
3727   return(sharp_image);
3728 }
3729 
3730 /*
3731 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3732 %                                                                             %
3733 %                                                                             %
3734 %                                                                             %
3735 %     S p r e a d I m a g e                                                   %
3736 %                                                                             %
3737 %                                                                             %
3738 %                                                                             %
3739 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3740 %
3741 %  SpreadImage() is a special effects method that randomly displaces each
3742 %  pixel in a square area defined by the radius parameter.
3743 %
3744 %  The format of the SpreadImage method is:
3745 %
3746 %      Image *SpreadImage(const Image *image,
3747 %        const PixelInterpolateMethod method,const double radius,
3748 %        ExceptionInfo *exception)
3749 %
3750 %  A description of each parameter follows:
3751 %
3752 %    o image: the image.
3753 %
3754 %    o method:  intepolation method.
3755 %
3756 %    o radius:  choose a random pixel in a neighborhood of this extent.
3757 %
3758 %    o exception: return any errors or warnings in this structure.
3759 %
3760 */
SpreadImage(const Image * image,const PixelInterpolateMethod method,const double radius,ExceptionInfo * exception)3761 MagickExport Image *SpreadImage(const Image *image,
3762   const PixelInterpolateMethod method,const double radius,
3763   ExceptionInfo *exception)
3764 {
3765 #define SpreadImageTag  "Spread/Image"
3766 
3767   CacheView
3768     *image_view,
3769     *spread_view;
3770 
3771   Image
3772     *spread_image;
3773 
3774   MagickBooleanType
3775     status;
3776 
3777   MagickOffsetType
3778     progress;
3779 
3780   RandomInfo
3781     **magick_restrict random_info;
3782 
3783   size_t
3784     width;
3785 
3786   ssize_t
3787     y;
3788 
3789 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3790   unsigned long
3791     key;
3792 #endif
3793 
3794   /*
3795     Initialize spread image attributes.
3796   */
3797   assert(image != (Image *) NULL);
3798   assert(image->signature == MagickCoreSignature);
3799   if (image->debug != MagickFalse)
3800     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3801   assert(exception != (ExceptionInfo *) NULL);
3802   assert(exception->signature == MagickCoreSignature);
3803   spread_image=CloneImage(image,0,0,MagickTrue,exception);
3804   if (spread_image == (Image *) NULL)
3805     return((Image *) NULL);
3806   if (SetImageStorageClass(spread_image,DirectClass,exception) == MagickFalse)
3807     {
3808       spread_image=DestroyImage(spread_image);
3809       return((Image *) NULL);
3810     }
3811   /*
3812     Spread image.
3813   */
3814   status=MagickTrue;
3815   progress=0;
3816   width=GetOptimalKernelWidth1D(radius,0.5);
3817   random_info=AcquireRandomInfoThreadSet();
3818   image_view=AcquireVirtualCacheView(image,exception);
3819   spread_view=AcquireAuthenticCacheView(spread_image,exception);
3820 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3821   key=GetRandomSecretKey(random_info[0]);
3822   #pragma omp parallel for schedule(static) shared(progress,status) \
3823     magick_number_threads(image,spread_image,image->rows,key == ~0UL)
3824 #endif
3825   for (y=0; y < (ssize_t) image->rows; y++)
3826   {
3827     const int
3828       id = GetOpenMPThreadId();
3829 
3830     register Quantum
3831       *magick_restrict q;
3832 
3833     register ssize_t
3834       x;
3835 
3836     if (status == MagickFalse)
3837       continue;
3838     q=QueueCacheViewAuthenticPixels(spread_view,0,y,spread_image->columns,1,
3839       exception);
3840     if (q == (Quantum *) NULL)
3841       {
3842         status=MagickFalse;
3843         continue;
3844       }
3845     for (x=0; x < (ssize_t) image->columns; x++)
3846     {
3847       PointInfo
3848         point;
3849 
3850       point.x=GetPseudoRandomValue(random_info[id]);
3851       point.y=GetPseudoRandomValue(random_info[id]);
3852       status=InterpolatePixelChannels(image,image_view,spread_image,method,
3853         (double) x+width*(point.x-0.5),(double) y+width*(point.y-0.5),q,
3854         exception);
3855       if (status == MagickFalse)
3856         break;
3857       q+=GetPixelChannels(spread_image);
3858     }
3859     if (SyncCacheViewAuthenticPixels(spread_view,exception) == MagickFalse)
3860       status=MagickFalse;
3861     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3862       {
3863         MagickBooleanType
3864           proceed;
3865 
3866 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3867         #pragma omp atomic
3868 #endif
3869         progress++;
3870         proceed=SetImageProgress(image,SpreadImageTag,progress,image->rows);
3871         if (proceed == MagickFalse)
3872           status=MagickFalse;
3873       }
3874   }
3875   spread_view=DestroyCacheView(spread_view);
3876   image_view=DestroyCacheView(image_view);
3877   random_info=DestroyRandomInfoThreadSet(random_info);
3878   if (status == MagickFalse)
3879     spread_image=DestroyImage(spread_image);
3880   return(spread_image);
3881 }
3882 
3883 /*
3884 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3885 %                                                                             %
3886 %                                                                             %
3887 %                                                                             %
3888 %     U n s h a r p M a s k I m a g e                                         %
3889 %                                                                             %
3890 %                                                                             %
3891 %                                                                             %
3892 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3893 %
3894 %  UnsharpMaskImage() sharpens one or more image channels.  We convolve the
3895 %  image with a Gaussian operator of the given radius and standard deviation
3896 %  (sigma).  For reasonable results, radius should be larger than sigma.  Use a
3897 %  radius of 0 and UnsharpMaskImage() selects a suitable radius for you.
3898 %
3899 %  The format of the UnsharpMaskImage method is:
3900 %
3901 %    Image *UnsharpMaskImage(const Image *image,const double radius,
3902 %      const double sigma,const double amount,const double threshold,
3903 %      ExceptionInfo *exception)
3904 %
3905 %  A description of each parameter follows:
3906 %
3907 %    o image: the image.
3908 %
3909 %    o radius: the radius of the Gaussian, in pixels, not counting the center
3910 %      pixel.
3911 %
3912 %    o sigma: the standard deviation of the Gaussian, in pixels.
3913 %
3914 %    o gain: the percentage of the difference between the original and the
3915 %      blur image that is added back into the original.
3916 %
3917 %    o threshold: the threshold in pixels needed to apply the diffence gain.
3918 %
3919 %    o exception: return any errors or warnings in this structure.
3920 %
3921 */
UnsharpMaskImage(const Image * image,const double radius,const double sigma,const double gain,const double threshold,ExceptionInfo * exception)3922 MagickExport Image *UnsharpMaskImage(const Image *image,const double radius,
3923   const double sigma,const double gain,const double threshold,
3924   ExceptionInfo *exception)
3925 {
3926 #define SharpenImageTag  "Sharpen/Image"
3927 
3928   CacheView
3929     *image_view,
3930     *unsharp_view;
3931 
3932   Image
3933     *unsharp_image;
3934 
3935   MagickBooleanType
3936     status;
3937 
3938   MagickOffsetType
3939     progress;
3940 
3941   double
3942     quantum_threshold;
3943 
3944   ssize_t
3945     y;
3946 
3947   assert(image != (const Image *) NULL);
3948   assert(image->signature == MagickCoreSignature);
3949   if (image->debug != MagickFalse)
3950     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3951   assert(exception != (ExceptionInfo *) NULL);
3952 #if defined(MAGICKCORE_OPENCL_SUPPORT)
3953   unsharp_image=AccelerateUnsharpMaskImage(image,radius,sigma,gain,threshold,
3954     exception);
3955   if (unsharp_image != (Image *) NULL)
3956     return(unsharp_image);
3957 #endif
3958   unsharp_image=BlurImage(image,radius,sigma,exception);
3959   if (unsharp_image == (Image *) NULL)
3960     return((Image *) NULL);
3961   quantum_threshold=(double) QuantumRange*threshold;
3962   /*
3963     Unsharp-mask image.
3964   */
3965   status=MagickTrue;
3966   progress=0;
3967   image_view=AcquireVirtualCacheView(image,exception);
3968   unsharp_view=AcquireAuthenticCacheView(unsharp_image,exception);
3969 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3970   #pragma omp parallel for schedule(static) shared(progress,status) \
3971     magick_number_threads(image,unsharp_image,image->rows,1)
3972 #endif
3973   for (y=0; y < (ssize_t) image->rows; y++)
3974   {
3975     register const Quantum
3976       *magick_restrict p;
3977 
3978     register Quantum
3979       *magick_restrict q;
3980 
3981     register ssize_t
3982       x;
3983 
3984     if (status == MagickFalse)
3985       continue;
3986     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3987     q=QueueCacheViewAuthenticPixels(unsharp_view,0,y,unsharp_image->columns,1,
3988       exception);
3989     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3990       {
3991         status=MagickFalse;
3992         continue;
3993       }
3994     for (x=0; x < (ssize_t) image->columns; x++)
3995     {
3996       register ssize_t
3997         i;
3998 
3999       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4000       {
4001         double
4002           pixel;
4003 
4004         PixelChannel
4005           channel;
4006 
4007         PixelTrait
4008           traits,
4009           unsharp_traits;
4010 
4011         channel=GetPixelChannelChannel(image,i);
4012         traits=GetPixelChannelTraits(image,channel);
4013         unsharp_traits=GetPixelChannelTraits(unsharp_image,channel);
4014         if ((traits == UndefinedPixelTrait) ||
4015             (unsharp_traits == UndefinedPixelTrait))
4016           continue;
4017         if ((unsharp_traits & CopyPixelTrait) != 0)
4018           {
4019             SetPixelChannel(unsharp_image,channel,p[i],q);
4020             continue;
4021           }
4022         pixel=p[i]-(double) GetPixelChannel(unsharp_image,channel,q);
4023         if (fabs(2.0*pixel) < quantum_threshold)
4024           pixel=(double) p[i];
4025         else
4026           pixel=(double) p[i]+gain*pixel;
4027         SetPixelChannel(unsharp_image,channel,ClampToQuantum(pixel),q);
4028       }
4029       p+=GetPixelChannels(image);
4030       q+=GetPixelChannels(unsharp_image);
4031     }
4032     if (SyncCacheViewAuthenticPixels(unsharp_view,exception) == MagickFalse)
4033       status=MagickFalse;
4034     if (image->progress_monitor != (MagickProgressMonitor) NULL)
4035       {
4036         MagickBooleanType
4037           proceed;
4038 
4039 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4040         #pragma omp atomic
4041 #endif
4042         progress++;
4043         proceed=SetImageProgress(image,SharpenImageTag,progress,image->rows);
4044         if (proceed == MagickFalse)
4045           status=MagickFalse;
4046       }
4047   }
4048   unsharp_image->type=image->type;
4049   unsharp_view=DestroyCacheView(unsharp_view);
4050   image_view=DestroyCacheView(image_view);
4051   if (status == MagickFalse)
4052     unsharp_image=DestroyImage(unsharp_image);
4053   return(unsharp_image);
4054 }
4055