• 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-2020 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*((ssize_t) 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*((ssize_t) columns+2)+x_offset);
935   s=q-(y_offset*((ssize_t) 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         RectangleInfo
2596           raise;
2597 
2598         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2599         if (preview_image == (Image *) NULL)
2600           break;
2601         raise.width=(size_t) (2*i+2);
2602         raise.height=(size_t) (2*i+2);
2603         raise.x=(i-1)/2;
2604         raise.y=(i-1)/2;
2605         (void) RaiseImage(preview_image,&raise,MagickTrue,exception);
2606         (void) FormatLocaleString(label,MagickPathExtent,
2607           "raise %.20gx%.20g%+.20g%+.20g",(double) raise.width,(double)
2608           raise.height,(double) raise.x,(double) raise.y);
2609         break;
2610       }
2611       case SegmentPreview:
2612       {
2613         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2614         if (preview_image == (Image *) NULL)
2615           break;
2616         threshold+=0.4f;
2617         (void) SegmentImage(preview_image,sRGBColorspace,MagickFalse,threshold,
2618           threshold,exception);
2619         (void) FormatLocaleString(label,MagickPathExtent,"segment %gx%g",
2620           threshold,threshold);
2621         break;
2622       }
2623       case SwirlPreview:
2624       {
2625         preview_image=SwirlImage(thumbnail,degrees,image->interpolate,
2626           exception);
2627         (void) FormatLocaleString(label,MagickPathExtent,"swirl %g",degrees);
2628         degrees+=45.0;
2629         break;
2630       }
2631       case ImplodePreview:
2632       {
2633         degrees+=0.1f;
2634         preview_image=ImplodeImage(thumbnail,degrees,image->interpolate,
2635           exception);
2636         (void) FormatLocaleString(label,MagickPathExtent,"implode %g",degrees);
2637         break;
2638       }
2639       case WavePreview:
2640       {
2641         degrees+=5.0f;
2642         preview_image=WaveImage(thumbnail,0.5*degrees,2.0*degrees,
2643           image->interpolate,exception);
2644         (void) FormatLocaleString(label,MagickPathExtent,"wave %gx%g",0.5*
2645           degrees,2.0*degrees);
2646         break;
2647       }
2648       case OilPaintPreview:
2649       {
2650         preview_image=OilPaintImage(thumbnail,(double) radius,(double) sigma,
2651           exception);
2652         (void) FormatLocaleString(label,MagickPathExtent,"charcoal %gx%g",
2653           radius,sigma);
2654         break;
2655       }
2656       case CharcoalDrawingPreview:
2657       {
2658         preview_image=CharcoalImage(thumbnail,(double) radius,(double) sigma,
2659           exception);
2660         (void) FormatLocaleString(label,MagickPathExtent,"charcoal %gx%g",
2661           radius,sigma);
2662         break;
2663       }
2664       case JPEGPreview:
2665       {
2666         char
2667           filename[MagickPathExtent];
2668 
2669         int
2670           file;
2671 
2672         MagickBooleanType
2673           status;
2674 
2675         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2676         if (preview_image == (Image *) NULL)
2677           break;
2678         preview_info->quality=(size_t) percentage;
2679         (void) FormatLocaleString(factor,MagickPathExtent,"%.20g",(double)
2680           preview_info->quality);
2681         file=AcquireUniqueFileResource(filename);
2682         if (file != -1)
2683           file=close(file)-1;
2684         (void) FormatLocaleString(preview_image->filename,MagickPathExtent,
2685           "jpeg:%s",filename);
2686         status=WriteImage(preview_info,preview_image,exception);
2687         if (status != MagickFalse)
2688           {
2689             Image
2690               *quality_image;
2691 
2692             (void) CopyMagickString(preview_info->filename,
2693               preview_image->filename,MagickPathExtent);
2694             quality_image=ReadImage(preview_info,exception);
2695             if (quality_image != (Image *) NULL)
2696               {
2697                 preview_image=DestroyImage(preview_image);
2698                 preview_image=quality_image;
2699               }
2700           }
2701         (void) RelinquishUniqueFileResource(preview_image->filename);
2702         if ((GetBlobSize(preview_image)/1024) >= 1024)
2703           (void) FormatLocaleString(label,MagickPathExtent,"quality %s\n%gmb ",
2704             factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
2705             1024.0/1024.0);
2706         else
2707           if (GetBlobSize(preview_image) >= 1024)
2708             (void) FormatLocaleString(label,MagickPathExtent,
2709               "quality %s\n%gkb ",factor,(double) ((MagickOffsetType)
2710               GetBlobSize(preview_image))/1024.0);
2711           else
2712             (void) FormatLocaleString(label,MagickPathExtent,
2713               "quality %s\n%.20gb ",factor,(double) ((MagickOffsetType)
2714               GetBlobSize(thumbnail)));
2715         break;
2716       }
2717     }
2718     thumbnail=DestroyImage(thumbnail);
2719     percentage+=12.5;
2720     radius+=0.5;
2721     sigma+=0.25;
2722     if (preview_image == (Image *) NULL)
2723       break;
2724     (void) DeleteImageProperty(preview_image,"label");
2725     (void) SetImageProperty(preview_image,"label",label,exception);
2726     AppendImageToList(&images,preview_image);
2727     proceed=SetImageProgress(image,PreviewImageTag,(MagickOffsetType) i,
2728       NumberTiles);
2729     if (proceed == MagickFalse)
2730       break;
2731   }
2732   if (images == (Image *) NULL)
2733     {
2734       preview_info=DestroyImageInfo(preview_info);
2735       return((Image *) NULL);
2736     }
2737   /*
2738     Create the montage.
2739   */
2740   montage_info=CloneMontageInfo(preview_info,(MontageInfo *) NULL);
2741   (void) CopyMagickString(montage_info->filename,image->filename,
2742     MagickPathExtent);
2743   montage_info->shadow=MagickTrue;
2744   (void) CloneString(&montage_info->tile,"3x3");
2745   (void) CloneString(&montage_info->geometry,DefaultPreviewGeometry);
2746   (void) CloneString(&montage_info->frame,DefaultTileFrame);
2747   montage_image=MontageImages(images,montage_info,exception);
2748   montage_info=DestroyMontageInfo(montage_info);
2749   images=DestroyImageList(images);
2750   if (montage_image == (Image *) NULL)
2751     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2752   if (montage_image->montage != (char *) NULL)
2753     {
2754       /*
2755         Free image directory.
2756       */
2757       montage_image->montage=(char *) RelinquishMagickMemory(
2758         montage_image->montage);
2759       if (image->directory != (char *) NULL)
2760         montage_image->directory=(char *) RelinquishMagickMemory(
2761           montage_image->directory);
2762     }
2763   preview_info=DestroyImageInfo(preview_info);
2764   return(montage_image);
2765 }
2766 
2767 /*
2768 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2769 %                                                                             %
2770 %                                                                             %
2771 %                                                                             %
2772 %     R o t a t i o n a l B l u r I m a g e                                   %
2773 %                                                                             %
2774 %                                                                             %
2775 %                                                                             %
2776 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2777 %
2778 %  RotationalBlurImage() applies a radial blur to the image.
2779 %
2780 %  Andrew Protano contributed this effect.
2781 %
2782 %  The format of the RotationalBlurImage method is:
2783 %
2784 %    Image *RotationalBlurImage(const Image *image,const double angle,
2785 %      ExceptionInfo *exception)
2786 %
2787 %  A description of each parameter follows:
2788 %
2789 %    o image: the image.
2790 %
2791 %    o angle: the angle of the radial blur.
2792 %
2793 %    o blur: the blur.
2794 %
2795 %    o exception: return any errors or warnings in this structure.
2796 %
2797 */
RotationalBlurImage(const Image * image,const double angle,ExceptionInfo * exception)2798 MagickExport Image *RotationalBlurImage(const Image *image,const double angle,
2799   ExceptionInfo *exception)
2800 {
2801   CacheView
2802     *blur_view,
2803     *image_view,
2804     *radial_view;
2805 
2806   double
2807     blur_radius,
2808     *cos_theta,
2809     offset,
2810     *sin_theta,
2811     theta;
2812 
2813   Image
2814     *blur_image;
2815 
2816   MagickBooleanType
2817     status;
2818 
2819   MagickOffsetType
2820     progress;
2821 
2822   PointInfo
2823     blur_center;
2824 
2825   register ssize_t
2826     i;
2827 
2828   size_t
2829     n;
2830 
2831   ssize_t
2832     y;
2833 
2834   /*
2835     Allocate blur image.
2836   */
2837   assert(image != (Image *) NULL);
2838   assert(image->signature == MagickCoreSignature);
2839   if (image->debug != MagickFalse)
2840     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2841   assert(exception != (ExceptionInfo *) NULL);
2842   assert(exception->signature == MagickCoreSignature);
2843 #if defined(MAGICKCORE_OPENCL_SUPPORT)
2844   blur_image=AccelerateRotationalBlurImage(image,angle,exception);
2845   if (blur_image != (Image *) NULL)
2846     return(blur_image);
2847 #endif
2848   blur_image=CloneImage(image,0,0,MagickTrue,exception);
2849   if (blur_image == (Image *) NULL)
2850     return((Image *) NULL);
2851   if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
2852     {
2853       blur_image=DestroyImage(blur_image);
2854       return((Image *) NULL);
2855     }
2856   blur_center.x=(double) (image->columns-1)/2.0;
2857   blur_center.y=(double) (image->rows-1)/2.0;
2858   blur_radius=hypot(blur_center.x,blur_center.y);
2859   n=(size_t) fabs(4.0*DegreesToRadians(angle)*sqrt((double) blur_radius)+2UL);
2860   theta=DegreesToRadians(angle)/(double) (n-1);
2861   cos_theta=(double *) AcquireQuantumMemory((size_t) n,
2862     sizeof(*cos_theta));
2863   sin_theta=(double *) AcquireQuantumMemory((size_t) n,
2864     sizeof(*sin_theta));
2865   if ((cos_theta == (double *) NULL) ||
2866       (sin_theta == (double *) NULL))
2867     {
2868       if (cos_theta != (double *) NULL)
2869         cos_theta=(double *) RelinquishMagickMemory(cos_theta);
2870       if (sin_theta != (double *) NULL)
2871         sin_theta=(double *) RelinquishMagickMemory(sin_theta);
2872       blur_image=DestroyImage(blur_image);
2873       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2874     }
2875   offset=theta*(double) (n-1)/2.0;
2876   for (i=0; i < (ssize_t) n; i++)
2877   {
2878     cos_theta[i]=cos((double) (theta*i-offset));
2879     sin_theta[i]=sin((double) (theta*i-offset));
2880   }
2881   /*
2882     Radial blur image.
2883   */
2884   status=MagickTrue;
2885   progress=0;
2886   image_view=AcquireVirtualCacheView(image,exception);
2887   radial_view=AcquireVirtualCacheView(image,exception);
2888   blur_view=AcquireAuthenticCacheView(blur_image,exception);
2889 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2890   #pragma omp parallel for schedule(static) shared(progress,status) \
2891     magick_number_threads(image,blur_image,image->rows,1)
2892 #endif
2893   for (y=0; y < (ssize_t) image->rows; y++)
2894   {
2895     register const Quantum
2896       *magick_restrict p;
2897 
2898     register Quantum
2899       *magick_restrict q;
2900 
2901     register ssize_t
2902       x;
2903 
2904     if (status == MagickFalse)
2905       continue;
2906     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2907     q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
2908       exception);
2909     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2910       {
2911         status=MagickFalse;
2912         continue;
2913       }
2914     for (x=0; x < (ssize_t) image->columns; x++)
2915     {
2916       double
2917         radius;
2918 
2919       PointInfo
2920         center;
2921 
2922       register ssize_t
2923         i;
2924 
2925       size_t
2926         step;
2927 
2928       center.x=(double) x-blur_center.x;
2929       center.y=(double) y-blur_center.y;
2930       radius=hypot((double) center.x,center.y);
2931       if (radius == 0)
2932         step=1;
2933       else
2934         {
2935           step=(size_t) (blur_radius/radius);
2936           if (step == 0)
2937             step=1;
2938           else
2939             if (step >= n)
2940               step=n-1;
2941         }
2942       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2943       {
2944         double
2945           gamma,
2946           pixel;
2947 
2948         PixelChannel
2949           channel;
2950 
2951         PixelTrait
2952           blur_traits,
2953           traits;
2954 
2955         register const Quantum
2956           *magick_restrict r;
2957 
2958         register ssize_t
2959           j;
2960 
2961         channel=GetPixelChannelChannel(image,i);
2962         traits=GetPixelChannelTraits(image,channel);
2963         blur_traits=GetPixelChannelTraits(blur_image,channel);
2964         if ((traits == UndefinedPixelTrait) ||
2965             (blur_traits == UndefinedPixelTrait))
2966           continue;
2967         if ((blur_traits & CopyPixelTrait) != 0)
2968           {
2969             SetPixelChannel(blur_image,channel,p[i],q);
2970             continue;
2971           }
2972         gamma=0.0;
2973         pixel=0.0;
2974         if ((GetPixelChannelTraits(image,AlphaPixelChannel) == UndefinedPixelTrait) ||
2975             (channel == AlphaPixelChannel))
2976           {
2977             for (j=0; j < (ssize_t) n; j+=(ssize_t) step)
2978             {
2979               r=GetCacheViewVirtualPixels(radial_view, (ssize_t) (blur_center.x+
2980                 center.x*cos_theta[j]-center.y*sin_theta[j]+0.5),(ssize_t)
2981                 (blur_center.y+center.x*sin_theta[j]+center.y*cos_theta[j]+0.5),
2982                 1,1,exception);
2983               if (r == (const Quantum *) NULL)
2984                 {
2985                   status=MagickFalse;
2986                   continue;
2987                 }
2988               pixel+=r[i];
2989               gamma++;
2990             }
2991             gamma=PerceptibleReciprocal(gamma);
2992             SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
2993             continue;
2994           }
2995         for (j=0; j < (ssize_t) n; j+=(ssize_t) step)
2996         {
2997           double
2998             alpha;
2999 
3000           r=GetCacheViewVirtualPixels(radial_view, (ssize_t) (blur_center.x+
3001             center.x*cos_theta[j]-center.y*sin_theta[j]+0.5),(ssize_t)
3002             (blur_center.y+center.x*sin_theta[j]+center.y*cos_theta[j]+0.5),
3003             1,1,exception);
3004           if (r == (const Quantum *) NULL)
3005             {
3006               status=MagickFalse;
3007               continue;
3008             }
3009           alpha=(double) QuantumScale*GetPixelAlpha(image,r);
3010           pixel+=alpha*r[i];
3011           gamma+=alpha;
3012         }
3013         gamma=PerceptibleReciprocal(gamma);
3014         SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
3015       }
3016       p+=GetPixelChannels(image);
3017       q+=GetPixelChannels(blur_image);
3018     }
3019     if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
3020       status=MagickFalse;
3021     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3022       {
3023         MagickBooleanType
3024           proceed;
3025 
3026 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3027         #pragma omp atomic
3028 #endif
3029         progress++;
3030         proceed=SetImageProgress(image,BlurImageTag,progress,image->rows);
3031         if (proceed == MagickFalse)
3032           status=MagickFalse;
3033       }
3034   }
3035   blur_view=DestroyCacheView(blur_view);
3036   radial_view=DestroyCacheView(radial_view);
3037   image_view=DestroyCacheView(image_view);
3038   cos_theta=(double *) RelinquishMagickMemory(cos_theta);
3039   sin_theta=(double *) RelinquishMagickMemory(sin_theta);
3040   if (status == MagickFalse)
3041     blur_image=DestroyImage(blur_image);
3042   return(blur_image);
3043 }
3044 
3045 /*
3046 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3047 %                                                                             %
3048 %                                                                             %
3049 %                                                                             %
3050 %     S e l e c t i v e B l u r I m a g e                                     %
3051 %                                                                             %
3052 %                                                                             %
3053 %                                                                             %
3054 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3055 %
3056 %  SelectiveBlurImage() selectively blur pixels within a contrast threshold.
3057 %  It is similar to the unsharpen mask that sharpens everything with contrast
3058 %  above a certain threshold.
3059 %
3060 %  The format of the SelectiveBlurImage method is:
3061 %
3062 %      Image *SelectiveBlurImage(const Image *image,const double radius,
3063 %        const double sigma,const double threshold,ExceptionInfo *exception)
3064 %
3065 %  A description of each parameter follows:
3066 %
3067 %    o image: the image.
3068 %
3069 %    o radius: the radius of the Gaussian, in pixels, not counting the center
3070 %      pixel.
3071 %
3072 %    o sigma: the standard deviation of the Gaussian, in pixels.
3073 %
3074 %    o threshold: only pixels within this contrast threshold are included
3075 %      in the blur operation.
3076 %
3077 %    o exception: return any errors or warnings in this structure.
3078 %
3079 */
SelectiveBlurImage(const Image * image,const double radius,const double sigma,const double threshold,ExceptionInfo * exception)3080 MagickExport Image *SelectiveBlurImage(const Image *image,const double radius,
3081   const double sigma,const double threshold,ExceptionInfo *exception)
3082 {
3083 #define SelectiveBlurImageTag  "SelectiveBlur/Image"
3084 
3085   CacheView
3086     *blur_view,
3087     *image_view,
3088     *luminance_view;
3089 
3090   Image
3091     *blur_image,
3092     *luminance_image;
3093 
3094   MagickBooleanType
3095     status;
3096 
3097   MagickOffsetType
3098     progress;
3099 
3100   MagickRealType
3101     *kernel;
3102 
3103   register ssize_t
3104     i;
3105 
3106   size_t
3107     width;
3108 
3109   ssize_t
3110     center,
3111     j,
3112     u,
3113     v,
3114     y;
3115 
3116   /*
3117     Initialize blur image attributes.
3118   */
3119   assert(image != (Image *) NULL);
3120   assert(image->signature == MagickCoreSignature);
3121   if (image->debug != MagickFalse)
3122     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3123   assert(exception != (ExceptionInfo *) NULL);
3124   assert(exception->signature == MagickCoreSignature);
3125   width=GetOptimalKernelWidth1D(radius,sigma);
3126   kernel=(MagickRealType *) MagickAssumeAligned(AcquireAlignedMemory((size_t)
3127     width,width*sizeof(*kernel)));
3128   if (kernel == (MagickRealType *) NULL)
3129     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3130   j=(ssize_t) (width-1)/2;
3131   i=0;
3132   for (v=(-j); v <= j; v++)
3133   {
3134     for (u=(-j); u <= j; u++)
3135       kernel[i++]=(MagickRealType) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
3136         MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
3137   }
3138   if (image->debug != MagickFalse)
3139     {
3140       char
3141         format[MagickPathExtent],
3142         *message;
3143 
3144       register const MagickRealType
3145         *k;
3146 
3147       ssize_t
3148         u,
3149         v;
3150 
3151       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
3152         "  SelectiveBlurImage with %.20gx%.20g kernel:",(double) width,(double)
3153         width);
3154       message=AcquireString("");
3155       k=kernel;
3156       for (v=0; v < (ssize_t) width; v++)
3157       {
3158         *message='\0';
3159         (void) FormatLocaleString(format,MagickPathExtent,"%.20g: ",(double) v);
3160         (void) ConcatenateString(&message,format);
3161         for (u=0; u < (ssize_t) width; u++)
3162         {
3163           (void) FormatLocaleString(format,MagickPathExtent,"%+f ",(double)
3164             *k++);
3165           (void) ConcatenateString(&message,format);
3166         }
3167         (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
3168       }
3169       message=DestroyString(message);
3170     }
3171   blur_image=CloneImage(image,0,0,MagickTrue,exception);
3172   if (blur_image == (Image *) NULL)
3173     return((Image *) NULL);
3174   if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
3175     {
3176       blur_image=DestroyImage(blur_image);
3177       kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
3178       return((Image *) NULL);
3179     }
3180   luminance_image=CloneImage(image,0,0,MagickTrue,exception);
3181   if (luminance_image == (Image *) NULL)
3182     {
3183       blur_image=DestroyImage(blur_image);
3184       kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
3185       return((Image *) NULL);
3186     }
3187   status=TransformImageColorspace(luminance_image,GRAYColorspace,exception);
3188   if (status == MagickFalse)
3189     {
3190       luminance_image=DestroyImage(luminance_image);
3191       blur_image=DestroyImage(blur_image);
3192       kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
3193       return((Image *) NULL);
3194     }
3195   /*
3196     Threshold blur image.
3197   */
3198   status=MagickTrue;
3199   progress=0;
3200   center=(ssize_t) (GetPixelChannels(image)*(image->columns+width)*
3201     ((width-1)/2L)+GetPixelChannels(image)*((width-1)/2L));
3202   image_view=AcquireVirtualCacheView(image,exception);
3203   luminance_view=AcquireVirtualCacheView(luminance_image,exception);
3204   blur_view=AcquireAuthenticCacheView(blur_image,exception);
3205 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3206   #pragma omp parallel for schedule(static) shared(progress,status) \
3207     magick_number_threads(image,blur_image,image->rows,1)
3208 #endif
3209   for (y=0; y < (ssize_t) image->rows; y++)
3210   {
3211     double
3212       contrast;
3213 
3214     MagickBooleanType
3215       sync;
3216 
3217     register const Quantum
3218       *magick_restrict l,
3219       *magick_restrict p;
3220 
3221     register Quantum
3222       *magick_restrict q;
3223 
3224     register ssize_t
3225       x;
3226 
3227     if (status == MagickFalse)
3228       continue;
3229     p=GetCacheViewVirtualPixels(image_view,-((ssize_t) (width-1)/2L),y-(ssize_t)
3230       ((width-1)/2L),image->columns+width,width,exception);
3231     l=GetCacheViewVirtualPixels(luminance_view,-((ssize_t) (width-1)/2L),y-
3232       (ssize_t) ((width-1)/2L),luminance_image->columns+width,width,exception);
3233     q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
3234       exception);
3235     if ((p == (const Quantum *) NULL) || (l == (const Quantum *) NULL) ||
3236         (q == (Quantum *) NULL))
3237       {
3238         status=MagickFalse;
3239         continue;
3240       }
3241     for (x=0; x < (ssize_t) image->columns; x++)
3242     {
3243       double
3244         intensity;
3245 
3246       register ssize_t
3247         i;
3248 
3249       intensity=GetPixelIntensity(image,p+center);
3250       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3251       {
3252         double
3253           alpha,
3254           gamma,
3255           pixel;
3256 
3257         PixelChannel
3258           channel;
3259 
3260         PixelTrait
3261           blur_traits,
3262           traits;
3263 
3264         register const MagickRealType
3265           *magick_restrict k;
3266 
3267         register const Quantum
3268           *magick_restrict luminance_pixels,
3269           *magick_restrict pixels;
3270 
3271         register ssize_t
3272           u;
3273 
3274         ssize_t
3275           v;
3276 
3277         channel=GetPixelChannelChannel(image,i);
3278         traits=GetPixelChannelTraits(image,channel);
3279         blur_traits=GetPixelChannelTraits(blur_image,channel);
3280         if ((traits == UndefinedPixelTrait) ||
3281             (blur_traits == UndefinedPixelTrait))
3282           continue;
3283         if ((blur_traits & CopyPixelTrait) != 0)
3284           {
3285             SetPixelChannel(blur_image,channel,p[center+i],q);
3286             continue;
3287           }
3288         k=kernel;
3289         pixel=0.0;
3290         pixels=p;
3291         luminance_pixels=l;
3292         gamma=0.0;
3293         if ((blur_traits & BlendPixelTrait) == 0)
3294           {
3295             for (v=0; v < (ssize_t) width; v++)
3296             {
3297               for (u=0; u < (ssize_t) width; u++)
3298               {
3299                 contrast=GetPixelIntensity(luminance_image,luminance_pixels)-
3300                   intensity;
3301                 if (fabs(contrast) < threshold)
3302                   {
3303                     pixel+=(*k)*pixels[i];
3304                     gamma+=(*k);
3305                   }
3306                 k++;
3307                 pixels+=GetPixelChannels(image);
3308                 luminance_pixels+=GetPixelChannels(luminance_image);
3309               }
3310               pixels+=GetPixelChannels(image)*image->columns;
3311               luminance_pixels+=GetPixelChannels(luminance_image)*
3312                 luminance_image->columns;
3313             }
3314             if (fabs((double) gamma) < MagickEpsilon)
3315               {
3316                 SetPixelChannel(blur_image,channel,p[center+i],q);
3317                 continue;
3318               }
3319             gamma=PerceptibleReciprocal(gamma);
3320             SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
3321             continue;
3322           }
3323         for (v=0; v < (ssize_t) width; v++)
3324         {
3325           for (u=0; u < (ssize_t) width; u++)
3326           {
3327             contrast=GetPixelIntensity(image,pixels)-intensity;
3328             if (fabs(contrast) < threshold)
3329               {
3330                 alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
3331                 pixel+=(*k)*alpha*pixels[i];
3332                 gamma+=(*k)*alpha;
3333               }
3334             k++;
3335             pixels+=GetPixelChannels(image);
3336             luminance_pixels+=GetPixelChannels(luminance_image);
3337           }
3338           pixels+=GetPixelChannels(image)*image->columns;
3339           luminance_pixels+=GetPixelChannels(luminance_image)*
3340             luminance_image->columns;
3341         }
3342         if (fabs((double) gamma) < MagickEpsilon)
3343           {
3344             SetPixelChannel(blur_image,channel,p[center+i],q);
3345             continue;
3346           }
3347         gamma=PerceptibleReciprocal(gamma);
3348         SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
3349       }
3350       p+=GetPixelChannels(image);
3351       l+=GetPixelChannels(luminance_image);
3352       q+=GetPixelChannels(blur_image);
3353     }
3354     sync=SyncCacheViewAuthenticPixels(blur_view,exception);
3355     if (sync == MagickFalse)
3356       status=MagickFalse;
3357     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3358       {
3359         MagickBooleanType
3360           proceed;
3361 
3362 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3363         #pragma omp atomic
3364 #endif
3365         progress++;
3366         proceed=SetImageProgress(image,SelectiveBlurImageTag,progress,
3367           image->rows);
3368         if (proceed == MagickFalse)
3369           status=MagickFalse;
3370       }
3371   }
3372   blur_image->type=image->type;
3373   blur_view=DestroyCacheView(blur_view);
3374   luminance_view=DestroyCacheView(luminance_view);
3375   image_view=DestroyCacheView(image_view);
3376   luminance_image=DestroyImage(luminance_image);
3377   kernel=(MagickRealType *) RelinquishAlignedMemory(kernel);
3378   if (status == MagickFalse)
3379     blur_image=DestroyImage(blur_image);
3380   return(blur_image);
3381 }
3382 
3383 /*
3384 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3385 %                                                                             %
3386 %                                                                             %
3387 %                                                                             %
3388 %     S h a d e I m a g e                                                     %
3389 %                                                                             %
3390 %                                                                             %
3391 %                                                                             %
3392 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3393 %
3394 %  ShadeImage() shines a distant light on an image to create a
3395 %  three-dimensional effect. You control the positioning of the light with
3396 %  azimuth and elevation; azimuth is measured in degrees off the x axis
3397 %  and elevation is measured in pixels above the Z axis.
3398 %
3399 %  The format of the ShadeImage method is:
3400 %
3401 %      Image *ShadeImage(const Image *image,const MagickBooleanType gray,
3402 %        const double azimuth,const double elevation,ExceptionInfo *exception)
3403 %
3404 %  A description of each parameter follows:
3405 %
3406 %    o image: the image.
3407 %
3408 %    o gray: A value other than zero shades the intensity of each pixel.
3409 %
3410 %    o azimuth, elevation:  Define the light source direction.
3411 %
3412 %    o exception: return any errors or warnings in this structure.
3413 %
3414 */
ShadeImage(const Image * image,const MagickBooleanType gray,const double azimuth,const double elevation,ExceptionInfo * exception)3415 MagickExport Image *ShadeImage(const Image *image,const MagickBooleanType gray,
3416   const double azimuth,const double elevation,ExceptionInfo *exception)
3417 {
3418 #define GetShadeIntensity(image,pixel) \
3419   ClampPixel(GetPixelIntensity((image),(pixel)))
3420 #define ShadeImageTag  "Shade/Image"
3421 
3422   CacheView
3423     *image_view,
3424     *shade_view;
3425 
3426   Image
3427     *linear_image,
3428     *shade_image;
3429 
3430   MagickBooleanType
3431     status;
3432 
3433   MagickOffsetType
3434     progress;
3435 
3436   PrimaryInfo
3437     light;
3438 
3439   ssize_t
3440     y;
3441 
3442   /*
3443     Initialize shaded image attributes.
3444   */
3445   assert(image != (const Image *) NULL);
3446   assert(image->signature == MagickCoreSignature);
3447   if (image->debug != MagickFalse)
3448     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3449   assert(exception != (ExceptionInfo *) NULL);
3450   assert(exception->signature == MagickCoreSignature);
3451   linear_image=CloneImage(image,0,0,MagickTrue,exception);
3452   shade_image=CloneImage(image,0,0,MagickTrue,exception);
3453   if ((linear_image == (Image *) NULL) || (shade_image == (Image *) NULL))
3454     {
3455       if (linear_image != (Image *) NULL)
3456         linear_image=DestroyImage(linear_image);
3457       if (shade_image != (Image *) NULL)
3458         shade_image=DestroyImage(shade_image);
3459       return((Image *) NULL);
3460     }
3461   if (SetImageStorageClass(shade_image,DirectClass,exception) == MagickFalse)
3462     {
3463       linear_image=DestroyImage(linear_image);
3464       shade_image=DestroyImage(shade_image);
3465       return((Image *) NULL);
3466     }
3467   /*
3468     Compute the light vector.
3469   */
3470   light.x=(double) QuantumRange*cos(DegreesToRadians(azimuth))*
3471     cos(DegreesToRadians(elevation));
3472   light.y=(double) QuantumRange*sin(DegreesToRadians(azimuth))*
3473     cos(DegreesToRadians(elevation));
3474   light.z=(double) QuantumRange*sin(DegreesToRadians(elevation));
3475   /*
3476     Shade image.
3477   */
3478   status=MagickTrue;
3479   progress=0;
3480   image_view=AcquireVirtualCacheView(linear_image,exception);
3481   shade_view=AcquireAuthenticCacheView(shade_image,exception);
3482 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3483   #pragma omp parallel for schedule(static) shared(progress,status) \
3484     magick_number_threads(linear_image,shade_image,linear_image->rows,1)
3485 #endif
3486   for (y=0; y < (ssize_t) linear_image->rows; y++)
3487   {
3488     double
3489       distance,
3490       normal_distance,
3491       shade;
3492 
3493     PrimaryInfo
3494       normal;
3495 
3496     register const Quantum
3497       *magick_restrict center,
3498       *magick_restrict p,
3499       *magick_restrict post,
3500       *magick_restrict pre;
3501 
3502     register Quantum
3503       *magick_restrict q;
3504 
3505     register ssize_t
3506       x;
3507 
3508     if (status == MagickFalse)
3509       continue;
3510     p=GetCacheViewVirtualPixels(image_view,-1,y-1,linear_image->columns+2,3,
3511       exception);
3512     q=QueueCacheViewAuthenticPixels(shade_view,0,y,shade_image->columns,1,
3513       exception);
3514     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3515       {
3516         status=MagickFalse;
3517         continue;
3518       }
3519     /*
3520       Shade this row of pixels.
3521     */
3522     normal.z=2.0*(double) QuantumRange;  /* constant Z of surface normal */
3523     for (x=0; x < (ssize_t) linear_image->columns; x++)
3524     {
3525       register ssize_t
3526         i;
3527 
3528       /*
3529         Determine the surface normal and compute shading.
3530       */
3531       pre=p+GetPixelChannels(linear_image);
3532       center=pre+(linear_image->columns+2)*GetPixelChannels(linear_image);
3533       post=center+(linear_image->columns+2)*GetPixelChannels(linear_image);
3534       normal.x=(double) (
3535         GetShadeIntensity(linear_image,pre-GetPixelChannels(linear_image))+
3536         GetShadeIntensity(linear_image,center-GetPixelChannels(linear_image))+
3537         GetShadeIntensity(linear_image,post-GetPixelChannels(linear_image))-
3538         GetShadeIntensity(linear_image,pre+GetPixelChannels(linear_image))-
3539         GetShadeIntensity(linear_image,center+GetPixelChannels(linear_image))-
3540         GetShadeIntensity(linear_image,post+GetPixelChannels(linear_image)));
3541       normal.y=(double) (
3542         GetShadeIntensity(linear_image,post-GetPixelChannels(linear_image))+
3543         GetShadeIntensity(linear_image,post)+
3544         GetShadeIntensity(linear_image,post+GetPixelChannels(linear_image))-
3545         GetShadeIntensity(linear_image,pre-GetPixelChannels(linear_image))-
3546         GetShadeIntensity(linear_image,pre)-
3547         GetShadeIntensity(linear_image,pre+GetPixelChannels(linear_image)));
3548       if ((fabs(normal.x) <= MagickEpsilon) &&
3549           (fabs(normal.y) <= MagickEpsilon))
3550         shade=light.z;
3551       else
3552         {
3553           shade=0.0;
3554           distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
3555           if (distance > MagickEpsilon)
3556             {
3557               normal_distance=normal.x*normal.x+normal.y*normal.y+
3558                 normal.z*normal.z;
3559               if (normal_distance > (MagickEpsilon*MagickEpsilon))
3560                 shade=distance/sqrt((double) normal_distance);
3561             }
3562         }
3563       for (i=0; i < (ssize_t) GetPixelChannels(linear_image); i++)
3564       {
3565         PixelChannel
3566           channel;
3567 
3568         PixelTrait
3569           shade_traits,
3570           traits;
3571 
3572         channel=GetPixelChannelChannel(linear_image,i);
3573         traits=GetPixelChannelTraits(linear_image,channel);
3574         shade_traits=GetPixelChannelTraits(shade_image,channel);
3575         if ((traits == UndefinedPixelTrait) ||
3576             (shade_traits == UndefinedPixelTrait))
3577           continue;
3578         if ((shade_traits & CopyPixelTrait) != 0)
3579           {
3580             SetPixelChannel(shade_image,channel,center[i],q);
3581             continue;
3582           }
3583         if ((traits & UpdatePixelTrait) == 0)
3584           {
3585             SetPixelChannel(shade_image,channel,center[i],q);
3586             continue;
3587           }
3588         if (gray != MagickFalse)
3589           {
3590             SetPixelChannel(shade_image,channel,ClampToQuantum(shade),q);
3591             continue;
3592           }
3593         SetPixelChannel(shade_image,channel,ClampToQuantum(QuantumScale*shade*
3594           center[i]),q);
3595       }
3596       p+=GetPixelChannels(linear_image);
3597       q+=GetPixelChannels(shade_image);
3598     }
3599     if (SyncCacheViewAuthenticPixels(shade_view,exception) == MagickFalse)
3600       status=MagickFalse;
3601     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3602       {
3603         MagickBooleanType
3604           proceed;
3605 
3606 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3607         #pragma omp atomic
3608 #endif
3609         progress++;
3610         proceed=SetImageProgress(image,ShadeImageTag,progress,image->rows);
3611         if (proceed == MagickFalse)
3612           status=MagickFalse;
3613       }
3614   }
3615   shade_view=DestroyCacheView(shade_view);
3616   image_view=DestroyCacheView(image_view);
3617   linear_image=DestroyImage(linear_image);
3618   if (status == MagickFalse)
3619     shade_image=DestroyImage(shade_image);
3620   return(shade_image);
3621 }
3622 
3623 /*
3624 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3625 %                                                                             %
3626 %                                                                             %
3627 %                                                                             %
3628 %     S h a r p e n I m a g e                                                 %
3629 %                                                                             %
3630 %                                                                             %
3631 %                                                                             %
3632 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3633 %
3634 %  SharpenImage() sharpens the image.  We convolve the image with a Gaussian
3635 %  operator of the given radius and standard deviation (sigma).  For
3636 %  reasonable results, radius should be larger than sigma.  Use a radius of 0
3637 %  and SharpenImage() selects a suitable radius for you.
3638 %
3639 %  Using a separable kernel would be faster, but the negative weights cancel
3640 %  out on the corners of the kernel producing often undesirable ringing in the
3641 %  filtered result; this can be avoided by using a 2D gaussian shaped image
3642 %  sharpening kernel instead.
3643 %
3644 %  The format of the SharpenImage method is:
3645 %
3646 %    Image *SharpenImage(const Image *image,const double radius,
3647 %      const double sigma,ExceptionInfo *exception)
3648 %
3649 %  A description of each parameter follows:
3650 %
3651 %    o image: the image.
3652 %
3653 %    o radius: the radius of the Gaussian, in pixels, not counting the center
3654 %      pixel.
3655 %
3656 %    o sigma: the standard deviation of the Laplacian, in pixels.
3657 %
3658 %    o exception: return any errors or warnings in this structure.
3659 %
3660 */
SharpenImage(const Image * image,const double radius,const double sigma,ExceptionInfo * exception)3661 MagickExport Image *SharpenImage(const Image *image,const double radius,
3662   const double sigma,ExceptionInfo *exception)
3663 {
3664   double
3665     gamma,
3666     normalize;
3667 
3668   Image
3669     *sharp_image;
3670 
3671   KernelInfo
3672     *kernel_info;
3673 
3674   register ssize_t
3675     i;
3676 
3677   size_t
3678     width;
3679 
3680   ssize_t
3681     j,
3682     u,
3683     v;
3684 
3685   assert(image != (const Image *) NULL);
3686   assert(image->signature == MagickCoreSignature);
3687   if (image->debug != MagickFalse)
3688     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3689   assert(exception != (ExceptionInfo *) NULL);
3690   assert(exception->signature == MagickCoreSignature);
3691   width=GetOptimalKernelWidth2D(radius,sigma);
3692   kernel_info=AcquireKernelInfo((const char *) NULL,exception);
3693   if (kernel_info == (KernelInfo *) NULL)
3694     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3695   (void) memset(kernel_info,0,sizeof(*kernel_info));
3696   kernel_info->width=width;
3697   kernel_info->height=width;
3698   kernel_info->x=(ssize_t) (width-1)/2;
3699   kernel_info->y=(ssize_t) (width-1)/2;
3700   kernel_info->signature=MagickCoreSignature;
3701   kernel_info->values=(MagickRealType *) MagickAssumeAligned(
3702     AcquireAlignedMemory(kernel_info->width,kernel_info->height*
3703     sizeof(*kernel_info->values)));
3704   if (kernel_info->values == (MagickRealType *) NULL)
3705     {
3706       kernel_info=DestroyKernelInfo(kernel_info);
3707       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3708     }
3709   normalize=0.0;
3710   j=(ssize_t) (kernel_info->width-1)/2;
3711   i=0;
3712   for (v=(-j); v <= j; v++)
3713   {
3714     for (u=(-j); u <= j; u++)
3715     {
3716       kernel_info->values[i]=(MagickRealType) (-exp(-((double) u*u+v*v)/(2.0*
3717         MagickSigma*MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
3718       normalize+=kernel_info->values[i];
3719       i++;
3720     }
3721   }
3722   kernel_info->values[i/2]=(double) ((-2.0)*normalize);
3723   normalize=0.0;
3724   for (i=0; i < (ssize_t) (kernel_info->width*kernel_info->height); i++)
3725     normalize+=kernel_info->values[i];
3726   gamma=PerceptibleReciprocal(normalize);
3727   for (i=0; i < (ssize_t) (kernel_info->width*kernel_info->height); i++)
3728     kernel_info->values[i]*=gamma;
3729   sharp_image=ConvolveImage(image,kernel_info,exception);
3730   kernel_info=DestroyKernelInfo(kernel_info);
3731   return(sharp_image);
3732 }
3733 
3734 /*
3735 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3736 %                                                                             %
3737 %                                                                             %
3738 %                                                                             %
3739 %     S p r e a d I m a g e                                                   %
3740 %                                                                             %
3741 %                                                                             %
3742 %                                                                             %
3743 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3744 %
3745 %  SpreadImage() is a special effects method that randomly displaces each
3746 %  pixel in a square area defined by the radius parameter.
3747 %
3748 %  The format of the SpreadImage method is:
3749 %
3750 %      Image *SpreadImage(const Image *image,
3751 %        const PixelInterpolateMethod method,const double radius,
3752 %        ExceptionInfo *exception)
3753 %
3754 %  A description of each parameter follows:
3755 %
3756 %    o image: the image.
3757 %
3758 %    o method:  intepolation method.
3759 %
3760 %    o radius:  choose a random pixel in a neighborhood of this extent.
3761 %
3762 %    o exception: return any errors or warnings in this structure.
3763 %
3764 */
SpreadImage(const Image * image,const PixelInterpolateMethod method,const double radius,ExceptionInfo * exception)3765 MagickExport Image *SpreadImage(const Image *image,
3766   const PixelInterpolateMethod method,const double radius,
3767   ExceptionInfo *exception)
3768 {
3769 #define SpreadImageTag  "Spread/Image"
3770 
3771   CacheView
3772     *image_view,
3773     *spread_view;
3774 
3775   Image
3776     *spread_image;
3777 
3778   MagickBooleanType
3779     status;
3780 
3781   MagickOffsetType
3782     progress;
3783 
3784   RandomInfo
3785     **magick_restrict random_info;
3786 
3787   size_t
3788     width;
3789 
3790   ssize_t
3791     y;
3792 
3793 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3794   unsigned long
3795     key;
3796 #endif
3797 
3798   /*
3799     Initialize spread image attributes.
3800   */
3801   assert(image != (Image *) NULL);
3802   assert(image->signature == MagickCoreSignature);
3803   if (image->debug != MagickFalse)
3804     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3805   assert(exception != (ExceptionInfo *) NULL);
3806   assert(exception->signature == MagickCoreSignature);
3807   spread_image=CloneImage(image,0,0,MagickTrue,exception);
3808   if (spread_image == (Image *) NULL)
3809     return((Image *) NULL);
3810   if (SetImageStorageClass(spread_image,DirectClass,exception) == MagickFalse)
3811     {
3812       spread_image=DestroyImage(spread_image);
3813       return((Image *) NULL);
3814     }
3815   /*
3816     Spread image.
3817   */
3818   status=MagickTrue;
3819   progress=0;
3820   width=GetOptimalKernelWidth1D(radius,0.5);
3821   random_info=AcquireRandomInfoThreadSet();
3822   image_view=AcquireVirtualCacheView(image,exception);
3823   spread_view=AcquireAuthenticCacheView(spread_image,exception);
3824 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3825   key=GetRandomSecretKey(random_info[0]);
3826   #pragma omp parallel for schedule(static) shared(progress,status) \
3827     magick_number_threads(image,spread_image,image->rows,key == ~0UL)
3828 #endif
3829   for (y=0; y < (ssize_t) image->rows; y++)
3830   {
3831     const int
3832       id = GetOpenMPThreadId();
3833 
3834     register Quantum
3835       *magick_restrict q;
3836 
3837     register ssize_t
3838       x;
3839 
3840     if (status == MagickFalse)
3841       continue;
3842     q=QueueCacheViewAuthenticPixels(spread_view,0,y,spread_image->columns,1,
3843       exception);
3844     if (q == (Quantum *) NULL)
3845       {
3846         status=MagickFalse;
3847         continue;
3848       }
3849     for (x=0; x < (ssize_t) image->columns; x++)
3850     {
3851       PointInfo
3852         point;
3853 
3854       point.x=GetPseudoRandomValue(random_info[id]);
3855       point.y=GetPseudoRandomValue(random_info[id]);
3856       status=InterpolatePixelChannels(image,image_view,spread_image,method,
3857         (double) x+width*(point.x-0.5),(double) y+width*(point.y-0.5),q,
3858         exception);
3859       if (status == MagickFalse)
3860         break;
3861       q+=GetPixelChannels(spread_image);
3862     }
3863     if (SyncCacheViewAuthenticPixels(spread_view,exception) == MagickFalse)
3864       status=MagickFalse;
3865     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3866       {
3867         MagickBooleanType
3868           proceed;
3869 
3870 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3871         #pragma omp atomic
3872 #endif
3873         progress++;
3874         proceed=SetImageProgress(image,SpreadImageTag,progress,image->rows);
3875         if (proceed == MagickFalse)
3876           status=MagickFalse;
3877       }
3878   }
3879   spread_view=DestroyCacheView(spread_view);
3880   image_view=DestroyCacheView(image_view);
3881   random_info=DestroyRandomInfoThreadSet(random_info);
3882   if (status == MagickFalse)
3883     spread_image=DestroyImage(spread_image);
3884   return(spread_image);
3885 }
3886 
3887 /*
3888 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3889 %                                                                             %
3890 %                                                                             %
3891 %                                                                             %
3892 %     U n s h a r p M a s k I m a g e                                         %
3893 %                                                                             %
3894 %                                                                             %
3895 %                                                                             %
3896 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3897 %
3898 %  UnsharpMaskImage() sharpens one or more image channels.  We convolve the
3899 %  image with a Gaussian operator of the given radius and standard deviation
3900 %  (sigma).  For reasonable results, radius should be larger than sigma.  Use a
3901 %  radius of 0 and UnsharpMaskImage() selects a suitable radius for you.
3902 %
3903 %  The format of the UnsharpMaskImage method is:
3904 %
3905 %    Image *UnsharpMaskImage(const Image *image,const double radius,
3906 %      const double sigma,const double amount,const double threshold,
3907 %      ExceptionInfo *exception)
3908 %
3909 %  A description of each parameter follows:
3910 %
3911 %    o image: the image.
3912 %
3913 %    o radius: the radius of the Gaussian, in pixels, not counting the center
3914 %      pixel.
3915 %
3916 %    o sigma: the standard deviation of the Gaussian, in pixels.
3917 %
3918 %    o gain: the percentage of the difference between the original and the
3919 %      blur image that is added back into the original.
3920 %
3921 %    o threshold: the threshold in pixels needed to apply the diffence gain.
3922 %
3923 %    o exception: return any errors or warnings in this structure.
3924 %
3925 */
UnsharpMaskImage(const Image * image,const double radius,const double sigma,const double gain,const double threshold,ExceptionInfo * exception)3926 MagickExport Image *UnsharpMaskImage(const Image *image,const double radius,
3927   const double sigma,const double gain,const double threshold,
3928   ExceptionInfo *exception)
3929 {
3930 #define SharpenImageTag  "Sharpen/Image"
3931 
3932   CacheView
3933     *image_view,
3934     *unsharp_view;
3935 
3936   Image
3937     *unsharp_image;
3938 
3939   MagickBooleanType
3940     status;
3941 
3942   MagickOffsetType
3943     progress;
3944 
3945   double
3946     quantum_threshold;
3947 
3948   ssize_t
3949     y;
3950 
3951   assert(image != (const Image *) NULL);
3952   assert(image->signature == MagickCoreSignature);
3953   if (image->debug != MagickFalse)
3954     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3955   assert(exception != (ExceptionInfo *) NULL);
3956 /* This kernel appears to be broken.
3957 #if defined(MAGICKCORE_OPENCL_SUPPORT)
3958   unsharp_image=AccelerateUnsharpMaskImage(image,radius,sigma,gain,threshold,
3959     exception);
3960   if (unsharp_image != (Image *) NULL)
3961     return(unsharp_image);
3962 #endif
3963 */
3964   unsharp_image=BlurImage(image,radius,sigma,exception);
3965   if (unsharp_image == (Image *) NULL)
3966     return((Image *) NULL);
3967   quantum_threshold=(double) QuantumRange*threshold;
3968   /*
3969     Unsharp-mask image.
3970   */
3971   status=MagickTrue;
3972   progress=0;
3973   image_view=AcquireVirtualCacheView(image,exception);
3974   unsharp_view=AcquireAuthenticCacheView(unsharp_image,exception);
3975 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3976   #pragma omp parallel for schedule(static) shared(progress,status) \
3977     magick_number_threads(image,unsharp_image,image->rows,1)
3978 #endif
3979   for (y=0; y < (ssize_t) image->rows; y++)
3980   {
3981     register const Quantum
3982       *magick_restrict p;
3983 
3984     register Quantum
3985       *magick_restrict q;
3986 
3987     register ssize_t
3988       x;
3989 
3990     if (status == MagickFalse)
3991       continue;
3992     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3993     q=QueueCacheViewAuthenticPixels(unsharp_view,0,y,unsharp_image->columns,1,
3994       exception);
3995     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3996       {
3997         status=MagickFalse;
3998         continue;
3999       }
4000     for (x=0; x < (ssize_t) image->columns; x++)
4001     {
4002       register ssize_t
4003         i;
4004 
4005       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4006       {
4007         double
4008           pixel;
4009 
4010         PixelChannel
4011           channel;
4012 
4013         PixelTrait
4014           traits,
4015           unsharp_traits;
4016 
4017         channel=GetPixelChannelChannel(image,i);
4018         traits=GetPixelChannelTraits(image,channel);
4019         unsharp_traits=GetPixelChannelTraits(unsharp_image,channel);
4020         if ((traits == UndefinedPixelTrait) ||
4021             (unsharp_traits == UndefinedPixelTrait))
4022           continue;
4023         if ((unsharp_traits & CopyPixelTrait) != 0)
4024           {
4025             SetPixelChannel(unsharp_image,channel,p[i],q);
4026             continue;
4027           }
4028         pixel=p[i]-(double) GetPixelChannel(unsharp_image,channel,q);
4029         if (fabs(2.0*pixel) < quantum_threshold)
4030           pixel=(double) p[i];
4031         else
4032           pixel=(double) p[i]+gain*pixel;
4033         SetPixelChannel(unsharp_image,channel,ClampToQuantum(pixel),q);
4034       }
4035       p+=GetPixelChannels(image);
4036       q+=GetPixelChannels(unsharp_image);
4037     }
4038     if (SyncCacheViewAuthenticPixels(unsharp_view,exception) == MagickFalse)
4039       status=MagickFalse;
4040     if (image->progress_monitor != (MagickProgressMonitor) NULL)
4041       {
4042         MagickBooleanType
4043           proceed;
4044 
4045 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4046         #pragma omp atomic
4047 #endif
4048         progress++;
4049         proceed=SetImageProgress(image,SharpenImageTag,progress,image->rows);
4050         if (proceed == MagickFalse)
4051           status=MagickFalse;
4052       }
4053   }
4054   unsharp_image->type=image->type;
4055   unsharp_view=DestroyCacheView(unsharp_view);
4056   image_view=DestroyCacheView(image_view);
4057   if (status == MagickFalse)
4058     unsharp_image=DestroyImage(unsharp_image);
4059   return(unsharp_image);
4060 }
4061