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