]> 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-2011 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/draw.h"
53 #include "MagickCore/enhance.h"
54 #include "MagickCore/exception.h"
55 #include "MagickCore/exception-private.h"
56 #include "MagickCore/effect.h"
57 #include "MagickCore/fx.h"
58 #include "MagickCore/gem.h"
59 #include "MagickCore/gem-private.h"
60 #include "MagickCore/geometry.h"
61 #include "MagickCore/image-private.h"
62 #include "MagickCore/list.h"
63 #include "MagickCore/log.h"
64 #include "MagickCore/memory_.h"
65 #include "MagickCore/monitor.h"
66 #include "MagickCore/monitor-private.h"
67 #include "MagickCore/montage.h"
68 #include "MagickCore/morphology.h"
69 #include "MagickCore/paint.h"
70 #include "MagickCore/pixel-accessor.h"
71 #include "MagickCore/property.h"
72 #include "MagickCore/quantize.h"
73 #include "MagickCore/quantum.h"
74 #include "MagickCore/quantum-private.h"
75 #include "MagickCore/random_.h"
76 #include "MagickCore/random-private.h"
77 #include "MagickCore/resample.h"
78 #include "MagickCore/resample-private.h"
79 #include "MagickCore/resize.h"
80 #include "MagickCore/resource_.h"
81 #include "MagickCore/segment.h"
82 #include "MagickCore/shear.h"
83 #include "MagickCore/signature-private.h"
84 #include "MagickCore/string_.h"
85 #include "MagickCore/thread-private.h"
86 #include "MagickCore/transform.h"
87 #include "MagickCore/threshold.h"
88 \f
89 /*
90 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
91 %                                                                             %
92 %                                                                             %
93 %                                                                             %
94 %     A d a p t i v e B l u r I m a g e                                       %
95 %                                                                             %
96 %                                                                             %
97 %                                                                             %
98 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
99 %
100 %  AdaptiveBlurImage() adaptively blurs the image by blurring less
101 %  intensely near image edges and more intensely far from edges.  We blur the
102 %  image with a Gaussian operator of the given radius and standard deviation
103 %  (sigma).  For reasonable results, radius should be larger than sigma.  Use a
104 %  radius of 0 and AdaptiveBlurImage() selects a suitable radius for you.
105 %
106 %  The format of the AdaptiveBlurImage method is:
107 %
108 %      Image *AdaptiveBlurImage(const Image *image,const double radius,
109 %        const double sigma,const double bias,ExceptionInfo *exception)
110 %
111 %  A description of each parameter follows:
112 %
113 %    o image: the image.
114 %
115 %    o radius: the radius of the Gaussian, in pixels, not counting the center
116 %      pixel.
117 %
118 %    o sigma: the standard deviation of the Laplacian, in pixels.
119 %
120 %    o bias: the bias.
121 %
122 %    o exception: return any errors or warnings in this structure.
123 %
124 */
125
126 MagickExport MagickBooleanType AdaptiveLevelImage(Image *image,
127   const char *levels,ExceptionInfo *exception)
128 {
129   double
130     black_point,
131     gamma,
132     white_point;
133
134   GeometryInfo
135     geometry_info;
136
137   MagickBooleanType
138     status;
139
140   MagickStatusType
141     flags;
142
143   /*
144     Parse levels.
145   */
146   if (levels == (char *) NULL)
147     return(MagickFalse);
148   flags=ParseGeometry(levels,&geometry_info);
149   black_point=geometry_info.rho;
150   white_point=(double) QuantumRange;
151   if ((flags & SigmaValue) != 0)
152     white_point=geometry_info.sigma;
153   gamma=1.0;
154   if ((flags & XiValue) != 0)
155     gamma=geometry_info.xi;
156   if ((flags & PercentValue) != 0)
157     {
158       black_point*=(double) image->columns*image->rows/100.0;
159       white_point*=(double) image->columns*image->rows/100.0;
160     }
161   if ((flags & SigmaValue) == 0)
162     white_point=(double) QuantumRange-black_point;
163   if ((flags & AspectValue ) == 0)
164     status=LevelImage(image,black_point,white_point,gamma,exception);
165   else
166     status=LevelizeImage(image,black_point,white_point,gamma,exception);
167   return(status);
168 }
169
170 MagickExport Image *AdaptiveBlurImage(const Image *image,
171   const double radius,const double sigma,const double bias,
172   ExceptionInfo *exception)
173 {
174 #define AdaptiveBlurImageTag  "Convolve/Image"
175 #define MagickSigma  (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
176
177   CacheView
178     *blur_view,
179     *edge_view,
180     *image_view;
181
182   double
183     **kernel,
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   register ssize_t
198     i;
199
200   size_t
201     width;
202
203   ssize_t
204     j,
205     k,
206     u,
207     v,
208     y;
209
210   assert(image != (const Image *) NULL);
211   assert(image->signature == MagickSignature);
212   if (image->debug != MagickFalse)
213     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
214   assert(exception != (ExceptionInfo *) NULL);
215   assert(exception->signature == MagickSignature);
216   blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
217   if (blur_image == (Image *) NULL)
218     return((Image *) NULL);
219   if (fabs(sigma) <= MagickEpsilon)
220     return(blur_image);
221   if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
222     {
223       blur_image=DestroyImage(blur_image);
224       return((Image *) NULL);
225     }
226   /*
227     Edge detect the image brighness channel, level, blur, and level again.
228   */
229   edge_image=EdgeImage(image,radius,sigma,exception);
230   if (edge_image == (Image *) NULL)
231     {
232       blur_image=DestroyImage(blur_image);
233       return((Image *) NULL);
234     }
235   (void) AdaptiveLevelImage(edge_image,"20%,95%",exception);
236   gaussian_image=GaussianBlurImage(edge_image,radius,sigma,bias,exception);
237   if (gaussian_image != (Image *) NULL)
238     {
239       edge_image=DestroyImage(edge_image);
240       edge_image=gaussian_image;
241     }
242   (void) AdaptiveLevelImage(edge_image,"10%,95%",exception);
243   /*
244     Create a set of kernels from maximum (radius,sigma) to minimum.
245   */
246   width=GetOptimalKernelWidth2D(radius,sigma);
247   kernel=(double **) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
248   if (kernel == (double **) NULL)
249     {
250       edge_image=DestroyImage(edge_image);
251       blur_image=DestroyImage(blur_image);
252       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
253     }
254   (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
255   for (i=0; i < (ssize_t) width; i+=2)
256   {
257     kernel[i]=(double *) AcquireQuantumMemory((size_t) (width-i),(width-i)*
258       sizeof(**kernel));
259     if (kernel[i] == (double *) NULL)
260       break;
261     normalize=0.0;
262     j=(ssize_t) (width-i)/2;
263     k=0;
264     for (v=(-j); v <= j; v++)
265     {
266       for (u=(-j); u <= j; u++)
267       {
268         kernel[i][k]=(double) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
269           MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
270         normalize+=kernel[i][k];
271         k++;
272       }
273     }
274     if (fabs(normalize) <= MagickEpsilon)
275       normalize=1.0;
276     normalize=1.0/normalize;
277     for (k=0; k < (j*j); k++)
278       kernel[i][k]=normalize*kernel[i][k];
279   }
280   if (i < (ssize_t) width)
281     {
282       for (i-=2; i >= 0; i-=2)
283         kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
284       kernel=(double **) RelinquishMagickMemory(kernel);
285       edge_image=DestroyImage(edge_image);
286       blur_image=DestroyImage(blur_image);
287       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
288     }
289   /*
290     Adaptively blur image.
291   */
292   status=MagickTrue;
293   progress=0;
294   image_view=AcquireCacheView(image);
295   edge_view=AcquireCacheView(edge_image);
296   blur_view=AcquireCacheView(blur_image);
297 #if defined(MAGICKCORE_OPENMP_SUPPORT)
298   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
299 #endif
300   for (y=0; y < (ssize_t) blur_image->rows; y++)
301   {
302     register const Quantum
303       *restrict r;
304
305     register Quantum
306       *restrict q;
307
308     register ssize_t
309       x;
310
311     if (status == MagickFalse)
312       continue;
313     r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
314     q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
315       exception);
316     if ((r == (const Quantum *) NULL) || (q == (Quantum *) NULL))
317       {
318         status=MagickFalse;
319         continue;
320       }
321     for (x=0; x < (ssize_t) blur_image->columns; x++)
322     {
323       register const Quantum
324         *restrict p;
325
326       register ssize_t
327         i;
328
329       ssize_t
330         center,
331         j;
332
333       j=(ssize_t) ceil((double) width*QuantumScale*
334         GetPixelIntensity(edge_image,r)-0.5);
335       if (j < 0)
336         j=0;
337       else
338         if (j > (ssize_t) width)
339           j=(ssize_t) width;
340       if ((j & 0x01) != 0)
341         j--;
342       p=GetCacheViewVirtualPixels(image_view,x-((ssize_t) (width-j)/2L),y-
343         (ssize_t) ((width-j)/2L),width-j,width-j,exception);
344       if (p == (const Quantum *) NULL)
345         break;
346       center=(ssize_t) GetPixelChannels(image)*(width-j)*
347         ((width-j)/2L)+GetPixelChannels(image)*((width-j)/2L);
348       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
349       {
350         MagickRealType
351           alpha,
352           gamma,
353           pixel;
354
355         PixelChannel
356           channel;
357
358         PixelTrait
359           blur_traits,
360           traits;
361
362         register const double
363           *restrict k;
364
365         register const Quantum
366           *restrict pixels;
367
368         register ssize_t
369           u;
370
371         ssize_t
372           v;
373
374         traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
375         channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
376         blur_traits=GetPixelChannelMapTraits(blur_image,channel);
377         if ((traits == UndefinedPixelTrait) ||
378             (blur_traits == UndefinedPixelTrait))
379           continue;
380         if ((blur_traits & CopyPixelTrait) != 0)
381           {
382             q[channel]=p[center+i];
383             continue;
384           }
385         k=kernel[j];
386         pixels=p;
387         pixel=bias;
388         gamma=0.0;
389         if ((blur_traits & BlendPixelTrait) == 0)
390           {
391             /*
392               No alpha blending.
393             */
394             for (v=0; v < (ssize_t) (width-j); v++)
395             {
396               for (u=0; u < (ssize_t) (width-j); u++)
397               {
398                 pixel+=(*k)*pixels[i];
399                 gamma+=(*k);
400                 k++;
401                 pixels+=GetPixelChannels(image);
402               }
403             }
404             gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
405             q[channel]=ClampToQuantum(gamma*pixel);
406             continue;
407           }
408         /*
409           Alpha blending.
410         */
411         for (v=0; v < (ssize_t) (width-j); v++)
412         {
413           for (u=0; u < (ssize_t) (width-j); u++)
414           {
415             alpha=(MagickRealType) (QuantumScale*GetPixelAlpha(image,pixels));
416             pixel+=(*k)*alpha*pixels[i];
417             gamma+=(*k)*alpha;
418             k++;
419             pixels+=GetPixelChannels(image);
420           }
421         }
422         gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
423         q[channel]=ClampToQuantum(gamma*pixel);
424       }
425       q+=GetPixelChannels(blur_image);
426       r+=GetPixelChannels(edge_image);
427     }
428     if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
429       status=MagickFalse;
430     if (image->progress_monitor != (MagickProgressMonitor) NULL)
431       {
432         MagickBooleanType
433           proceed;
434
435 #if defined(MAGICKCORE_OPENMP_SUPPORT)
436   #pragma omp critical (MagickCore_AdaptiveBlurImage)
437 #endif
438         proceed=SetImageProgress(image,AdaptiveBlurImageTag,progress++,
439           image->rows);
440         if (proceed == MagickFalse)
441           status=MagickFalse;
442       }
443   }
444   blur_image->type=image->type;
445   blur_view=DestroyCacheView(blur_view);
446   edge_view=DestroyCacheView(edge_view);
447   image_view=DestroyCacheView(image_view);
448   edge_image=DestroyImage(edge_image);
449   for (i=0; i < (ssize_t) width;  i+=2)
450     kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
451   kernel=(double **) RelinquishMagickMemory(kernel);
452   if (status == MagickFalse)
453     blur_image=DestroyImage(blur_image);
454   return(blur_image);
455 }
456 \f
457 /*
458 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
459 %                                                                             %
460 %                                                                             %
461 %                                                                             %
462 %     A d a p t i v e S h a r p e n I m a g e                                 %
463 %                                                                             %
464 %                                                                             %
465 %                                                                             %
466 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
467 %
468 %  AdaptiveSharpenImage() adaptively sharpens the image by sharpening more
469 %  intensely near image edges and less intensely far from edges. We sharpen the
470 %  image with a Gaussian operator of the given radius and standard deviation
471 %  (sigma).  For reasonable results, radius should be larger than sigma.  Use a
472 %  radius of 0 and AdaptiveSharpenImage() selects a suitable radius for you.
473 %
474 %  The format of the AdaptiveSharpenImage method is:
475 %
476 %      Image *AdaptiveSharpenImage(const Image *image,const double radius,
477 %        const double sigma,const double bias,ExceptionInfo *exception)
478 %
479 %  A description of each parameter follows:
480 %
481 %    o image: the image.
482 %
483 %    o radius: the radius of the Gaussian, in pixels, not counting the center
484 %      pixel.
485 %
486 %    o sigma: the standard deviation of the Laplacian, in pixels.
487 %
488 %    o bias: the bias.
489 %
490 %    o exception: return any errors or warnings in this structure.
491 %
492 */
493 MagickExport Image *AdaptiveSharpenImage(const Image *image,const double radius,
494   const double sigma,const double bias,ExceptionInfo *exception)
495 {
496 #define AdaptiveSharpenImageTag  "Convolve/Image"
497 #define MagickSigma  (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
498
499   CacheView
500     *sharp_view,
501     *edge_view,
502     *image_view;
503
504   double
505     **kernel,
506     normalize;
507
508   Image
509     *sharp_image,
510     *edge_image,
511     *gaussian_image;
512
513   MagickBooleanType
514     status;
515
516   MagickOffsetType
517     progress;
518
519   register ssize_t
520     i;
521
522   size_t
523     width;
524
525   ssize_t
526     j,
527     k,
528     u,
529     v,
530     y;
531
532   assert(image != (const Image *) NULL);
533   assert(image->signature == MagickSignature);
534   if (image->debug != MagickFalse)
535     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
536   assert(exception != (ExceptionInfo *) NULL);
537   assert(exception->signature == MagickSignature);
538   sharp_image=CloneImage(image,0,0,MagickTrue,exception);
539   if (sharp_image == (Image *) NULL)
540     return((Image *) NULL);
541   if (fabs(sigma) <= MagickEpsilon)
542     return(sharp_image);
543   if (SetImageStorageClass(sharp_image,DirectClass,exception) == MagickFalse)
544     {
545       sharp_image=DestroyImage(sharp_image);
546       return((Image *) NULL);
547     }
548   /*
549     Edge detect the image brighness channel, level, sharp, and level again.
550   */
551   edge_image=EdgeImage(image,radius,sigma,exception);
552   if (edge_image == (Image *) NULL)
553     {
554       sharp_image=DestroyImage(sharp_image);
555       return((Image *) NULL);
556     }
557   (void) AdaptiveLevelImage(edge_image,"20%,95%",exception);
558   gaussian_image=GaussianBlurImage(edge_image,radius,sigma,bias,exception);
559   if (gaussian_image != (Image *) NULL)
560     {
561       edge_image=DestroyImage(edge_image);
562       edge_image=gaussian_image;
563     }
564   (void) AdaptiveLevelImage(edge_image,"10%,95%",exception);
565   /*
566     Create a set of kernels from maximum (radius,sigma) to minimum.
567   */
568   width=GetOptimalKernelWidth2D(radius,sigma);
569   kernel=(double **) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
570   if (kernel == (double **) NULL)
571     {
572       edge_image=DestroyImage(edge_image);
573       sharp_image=DestroyImage(sharp_image);
574       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
575     }
576   (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
577   for (i=0; i < (ssize_t) width; i+=2)
578   {
579     kernel[i]=(double *) AcquireQuantumMemory((size_t) (width-i),(width-i)*
580       sizeof(**kernel));
581     if (kernel[i] == (double *) NULL)
582       break;
583     normalize=0.0;
584     j=(ssize_t) (width-i)/2;
585     k=0;
586     for (v=(-j); v <= j; v++)
587     {
588       for (u=(-j); u <= j; u++)
589       {
590         kernel[i][k]=(double) (-exp(-((double) u*u+v*v)/(2.0*MagickSigma*
591           MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
592         normalize+=kernel[i][k];
593         k++;
594       }
595     }
596     if (fabs(normalize) <= MagickEpsilon)
597       normalize=1.0;
598     normalize=1.0/normalize;
599     for (k=0; k < (j*j); k++)
600       kernel[i][k]=normalize*kernel[i][k];
601   }
602   if (i < (ssize_t) width)
603     {
604       for (i-=2; i >= 0; i-=2)
605         kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
606       kernel=(double **) RelinquishMagickMemory(kernel);
607       edge_image=DestroyImage(edge_image);
608       sharp_image=DestroyImage(sharp_image);
609       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
610     }
611   /*
612     Adaptively sharpen image.
613   */
614   status=MagickTrue;
615   progress=0;
616   image_view=AcquireCacheView(image);
617   edge_view=AcquireCacheView(edge_image);
618   sharp_view=AcquireCacheView(sharp_image);
619 #if defined(MAGICKCORE_OPENMP_SUPPORT)
620   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
621 #endif
622   for (y=0; y < (ssize_t) sharp_image->rows; y++)
623   {
624     register const Quantum
625       *restrict r;
626
627     register Quantum
628       *restrict q;
629
630     register ssize_t
631       x;
632
633     if (status == MagickFalse)
634       continue;
635     r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
636     q=QueueCacheViewAuthenticPixels(sharp_view,0,y,sharp_image->columns,1,
637       exception);
638     if ((r == (const Quantum *) NULL) || (q == (Quantum *) NULL))
639       {
640         status=MagickFalse;
641         continue;
642       }
643     for (x=0; x < (ssize_t) sharp_image->columns; x++)
644     {
645       register const Quantum
646         *restrict p;
647
648       register ssize_t
649         i;
650
651       ssize_t
652         center,
653         j;
654
655       j=(ssize_t) ceil((double) width*QuantumScale*
656         GetPixelIntensity(edge_image,r)-0.5);
657       if (j < 0)
658         j=0;
659       else
660         if (j > (ssize_t) width)
661           j=(ssize_t) width;
662       if ((j & 0x01) != 0)
663         j--;
664       p=GetCacheViewVirtualPixels(image_view,x-((ssize_t) (width-j)/2L),y-
665         (ssize_t) ((width-j)/2L),width-j,width-j,exception);
666       if (p == (const Quantum *) NULL)
667         break;
668       center=(ssize_t) GetPixelChannels(image)*(width-j)*
669         ((width-j)/2L)+GetPixelChannels(image)*((width-j)/2);
670       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
671       {
672         MagickRealType
673           alpha,
674           gamma,
675           pixel;
676
677         PixelChannel
678           channel;
679
680         PixelTrait
681           sharp_traits,
682           traits;
683
684         register const double
685           *restrict k;
686
687         register const Quantum
688           *restrict pixels;
689
690         register ssize_t
691           u;
692
693         ssize_t
694           v;
695
696         traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
697         channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
698         sharp_traits=GetPixelChannelMapTraits(sharp_image,channel);
699         if ((traits == UndefinedPixelTrait) ||
700             (sharp_traits == UndefinedPixelTrait))
701           continue;
702         if ((sharp_traits & CopyPixelTrait) != 0)
703           {
704             q[channel]=p[center+i];
705             continue;
706           }
707         k=kernel[j];
708         pixels=p;
709         pixel=bias;
710         gamma=0.0;
711         if ((sharp_traits & BlendPixelTrait) == 0)
712           {
713             /*
714               No alpha blending.
715             */
716             for (v=0; v < (ssize_t) (width-j); v++)
717             {
718               for (u=0; u < (ssize_t) (width-j); u++)
719               {
720                 pixel+=(*k)*pixels[i];
721                 gamma+=(*k);
722                 k++;
723                 pixels+=GetPixelChannels(image);
724               }
725             }
726             gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
727             q[channel]=ClampToQuantum(gamma*pixel);
728             continue;
729           }
730         /*
731           Alpha blending.
732         */
733         for (v=0; v < (ssize_t) (width-j); v++)
734         {
735           for (u=0; u < (ssize_t) (width-j); u++)
736           {
737             alpha=(MagickRealType) (QuantumScale*GetPixelAlpha(image,pixels));
738             pixel+=(*k)*alpha*pixels[i];
739             gamma+=(*k)*alpha;
740             k++;
741             pixels+=GetPixelChannels(image);
742           }
743         }
744         gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
745         q[channel]=ClampToQuantum(gamma*pixel);
746       }
747       q+=GetPixelChannels(sharp_image);
748       r+=GetPixelChannels(edge_image);
749     }
750     if (SyncCacheViewAuthenticPixels(sharp_view,exception) == MagickFalse)
751       status=MagickFalse;
752     if (image->progress_monitor != (MagickProgressMonitor) NULL)
753       {
754         MagickBooleanType
755           proceed;
756
757 #if defined(MAGICKCORE_OPENMP_SUPPORT)
758   #pragma omp critical (MagickCore_AdaptiveSharpenImage)
759 #endif
760         proceed=SetImageProgress(image,AdaptiveSharpenImageTag,progress++,
761           image->rows);
762         if (proceed == MagickFalse)
763           status=MagickFalse;
764       }
765   }
766   sharp_image->type=image->type;
767   sharp_view=DestroyCacheView(sharp_view);
768   edge_view=DestroyCacheView(edge_view);
769   image_view=DestroyCacheView(image_view);
770   edge_image=DestroyImage(edge_image);
771   for (i=0; i < (ssize_t) width;  i+=2)
772     kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
773   kernel=(double **) RelinquishMagickMemory(kernel);
774   if (status == MagickFalse)
775     sharp_image=DestroyImage(sharp_image);
776   return(sharp_image);
777 }
778 \f
779 /*
780 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
781 %                                                                             %
782 %                                                                             %
783 %                                                                             %
784 %     B l u r I m a g e                                                       %
785 %                                                                             %
786 %                                                                             %
787 %                                                                             %
788 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
789 %
790 %  BlurImage() blurs an image.  We convolve the image with a Gaussian operator
791 %  of the given radius and standard deviation (sigma).  For reasonable results,
792 %  the radius should be larger than sigma.  Use a radius of 0 and BlurImage()
793 %  selects a suitable radius for you.
794 %
795 %  BlurImage() differs from GaussianBlurImage() in that it uses a separable
796 %  kernel which is faster but mathematically equivalent to the non-separable
797 %  kernel.
798 %
799 %  The format of the BlurImage method is:
800 %
801 %      Image *BlurImage(const Image *image,const double radius,
802 %        const double sigma,const double bias,ExceptionInfo *exception)
803 %
804 %  A description of each parameter follows:
805 %
806 %    o image: the image.
807 %
808 %    o radius: the radius of the Gaussian, in pixels, not counting the center
809 %      pixel.
810 %
811 %    o sigma: the standard deviation of the Gaussian, in pixels.
812 %
813 %    o bias: the bias.
814 %
815 %    o exception: return any errors or warnings in this structure.
816 %
817 */
818
819 static double *GetBlurKernel(const size_t width,const double sigma)
820 {
821   double
822     *kernel,
823     normalize;
824
825   register ssize_t
826     i;
827
828   ssize_t
829     j,
830     k;
831
832   /*
833     Generate a 1-D convolution kernel.
834   */
835   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
836   kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
837   if (kernel == (double *) NULL)
838     return(0);
839   normalize=0.0;
840   j=(ssize_t) width/2;
841   i=0;
842   for (k=(-j); k <= j; k++)
843   {
844     kernel[i]=(double) (exp(-((double) k*k)/(2.0*MagickSigma*MagickSigma))/
845       (MagickSQ2PI*MagickSigma));
846     normalize+=kernel[i];
847     i++;
848   }
849   for (i=0; i < (ssize_t) width; i++)
850     kernel[i]/=normalize;
851   return(kernel);
852 }
853
854 MagickExport Image *BlurImage(const Image *image,const double radius,
855   const double sigma,const double bias,ExceptionInfo *exception)
856 {
857 #define BlurImageTag  "Blur/Image"
858
859   CacheView
860     *blur_view,
861     *image_view;
862
863   double
864     *kernel;
865
866   Image
867     *blur_image;
868
869   MagickBooleanType
870     status;
871
872   MagickOffsetType
873     progress;
874
875   register ssize_t
876     i;
877
878   size_t
879     width;
880
881   ssize_t
882     center,
883     x,
884     y;
885
886   /*
887     Initialize blur image attributes.
888   */
889   assert(image != (Image *) NULL);
890   assert(image->signature == MagickSignature);
891   if (image->debug != MagickFalse)
892     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
893   assert(exception != (ExceptionInfo *) NULL);
894   assert(exception->signature == MagickSignature);
895   blur_image=CloneImage(image,0,0,MagickTrue,exception);
896   if (blur_image == (Image *) NULL)
897     return((Image *) NULL);
898   if (fabs(sigma) <= MagickEpsilon)
899     return(blur_image);
900   if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
901     {
902       blur_image=DestroyImage(blur_image);
903       return((Image *) NULL);
904     }
905   width=GetOptimalKernelWidth1D(radius,sigma);
906   kernel=GetBlurKernel(width,sigma);
907   if (kernel == (double *) NULL)
908     {
909       blur_image=DestroyImage(blur_image);
910       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
911     }
912   if (image->debug != MagickFalse)
913     {
914       char
915         format[MaxTextExtent],
916         *message;
917
918       register const double
919         *k;
920
921       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
922         "  BlurImage with %.20g kernel:",(double) width);
923       message=AcquireString("");
924       k=kernel;
925       for (i=0; i < (ssize_t) width; i++)
926       {
927         *message='\0';
928         (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) i);
929         (void) ConcatenateString(&message,format);
930         (void) FormatLocaleString(format,MaxTextExtent,"%g ",*k++);
931         (void) ConcatenateString(&message,format);
932         (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
933       }
934       message=DestroyString(message);
935     }
936   /*
937     Blur rows.
938   */
939   status=MagickTrue;
940   progress=0;
941   center=(ssize_t) GetPixelChannels(image)*(width/2L);
942   image_view=AcquireCacheView(image);
943   blur_view=AcquireCacheView(blur_image);
944 #if defined(MAGICKCORE_OPENMP_SUPPORT)
945   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
946 #endif
947   for (y=0; y < (ssize_t) image->rows; y++)
948   {
949     register const Quantum
950       *restrict p;
951
952     register Quantum
953       *restrict q;
954
955     register ssize_t
956       x;
957
958     if (status == MagickFalse)
959       continue;
960     p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y,
961       image->columns+width,1,exception);
962     q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
963       exception);
964     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
965       {
966         status=MagickFalse;
967         continue;
968       }
969     for (x=0; x < (ssize_t) image->columns; x++)
970     {
971       register ssize_t
972         i;
973
974       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
975       {
976         MagickRealType
977           alpha,
978           gamma,
979           pixel;
980
981         PixelChannel
982           channel;
983
984         PixelTrait
985           blur_traits,
986           traits;
987
988         register const double
989           *restrict k;
990
991         register const Quantum
992           *restrict pixels;
993
994         register ssize_t
995           u;
996
997         traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
998         channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
999         blur_traits=GetPixelChannelMapTraits(blur_image,channel);
1000         if ((traits == UndefinedPixelTrait) ||
1001             (blur_traits == UndefinedPixelTrait))
1002           continue;
1003         if ((blur_traits & CopyPixelTrait) != 0)
1004           {
1005             q[channel]=p[center+i];
1006             continue;
1007           }
1008         k=kernel;
1009         pixels=p;
1010         pixel=0.0;
1011         if ((blur_traits & BlendPixelTrait) == 0)
1012           {
1013             /*
1014               No alpha blending.
1015             */
1016             for (u=0; u < (ssize_t) width; u++)
1017             {
1018               pixel+=(*k)*pixels[i];
1019               k++;
1020               pixels+=GetPixelChannels(image);
1021             }
1022             q[channel]=ClampToQuantum(pixel);
1023             continue;
1024           }
1025         /*
1026           Alpha blending.
1027         */
1028         gamma=0.0;
1029         for (u=0; u < (ssize_t) width; u++)
1030         {
1031           alpha=(MagickRealType) (QuantumScale*GetPixelAlpha(image,pixels));
1032           pixel+=(*k)*alpha*pixels[i];
1033           gamma+=(*k)*alpha;
1034           k++;
1035           pixels+=GetPixelChannels(image);
1036         }
1037         gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1038         q[channel]=ClampToQuantum(gamma*pixel);
1039       }
1040       p+=GetPixelChannels(image);
1041       q+=GetPixelChannels(blur_image);
1042     }
1043     if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
1044       status=MagickFalse;
1045     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1046       {
1047         MagickBooleanType
1048           proceed;
1049
1050 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1051   #pragma omp critical (MagickCore_BlurImage)
1052 #endif
1053         proceed=SetImageProgress(image,BlurImageTag,progress++,blur_image->rows+
1054           blur_image->columns);
1055         if (proceed == MagickFalse)
1056           status=MagickFalse;
1057       }
1058   }
1059   blur_view=DestroyCacheView(blur_view);
1060   image_view=DestroyCacheView(image_view);
1061   /*
1062     Blur columns.
1063   */
1064   image_view=AcquireCacheView(blur_image);
1065   blur_view=AcquireCacheView(blur_image);
1066 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1067   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1068 #endif
1069   for (x=0; x < (ssize_t) image->columns; x++)
1070   {
1071     register const Quantum
1072       *restrict p;
1073
1074     register Quantum
1075       *restrict q;
1076
1077     register ssize_t
1078       y;
1079
1080     if (status == MagickFalse)
1081       continue;
1082     p=GetCacheViewVirtualPixels(image_view,x,-((ssize_t) width/2L),1,
1083       image->rows+width,exception);
1084     q=GetCacheViewAuthenticPixels(blur_view,x,0,1,blur_image->rows,exception);
1085     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1086       {
1087         status=MagickFalse;
1088         continue;
1089       }
1090     for (y=0; y < (ssize_t) image->rows; y++)
1091     {
1092       register ssize_t
1093         i;
1094
1095       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1096       {
1097         MagickRealType
1098           alpha,
1099           gamma,
1100           pixel;
1101
1102         PixelChannel
1103           channel;
1104
1105         PixelTrait
1106           blur_traits,
1107           traits;
1108
1109         register const double
1110           *restrict k;
1111
1112         register const Quantum
1113           *restrict pixels;
1114
1115         register ssize_t
1116           u;
1117
1118         traits=GetPixelChannelMapTraits(blur_image,(PixelChannel) i);
1119         channel=GetPixelChannelMapChannel(blur_image,(PixelChannel) i);
1120         blur_traits=GetPixelChannelMapTraits(blur_image,channel);
1121         if ((traits == UndefinedPixelTrait) ||
1122             (blur_traits == UndefinedPixelTrait))
1123           continue;
1124         if ((blur_traits & CopyPixelTrait) != 0)
1125           {
1126             q[channel]=p[center+i];
1127             continue;
1128           }
1129         k=kernel;
1130         pixels=p;
1131         pixel=0.0;
1132         if ((blur_traits & BlendPixelTrait) == 0)
1133           {
1134             /*
1135               No alpha blending.
1136             */
1137             for (u=0; u < (ssize_t) width; u++)
1138             {
1139               pixel+=(*k)*pixels[i];
1140               k++;
1141               pixels+=GetPixelChannels(blur_image);
1142             }
1143             q[channel]=ClampToQuantum(pixel);
1144             continue;
1145           }
1146         /*
1147           Alpha blending.
1148         */
1149         gamma=0.0;
1150         for (u=0; u < (ssize_t) width; u++)
1151         {
1152           alpha=(MagickRealType) (QuantumScale*
1153             GetPixelAlpha(blur_image,pixels));
1154           pixel+=(*k)*alpha*pixels[i];
1155           gamma+=(*k)*alpha;
1156           k++;
1157           pixels+=GetPixelChannels(blur_image);
1158         }
1159         gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1160         q[channel]=ClampToQuantum(gamma*pixel);
1161       }
1162       p+=GetPixelChannels(blur_image);
1163       q+=GetPixelChannels(blur_image);
1164     }
1165     if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
1166       status=MagickFalse;
1167     if (blur_image->progress_monitor != (MagickProgressMonitor) NULL)
1168       {
1169         MagickBooleanType
1170           proceed;
1171
1172 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1173   #pragma omp critical (MagickCore_BlurImage)
1174 #endif
1175         proceed=SetImageProgress(blur_image,BlurImageTag,progress++,
1176           blur_image->rows+blur_image->columns);
1177         if (proceed == MagickFalse)
1178           status=MagickFalse;
1179       }
1180   }
1181   blur_view=DestroyCacheView(blur_view);
1182   image_view=DestroyCacheView(image_view);
1183   kernel=(double *) RelinquishMagickMemory(kernel);
1184   if (status == MagickFalse)
1185     blur_image=DestroyImage(blur_image);
1186   blur_image->type=image->type;
1187   return(blur_image);
1188 }
1189 \f
1190 /*
1191 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1192 %                                                                             %
1193 %                                                                             %
1194 %                                                                             %
1195 %     C o n v o l v e I m a g e                                               %
1196 %                                                                             %
1197 %                                                                             %
1198 %                                                                             %
1199 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1200 %
1201 %  ConvolveImage() applies a custom convolution kernel to the image.
1202 %
1203 %  The format of the ConvolveImage method is:
1204 %
1205 %      Image *ConvolveImage(const Image *image,const KernelInfo *kernel,
1206 %        ExceptionInfo *exception)
1207 %
1208 %  A description of each parameter follows:
1209 %
1210 %    o image: the image.
1211 %
1212 %    o kernel: the filtering kernel.
1213 %
1214 %    o exception: return any errors or warnings in this structure.
1215 %
1216 */
1217 MagickExport Image *ConvolveImage(const Image *image,
1218   const KernelInfo *kernel_info,ExceptionInfo *exception)
1219 {
1220 #define ConvolveImageTag  "Convolve/Image"
1221
1222   CacheView
1223     *convolve_view,
1224     *image_view;
1225
1226   Image
1227     *convolve_image;
1228
1229   MagickBooleanType
1230     status;
1231
1232   MagickOffsetType
1233     progress;
1234
1235   ssize_t
1236     center,
1237     y;
1238
1239   /*
1240     Initialize convolve image attributes.
1241   */
1242   assert(image != (Image *) NULL);
1243   assert(image->signature == MagickSignature);
1244   if (image->debug != MagickFalse)
1245     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1246   assert(exception != (ExceptionInfo *) NULL);
1247   assert(exception->signature == MagickSignature);
1248   if ((kernel_info->width % 2) == 0)
1249     ThrowImageException(OptionError,"KernelWidthMustBeAnOddNumber");
1250   convolve_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1251     exception);
1252   if (convolve_image == (Image *) NULL)
1253     return((Image *) NULL);
1254   if (SetImageStorageClass(convolve_image,DirectClass,exception) == MagickFalse)
1255     {
1256       convolve_image=DestroyImage(convolve_image);
1257       return((Image *) NULL);
1258     }
1259   if (image->debug != MagickFalse)
1260     {
1261       char
1262         format[MaxTextExtent],
1263         *message;
1264
1265       register const double
1266         *k;
1267
1268       register ssize_t
1269         u;
1270
1271       ssize_t
1272         v;
1273
1274       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1275         "  ConvolveImage with %.20gx%.20g kernel:",(double) kernel_info->width,
1276         (double) kernel_info->height);
1277       message=AcquireString("");
1278       k=kernel_info->values;
1279       for (v=0; v < (ssize_t) kernel_info->width; v++)
1280       {
1281         *message='\0';
1282         (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) v);
1283         (void) ConcatenateString(&message,format);
1284         for (u=0; u < (ssize_t) kernel_info->height; u++)
1285         {
1286           (void) FormatLocaleString(format,MaxTextExtent,"%g ",*k++);
1287           (void) ConcatenateString(&message,format);
1288         }
1289         (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
1290       }
1291       message=DestroyString(message);
1292     }
1293   status=AccelerateConvolveImage(image,kernel_info,convolve_image,exception);
1294   if (status == MagickTrue)
1295     return(convolve_image);
1296   /*
1297     Convolve image.
1298   */
1299   center=(ssize_t) GetPixelChannels(image)*(image->columns+kernel_info->width)*
1300     (kernel_info->height/2L)+GetPixelChannels(image)*(kernel_info->width/2L);
1301   status=MagickTrue;
1302   progress=0;
1303   image_view=AcquireCacheView(image);
1304   convolve_view=AcquireCacheView(convolve_image);
1305 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1306   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1307 #endif
1308   for (y=0; y < (ssize_t) image->rows; y++)
1309   {
1310     register const Quantum
1311       *restrict p;
1312
1313     register Quantum
1314       *restrict q;
1315
1316     register ssize_t
1317       x;
1318
1319     if (status == MagickFalse)
1320       continue;
1321     p=GetCacheViewVirtualPixels(image_view,-((ssize_t) kernel_info->width/2L),y-
1322       (ssize_t) (kernel_info->height/2L),image->columns+kernel_info->width,
1323       kernel_info->height,exception);
1324     q=QueueCacheViewAuthenticPixels(convolve_view,0,y,convolve_image->columns,1,
1325       exception);
1326     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1327       {
1328         status=MagickFalse;
1329         continue;
1330       }
1331     for (x=0; x < (ssize_t) image->columns; x++)
1332     {
1333       register ssize_t
1334         i;
1335
1336       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1337       {
1338         MagickRealType
1339           alpha,
1340           gamma,
1341           pixel;
1342
1343         PixelChannel
1344           channel;
1345
1346         PixelTrait
1347           convolve_traits,
1348           traits;
1349
1350         register const double
1351           *restrict k;
1352
1353         register const Quantum
1354           *restrict pixels;
1355
1356         register ssize_t
1357           u;
1358
1359         ssize_t
1360           v;
1361
1362         traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
1363         channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
1364         convolve_traits=GetPixelChannelMapTraits(convolve_image,channel);
1365         if ((traits == UndefinedPixelTrait) ||
1366             (convolve_traits == UndefinedPixelTrait))
1367           continue;
1368         if ((convolve_traits & CopyPixelTrait) != 0)
1369           {
1370             q[channel]=p[center+i];
1371             continue;
1372           }
1373         k=kernel_info->values;
1374         pixels=p;
1375         pixel=kernel_info->bias;
1376         if ((convolve_traits & BlendPixelTrait) == 0)
1377           {
1378             /*
1379               No alpha blending.
1380             */
1381             for (v=0; v < (ssize_t) kernel_info->height; v++)
1382             {
1383               for (u=0; u < (ssize_t) kernel_info->width; u++)
1384               {
1385                 pixel+=(*k)*pixels[i];
1386                 k++;
1387                 pixels+=GetPixelChannels(image);
1388               }
1389               pixels+=image->columns*GetPixelChannels(image);
1390             }
1391             q[channel]=ClampToQuantum(pixel);
1392             continue;
1393           }
1394         /*
1395           Alpha blending.
1396         */
1397         gamma=0.0;
1398         for (v=0; v < (ssize_t) kernel_info->height; v++)
1399         {
1400           for (u=0; u < (ssize_t) kernel_info->width; u++)
1401           {
1402             alpha=(MagickRealType) (QuantumScale*GetPixelAlpha(image,pixels));
1403             pixel+=(*k)*alpha*pixels[i];
1404             gamma+=(*k)*alpha;
1405             k++;
1406             pixels+=GetPixelChannels(image);
1407           }
1408           pixels+=image->columns*GetPixelChannels(image);
1409         }
1410         gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1411         q[channel]=ClampToQuantum(gamma*pixel);
1412       }
1413       p+=GetPixelChannels(image);
1414       q+=GetPixelChannels(convolve_image);
1415     }
1416     if (SyncCacheViewAuthenticPixels(convolve_view,exception) == MagickFalse)
1417       status=MagickFalse;
1418     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1419       {
1420         MagickBooleanType
1421           proceed;
1422
1423 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1424   #pragma omp critical (MagickCore_ConvolveImage)
1425 #endif
1426         proceed=SetImageProgress(image,ConvolveImageTag,progress++,image->rows);
1427         if (proceed == MagickFalse)
1428           status=MagickFalse;
1429       }
1430   }
1431   convolve_image->type=image->type;
1432   convolve_view=DestroyCacheView(convolve_view);
1433   image_view=DestroyCacheView(image_view);
1434   if (status == MagickFalse)
1435     convolve_image=DestroyImage(convolve_image);
1436   return(convolve_image);
1437 }
1438 \f
1439 /*
1440 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1441 %                                                                             %
1442 %                                                                             %
1443 %                                                                             %
1444 %     D e s p e c k l e I m a g e                                             %
1445 %                                                                             %
1446 %                                                                             %
1447 %                                                                             %
1448 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1449 %
1450 %  DespeckleImage() reduces the speckle noise in an image while perserving the
1451 %  edges of the original image.
1452 %
1453 %  The format of the DespeckleImage method is:
1454 %
1455 %      Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1456 %
1457 %  A description of each parameter follows:
1458 %
1459 %    o image: the image.
1460 %
1461 %    o exception: return any errors or warnings in this structure.
1462 %
1463 */
1464
1465 static void Hull(const ssize_t x_offset,const ssize_t y_offset,
1466   const size_t columns,const size_t rows,Quantum *f,Quantum *g,
1467   const int polarity)
1468 {
1469   MagickRealType
1470     v;
1471
1472   register Quantum
1473     *p,
1474     *q,
1475     *r,
1476     *s;
1477
1478   register ssize_t
1479     x;
1480
1481   ssize_t
1482     y;
1483
1484   assert(f != (Quantum *) NULL);
1485   assert(g != (Quantum *) NULL);
1486   p=f+(columns+2);
1487   q=g+(columns+2);
1488   r=p+(y_offset*((ssize_t) columns+2)+x_offset);
1489   for (y=0; y < (ssize_t) rows; y++)
1490   {
1491     p++;
1492     q++;
1493     r++;
1494     if (polarity > 0)
1495       for (x=(ssize_t) columns; x != 0; x--)
1496       {
1497         v=(MagickRealType) (*p);
1498         if ((MagickRealType) *r >= (v+(MagickRealType) ScaleCharToQuantum(2)))
1499           v+=ScaleCharToQuantum(1);
1500         *q=(Quantum) v;
1501         p++;
1502         q++;
1503         r++;
1504       }
1505     else
1506       for (x=(ssize_t) columns; x != 0; x--)
1507       {
1508         v=(MagickRealType) (*p);
1509         if ((MagickRealType) *r <= (v-(MagickRealType) ScaleCharToQuantum(2)))
1510           v-=(ssize_t) ScaleCharToQuantum(1);
1511         *q=(Quantum) v;
1512         p++;
1513         q++;
1514         r++;
1515       }
1516     p++;
1517     q++;
1518     r++;
1519   }
1520   p=f+(columns+2);
1521   q=g+(columns+2);
1522   r=q+(y_offset*((ssize_t) columns+2)+x_offset);
1523   s=q-(y_offset*((ssize_t) columns+2)+x_offset);
1524   for (y=0; y < (ssize_t) rows; y++)
1525   {
1526     p++;
1527     q++;
1528     r++;
1529     s++;
1530     if (polarity > 0)
1531       for (x=(ssize_t) columns; x != 0; x--)
1532       {
1533         v=(MagickRealType) (*q);
1534         if (((MagickRealType) *s >=
1535              (v+(MagickRealType) ScaleCharToQuantum(2))) &&
1536             ((MagickRealType) *r > v))
1537           v+=ScaleCharToQuantum(1);
1538         *p=(Quantum) v;
1539         p++;
1540         q++;
1541         r++;
1542         s++;
1543       }
1544     else
1545       for (x=(ssize_t) columns; x != 0; x--)
1546       {
1547         v=(MagickRealType) (*q);
1548         if (((MagickRealType) *s <=
1549              (v-(MagickRealType) ScaleCharToQuantum(2))) &&
1550             ((MagickRealType) *r < v))
1551           v-=(MagickRealType) ScaleCharToQuantum(1);
1552         *p=(Quantum) v;
1553         p++;
1554         q++;
1555         r++;
1556         s++;
1557       }
1558     p++;
1559     q++;
1560     r++;
1561     s++;
1562   }
1563 }
1564
1565 MagickExport Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1566 {
1567 #define DespeckleImageTag  "Despeckle/Image"
1568
1569   CacheView
1570     *despeckle_view,
1571     *image_view;
1572
1573   Image
1574     *despeckle_image;
1575
1576   MagickBooleanType
1577     status;
1578
1579   Quantum
1580     *restrict buffers,
1581     *restrict pixels;
1582
1583   register ssize_t
1584     i;
1585
1586   size_t
1587     length;
1588
1589   static const ssize_t
1590     X[4] = {0, 1, 1,-1},
1591     Y[4] = {1, 0, 1, 1};
1592
1593   /*
1594     Allocate despeckled image.
1595   */
1596   assert(image != (const Image *) NULL);
1597   assert(image->signature == MagickSignature);
1598   if (image->debug != MagickFalse)
1599     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1600   assert(exception != (ExceptionInfo *) NULL);
1601   assert(exception->signature == MagickSignature);
1602   despeckle_image=CloneImage(image,0,0,MagickTrue,exception);
1603   if (despeckle_image == (Image *) NULL)
1604     return((Image *) NULL);
1605   status=SetImageStorageClass(despeckle_image,DirectClass,exception);
1606   if (status == MagickFalse)
1607     {
1608       despeckle_image=DestroyImage(despeckle_image);
1609       return((Image *) NULL);
1610     }
1611   /*
1612     Allocate image buffers.
1613   */
1614   length=(size_t) ((image->columns+2)*(image->rows+2));
1615   pixels=(Quantum *) AcquireQuantumMemory(length,2*sizeof(*pixels));
1616   buffers=(Quantum *) AcquireQuantumMemory(length,2*sizeof(*pixels));
1617   if ((pixels == (Quantum *) NULL) || (buffers == (Quantum *) NULL))
1618     {
1619       if (buffers != (Quantum *) NULL)
1620         buffers=(Quantum *) RelinquishMagickMemory(buffers);
1621       if (pixels != (Quantum *) NULL)
1622         pixels=(Quantum *) RelinquishMagickMemory(pixels);
1623       despeckle_image=DestroyImage(despeckle_image);
1624       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1625     }
1626   /*
1627     Reduce speckle in the image.
1628   */
1629   status=MagickTrue;
1630   image_view=AcquireCacheView(image);
1631   despeckle_view=AcquireCacheView(despeckle_image);
1632   for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1633   {
1634     PixelChannel
1635        channel;
1636
1637     PixelTrait
1638       despeckle_traits,
1639       traits;
1640
1641     register Quantum
1642       *buffer,
1643       *pixel;
1644
1645     register ssize_t
1646       k,
1647       x;
1648
1649     ssize_t
1650       j,
1651       y;
1652
1653     if (status == MagickFalse)
1654       continue;
1655     traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
1656     channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
1657     despeckle_traits=GetPixelChannelMapTraits(despeckle_image,channel);
1658     if ((traits == UndefinedPixelTrait) ||
1659         (despeckle_traits == UndefinedPixelTrait))
1660       continue;
1661     if ((despeckle_traits & CopyPixelTrait) != 0)
1662       continue;
1663     pixel=pixels;
1664     (void) ResetMagickMemory(pixel,0,length*sizeof(*pixel));
1665     buffer=buffers;
1666     j=(ssize_t) image->columns+2;
1667     for (y=0; y < (ssize_t) image->rows; y++)
1668     {
1669       register const Quantum
1670         *restrict p;
1671
1672       p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1673       if (p == (const Quantum *) NULL)
1674         {
1675           status=MagickFalse;
1676           continue;
1677         }
1678       j++;
1679       for (x=0; x < (ssize_t) image->columns; x++)
1680       {
1681         pixel[j++]=p[i];
1682         p+=GetPixelChannels(image);
1683       }
1684       j++;
1685     }
1686     (void) ResetMagickMemory(buffer,0,length*sizeof(*buffer));
1687     for (k=0; k < 4; k++)
1688     {
1689       Hull(X[k],Y[k],image->columns,image->rows,pixel,buffer,1);
1690       Hull(-X[k],-Y[k],image->columns,image->rows,pixel,buffer,1);
1691       Hull(-X[k],-Y[k],image->columns,image->rows,pixel,buffer,-1);
1692       Hull(X[k],Y[k],image->columns,image->rows,pixel,buffer,-1);
1693     }
1694     j=(ssize_t) image->columns+2;
1695     for (y=0; y < (ssize_t) image->rows; y++)
1696     {
1697       MagickBooleanType
1698         sync;
1699
1700       register Quantum
1701         *restrict q;
1702
1703       q=GetCacheViewAuthenticPixels(despeckle_view,0,y,despeckle_image->columns,
1704         1,exception);
1705       if (q == (Quantum *) NULL)
1706         {
1707           status=MagickFalse;
1708           continue;
1709         }
1710       j++;
1711       for (x=0; x < (ssize_t) image->columns; x++)
1712       {
1713         q[channel]=pixel[j++];
1714         q+=GetPixelChannels(despeckle_image);
1715       }
1716       sync=SyncCacheViewAuthenticPixels(despeckle_view,exception);
1717       if (sync == MagickFalse)
1718         status=MagickFalse;
1719       j++;
1720     }
1721     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1722       {
1723         MagickBooleanType
1724           proceed;
1725
1726         proceed=SetImageProgress(image,DespeckleImageTag,(MagickOffsetType) i,
1727           GetPixelChannels(image));
1728         if (proceed == MagickFalse)
1729           status=MagickFalse;
1730       }
1731   }
1732   despeckle_view=DestroyCacheView(despeckle_view);
1733   image_view=DestroyCacheView(image_view);
1734   buffers=(Quantum *) RelinquishMagickMemory(buffers);
1735   pixels=(Quantum *) RelinquishMagickMemory(pixels);
1736   despeckle_image->type=image->type;
1737   if (status == MagickFalse)
1738     despeckle_image=DestroyImage(despeckle_image);
1739   return(despeckle_image);
1740 }
1741 \f
1742 /*
1743 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1744 %                                                                             %
1745 %                                                                             %
1746 %                                                                             %
1747 %     E d g e I m a g e                                                       %
1748 %                                                                             %
1749 %                                                                             %
1750 %                                                                             %
1751 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1752 %
1753 %  EdgeImage() finds edges in an image.  Radius defines the radius of the
1754 %  convolution filter.  Use a radius of 0 and EdgeImage() selects a suitable
1755 %  radius for you.
1756 %
1757 %  The format of the EdgeImage method is:
1758 %
1759 %      Image *EdgeImage(const Image *image,const double radius,
1760 %        const double sigma,ExceptionInfo *exception)
1761 %
1762 %  A description of each parameter follows:
1763 %
1764 %    o image: the image.
1765 %
1766 %    o radius: the radius of the pixel neighborhood.
1767 %
1768 %    o sigma: the standard deviation of the Gaussian, in pixels.
1769 %
1770 %    o exception: return any errors or warnings in this structure.
1771 %
1772 */
1773 MagickExport Image *EdgeImage(const Image *image,const double radius,
1774   const double sigma,ExceptionInfo *exception)
1775 {
1776   Image
1777     *edge_image;
1778
1779   KernelInfo
1780     *kernel_info;
1781
1782   register ssize_t
1783     i;
1784
1785   size_t
1786     width;
1787
1788   ssize_t
1789     j,
1790     u,
1791     v;
1792
1793   assert(image != (const Image *) NULL);
1794   assert(image->signature == MagickSignature);
1795   if (image->debug != MagickFalse)
1796     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1797   assert(exception != (ExceptionInfo *) NULL);
1798   assert(exception->signature == MagickSignature);
1799   width=GetOptimalKernelWidth2D(radius,sigma);
1800   kernel_info=AcquireKernelInfo((const char *) NULL);
1801   if (kernel_info == (KernelInfo *) NULL)
1802     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1803   kernel_info->width=width;
1804   kernel_info->height=width;
1805   kernel_info->values=(double *) AcquireAlignedMemory(kernel_info->width,
1806     kernel_info->width*sizeof(*kernel_info->values));
1807   if (kernel_info->values == (double *) NULL)
1808     {
1809       kernel_info=DestroyKernelInfo(kernel_info);
1810       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1811     }
1812   j=(ssize_t) kernel_info->width/2;
1813   i=0;
1814   for (v=(-j); v <= j; v++)
1815   {
1816     for (u=(-j); u <= j; u++)
1817     {
1818       kernel_info->values[i]=(-1.0);
1819       i++;
1820     }
1821   }
1822   kernel_info->values[i/2]=(double) (width*width-1.0);
1823   kernel_info->bias=image->bias;
1824   edge_image=ConvolveImage(image,kernel_info,exception);
1825   kernel_info=DestroyKernelInfo(kernel_info);
1826   return(edge_image);
1827 }
1828 \f
1829 /*
1830 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1831 %                                                                             %
1832 %                                                                             %
1833 %                                                                             %
1834 %     E m b o s s I m a g e                                                   %
1835 %                                                                             %
1836 %                                                                             %
1837 %                                                                             %
1838 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1839 %
1840 %  EmbossImage() returns a grayscale image with a three-dimensional effect.
1841 %  We convolve the image with a Gaussian operator of the given radius and
1842 %  standard deviation (sigma).  For reasonable results, radius should be
1843 %  larger than sigma.  Use a radius of 0 and Emboss() selects a suitable
1844 %  radius for you.
1845 %
1846 %  The format of the EmbossImage method is:
1847 %
1848 %      Image *EmbossImage(const Image *image,const double radius,
1849 %        const double sigma,ExceptionInfo *exception)
1850 %
1851 %  A description of each parameter follows:
1852 %
1853 %    o image: the image.
1854 %
1855 %    o radius: the radius of the pixel neighborhood.
1856 %
1857 %    o sigma: the standard deviation of the Gaussian, in pixels.
1858 %
1859 %    o exception: return any errors or warnings in this structure.
1860 %
1861 */
1862 MagickExport Image *EmbossImage(const Image *image,const double radius,
1863   const double sigma,ExceptionInfo *exception)
1864 {
1865   Image
1866     *emboss_image;
1867
1868   KernelInfo
1869     *kernel_info;
1870
1871   register ssize_t
1872     i;
1873
1874   size_t
1875     width;
1876
1877   ssize_t
1878     j,
1879     k,
1880     u,
1881     v;
1882
1883   assert(image != (const Image *) NULL);
1884   assert(image->signature == MagickSignature);
1885   if (image->debug != MagickFalse)
1886     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1887   assert(exception != (ExceptionInfo *) NULL);
1888   assert(exception->signature == MagickSignature);
1889   width=GetOptimalKernelWidth2D(radius,sigma);
1890   kernel_info=AcquireKernelInfo((const char *) NULL);
1891   if (kernel_info == (KernelInfo *) NULL)
1892     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1893   kernel_info->width=width;
1894   kernel_info->height=width;
1895   kernel_info->values=(double *) AcquireAlignedMemory(kernel_info->width,
1896     kernel_info->width*sizeof(*kernel_info->values));
1897   if (kernel_info->values == (double *) NULL)
1898     {
1899       kernel_info=DestroyKernelInfo(kernel_info);
1900       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1901     }
1902   j=(ssize_t) kernel_info->width/2;
1903   k=j;
1904   i=0;
1905   for (v=(-j); v <= j; v++)
1906   {
1907     for (u=(-j); u <= j; u++)
1908     {
1909       kernel_info->values[i]=(double) (((u < 0) || (v < 0) ? -8.0 : 8.0)*
1910         exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
1911         (2.0*MagickPI*MagickSigma*MagickSigma));
1912       if (u != k)
1913         kernel_info->values[i]=0.0;
1914       i++;
1915     }
1916     k--;
1917   }
1918   kernel_info->bias=image->bias;
1919   emboss_image=ConvolveImage(image,kernel_info,exception);
1920   kernel_info=DestroyKernelInfo(kernel_info);
1921   if (emboss_image != (Image *) NULL)
1922     (void) EqualizeImage(emboss_image,exception);
1923   return(emboss_image);
1924 }
1925 \f
1926 /*
1927 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1928 %                                                                             %
1929 %                                                                             %
1930 %                                                                             %
1931 %     G a u s s i a n B l u r I m a g e                                       %
1932 %                                                                             %
1933 %                                                                             %
1934 %                                                                             %
1935 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1936 %
1937 %  GaussianBlurImage() blurs an image.  We convolve the image with a
1938 %  Gaussian operator of the given radius and standard deviation (sigma).
1939 %  For reasonable results, the radius should be larger than sigma.  Use a
1940 %  radius of 0 and GaussianBlurImage() selects a suitable radius for you
1941 %
1942 %  The format of the GaussianBlurImage method is:
1943 %
1944 %      Image *GaussianBlurImage(const Image *image,onst double radius,
1945 %        const double sigma,const double bias,ExceptionInfo *exception)
1946 %
1947 %  A description of each parameter follows:
1948 %
1949 %    o image: the image.
1950 %
1951 %    o radius: the radius of the Gaussian, in pixels, not counting the center
1952 %      pixel.
1953 %
1954 %    o sigma: the standard deviation of the Gaussian, in pixels.
1955 %
1956 %    o bias: the bias.
1957 %
1958 %    o exception: return any errors or warnings in this structure.
1959 %
1960 */
1961 MagickExport Image *GaussianBlurImage(const Image *image,const double radius,
1962   const double sigma,const double bias,ExceptionInfo *exception)
1963 {
1964   Image
1965     *blur_image;
1966
1967   KernelInfo
1968     *kernel_info;
1969
1970   register ssize_t
1971     i;
1972
1973   size_t
1974     width;
1975
1976   ssize_t
1977     j,
1978     u,
1979     v;
1980
1981   assert(image != (const Image *) NULL);
1982   assert(image->signature == MagickSignature);
1983   if (image->debug != MagickFalse)
1984     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1985   assert(exception != (ExceptionInfo *) NULL);
1986   assert(exception->signature == MagickSignature);
1987   width=GetOptimalKernelWidth2D(radius,sigma);
1988   kernel_info=AcquireKernelInfo((const char *) NULL);
1989   if (kernel_info == (KernelInfo *) NULL)
1990     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1991   (void) ResetMagickMemory(kernel_info,0,sizeof(*kernel_info));
1992   kernel_info->width=width;
1993   kernel_info->height=width;
1994   kernel_info->bias=bias;
1995   kernel_info->signature=MagickSignature;
1996   kernel_info->values=(double *) AcquireAlignedMemory(kernel_info->width,
1997     kernel_info->width*sizeof(*kernel_info->values));
1998   if (kernel_info->values == (double *) NULL)
1999     {
2000       kernel_info=DestroyKernelInfo(kernel_info);
2001       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2002     }
2003   j=(ssize_t) kernel_info->width/2;
2004   i=0;
2005   for (v=(-j); v <= j; v++)
2006   {
2007     for (u=(-j); u <= j; u++)
2008     {
2009       kernel_info->values[i]=(double) (exp(-((double) u*u+v*v)/(2.0*
2010         MagickSigma*MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
2011       i++;
2012     }
2013   }
2014   blur_image=ConvolveImage(image,kernel_info,exception);
2015   kernel_info=DestroyKernelInfo(kernel_info);
2016   return(blur_image);
2017 }
2018 \f
2019 /*
2020 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2021 %                                                                             %
2022 %                                                                             %
2023 %                                                                             %
2024 %     M o t i o n B l u r I m a g e                                           %
2025 %                                                                             %
2026 %                                                                             %
2027 %                                                                             %
2028 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2029 %
2030 %  MotionBlurImage() simulates motion blur.  We convolve the image with a
2031 %  Gaussian operator of the given radius and standard deviation (sigma).
2032 %  For reasonable results, radius should be larger than sigma.  Use a
2033 %  radius of 0 and MotionBlurImage() selects a suitable radius for you.
2034 %  Angle gives the angle of the blurring motion.
2035 %
2036 %  Andrew Protano contributed this effect.
2037 %
2038 %  The format of the MotionBlurImage method is:
2039 %
2040 %    Image *MotionBlurImage(const Image *image,const double radius,
2041 %      const double sigma,const double angle,const double bias,
2042 %      ExceptionInfo *exception)
2043 %
2044 %  A description of each parameter follows:
2045 %
2046 %    o image: the image.
2047 %
2048 %    o radius: the radius of the Gaussian, in pixels, not counting
2049 %      the center pixel.
2050 %
2051 %    o sigma: the standard deviation of the Gaussian, in pixels.
2052 %
2053 %    o angle: Apply the effect along this angle.
2054 %
2055 %    o bias: the bias.
2056 %
2057 %    o exception: return any errors or warnings in this structure.
2058 %
2059 */
2060
2061 static double *GetMotionBlurKernel(const size_t width,const double sigma)
2062 {
2063   double
2064     *kernel,
2065     normalize;
2066
2067   register ssize_t
2068     i;
2069
2070   /*
2071    Generate a 1-D convolution kernel.
2072   */
2073   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2074   kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
2075   if (kernel == (double *) NULL)
2076     return(kernel);
2077   normalize=0.0;
2078   for (i=0; i < (ssize_t) width; i++)
2079   {
2080     kernel[i]=(double) (exp((-((double) i*i)/(double) (2.0*MagickSigma*
2081       MagickSigma)))/(MagickSQ2PI*MagickSigma));
2082     normalize+=kernel[i];
2083   }
2084   for (i=0; i < (ssize_t) width; i++)
2085     kernel[i]/=normalize;
2086   return(kernel);
2087 }
2088
2089 MagickExport Image *MotionBlurImage(const Image *image,const double radius,
2090   const double sigma,const double angle,const double bias,
2091   ExceptionInfo *exception)
2092 {
2093   CacheView
2094     *blur_view,
2095     *image_view;
2096
2097   double
2098     *kernel;
2099
2100   Image
2101     *blur_image;
2102
2103   MagickBooleanType
2104     status;
2105
2106   MagickOffsetType
2107     progress;
2108
2109   OffsetInfo
2110     *offset;
2111
2112   PointInfo
2113     point;
2114
2115   register ssize_t
2116     i;
2117
2118   size_t
2119     width;
2120
2121   ssize_t
2122     y;
2123
2124   assert(image != (Image *) NULL);
2125   assert(image->signature == MagickSignature);
2126   if (image->debug != MagickFalse)
2127     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2128   assert(exception != (ExceptionInfo *) NULL);
2129   width=GetOptimalKernelWidth1D(radius,sigma);
2130   kernel=GetMotionBlurKernel(width,sigma);
2131   if (kernel == (double *) NULL)
2132     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2133   offset=(OffsetInfo *) AcquireQuantumMemory(width,sizeof(*offset));
2134   if (offset == (OffsetInfo *) NULL)
2135     {
2136       kernel=(double *) RelinquishMagickMemory(kernel);
2137       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2138     }
2139   blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
2140   if (blur_image == (Image *) NULL)
2141     {
2142       kernel=(double *) RelinquishMagickMemory(kernel);
2143       offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2144       return((Image *) NULL);
2145     }
2146   if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
2147     {
2148       kernel=(double *) RelinquishMagickMemory(kernel);
2149       offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2150       blur_image=DestroyImage(blur_image);
2151       return((Image *) NULL);
2152     }
2153   point.x=(double) width*sin(DegreesToRadians(angle));
2154   point.y=(double) width*cos(DegreesToRadians(angle));
2155   for (i=0; i < (ssize_t) width; i++)
2156   {
2157     offset[i].x=(ssize_t) ceil((double) (i*point.y)/hypot(point.x,point.y)-0.5);
2158     offset[i].y=(ssize_t) ceil((double) (i*point.x)/hypot(point.x,point.y)-0.5);
2159   }
2160   /*
2161     Motion blur image.
2162   */
2163   status=MagickTrue;
2164   progress=0;
2165   image_view=AcquireCacheView(image);
2166   blur_view=AcquireCacheView(blur_image);
2167 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2168   #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
2169 #endif
2170   for (y=0; y < (ssize_t) image->rows; y++)
2171   {
2172     register const Quantum
2173       *restrict p;
2174
2175     register Quantum
2176       *restrict q;
2177
2178     register ssize_t
2179       x;
2180
2181     if (status == MagickFalse)
2182       continue;
2183     p=GetCacheViewVirtualPixels(blur_view,0,y,image->columns,1,exception);
2184     q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
2185       exception);
2186     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2187       {
2188         status=MagickFalse;
2189         continue;
2190       }
2191     for (x=0; x < (ssize_t) image->columns; x++)
2192     {
2193       register ssize_t
2194         i;
2195
2196       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2197       {
2198         MagickRealType
2199           alpha,
2200           gamma,
2201           pixel;
2202
2203         PixelChannel
2204           channel;
2205
2206         PixelTrait
2207           blur_traits,
2208           traits;
2209
2210         register const Quantum
2211           *restrict r;
2212
2213         register double
2214           *restrict k;
2215
2216         register ssize_t
2217           j;
2218
2219         traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
2220         channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
2221         blur_traits=GetPixelChannelMapTraits(blur_image,channel);
2222         if ((traits == UndefinedPixelTrait) ||
2223             (blur_traits == UndefinedPixelTrait))
2224           continue;
2225         if ((blur_traits & CopyPixelTrait) != 0)
2226           {
2227             q[channel]=p[i];
2228             continue;
2229           }
2230         k=kernel;
2231         pixel=bias;
2232         if ((blur_traits & BlendPixelTrait) == 0)
2233           {
2234             for (j=0; j < (ssize_t) width; j++)
2235             {
2236               r=GetCacheViewVirtualPixels(image_view,x+offset[j].x,y+
2237                 offset[j].y,1,1,exception);
2238               if (r == (const Quantum *) NULL)
2239                 {
2240                   status=MagickFalse;
2241                   continue;
2242                 }
2243               pixel+=(*k)*r[i];
2244               k++;
2245             }
2246             q[channel]=ClampToQuantum(pixel);
2247             continue;
2248           }
2249         alpha=0.0;
2250         gamma=0.0;
2251         for (j=0; j < (ssize_t) width; j++)
2252         {
2253           r=GetCacheViewVirtualPixels(image_view,x+offset[j].x,y+offset[j].y,1,
2254             1,exception);
2255           if (r == (const Quantum *) NULL)
2256             {
2257               status=MagickFalse;
2258               continue;
2259             }
2260           alpha=(MagickRealType) (QuantumScale*GetPixelAlpha(image,r));
2261           pixel+=(*k)*alpha*r[i];
2262           gamma+=(*k)*alpha;
2263           k++;
2264         }
2265         gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2266         q[channel]=ClampToQuantum(gamma*pixel);
2267       }
2268       p+=GetPixelChannels(image);
2269       q+=GetPixelChannels(blur_image);
2270     }
2271     if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
2272       status=MagickFalse;
2273     if (image->progress_monitor != (MagickProgressMonitor) NULL)
2274       {
2275         MagickBooleanType
2276           proceed;
2277
2278 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2279   #pragma omp critical (MagickCore_MotionBlurImage)
2280 #endif
2281         proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
2282         if (proceed == MagickFalse)
2283           status=MagickFalse;
2284       }
2285   }
2286   blur_view=DestroyCacheView(blur_view);
2287   image_view=DestroyCacheView(image_view);
2288   kernel=(double *) RelinquishMagickMemory(kernel);
2289   offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2290   if (status == MagickFalse)
2291     blur_image=DestroyImage(blur_image);
2292   return(blur_image);
2293 }
2294 \f
2295 /*
2296 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2297 %                                                                             %
2298 %                                                                             %
2299 %                                                                             %
2300 %     P r e v i e w I m a g e                                                 %
2301 %                                                                             %
2302 %                                                                             %
2303 %                                                                             %
2304 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2305 %
2306 %  PreviewImage() tiles 9 thumbnails of the specified image with an image
2307 %  processing operation applied with varying parameters.  This may be helpful
2308 %  pin-pointing an appropriate parameter for a particular image processing
2309 %  operation.
2310 %
2311 %  The format of the PreviewImages method is:
2312 %
2313 %      Image *PreviewImages(const Image *image,const PreviewType preview,
2314 %        ExceptionInfo *exception)
2315 %
2316 %  A description of each parameter follows:
2317 %
2318 %    o image: the image.
2319 %
2320 %    o preview: the image processing operation.
2321 %
2322 %    o exception: return any errors or warnings in this structure.
2323 %
2324 */
2325 MagickExport Image *PreviewImage(const Image *image,const PreviewType preview,
2326   ExceptionInfo *exception)
2327 {
2328 #define NumberTiles  9
2329 #define PreviewImageTag  "Preview/Image"
2330 #define DefaultPreviewGeometry  "204x204+10+10"
2331
2332   char
2333     factor[MaxTextExtent],
2334     label[MaxTextExtent];
2335
2336   double
2337     degrees,
2338     gamma,
2339     percentage,
2340     radius,
2341     sigma,
2342     threshold;
2343
2344   Image
2345     *images,
2346     *montage_image,
2347     *preview_image,
2348     *thumbnail;
2349
2350   ImageInfo
2351     *preview_info;
2352
2353   MagickBooleanType
2354     proceed;
2355
2356   MontageInfo
2357     *montage_info;
2358
2359   QuantizeInfo
2360     quantize_info;
2361
2362   RectangleInfo
2363     geometry;
2364
2365   register ssize_t
2366     i,
2367     x;
2368
2369   size_t
2370     colors;
2371
2372   ssize_t
2373     y;
2374
2375   /*
2376     Open output image file.
2377   */
2378   assert(image != (Image *) NULL);
2379   assert(image->signature == MagickSignature);
2380   if (image->debug != MagickFalse)
2381     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2382   colors=2;
2383   degrees=0.0;
2384   gamma=(-0.2f);
2385   preview_info=AcquireImageInfo();
2386   SetGeometry(image,&geometry);
2387   (void) ParseMetaGeometry(DefaultPreviewGeometry,&geometry.x,&geometry.y,
2388     &geometry.width,&geometry.height);
2389   images=NewImageList();
2390   percentage=12.5;
2391   GetQuantizeInfo(&quantize_info);
2392   radius=0.0;
2393   sigma=1.0;
2394   threshold=0.0;
2395   x=0;
2396   y=0;
2397   for (i=0; i < NumberTiles; i++)
2398   {
2399     thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
2400     if (thumbnail == (Image *) NULL)
2401       break;
2402     (void) SetImageProgressMonitor(thumbnail,(MagickProgressMonitor) NULL,
2403       (void *) NULL);
2404     (void) SetImageProperty(thumbnail,"label",DefaultTileLabel);
2405     if (i == (NumberTiles/2))
2406       {
2407         (void) QueryColorDatabase("#dfdfdf",&thumbnail->matte_color,exception);
2408         AppendImageToList(&images,thumbnail);
2409         continue;
2410       }
2411     switch (preview)
2412     {
2413       case RotatePreview:
2414       {
2415         degrees+=45.0;
2416         preview_image=RotateImage(thumbnail,degrees,exception);
2417         (void) FormatLocaleString(label,MaxTextExtent,"rotate %g",degrees);
2418         break;
2419       }
2420       case ShearPreview:
2421       {
2422         degrees+=5.0;
2423         preview_image=ShearImage(thumbnail,degrees,degrees,exception);
2424         (void) FormatLocaleString(label,MaxTextExtent,"shear %gx%g",
2425           degrees,2.0*degrees);
2426         break;
2427       }
2428       case RollPreview:
2429       {
2430         x=(ssize_t) ((i+1)*thumbnail->columns)/NumberTiles;
2431         y=(ssize_t) ((i+1)*thumbnail->rows)/NumberTiles;
2432         preview_image=RollImage(thumbnail,x,y,exception);
2433         (void) FormatLocaleString(label,MaxTextExtent,"roll %+.20gx%+.20g",
2434           (double) x,(double) y);
2435         break;
2436       }
2437       case HuePreview:
2438       {
2439         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2440         if (preview_image == (Image *) NULL)
2441           break;
2442         (void) FormatLocaleString(factor,MaxTextExtent,"100,100,%g",
2443           2.0*percentage);
2444         (void) ModulateImage(preview_image,factor,exception);
2445         (void) FormatLocaleString(label,MaxTextExtent,"modulate %s",factor);
2446         break;
2447       }
2448       case SaturationPreview:
2449       {
2450         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2451         if (preview_image == (Image *) NULL)
2452           break;
2453         (void) FormatLocaleString(factor,MaxTextExtent,"100,%g",
2454           2.0*percentage);
2455         (void) ModulateImage(preview_image,factor,exception);
2456         (void) FormatLocaleString(label,MaxTextExtent,"modulate %s",factor);
2457         break;
2458       }
2459       case BrightnessPreview:
2460       {
2461         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2462         if (preview_image == (Image *) NULL)
2463           break;
2464         (void) FormatLocaleString(factor,MaxTextExtent,"%g",2.0*percentage);
2465         (void) ModulateImage(preview_image,factor,exception);
2466         (void) FormatLocaleString(label,MaxTextExtent,"modulate %s",factor);
2467         break;
2468       }
2469       case GammaPreview:
2470       default:
2471       {
2472         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2473         if (preview_image == (Image *) NULL)
2474           break;
2475         gamma+=0.4f;
2476         (void) GammaImage(preview_image,gamma,exception);
2477         (void) FormatLocaleString(label,MaxTextExtent,"gamma %g",gamma);
2478         break;
2479       }
2480       case SpiffPreview:
2481       {
2482         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2483         if (preview_image != (Image *) NULL)
2484           for (x=0; x < i; x++)
2485             (void) ContrastImage(preview_image,MagickTrue,exception);
2486         (void) FormatLocaleString(label,MaxTextExtent,"contrast (%.20g)",
2487           (double) i+1);
2488         break;
2489       }
2490       case DullPreview:
2491       {
2492         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2493         if (preview_image == (Image *) NULL)
2494           break;
2495         for (x=0; x < i; x++)
2496           (void) ContrastImage(preview_image,MagickFalse,exception);
2497         (void) FormatLocaleString(label,MaxTextExtent,"+contrast (%.20g)",
2498           (double) i+1);
2499         break;
2500       }
2501       case GrayscalePreview:
2502       {
2503         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2504         if (preview_image == (Image *) NULL)
2505           break;
2506         colors<<=1;
2507         quantize_info.number_colors=colors;
2508         quantize_info.colorspace=GRAYColorspace;
2509         (void) QuantizeImage(&quantize_info,preview_image,exception);
2510         (void) FormatLocaleString(label,MaxTextExtent,
2511           "-colorspace gray -colors %.20g",(double) colors);
2512         break;
2513       }
2514       case QuantizePreview:
2515       {
2516         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2517         if (preview_image == (Image *) NULL)
2518           break;
2519         colors<<=1;
2520         quantize_info.number_colors=colors;
2521         (void) QuantizeImage(&quantize_info,preview_image,exception);
2522         (void) FormatLocaleString(label,MaxTextExtent,"colors %.20g",(double)
2523           colors);
2524         break;
2525       }
2526       case DespecklePreview:
2527       {
2528         for (x=0; x < (i-1); x++)
2529         {
2530           preview_image=DespeckleImage(thumbnail,exception);
2531           if (preview_image == (Image *) NULL)
2532             break;
2533           thumbnail=DestroyImage(thumbnail);
2534           thumbnail=preview_image;
2535         }
2536         preview_image=DespeckleImage(thumbnail,exception);
2537         if (preview_image == (Image *) NULL)
2538           break;
2539         (void) FormatLocaleString(label,MaxTextExtent,"despeckle (%.20g)",
2540           (double) i+1);
2541         break;
2542       }
2543       case ReduceNoisePreview:
2544       {
2545         preview_image=StatisticImage(thumbnail,NonpeakStatistic,(size_t) radius,
2546           (size_t) radius,exception);
2547         (void) FormatLocaleString(label,MaxTextExtent,"noise %g",radius);
2548         break;
2549       }
2550       case AddNoisePreview:
2551       {
2552         switch ((int) i)
2553         {
2554           case 0:
2555           {
2556             (void) CopyMagickString(factor,"uniform",MaxTextExtent);
2557             break;
2558           }
2559           case 1:
2560           {
2561             (void) CopyMagickString(factor,"gaussian",MaxTextExtent);
2562             break;
2563           }
2564           case 2:
2565           {
2566             (void) CopyMagickString(factor,"multiplicative",MaxTextExtent);
2567             break;
2568           }
2569           case 3:
2570           {
2571             (void) CopyMagickString(factor,"impulse",MaxTextExtent);
2572             break;
2573           }
2574           case 4:
2575           {
2576             (void) CopyMagickString(factor,"laplacian",MaxTextExtent);
2577             break;
2578           }
2579           case 5:
2580           {
2581             (void) CopyMagickString(factor,"Poisson",MaxTextExtent);
2582             break;
2583           }
2584           default:
2585           {
2586             (void) CopyMagickString(thumbnail->magick,"NULL",MaxTextExtent);
2587             break;
2588           }
2589         }
2590         preview_image=StatisticImage(thumbnail,NonpeakStatistic,(size_t) i,
2591           (size_t) i,exception);
2592         (void) FormatLocaleString(label,MaxTextExtent,"+noise %s",factor);
2593         break;
2594       }
2595       case SharpenPreview:
2596       {
2597         preview_image=SharpenImage(thumbnail,radius,sigma,image->bias,
2598           exception);
2599         (void) FormatLocaleString(label,MaxTextExtent,"sharpen %gx%g",
2600           radius,sigma);
2601         break;
2602       }
2603       case BlurPreview:
2604       {
2605         preview_image=BlurImage(thumbnail,radius,sigma,image->bias,exception);
2606         (void) FormatLocaleString(label,MaxTextExtent,"blur %gx%g",radius,
2607           sigma);
2608         break;
2609       }
2610       case ThresholdPreview:
2611       {
2612         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2613         if (preview_image == (Image *) NULL)
2614           break;
2615         (void) BilevelImage(thumbnail,
2616           (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
2617         (void) FormatLocaleString(label,MaxTextExtent,"threshold %g",
2618           (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
2619         break;
2620       }
2621       case EdgeDetectPreview:
2622       {
2623         preview_image=EdgeImage(thumbnail,radius,sigma,exception);
2624         (void) FormatLocaleString(label,MaxTextExtent,"edge %g",radius);
2625         break;
2626       }
2627       case SpreadPreview:
2628       {
2629         preview_image=SpreadImage(thumbnail,radius,thumbnail->interpolate,
2630           exception);
2631         (void) FormatLocaleString(label,MaxTextExtent,"spread %g",
2632           radius+0.5);
2633         break;
2634       }
2635       case SolarizePreview:
2636       {
2637         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2638         if (preview_image == (Image *) NULL)
2639           break;
2640         (void) SolarizeImage(preview_image,(double) QuantumRange*
2641           percentage/100.0,exception);
2642         (void) FormatLocaleString(label,MaxTextExtent,"solarize %g",
2643           (QuantumRange*percentage)/100.0);
2644         break;
2645       }
2646       case ShadePreview:
2647       {
2648         degrees+=10.0;
2649         preview_image=ShadeImage(thumbnail,MagickTrue,degrees,degrees,
2650           exception);
2651         (void) FormatLocaleString(label,MaxTextExtent,"shade %gx%g",
2652           degrees,degrees);
2653         break;
2654       }
2655       case RaisePreview:
2656       {
2657         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2658         if (preview_image == (Image *) NULL)
2659           break;
2660         geometry.width=(size_t) (2*i+2);
2661         geometry.height=(size_t) (2*i+2);
2662         geometry.x=i/2;
2663         geometry.y=i/2;
2664         (void) RaiseImage(preview_image,&geometry,MagickTrue,exception);
2665         (void) FormatLocaleString(label,MaxTextExtent,
2666           "raise %.20gx%.20g%+.20g%+.20g",(double) geometry.width,(double)
2667           geometry.height,(double) geometry.x,(double) geometry.y);
2668         break;
2669       }
2670       case SegmentPreview:
2671       {
2672         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2673         if (preview_image == (Image *) NULL)
2674           break;
2675         threshold+=0.4f;
2676         (void) SegmentImage(preview_image,RGBColorspace,MagickFalse,threshold,
2677           threshold,exception);
2678         (void) FormatLocaleString(label,MaxTextExtent,"segment %gx%g",
2679           threshold,threshold);
2680         break;
2681       }
2682       case SwirlPreview:
2683       {
2684         preview_image=SwirlImage(thumbnail,degrees,image->interpolate,
2685           exception);
2686         (void) FormatLocaleString(label,MaxTextExtent,"swirl %g",degrees);
2687         degrees+=45.0;
2688         break;
2689       }
2690       case ImplodePreview:
2691       {
2692         degrees+=0.1f;
2693         preview_image=ImplodeImage(thumbnail,degrees,image->interpolate,
2694           exception);
2695         (void) FormatLocaleString(label,MaxTextExtent,"implode %g",degrees);
2696         break;
2697       }
2698       case WavePreview:
2699       {
2700         degrees+=5.0f;
2701         preview_image=WaveImage(thumbnail,0.5*degrees,2.0*degrees,
2702           image->interpolate,exception);
2703         (void) FormatLocaleString(label,MaxTextExtent,"wave %gx%g",
2704           0.5*degrees,2.0*degrees);
2705         break;
2706       }
2707       case OilPaintPreview:
2708       {
2709         preview_image=OilPaintImage(thumbnail,(double) radius,(double) sigma,
2710           exception);
2711         (void) FormatLocaleString(label,MaxTextExtent,"charcoal %gx%g",
2712           radius,sigma);
2713         break;
2714       }
2715       case CharcoalDrawingPreview:
2716       {
2717         preview_image=CharcoalImage(thumbnail,(double) radius,(double) sigma,
2718           image->bias,exception);
2719         (void) FormatLocaleString(label,MaxTextExtent,"charcoal %gx%g",
2720           radius,sigma);
2721         break;
2722       }
2723       case JPEGPreview:
2724       {
2725         char
2726           filename[MaxTextExtent];
2727
2728         int
2729           file;
2730
2731         MagickBooleanType
2732           status;
2733
2734         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2735         if (preview_image == (Image *) NULL)
2736           break;
2737         preview_info->quality=(size_t) percentage;
2738         (void) FormatLocaleString(factor,MaxTextExtent,"%.20g",(double)
2739           preview_info->quality);
2740         file=AcquireUniqueFileResource(filename);
2741         if (file != -1)
2742           file=close(file)-1;
2743         (void) FormatLocaleString(preview_image->filename,MaxTextExtent,
2744           "jpeg:%s",filename);
2745         status=WriteImage(preview_info,preview_image,exception);
2746         if (status != MagickFalse)
2747           {
2748             Image
2749               *quality_image;
2750
2751             (void) CopyMagickString(preview_info->filename,
2752               preview_image->filename,MaxTextExtent);
2753             quality_image=ReadImage(preview_info,exception);
2754             if (quality_image != (Image *) NULL)
2755               {
2756                 preview_image=DestroyImage(preview_image);
2757                 preview_image=quality_image;
2758               }
2759           }
2760         (void) RelinquishUniqueFileResource(preview_image->filename);
2761         if ((GetBlobSize(preview_image)/1024) >= 1024)
2762           (void) FormatLocaleString(label,MaxTextExtent,"quality %s\n%gmb ",
2763             factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
2764             1024.0/1024.0);
2765         else
2766           if (GetBlobSize(preview_image) >= 1024)
2767             (void) FormatLocaleString(label,MaxTextExtent,
2768               "quality %s\n%gkb ",factor,(double) ((MagickOffsetType)
2769               GetBlobSize(preview_image))/1024.0);
2770           else
2771             (void) FormatLocaleString(label,MaxTextExtent,"quality %s\n%.20gb ",
2772               factor,(double) ((MagickOffsetType) GetBlobSize(thumbnail)));
2773         break;
2774       }
2775     }
2776     thumbnail=DestroyImage(thumbnail);
2777     percentage+=12.5;
2778     radius+=0.5;
2779     sigma+=0.25;
2780     if (preview_image == (Image *) NULL)
2781       break;
2782     (void) DeleteImageProperty(preview_image,"label");
2783     (void) SetImageProperty(preview_image,"label",label);
2784     AppendImageToList(&images,preview_image);
2785     proceed=SetImageProgress(image,PreviewImageTag,(MagickOffsetType) i,
2786       NumberTiles);
2787     if (proceed == MagickFalse)
2788       break;
2789   }
2790   if (images == (Image *) NULL)
2791     {
2792       preview_info=DestroyImageInfo(preview_info);
2793       return((Image *) NULL);
2794     }
2795   /*
2796     Create the montage.
2797   */
2798   montage_info=CloneMontageInfo(preview_info,(MontageInfo *) NULL);
2799   (void) CopyMagickString(montage_info->filename,image->filename,MaxTextExtent);
2800   montage_info->shadow=MagickTrue;
2801   (void) CloneString(&montage_info->tile,"3x3");
2802   (void) CloneString(&montage_info->geometry,DefaultPreviewGeometry);
2803   (void) CloneString(&montage_info->frame,DefaultTileFrame);
2804   montage_image=MontageImages(images,montage_info,exception);
2805   montage_info=DestroyMontageInfo(montage_info);
2806   images=DestroyImageList(images);
2807   if (montage_image == (Image *) NULL)
2808     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2809   if (montage_image->montage != (char *) NULL)
2810     {
2811       /*
2812         Free image directory.
2813       */
2814       montage_image->montage=(char *) RelinquishMagickMemory(
2815         montage_image->montage);
2816       if (image->directory != (char *) NULL)
2817         montage_image->directory=(char *) RelinquishMagickMemory(
2818           montage_image->directory);
2819     }
2820   preview_info=DestroyImageInfo(preview_info);
2821   return(montage_image);
2822 }
2823 \f
2824 /*
2825 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2826 %                                                                             %
2827 %                                                                             %
2828 %                                                                             %
2829 %     R a d i a l B l u r I m a g e                                           %
2830 %                                                                             %
2831 %                                                                             %
2832 %                                                                             %
2833 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2834 %
2835 %  RadialBlurImage() applies a radial blur to the image.
2836 %
2837 %  Andrew Protano contributed this effect.
2838 %
2839 %  The format of the RadialBlurImage method is:
2840 %
2841 %    Image *RadialBlurImage(const Image *image,const double angle,
2842 %      const double blur,ExceptionInfo *exception)
2843 %
2844 %  A description of each parameter follows:
2845 %
2846 %    o image: the image.
2847 %
2848 %    o angle: the angle of the radial blur.
2849 %
2850 %    o blur: the blur.
2851 %
2852 %    o exception: return any errors or warnings in this structure.
2853 %
2854 */
2855 MagickExport Image *RadialBlurImage(const Image *image,
2856   const double angle,const double bias,ExceptionInfo *exception)
2857 {
2858   CacheView
2859     *blur_view,
2860     *image_view;
2861
2862   Image
2863     *blur_image;
2864
2865   MagickBooleanType
2866     status;
2867
2868   MagickOffsetType
2869     progress;
2870
2871   MagickRealType
2872     blur_radius,
2873     *cos_theta,
2874     offset,
2875     *sin_theta,
2876     theta;
2877
2878   PointInfo
2879     blur_center;
2880
2881   register ssize_t
2882     i;
2883
2884   size_t
2885     n;
2886
2887   ssize_t
2888     y;
2889
2890   /*
2891     Allocate blur image.
2892   */
2893   assert(image != (Image *) NULL);
2894   assert(image->signature == MagickSignature);
2895   if (image->debug != MagickFalse)
2896     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2897   assert(exception != (ExceptionInfo *) NULL);
2898   assert(exception->signature == MagickSignature);
2899   blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
2900   if (blur_image == (Image *) NULL)
2901     return((Image *) NULL);
2902   if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
2903     {
2904       blur_image=DestroyImage(blur_image);
2905       return((Image *) NULL);
2906     }
2907   blur_center.x=(double) image->columns/2.0;
2908   blur_center.y=(double) image->rows/2.0;
2909   blur_radius=hypot(blur_center.x,blur_center.y);
2910   n=(size_t) fabs(4.0*DegreesToRadians(angle)*sqrt((double) blur_radius)+2UL);
2911   theta=DegreesToRadians(angle)/(MagickRealType) (n-1);
2912   cos_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
2913     sizeof(*cos_theta));
2914   sin_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
2915     sizeof(*sin_theta));
2916   if ((cos_theta == (MagickRealType *) NULL) ||
2917       (sin_theta == (MagickRealType *) NULL))
2918     {
2919       blur_image=DestroyImage(blur_image);
2920       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2921     }
2922   offset=theta*(MagickRealType) (n-1)/2.0;
2923   for (i=0; i < (ssize_t) n; i++)
2924   {
2925     cos_theta[i]=cos((double) (theta*i-offset));
2926     sin_theta[i]=sin((double) (theta*i-offset));
2927   }
2928   /*
2929     Radial blur image.
2930   */
2931   status=MagickTrue;
2932   progress=0;
2933   image_view=AcquireCacheView(image);
2934   blur_view=AcquireCacheView(blur_image);
2935 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2936   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2937 #endif
2938   for (y=0; y < (ssize_t) image->rows; y++)
2939   {
2940     register const Quantum
2941       *restrict p;
2942
2943     register Quantum
2944       *restrict q;
2945
2946     register ssize_t
2947       x;
2948
2949     if (status == MagickFalse)
2950       continue;
2951     p=GetCacheViewVirtualPixels(blur_view,0,y,image->columns,1,exception);
2952     q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
2953       exception);
2954     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2955       {
2956         status=MagickFalse;
2957         continue;
2958       }
2959     for (x=0; x < (ssize_t) image->columns; x++)
2960     {
2961       MagickRealType
2962         radius;
2963
2964       PointInfo
2965         center;
2966
2967       register ssize_t
2968         i;
2969
2970       size_t
2971         step;
2972
2973       center.x=(double) x-blur_center.x;
2974       center.y=(double) y-blur_center.y;
2975       radius=hypot((double) center.x,center.y);
2976       if (radius == 0)
2977         step=1;
2978       else
2979         {
2980           step=(size_t) (blur_radius/radius);
2981           if (step == 0)
2982             step=1;
2983           else
2984             if (step >= n)
2985               step=n-1;
2986         }
2987       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2988       {
2989         MagickRealType
2990           gamma,
2991           pixel;
2992
2993         PixelChannel
2994           channel;
2995
2996         PixelTrait
2997           blur_traits,
2998           traits;
2999
3000         register const Quantum
3001           *restrict r;
3002
3003         register ssize_t
3004           j;
3005
3006         traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
3007         channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
3008         blur_traits=GetPixelChannelMapTraits(blur_image,channel);
3009         if ((traits == UndefinedPixelTrait) ||
3010             (blur_traits == UndefinedPixelTrait))
3011           continue;
3012         if ((blur_traits & CopyPixelTrait) != 0)
3013           {
3014             q[channel]=p[i];
3015             continue;
3016           }
3017         gamma=0.0;
3018         pixel=bias;
3019         if ((blur_traits & BlendPixelTrait) == 0)
3020           {
3021             for (j=0; j < (ssize_t) n; j+=(ssize_t) step)
3022             {
3023               r=GetCacheViewVirtualPixels(image_view, (ssize_t) (blur_center.x+
3024                 center.x*cos_theta[j]-center.y*sin_theta[j]+0.5),(ssize_t)
3025                 (blur_center.y+center.x*sin_theta[j]+center.y*cos_theta[j]+0.5),
3026                 1,1,exception);
3027               if (r == (const Quantum *) NULL)
3028                 {
3029                   status=MagickFalse;
3030                   continue;
3031                 }
3032               pixel+=r[i];
3033               gamma++;
3034             }
3035             gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
3036             q[channel]=ClampToQuantum(gamma*pixel);
3037             continue;
3038           }
3039         for (j=0; j < (ssize_t) n; j+=(ssize_t) step)
3040         {
3041           r=GetCacheViewVirtualPixels(image_view, (ssize_t) (blur_center.x+
3042             center.x*cos_theta[j]-center.y*sin_theta[j]+0.5),(ssize_t)
3043             (blur_center.y+center.x*sin_theta[j]+center.y*cos_theta[j]+0.5),
3044             1,1,exception);
3045           if (r == (const Quantum *) NULL)
3046             {
3047               status=MagickFalse;
3048               continue;
3049             }
3050           pixel+=GetPixelAlpha(image,r)*r[i];
3051           gamma+=GetPixelAlpha(image,r);
3052         }
3053         gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
3054         q[channel]=ClampToQuantum(gamma*pixel);
3055       }
3056       p+=GetPixelChannels(image);
3057       q+=GetPixelChannels(blur_image);
3058     }
3059     if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
3060       status=MagickFalse;
3061     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3062       {
3063         MagickBooleanType
3064           proceed;
3065
3066 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3067   #pragma omp critical (MagickCore_RadialBlurImage)
3068 #endif
3069         proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
3070         if (proceed == MagickFalse)
3071           status=MagickFalse;
3072       }
3073   }
3074   blur_view=DestroyCacheView(blur_view);
3075   image_view=DestroyCacheView(image_view);
3076   cos_theta=(MagickRealType *) RelinquishMagickMemory(cos_theta);
3077   sin_theta=(MagickRealType *) RelinquishMagickMemory(sin_theta);
3078   if (status == MagickFalse)
3079     blur_image=DestroyImage(blur_image);
3080   return(blur_image);
3081 }
3082 \f
3083 /*
3084 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3085 %                                                                             %
3086 %                                                                             %
3087 %                                                                             %
3088 %     S e l e c t i v e B l u r I m a g e                                     %
3089 %                                                                             %
3090 %                                                                             %
3091 %                                                                             %
3092 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3093 %
3094 %  SelectiveBlurImage() selectively blur pixels within a contrast threshold.
3095 %  It is similar to the unsharpen mask that sharpens everything with contrast
3096 %  above a certain threshold.
3097 %
3098 %  The format of the SelectiveBlurImage method is:
3099 %
3100 %      Image *SelectiveBlurImage(const Image *image,const double radius,
3101 %        const double sigma,const double threshold,const double bias,
3102 %        ExceptionInfo *exception)
3103 %
3104 %  A description of each parameter follows:
3105 %
3106 %    o image: the image.
3107 %
3108 %    o radius: the radius of the Gaussian, in pixels, not counting the center
3109 %      pixel.
3110 %
3111 %    o sigma: the standard deviation of the Gaussian, in pixels.
3112 %
3113 %    o threshold: only pixels within this contrast threshold are included
3114 %      in the blur operation.
3115 %
3116 %    o bias: the bias.
3117 %
3118 %    o exception: return any errors or warnings in this structure.
3119 %
3120 */
3121 MagickExport Image *SelectiveBlurImage(const Image *image,
3122   const double radius,const double sigma,const double threshold,
3123   const double bias,ExceptionInfo *exception)
3124 {
3125 #define SelectiveBlurImageTag  "SelectiveBlur/Image"
3126
3127   CacheView
3128     *blur_view,
3129     *image_view;
3130
3131   double
3132     *kernel;
3133
3134   Image
3135     *blur_image;
3136
3137   MagickBooleanType
3138     status;
3139
3140   MagickOffsetType
3141     progress;
3142
3143   register ssize_t
3144     i;
3145
3146   size_t
3147     center,
3148     width;
3149
3150   ssize_t
3151     j,
3152     u,
3153     v,
3154     y;
3155
3156   /*
3157     Initialize blur image attributes.
3158   */
3159   assert(image != (Image *) NULL);
3160   assert(image->signature == MagickSignature);
3161   if (image->debug != MagickFalse)
3162     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3163   assert(exception != (ExceptionInfo *) NULL);
3164   assert(exception->signature == MagickSignature);
3165   width=GetOptimalKernelWidth1D(radius,sigma);
3166   kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
3167   if (kernel == (double *) NULL)
3168     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3169   j=(ssize_t) width/2;
3170   i=0;
3171   for (v=(-j); v <= j; v++)
3172   {
3173     for (u=(-j); u <= j; u++)
3174       kernel[i++]=(double) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
3175         MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
3176   }
3177   if (image->debug != MagickFalse)
3178     {
3179       char
3180         format[MaxTextExtent],
3181         *message;
3182
3183       register const double
3184         *k;
3185
3186       ssize_t
3187         u,
3188         v;
3189
3190       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
3191         "  SelectiveBlurImage with %.20gx%.20g kernel:",(double) width,(double)
3192         width);
3193       message=AcquireString("");
3194       k=kernel;
3195       for (v=0; v < (ssize_t) width; v++)
3196       {
3197         *message='\0';
3198         (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) v);
3199         (void) ConcatenateString(&message,format);
3200         for (u=0; u < (ssize_t) width; u++)
3201         {
3202           (void) FormatLocaleString(format,MaxTextExtent,"%+f ",*k++);
3203           (void) ConcatenateString(&message,format);
3204         }
3205         (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
3206       }
3207       message=DestroyString(message);
3208     }
3209   blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
3210   if (blur_image == (Image *) NULL)
3211     return((Image *) NULL);
3212   if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
3213     {
3214       blur_image=DestroyImage(blur_image);
3215       return((Image *) NULL);
3216     }
3217   /*
3218     Threshold blur image.
3219   */
3220   status=MagickTrue;
3221   progress=0;
3222   center=(ssize_t) GetPixelChannels(image)*(image->columns+width)*(width/2L)+
3223     GetPixelChannels(image)*(width/2L);
3224   image_view=AcquireCacheView(image);
3225   blur_view=AcquireCacheView(blur_image);
3226 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3227   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3228 #endif
3229   for (y=0; y < (ssize_t) image->rows; y++)
3230   {
3231     double
3232       contrast;
3233
3234     MagickBooleanType
3235       sync;
3236
3237     register const Quantum
3238       *restrict p;
3239
3240     register Quantum
3241       *restrict q;
3242
3243     register ssize_t
3244       x;
3245
3246     if (status == MagickFalse)
3247       continue;
3248     p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
3249       (width/2L),image->columns+width,width,exception);
3250     q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
3251       exception);
3252     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3253       {
3254         status=MagickFalse;
3255         continue;
3256       }
3257     for (x=0; x < (ssize_t) image->columns; x++)
3258     {
3259       register ssize_t
3260         i;
3261
3262       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3263       {
3264         MagickRealType
3265           alpha,
3266           gamma,
3267           intensity,
3268           pixel;
3269
3270         PixelChannel
3271           channel;
3272
3273         PixelTrait
3274           blur_traits,
3275           traits;
3276
3277         register const double
3278           *restrict k;
3279
3280         register const Quantum
3281           *restrict pixels;
3282
3283         register ssize_t
3284           u;
3285
3286         ssize_t
3287           v;
3288
3289         traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
3290         channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
3291         blur_traits=GetPixelChannelMapTraits(blur_image,channel);
3292         if ((traits == UndefinedPixelTrait) ||
3293             (blur_traits == UndefinedPixelTrait))
3294           continue;
3295         if ((blur_traits & CopyPixelTrait) != 0)
3296           {
3297             q[channel]=p[center+i];
3298             continue;
3299           }
3300         k=kernel;
3301         pixel=bias;
3302         pixels=p;
3303         intensity=GetPixelIntensity(image,p+center);
3304         gamma=0.0;
3305         if ((blur_traits & BlendPixelTrait) == 0)
3306           {
3307             for (v=0; v < (ssize_t) width; v++)
3308             {
3309               for (u=0; u < (ssize_t) width; u++)
3310               {
3311                 contrast=GetPixelIntensity(image,pixels)-intensity;
3312                 if (fabs(contrast) < threshold)
3313                   {
3314                     pixel+=(*k)*pixels[i];
3315                     gamma+=(*k);
3316                   }
3317                 k++;
3318                 pixels+=GetPixelChannels(image);
3319               }
3320               pixels+=image->columns*GetPixelChannels(image);
3321             }
3322             if (fabs((double) gamma) < MagickEpsilon)
3323               {
3324                 q[channel]=p[center+i];
3325                 continue;
3326               }
3327             gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
3328             q[channel]=ClampToQuantum(gamma*pixel);
3329             continue;
3330           }
3331         for (v=0; v < (ssize_t) width; v++)
3332         {
3333           for (u=0; u < (ssize_t) width; u++)
3334           {
3335             contrast=GetPixelIntensity(image,pixels)-intensity;
3336             if (fabs(contrast) < threshold)
3337               {
3338                 alpha=(MagickRealType) (QuantumScale*
3339                   GetPixelAlpha(image,pixels));
3340                 pixel+=(*k)*alpha*pixels[i];
3341                 gamma+=(*k)*alpha;
3342               }
3343             k++;
3344             pixels+=GetPixelChannels(image);
3345           }
3346           pixels+=image->columns*GetPixelChannels(image);
3347         }
3348         if (fabs((double) gamma) < MagickEpsilon)
3349           {
3350             q[channel]=p[center+i];
3351             continue;
3352           }
3353         gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
3354         q[channel]=ClampToQuantum(gamma*pixel);
3355       }
3356       p+=GetPixelChannels(image);
3357       q+=GetPixelChannels(blur_image);
3358     }
3359     sync=SyncCacheViewAuthenticPixels(blur_view,exception);
3360     if (sync == MagickFalse)
3361       status=MagickFalse;
3362     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3363       {
3364         MagickBooleanType
3365           proceed;
3366
3367 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3368   #pragma omp critical (MagickCore_SelectiveBlurImage)
3369 #endif
3370         proceed=SetImageProgress(image,SelectiveBlurImageTag,progress++,
3371           image->rows);
3372         if (proceed == MagickFalse)
3373           status=MagickFalse;
3374       }
3375   }
3376   blur_image->type=image->type;
3377   blur_view=DestroyCacheView(blur_view);
3378   image_view=DestroyCacheView(image_view);
3379   kernel=(double *) RelinquishMagickMemory(kernel);
3380   if (status == MagickFalse)
3381     blur_image=DestroyImage(blur_image);
3382   return(blur_image);
3383 }
3384 \f
3385 /*
3386 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3387 %                                                                             %
3388 %                                                                             %
3389 %                                                                             %
3390 %     S h a d e I m a g e                                                     %
3391 %                                                                             %
3392 %                                                                             %
3393 %                                                                             %
3394 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3395 %
3396 %  ShadeImage() shines a distant light on an image to create a
3397 %  three-dimensional effect. You control the positioning of the light with
3398 %  azimuth and elevation; azimuth is measured in degrees off the x axis
3399 %  and elevation is measured in pixels above the Z axis.
3400 %
3401 %  The format of the ShadeImage method is:
3402 %
3403 %      Image *ShadeImage(const Image *image,const MagickBooleanType gray,
3404 %        const double azimuth,const double elevation,ExceptionInfo *exception)
3405 %
3406 %  A description of each parameter follows:
3407 %
3408 %    o image: the image.
3409 %
3410 %    o gray: A value other than zero shades the intensity of each pixel.
3411 %
3412 %    o azimuth, elevation:  Define the light source direction.
3413 %
3414 %    o exception: return any errors or warnings in this structure.
3415 %
3416 */
3417 MagickExport Image *ShadeImage(const Image *image,const MagickBooleanType gray,
3418   const double azimuth,const double elevation,ExceptionInfo *exception)
3419 {
3420 #define ShadeImageTag  "Shade/Image"
3421
3422   CacheView
3423     *image_view,
3424     *shade_view;
3425
3426   Image
3427     *shade_image;
3428
3429   MagickBooleanType
3430     status;
3431
3432   MagickOffsetType
3433     progress;
3434
3435   PrimaryInfo
3436     light;
3437
3438   ssize_t
3439     y;
3440
3441   /*
3442     Initialize shaded image attributes.
3443   */
3444   assert(image != (const Image *) NULL);
3445   assert(image->signature == MagickSignature);
3446   if (image->debug != MagickFalse)
3447     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3448   assert(exception != (ExceptionInfo *) NULL);
3449   assert(exception->signature == MagickSignature);
3450   shade_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
3451   if (shade_image == (Image *) NULL)
3452     return((Image *) NULL);
3453   if (SetImageStorageClass(shade_image,DirectClass,exception) == MagickFalse)
3454     {
3455       shade_image=DestroyImage(shade_image);
3456       return((Image *) NULL);
3457     }
3458   /*
3459     Compute the light vector.
3460   */
3461   light.x=(double) QuantumRange*cos(DegreesToRadians(azimuth))*
3462     cos(DegreesToRadians(elevation));
3463   light.y=(double) QuantumRange*sin(DegreesToRadians(azimuth))*
3464     cos(DegreesToRadians(elevation));
3465   light.z=(double) QuantumRange*sin(DegreesToRadians(elevation));
3466   /*
3467     Shade image.
3468   */
3469   status=MagickTrue;
3470   progress=0;
3471   image_view=AcquireCacheView(image);
3472   shade_view=AcquireCacheView(shade_image);
3473 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3474   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3475 #endif
3476   for (y=0; y < (ssize_t) image->rows; y++)
3477   {
3478     MagickRealType
3479       distance,
3480       normal_distance,
3481       shade;
3482
3483     PrimaryInfo
3484       normal;
3485
3486     register const Quantum
3487       *restrict center,
3488       *restrict p,
3489       *restrict post,
3490       *restrict pre;
3491
3492     register Quantum
3493       *restrict q;
3494
3495     register ssize_t
3496       x;
3497
3498     if (status == MagickFalse)
3499       continue;
3500     p=GetCacheViewVirtualPixels(image_view,-1,y-1,image->columns+2,3,exception);
3501     q=QueueCacheViewAuthenticPixels(shade_view,0,y,shade_image->columns,1,
3502       exception);
3503     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3504       {
3505         status=MagickFalse;
3506         continue;
3507       }
3508     /*
3509       Shade this row of pixels.
3510     */
3511     normal.z=2.0*(double) QuantumRange;  /* constant Z of surface normal */
3512     pre=p+GetPixelChannels(image);
3513     center=pre+(image->columns+2)*GetPixelChannels(image);
3514     post=center+(image->columns+2)*GetPixelChannels(image);
3515     for (x=0; x < (ssize_t) image->columns; x++)
3516     {
3517       register ssize_t
3518         i;
3519
3520       /*
3521         Determine the surface normal and compute shading.
3522       */
3523       normal.x=(double) (GetPixelIntensity(image,pre-GetPixelChannels(image))+
3524         GetPixelIntensity(image,center-GetPixelChannels(image))+
3525         GetPixelIntensity(image,post-GetPixelChannels(image))-
3526         GetPixelIntensity(image,pre+GetPixelChannels(image))-
3527         GetPixelIntensity(image,center+GetPixelChannels(image))-
3528         GetPixelIntensity(image,post+GetPixelChannels(image)));
3529       normal.y=(double) (GetPixelIntensity(image,post-GetPixelChannels(image))+
3530         GetPixelIntensity(image,post)+GetPixelIntensity(image,post+
3531         GetPixelChannels(image))-GetPixelIntensity(image,pre-
3532         GetPixelChannels(image))-GetPixelIntensity(image,pre)-
3533         GetPixelIntensity(image,pre+GetPixelChannels(image)));
3534       if ((normal.x == 0.0) && (normal.y == 0.0))
3535         shade=light.z;
3536       else
3537         {
3538           shade=0.0;
3539           distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
3540           if (distance > MagickEpsilon)
3541             {
3542               normal_distance=
3543                 normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
3544               if (normal_distance > (MagickEpsilon*MagickEpsilon))
3545                 shade=distance/sqrt((double) normal_distance);
3546             }
3547         }
3548       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3549       {
3550         PixelChannel
3551           channel;
3552
3553         PixelTrait
3554           shade_traits,
3555           traits;
3556
3557         traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
3558         channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
3559         shade_traits=GetPixelChannelMapTraits(shade_image,channel);
3560         if ((traits == UndefinedPixelTrait) ||
3561             (shade_traits == UndefinedPixelTrait))
3562           continue;
3563         if ((shade_traits & CopyPixelTrait) != 0)
3564           {
3565             q[channel]=center[i];
3566             continue;
3567           }
3568         if (gray != MagickFalse)
3569           {
3570             q[channel]=ClampToQuantum(shade);
3571             continue;
3572           }
3573         q[channel]=ClampToQuantum(QuantumScale*shade*center[i]);
3574       }
3575       pre+=GetPixelChannels(image);
3576       center+=GetPixelChannels(image);
3577       post+=GetPixelChannels(image);
3578       q+=GetPixelChannels(shade_image);
3579     }
3580     if (SyncCacheViewAuthenticPixels(shade_view,exception) == MagickFalse)
3581       status=MagickFalse;
3582     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3583       {
3584         MagickBooleanType
3585           proceed;
3586
3587 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3588   #pragma omp critical (MagickCore_ShadeImage)
3589 #endif
3590         proceed=SetImageProgress(image,ShadeImageTag,progress++,image->rows);
3591         if (proceed == MagickFalse)
3592           status=MagickFalse;
3593       }
3594   }
3595   shade_view=DestroyCacheView(shade_view);
3596   image_view=DestroyCacheView(image_view);
3597   if (status == MagickFalse)
3598     shade_image=DestroyImage(shade_image);
3599   return(shade_image);
3600 }
3601 \f
3602 /*
3603 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3604 %                                                                             %
3605 %                                                                             %
3606 %                                                                             %
3607 %     S h a r p e n I m a g e                                                 %
3608 %                                                                             %
3609 %                                                                             %
3610 %                                                                             %
3611 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3612 %
3613 %  SharpenImage() sharpens the image.  We convolve the image with a Gaussian
3614 %  operator of the given radius and standard deviation (sigma).  For
3615 %  reasonable results, radius should be larger than sigma.  Use a radius of 0
3616 %  and SharpenImage() selects a suitable radius for you.
3617 %
3618 %  Using a separable kernel would be faster, but the negative weights cancel
3619 %  out on the corners of the kernel producing often undesirable ringing in the
3620 %  filtered result; this can be avoided by using a 2D gaussian shaped image
3621 %  sharpening kernel instead.
3622 %
3623 %  The format of the SharpenImage method is:
3624 %
3625 %    Image *SharpenImage(const Image *image,const double radius,
3626 %      const double sigma,const double bias,ExceptionInfo *exception)
3627 %
3628 %  A description of each parameter follows:
3629 %
3630 %    o image: the image.
3631 %
3632 %    o radius: the radius of the Gaussian, in pixels, not counting the center
3633 %      pixel.
3634 %
3635 %    o sigma: the standard deviation of the Laplacian, in pixels.
3636 %
3637 %    o bias: bias.
3638 %
3639 %    o exception: return any errors or warnings in this structure.
3640 %
3641 */
3642 MagickExport Image *SharpenImage(const Image *image,const double radius,
3643   const double sigma,const double bias,ExceptionInfo *exception)
3644 {
3645   double
3646     normalize;
3647
3648   Image
3649     *sharp_image;
3650
3651   KernelInfo
3652     *kernel_info;
3653
3654   register ssize_t
3655     i;
3656
3657   size_t
3658     width;
3659
3660   ssize_t
3661     j,
3662     u,
3663     v;
3664
3665   assert(image != (const Image *) NULL);
3666   assert(image->signature == MagickSignature);
3667   if (image->debug != MagickFalse)
3668     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3669   assert(exception != (ExceptionInfo *) NULL);
3670   assert(exception->signature == MagickSignature);
3671   width=GetOptimalKernelWidth2D(radius,sigma);
3672   kernel_info=AcquireKernelInfo((const char *) NULL);
3673   if (kernel_info == (KernelInfo *) NULL)
3674     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3675   (void) ResetMagickMemory(kernel_info,0,sizeof(*kernel_info));
3676   kernel_info->width=width;
3677   kernel_info->height=width;
3678   kernel_info->bias=bias;
3679   kernel_info->signature=MagickSignature;
3680   kernel_info->values=(double *) AcquireAlignedMemory(kernel_info->width,
3681     kernel_info->width*sizeof(*kernel_info->values));
3682   if (kernel_info->values == (double *) NULL)
3683     {
3684       kernel_info=DestroyKernelInfo(kernel_info);
3685       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3686     }
3687   normalize=0.0;
3688   j=(ssize_t) kernel_info->width/2;
3689   i=0;
3690   for (v=(-j); v <= j; v++)
3691   {
3692     for (u=(-j); u <= j; u++)
3693     {
3694       kernel_info->values[i]=(double) (-exp(-((double) u*u+v*v)/(2.0*
3695         MagickSigma*MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
3696       normalize+=kernel_info->values[i];
3697       i++;
3698     }
3699   }
3700   kernel_info->values[i/2]=(double) ((-2.0)*normalize);
3701   sharp_image=ConvolveImage(image,kernel_info,exception);
3702   kernel_info=DestroyKernelInfo(kernel_info);
3703   return(sharp_image);
3704 }
3705 \f
3706 /*
3707 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3708 %                                                                             %
3709 %                                                                             %
3710 %                                                                             %
3711 %     S p r e a d I m a g e                                                   %
3712 %                                                                             %
3713 %                                                                             %
3714 %                                                                             %
3715 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3716 %
3717 %  SpreadImage() is a special effects method that randomly displaces each
3718 %  pixel in a block defined by the radius parameter.
3719 %
3720 %  The format of the SpreadImage method is:
3721 %
3722 %      Image *SpreadImage(const Image *image,const double radius,
3723 %        const PixelInterpolateMethod method,ExceptionInfo *exception)
3724 %
3725 %  A description of each parameter follows:
3726 %
3727 %    o image: the image.
3728 %
3729 %    o radius:  choose a random pixel in a neighborhood of this extent.
3730 %
3731 %    o method:  the pixel interpolation method.
3732 %
3733 %    o exception: return any errors or warnings in this structure.
3734 %
3735 */
3736 MagickExport Image *SpreadImage(const Image *image,const double radius,
3737   const PixelInterpolateMethod method,ExceptionInfo *exception)
3738 {
3739 #define SpreadImageTag  "Spread/Image"
3740
3741   CacheView
3742     *image_view,
3743     *spread_view;
3744
3745   Image
3746     *spread_image;
3747
3748   MagickBooleanType
3749     status;
3750
3751   MagickOffsetType
3752     progress;
3753
3754   RandomInfo
3755     **restrict random_info;
3756
3757   size_t
3758     width;
3759
3760   ssize_t
3761     y;
3762
3763   /*
3764     Initialize spread image attributes.
3765   */
3766   assert(image != (Image *) NULL);
3767   assert(image->signature == MagickSignature);
3768   if (image->debug != MagickFalse)
3769     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3770   assert(exception != (ExceptionInfo *) NULL);
3771   assert(exception->signature == MagickSignature);
3772   spread_image=CloneImage(image,image->columns,image->rows,MagickTrue,
3773     exception);
3774   if (spread_image == (Image *) NULL)
3775     return((Image *) NULL);
3776   if (SetImageStorageClass(spread_image,DirectClass,exception) == MagickFalse)
3777     {
3778       spread_image=DestroyImage(spread_image);
3779       return((Image *) NULL);
3780     }
3781   /*
3782     Spread image.
3783   */
3784   status=MagickTrue;
3785   progress=0;
3786   width=GetOptimalKernelWidth1D(radius,0.5);
3787   random_info=AcquireRandomInfoThreadSet();
3788   image_view=AcquireCacheView(image);
3789   spread_view=AcquireCacheView(spread_image);
3790 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3791   #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
3792 #endif
3793   for (y=0; y < (ssize_t) image->rows; y++)
3794   {
3795     const int
3796       id = GetOpenMPThreadId();
3797
3798     register const Quantum
3799       *restrict p;
3800
3801     register Quantum
3802       *restrict q;
3803
3804     register ssize_t
3805       x;
3806
3807     if (status == MagickFalse)
3808       continue;
3809     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3810     q=QueueCacheViewAuthenticPixels(spread_view,0,y,spread_image->columns,1,
3811       exception);
3812     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3813       {
3814         status=MagickFalse;
3815         continue;
3816       }
3817     for (x=0; x < (ssize_t) image->columns; x++)
3818     {
3819       PointInfo
3820         point;
3821
3822       point.x=GetPseudoRandomValue(random_info[id]);
3823       point.y=GetPseudoRandomValue(random_info[id]);
3824       status=InterpolatePixelChannels(image,image_view,spread_image,method,
3825         (double) x+width*point.x-0.5,(double) y+width*point.y-0.5,q,exception);
3826       q+=GetPixelChannels(spread_image);
3827     }
3828     if (SyncCacheViewAuthenticPixels(spread_view,exception) == MagickFalse)
3829       status=MagickFalse;
3830     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3831       {
3832         MagickBooleanType
3833           proceed;
3834
3835 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3836   #pragma omp critical (MagickCore_SpreadImage)
3837 #endif
3838         proceed=SetImageProgress(image,SpreadImageTag,progress++,image->rows);
3839         if (proceed == MagickFalse)
3840           status=MagickFalse;
3841       }
3842   }
3843   spread_view=DestroyCacheView(spread_view);
3844   image_view=DestroyCacheView(image_view);
3845   random_info=DestroyRandomInfoThreadSet(random_info);
3846   return(spread_image);
3847 }
3848 \f
3849 /*
3850 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3851 %                                                                             %
3852 %                                                                             %
3853 %                                                                             %
3854 %     S t a t i s t i c I m a g e                                             %
3855 %                                                                             %
3856 %                                                                             %
3857 %                                                                             %
3858 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3859 %
3860 %  StatisticImage() makes each pixel the min / max / median / mode / etc. of
3861 %  the neighborhood of the specified width and height.
3862 %
3863 %  The format of the StatisticImage method is:
3864 %
3865 %      Image *StatisticImage(const Image *image,const StatisticType type,
3866 %        const size_t width,const size_t height,ExceptionInfo *exception)
3867 %
3868 %  A description of each parameter follows:
3869 %
3870 %    o image: the image.
3871 %
3872 %    o type: the statistic type (median, mode, etc.).
3873 %
3874 %    o width: the width of the pixel neighborhood.
3875 %
3876 %    o height: the height of the pixel neighborhood.
3877 %
3878 %    o exception: return any errors or warnings in this structure.
3879 %
3880 */
3881
3882 typedef struct _SkipNode
3883 {
3884   size_t
3885     next[9],
3886     count,
3887     signature;
3888 } SkipNode;
3889
3890 typedef struct _SkipList
3891 {
3892   ssize_t
3893     level;
3894
3895   SkipNode
3896     *nodes;
3897 } SkipList;
3898
3899 typedef struct _PixelList
3900 {
3901   size_t
3902     length,
3903     seed;
3904
3905   SkipList
3906     skip_list;
3907
3908   size_t
3909     signature;
3910 } PixelList;
3911
3912 static PixelList *DestroyPixelList(PixelList *pixel_list)
3913 {
3914   if (pixel_list == (PixelList *) NULL)
3915     return((PixelList *) NULL);
3916   if (pixel_list->skip_list.nodes != (SkipNode *) NULL)
3917     pixel_list->skip_list.nodes=(SkipNode *) RelinquishMagickMemory(
3918       pixel_list->skip_list.nodes);
3919   pixel_list=(PixelList *) RelinquishMagickMemory(pixel_list);
3920   return(pixel_list);
3921 }
3922
3923 static PixelList **DestroyPixelListThreadSet(PixelList **pixel_list)
3924 {
3925   register ssize_t
3926     i;
3927
3928   assert(pixel_list != (PixelList **) NULL);
3929   for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
3930     if (pixel_list[i] != (PixelList *) NULL)
3931       pixel_list[i]=DestroyPixelList(pixel_list[i]);
3932   pixel_list=(PixelList **) RelinquishMagickMemory(pixel_list);
3933   return(pixel_list);
3934 }
3935
3936 static PixelList *AcquirePixelList(const size_t width,const size_t height)
3937 {
3938   PixelList
3939     *pixel_list;
3940
3941   pixel_list=(PixelList *) AcquireMagickMemory(sizeof(*pixel_list));
3942   if (pixel_list == (PixelList *) NULL)
3943     return(pixel_list);
3944   (void) ResetMagickMemory((void *) pixel_list,0,sizeof(*pixel_list));
3945   pixel_list->length=width*height;
3946   pixel_list->skip_list.nodes=(SkipNode *) AcquireQuantumMemory(65537UL,
3947     sizeof(*pixel_list->skip_list.nodes));
3948   if (pixel_list->skip_list.nodes == (SkipNode *) NULL)
3949     return(DestroyPixelList(pixel_list));
3950   (void) ResetMagickMemory(pixel_list->skip_list.nodes,0,65537UL*
3951     sizeof(*pixel_list->skip_list.nodes));
3952   pixel_list->signature=MagickSignature;
3953   return(pixel_list);
3954 }
3955
3956 static PixelList **AcquirePixelListThreadSet(const size_t width,
3957   const size_t height)
3958 {
3959   PixelList
3960     **pixel_list;
3961
3962   register ssize_t
3963     i;
3964
3965   size_t
3966     number_threads;
3967
3968   number_threads=GetOpenMPMaximumThreads();
3969   pixel_list=(PixelList **) AcquireQuantumMemory(number_threads,
3970     sizeof(*pixel_list));
3971   if (pixel_list == (PixelList **) NULL)
3972     return((PixelList **) NULL);
3973   (void) ResetMagickMemory(pixel_list,0,number_threads*sizeof(*pixel_list));
3974   for (i=0; i < (ssize_t) number_threads; i++)
3975   {
3976     pixel_list[i]=AcquirePixelList(width,height);
3977     if (pixel_list[i] == (PixelList *) NULL)
3978       return(DestroyPixelListThreadSet(pixel_list));
3979   }
3980   return(pixel_list);
3981 }
3982
3983 static void AddNodePixelList(PixelList *pixel_list,const size_t color)
3984 {
3985   register SkipList
3986     *p;
3987
3988   register ssize_t
3989     level;
3990
3991   size_t
3992     search,
3993     update[9];
3994
3995   /*
3996     Initialize the node.
3997   */
3998   p=(&pixel_list->skip_list);
3999   p->nodes[color].signature=pixel_list->signature;
4000   p->nodes[color].count=1;
4001   /*
4002     Determine where it belongs in the list.
4003   */
4004   search=65536UL;
4005   for (level=p->level; level >= 0; level--)
4006   {
4007     while (p->nodes[search].next[level] < color)
4008       search=p->nodes[search].next[level];
4009     update[level]=search;
4010   }
4011   /*
4012     Generate a pseudo-random level for this node.
4013   */
4014   for (level=0; ; level++)
4015   {
4016     pixel_list->seed=(pixel_list->seed*42893621L)+1L;
4017     if ((pixel_list->seed & 0x300) != 0x300)
4018       break;
4019   }
4020   if (level > 8)
4021     level=8;
4022   if (level > (p->level+2))
4023     level=p->level+2;
4024   /*
4025     If we're raising the list's level, link back to the root node.
4026   */
4027   while (level > p->level)
4028   {
4029     p->level++;
4030     update[p->level]=65536UL;
4031   }
4032   /*
4033     Link the node into the skip-list.
4034   */
4035   do
4036   {
4037     p->nodes[color].next[level]=p->nodes[update[level]].next[level];
4038     p->nodes[update[level]].next[level]=color;
4039   } while (level-- > 0);
4040 }
4041
4042 static Quantum GetMaximumPixelList(PixelList *pixel_list)
4043 {
4044   register SkipList
4045     *p;
4046
4047   size_t
4048     color,
4049     maximum;
4050
4051   ssize_t
4052     count;
4053
4054   /*
4055     Find the maximum value for each of the color.
4056   */
4057   p=(&pixel_list->skip_list);
4058   color=65536L;
4059   count=0;
4060   maximum=p->nodes[color].next[0];
4061   do
4062   {
4063     color=p->nodes[color].next[0];
4064     if (color > maximum)
4065       maximum=color;
4066     count+=p->nodes[color].count;
4067   } while (count < (ssize_t) pixel_list->length);
4068   return(ScaleShortToQuantum((unsigned short) maximum));
4069 }
4070
4071 static Quantum GetMeanPixelList(PixelList *pixel_list)
4072 {
4073   MagickRealType
4074     sum;
4075
4076   register SkipList
4077     *p;
4078
4079   size_t
4080     color;
4081
4082   ssize_t
4083     count;
4084
4085   /*
4086     Find the mean value for each of the color.
4087   */
4088   p=(&pixel_list->skip_list);
4089   color=65536L;
4090   count=0;
4091   sum=0.0;
4092   do
4093   {
4094     color=p->nodes[color].next[0];
4095     sum+=(MagickRealType) p->nodes[color].count*color;
4096     count+=p->nodes[color].count;
4097   } while (count < (ssize_t) pixel_list->length);
4098   sum/=pixel_list->length;
4099   return(ScaleShortToQuantum((unsigned short) sum));
4100 }
4101
4102 static Quantum GetMedianPixelList(PixelList *pixel_list)
4103 {
4104   register SkipList
4105     *p;
4106
4107   size_t
4108     color;
4109
4110   ssize_t
4111     count;
4112
4113   /*
4114     Find the median value for each of the color.
4115   */
4116   p=(&pixel_list->skip_list);
4117   color=65536L;
4118   count=0;
4119   do
4120   {
4121     color=p->nodes[color].next[0];
4122     count+=p->nodes[color].count;
4123   } while (count <= (ssize_t) (pixel_list->length >> 1));
4124   return(ScaleShortToQuantum((unsigned short) color));
4125 }
4126
4127 static Quantum GetMinimumPixelList(PixelList *pixel_list)
4128 {
4129   register SkipList
4130     *p;
4131
4132   size_t
4133     color,
4134     minimum;
4135
4136   ssize_t
4137     count;
4138
4139   /*
4140     Find the minimum value for each of the color.
4141   */
4142   p=(&pixel_list->skip_list);
4143   count=0;
4144   color=65536UL;
4145   minimum=p->nodes[color].next[0];
4146   do
4147   {
4148     color=p->nodes[color].next[0];
4149     if (color < minimum)
4150       minimum=color;
4151     count+=p->nodes[color].count;
4152   } while (count < (ssize_t) pixel_list->length);
4153   return(ScaleShortToQuantum((unsigned short) minimum));
4154 }
4155
4156 static Quantum GetModePixelList(PixelList *pixel_list)
4157 {
4158   register SkipList
4159     *p;
4160
4161   size_t
4162     color,
4163     max_count,
4164     mode;
4165
4166   ssize_t
4167     count;
4168
4169   /*
4170     Make each pixel the 'predominant color' of the specified neighborhood.
4171   */
4172   p=(&pixel_list->skip_list);
4173   color=65536L;
4174   mode=color;
4175   max_count=p->nodes[mode].count;
4176   count=0;
4177   do
4178   {
4179     color=p->nodes[color].next[0];
4180     if (p->nodes[color].count > max_count)
4181       {
4182         mode=color;
4183         max_count=p->nodes[mode].count;
4184       }
4185     count+=p->nodes[color].count;
4186   } while (count < (ssize_t) pixel_list->length);
4187   return(ScaleShortToQuantum((unsigned short) mode));
4188 }
4189
4190 static Quantum GetNonpeakPixelList(PixelList *pixel_list)
4191 {
4192   register SkipList
4193     *p;
4194
4195   size_t
4196     color,
4197     next,
4198     previous;
4199
4200   ssize_t
4201     count;
4202
4203   /*
4204     Finds the non peak value for each of the colors.
4205   */
4206   p=(&pixel_list->skip_list);
4207   color=65536L;
4208   next=p->nodes[color].next[0];
4209   count=0;
4210   do
4211   {
4212     previous=color;
4213     color=next;
4214     next=p->nodes[color].next[0];
4215     count+=p->nodes[color].count;
4216   } while (count <= (ssize_t) (pixel_list->length >> 1));
4217   if ((previous == 65536UL) && (next != 65536UL))
4218     color=next;
4219   else
4220     if ((previous != 65536UL) && (next == 65536UL))
4221       color=previous;
4222   return(ScaleShortToQuantum((unsigned short) color));
4223 }
4224
4225 static Quantum GetStandardDeviationPixelList(PixelList *pixel_list)
4226 {
4227   MagickRealType
4228     sum,
4229     sum_squared;
4230
4231   register SkipList
4232     *p;
4233
4234   size_t
4235     color;
4236
4237   ssize_t
4238     count;
4239
4240   /*
4241     Find the standard-deviation value for each of the color.
4242   */
4243   p=(&pixel_list->skip_list);
4244   color=65536L;
4245   count=0;
4246   sum=0.0;
4247   sum_squared=0.0;
4248   do
4249   {
4250     register ssize_t
4251       i;
4252
4253     color=p->nodes[color].next[0];
4254     sum+=(MagickRealType) p->nodes[color].count*color;
4255     for (i=0; i < (ssize_t) p->nodes[color].count; i++)
4256       sum_squared+=((MagickRealType) color)*((MagickRealType) color);
4257     count+=p->nodes[color].count;
4258   } while (count < (ssize_t) pixel_list->length);
4259   sum/=pixel_list->length;
4260   sum_squared/=pixel_list->length;
4261   return(ScaleShortToQuantum((unsigned short) sqrt(sum_squared-(sum*sum))));
4262 }
4263
4264 static inline void InsertPixelList(const Image *image,const Quantum pixel,
4265   PixelList *pixel_list)
4266 {
4267   size_t
4268     signature;
4269
4270   unsigned short
4271     index;
4272
4273   index=ScaleQuantumToShort(pixel);
4274   signature=pixel_list->skip_list.nodes[index].signature;
4275   if (signature == pixel_list->signature)
4276     {
4277       pixel_list->skip_list.nodes[index].count++;
4278       return;
4279     }
4280   AddNodePixelList(pixel_list,index);
4281 }
4282
4283 static inline MagickRealType MagickAbsoluteValue(const MagickRealType x)
4284 {
4285   if (x < 0)
4286     return(-x);
4287   return(x);
4288 }
4289
4290 static inline size_t MagickMax(const size_t x,const size_t y)
4291 {
4292   if (x > y)
4293     return(x);
4294   return(y);
4295 }
4296
4297 static void ResetPixelList(PixelList *pixel_list)
4298 {
4299   int
4300     level;
4301
4302   register SkipNode
4303     *root;
4304
4305   register SkipList
4306     *p;
4307
4308   /*
4309     Reset the skip-list.
4310   */
4311   p=(&pixel_list->skip_list);
4312   root=p->nodes+65536UL;
4313   p->level=0;
4314   for (level=0; level < 9; level++)
4315     root->next[level]=65536UL;
4316   pixel_list->seed=pixel_list->signature++;
4317 }
4318
4319 MagickExport Image *StatisticImage(const Image *image,const StatisticType type,
4320   const size_t width,const size_t height,ExceptionInfo *exception)
4321 {
4322 #define StatisticImageTag  "Statistic/Image"
4323
4324   CacheView
4325     *image_view,
4326     *statistic_view;
4327
4328   Image
4329     *statistic_image;
4330
4331   MagickBooleanType
4332     status;
4333
4334   MagickOffsetType
4335     progress;
4336
4337   PixelList
4338     **restrict pixel_list;
4339
4340   ssize_t
4341     center,
4342     y;
4343
4344   /*
4345     Initialize statistics image attributes.
4346   */
4347   assert(image != (Image *) NULL);
4348   assert(image->signature == MagickSignature);
4349   if (image->debug != MagickFalse)
4350     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4351   assert(exception != (ExceptionInfo *) NULL);
4352   assert(exception->signature == MagickSignature);
4353   statistic_image=CloneImage(image,image->columns,image->rows,MagickTrue,
4354     exception);
4355   if (statistic_image == (Image *) NULL)
4356     return((Image *) NULL);
4357   status=SetImageStorageClass(statistic_image,DirectClass,exception);
4358   if (status == MagickFalse)
4359     {
4360       statistic_image=DestroyImage(statistic_image);
4361       return((Image *) NULL);
4362     }
4363   pixel_list=AcquirePixelListThreadSet(MagickMax(width,1),MagickMax(height,1));
4364   if (pixel_list == (PixelList **) NULL)
4365     {
4366       statistic_image=DestroyImage(statistic_image);
4367       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
4368     }
4369   /*
4370     Make each pixel the min / max / median / mode / etc. of the neighborhood.
4371   */
4372   center=(ssize_t) GetPixelChannels(image)*(image->columns+MagickMax(width,1))*
4373     (MagickMax(height,1)/2L)+GetPixelChannels(image)*(MagickMax(width,1)/2L);
4374   status=MagickTrue;
4375   progress=0;
4376   image_view=AcquireCacheView(image);
4377   statistic_view=AcquireCacheView(statistic_image);
4378 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4379   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4380 #endif
4381   for (y=0; y < (ssize_t) statistic_image->rows; y++)
4382   {
4383     const int
4384       id = GetOpenMPThreadId();
4385
4386     register const Quantum
4387       *restrict p;
4388
4389     register Quantum
4390       *restrict q;
4391
4392     register ssize_t
4393       x;
4394
4395     if (status == MagickFalse)
4396       continue;
4397     p=GetCacheViewVirtualPixels(image_view,-((ssize_t) MagickMax(width,1)/2L),y-
4398       (ssize_t) (MagickMax(height,1)/2L),image->columns+MagickMax(width,1),
4399       MagickMax(height,1),exception);
4400     q=QueueCacheViewAuthenticPixels(statistic_view,0,y,statistic_image->columns,      1,exception);
4401     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
4402       {
4403         status=MagickFalse;
4404         continue;
4405       }
4406     for (x=0; x < (ssize_t) statistic_image->columns; x++)
4407     {
4408       register ssize_t
4409         i;
4410
4411       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4412       {
4413         PixelChannel
4414           channel;
4415
4416         PixelTrait
4417           statistic_traits,
4418           traits;
4419
4420         Quantum
4421           pixel;
4422
4423         register const Quantum
4424           *restrict pixels;
4425
4426         register ssize_t
4427           u;
4428
4429         ssize_t
4430           v;
4431
4432         traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
4433         channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
4434         statistic_traits=GetPixelChannelMapTraits(statistic_image,channel);
4435         if ((traits == UndefinedPixelTrait) ||
4436             (statistic_traits == UndefinedPixelTrait))
4437           continue;
4438         if ((statistic_traits & CopyPixelTrait) != 0)
4439           {
4440             q[channel]=p[center+i];
4441             continue;
4442           }
4443         pixels=p;
4444         ResetPixelList(pixel_list[id]);
4445         for (v=0; v < (ssize_t) MagickMax(height,1); v++)
4446         {
4447           for (u=0; u < (ssize_t) MagickMax(width,1); u++)
4448           {
4449             InsertPixelList(image,pixels[i],pixel_list[id]);
4450             pixels+=GetPixelChannels(image);
4451           }
4452           pixels+=image->columns*GetPixelChannels(image);
4453         }
4454         switch (type)
4455         {
4456           case GradientStatistic:
4457           {
4458             MagickRealType
4459               maximum,
4460               minimum;
4461
4462             minimum=(MagickRealType) GetMinimumPixelList(pixel_list[id]);
4463             maximum=(MagickRealType) GetMaximumPixelList(pixel_list[id]);
4464             pixel=ClampToQuantum(MagickAbsoluteValue(maximum-minimum));
4465             break;
4466           }
4467           case MaximumStatistic:
4468           {
4469             pixel=GetMaximumPixelList(pixel_list[id]);
4470             break;
4471           }
4472           case MeanStatistic:
4473           {
4474             pixel=GetMeanPixelList(pixel_list[id]);
4475             break;
4476           }
4477           case MedianStatistic:
4478           default:
4479           {
4480             pixel=GetMedianPixelList(pixel_list[id]);
4481             break;
4482           }
4483           case MinimumStatistic:
4484           {
4485             pixel=GetMinimumPixelList(pixel_list[id]);
4486             break;
4487           }
4488           case ModeStatistic:
4489           {
4490             pixel=GetModePixelList(pixel_list[id]);
4491             break;
4492           }
4493           case NonpeakStatistic:
4494           {
4495             pixel=GetNonpeakPixelList(pixel_list[id]);
4496             break;
4497           }
4498           case StandardDeviationStatistic:
4499           {
4500             pixel=GetStandardDeviationPixelList(pixel_list[id]);
4501             break;
4502           }
4503         }
4504         q[channel]=pixel;
4505       }
4506       p+=GetPixelChannels(image);
4507       q+=GetPixelChannels(statistic_image);
4508     }
4509     if (SyncCacheViewAuthenticPixels(statistic_view,exception) == MagickFalse)
4510       status=MagickFalse;
4511     if (image->progress_monitor != (MagickProgressMonitor) NULL)
4512       {
4513         MagickBooleanType
4514           proceed;
4515
4516 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4517   #pragma omp critical (MagickCore_StatisticImage)
4518 #endif
4519         proceed=SetImageProgress(image,StatisticImageTag,progress++,
4520           image->rows);
4521         if (proceed == MagickFalse)
4522           status=MagickFalse;
4523       }
4524   }
4525   statistic_view=DestroyCacheView(statistic_view);
4526   image_view=DestroyCacheView(image_view);
4527   pixel_list=DestroyPixelListThreadSet(pixel_list);
4528   return(statistic_image);
4529 }
4530 \f
4531 /*
4532 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4533 %                                                                             %
4534 %                                                                             %
4535 %                                                                             %
4536 %     U n s h a r p M a s k I m a g e                                         %
4537 %                                                                             %
4538 %                                                                             %
4539 %                                                                             %
4540 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4541 %
4542 %  UnsharpMaskImage() sharpens one or more image channels.  We convolve the
4543 %  image with a Gaussian operator of the given radius and standard deviation
4544 %  (sigma).  For reasonable results, radius should be larger than sigma.  Use a
4545 %  radius of 0 and UnsharpMaskImage() selects a suitable radius for you.
4546 %
4547 %  The format of the UnsharpMaskImage method is:
4548 %
4549 %    Image *UnsharpMaskImage(const Image *image,const double radius,
4550 %      const double sigma,const double amount,const double threshold,
4551 %      ExceptionInfo *exception)
4552 %
4553 %  A description of each parameter follows:
4554 %
4555 %    o image: the image.
4556 %
4557 %    o radius: the radius of the Gaussian, in pixels, not counting the center
4558 %      pixel.
4559 %
4560 %    o sigma: the standard deviation of the Gaussian, in pixels.
4561 %
4562 %    o amount: the percentage of the difference between the original and the
4563 %      blur image that is added back into the original.
4564 %
4565 %    o threshold: the threshold in pixels needed to apply the diffence amount.
4566 %
4567 %    o exception: return any errors or warnings in this structure.
4568 %
4569 */
4570 MagickExport Image *UnsharpMaskImage(const Image *image,
4571   const double radius,const double sigma,const double amount,
4572   const double threshold,ExceptionInfo *exception)
4573 {
4574 #define SharpenImageTag  "Sharpen/Image"
4575
4576   CacheView
4577     *image_view,
4578     *unsharp_view;
4579
4580   Image
4581     *unsharp_image;
4582
4583   MagickBooleanType
4584     status;
4585
4586   MagickOffsetType
4587     progress;
4588
4589   MagickRealType
4590     quantum_threshold;
4591
4592   ssize_t
4593     y;
4594
4595   assert(image != (const Image *) NULL);
4596   assert(image->signature == MagickSignature);
4597   if (image->debug != MagickFalse)
4598     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4599   assert(exception != (ExceptionInfo *) NULL);
4600   unsharp_image=BlurImage(image,radius,sigma,image->bias,exception);
4601   if (unsharp_image == (Image *) NULL)
4602     return((Image *) NULL);
4603   quantum_threshold=(MagickRealType) QuantumRange*threshold;
4604   /*
4605     Unsharp-mask image.
4606   */
4607   status=MagickTrue;
4608   progress=0;
4609   image_view=AcquireCacheView(image);
4610   unsharp_view=AcquireCacheView(unsharp_image);
4611 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4612   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4613 #endif
4614   for (y=0; y < (ssize_t) image->rows; y++)
4615   {
4616     register const Quantum
4617       *restrict p;
4618
4619     register Quantum
4620       *restrict q;
4621
4622     register ssize_t
4623       x;
4624
4625     if (status == MagickFalse)
4626       continue;
4627     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
4628     q=GetCacheViewAuthenticPixels(unsharp_view,0,y,unsharp_image->columns,1,
4629       exception);
4630     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
4631       {
4632         status=MagickFalse;
4633         continue;
4634       }
4635     for (x=0; x < (ssize_t) image->columns; x++)
4636     {
4637       register ssize_t
4638         i;
4639
4640       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4641       {
4642         MagickRealType
4643           pixel;
4644
4645         PixelChannel
4646           channel;
4647
4648         PixelTrait
4649           traits,
4650           unsharp_traits;
4651
4652         traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
4653         channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
4654         unsharp_traits=GetPixelChannelMapTraits(unsharp_image,channel);
4655         if ((traits == UndefinedPixelTrait) ||
4656             (unsharp_traits == UndefinedPixelTrait))
4657           continue;
4658         if ((unsharp_traits & CopyPixelTrait) != 0)
4659           {
4660             q[channel]=p[i];
4661             continue;
4662           }
4663         pixel=p[i]-(MagickRealType) q[channel];
4664         if (fabs(2.0*pixel) < quantum_threshold)
4665           pixel=(MagickRealType) p[i];
4666         else
4667           pixel=(MagickRealType) p[i]+amount*pixel;
4668         q[channel]=ClampToQuantum(pixel);
4669       }
4670       p+=GetPixelChannels(image);
4671       q+=GetPixelChannels(unsharp_image);
4672     }
4673     if (SyncCacheViewAuthenticPixels(unsharp_view,exception) == MagickFalse)
4674       status=MagickFalse;
4675     if (image->progress_monitor != (MagickProgressMonitor) NULL)
4676       {
4677         MagickBooleanType
4678           proceed;
4679
4680 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4681   #pragma omp critical (MagickCore_UnsharpMaskImage)
4682 #endif
4683         proceed=SetImageProgress(image,SharpenImageTag,progress++,image->rows);
4684         if (proceed == MagickFalse)
4685           status=MagickFalse;
4686       }
4687   }
4688   unsharp_image->type=image->type;
4689   unsharp_view=DestroyCacheView(unsharp_view);
4690   image_view=DestroyCacheView(image_view);
4691   if (status == MagickFalse)
4692     unsharp_image=DestroyImage(unsharp_image);
4693   return(unsharp_image);
4694 }