]> 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   /*
1294     Convolve image.
1295   */
1296   center=(ssize_t) GetPixelChannels(image)*(image->columns+kernel_info->width)*
1297     (kernel_info->height/2L)+GetPixelChannels(image)*(kernel_info->width/2L);
1298   status=MagickTrue;
1299   progress=0;
1300   image_view=AcquireCacheView(image);
1301   convolve_view=AcquireCacheView(convolve_image);
1302 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1303   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1304 #endif
1305   for (y=0; y < (ssize_t) image->rows; y++)
1306   {
1307     register const Quantum
1308       *restrict p;
1309
1310     register Quantum
1311       *restrict q;
1312
1313     register ssize_t
1314       x;
1315
1316     if (status == MagickFalse)
1317       continue;
1318     p=GetCacheViewVirtualPixels(image_view,-((ssize_t) kernel_info->width/2L),y-
1319       (ssize_t) (kernel_info->height/2L),image->columns+kernel_info->width,
1320       kernel_info->height,exception);
1321     q=QueueCacheViewAuthenticPixels(convolve_view,0,y,convolve_image->columns,1,
1322       exception);
1323     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1324       {
1325         status=MagickFalse;
1326         continue;
1327       }
1328     for (x=0; x < (ssize_t) image->columns; x++)
1329     {
1330       register ssize_t
1331         i;
1332
1333       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1334       {
1335         MagickRealType
1336           alpha,
1337           gamma,
1338           pixel;
1339
1340         PixelChannel
1341           channel;
1342
1343         PixelTrait
1344           convolve_traits,
1345           traits;
1346
1347         register const double
1348           *restrict k;
1349
1350         register const Quantum
1351           *restrict pixels;
1352
1353         register ssize_t
1354           u;
1355
1356         ssize_t
1357           v;
1358
1359         traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
1360         channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
1361         convolve_traits=GetPixelChannelMapTraits(convolve_image,channel);
1362         if ((traits == UndefinedPixelTrait) ||
1363             (convolve_traits == UndefinedPixelTrait))
1364           continue;
1365         if ((convolve_traits & CopyPixelTrait) != 0)
1366           {
1367             q[channel]=p[center+i];
1368             continue;
1369           }
1370         k=kernel_info->values;
1371         pixels=p;
1372         pixel=kernel_info->bias;
1373         if ((convolve_traits & BlendPixelTrait) == 0)
1374           {
1375             /*
1376               No alpha blending.
1377             */
1378             for (v=0; v < (ssize_t) kernel_info->height; v++)
1379             {
1380               for (u=0; u < (ssize_t) kernel_info->width; u++)
1381               {
1382                 pixel+=(*k)*pixels[i];
1383                 k++;
1384                 pixels+=GetPixelChannels(image);
1385               }
1386               pixels+=image->columns*GetPixelChannels(image);
1387             }
1388             q[channel]=ClampToQuantum(pixel);
1389             continue;
1390           }
1391         /*
1392           Alpha blending.
1393         */
1394         gamma=0.0;
1395         for (v=0; v < (ssize_t) kernel_info->height; v++)
1396         {
1397           for (u=0; u < (ssize_t) kernel_info->width; u++)
1398           {
1399             alpha=(MagickRealType) (QuantumScale*GetPixelAlpha(image,pixels));
1400             pixel+=(*k)*alpha*pixels[i];
1401             gamma+=(*k)*alpha;
1402             k++;
1403             pixels+=GetPixelChannels(image);
1404           }
1405           pixels+=image->columns*GetPixelChannels(image);
1406         }
1407         gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
1408         q[channel]=ClampToQuantum(gamma*pixel);
1409       }
1410       p+=GetPixelChannels(image);
1411       q+=GetPixelChannels(convolve_image);
1412     }
1413     if (SyncCacheViewAuthenticPixels(convolve_view,exception) == MagickFalse)
1414       status=MagickFalse;
1415     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1416       {
1417         MagickBooleanType
1418           proceed;
1419
1420 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1421   #pragma omp critical (MagickCore_ConvolveImage)
1422 #endif
1423         proceed=SetImageProgress(image,ConvolveImageTag,progress++,image->rows);
1424         if (proceed == MagickFalse)
1425           status=MagickFalse;
1426       }
1427   }
1428   convolve_image->type=image->type;
1429   convolve_view=DestroyCacheView(convolve_view);
1430   image_view=DestroyCacheView(image_view);
1431   if (status == MagickFalse)
1432     convolve_image=DestroyImage(convolve_image);
1433   return(convolve_image);
1434 }
1435 \f
1436 /*
1437 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1438 %                                                                             %
1439 %                                                                             %
1440 %                                                                             %
1441 %     D e s p e c k l e I m a g e                                             %
1442 %                                                                             %
1443 %                                                                             %
1444 %                                                                             %
1445 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1446 %
1447 %  DespeckleImage() reduces the speckle noise in an image while perserving the
1448 %  edges of the original image.
1449 %
1450 %  The format of the DespeckleImage method is:
1451 %
1452 %      Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1453 %
1454 %  A description of each parameter follows:
1455 %
1456 %    o image: the image.
1457 %
1458 %    o exception: return any errors or warnings in this structure.
1459 %
1460 */
1461
1462 static void Hull(const ssize_t x_offset,const ssize_t y_offset,
1463   const size_t columns,const size_t rows,Quantum *f,Quantum *g,
1464   const int polarity)
1465 {
1466   MagickRealType
1467     v;
1468
1469   register Quantum
1470     *p,
1471     *q,
1472     *r,
1473     *s;
1474
1475   register ssize_t
1476     x;
1477
1478   ssize_t
1479     y;
1480
1481   assert(f != (Quantum *) NULL);
1482   assert(g != (Quantum *) NULL);
1483   p=f+(columns+2);
1484   q=g+(columns+2);
1485   r=p+(y_offset*((ssize_t) columns+2)+x_offset);
1486   for (y=0; y < (ssize_t) rows; y++)
1487   {
1488     p++;
1489     q++;
1490     r++;
1491     if (polarity > 0)
1492       for (x=(ssize_t) columns; x != 0; x--)
1493       {
1494         v=(MagickRealType) (*p);
1495         if ((MagickRealType) *r >= (v+(MagickRealType) ScaleCharToQuantum(2)))
1496           v+=ScaleCharToQuantum(1);
1497         *q=(Quantum) v;
1498         p++;
1499         q++;
1500         r++;
1501       }
1502     else
1503       for (x=(ssize_t) columns; x != 0; x--)
1504       {
1505         v=(MagickRealType) (*p);
1506         if ((MagickRealType) *r <= (v-(MagickRealType) ScaleCharToQuantum(2)))
1507           v-=(ssize_t) ScaleCharToQuantum(1);
1508         *q=(Quantum) v;
1509         p++;
1510         q++;
1511         r++;
1512       }
1513     p++;
1514     q++;
1515     r++;
1516   }
1517   p=f+(columns+2);
1518   q=g+(columns+2);
1519   r=q+(y_offset*((ssize_t) columns+2)+x_offset);
1520   s=q-(y_offset*((ssize_t) columns+2)+x_offset);
1521   for (y=0; y < (ssize_t) rows; y++)
1522   {
1523     p++;
1524     q++;
1525     r++;
1526     s++;
1527     if (polarity > 0)
1528       for (x=(ssize_t) columns; x != 0; x--)
1529       {
1530         v=(MagickRealType) (*q);
1531         if (((MagickRealType) *s >=
1532              (v+(MagickRealType) ScaleCharToQuantum(2))) &&
1533             ((MagickRealType) *r > v))
1534           v+=ScaleCharToQuantum(1);
1535         *p=(Quantum) v;
1536         p++;
1537         q++;
1538         r++;
1539         s++;
1540       }
1541     else
1542       for (x=(ssize_t) columns; x != 0; x--)
1543       {
1544         v=(MagickRealType) (*q);
1545         if (((MagickRealType) *s <=
1546              (v-(MagickRealType) ScaleCharToQuantum(2))) &&
1547             ((MagickRealType) *r < v))
1548           v-=(MagickRealType) ScaleCharToQuantum(1);
1549         *p=(Quantum) v;
1550         p++;
1551         q++;
1552         r++;
1553         s++;
1554       }
1555     p++;
1556     q++;
1557     r++;
1558     s++;
1559   }
1560 }
1561
1562 MagickExport Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
1563 {
1564 #define DespeckleImageTag  "Despeckle/Image"
1565
1566   CacheView
1567     *despeckle_view,
1568     *image_view;
1569
1570   Image
1571     *despeckle_image;
1572
1573   MagickBooleanType
1574     status;
1575
1576   Quantum
1577     *restrict buffers,
1578     *restrict pixels;
1579
1580   register ssize_t
1581     i;
1582
1583   size_t
1584     length;
1585
1586   static const ssize_t
1587     X[4] = {0, 1, 1,-1},
1588     Y[4] = {1, 0, 1, 1};
1589
1590   /*
1591     Allocate despeckled image.
1592   */
1593   assert(image != (const Image *) NULL);
1594   assert(image->signature == MagickSignature);
1595   if (image->debug != MagickFalse)
1596     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1597   assert(exception != (ExceptionInfo *) NULL);
1598   assert(exception->signature == MagickSignature);
1599   despeckle_image=CloneImage(image,0,0,MagickTrue,exception);
1600   if (despeckle_image == (Image *) NULL)
1601     return((Image *) NULL);
1602   status=SetImageStorageClass(despeckle_image,DirectClass,exception);
1603   if (status == MagickFalse)
1604     {
1605       despeckle_image=DestroyImage(despeckle_image);
1606       return((Image *) NULL);
1607     }
1608   /*
1609     Allocate image buffers.
1610   */
1611   length=(size_t) ((image->columns+2)*(image->rows+2));
1612   pixels=(Quantum *) AcquireQuantumMemory(length,2*sizeof(*pixels));
1613   buffers=(Quantum *) AcquireQuantumMemory(length,2*sizeof(*pixels));
1614   if ((pixels == (Quantum *) NULL) || (buffers == (Quantum *) NULL))
1615     {
1616       if (buffers != (Quantum *) NULL)
1617         buffers=(Quantum *) RelinquishMagickMemory(buffers);
1618       if (pixels != (Quantum *) NULL)
1619         pixels=(Quantum *) RelinquishMagickMemory(pixels);
1620       despeckle_image=DestroyImage(despeckle_image);
1621       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1622     }
1623   /*
1624     Reduce speckle in the image.
1625   */
1626   status=MagickTrue;
1627   image_view=AcquireCacheView(image);
1628   despeckle_view=AcquireCacheView(despeckle_image);
1629   for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1630   {
1631     PixelChannel
1632        channel;
1633
1634     PixelTrait
1635       despeckle_traits,
1636       traits;
1637
1638     register Quantum
1639       *buffer,
1640       *pixel;
1641
1642     register ssize_t
1643       k,
1644       x;
1645
1646     ssize_t
1647       j,
1648       y;
1649
1650     if (status == MagickFalse)
1651       continue;
1652     traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
1653     channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
1654     despeckle_traits=GetPixelChannelMapTraits(despeckle_image,channel);
1655     if ((traits == UndefinedPixelTrait) ||
1656         (despeckle_traits == UndefinedPixelTrait))
1657       continue;
1658     if ((despeckle_traits & CopyPixelTrait) != 0)
1659       continue;
1660     pixel=pixels;
1661     (void) ResetMagickMemory(pixel,0,length*sizeof(*pixel));
1662     buffer=buffers;
1663     j=(ssize_t) image->columns+2;
1664     for (y=0; y < (ssize_t) image->rows; y++)
1665     {
1666       register const Quantum
1667         *restrict p;
1668
1669       p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1670       if (p == (const Quantum *) NULL)
1671         {
1672           status=MagickFalse;
1673           continue;
1674         }
1675       j++;
1676       for (x=0; x < (ssize_t) image->columns; x++)
1677       {
1678         pixel[j++]=p[i];
1679         p+=GetPixelChannels(image);
1680       }
1681       j++;
1682     }
1683     (void) ResetMagickMemory(buffer,0,length*sizeof(*buffer));
1684     for (k=0; k < 4; k++)
1685     {
1686       Hull(X[k],Y[k],image->columns,image->rows,pixel,buffer,1);
1687       Hull(-X[k],-Y[k],image->columns,image->rows,pixel,buffer,1);
1688       Hull(-X[k],-Y[k],image->columns,image->rows,pixel,buffer,-1);
1689       Hull(X[k],Y[k],image->columns,image->rows,pixel,buffer,-1);
1690     }
1691     j=(ssize_t) image->columns+2;
1692     for (y=0; y < (ssize_t) image->rows; y++)
1693     {
1694       MagickBooleanType
1695         sync;
1696
1697       register Quantum
1698         *restrict q;
1699
1700       q=GetCacheViewAuthenticPixels(despeckle_view,0,y,despeckle_image->columns,
1701         1,exception);
1702       if (q == (Quantum *) NULL)
1703         {
1704           status=MagickFalse;
1705           continue;
1706         }
1707       j++;
1708       for (x=0; x < (ssize_t) image->columns; x++)
1709       {
1710         q[channel]=pixel[j++];
1711         q+=GetPixelChannels(despeckle_image);
1712       }
1713       sync=SyncCacheViewAuthenticPixels(despeckle_view,exception);
1714       if (sync == MagickFalse)
1715         status=MagickFalse;
1716       j++;
1717     }
1718     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1719       {
1720         MagickBooleanType
1721           proceed;
1722
1723         proceed=SetImageProgress(image,DespeckleImageTag,(MagickOffsetType) i,
1724           GetPixelChannels(image));
1725         if (proceed == MagickFalse)
1726           status=MagickFalse;
1727       }
1728   }
1729   despeckle_view=DestroyCacheView(despeckle_view);
1730   image_view=DestroyCacheView(image_view);
1731   buffers=(Quantum *) RelinquishMagickMemory(buffers);
1732   pixels=(Quantum *) RelinquishMagickMemory(pixels);
1733   despeckle_image->type=image->type;
1734   if (status == MagickFalse)
1735     despeckle_image=DestroyImage(despeckle_image);
1736   return(despeckle_image);
1737 }
1738 \f
1739 /*
1740 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1741 %                                                                             %
1742 %                                                                             %
1743 %                                                                             %
1744 %     E d g e I m a g e                                                       %
1745 %                                                                             %
1746 %                                                                             %
1747 %                                                                             %
1748 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1749 %
1750 %  EdgeImage() finds edges in an image.  Radius defines the radius of the
1751 %  convolution filter.  Use a radius of 0 and EdgeImage() selects a suitable
1752 %  radius for you.
1753 %
1754 %  The format of the EdgeImage method is:
1755 %
1756 %      Image *EdgeImage(const Image *image,const double radius,
1757 %        const double sigma,ExceptionInfo *exception)
1758 %
1759 %  A description of each parameter follows:
1760 %
1761 %    o image: the image.
1762 %
1763 %    o radius: the radius of the pixel neighborhood.
1764 %
1765 %    o sigma: the standard deviation of the Gaussian, in pixels.
1766 %
1767 %    o exception: return any errors or warnings in this structure.
1768 %
1769 */
1770 MagickExport Image *EdgeImage(const Image *image,const double radius,
1771   const double sigma,ExceptionInfo *exception)
1772 {
1773   Image
1774     *edge_image;
1775
1776   KernelInfo
1777     *kernel_info;
1778
1779   register ssize_t
1780     i;
1781
1782   size_t
1783     width;
1784
1785   ssize_t
1786     j,
1787     u,
1788     v;
1789
1790   assert(image != (const Image *) NULL);
1791   assert(image->signature == MagickSignature);
1792   if (image->debug != MagickFalse)
1793     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1794   assert(exception != (ExceptionInfo *) NULL);
1795   assert(exception->signature == MagickSignature);
1796   width=GetOptimalKernelWidth2D(radius,sigma);
1797   kernel_info=AcquireKernelInfo((const char *) NULL);
1798   if (kernel_info == (KernelInfo *) NULL)
1799     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1800   kernel_info->width=width;
1801   kernel_info->height=width;
1802   kernel_info->values=(double *) AcquireAlignedMemory(kernel_info->width,
1803     kernel_info->width*sizeof(*kernel_info->values));
1804   if (kernel_info->values == (double *) NULL)
1805     {
1806       kernel_info=DestroyKernelInfo(kernel_info);
1807       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1808     }
1809   j=(ssize_t) kernel_info->width/2;
1810   i=0;
1811   for (v=(-j); v <= j; v++)
1812   {
1813     for (u=(-j); u <= j; u++)
1814     {
1815       kernel_info->values[i]=(-1.0);
1816       i++;
1817     }
1818   }
1819   kernel_info->values[i/2]=(double) (width*width-1.0);
1820   kernel_info->bias=image->bias;
1821   edge_image=ConvolveImage(image,kernel_info,exception);
1822   kernel_info=DestroyKernelInfo(kernel_info);
1823   return(edge_image);
1824 }
1825 \f
1826 /*
1827 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1828 %                                                                             %
1829 %                                                                             %
1830 %                                                                             %
1831 %     E m b o s s I m a g e                                                   %
1832 %                                                                             %
1833 %                                                                             %
1834 %                                                                             %
1835 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1836 %
1837 %  EmbossImage() returns a grayscale image with a three-dimensional effect.
1838 %  We convolve the image with a Gaussian operator of the given radius and
1839 %  standard deviation (sigma).  For reasonable results, radius should be
1840 %  larger than sigma.  Use a radius of 0 and Emboss() selects a suitable
1841 %  radius for you.
1842 %
1843 %  The format of the EmbossImage method is:
1844 %
1845 %      Image *EmbossImage(const Image *image,const double radius,
1846 %        const double sigma,ExceptionInfo *exception)
1847 %
1848 %  A description of each parameter follows:
1849 %
1850 %    o image: the image.
1851 %
1852 %    o radius: the radius of the pixel neighborhood.
1853 %
1854 %    o sigma: the standard deviation of the Gaussian, in pixels.
1855 %
1856 %    o exception: return any errors or warnings in this structure.
1857 %
1858 */
1859 MagickExport Image *EmbossImage(const Image *image,const double radius,
1860   const double sigma,ExceptionInfo *exception)
1861 {
1862   Image
1863     *emboss_image;
1864
1865   KernelInfo
1866     *kernel_info;
1867
1868   register ssize_t
1869     i;
1870
1871   size_t
1872     width;
1873
1874   ssize_t
1875     j,
1876     k,
1877     u,
1878     v;
1879
1880   assert(image != (const Image *) NULL);
1881   assert(image->signature == MagickSignature);
1882   if (image->debug != MagickFalse)
1883     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1884   assert(exception != (ExceptionInfo *) NULL);
1885   assert(exception->signature == MagickSignature);
1886   width=GetOptimalKernelWidth2D(radius,sigma);
1887   kernel_info=AcquireKernelInfo((const char *) NULL);
1888   if (kernel_info == (KernelInfo *) NULL)
1889     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1890   kernel_info->width=width;
1891   kernel_info->height=width;
1892   kernel_info->values=(double *) AcquireAlignedMemory(kernel_info->width,
1893     kernel_info->width*sizeof(*kernel_info->values));
1894   if (kernel_info->values == (double *) NULL)
1895     {
1896       kernel_info=DestroyKernelInfo(kernel_info);
1897       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1898     }
1899   j=(ssize_t) kernel_info->width/2;
1900   k=j;
1901   i=0;
1902   for (v=(-j); v <= j; v++)
1903   {
1904     for (u=(-j); u <= j; u++)
1905     {
1906       kernel_info->values[i]=(double) (((u < 0) || (v < 0) ? -8.0 : 8.0)*
1907         exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
1908         (2.0*MagickPI*MagickSigma*MagickSigma));
1909       if (u != k)
1910         kernel_info->values[i]=0.0;
1911       i++;
1912     }
1913     k--;
1914   }
1915   kernel_info->bias=image->bias;
1916   emboss_image=ConvolveImage(image,kernel_info,exception);
1917   kernel_info=DestroyKernelInfo(kernel_info);
1918   if (emboss_image != (Image *) NULL)
1919     (void) EqualizeImage(emboss_image,exception);
1920   return(emboss_image);
1921 }
1922 \f
1923 /*
1924 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1925 %                                                                             %
1926 %                                                                             %
1927 %                                                                             %
1928 %     G a u s s i a n B l u r I m a g e                                       %
1929 %                                                                             %
1930 %                                                                             %
1931 %                                                                             %
1932 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1933 %
1934 %  GaussianBlurImage() blurs an image.  We convolve the image with a
1935 %  Gaussian operator of the given radius and standard deviation (sigma).
1936 %  For reasonable results, the radius should be larger than sigma.  Use a
1937 %  radius of 0 and GaussianBlurImage() selects a suitable radius for you
1938 %
1939 %  The format of the GaussianBlurImage method is:
1940 %
1941 %      Image *GaussianBlurImage(const Image *image,onst double radius,
1942 %        const double sigma,const double bias,ExceptionInfo *exception)
1943 %
1944 %  A description of each parameter follows:
1945 %
1946 %    o image: the image.
1947 %
1948 %    o radius: the radius of the Gaussian, in pixels, not counting the center
1949 %      pixel.
1950 %
1951 %    o sigma: the standard deviation of the Gaussian, in pixels.
1952 %
1953 %    o bias: the bias.
1954 %
1955 %    o exception: return any errors or warnings in this structure.
1956 %
1957 */
1958 MagickExport Image *GaussianBlurImage(const Image *image,const double radius,
1959   const double sigma,const double bias,ExceptionInfo *exception)
1960 {
1961   Image
1962     *blur_image;
1963
1964   KernelInfo
1965     *kernel_info;
1966
1967   register ssize_t
1968     i;
1969
1970   size_t
1971     width;
1972
1973   ssize_t
1974     j,
1975     u,
1976     v;
1977
1978   assert(image != (const Image *) NULL);
1979   assert(image->signature == MagickSignature);
1980   if (image->debug != MagickFalse)
1981     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1982   assert(exception != (ExceptionInfo *) NULL);
1983   assert(exception->signature == MagickSignature);
1984   width=GetOptimalKernelWidth2D(radius,sigma);
1985   kernel_info=AcquireKernelInfo((const char *) NULL);
1986   if (kernel_info == (KernelInfo *) NULL)
1987     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1988   (void) ResetMagickMemory(kernel_info,0,sizeof(*kernel_info));
1989   kernel_info->width=width;
1990   kernel_info->height=width;
1991   kernel_info->bias=bias;
1992   kernel_info->signature=MagickSignature;
1993   kernel_info->values=(double *) AcquireAlignedMemory(kernel_info->width,
1994     kernel_info->width*sizeof(*kernel_info->values));
1995   if (kernel_info->values == (double *) NULL)
1996     {
1997       kernel_info=DestroyKernelInfo(kernel_info);
1998       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
1999     }
2000   j=(ssize_t) kernel_info->width/2;
2001   i=0;
2002   for (v=(-j); v <= j; v++)
2003   {
2004     for (u=(-j); u <= j; u++)
2005     {
2006       kernel_info->values[i]=(double) (exp(-((double) u*u+v*v)/(2.0*
2007         MagickSigma*MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
2008       i++;
2009     }
2010   }
2011   blur_image=ConvolveImage(image,kernel_info,exception);
2012   kernel_info=DestroyKernelInfo(kernel_info);
2013   return(blur_image);
2014 }
2015 \f
2016 /*
2017 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2018 %                                                                             %
2019 %                                                                             %
2020 %                                                                             %
2021 %     M o t i o n B l u r I m a g e                                           %
2022 %                                                                             %
2023 %                                                                             %
2024 %                                                                             %
2025 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2026 %
2027 %  MotionBlurImage() simulates motion blur.  We convolve the image with a
2028 %  Gaussian operator of the given radius and standard deviation (sigma).
2029 %  For reasonable results, radius should be larger than sigma.  Use a
2030 %  radius of 0 and MotionBlurImage() selects a suitable radius for you.
2031 %  Angle gives the angle of the blurring motion.
2032 %
2033 %  Andrew Protano contributed this effect.
2034 %
2035 %  The format of the MotionBlurImage method is:
2036 %
2037 %    Image *MotionBlurImage(const Image *image,const double radius,
2038 %      const double sigma,const double angle,const double bias,
2039 %      ExceptionInfo *exception)
2040 %
2041 %  A description of each parameter follows:
2042 %
2043 %    o image: the image.
2044 %
2045 %    o radius: the radius of the Gaussian, in pixels, not counting
2046 %      the center pixel.
2047 %
2048 %    o sigma: the standard deviation of the Gaussian, in pixels.
2049 %
2050 %    o angle: Apply the effect along this angle.
2051 %
2052 %    o bias: the bias.
2053 %
2054 %    o exception: return any errors or warnings in this structure.
2055 %
2056 */
2057
2058 static double *GetMotionBlurKernel(const size_t width,const double sigma)
2059 {
2060   double
2061     *kernel,
2062     normalize;
2063
2064   register ssize_t
2065     i;
2066
2067   /*
2068    Generate a 1-D convolution kernel.
2069   */
2070   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2071   kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
2072   if (kernel == (double *) NULL)
2073     return(kernel);
2074   normalize=0.0;
2075   for (i=0; i < (ssize_t) width; i++)
2076   {
2077     kernel[i]=(double) (exp((-((double) i*i)/(double) (2.0*MagickSigma*
2078       MagickSigma)))/(MagickSQ2PI*MagickSigma));
2079     normalize+=kernel[i];
2080   }
2081   for (i=0; i < (ssize_t) width; i++)
2082     kernel[i]/=normalize;
2083   return(kernel);
2084 }
2085
2086 MagickExport Image *MotionBlurImage(const Image *image,const double radius,
2087   const double sigma,const double angle,const double bias,
2088   ExceptionInfo *exception)
2089 {
2090   CacheView
2091     *blur_view,
2092     *image_view;
2093
2094   double
2095     *kernel;
2096
2097   Image
2098     *blur_image;
2099
2100   MagickBooleanType
2101     status;
2102
2103   MagickOffsetType
2104     progress;
2105
2106   OffsetInfo
2107     *offset;
2108
2109   PointInfo
2110     point;
2111
2112   register ssize_t
2113     i;
2114
2115   size_t
2116     width;
2117
2118   ssize_t
2119     y;
2120
2121   assert(image != (Image *) NULL);
2122   assert(image->signature == MagickSignature);
2123   if (image->debug != MagickFalse)
2124     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2125   assert(exception != (ExceptionInfo *) NULL);
2126   width=GetOptimalKernelWidth1D(radius,sigma);
2127   kernel=GetMotionBlurKernel(width,sigma);
2128   if (kernel == (double *) NULL)
2129     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2130   offset=(OffsetInfo *) AcquireQuantumMemory(width,sizeof(*offset));
2131   if (offset == (OffsetInfo *) NULL)
2132     {
2133       kernel=(double *) RelinquishMagickMemory(kernel);
2134       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2135     }
2136   blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
2137   if (blur_image == (Image *) NULL)
2138     {
2139       kernel=(double *) RelinquishMagickMemory(kernel);
2140       offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2141       return((Image *) NULL);
2142     }
2143   if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
2144     {
2145       kernel=(double *) RelinquishMagickMemory(kernel);
2146       offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2147       blur_image=DestroyImage(blur_image);
2148       return((Image *) NULL);
2149     }
2150   point.x=(double) width*sin(DegreesToRadians(angle));
2151   point.y=(double) width*cos(DegreesToRadians(angle));
2152   for (i=0; i < (ssize_t) width; i++)
2153   {
2154     offset[i].x=(ssize_t) ceil((double) (i*point.y)/hypot(point.x,point.y)-0.5);
2155     offset[i].y=(ssize_t) ceil((double) (i*point.x)/hypot(point.x,point.y)-0.5);
2156   }
2157   /*
2158     Motion blur image.
2159   */
2160   status=MagickTrue;
2161   progress=0;
2162   image_view=AcquireCacheView(image);
2163   blur_view=AcquireCacheView(blur_image);
2164 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2165   #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
2166 #endif
2167   for (y=0; y < (ssize_t) image->rows; y++)
2168   {
2169     register const Quantum
2170       *restrict p;
2171
2172     register Quantum
2173       *restrict q;
2174
2175     register ssize_t
2176       x;
2177
2178     if (status == MagickFalse)
2179       continue;
2180     p=GetCacheViewVirtualPixels(blur_view,0,y,image->columns,1,exception);
2181     q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
2182       exception);
2183     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2184       {
2185         status=MagickFalse;
2186         continue;
2187       }
2188     for (x=0; x < (ssize_t) image->columns; x++)
2189     {
2190       register ssize_t
2191         i;
2192
2193       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2194       {
2195         MagickRealType
2196           alpha,
2197           gamma,
2198           pixel;
2199
2200         PixelChannel
2201           channel;
2202
2203         PixelTrait
2204           blur_traits,
2205           traits;
2206
2207         register const Quantum
2208           *restrict r;
2209
2210         register double
2211           *restrict k;
2212
2213         register ssize_t
2214           j;
2215
2216         traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
2217         channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
2218         blur_traits=GetPixelChannelMapTraits(blur_image,channel);
2219         if ((traits == UndefinedPixelTrait) ||
2220             (blur_traits == UndefinedPixelTrait))
2221           continue;
2222         if ((blur_traits & CopyPixelTrait) != 0)
2223           {
2224             q[channel]=p[i];
2225             continue;
2226           }
2227         k=kernel;
2228         pixel=bias;
2229         if ((blur_traits & BlendPixelTrait) == 0)
2230           {
2231             for (j=0; j < (ssize_t) width; j++)
2232             {
2233               r=GetCacheViewVirtualPixels(image_view,x+offset[j].x,y+
2234                 offset[j].y,1,1,exception);
2235               if (r == (const Quantum *) NULL)
2236                 {
2237                   status=MagickFalse;
2238                   continue;
2239                 }
2240               pixel+=(*k)*r[i];
2241               k++;
2242             }
2243             q[channel]=ClampToQuantum(pixel);
2244             continue;
2245           }
2246         alpha=0.0;
2247         gamma=0.0;
2248         for (j=0; j < (ssize_t) width; j++)
2249         {
2250           r=GetCacheViewVirtualPixels(image_view,x+offset[j].x,y+offset[j].y,1,
2251             1,exception);
2252           if (r == (const Quantum *) NULL)
2253             {
2254               status=MagickFalse;
2255               continue;
2256             }
2257           alpha=(MagickRealType) (QuantumScale*GetPixelAlpha(image,r));
2258           pixel+=(*k)*alpha*r[i];
2259           gamma+=(*k)*alpha;
2260           k++;
2261         }
2262         gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
2263         q[channel]=ClampToQuantum(gamma*pixel);
2264       }
2265       p+=GetPixelChannels(image);
2266       q+=GetPixelChannels(blur_image);
2267     }
2268     if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
2269       status=MagickFalse;
2270     if (image->progress_monitor != (MagickProgressMonitor) NULL)
2271       {
2272         MagickBooleanType
2273           proceed;
2274
2275 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2276   #pragma omp critical (MagickCore_MotionBlurImage)
2277 #endif
2278         proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
2279         if (proceed == MagickFalse)
2280           status=MagickFalse;
2281       }
2282   }
2283   blur_view=DestroyCacheView(blur_view);
2284   image_view=DestroyCacheView(image_view);
2285   kernel=(double *) RelinquishMagickMemory(kernel);
2286   offset=(OffsetInfo *) RelinquishMagickMemory(offset);
2287   if (status == MagickFalse)
2288     blur_image=DestroyImage(blur_image);
2289   return(blur_image);
2290 }
2291 \f
2292 /*
2293 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2294 %                                                                             %
2295 %                                                                             %
2296 %                                                                             %
2297 %     P r e v i e w I m a g e                                                 %
2298 %                                                                             %
2299 %                                                                             %
2300 %                                                                             %
2301 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2302 %
2303 %  PreviewImage() tiles 9 thumbnails of the specified image with an image
2304 %  processing operation applied with varying parameters.  This may be helpful
2305 %  pin-pointing an appropriate parameter for a particular image processing
2306 %  operation.
2307 %
2308 %  The format of the PreviewImages method is:
2309 %
2310 %      Image *PreviewImages(const Image *image,const PreviewType preview,
2311 %        ExceptionInfo *exception)
2312 %
2313 %  A description of each parameter follows:
2314 %
2315 %    o image: the image.
2316 %
2317 %    o preview: the image processing operation.
2318 %
2319 %    o exception: return any errors or warnings in this structure.
2320 %
2321 */
2322 MagickExport Image *PreviewImage(const Image *image,const PreviewType preview,
2323   ExceptionInfo *exception)
2324 {
2325 #define NumberTiles  9
2326 #define PreviewImageTag  "Preview/Image"
2327 #define DefaultPreviewGeometry  "204x204+10+10"
2328
2329   char
2330     factor[MaxTextExtent],
2331     label[MaxTextExtent];
2332
2333   double
2334     degrees,
2335     gamma,
2336     percentage,
2337     radius,
2338     sigma,
2339     threshold;
2340
2341   Image
2342     *images,
2343     *montage_image,
2344     *preview_image,
2345     *thumbnail;
2346
2347   ImageInfo
2348     *preview_info;
2349
2350   MagickBooleanType
2351     proceed;
2352
2353   MontageInfo
2354     *montage_info;
2355
2356   QuantizeInfo
2357     quantize_info;
2358
2359   RectangleInfo
2360     geometry;
2361
2362   register ssize_t
2363     i,
2364     x;
2365
2366   size_t
2367     colors;
2368
2369   ssize_t
2370     y;
2371
2372   /*
2373     Open output image file.
2374   */
2375   assert(image != (Image *) NULL);
2376   assert(image->signature == MagickSignature);
2377   if (image->debug != MagickFalse)
2378     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2379   colors=2;
2380   degrees=0.0;
2381   gamma=(-0.2f);
2382   preview_info=AcquireImageInfo();
2383   SetGeometry(image,&geometry);
2384   (void) ParseMetaGeometry(DefaultPreviewGeometry,&geometry.x,&geometry.y,
2385     &geometry.width,&geometry.height);
2386   images=NewImageList();
2387   percentage=12.5;
2388   GetQuantizeInfo(&quantize_info);
2389   radius=0.0;
2390   sigma=1.0;
2391   threshold=0.0;
2392   x=0;
2393   y=0;
2394   for (i=0; i < NumberTiles; i++)
2395   {
2396     thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
2397     if (thumbnail == (Image *) NULL)
2398       break;
2399     (void) SetImageProgressMonitor(thumbnail,(MagickProgressMonitor) NULL,
2400       (void *) NULL);
2401     (void) SetImageProperty(thumbnail,"label",DefaultTileLabel);
2402     if (i == (NumberTiles/2))
2403       {
2404         (void) QueryColorDatabase("#dfdfdf",&thumbnail->matte_color,exception);
2405         AppendImageToList(&images,thumbnail);
2406         continue;
2407       }
2408     switch (preview)
2409     {
2410       case RotatePreview:
2411       {
2412         degrees+=45.0;
2413         preview_image=RotateImage(thumbnail,degrees,exception);
2414         (void) FormatLocaleString(label,MaxTextExtent,"rotate %g",degrees);
2415         break;
2416       }
2417       case ShearPreview:
2418       {
2419         degrees+=5.0;
2420         preview_image=ShearImage(thumbnail,degrees,degrees,exception);
2421         (void) FormatLocaleString(label,MaxTextExtent,"shear %gx%g",
2422           degrees,2.0*degrees);
2423         break;
2424       }
2425       case RollPreview:
2426       {
2427         x=(ssize_t) ((i+1)*thumbnail->columns)/NumberTiles;
2428         y=(ssize_t) ((i+1)*thumbnail->rows)/NumberTiles;
2429         preview_image=RollImage(thumbnail,x,y,exception);
2430         (void) FormatLocaleString(label,MaxTextExtent,"roll %+.20gx%+.20g",
2431           (double) x,(double) y);
2432         break;
2433       }
2434       case HuePreview:
2435       {
2436         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2437         if (preview_image == (Image *) NULL)
2438           break;
2439         (void) FormatLocaleString(factor,MaxTextExtent,"100,100,%g",
2440           2.0*percentage);
2441         (void) ModulateImage(preview_image,factor,exception);
2442         (void) FormatLocaleString(label,MaxTextExtent,"modulate %s",factor);
2443         break;
2444       }
2445       case SaturationPreview:
2446       {
2447         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2448         if (preview_image == (Image *) NULL)
2449           break;
2450         (void) FormatLocaleString(factor,MaxTextExtent,"100,%g",
2451           2.0*percentage);
2452         (void) ModulateImage(preview_image,factor,exception);
2453         (void) FormatLocaleString(label,MaxTextExtent,"modulate %s",factor);
2454         break;
2455       }
2456       case BrightnessPreview:
2457       {
2458         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2459         if (preview_image == (Image *) NULL)
2460           break;
2461         (void) FormatLocaleString(factor,MaxTextExtent,"%g",2.0*percentage);
2462         (void) ModulateImage(preview_image,factor,exception);
2463         (void) FormatLocaleString(label,MaxTextExtent,"modulate %s",factor);
2464         break;
2465       }
2466       case GammaPreview:
2467       default:
2468       {
2469         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2470         if (preview_image == (Image *) NULL)
2471           break;
2472         gamma+=0.4f;
2473         (void) GammaImage(preview_image,gamma,exception);
2474         (void) FormatLocaleString(label,MaxTextExtent,"gamma %g",gamma);
2475         break;
2476       }
2477       case SpiffPreview:
2478       {
2479         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2480         if (preview_image != (Image *) NULL)
2481           for (x=0; x < i; x++)
2482             (void) ContrastImage(preview_image,MagickTrue,exception);
2483         (void) FormatLocaleString(label,MaxTextExtent,"contrast (%.20g)",
2484           (double) i+1);
2485         break;
2486       }
2487       case DullPreview:
2488       {
2489         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2490         if (preview_image == (Image *) NULL)
2491           break;
2492         for (x=0; x < i; x++)
2493           (void) ContrastImage(preview_image,MagickFalse,exception);
2494         (void) FormatLocaleString(label,MaxTextExtent,"+contrast (%.20g)",
2495           (double) i+1);
2496         break;
2497       }
2498       case GrayscalePreview:
2499       {
2500         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2501         if (preview_image == (Image *) NULL)
2502           break;
2503         colors<<=1;
2504         quantize_info.number_colors=colors;
2505         quantize_info.colorspace=GRAYColorspace;
2506         (void) QuantizeImage(&quantize_info,preview_image,exception);
2507         (void) FormatLocaleString(label,MaxTextExtent,
2508           "-colorspace gray -colors %.20g",(double) colors);
2509         break;
2510       }
2511       case QuantizePreview:
2512       {
2513         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2514         if (preview_image == (Image *) NULL)
2515           break;
2516         colors<<=1;
2517         quantize_info.number_colors=colors;
2518         (void) QuantizeImage(&quantize_info,preview_image,exception);
2519         (void) FormatLocaleString(label,MaxTextExtent,"colors %.20g",(double)
2520           colors);
2521         break;
2522       }
2523       case DespecklePreview:
2524       {
2525         for (x=0; x < (i-1); x++)
2526         {
2527           preview_image=DespeckleImage(thumbnail,exception);
2528           if (preview_image == (Image *) NULL)
2529             break;
2530           thumbnail=DestroyImage(thumbnail);
2531           thumbnail=preview_image;
2532         }
2533         preview_image=DespeckleImage(thumbnail,exception);
2534         if (preview_image == (Image *) NULL)
2535           break;
2536         (void) FormatLocaleString(label,MaxTextExtent,"despeckle (%.20g)",
2537           (double) i+1);
2538         break;
2539       }
2540       case ReduceNoisePreview:
2541       {
2542         preview_image=StatisticImage(thumbnail,NonpeakStatistic,(size_t) radius,
2543           (size_t) radius,exception);
2544         (void) FormatLocaleString(label,MaxTextExtent,"noise %g",radius);
2545         break;
2546       }
2547       case AddNoisePreview:
2548       {
2549         switch ((int) i)
2550         {
2551           case 0:
2552           {
2553             (void) CopyMagickString(factor,"uniform",MaxTextExtent);
2554             break;
2555           }
2556           case 1:
2557           {
2558             (void) CopyMagickString(factor,"gaussian",MaxTextExtent);
2559             break;
2560           }
2561           case 2:
2562           {
2563             (void) CopyMagickString(factor,"multiplicative",MaxTextExtent);
2564             break;
2565           }
2566           case 3:
2567           {
2568             (void) CopyMagickString(factor,"impulse",MaxTextExtent);
2569             break;
2570           }
2571           case 4:
2572           {
2573             (void) CopyMagickString(factor,"laplacian",MaxTextExtent);
2574             break;
2575           }
2576           case 5:
2577           {
2578             (void) CopyMagickString(factor,"Poisson",MaxTextExtent);
2579             break;
2580           }
2581           default:
2582           {
2583             (void) CopyMagickString(thumbnail->magick,"NULL",MaxTextExtent);
2584             break;
2585           }
2586         }
2587         preview_image=StatisticImage(thumbnail,NonpeakStatistic,(size_t) i,
2588           (size_t) i,exception);
2589         (void) FormatLocaleString(label,MaxTextExtent,"+noise %s",factor);
2590         break;
2591       }
2592       case SharpenPreview:
2593       {
2594         preview_image=SharpenImage(thumbnail,radius,sigma,image->bias,
2595           exception);
2596         (void) FormatLocaleString(label,MaxTextExtent,"sharpen %gx%g",
2597           radius,sigma);
2598         break;
2599       }
2600       case BlurPreview:
2601       {
2602         preview_image=BlurImage(thumbnail,radius,sigma,image->bias,exception);
2603         (void) FormatLocaleString(label,MaxTextExtent,"blur %gx%g",radius,
2604           sigma);
2605         break;
2606       }
2607       case ThresholdPreview:
2608       {
2609         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2610         if (preview_image == (Image *) NULL)
2611           break;
2612         (void) BilevelImage(thumbnail,
2613           (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
2614         (void) FormatLocaleString(label,MaxTextExtent,"threshold %g",
2615           (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
2616         break;
2617       }
2618       case EdgeDetectPreview:
2619       {
2620         preview_image=EdgeImage(thumbnail,radius,sigma,exception);
2621         (void) FormatLocaleString(label,MaxTextExtent,"edge %g",radius);
2622         break;
2623       }
2624       case SpreadPreview:
2625       {
2626         preview_image=SpreadImage(thumbnail,radius,thumbnail->interpolate,
2627           exception);
2628         (void) FormatLocaleString(label,MaxTextExtent,"spread %g",
2629           radius+0.5);
2630         break;
2631       }
2632       case SolarizePreview:
2633       {
2634         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2635         if (preview_image == (Image *) NULL)
2636           break;
2637         (void) SolarizeImage(preview_image,(double) QuantumRange*
2638           percentage/100.0,exception);
2639         (void) FormatLocaleString(label,MaxTextExtent,"solarize %g",
2640           (QuantumRange*percentage)/100.0);
2641         break;
2642       }
2643       case ShadePreview:
2644       {
2645         degrees+=10.0;
2646         preview_image=ShadeImage(thumbnail,MagickTrue,degrees,degrees,
2647           exception);
2648         (void) FormatLocaleString(label,MaxTextExtent,"shade %gx%g",
2649           degrees,degrees);
2650         break;
2651       }
2652       case RaisePreview:
2653       {
2654         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2655         if (preview_image == (Image *) NULL)
2656           break;
2657         geometry.width=(size_t) (2*i+2);
2658         geometry.height=(size_t) (2*i+2);
2659         geometry.x=i/2;
2660         geometry.y=i/2;
2661         (void) RaiseImage(preview_image,&geometry,MagickTrue,exception);
2662         (void) FormatLocaleString(label,MaxTextExtent,
2663           "raise %.20gx%.20g%+.20g%+.20g",(double) geometry.width,(double)
2664           geometry.height,(double) geometry.x,(double) geometry.y);
2665         break;
2666       }
2667       case SegmentPreview:
2668       {
2669         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2670         if (preview_image == (Image *) NULL)
2671           break;
2672         threshold+=0.4f;
2673         (void) SegmentImage(preview_image,RGBColorspace,MagickFalse,threshold,
2674           threshold,exception);
2675         (void) FormatLocaleString(label,MaxTextExtent,"segment %gx%g",
2676           threshold,threshold);
2677         break;
2678       }
2679       case SwirlPreview:
2680       {
2681         preview_image=SwirlImage(thumbnail,degrees,image->interpolate,
2682           exception);
2683         (void) FormatLocaleString(label,MaxTextExtent,"swirl %g",degrees);
2684         degrees+=45.0;
2685         break;
2686       }
2687       case ImplodePreview:
2688       {
2689         degrees+=0.1f;
2690         preview_image=ImplodeImage(thumbnail,degrees,image->interpolate,
2691           exception);
2692         (void) FormatLocaleString(label,MaxTextExtent,"implode %g",degrees);
2693         break;
2694       }
2695       case WavePreview:
2696       {
2697         degrees+=5.0f;
2698         preview_image=WaveImage(thumbnail,0.5*degrees,2.0*degrees,
2699           image->interpolate,exception);
2700         (void) FormatLocaleString(label,MaxTextExtent,"wave %gx%g",
2701           0.5*degrees,2.0*degrees);
2702         break;
2703       }
2704       case OilPaintPreview:
2705       {
2706         preview_image=OilPaintImage(thumbnail,(double) radius,(double) sigma,
2707           exception);
2708         (void) FormatLocaleString(label,MaxTextExtent,"charcoal %gx%g",
2709           radius,sigma);
2710         break;
2711       }
2712       case CharcoalDrawingPreview:
2713       {
2714         preview_image=CharcoalImage(thumbnail,(double) radius,(double) sigma,
2715           image->bias,exception);
2716         (void) FormatLocaleString(label,MaxTextExtent,"charcoal %gx%g",
2717           radius,sigma);
2718         break;
2719       }
2720       case JPEGPreview:
2721       {
2722         char
2723           filename[MaxTextExtent];
2724
2725         int
2726           file;
2727
2728         MagickBooleanType
2729           status;
2730
2731         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
2732         if (preview_image == (Image *) NULL)
2733           break;
2734         preview_info->quality=(size_t) percentage;
2735         (void) FormatLocaleString(factor,MaxTextExtent,"%.20g",(double)
2736           preview_info->quality);
2737         file=AcquireUniqueFileResource(filename);
2738         if (file != -1)
2739           file=close(file)-1;
2740         (void) FormatLocaleString(preview_image->filename,MaxTextExtent,
2741           "jpeg:%s",filename);
2742         status=WriteImage(preview_info,preview_image,exception);
2743         if (status != MagickFalse)
2744           {
2745             Image
2746               *quality_image;
2747
2748             (void) CopyMagickString(preview_info->filename,
2749               preview_image->filename,MaxTextExtent);
2750             quality_image=ReadImage(preview_info,exception);
2751             if (quality_image != (Image *) NULL)
2752               {
2753                 preview_image=DestroyImage(preview_image);
2754                 preview_image=quality_image;
2755               }
2756           }
2757         (void) RelinquishUniqueFileResource(preview_image->filename);
2758         if ((GetBlobSize(preview_image)/1024) >= 1024)
2759           (void) FormatLocaleString(label,MaxTextExtent,"quality %s\n%gmb ",
2760             factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
2761             1024.0/1024.0);
2762         else
2763           if (GetBlobSize(preview_image) >= 1024)
2764             (void) FormatLocaleString(label,MaxTextExtent,
2765               "quality %s\n%gkb ",factor,(double) ((MagickOffsetType)
2766               GetBlobSize(preview_image))/1024.0);
2767           else
2768             (void) FormatLocaleString(label,MaxTextExtent,"quality %s\n%.20gb ",
2769               factor,(double) ((MagickOffsetType) GetBlobSize(thumbnail)));
2770         break;
2771       }
2772     }
2773     thumbnail=DestroyImage(thumbnail);
2774     percentage+=12.5;
2775     radius+=0.5;
2776     sigma+=0.25;
2777     if (preview_image == (Image *) NULL)
2778       break;
2779     (void) DeleteImageProperty(preview_image,"label");
2780     (void) SetImageProperty(preview_image,"label",label);
2781     AppendImageToList(&images,preview_image);
2782     proceed=SetImageProgress(image,PreviewImageTag,(MagickOffsetType) i,
2783       NumberTiles);
2784     if (proceed == MagickFalse)
2785       break;
2786   }
2787   if (images == (Image *) NULL)
2788     {
2789       preview_info=DestroyImageInfo(preview_info);
2790       return((Image *) NULL);
2791     }
2792   /*
2793     Create the montage.
2794   */
2795   montage_info=CloneMontageInfo(preview_info,(MontageInfo *) NULL);
2796   (void) CopyMagickString(montage_info->filename,image->filename,MaxTextExtent);
2797   montage_info->shadow=MagickTrue;
2798   (void) CloneString(&montage_info->tile,"3x3");
2799   (void) CloneString(&montage_info->geometry,DefaultPreviewGeometry);
2800   (void) CloneString(&montage_info->frame,DefaultTileFrame);
2801   montage_image=MontageImages(images,montage_info,exception);
2802   montage_info=DestroyMontageInfo(montage_info);
2803   images=DestroyImageList(images);
2804   if (montage_image == (Image *) NULL)
2805     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2806   if (montage_image->montage != (char *) NULL)
2807     {
2808       /*
2809         Free image directory.
2810       */
2811       montage_image->montage=(char *) RelinquishMagickMemory(
2812         montage_image->montage);
2813       if (image->directory != (char *) NULL)
2814         montage_image->directory=(char *) RelinquishMagickMemory(
2815           montage_image->directory);
2816     }
2817   preview_info=DestroyImageInfo(preview_info);
2818   return(montage_image);
2819 }
2820 \f
2821 /*
2822 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2823 %                                                                             %
2824 %                                                                             %
2825 %                                                                             %
2826 %     R a d i a l B l u r I m a g e                                           %
2827 %                                                                             %
2828 %                                                                             %
2829 %                                                                             %
2830 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2831 %
2832 %  RadialBlurImage() applies a radial blur to the image.
2833 %
2834 %  Andrew Protano contributed this effect.
2835 %
2836 %  The format of the RadialBlurImage method is:
2837 %
2838 %    Image *RadialBlurImage(const Image *image,const double angle,
2839 %      const double blur,ExceptionInfo *exception)
2840 %
2841 %  A description of each parameter follows:
2842 %
2843 %    o image: the image.
2844 %
2845 %    o angle: the angle of the radial blur.
2846 %
2847 %    o blur: the blur.
2848 %
2849 %    o exception: return any errors or warnings in this structure.
2850 %
2851 */
2852 MagickExport Image *RadialBlurImage(const Image *image,
2853   const double angle,const double bias,ExceptionInfo *exception)
2854 {
2855   CacheView
2856     *blur_view,
2857     *image_view;
2858
2859   Image
2860     *blur_image;
2861
2862   MagickBooleanType
2863     status;
2864
2865   MagickOffsetType
2866     progress;
2867
2868   MagickRealType
2869     blur_radius,
2870     *cos_theta,
2871     offset,
2872     *sin_theta,
2873     theta;
2874
2875   PointInfo
2876     blur_center;
2877
2878   register ssize_t
2879     i;
2880
2881   size_t
2882     n;
2883
2884   ssize_t
2885     y;
2886
2887   /*
2888     Allocate blur image.
2889   */
2890   assert(image != (Image *) NULL);
2891   assert(image->signature == MagickSignature);
2892   if (image->debug != MagickFalse)
2893     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2894   assert(exception != (ExceptionInfo *) NULL);
2895   assert(exception->signature == MagickSignature);
2896   blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
2897   if (blur_image == (Image *) NULL)
2898     return((Image *) NULL);
2899   if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
2900     {
2901       blur_image=DestroyImage(blur_image);
2902       return((Image *) NULL);
2903     }
2904   blur_center.x=(double) image->columns/2.0;
2905   blur_center.y=(double) image->rows/2.0;
2906   blur_radius=hypot(blur_center.x,blur_center.y);
2907   n=(size_t) fabs(4.0*DegreesToRadians(angle)*sqrt((double) blur_radius)+2UL);
2908   theta=DegreesToRadians(angle)/(MagickRealType) (n-1);
2909   cos_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
2910     sizeof(*cos_theta));
2911   sin_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
2912     sizeof(*sin_theta));
2913   if ((cos_theta == (MagickRealType *) NULL) ||
2914       (sin_theta == (MagickRealType *) NULL))
2915     {
2916       blur_image=DestroyImage(blur_image);
2917       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2918     }
2919   offset=theta*(MagickRealType) (n-1)/2.0;
2920   for (i=0; i < (ssize_t) n; i++)
2921   {
2922     cos_theta[i]=cos((double) (theta*i-offset));
2923     sin_theta[i]=sin((double) (theta*i-offset));
2924   }
2925   /*
2926     Radial blur image.
2927   */
2928   status=MagickTrue;
2929   progress=0;
2930   image_view=AcquireCacheView(image);
2931   blur_view=AcquireCacheView(blur_image);
2932 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2933   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2934 #endif
2935   for (y=0; y < (ssize_t) image->rows; y++)
2936   {
2937     register const Quantum
2938       *restrict p;
2939
2940     register Quantum
2941       *restrict q;
2942
2943     register ssize_t
2944       x;
2945
2946     if (status == MagickFalse)
2947       continue;
2948     p=GetCacheViewVirtualPixels(blur_view,0,y,image->columns,1,exception);
2949     q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
2950       exception);
2951     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2952       {
2953         status=MagickFalse;
2954         continue;
2955       }
2956     for (x=0; x < (ssize_t) image->columns; x++)
2957     {
2958       MagickRealType
2959         radius;
2960
2961       PointInfo
2962         center;
2963
2964       register ssize_t
2965         i;
2966
2967       size_t
2968         step;
2969
2970       center.x=(double) x-blur_center.x;
2971       center.y=(double) y-blur_center.y;
2972       radius=hypot((double) center.x,center.y);
2973       if (radius == 0)
2974         step=1;
2975       else
2976         {
2977           step=(size_t) (blur_radius/radius);
2978           if (step == 0)
2979             step=1;
2980           else
2981             if (step >= n)
2982               step=n-1;
2983         }
2984       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2985       {
2986         MagickRealType
2987           gamma,
2988           pixel;
2989
2990         PixelChannel
2991           channel;
2992
2993         PixelTrait
2994           blur_traits,
2995           traits;
2996
2997         register const Quantum
2998           *restrict r;
2999
3000         register ssize_t
3001           j;
3002
3003         traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
3004         channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
3005         blur_traits=GetPixelChannelMapTraits(blur_image,channel);
3006         if ((traits == UndefinedPixelTrait) ||
3007             (blur_traits == UndefinedPixelTrait))
3008           continue;
3009         if ((blur_traits & CopyPixelTrait) != 0)
3010           {
3011             q[channel]=p[i];
3012             continue;
3013           }
3014         gamma=0.0;
3015         pixel=bias;
3016         if ((blur_traits & BlendPixelTrait) == 0)
3017           {
3018             for (j=0; j < (ssize_t) n; j+=(ssize_t) step)
3019             {
3020               r=GetCacheViewVirtualPixels(image_view, (ssize_t) (blur_center.x+
3021                 center.x*cos_theta[j]-center.y*sin_theta[j]+0.5),(ssize_t)
3022                 (blur_center.y+center.x*sin_theta[j]+center.y*cos_theta[j]+0.5),
3023                 1,1,exception);
3024               if (r == (const Quantum *) NULL)
3025                 {
3026                   status=MagickFalse;
3027                   continue;
3028                 }
3029               pixel+=r[i];
3030               gamma++;
3031             }
3032             gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
3033             q[channel]=ClampToQuantum(gamma*pixel);
3034             continue;
3035           }
3036         for (j=0; j < (ssize_t) n; j+=(ssize_t) step)
3037         {
3038           r=GetCacheViewVirtualPixels(image_view, (ssize_t) (blur_center.x+
3039             center.x*cos_theta[j]-center.y*sin_theta[j]+0.5),(ssize_t)
3040             (blur_center.y+center.x*sin_theta[j]+center.y*cos_theta[j]+0.5),
3041             1,1,exception);
3042           if (r == (const Quantum *) NULL)
3043             {
3044               status=MagickFalse;
3045               continue;
3046             }
3047           pixel+=GetPixelAlpha(image,r)*r[i];
3048           gamma+=GetPixelAlpha(image,r);
3049         }
3050         gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
3051         q[channel]=ClampToQuantum(gamma*pixel);
3052       }
3053       p+=GetPixelChannels(image);
3054       q+=GetPixelChannels(blur_image);
3055     }
3056     if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
3057       status=MagickFalse;
3058     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3059       {
3060         MagickBooleanType
3061           proceed;
3062
3063 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3064   #pragma omp critical (MagickCore_RadialBlurImage)
3065 #endif
3066         proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
3067         if (proceed == MagickFalse)
3068           status=MagickFalse;
3069       }
3070   }
3071   blur_view=DestroyCacheView(blur_view);
3072   image_view=DestroyCacheView(image_view);
3073   cos_theta=(MagickRealType *) RelinquishMagickMemory(cos_theta);
3074   sin_theta=(MagickRealType *) RelinquishMagickMemory(sin_theta);
3075   if (status == MagickFalse)
3076     blur_image=DestroyImage(blur_image);
3077   return(blur_image);
3078 }
3079 \f
3080 /*
3081 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3082 %                                                                             %
3083 %                                                                             %
3084 %                                                                             %
3085 %     S e l e c t i v e B l u r I m a g e                                     %
3086 %                                                                             %
3087 %                                                                             %
3088 %                                                                             %
3089 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3090 %
3091 %  SelectiveBlurImage() selectively blur pixels within a contrast threshold.
3092 %  It is similar to the unsharpen mask that sharpens everything with contrast
3093 %  above a certain threshold.
3094 %
3095 %  The format of the SelectiveBlurImage method is:
3096 %
3097 %      Image *SelectiveBlurImage(const Image *image,const double radius,
3098 %        const double sigma,const double threshold,const double bias,
3099 %        ExceptionInfo *exception)
3100 %
3101 %  A description of each parameter follows:
3102 %
3103 %    o image: the image.
3104 %
3105 %    o radius: the radius of the Gaussian, in pixels, not counting the center
3106 %      pixel.
3107 %
3108 %    o sigma: the standard deviation of the Gaussian, in pixels.
3109 %
3110 %    o threshold: only pixels within this contrast threshold are included
3111 %      in the blur operation.
3112 %
3113 %    o bias: the bias.
3114 %
3115 %    o exception: return any errors or warnings in this structure.
3116 %
3117 */
3118 MagickExport Image *SelectiveBlurImage(const Image *image,
3119   const double radius,const double sigma,const double threshold,
3120   const double bias,ExceptionInfo *exception)
3121 {
3122 #define SelectiveBlurImageTag  "SelectiveBlur/Image"
3123
3124   CacheView
3125     *blur_view,
3126     *image_view;
3127
3128   double
3129     *kernel;
3130
3131   Image
3132     *blur_image;
3133
3134   MagickBooleanType
3135     status;
3136
3137   MagickOffsetType
3138     progress;
3139
3140   register ssize_t
3141     i;
3142
3143   size_t
3144     center,
3145     width;
3146
3147   ssize_t
3148     j,
3149     u,
3150     v,
3151     y;
3152
3153   /*
3154     Initialize blur image attributes.
3155   */
3156   assert(image != (Image *) NULL);
3157   assert(image->signature == MagickSignature);
3158   if (image->debug != MagickFalse)
3159     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3160   assert(exception != (ExceptionInfo *) NULL);
3161   assert(exception->signature == MagickSignature);
3162   width=GetOptimalKernelWidth1D(radius,sigma);
3163   kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
3164   if (kernel == (double *) NULL)
3165     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3166   j=(ssize_t) width/2;
3167   i=0;
3168   for (v=(-j); v <= j; v++)
3169   {
3170     for (u=(-j); u <= j; u++)
3171       kernel[i++]=(double) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
3172         MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
3173   }
3174   if (image->debug != MagickFalse)
3175     {
3176       char
3177         format[MaxTextExtent],
3178         *message;
3179
3180       register const double
3181         *k;
3182
3183       ssize_t
3184         u,
3185         v;
3186
3187       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
3188         "  SelectiveBlurImage with %.20gx%.20g kernel:",(double) width,(double)
3189         width);
3190       message=AcquireString("");
3191       k=kernel;
3192       for (v=0; v < (ssize_t) width; v++)
3193       {
3194         *message='\0';
3195         (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) v);
3196         (void) ConcatenateString(&message,format);
3197         for (u=0; u < (ssize_t) width; u++)
3198         {
3199           (void) FormatLocaleString(format,MaxTextExtent,"%+f ",*k++);
3200           (void) ConcatenateString(&message,format);
3201         }
3202         (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
3203       }
3204       message=DestroyString(message);
3205     }
3206   blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
3207   if (blur_image == (Image *) NULL)
3208     return((Image *) NULL);
3209   if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
3210     {
3211       blur_image=DestroyImage(blur_image);
3212       return((Image *) NULL);
3213     }
3214   /*
3215     Threshold blur image.
3216   */
3217   status=MagickTrue;
3218   progress=0;
3219   center=(ssize_t) GetPixelChannels(image)*(image->columns+width)*(width/2L)+
3220     GetPixelChannels(image)*(width/2L);
3221   image_view=AcquireCacheView(image);
3222   blur_view=AcquireCacheView(blur_image);
3223 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3224   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3225 #endif
3226   for (y=0; y < (ssize_t) image->rows; y++)
3227   {
3228     double
3229       contrast;
3230
3231     MagickBooleanType
3232       sync;
3233
3234     register const Quantum
3235       *restrict p;
3236
3237     register Quantum
3238       *restrict q;
3239
3240     register ssize_t
3241       x;
3242
3243     if (status == MagickFalse)
3244       continue;
3245     p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
3246       (width/2L),image->columns+width,width,exception);
3247     q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
3248       exception);
3249     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3250       {
3251         status=MagickFalse;
3252         continue;
3253       }
3254     for (x=0; x < (ssize_t) image->columns; x++)
3255     {
3256       register ssize_t
3257         i;
3258
3259       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3260       {
3261         MagickRealType
3262           alpha,
3263           gamma,
3264           intensity,
3265           pixel;
3266
3267         PixelChannel
3268           channel;
3269
3270         PixelTrait
3271           blur_traits,
3272           traits;
3273
3274         register const double
3275           *restrict k;
3276
3277         register const Quantum
3278           *restrict pixels;
3279
3280         register ssize_t
3281           u;
3282
3283         ssize_t
3284           v;
3285
3286         traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
3287         channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
3288         blur_traits=GetPixelChannelMapTraits(blur_image,channel);
3289         if ((traits == UndefinedPixelTrait) ||
3290             (blur_traits == UndefinedPixelTrait))
3291           continue;
3292         if ((blur_traits & CopyPixelTrait) != 0)
3293           {
3294             q[channel]=p[center+i];
3295             continue;
3296           }
3297         k=kernel;
3298         pixel=bias;
3299         pixels=p;
3300         intensity=GetPixelIntensity(image,p+center);
3301         gamma=0.0;
3302         if ((blur_traits & BlendPixelTrait) == 0)
3303           {
3304             for (v=0; v < (ssize_t) width; v++)
3305             {
3306               for (u=0; u < (ssize_t) width; u++)
3307               {
3308                 contrast=GetPixelIntensity(image,pixels)-intensity;
3309                 if (fabs(contrast) < threshold)
3310                   {
3311                     pixel+=(*k)*pixels[i];
3312                     gamma+=(*k);
3313                   }
3314                 k++;
3315                 pixels+=GetPixelChannels(image);
3316               }
3317               pixels+=image->columns*GetPixelChannels(image);
3318             }
3319             if (fabs((double) gamma) < MagickEpsilon)
3320               {
3321                 q[channel]=p[center+i];
3322                 continue;
3323               }
3324             gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
3325             q[channel]=ClampToQuantum(gamma*pixel);
3326             continue;
3327           }
3328         for (v=0; v < (ssize_t) width; v++)
3329         {
3330           for (u=0; u < (ssize_t) width; u++)
3331           {
3332             contrast=GetPixelIntensity(image,pixels)-intensity;
3333             if (fabs(contrast) < threshold)
3334               {
3335                 alpha=(MagickRealType) (QuantumScale*
3336                   GetPixelAlpha(image,pixels));
3337                 pixel+=(*k)*alpha*pixels[i];
3338                 gamma+=(*k)*alpha;
3339               }
3340             k++;
3341             pixels+=GetPixelChannels(image);
3342           }
3343           pixels+=image->columns*GetPixelChannels(image);
3344         }
3345         if (fabs((double) gamma) < MagickEpsilon)
3346           {
3347             q[channel]=p[center+i];
3348             continue;
3349           }
3350         gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
3351         q[channel]=ClampToQuantum(gamma*pixel);
3352       }
3353       p+=GetPixelChannels(image);
3354       q+=GetPixelChannels(blur_image);
3355     }
3356     sync=SyncCacheViewAuthenticPixels(blur_view,exception);
3357     if (sync == MagickFalse)
3358       status=MagickFalse;
3359     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3360       {
3361         MagickBooleanType
3362           proceed;
3363
3364 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3365   #pragma omp critical (MagickCore_SelectiveBlurImage)
3366 #endif
3367         proceed=SetImageProgress(image,SelectiveBlurImageTag,progress++,
3368           image->rows);
3369         if (proceed == MagickFalse)
3370           status=MagickFalse;
3371       }
3372   }
3373   blur_image->type=image->type;
3374   blur_view=DestroyCacheView(blur_view);
3375   image_view=DestroyCacheView(image_view);
3376   kernel=(double *) RelinquishMagickMemory(kernel);
3377   if (status == MagickFalse)
3378     blur_image=DestroyImage(blur_image);
3379   return(blur_image);
3380 }
3381 \f
3382 /*
3383 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3384 %                                                                             %
3385 %                                                                             %
3386 %                                                                             %
3387 %     S h a d e I m a g e                                                     %
3388 %                                                                             %
3389 %                                                                             %
3390 %                                                                             %
3391 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3392 %
3393 %  ShadeImage() shines a distant light on an image to create a
3394 %  three-dimensional effect. You control the positioning of the light with
3395 %  azimuth and elevation; azimuth is measured in degrees off the x axis
3396 %  and elevation is measured in pixels above the Z axis.
3397 %
3398 %  The format of the ShadeImage method is:
3399 %
3400 %      Image *ShadeImage(const Image *image,const MagickBooleanType gray,
3401 %        const double azimuth,const double elevation,ExceptionInfo *exception)
3402 %
3403 %  A description of each parameter follows:
3404 %
3405 %    o image: the image.
3406 %
3407 %    o gray: A value other than zero shades the intensity of each pixel.
3408 %
3409 %    o azimuth, elevation:  Define the light source direction.
3410 %
3411 %    o exception: return any errors or warnings in this structure.
3412 %
3413 */
3414 MagickExport Image *ShadeImage(const Image *image,const MagickBooleanType gray,
3415   const double azimuth,const double elevation,ExceptionInfo *exception)
3416 {
3417 #define ShadeImageTag  "Shade/Image"
3418
3419   CacheView
3420     *image_view,
3421     *shade_view;
3422
3423   Image
3424     *shade_image;
3425
3426   MagickBooleanType
3427     status;
3428
3429   MagickOffsetType
3430     progress;
3431
3432   PrimaryInfo
3433     light;
3434
3435   ssize_t
3436     y;
3437
3438   /*
3439     Initialize shaded image attributes.
3440   */
3441   assert(image != (const Image *) NULL);
3442   assert(image->signature == MagickSignature);
3443   if (image->debug != MagickFalse)
3444     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3445   assert(exception != (ExceptionInfo *) NULL);
3446   assert(exception->signature == MagickSignature);
3447   shade_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
3448   if (shade_image == (Image *) NULL)
3449     return((Image *) NULL);
3450   if (SetImageStorageClass(shade_image,DirectClass,exception) == MagickFalse)
3451     {
3452       shade_image=DestroyImage(shade_image);
3453       return((Image *) NULL);
3454     }
3455   /*
3456     Compute the light vector.
3457   */
3458   light.x=(double) QuantumRange*cos(DegreesToRadians(azimuth))*
3459     cos(DegreesToRadians(elevation));
3460   light.y=(double) QuantumRange*sin(DegreesToRadians(azimuth))*
3461     cos(DegreesToRadians(elevation));
3462   light.z=(double) QuantumRange*sin(DegreesToRadians(elevation));
3463   /*
3464     Shade image.
3465   */
3466   status=MagickTrue;
3467   progress=0;
3468   image_view=AcquireCacheView(image);
3469   shade_view=AcquireCacheView(shade_image);
3470 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3471   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3472 #endif
3473   for (y=0; y < (ssize_t) image->rows; y++)
3474   {
3475     MagickRealType
3476       distance,
3477       normal_distance,
3478       shade;
3479
3480     PrimaryInfo
3481       normal;
3482
3483     register const Quantum
3484       *restrict center,
3485       *restrict p,
3486       *restrict post,
3487       *restrict pre;
3488
3489     register Quantum
3490       *restrict q;
3491
3492     register ssize_t
3493       x;
3494
3495     if (status == MagickFalse)
3496       continue;
3497     p=GetCacheViewVirtualPixels(image_view,-1,y-1,image->columns+2,3,exception);
3498     q=QueueCacheViewAuthenticPixels(shade_view,0,y,shade_image->columns,1,
3499       exception);
3500     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3501       {
3502         status=MagickFalse;
3503         continue;
3504       }
3505     /*
3506       Shade this row of pixels.
3507     */
3508     normal.z=2.0*(double) QuantumRange;  /* constant Z of surface normal */
3509     pre=p+GetPixelChannels(image);
3510     center=pre+(image->columns+2)*GetPixelChannels(image);
3511     post=center+(image->columns+2)*GetPixelChannels(image);
3512     for (x=0; x < (ssize_t) image->columns; x++)
3513     {
3514       register ssize_t
3515         i;
3516
3517       /*
3518         Determine the surface normal and compute shading.
3519       */
3520       normal.x=(double) (GetPixelIntensity(image,pre-GetPixelChannels(image))+
3521         GetPixelIntensity(image,center-GetPixelChannels(image))+
3522         GetPixelIntensity(image,post-GetPixelChannels(image))-
3523         GetPixelIntensity(image,pre+GetPixelChannels(image))-
3524         GetPixelIntensity(image,center+GetPixelChannels(image))-
3525         GetPixelIntensity(image,post+GetPixelChannels(image)));
3526       normal.y=(double) (GetPixelIntensity(image,post-GetPixelChannels(image))+
3527         GetPixelIntensity(image,post)+GetPixelIntensity(image,post+
3528         GetPixelChannels(image))-GetPixelIntensity(image,pre-
3529         GetPixelChannels(image))-GetPixelIntensity(image,pre)-
3530         GetPixelIntensity(image,pre+GetPixelChannels(image)));
3531       if ((normal.x == 0.0) && (normal.y == 0.0))
3532         shade=light.z;
3533       else
3534         {
3535           shade=0.0;
3536           distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
3537           if (distance > MagickEpsilon)
3538             {
3539               normal_distance=
3540                 normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
3541               if (normal_distance > (MagickEpsilon*MagickEpsilon))
3542                 shade=distance/sqrt((double) normal_distance);
3543             }
3544         }
3545       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3546       {
3547         PixelChannel
3548           channel;
3549
3550         PixelTrait
3551           shade_traits,
3552           traits;
3553
3554         traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
3555         channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
3556         shade_traits=GetPixelChannelMapTraits(shade_image,channel);
3557         if ((traits == UndefinedPixelTrait) ||
3558             (shade_traits == UndefinedPixelTrait))
3559           continue;
3560         if ((shade_traits & CopyPixelTrait) != 0)
3561           {
3562             q[channel]=center[i];
3563             continue;
3564           }
3565         if (gray != MagickFalse)
3566           {
3567             q[channel]=ClampToQuantum(shade);
3568             continue;
3569           }
3570         q[channel]=ClampToQuantum(QuantumScale*shade*center[i]);
3571       }
3572       pre+=GetPixelChannels(image);
3573       center+=GetPixelChannels(image);
3574       post+=GetPixelChannels(image);
3575       q+=GetPixelChannels(shade_image);
3576     }
3577     if (SyncCacheViewAuthenticPixels(shade_view,exception) == MagickFalse)
3578       status=MagickFalse;
3579     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3580       {
3581         MagickBooleanType
3582           proceed;
3583
3584 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3585   #pragma omp critical (MagickCore_ShadeImage)
3586 #endif
3587         proceed=SetImageProgress(image,ShadeImageTag,progress++,image->rows);
3588         if (proceed == MagickFalse)
3589           status=MagickFalse;
3590       }
3591   }
3592   shade_view=DestroyCacheView(shade_view);
3593   image_view=DestroyCacheView(image_view);
3594   if (status == MagickFalse)
3595     shade_image=DestroyImage(shade_image);
3596   return(shade_image);
3597 }
3598 \f
3599 /*
3600 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3601 %                                                                             %
3602 %                                                                             %
3603 %                                                                             %
3604 %     S h a r p e n I m a g e                                                 %
3605 %                                                                             %
3606 %                                                                             %
3607 %                                                                             %
3608 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3609 %
3610 %  SharpenImage() sharpens the image.  We convolve the image with a Gaussian
3611 %  operator of the given radius and standard deviation (sigma).  For
3612 %  reasonable results, radius should be larger than sigma.  Use a radius of 0
3613 %  and SharpenImage() selects a suitable radius for you.
3614 %
3615 %  Using a separable kernel would be faster, but the negative weights cancel
3616 %  out on the corners of the kernel producing often undesirable ringing in the
3617 %  filtered result; this can be avoided by using a 2D gaussian shaped image
3618 %  sharpening kernel instead.
3619 %
3620 %  The format of the SharpenImage method is:
3621 %
3622 %    Image *SharpenImage(const Image *image,const double radius,
3623 %      const double sigma,const double bias,ExceptionInfo *exception)
3624 %
3625 %  A description of each parameter follows:
3626 %
3627 %    o image: the image.
3628 %
3629 %    o radius: the radius of the Gaussian, in pixels, not counting the center
3630 %      pixel.
3631 %
3632 %    o sigma: the standard deviation of the Laplacian, in pixels.
3633 %
3634 %    o bias: bias.
3635 %
3636 %    o exception: return any errors or warnings in this structure.
3637 %
3638 */
3639 MagickExport Image *SharpenImage(const Image *image,const double radius,
3640   const double sigma,const double bias,ExceptionInfo *exception)
3641 {
3642   double
3643     normalize;
3644
3645   Image
3646     *sharp_image;
3647
3648   KernelInfo
3649     *kernel_info;
3650
3651   register ssize_t
3652     i;
3653
3654   size_t
3655     width;
3656
3657   ssize_t
3658     j,
3659     u,
3660     v;
3661
3662   assert(image != (const Image *) NULL);
3663   assert(image->signature == MagickSignature);
3664   if (image->debug != MagickFalse)
3665     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3666   assert(exception != (ExceptionInfo *) NULL);
3667   assert(exception->signature == MagickSignature);
3668   width=GetOptimalKernelWidth2D(radius,sigma);
3669   kernel_info=AcquireKernelInfo((const char *) NULL);
3670   if (kernel_info == (KernelInfo *) NULL)
3671     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3672   (void) ResetMagickMemory(kernel_info,0,sizeof(*kernel_info));
3673   kernel_info->width=width;
3674   kernel_info->height=width;
3675   kernel_info->bias=bias;
3676   kernel_info->signature=MagickSignature;
3677   kernel_info->values=(double *) AcquireAlignedMemory(kernel_info->width,
3678     kernel_info->width*sizeof(*kernel_info->values));
3679   if (kernel_info->values == (double *) NULL)
3680     {
3681       kernel_info=DestroyKernelInfo(kernel_info);
3682       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3683     }
3684   normalize=0.0;
3685   j=(ssize_t) kernel_info->width/2;
3686   i=0;
3687   for (v=(-j); v <= j; v++)
3688   {
3689     for (u=(-j); u <= j; u++)
3690     {
3691       kernel_info->values[i]=(double) (-exp(-((double) u*u+v*v)/(2.0*
3692         MagickSigma*MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
3693       normalize+=kernel_info->values[i];
3694       i++;
3695     }
3696   }
3697   kernel_info->values[i/2]=(double) ((-2.0)*normalize);
3698   sharp_image=ConvolveImage(image,kernel_info,exception);
3699   kernel_info=DestroyKernelInfo(kernel_info);
3700   return(sharp_image);
3701 }
3702 \f
3703 /*
3704 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3705 %                                                                             %
3706 %                                                                             %
3707 %                                                                             %
3708 %     S p r e a d I m a g e                                                   %
3709 %                                                                             %
3710 %                                                                             %
3711 %                                                                             %
3712 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3713 %
3714 %  SpreadImage() is a special effects method that randomly displaces each
3715 %  pixel in a block defined by the radius parameter.
3716 %
3717 %  The format of the SpreadImage method is:
3718 %
3719 %      Image *SpreadImage(const Image *image,const double radius,
3720 %        const PixelInterpolateMethod method,ExceptionInfo *exception)
3721 %
3722 %  A description of each parameter follows:
3723 %
3724 %    o image: the image.
3725 %
3726 %    o radius:  choose a random pixel in a neighborhood of this extent.
3727 %
3728 %    o method:  the pixel interpolation method.
3729 %
3730 %    o exception: return any errors or warnings in this structure.
3731 %
3732 */
3733 MagickExport Image *SpreadImage(const Image *image,const double radius,
3734   const PixelInterpolateMethod method,ExceptionInfo *exception)
3735 {
3736 #define SpreadImageTag  "Spread/Image"
3737
3738   CacheView
3739     *image_view,
3740     *spread_view;
3741
3742   Image
3743     *spread_image;
3744
3745   MagickBooleanType
3746     status;
3747
3748   MagickOffsetType
3749     progress;
3750
3751   RandomInfo
3752     **restrict random_info;
3753
3754   size_t
3755     width;
3756
3757   ssize_t
3758     y;
3759
3760   /*
3761     Initialize spread image attributes.
3762   */
3763   assert(image != (Image *) NULL);
3764   assert(image->signature == MagickSignature);
3765   if (image->debug != MagickFalse)
3766     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3767   assert(exception != (ExceptionInfo *) NULL);
3768   assert(exception->signature == MagickSignature);
3769   spread_image=CloneImage(image,image->columns,image->rows,MagickTrue,
3770     exception);
3771   if (spread_image == (Image *) NULL)
3772     return((Image *) NULL);
3773   if (SetImageStorageClass(spread_image,DirectClass,exception) == MagickFalse)
3774     {
3775       spread_image=DestroyImage(spread_image);
3776       return((Image *) NULL);
3777     }
3778   /*
3779     Spread image.
3780   */
3781   status=MagickTrue;
3782   progress=0;
3783   width=GetOptimalKernelWidth1D(radius,0.5);
3784   random_info=AcquireRandomInfoThreadSet();
3785   image_view=AcquireCacheView(image);
3786   spread_view=AcquireCacheView(spread_image);
3787 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3788   #pragma omp parallel for schedule(dynamic,4) shared(progress,status) omp_throttle(1)
3789 #endif
3790   for (y=0; y < (ssize_t) image->rows; y++)
3791   {
3792     const int
3793       id = GetOpenMPThreadId();
3794
3795     register const Quantum
3796       *restrict p;
3797
3798     register Quantum
3799       *restrict q;
3800
3801     register ssize_t
3802       x;
3803
3804     if (status == MagickFalse)
3805       continue;
3806     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3807     q=QueueCacheViewAuthenticPixels(spread_view,0,y,spread_image->columns,1,
3808       exception);
3809     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3810       {
3811         status=MagickFalse;
3812         continue;
3813       }
3814     for (x=0; x < (ssize_t) image->columns; x++)
3815     {
3816       PointInfo
3817         point;
3818
3819       point.x=GetPseudoRandomValue(random_info[id]);
3820       point.y=GetPseudoRandomValue(random_info[id]);
3821       status=InterpolatePixelChannels(image,image_view,spread_image,method,
3822         (double) x+width*point.x-0.5,(double) y+width*point.y-0.5,q,exception);
3823       q+=GetPixelChannels(spread_image);
3824     }
3825     if (SyncCacheViewAuthenticPixels(spread_view,exception) == MagickFalse)
3826       status=MagickFalse;
3827     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3828       {
3829         MagickBooleanType
3830           proceed;
3831
3832 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3833   #pragma omp critical (MagickCore_SpreadImage)
3834 #endif
3835         proceed=SetImageProgress(image,SpreadImageTag,progress++,image->rows);
3836         if (proceed == MagickFalse)
3837           status=MagickFalse;
3838       }
3839   }
3840   spread_view=DestroyCacheView(spread_view);
3841   image_view=DestroyCacheView(image_view);
3842   random_info=DestroyRandomInfoThreadSet(random_info);
3843   return(spread_image);
3844 }
3845 \f
3846 /*
3847 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3848 %                                                                             %
3849 %                                                                             %
3850 %                                                                             %
3851 %     S t a t i s t i c I m a g e                                             %
3852 %                                                                             %
3853 %                                                                             %
3854 %                                                                             %
3855 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3856 %
3857 %  StatisticImage() makes each pixel the min / max / median / mode / etc. of
3858 %  the neighborhood of the specified width and height.
3859 %
3860 %  The format of the StatisticImage method is:
3861 %
3862 %      Image *StatisticImage(const Image *image,const StatisticType type,
3863 %        const size_t width,const size_t height,ExceptionInfo *exception)
3864 %
3865 %  A description of each parameter follows:
3866 %
3867 %    o image: the image.
3868 %
3869 %    o type: the statistic type (median, mode, etc.).
3870 %
3871 %    o width: the width of the pixel neighborhood.
3872 %
3873 %    o height: the height of the pixel neighborhood.
3874 %
3875 %    o exception: return any errors or warnings in this structure.
3876 %
3877 */
3878
3879 typedef struct _SkipNode
3880 {
3881   size_t
3882     next[9],
3883     count,
3884     signature;
3885 } SkipNode;
3886
3887 typedef struct _SkipList
3888 {
3889   ssize_t
3890     level;
3891
3892   SkipNode
3893     *nodes;
3894 } SkipList;
3895
3896 typedef struct _PixelList
3897 {
3898   size_t
3899     length,
3900     seed;
3901
3902   SkipList
3903     skip_list;
3904
3905   size_t
3906     signature;
3907 } PixelList;
3908
3909 static PixelList *DestroyPixelList(PixelList *pixel_list)
3910 {
3911   if (pixel_list == (PixelList *) NULL)
3912     return((PixelList *) NULL);
3913   if (pixel_list->skip_list.nodes != (SkipNode *) NULL)
3914     pixel_list->skip_list.nodes=(SkipNode *) RelinquishMagickMemory(
3915       pixel_list->skip_list.nodes);
3916   pixel_list=(PixelList *) RelinquishMagickMemory(pixel_list);
3917   return(pixel_list);
3918 }
3919
3920 static PixelList **DestroyPixelListThreadSet(PixelList **pixel_list)
3921 {
3922   register ssize_t
3923     i;
3924
3925   assert(pixel_list != (PixelList **) NULL);
3926   for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
3927     if (pixel_list[i] != (PixelList *) NULL)
3928       pixel_list[i]=DestroyPixelList(pixel_list[i]);
3929   pixel_list=(PixelList **) RelinquishMagickMemory(pixel_list);
3930   return(pixel_list);
3931 }
3932
3933 static PixelList *AcquirePixelList(const size_t width,const size_t height)
3934 {
3935   PixelList
3936     *pixel_list;
3937
3938   pixel_list=(PixelList *) AcquireMagickMemory(sizeof(*pixel_list));
3939   if (pixel_list == (PixelList *) NULL)
3940     return(pixel_list);
3941   (void) ResetMagickMemory((void *) pixel_list,0,sizeof(*pixel_list));
3942   pixel_list->length=width*height;
3943   pixel_list->skip_list.nodes=(SkipNode *) AcquireQuantumMemory(65537UL,
3944     sizeof(*pixel_list->skip_list.nodes));
3945   if (pixel_list->skip_list.nodes == (SkipNode *) NULL)
3946     return(DestroyPixelList(pixel_list));
3947   (void) ResetMagickMemory(pixel_list->skip_list.nodes,0,65537UL*
3948     sizeof(*pixel_list->skip_list.nodes));
3949   pixel_list->signature=MagickSignature;
3950   return(pixel_list);
3951 }
3952
3953 static PixelList **AcquirePixelListThreadSet(const size_t width,
3954   const size_t height)
3955 {
3956   PixelList
3957     **pixel_list;
3958
3959   register ssize_t
3960     i;
3961
3962   size_t
3963     number_threads;
3964
3965   number_threads=GetOpenMPMaximumThreads();
3966   pixel_list=(PixelList **) AcquireQuantumMemory(number_threads,
3967     sizeof(*pixel_list));
3968   if (pixel_list == (PixelList **) NULL)
3969     return((PixelList **) NULL);
3970   (void) ResetMagickMemory(pixel_list,0,number_threads*sizeof(*pixel_list));
3971   for (i=0; i < (ssize_t) number_threads; i++)
3972   {
3973     pixel_list[i]=AcquirePixelList(width,height);
3974     if (pixel_list[i] == (PixelList *) NULL)
3975       return(DestroyPixelListThreadSet(pixel_list));
3976   }
3977   return(pixel_list);
3978 }
3979
3980 static void AddNodePixelList(PixelList *pixel_list,const size_t color)
3981 {
3982   register SkipList
3983     *p;
3984
3985   register ssize_t
3986     level;
3987
3988   size_t
3989     search,
3990     update[9];
3991
3992   /*
3993     Initialize the node.
3994   */
3995   p=(&pixel_list->skip_list);
3996   p->nodes[color].signature=pixel_list->signature;
3997   p->nodes[color].count=1;
3998   /*
3999     Determine where it belongs in the list.
4000   */
4001   search=65536UL;
4002   for (level=p->level; level >= 0; level--)
4003   {
4004     while (p->nodes[search].next[level] < color)
4005       search=p->nodes[search].next[level];
4006     update[level]=search;
4007   }
4008   /*
4009     Generate a pseudo-random level for this node.
4010   */
4011   for (level=0; ; level++)
4012   {
4013     pixel_list->seed=(pixel_list->seed*42893621L)+1L;
4014     if ((pixel_list->seed & 0x300) != 0x300)
4015       break;
4016   }
4017   if (level > 8)
4018     level=8;
4019   if (level > (p->level+2))
4020     level=p->level+2;
4021   /*
4022     If we're raising the list's level, link back to the root node.
4023   */
4024   while (level > p->level)
4025   {
4026     p->level++;
4027     update[p->level]=65536UL;
4028   }
4029   /*
4030     Link the node into the skip-list.
4031   */
4032   do
4033   {
4034     p->nodes[color].next[level]=p->nodes[update[level]].next[level];
4035     p->nodes[update[level]].next[level]=color;
4036   } while (level-- > 0);
4037 }
4038
4039 static Quantum GetMaximumPixelList(PixelList *pixel_list)
4040 {
4041   register SkipList
4042     *p;
4043
4044   size_t
4045     color,
4046     maximum;
4047
4048   ssize_t
4049     count;
4050
4051   /*
4052     Find the maximum value for each of the color.
4053   */
4054   p=(&pixel_list->skip_list);
4055   color=65536L;
4056   count=0;
4057   maximum=p->nodes[color].next[0];
4058   do
4059   {
4060     color=p->nodes[color].next[0];
4061     if (color > maximum)
4062       maximum=color;
4063     count+=p->nodes[color].count;
4064   } while (count < (ssize_t) pixel_list->length);
4065   return(ScaleShortToQuantum((unsigned short) maximum));
4066 }
4067
4068 static Quantum GetMeanPixelList(PixelList *pixel_list)
4069 {
4070   MagickRealType
4071     sum;
4072
4073   register SkipList
4074     *p;
4075
4076   size_t
4077     color;
4078
4079   ssize_t
4080     count;
4081
4082   /*
4083     Find the mean value for each of the color.
4084   */
4085   p=(&pixel_list->skip_list);
4086   color=65536L;
4087   count=0;
4088   sum=0.0;
4089   do
4090   {
4091     color=p->nodes[color].next[0];
4092     sum+=(MagickRealType) p->nodes[color].count*color;
4093     count+=p->nodes[color].count;
4094   } while (count < (ssize_t) pixel_list->length);
4095   sum/=pixel_list->length;
4096   return(ScaleShortToQuantum((unsigned short) sum));
4097 }
4098
4099 static Quantum GetMedianPixelList(PixelList *pixel_list)
4100 {
4101   register SkipList
4102     *p;
4103
4104   size_t
4105     color;
4106
4107   ssize_t
4108     count;
4109
4110   /*
4111     Find the median value for each of the color.
4112   */
4113   p=(&pixel_list->skip_list);
4114   color=65536L;
4115   count=0;
4116   do
4117   {
4118     color=p->nodes[color].next[0];
4119     count+=p->nodes[color].count;
4120   } while (count <= (ssize_t) (pixel_list->length >> 1));
4121   return(ScaleShortToQuantum((unsigned short) color));
4122 }
4123
4124 static Quantum GetMinimumPixelList(PixelList *pixel_list)
4125 {
4126   register SkipList
4127     *p;
4128
4129   size_t
4130     color,
4131     minimum;
4132
4133   ssize_t
4134     count;
4135
4136   /*
4137     Find the minimum value for each of the color.
4138   */
4139   p=(&pixel_list->skip_list);
4140   count=0;
4141   color=65536UL;
4142   minimum=p->nodes[color].next[0];
4143   do
4144   {
4145     color=p->nodes[color].next[0];
4146     if (color < minimum)
4147       minimum=color;
4148     count+=p->nodes[color].count;
4149   } while (count < (ssize_t) pixel_list->length);
4150   return(ScaleShortToQuantum((unsigned short) minimum));
4151 }
4152
4153 static Quantum GetModePixelList(PixelList *pixel_list)
4154 {
4155   register SkipList
4156     *p;
4157
4158   size_t
4159     color,
4160     max_count,
4161     mode;
4162
4163   ssize_t
4164     count;
4165
4166   /*
4167     Make each pixel the 'predominant color' of the specified neighborhood.
4168   */
4169   p=(&pixel_list->skip_list);
4170   color=65536L;
4171   mode=color;
4172   max_count=p->nodes[mode].count;
4173   count=0;
4174   do
4175   {
4176     color=p->nodes[color].next[0];
4177     if (p->nodes[color].count > max_count)
4178       {
4179         mode=color;
4180         max_count=p->nodes[mode].count;
4181       }
4182     count+=p->nodes[color].count;
4183   } while (count < (ssize_t) pixel_list->length);
4184   return(ScaleShortToQuantum((unsigned short) mode));
4185 }
4186
4187 static Quantum GetNonpeakPixelList(PixelList *pixel_list)
4188 {
4189   register SkipList
4190     *p;
4191
4192   size_t
4193     color,
4194     next,
4195     previous;
4196
4197   ssize_t
4198     count;
4199
4200   /*
4201     Finds the non peak value for each of the colors.
4202   */
4203   p=(&pixel_list->skip_list);
4204   color=65536L;
4205   next=p->nodes[color].next[0];
4206   count=0;
4207   do
4208   {
4209     previous=color;
4210     color=next;
4211     next=p->nodes[color].next[0];
4212     count+=p->nodes[color].count;
4213   } while (count <= (ssize_t) (pixel_list->length >> 1));
4214   if ((previous == 65536UL) && (next != 65536UL))
4215     color=next;
4216   else
4217     if ((previous != 65536UL) && (next == 65536UL))
4218       color=previous;
4219   return(ScaleShortToQuantum((unsigned short) color));
4220 }
4221
4222 static Quantum GetStandardDeviationPixelList(PixelList *pixel_list)
4223 {
4224   MagickRealType
4225     sum,
4226     sum_squared;
4227
4228   register SkipList
4229     *p;
4230
4231   size_t
4232     color;
4233
4234   ssize_t
4235     count;
4236
4237   /*
4238     Find the standard-deviation value for each of the color.
4239   */
4240   p=(&pixel_list->skip_list);
4241   color=65536L;
4242   count=0;
4243   sum=0.0;
4244   sum_squared=0.0;
4245   do
4246   {
4247     register ssize_t
4248       i;
4249
4250     color=p->nodes[color].next[0];
4251     sum+=(MagickRealType) p->nodes[color].count*color;
4252     for (i=0; i < (ssize_t) p->nodes[color].count; i++)
4253       sum_squared+=((MagickRealType) color)*((MagickRealType) color);
4254     count+=p->nodes[color].count;
4255   } while (count < (ssize_t) pixel_list->length);
4256   sum/=pixel_list->length;
4257   sum_squared/=pixel_list->length;
4258   return(ScaleShortToQuantum((unsigned short) sqrt(sum_squared-(sum*sum))));
4259 }
4260
4261 static inline void InsertPixelList(const Image *image,const Quantum pixel,
4262   PixelList *pixel_list)
4263 {
4264   size_t
4265     signature;
4266
4267   unsigned short
4268     index;
4269
4270   index=ScaleQuantumToShort(pixel);
4271   signature=pixel_list->skip_list.nodes[index].signature;
4272   if (signature == pixel_list->signature)
4273     {
4274       pixel_list->skip_list.nodes[index].count++;
4275       return;
4276     }
4277   AddNodePixelList(pixel_list,index);
4278 }
4279
4280 static inline MagickRealType MagickAbsoluteValue(const MagickRealType x)
4281 {
4282   if (x < 0)
4283     return(-x);
4284   return(x);
4285 }
4286
4287 static inline size_t MagickMax(const size_t x,const size_t y)
4288 {
4289   if (x > y)
4290     return(x);
4291   return(y);
4292 }
4293
4294 static void ResetPixelList(PixelList *pixel_list)
4295 {
4296   int
4297     level;
4298
4299   register SkipNode
4300     *root;
4301
4302   register SkipList
4303     *p;
4304
4305   /*
4306     Reset the skip-list.
4307   */
4308   p=(&pixel_list->skip_list);
4309   root=p->nodes+65536UL;
4310   p->level=0;
4311   for (level=0; level < 9; level++)
4312     root->next[level]=65536UL;
4313   pixel_list->seed=pixel_list->signature++;
4314 }
4315
4316 MagickExport Image *StatisticImage(const Image *image,const StatisticType type,
4317   const size_t width,const size_t height,ExceptionInfo *exception)
4318 {
4319 #define StatisticImageTag  "Statistic/Image"
4320
4321   CacheView
4322     *image_view,
4323     *statistic_view;
4324
4325   Image
4326     *statistic_image;
4327
4328   MagickBooleanType
4329     status;
4330
4331   MagickOffsetType
4332     progress;
4333
4334   PixelList
4335     **restrict pixel_list;
4336
4337   ssize_t
4338     center,
4339     y;
4340
4341   /*
4342     Initialize statistics image attributes.
4343   */
4344   assert(image != (Image *) NULL);
4345   assert(image->signature == MagickSignature);
4346   if (image->debug != MagickFalse)
4347     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4348   assert(exception != (ExceptionInfo *) NULL);
4349   assert(exception->signature == MagickSignature);
4350   statistic_image=CloneImage(image,image->columns,image->rows,MagickTrue,
4351     exception);
4352   if (statistic_image == (Image *) NULL)
4353     return((Image *) NULL);
4354   status=SetImageStorageClass(statistic_image,DirectClass,exception);
4355   if (status == MagickFalse)
4356     {
4357       statistic_image=DestroyImage(statistic_image);
4358       return((Image *) NULL);
4359     }
4360   pixel_list=AcquirePixelListThreadSet(MagickMax(width,1),MagickMax(height,1));
4361   if (pixel_list == (PixelList **) NULL)
4362     {
4363       statistic_image=DestroyImage(statistic_image);
4364       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
4365     }
4366   /*
4367     Make each pixel the min / max / median / mode / etc. of the neighborhood.
4368   */
4369   center=(ssize_t) GetPixelChannels(image)*(image->columns+MagickMax(width,1))*
4370     (MagickMax(height,1)/2L)+GetPixelChannels(image)*(MagickMax(width,1)/2L);
4371   status=MagickTrue;
4372   progress=0;
4373   image_view=AcquireCacheView(image);
4374   statistic_view=AcquireCacheView(statistic_image);
4375 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4376   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4377 #endif
4378   for (y=0; y < (ssize_t) statistic_image->rows; y++)
4379   {
4380     const int
4381       id = GetOpenMPThreadId();
4382
4383     register const Quantum
4384       *restrict p;
4385
4386     register Quantum
4387       *restrict q;
4388
4389     register ssize_t
4390       x;
4391
4392     if (status == MagickFalse)
4393       continue;
4394     p=GetCacheViewVirtualPixels(image_view,-((ssize_t) MagickMax(width,1)/2L),y-
4395       (ssize_t) (MagickMax(height,1)/2L),image->columns+MagickMax(width,1),
4396       MagickMax(height,1),exception);
4397     q=QueueCacheViewAuthenticPixels(statistic_view,0,y,statistic_image->columns,      1,exception);
4398     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
4399       {
4400         status=MagickFalse;
4401         continue;
4402       }
4403     for (x=0; x < (ssize_t) statistic_image->columns; x++)
4404     {
4405       register ssize_t
4406         i;
4407
4408       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4409       {
4410         PixelChannel
4411           channel;
4412
4413         PixelTrait
4414           statistic_traits,
4415           traits;
4416
4417         Quantum
4418           pixel;
4419
4420         register const Quantum
4421           *restrict pixels;
4422
4423         register ssize_t
4424           u;
4425
4426         ssize_t
4427           v;
4428
4429         traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
4430         channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
4431         statistic_traits=GetPixelChannelMapTraits(statistic_image,channel);
4432         if ((traits == UndefinedPixelTrait) ||
4433             (statistic_traits == UndefinedPixelTrait))
4434           continue;
4435         if ((statistic_traits & CopyPixelTrait) != 0)
4436           {
4437             q[channel]=p[center+i];
4438             continue;
4439           }
4440         pixels=p;
4441         ResetPixelList(pixel_list[id]);
4442         for (v=0; v < (ssize_t) MagickMax(height,1); v++)
4443         {
4444           for (u=0; u < (ssize_t) MagickMax(width,1); u++)
4445           {
4446             InsertPixelList(image,pixels[i],pixel_list[id]);
4447             pixels+=GetPixelChannels(image);
4448           }
4449           pixels+=image->columns*GetPixelChannels(image);
4450         }
4451         switch (type)
4452         {
4453           case GradientStatistic:
4454           {
4455             MagickRealType
4456               maximum,
4457               minimum;
4458
4459             minimum=(MagickRealType) GetMinimumPixelList(pixel_list[id]);
4460             maximum=(MagickRealType) GetMaximumPixelList(pixel_list[id]);
4461             pixel=ClampToQuantum(MagickAbsoluteValue(maximum-minimum));
4462             break;
4463           }
4464           case MaximumStatistic:
4465           {
4466             pixel=GetMaximumPixelList(pixel_list[id]);
4467             break;
4468           }
4469           case MeanStatistic:
4470           {
4471             pixel=GetMeanPixelList(pixel_list[id]);
4472             break;
4473           }
4474           case MedianStatistic:
4475           default:
4476           {
4477             pixel=GetMedianPixelList(pixel_list[id]);
4478             break;
4479           }
4480           case MinimumStatistic:
4481           {
4482             pixel=GetMinimumPixelList(pixel_list[id]);
4483             break;
4484           }
4485           case ModeStatistic:
4486           {
4487             pixel=GetModePixelList(pixel_list[id]);
4488             break;
4489           }
4490           case NonpeakStatistic:
4491           {
4492             pixel=GetNonpeakPixelList(pixel_list[id]);
4493             break;
4494           }
4495           case StandardDeviationStatistic:
4496           {
4497             pixel=GetStandardDeviationPixelList(pixel_list[id]);
4498             break;
4499           }
4500         }
4501         q[channel]=pixel;
4502       }
4503       p+=GetPixelChannels(image);
4504       q+=GetPixelChannels(statistic_image);
4505     }
4506     if (SyncCacheViewAuthenticPixels(statistic_view,exception) == MagickFalse)
4507       status=MagickFalse;
4508     if (image->progress_monitor != (MagickProgressMonitor) NULL)
4509       {
4510         MagickBooleanType
4511           proceed;
4512
4513 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4514   #pragma omp critical (MagickCore_StatisticImage)
4515 #endif
4516         proceed=SetImageProgress(image,StatisticImageTag,progress++,
4517           image->rows);
4518         if (proceed == MagickFalse)
4519           status=MagickFalse;
4520       }
4521   }
4522   statistic_view=DestroyCacheView(statistic_view);
4523   image_view=DestroyCacheView(image_view);
4524   pixel_list=DestroyPixelListThreadSet(pixel_list);
4525   return(statistic_image);
4526 }
4527 \f
4528 /*
4529 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4530 %                                                                             %
4531 %                                                                             %
4532 %                                                                             %
4533 %     U n s h a r p M a s k I m a g e                                         %
4534 %                                                                             %
4535 %                                                                             %
4536 %                                                                             %
4537 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4538 %
4539 %  UnsharpMaskImage() sharpens one or more image channels.  We convolve the
4540 %  image with a Gaussian operator of the given radius and standard deviation
4541 %  (sigma).  For reasonable results, radius should be larger than sigma.  Use a
4542 %  radius of 0 and UnsharpMaskImage() selects a suitable radius for you.
4543 %
4544 %  The format of the UnsharpMaskImage method is:
4545 %
4546 %    Image *UnsharpMaskImage(const Image *image,const double radius,
4547 %      const double sigma,const double amount,const double threshold,
4548 %      ExceptionInfo *exception)
4549 %
4550 %  A description of each parameter follows:
4551 %
4552 %    o image: the image.
4553 %
4554 %    o radius: the radius of the Gaussian, in pixels, not counting the center
4555 %      pixel.
4556 %
4557 %    o sigma: the standard deviation of the Gaussian, in pixels.
4558 %
4559 %    o amount: the percentage of the difference between the original and the
4560 %      blur image that is added back into the original.
4561 %
4562 %    o threshold: the threshold in pixels needed to apply the diffence amount.
4563 %
4564 %    o exception: return any errors or warnings in this structure.
4565 %
4566 */
4567 MagickExport Image *UnsharpMaskImage(const Image *image,
4568   const double radius,const double sigma,const double amount,
4569   const double threshold,ExceptionInfo *exception)
4570 {
4571 #define SharpenImageTag  "Sharpen/Image"
4572
4573   CacheView
4574     *image_view,
4575     *unsharp_view;
4576
4577   Image
4578     *unsharp_image;
4579
4580   MagickBooleanType
4581     status;
4582
4583   MagickOffsetType
4584     progress;
4585
4586   MagickRealType
4587     quantum_threshold;
4588
4589   ssize_t
4590     y;
4591
4592   assert(image != (const Image *) NULL);
4593   assert(image->signature == MagickSignature);
4594   if (image->debug != MagickFalse)
4595     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4596   assert(exception != (ExceptionInfo *) NULL);
4597   unsharp_image=BlurImage(image,radius,sigma,image->bias,exception);
4598   if (unsharp_image == (Image *) NULL)
4599     return((Image *) NULL);
4600   quantum_threshold=(MagickRealType) QuantumRange*threshold;
4601   /*
4602     Unsharp-mask image.
4603   */
4604   status=MagickTrue;
4605   progress=0;
4606   image_view=AcquireCacheView(image);
4607   unsharp_view=AcquireCacheView(unsharp_image);
4608 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4609   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4610 #endif
4611   for (y=0; y < (ssize_t) image->rows; y++)
4612   {
4613     register const Quantum
4614       *restrict p;
4615
4616     register Quantum
4617       *restrict q;
4618
4619     register ssize_t
4620       x;
4621
4622     if (status == MagickFalse)
4623       continue;
4624     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
4625     q=GetCacheViewAuthenticPixels(unsharp_view,0,y,unsharp_image->columns,1,
4626       exception);
4627     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
4628       {
4629         status=MagickFalse;
4630         continue;
4631       }
4632     for (x=0; x < (ssize_t) image->columns; x++)
4633     {
4634       register ssize_t
4635         i;
4636
4637       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4638       {
4639         MagickRealType
4640           pixel;
4641
4642         PixelChannel
4643           channel;
4644
4645         PixelTrait
4646           traits,
4647           unsharp_traits;
4648
4649         traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
4650         channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
4651         unsharp_traits=GetPixelChannelMapTraits(unsharp_image,channel);
4652         if ((traits == UndefinedPixelTrait) ||
4653             (unsharp_traits == UndefinedPixelTrait))
4654           continue;
4655         if ((unsharp_traits & CopyPixelTrait) != 0)
4656           {
4657             q[channel]=p[i];
4658             continue;
4659           }
4660         pixel=p[i]-(MagickRealType) q[channel];
4661         if (fabs(2.0*pixel) < quantum_threshold)
4662           pixel=(MagickRealType) p[i];
4663         else
4664           pixel=(MagickRealType) p[i]+amount*pixel;
4665         q[channel]=ClampToQuantum(pixel);
4666       }
4667       p+=GetPixelChannels(image);
4668       q+=GetPixelChannels(unsharp_image);
4669     }
4670     if (SyncCacheViewAuthenticPixels(unsharp_view,exception) == MagickFalse)
4671       status=MagickFalse;
4672     if (image->progress_monitor != (MagickProgressMonitor) NULL)
4673       {
4674         MagickBooleanType
4675           proceed;
4676
4677 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4678   #pragma omp critical (MagickCore_UnsharpMaskImage)
4679 #endif
4680         proceed=SetImageProgress(image,SharpenImageTag,progress++,image->rows);
4681         if (proceed == MagickFalse)
4682           status=MagickFalse;
4683       }
4684   }
4685   unsharp_image->type=image->type;
4686   unsharp_view=DestroyCacheView(unsharp_view);
4687   image_view=DestroyCacheView(image_view);
4688   if (status == MagickFalse)
4689     unsharp_image=DestroyImage(unsharp_image);
4690   return(unsharp_image);
4691 }