fx.c

Go to the documentation of this file.
00001 /*
00002 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00003 %                                                                             %
00004 %                                                                             %
00005 %                                                                             %
00006 %                                 FFFFF  X   X                                %
00007 %                                 F       X X                                 %
00008 %                                 FFF      X                                  %
00009 %                                 F       X X                                 %
00010 %                                 F      X   X                                %
00011 %                                                                             %
00012 %                                                                             %
00013 %                   MagickCore Image Special Effects Methods                  %
00014 %                                                                             %
00015 %                               Software Design                               %
00016 %                                 John Cristy                                 %
00017 %                                 October 1996                                %
00018 %                                                                             %
00019 %                                                                             %
00020 %  Copyright 1999-2008 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 "magick/studio.h"
00044 #include "magick/annotate.h"
00045 #include "magick/cache.h"
00046 #include "magick/cache-view.h"
00047 #include "magick/color.h"
00048 #include "magick/color-private.h"
00049 #include "magick/composite.h"
00050 #include "magick/decorate.h"
00051 #include "magick/draw.h"
00052 #include "magick/effect.h"
00053 #include "magick/enhance.h"
00054 #include "magick/exception.h"
00055 #include "magick/exception-private.h"
00056 #include "magick/fx.h"
00057 #include "magick/fx-private.h"
00058 #include "magick/gem.h"
00059 #include "magick/geometry.h"
00060 #include "magick/layer.h"
00061 #include "magick/list.h"
00062 #include "magick/log.h"
00063 #include "magick/image.h"
00064 #include "magick/image-private.h"
00065 #include "magick/memory_.h"
00066 #include "magick/monitor.h"
00067 #include "magick/monitor-private.h"
00068 #include "magick/option.h"
00069 #include "magick/pixel-private.h"
00070 #include "magick/property.h"
00071 #include "magick/quantum.h"
00072 #include "magick/random_.h"
00073 #include "magick/resample.h"
00074 #include "magick/resample-private.h"
00075 #include "magick/resize.h"
00076 #include "magick/shear.h"
00077 #include "magick/splay-tree.h"
00078 #include "magick/statistic.h"
00079 #include "magick/string_.h"
00080 #include "magick/transform.h"
00081 #include "magick/utility.h"
00082 
00083 /*
00084   Define declarations.
00085 */
00086 #define LeftShiftOperator 0xf5
00087 #define RightShiftOperator 0xf6
00088 #define LessThanEqualOperator 0xf7
00089 #define GreaterThanEqualOperator 0xf8
00090 #define EqualOperator 0xf9
00091 #define NotEqualOperator 0xfa
00092 #define LogicalAndOperator 0xfb
00093 #define LogicalOrOperator 0xfc
00094 
00095 struct _FxInfo
00096 {
00097   const Image
00098     *images;
00099 
00100   MagickBooleanType
00101     matte;
00102 
00103   char
00104     *expression;
00105 
00106   SplayTreeInfo
00107     *colors,
00108     *symbols;
00109 
00110   ResampleFilter
00111     **resample_filter;
00112 
00113   ExceptionInfo
00114     *exception;
00115 };
00116 
00117 /*
00118 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00119 %                                                                             %
00120 %                                                                             %
00121 %                                                                             %
00122 +   A c q u i r e F x I n f o                                                 %
00123 %                                                                             %
00124 %                                                                             %
00125 %                                                                             %
00126 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00127 %
00128 %  AcquireFxInfo() allocates the FxInfo structure.
00129 %
00130 %  The format of the AcquireFxInfo method is:
00131 %
00132 %      FxInfo *AcquireFxInfo(Image *image,const char *expression)
00133 %  A description of each parameter follows:
00134 %
00135 %    o image: the image.
00136 %
00137 %    o expression: the expression.
00138 %
00139 */
00140 MagickExport FxInfo *AcquireFxInfo(const Image *image,const char *expression)
00141 {
00142   char
00143     fx_op[2];
00144 
00145   FxInfo
00146     *fx_info;
00147 
00148   register long
00149     i;
00150 
00151   fx_info=(FxInfo *) AcquireMagickMemory(sizeof(*fx_info));
00152   if (fx_info == (FxInfo *) NULL)
00153     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
00154   (void) ResetMagickMemory(fx_info,0,sizeof(*fx_info));
00155   fx_info->exception=AcquireExceptionInfo();
00156   fx_info->images=image;
00157   fx_info->matte=image->matte;
00158   fx_info->colors=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
00159     RelinquishMagickMemory);
00160   fx_info->symbols=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
00161     RelinquishMagickMemory);
00162   fx_info->resample_filter=(ResampleFilter **) AcquireQuantumMemory(
00163     GetImageListLength(fx_info->images),sizeof(*fx_info->resample_filter));
00164   if (fx_info->resample_filter == (ResampleFilter **) NULL)
00165     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
00166   for (i=0; i < (long) GetImageListLength(fx_info->images); i++)
00167     fx_info->resample_filter[i]=AcquireResampleFilter(GetImageFromList(
00168       fx_info->images,i),fx_info->exception);
00169   fx_info->expression=ConstantString(expression);
00170   (void) SubstituteString(&fx_info->expression," ","");  /* compact string */
00171   if ((strstr(fx_info->expression,"e+") != (char *) NULL) ||
00172       (strstr(fx_info->expression,"e-") != (char *) NULL))
00173     {
00174       /*
00175         Convert scientific notation.
00176       */
00177       (void) SubstituteString(&fx_info->expression,"0e+","0*10^");
00178       (void) SubstituteString(&fx_info->expression,"1e+","1*10^");
00179       (void) SubstituteString(&fx_info->expression,"2e+","2*10^");
00180       (void) SubstituteString(&fx_info->expression,"3e+","3*10^");
00181       (void) SubstituteString(&fx_info->expression,"4e+","4*10^");
00182       (void) SubstituteString(&fx_info->expression,"5e+","5*10^");
00183       (void) SubstituteString(&fx_info->expression,"6e+","6*10^");
00184       (void) SubstituteString(&fx_info->expression,"7e+","7*10^");
00185       (void) SubstituteString(&fx_info->expression,"8e+","8*10^");
00186       (void) SubstituteString(&fx_info->expression,"9e+","9*10^");
00187       (void) SubstituteString(&fx_info->expression,"0e-","0*10^-");
00188       (void) SubstituteString(&fx_info->expression,"1e-","1*10^-");
00189       (void) SubstituteString(&fx_info->expression,"2e-","2*10^-");
00190       (void) SubstituteString(&fx_info->expression,"3e-","3*10^-");
00191       (void) SubstituteString(&fx_info->expression,"4e-","4*10^-");
00192       (void) SubstituteString(&fx_info->expression,"5e-","5*10^-");
00193       (void) SubstituteString(&fx_info->expression,"6e-","6*10^-");
00194       (void) SubstituteString(&fx_info->expression,"7e-","7*10^-");
00195       (void) SubstituteString(&fx_info->expression,"8e-","8*10^-");
00196       (void) SubstituteString(&fx_info->expression,"9e-","9*10^-");
00197     }
00198   /*
00199     Convert complex to simple operators.
00200   */
00201   fx_op[1]='\0';
00202   *fx_op=(char) LeftShiftOperator;
00203   (void) SubstituteString(&fx_info->expression,"<<",fx_op);
00204   *fx_op=(char) RightShiftOperator;
00205   (void) SubstituteString(&fx_info->expression,">>",fx_op);
00206   *fx_op=(char) LessThanEqualOperator;
00207   (void) SubstituteString(&fx_info->expression,"<=",fx_op);
00208   *fx_op=(char) GreaterThanEqualOperator;
00209   (void) SubstituteString(&fx_info->expression,">=",fx_op);
00210   *fx_op=(char) EqualOperator;
00211   (void) SubstituteString(&fx_info->expression,"==",fx_op);
00212   *fx_op=(char) NotEqualOperator;
00213   (void) SubstituteString(&fx_info->expression,"!=",fx_op);
00214   *fx_op=(char) LogicalAndOperator;
00215   (void) SubstituteString(&fx_info->expression,"&&",fx_op);
00216   *fx_op=(char) LogicalOrOperator;
00217   (void) SubstituteString(&fx_info->expression,"||",fx_op);
00218   return(fx_info);
00219 }
00220 
00221 /*
00222 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00223 %                                                                             %
00224 %                                                                             %
00225 %                                                                             %
00226 %     A d d N o i s e I m a g e                                               %
00227 %                                                                             %
00228 %                                                                             %
00229 %                                                                             %
00230 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00231 %
00232 %  AddNoiseImage() adds random noise to the image.
00233 %
00234 %  The format of the AddNoiseImage method is:
00235 %
00236 %      Image *AddNoiseImage(const Image *image,const NoiseType noise_type,
00237 %        ExceptionInfo *exception)
00238 %      Image *AddNoiseImageChannel(const Image *image,const ChannelType channel,
00239 %        const NoiseType noise_type,ExceptionInfo *exception)
00240 %
00241 %  A description of each parameter follows:
00242 %
00243 %    o image: the image.
00244 %
00245 %    o channel: the channel type.
00246 %
00247 %    o noise_type:  The type of noise: Uniform, Gaussian, Multiplicative,
00248 %      Impulse, Laplacian, or Poisson.
00249 %
00250 %    o exception: return any errors or warnings in this structure.
00251 %
00252 */
00253 
00254 static Quantum GenerateNoise(const Quantum pixel,const NoiseType noise_type,
00255   const MagickRealType attenuate)
00256 {
00257 #define NoiseEpsilon  (attenuate*1.0e-5)
00258 #define SigmaUniform  ScaleCharToQuantum((unsigned char) (attenuate*4.0+0.5))
00259 #define SigmaGaussian  ScaleCharToQuantum((unsigned char) (attenuate*4.0+0.5))
00260 #define SigmaImpulse  (attenuate*0.10)
00261 #define SigmaLaplacian ScaleCharToQuantum((unsigned char) (attenuate*10.0+0.5))
00262 #define SigmaMultiplicativeGaussian \
00263   ScaleCharToQuantum((unsigned char) (attenuate*1.0+0.5))
00264 #define SigmaPoisson  (attenuate*0.05)
00265 #define TauGaussian  ScaleCharToQuantum((unsigned char) (attenuate*20.0+0.5))
00266 
00267   MagickRealType
00268     alpha,
00269     beta,
00270     noise,
00271     sigma;
00272 
00273   alpha=GetPseudoRandomValue();
00274   if (alpha == 0.0)
00275     alpha=1.0;
00276   switch (noise_type)
00277   {
00278     case UniformNoise:
00279     default:
00280     {
00281       noise=(MagickRealType) pixel+SigmaUniform*(alpha-0.5);
00282       break;
00283     }
00284     case GaussianNoise:
00285     {
00286       MagickRealType
00287         tau;
00288 
00289       beta=GetPseudoRandomValue();
00290       sigma=sqrt(-2.0*log((double) alpha))*cos((double) (2.0*MagickPI*beta));
00291       tau=sqrt(-2.0*log((double) alpha))*sin((double) (2.0*MagickPI*beta));
00292       noise=(MagickRealType) pixel+sqrt((double) pixel)*SigmaGaussian*sigma+
00293         TauGaussian*tau;
00294       break;
00295     }
00296     case MultiplicativeGaussianNoise:
00297     {
00298       if (alpha <= NoiseEpsilon)
00299         sigma=(MagickRealType) QuantumRange;
00300       else
00301         sigma=sqrt(-2.0*log((double) alpha));
00302       beta=GetPseudoRandomValue();
00303       noise=(MagickRealType) pixel+pixel*SigmaMultiplicativeGaussian*sigma/2.0*
00304         cos((double) (2.0*MagickPI*beta));
00305       break;
00306     }
00307     case ImpulseNoise:
00308     {
00309       if (alpha < (SigmaImpulse/2.0))
00310         noise=0.0;
00311        else
00312          if (alpha >= (1.0-(SigmaImpulse/2.0)))
00313            noise=(MagickRealType) QuantumRange;
00314          else
00315            noise=(MagickRealType) pixel;
00316       break;
00317     }
00318     case LaplacianNoise:
00319     {
00320       if (alpha <= 0.5)
00321         {
00322           if (alpha <= NoiseEpsilon)
00323             noise=(MagickRealType) pixel-(MagickRealType) QuantumRange;
00324           else
00325             noise=(MagickRealType) pixel+ScaleCharToQuantum((unsigned char)
00326               (SigmaLaplacian*log((double) (2.0*alpha))+0.5));
00327           break;
00328         }
00329       beta=1.0-alpha;
00330       if (beta <= (0.5*NoiseEpsilon))
00331         noise=(MagickRealType) (pixel+QuantumRange);
00332       else
00333         noise=(MagickRealType) pixel-ScaleCharToQuantum((unsigned char)
00334           (SigmaLaplacian*log((double) (2.0*beta))+0.5));
00335       break;
00336     }
00337     case PoissonNoise:
00338     {
00339       MagickRealType
00340         poisson;
00341 
00342       register long
00343         i;
00344 
00345       poisson=exp(-SigmaPoisson*(double) ScaleQuantumToChar(pixel));
00346       for (i=0; alpha > poisson; i++)
00347       {
00348         beta=GetPseudoRandomValue();
00349         alpha=alpha*beta;
00350       }
00351       noise=(MagickRealType) ScaleCharToQuantum((unsigned char)
00352         (i/SigmaPoisson));
00353       break;
00354     }
00355     case RandomNoise:
00356     {
00357       noise=(MagickRealType) QuantumRange*GetPseudoRandomValue();
00358       break;
00359     }
00360   }
00361   return(RoundToQuantum(noise));
00362 }
00363 
00364 MagickExport Image *AddNoiseImage(const Image *image,const NoiseType noise_type,
00365   ExceptionInfo *exception)
00366 {
00367   Image
00368     *noise_image;
00369 
00370   noise_image=AddNoiseImageChannel(image,DefaultChannels,noise_type,exception);
00371   return(noise_image);
00372 }
00373 
00374 MagickExport Image *AddNoiseImageChannel(const Image *image,
00375   const ChannelType channel,const NoiseType noise_type,ExceptionInfo *exception)
00376 {
00377 #define AddNoiseImageTag  "AddNoise/Image"
00378 
00379   const char
00380     *option;
00381 
00382   Image
00383     *noise_image;
00384 
00385   long
00386     progress,
00387     y;
00388 
00389   MagickBooleanType
00390     status;
00391 
00392   MagickRealType
00393     attenuate;
00394 
00395   ViewInfo
00396     *image_view,
00397     *noise_view;
00398 
00399   /*
00400     Initialize noise image attributes.
00401   */
00402   assert(image != (const Image *) NULL);
00403   assert(image->signature == MagickSignature);
00404   if (image->debug != MagickFalse)
00405     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00406   assert(exception != (ExceptionInfo *) NULL);
00407   assert(exception->signature == MagickSignature);
00408   noise_image=CloneImage(image,0,0,MagickTrue,exception);
00409   if (noise_image == (Image *) NULL)
00410     return((Image *) NULL);
00411   if (SetImageStorageClass(noise_image,DirectClass) == MagickFalse)
00412     {
00413       InheritException(exception,&noise_image->exception);
00414       noise_image=DestroyImage(noise_image);
00415       return((Image *) NULL);
00416     }
00417   /*
00418     Add noise in each row.
00419   */
00420   attenuate=1.0;
00421   option=GetImageProperty(image,"attenuate");
00422   if (option != (char *) NULL)
00423     attenuate=atof(option);
00424   status=MagickTrue;
00425   progress=0;
00426   image_view=AcquireCacheView(image);
00427   noise_view=AcquireCacheView(noise_image);
00428   for (y=0; y < (long) image->rows; y++)
00429   {
00430     register const IndexPacket
00431       *indexes;
00432 
00433     register const PixelPacket
00434       *p;
00435 
00436     register IndexPacket
00437       *noise_indexes;
00438 
00439     register long
00440       x;
00441 
00442     register PixelPacket
00443       *q;
00444 
00445     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
00446     q=GetCacheViewAuthenticPixels(noise_view,0,y,noise_image->columns,1,
00447       exception);
00448     if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
00449       {
00450         status=MagickFalse;
00451         break;
00452       }
00453     indexes=GetCacheViewVirtualIndexes(image_view);
00454     noise_indexes=GetCacheViewAuthenticIndexes(noise_view);
00455     for (x=0; x < (long) image->columns; x++)
00456     {
00457       if ((channel & RedChannel) != 0)
00458         q->red=GenerateNoise(p->red,noise_type,attenuate);
00459       if ((channel & GreenChannel) != 0)
00460         q->green=GenerateNoise(p->green,noise_type,attenuate);
00461       if ((channel & BlueChannel) != 0)
00462         q->blue=GenerateNoise(p->blue,noise_type,attenuate);
00463       if ((channel & OpacityChannel) != 0)
00464         q->opacity=GenerateNoise(p->opacity,noise_type,attenuate);
00465       if (((channel & IndexChannel) != 0) &&
00466           (image->colorspace == CMYKColorspace))
00467         noise_indexes[x]=(IndexPacket) GenerateNoise(indexes[x],noise_type,
00468           attenuate);
00469       p++;
00470       q++;
00471     }
00472     if (SyncCacheViewAuthenticPixels(noise_view,exception) == MagickFalse)
00473       status=MagickFalse;
00474     if (image->progress_monitor != (MagickProgressMonitor) NULL)
00475       {
00476         MagickBooleanType
00477           proceed;
00478 
00479         proceed=SetImageProgress(image,AddNoiseImageTag,progress++,
00480           image->rows);
00481         if (proceed == MagickFalse)
00482           status=MagickFalse;
00483       }
00484     if (status == MagickFalse)
00485       break;
00486   }
00487   noise_view=DestroyCacheView(noise_view);
00488   image_view=DestroyCacheView(image_view);
00489   if (status == MagickFalse)
00490     noise_image=DestroyImage(noise_image);
00491   return(noise_image);
00492 }
00493 
00494 /*
00495 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00496 %                                                                             %
00497 %                                                                             %
00498 %                                                                             %
00499 %     C h a r c o a l I m a g e                                               %
00500 %                                                                             %
00501 %                                                                             %
00502 %                                                                             %
00503 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00504 %
00505 %  CharcoalImage() creates a new image that is a copy of an existing one with
00506 %  the edge highlighted.  It allocates the memory necessary for the new Image
00507 %  structure and returns a pointer to the new image.
00508 %
00509 %  The format of the CharcoalImage method is:
00510 %
00511 %      Image *CharcoalImage(const Image *image,const double radius,
00512 %        const double sigma,ExceptionInfo *exception)
00513 %
00514 %  A description of each parameter follows:
00515 %
00516 %    o image: the image.
00517 %
00518 %    o radius: the radius of the pixel neighborhood.
00519 %
00520 %    o sigma: the standard deviation of the Gaussian, in pixels.
00521 %
00522 %    o exception: return any errors or warnings in this structure.
00523 %
00524 */
00525 MagickExport Image *CharcoalImage(const Image *image,const double radius,
00526   const double sigma,ExceptionInfo *exception)
00527 {
00528   Image
00529     *charcoal_image,
00530     *clone_image,
00531     *edge_image;
00532 
00533   assert(image != (Image *) NULL);
00534   assert(image->signature == MagickSignature);
00535   if (image->debug != MagickFalse)
00536     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00537   assert(exception != (ExceptionInfo *) NULL);
00538   assert(exception->signature == MagickSignature);
00539   clone_image=CloneImage(image,0,0,MagickTrue,exception);
00540   if (clone_image == (Image *) NULL)
00541     return((Image *) NULL);
00542   (void) SetImageType(clone_image,GrayscaleType);
00543   edge_image=EdgeImage(clone_image,radius,exception);
00544   clone_image=DestroyImage(clone_image);
00545   if (edge_image == (Image *) NULL)
00546     return((Image *) NULL);
00547   charcoal_image=BlurImage(edge_image,radius,sigma,exception);
00548   edge_image=DestroyImage(edge_image);
00549   if (charcoal_image == (Image *) NULL)
00550     return((Image *) NULL);
00551   (void) NormalizeImage(charcoal_image);
00552   (void) NegateImage(charcoal_image,MagickFalse);
00553   (void) SetImageType(charcoal_image,GrayscaleType);
00554   return(charcoal_image);
00555 }
00556 
00557 /*
00558 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00559 %                                                                             %
00560 %                                                                             %
00561 %                                                                             %
00562 %     C o l o r i z e I m a g e                                               %
00563 %                                                                             %
00564 %                                                                             %
00565 %                                                                             %
00566 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00567 %
00568 %  ColorizeImage() blends the fill color with each pixel in the image.
00569 %  A percentage blend is specified with opacity.  Control the application
00570 %  of different color components by specifying a different percentage for
00571 %  each component (e.g. 90/100/10 is 90% red, 100% green, and 10% blue).
00572 %
00573 %  The format of the ColorizeImage method is:
00574 %
00575 %      Image *ColorizeImage(const Image *image,const char *opacity,
00576 %        const PixelPacket colorize,ExceptionInfo *exception)
00577 %
00578 %  A description of each parameter follows:
00579 %
00580 %    o image: the image.
00581 %
00582 %    o opacity:  A character string indicating the level of opacity as a
00583 %      percentage.
00584 %
00585 %    o colorize: A color value.
00586 %
00587 %    o exception: return any errors or warnings in this structure.
00588 %
00589 */
00590 MagickExport Image *ColorizeImage(const Image *image,const char *opacity,
00591   const PixelPacket colorize,ExceptionInfo *exception)
00592 {
00593 #define ColorizeImageTag  "Colorize/Image"
00594 
00595   GeometryInfo
00596     geometry_info;
00597 
00598   Image
00599     *colorize_image;
00600 
00601   long
00602     progress,
00603     y;
00604 
00605   MagickBooleanType
00606     status;
00607 
00608   MagickPixelPacket
00609     pixel;
00610 
00611   MagickStatusType
00612     flags;
00613 
00614   ViewInfo
00615     **colorize_view,
00616     **image_view;
00617 
00618   /*
00619     Allocate colorized image.
00620   */
00621   assert(image != (const Image *) NULL);
00622   assert(image->signature == MagickSignature);
00623   if (image->debug != MagickFalse)
00624     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00625   assert(exception != (ExceptionInfo *) NULL);
00626   assert(exception->signature == MagickSignature);
00627   colorize_image=CloneImage(image,image->columns,image->rows,MagickTrue,
00628     exception);
00629   if (colorize_image == (Image *) NULL)
00630     return((Image *) NULL);
00631   if (SetImageStorageClass(colorize_image,DirectClass) == MagickFalse)
00632     {
00633       InheritException(exception,&colorize_image->exception);
00634       colorize_image=DestroyImage(colorize_image);
00635       return((Image *) NULL);
00636     }
00637   if (opacity == (const char *) NULL)
00638     return(colorize_image);
00639   /*
00640     Determine RGB values of the pen color.
00641   */
00642   flags=ParseGeometry(opacity,&geometry_info);
00643   pixel.red=geometry_info.rho;
00644   pixel.green=geometry_info.rho;
00645   pixel.blue=geometry_info.rho;
00646   pixel.opacity=(MagickRealType) OpaqueOpacity;
00647   if ((flags & SigmaValue) != 0)
00648     pixel.green=geometry_info.sigma;
00649   if ((flags & XiValue) != 0)
00650     pixel.blue=geometry_info.xi;
00651   if ((flags & PsiValue) != 0)
00652     pixel.opacity=geometry_info.psi;
00653   /*
00654     Colorize DirectClass image.
00655   */
00656   status=MagickTrue;
00657   progress=0;
00658   image_view=AcquireCacheViewThreadSet(image);
00659   colorize_view=AcquireCacheViewThreadSet(colorize_image);
00660 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00661   #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
00662 #endif
00663   for (y=0; y < (long) image->rows; y++)
00664   {
00665     MagickBooleanType
00666       sync;
00667 
00668     register const PixelPacket
00669       *p;
00670 
00671     register long
00672       id,
00673       x;
00674 
00675     register PixelPacket
00676       *q;
00677 
00678     if (status == MagickFalse)
00679       continue;
00680     id=GetCacheViewThreadId();
00681     p=GetCacheViewVirtualPixels(image_view[id],0,y,image->columns,1,exception);
00682     q=QueueCacheViewAuthenticPixels(colorize_view[id],0,y,
00683       colorize_image->columns,1,exception);
00684     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
00685       {
00686         status=MagickFalse;
00687         continue;
00688       }
00689     for (x=0; x < (long) image->columns; x++)
00690     {
00691       q->red=(Quantum) ((p->red*(100.0-pixel.red)+
00692         colorize.red*pixel.red)/100.0);
00693       q->green=(Quantum) ((p->green*(100.0-pixel.green)+
00694         colorize.green*pixel.green)/100.0);
00695       q->blue=(Quantum) ((p->blue*(100.0-pixel.blue)+
00696         colorize.blue*pixel.blue)/100.0);
00697       q->opacity=(Quantum) ((p->opacity*(100.0-pixel.opacity)+
00698         colorize.opacity*pixel.opacity)/100.0);
00699       p++;
00700       q++;
00701     }
00702     sync=SyncCacheViewAuthenticPixels(colorize_view[id],exception);
00703     if (sync == MagickFalse)
00704       status=MagickFalse;
00705     if (image->progress_monitor != (MagickProgressMonitor) NULL)
00706       {
00707         MagickBooleanType
00708           proceed;
00709 
00710 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00711   #pragma omp critical
00712 #endif
00713         proceed=SetImageProgress(image,ColorizeImageTag,progress++,image->rows);
00714         if (proceed == MagickFalse)
00715           status=MagickFalse;
00716       }
00717   }
00718   image_view=DestroyCacheViewThreadSet(image_view);
00719   colorize_view=DestroyCacheViewThreadSet(colorize_view);
00720   if (status == MagickFalse)
00721     colorize_image=DestroyImage(colorize_image);
00722   return(colorize_image);
00723 }
00724 
00725 /*
00726 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00727 %                                                                             %
00728 %                                                                             %
00729 %                                                                             %
00730 %     C o n v o l v e I m a g e                                               %
00731 %                                                                             %
00732 %                                                                             %
00733 %                                                                             %
00734 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00735 %
00736 %  ConvolveImage() applies a custom convolution kernel to the image.
00737 %
00738 %  The format of the ConvolveImage method is:
00739 %
00740 %      Image *ConvolveImage(const Image *image,const unsigned long order,
00741 %        const double *kernel,ExceptionInfo *exception)
00742 %      Image *ConvolveImageChannel(const Image *image,const ChannelType channel,
00743 %        const unsigned long order,const double *kernel,
00744 %        ExceptionInfo *exception)
00745 %
00746 %  A description of each parameter follows:
00747 %
00748 %    o image: the image.
00749 %
00750 %    o channel: the channel type.
00751 %
00752 %    o order: the number of columns and rows in the filter kernel.
00753 %
00754 %    o kernel: An array of double representing the convolution kernel.
00755 %
00756 %    o exception: return any errors or warnings in this structure.
00757 %
00758 */
00759 
00760 MagickExport Image *ConvolveImage(const Image *image,const unsigned long order,
00761   const double *kernel,ExceptionInfo *exception)
00762 {
00763   Image
00764     *convolve_image;
00765 
00766   convolve_image=ConvolveImageChannel(image,DefaultChannels,order,kernel,
00767     exception);
00768   return(convolve_image);
00769 }
00770 
00771 MagickExport Image *ConvolveImageChannel(const Image *image,
00772   const ChannelType channel,const unsigned long order,const double *kernel,
00773   ExceptionInfo *exception)
00774 {
00775 #define ConvolveImageTag  "Convolve/Image"
00776 
00777   double
00778     *normal_kernel;
00779 
00780   Image
00781     *convolve_image;
00782 
00783   long
00784     progress,
00785     y;
00786 
00787   MagickBooleanType
00788     status;
00789 
00790   MagickPixelPacket
00791     zero;
00792 
00793   MagickRealType
00794     bias,
00795     gamma;
00796 
00797   register long
00798     i;
00799 
00800   unsigned long
00801     width;
00802 
00803   /*
00804     Initialize convolve image attributes.
00805   */
00806   assert(image != (Image *) NULL);
00807   assert(image->signature == MagickSignature);
00808   if (image->debug != MagickFalse)
00809     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00810   assert(exception != (ExceptionInfo *) NULL);
00811   assert(exception->signature == MagickSignature);
00812   width=order;
00813   if ((width % 2) == 0)
00814     ThrowImageException(OptionError,"KernelWidthMustBeAnOddNumber");
00815   convolve_image=CloneImage(image,0,0,MagickTrue,exception);
00816   if (convolve_image == (Image *) NULL)
00817     return((Image *) NULL);
00818   if (SetImageStorageClass(convolve_image,DirectClass) == MagickFalse)
00819     {
00820       InheritException(exception,&convolve_image->exception);
00821       convolve_image=DestroyImage(convolve_image);
00822       return((Image *) NULL);
00823     }
00824   if (image->debug != MagickFalse)
00825     {
00826       char
00827         format[MaxTextExtent],
00828         *message;
00829 
00830       long
00831         u,
00832         v;
00833 
00834       register const double
00835         *k;
00836 
00837       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
00838         "  ConvolveImage with %ldx%ld kernel:",width,width);
00839       message=AcquireString("");
00840       k=kernel;
00841       for (v=0; v < (long) width; v++)
00842       {
00843         *message='\0';
00844         (void) FormatMagickString(format,MaxTextExtent,"%ld: ",v);
00845         (void) ConcatenateString(&message,format);
00846         for (u=0; u < (long) width; u++)
00847         {
00848           (void) FormatMagickString(format,MaxTextExtent,"%+f ",*k++);
00849           (void) ConcatenateString(&message,format);
00850         }
00851         (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
00852       }
00853       message=DestroyString(message);
00854     }
00855   /*
00856     Normalize kernel.
00857   */
00858   normal_kernel=(double *) AcquireQuantumMemory(width*width,
00859     sizeof(*normal_kernel));
00860   if (normal_kernel == (double *) NULL)
00861     {
00862       convolve_image=DestroyImage(convolve_image);
00863       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00864     }
00865   gamma=0.0;
00866   for (i=0; i < (long) (width*width); i++)
00867     gamma+=kernel[i];
00868   gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
00869   for (i=0; i < (long) (width*width); i++)
00870     normal_kernel[i]=gamma*kernel[i];
00871   /*
00872     Convolve image.
00873   */
00874   status=MagickTrue;
00875   progress=0;
00876   GetMagickPixelPacket(image,&zero);
00877   bias=image->bias;
00878 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00879   #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
00880 #endif
00881   for (y=0; y < (long) image->rows; y++)
00882   {
00883     MagickBooleanType
00884       sync;
00885 
00886     register const IndexPacket
00887       *indexes;
00888 
00889     register const PixelPacket
00890       *p;
00891 
00892     register IndexPacket
00893       *convolve_indexes;
00894 
00895     register long
00896       x;
00897 
00898     register PixelPacket
00899       *q;
00900 
00901     if (status == MagickFalse)
00902       continue;
00903     p=GetVirtualPixels(image,-((long) width/2L),y-(long) (width/2L),
00904       image->columns+width,width,exception);
00905     q=GetAuthenticPixels(convolve_image,0,y,convolve_image->columns,1,
00906       exception);
00907     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
00908       {
00909         status=MagickFalse;
00910         continue;
00911       }
00912     indexes=GetVirtualIndexQueue(image);
00913     convolve_indexes=GetAuthenticIndexQueue(convolve_image);
00914     for (x=0; x < (long) image->columns; x++)
00915     {
00916       long
00917         j,
00918         v;
00919 
00920       MagickPixelPacket
00921         pixel;
00922 
00923       register const double
00924         *k;
00925 
00926       register long
00927         u;
00928 
00929       pixel=zero;
00930       k=normal_kernel;
00931       j=0;
00932       if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
00933         {
00934           for (v=0; v < (long) width; v++)
00935           {
00936             for (u=0; u < (long) width; u++)
00937             {
00938               pixel.red+=(*k)*(p+u+j)->red;
00939               pixel.green+=(*k)*(p+u+j)->green;
00940               pixel.blue+=(*k)*(p+u+j)->blue;
00941               pixel.opacity+=(*k)*(p+u+j)->opacity;
00942               k++;
00943             }
00944             j+=image->columns+width;
00945           }
00946           if ((channel & RedChannel) != 0)
00947             q->red=RoundToQuantum(pixel.red+bias);
00948           if ((channel & GreenChannel) != 0)
00949             q->green=RoundToQuantum(pixel.green+bias);
00950           if ((channel & BlueChannel) != 0)
00951             q->blue=RoundToQuantum(pixel.blue+bias);
00952           if ((channel & OpacityChannel) != 0)
00953             q->opacity=RoundToQuantum(pixel.opacity+bias);
00954           if (((channel & IndexChannel) != 0) &&
00955               (image->colorspace == CMYKColorspace))
00956             {
00957               k=normal_kernel;
00958               j=0;
00959               for (v=0; v < (long) width; v++)
00960               {
00961                 for (u=0; u < (long) width; u++)
00962                 {
00963                   pixel.index+=(*k)*indexes[x+u+j];
00964                   k++;
00965                 }
00966                 j+=image->columns+width;
00967               }
00968               convolve_indexes[x]=RoundToQuantum(pixel.index+bias);
00969             }
00970         }
00971       else
00972         {
00973           MagickRealType
00974             alpha,
00975             gamma;
00976 
00977           gamma=0.0;
00978           for (v=0; v < (long) width; v++)
00979           {
00980             for (u=0; u < (long) width; u++)
00981             {
00982               alpha=(MagickRealType) (QuantumScale*(QuantumRange-
00983                 (p+u+j)->opacity));
00984               pixel.red+=(*k)*alpha*(p+u+j)->red;
00985               pixel.green+=(*k)*alpha*(p+u+j)->green;
00986               pixel.blue+=(*k)*alpha*(p+u+j)->blue;
00987               pixel.opacity+=(*k)*(p+u+j)->opacity;
00988               gamma+=alpha;
00989               k++;
00990             }
00991             j+=image->columns+width;
00992           }
00993           gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
00994           if ((channel & RedChannel) != 0)
00995             q->red=RoundToQuantum(gamma*pixel.red+bias);
00996           if ((channel & GreenChannel) != 0)
00997             q->green=RoundToQuantum(gamma*pixel.green+bias);
00998           if ((channel & BlueChannel) != 0)
00999             q->blue=RoundToQuantum(gamma*pixel.blue+bias);
01000           if ((channel & OpacityChannel) != 0)
01001             q->opacity=RoundToQuantum(pixel.opacity+bias);
01002           if (((channel & IndexChannel) != 0) &&
01003               (image->colorspace == CMYKColorspace))
01004             {
01005               k=normal_kernel;
01006               j=0;
01007               for (v=0; v < (long) width; v++)
01008               {
01009