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