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-2009 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/artifact.h"
00046 #include "magick/attribute.h"
00047 #include "magick/cache.h"
00048 #include "magick/cache-view.h"
00049 #include "magick/color.h"
00050 #include "magick/color-private.h"
00051 #include "magick/composite.h"
00052 #include "magick/decorate.h"
00053 #include "magick/draw.h"
00054 #include "magick/effect.h"
00055 #include "magick/enhance.h"
00056 #include "magick/exception.h"
00057 #include "magick/exception-private.h"
00058 #include "magick/fx.h"
00059 #include "magick/fx-private.h"
00060 #include "magick/gem.h"
00061 #include "magick/geometry.h"
00062 #include "magick/layer.h"
00063 #include "magick/list.h"
00064 #include "magick/log.h"
00065 #include "magick/image.h"
00066 #include "magick/image-private.h"
00067 #include "magick/memory_.h"
00068 #include "magick/monitor.h"
00069 #include "magick/monitor-private.h"
00070 #include "magick/option.h"
00071 #include "magick/pixel-private.h"
00072 #include "magick/property.h"
00073 #include "magick/quantum.h"
00074 #include "magick/random_.h"
00075 #include "magick/random-private.h"
00076 #include "magick/resample.h"
00077 #include "magick/resample-private.h"
00078 #include "magick/resize.h"
00079 #include "magick/shear.h"
00080 #include "magick/splay-tree.h"
00081 #include "magick/statistic.h"
00082 #include "magick/string_.h"
00083 #include "magick/thread-private.h"
00084 #include "magick/transform.h"
00085 #include "magick/utility.h"
00086 
00087 /*
00088   Define declarations.
00089 */
00090 #define LeftShiftOperator 0xf5
00091 #define RightShiftOperator 0xf6
00092 #define LessThanEqualOperator 0xf7
00093 #define GreaterThanEqualOperator 0xf8
00094 #define EqualOperator 0xf9
00095 #define NotEqualOperator 0xfa
00096 #define LogicalAndOperator 0xfb
00097 #define LogicalOrOperator 0xfc
00098 
00099 struct _FxInfo
00100 {
00101   const Image
00102     *images;
00103 
00104   MagickBooleanType
00105     matte;
00106 
00107   char
00108     *expression;
00109 
00110   FILE
00111     *file;
00112 
00113   SplayTreeInfo
00114     *colors,
00115     *symbols;
00116 
00117   ResampleFilter
00118     **resample_filter;
00119 
00120   RandomInfo
00121     *random_info;
00122 
00123   ExceptionInfo
00124     *exception;
00125 };
00126 
00127 /*
00128 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00129 %                                                                             %
00130 %                                                                             %
00131 %                                                                             %
00132 +   A c q u i r e F x I n f o                                                 %
00133 %                                                                             %
00134 %                                                                             %
00135 %                                                                             %
00136 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00137 %
00138 %  AcquireFxInfo() allocates the FxInfo structure.
00139 %
00140 %  The format of the AcquireFxInfo method is:
00141 %
00142 %      FxInfo *AcquireFxInfo(Image *image,const char *expression)
00143 %  A description of each parameter follows:
00144 %
00145 %    o image: the image.
00146 %
00147 %    o expression: the expression.
00148 %
00149 */
00150 MagickExport FxInfo *AcquireFxInfo(const Image *image,const char *expression)
00151 {
00152   char
00153     fx_op[2];
00154 
00155   FxInfo
00156     *fx_info;
00157 
00158   register long
00159     i;
00160 
00161   fx_info=(FxInfo *) AcquireMagickMemory(sizeof(*fx_info));
00162   if (fx_info == (FxInfo *) NULL)
00163     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
00164   (void) ResetMagickMemory(fx_info,0,sizeof(*fx_info));
00165   fx_info->exception=AcquireExceptionInfo();
00166   fx_info->images=image;
00167   fx_info->matte=image->matte;
00168   fx_info->colors=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
00169     RelinquishMagickMemory);
00170   fx_info->symbols=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
00171     RelinquishMagickMemory);
00172   fx_info->resample_filter=(ResampleFilter **) AcquireQuantumMemory(
00173     GetImageListLength(fx_info->images),sizeof(*fx_info->resample_filter));
00174   if (fx_info->resample_filter == (ResampleFilter **) NULL)
00175     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
00176   for (i=0; i < (long) GetImageListLength(fx_info->images); i++)
00177   {
00178     fx_info->resample_filter[i]=AcquireResampleFilter(GetImageFromList(
00179       fx_info->images,i),fx_info->exception);
00180     SetResampleFilter(fx_info->resample_filter[i],PointFilter,1.0);
00181   }
00182   fx_info->random_info=AcquireRandomInfo();
00183   fx_info->expression=ConstantString(expression);
00184   fx_info->file=stderr;
00185   (void) SubstituteString(&fx_info->expression," ","");  /* compact string */
00186   if ((strstr(fx_info->expression,"e+") != (char *) NULL) ||
00187       (strstr(fx_info->expression,"e-") != (char *) NULL))
00188     {
00189       /*
00190         Convert scientific notation.
00191       */
00192       (void) SubstituteString(&fx_info->expression,"0e+","0*10^");
00193       (void) SubstituteString(&fx_info->expression,"1e+","1*10^");
00194       (void) SubstituteString(&fx_info->expression,"2e+","2*10^");
00195       (void) SubstituteString(&fx_info->expression,"3e+","3*10^");
00196       (void) SubstituteString(&fx_info->expression,"4e+","4*10^");
00197       (void) SubstituteString(&fx_info->expression,"5e+","5*10^");
00198       (void) SubstituteString(&fx_info->expression,"6e+","6*10^");
00199       (void) SubstituteString(&fx_info->expression,"7e+","7*10^");
00200       (void) SubstituteString(&fx_info->expression,"8e+","8*10^");
00201       (void) SubstituteString(&fx_info->expression,"9e+","9*10^");
00202       (void) SubstituteString(&fx_info->expression,"0e-","0*10^-");
00203       (void) SubstituteString(&fx_info->expression,"1e-","1*10^-");
00204       (void) SubstituteString(&fx_info->expression,"2e-","2*10^-");
00205       (void) SubstituteString(&fx_info->expression,"3e-","3*10^-");
00206       (void) SubstituteString(&fx_info->expression,"4e-","4*10^-");
00207       (void) SubstituteString(&fx_info->expression,"5e-","5*10^-");
00208       (void) SubstituteString(&fx_info->expression,"6e-","6*10^-");
00209       (void) SubstituteString(&fx_info->expression,"7e-","7*10^-");
00210       (void) SubstituteString(&fx_info->expression,"8e-","8*10^-");
00211       (void) SubstituteString(&fx_info->expression,"9e-","9*10^-");
00212     }
00213   /*
00214     Convert complex to simple operators.
00215   */
00216   fx_op[1]='\0';
00217   *fx_op=(char) LeftShiftOperator;
00218   (void) SubstituteString(&fx_info->expression,"<<",fx_op);
00219   *fx_op=(char) RightShiftOperator;
00220   (void) SubstituteString(&fx_info->expression,">>",fx_op);
00221   *fx_op=(char) LessThanEqualOperator;
00222   (void) SubstituteString(&fx_info->expression,"<=",fx_op);
00223   *fx_op=(char) GreaterThanEqualOperator;
00224   (void) SubstituteString(&fx_info->expression,">=",fx_op);
00225   *fx_op=(char) EqualOperator;
00226   (void) SubstituteString(&fx_info->expression,"==",fx_op);
00227   *fx_op=(char) NotEqualOperator;
00228   (void) SubstituteString(&fx_info->expression,"!=",fx_op);
00229   *fx_op=(char) LogicalAndOperator;
00230   (void) SubstituteString(&fx_info->expression,"&&",fx_op);
00231   *fx_op=(char) LogicalOrOperator;
00232   (void) SubstituteString(&fx_info->expression,"||",fx_op);
00233   return(fx_info);
00234 }
00235 
00236 /*
00237 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00238 %                                                                             %
00239 %                                                                             %
00240 %                                                                             %
00241 %     A d d N o i s e I m a g e                                               %
00242 %                                                                             %
00243 %                                                                             %
00244 %                                                                             %
00245 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00246 %
00247 %  AddNoiseImage() adds random noise to the image.
00248 %
00249 %  The format of the AddNoiseImage method is:
00250 %
00251 %      Image *AddNoiseImage(const Image *image,const NoiseType noise_type,
00252 %        ExceptionInfo *exception)
00253 %      Image *AddNoiseImageChannel(const Image *image,const ChannelType channel,
00254 %        const NoiseType noise_type,ExceptionInfo *exception)
00255 %
00256 %  A description of each parameter follows:
00257 %
00258 %    o image: the image.
00259 %
00260 %    o channel: the channel type.
00261 %
00262 %    o noise_type:  The type of noise: Uniform, Gaussian, Multiplicative,
00263 %      Impulse, Laplacian, or Poisson.
00264 %
00265 %    o exception: return any errors or warnings in this structure.
00266 %
00267 */
00268 MagickExport Image *AddNoiseImage(const Image *image,const NoiseType noise_type,
00269   ExceptionInfo *exception)
00270 {
00271   Image
00272     *noise_image;
00273 
00274   noise_image=AddNoiseImageChannel(image,DefaultChannels,noise_type,exception);
00275   return(noise_image);
00276 }
00277 
00278 MagickExport Image *AddNoiseImageChannel(const Image *image,
00279   const ChannelType channel,const NoiseType noise_type,ExceptionInfo *exception)
00280 {
00281 #define AddNoiseImageTag  "AddNoise/Image"
00282 
00283   const char
00284     *option;
00285 
00286   Image
00287     *noise_image;
00288 
00289   long
00290     progress,
00291     y;
00292 
00293   MagickBooleanType
00294     status;
00295 
00296   MagickRealType
00297     attenuate;
00298 
00299   RandomInfo
00300     **random_info;
00301 
00302   CacheView
00303     *image_view,
00304     *noise_view;
00305 
00306   /*
00307     Initialize noise image attributes.
00308   */
00309   assert(image != (const Image *) NULL);
00310   assert(image->signature == MagickSignature);
00311   if (image->debug != MagickFalse)
00312     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00313   assert(exception != (ExceptionInfo *) NULL);
00314   assert(exception->signature == MagickSignature);
00315   noise_image=CloneImage(image,0,0,MagickTrue,exception);
00316   if (noise_image == (Image *) NULL)
00317     return((Image *) NULL);
00318   if (SetImageStorageClass(noise_image,DirectClass) == MagickFalse)
00319     {
00320       InheritException(exception,&noise_image->exception);
00321       noise_image=DestroyImage(noise_image);
00322       return((Image *) NULL);
00323     }
00324   /*
00325     Add noise in each row.
00326   */
00327   attenuate=1.0;
00328   option=GetImageArtifact(image,"attenuate");
00329   if (option != (char *) NULL)
00330     attenuate=atof(option);
00331   status=MagickTrue;
00332   progress=0;
00333   random_info=AcquireRandomInfoThreadSet();
00334   image_view=AcquireCacheView(image);
00335   noise_view=AcquireCacheView(noise_image);
00336 #if defined(MAGICKCOREMAGICKCORE_OPENMP_SUPPORT_SUPPORT_DEBUG)
00337   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
00338 #endif
00339   for (y=0; y < (long) image->rows; y++)
00340   {
00341     MagickBooleanType
00342       sync;
00343 
00344     register const IndexPacket
00345       *__restrict indexes;
00346 
00347     register const PixelPacket
00348       *__restrict p;
00349 
00350     register IndexPacket
00351       *__restrict noise_indexes;
00352 
00353     register long
00354       id,
00355       x;
00356 
00357     register PixelPacket
00358       *__restrict q;
00359 
00360     if (status == MagickFalse)
00361       continue;
00362     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
00363     q=GetCacheViewAuthenticPixels(noise_view,0,y,noise_image->columns,1,
00364       exception);
00365     if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
00366       {
00367         status=MagickFalse;
00368         continue;
00369       }
00370     indexes=GetCacheViewVirtualIndexQueue(image_view);
00371     noise_indexes=GetCacheViewAuthenticIndexQueue(noise_view);
00372     id=GetOpenMPThreadId();
00373     for (x=0; x < (long) image->columns; x++)
00374     {
00375       if ((channel & RedChannel) != 0)
00376         q->red=RoundToQuantum(GenerateDifferentialNoise(random_info[id],
00377           p->red,noise_type,attenuate));
00378       if ((channel & GreenChannel) != 0)
00379         q->green=RoundToQuantum(GenerateDifferentialNoise(random_info[id],
00380           p->green,noise_type,attenuate));
00381       if ((channel & BlueChannel) != 0)
00382         q->blue=RoundToQuantum(GenerateDifferentialNoise(random_info[id],
00383           p->blue,noise_type,attenuate));
00384       if ((channel & OpacityChannel) != 0)
00385         q->opacity=RoundToQuantum(GenerateDifferentialNoise(random_info[id],
00386           p->opacity,noise_type,attenuate));
00387       if (((channel & IndexChannel) != 0) &&
00388           (image->colorspace == CMYKColorspace))
00389         noise_indexes[x]=(IndexPacket) RoundToQuantum(GenerateDifferentialNoise(
00390           random_info[id],indexes[x],noise_type,attenuate));
00391       p++;
00392       q++;
00393     }
00394     sync=SyncCacheViewAuthenticPixels(noise_view,exception);
00395     if (sync == MagickFalse)
00396       status=MagickFalse;
00397     if (image->progress_monitor != (MagickProgressMonitor) NULL)
00398       {
00399         MagickBooleanType
00400           proceed;
00401 
00402 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00403         #pragma omp critical (MagickCore_AverageImages)
00404 #endif
00405         proceed=SetImageProgress(image,AddNoiseImageTag,progress++,
00406           image->rows);
00407         if (proceed == MagickFalse)
00408           status=MagickFalse;
00409       }
00410   }
00411   noise_view=DestroyCacheView(noise_view);
00412   image_view=DestroyCacheView(image_view);
00413   random_info=DestroyRandomInfoThreadSet(random_info);
00414   if (status == MagickFalse)
00415     noise_image=DestroyImage(noise_image);
00416   return(noise_image);
00417 }
00418 
00419 /*
00420 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00421 %                                                                             %
00422 %                                                                             %
00423 %                                                                             %
00424 %     B l u e S h i f t I m a g e                                             %
00425 %                                                                             %
00426 %                                                                             %
00427 %                                                                             %
00428 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00429 %
00430 %  BlueShiftImage() mutes the colors of the image to simulate a scene at
00431 %  nighttime in the moonlight.
00432 %
00433 %  The format of the BlueShiftImage method is:
00434 %
00435 %      Image *BlueShiftImage(const Image *image,const double factor,
00436 %        ExceptionInfo *exception)
00437 %
00438 %  A description of each parameter follows:
00439 %
00440 %    o image: the image.
00441 %
00442 %    o factor: the shift factor.
00443 %
00444 %    o exception: return any errors or warnings in this structure.
00445 %
00446 */
00447 MagickExport Image *BlueShiftImage(const Image *image,const double factor,
00448   ExceptionInfo *exception)
00449 {
00450 #define BlueShiftImageTag  "BlueShift/Image"
00451 
00452   Image
00453     *shift_image;
00454 
00455   long
00456     progress,
00457     y;
00458 
00459   MagickBooleanType
00460     status;
00461 
00462   CacheView
00463     *image_view,
00464     *shift_view;
00465 
00466   /*
00467     Allocate blue shift image.
00468   */
00469   assert(image != (const Image *) NULL);
00470   assert(image->signature == MagickSignature);
00471   if (image->debug != MagickFalse)
00472     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00473   assert(exception != (ExceptionInfo *) NULL);
00474   assert(exception->signature == MagickSignature);
00475   shift_image=CloneImage(image,image->columns,image->rows,MagickTrue,
00476     exception);
00477   if (shift_image == (Image *) NULL)
00478     return((Image *) NULL);
00479   if (SetImageStorageClass(shift_image,DirectClass) == MagickFalse)
00480     {
00481       InheritException(exception,&shift_image->exception);
00482       shift_image=DestroyImage(shift_image);
00483       return((Image *) NULL);
00484     }
00485   /*
00486     Blue-shift DirectClass image.
00487   */
00488   status=MagickTrue;
00489   progress=0;
00490   image_view=AcquireCacheView(image);
00491   shift_view=AcquireCacheView(shift_image);
00492 #if defined(MAGICKCOREMAGICKCORE_OPENMP_SUPPORT_SUPPORT_DEBUG)
00493   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
00494 #endif
00495   for (y=0; y < (long) image->rows; y++)
00496   {
00497     MagickBooleanType
00498       sync;
00499 
00500     MagickPixelPacket
00501       pixel;
00502 
00503     Quantum
00504       quantum;
00505 
00506     register const PixelPacket
00507       *__restrict p;
00508 
00509     register long
00510       x;
00511 
00512     register PixelPacket
00513       *__restrict q;
00514 
00515     if (status == MagickFalse)
00516       continue;
00517     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
00518     q=QueueCacheViewAuthenticPixels(shift_view,0,y,shift_image->columns,1,
00519       exception);
00520     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
00521       {
00522         status=MagickFalse;
00523         continue;
00524       }
00525     for (x=0; x < (long) image->columns; x++)
00526     {
00527       quantum=p->red;
00528       if (p->green < quantum)
00529         quantum=p->green;
00530       if (p->blue < quantum)
00531         quantum=p->blue;
00532       pixel.red=0.5*(p->red+factor*quantum);
00533       pixel.green=0.5*(p->green+factor*quantum);
00534       pixel.blue=0.5*(p->blue+factor*quantum);
00535       quantum=p->red;
00536       if (p->green > quantum)
00537         quantum=p->green;
00538       if (p->blue > quantum)
00539         quantum=p->blue;
00540       pixel.red=0.5*(pixel.red+factor*quantum);
00541       pixel.green=0.5*(pixel.green+factor*quantum);
00542       pixel.blue=0.5*(pixel.blue+factor*quantum);
00543       q->red=RoundToQuantum(pixel.red);
00544       q->green=RoundToQuantum(pixel.green);
00545       q->blue=RoundToQuantum(pixel.blue);
00546       p++;
00547       q++;
00548     }
00549     sync=SyncCacheViewAuthenticPixels(shift_view,exception);
00550     if (sync == MagickFalse)
00551       status=MagickFalse;
00552     if (image->progress_monitor != (MagickProgressMonitor) NULL)
00553       {
00554         MagickBooleanType
00555           proceed;
00556 
00557 #if defined(MAGICKCOREMAGICKCORE_OPENMP_SUPPORT_SUPPORT_DEBUG)
00558   #pragma omp critical (MagickCore_BlueShiftImage)
00559 #endif
00560         proceed=SetImageProgress(image,BlueShiftImageTag,progress++,
00561           image->rows);
00562         if (proceed == MagickFalse)
00563           status=MagickFalse;
00564       }
00565   }
00566   image_view=DestroyCacheView(image_view);
00567   shift_view=DestroyCacheView(shift_view);
00568   if (status == MagickFalse)
00569     shift_image=DestroyImage(shift_image);
00570   return(shift_image);
00571 }
00572 
00573 /*
00574 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00575 %                                                                             %
00576 %                                                                             %
00577 %                                                                             %
00578 %     C h a r c o a l I m a g e                                               %
00579 %                                                                             %
00580 %                                                                             %
00581 %                                                                             %
00582 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00583 %
00584 %  CharcoalImage() creates a new image that is a copy of an existing one with
00585 %  the edge highlighted.  It allocates the memory necessary for the new Image
00586 %  structure and returns a pointer to the new image.
00587 %
00588 %  The format of the CharcoalImage method is:
00589 %
00590 %      Image *CharcoalImage(const Image *image,const double radius,
00591 %        const double sigma,ExceptionInfo *exception)
00592 %
00593 %  A description of each parameter follows:
00594 %
00595 %    o image: the image.
00596 %
00597 %    o radius: the radius of the pixel neighborhood.
00598 %
00599 %    o sigma: the standard deviation of the Gaussian, in pixels.
00600 %
00601 %    o exception: return any errors or warnings in this structure.
00602 %
00603 */
00604 MagickExport Image *CharcoalImage(const Image *image,const double radius,
00605   const double sigma,ExceptionInfo *exception)
00606 {
00607   Image
00608     *charcoal_image,
00609     *clone_image,
00610     *edge_image;
00611 
00612   assert(image != (Image *) NULL);
00613   assert(image->signature == MagickSignature);
00614   if (image->debug != MagickFalse)
00615     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00616   assert(exception != (ExceptionInfo *) NULL);
00617   assert(exception->signature == MagickSignature);
00618   clone_image=CloneImage(image,0,0,MagickTrue,exception);
00619   if (clone_image == (Image *) NULL)
00620     return((Image *) NULL);
00621   (void) SetImageType(clone_image,GrayscaleType);
00622   edge_image=EdgeImage(clone_image,radius,exception);
00623   clone_image=DestroyImage(clone_image);
00624   if (edge_image == (Image *) NULL)
00625     return((Image *) NULL);
00626   charcoal_image=BlurImage(edge_image,radius,sigma,exception);
00627   edge_image=DestroyImage(edge_image);
00628   if (charcoal_image == (Image *) NULL)
00629     return((Image *) NULL);
00630   (void) NormalizeImage(charcoal_image);
00631   (void) NegateImage(charcoal_image,MagickFalse);
00632   (void) SetImageType(charcoal_image,GrayscaleType);
00633   return(charcoal_image);
00634 }
00635 
00636 /*
00637 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00638 %                                                                             %
00639 %                                                                             %
00640 %                                                                             %
00641 %     C o l o r i z e I m a g e                                               %
00642 %                                                                             %
00643 %                                                                             %
00644 %                                                                             %
00645 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00646 %
00647 %  ColorizeImage() blends the fill color with each pixel in the image.
00648 %  A percentage blend is specified with opacity.  Control the application
00649 %  of different color components by specifying a different percentage for
00650 %  each component (e.g. 90/100/10 is 90% red, 100% green, and 10% blue).
00651 %
00652 %  The format of the ColorizeImage method is:
00653 %
00654 %      Image *ColorizeImage(const Image *image,const char *opacity,
00655 %        const PixelPacket colorize,ExceptionInfo *exception)
00656 %
00657 %  A description of each parameter follows:
00658 %
00659 %    o image: the image.
00660 %
00661 %    o opacity:  A character string indicating the level of opacity as a
00662 %      percentage.
00663 %
00664 %    o colorize: A color value.
00665 %
00666 %    o exception: return any errors or warnings in this structure.
00667 %
00668 */
00669 MagickExport Image *ColorizeImage(const Image *image,const char *opacity,
00670   const PixelPacket colorize,ExceptionInfo *exception)
00671 {
00672 #define ColorizeImageTag  "Colorize/Image"
00673 
00674   GeometryInfo
00675     geometry_info;
00676 
00677   Image
00678     *colorize_image;
00679 
00680   long
00681     progress,
00682     y;
00683 
00684   MagickBooleanType
00685     status;
00686 
00687   MagickPixelPacket
00688     pixel;
00689 
00690   MagickStatusType
00691     flags;
00692 
00693   CacheView
00694     *colorize_view,
00695     *image_view;
00696 
00697   /*
00698     Allocate colorized image.
00699   */
00700   assert(image != (const Image *) NULL);
00701   assert(image->signature == MagickSignature);
00702   if (image->debug != MagickFalse)
00703     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00704   assert(exception != (ExceptionInfo *) NULL);
00705   assert(exception->signature == MagickSignature);
00706   colorize_image=CloneImage(image,image->columns,image->rows,MagickTrue,
00707     exception);
00708   if (colorize_image == (Image *) NULL)
00709     return((Image *) NULL);
00710   if (SetImageStorageClass(colorize_image,DirectClass) == MagickFalse)
00711     {
00712       InheritException(exception,&colorize_image->exception);
00713       colorize_image=DestroyImage(colorize_image);
00714       return((Image *) NULL);
00715     }
00716   if (opacity == (const char *) NULL)
00717     return(colorize_image);
00718   /*
00719     Determine RGB values of the pen color.
00720   */
00721   flags=ParseGeometry(opacity,&geometry_info);
00722   pixel.red=geometry_info.rho;
00723   pixel.green=geometry_info.rho;
00724   pixel.blue=geometry_info.rho;
00725   pixel.opacity=(MagickRealType) OpaqueOpacity;
00726   if ((flags & SigmaValue) != 0)
00727     pixel.green=geometry_info.sigma;
00728   if ((flags & XiValue) != 0)
00729     pixel.blue=geometry_info.xi;
00730   if ((flags & PsiValue) != 0)
00731     pixel.opacity=geometry_info.psi;
00732   /*
00733     Colorize DirectClass image.
00734   */
00735   status=MagickTrue;
00736   progress=0;
00737   image_view=AcquireCacheView(image);
00738   colorize_view=AcquireCacheView(colorize_image);
00739 #if defined(MAGICKCOREMAGICKCORE_OPENMP_SUPPORT_SUPPORT_DEBUG)
00740   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
00741 #endif
00742   for (y=0; y < (long) image->rows; y++)
00743   {
00744     MagickBooleanType
00745       sync;
00746 
00747     register const PixelPacket
00748       *__restrict p;
00749 
00750     register long
00751       x;
00752 
00753     register PixelPacket
00754       *__restrict q;
00755 
00756     if (status == MagickFalse)
00757       continue;
00758     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
00759     q=QueueCacheViewAuthenticPixels(colorize_view,0,y,colorize_image->columns,1,
00760       exception);
00761     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
00762       {
00763         status=MagickFalse;
00764         continue;
00765       }
00766     for (x=0; x < (long) image->columns; x++)
00767     {
00768       q->red=(Quantum) ((p->red*(100.0-pixel.red)+
00769         colorize.red*pixel.red)/100.0);
00770       q->green=(Quantum) ((p->green*(100.0-pixel.green)+
00771         colorize.green*pixel.green)/100.0);
00772       q->blue=(Quantum) ((p->blue*(100.0-pixel.blue)+
00773         colorize.blue*pixel.blue)/100.0);
00774       q->opacity=(Quantum) ((p->opacity*(100.0-pixel.opacity)+
00775         colorize.opacity*pixel.opacity)/100.0);
00776       p++;
00777       q++;
00778     }
00779     sync=SyncCacheViewAuthenticPixels(colorize_view,exception);
00780     if (sync == MagickFalse)
00781       status=MagickFalse;
00782     if (image->progress_monitor != (MagickProgressMonitor) NULL)
00783       {
00784         MagickBooleanType
00785           proceed;
00786 
00787 #if defined(MAGICKCOREMAGICKCORE_OPENMP_SUPPORT_SUPPORT_DEBUG)
00788   #pragma omp critical (MagickCore_ColorizeImage)
00789 #endif
00790         proceed=SetImageProgress(image,ColorizeImageTag,progress++,image->rows);
00791         if (proceed == MagickFalse)
00792           status=MagickFalse;
00793       }
00794   }
00795   image_view=DestroyCacheView(image_view);
00796   colorize_view=DestroyCacheView(colorize_view);
00797   if (status == MagickFalse)
00798     colorize_image=DestroyImage(colorize_image);
00799   return(colorize_image);
00800 }
00801 
00802 /*
00803 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00804 %                                                                             %
00805 %                                                                             %
00806 %                                                                             %
00807 %     C o n v o l v e I m a g e                                               %
00808 %                                                                             %
00809 %                                                                             %
00810 %                                                                             %
00811 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00812 %
00813 %  ConvolveImage() applies a custom convolution kernel to the image.
00814 %
00815 %  The format of the ConvolveImage method is:
00816 %
00817 %      Image *ConvolveImage(const Image *image,const unsigned long order,
00818 %        const double *kernel,ExceptionInfo *exception)
00819 %      Image *ConvolveImageChannel(const Image *image,const ChannelType channel,
00820 %        const unsigned long order,const double *kernel,
00821 %        ExceptionInfo *exception)
00822 %
00823 %  A description of each parameter follows:
00824 %
00825 %    o image: the image.
00826 %
00827 %    o channel: the channel type.
00828 %
00829 %    o order: the number of columns and rows in the filter kernel.
00830 %
00831 %    o kernel: An array of double representing the convolution kernel.
00832 %
00833 %    o exception: return any errors or warnings in this structure.
00834 %
00835 */
00836 
00837 MagickExport Image *ConvolveImage(const Image *image,const unsigned long order,
00838   const double *kernel,ExceptionInfo *exception)
00839 {
00840   Image
00841     *convolve_image;
00842 
00843   convolve_image=ConvolveImageChannel(image,DefaultChannels,order,kernel,
00844     exception);
00845   return(convolve_image);
00846 }
00847 
00848 MagickExport Image *ConvolveImageChannel(const Image *image,
00849   const ChannelType channel,const unsigned long order,const double *kernel,
00850   ExceptionInfo *exception)
00851 {
00852 #define ConvolveImageTag  "Convolve/Image"
00853 
00854   double
00855     *normal_kernel;
00856 
00857   Image
00858     *convolve_image;
00859 
00860   long
00861     progress,
00862     y;
00863 
00864   MagickBooleanType
00865     status;
00866 
00867   MagickPixelPacket
00868     bias;
00869 
00870   MagickRealType
00871     gamma;
00872 
00873   register long
00874     i;
00875 
00876   unsigned long
00877     width;
00878 
00879   CacheView
00880     *convolve_view,
00881     *image_view;
00882 
00883   /*
00884     Initialize convolve image attributes.
00885   */
00886   assert(image != (Image *) NULL);
00887   assert(image->signature == MagickSignature);
00888   if (image->debug != MagickFalse)
00889     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00890   assert(exception != (ExceptionInfo *) NULL);
00891   assert(exception->signature == MagickSignature);
00892   width=order;
00893   if ((width % 2) == 0)
00894     ThrowImageException(OptionError,"KernelWidthMustBeAnOddNumber");
00895   convolve_image=CloneImage(image,0,0,MagickTrue,exception);
00896   if (convolve_image == (Image *) NULL)
00897     return((Image *) NULL);
00898   if (SetImageStorageClass(convolve_image,DirectClass) == MagickFalse)
00899     {
00900       InheritException(exception,&convolve_image->exception);
00901       convolve_image=DestroyImage(convolve_image);
00902       return((Image *) NULL);
00903     }
00904   if (image->debug != MagickFalse)
00905     {
00906       char
00907         format[MaxTextExtent],
00908         *message;
00909 
00910       long
00911         u,
00912         v;
00913 
00914       register const double
00915         *k;
00916 
00917       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
00918         "  ConvolveImage with %ldx%ld kernel:",width,width);
00919       message=AcquireString("");
00920       k=kernel;
00921       for (v=0; v < (long) width; v++)
00922       {
00923         *message='\0';
00924         (void) FormatMagickString(format,MaxTextExtent,"%ld: ",v);
00925         (void) ConcatenateString(&message,format);
00926         for (u=0; u < (long) width; u++)
00927         {
00928           (void) FormatMagickString(format,MaxTextExtent,"%+f ",*k++);
00929           (void) ConcatenateString(&message,format);
00930         }
00931         (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
00932       }
00933       message=DestroyString(message);
00934     }
00935   /*
00936     Normalize kernel.
00937   */
00938   normal_kernel=(double *) AcquireQuantumMemory(width*width,
00939     sizeof(*normal_kernel));
00940   if (normal_kernel == (double *) NULL)
00941     {
00942       convolve_image=DestroyImage(convolve_image);
00943       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00944     }
00945   gamma=0.0;
00946   for (i=0; i < (long) (width*width); i++)
00947     gamma+=kernel[i];
00948   gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
00949   for (i=0; i < (long) (width*width); i++)
00950     normal_kernel[i]=gamma*kernel[i];
00951   /*
00952     Convolve image.
00953   */
00954   status=MagickTrue;
00955   progress=0;
00956   GetMagickPixelPacket(image,&bias);
00957   SetMagickPixelPacketBias(image,&bias);
00958   image_view=AcquireCacheView(image);
00959   convolve_view=AcquireCacheView(convolve_image);
00960 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00961   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
00962 #endif
00963   for (y=0; y < (long) image->rows; y++)
00964   {
00965     MagickBooleanType
00966       sync;
00967 
00968     register const IndexPacket
00969       *__restrict indexes;
00970 
00971     register const PixelPacket
00972       *__restrict p;
00973 
00974     register IndexPacket
00975       *__restrict convolve_indexes;
00976 
00977     register long
00978       x;
00979 
00980     register PixelPacket
00981       *__restrict q;
00982 
00983     if (status == MagickFalse)
00984       continue;
00985     p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
00986       2L),image->columns+width,width,exception);
00987     q=GetCacheViewAuthenticPixels(convolve_view,0,y,convolve_image->columns,1,
00988       exception);
00989     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
00990       {
00991         status=MagickFalse;
00992         continue;
00993       }
00994     indexes=GetCacheViewVirtualIndexQueue(image_view);
00995     convolve_indexes=GetCacheViewAuthenticIndexQueue(convolve_view);
00996     for (x=0; x < (long) image->columns; x++)
00997     {
00998       long
00999         v;
01000 
01001       MagickPixelPacket
01002         pixel;
01003 
01004       register const double
01005         *__restrict k;
01006 
01007       register const PixelPacket
01008         *__restrict kernel_pixels;
01009 
01010       register long
01011         u;
01012 
01013       pixel=bias;
01014       k=normal_kernel;
01015       kernel_pixels=p;
01016       if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
01017         {
01018           for (v=0; v < (long) width; v++)
01019           {
01020             for (u=0; u < (long) width; u++)
01021             {
01022               pixel.red+=(*k)*kernel_pixels[u].red;
01023               pixel.green+=(*k)*kernel_pixels[u].green;
01024               pixel.blue+=(*k)*kernel_pixels[u].blue;
01025               k++;
01026             }
01027             kernel_pixels+=image->columns+width;
01028           }
01029           if ((channel & RedChannel) != 0)
01030             q->red=RoundToQuantum(pixel.red);
01031           if ((channel & GreenChannel) != 0)
01032             q->green=RoundToQuantum(pixel.green);
01033           if ((channel & BlueChannel) != 0)
01034             q->blue=RoundToQuantum(pixel.blue);
01035           if ((channel & OpacityChannel) != 0)
01036             {
01037               k=normal_kernel;
01038               kernel_pixels=p;
01039               for (v=0; v < (long) width; v++)
01040               {
01041                 for (u=0; u < (long) width; u++)
01042                 {
01043                   pixel.opacity+=(*k)*kernel_pixels[u].opacity;
01044                   k++;
01045                 }
01046                 kernel_pixels+=image->columns+width;
01047               }
01048               q->opacity=RoundToQuantum(pixel.opacity);
01049             }
01050           if (((channel & IndexChannel) != 0) &&
01051               (image->colorspace == CMYKColorspace))
01052             {
01053               register const IndexPacket
01054                 *__restrict kernel_indexes;
01055 
01056               k=normal_kernel;
01057               kernel_indexes=indexes;
01058               for (v=0; v < (long) width; v++)
01059               {
01060                 for (u=0; u < (long) width; u++)
01061                 {
01062                   pixel.index+=(*k)*kernel_indexes[u];
01063                   k++;
01064                 }
01065                 kernel_indexes+=image->columns+width;
01066               }
01067               convolve_indexes[x]=RoundToQuantum(pixel.index);
01068             }
01069         }
01070       else
01071         {
01072           MagickRealType
01073             alpha,
01074             gamma;
01075 
01076           gamma=0.0;
01077           for (v=0; v < (long) width; v++)
01078           {
01079             for (u=0; u < (long) width; u++)
01080             {
01081               alpha=(MagickRealType) (QuantumScale*(QuantumRange-
01082                 kernel_pixels[u].opacity));
01083               pixel.red+=(*k)*alpha*kernel_pixels[u].red;
01084               pixel.green+=(*k)*alpha*kernel_pixels[u].green;
01085               pixel.blue+=(*k)*alpha*kernel_pixels[u].blue;
01086               pixel.opacity+=(*k)*kernel_pixels[u].opacity;
01087               gamma+=(*k)*alpha;
01088               k++;
01089             }
01090             kernel_pixels+=image->columns+width;
01091           }
01092           gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
01093           if ((channel & RedChannel) != 0)
01094             q->red=RoundToQuantum(gamma*pixel.red);
01095           if ((channel & GreenChannel) != 0)
01096             q->green=RoundToQuantum(gamma*pixel.green);
01097           if ((channel & BlueChannel) != 0)
01098             q->blue=RoundToQuantum(gamma*pixel.blue);
01099           if ((channel & OpacityChannel) != 0)
01100             {
01101               k=normal_kernel;
01102               kernel_pixels=p;
01103               for (v=0; v < (long) width; v++)
01104               {
01105                 for (u=0; u < (long) width; u++)
01106                 {
01107                   pixel.opacity+=(*k)*kernel_pixels[u].opacity;
01108                   k++;
01109                 }
01110                 kernel_pixels+=image->columns+width;
01111               }
01112               q->opacity=RoundToQuantum(pixel.opacity);
01113             }
01114           if (((channel & IndexChannel) != 0) &&
01115               (image->colorspace == CMYKColorspace))
01116             {
01117               register const IndexPacket
01118                 *__restrict kernel_indexes;
01119 
01120               k=normal_kernel;
01121               kernel_pixels=p;
01122               kernel_indexes=indexes;
01123               for (v=0; v < (long) width; v++)
01124               {
01125                 for (u=0; u < (long) width; u++)
01126                 {
01127                   alpha=(MagickRealType) (QuantumScale*(QuantumRange-
01128                     kernel_pixels[u].opacity));
01129                   pixel.index+=(*k)*alpha*kernel_indexes[u];
01130                   k++;
01131                 }
01132                 kernel_pixels+=image->columns+width;
01133                 kernel_indexes+=image->columns+width;
01134               }
01135               convolve_indexes[x]=RoundToQuantum(gamma*pixel.index);
01136             }
01137         }
01138       p++;
01139       q++;
01140     }
01141     sync=SyncCacheViewAuthenticPixels(convolve_view,exception);
01142     if (sync == MagickFalse)
01143       status=MagickFalse;
01144     if (image->progress_monitor != (MagickProgressMonitor) NULL)
01145       {
01146         MagickBooleanType
01147           proceed;
01148 
01149 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01150   #pragma omp critical (MagickCore_ConvolveImageChannel)
01151 #endif
01152         proceed=SetImageProgress(image,ConvolveImageTag,progress++,image->rows);
01153         if (proceed == MagickFalse)
01154           status=MagickFalse;
01155       }
01156   }
01157   convolve_image->type=image->type;
01158   convolve_view=DestroyCacheView(convolve_view);
01159   image_view=DestroyCacheView(image_view);
01160   normal_kernel=(double *) RelinquishMagickMemory(normal_kernel);
01161   if (status == MagickFalse)
01162     convolve_image=DestroyImage(convolve_image);
01163   return(convolve_image);
01164 }
01165 
01166 /*
01167 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01168 %                                                                             %
01169 %                                                                             %
01170 %                                                                             %
01171 +   D e s t r o y F x I n f o                                                 %
01172 %                                                                             %
01173 %                                                                             %
01174 %                                                                             %
01175 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01176 %
01177 %  DestroyFxInfo() deallocates memory associated with an FxInfo structure.
01178 %
01179 %  The format of the DestroyFxInfo method is:
01180 %
01181 %      ImageInfo *DestroyFxInfo(ImageInfo *fx_info)
01182 %
01183 %  A description of each parameter follows:
01184 %
01185 %    o fx_info: the fx info.
01186 %
01187 */
01188 MagickExport FxInfo *DestroyFxInfo(FxInfo *fx_info)
01189 {
01190   register long
01191     i;
01192 
01193   fx_info->exception=DestroyExceptionInfo(fx_info->exception);
01194   fx_info->expression=DestroyString(fx_info->expression);
01195   fx_info->symbols=DestroySplayTree(fx_info->symbols);
01196   fx_info->colors=DestroySplayTree(fx_info->colors);
01197   for (i=0; i < (long) GetImageListLength(fx_info->images); i++)
01198     fx_info->resample_filter[i]=DestroyResampleFilter(
01199       fx_info->resample_filter[i]);
01200   fx_info->resample_filter=(ResampleFilter **) RelinquishMagickMemory(
01201     fx_info->resample_filter);
01202   fx_info->random_info=DestroyRandomInfo(fx_info->random_info);
01203   fx_info=(FxInfo *) RelinquishMagickMemory(fx_info);
01204   return(fx_info);
01205 }
01206 
01207 /*
01208 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01209 %                                                                             %
01210 %                                                                             %
01211 %                                                                             %
01212 %     E v a l u a t e I m a g e                                               %
01213 %                                                                             %
01214 %                                                                             %
01215 %                                                                             %
01216 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01217 %
01218 %  EvaluateImage() applies a value to the image with an arithmetic, relational,
01219 %  or logical operator to an image. Use these operations to lighten or darken
01220 %  an image, to increase or decrease contrast in an image, or to produce the
01221 %  "negative" of an image.
01222 %
01223 %  The format of the EvaluateImageChannel method is:
01224 %
01225 %      MagickBooleanType EvaluateImage(Image *image,
01226 %        const MagickEvaluateOperator op,const double value,
01227 %        ExceptionInfo *exception)
01228 %      MagickBooleanType EvaluateImageChannel(Image *image,
01229 %        const ChannelType channel,const MagickEvaluateOperator op,
01230 %        const double value,ExceptionInfo *exception)
01231 %
01232 %  A description of each parameter follows:
01233 %
01234 %    o image: the image.
01235 %
01236 %    o channel: the channel.
01237 %
01238 %    o op: A channel op.
01239 %
01240 %    o value: A value value.
01241 %
01242 %    o exception: return any errors or warnings in this structure.
01243 %
01244 */
01245 
01246 static inline double MagickMax(const double x,const double y)
01247 {
01248   if (x > y)
01249     return(x);
01250   return(y);
01251 }
01252 
01253 static inline double MagickMin(const double x,const double y)
01254 {
01255   if (x < y)
01256     return(x);
01257   return(y);
01258 }
01259 
01260 static Quantum ApplyEvaluateOperator(RandomInfo *random_info,Quantum pixel,
01261   const MagickEvaluateOperator op,const MagickRealType value)
01262 {
01263   MagickRealType
01264     result;
01265 
01266   result=0.0;
01267   switch (op)
01268   {
01269     case UndefinedEvaluateOperator:
01270       break;
01271     case AddEvaluateOperator:
01272     {
01273       result=(MagickRealType) (pixel+value);
01274       break;
01275     }
01276     case AddModulusEvaluateOperator:
01277     {
01278       /* This will return a 'floored modulus' of the addition which will
01279        * always return a positive result, regardless of if the result is
01280        * positive or negative, and thus in the 0 to QuantumRange range.
01281        *
01282        * WARNING: this is NOT the same as a % or fmod() which returns a
01283        * 'truncated modulus' result, where floor() is replaced by trunc()
01284        * and could return a negative result, which will be clipped.
01285        */
01286       result = pixel+value;
01287       result -= (QuantumRange+1)*floor(result/(QuantumRange+1));
01288       break;
01289     }
01290     case AndEvaluateOperator:
01291     {
01292       result=(MagickRealType) ((unsigned long) pixel & (unsigned long)
01293         (value+0.5));
01294       break;
01295     }
01296     case CosineEvaluateOperator:
01297     {
01298       result=(MagickRealType) (QuantumRange*(0.5*cos((double) (2.0*MagickPI*
01299         QuantumScale*pixel*value))+0.5));
01300       break;
01301     }
01302     case DivideEvaluateOperator:
01303     {
01304       result=pixel/(value == 0.0 ? 1.0 : value);
01305       break;
01306     }
01307     case GaussianNoiseEvaluateOperator:
01308     {
01309       result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
01310         GaussianNoise,value);
01311       break;
01312     }
01313     case ImpulseNoiseEvaluateOperator:
01314     {
01315       result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
01316         ImpulseNoise,value);
01317       break;
01318     }
01319     case LaplacianNoiseEvaluateOperator:
01320     {
01321       result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
01322         LaplacianNoise,value);
01323       break;
01324     }
01325     case LeftShiftEvaluateOperator:
01326     {
01327       result=(MagickRealType) ((unsigned long) pixel << (unsigned long)
01328         (value+0.5));
01329       break;
01330     }
01331     case LogEvaluateOperator:
01332     {
01333       result=(MagickRealType) (QuantumRange*log((double) (QuantumScale*value*
01334         pixel+1.0))/log((double) (value+1.0)));
01335       break;
01336     }
01337     case MaxEvaluateOperator:
01338     {
01339       result=(MagickRealType) MagickMax((double) pixel,value);
01340       break;
01341     }
01342     case MinEvaluateOperator:
01343     {
01344       result=(MagickRealType) MagickMin((double) pixel,value);
01345       break;
01346     }
01347     case MultiplicativeNoiseEvaluateOperator:
01348     {
01349       result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
01350         MultiplicativeGaussianNoise,value);
01351       break;
01352     }
01353     case MultiplyEvaluateOperator:
01354     {
01355       result=(MagickRealType) (value*pixel);
01356       break;
01357     }
01358     case OrEvaluateOperator:
01359     {
01360       result=(MagickRealType) ((unsigned long) pixel | (unsigned long)
01361         (value+0.5));
01362       break;
01363     }
01364     case PoissonNoiseEvaluateOperator:
01365     {
01366       result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
01367         PoissonNoise,value);
01368       break;
01369     }
01370     case PowEvaluateOperator:
01371     {
01372       result=(MagickRealType) (QuantumRange*pow((double) (QuantumScale*pixel),
01373         (double) value));
01374       break;
01375     }
01376     case RightShiftEvaluateOperator:
01377     {
01378       result=(MagickRealType) ((unsigned long) pixel >> (unsigned long)
01379         (value+0.5));
01380       break;
01381     }
01382     case SetEvaluateOperator:
01383     {
01384       result=value;
01385       break;
01386     }
01387     case SineEvaluateOperator:
01388     {
01389       result=(MagickRealType) (QuantumRange*(0.5*sin((double) (2.0*MagickPI*
01390         QuantumScale*pixel*value))+0.5));
01391       break;
01392     }
01393     case SubtractEvaluateOperator:
01394     {
01395       result=(MagickRealType) (pixel-value);
01396       break;
01397     }
01398     case ThresholdEvaluateOperator:
01399     {
01400       result=(MagickRealType) (((MagickRealType) pixel <= value) ? 0 :
01401         QuantumRange);
01402       break;
01403     }
01404     case ThresholdBlackEvaluateOperator:
01405     {
01406       result=(MagickRealType) (((MagickRealType) pixel <= value) ? 0 : pixel);
01407       break;
01408     }
01409     case ThresholdWhiteEvaluateOperator:
01410     {
01411       result=(MagickRealType) (((MagickRealType) pixel > value) ? QuantumRange :
01412         pixel);
01413       break;
01414     }
01415     case UniformNoiseEvaluateOperator:
01416     {
01417       result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
01418         UniformNoise,value);
01419       break;
01420     }
01421     case XorEvaluateOperator:
01422     {
01423       result=(MagickRealType) ((unsigned long) pixel ^ (unsigned long)
01424         (value+0.5));
01425       break;
01426     }
01427   }
01428   return(RoundToQuantum(result));
01429 }
01430 
01431 MagickExport MagickBooleanType EvaluateImage(Image *image,
01432   const MagickEvaluateOperator op,const double value,ExceptionInfo *exception)
01433 {
01434   MagickBooleanType
01435     status;
01436 
01437   status=EvaluateImageChannel(image,AllChannels,op,value,exception);
01438   return(status);
01439 }
01440 
01441 MagickExport MagickBooleanType EvaluateImageChannel(Image *image,
01442   const ChannelType channel,const MagickEvaluateOperator op,const double value,
01443   ExceptionInfo *exception)
01444 {
01445 #define EvaluateImageTag  "Evaluate/Image "
01446 
01447   long
01448     progress,
01449     y;
01450 
01451   MagickBooleanType
01452     status;
01453 
01454   RandomInfo
01455     **random_info;
01456 
01457   CacheView
01458     *image_view;
01459 
01460   assert(image != (Image *) NULL);
01461   assert(image->signature == MagickSignature);
01462   if (image->debug != MagickFalse)
01463     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01464   assert(exception != (ExceptionInfo *) NULL);
01465   assert(exception->signature == MagickSignature);
01466   if (SetImageStorageClass(image,DirectClass) == MagickFalse)
01467     {
01468       InheritException(exception,&image->exception);
01469       return(MagickFalse);
01470     }
01471   status=MagickTrue;
01472   progress=0;
01473   random_info=AcquireRandomInfoThreadSet();
01474   image_view=AcquireCacheView(image);
01475 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01476   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
01477 #endif
01478   for (y=0; y < (long) image->rows; y++)
01479   {
01480     register IndexPacket
01481       *__restrict indexes;
01482 
01483     register long
01484       id,
01485       x;
01486 
01487     register PixelPacket
01488       *__restrict q;
01489 
01490     if (status == MagickFalse)
01491       continue;
01492     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
01493     if (q == (PixelPacket *) NULL)
01494       {
01495         status=MagickFalse;
01496         continue;
01497       }
01498     indexes=GetCacheViewAuthenticIndexQueue(image_view);
01499     id=GetOpenMPThreadId();
01500     for (x=0; x < (long) image->columns; x++)
01501     {
01502       if ((channel & RedChannel) != 0)
01503         q->red=ApplyEvaluateOperator(random_info[id],q->red,op,value);
01504       if ((channel & GreenChannel) != 0)
01505         q->green=ApplyEvaluateOperator(random_info[id],q->green,op,value);
01506       if ((channel & BlueChannel) != 0)
01507         q->blue=ApplyEvaluateOperator(random_info[id],q->blue,op,value);
01508       if ((channel & OpacityChannel) != 0)
01509         {
01510           if (image->matte == MagickFalse)
01511             q->opacity=ApplyEvaluateOperator(random_info[id],q->opacity,op,
01512               value);
01513           else
01514             q->opacity=(Quantum) QuantumRange-ApplyEvaluateOperator(
01515               random_info[id],(Quantum) (QuantumRange-q->opacity),op,value);
01516         }
01517       if (((channel & IndexChannel) != 0) && (indexes != (IndexPacket *) NULL))
01518         indexes[x]=(IndexPacket) ApplyEvaluateOperator(random_info[id],
01519           indexes[x],op,value);
01520       q++;
01521     }
01522     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
01523       status=MagickFalse;
01524     if (image->progress_monitor != (MagickProgressMonitor) NULL)
01525       {
01526         MagickBooleanType
01527           proceed;
01528 
01529 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01530   #pragma omp critical (MagickCore_EvaluateImageChannel)
01531 #endif
01532         proceed=SetImageProgress(image,EvaluateImageTag,progress++,image->rows);
01533         if (proceed == MagickFalse)
01534           status=MagickFalse;
01535       }
01536   }
01537   image_view=DestroyCacheView(image_view);
01538   random_info=DestroyRandomInfoThreadSet(random_info);
01539   return(status);
01540 }
01541 
01542 /*
01543 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01544 %                                                                             %
01545 %                                                                             %
01546 %                                                                             %
01547 %     F u n c t i o n I m a g e                                               %
01548 %                                                                             %
01549 %                                                                             %
01550 %                                                                             %
01551 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01552 %
01553 %  FunctionImage() applies a value to the image with an arithmetic, relational,
01554 %  or logical operator to an image. Use these operations to lighten or darken
01555 %  an image, to increase or decrease contrast in an image, or to produce the
01556 %  "negative" of an image.
01557 %
01558 %  The format of the FunctionImageChannel method is:
01559 %
01560 %      MagickBooleanType FunctionImage(Image *image,
01561 %        const MagickFunction function,const long number_parameters,
01562 %        const double *parameters,ExceptionInfo *exception)
01563 %      MagickBooleanType FunctionImageChannel(Image *image,
01564 %        const ChannelType channel,const MagickFunction function,
01565 %        const long number_parameters,const double *argument,
01566 %        ExceptionInfo *exception)
01567 %
01568 %  A description of each parameter follows:
01569 %
01570 %    o image: the image.
01571 %
01572 %    o channel: the channel.
01573 %
01574 %    o function: A channel function.
01575 %
01576 %    o parameters: one or more parameters.
01577 %
01578 %    o exception: return any errors or warnings in this structure.
01579 %
01580 */
01581 
01582 static Quantum ApplyFunction(Quantum pixel,const MagickFunction function,
01583   const unsigned long number_parameters,const double *parameters,
01584   ExceptionInfo *exception)
01585 {
01586   MagickRealType
01587     result;
01588 
01589   register long
01590     i;
01591 
01592   (void) exception;
01593   result=0.0;
01594   switch (function)
01595   {
01596     case PolynomialFunction:
01597     {
01598       /*
01599        * Polynomial
01600        * Parameters:   polynomial constants,  highest to lowest order
01601        *   For example:      c0*x^3 + c1*x^2 + c2*x  + c3
01602        */
01603       result=0.0;
01604       for (i=0; i < (long) number_parameters; i++)
01605         result = result*QuantumScale*pixel + parameters[i];
01606       result *= QuantumRange;
01607       break;
01608     }
01609     case SinusoidFunction:
01610     {
01611       /* Sinusoid Function
01612        * Parameters:   Freq, Phase, Ampl, bias
01613        */
01614       double  freq,phase,ampl,bias;
01615       freq  = ( number_parameters >= 1 ) ? parameters[0] : 1.0;
01616       phase = ( number_parameters >= 2 ) ? parameters[1] : 0.0;
01617       ampl  = ( number_parameters >= 3 ) ? parameters[2] : 0.5;
01618       bias  = ( number_parameters >= 4 ) ? parameters[3] : 0.5;
01619       result=(MagickRealType) (QuantumRange*(ampl*sin((double) (2.0*MagickPI*
01620         (freq*QuantumScale*pixel + phase/360.0) )) + bias ) );
01621       break;
01622     }
01623     case ArcsinFunction:
01624     {
01625       /* Arcsin Function  (peged at range limits for invalid results)
01626        * Parameters:   Width, Center, Range, Bias
01627        */
01628       double  width,range,center,bias;
01629       width  = ( number_parameters >= 1 ) ? parameters[0] : 1.0;
01630       center = ( number_parameters >= 2 ) ? parameters[1] : 0.5;
01631       range  = ( number_parameters >= 3 ) ? parameters[2] : 1.0;
01632       bias   = ( number_parameters >= 4 ) ? parameters[3] : 0.5;
01633       result = 2.0/width*(QuantumScale*pixel - center);
01634       if ( result <= -1.0 )
01635         result = bias - range/2.0;
01636       else if ( result >= 1.0 )
01637         result = bias + range/2.0;
01638       else
01639         result=range/MagickPI*asin((double)result) + bias;
01640       result *= QuantumRange;
01641       break;
01642     }
01643     case ArctanFunction:
01644     {
01645       /* Arctan Function
01646        * Parameters:   Slope, Center, Range, Bias
01647        */
01648       double  slope,range,center,bias;
01649       slope  = ( number_parameters >= 1 ) ? parameters[0] : 1.0;
01650       center = ( number_parameters >= 2 ) ? parameters[1] : 0.5;
01651       range  = ( number_parameters >= 3 ) ? parameters[2] : 1.0;
01652       bias   = ( number_parameters >= 4 ) ? parameters[3] : 0.5;
01653       result = MagickPI*slope*(QuantumScale*pixel - center);
01654       result=(MagickRealType) (QuantumRange*(range/MagickPI*atan((double)
01655                   result) + bias ) );
01656       break;
01657     }
01658     case UndefinedFunction:
01659       break;
01660   }
01661   return(RoundToQuantum(result));
01662 }
01663 
01664 MagickExport MagickBooleanType FunctionImage(Image *image,
01665   const MagickFunction function,const unsigned long number_parameters,
01666   const double *parameters,ExceptionInfo *exception)
01667 {
01668   MagickBooleanType
01669     status;
01670 
01671   status=FunctionImageChannel(image,AllChannels,function,number_parameters,
01672     parameters,exception);
01673   return(status);
01674 }
01675 
01676 MagickExport MagickBooleanType FunctionImageChannel(Image *image,
01677   const ChannelType channel,const MagickFunction function,
01678   const unsigned long number_parameters,const double *parameters,
01679   ExceptionInfo *exception)
01680 {
01681 #define FunctionImageTag  "Function/Image "
01682 
01683   long
01684     progress,
01685     y;
01686 
01687   MagickBooleanType
01688     status;
01689 
01690   CacheView
01691     *image_view;
01692 
01693   assert(image != (Image *) NULL);
01694   assert(image->signature == MagickSignature);
01695   if (image->debug != MagickFalse)
01696     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01697   assert(exception != (ExceptionInfo *) NULL);
01698   assert(exception->signature == MagickSignature);
01699   if (SetImageStorageClass(image,DirectClass) == MagickFalse)
01700     {
01701       InheritException(exception,&image->exception);
01702       return(MagickFalse);
01703     }
01704   status=MagickTrue;
01705   progress=0;
01706   image_view=AcquireCacheView(image);
01707 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01708   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
01709 #endif
01710   for (y=0; y < (long) image->rows; y++)
01711   {
01712     register IndexPacket
01713       *__restrict indexes;
01714 
01715     register long
01716       x;
01717 
01718     register PixelPacket
01719       *__restrict q;
01720 
01721     if (status == MagickFalse)
01722       continue;
01723     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
01724     if (q == (PixelPacket *) NULL)
01725       {
01726         status=MagickFalse;
01727         continue;
01728       }
01729     indexes=GetCacheViewAuthenticIndexQueue(image_view);
01730     for (x=0; x < (long) image->columns; x++)
01731     {
01732       if ((channel & RedChannel) != 0)
01733         q->red=ApplyFunction(q->red,function,number_parameters,parameters,
01734           exception);
01735       if ((channel & GreenChannel) != 0)
01736         q->green=ApplyFunction(q->green,function,number_parameters,parameters,
01737           exception);
01738       if ((channel & BlueChannel) != 0)
01739         q->blue=ApplyFunction(q->blue,function,number_parameters,parameters,
01740           exception);
01741       if ((channel & OpacityChannel) != 0)
01742         {
01743           if (image->matte == MagickFalse)
01744             q->opacity=ApplyFunction(q->opacity,function,number_parameters,
01745               parameters,exception);
01746           else
01747             q->opacity=(Quantum) QuantumRange-ApplyFunction((Quantum) (
01748               QuantumRange-q->opacity),function,number_parameters,parameters,
01749               exception);
01750         }
01751       if (((channel & IndexChannel) != 0) && (indexes != (IndexPacket *) NULL))
01752         indexes[x]=(IndexPacket) ApplyFunction(indexes[x],function,
01753           number_parameters,parameters,exception);
01754       q++;
01755     }
01756     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
01757       status=MagickFalse;
01758     if (image->progress_monitor != (MagickProgressMonitor) NULL)
01759       {
01760         MagickBooleanType
01761           proceed;
01762 
01763 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01764   #pragma omp critical (MagickCore_FunctionImageChannel)
01765 #endif
01766         proceed=SetImageProgress(image,FunctionImageTag,progress++,image->rows);
01767         if (proceed == MagickFalse)
01768           status=MagickFalse;
01769       }
01770   }
01771   image_view=DestroyCacheView(image_view);
01772   return(status);
01773 }
01774 
01775 /*
01776 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01777 %                                                                             %
01778 %                                                                             %
01779 %                                                                             %
01780 +     F x E v a l u a t e C h a n n e l E x p r e s s i o n                   %
01781 %                                                                             %
01782 %                                                                             %
01783 %                                                                             %
01784 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01785 %
01786 %  FxEvaluateChannelExpression() evaluates an expression and returns the
01787 %  results.
01788 %
01789 %  The format of the FxEvaluateExpression method is:
01790 %
01791 %      MagickRealType FxEvaluateChannelExpression(FxInfo *fx_info,
01792 %        const ChannelType channel,const long x,const long y,
01793 %        MagickRealType *alpha,Exceptioninfo *exception)
01794 %      MagickRealType FxEvaluateExpression(FxInfo *fx_info,
01795 %        MagickRealType *alpha,Exceptioninfo *exception)
01796 %
01797 %  A description of each parameter follows:
01798 %
01799 %    o fx_info: the fx info.
01800 %
01801 %    o channel: the channel.
01802 %
01803 %    o x,y: the pixel position.
01804 %
01805 %    o alpha: the result.
01806 %
01807 %    o exception: return any errors or warnings in this structure.
01808 %
01809 */
01810 
01811 static MagickRealType FxChannelStatistics(FxInfo *fx_info,const Image *image,
01812   ChannelType channel,const char *symbol,ExceptionInfo *exception)
01813 {
01814   char
01815     key[MaxTextExtent],
01816     statistic[MaxTextExtent];
01817 
01818   const char
01819     *value;
01820 
01821   register const char
01822     *p;
01823 
01824   for (p=symbol; (*p != '.') && (*p != '\0'); p++) ;
01825   if (*p == '.')
01826     switch (*++p)  /* e.g. depth.r */
01827     {
01828       case 'r': channel=RedChannel; break;
01829       case 'g': channel=GreenChannel; break;
01830       case 'b': channel=BlueChannel; break;
01831       case 'c': channel=CyanChannel; break;
01832       case 'm': channel=MagentaChannel; break;
01833       case 'y': channel=YellowChannel; break;
01834       case 'k': channel=BlackChannel; break;
01835       default: break;
01836     }
01837   (void) FormatMagickString(key,MaxTextExtent,"%p.%ld.%s",image,(long) channel,
01838     symbol);
01839   value=(const char *) GetValueFromSplayTree(fx_info->symbols,key);
01840   if (value != (const char *) NULL)
01841     return(QuantumScale*atof(value));
01842   (void) DeleteNodeFromSplayTree(fx_info->symbols,key);
01843   if (LocaleNCompare(symbol,"depth",5) == 0)
01844     {
01845       unsigned long
01846         depth;
01847 
01848       depth=GetImageChannelDepth(image,channel,exception);
01849       (void) FormatMagickString(statistic,MaxTextExtent,"%lu",depth);
01850     }
01851   if (LocaleNCompare(symbol,"kurtosis",8) == 0)
01852     {
01853       double
01854         kurtosis,
01855         skewness;
01856 
01857       (void) GetImageChannelKurtosis(image,channel,&kurtosis,&skewness,
01858         exception);
01859       (void) FormatMagickString(statistic,MaxTextExtent,"%g",kurtosis);
01860     }
01861   if (LocaleNCompare(symbol,"maxima",6) == 0)
01862     {
01863       double
01864         maxima,
01865         minima;
01866 
01867       (void) GetImageChannelRange(image,channel,&minima,&maxima,exception);
01868       (void) FormatMagickString(statistic,MaxTextExtent,"%g",maxima);
01869     }
01870   if (LocaleNCompare(symbol,"mean",4) == 0)
01871     {
01872       double
01873         mean,
01874         standard_deviation;
01875 
01876       (void) GetImageChannelMean(image,channel,&mean,&standard_deviation,
01877         exception);
01878       (void) FormatMagickString(statistic,MaxTextExtent,"%g",mean);
01879     }
01880   if (LocaleNCompare(symbol,"minima",6) == 0)
01881     {
01882       double
01883         maxima,
01884         minima;
01885 
01886       (void) GetImageChannelRange(image,channel,&minima,&maxima,exception);
01887       (void) FormatMagickString(statistic,MaxTextExtent,"%g",minima);
01888     }
01889   if (LocaleNCompare(symbol,"skewness",8) == 0)
01890     {
01891       double
01892         kurtosis,
01893         skewness;
01894 
01895       (void) GetImageChannelKurtosis(image,channel,&kurtosis,&skewness,
01896         exception);
01897       (void) FormatMagickString(statistic,MaxTextExtent,"%g",skewness);
01898     }
01899   if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
01900     {
01901       double
01902         mean,
01903         standard_deviation;
01904 
01905       (void) GetImageChannelMean(image,channel,&mean,&standard_deviation,
01906         exception);
01907       (void) FormatMagickString(statistic,MaxTextExtent,"%g",
01908         standard_deviation);
01909     }
01910   (void) AddValueToSplayTree(fx_info->symbols,ConstantString(key),
01911     ConstantString(statistic));
01912   return(QuantumScale*atof(statistic));
01913 }
01914 
01915 static MagickRealType
01916   FxEvaluateSubexpression(FxInfo *,const ChannelType,const long,const long,
01917     const char *,MagickRealType *,ExceptionInfo *);
01918 
01919 static inline MagickRealType FxMax(FxInfo *fx_info,const ChannelType channel,
01920   const long x,const long y,const char *expression,ExceptionInfo *exception)
01921 {
01922   MagickRealType
01923     alpha,
01924     beta;
01925 
01926   alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression,&beta,exception);
01927   return((MagickRealType) MagickMax((double) alpha,(double) beta));
01928 }
01929 
01930 static inline MagickRealType FxMin(FxInfo *fx_info,ChannelType channel,
01931   const long x,const long y,const char *expression,ExceptionInfo *exception)
01932 {
01933   MagickRealType
01934     alpha,
01935     beta;
01936 
01937   alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression,&beta,exception);
01938   return((MagickRealType) MagickMin((double) alpha,(double) beta));
01939 }
01940 
01941 static inline const char *FxSubexpression(const char *expression,
01942   ExceptionInfo *exception)
01943 {
01944   const char
01945     *subexpression;
01946 
01947   register long
01948     level;
01949 
01950   level=0;
01951   subexpression=expression;
01952   while ((*subexpression != '\0') &&
01953          ((level != 1) || (strchr(")",(int) *subexpression) == (char *) NULL)))
01954   {
01955     if (strchr("(",(int) *subexpression) != (char *) NULL)
01956       level++;
01957     else
01958       if (strchr(")",(int) *subexpression) != (char *) NULL)
01959         level--;
01960     subexpression++;
01961   }
01962   if (*subexpression == '\0')
01963     (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
01964       "UnbalancedParenthesis","`%s'",expression);
01965   return(subexpression);
01966 }
01967 
01968 static MagickRealType FxGetSymbol(FxInfo *fx_info,const ChannelType channel,
01969   const long x,const long y,const char *expression,ExceptionInfo *exception)
01970 {
01971   char
01972     *q,
01973     subexpression[MaxTextExtent],
01974     symbol[MaxTextExtent];
01975 
01976   const char
01977     *p,
01978     *value;
01979 
01980   Image
01981     *image;
01982 
01983   MagickPixelPacket
01984     pixel;
01985 
01986   MagickRealType
01987     alpha,
01988     beta;
01989 
01990   PointInfo
01991     point;
01992 
01993   register long
01994     i;
01995 
01996   size_t
01997     length;
01998 
01999   unsigned long
02000     level;
02001 
02002   p=expression;
02003   i=GetImageIndexInList(fx_info->images);
02004   level=0;
02005   point.x=(double) x;
02006   point.y=(double) y;
02007   if (isalpha((int) *(p+1)) == 0)
02008     {
02009       if (strchr("suv",(int) *p) != (char *) NULL)
02010         {
02011           switch (*p)
02012           {
02013             case 's':
02014             default:
02015             {
02016               i=GetImageIndexInList(fx_info->images);
02017               break;
02018             }
02019             case 'u': i=0; break;
02020             case 'v': i=1; break;
02021           }
02022           p++;
02023           if (*p == '[')
02024             {
02025               level++;
02026               q=subexpression;
02027               for (p++; *p != '\0'; )
02028               {
02029                 if (*p == '[')
02030                   level++;
02031                 else
02032                   if (*p == ']')
02033                     {
02034                       level--;
02035                       if (level == 0)
02036                         break;
02037                     }
02038                 *q++=(*p++);
02039               }
02040               *q='\0';
02041               alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
02042                 &beta,exception);
02043               i=(long) (alpha+0.5);
02044               p++;
02045             }
02046           if (*p == '.')
02047             p++;
02048         }
02049       if ((isalpha((int) *(p+1)) == 0) && (*p == 'p'))
02050         {
02051           p++;
02052           if (*p == '{')
02053             {
02054               level++;
02055               q=subexpression;
02056               for (p++; *p != '\0'; )
02057               {
02058                 if (*p == '{')
02059                   level++;
02060                 else
02061                   if (*p == '}')
02062                     {
02063                       level--;
02064                       if (level == 0)
02065                         break;
02066                     }
02067                 *q++=(*p++);
02068               }
02069               *q='\0';
02070               alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
02071                 &beta,exception);
02072               point.x=alpha;
02073               point.y=beta;
02074               p++;
02075             }
02076           else
02077             if (*p == '[')
02078               {
02079                 level++;
02080                 q=subexpression;
02081                 for (p++; *p != '\0'; )
02082                 {
02083                   if (*p == '[')
02084                     level++;
02085                   else
02086                     if (*p == ']')
02087                       {
02088                         level--;
02089                         if (level == 0)
02090                           break;
02091                       }
02092                   *q++=(*p++);
02093                 }
02094                 *q='\0';
02095                 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
02096                   &beta,exception);
02097                 point.x+=alpha;
02098                 point.y+=beta;
02099                 p++;
02100               }
02101           if (*p == '.')
02102             p++;
02103         }
02104     }
02105   length=GetImageListLength(fx_info->images);
02106   while (i < 0)
02107     i+=(long) length;
02108   i%=length;
02109   image=GetImageFromList(fx_info->images,i);
02110   if (image == (Image *) NULL)
02111     {
02112       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
02113         "NoSuchImage","`%s'",expression);
02114       return(0.0);
02115     }
02116   (void) ResamplePixelColor(fx_info->resample_filter[i],point.x,point.y,&pixel);
02117   if ((strlen(p) > 2) &&
02118       (LocaleCompare(p,"intensity") != 0) &&
02119       (LocaleCompare(p,"luminance") != 0) &&
02120       (LocaleCompare(p,"hue") != 0) &&
02121       (LocaleCompare(p,"saturation") != 0) &&
02122       (LocaleCompare(p,"lightness") != 0))
02123     {
02124       char
02125         name[MaxTextExtent];
02126 
02127       (void) CopyMagickString(name,p,MaxTextExtent);
02128       for (q=name+(strlen(name)-1); q > name; q--)
02129       {
02130         if (*q == ')')
02131           break;
02132         if (*q == '.')
02133           {
02134             *q='\0';
02135             break;
02136           }
02137       }
02138       if ((strlen(name) > 2) &&
02139           (GetValueFromSplayTree(fx_info->symbols,name) == (const char *) NULL))
02140         {
02141           MagickPixelPacket
02142             *color;
02143 
02144           color=(MagickPixelPacket *) GetValueFromSplayTree(fx_info->colors,
02145             name);
02146           if (color != (MagickPixelPacket *) NULL)
02147             {
02148               pixel=(*color);
02149               p+=strlen(name);
02150             }
02151           else
02152             if (QueryMagickColor(name,&pixel,fx_info->exception) != MagickFalse)
02153               {
02154                 (void) AddValueToSplayTree(fx_info->colors,ConstantString(name),
02155                   CloneMagickPixelPacket(&pixel));
02156                 p+=strlen(name);
02157               }
02158         }
02159     }
02160   (void) CopyMagickString(symbol,p,MaxTextExtent);
02161   StripString(symbol);
02162   if (*symbol == '\0')
02163     {
02164       switch (channel)
02165       {
02166         case RedChannel: return(QuantumScale*pixel.red);
02167         case GreenChannel: return(QuantumScale*pixel.green);
02168         case BlueChannel: return(QuantumScale*pixel.blue);
02169         case OpacityChannel:
02170         {
02171           if (pixel.matte == MagickFalse)
02172             {
02173               fx_info->matte=MagickFalse;
02174               return(1.0);
02175             }
02176           return((MagickRealType) (QuantumScale*(QuantumRange-pixel.opacity)));
02177         }
02178         case IndexChannel:
02179         {
02180           if (image->colorspace != CMYKColorspace)
02181             {
02182               (void) ThrowMagickException(exception,GetMagickModule(),
02183                 OptionError,"ColorSeparatedImageRequired","`%s'",
02184                 image->filename);
02185               return(0.0);
02186             }
02187           return(QuantumScale*pixel.index);
02188         }
02189         default:
02190           break;
02191       }
02192       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
02193         "UnableToParseExpression","`%s'",p);
02194       return(0.0);
02195     }
02196   switch (*symbol)
02197   {
02198     case 'A':
02199     case 'a':
02200     {
02201       if (LocaleCompare(symbol,"a") == 0)
02202         return((MagickRealType) (QuantumScale*(QuantumRange-pixel.opacity)));
02203       break;
02204     }
02205     case 'B':
02206     case 'b':
02207     {
02208       if (LocaleCompare(symbol,"b") == 0)
02209         return(QuantumScale*pixel.blue);
02210       break;
02211     }
02212     case 'C':
02213     case 'c':
02214     {
02215       if (LocaleNCompare(symbol,"channel",7) == 0)
02216         {
02217           GeometryInfo
02218             channel_info;
02219 
02220           MagickStatusType
02221             flags;
02222 
02223           flags=ParseGeometry(symbol+7,&channel_info);
02224           if (image->colorspace == CMYKColorspace)
02225             switch (channel)
02226             {
02227               case CyanChannel:
02228               {
02229                 if ((flags & RhoValue) == 0)
02230                   return(0.0);
02231                 return(channel_info.rho);
02232               }
02233               case MagentaChannel:
02234               {
02235                 if ((flags & SigmaValue) == 0)
02236                   return(0.0);
02237                 return(channel_info.sigma);
02238               }
02239               case YellowChannel:
02240               {
02241                 if ((flags & XiValue) == 0)
02242                   return(0.0);
02243                 return(channel_info.xi);
02244               }
02245               case BlackChannel:
02246               {
02247                 if ((flags & PsiValue) == 0)
02248                   return(0.0);
02249                 return(channel_info.psi);
02250               }
02251               case OpacityChannel:
02252               {
02253                 if ((flags & ChiValue) == 0)
02254                   return(0.0);
02255                 return(channel_info.chi);
02256               }
02257               default:
02258                 return(0.0);
02259             }
02260           switch (channel)
02261           {
02262             case RedChannel:
02263             {
02264               if ((flags & RhoValue) == 0)
02265                 return(0.0);
02266               return(channel_info.rho);
02267             }
02268             case GreenChannel:
02269             {
02270               if ((flags & SigmaValue) == 0)
02271                 return(0.0);
02272               return(channel_info.sigma);
02273             }
02274             case BlueChannel:
02275             {
02276               if ((flags & XiValue) == 0)
02277                 return(0.0);
02278               return(channel_info.xi);
02279             }
02280             case OpacityChannel:
02281             {
02282               if ((flags & PsiValue) == 0)
02283                 return(0.0);
02284               return(channel_info.psi);
02285             }
02286             case IndexChannel:
02287             {
02288               if ((flags & ChiValue) == 0)
02289                 return(0.0);
02290               return(channel_info.chi);
02291             }
02292             default:
02293               return(0.0);
02294           }
02295           return(0.0);
02296         }
02297       if (LocaleCompare(symbol,"c") == 0)
02298         return(QuantumScale*pixel.red);
02299       break;
02300     }
02301     case 'D':
02302     case 'd':
02303     {
02304       if (LocaleNCompare(symbol,"depth",5) == 0)
02305         return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
02306       break;
02307     }
02308     case 'G':
02309     case 'g':
02310     {
02311       if (LocaleCompare(symbol,"g") == 0)
02312         return(QuantumScale*pixel.green);
02313       break;
02314     }
02315     case 'K':
02316     case 'k':
02317     {
02318       if (LocaleNCompare(symbol,"kurtosis",8) == 0)
02319         return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
02320       if (LocaleCompare(symbol,"k") == 0)
02321         {
02322           if (image->colorspace != CMYKColorspace)
02323             {
02324               (void) ThrowMagickException(exception,GetMagickModule(),
02325                 OptionError,"ColorSeparatedImageRequired","`%s'",
02326                 image->filename);
02327               return(0.0);
02328             }
02329           return(QuantumScale*pixel.index);
02330         }
02331       break;
02332     }
02333     case 'H':
02334     case 'h':
02335     {
02336       if (LocaleCompare(symbol,"h") == 0)
02337         return((MagickRealType) image->rows);
02338       if (LocaleCompare(symbol,"hue") == 0)
02339         {
02340           double
02341             hue,
02342             lightness,
02343             saturation;
02344 
02345           ConvertRGBToHSL(RoundToQuantum(pixel.red),RoundToQuantum(pixel.green),
02346             RoundToQuantum(pixel.blue),&hue,&saturation,&lightness);
02347           return(hue);
02348         }
02349       break;
02350     }
02351     case 'I':
02352     case 'i':
02353     {
02354       if ((LocaleCompare(symbol,"image.depth") == 0) ||
02355           (LocaleCompare(symbol,"image.minima") == 0) ||
02356           (LocaleCompare(symbol,"image.maxima") == 0) ||
02357           (LocaleCompare(symbol,"image.mean") == 0) ||
02358           (LocaleCompare(symbol,"image.kurtosis") == 0) ||
02359           (LocaleCompare(symbol,"image.skewness") == 0) ||
02360           (LocaleCompare(symbol,"image.standard_deviation") == 0))
02361         return(FxChannelStatistics(fx_info,image,channel,symbol+6,exception));
02362       if (LocaleCompare(symbol,"image.resolution.x") == 0)
02363         return(image->x_resolution);
02364       if (LocaleCompare(symbol,"image.resolution.y") == 0)
02365         return(image->y_resolution);
02366       if (LocaleCompare(symbol,"intensity") == 0)
02367         return(QuantumScale*MagickPixelIntensityToQuantum(&pixel));
02368       if (LocaleCompare(symbol,"i") == 0)
02369         return((MagickRealType) x);
02370       break;
02371     }
02372     case 'J':
02373     case 'j':
02374     {
02375       if (LocaleCompare(symbol,"j") == 0)
02376         return((MagickRealType) y);
02377       break;
02378     }
02379     case 'L':
02380     case 'l':
02381     {
02382       if (LocaleCompare(symbol,"lightness") == 0)
02383         {
02384           double
02385             hue,
02386             lightness,
02387             saturation;
02388 
02389           ConvertRGBToHSL(RoundToQuantum(pixel.red),RoundToQuantum(pixel.green),
02390             RoundToQuantum(pixel.blue),&hue,&saturation,&lightness);
02391           return(lightness);
02392         }
02393       if (LocaleCompare(symbol,"luminance") == 0)
02394         {
02395           double
02396             luminence;
02397 
02398           luminence=0.2126*pixel.red+0.7152*pixel.green+0.0722*pixel.blue;
02399           return(QuantumScale*luminence);
02400         }
02401       break;
02402     }
02403     case 'M':
02404     case 'm':
02405     {
02406       if (LocaleNCompare(symbol,"maxima",6) == 0)
02407         return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
02408       if (LocaleNCompare(symbol,"mean",4) == 0)
02409         return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
02410       if (LocaleNCompare(symbol,"minima",6) == 0)
02411         return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
02412       if (LocaleCompare(symbol,"m") == 0)
02413         return(QuantumScale*pixel.blue);
02414       break;
02415     }
02416     case 'N':
02417     case 'n':
02418     {
02419       if (LocaleCompare(symbol,"n") == 0)
02420         return((MagickRealType) GetImageListLength(fx_info->images));
02421       break;
02422     }
02423     case 'O':
02424     case 'o':
02425     {
02426       if (LocaleCompare(symbol,"o") == 0)
02427         return(QuantumScale*pixel.opacity);
02428       break;
02429     }
02430     case 'P':
02431     case 'p':
02432     {
02433       if (LocaleCompare(symbol,"page.height") == 0)
02434         return((MagickRealType) image->page.height);
02435       if (LocaleCompare(symbol,"page.width") == 0)
02436         return((MagickRealType) image->page.width);
02437       if (LocaleCompare(symbol,"page.x") == 0)
02438         return((MagickRealType) image->page.x);
02439       if (LocaleCompare(symbol,"page.y") == 0)
02440         return((MagickRealType) image->page.y);
02441       break;
02442     }
02443     case 'R':
02444     case 'r':
02445     {
02446       if (LocaleCompare(symbol,"resolution.x") == 0)
02447         return(image->x_resolution);
02448       if (LocaleCompare(symbol,"resolution.y") == 0)
02449         return(image->y_resolution);
02450       if (LocaleCompare(symbol,"r") == 0)
02451         return(QuantumScale*pixel.red);
02452       break;
02453     }
02454     case 'S':
02455     case 's':
02456     {
02457       if (LocaleCompare(symbol,"saturation") == 0)
02458         {
02459           double
02460             hue,
02461             lightness,
02462             saturation;
02463 
02464           ConvertRGBToHSL(RoundToQuantum(pixel.red),RoundToQuantum(pixel.green),
02465             RoundToQuantum(pixel.blue),&hue,&saturation,&lightness);
02466           return(saturation);
02467         }
02468       if (LocaleNCompare(symbol,"skewness",8) == 0)
02469         return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
02470       if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
02471         return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
02472       break;
02473     }
02474     case 'T':
02475     case 't':
02476     {
02477       if (LocaleCompare(symbol,"t") == 0)
02478         return((MagickRealType) fx_info->images->scene);
02479       break;
02480     }
02481     case 'W':
02482     case 'w':
02483     {
02484       if (LocaleCompare(symbol,"w") == 0)
02485         return((MagickRealType) image->columns);
02486       break;
02487     }
02488     case 'Y':
02489     case 'y':
02490     {
02491       if (LocaleCompare(symbol,"y") == 0)
02492         return(QuantumScale*pixel.green);
02493       break;
02494     }
02495     case 'Z':
02496     case 'z':
02497     {
02498       if (LocaleCompare(symbol,"z") == 0)
02499         {
02500           MagickRealType
02501             depth;
02502 
02503           depth=(MagickRealType) GetImageChannelDepth(image,channel,
02504             fx_info->exception);
02505           return(depth);
02506         }
02507       break;
02508     }
02509     default:
02510       break;
02511   }
02512   value=(const char *) GetValueFromSplayTree(fx_info->symbols,symbol);
02513   if (value != (const char *) NULL)
02514     return((MagickRealType) atof(value));
02515   (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
02516     "UnableToParseExpression","`%s'",symbol);
02517   return(0.0);
02518 }
02519 
02520 static const char *FxOperatorPrecedence(const char *expression,
02521   ExceptionInfo *exception)
02522 {
02523   typedef enum
02524   {
02525     UndefinedPrecedence,
02526     NullPrecedence,
02527     BitwiseComplementPrecedence,
02528     ExponentPrecedence,
02529     MultiplyPrecedence,
02530     AdditionPrecedence,
02531     ShiftPrecedence,
02532     RelationalPrecedence,
02533     EquivalencyPrecedence,
02534     BitwiseAndPrecedence,
02535     BitwiseOrPrecedence,
02536     LogicalAndPrecedence,
02537     LogicalOrPrecedence,
02538     TernaryPrecedence,
02539     AssignmentPrecedence,
02540     CommaPrecedence,
02541     SeparatorPrecedence
02542   } FxPrecedence;
02543 
02544   FxPrecedence
02545     precedence,
02546     target;
02547 
02548   register const char
02549     *subexpression;
02550 
02551   register int
02552     c;
02553 
02554   unsigned long
02555     level;
02556 
02557   c=0;
02558   level=0;
02559   subexpression=(const char *) NULL;
02560   target=NullPrecedence;
02561   while (*expression != '\0')
02562   {
02563     precedence=UndefinedPrecedence;
02564     if ((isspace((int) ((char) *expression)) != 0) || (c == (int) '@'))
02565       {
02566         expression++;
02567         continue;
02568       }
02569     if (LocaleNCompare(expression,"atan2",5) == 0)
02570       {
02571         expression+=5;
02572         continue;
02573       }
02574     if ((c == (int) '{') || (c == (int) '['))
02575       level++;
02576     else
02577       if ((c == (int) '}') || (c == (int) ']'))
02578         level--;
02579     if (level == 0)
02580       switch ((unsigned char) *expression)
02581       {
02582         case '~':
02583         case '!':
02584         {
02585           precedence=BitwiseComplementPrecedence;
02586           break;
02587         }
02588         case '^':
02589         {
02590           precedence=ExponentPrecedence;
02591           break;
02592         }
02593         default:
02594         {
02595           if (((c != 0) && ((isdigit((int) ((char) c)) != 0) ||
02596                (strchr(")",c) != (char *) NULL))) &&
02597               (((islower((int) ((char) *expression)) != 0) ||
02598                (strchr("(",(int) *expression) != (char *) NULL)) ||
02599                ((isdigit((int) ((char) c)) == 0) &&
02600                 (isdigit((int) ((char) *expression)) != 0))) &&
02601               (strchr("xy",(int) *expression) == (char *) NULL))
02602             precedence=MultiplyPrecedence;
02603           break;
02604         }
02605         case '*':
02606         case '/':
02607         case '%':
02608         {
02609           precedence=MultiplyPrecedence;
02610           break;
02611         }
02612         case '+':
02613         case '-':
02614         {
02615           if ((strchr("(+-/*%:&^|<>~,",c) == (char *) NULL) ||
02616               (isalpha(c) != 0))
02617             precedence=AdditionPrecedence;
02618           break;
02619         }
02620         case LeftShiftOperator:
02621         case RightShiftOperator:
02622         {
02623           precedence=ShiftPrecedence;
02624           break;
02625         }
02626         case '<':
02627         case LessThanEqualOperator:
02628         case GreaterThanEqualOperator:
02629         case '>':
02630         {
02631           precedence=RelationalPrecedence;
02632           break;
02633         }
02634         case EqualOperator:
02635         case NotEqualOperator:
02636         {
02637           precedence=EquivalencyPrecedence;
02638           break;
02639         }
02640         case '&':
02641         {
02642           precedence=BitwiseAndPrecedence;
02643           break;
02644         }
02645         case '|':
02646         {
02647           precedence=BitwiseOrPrecedence;
02648           break;
02649         }
02650         case LogicalAndOperator:
02651         {
02652           precedence=LogicalAndPrecedence;
02653           break;
02654         }
02655         case LogicalOrOperator:
02656         {
02657           precedence=LogicalOrPrecedence;
02658           break;
02659         }
02660         case ':':
02661         case '?':
02662         {
02663           precedence=TernaryPrecedence;
02664           break;
02665         }
02666         case '=':
02667         {
02668           precedence=AssignmentPrecedence;
02669           break;
02670         }
02671         case ',':
02672         {
02673           precedence=CommaPrecedence;
02674           break;
02675         }
02676         case ';':
02677         {
02678           precedence=SeparatorPrecedence;
02679           break;
02680         }
02681       }
02682     if ((precedence == BitwiseComplementPrecedence) ||
02683         (precedence == TernaryPrecedence) ||
02684         (precedence == AssignmentPrecedence))
02685       {
02686         if (precedence > target)
02687           {
02688             /*
02689               Right-to-left associativity.
02690             */
02691             target=precedence;
02692             subexpression=expression;
02693           }
02694       }
02695     else
02696       if (precedence >= target)
02697         {
02698           /*
02699             Left-to-right associativity.
02700           */
02701           target=precedence;
02702           subexpression=expression;
02703         }
02704     if (strchr("(",(int) *expression) != (char *) NULL)
02705       expression=FxSubexpression(expression,exception);
02706     c=(int) (*expression++);
02707   }
02708   return(subexpression);
02709 }
02710 
02711 static MagickRealType FxEvaluateSubexpression(FxInfo *fx_info,
02712   const ChannelType channel,const long x,const long y,const char *expression,
02713   MagickRealType *beta,ExceptionInfo *exception)
02714 {
02715   char
02716     *q,
02717     subexpression[MaxTextExtent];
02718 
02719   MagickRealType
02720     alpha,
02721     gamma;
02722 
02723   register const char
02724     *p;
02725 
02726   *beta=0.0;
02727   if (exception->severity != UndefinedException)
02728     return(0.0);
02729   while (isspace((int) *expression) != 0)
02730     expression++;
02731   if (*expression == '\0')
02732     {
02733       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
02734         "MissingExpression","`%s'",expression);
02735       return(0.0);
02736     }
02737   p=FxOperatorPrecedence(expression,exception);
02738   if (p != (const char *) NULL)
02739     {
02740       (void) CopyMagickString(subexpression,expression,(size_t)
02741         (p-expression+1));
02742       alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
02743         exception);
02744       switch ((unsigned char) *p)
02745       {
02746         case '~':
02747         {
02748           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
02749           *beta=(MagickRealType) (~(unsigned long) *beta);
02750           return(*beta);
02751         }
02752         case '!':
02753         {
02754           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
02755           return(*beta == 0.0 ? 1.0 : 0.0);
02756         }
02757         case '^':
02758         {
02759           *beta=pow((double) alpha,(double) FxEvaluateSubexpression(fx_info,
02760             channel,x,y,++p,beta,exception));
02761           return(*beta);
02762         }
02763         case '*':
02764         {
02765           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
02766           return(alpha*(*beta));
02767         }
02768         case '/':
02769         {
02770           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
02771           if (*beta == 0.0)
02772             {
02773               if (exception->severity == UndefinedException)
02774                 (void) ThrowMagickException(exception,GetMagickModule(),
02775                   OptionError,"DivideByZero","`%s'",expression);
02776               return(0.0);
02777             }
02778           return(alpha/(*beta));
02779         }
02780         case '%':
02781         {
02782           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
02783           *beta=fabs(floor(((double) *beta)+0.5));
02784           if (*beta == 0.0)
02785             {
02786               (void) ThrowMagickException(exception,GetMagickModule(),
02787                 OptionError,"DivideByZero","`%s'",expression);
02788               return(0.0);
02789             }
02790           return(fmod((double) alpha,(double) *beta));
02791         }
02792         case '+':
02793         {
02794           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
02795           return(alpha+(*beta));
02796         }
02797         case '-':
02798         {
02799           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
02800           return(alpha-(*beta));
02801         }
02802         case LeftShiftOperator:
02803         {
02804           gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
02805           *beta=(MagickRealType) ((unsigned long) (alpha+0.5) << (unsigned long)
02806             (gamma+0.5));
02807           return(*beta);
02808         }
02809         case RightShiftOperator:
02810         {
02811           gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
02812           *beta=(MagickRealType) ((unsigned long) (alpha+0.5) >> (unsigned long)
02813             (gamma+0.5));
02814           return(*beta);
02815         }
02816         case '<':
02817         {
02818           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
02819           return(alpha < *beta ? 1.0 : 0.0);
02820         }
02821         case LessThanEqualOperator:
02822         {
02823           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
02824           return(alpha <= *beta ? 1.0 : 0.0);
02825         }
02826         case '>':
02827         {
02828           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
02829           return(alpha > *beta ? 1.0 : 0.0);
02830         }
02831         case GreaterThanEqualOperator:
02832         {
02833           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
02834           return(alpha >= *beta ? 1.0 : 0.0);
02835         }
02836         case EqualOperator:
02837         {
02838           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
02839           return(fabs(alpha-(*beta)) <= MagickEpsilon ? 1.0 : 0.0);
02840         }
02841         case NotEqualOperator:
02842         {
02843           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
02844           return(fabs(alpha-(*beta)) > MagickEpsilon ? 1.0 : 0.0);
02845         }
02846         case '&':
02847         {
02848           gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
02849           *beta=(MagickRealType) ((unsigned long) (alpha+0.5) & (unsigned long)
02850             (gamma+0.5));
02851           return(*beta);
02852         }
02853         case '|':
02854         {
02855           gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
02856           *beta=(MagickRealType) ((unsigned long) (alpha+0.5) | (unsigned long)
02857             (gamma+0.5));
02858           return(*beta);
02859         }
02860         case LogicalAndOperator:
02861         {
02862           gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
02863           *beta=(alpha > 0.0) && (gamma > 0.0) ? 1.0 : 0.0;
02864           return(*beta);
02865         }
02866         case LogicalOrOperator:
02867         {
02868           gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
02869           *beta=(alpha > 0.0) || (gamma > 0.0) ? 1.0 : 0.0;
02870           return(*beta);
02871         }
02872         case '?':
02873         {
02874           MagickRealType
02875             gamma;
02876 
02877           (void) CopyMagickString(subexpression,++p,MaxTextExtent);
02878           q=subexpression;
02879           p=StringToken(":",&q);
02880           if (q == (char *) NULL)
02881             {
02882               (void) ThrowMagickException(exception,GetMagickModule(),
02883                 OptionError,"UnableToParseExpression","`%s'",subexpression);
02884               return(0.0);
02885             }
02886           if (fabs((double) alpha) > MagickEpsilon)
02887             gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,exception);
02888           else
02889             gamma=FxEvaluateSubexpression(fx_info,channel,x,y,q,beta,exception);
02890           return(gamma);
02891         }
02892         case '=':
02893         {
02894           char
02895             numeric[MaxTextExtent];
02896 
02897           q=subexpression;
02898           while (isalpha((int) ((unsigned char) *q)) != 0)
02899             q++;
02900           if (*q != '\0')
02901             {
02902               (void) ThrowMagickException(exception,GetMagickModule(),
02903                 OptionError,"UnableToParseExpression","`%s'",subexpression);
02904               return(0.0);
02905             }
02906           ClearMagickException(exception);
02907           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
02908           (void) FormatMagickString(numeric,MaxTextExtent,"%g",(double) *beta);
02909           (void) DeleteNodeFromSplayTree(fx_info->symbols,subexpression);
02910           (void) AddValueToSplayTree(fx_info->symbols,ConstantString(
02911             subexpression),ConstantString(numeric));
02912           return(*beta);
02913         }
02914         case ',':
02915         {
02916           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
02917           return(alpha);
02918         }
02919         case ';':
02920         {
02921           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
02922           return(*beta);
02923         }
02924         default:
02925         {
02926           gamma=alpha*FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,
02927             exception);
02928           return(gamma);
02929         }
02930       }
02931     }
02932   if (strchr("(",(int) *expression) != (char *) NULL)
02933     {
02934       (void) CopyMagickString(subexpression,expression+1,MaxTextExtent);
02935       subexpression[strlen(subexpression)-1]='\0';
02936       gamma=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
02937         exception);
02938       return(gamma);
02939     }
02940   switch (*expression)
02941   {
02942     case '+':
02943     {
02944       gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
02945         exception);
02946       return(1.0*gamma);
02947     }
02948     case '-':
02949     {
02950       gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
02951         exception);
02952       return(-1.0*gamma);
02953     }
02954     case '~':
02955     {
02956       gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
02957         exception);
02958       return((MagickRealType) (~(unsigned long) (gamma+0.5)));
02959     }
02960     case 'A':
02961     case 'a':
02962     {
02963       if (LocaleNCompare(expression,"abs",3) == 0)
02964         {
02965           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
02966             exception);
02967           return((MagickRealType) fabs((double) alpha));
02968         }
02969       if (LocaleNCompare(expression,"acos",4) == 0)
02970         {
02971           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
02972             exception);
02973           return((MagickRealType) acos((double) alpha));
02974         }
02975       if (LocaleNCompare(expression,"asin",4) == 0)
02976         {
02977           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
02978             exception);
02979           return((MagickRealType) asin((double) alpha));
02980         }
02981       if (LocaleNCompare(expression,"alt",3) == 0)
02982         {
02983           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
02984             exception);
02985           return(((long) alpha) & 0x01 ? -1.0 : 1.0);
02986         }
02987       if (LocaleNCompare(expression,"atan2",5) == 0)
02988         {
02989           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
02990             exception);
02991           return((MagickRealType) atan2((double) alpha,(double) *beta));
02992         }
02993       if (LocaleNCompare(expression,"atan",4) == 0)
02994         {
02995           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
02996             exception);
02997           return((MagickRealType) atan((double) alpha));
02998         }
02999       if (LocaleCompare(expression,"a") == 0)
03000         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
03001       break;
03002     }
03003     case 'B':
03004     case 'b':
03005     {
03006       if (LocaleCompare(expression,"b") == 0)
03007         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
03008       break;
03009     }
03010     case 'C':
03011     case 'c':
03012     {
03013       if (LocaleNCompare(expression,"ceil",4) == 0)
03014         {
03015           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
03016             exception);
03017           return((MagickRealType) ceil((double) alpha));
03018         }
03019       if (LocaleNCompare(expression,"cosh",4) == 0)
03020         {
03021           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
03022             exception);
03023           return((MagickRealType) cosh((double) alpha));
03024         }
03025       if (LocaleNCompare(expression,"cos",3) == 0)
03026         {
03027           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
03028             exception);
03029           return((MagickRealType) cos((double) alpha));
03030         }
03031       if (LocaleCompare(expression,"c") == 0)
03032         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
03033       break;
03034     }
03035     case 'D':
03036     case 'd':
03037     {
03038       if (LocaleNCompare(expression,"debug",5) == 0)
03039         {
03040           const char
03041             *type;
03042 
03043           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
03044             exception);
03045           if (fx_info->images->colorspace == CMYKColorspace)
03046             switch (channel)
03047             {
03048               case CyanChannel: type="cyan"; break;
03049               case MagentaChannel: type="magenta"; break;
03050               case YellowChannel: type="yellow"; break;
03051               case OpacityChannel: type="opacity"; break;
03052               case BlackChannel: type="black"; break;
03053               default: type="unknown"; break;
03054             }
03055           else
03056             switch (channel)
03057             {
03058               case RedChannel: type="red"; break;
03059               case GreenChannel: type="green"; break;
03060               case BlueChannel: type="blue"; break;
03061               case OpacityChannel: type="opacity"; break;
03062               default: type="unknown"; break;
03063             }
03064           (void) CopyMagickString(subexpression,expression+6,MaxTextExtent);
03065           if (strlen(subexpression) > 1)
03066             subexpression[strlen(subexpression)-1]='\0';
03067           if (fx_info->file != (FILE *) NULL)
03068             (void) fprintf(fx_info->file,"%s[%ld,%ld].%s: %s=%g\n",
03069               fx_info->images->filename,x,y,type,subexpression,(double) alpha);
03070           return(0.0);
03071         }
03072       break;
03073     }
03074     case 'E':
03075     case 'e':
03076     {
03077       if (LocaleCompare(expression,"epsilon") == 0)
03078         return((MagickRealType) MagickEpsilon);
03079       if (LocaleNCompare(expression,"exp",3) == 0)
03080         {
03081           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
03082             exception);
03083           return((MagickRealType) exp((double) alpha));
03084         }
03085       if (LocaleCompare(expression,"e") == 0)
03086         return((MagickRealType) 2.7182818284590452354);
03087       break;
03088     }
03089     case 'F':
03090     case 'f':
03091     {
03092       if (LocaleNCompare(expression,"floor",5) == 0)
03093         {
03094           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
03095             exception);
03096           return((MagickRealType) floor((double) alpha));
03097         }
03098       break;
03099     }
03100     case 'G':
03101     case 'g':
03102     {
03103       if (LocaleCompare(expression,"g") == 0)
03104         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
03105       break;
03106     }
03107     case 'H':
03108     case 'h':
03109     {
03110       if (LocaleCompare(expression,"h") == 0)
03111         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
03112       if (LocaleCompare(expression,"hue") == 0)
03113         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
03114       if (LocaleNCompare(expression,"hypot",5) == 0)
03115         {
03116           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
03117             exception);
03118           return((MagickRealType) hypot((double) alpha,(double) *beta));
03119         }
03120       break;
03121     }
03122     case 'K':
03123     case 'k':
03124     {
03125       if (LocaleCompare(expression,"k") == 0)
03126         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
03127       break;
03128     }
03129     case 'I':
03130     case 'i':
03131     {
03132       if (LocaleCompare(expression,"intensity") == 0)
03133         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
03134       if (LocaleNCompare(expression,"int",3) == 0)
03135         {
03136           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
03137             exception);
03138           return((MagickRealType) floor(alpha+0.5));
03139         }
03140       if (LocaleCompare(expression,"i") == 0)
03141         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
03142       break;
03143     }
03144     case 'J':
03145     case 'j':
03146     {
03147       if (LocaleCompare(expression,"j") == 0)
03148         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
03149       break;
03150     }
03151     case 'L':
03152     case 'l':
03153     {
03154       if (LocaleNCompare(expression,"ln",2) == 0)
03155         {
03156           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
03157             exception);
03158           return((MagickRealType) log((double) alpha));
03159         }
03160       if (LocaleNCompare(expression,"logtwo",4) == 0)
03161         {
03162           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
03163             exception);
03164           return((MagickRealType) log10((double) alpha))/log10(2.0);
03165         }
03166       if (LocaleNCompare(expression,"log",3) == 0)
03167         {
03168           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
03169             exception);
03170           return((MagickRealType) log10((double) alpha));
03171         }
03172       if (LocaleCompare(expression,"lightness") == 0)
03173         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
03174       break;
03175     }
03176     case 'M':
03177     case 'm':
03178     {
03179       if (LocaleCompare(expression,"MaxRGB") == 0)
03180         return((MagickRealType) QuantumRange);
03181       if (LocaleNCompare(expression,"maxima",6) == 0)
03182         break;
03183       if (LocaleNCompare(expression,"max",3) == 0)
03184         return(FxMax(fx_info,channel,x,y,expression+3,exception));
03185       if (LocaleNCompare(expression,"minima",6) == 0)
03186         break;
03187       if (LocaleNCompare(expression,"min",3) == 0)
03188         return(FxMin(fx_info,channel,x,y,expression+3,exception));
03189       if (LocaleNCompare(expression,"mod",3) == 0)
03190         {
03191           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
03192             exception);
03193           return((MagickRealType) fmod((double) alpha,(double) *beta));
03194         }
03195       if (LocaleCompare(expression,"m") == 0)
03196         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
03197       break;
03198     }
03199     case 'N':
03200     case 'n':
03201     {
03202       if (LocaleCompare(expression,"n") == 0)
03203         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
03204       break;
03205     }
03206     case 'O':
03207     case 'o':
03208     {
03209       if (LocaleCompare(expression,"Opaque") == 0)
03210         return(1.0);
03211       if (LocaleCompare(expression,"o") == 0)
03212         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
03213       break;
03214     }
03215     case 'P':
03216     case 'p':
03217     {
03218       if (LocaleCompare(expression,"pi") == 0)
03219         return((MagickRealType) MagickPI);
03220       if (LocaleNCompare(expression,"pow",3) == 0)
03221         {
03222           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
03223             exception);
03224           return((MagickRealType) pow((double) alpha,(double) *beta));
03225         }
03226       if (LocaleCompare(expression,"p") == 0)
03227         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
03228       break;
03229     }
03230     case 'Q':
03231     case 'q':
03232     {
03233       if (LocaleCompare(expression,"QuantumRange") == 0)
03234         return((MagickRealType) QuantumRange);
03235       if (LocaleCompare(expression,"QuantumScale") == 0)
03236         return((MagickRealType) QuantumScale);
03237       break;
03238     }
03239     case 'R':
03240     case 'r':
03241     {
03242       if (LocaleNCompare(expression,"rand",4) == 0)
03243         return((MagickRealType) GetPseudoRandomValue(fx_info->random_info));
03244       if (LocaleNCompare(expression,"round",5) == 0)
03245         {
03246           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
03247             exception);
03248           if (alpha >= 0.0)
03249             return((MagickRealType) floor((double) alpha+0.5));
03250           return((MagickRealType) ceil((double) alpha-0.5));
03251         }
03252       if (LocaleCompare(expression,"r") == 0)
03253         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
03254       break;
03255     }
03256     case 'S':
03257     case 's':
03258     {
03259       if (LocaleCompare(expression,"saturation") == 0)
03260         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
03261       if (LocaleNCompare(expression,"sign",4) == 0)
03262         {
03263           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
03264             exception);
03265           return(alpha < 0.0 ? -1.0 : 1.0);
03266         }
03267       if (LocaleNCompare(expression,"sinh",4) == 0)
03268         {
03269           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
03270             exception);
03271           return((MagickRealType) sinh((double) alpha));
03272         }
03273       if (LocaleNCompare(expression,"sin",3) == 0)
03274         {
03275           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
03276             exception);
03277           return((MagickRealType) sin((double) alpha));
03278         }
03279       if (LocaleNCompare(expression,"sqrt",4) == 0)
03280         {
03281           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
03282             exception);
03283           return((MagickRealType) sqrt((double) alpha));
03284         }
03285       if (LocaleCompare(expression,"s") == 0)
03286         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
03287       break;
03288     }
03289     case 'T':
03290     case 't':
03291     {
03292       if (LocaleNCompare(expression,"tanh",4) == 0)
03293         {
03294           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
03295             exception);
03296           return((MagickRealType) tanh((double) alpha));
03297         }
03298       if (LocaleNCompare(expression,"tan",3) == 0)
03299         {
03300           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
03301             exception);
03302           return((MagickRealType) tan((double) alpha));
03303         }
03304       if (LocaleCompare(expression,"Transparent") == 0)
03305         return(0.0);
03306       if (LocaleCompare(expression,"t") == 0)
03307         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
03308       break;
03309     }
03310     case 'U':
03311     case 'u':
03312     {
03313       if (LocaleCompare(expression,"u") == 0)
03314         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
03315       break;
03316     }
03317     case 'V':
03318     case 'v':
03319     {
03320       if (LocaleCompare(expression,"v") == 0)
03321         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
03322       break;
03323     }
03324     case 'W':
03325     case 'w':
03326     {
03327       if (LocaleCompare(expression,"w") == 0)
03328         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
03329       break;
03330     }
03331     case 'Y':
03332     case 'y':
03333     {
03334       if (LocaleCompare(expression,"y") == 0)
03335         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
03336       break;
03337     }
03338     case 'Z':
03339     case 'z':
03340     {
03341       if (LocaleCompare(expression,"z") == 0)
03342         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
03343       break;
03344     }
03345     default:
03346       break;
03347   }
03348   q=(char *) expression;
03349   alpha=strtod(expression,&q);
03350   if (q == expression)
03351     return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
03352   return(alpha);
03353 }
03354 
03355 MagickExport MagickBooleanType FxEvaluateExpression(FxInfo *fx_info,
03356   MagickRealType *alpha,ExceptionInfo *exception)
03357 {
03358   MagickBooleanType
03359     status;
03360 
03361   status=FxEvaluateChannelExpression(fx_info,GrayChannel,0,0,alpha,exception);
03362   return(status);
03363 }
03364 
03365 MagickExport MagickBooleanType FxPreprocessExpression(FxInfo *fx_info,
03366   MagickRealType *alpha,ExceptionInfo *exception)
03367 {
03368   FILE
03369     *file;
03370 
03371   MagickBooleanType
03372     status;
03373 
03374   file=fx_info->file;
03375   fx_info->file=(FILE *) NULL;
03376   status=FxEvaluateChannelExpression(fx_info,GrayChannel,0,0,alpha,exception);
03377   fx_info->file=file;
03378   return(status);
03379 }
03380 
03381 MagickExport MagickBooleanType FxEvaluateChannelExpression(FxInfo *fx_info,
03382   const ChannelType channel,const long x,const long y,MagickRealType *alpha,
03383   ExceptionInfo *exception)
03384 {
03385   MagickRealType
03386     beta;
03387 
03388   beta=0.0;
03389   *alpha=FxEvaluateSubexpression(fx_info,channel,x,y,fx_info->expression,&beta,
03390     exception);
03391   return(exception->severity == OptionError ? MagickFalse : MagickTrue);
03392 }
03393 
03394 /*
03395 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03396 %                                                                             %
03397 %                                                                             %
03398 %                                                                             %
03399 %     F x I m a g e                                                           %
03400 %                                                                             %
03401 %                                                                             %
03402 %                                                                             %
03403 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03404 %
03405 %  FxImage() applies a mathematical expression to the specified image.
03406 %
03407 %  The format of the FxImage method is:
03408 %
03409 %      Image *FxImage(const Image *image,const char *expression,
03410 %        ExceptionInfo *exception)
03411 %      Image *FxImageChannel(const Image *image,const ChannelType channel,
03412 %        const char *expression,ExceptionInfo *exception)
03413 %
03414 %  A description of each parameter follows:
03415 %
03416 %    o image: the image.
03417 %
03418 %    o channel: the channel.
03419 %
03420 %    o expression: A mathematical expression.
03421 %
03422 %    o exception: return any errors or warnings in this structure.
03423 %
03424 */
03425 
03426 static FxInfo **DestroyFxThreadSet(FxInfo **fx_info)
03427 {
03428   register long
03429     i;
03430 
03431   assert(fx_info != (FxInfo **) NULL);
03432   for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
03433     if (fx_info[i] != (FxInfo *) NULL)
03434       fx_info[i]=DestroyFxInfo(fx_info[i]);
03435   fx_info=(FxInfo **) RelinquishAlignedMemory(fx_info);
03436   return(fx_info);
03437 }
03438 
03439 static FxInfo **AcquireFxThreadSet(const Image *image,const char *expression,
03440   ExceptionInfo *exception)
03441 {
03442   char
03443     *fx_expression;
03444 
03445   FxInfo
03446     **fx_info;
03447 
03448   MagickRealType
03449     alpha;
03450 
03451   register long
03452     i;
03453 
03454   unsigned long
03455     number_threads;
03456 
03457   number_threads=GetOpenMPMaximumThreads();
03458   fx_info=(FxInfo **) AcquireAlignedMemory(number_threads,sizeof(*fx_info));
03459   if (fx_info == (FxInfo **) NULL)
03460     return((FxInfo **) NULL);
03461   (void) ResetMagickMemory(fx_info,0,number_threads*sizeof(*fx_info));
03462   if (*expression != '@')
03463     fx_expression=ConstantString(expression);
03464   else
03465     fx_expression=FileToString(expression+1,~0,exception);
03466   for (i=0; i < (long) number_threads; i++)
03467   {
03468     fx_info[i]=AcquireFxInfo(image,fx_expression);
03469     if (fx_info[i] == (FxInfo *) NULL)
03470       return(DestroyFxThreadSet(fx_info));
03471     (void) FxPreprocessExpression(fx_info[i],&alpha,fx_info[i]->exception);
03472   }
03473   fx_expression=DestroyString(fx_expression);
03474   return(fx_info);
03475 }
03476 
03477 MagickExport Image *FxImage(const Image *image,const char *expression,
03478   ExceptionInfo *exception)
03479 {
03480   Image
03481     *fx_image;
03482 
03483   fx_image=FxImageChannel(image,GrayChannel,expression,exception);
03484   return(fx_image);
03485 }
03486 
03487 MagickExport Image *FxImageChannel(const Image *image,const ChannelType channel,
03488   const char *expression,ExceptionInfo *exception)
03489 {
03490 #define FxImageTag  "Fx/Image"
03491 
03492   FxInfo
03493     **fx_info;
03494 
03495   Image
03496     *fx_image;
03497 
03498   long
03499     progress,
03500     y;
03501 
03502   MagickBooleanType
03503     status;
03504 
03505   MagickRealType
03506     alpha;
03507 
03508   CacheView
03509     *fx_view;
03510 
03511   assert(image != (Image *) NULL);
03512   assert(image->signature == MagickSignature);
03513   if (image->debug != MagickFalse)
03514     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
03515   fx_image=CloneImage(image,0,0,MagickTrue,exception);
03516   if (fx_image == (Image *) NULL)
03517     return((Image *) NULL);
03518   if (SetImageStorageClass(fx_image,DirectClass) == MagickFalse)
03519     {
03520       InheritException(exception,&fx_image->exception);
03521       fx_image=DestroyImage(fx_image);
03522       return((Image *) NULL);
03523     }
03524   fx_info=AcquireFxThreadSet(image,expression,exception);
03525   if (fx_info == (FxInfo **) NULL)
03526     {
03527       fx_image=DestroyImage(fx_image);
03528       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
03529     }
03530   status=FxPreprocessExpression(fx_info[0],&alpha,exception);
03531   if (status == MagickFalse)
03532     {
03533       fx_image=DestroyImage(fx_image);
03534       fx_info=DestroyFxThreadSet(fx_info);
03535       return((Image *) NULL);
03536     }
03537   /*
03538     Fx image.
03539   */
03540   status=MagickTrue;
03541   progress=0;
03542   fx_view=AcquireCacheView(fx_image);
03543 #if defined(MAGICKCORE_OPENMP_SUPPORT)
03544   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
03545 #endif
03546   for (y=0; y < (long) fx_image->rows; y++)
03547   {
03548     MagickRealType
03549       alpha;
03550 
03551     register IndexPacket
03552       *__restrict fx_indexes;
03553 
03554     register long
03555       id,
03556       x;
03557 
03558     register PixelPacket
03559       *__restrict q;
03560 
03561     if (status == MagickFalse)
03562       continue;
03563     q=GetCacheViewAuthenticPixels(fx_view,0,y,fx_image->columns,1,exception);
03564     if (q == (PixelPacket *) NULL)
03565       {
03566         status=MagickFalse;
03567         continue;
03568       }
03569     fx_indexes=GetCacheViewAuthenticIndexQueue(fx_view);
03570     id=GetOpenMPThreadId();
03571     alpha=0.0;
03572     for (x=0; x < (long) fx_image->columns; x++)
03573     {
03574       if ((channel & RedChannel) != 0)
03575         {
03576           (void) FxEvaluateChannelExpression(fx_info[id],RedChannel,x,y,
03577             &alpha,exception);
03578           q->red=RoundToQuantum((MagickRealType) QuantumRange*alpha);
03579         }
03580       if ((channel & GreenChannel) != 0)
03581         {
03582           (void) FxEvaluateChannelExpression(fx_info[id],GreenChannel,x,y,
03583             &alpha,exception);
03584           q->green=RoundToQuantum((MagickRealType) QuantumRange*alpha);
03585         }
03586       if ((channel & BlueChannel) != 0)
03587         {
03588           (void) FxEvaluateChannelExpression(fx_info[id],BlueChannel,x,y,
03589             &alpha,exception);
03590           q->blue=RoundToQuantum((MagickRealType) QuantumRange*alpha);
03591         }
03592       if ((channel & OpacityChannel) != 0)
03593         {
03594           (void) FxEvaluateChannelExpression(fx_info[id],OpacityChannel,x,y,
03595             &alpha,exception);
03596           if (image->matte == MagickFalse)
03597             q->opacity=RoundToQuantum((MagickRealType) QuantumRange*alpha);
03598           else
03599             q->opacity=RoundToQuantum((MagickRealType) (QuantumRange-
03600               QuantumRange*alpha));
03601         }
03602       if (((channel & IndexChannel) != 0) &&
03603           (fx_image->colorspace == CMYKColorspace))
03604         {
03605           (void) FxEvaluateChannelExpression(fx_info[id],IndexChannel,x,y,
03606             &alpha,exception);
03607           fx_indexes[x]=(IndexPacket) RoundToQuantum((MagickRealType)
03608             QuantumRange*alpha);
03609         }
03610       q++;
03611     }
03612     if (SyncCacheViewAuthenticPixels(fx_view,exception) == MagickFalse)
03613       status=MagickFalse;
03614     if (image->progress_monitor != (MagickProgressMonitor) NULL)
03615       {
03616         MagickBooleanType
03617           proceed;
03618 
03619 #if defined(MAGICKCORE_OPENMP_SUPPORT)
03620   #pragma omp critical (MagickCore_FxImageChannel)
03621 #endif
03622         proceed=SetImageProgress(image,FxImageTag,progress++,image->rows);
03623         if (proceed == MagickFalse)
03624           status=MagickFalse;
03625       }
03626   }
03627   fx_image->matte=fx_info[0]->matte;
03628   fx_view=DestroyCacheView(fx_view);
03629   fx_info=DestroyFxThreadSet(fx_info);
03630   if (status == MagickFalse)
03631     fx_image=DestroyImage(fx_image);
03632   return(fx_image);
03633 }
03634 
03635 /*
03636 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03637 %                                                                             %
03638 %                                                                             %
03639 %                                                                             %
03640 %     I m p l o d e I m a g e                                                 %
03641 %                                                                             %
03642 %                                                                             %
03643 %                                                                             %
03644 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03645 %
03646 %  ImplodeImage() creates a new image that is a copy of an existing
03647 %  one with the image pixels "implode" by the specified percentage.  It
03648 %  allocates the memory necessary for the new Image structure and returns a
03649 %  pointer to the new image.
03650 %
03651 %  The format of the ImplodeImage method is:
03652 %
03653 %      Image *ImplodeImage(const Image *image,const double amount,
03654 %        ExceptionInfo *exception)
03655 %
03656 %  A description of each parameter follows:
03657 %
03658 %    o implode_image: Method ImplodeImage returns a pointer to the image
03659 %      after it is implode.  A null image is returned if there is a memory
03660 %      shortage.
03661 %
03662 %    o image: the image.
03663 %
03664 %    o amount:  Define the extent of the implosion.
03665 %
03666 %    o exception: return any errors or warnings in this structure.
03667 %
03668 */
03669 MagickExport Image *ImplodeImage(const Image *image,const double amount,
03670   ExceptionInfo *exception)
03671 {
03672 #define ImplodeImageTag  "Implode/Image"
03673 
03674   Image
03675     *implode_image;
03676 
03677   long
03678     progress,
03679     y;
03680 
03681   MagickBooleanType
03682     status;
03683 
03684   MagickPixelPacket
03685     zero;
03686 
03687   MagickRealType
03688     radius;
03689 
03690   PointInfo
03691     center,
03692     scale;
03693 
03694   ResampleFilter
03695     **resample_filter;
03696 
03697   CacheView
03698     *image_view,
03699     *implode_view;
03700 
03701   /*
03702     Initialize implode image attributes.
03703   */
03704   assert(image != (Image *) NULL);
03705   assert(image->signature == MagickSignature);
03706   if (image->debug != MagickFalse)
03707     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
03708   assert(exception != (ExceptionInfo *) NULL);
03709   assert(exception->signature == MagickSignature);
03710   implode_image=CloneImage(image,0,0,MagickTrue,exception);
03711   if (implode_image == (Image *) NULL)
03712     return((Image *) NULL);
03713   if (SetImageStorageClass(implode_image,DirectClass) == MagickFalse)
03714     {
03715       InheritException(exception,&implode_image->exception);
03716       implode_image=DestroyImage(implode_image);
03717       return((Image *) NULL);
03718     }
03719   if (implode_image->background_color.opacity != OpaqueOpacity)
03720     implode_image->matte=MagickTrue;
03721   /*
03722     Compute scaling factor.
03723   */
03724   scale.x=1.0;
03725   scale.y=1.0;
03726   center.x=0.5*image->columns;
03727   center.y=0.5*image->rows;
03728   radius=center.x;
03729   if (image->columns > image->rows)
03730     scale.y=(double) image->columns/(double) image->rows;
03731   else
03732     if (image->columns < image->rows)
03733       {
03734         scale.x=(double) image->rows/(double) image->columns;
03735         radius=center.y;
03736       }
03737   /*
03738     Implode image.
03739   */
03740   status=MagickTrue;
03741   progress=0;
03742   GetMagickPixelPacket(implode_image,&zero);
03743   resample_filter=AcquireResampleFilterThreadSet(image,MagickTrue,exception);
03744   image_view=AcquireCacheView(image);
03745   implode_view=AcquireCacheView(implode_image);
03746 #if defined(MAGICKCORE_OPENMP_SUPPORT)
03747   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
03748 #endif
03749   for (y=0; y < (long) image->rows; y++)
03750   {
03751     MagickPixelPacket
03752       pixel;
03753 
03754     MagickRealType
03755       distance;
03756 
03757     PointInfo
03758       delta;
03759 
03760     register IndexPacket
03761       *__restrict implode_indexes;
03762 
03763     register long
03764       id,
03765       x;
03766 
03767     register PixelPacket
03768       *__restrict q;
03769 
03770     if (status == MagickFalse)
03771       continue;
03772     q=GetCacheViewAuthenticPixels(implode_view,0,y,implode_image->columns,1,
03773       exception);
03774     if (q == (PixelPacket *) NULL)
03775       {
03776         status=MagickFalse;
03777         continue;
03778       }
03779     implode_indexes=GetCacheViewAuthenticIndexQueue(implode_view);
03780     delta.y=scale.y*(double) (y-center.y);
03781     pixel=zero;
03782     id=GetOpenMPThreadId();
03783     for (x=0; x < (long) image->columns; x++)
03784     {
03785       /*
03786         Determine if the pixel is within an ellipse.
03787       */
03788       delta.x=scale.x*(double) (x-center.x);
03789       distance=delta.x*delta.x+delta.y*delta.y;
03790       if (distance < (radius*radius))
03791         {
03792           double
03793             factor;
03794 
03795           /*
03796             Implode the pixel.
03797           */
03798           factor=1.0;
03799           if (distance > 0.0)
03800             factor=pow(sin((double) (MagickPI*sqrt((double) distance)/
03801               radius/2)),-amount);
03802           (void) ResamplePixelColor(resample_filter[id],(double)
03803             (factor*delta.x/scale.x+center.x),(double) (factor*delta.y/
03804             scale.y+center.y),&pixel);
03805           SetPixelPacket(implode_image,&pixel,q,implode_indexes+x);
03806         }
03807       q++;
03808     }
03809     if (SyncCacheViewAuthenticPixels(implode_view,exception) == MagickFalse)
03810       status=MagickFalse;
03811     if (image->progress_monitor != (MagickProgressMonitor) NULL)
03812       {
03813         MagickBooleanType
03814           proceed;
03815 
03816 #if defined(MAGICKCORE_OPENMP_SUPPORT)
03817   #pragma omp critical (MagickCore_ImplodeImage)
03818 #endif
03819         proceed=SetImageProgress(image,ImplodeImageTag,progress++,image->rows);
03820         if (proceed == MagickFalse)
03821           status=MagickFalse;
03822       }
03823   }
03824   implode_view=DestroyCacheView(implode_view);
03825   image_view=DestroyCacheView(image_view);
03826   resample_filter=DestroyResampleFilterThreadSet(resample_filter);
03827   if (status == MagickFalse)
03828     implode_image=DestroyImage(implode_image);
03829   return(implode_image);
03830 }
03831 
03832 /*
03833 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03834 %                                                                             %
03835 %                                                                             %
03836 %                                                                             %
03837 %     M o r p h I m a g e s                                                   %
03838 %                                                                             %
03839 %                                                                             %
03840 %                                                                             %
03841 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03842 %
03843 %  The MorphImages() method requires a minimum of two images.  The first
03844 %  image is transformed into the second by a number of intervening images
03845 %  as specified by frames.
03846 %
03847 %  The format of the MorphImage method is:
03848 %
03849 %      Image *MorphImages(const Image *image,const unsigned long number_frames,
03850 %        ExceptionInfo *exception)
03851 %
03852 %  A description of each parameter follows:
03853 %
03854 %    o image: the image.
03855 %
03856 %    o number_frames:  Define the number of in-between image to generate.
03857 %      The more in-between frames, the smoother the morph.
03858 %
03859 %    o exception: return any errors or warnings in this structure.
03860 %
03861 */
03862 MagickExport Image *MorphImages(const Image *image,
03863   const unsigned long number_frames,ExceptionInfo *exception)
03864 {
03865 #define MorphImageTag  "Morph/Image"
03866 
03867   Image
03868     *morph_image,
03869     *morph_images;
03870 
03871   long
03872     y;
03873 
03874   MagickOffsetType
03875     scene;
03876 
03877   MagickRealType
03878     alpha,
03879     beta;
03880 
03881   register const Image
03882     *next;
03883 
03884   register long
03885     i;
03886 
03887   MagickBooleanType
03888     status;
03889 
03890   /*
03891     Clone first frame in sequence.
03892   */
03893   assert(image != (Image *) NULL);
03894   assert(image->signature == MagickSignature);
03895   if (image->debug != MagickFalse)
03896     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
03897   assert(exception != (ExceptionInfo *) NULL);
03898   assert(exception->signature == MagickSignature);
03899   morph_images=CloneImage(image,0,0,MagickTrue,exception);
03900   if (morph_images == (Image *) NULL)
03901     return((Image *) NULL);
03902   if (GetNextImageInList(image) == (Image *) NULL)
03903     {
03904       /*
03905         Morph single image.
03906       */
03907       for (i=1; i < (long) number_frames; i++)
03908       {
03909         morph_image=CloneImage(image,0,0,MagickTrue,exception);
03910         if (morph_image == (Image *) NULL)
03911           {
03912             morph_images=DestroyImageList(morph_images);
03913             return((Image *) NULL);
03914           }
03915         AppendImageToList(&morph_images,morph_image);
03916         if ((image->progress_monitor != (MagickProgressMonitor) NULL) &&
03917             (QuantumTick(i,number_frames) != MagickFalse))
03918           {
03919             status=image->progress_monitor(MorphImageTag,i,number_frames,
03920               image->client_data);
03921             if (status == MagickFalse)
03922               break;
03923           }
03924       }
03925       return(GetFirstImageInList(morph_images));
03926     }
03927   /*
03928     Morph image sequence.
03929   */
03930   status=MagickTrue;
03931   scene=0;
03932   next=image;
03933   for ( ; GetNextImageInList(next) != (Image *) NULL; next=GetNextImageInList(next))
03934   {
03935     for (i=0; i < (long) number_frames; i++)
03936     {
03937       CacheView
03938         *image_view,
03939         *morph_view;
03940 
03941       beta=(MagickRealType) (i+1.0)/(MagickRealType) (number_frames+1.0);
03942       alpha=1.0-beta;
03943       morph_image=ZoomImage(next,(unsigned long) (alpha*next->columns+beta*
03944         GetNextImageInList(next)->columns+0.5),(unsigned long) (alpha*
03945         next->rows+beta*GetNextImageInList(next)->rows+0.5),exception);
03946       if (morph_image == (Image *) NULL)
03947         {
03948           morph_images=DestroyImageList(morph_images);
03949           return((Image *) NULL);
03950         }
03951       if (SetImageStorageClass(morph_image,DirectClass) == MagickFalse)
03952         {
03953           InheritException(exception,&morph_image->exception);
03954           morph_image=DestroyImage(morph_image);
03955           return((Image *) NULL);
03956         }
03957       AppendImageToList(&morph_images,morph_image);
03958       morph_images=GetLastImageInList(morph_images);
03959       morph_image=ZoomImage(GetNextImageInList(next),morph_images->columns,
03960         morph_images->rows,exception);
03961       if (morph_image == (Image *) NULL)
03962         {
03963           morph_images=DestroyImageList(morph_images);
03964           return((Image *) NULL);
03965         }
03966       image_view=AcquireCacheView(morph_image);
03967       morph_view=AcquireCacheView(morph_images);
03968 #if defined(MAGICKCORE_OPENMP_SUPPORT)
03969   #pragma omp parallel for schedule(dynamic,4) shared(status)
03970 #endif
03971       for (y=0; y < (long) morph_images->rows; y++)
03972       {
03973         MagickBooleanType
03974           sync;
03975 
03976         register const PixelPacket
03977           *__restrict p;
03978 
03979         register long
03980           x;
03981 
03982         register PixelPacket
03983           *__restrict q;
03984 
03985         if (status == MagickFalse)
03986           continue;
03987         p=GetCacheViewVirtualPixels(image_view,0,y,morph_image->columns,1,
03988           exception);
03989         q=GetCacheViewAuthenticPixels(morph_view,0,y,morph_images->columns,1,
03990           exception);
03991         if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
03992           {
03993             status=MagickFalse;
03994             continue;
03995           }
03996         for (x=0; x < (long) morph_images->columns; x++)
03997         {
03998           q->red=RoundToQuantum(alpha*q->red+beta*p->red);
03999           q->green=RoundToQuantum(alpha*q->green+beta*p->green);
04000           q->blue=RoundToQuantum(alpha*q->blue+beta*p->blue);
04001           q->opacity=RoundToQuantum(alpha*q->opacity+beta*p->opacity);
04002           p++;
04003           q++;
04004         }
04005         sync=SyncCacheViewAuthenticPixels(morph_view,exception);
04006         if (sync == MagickFalse)
04007           status=MagickFalse;
04008       }
04009       morph_view=DestroyCacheView(morph_view);
04010       image_view=DestroyCacheView(image_view);
04011       morph_image=DestroyImage(morph_image);
04012     }
04013     if (i < (long) number_frames)
04014       break;
04015     /*
04016       Clone last frame in sequence.
04017     */
04018     morph_image=CloneImage(GetNextImageInList(next),0,0,MagickTrue,exception);
04019     if (morph_image == (Image *) NULL)
04020       {
04021         morph_images=DestroyImageList(morph_images);
04022         return((Image *) NULL);
04023       }
04024     AppendImageToList(&morph_images,morph_image);
04025     morph_images=GetLastImageInList(morph_images);
04026     if (image->progress_monitor != (MagickProgressMonitor) NULL)
04027       {
04028         MagickBooleanType
04029           proceed;
04030 
04031 #if defined(MAGICKCORE_OPENMP_SUPPORT)
04032   #pragma omp critical (MagickCore_MorphImages)
04033 #endif
04034         proceed=SetImageProgress(image,MorphImageTag,scene,
04035           GetImageListLength(image));
04036         if (proceed == MagickFalse)
04037           status=MagickFalse;
04038       }
04039     scene++;
04040   }
04041   if (GetNextImageInList(next) != (Image *) NULL)
04042     {
04043       morph_images=DestroyImageList(morph_images);
04044       return((Image *) NULL);
04045     }
04046   return(GetFirstImageInList(morph_images));
04047 }
04048 
04049 /*
04050 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
04051 %                                                                             %
04052 %                                                                             %
04053 %                                                                             %
04054 %     P l a s m a I m a g e                                                   %
04055 %                                                                             %
04056 %                                                                             %
04057 %                                                                             %
04058 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
04059 %
04060 %  PlasmaImage() initializes an image with plasma fractal values.  The image
04061 %  must be initialized with a base color and the random number generator
04062 %  seeded before this method is called.
04063 %
04064 %  The format of the PlasmaImage method is:
04065 %
04066 %      MagickBooleanType PlasmaImage(Image *image,const SegmentInfo *segment,
04067 %        unsigned long attenuate,unsigned long depth)
04068 %
04069 %  A description of each parameter follows:
04070 %
04071 %    o image: the image.
04072 %
04073 %    o segment:   Define the region to apply plasma fractals values.
04074 %
04075 %    o attenuate: Define the plasmattenuation factor.
04076 %
04077 %    o depth: Limit the plasma recursion depth.
04078 %
04079 */
04080 
04081 static inline Quantum PlasmaPixel(RandomInfo *random_info,
04082   const MagickRealType pixel,const MagickRealType noise)
04083 {
04084   Quantum
04085     plasma;
04086 
04087   plasma=RoundToQuantum(pixel+noise*GetPseudoRandomValue(random_info)-
04088     noise/2.0);
04089   return(plasma);
04090 }
04091 
04092 MagickExport MagickBooleanType PlasmaImageProxy(Image *image,
04093   RandomInfo *random_info,const SegmentInfo *segment,unsigned long attenuate,
04094   unsigned long depth)
04095 {
04096   ExceptionInfo
04097     *exception;
04098 
04099   long
04100     x,
04101     x_mid,
04102     y,
04103     y_mid;
04104 
04105   MagickRealType
04106     plasma;
04107 
04108   PixelPacket
04109     u,
04110     v;
04111 
04112   if (((segment->x2-segment->x1) == 0.0) && ((segment->y2-segment->y1) == 0.0))
04113     return(MagickTrue);
04114   if (depth != 0)
04115     {
04116       SegmentInfo
04117         local_info;
04118 
04119       /*
04120         Divide the area into quadrants and recurse.
04121       */
04122       depth--;
04123       attenuate++;
04124       x_mid=(long) (segment->x1+segment->x2+0.5)/2;
04125       y_mid=(long) (segment->y1+segment->y2+0.5)/2;
04126       local_info=(*segment);
04127       local_info.x2=(double) x_mid;
04128       local_info.y2=(double) y_mid;
04129       (void) PlasmaImageProxy(image,random_info,&local_info,attenuate,depth);
04130       local_info=(*segment);
04131       local_info.y1=(double) y_mid;
04132       local_info.x2=(double) x_mid;
04133       (void) PlasmaImageProxy(image,random_info,&local_info,attenuate,depth);
04134       local_info=(*segment);
04135       local_info.x1=(double) x_mid;
04136       local_info.y2=(double) y_mid;
04137       (void) PlasmaImageProxy(image,random_info,&local_info,attenuate,depth);
04138       local_info=(*segment);
04139       local_info.x1=(double) x_mid;
04140       local_info.y1=(double) y_mid;
04141       return(PlasmaImageProxy(image,random_info,&local_info,attenuate,depth));
04142     }
04143   if (SetImageStorageClass(image,DirectClass) == MagickFalse)
04144     return(MagickFalse);
04145   x_mid=(long) (segment->x1+segment->x2+0.5)/2;
04146   y_mid=(long) (segment->y1+segment->y2+0.5)/2;
04147   if ((segment->x1 == (double) x_mid) && (segment->x2 == (double) x_mid) &&
04148       (segment->y1 == (double) y_mid) && (segment->y2 == (double) y_mid))
04149     return(MagickFalse);
04150   /*
04151     Average pixels and apply plasma.
04152   */
04153   exception=(&image->exception);
04154   plasma=(MagickRealType) QuantumRange/(2.0*attenuate);
04155   if ((segment->x1 != (double) x_mid) || (segment->x2 != (double) x_mid))
04156     {
04157       register PixelPacket
04158         *__restrict q;
04159 
04160       /*
04161         Left pixel.
04162       */
04163       x=(long) (segment->x1+0.5);
04164       (void) GetOneVirtualPixel(image,x,(long) (segment->y1+0.5),&u,exception);
04165       (void) GetOneVirtualPixel(image,x,(long) (segment->y2+0.5),&v,exception);
04166       q=QueueAuthenticPixels(image,x,y_mid,1,1,exception);
04167       if (q == (PixelPacket *) NULL)
04168         return(MagickTrue);
04169       q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
04170         plasma);
04171       q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/2.0,
04172         plasma);
04173       q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
04174         plasma);
04175       (void) SyncAuthenticPixels(image,exception);
04176       if (segment->x1 != segment->x2)
04177         {
04178           /*
04179             Right pixel.
04180           */
04181           x=(long) (segment->x2+0.5);
04182           (void) GetOneVirtualPixel(image,x,(long) (segment->y1+0.5),&u,
04183             exception);
04184           (void) GetOneVirtualPixel(image,x,(long) (segment->y2+0.5),&v,
04185             exception);
04186           q=QueueAuthenticPixels(image,x,y_mid,1,1,exception);
04187           if (q == (PixelPacket *) NULL)
04188             return(MagickTrue);
04189           q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
04190             plasma);
04191           q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/
04192             2.0,plasma);
04193           q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
04194              plasma);
04195           (void) SyncAuthenticPixels(image,exception);
04196         }
04197     }
04198   if ((segment->y1 != (double) y_mid) || (segment->y2 != (double) y_mid))
04199     {
04200       if ((segment->x1 != (double) x_mid) || (segment->y2 != (double) y_mid))
04201         {
04202           register PixelPacket
04203             *__restrict q;
04204 
04205           /*
04206             Bottom pixel.
04207           */
04208           y=(long) (segment->y2+0.5);
04209           (void) GetOneVirtualPixel(image,(long) (segment->x1+0.5),y,&u,
04210             exception);
04211           (void) GetOneVirtualPixel(image,(long) (segment->x2+0.5),y,&v,
04212             exception);
04213           q=QueueAuthenticPixels(image,x_mid,y,1,1,exception);
04214           if (q == (PixelPacket *) NULL)
04215             return(MagickTrue);
04216           q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
04217             plasma);
04218           q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/
04219             2.0,plasma);
04220           q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
04221             plasma);
04222           (void) SyncAuthenticPixels(image,exception);
04223         }
04224       if (segment->y1 != segment->y2)
04225         {
04226           register PixelPacket
04227             *__restrict q;
04228 
04229           /*
04230             Top pixel.
04231           */
04232           y=(long) (segment->y1+0.5);
04233           (void) GetOneVirtualPixel(image,(long) (segment->x1+0.5),y,&u,
04234             exception);
04235           (void) GetOneVirtualPixel(image,(long) (segment->x2+0.5),y,&v,
04236             exception);
04237           q=QueueAuthenticPixels(image,x_mid,y,1,1,exception);
04238           if (q == (PixelPacket *) NULL)
04239             return(MagickTrue);
04240           q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
04241             plasma);
04242           q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/
04243             2.0,plasma);
04244           q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
04245             plasma);
04246           (void) SyncAuthenticPixels(image,exception);
04247         }
04248     }
04249   if ((segment->x1 != segment->x2) || (segment->y1 != segment->y2))
04250     {
04251       register PixelPacket
04252         *__restrict q;
04253 
04254       /*
04255         Middle pixel.
04256       */
04257       x=(long) (segment->x1+0.5);
04258       y=(long) (segment->y1+0.5);
04259       (void) GetOneVirtualPixel(image,x,y,&u,exception);
04260       x=(long) (segment->x2+0.5);
04261       y=(long) (segment->y2+0.5);
04262       (void) GetOneVirtualPixel(image,x,y,&v,exception);
04263       q=QueueAuthenticPixels(image,x_mid,y_mid,1,1,exception);
04264       if (q == (PixelPacket *) NULL)
04265         return(MagickTrue);
04266       q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
04267         plasma);
04268       q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/2.0,
04269         plasma);
04270       q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.