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