MagickCore  6.7.5
effect.c
Go to the documentation of this file.
00001 /*
00002 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00003 %                                                                             %
00004 %                                                                             %
00005 %                                                                             %
00006 %                   EEEEE  FFFFF  FFFFF  EEEEE  CCCC  TTTTT                   %
00007 %                   E      F      F      E     C        T                     %
00008 %                   EEE    FFF    FFF    EEE   C        T                     %
00009 %                   E      F      F      E     C        T                     %
00010 %                   EEEEE  F      F      EEEEE  CCCC    T                     %
00011 %                                                                             %
00012 %                                                                             %
00013 %                       MagickCore Image Effects Methods                      %
00014 %                                                                             %
00015 %                               Software Design                               %
00016 %                                 John Cristy                                 %
00017 %                                 October 1996                                %
00018 %                                                                             %
00019 %                                                                             %
00020 %  Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization      %
00021 %  dedicated to making software imaging solutions freely available.           %
00022 %                                                                             %
00023 %  You may not use this file except in compliance with the License.  You may  %
00024 %  obtain a copy of the License at                                            %
00025 %                                                                             %
00026 %    http://www.imagemagick.org/script/license.php                            %
00027 %                                                                             %
00028 %  Unless required by applicable law or agreed to in writing, software        %
00029 %  distributed under the License is distributed on an "AS IS" BASIS,          %
00030 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
00031 %  See the License for the specific language governing permissions and        %
00032 %  limitations under the License.                                             %
00033 %                                                                             %
00034 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00035 %
00036 %
00037 %
00038 */
00039 
00040 /*
00041   Include declarations.
00042 */
00043 #include "MagickCore/studio.h"
00044 #include "MagickCore/accelerate.h"
00045 #include "MagickCore/blob.h"
00046 #include "MagickCore/cache-view.h"
00047 #include "MagickCore/color.h"
00048 #include "MagickCore/color-private.h"
00049 #include "MagickCore/colorspace.h"
00050 #include "MagickCore/constitute.h"
00051 #include "MagickCore/decorate.h"
00052 #include "MagickCore/distort.h"
00053 #include "MagickCore/draw.h"
00054 #include "MagickCore/enhance.h"
00055 #include "MagickCore/exception.h"
00056 #include "MagickCore/exception-private.h"
00057 #include "MagickCore/effect.h"
00058 #include "MagickCore/fx.h"
00059 #include "MagickCore/gem.h"
00060 #include "MagickCore/gem-private.h"
00061 #include "MagickCore/geometry.h"
00062 #include "MagickCore/image-private.h"
00063 #include "MagickCore/list.h"
00064 #include "MagickCore/log.h"
00065 #include "MagickCore/memory_.h"
00066 #include "MagickCore/monitor.h"
00067 #include "MagickCore/monitor-private.h"
00068 #include "MagickCore/montage.h"
00069 #include "MagickCore/morphology.h"
00070 #include "MagickCore/paint.h"
00071 #include "MagickCore/pixel-accessor.h"
00072 #include "MagickCore/property.h"
00073 #include "MagickCore/quantize.h"
00074 #include "MagickCore/quantum.h"
00075 #include "MagickCore/quantum-private.h"
00076 #include "MagickCore/random_.h"
00077 #include "MagickCore/random-private.h"
00078 #include "MagickCore/resample.h"
00079 #include "MagickCore/resample-private.h"
00080 #include "MagickCore/resize.h"
00081 #include "MagickCore/resource_.h"
00082 #include "MagickCore/segment.h"
00083 #include "MagickCore/shear.h"
00084 #include "MagickCore/signature-private.h"
00085 #include "MagickCore/statistic.h"
00086 #include "MagickCore/string_.h"
00087 #include "MagickCore/thread-private.h"
00088 #include "MagickCore/transform.h"
00089 #include "MagickCore/threshold.h"
00090 
00091 /*
00092 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00093 %                                                                             %
00094 %                                                                             %
00095 %                                                                             %
00096 %     A d a p t i v e B l u r I m a g e                                       %
00097 %                                                                             %
00098 %                                                                             %
00099 %                                                                             %
00100 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00101 %
00102 %  AdaptiveBlurImage() adaptively blurs the image by blurring less
00103 %  intensely near image edges and more intensely far from edges.  We blur the
00104 %  image with a Gaussian operator of the given radius and standard deviation
00105 %  (sigma).  For reasonable results, radius should be larger than sigma.  Use a
00106 %  radius of 0 and AdaptiveBlurImage() selects a suitable radius for you.
00107 %
00108 %  The format of the AdaptiveBlurImage method is:
00109 %
00110 %      Image *AdaptiveBlurImage(const Image *image,const double radius,
00111 %        const double sigma,const double bias,ExceptionInfo *exception)
00112 %
00113 %  A description of each parameter follows:
00114 %
00115 %    o image: the image.
00116 %
00117 %    o radius: the radius of the Gaussian, in pixels, not counting the center
00118 %      pixel.
00119 %
00120 %    o sigma: the standard deviation of the Laplacian, in pixels.
00121 %
00122 %    o bias: the bias.
00123 %
00124 %    o exception: return any errors or warnings in this structure.
00125 %
00126 */
00127 
00128 MagickExport MagickBooleanType AdaptiveLevelImage(Image *image,
00129   const char *levels,ExceptionInfo *exception)
00130 {
00131   double
00132     black_point,
00133     gamma,
00134     white_point;
00135 
00136   GeometryInfo
00137     geometry_info;
00138 
00139   MagickBooleanType
00140     status;
00141 
00142   MagickStatusType
00143     flags;
00144 
00145   /*
00146     Parse levels.
00147   */
00148   if (levels == (char *) NULL)
00149     return(MagickFalse);
00150   flags=ParseGeometry(levels,&geometry_info);
00151   black_point=geometry_info.rho;
00152   white_point=(double) QuantumRange;
00153   if ((flags & SigmaValue) != 0)
00154     white_point=geometry_info.sigma;
00155   gamma=1.0;
00156   if ((flags & XiValue) != 0)
00157     gamma=geometry_info.xi;
00158   if ((flags & PercentValue) != 0)
00159     {
00160       black_point*=(double) image->columns*image->rows/100.0;
00161       white_point*=(double) image->columns*image->rows/100.0;
00162     }
00163   if ((flags & SigmaValue) == 0)
00164     white_point=(double) QuantumRange-black_point;
00165   if ((flags & AspectValue ) == 0)
00166     status=LevelImage(image,black_point,white_point,gamma,exception);
00167   else
00168     status=LevelizeImage(image,black_point,white_point,gamma,exception);
00169   return(status);
00170 }
00171 
00172 MagickExport Image *AdaptiveBlurImage(const Image *image,const double radius,
00173   const double sigma,const double bias,ExceptionInfo *exception)
00174 {
00175 #define AdaptiveBlurImageTag  "Convolve/Image"
00176 #define MagickSigma  (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
00177 
00178   CacheView
00179     *blur_view,
00180     *edge_view,
00181     *image_view;
00182 
00183   double
00184     **kernel,
00185     normalize;
00186 
00187   Image
00188     *blur_image,
00189     *edge_image,
00190     *gaussian_image;
00191 
00192   MagickBooleanType
00193     status;
00194 
00195   MagickOffsetType
00196     progress;
00197 
00198   register ssize_t
00199     i;
00200 
00201   size_t
00202     width;
00203 
00204   ssize_t
00205     j,
00206     k,
00207     u,
00208     v,
00209     y;
00210 
00211   assert(image != (const Image *) NULL);
00212   assert(image->signature == MagickSignature);
00213   if (image->debug != MagickFalse)
00214     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00215   assert(exception != (ExceptionInfo *) NULL);
00216   assert(exception->signature == MagickSignature);
00217   blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
00218   if (blur_image == (Image *) NULL)
00219     return((Image *) NULL);
00220   if (fabs(sigma) <= MagickEpsilon)
00221     return(blur_image);
00222   if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
00223     {
00224       blur_image=DestroyImage(blur_image);
00225       return((Image *) NULL);
00226     }
00227   /*
00228     Edge detect the image brighness channel, level, blur, and level again.
00229   */
00230   edge_image=EdgeImage(image,radius,sigma,exception);
00231   if (edge_image == (Image *) NULL)
00232     {
00233       blur_image=DestroyImage(blur_image);
00234       return((Image *) NULL);
00235     }
00236   (void) AdaptiveLevelImage(edge_image,"20%,95%",exception);
00237   gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
00238   if (gaussian_image != (Image *) NULL)
00239     {
00240       edge_image=DestroyImage(edge_image);
00241       edge_image=gaussian_image;
00242     }
00243   (void) AdaptiveLevelImage(edge_image,"10%,95%",exception);
00244   /*
00245     Create a set of kernels from maximum (radius,sigma) to minimum.
00246   */
00247   width=GetOptimalKernelWidth2D(radius,sigma);
00248   kernel=(double **) AcquireAlignedMemory((size_t) width,sizeof(*kernel));
00249   if (kernel == (double **) NULL)
00250     {
00251       edge_image=DestroyImage(edge_image);
00252       blur_image=DestroyImage(blur_image);
00253       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00254     }
00255   (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
00256   for (i=0; i < (ssize_t) width; i+=2)
00257   {
00258     kernel[i]=(double *) AcquireAlignedMemory((size_t) (width-i),(width-i)*
00259       sizeof(**kernel));
00260     if (kernel[i] == (double *) NULL)
00261       break;
00262     normalize=0.0;
00263     j=(ssize_t) (width-i)/2;
00264     k=0;
00265     for (v=(-j); v <= j; v++)
00266     {
00267       for (u=(-j); u <= j; u++)
00268       {
00269         kernel[i][k]=(double) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
00270           MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
00271         normalize+=kernel[i][k];
00272         k++;
00273       }
00274     }
00275     if (fabs(normalize) <= MagickEpsilon)
00276       normalize=1.0;
00277     normalize=1.0/normalize;
00278     for (k=0; k < (j*j); k++)
00279       kernel[i][k]=normalize*kernel[i][k];
00280   }
00281   if (i < (ssize_t) width)
00282     {
00283       for (i-=2; i >= 0; i-=2)
00284         kernel[i]=(double *) RelinquishAlignedMemory(kernel[i]);
00285       kernel=(double **) RelinquishAlignedMemory(kernel);
00286       edge_image=DestroyImage(edge_image);
00287       blur_image=DestroyImage(blur_image);
00288       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00289     }
00290   /*
00291     Adaptively blur image.
00292   */
00293   status=MagickTrue;
00294   progress=0;
00295   image_view=AcquireCacheView(image);
00296   edge_view=AcquireCacheView(edge_image);
00297   blur_view=AcquireCacheView(blur_image);
00298 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00299   #pragma omp parallel for schedule(static,4) shared(progress,status)
00300 #endif
00301   for (y=0; y < (ssize_t) blur_image->rows; y++)
00302   {
00303     register const Quantum
00304       *restrict r;
00305 
00306     register Quantum
00307       *restrict q;
00308 
00309     register ssize_t
00310       x;
00311 
00312     if (status == MagickFalse)
00313       continue;
00314     r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
00315     q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
00316       exception);
00317     if ((r == (const Quantum *) NULL) || (q == (Quantum *) NULL))
00318       {
00319         status=MagickFalse;
00320         continue;
00321       }
00322     for (x=0; x < (ssize_t) blur_image->columns; x++)
00323     {
00324       register const Quantum
00325         *restrict p;
00326 
00327       register ssize_t
00328         i;
00329 
00330       ssize_t
00331         center,
00332         j;
00333 
00334       j=(ssize_t) ceil((double) width*QuantumScale*
00335         GetPixelIntensity(edge_image,r)-0.5);
00336       if (j < 0)
00337         j=0;
00338       else
00339         if (j > (ssize_t) width)
00340           j=(ssize_t) width;
00341       if ((j & 0x01) != 0)
00342         j--;
00343       p=GetCacheViewVirtualPixels(image_view,x-((ssize_t) (width-j)/2L),y-
00344         (ssize_t) ((width-j)/2L),width-j,width-j,exception);
00345       if (p == (const Quantum *) NULL)
00346         break;
00347       center=(ssize_t) GetPixelChannels(image)*(width-j)*((width-j)/2L)+
00348         GetPixelChannels(image)*((width-j)/2L);
00349       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
00350       {
00351         MagickRealType
00352           alpha,
00353           gamma,
00354           pixel;
00355 
00356         PixelChannel
00357           channel;
00358 
00359         PixelTrait
00360           blur_traits,
00361           traits;
00362 
00363         register const double
00364           *restrict k;
00365 
00366         register const Quantum
00367           *restrict pixels;
00368 
00369         register ssize_t
00370           u;
00371 
00372         ssize_t
00373           v;
00374 
00375         channel=GetPixelChannelMapChannel(image,i);
00376         traits=GetPixelChannelMapTraits(image,channel);
00377         blur_traits=GetPixelChannelMapTraits(blur_image,channel);
00378         if ((traits == UndefinedPixelTrait) ||
00379             (blur_traits == UndefinedPixelTrait))
00380           continue;
00381         if (((blur_traits & CopyPixelTrait) != 0) ||
00382             (GetPixelMask(image,q) != 0))
00383           {
00384             SetPixelChannel(blur_image,channel,p[center+i],q);
00385             continue;
00386           }
00387         k=kernel[j];
00388         pixels=p;
00389         pixel=bias;
00390         gamma=0.0;
00391         if ((blur_traits & BlendPixelTrait) == 0)
00392           {
00393             /*
00394               No alpha blending.
00395             */
00396             for (v=0; v < (ssize_t) (width-j); v++)
00397             {
00398               for (u=0; u < (ssize_t) (width-j); u++)
00399               {
00400                 pixel+=(*k)*pixels[i];
00401                 gamma+=(*k);
00402                 k++;
00403                 pixels+=GetPixelChannels(image);
00404               }
00405             }
00406             gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
00407             SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
00408             continue;
00409           }
00410         /*
00411           Alpha blending.
00412         */
00413         for (v=0; v < (ssize_t) (width-j); v++)
00414         {
00415           for (u=0; u < (ssize_t) (width-j); u++)
00416           {
00417             alpha=(MagickRealType) (QuantumScale*GetPixelAlpha(image,pixels));
00418             pixel+=(*k)*alpha*pixels[i];
00419             gamma+=(*k)*alpha;
00420             k++;
00421             pixels+=GetPixelChannels(image);
00422           }
00423         }
00424         gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
00425         SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
00426       }
00427       q+=GetPixelChannels(blur_image);
00428       r+=GetPixelChannels(edge_image);
00429     }
00430     if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
00431       status=MagickFalse;
00432     if (image->progress_monitor != (MagickProgressMonitor) NULL)
00433       {
00434         MagickBooleanType
00435           proceed;
00436 
00437 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00438         #pragma omp critical (MagickCore_AdaptiveBlurImage)
00439 #endif
00440         proceed=SetImageProgress(image,AdaptiveBlurImageTag,progress++,
00441           image->rows);
00442         if (proceed == MagickFalse)
00443           status=MagickFalse;
00444       }
00445   }
00446   blur_image->type=image->type;
00447   blur_view=DestroyCacheView(blur_view);
00448   edge_view=DestroyCacheView(edge_view);
00449   image_view=DestroyCacheView(image_view);
00450   edge_image=DestroyImage(edge_image);
00451   for (i=0; i < (ssize_t) width;  i+=2)
00452     kernel[i]=(double *) RelinquishAlignedMemory(kernel[i]);
00453   kernel=(double **) RelinquishAlignedMemory(kernel);
00454   if (status == MagickFalse)
00455     blur_image=DestroyImage(blur_image);
00456   return(blur_image);
00457 }
00458 
00459 /*
00460 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00461 %                                                                             %
00462 %                                                                             %
00463 %                                                                             %
00464 %     A d a p t i v e S h a r p e n I m a g e                                 %
00465 %                                                                             %
00466 %                                                                             %
00467 %                                                                             %
00468 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00469 %
00470 %  AdaptiveSharpenImage() adaptively sharpens the image by sharpening more
00471 %  intensely near image edges and less intensely far from edges. We sharpen the
00472 %  image with a Gaussian operator of the given radius and standard deviation
00473 %  (sigma).  For reasonable results, radius should be larger than sigma.  Use a
00474 %  radius of 0 and AdaptiveSharpenImage() selects a suitable radius for you.
00475 %
00476 %  The format of the AdaptiveSharpenImage method is:
00477 %
00478 %      Image *AdaptiveSharpenImage(const Image *image,const double radius,
00479 %        const double sigma,const double bias,ExceptionInfo *exception)
00480 %
00481 %  A description of each parameter follows:
00482 %
00483 %    o image: the image.
00484 %
00485 %    o radius: the radius of the Gaussian, in pixels, not counting the center
00486 %      pixel.
00487 %
00488 %    o sigma: the standard deviation of the Laplacian, in pixels.
00489 %
00490 %    o bias: the bias.
00491 %
00492 %    o exception: return any errors or warnings in this structure.
00493 %
00494 */
00495 MagickExport Image *AdaptiveSharpenImage(const Image *image,const double radius,
00496   const double sigma,const double bias,ExceptionInfo *exception)
00497 {
00498 #define AdaptiveSharpenImageTag  "Convolve/Image"
00499 #define MagickSigma  (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
00500 
00501   CacheView
00502     *sharp_view,
00503     *edge_view,
00504     *image_view;
00505 
00506   double
00507     **kernel,
00508     normalize;
00509 
00510   Image
00511     *sharp_image,
00512     *edge_image,
00513     *gaussian_image;
00514 
00515   MagickBooleanType
00516     status;
00517 
00518   MagickOffsetType
00519     progress;
00520 
00521   register ssize_t
00522     i;
00523 
00524   size_t
00525     width;
00526 
00527   ssize_t
00528     j,
00529     k,
00530     u,
00531     v,
00532     y;
00533 
00534   assert(image != (const Image *) NULL);
00535   assert(image->signature == MagickSignature);
00536   if (image->debug != MagickFalse)
00537     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00538   assert(exception != (ExceptionInfo *) NULL);
00539   assert(exception->signature == MagickSignature);
00540   sharp_image=CloneImage(image,0,0,MagickTrue,exception);
00541   if (sharp_image == (Image *) NULL)
00542     return((Image *) NULL);
00543   if (fabs(sigma) <= MagickEpsilon)
00544     return(sharp_image);
00545   if (SetImageStorageClass(sharp_image,DirectClass,exception) == MagickFalse)
00546     {
00547       sharp_image=DestroyImage(sharp_image);
00548       return((Image *) NULL);
00549     }
00550   /*
00551     Edge detect the image brighness channel, level, sharp, and level again.
00552   */
00553   edge_image=EdgeImage(image,radius,sigma,exception);
00554   if (edge_image == (Image *) NULL)
00555     {
00556       sharp_image=DestroyImage(sharp_image);
00557       return((Image *) NULL);
00558     }
00559   (void) AdaptiveLevelImage(edge_image,"20%,95%",exception);
00560   gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
00561   if (gaussian_image != (Image *) NULL)
00562     {
00563       edge_image=DestroyImage(edge_image);
00564       edge_image=gaussian_image;
00565     }
00566   (void) AdaptiveLevelImage(edge_image,"10%,95%",exception);
00567   /*
00568     Create a set of kernels from maximum (radius,sigma) to minimum.
00569   */
00570   width=GetOptimalKernelWidth2D(radius,sigma);
00571   kernel=(double **) AcquireAlignedMemory((size_t) width,sizeof(*kernel));
00572   if (kernel == (double **) NULL)
00573     {
00574       edge_image=DestroyImage(edge_image);
00575       sharp_image=DestroyImage(sharp_image);
00576       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00577     }
00578   (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
00579   for (i=0; i < (ssize_t) width; i+=2)
00580   {
00581     kernel[i]=(double *) AcquireAlignedMemory((size_t) (width-i),(width-i)*
00582       sizeof(**kernel));
00583     if (kernel[i] == (double *) NULL)
00584       break;
00585     normalize=0.0;
00586     j=(ssize_t) (width-i)/2;
00587     k=0;
00588     for (v=(-j); v <= j; v++)
00589     {
00590       for (u=(-j); u <= j; u++)
00591       {
00592         kernel[i][k]=(double) (-exp(-((double) u*u+v*v)/(2.0*MagickSigma*
00593           MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
00594         normalize+=kernel[i][k];
00595         k++;
00596       }
00597     }
00598     if (fabs(normalize) <= MagickEpsilon)
00599       normalize=1.0;
00600     normalize=1.0/normalize;
00601     for (k=0; k < (j*j); k++)
00602       kernel[i][k]=normalize*kernel[i][k];
00603   }
00604   if (i < (ssize_t) width)
00605     {
00606       for (i-=2; i >= 0; i-=2)
00607         kernel[i]=(double *) RelinquishAlignedMemory(kernel[i]);
00608       kernel=(double **) RelinquishAlignedMemory(kernel);
00609       edge_image=DestroyImage(edge_image);
00610       sharp_image=DestroyImage(sharp_image);
00611       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00612     }
00613   /*
00614     Adaptively sharpen image.
00615   */
00616   status=MagickTrue;
00617   progress=0;
00618   image_view=AcquireCacheView(image);
00619   edge_view=AcquireCacheView(edge_image);
00620   sharp_view=AcquireCacheView(sharp_image);
00621 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00622   #pragma omp parallel for schedule(static,4) shared(progress,status)
00623 #endif
00624   for (y=0; y < (ssize_t) sharp_image->rows; y++)
00625   {
00626     register const Quantum
00627       *restrict r;
00628 
00629     register Quantum
00630       *restrict q;
00631 
00632     register ssize_t
00633       x;
00634 
00635     if (status == MagickFalse)
00636       continue;
00637     r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
00638     q=QueueCacheViewAuthenticPixels(sharp_view,0,y,sharp_image->columns,1,
00639       exception);
00640     if ((r == (const Quantum *) NULL) || (q == (Quantum *) NULL))
00641       {
00642         status=MagickFalse;
00643         continue;
00644       }
00645     for (x=0; x < (ssize_t) sharp_image->columns; x++)
00646     {
00647       register const Quantum
00648         *restrict p;
00649 
00650       register ssize_t
00651         i;
00652 
00653       ssize_t
00654         center,
00655         j;
00656 
00657       j=(ssize_t) ceil((double) width*QuantumScale*
00658         GetPixelIntensity(edge_image,r)-0.5);
00659       if (j < 0)
00660         j=0;
00661       else
00662         if (j > (ssize_t) width)
00663           j=(ssize_t) width;
00664       if ((j & 0x01) != 0)
00665         j--;
00666       p=GetCacheViewVirtualPixels(image_view,x-((ssize_t) (width-j)/2L),y-
00667         (ssize_t) ((width-j)/2L),width-j,width-j,exception);
00668       if (p == (const Quantum *) NULL)
00669         break;
00670       center=(ssize_t) GetPixelChannels(image)*(width-j)*((width-j)/2L)+
00671         GetPixelChannels(image)*((width-j)/2);
00672       for (i=0; i < (ssize_t) GetPixelChannels(sharp_image); i++)
00673       {
00674         MagickRealType
00675           alpha,
00676           gamma,
00677           pixel;
00678 
00679         PixelChannel
00680           channel;
00681 
00682         PixelTrait
00683           sharp_traits,
00684           traits;
00685 
00686         register const double
00687           *restrict k;
00688 
00689         register const Quantum
00690           *restrict pixels;
00691 
00692         register ssize_t
00693           u;
00694 
00695         ssize_t
00696           v;
00697 
00698         channel=GetPixelChannelMapChannel(image,i);
00699         traits=GetPixelChannelMapTraits(image,channel);
00700         sharp_traits=GetPixelChannelMapTraits(sharp_image,channel);
00701         if ((traits == UndefinedPixelTrait) ||
00702             (sharp_traits == UndefinedPixelTrait))
00703           continue;
00704         if (((sharp_traits & CopyPixelTrait) != 0) ||
00705             (GetPixelMask(image,q) != 0))
00706           {
00707             SetPixelChannel(sharp_image,channel,p[center+i],q);
00708             continue;
00709           }
00710         k=kernel[j];
00711         pixels=p;
00712         pixel=bias;
00713         gamma=0.0;
00714         if ((sharp_traits & BlendPixelTrait) == 0)
00715           {
00716             /*
00717               No alpha blending.
00718             */
00719             for (v=0; v < (ssize_t) (width-j); v++)
00720             {
00721               for (u=0; u < (ssize_t) (width-j); u++)
00722               {
00723                 pixel+=(*k)*pixels[i];
00724                 gamma+=(*k);
00725                 k++;
00726                 pixels+=GetPixelChannels(image);
00727               }
00728             }
00729             gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
00730             SetPixelChannel(sharp_image,channel,ClampToQuantum(gamma*pixel),q);
00731             continue;
00732           }
00733         /*
00734           Alpha blending.
00735         */
00736         for (v=0; v < (ssize_t) (width-j); v++)
00737         {
00738           for (u=0; u < (ssize_t) (width-j); u++)
00739           {
00740             alpha=(MagickRealType) (QuantumScale*GetPixelAlpha(image,pixels));
00741             pixel+=(*k)*alpha*pixels[i];
00742             gamma+=(*k)*alpha;
00743             k++;
00744             pixels+=GetPixelChannels(image);
00745           }
00746         }
00747         gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
00748         SetPixelChannel(sharp_image,channel,ClampToQuantum(gamma*pixel),q);
00749       }
00750       q+=GetPixelChannels(sharp_image);
00751       r+=GetPixelChannels(edge_image);
00752     }
00753     if (SyncCacheViewAuthenticPixels(sharp_view,exception) == MagickFalse)
00754       status=MagickFalse;
00755     if (image->progress_monitor != (MagickProgressMonitor) NULL)
00756       {
00757         MagickBooleanType
00758           proceed;
00759 
00760 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00761         #pragma omp critical (MagickCore_AdaptiveSharpenImage)
00762 #endif
00763         proceed=SetImageProgress(image,AdaptiveSharpenImageTag,progress++,
00764           image->rows);
00765         if (proceed == MagickFalse)
00766           status=MagickFalse;
00767       }
00768   }
00769   sharp_image->type=image->type;
00770   sharp_view=DestroyCacheView(sharp_view);
00771   edge_view=DestroyCacheView(edge_view);
00772   image_view=DestroyCacheView(image_view);
00773   edge_image=DestroyImage(edge_image);
00774   for (i=0; i < (ssize_t) width;  i+=2)
00775     kernel[i]=(double *) RelinquishAlignedMemory(kernel[i]);
00776   kernel=(double **) RelinquishAlignedMemory(kernel);
00777   if (status == MagickFalse)
00778     sharp_image=DestroyImage(sharp_image);
00779   return(sharp_image);
00780 }
00781 
00782 /*
00783 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00784 %                                                                             %
00785 %                                                                             %
00786 %                                                                             %
00787 %     B l u r I m a g e                                                       %
00788 %                                                                             %
00789 %                                                                             %
00790 %                                                                             %
00791 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00792 %
00793 %  BlurImage() blurs an image.  We convolve the image with a Gaussian operator
00794 %  of the given radius and standard deviation (sigma).  For reasonable results,
00795 %  the radius should be larger than sigma.  Use a radius of 0 and BlurImage()
00796 %  selects a suitable radius for you.
00797 %
00798 %  BlurImage() differs from GaussianBlurImage() in that it uses a separable
00799 %  kernel which is faster but mathematically equivalent to the non-separable
00800 %  kernel.
00801 %
00802 %  The format of the BlurImage method is:
00803 %
00804 %      Image *BlurImage(const Image *image,const double radius,
00805 %        const double sigma,const double bias,ExceptionInfo *exception)
00806 %
00807 %  A description of each parameter follows:
00808 %
00809 %    o image: the image.
00810 %
00811 %    o radius: the radius of the Gaussian, in pixels, not counting the center
00812 %      pixel.
00813 %
00814 %    o sigma: the standard deviation of the Gaussian, in pixels.
00815 %
00816 %    o bias: the bias.
00817 %
00818 %    o exception: return any errors or warnings in this structure.
00819 %
00820 */
00821 
00822 static double *GetBlurKernel(const size_t width,const double sigma)
00823 {
00824   double
00825     *kernel,
00826     normalize;
00827 
00828   register ssize_t
00829     i;
00830 
00831   ssize_t
00832     j,
00833     k;
00834 
00835   /*
00836     Generate a 1-D convolution kernel.
00837   */
00838   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
00839   kernel=(double *) AcquireAlignedMemory((size_t) width,sizeof(*kernel));
00840   if (kernel == (double *) NULL)
00841     return(0);
00842   normalize=0.0;
00843   j=(ssize_t) width/2;
00844   i=0;
00845   for (k=(-j); k <= j; k++)
00846   {
00847     kernel[i]=(double) (exp(-((double) k*k)/(2.0*MagickSigma*MagickSigma))/
00848       (MagickSQ2PI*MagickSigma));
00849     normalize+=kernel[i];
00850     i++;
00851   }
00852   for (i=0; i < (ssize_t) width; i++)
00853     kernel[i]/=normalize;
00854   return(kernel);
00855 }
00856 
00857 MagickExport Image *BlurImage(const Image *image,const double radius,
00858   const double sigma,const double bias,ExceptionInfo *exception)
00859 {
00860 #define BlurImageTag  "Blur/Image"
00861 
00862   CacheView
00863     *blur_view,
00864     *image_view;
00865 
00866   double
00867     *kernel;
00868 
00869   Image
00870     *blur_image;
00871 
00872   MagickBooleanType
00873     status;
00874 
00875   MagickOffsetType
00876     progress;
00877 
00878   register ssize_t
00879     i;
00880 
00881   size_t
00882     width;
00883 
00884   ssize_t
00885     center,
00886     x,
00887     y;
00888 
00889   /*
00890     Initialize blur image attributes.
00891   */
00892   assert(image != (Image *) NULL);
00893   assert(image->signature == MagickSignature);
00894   if (image->debug != MagickFalse)
00895     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00896   assert(exception != (ExceptionInfo *) NULL);
00897   assert(exception->signature == MagickSignature);
00898   blur_image=CloneImage(image,0,0,MagickTrue,exception);
00899   if (blur_image == (Image *) NULL)
00900     return((Image *) NULL);
00901   if (fabs(sigma) <= MagickEpsilon)
00902     return(blur_image);
00903   if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
00904     {
00905       blur_image=DestroyImage(blur_image);
00906       return((Image *) NULL);
00907     }
00908   width=GetOptimalKernelWidth1D(radius,sigma);
00909   kernel=GetBlurKernel(width,sigma);
00910   if (kernel == (double *) NULL)
00911     {
00912       blur_image=DestroyImage(blur_image);
00913       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00914     }
00915   if (image->debug != MagickFalse)
00916     {
00917       char
00918         format[MaxTextExtent],
00919         *message;
00920 
00921       register const double
00922         *k;
00923 
00924       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
00925         "  blur image with kernel width %.20g:",(double) width);
00926       message=AcquireString("");
00927       k=kernel;
00928       for (i=0; i < (ssize_t) width; i++)
00929       {
00930         *message='\0';
00931         (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) i);
00932         (void) ConcatenateString(&message,format);
00933         (void) FormatLocaleString(format,MaxTextExtent,"%g ",*k++);
00934         (void) ConcatenateString(&message,format);
00935         (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
00936       }
00937       message=DestroyString(message);
00938     }
00939   /*
00940     Blur rows.
00941   */
00942   status=MagickTrue;
00943   progress=0;
00944   center=(ssize_t) GetPixelChannels(image)*(width/2L);
00945   image_view=AcquireCacheView(image);
00946   blur_view=AcquireCacheView(blur_image);
00947 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00948   #pragma omp parallel for schedule(static,4) shared(progress,status)
00949 #endif
00950   for (y=0; y < (ssize_t) image->rows; y++)
00951   {
00952     register const Quantum
00953       *restrict p;
00954 
00955     register Quantum
00956       *restrict q;
00957 
00958     register ssize_t
00959       x;
00960 
00961     if (status == MagickFalse)
00962       continue;
00963     p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y,
00964       image->columns+width,1,exception);
00965     q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
00966       exception);
00967     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
00968       {
00969         status=MagickFalse;
00970         continue;
00971       }
00972     for (x=0; x < (ssize_t) image->columns; x++)
00973     {
00974       register ssize_t
00975         i;
00976 
00977       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
00978       {
00979         MagickRealType
00980           alpha,
00981           gamma,
00982           pixel;
00983 
00984         PixelChannel
00985           channel;
00986 
00987         PixelTrait
00988           blur_traits,
00989           traits;
00990 
00991         register const double
00992           *restrict k;
00993 
00994         register const Quantum
00995           *restrict pixels;
00996 
00997         register ssize_t
00998           u;
00999 
01000         channel=GetPixelChannelMapChannel(image,i);
01001         traits=GetPixelChannelMapTraits(image,channel);
01002         blur_traits=GetPixelChannelMapTraits(blur_image,channel);
01003         if ((traits == UndefinedPixelTrait) ||
01004             (blur_traits == UndefinedPixelTrait))
01005           continue;
01006         if (((blur_traits & CopyPixelTrait) != 0) ||
01007             (GetPixelMask(image,p) != 0))
01008           {
01009             SetPixelChannel(blur_image,channel,p[center+i],q);
01010             continue;
01011           }
01012         k=kernel;
01013         pixels=p;
01014         pixel=0.0;
01015         if ((blur_traits & BlendPixelTrait) == 0)
01016           {
01017             /*
01018               No alpha blending.
01019             */
01020             for (u=0; u < (ssize_t) width; u++)
01021             {
01022               pixel+=(*k)*pixels[i];
01023               k++;
01024               pixels+=GetPixelChannels(image);
01025             }
01026             SetPixelChannel(blur_image,channel,ClampToQuantum(pixel),q);
01027             continue;
01028           }
01029         /*
01030           Alpha blending.
01031         */
01032         gamma=0.0;
01033         for (u=0; u < (ssize_t) width; u++)
01034         {
01035           alpha=(MagickRealType) (QuantumScale*GetPixelAlpha(image,pixels));
01036           pixel+=(*k)*alpha*pixels[i];
01037           gamma+=(*k)*alpha;
01038           k++;
01039           pixels+=GetPixelChannels(image);
01040         }
01041         gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
01042         SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
01043       }
01044       p+=GetPixelChannels(image);
01045       q+=GetPixelChannels(blur_image);
01046     }
01047     if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
01048       status=MagickFalse;
01049     if (image->progress_monitor != (MagickProgressMonitor) NULL)
01050       {
01051         MagickBooleanType
01052           proceed;
01053 
01054 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01055         #pragma omp critical (MagickCore_BlurImage)
01056 #endif
01057         proceed=SetImageProgress(image,BlurImageTag,progress++,blur_image->rows+
01058           blur_image->columns);
01059         if (proceed == MagickFalse)
01060           status=MagickFalse;
01061       }
01062   }
01063   blur_view=DestroyCacheView(blur_view);
01064   image_view=DestroyCacheView(image_view);
01065   /*
01066     Blur columns.
01067   */
01068   center=(ssize_t) GetPixelChannels(blur_image)*(width/2L);
01069   image_view=AcquireCacheView(blur_image);
01070   blur_view=AcquireCacheView(blur_image);
01071 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01072   #pragma omp parallel for schedule(static,4) shared(progress,status)
01073 #endif
01074   for (x=0; x < (ssize_t) blur_image->columns; x++)
01075   {
01076     register const Quantum
01077       *restrict p;
01078 
01079     register Quantum
01080       *restrict q;
01081 
01082     register ssize_t
01083       y;
01084 
01085     if (status == MagickFalse)
01086       continue;
01087     p=GetCacheViewVirtualPixels(image_view,x,-((ssize_t) width/2L),1,
01088       blur_image->rows+width,exception);
01089     q=GetCacheViewAuthenticPixels(blur_view,x,0,1,blur_image->rows,exception);
01090     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
01091       {
01092         status=MagickFalse;
01093         continue;
01094       }
01095     for (y=0; y < (ssize_t) blur_image->rows; y++)
01096     {
01097       register ssize_t
01098         i;
01099 
01100       for (i=0; i < (ssize_t) GetPixelChannels(blur_image); i++)
01101       {
01102         MagickRealType
01103           alpha,
01104           gamma,
01105           pixel;
01106 
01107         PixelChannel
01108           channel;
01109 
01110         PixelTrait
01111           blur_traits,
01112           traits;
01113 
01114         register const double
01115           *restrict k;
01116 
01117         register const Quantum
01118           *restrict pixels;
01119 
01120         register ssize_t
01121           u;
01122 
01123         channel=GetPixelChannelMapChannel(blur_image,i);
01124         traits=GetPixelChannelMapTraits(blur_image,channel);
01125         blur_traits=GetPixelChannelMapTraits(blur_image,channel);
01126         if ((traits == UndefinedPixelTrait) ||
01127             (blur_traits == UndefinedPixelTrait))
01128           continue;
01129         if (((blur_traits & CopyPixelTrait) != 0) ||
01130             (GetPixelMask(blur_image,p) != 0))
01131           {
01132             SetPixelChannel(blur_image,channel,p[center+i],q);
01133             continue;
01134           }
01135         k=kernel;
01136         pixels=p;
01137         pixel=0.0;
01138         if ((blur_traits & BlendPixelTrait) == 0)
01139           {
01140             /*
01141               No alpha blending.
01142             */
01143             for (u=0; u < (ssize_t) width; u++)
01144             {
01145               pixel+=(*k)*pixels[i];
01146               k++;
01147               pixels+=GetPixelChannels(blur_image);
01148             }
01149             SetPixelChannel(blur_image,channel,ClampToQuantum(pixel),q);
01150             continue;
01151           }
01152         /*
01153           Alpha blending.
01154         */
01155         gamma=0.0;
01156         for (u=0; u < (ssize_t) width; u++)
01157         {
01158           alpha=(MagickRealType) (QuantumScale*GetPixelAlpha(blur_image,
01159             pixels));
01160           pixel+=(*k)*alpha*pixels[i];
01161           gamma+=(*k)*alpha;
01162           k++;
01163           pixels+=GetPixelChannels(blur_image);
01164         }
01165         gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
01166         SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
01167       }
01168       p+=GetPixelChannels(blur_image);
01169       q+=GetPixelChannels(blur_image);
01170     }
01171     if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
01172       status=MagickFalse;
01173     if (blur_image->progress_monitor != (MagickProgressMonitor) NULL)
01174       {
01175         MagickBooleanType
01176           proceed;
01177 
01178 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01179         #pragma omp critical (MagickCore_BlurImage)
01180 #endif
01181         proceed=SetImageProgress(blur_image,BlurImageTag,progress++,
01182           blur_image->rows+blur_image->columns);
01183         if (proceed == MagickFalse)
01184           status=MagickFalse;
01185       }
01186   }
01187   blur_view=DestroyCacheView(blur_view);
01188   image_view=DestroyCacheView(image_view);
01189   kernel=(double *) RelinquishAlignedMemory(kernel);
01190   blur_image->type=image->type;
01191   if (status == MagickFalse)
01192     blur_image=DestroyImage(blur_image);
01193   return(blur_image);
01194 }
01195 
01196 /*
01197 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01198 %                                                                             %
01199 %                                                                             %
01200 %                                                                             %
01201 %     C o n v o l v e I m a g e                                               %
01202 %                                                                             %
01203 %                                                                             %
01204 %                                                                             %
01205 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01206 %
01207 %  ConvolveImage() applies a custom convolution kernel to the image.
01208 %
01209 %  The format of the ConvolveImage method is:
01210 %
01211 %      Image *ConvolveImage(const Image *image,const KernelInfo *kernel,
01212 %        ExceptionInfo *exception)
01213 %
01214 %  A description of each parameter follows:
01215 %
01216 %    o image: the image.
01217 %
01218 %    o kernel: the filtering kernel.
01219 %
01220 %    o exception: return any errors or warnings in this structure.
01221 %
01222 */
01223 MagickExport Image *ConvolveImage(const Image *image,
01224   const KernelInfo *kernel_info,ExceptionInfo *exception)
01225 {
01226 #define ConvolveImageTag  "Convolve/Image"
01227 
01228   CacheView
01229     *convolve_view,
01230     *image_view;
01231 
01232   Image
01233     *convolve_image;
01234 
01235   MagickBooleanType
01236     status;
01237 
01238   MagickOffsetType
01239     progress;
01240 
01241   ssize_t
01242     center,
01243     y;
01244 
01245   /*
01246     Initialize convolve image attributes.
01247   */
01248   assert(image != (Image *) NULL);
01249   assert(image->signature == MagickSignature);
01250   if (image->debug != MagickFalse)
01251     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01252   assert(exception != (ExceptionInfo *) NULL);
01253   assert(exception->signature == MagickSignature);
01254   if ((kernel_info->width % 2) == 0)
01255     ThrowImageException(OptionError,"KernelWidthMustBeAnOddNumber");
01256   convolve_image=CloneImage(image,image->columns,image->rows,MagickTrue,
01257     exception);
01258   if (convolve_image == (Image *) NULL)
01259     return((Image *) NULL);
01260   if (SetImageStorageClass(convolve_image,DirectClass,exception) == MagickFalse)
01261     {
01262       convolve_image=DestroyImage(convolve_image);
01263       return((Image *) NULL);
01264     }
01265   if (image->debug != MagickFalse)
01266     {
01267       char
01268         format[MaxTextExtent],
01269         *message;
01270 
01271       register const MagickRealType
01272         *k;
01273 
01274       register ssize_t
01275         u;
01276 
01277       ssize_t
01278         v;
01279 
01280       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
01281         "  ConvolveImage with %.20gx%.20g kernel:",(double) kernel_info->width,
01282         (double) kernel_info->height);
01283       message=AcquireString("");
01284       k=kernel_info->values;
01285       for (v=0; v < (ssize_t) kernel_info->width; v++)
01286       {
01287         *message='\0';
01288         (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) v);
01289         (void) ConcatenateString(&message,format);
01290         for (u=0; u < (ssize_t) kernel_info->height; u++)
01291         {
01292           (void) FormatLocaleString(format,MaxTextExtent,"%g ",*k++);
01293           (void) ConcatenateString(&message,format);
01294         }
01295         (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
01296       }
01297       message=DestroyString(message);
01298     }
01299   status=AccelerateConvolveImage(image,kernel_info,convolve_image,exception);
01300   if (status == MagickTrue)
01301     return(convolve_image);
01302   /*
01303     Convolve image.
01304     FUTURE: Use Morphology Convolve instead.
01305   */
01306   center=(ssize_t) GetPixelChannels(image)*(image->columns+kernel_info->width)*
01307     (kernel_info->height/2L)+GetPixelChannels(image)*(kernel_info->width/2L);
01308   status=MagickTrue;
01309   progress=0;
01310   image_view=AcquireCacheView(image);
01311   convolve_view=AcquireCacheView(convolve_image);
01312 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01313   #pragma omp parallel for schedule(static,4) shared(progress,status)
01314 #endif
01315   for (y=0; y < (ssize_t) image->rows; y++)
01316   {
01317     register const Quantum
01318       *restrict p;
01319 
01320     register Quantum
01321       *restrict q;
01322 
01323     register ssize_t
01324       x;
01325 
01326     if (status == MagickFalse)
01327       continue;
01328     p=GetCacheViewVirtualPixels(image_view,-((ssize_t) kernel_info->width/2L),y-
01329       (ssize_t) (kernel_info->height/2L),image->columns+kernel_info->width,
01330       kernel_info->height,exception);
01331     q=QueueCacheViewAuthenticPixels(convolve_view,0,y,convolve_image->columns,1,
01332       exception);
01333     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
01334       {
01335         status=MagickFalse;
01336         continue;
01337       }
01338     for (x=0; x < (ssize_t) image->columns; x++)
01339     {
01340       register ssize_t
01341         i;
01342 
01343       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
01344       {
01345         MagickRealType
01346           alpha,
01347           gamma,
01348           pixel;
01349 
01350         PixelChannel
01351           channel;
01352 
01353         PixelTrait
01354           convolve_traits,
01355           traits;
01356 
01357         register const MagickRealType
01358           *restrict k;
01359 
01360         register const Quantum
01361           *restrict pixels;
01362 
01363         register ssize_t
01364           u;
01365 
01366         ssize_t
01367           v;
01368 
01369         channel=GetPixelChannelMapChannel(image,i);
01370         traits=GetPixelChannelMapTraits(image,channel);
01371         convolve_traits=GetPixelChannelMapTraits(convolve_image,channel);
01372         if ((traits == UndefinedPixelTrait) ||
01373             (convolve_traits == UndefinedPixelTrait))
01374           continue;
01375         if (((convolve_traits & CopyPixelTrait) != 0) ||
01376             (GetPixelMask(image,p) != 0))
01377           {
01378             SetPixelChannel(convolve_image,channel,p[center+i],q);
01379             continue;
01380           }
01381         k=kernel_info->values;
01382         pixels=p;
01383         pixel=kernel_info->bias;
01384         if ((convolve_traits & BlendPixelTrait) == 0)
01385           {
01386             /*
01387               No alpha blending.
01388             */
01389             for (v=0; v < (ssize_t) kernel_info->height; v++)
01390             {
01391               for (u=0; u < (ssize_t) kernel_info->width; u++)
01392               {
01393                 pixel+=(*k)*pixels[i];
01394                 k++;
01395                 pixels+=GetPixelChannels(image);
01396               }
01397               pixels+=image->columns*GetPixelChannels(image);
01398             }
01399             SetPixelChannel(convolve_image,channel,ClampToQuantum(pixel),q);
01400             continue;
01401           }
01402         /*
01403           Alpha blending.
01404         */
01405         gamma=0.0;
01406         for (v=0; v < (ssize_t) kernel_info->height; v++)
01407         {
01408           for (u=0; u < (ssize_t) kernel_info->width; u++)
01409           {
01410             alpha=(MagickRealType) (QuantumScale*GetPixelAlpha(image,pixels));
01411             pixel+=(*k)*alpha*pixels[i];
01412             gamma+=(*k)*alpha;
01413             k++;
01414             pixels+=GetPixelChannels(image);
01415           }
01416           pixels+=image->columns*GetPixelChannels(image);
01417         }
01418         gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
01419         SetPixelChannel(convolve_image,channel,ClampToQuantum(gamma*pixel),q);
01420       }
01421       p+=GetPixelChannels(image);
01422       q+=GetPixelChannels(convolve_image);
01423     }
01424     if (SyncCacheViewAuthenticPixels(convolve_view,exception) == MagickFalse)
01425       status=MagickFalse;
01426     if (image->progress_monitor != (MagickProgressMonitor) NULL)
01427       {
01428         MagickBooleanType
01429           proceed;
01430 
01431 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01432         #pragma omp critical (MagickCore_ConvolveImage)
01433 #endif
01434         proceed=SetImageProgress(image,ConvolveImageTag,progress++,image->rows);
01435         if (proceed == MagickFalse)
01436           status=MagickFalse;
01437       }
01438   }
01439   convolve_image->type=image->type;
01440   convolve_view=DestroyCacheView(convolve_view);
01441   image_view=DestroyCacheView(image_view);
01442   if (status == MagickFalse)
01443     convolve_image=DestroyImage(convolve_image);
01444   return(convolve_image);
01445 }
01446 
01447 /*
01448 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01449 %                                                                             %
01450 %                                                                             %
01451 %                                                                             %
01452 %     D e s p e c k l e I m a g e                                             %
01453 %                                                                             %
01454 %                                                                             %
01455 %                                                                             %
01456 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01457 %
01458 %  DespeckleImage() reduces the speckle noise in an image while perserving the
01459 %  edges of the original image.  A speckle removing filter uses a complementary %  hulling technique (raising pixels that are darker than their surrounding
01460 %  neighbors, then complementarily lowering pixels that are brighter than their
01461 %  surrounding neighbors) to reduce the speckle index of that image (reference
01462 %  Crimmins speckle removal).
01463 %
01464 %  The format of the DespeckleImage method is:
01465 %
01466 %      Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
01467 %
01468 %  A description of each parameter follows:
01469 %
01470 %    o image: the image.
01471 %
01472 %    o exception: return any errors or warnings in this structure.
01473 %
01474 */
01475 
01476 static void Hull(const ssize_t x_offset,const ssize_t y_offset,
01477   const size_t columns,const size_t rows,const int polarity,Quantum *restrict f,
01478   Quantum *restrict g)
01479 {
01480   register Quantum
01481     *p,
01482     *q,
01483     *r,
01484     *s;
01485 
01486   ssize_t
01487     y;
01488 
01489   assert(f != (Quantum *) NULL);
01490   assert(g != (Quantum *) NULL);
01491   p=f+(columns+2);
01492   q=g+(columns+2);
01493   r=p+(y_offset*(columns+2)+x_offset);
01494 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01495   #pragma omp parallel for schedule(static)
01496 #endif
01497   for (y=0; y < (ssize_t) rows; y++)
01498   {
01499     register ssize_t
01500       i,
01501       x;
01502 
01503     SignedQuantum
01504       v;
01505 
01506     i=(2*y+1)+y*columns;
01507     if (polarity > 0)
01508       for (x=0; x < (ssize_t) columns; x++)
01509       {
01510         v=(SignedQuantum) p[i];
01511         if ((SignedQuantum) r[i] >= (v+ScaleCharToQuantum(2)))
01512           v+=ScaleCharToQuantum(1);
01513         q[i]=(Quantum) v;
01514         i++;
01515       }
01516     else
01517       for (x=0; x < (ssize_t) columns; x++)
01518       {
01519         v=(SignedQuantum) p[i];
01520         if ((SignedQuantum) r[i] <= (v-ScaleCharToQuantum(2)))
01521           v-=ScaleCharToQuantum(1);
01522         q[i]=(Quantum) v;
01523         i++;
01524       }
01525   }
01526   p=f+(columns+2);
01527   q=g+(columns+2);
01528   r=q+(y_offset*(columns+2)+x_offset);
01529   s=q-(y_offset*(columns+2)+x_offset);
01530 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01531   #pragma omp parallel for schedule(static)
01532 #endif
01533   for (y=0; y < (ssize_t) rows; y++)
01534   {
01535     register ssize_t
01536       i,
01537       x;
01538 
01539     SignedQuantum
01540       v;
01541 
01542     i=(2*y+1)+y*columns;
01543     if (polarity > 0)
01544       for (x=0; x < (ssize_t) columns; x++)
01545       {
01546         v=(SignedQuantum) q[i];
01547         if (((SignedQuantum) s[i] >= (v+ScaleCharToQuantum(2))) &&
01548             ((SignedQuantum) r[i] > v))
01549           v+=ScaleCharToQuantum(1);
01550         p[i]=(Quantum) v;
01551         i++;
01552       }
01553     else
01554       for (x=0; x < (ssize_t) columns; x++)
01555       {
01556         v=(SignedQuantum) q[i];
01557         if (((SignedQuantum) s[i] <= (v-ScaleCharToQuantum(2))) &&
01558             ((SignedQuantum) r[i] < v))
01559           v-=ScaleCharToQuantum(1);
01560         p[i]=(Quantum) v;
01561         i++;
01562       }
01563   }
01564 }
01565 
01566 MagickExport Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
01567 {
01568 #define DespeckleImageTag  "Despeckle/Image"
01569 
01570   CacheView
01571     *despeckle_view,
01572     *image_view;
01573 
01574   Image
01575     *despeckle_image;
01576 
01577   MagickBooleanType
01578     status;
01579 
01580   Quantum
01581     *restrict buffer,
01582     *restrict pixels;
01583 
01584   register ssize_t
01585     i;
01586 
01587   size_t
01588     length;
01589 
01590   static const ssize_t
01591     X[4] = {0, 1, 1,-1},
01592     Y[4] = {1, 0, 1, 1};
01593 
01594   /*
01595     Allocate despeckled image.
01596   */
01597   assert(image != (const Image *) NULL);
01598   assert(image->signature == MagickSignature);
01599   if (image->debug != MagickFalse)
01600     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01601   assert(exception != (ExceptionInfo *) NULL);
01602   assert(exception->signature == MagickSignature);
01603   despeckle_image=CloneImage(image,0,0,MagickTrue,exception);
01604   if (despeckle_image == (Image *) NULL)
01605     return((Image *) NULL);
01606   status=SetImageStorageClass(despeckle_image,DirectClass,exception);
01607   if (status == MagickFalse)
01608     {
01609       despeckle_image=DestroyImage(despeckle_image);
01610       return((Image *) NULL);
01611     }
01612   /*
01613     Allocate image buffer.
01614   */
01615   length=(size_t) ((image->columns+2)*(image->rows+2));
01616   pixels=(Quantum *) AcquireQuantumMemory(length,sizeof(*pixels));
01617   buffer=(Quantum *) AcquireQuantumMemory(length,sizeof(*buffer));
01618   if ((pixels == (Quantum *) NULL) || (buffer == (Quantum *) NULL))
01619     {
01620       if (buffer != (Quantum *) NULL)
01621         buffer=(Quantum *) RelinquishMagickMemory(buffer);
01622       if (pixels != (Quantum *) NULL)
01623         pixels=(Quantum *) RelinquishMagickMemory(pixels);
01624       despeckle_image=DestroyImage(despeckle_image);
01625       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
01626     }
01627   /*
01628     Reduce speckle in the image.
01629   */
01630   status=MagickTrue;
01631   image_view=AcquireCacheView(image);
01632   despeckle_view=AcquireCacheView(despeckle_image);
01633   for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
01634   {
01635     PixelChannel
01636        channel;
01637 
01638     PixelTrait
01639       despeckle_traits,
01640       traits;
01641 
01642     register ssize_t
01643       k,
01644       x;
01645 
01646     ssize_t
01647       j,
01648       y;
01649 
01650     if (status == MagickFalse)
01651       continue;
01652     channel=GetPixelChannelMapChannel(image,i);
01653     traits=GetPixelChannelMapTraits(image,channel);
01654     despeckle_traits=GetPixelChannelMapTraits(despeckle_image,channel);
01655     if ((traits == UndefinedPixelTrait) ||
01656         (despeckle_traits == UndefinedPixelTrait))
01657       continue;
01658     if ((despeckle_traits & CopyPixelTrait) != 0)
01659       continue;
01660     (void) ResetMagickMemory(pixels,0,length*sizeof(*pixels));
01661     j=(ssize_t) image->columns+2;
01662     for (y=0; y < (ssize_t) image->rows; y++)
01663     {
01664       register const Quantum
01665         *restrict p;
01666 
01667       p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
01668       if (p == (const Quantum *) NULL)
01669         {
01670           status=MagickFalse;
01671           continue;
01672         }
01673       j++;
01674       for (x=0; x < (ssize_t) image->columns; x++)
01675       {
01676         pixels[j++]=p[i];
01677         p+=GetPixelChannels(image);
01678       }
01679       j++;
01680     }
01681     (void) ResetMagickMemory(buffer,0,length*sizeof(*buffer));
01682     for (k=0; k < 4; k++)
01683     {
01684       Hull(X[k],Y[k],image->columns,image->rows,1,pixels,buffer);
01685       Hull(-X[k],-Y[k],image->columns,image->rows,1,pixels,buffer);
01686       Hull(-X[k],-Y[k],image->columns,image->rows,-1,pixels,buffer);
01687       Hull(X[k],Y[k],image->columns,image->rows,-1,pixels,buffer);
01688     }
01689     j=(ssize_t) image->columns+2;
01690     for (y=0; y < (ssize_t) image->rows; y++)
01691     {
01692       MagickBooleanType
01693         sync;
01694 
01695       register Quantum
01696         *restrict q;
01697 
01698       q=QueueCacheViewAuthenticPixels(despeckle_view,0,y,
01699         despeckle_image->columns,1,exception);
01700       if (q == (Quantum *) NULL)
01701         {
01702           status=MagickFalse;
01703           continue;
01704         }
01705       j++;
01706       for (x=0; x < (ssize_t) image->columns; x++)
01707       {
01708         SetPixelChannel(despeckle_image,channel,pixels[j++],q);
01709         q+=GetPixelChannels(despeckle_image);
01710       }
01711       sync=SyncCacheViewAuthenticPixels(despeckle_view,exception);
01712       if (sync == MagickFalse)
01713         status=MagickFalse;
01714       j++;
01715     }
01716     if (image->progress_monitor != (MagickProgressMonitor) NULL)
01717       {
01718         MagickBooleanType
01719           proceed;
01720 
01721         proceed=SetImageProgress(image,DespeckleImageTag,(MagickOffsetType) i,
01722           GetPixelChannels(image));
01723         if (proceed == MagickFalse)
01724           status=MagickFalse;
01725       }
01726   }
01727   despeckle_view=DestroyCacheView(despeckle_view);
01728   image_view=DestroyCacheView(image_view);
01729   buffer=(Quantum *) RelinquishMagickMemory(buffer);
01730   pixels=(Quantum *) RelinquishMagickMemory(pixels);
01731   despeckle_image->type=image->type;
01732   if (status == MagickFalse)
01733     despeckle_image=DestroyImage(despeckle_image);
01734   return(despeckle_image);
01735 }
01736 
01737 /*
01738 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01739 %                                                                             %
01740 %                                                                             %
01741 %                                                                             %
01742 %     E d g e I m a g e                                                       %
01743 %                                                                             %
01744 %                                                                             %
01745 %                                                                             %
01746 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01747 %
01748 %  EdgeImage() finds edges in an image.  Radius defines the radius of the
01749 %  convolution filter.  Use a radius of 0 and EdgeImage() selects a suitable
01750 %  radius for you.
01751 %
01752 %  The format of the EdgeImage method is:
01753 %
01754 %      Image *EdgeImage(const Image *image,const double radius,
01755 %        const double sigma,ExceptionInfo *exception)
01756 %
01757 %  A description of each parameter follows:
01758 %
01759 %    o image: the image.
01760 %
01761 %    o radius: the radius of the pixel neighborhood.
01762 %
01763 %    o sigma: the standard deviation of the Gaussian, in pixels.
01764 %
01765 %    o exception: return any errors or warnings in this structure.
01766 %
01767 */
01768 MagickExport Image *EdgeImage(const Image *image,const double radius,
01769   const double sigma,ExceptionInfo *exception)
01770 {
01771   Image
01772     *edge_image;
01773 
01774   KernelInfo
01775     *kernel_info;
01776 
01777   register ssize_t
01778     i;
01779 
01780   size_t
01781     width;
01782 
01783   ssize_t
01784     j,
01785     u,
01786     v;
01787 
01788   assert(image != (const Image *) NULL);
01789   assert(image->signature == MagickSignature);
01790   if (image->debug != MagickFalse)
01791     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01792   assert(exception != (ExceptionInfo *) NULL);
01793   assert(exception->signature == MagickSignature);
01794   width=GetOptimalKernelWidth1D(radius,sigma);
01795   kernel_info=AcquireKernelInfo((const char *) NULL);
01796   if (kernel_info == (KernelInfo *) NULL)
01797     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
01798   kernel_info->width=width;
01799   kernel_info->height=width;
01800   kernel_info->values=(MagickRealType *) AcquireAlignedMemory(
01801     kernel_info->width,kernel_info->width*sizeof(*kernel_info->values));
01802   if (kernel_info->values == (MagickRealType *) NULL)
01803     {
01804       kernel_info=DestroyKernelInfo(kernel_info);
01805       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
01806     }
01807   j=(ssize_t) kernel_info->width/2;
01808   i=0;
01809   for (v=(-j); v <= j; v++)
01810   {
01811     for (u=(-j); u <= j; u++)
01812     {
01813       kernel_info->values[i]=(-1.0);
01814       i++;
01815     }
01816   }
01817   kernel_info->values[i/2]=(double) (width*width-1.0);
01818   kernel_info->bias=image->bias;   /* FUTURE: User bias on a edge image? */
01819   edge_image=ConvolveImage(image,kernel_info,exception);
01820   kernel_info=DestroyKernelInfo(kernel_info);
01821   return(edge_image);
01822 }
01823 
01824 /*
01825 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01826 %                                                                             %
01827 %                                                                             %
01828 %                                                                             %
01829 %     E m b o s s I m a g e                                                   %
01830 %                                                                             %
01831 %                                                                             %
01832 %                                                                             %
01833 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01834 %
01835 %  EmbossImage() returns a grayscale image with a three-dimensional effect.
01836 %  We convolve the image with a Gaussian operator of the given radius and
01837 %  standard deviation (sigma).  For reasonable results, radius should be
01838 %  larger than sigma.  Use a radius of 0 and Emboss() selects a suitable
01839 %  radius for you.
01840 %
01841 %  The format of the EmbossImage method is:
01842 %
01843 %      Image *EmbossImage(const Image *image,const double radius,
01844 %        const double sigma,ExceptionInfo *exception)
01845 %
01846 %  A description of each parameter follows:
01847 %
01848 %    o image: the image.
01849 %
01850 %    o radius: the radius of the pixel neighborhood.
01851 %
01852 %    o sigma: the standard deviation of the Gaussian, in pixels.
01853 %
01854 %    o exception: return any errors or warnings in this structure.
01855 %
01856 */
01857 MagickExport Image *EmbossImage(const Image *image,const double radius,
01858   const double sigma,ExceptionInfo *exception)
01859 {
01860   Image
01861     *emboss_image;
01862 
01863   KernelInfo
01864     *kernel_info;
01865 
01866   register ssize_t
01867     i;
01868 
01869   size_t
01870     width;
01871 
01872   ssize_t
01873     j,
01874     k,
01875     u,
01876     v;
01877 
01878   assert(image != (const Image *) NULL);
01879   assert(image->signature == MagickSignature);
01880   if (image->debug != MagickFalse)
01881     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01882   assert(exception != (ExceptionInfo *) NULL);
01883   assert(exception->signature == MagickSignature);
01884   width=GetOptimalKernelWidth1D(radius,sigma);
01885   kernel_info=AcquireKernelInfo((const char *) NULL);
01886   if (kernel_info == (KernelInfo *) NULL)
01887     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
01888   kernel_info->width=width;
01889   kernel_info->height=width;
01890   kernel_info->values=(MagickRealType *) AcquireAlignedMemory(
01891     kernel_info->width,kernel_info->width*sizeof(*kernel_info->values));
01892   if (kernel_info->values == (MagickRealType *) NULL)
01893     {
01894       kernel_info=DestroyKernelInfo(kernel_info);
01895       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
01896     }
01897   j=(ssize_t) kernel_info->width/2;
01898   k=j;
01899   i=0;
01900   for (v=(-j); v <= j; v++)
01901   {
01902     for (u=(-j); u <= j; u++)
01903     {
01904       kernel_info->values[i]=(double) (((u < 0) || (v < 0) ? -8.0 : 8.0)*
01905         exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma))/
01906         (2.0*MagickPI*MagickSigma*MagickSigma));
01907       if (u != k)
01908         kernel_info->values[i]=0.0;
01909       i++;
01910     }
01911     k--;
01912   }
01913   kernel_info->bias=image->bias;  /* FUTURE: user bias on an edge image */
01914   emboss_image=ConvolveImage(image,kernel_info,exception);
01915   kernel_info=DestroyKernelInfo(kernel_info);
01916   if (emboss_image != (Image *) NULL)
01917     (void) EqualizeImage(emboss_image,exception);
01918   return(emboss_image);
01919 }
01920 
01921 /*
01922 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01923 %                                                                             %
01924 %                                                                             %
01925 %                                                                             %
01926 %     G a u s s i a n B l u r I m a g e                                       %
01927 %                                                                             %
01928 %                                                                             %
01929 %                                                                             %
01930 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01931 %
01932 %  GaussianBlurImage() blurs an image.  We convolve the image with a
01933 %  Gaussian operator of the given radius and standard deviation (sigma).
01934 %  For reasonable results, the radius should be larger than sigma.  Use a
01935 %  radius of 0 and GaussianBlurImage() selects a suitable radius for you
01936 %
01937 %  The format of the GaussianBlurImage method is:
01938 %
01939 %      Image *GaussianBlurImage(const Image *image,onst double radius,
01940 %        const double sigma,ExceptionInfo *exception)
01941 %
01942 %  A description of each parameter follows:
01943 %
01944 %    o image: the image.
01945 %
01946 %    o radius: the radius of the Gaussian, in pixels, not counting the center
01947 %      pixel.
01948 %
01949 %    o sigma: the standard deviation of the Gaussian, in pixels.
01950 %
01951 %    o exception: return any errors or warnings in this structure.
01952 %
01953 */
01954 MagickExport Image *GaussianBlurImage(const Image *image,const double radius,
01955   const double sigma,ExceptionInfo *exception)
01956 {
01957   Image
01958     *blur_image;
01959 
01960   KernelInfo
01961     *kernel_info;
01962 
01963   register ssize_t
01964     i;
01965 
01966   size_t
01967     width;
01968 
01969   ssize_t
01970     j,
01971     u,
01972     v;
01973 
01974   assert(image != (const Image *) NULL);
01975   assert(image->signature == MagickSignature);
01976   if (image->debug != MagickFalse)
01977     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01978   assert(exception != (ExceptionInfo *) NULL);
01979   assert(exception->signature == MagickSignature);
01980   width=GetOptimalKernelWidth2D(radius,sigma);
01981   kernel_info=AcquireKernelInfo((const char *) NULL);
01982   if (kernel_info == (KernelInfo *) NULL)
01983     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
01984   (void) ResetMagickMemory(kernel_info,0,sizeof(*kernel_info));
01985   kernel_info->width=width;
01986   kernel_info->height=width;
01987   kernel_info->signature=MagickSignature;
01988   kernel_info->values=(MagickRealType *) AcquireAlignedMemory(
01989     kernel_info->width,kernel_info->width*sizeof(*kernel_info->values));
01990   if (kernel_info->values == (MagickRealType *) NULL)
01991     {
01992       kernel_info=DestroyKernelInfo(kernel_info);
01993       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
01994     }
01995   j=(ssize_t) kernel_info->width/2;
01996   i=0;
01997   for (v=(-j); v <= j; v++)
01998   {
01999     for (u=(-j); u <= j; u++)
02000     {
02001       kernel_info->values[i]=(double) (exp(-((double) u*u+v*v)/(2.0*
02002         MagickSigma*MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
02003       i++;
02004     }
02005   }
02006   blur_image=ConvolveImage(image,kernel_info,exception);
02007   kernel_info=DestroyKernelInfo(kernel_info);
02008   return(blur_image);
02009 }
02010 
02011 /*
02012 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02013 %                                                                             %
02014 %                                                                             %
02015 %                                                                             %
02016 %     M o t i o n B l u r I m a g e                                           %
02017 %                                                                             %
02018 %                                                                             %
02019 %                                                                             %
02020 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02021 %
02022 %  MotionBlurImage() simulates motion blur.  We convolve the image with a
02023 %  Gaussian operator of the given radius and standard deviation (sigma).
02024 %  For reasonable results, radius should be larger than sigma.  Use a
02025 %  radius of 0 and MotionBlurImage() selects a suitable radius for you.
02026 %  Angle gives the angle of the blurring motion.
02027 %
02028 %  Andrew Protano contributed this effect.
02029 %
02030 %  The format of the MotionBlurImage method is:
02031 %
02032 %    Image *MotionBlurImage(const Image *image,const double radius,
02033 %      const double sigma,const double angle,const double bias,
02034 %      ExceptionInfo *exception)
02035 %
02036 %  A description of each parameter follows:
02037 %
02038 %    o image: the image.
02039 %
02040 %    o radius: the radius of the Gaussian, in pixels, not counting
02041 %      the center pixel.
02042 %
02043 %    o sigma: the standard deviation of the Gaussian, in pixels.
02044 %
02045 %    o angle: Apply the effect along this angle.
02046 %
02047 %    o bias: the bias.
02048 %
02049 %    o exception: return any errors or warnings in this structure.
02050 %
02051 */
02052 
02053 static double *GetMotionBlurKernel(const size_t width,const double sigma)
02054 {
02055   double
02056     *kernel,
02057     normalize;
02058 
02059   register ssize_t
02060     i;
02061 
02062   /*
02063    Generate a 1-D convolution kernel.
02064   */
02065   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
02066   kernel=(double *) AcquireAlignedMemory((size_t) width,sizeof(*kernel));
02067   if (kernel == (double *) NULL)
02068     return(kernel);
02069   normalize=0.0;
02070   for (i=0; i < (ssize_t) width; i++)
02071   {
02072     kernel[i]=(double) (exp((-((double) i*i)/(double) (2.0*MagickSigma*
02073       MagickSigma)))/(MagickSQ2PI*MagickSigma));
02074     normalize+=kernel[i];
02075   }
02076   for (i=0; i < (ssize_t) width; i++)
02077     kernel[i]/=normalize;
02078   return(kernel);
02079 }
02080 
02081 MagickExport Image *MotionBlurImage(const Image *image,const double radius,
02082   const double sigma,const double angle,const double bias,
02083   ExceptionInfo *exception)
02084 {
02085   CacheView
02086     *blur_view,
02087     *image_view,
02088     *motion_view;
02089 
02090   double
02091     *kernel;
02092 
02093   Image
02094     *blur_image;
02095 
02096   MagickBooleanType
02097     status;
02098 
02099   MagickOffsetType
02100     progress;
02101 
02102   OffsetInfo
02103     *offset;
02104 
02105   PointInfo
02106     point;
02107 
02108   register ssize_t
02109     i;
02110 
02111   size_t
02112     width;
02113 
02114   ssize_t
02115     y;
02116 
02117   assert(image != (Image *) NULL);
02118   assert(image->signature == MagickSignature);
02119   if (image->debug != MagickFalse)
02120     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
02121   assert(exception != (ExceptionInfo *) NULL);
02122   width=GetOptimalKernelWidth1D(radius,sigma);
02123   kernel=GetMotionBlurKernel(width,sigma);
02124   if (kernel == (double *) NULL)
02125     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
02126   offset=(OffsetInfo *) AcquireQuantumMemory(width,sizeof(*offset));
02127   if (offset == (OffsetInfo *) NULL)
02128     {
02129       kernel=(double *) RelinquishAlignedMemory(kernel);
02130       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
02131     }
02132   blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
02133   if (blur_image == (Image *) NULL)
02134     {
02135       kernel=(double *) RelinquishAlignedMemory(kernel);
02136       offset=(OffsetInfo *) RelinquishMagickMemory(offset);
02137       return((Image *) NULL);
02138     }
02139   if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
02140     {
02141       kernel=(double *) RelinquishAlignedMemory(kernel);
02142       offset=(OffsetInfo *) RelinquishMagickMemory(offset);
02143       blur_image=DestroyImage(blur_image);
02144       return((Image *) NULL);
02145     }
02146   point.x=(double) width*sin(DegreesToRadians(angle));
02147   point.y=(double) width*cos(DegreesToRadians(angle));
02148   for (i=0; i < (ssize_t) width; i++)
02149   {
02150     offset[i].x=(ssize_t) ceil((double) (i*point.y)/hypot(point.x,point.y)-0.5);
02151     offset[i].y=(ssize_t) ceil((double) (i*point.x)/hypot(point.x,point.y)-0.5);
02152   }
02153   /*
02154     Motion blur image.
02155   */
02156   status=MagickTrue;
02157   progress=0;
02158   image_view=AcquireCacheView(image);
02159   motion_view=AcquireCacheView(image);
02160   blur_view=AcquireCacheView(blur_image);
02161 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02162   #pragma omp parallel for schedule(static,4) shared(progress,status)
02163 #endif
02164   for (y=0; y < (ssize_t) image->rows; y++)
02165   {
02166     register const Quantum
02167       *restrict p;
02168 
02169     register Quantum
02170       *restrict q;
02171 
02172     register ssize_t
02173       x;
02174 
02175     if (status == MagickFalse)
02176       continue;
02177     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
02178     q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
02179       exception);
02180     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
02181       {
02182         status=MagickFalse;
02183         continue;
02184       }
02185     for (x=0; x < (ssize_t) image->columns; x++)
02186     {
02187       register ssize_t
02188         i;
02189 
02190       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
02191       {
02192         MagickRealType
02193           alpha,
02194           gamma,
02195           pixel;
02196 
02197         PixelChannel
02198           channel;
02199 
02200         PixelTrait
02201           blur_traits,
02202           traits;
02203 
02204         register const Quantum
02205           *restrict r;
02206 
02207         register double
02208           *restrict k;
02209 
02210         register ssize_t
02211           j;
02212 
02213         channel=GetPixelChannelMapChannel(image,i);
02214         traits=GetPixelChannelMapTraits(image,channel);
02215         blur_traits=GetPixelChannelMapTraits(blur_image,channel);
02216         if ((traits == UndefinedPixelTrait) ||
02217             (blur_traits == UndefinedPixelTrait))
02218           continue;
02219         if (((blur_traits & CopyPixelTrait) != 0) ||
02220             (GetPixelMask(image,p) != 0))
02221           {
02222             SetPixelChannel(blur_image,channel,p[i],q);
02223             continue;
02224           }
02225         k=kernel;
02226         pixel=bias;
02227         if ((blur_traits & BlendPixelTrait) == 0)
02228           {
02229             for (j=0; j < (ssize_t) width; j++)
02230             {
02231               r=GetCacheViewVirtualPixels(motion_view,x+offset[j].x,y+
02232                 offset[j].y,1,1,exception);
02233               if (r == (const Quantum *) NULL)
02234                 {
02235                   status=MagickFalse;
02236                   continue;
02237                 }
02238               pixel+=(*k)*r[i];
02239               k++;
02240             }
02241             SetPixelChannel(blur_image,channel,ClampToQuantum(pixel),q);
02242             continue;
02243           }
02244         alpha=0.0;
02245         gamma=0.0;
02246         for (j=0; j < (ssize_t) width; j++)
02247         {
02248           r=GetCacheViewVirtualPixels(motion_view,x+offset[j].x,y+offset[j].y,1,
02249             1,exception);
02250           if (r == (const Quantum *) NULL)
02251             {
02252               status=MagickFalse;
02253               continue;
02254             }
02255           alpha=(MagickRealType) (QuantumScale*GetPixelAlpha(image,r));
02256           pixel+=(*k)*alpha*r[i];
02257           gamma+=(*k)*alpha;
02258           k++;
02259         }
02260         gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
02261         SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
02262       }
02263       p+=GetPixelChannels(image);
02264       q+=GetPixelChannels(blur_image);
02265     }
02266     if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
02267       status=MagickFalse;
02268     if (image->progress_monitor != (MagickProgressMonitor) NULL)
02269       {
02270         MagickBooleanType
02271           proceed;
02272 
02273 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02274         #pragma omp critical (MagickCore_MotionBlurImage)
02275 #endif
02276         proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
02277         if (proceed == MagickFalse)
02278           status=MagickFalse;
02279       }
02280   }
02281   blur_view=DestroyCacheView(blur_view);
02282   motion_view=DestroyCacheView(motion_view);
02283   image_view=DestroyCacheView(image_view);
02284   kernel=(double *) RelinquishAlignedMemory(kernel);
02285   offset=(OffsetInfo *) RelinquishMagickMemory(offset);
02286   if (status == MagickFalse)
02287     blur_image=DestroyImage(blur_image);
02288   return(blur_image);
02289 }
02290 
02291 /*
02292 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02293 %                                                                             %
02294 %                                                                             %
02295 %                                                                             %
02296 %     P r e v i e w I m a g e                                                 %
02297 %                                                                             %
02298 %                                                                             %
02299 %                                                                             %
02300 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02301 %
02302 %  PreviewImage() tiles 9 thumbnails of the specified image with an image
02303 %  processing operation applied with varying parameters.  This may be helpful
02304 %  pin-pointing an appropriate parameter for a particular image processing
02305 %  operation.
02306 %
02307 %  The format of the PreviewImages method is:
02308 %
02309 %      Image *PreviewImages(const Image *image,const PreviewType preview,
02310 %        ExceptionInfo *exception)
02311 %
02312 %  A description of each parameter follows:
02313 %
02314 %    o image: the image.
02315 %
02316 %    o preview: the image processing operation.
02317 %
02318 %    o exception: return any errors or warnings in this structure.
02319 %
02320 */
02321 MagickExport Image *PreviewImage(const Image *image,const PreviewType preview,
02322   ExceptionInfo *exception)
02323 {
02324 #define NumberTiles  9
02325 #define PreviewImageTag  "Preview/Image"
02326 #define DefaultPreviewGeometry  "204x204+10+10"
02327 
02328   char
02329     factor[MaxTextExtent],
02330     label[MaxTextExtent];
02331 
02332   double
02333     degrees,
02334     gamma,
02335     percentage,
02336     radius,
02337     sigma,
02338     threshold;
02339 
02340   Image
02341     *images,
02342     *montage_image,
02343     *preview_image,
02344     *thumbnail;
02345 
02346   ImageInfo
02347     *preview_info;
02348 
02349   MagickBooleanType
02350     proceed;
02351 
02352   MontageInfo
02353     *montage_info;
02354 
02355   QuantizeInfo
02356     quantize_info;
02357 
02358   RectangleInfo
02359     geometry;
02360 
02361   register ssize_t
02362     i,
02363     x;
02364 
02365   size_t
02366     colors;
02367 
02368   ssize_t
02369     y;
02370 
02371   /*
02372     Open output image file.
02373   */
02374   assert(image != (Image *) NULL);
02375   assert(image->signature == MagickSignature);
02376   if (image->debug != MagickFalse)
02377     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
02378   colors=2;
02379   degrees=0.0;
02380   gamma=(-0.2f);
02381   preview_info=AcquireImageInfo();
02382   SetGeometry(image,&geometry);
02383   (void) ParseMetaGeometry(DefaultPreviewGeometry,&geometry.x,&geometry.y,
02384     &geometry.width,&geometry.height);
02385   images=NewImageList();
02386   percentage=12.5;
02387   GetQuantizeInfo(&quantize_info);
02388   radius=0.0;
02389   sigma=1.0;
02390   threshold=0.0;
02391   x=0;
02392   y=0;
02393   for (i=0; i < NumberTiles; i++)
02394   {
02395     thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
02396     if (thumbnail == (Image *) NULL)
02397       break;
02398     (void) SetImageProgressMonitor(thumbnail,(MagickProgressMonitor) NULL,
02399       (void *) NULL);
02400     (void) SetImageProperty(thumbnail,"label",DefaultTileLabel,exception);
02401     if (i == (NumberTiles/2))
02402       {
02403         (void) QueryColorCompliance("#dfdfdf",AllCompliance,
02404           &thumbnail->matte_color,exception);
02405         AppendImageToList(&images,thumbnail);
02406         continue;
02407       }
02408     switch (preview)
02409     {
02410       case RotatePreview:
02411       {
02412         degrees+=45.0;
02413         preview_image=RotateImage(thumbnail,degrees,exception);
02414         (void) FormatLocaleString(label,MaxTextExtent,"rotate %g",degrees);
02415         break;
02416       }
02417       case ShearPreview:
02418       {
02419         degrees+=5.0;
02420         preview_image=ShearImage(thumbnail,degrees,degrees,exception);
02421         (void) FormatLocaleString(label,MaxTextExtent,"shear %gx%g",
02422           degrees,2.0*degrees);
02423         break;
02424       }
02425       case RollPreview:
02426       {
02427         x=(ssize_t) ((i+1)*thumbnail->columns)/NumberTiles;
02428         y=(ssize_t) ((i+1)*thumbnail->rows)/NumberTiles;
02429         preview_image=RollImage(thumbnail,x,y,exception);
02430         (void) FormatLocaleString(label,MaxTextExtent,"roll %+.20gx%+.20g",
02431           (double) x,(double) y);
02432         break;
02433       }
02434       case HuePreview:
02435       {
02436         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
02437         if (preview_image == (Image *) NULL)
02438           break;
02439         (void) FormatLocaleString(factor,MaxTextExtent,"100,100,%g",
02440           2.0*percentage);
02441         (void) ModulateImage(preview_image,factor,exception);
02442         (void) FormatLocaleString(label,MaxTextExtent,"modulate %s",factor);
02443         break;
02444       }
02445       case SaturationPreview:
02446       {
02447         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
02448         if (preview_image == (Image *) NULL)
02449           break;
02450         (void) FormatLocaleString(factor,MaxTextExtent,"100,%g",
02451           2.0*percentage);
02452         (void) ModulateImage(preview_image,factor,exception);
02453         (void) FormatLocaleString(label,MaxTextExtent,"modulate %s",factor);
02454         break;
02455       }
02456       case BrightnessPreview:
02457       {
02458         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
02459         if (preview_image == (Image *) NULL)
02460           break;
02461         (void) FormatLocaleString(factor,MaxTextExtent,"%g",2.0*percentage);
02462         (void) ModulateImage(preview_image,factor,exception);
02463         (void) FormatLocaleString(label,MaxTextExtent,"modulate %s",factor);
02464         break;
02465       }
02466       case GammaPreview:
02467       default:
02468       {
02469         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
02470         if (preview_image == (Image *) NULL)
02471           break;
02472         gamma+=0.4f;
02473         (void) GammaImage(preview_image,gamma,exception);
02474         (void) FormatLocaleString(label,MaxTextExtent,"gamma %g",gamma);
02475         break;
02476       }
02477       case SpiffPreview:
02478       {
02479         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
02480         if (preview_image != (Image *) NULL)
02481           for (x=0; x < i; x++)
02482             (void) ContrastImage(preview_image,MagickTrue,exception);
02483         (void) FormatLocaleString(label,MaxTextExtent,"contrast (%.20g)",
02484           (double) i+1);
02485         break;
02486       }
02487       case DullPreview:
02488       {
02489         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
02490         if (preview_image == (Image *) NULL)
02491           break;
02492         for (x=0; x < i; x++)
02493           (void) ContrastImage(preview_image,MagickFalse,exception);
02494         (void) FormatLocaleString(label,MaxTextExtent,"+contrast (%.20g)",
02495           (double) i+1);
02496         break;
02497       }
02498       case GrayscalePreview:
02499       {
02500         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
02501         if (preview_image == (Image *) NULL)
02502           break;
02503         colors<<=1;
02504         quantize_info.number_colors=colors;
02505         quantize_info.colorspace=GRAYColorspace;
02506         (void) QuantizeImage(&quantize_info,preview_image,exception);
02507         (void) FormatLocaleString(label,MaxTextExtent,
02508           "-colorspace gray -colors %.20g",(double) colors);
02509         break;
02510       }
02511       case QuantizePreview:
02512       {
02513         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
02514         if (preview_image == (Image *) NULL)
02515           break;
02516         colors<<=1;
02517         quantize_info.number_colors=colors;
02518         (void) QuantizeImage(&quantize_info,preview_image,exception);
02519         (void) FormatLocaleString(label,MaxTextExtent,"colors %.20g",(double)
02520           colors);
02521         break;
02522       }
02523       case DespecklePreview:
02524       {
02525         for (x=0; x < (i-1); x++)
02526         {
02527           preview_image=DespeckleImage(thumbnail,exception);
02528           if (preview_image == (Image *) NULL)
02529             break;
02530           thumbnail=DestroyImage(thumbnail);
02531           thumbnail=preview_image;
02532         }
02533         preview_image=DespeckleImage(thumbnail,exception);
02534         if (preview_image == (Image *) NULL)
02535           break;
02536         (void) FormatLocaleString(label,MaxTextExtent,"despeckle (%.20g)",
02537           (double) i+1);
02538         break;
02539       }
02540       case ReduceNoisePreview:
02541       {
02542         preview_image=StatisticImage(thumbnail,NonpeakStatistic,(size_t) radius,
02543           (size_t) radius,exception);
02544         (void) FormatLocaleString(label,MaxTextExtent,"noise %g",radius);
02545         break;
02546       }
02547       case AddNoisePreview:
02548       {
02549         switch ((int) i)
02550         {
02551           case 0:
02552           {
02553             (void) CopyMagickString(factor,"uniform",MaxTextExtent);
02554             break;
02555           }
02556           case 1:
02557           {
02558             (void) CopyMagickString(factor,"gaussian",MaxTextExtent);
02559             break;
02560           }
02561           case 2:
02562           {
02563             (void) CopyMagickString(factor,"multiplicative",MaxTextExtent);
02564             break;
02565           }
02566           case 3:
02567           {
02568             (void) CopyMagickString(factor,"impulse",MaxTextExtent);
02569             break;
02570           }
02571           case 4:
02572           {
02573             (void) CopyMagickString(factor,"laplacian",MaxTextExtent);
02574             break;
02575           }
02576           case 5:
02577           {
02578             (void) CopyMagickString(factor,"Poisson",MaxTextExtent);
02579             break;
02580           }
02581           default:
02582           {
02583             (void) CopyMagickString(thumbnail->magick,"NULL",MaxTextExtent);
02584             break;
02585           }
02586         }
02587         preview_image=StatisticImage(thumbnail,NonpeakStatistic,(size_t) i,
02588           (size_t) i,exception);
02589         (void) FormatLocaleString(label,MaxTextExtent,"+noise %s",factor);
02590         break;
02591       }
02592       case SharpenPreview:
02593       {
02594         /* FUTURE: user bias on sharpen! This is non-sensical! */
02595         preview_image=SharpenImage(thumbnail,radius,sigma,image->bias,
02596           exception);
02597         (void) FormatLocaleString(label,MaxTextExtent,"sharpen %gx%g",
02598           radius,sigma);
02599         break;
02600       }
02601       case BlurPreview:
02602       {
02603         /* FUTURE: user bias on blur! This is non-sensical! */
02604         preview_image=BlurImage(thumbnail,radius,sigma,image->bias,exception);
02605         (void) FormatLocaleString(label,MaxTextExtent,"blur %gx%g",radius,
02606           sigma);
02607         break;
02608       }
02609       case ThresholdPreview:
02610       {
02611         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
02612         if (preview_image == (Image *) NULL)
02613           break;
02614         (void) BilevelImage(thumbnail,(double) (percentage*((MagickRealType)
02615           QuantumRange+1.0))/100.0,exception);
02616         (void) FormatLocaleString(label,MaxTextExtent,"threshold %g",
02617           (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
02618         break;
02619       }
02620       case EdgeDetectPreview:
02621       {
02622         preview_image=EdgeImage(thumbnail,radius,sigma,exception);
02623         (void) FormatLocaleString(label,MaxTextExtent,"edge %g",radius);
02624         break;
02625       }
02626       case SpreadPreview:
02627       {
02628         preview_image=SpreadImage(thumbnail,radius,thumbnail->interpolate,
02629           exception);
02630         (void) FormatLocaleString(label,MaxTextExtent,"spread %g",
02631           radius+0.5);
02632         break;
02633       }
02634       case SolarizePreview:
02635       {
02636         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
02637         if (preview_image == (Image *) NULL)
02638           break;
02639         (void) SolarizeImage(preview_image,(double) QuantumRange*
02640           percentage/100.0,exception);
02641         (void) FormatLocaleString(label,MaxTextExtent,"solarize %g",
02642           (QuantumRange*percentage)/100.0);
02643         break;
02644       }
02645       case ShadePreview:
02646       {
02647         degrees+=10.0;
02648         preview_image=ShadeImage(thumbnail,MagickTrue,degrees,degrees,
02649           exception);
02650         (void) FormatLocaleString(label,MaxTextExtent,"shade %gx%g",
02651           degrees,degrees);
02652         break;
02653       }
02654       case RaisePreview:
02655       {
02656         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
02657         if (preview_image == (Image *) NULL)
02658           break;
02659         geometry.width=(size_t) (2*i+2);
02660         geometry.height=(size_t) (2*i+2);
02661         geometry.x=i/2;
02662         geometry.y=i/2;
02663         (void) RaiseImage(preview_image,&geometry,MagickTrue,exception);
02664         (void) FormatLocaleString(label,MaxTextExtent,
02665           "raise %.20gx%.20g%+.20g%+.20g",(double) geometry.width,(double)
02666           geometry.height,(double) geometry.x,(double) geometry.y);
02667         break;
02668       }
02669       case SegmentPreview:
02670       {
02671         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
02672         if (preview_image == (Image *) NULL)
02673           break;
02674         threshold+=0.4f;
02675         (void) SegmentImage(preview_image,RGBColorspace,MagickFalse,threshold,
02676           threshold,exception);
02677         (void) FormatLocaleString(label,MaxTextExtent,"segment %gx%g",
02678           threshold,threshold);
02679         break;
02680       }
02681       case SwirlPreview:
02682       {
02683         preview_image=SwirlImage(thumbnail,degrees,image->interpolate,
02684           exception);
02685         (void) FormatLocaleString(label,MaxTextExtent,"swirl %g",degrees);
02686         degrees+=45.0;
02687         break;
02688       }
02689       case ImplodePreview:
02690       {
02691         degrees+=0.1f;
02692         preview_image=ImplodeImage(thumbnail,degrees,image->interpolate,
02693           exception);
02694         (void) FormatLocaleString(label,MaxTextExtent,"implode %g",degrees);
02695         break;
02696       }
02697       case WavePreview:
02698       {
02699         degrees+=5.0f;
02700         preview_image=WaveImage(thumbnail,0.5*degrees,2.0*degrees,
02701           image->interpolate,exception);
02702         (void) FormatLocaleString(label,MaxTextExtent,"wave %gx%g",
02703           0.5*degrees,2.0*degrees);
02704         break;
02705       }
02706       case OilPaintPreview:
02707       {
02708         preview_image=OilPaintImage(thumbnail,(double) radius,(double) sigma,
02709           exception);
02710         (void) FormatLocaleString(label,MaxTextExtent,"charcoal %gx%g",
02711           radius,sigma);
02712         break;
02713       }
02714       case CharcoalDrawingPreview:
02715       {
02716         /* FUTURE: user bias on charcoal! This is non-sensical! */
02717         preview_image=CharcoalImage(thumbnail,(double) radius,(double) sigma,
02718           image->bias,exception);
02719         (void) FormatLocaleString(label,MaxTextExtent,"charcoal %gx%g",
02720           radius,sigma);
02721         break;
02722       }
02723       case JPEGPreview:
02724       {
02725         char
02726           filename[MaxTextExtent];
02727 
02728         int
02729           file;
02730 
02731         MagickBooleanType
02732           status;
02733 
02734         preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
02735         if (preview_image == (Image *) NULL)
02736           break;
02737         preview_info->quality=(size_t) percentage;
02738         (void) FormatLocaleString(factor,MaxTextExtent,"%.20g",(double)
02739           preview_info->quality);
02740         file=AcquireUniqueFileResource(filename);
02741         if (file != -1)
02742           file=close(file)-1;
02743         (void) FormatLocaleString(preview_image->filename,MaxTextExtent,
02744           "jpeg:%s",filename);
02745         status=WriteImage(preview_info,preview_image,exception);
02746         if (status != MagickFalse)
02747           {
02748             Image
02749               *quality_image;
02750 
02751             (void) CopyMagickString(preview_info->filename,
02752               preview_image->filename,MaxTextExtent);
02753             quality_image=ReadImage(preview_info,exception);
02754             if (quality_image != (Image *) NULL)
02755               {
02756                 preview_image=DestroyImage(preview_image);
02757                 preview_image=quality_image;
02758               }
02759           }
02760         (void) RelinquishUniqueFileResource(preview_image->filename);
02761         if ((GetBlobSize(preview_image)/1024) >= 1024)
02762           (void) FormatLocaleString(label,MaxTextExtent,"quality %s\n%gmb ",
02763             factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
02764             1024.0/1024.0);
02765         else
02766           if (GetBlobSize(preview_image) >= 1024)
02767             (void) FormatLocaleString(label,MaxTextExtent,
02768               "quality %s\n%gkb ",factor,(double) ((MagickOffsetType)
02769               GetBlobSize(preview_image))/1024.0);
02770           else
02771             (void) FormatLocaleString(label,MaxTextExtent,"quality %s\n%.20gb ",
02772               factor,(double) ((MagickOffsetType) GetBlobSize(thumbnail)));
02773         break;
02774       }
02775     }
02776     thumbnail=DestroyImage(thumbnail);
02777     percentage+=12.5;
02778     radius+=0.5;
02779     sigma+=0.25;
02780     if (preview_image == (Image *) NULL)
02781       break;
02782     (void) DeleteImageProperty(preview_image,"label");
02783     (void) SetImageProperty(preview_image,"label",label,exception);
02784     AppendImageToList(&images,preview_image);
02785     proceed=SetImageProgress(image,PreviewImageTag,(MagickOffsetType) i,
02786       NumberTiles);
02787     if (proceed == MagickFalse)
02788       break;
02789   }
02790   if (images == (Image *) NULL)
02791     {
02792       preview_info=DestroyImageInfo(preview_info);
02793       return((Image *) NULL);
02794     }
02795   /*
02796     Create the montage.
02797   */
02798   montage_info=CloneMontageInfo(preview_info,(MontageInfo *) NULL);
02799   (void) CopyMagickString(montage_info->filename,image->filename,MaxTextExtent);
02800   montage_info->shadow=MagickTrue;
02801   (void) CloneString(&montage_info->tile,"3x3");
02802   (void) CloneString(&montage_info->geometry,DefaultPreviewGeometry);
02803   (void) CloneString(&montage_info->frame,DefaultTileFrame);
02804   montage_image=MontageImages(images,montage_info,exception);
02805   montage_info=DestroyMontageInfo(montage_info);
02806   images=DestroyImageList(images);
02807   if (montage_image == (Image *) NULL)
02808     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
02809   if (montage_image->montage != (char *) NULL)
02810     {
02811       /*
02812         Free image directory.
02813       */
02814       montage_image->montage=(char *) RelinquishMagickMemory(
02815         montage_image->montage);
02816       if (image->directory != (char *) NULL)
02817         montage_image->directory=(char *) RelinquishMagickMemory(
02818           montage_image->directory);
02819     }
02820   preview_info=DestroyImageInfo(preview_info);
02821   return(montage_image);
02822 }
02823 
02824 /*
02825 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02826 %                                                                             %
02827 %                                                                             %
02828 %                                                                             %
02829 %     R a d i a l B l u r I m a g e                                           %
02830 %                                                                             %
02831 %                                                                             %
02832 %                                                                             %
02833 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02834 %
02835 %  RadialBlurImage() applies a radial blur to the image.
02836 %
02837 %  Andrew Protano contributed this effect.
02838 %
02839 %  The format of the RadialBlurImage method is:
02840 %
02841 %    Image *RadialBlurImage(const Image *image,const double angle,
02842 %      const double blur,ExceptionInfo *exception)
02843 %
02844 %  A description of each parameter follows:
02845 %
02846 %    o image: the image.
02847 %
02848 %    o angle: the angle of the radial blur.
02849 %
02850 %    o blur: the blur.
02851 %
02852 %    o exception: return any errors or warnings in this structure.
02853 %
02854 */
02855 MagickExport Image *RadialBlurImage(const Image *image,const double angle,
02856   const double bias,ExceptionInfo *exception)
02857 {
02858   CacheView
02859     *blur_view,
02860     *image_view,
02861     *radial_view;
02862 
02863   Image
02864     *blur_image;
02865 
02866   MagickBooleanType
02867     status;
02868 
02869   MagickOffsetType
02870     progress;
02871 
02872   MagickRealType
02873     blur_radius,
02874     *cos_theta,
02875     offset,
02876     *sin_theta,
02877     theta;
02878 
02879   PointInfo
02880     blur_center;
02881 
02882   register ssize_t
02883     i;
02884 
02885   size_t
02886     n;
02887 
02888   ssize_t
02889     y;
02890 
02891   /*
02892     Allocate blur image.
02893   */
02894   assert(image != (Image *) NULL);
02895   assert(image->signature == MagickSignature);
02896   if (image->debug != MagickFalse)
02897     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
02898   assert(exception != (ExceptionInfo *) NULL);
02899   assert(exception->signature == MagickSignature);
02900   blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
02901   if (blur_image == (Image *) NULL)
02902     return((Image *) NULL);
02903   if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
02904     {
02905       blur_image=DestroyImage(blur_image);
02906       return((Image *) NULL);
02907     }
02908   blur_center.x=(double) image->columns/2.0;
02909   blur_center.y=(double) image->rows/2.0;
02910   blur_radius=hypot(blur_center.x,blur_center.y);
02911   n=(size_t) fabs(4.0*DegreesToRadians(angle)*sqrt((double) blur_radius)+2UL);
02912   theta=DegreesToRadians(angle)/(MagickRealType) (n-1);
02913   cos_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
02914     sizeof(*cos_theta));
02915   sin_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
02916     sizeof(*sin_theta));
02917   if ((cos_theta == (MagickRealType *) NULL) ||
02918       (sin_theta == (MagickRealType *) NULL))
02919     {
02920       blur_image=DestroyImage(blur_image);
02921       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
02922     }
02923   offset=theta*(MagickRealType) (n-1)/2.0;
02924   for (i=0; i < (ssize_t) n; i++)
02925   {
02926     cos_theta[i]=cos((double) (theta*i-offset));
02927     sin_theta[i]=sin((double) (theta*i-offset));
02928   }
02929   /*
02930     Radial blur image.
02931   */
02932   status=MagickTrue;
02933   progress=0;
02934   image_view=AcquireCacheView(image);
02935   radial_view=AcquireCacheView(image);
02936   blur_view=AcquireCacheView(blur_image);
02937 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02938   #pragma omp parallel for schedule(static,4) shared(progress,status)
02939 #endif
02940   for (y=0; y < (ssize_t) image->rows; y++)
02941   {
02942     register const Quantum
02943       *restrict p;
02944 
02945     register Quantum
02946       *restrict q;
02947 
02948     register ssize_t
02949       x;
02950 
02951     if (status == MagickFalse)
02952       continue;
02953     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
02954     q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
02955       exception);
02956     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
02957       {
02958         status=MagickFalse;
02959         continue;
02960       }
02961     for (x=0; x < (ssize_t) image->columns; x++)
02962     {
02963       MagickRealType
02964         radius;
02965 
02966       PointInfo
02967         center;
02968 
02969       register ssize_t
02970         i;
02971 
02972       size_t
02973         step;
02974 
02975       center.x=(double) x-blur_center.x;
02976       center.y=(double) y-blur_center.y;
02977       radius=hypot((double) center.x,center.y);
02978       if (radius == 0)
02979         step=1;
02980       else
02981         {
02982           step=(size_t) (blur_radius/radius);
02983           if (step == 0)
02984             step=1;
02985           else
02986             if (step >= n)
02987               step=n-1;
02988         }
02989       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
02990       {
02991         MagickRealType
02992           gamma,
02993           pixel;
02994 
02995         PixelChannel
02996           channel;
02997 
02998         PixelTrait
02999           blur_traits,
03000           traits;
03001 
03002         register const Quantum
03003           *restrict r;
03004 
03005         register ssize_t
03006           j;
03007 
03008         channel=GetPixelChannelMapChannel(image,i);
03009         traits=GetPixelChannelMapTraits(image,channel);
03010         blur_traits=GetPixelChannelMapTraits(blur_image,channel);
03011         if ((traits == UndefinedPixelTrait) ||
03012             (blur_traits == UndefinedPixelTrait))
03013           continue;
03014         if (((blur_traits & CopyPixelTrait) != 0) ||
03015             (GetPixelMask(image,p) != 0))
03016           {
03017             SetPixelChannel(blur_image,channel,p[i],q);
03018             continue;
03019           }
03020         gamma=0.0;
03021         pixel=bias;
03022         if ((blur_traits & BlendPixelTrait) == 0)
03023           {
03024             for (j=0; j < (ssize_t) n; j+=(ssize_t) step)
03025             {
03026               r=GetCacheViewVirtualPixels(radial_view, (ssize_t) (blur_center.x+
03027                 center.x*cos_theta[j]-center.y*sin_theta[j]+0.5),(ssize_t)
03028                 (blur_center.y+center.x*sin_theta[j]+center.y*cos_theta[j]+0.5),
03029                 1,1,exception);
03030               if (r == (const Quantum *) NULL)
03031                 {
03032                   status=MagickFalse;
03033                   continue;
03034                 }
03035               pixel+=r[i];
03036               gamma++;
03037             }
03038             gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
03039             SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
03040             continue;
03041           }
03042         for (j=0; j < (ssize_t) n; j+=(ssize_t) step)
03043         {
03044           r=GetCacheViewVirtualPixels(radial_view, (ssize_t) (blur_center.x+
03045             center.x*cos_theta[j]-center.y*sin_theta[j]+0.5),(ssize_t)
03046             (blur_center.y+center.x*sin_theta[j]+center.y*cos_theta[j]+0.5),
03047             1,1,exception);
03048           if (r == (const Quantum *) NULL)
03049             {
03050               status=MagickFalse;
03051               continue;
03052             }
03053           pixel+=GetPixelAlpha(image,r)*r[i];
03054           gamma+=GetPixelAlpha(image,r);
03055         }
03056         gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
03057         SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
03058       }
03059       p+=GetPixelChannels(image);
03060       q+=GetPixelChannels(blur_image);
03061     }
03062     if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
03063       status=MagickFalse;
03064     if (image->progress_monitor != (MagickProgressMonitor) NULL)
03065       {
03066         MagickBooleanType
03067           proceed;
03068 
03069 #if defined(MAGICKCORE_OPENMP_SUPPORT)
03070         #pragma omp critical (MagickCore_RadialBlurImage)
03071 #endif
03072         proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
03073         if (proceed == MagickFalse)
03074           status=MagickFalse;
03075       }
03076   }
03077   blur_view=DestroyCacheView(blur_view);
03078   radial_view=DestroyCacheView(radial_view);
03079   image_view=DestroyCacheView(image_view);
03080   cos_theta=(MagickRealType *) RelinquishMagickMemory(cos_theta);
03081   sin_theta=(MagickRealType *) RelinquishMagickMemory(sin_theta);
03082   if (status == MagickFalse)
03083     blur_image=DestroyImage(blur_image);
03084   return(blur_image);
03085 }
03086 
03087 /*
03088 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03089 %                                                                             %
03090 %                                                                             %
03091 %                                                                             %
03092 %     S e l e c t i v e B l u r I m a g e                                     %
03093 %                                                                             %
03094 %                                                                             %
03095 %                                                                             %
03096 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03097 %
03098 %  SelectiveBlurImage() selectively blur pixels within a contrast threshold.
03099 %  It is similar to the unsharpen mask that sharpens everything with contrast
03100 %  above a certain threshold.
03101 %
03102 %  The format of the SelectiveBlurImage method is:
03103 %
03104 %      Image *SelectiveBlurImage(const Image *image,const double radius,
03105 %        const double sigma,const double threshold,const double bias,
03106 %        ExceptionInfo *exception)
03107 %
03108 %  A description of each parameter follows:
03109 %
03110 %    o image: the image.
03111 %
03112 %    o radius: the radius of the Gaussian, in pixels, not counting the center
03113 %      pixel.
03114 %
03115 %    o sigma: the standard deviation of the Gaussian, in pixels.
03116 %
03117 %    o threshold: only pixels within this contrast threshold are included
03118 %      in the blur operation.
03119 %
03120 %    o bias: the bias.
03121 %
03122 %    o exception: return any errors or warnings in this structure.
03123 %
03124 */
03125 MagickExport Image *SelectiveBlurImage(const Image *image,const double radius,
03126   const double sigma,const double threshold,const double bias,
03127   ExceptionInfo *exception)
03128 {
03129 #define SelectiveBlurImageTag  "SelectiveBlur/Image"
03130 
03131   CacheView
03132     *blur_view,
03133     *image_view;
03134 
03135   double
03136     *kernel;
03137 
03138   Image
03139     *blur_image;
03140 
03141   MagickBooleanType
03142     status;
03143 
03144   MagickOffsetType
03145     progress;
03146 
03147   register ssize_t
03148     i;
03149 
03150   size_t
03151     width;
03152 
03153   ssize_t
03154     center,
03155     j,
03156     u,
03157     v,
03158     y;
03159 
03160   /*
03161     Initialize blur image attributes.
03162   */
03163   assert(image != (Image *) NULL);
03164   assert(image->signature == MagickSignature);
03165   if (image->debug != MagickFalse)
03166     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
03167   assert(exception != (ExceptionInfo *) NULL);
03168   assert(exception->signature == MagickSignature);
03169   width=GetOptimalKernelWidth1D(radius,sigma);
03170   kernel=(double *) AcquireAlignedMemory((size_t) width,width*sizeof(*kernel));
03171   if (kernel == (double *) NULL)
03172     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
03173   j=(ssize_t) width/2;
03174   i=0;
03175   for (v=(-j); v <= j; v++)
03176   {
03177     for (u=(-j); u <= j; u++)
03178       kernel[i++]=(double) (exp(-((double) u*u+v*v)/(2.0*MagickSigma*
03179         MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
03180   }
03181   if (image->debug != MagickFalse)
03182     {
03183       char
03184         format[MaxTextExtent],
03185         *message;
03186 
03187       register const double
03188         *k;
03189 
03190       ssize_t
03191         u,
03192         v;
03193 
03194       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
03195         "  SelectiveBlurImage with %.20gx%.20g kernel:",(double) width,(double)
03196         width);
03197       message=AcquireString("");
03198       k=kernel;
03199       for (v=0; v < (ssize_t) width; v++)
03200       {
03201         *message='\0';
03202         (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) v);
03203         (void) ConcatenateString(&message,format);
03204         for (u=0; u < (ssize_t) width; u++)
03205         {
03206           (void) FormatLocaleString(format,MaxTextExtent,"%+f ",*k++);
03207           (void) ConcatenateString(&message,format);
03208         }
03209         (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
03210       }
03211       message=DestroyString(message);
03212     }
03213   blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
03214   if (blur_image == (Image *) NULL)
03215     return((Image *) NULL);
03216   if (SetImageStorageClass(blur_image,DirectClass,exception) == MagickFalse)
03217     {
03218       blur_image=DestroyImage(blur_image);
03219       return((Image *) NULL);
03220     }
03221   /*
03222     Threshold blur image.
03223   */
03224   status=MagickTrue;
03225   progress=0;
03226   center=(ssize_t) (GetPixelChannels(image)*(image->columns+width)*(width/2L)+
03227     GetPixelChannels(image)*(width/2L));
03228   image_view=AcquireCacheView(image);
03229   blur_view=AcquireCacheView(blur_image);
03230 #if defined(MAGICKCORE_OPENMP_SUPPORT)
03231   #pragma omp parallel for schedule(static,4) shared(progress,status)
03232 #endif
03233   for (y=0; y < (ssize_t) image->rows; y++)
03234   {
03235     double
03236       contrast;
03237 
03238     MagickBooleanType
03239       sync;
03240 
03241     register const Quantum
03242       *restrict p;
03243 
03244     register Quantum
03245       *restrict q;
03246 
03247     register ssize_t
03248       x;
03249 
03250     if (status == MagickFalse)
03251       continue;
03252     p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
03253       (width/2L),image->columns+width,width,exception);
03254     q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
03255       exception);
03256     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
03257       {
03258         status=MagickFalse;
03259         continue;
03260       }
03261     for (x=0; x < (ssize_t) image->columns; x++)
03262     {
03263       register ssize_t
03264         i;
03265 
03266       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
03267       {
03268         MagickRealType
03269           alpha,
03270           gamma,
03271           intensity,
03272           pixel;
03273 
03274         PixelChannel
03275           channel;
03276 
03277         PixelTrait
03278           blur_traits,
03279           traits;
03280 
03281         register const double
03282           *restrict k;
03283 
03284         register const Quantum
03285           *restrict pixels;
03286 
03287         register ssize_t
03288           u;
03289 
03290         ssize_t
03291           v;
03292 
03293         channel=GetPixelChannelMapChannel(image,i);
03294         traits=GetPixelChannelMapTraits(image,channel);
03295         blur_traits=GetPixelChannelMapTraits(blur_image,channel);
03296         if ((traits == UndefinedPixelTrait) ||
03297             (blur_traits == UndefinedPixelTrait))
03298           continue;
03299         if (((blur_traits & CopyPixelTrait) != 0) ||
03300             (GetPixelMask(image,p) != 0))
03301           {
03302             SetPixelChannel(blur_image,channel,p[center+i],q);
03303             continue;
03304           }
03305         k=kernel;
03306         pixel=bias;
03307         pixels=p;
03308         intensity=(MagickRealType) GetPixelIntensity(image,p+center);
03309         gamma=0.0;
03310         if ((blur_traits & BlendPixelTrait) == 0)
03311           {
03312             for (v=0; v < (ssize_t) width; v++)
03313             {
03314               for (u=0; u < (ssize_t) width; u++)
03315               {
03316                 contrast=GetPixelIntensity(image,pixels)-intensity;
03317                 if (fabs(contrast) < threshold)
03318                   {
03319                     pixel+=(*k)*pixels[i];
03320                     gamma+=(*k);
03321                   }
03322                 k++;
03323                 pixels+=GetPixelChannels(image);
03324               }
03325               pixels+=image->columns*GetPixelChannels(image);
03326             }
03327             if (fabs((double) gamma) < MagickEpsilon)
03328               {
03329                 SetPixelChannel(blur_image,channel,p[center+i],q);
03330                 continue;
03331               }
03332             gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
03333             SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
03334             continue;
03335           }
03336         for (v=0; v < (ssize_t) width; v++)
03337         {
03338           for (u=0; u < (ssize_t) width; u++)
03339           {
03340             contrast=GetPixelIntensity(image,pixels)-intensity;
03341             if (fabs(contrast) < threshold)
03342               {
03343                 alpha=(MagickRealType) (QuantumScale*
03344                   GetPixelAlpha(image,pixels));
03345                 pixel+=(*k)*alpha*pixels[i];
03346                 gamma+=(*k)*alpha;
03347               }
03348             k++;
03349             pixels+=GetPixelChannels(image);
03350           }
03351           pixels+=image->columns*GetPixelChannels(image);
03352         }
03353         if (fabs((double) gamma) < MagickEpsilon)
03354           {
03355             SetPixelChannel(blur_image,channel,p[center+i],q);
03356             continue;
03357           }
03358         gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
03359         SetPixelChannel(blur_image,channel,ClampToQuantum(gamma*pixel),q);
03360       }
03361       p+=GetPixelChannels(image);
03362       q+=GetPixelChannels(blur_image);
03363     }
03364     sync=SyncCacheViewAuthenticPixels(blur_view,exception);
03365     if (sync == MagickFalse)
03366       status=MagickFalse;
03367     if (image->progress_monitor != (MagickProgressMonitor) NULL)
03368       {
03369         MagickBooleanType
03370           proceed;
03371 
03372 #if defined(MAGICKCORE_OPENMP_SUPPORT)
03373         #pragma omp critical (MagickCore_SelectiveBlurImage)
03374 #endif
03375         proceed=SetImageProgress(image,SelectiveBlurImageTag,progress++,
03376           image->rows);
03377         if (proceed == MagickFalse)
03378           status=MagickFalse;
03379       }
03380   }
03381   blur_image->type=image->type;
03382   blur_view=DestroyCacheView(blur_view);
03383   image_view=DestroyCacheView(image_view);
03384   kernel=(double *) RelinquishAlignedMemory(kernel);
03385   if (status == MagickFalse)
03386     blur_image=DestroyImage(blur_image);
03387   return(blur_image);
03388 }
03389 
03390 /*
03391 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03392 %                                                                             %
03393 %                                                                             %
03394 %                                                                             %
03395 %     S h a d e I m a g e                                                     %
03396 %                                                                             %
03397 %                                                                             %
03398 %                                                                             %
03399 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03400 %
03401 %  ShadeImage() shines a distant light on an image to create a
03402 %  three-dimensional effect. You control the positioning of the light with
03403 %  azimuth and elevation; azimuth is measured in degrees off the x axis
03404 %  and elevation is measured in pixels above the Z axis.
03405 %
03406 %  The format of the ShadeImage method is:
03407 %
03408 %      Image *ShadeImage(const Image *image,const MagickBooleanType gray,
03409 %        const double azimuth,const double elevation,ExceptionInfo *exception)
03410 %
03411 %  A description of each parameter follows:
03412 %
03413 %    o image: the image.
03414 %
03415 %    o gray: A value other than zero shades the intensity of each pixel.
03416 %
03417 %    o azimuth, elevation:  Define the light source direction.
03418 %
03419 %    o exception: return any errors or warnings in this structure.
03420 %
03421 */
03422 MagickExport Image *ShadeImage(const Image *image,const MagickBooleanType gray,
03423   const double azimuth,const double elevation,ExceptionInfo *exception)
03424 {
03425 #define ShadeImageTag  "Shade/Image"
03426 
03427   CacheView
03428     *image_view,
03429     *shade_view;
03430 
03431   Image
03432     *shade_image;
03433 
03434   MagickBooleanType
03435     status;
03436 
03437   MagickOffsetType
03438     progress;
03439 
03440   PrimaryInfo
03441     light;
03442 
03443   ssize_t
03444     y;
03445 
03446   /*
03447     Initialize shaded image attributes.
03448   */
03449   assert(image != (const Image *) NULL);
03450   assert(image->signature == MagickSignature);
03451   if (image->debug != MagickFalse)
03452     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
03453   assert(exception != (ExceptionInfo *) NULL);
03454   assert(exception->signature == MagickSignature);
03455   shade_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
03456   if (shade_image == (Image *) NULL)
03457     return((Image *) NULL);
03458   if (SetImageStorageClass(shade_image,DirectClass,exception) == MagickFalse)
03459     {
03460       shade_image=DestroyImage(shade_image);
03461       return((Image *) NULL);
03462     }
03463   /*
03464     Compute the light vector.
03465   */
03466   light.x=(double) QuantumRange*cos(DegreesToRadians(azimuth))*
03467     cos(DegreesToRadians(elevation));
03468   light.y=(double) QuantumRange*sin(DegreesToRadians(azimuth))*
03469     cos(DegreesToRadians(elevation));
03470   light.z=(double) QuantumRange*sin(DegreesToRadians(elevation));
03471   /*
03472     Shade image.
03473   */
03474   status=MagickTrue;
03475   progress=0;
03476   image_view=AcquireCacheView(image);
03477   shade_view=AcquireCacheView(shade_image);
03478 #if defined(MAGICKCORE_OPENMP_SUPPORT)
03479   #pragma omp parallel for schedule(static,4) shared(progress,status)
03480 #endif
03481   for (y=0; y < (ssize_t) image->rows; y++)
03482   {
03483     MagickRealType
03484       distance,
03485       normal_distance,
03486       shade;
03487 
03488     PrimaryInfo
03489       normal;
03490 
03491     register const Quantum
03492       *restrict center,
03493       *restrict p,
03494       *restrict post,
03495       *restrict pre;
03496 
03497     register Quantum
03498       *restrict q;
03499 
03500     register ssize_t
03501       x;
03502 
03503     if (status == MagickFalse)
03504       continue;
03505     p=GetCacheViewVirtualPixels(image_view,-1,y-1,image->columns+2,3,exception);
03506     q=QueueCacheViewAuthenticPixels(shade_view,0,y,shade_image->columns,1,
03507       exception);
03508     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
03509       {
03510         status=MagickFalse;
03511         continue;
03512       }
03513     /*
03514       Shade this row of pixels.
03515     */
03516     normal.z=2.0*(double) QuantumRange;  /* constant Z of surface normal */
03517     pre=p+GetPixelChannels(image);
03518     center=pre+(image->columns+2)*GetPixelChannels(image);
03519     post=center+(image->columns+2)*GetPixelChannels(image);
03520     for (x=0; x < (ssize_t) image->columns; x++)
03521     {
03522       register ssize_t
03523         i;
03524 
03525       /*
03526         Determine the surface normal and compute shading.
03527       */
03528       normal.x=(double) (GetPixelIntensity(image,pre-GetPixelChannels(image))+
03529         GetPixelIntensity(image,center-GetPixelChannels(image))+
03530         GetPixelIntensity(image,post-GetPixelChannels(image))-
03531         GetPixelIntensity(image,pre+GetPixelChannels(image))-
03532         GetPixelIntensity(image,center+GetPixelChannels(image))-
03533         GetPixelIntensity(image,post+GetPixelChannels(image)));
03534       normal.y=(double) (GetPixelIntensity(image,post-GetPixelChannels(image))+
03535         GetPixelIntensity(image,post)+GetPixelIntensity(image,post+
03536         GetPixelChannels(image))-GetPixelIntensity(image,pre-
03537         GetPixelChannels(image))-GetPixelIntensity(image,pre)-
03538         GetPixelIntensity(image,pre+GetPixelChannels(image)));
03539       if ((normal.x == 0.0) && (normal.y == 0.0))
03540         shade=light.z;
03541       else
03542         {
03543           shade=0.0;
03544           distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
03545           if (distance > MagickEpsilon)
03546             {
03547               normal_distance=
03548                 normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
03549               if (normal_distance > (MagickEpsilon*MagickEpsilon))
03550                 shade=distance/sqrt((double) normal_distance);
03551             }
03552         }
03553       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
03554       {
03555         PixelChannel
03556           channel;
03557 
03558         PixelTrait
03559           shade_traits,
03560           traits;
03561 
03562         channel=GetPixelChannelMapChannel(image,i);
03563         traits=GetPixelChannelMapTraits(image,channel);
03564         shade_traits=GetPixelChannelMapTraits(shade_image,channel);
03565         if ((traits == UndefinedPixelTrait) ||
03566             (shade_traits == UndefinedPixelTrait))
03567           continue;
03568         if (((shade_traits & CopyPixelTrait) != 0) ||
03569             (GetPixelMask(image,pre) != 0))
03570           {
03571             SetPixelChannel(shade_image,channel,center[i],q);
03572             continue;
03573           }
03574         if (gray != MagickFalse)
03575           {
03576             SetPixelChannel(shade_image,channel,ClampToQuantum(shade),q);
03577             continue;
03578           }
03579         SetPixelChannel(shade_image,channel,ClampToQuantum(QuantumScale*shade*
03580           center[i]),q);
03581       }
03582       pre+=GetPixelChannels(image);
03583       center+=GetPixelChannels(image);
03584       post+=GetPixelChannels(image);
03585       q+=GetPixelChannels(shade_image);
03586     }
03587     if (SyncCacheViewAuthenticPixels(shade_view,exception) == MagickFalse)
03588       status=MagickFalse;
03589     if (image->progress_monitor != (MagickProgressMonitor) NULL)
03590       {
03591         MagickBooleanType
03592           proceed;
03593 
03594 #if defined(MAGICKCORE_OPENMP_SUPPORT)
03595         #pragma omp critical (MagickCore_ShadeImage)
03596 #endif
03597         proceed=SetImageProgress(image,ShadeImageTag,progress++,image->rows);
03598         if (proceed == MagickFalse)
03599           status=MagickFalse;
03600       }
03601   }
03602   shade_view=DestroyCacheView(shade_view);
03603   image_view=DestroyCacheView(image_view);
03604   if (status == MagickFalse)
03605     shade_image=DestroyImage(shade_image);
03606   return(shade_image);
03607 }
03608 
03609 /*
03610 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03611 %                                                                             %
03612 %                                                                             %
03613 %                                                                             %
03614 %     S h a r p e n I m a g e                                                 %
03615 %                                                                             %
03616 %                                                                             %
03617 %                                                                             %
03618 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03619 %
03620 %  SharpenImage() sharpens the image.  We convolve the image with a Gaussian
03621 %  operator of the given radius and standard deviation (sigma).  For
03622 %  reasonable results, radius should be larger than sigma.  Use a radius of 0
03623 %  and SharpenImage() selects a suitable radius for you.
03624 %
03625 %  Using a separable kernel would be faster, but the negative weights cancel
03626 %  out on the corners of the kernel producing often undesirable ringing in the
03627 %  filtered result; this can be avoided by using a 2D gaussian shaped image
03628 %  sharpening kernel instead.
03629 %
03630 %  The format of the SharpenImage method is:
03631 %
03632 %    Image *SharpenImage(const Image *image,const double radius,
03633 %      const double sigma,const double bias,ExceptionInfo *exception)
03634 %
03635 %  A description of each parameter follows:
03636 %
03637 %    o image: the image.
03638 %
03639 %    o radius: the radius of the Gaussian, in pixels, not counting the center
03640 %      pixel.
03641 %
03642 %    o sigma: the standard deviation of the Laplacian, in pixels.
03643 %
03644 %    o bias: bias.
03645 %
03646 %    o exception: return any errors or warnings in this structure.
03647 %
03648 */
03649 MagickExport Image *SharpenImage(const Image *image,const double radius,
03650   const double sigma,const double bias,ExceptionInfo *exception)
03651 {
03652   double
03653     normalize;
03654 
03655   Image
03656     *sharp_image;
03657 
03658   KernelInfo
03659     *kernel_info;
03660 
03661   register ssize_t
03662     i;
03663 
03664   size_t
03665     width;
03666 
03667   ssize_t
03668     j,
03669     u,
03670     v;
03671 
03672   assert(image != (const Image *) NULL);
03673   assert(image->signature == MagickSignature);
03674   if (image->debug != MagickFalse)
03675     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
03676   assert(exception != (ExceptionInfo *) NULL);
03677   assert(exception->signature == MagickSignature);
03678   width=GetOptimalKernelWidth2D(radius,sigma);
03679   kernel_info=AcquireKernelInfo((const char *) NULL);
03680   if (kernel_info == (KernelInfo *) NULL)
03681     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
03682   (void) ResetMagickMemory(kernel_info,0,sizeof(*kernel_info));
03683   kernel_info->width=width;
03684   kernel_info->height=width;
03685   kernel_info->bias=bias;   /* FUTURE: user bias - non-sensical! */
03686   kernel_info->signature=MagickSignature;
03687   kernel_info->values=(MagickRealType *) AcquireAlignedMemory(
03688     kernel_info->width,kernel_info->width*sizeof(*kernel_info->values));
03689   if (kernel_info->values == (MagickRealType *) NULL)
03690     {
03691       kernel_info=DestroyKernelInfo(kernel_info);
03692       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
03693     }
03694   normalize=0.0;
03695   j=(ssize_t) kernel_info->width/2;
03696   i=0;
03697   for (v=(-j); v <= j; v++)
03698   {
03699     for (u=(-j); u <= j; u++)
03700     {
03701       kernel_info->values[i]=(double) (-exp(-((double) u*u+v*v)/(2.0*
03702         MagickSigma*MagickSigma))/(2.0*MagickPI*MagickSigma*MagickSigma));
03703       normalize+=kernel_info->values[i];
03704       i++;
03705     }
03706   }
03707   kernel_info->values[i/2]=(double) ((-2.0)*normalize);
03708   sharp_image=ConvolveImage(image,kernel_info,exception);
03709   kernel_info=DestroyKernelInfo(kernel_info);
03710   return(sharp_image);
03711 }
03712 
03713 /*
03714 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03715 %                                                                             %
03716 %                                                                             %
03717 %                                                                             %
03718 %     S p r e a d I m a g e                                                   %
03719 %                                                                             %
03720 %                                                                             %
03721 %                                                                             %
03722 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03723 %
03724 %  SpreadImage() is a special effects method that randomly displaces each
03725 %  pixel in a block defined by the radius parameter.
03726 %
03727 %  The format of the SpreadImage method is:
03728 %
03729 %      Image *SpreadImage(const Image *image,const double radius,
03730 %        const PixelInterpolateMethod method,ExceptionInfo *exception)
03731 %
03732 %  A description of each parameter follows:
03733 %
03734 %    o image: the image.
03735 %
03736 %    o radius:  choose a random pixel in a neighborhood of this extent.
03737 %
03738 %    o method:  the pixel interpolation method.
03739 %
03740 %    o exception: return any errors or warnings in this structure.
03741 %
03742 */
03743 MagickExport Image *SpreadImage(const Image *image,const double radius,
03744   const PixelInterpolateMethod method,ExceptionInfo *exception)
03745 {
03746 #define SpreadImageTag  "Spread/Image"
03747 
03748   CacheView
03749     *image_view,
03750     *spread_view;
03751 
03752   Image
03753     *spread_image;
03754 
03755   MagickBooleanType
03756     status;
03757 
03758   MagickOffsetType
03759     progress;
03760 
03761   RandomInfo
03762     **restrict random_info;
03763 
03764   size_t
03765     width;
03766 
03767   ssize_t
03768     y;
03769 
03770   /*
03771     Initialize spread image attributes.
03772   */
03773   assert(image != (Image *) NULL);
03774   assert(image->signature == MagickSignature);
03775   if (image->debug != MagickFalse)
03776     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
03777   assert(exception != (ExceptionInfo *) NULL);
03778   assert(exception->signature == MagickSignature);
03779   spread_image=CloneImage(image,image->columns,image->rows,MagickTrue,
03780     exception);
03781   if (spread_image == (Image *) NULL)
03782     return((Image *) NULL);
03783   if (SetImageStorageClass(spread_image,DirectClass,exception) == MagickFalse)
03784     {
03785       spread_image=DestroyImage(spread_image);
03786       return((Image *) NULL);
03787     }
03788   /*
03789     Spread image.
03790   */
03791   status=MagickTrue;
03792   progress=0;
03793   width=GetOptimalKernelWidth1D(radius,0.5);
03794   random_info=AcquireRandomInfoThreadSet();
03795   image_view=AcquireCacheView(image);
03796   spread_view=AcquireCacheView(spread_image);
03797 #if defined(MAGICKCORE_OPENMP_SUPPORT)
03798   #pragma omp parallel for schedule(static,8) shared(progress,status)
03799 #endif
03800   for (y=0; y < (ssize_t) image->rows; y++)
03801   {
03802     const int
03803       id = GetOpenMPThreadId();
03804 
03805     register const Quantum
03806       *restrict p;
03807 
03808     register Quantum
03809       *restrict q;
03810 
03811     register ssize_t
03812       x;
03813 
03814     if (status == MagickFalse)
03815       continue;
03816     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
03817     q=QueueCacheViewAuthenticPixels(spread_view,0,y,spread_image->columns,1,
03818       exception);
03819     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
03820       {
03821         status=MagickFalse;
03822         continue;
03823       }
03824     for (x=0; x < (ssize_t) image->columns; x++)
03825     {
03826       PointInfo
03827         point;
03828 
03829       point.x=GetPseudoRandomValue(random_info[id]);
03830       point.y=GetPseudoRandomValue(random_info[id]);
03831       status=InterpolatePixelChannels(image,image_view,spread_image,method,
03832         (double) x+width*point.x-0.5,(double) y+width*point.y-0.5,q,exception);
03833       q+=GetPixelChannels(spread_image);
03834     }
03835     if (SyncCacheViewAuthenticPixels(spread_view,exception) == MagickFalse)
03836       status=MagickFalse;
03837     if (image->progress_monitor != (MagickProgressMonitor) NULL)
03838       {
03839         MagickBooleanType
03840           proceed;
03841 
03842 #if defined(MAGICKCORE_OPENMP_SUPPORT)
03843         #pragma omp critical (MagickCore_SpreadImage)
03844 #endif
03845         proceed=SetImageProgress(image,SpreadImageTag,progress++,image->rows);
03846         if (proceed == MagickFalse)
03847           status=MagickFalse;
03848       }
03849   }
03850   spread_view=DestroyCacheView(spread_view);
03851   image_view=DestroyCacheView(image_view);
03852   random_info=DestroyRandomInfoThreadSet(random_info);
03853   return(spread_image);
03854 }
03855 
03856 /*
03857 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03858 %                                                                             %
03859 %                                                                             %
03860 %                                                                             %
03861 %     U n s h a r p M a s k I m a g e                                         %
03862 %                                                                             %
03863 %                                                                             %
03864 %                                                                             %
03865 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03866 %
03867 %  UnsharpMaskImage() sharpens one or more image channels.  We convolve the
03868 %  image with a Gaussian operator of the given radius and standard deviation
03869 %  (sigma).  For reasonable results, radius should be larger than sigma.  Use a
03870 %  radius of 0 and UnsharpMaskImage() selects a suitable radius for you.
03871 %
03872 %  The format of the UnsharpMaskImage method is:
03873 %
03874 %    Image *UnsharpMaskImage(const Image *image,const double radius,
03875 %      const double sigma,const double amount,const double threshold,
03876 %      ExceptionInfo *exception)
03877 %
03878 %  A description of each parameter follows:
03879 %
03880 %    o image: the image.
03881 %
03882 %    o radius: the radius of the Gaussian, in pixels, not counting the center
03883 %      pixel.
03884 %
03885 %    o sigma: the standard deviation of the Gaussian, in pixels.
03886 %
03887 %    o amount: the percentage of the difference between the original and the
03888 %      blur image that is added back into the original.
03889 %
03890 %    o threshold: the threshold in pixels needed to apply the diffence amount.
03891 %
03892 %    o exception: return any errors or warnings in this structure.
03893 %
03894 */
03895 MagickExport Image *UnsharpMaskImage(const Image *image,const double radius,
03896   const double sigma,const double amount,const double threshold,
03897   ExceptionInfo *exception)
03898 {
03899 #define SharpenImageTag  "Sharpen/Image"
03900 
03901   CacheView
03902     *image_view,
03903     *unsharp_view;
03904 
03905   Image
03906     *unsharp_image;
03907 
03908   MagickBooleanType
03909     status;
03910 
03911   MagickOffsetType
03912     progress;
03913 
03914   MagickRealType
03915     quantum_threshold;
03916 
03917   ssize_t
03918     y;
03919 
03920   assert(image != (const Image *) NULL);
03921   assert(image->signature == MagickSignature);
03922   if (image->debug != MagickFalse)
03923     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
03924   assert(exception != (ExceptionInfo *) NULL);
03925   unsharp_image=BlurImage(image,radius,sigma,image->bias,exception);
03926   if (unsharp_image == (Image *) NULL)
03927     return((Image *) NULL);
03928   quantum_threshold=(MagickRealType) QuantumRange*threshold;
03929   /*
03930     Unsharp-mask image.
03931   */
03932   status=MagickTrue;
03933   progress=0;
03934   image_view=AcquireCacheView(image);
03935   unsharp_view=AcquireCacheView(unsharp_image);
03936 #if defined(MAGICKCORE_OPENMP_SUPPORT)
03937   #pragma omp parallel for schedule(static,4) shared(progress,status)
03938 #endif
03939   for (y=0; y < (ssize_t) image->rows; y++)
03940   {
03941     register const Quantum
03942       *restrict p;
03943 
03944     register Quantum
03945       *restrict q;
03946 
03947     register ssize_t
03948       x;
03949 
03950     if (status == MagickFalse)
03951       continue;
03952     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
03953     q=QueueCacheViewAuthenticPixels(unsharp_view,0,y,unsharp_image->columns,1,
03954       exception);
03955     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
03956       {
03957         status=MagickFalse;
03958         continue;
03959       }
03960     for (x=0; x < (ssize_t) image->columns; x++)
03961     {
03962       register ssize_t
03963         i;
03964 
03965       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
03966       {
03967         MagickRealType
03968           pixel;
03969 
03970         PixelChannel
03971           channel;
03972 
03973         PixelTrait
03974           traits,
03975           unsharp_traits;
03976 
03977         channel=GetPixelChannelMapChannel(image,i);
03978         traits=GetPixelChannelMapTraits(image,channel);
03979         unsharp_traits=GetPixelChannelMapTraits(unsharp_image,channel);
03980         if ((traits == UndefinedPixelTrait) ||
03981             (unsharp_traits == UndefinedPixelTrait))
03982           continue;
03983         if (((unsharp_traits & CopyPixelTrait) != 0) ||
03984             (GetPixelMask(image,p) != 0))
03985           {
03986             SetPixelChannel(unsharp_image,channel,p[i],q);
03987             continue;
03988           }
03989         pixel=p[i]-(MagickRealType) GetPixelChannel(unsharp_image,channel,q);
03990         if (fabs(2.0*pixel) < quantum_threshold)
03991           pixel=(MagickRealType) p[i];
03992         else
03993           pixel=(MagickRealType) p[i]+amount*pixel;
03994         SetPixelChannel(unsharp_image,channel,ClampToQuantum(pixel),q);
03995       }
03996       p+=GetPixelChannels(image);
03997       q+=GetPixelChannels(unsharp_image);
03998     }
03999     if (SyncCacheViewAuthenticPixels(unsharp_view,exception) == MagickFalse)
04000       status=MagickFalse;
04001     if (image->progress_monitor != (MagickProgressMonitor) NULL)
04002       {
04003         MagickBooleanType
04004           proceed;
04005 
04006 #if defined(MAGICKCORE_OPENMP_SUPPORT)
04007         #pragma omp critical (MagickCore_UnsharpMaskImage)
04008 #endif
04009         proceed=SetImageProgress(image,SharpenImageTag,progress++,image->rows);
04010         if (proceed == MagickFalse)
04011           status=MagickFalse;
04012       }
04013   }
04014   unsharp_image->type=image->type;
04015   unsharp_view=DestroyCacheView(unsharp_view);
04016   image_view=DestroyCacheView(image_view);
04017   if (status == MagickFalse)
04018     unsharp_image=DestroyImage(unsharp_image);
04019   return(unsharp_image);
04020 }