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