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