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