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