source: ImageMagick/branches/ImageMagick-6/magick/effect.c @ 7369

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