Home | History | Annotate | Download | only in MagickCore
      1 /*
      2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      3 %                                                                             %
      4 %                                                                             %
      5 %                                                                             %
      6 %                                 FFFFF  X   X                                %
      7 %                                 F       X X                                 %
      8 %                                 FFF      X                                  %
      9 %                                 F       X X                                 %
     10 %                                 F      X   X                                %
     11 %                                                                             %
     12 %                                                                             %
     13 %                   MagickCore Image Special Effects Methods                  %
     14 %                                                                             %
     15 %                               Software Design                               %
     16 %                                    Cristy                                   %
     17 %                                 October 1996                                %
     18 %                                                                             %
     19 %                                                                             %
     20 %  Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization      %
     21 %  dedicated to making software imaging solutions freely available.           %
     22 %                                                                             %
     23 %  You may not use this file except in compliance with the License.  You may  %
     24 %  obtain a copy of the License at                                            %
     25 %                                                                             %
     26 %    http://www.imagemagick.org/script/license.php                            %
     27 %                                                                             %
     28 %  Unless required by applicable law or agreed to in writing, software        %
     29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
     30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
     31 %  See the License for the specific language governing permissions and        %
     32 %  limitations under the License.                                             %
     33 %                                                                             %
     34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     35 %
     36 %
     37 %
     38 */
     39 
     40 
     41 /*
     42   Include declarations.
     43 */
     44 #include "MagickCore/studio.h"
     45 #include "MagickCore/accelerate-private.h"
     46 #include "MagickCore/annotate.h"
     47 #include "MagickCore/artifact.h"
     48 #include "MagickCore/attribute.h"
     49 #include "MagickCore/cache.h"
     50 #include "MagickCore/cache-view.h"
     51 #include "MagickCore/channel.h"
     52 #include "MagickCore/color.h"
     53 #include "MagickCore/color-private.h"
     54 #include "MagickCore/colorspace-private.h"
     55 #include "MagickCore/composite.h"
     56 #include "MagickCore/decorate.h"
     57 #include "MagickCore/distort.h"
     58 #include "MagickCore/draw.h"
     59 #include "MagickCore/effect.h"
     60 #include "MagickCore/enhance.h"
     61 #include "MagickCore/exception.h"
     62 #include "MagickCore/exception-private.h"
     63 #include "MagickCore/fx.h"
     64 #include "MagickCore/fx-private.h"
     65 #include "MagickCore/gem.h"
     66 #include "MagickCore/gem-private.h"
     67 #include "MagickCore/geometry.h"
     68 #include "MagickCore/layer.h"
     69 #include "MagickCore/list.h"
     70 #include "MagickCore/log.h"
     71 #include "MagickCore/image.h"
     72 #include "MagickCore/image-private.h"
     73 #include "MagickCore/magick.h"
     74 #include "MagickCore/memory_.h"
     75 #include "MagickCore/monitor.h"
     76 #include "MagickCore/monitor-private.h"
     77 #include "MagickCore/option.h"
     78 #include "MagickCore/pixel.h"
     79 #include "MagickCore/pixel-accessor.h"
     80 #include "MagickCore/property.h"
     81 #include "MagickCore/quantum.h"
     82 #include "MagickCore/quantum-private.h"
     83 #include "MagickCore/random_.h"
     84 #include "MagickCore/random-private.h"
     85 #include "MagickCore/resample.h"
     86 #include "MagickCore/resample-private.h"
     87 #include "MagickCore/resize.h"
     88 #include "MagickCore/resource_.h"
     89 #include "MagickCore/splay-tree.h"
     90 #include "MagickCore/statistic.h"
     91 #include "MagickCore/string_.h"
     92 #include "MagickCore/string-private.h"
     93 #include "MagickCore/thread-private.h"
     94 #include "MagickCore/transform.h"
     95 #include "MagickCore/transform-private.h"
     96 #include "MagickCore/utility.h"
     97 
     98 
     99 /*
    100   Define declarations.
    101 */
    102 #define LeftShiftOperator  0xf5U
    103 #define RightShiftOperator  0xf6U
    104 #define LessThanEqualOperator  0xf7U
    105 #define GreaterThanEqualOperator  0xf8U
    106 #define EqualOperator  0xf9U
    107 #define NotEqualOperator  0xfaU
    108 #define LogicalAndOperator  0xfbU
    109 #define LogicalOrOperator  0xfcU
    110 #define ExponentialNotation 0xfdU
    111 
    112 struct _FxInfo
    113 {
    114   const Image
    115     *images;
    116 
    117   char
    118     *expression;
    119 
    120   FILE
    121     *file;
    122 
    123   SplayTreeInfo
    124     *colors,
    125     *symbols;
    126 
    127   CacheView
    128     **view;
    129 
    130   RandomInfo
    131     *random_info;
    132 
    133   ExceptionInfo
    134     *exception;
    135 };
    136 
    137 
    138 /*
    139 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    140 %                                                                             %
    141 %                                                                             %
    142 %                                                                             %
    143 +   A c q u i r e F x I n f o                                                 %
    144 %                                                                             %
    145 %                                                                             %
    146 %                                                                             %
    147 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    148 %
    149 %  AcquireFxInfo() allocates the FxInfo structure.
    150 %
    151 %  The format of the AcquireFxInfo method is:
    152 %
    153 %      FxInfo *AcquireFxInfo(Image *image,const char *expression,
    154 %        ExceptionInfo *exception)
    155 %
    156 %  A description of each parameter follows:
    157 %
    158 %    o image: the image.
    159 %
    160 %    o expression: the expression.
    161 %
    162 %    o exception: return any errors or warnings in this structure.
    163 %
    164 */
    165 MagickPrivate FxInfo *AcquireFxInfo(const Image *image,const char *expression,
    166   ExceptionInfo *exception)
    167 {
    168   char
    169     fx_op[2];
    170 
    171   const Image
    172     *next;
    173 
    174   FxInfo
    175     *fx_info;
    176 
    177   register ssize_t
    178     i;
    179 
    180   fx_info=(FxInfo *) AcquireMagickMemory(sizeof(*fx_info));
    181   if (fx_info == (FxInfo *) NULL)
    182     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
    183   (void) ResetMagickMemory(fx_info,0,sizeof(*fx_info));
    184   fx_info->exception=AcquireExceptionInfo();
    185   fx_info->images=image;
    186   fx_info->colors=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
    187     RelinquishAlignedMemory);
    188   fx_info->symbols=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
    189     RelinquishMagickMemory);
    190   fx_info->view=(CacheView **) AcquireQuantumMemory(GetImageListLength(
    191     fx_info->images),sizeof(*fx_info->view));
    192   if (fx_info->view == (CacheView **) NULL)
    193     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
    194   i=0;
    195   next=GetFirstImageInList(fx_info->images);
    196   for ( ; next != (Image *) NULL; next=next->next)
    197   {
    198     fx_info->view[i]=AcquireVirtualCacheView(next,exception);
    199     i++;
    200   }
    201   fx_info->random_info=AcquireRandomInfo();
    202   fx_info->expression=ConstantString(expression);
    203   fx_info->file=stderr;
    204   (void) SubstituteString(&fx_info->expression," ","");  /* compact string */
    205   /*
    206     Force right-to-left associativity for unary negation.
    207   */
    208   (void) SubstituteString(&fx_info->expression,"-","-1.0*");
    209   (void) SubstituteString(&fx_info->expression,"^-1.0*","^-");
    210   (void) SubstituteString(&fx_info->expression,"E-1.0*","E-");
    211   (void) SubstituteString(&fx_info->expression,"e-1.0*","e-");
    212   /*
    213     Convert compound to simple operators.
    214   */
    215   fx_op[1]='\0';
    216   *fx_op=(char) LeftShiftOperator;
    217   (void) SubstituteString(&fx_info->expression,"<<",fx_op);
    218   *fx_op=(char) RightShiftOperator;
    219   (void) SubstituteString(&fx_info->expression,">>",fx_op);
    220   *fx_op=(char) LessThanEqualOperator;
    221   (void) SubstituteString(&fx_info->expression,"<=",fx_op);
    222   *fx_op=(char) GreaterThanEqualOperator;
    223   (void) SubstituteString(&fx_info->expression,">=",fx_op);
    224   *fx_op=(char) EqualOperator;
    225   (void) SubstituteString(&fx_info->expression,"==",fx_op);
    226   *fx_op=(char) NotEqualOperator;
    227   (void) SubstituteString(&fx_info->expression,"!=",fx_op);
    228   *fx_op=(char) LogicalAndOperator;
    229   (void) SubstituteString(&fx_info->expression,"&&",fx_op);
    230   *fx_op=(char) LogicalOrOperator;
    231   (void) SubstituteString(&fx_info->expression,"||",fx_op);
    232   *fx_op=(char) ExponentialNotation;
    233   (void) SubstituteString(&fx_info->expression,"**",fx_op);
    234   return(fx_info);
    235 }
    236 
    237 
    238 /*
    239 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    240 %                                                                             %
    241 %                                                                             %
    242 %                                                                             %
    243 %     A d d N o i s e I m a g e                                               %
    244 %                                                                             %
    245 %                                                                             %
    246 %                                                                             %
    247 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    248 %
    249 %  AddNoiseImage() adds random noise to the image.
    250 %
    251 %  The format of the AddNoiseImage method is:
    252 %
    253 %      Image *AddNoiseImage(const Image *image,const NoiseType noise_type,
    254 %        const double attenuate,ExceptionInfo *exception)
    255 %
    256 %  A description of each parameter follows:
    257 %
    258 %    o image: the image.
    259 %
    260 %    o channel: the channel type.
    261 %
    262 %    o noise_type:  The type of noise: Uniform, Gaussian, Multiplicative,
    263 %      Impulse, Laplacian, or Poisson.
    264 %
    265 %    o attenuate:  attenuate the random distribution.
    266 %
    267 %    o exception: return any errors or warnings in this structure.
    268 %
    269 */
    270 MagickExport Image *AddNoiseImage(const Image *image,const NoiseType noise_type,
    271   const double attenuate,ExceptionInfo *exception)
    272 {
    273 #define AddNoiseImageTag  "AddNoise/Image"
    274 
    275   CacheView
    276     *image_view,
    277     *noise_view;
    278 
    279   Image
    280     *noise_image;
    281 
    282   MagickBooleanType
    283     status;
    284 
    285   MagickOffsetType
    286     progress;
    287 
    288   RandomInfo
    289     **magick_restrict random_info;
    290 
    291   ssize_t
    292     y;
    293 
    294 #if defined(MAGICKCORE_OPENMP_SUPPORT)
    295   unsigned long
    296     key;
    297 #endif
    298 
    299   /*
    300     Initialize noise image attributes.
    301   */
    302   assert(image != (const Image *) NULL);
    303   assert(image->signature == MagickCoreSignature);
    304   if (image->debug != MagickFalse)
    305     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
    306   assert(exception != (ExceptionInfo *) NULL);
    307   assert(exception->signature == MagickCoreSignature);
    308 #if defined(MAGICKCORE_OPENCL_SUPPORT)
    309   noise_image=AccelerateAddNoiseImage(image,noise_type,exception);
    310   if (noise_image != (Image *) NULL)
    311     return(noise_image);
    312 #endif
    313   noise_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
    314   if (noise_image == (Image *) NULL)
    315     return((Image *) NULL);
    316   if (SetImageStorageClass(noise_image,DirectClass,exception) == MagickFalse)
    317     {
    318       noise_image=DestroyImage(noise_image);
    319       return((Image *) NULL);
    320     }
    321   /*
    322     Add noise in each row.
    323   */
    324   status=MagickTrue;
    325   progress=0;
    326   random_info=AcquireRandomInfoThreadSet();
    327   image_view=AcquireVirtualCacheView(image,exception);
    328   noise_view=AcquireAuthenticCacheView(noise_image,exception);
    329 #if defined(MAGICKCORE_OPENMP_SUPPORT)
    330   key=GetRandomSecretKey(random_info[0]);
    331   #pragma omp parallel for schedule(static,4) shared(progress,status) \
    332     magick_threads(image,noise_image,image->rows,key == ~0UL)
    333 #endif
    334   for (y=0; y < (ssize_t) image->rows; y++)
    335   {
    336     const int
    337       id = GetOpenMPThreadId();
    338 
    339     MagickBooleanType
    340       sync;
    341 
    342     register const Quantum
    343       *magick_restrict p;
    344 
    345     register ssize_t
    346       x;
    347 
    348     register Quantum
    349       *magick_restrict q;
    350 
    351     if (status == MagickFalse)
    352       continue;
    353     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
    354     q=QueueCacheViewAuthenticPixels(noise_view,0,y,noise_image->columns,1,
    355       exception);
    356     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
    357       {
    358         status=MagickFalse;
    359         continue;
    360       }
    361     for (x=0; x < (ssize_t) image->columns; x++)
    362     {
    363       register ssize_t
    364         i;
    365 
    366       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
    367       {
    368         PixelChannel channel=GetPixelChannelChannel(image,i);
    369         PixelTrait traits=GetPixelChannelTraits(image,channel);
    370         PixelTrait noise_traits=GetPixelChannelTraits(noise_image,channel);
    371         if ((traits == UndefinedPixelTrait) ||
    372             (noise_traits == UndefinedPixelTrait))
    373           continue;
    374         if (((noise_traits & CopyPixelTrait) != 0) ||
    375             (GetPixelReadMask(image,p) == 0))
    376           {
    377             SetPixelChannel(noise_image,channel,p[i],q);
    378             continue;
    379           }
    380         SetPixelChannel(noise_image,channel,ClampToQuantum(
    381           GenerateDifferentialNoise(random_info[id],p[i],noise_type,attenuate)),
    382           q);
    383       }
    384       p+=GetPixelChannels(image);
    385       q+=GetPixelChannels(noise_image);
    386     }
    387     sync=SyncCacheViewAuthenticPixels(noise_view,exception);
    388     if (sync == MagickFalse)
    389       status=MagickFalse;
    390     if (image->progress_monitor != (MagickProgressMonitor) NULL)
    391       {
    392         MagickBooleanType
    393           proceed;
    394 
    395 #if defined(MAGICKCORE_OPENMP_SUPPORT)
    396         #pragma omp critical (MagickCore_AddNoiseImage)
    397 #endif
    398         proceed=SetImageProgress(image,AddNoiseImageTag,progress++,
    399           image->rows);
    400         if (proceed == MagickFalse)
    401           status=MagickFalse;
    402       }
    403   }
    404   noise_view=DestroyCacheView(noise_view);
    405   image_view=DestroyCacheView(image_view);
    406   random_info=DestroyRandomInfoThreadSet(random_info);
    407   if (status == MagickFalse)
    408     noise_image=DestroyImage(noise_image);
    409   return(noise_image);
    410 }
    411 
    412 
    413 /*
    414 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    415 %                                                                             %
    416 %                                                                             %
    417 %                                                                             %
    418 %     B l u e S h i f t I m a g e                                             %
    419 %                                                                             %
    420 %                                                                             %
    421 %                                                                             %
    422 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    423 %
    424 %  BlueShiftImage() mutes the colors of the image to simulate a scene at
    425 %  nighttime in the moonlight.
    426 %
    427 %  The format of the BlueShiftImage method is:
    428 %
    429 %      Image *BlueShiftImage(const Image *image,const double factor,
    430 %        ExceptionInfo *exception)
    431 %
    432 %  A description of each parameter follows:
    433 %
    434 %    o image: the image.
    435 %
    436 %    o factor: the shift factor.
    437 %
    438 %    o exception: return any errors or warnings in this structure.
    439 %
    440 */
    441 MagickExport Image *BlueShiftImage(const Image *image,const double factor,
    442   ExceptionInfo *exception)
    443 {
    444 #define BlueShiftImageTag  "BlueShift/Image"
    445 
    446   CacheView
    447     *image_view,
    448     *shift_view;
    449 
    450   Image
    451     *shift_image;
    452 
    453   MagickBooleanType
    454     status;
    455 
    456   MagickOffsetType
    457     progress;
    458 
    459   ssize_t
    460     y;
    461 
    462   /*
    463     Allocate blue shift image.
    464   */
    465   assert(image != (const Image *) NULL);
    466   assert(image->signature == MagickCoreSignature);
    467   if (image->debug != MagickFalse)
    468     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
    469   assert(exception != (ExceptionInfo *) NULL);
    470   assert(exception->signature == MagickCoreSignature);
    471   shift_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
    472   if (shift_image == (Image *) NULL)
    473     return((Image *) NULL);
    474   if (SetImageStorageClass(shift_image,DirectClass,exception) == MagickFalse)
    475     {
    476       shift_image=DestroyImage(shift_image);
    477       return((Image *) NULL);
    478     }
    479   /*
    480     Blue-shift DirectClass image.
    481   */
    482   status=MagickTrue;
    483   progress=0;
    484   image_view=AcquireVirtualCacheView(image,exception);
    485   shift_view=AcquireAuthenticCacheView(shift_image,exception);
    486 #if defined(MAGICKCORE_OPENMP_SUPPORT)
    487   #pragma omp parallel for schedule(static,4) shared(progress,status) \
    488     magick_threads(image,shift_image,image->rows,1)
    489 #endif
    490   for (y=0; y < (ssize_t) image->rows; y++)
    491   {
    492     MagickBooleanType
    493       sync;
    494 
    495     PixelInfo
    496       pixel;
    497 
    498     Quantum
    499       quantum;
    500 
    501     register const Quantum
    502       *magick_restrict p;
    503 
    504     register ssize_t
    505       x;
    506 
    507     register Quantum
    508       *magick_restrict q;
    509 
    510     if (status == MagickFalse)
    511       continue;
    512     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
    513     q=QueueCacheViewAuthenticPixels(shift_view,0,y,shift_image->columns,1,
    514       exception);
    515     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
    516       {
    517         status=MagickFalse;
    518         continue;
    519       }
    520     for (x=0; x < (ssize_t) image->columns; x++)
    521     {
    522       quantum=GetPixelRed(image,p);
    523       if (GetPixelGreen(image,p) < quantum)
    524         quantum=GetPixelGreen(image,p);
    525       if (GetPixelBlue(image,p) < quantum)
    526         quantum=GetPixelBlue(image,p);
    527       pixel.red=0.5*(GetPixelRed(image,p)+factor*quantum);
    528       pixel.green=0.5*(GetPixelGreen(image,p)+factor*quantum);
    529       pixel.blue=0.5*(GetPixelBlue(image,p)+factor*quantum);
    530       quantum=GetPixelRed(image,p);
    531       if (GetPixelGreen(image,p) > quantum)
    532         quantum=GetPixelGreen(image,p);
    533       if (GetPixelBlue(image,p) > quantum)
    534         quantum=GetPixelBlue(image,p);
    535       pixel.red=0.5*(pixel.red+factor*quantum);
    536       pixel.green=0.5*(pixel.green+factor*quantum);
    537       pixel.blue=0.5*(pixel.blue+factor*quantum);
    538       SetPixelRed(shift_image,ClampToQuantum(pixel.red),q);
    539       SetPixelGreen(shift_image,ClampToQuantum(pixel.green),q);
    540       SetPixelBlue(shift_image,ClampToQuantum(pixel.blue),q);
    541       p+=GetPixelChannels(image);
    542       q+=GetPixelChannels(shift_image);
    543     }
    544     sync=SyncCacheViewAuthenticPixels(shift_view,exception);
    545     if (sync == MagickFalse)
    546       status=MagickFalse;
    547     if (image->progress_monitor != (MagickProgressMonitor) NULL)
    548       {
    549         MagickBooleanType
    550           proceed;
    551 
    552 #if defined(MAGICKCORE_OPENMP_SUPPORT)
    553         #pragma omp critical (MagickCore_BlueShiftImage)
    554 #endif
    555         proceed=SetImageProgress(image,BlueShiftImageTag,progress++,
    556           image->rows);
    557         if (proceed == MagickFalse)
    558           status=MagickFalse;
    559       }
    560   }
    561   image_view=DestroyCacheView(image_view);
    562   shift_view=DestroyCacheView(shift_view);
    563   if (status == MagickFalse)
    564     shift_image=DestroyImage(shift_image);
    565   return(shift_image);
    566 }
    567 
    568 
    569 /*
    570 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    571 %                                                                             %
    572 %                                                                             %
    573 %                                                                             %
    574 %     C h a r c o a l I m a g e                                               %
    575 %                                                                             %
    576 %                                                                             %
    577 %                                                                             %
    578 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    579 %
    580 %  CharcoalImage() creates a new image that is a copy of an existing one with
    581 %  the edge highlighted.  It allocates the memory necessary for the new Image
    582 %  structure and returns a pointer to the new image.
    583 %
    584 %  The format of the CharcoalImage method is:
    585 %
    586 %      Image *CharcoalImage(const Image *image,const double radius,
    587 %        const double sigma,ExceptionInfo *exception)
    588 %
    589 %  A description of each parameter follows:
    590 %
    591 %    o image: the image.
    592 %
    593 %    o radius: the radius of the pixel neighborhood.
    594 %
    595 %    o sigma: the standard deviation of the Gaussian, in pixels.
    596 %
    597 %    o exception: return any errors or warnings in this structure.
    598 %
    599 */
    600 MagickExport Image *CharcoalImage(const Image *image,const double radius,
    601   const double sigma,ExceptionInfo *exception)
    602 {
    603   Image
    604     *charcoal_image,
    605     *clone_image,
    606     *edge_image;
    607 
    608   assert(image != (Image *) NULL);
    609   assert(image->signature == MagickCoreSignature);
    610   if (image->debug != MagickFalse)
    611     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
    612   assert(exception != (ExceptionInfo *) NULL);
    613   assert(exception->signature == MagickCoreSignature);
    614   clone_image=CloneImage(image,0,0,MagickTrue,exception);
    615   if (clone_image == (Image *) NULL)
    616     return((Image *) NULL);
    617   edge_image=EdgeImage(clone_image,radius,exception);
    618   clone_image=DestroyImage(clone_image);
    619   if (edge_image == (Image *) NULL)
    620     return((Image *) NULL);
    621   charcoal_image=BlurImage(edge_image,radius,sigma,exception);
    622   edge_image=DestroyImage(edge_image);
    623   if (charcoal_image == (Image *) NULL)
    624     return((Image *) NULL);
    625   (void) NormalizeImage(charcoal_image,exception);
    626   (void) NegateImage(charcoal_image,MagickFalse,exception);
    627   (void) GrayscaleImage(charcoal_image,image->intensity,exception);
    628   return(charcoal_image);
    629 }
    630 
    631 
    632 /*
    633 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    634 %                                                                             %
    635 %                                                                             %
    636 %                                                                             %
    637 %     C o l o r i z e I m a g e                                               %
    638 %                                                                             %
    639 %                                                                             %
    640 %                                                                             %
    641 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    642 %
    643 %  ColorizeImage() blends the fill color with each pixel in the image.
    644 %  A percentage blend is specified with opacity.  Control the application
    645 %  of different color components by specifying a different percentage for
    646 %  each component (e.g. 90/100/10 is 90% red, 100% green, and 10% blue).
    647 %
    648 %  The format of the ColorizeImage method is:
    649 %
    650 %      Image *ColorizeImage(const Image *image,const char *blend,
    651 %        const PixelInfo *colorize,ExceptionInfo *exception)
    652 %
    653 %  A description of each parameter follows:
    654 %
    655 %    o image: the image.
    656 %
    657 %    o blend:  A character string indicating the level of blending as a
    658 %      percentage.
    659 %
    660 %    o colorize: A color value.
    661 %
    662 %    o exception: return any errors or warnings in this structure.
    663 %
    664 */
    665 MagickExport Image *ColorizeImage(const Image *image,const char *blend,
    666   const PixelInfo *colorize,ExceptionInfo *exception)
    667 {
    668 #define ColorizeImageTag  "Colorize/Image"
    669 #define Colorize(pixel,blend_percentage,colorize)  \
    670   (((pixel)*(100.0-(blend_percentage))+(colorize)*(blend_percentage))/100.0)
    671 
    672   CacheView
    673     *image_view;
    674 
    675   GeometryInfo
    676     geometry_info;
    677 
    678   Image
    679     *colorize_image;
    680 
    681   MagickBooleanType
    682     status;
    683 
    684   MagickOffsetType
    685     progress;
    686 
    687   MagickStatusType
    688     flags;
    689 
    690   PixelInfo
    691     blend_percentage;
    692 
    693   ssize_t
    694     y;
    695 
    696   /*
    697     Allocate colorized image.
    698   */
    699   assert(image != (const Image *) NULL);
    700   assert(image->signature == MagickCoreSignature);
    701   if (image->debug != MagickFalse)
    702     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
    703   assert(exception != (ExceptionInfo *) NULL);
    704   assert(exception->signature == MagickCoreSignature);
    705   colorize_image=CloneImage(image,0,0,MagickTrue,exception);
    706   if (colorize_image == (Image *) NULL)
    707     return((Image *) NULL);
    708   if (SetImageStorageClass(colorize_image,DirectClass,exception) == MagickFalse)
    709     {
    710       colorize_image=DestroyImage(colorize_image);
    711       return((Image *) NULL);
    712     }
    713   if ((IsGrayColorspace(colorize_image->colorspace) != MagickFalse) ||
    714       (IsPixelInfoGray(colorize) != MagickFalse))
    715     (void) SetImageColorspace(colorize_image,sRGBColorspace,exception);
    716   if ((colorize_image->alpha_trait == UndefinedPixelTrait) &&
    717       (colorize->alpha_trait != UndefinedPixelTrait))
    718     (void) SetImageAlpha(colorize_image,OpaqueAlpha,exception);
    719   if (blend == (const char *) NULL)
    720     return(colorize_image);
    721   GetPixelInfo(colorize_image,&blend_percentage);
    722   flags=ParseGeometry(blend,&geometry_info);
    723   blend_percentage.red=geometry_info.rho;
    724   blend_percentage.green=geometry_info.rho;
    725   blend_percentage.blue=geometry_info.rho;
    726   blend_percentage.black=geometry_info.rho;
    727   blend_percentage.alpha=(MagickRealType) TransparentAlpha;
    728   if ((flags & SigmaValue) != 0)
    729     blend_percentage.green=geometry_info.sigma;
    730   if ((flags & XiValue) != 0)
    731     blend_percentage.blue=geometry_info.xi;
    732   if ((flags & PsiValue) != 0)
    733     blend_percentage.alpha=geometry_info.psi;
    734   if (blend_percentage.colorspace == CMYKColorspace)
    735     {
    736       if ((flags & PsiValue) != 0)
    737         blend_percentage.black=geometry_info.psi;
    738       if ((flags & ChiValue) != 0)
    739         blend_percentage.alpha=geometry_info.chi;
    740     }
    741   /*
    742     Colorize DirectClass image.
    743   */
    744   status=MagickTrue;
    745   progress=0;
    746   image_view=AcquireVirtualCacheView(colorize_image,exception);
    747 #if defined(MAGICKCORE_OPENMP_SUPPORT)
    748   #pragma omp parallel for schedule(static,4) shared(progress,status) \
    749     magick_threads(colorize_image,colorize_image,colorize_image->rows,1)
    750 #endif
    751   for (y=0; y < (ssize_t) colorize_image->rows; y++)
    752   {
    753     MagickBooleanType
    754       sync;
    755 
    756     register Quantum
    757       *magick_restrict q;
    758 
    759     register ssize_t
    760       x;
    761 
    762     if (status == MagickFalse)
    763       continue;
    764     q=GetCacheViewAuthenticPixels(image_view,0,y,colorize_image->columns,1,
    765       exception);
    766     if (q == (Quantum *) NULL)
    767       {
    768         status=MagickFalse;
    769         continue;
    770       }
    771     for (x=0; x < (ssize_t) colorize_image->columns; x++)
    772     {
    773       register ssize_t
    774         i;
    775 
    776       for (i=0; i < (ssize_t) GetPixelChannels(colorize_image); i++)
    777       {
    778         PixelTrait traits=GetPixelChannelTraits(colorize_image,
    779           (PixelChannel) i);
    780         if (traits == UndefinedPixelTrait)
    781           continue;
    782         if (((traits & CopyPixelTrait) != 0) ||
    783             (GetPixelReadMask(colorize_image,q) == 0))
    784           continue;
    785         SetPixelChannel(colorize_image,(PixelChannel) i,ClampToQuantum(
    786           Colorize(q[i],GetPixelInfoChannel(&blend_percentage,(PixelChannel) i),
    787           GetPixelInfoChannel(colorize,(PixelChannel) i))),q);
    788       }
    789       q+=GetPixelChannels(colorize_image);
    790     }
    791     sync=SyncCacheViewAuthenticPixels(image_view,exception);
    792     if (sync == MagickFalse)
    793       status=MagickFalse;
    794     if (image->progress_monitor != (MagickProgressMonitor) NULL)
    795       {
    796         MagickBooleanType
    797           proceed;
    798 
    799 #if defined(MAGICKCORE_OPENMP_SUPPORT)
    800         #pragma omp critical (MagickCore_ColorizeImage)
    801 #endif
    802         proceed=SetImageProgress(image,ColorizeImageTag,progress++,
    803           colorize_image->rows);
    804         if (proceed == MagickFalse)
    805           status=MagickFalse;
    806       }
    807   }
    808   image_view=DestroyCacheView(image_view);
    809   if (status == MagickFalse)
    810     colorize_image=DestroyImage(colorize_image);
    811   return(colorize_image);
    812 }
    813 
    814 
    815 /*
    816 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    817 %                                                                             %
    818 %                                                                             %
    819 %                                                                             %
    820 %     C o l o r M a t r i x I m a g e                                         %
    821 %                                                                             %
    822 %                                                                             %
    823 %                                                                             %
    824 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    825 %
    826 %  ColorMatrixImage() applies color transformation to an image. This method
    827 %  permits saturation changes, hue rotation, luminance to alpha, and various
    828 %  other effects.  Although variable-sized transformation matrices can be used,
    829 %  typically one uses a 5x5 matrix for an RGBA image and a 6x6 for CMYKA
    830 %  (or RGBA with offsets).  The matrix is similar to those used by Adobe Flash
    831 %  except offsets are in column 6 rather than 5 (in support of CMYKA images)
    832 %  and offsets are normalized (divide Flash offset by 255).
    833 %
    834 %  The format of the ColorMatrixImage method is:
    835 %
    836 %      Image *ColorMatrixImage(const Image *image,
    837 %        const KernelInfo *color_matrix,ExceptionInfo *exception)
    838 %
    839 %  A description of each parameter follows:
    840 %
    841 %    o image: the image.
    842 %
    843 %    o color_matrix:  the color matrix.
    844 %
    845 %    o exception: return any errors or warnings in this structure.
    846 %
    847 */
    848 /* FUTURE: modify to make use of a MagickMatrix Mutliply function
    849    That should be provided in "matrix.c"
    850    (ASIDE: actually distorts should do this too but currently doesn't)
    851 */
    852 
    853 MagickExport Image *ColorMatrixImage(const Image *image,
    854   const KernelInfo *color_matrix,ExceptionInfo *exception)
    855 {
    856 #define ColorMatrixImageTag  "ColorMatrix/Image"
    857 
    858   CacheView
    859     *color_view,
    860     *image_view;
    861 
    862   double
    863     ColorMatrix[6][6] =
    864     {
    865       { 1.0, 0.0, 0.0, 0.0, 0.0, 0.0 },
    866       { 0.0, 1.0, 0.0, 0.0, 0.0, 0.0 },
    867       { 0.0, 0.0, 1.0, 0.0, 0.0, 0.0 },
    868       { 0.0, 0.0, 0.0, 1.0, 0.0, 0.0 },
    869       { 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 },
    870       { 0.0, 0.0, 0.0, 0.0, 0.0, 1.0 }
    871     };
    872 
    873   Image
    874     *color_image;
    875 
    876   MagickBooleanType
    877     status;
    878 
    879   MagickOffsetType
    880     progress;
    881 
    882   register ssize_t
    883     i;
    884 
    885   ssize_t
    886     u,
    887     v,
    888     y;
    889 
    890   /*
    891     Map given color_matrix, into a 6x6 matrix   RGBKA and a constant
    892   */
    893   assert(image != (Image *) NULL);
    894   assert(image->signature == MagickCoreSignature);
    895   if (image->debug != MagickFalse)
    896     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
    897   assert(exception != (ExceptionInfo *) NULL);
    898   assert(exception->signature == MagickCoreSignature);
    899   i=0;
    900   for (v=0; v < (ssize_t) color_matrix->height; v++)
    901     for (u=0; u < (ssize_t) color_matrix->width; u++)
    902     {
    903       if ((v < 6) && (u < 6))
    904         ColorMatrix[v][u]=color_matrix->values[i];
    905       i++;
    906     }
    907   /*
    908     Initialize color image.
    909   */
    910   color_image=CloneImage(image,0,0,MagickTrue,exception);
    911   if (color_image == (Image *) NULL)
    912     return((Image *) NULL);
    913   if (SetImageStorageClass(color_image,DirectClass,exception) == MagickFalse)
    914     {
    915       color_image=DestroyImage(color_image);
    916       return((Image *) NULL);
    917     }
    918   if (image->debug != MagickFalse)
    919     {
    920       char
    921         format[MagickPathExtent],
    922         *message;
    923 
    924       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
    925         "  ColorMatrix image with color matrix:");
    926       message=AcquireString("");
    927       for (v=0; v < 6; v++)
    928       {
    929         *message='\0';
    930         (void) FormatLocaleString(format,MagickPathExtent,"%.20g: ",(double) v);
    931         (void) ConcatenateString(&message,format);
    932         for (u=0; u < 6; u++)
    933         {
    934           (void) FormatLocaleString(format,MagickPathExtent,"%+f ",
    935             ColorMatrix[v][u]);
    936           (void) ConcatenateString(&message,format);
    937         }
    938         (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
    939       }
    940       message=DestroyString(message);
    941     }
    942   /*
    943     Apply the ColorMatrix to image.
    944   */
    945   status=MagickTrue;
    946   progress=0;
    947   image_view=AcquireVirtualCacheView(image,exception);
    948   color_view=AcquireAuthenticCacheView(color_image,exception);
    949 #if defined(MAGICKCORE_OPENMP_SUPPORT)
    950   #pragma omp parallel for schedule(static,4) shared(progress,status) \
    951     magick_threads(image,color_image,image->rows,1)
    952 #endif
    953   for (y=0; y < (ssize_t) image->rows; y++)
    954   {
    955     PixelInfo
    956       pixel;
    957 
    958     register const Quantum
    959       *magick_restrict p;
    960 
    961     register Quantum
    962       *magick_restrict q;
    963 
    964     register ssize_t
    965       x;
    966 
    967     if (status == MagickFalse)
    968       continue;
    969     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
    970     q=GetCacheViewAuthenticPixels(color_view,0,y,color_image->columns,1,
    971       exception);
    972     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
    973       {
    974         status=MagickFalse;
    975         continue;
    976       }
    977     GetPixelInfo(image,&pixel);
    978     for (x=0; x < (ssize_t) image->columns; x++)
    979     {
    980       register ssize_t
    981         v;
    982 
    983       size_t
    984         height;
    985 
    986       GetPixelInfoPixel(image,p,&pixel);
    987       height=color_matrix->height > 6 ? 6UL : color_matrix->height;
    988       for (v=0; v < (ssize_t) height; v++)
    989       {
    990         double
    991           sum;
    992 
    993         sum=ColorMatrix[v][0]*GetPixelRed(image,p)+ColorMatrix[v][1]*
    994           GetPixelGreen(image,p)+ColorMatrix[v][2]*GetPixelBlue(image,p);
    995         if (image->colorspace == CMYKColorspace)
    996           sum+=ColorMatrix[v][3]*GetPixelBlack(image,p);
    997         if (image->alpha_trait != UndefinedPixelTrait)
    998           sum+=ColorMatrix[v][4]*GetPixelAlpha(image,p);
    999         sum+=QuantumRange*ColorMatrix[v][5];
   1000         switch (v)
   1001         {
   1002           case 0: pixel.red=sum; break;
   1003           case 1: pixel.green=sum; break;
   1004           case 2: pixel.blue=sum; break;
   1005           case 3: pixel.black=sum; break;
   1006           case 4: pixel.alpha=sum; break;
   1007           default: break;
   1008         }
   1009       }
   1010       SetPixelViaPixelInfo(color_image,&pixel,q);
   1011       p+=GetPixelChannels(image);
   1012       q+=GetPixelChannels(color_image);
   1013     }
   1014     if (SyncCacheViewAuthenticPixels(color_view,exception) == MagickFalse)
   1015       status=MagickFalse;
   1016     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   1017       {
   1018         MagickBooleanType
   1019           proceed;
   1020 
   1021 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   1022         #pragma omp critical (MagickCore_ColorMatrixImage)
   1023 #endif
   1024         proceed=SetImageProgress(image,ColorMatrixImageTag,progress++,
   1025           image->rows);
   1026         if (proceed == MagickFalse)
   1027           status=MagickFalse;
   1028       }
   1029   }
   1030   color_view=DestroyCacheView(color_view);
   1031   image_view=DestroyCacheView(image_view);
   1032   if (status == MagickFalse)
   1033     color_image=DestroyImage(color_image);
   1034   return(color_image);
   1035 }
   1036 
   1037 
   1038 /*
   1039 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1040 %                                                                             %
   1041 %                                                                             %
   1042 %                                                                             %
   1043 +   D e s t r o y F x I n f o                                                 %
   1044 %                                                                             %
   1045 %                                                                             %
   1046 %                                                                             %
   1047 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1048 %
   1049 %  DestroyFxInfo() deallocates memory associated with an FxInfo structure.
   1050 %
   1051 %  The format of the DestroyFxInfo method is:
   1052 %
   1053 %      ImageInfo *DestroyFxInfo(ImageInfo *fx_info)
   1054 %
   1055 %  A description of each parameter follows:
   1056 %
   1057 %    o fx_info: the fx info.
   1058 %
   1059 */
   1060 MagickPrivate FxInfo *DestroyFxInfo(FxInfo *fx_info)
   1061 {
   1062   register ssize_t
   1063     i;
   1064 
   1065   fx_info->exception=DestroyExceptionInfo(fx_info->exception);
   1066   fx_info->expression=DestroyString(fx_info->expression);
   1067   fx_info->symbols=DestroySplayTree(fx_info->symbols);
   1068   fx_info->colors=DestroySplayTree(fx_info->colors);
   1069   for (i=(ssize_t) GetImageListLength(fx_info->images)-1; i >= 0; i--)
   1070     fx_info->view[i]=DestroyCacheView(fx_info->view[i]);
   1071   fx_info->view=(CacheView **) RelinquishMagickMemory(fx_info->view);
   1072   fx_info->random_info=DestroyRandomInfo(fx_info->random_info);
   1073   fx_info=(FxInfo *) RelinquishMagickMemory(fx_info);
   1074   return(fx_info);
   1075 }
   1076 
   1077 
   1078 /*
   1079 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1080 %                                                                             %
   1081 %                                                                             %
   1082 %                                                                             %
   1083 +     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                   %
   1084 %                                                                             %
   1085 %                                                                             %
   1086 %                                                                             %
   1087 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1088 %
   1089 %  FxEvaluateChannelExpression() evaluates an expression and returns the
   1090 %  results.
   1091 %
   1092 %  The format of the FxEvaluateExpression method is:
   1093 %
   1094 %      double FxEvaluateChannelExpression(FxInfo *fx_info,
   1095 %        const PixelChannel channel,const ssize_t x,const ssize_t y,
   1096 %        double *alpha,Exceptioninfo *exception)
   1097 %      double FxEvaluateExpression(FxInfo *fx_info,
   1098 %        double *alpha,Exceptioninfo *exception)
   1099 %
   1100 %  A description of each parameter follows:
   1101 %
   1102 %    o fx_info: the fx info.
   1103 %
   1104 %    o channel: the channel.
   1105 %
   1106 %    o x,y: the pixel position.
   1107 %
   1108 %    o alpha: the result.
   1109 %
   1110 %    o exception: return any errors or warnings in this structure.
   1111 %
   1112 */
   1113 
   1114 static double FxChannelStatistics(FxInfo *fx_info,Image *image,
   1115   PixelChannel channel,const char *symbol,ExceptionInfo *exception)
   1116 {
   1117   ChannelType
   1118     channel_mask;
   1119 
   1120   char
   1121     key[MagickPathExtent],
   1122     statistic[MagickPathExtent];
   1123 
   1124   const char
   1125     *value;
   1126 
   1127   register const char
   1128     *p;
   1129 
   1130   channel_mask=UndefinedChannel;
   1131   for (p=symbol; (*p != '.') && (*p != '\0'); p++) ;
   1132   if (*p == '.')
   1133     {
   1134       ssize_t
   1135         option;
   1136 
   1137       option=ParseCommandOption(MagickPixelChannelOptions,MagickTrue,p+1);
   1138       if (option >= 0)
   1139         {
   1140           channel=(PixelChannel) option;
   1141           channel_mask=(ChannelType) (channel_mask | (1 << channel));
   1142           (void) SetPixelChannelMask(image,channel_mask);
   1143         }
   1144     }
   1145   (void) FormatLocaleString(key,MagickPathExtent,"%p.%.20g.%s",(void *) image,
   1146     (double) channel,symbol);
   1147   value=(const char *) GetValueFromSplayTree(fx_info->symbols,key);
   1148   if (value != (const char *) NULL)
   1149     {
   1150       if (channel_mask != UndefinedChannel)
   1151         (void) SetPixelChannelMask(image,channel_mask);
   1152       return(QuantumScale*StringToDouble(value,(char **) NULL));
   1153     }
   1154   (void) DeleteNodeFromSplayTree(fx_info->symbols,key);
   1155   if (LocaleNCompare(symbol,"depth",5) == 0)
   1156     {
   1157       size_t
   1158         depth;
   1159 
   1160       depth=GetImageDepth(image,exception);
   1161       (void) FormatLocaleString(statistic,MagickPathExtent,"%.20g",(double)
   1162         depth);
   1163     }
   1164   if (LocaleNCompare(symbol,"kurtosis",8) == 0)
   1165     {
   1166       double
   1167         kurtosis,
   1168         skewness;
   1169 
   1170       (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
   1171       (void) FormatLocaleString(statistic,MagickPathExtent,"%g",kurtosis);
   1172     }
   1173   if (LocaleNCompare(symbol,"maxima",6) == 0)
   1174     {
   1175       double
   1176         maxima,
   1177         minima;
   1178 
   1179       (void) GetImageRange(image,&minima,&maxima,exception);
   1180       (void) FormatLocaleString(statistic,MagickPathExtent,"%g",maxima);
   1181     }
   1182   if (LocaleNCompare(symbol,"mean",4) == 0)
   1183     {
   1184       double
   1185         mean,
   1186         standard_deviation;
   1187 
   1188       (void) GetImageMean(image,&mean,&standard_deviation,exception);
   1189       (void) FormatLocaleString(statistic,MagickPathExtent,"%g",mean);
   1190     }
   1191   if (LocaleNCompare(symbol,"minima",6) == 0)
   1192     {
   1193       double
   1194         maxima,
   1195         minima;
   1196 
   1197       (void) GetImageRange(image,&minima,&maxima,exception);
   1198       (void) FormatLocaleString(statistic,MagickPathExtent,"%g",minima);
   1199     }
   1200   if (LocaleNCompare(symbol,"skewness",8) == 0)
   1201     {
   1202       double
   1203         kurtosis,
   1204         skewness;
   1205 
   1206       (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
   1207       (void) FormatLocaleString(statistic,MagickPathExtent,"%g",skewness);
   1208     }
   1209   if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
   1210     {
   1211       double
   1212         mean,
   1213         standard_deviation;
   1214 
   1215       (void) GetImageMean(image,&mean,&standard_deviation,exception);
   1216       (void) FormatLocaleString(statistic,MagickPathExtent,"%g",
   1217         standard_deviation);
   1218     }
   1219   if (channel_mask != UndefinedChannel)
   1220     (void) SetPixelChannelMask(image,channel_mask);
   1221   (void) AddValueToSplayTree(fx_info->symbols,ConstantString(key),
   1222     ConstantString(statistic));
   1223   return(QuantumScale*StringToDouble(statistic,(char **) NULL));
   1224 }
   1225 
   1226 static double
   1227   FxEvaluateSubexpression(FxInfo *,const PixelChannel,const ssize_t,
   1228     const ssize_t,const char *,size_t *,double *,ExceptionInfo *);
   1229 
   1230 static MagickOffsetType FxGCD(MagickOffsetType alpha,MagickOffsetType beta)
   1231 {
   1232   if (beta != 0)
   1233     return(FxGCD(beta,alpha % beta));
   1234   return(alpha);
   1235 }
   1236 
   1237 static inline const char *FxSubexpression(const char *expression,
   1238   ExceptionInfo *exception)
   1239 {
   1240   const char
   1241     *subexpression;
   1242 
   1243   register ssize_t
   1244     level;
   1245 
   1246   level=0;
   1247   subexpression=expression;
   1248   while ((*subexpression != '\0') &&
   1249          ((level != 1) || (strchr(")",(int) *subexpression) == (char *) NULL)))
   1250   {
   1251     if (strchr("(",(int) *subexpression) != (char *) NULL)
   1252       level++;
   1253     else
   1254       if (strchr(")",(int) *subexpression) != (char *) NULL)
   1255         level--;
   1256     subexpression++;
   1257   }
   1258   if (*subexpression == '\0')
   1259     (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
   1260       "UnbalancedParenthesis","`%s'",expression);
   1261   return(subexpression);
   1262 }
   1263 
   1264 static double FxGetSymbol(FxInfo *fx_info,const PixelChannel channel,
   1265   const ssize_t x,const ssize_t y,const char *expression,
   1266   ExceptionInfo *exception)
   1267 {
   1268   char
   1269     *q,
   1270     subexpression[MagickPathExtent],
   1271     symbol[MagickPathExtent];
   1272 
   1273   const char
   1274     *p,
   1275     *value;
   1276 
   1277   Image
   1278     *image;
   1279 
   1280   PixelInfo
   1281     pixel;
   1282 
   1283   double
   1284     alpha,
   1285     beta;
   1286 
   1287   PointInfo
   1288     point;
   1289 
   1290   register ssize_t
   1291     i;
   1292 
   1293   size_t
   1294     depth,
   1295     length,
   1296     level;
   1297 
   1298   p=expression;
   1299   i=GetImageIndexInList(fx_info->images);
   1300   depth=0;
   1301   level=0;
   1302   point.x=(double) x;
   1303   point.y=(double) y;
   1304   if (isalpha((int) ((unsigned char) *(p+1))) == 0)
   1305     {
   1306       if (strchr("suv",(int) *p) != (char *) NULL)
   1307         {
   1308           switch (*p)
   1309           {
   1310             case 's':
   1311             default:
   1312             {
   1313               i=GetImageIndexInList(fx_info->images);
   1314               break;
   1315             }
   1316             case 'u': i=0; break;
   1317             case 'v': i=1; break;
   1318           }
   1319           p++;
   1320           if (*p == '[')
   1321             {
   1322               level++;
   1323               q=subexpression;
   1324               for (p++; *p != '\0'; )
   1325               {
   1326                 if (*p == '[')
   1327                   level++;
   1328                 else
   1329                   if (*p == ']')
   1330                     {
   1331                       level--;
   1332                       if (level == 0)
   1333                         break;
   1334                     }
   1335                 *q++=(*p++);
   1336               }
   1337               *q='\0';
   1338               alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
   1339                 &depth,&beta,exception);
   1340               i=(ssize_t) (alpha+0.5);
   1341               p++;
   1342             }
   1343           if (*p == '.')
   1344             p++;
   1345         }
   1346       if ((*p == 'p') && (isalpha((int) ((unsigned char) *(p+1))) == 0))
   1347         {
   1348           p++;
   1349           if (*p == '{')
   1350             {
   1351               level++;
   1352               q=subexpression;
   1353               for (p++; *p != '\0'; )
   1354               {
   1355                 if (*p == '{')
   1356                   level++;
   1357                 else
   1358                   if (*p == '}')
   1359                     {
   1360                       level--;
   1361                       if (level == 0)
   1362                         break;
   1363                     }
   1364                 *q++=(*p++);
   1365               }
   1366               *q='\0';
   1367               alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
   1368                 &depth,&beta,exception);
   1369               point.x=alpha;
   1370               point.y=beta;
   1371               p++;
   1372             }
   1373           else
   1374             if (*p == '[')
   1375               {
   1376                 level++;
   1377                 q=subexpression;
   1378                 for (p++; *p != '\0'; )
   1379                 {
   1380                   if (*p == '[')
   1381                     level++;
   1382                   else
   1383                     if (*p == ']')
   1384                       {
   1385                         level--;
   1386                         if (level == 0)
   1387                           break;
   1388                       }
   1389                   *q++=(*p++);
   1390                 }
   1391                 *q='\0';
   1392                 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
   1393                   &depth,&beta,exception);
   1394                 point.x+=alpha;
   1395                 point.y+=beta;
   1396                 p++;
   1397               }
   1398           if (*p == '.')
   1399             p++;
   1400         }
   1401     }
   1402   length=GetImageListLength(fx_info->images);
   1403   while (i < 0)
   1404     i+=(ssize_t) length;
   1405   if (length != 0)
   1406     i%=length;
   1407   image=GetImageFromList(fx_info->images,i);
   1408   if (image == (Image *) NULL)
   1409     {
   1410       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
   1411         "NoSuchImage","`%s'",expression);
   1412       return(0.0);
   1413     }
   1414   GetPixelInfo(image,&pixel);
   1415   (void) InterpolatePixelInfo(image,fx_info->view[i],image->interpolate,
   1416     point.x,point.y,&pixel,exception);
   1417   if ((strlen(p) > 2) && (LocaleCompare(p,"intensity") != 0) &&
   1418       (LocaleCompare(p,"luma") != 0) && (LocaleCompare(p,"luminance") != 0) &&
   1419       (LocaleCompare(p,"hue") != 0) && (LocaleCompare(p,"saturation") != 0) &&
   1420       (LocaleCompare(p,"lightness") != 0))
   1421     {
   1422       char
   1423         name[MagickPathExtent];
   1424 
   1425       (void) CopyMagickString(name,p,MagickPathExtent);
   1426       for (q=name+(strlen(name)-1); q > name; q--)
   1427       {
   1428         if (*q == ')')
   1429           break;
   1430         if (*q == '.')
   1431           {
   1432             *q='\0';
   1433             break;
   1434           }
   1435       }
   1436       if ((strlen(name) > 2) &&
   1437           (GetValueFromSplayTree(fx_info->symbols,name) == (const char *) NULL))
   1438         {
   1439           PixelInfo
   1440             *color;
   1441 
   1442           color=(PixelInfo *) GetValueFromSplayTree(fx_info->colors,name);
   1443           if (color != (PixelInfo *) NULL)
   1444             {
   1445               pixel=(*color);
   1446               p+=strlen(name);
   1447             }
   1448           else
   1449             {
   1450               MagickBooleanType
   1451                 status;
   1452 
   1453               status=QueryColorCompliance(name,AllCompliance,&pixel,
   1454                 fx_info->exception);
   1455               if (status != MagickFalse)
   1456                 {
   1457                   (void) AddValueToSplayTree(fx_info->colors,ConstantString(
   1458                     name),ClonePixelInfo(&pixel));
   1459                   p+=strlen(name);
   1460                 }
   1461             }
   1462         }
   1463     }
   1464   (void) CopyMagickString(symbol,p,MagickPathExtent);
   1465   StripString(symbol);
   1466   if (*symbol == '\0')
   1467     {
   1468       switch (channel)
   1469       {
   1470         case RedPixelChannel: return(QuantumScale*pixel.red);
   1471         case GreenPixelChannel: return(QuantumScale*pixel.green);
   1472         case BluePixelChannel: return(QuantumScale*pixel.blue);
   1473         case BlackPixelChannel:
   1474         {
   1475           if (image->colorspace != CMYKColorspace)
   1476             {
   1477               (void) ThrowMagickException(exception,GetMagickModule(),
   1478                 ImageError,"ColorSeparatedImageRequired","`%s'",
   1479                 image->filename);
   1480               return(0.0);
   1481             }
   1482           return(QuantumScale*pixel.black);
   1483         }
   1484         case AlphaPixelChannel:
   1485         {
   1486           if (pixel.alpha_trait == UndefinedPixelTrait)
   1487             return(1.0);
   1488           alpha=(double) (QuantumScale*pixel.alpha);
   1489           return(alpha);
   1490         }
   1491         case IndexPixelChannel:
   1492           return(0.0);
   1493         case IntensityPixelChannel:
   1494         {
   1495           Quantum
   1496             quantum_pixel[MaxPixelChannels];
   1497 
   1498           SetPixelViaPixelInfo(image,&pixel,quantum_pixel);
   1499           return(QuantumScale*GetPixelIntensity(image,quantum_pixel));
   1500         }
   1501         default:
   1502           break;
   1503       }
   1504       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
   1505         "UnableToParseExpression","`%s'",p);
   1506       return(0.0);
   1507     }
   1508   switch (*symbol)
   1509   {
   1510     case 'A':
   1511     case 'a':
   1512     {
   1513       if (LocaleCompare(symbol,"a") == 0)
   1514         return((QuantumScale*pixel.alpha));
   1515       break;
   1516     }
   1517     case 'B':
   1518     case 'b':
   1519     {
   1520       if (LocaleCompare(symbol,"b") == 0)
   1521         return(QuantumScale*pixel.blue);
   1522       break;
   1523     }
   1524     case 'C':
   1525     case 'c':
   1526     {
   1527       if (LocaleNCompare(symbol,"channel",7) == 0)
   1528         {
   1529           GeometryInfo
   1530             channel_info;
   1531 
   1532           MagickStatusType
   1533             flags;
   1534 
   1535           flags=ParseGeometry(symbol+7,&channel_info);
   1536           if (image->colorspace == CMYKColorspace)
   1537             switch (channel)
   1538             {
   1539               case CyanPixelChannel:
   1540               {
   1541                 if ((flags & RhoValue) == 0)
   1542                   return(0.0);
   1543                 return(channel_info.rho);
   1544               }
   1545               case MagentaPixelChannel:
   1546               {
   1547                 if ((flags & SigmaValue) == 0)
   1548                   return(0.0);
   1549                 return(channel_info.sigma);
   1550               }
   1551               case YellowPixelChannel:
   1552               {
   1553                 if ((flags & XiValue) == 0)
   1554                   return(0.0);
   1555                 return(channel_info.xi);
   1556               }
   1557               case BlackPixelChannel:
   1558               {
   1559                 if ((flags & PsiValue) == 0)
   1560                   return(0.0);
   1561                 return(channel_info.psi);
   1562               }
   1563               case AlphaPixelChannel:
   1564               {
   1565                 if ((flags & ChiValue) == 0)
   1566                   return(0.0);
   1567                 return(channel_info.chi);
   1568               }
   1569               default:
   1570                 return(0.0);
   1571             }
   1572           switch (channel)
   1573           {
   1574             case RedPixelChannel:
   1575             {
   1576               if ((flags & RhoValue) == 0)
   1577                 return(0.0);
   1578               return(channel_info.rho);
   1579             }
   1580             case GreenPixelChannel:
   1581             {
   1582               if ((flags & SigmaValue) == 0)
   1583                 return(0.0);
   1584               return(channel_info.sigma);
   1585             }
   1586             case BluePixelChannel:
   1587             {
   1588               if ((flags & XiValue) == 0)
   1589                 return(0.0);
   1590               return(channel_info.xi);
   1591             }
   1592             case BlackPixelChannel:
   1593             {
   1594               if ((flags & ChiValue) == 0)
   1595                 return(0.0);
   1596               return(channel_info.chi);
   1597             }
   1598             case AlphaPixelChannel:
   1599             {
   1600               if ((flags & PsiValue) == 0)
   1601                 return(0.0);
   1602               return(channel_info.psi);
   1603             }
   1604             default:
   1605               return(0.0);
   1606           }
   1607         }
   1608       if (LocaleCompare(symbol,"c") == 0)
   1609         return(QuantumScale*pixel.red);
   1610       break;
   1611     }
   1612     case 'D':
   1613     case 'd':
   1614     {
   1615       if (LocaleNCompare(symbol,"depth",5) == 0)
   1616         return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
   1617       break;
   1618     }
   1619     case 'G':
   1620     case 'g':
   1621     {
   1622       if (LocaleCompare(symbol,"g") == 0)
   1623         return(QuantumScale*pixel.green);
   1624       break;
   1625     }
   1626     case 'K':
   1627     case 'k':
   1628     {
   1629       if (LocaleNCompare(symbol,"kurtosis",8) == 0)
   1630         return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
   1631       if (LocaleCompare(symbol,"k") == 0)
   1632         {
   1633           if (image->colorspace != CMYKColorspace)
   1634             {
   1635               (void) ThrowMagickException(exception,GetMagickModule(),
   1636                 OptionError,"ColorSeparatedImageRequired","`%s'",
   1637                 image->filename);
   1638               return(0.0);
   1639             }
   1640           return(QuantumScale*pixel.black);
   1641         }
   1642       break;
   1643     }
   1644     case 'H':
   1645     case 'h':
   1646     {
   1647       if (LocaleCompare(symbol,"h") == 0)
   1648         return(image->rows);
   1649       if (LocaleCompare(symbol,"hue") == 0)
   1650         {
   1651           double
   1652             hue,
   1653             lightness,
   1654             saturation;
   1655 
   1656           ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
   1657             &lightness);
   1658           return(hue);
   1659         }
   1660       break;
   1661     }
   1662     case 'I':
   1663     case 'i':
   1664     {
   1665       if ((LocaleCompare(symbol,"image.depth") == 0) ||
   1666           (LocaleCompare(symbol,"image.minima") == 0) ||
   1667           (LocaleCompare(symbol,"image.maxima") == 0) ||
   1668           (LocaleCompare(symbol,"image.mean") == 0) ||
   1669           (LocaleCompare(symbol,"image.kurtosis") == 0) ||
   1670           (LocaleCompare(symbol,"image.skewness") == 0) ||
   1671           (LocaleCompare(symbol,"image.standard_deviation") == 0))
   1672         return(FxChannelStatistics(fx_info,image,channel,symbol+6,exception));
   1673       if (LocaleCompare(symbol,"image.resolution.x") == 0)
   1674         return(image->resolution.x);
   1675       if (LocaleCompare(symbol,"image.resolution.y") == 0)
   1676         return(image->resolution.y);
   1677       if (LocaleCompare(symbol,"intensity") == 0)
   1678         {
   1679           Quantum
   1680             quantum_pixel[MaxPixelChannels];
   1681 
   1682           SetPixelViaPixelInfo(image,&pixel,quantum_pixel);
   1683           return(QuantumScale*GetPixelIntensity(image,quantum_pixel));
   1684         }
   1685       if (LocaleCompare(symbol,"i") == 0)
   1686         return(x);
   1687       break;
   1688     }
   1689     case 'J':
   1690     case 'j':
   1691     {
   1692       if (LocaleCompare(symbol,"j") == 0)
   1693         return(y);
   1694       break;
   1695     }
   1696     case 'L':
   1697     case 'l':
   1698     {
   1699       if (LocaleCompare(symbol,"lightness") == 0)
   1700         {
   1701           double
   1702             hue,
   1703             lightness,
   1704             saturation;
   1705 
   1706           ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
   1707             &lightness);
   1708           return(lightness);
   1709         }
   1710       if (LocaleCompare(symbol,"luma") == 0)
   1711         {
   1712           double
   1713             luma;
   1714 
   1715           luma=0.212656*pixel.red+0.715158*pixel.green+0.072186*pixel.blue;
   1716           return(QuantumScale*luma);
   1717         }
   1718       if (LocaleCompare(symbol,"luminance") == 0)
   1719         {
   1720           double
   1721             luminence;
   1722 
   1723           luminence=0.212656*pixel.red+0.715158*pixel.green+0.072186*pixel.blue;
   1724           return(QuantumScale*luminence);
   1725         }
   1726       break;
   1727     }
   1728     case 'M':
   1729     case 'm':
   1730     {
   1731       if (LocaleNCompare(symbol,"maxima",6) == 0)
   1732         return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
   1733       if (LocaleNCompare(symbol,"mean",4) == 0)
   1734         return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
   1735       if (LocaleNCompare(symbol,"minima",6) == 0)
   1736         return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
   1737       if (LocaleCompare(symbol,"m") == 0)
   1738         return(QuantumScale*pixel.green);
   1739       break;
   1740     }
   1741     case 'N':
   1742     case 'n':
   1743     {
   1744       if (LocaleCompare(symbol,"n") == 0)
   1745         return(GetImageListLength(fx_info->images));
   1746       break;
   1747     }
   1748     case 'O':
   1749     case 'o':
   1750     {
   1751       if (LocaleCompare(symbol,"o") == 0)
   1752         return(QuantumScale*pixel.alpha);
   1753       break;
   1754     }
   1755     case 'P':
   1756     case 'p':
   1757     {
   1758       if (LocaleCompare(symbol,"page.height") == 0)
   1759         return(image->page.height);
   1760       if (LocaleCompare(symbol,"page.width") == 0)
   1761         return(image->page.width);
   1762       if (LocaleCompare(symbol,"page.x") == 0)
   1763         return(image->page.x);
   1764       if (LocaleCompare(symbol,"page.y") == 0)
   1765         return(image->page.y);
   1766       break;
   1767     }
   1768     case 'Q':
   1769     case 'q':
   1770     {
   1771       if (LocaleCompare(symbol,"quality") == 0)
   1772         return(image->quality);
   1773       break;
   1774     }
   1775     case 'R':
   1776     case 'r':
   1777     {
   1778       if (LocaleCompare(symbol,"resolution.x") == 0)
   1779         return(image->resolution.x);
   1780       if (LocaleCompare(symbol,"resolution.y") == 0)
   1781         return(image->resolution.y);
   1782       if (LocaleCompare(symbol,"r") == 0)
   1783         return(QuantumScale*pixel.red);
   1784       break;
   1785     }
   1786     case 'S':
   1787     case 's':
   1788     {
   1789       if (LocaleCompare(symbol,"saturation") == 0)
   1790         {
   1791           double
   1792             hue,
   1793             lightness,
   1794             saturation;
   1795 
   1796           ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
   1797             &lightness);
   1798           return(saturation);
   1799         }
   1800       if (LocaleNCompare(symbol,"skewness",8) == 0)
   1801         return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
   1802       if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
   1803         return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
   1804       break;
   1805     }
   1806     case 'T':
   1807     case 't':
   1808     {
   1809       if (LocaleCompare(symbol,"t") == 0)
   1810         return(GetImageIndexInList(fx_info->images));
   1811       break;
   1812     }
   1813     case 'W':
   1814     case 'w':
   1815     {
   1816       if (LocaleCompare(symbol,"w") == 0)
   1817         return(image->columns);
   1818       break;
   1819     }
   1820     case 'Y':
   1821     case 'y':
   1822     {
   1823       if (LocaleCompare(symbol,"y") == 0)
   1824         return(QuantumScale*pixel.blue);
   1825       break;
   1826     }
   1827     case 'Z':
   1828     case 'z':
   1829     {
   1830       if (LocaleCompare(symbol,"z") == 0)
   1831         return((double)GetImageDepth(image, fx_info->exception));
   1832       break;
   1833     }
   1834     default:
   1835       break;
   1836   }
   1837   value=(const char *) GetValueFromSplayTree(fx_info->symbols,symbol);
   1838   if (value != (const char *) NULL)
   1839     return(StringToDouble(value,(char **) NULL));
   1840   (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
   1841     "UnableToParseExpression","`%s'",symbol);
   1842   return(0.0);
   1843 }
   1844 
   1845 static const char *FxOperatorPrecedence(const char *expression,
   1846   ExceptionInfo *exception)
   1847 {
   1848   typedef enum
   1849   {
   1850     UndefinedPrecedence,
   1851     NullPrecedence,
   1852     BitwiseComplementPrecedence,
   1853     ExponentPrecedence,
   1854     ExponentialNotationPrecedence,
   1855     MultiplyPrecedence,
   1856     AdditionPrecedence,
   1857     ShiftPrecedence,
   1858     RelationalPrecedence,
   1859     EquivalencyPrecedence,
   1860     BitwiseAndPrecedence,
   1861     BitwiseOrPrecedence,
   1862     LogicalAndPrecedence,
   1863     LogicalOrPrecedence,
   1864     TernaryPrecedence,
   1865     AssignmentPrecedence,
   1866     CommaPrecedence,
   1867     SeparatorPrecedence
   1868   } FxPrecedence;
   1869 
   1870   FxPrecedence
   1871     precedence,
   1872     target;
   1873 
   1874   register const char
   1875     *subexpression;
   1876 
   1877   register int
   1878     c;
   1879 
   1880   size_t
   1881     level;
   1882 
   1883   c=0;
   1884   level=0;
   1885   subexpression=(const char *) NULL;
   1886   target=NullPrecedence;
   1887   while (*expression != '\0')
   1888   {
   1889     precedence=UndefinedPrecedence;
   1890     if ((isspace((int) ((unsigned char) *expression)) != 0) || (c == (int) '@'))
   1891       {
   1892         expression++;
   1893         continue;
   1894       }
   1895     switch (*expression)
   1896     {
   1897       case 'A':
   1898       case 'a':
   1899       {
   1900 #if defined(MAGICKCORE_HAVE_ACOSH)
   1901         if (LocaleNCompare(expression,"acosh",5) == 0)
   1902           {
   1903             expression+=5;
   1904             break;
   1905           }
   1906 #endif
   1907 #if defined(MAGICKCORE_HAVE_ASINH)
   1908         if (LocaleNCompare(expression,"asinh",5) == 0)
   1909           {
   1910             expression+=5;
   1911             break;
   1912           }
   1913 #endif
   1914 #if defined(MAGICKCORE_HAVE_ATANH)
   1915         if (LocaleNCompare(expression,"atanh",5) == 0)
   1916           {
   1917             expression+=5;
   1918             break;
   1919           }
   1920 #endif
   1921         if (LocaleNCompare(expression,"atan2",5) == 0)
   1922           {
   1923             expression+=5;
   1924             break;
   1925           }
   1926         break;
   1927       }
   1928       case 'E':
   1929       case 'e':
   1930       {
   1931         if ((isdigit((int) ((unsigned char) c)) != 0) &&
   1932             ((LocaleNCompare(expression,"E+",2) == 0) ||
   1933              (LocaleNCompare(expression,"E-",2) == 0)))
   1934           {
   1935             expression+=2;  /* scientific notation */
   1936             break;
   1937           }
   1938       }
   1939       case 'J':
   1940       case 'j':
   1941       {
   1942         if ((LocaleNCompare(expression,"j0",2) == 0) ||
   1943             (LocaleNCompare(expression,"j1",2) == 0))
   1944           {
   1945             expression+=2;
   1946             break;
   1947           }
   1948         break;
   1949       }
   1950       case '#':
   1951       {
   1952         while (isxdigit((int) ((unsigned char) *(expression+1))) != 0)
   1953           expression++;
   1954         break;
   1955       }
   1956       default:
   1957         break;
   1958     }
   1959     if ((c == (int) '{') || (c == (int) '['))
   1960       level++;
   1961     else
   1962       if ((c == (int) '}') || (c == (int) ']'))
   1963         level--;
   1964     if (level == 0)
   1965       switch ((unsigned char) *expression)
   1966       {
   1967         case '~':
   1968         case '!':
   1969         {
   1970           precedence=BitwiseComplementPrecedence;
   1971           break;
   1972         }
   1973         case '^':
   1974         case '@':
   1975         {
   1976           precedence=ExponentPrecedence;
   1977           break;
   1978         }
   1979         default:
   1980         {
   1981           if (((c != 0) && ((isdigit((int) ((unsigned char) c)) != 0) ||
   1982                (strchr(")",(int) ((unsigned char) c)) != (char *) NULL))) &&
   1983               (((islower((int) ((unsigned char) *expression)) != 0) ||
   1984                (strchr("(",(int) ((unsigned char) *expression)) != (char *) NULL)) ||
   1985                ((isdigit((int) ((unsigned char) c)) == 0) &&
   1986                 (isdigit((int) ((unsigned char) *expression)) != 0))) &&
   1987               (strchr("xy",(int) ((unsigned char) *expression)) == (char *) NULL))
   1988             precedence=MultiplyPrecedence;
   1989           break;
   1990         }
   1991         case '*':
   1992         case '/':
   1993         case '%':
   1994         {
   1995           precedence=MultiplyPrecedence;
   1996           break;
   1997         }
   1998         case '+':
   1999         case '-':
   2000         {
   2001           if ((strchr("(+-/*%:&^|<>~,",c) == (char *) NULL) ||
   2002               (isalpha(c) != 0))
   2003             precedence=AdditionPrecedence;
   2004           break;
   2005         }
   2006         case LeftShiftOperator:
   2007         case RightShiftOperator:
   2008         {
   2009           precedence=ShiftPrecedence;
   2010           break;
   2011         }
   2012         case '<':
   2013         case LessThanEqualOperator:
   2014         case GreaterThanEqualOperator:
   2015         case '>':
   2016         {
   2017           precedence=RelationalPrecedence;
   2018           break;
   2019         }
   2020         case EqualOperator:
   2021         case NotEqualOperator:
   2022         {
   2023           precedence=EquivalencyPrecedence;
   2024           break;
   2025         }
   2026         case '&':
   2027         {
   2028           precedence=BitwiseAndPrecedence;
   2029           break;
   2030         }
   2031         case '|':
   2032         {
   2033           precedence=BitwiseOrPrecedence;
   2034           break;
   2035         }
   2036         case LogicalAndOperator:
   2037         {
   2038           precedence=LogicalAndPrecedence;
   2039           break;
   2040         }
   2041         case LogicalOrOperator:
   2042         {
   2043           precedence=LogicalOrPrecedence;
   2044           break;
   2045         }
   2046         case ExponentialNotation:
   2047         {
   2048           precedence=ExponentialNotationPrecedence;
   2049           break;
   2050         }
   2051         case ':':
   2052         case '?':
   2053         {
   2054           precedence=TernaryPrecedence;
   2055           break;
   2056         }
   2057         case '=':
   2058         {
   2059           precedence=AssignmentPrecedence;
   2060           break;
   2061         }
   2062         case ',':
   2063         {
   2064           precedence=CommaPrecedence;
   2065           break;
   2066         }
   2067         case ';':
   2068         {
   2069           precedence=SeparatorPrecedence;
   2070           break;
   2071         }
   2072       }
   2073     if ((precedence == BitwiseComplementPrecedence) ||
   2074         (precedence == TernaryPrecedence) ||
   2075         (precedence == AssignmentPrecedence))
   2076       {
   2077         if (precedence > target)
   2078           {
   2079             /*
   2080               Right-to-left associativity.
   2081             */
   2082             target=precedence;
   2083             subexpression=expression;
   2084           }
   2085       }
   2086     else
   2087       if (precedence >= target)
   2088         {
   2089           /*
   2090             Left-to-right associativity.
   2091           */
   2092           target=precedence;
   2093           subexpression=expression;
   2094         }
   2095     if (strchr("(",(int) *expression) != (char *) NULL)
   2096       expression=FxSubexpression(expression,exception);
   2097     c=(int) (*expression++);
   2098   }
   2099   return(subexpression);
   2100 }
   2101 
   2102 static double FxEvaluateSubexpression(FxInfo *fx_info,
   2103   const PixelChannel channel,const ssize_t x,const ssize_t y,
   2104   const char *expression,size_t *depth,double *beta,ExceptionInfo *exception)
   2105 {
   2106 #define FxMaxParenthesisDepth  58
   2107 
   2108   char
   2109     *q,
   2110     subexpression[MagickPathExtent];
   2111 
   2112   double
   2113     alpha,
   2114     gamma;
   2115 
   2116   register const char
   2117     *p;
   2118 
   2119   *beta=0.0;
   2120   if (exception->severity >= ErrorException)
   2121     return(0.0);
   2122   while (isspace((int) ((unsigned char) *expression)) != 0)
   2123     expression++;
   2124   if (*expression == '\0')
   2125     return(0.0);
   2126   *subexpression='\0';
   2127   p=FxOperatorPrecedence(expression,exception);
   2128   if (p != (const char *) NULL)
   2129     {
   2130       (void) CopyMagickString(subexpression,expression,(size_t)
   2131         (p-expression+1));
   2132       alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,depth,
   2133         beta,exception);
   2134       switch ((unsigned char) *p)
   2135       {
   2136         case '~':
   2137         {
   2138           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth,beta,
   2139             exception);
   2140           *beta=(double) (~(size_t) *beta);
   2141           return(*beta);
   2142         }
   2143         case '!':
   2144         {
   2145           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth,beta,
   2146             exception);
   2147           return(*beta == 0.0 ? 1.0 : 0.0);
   2148         }
   2149         case '^':
   2150         {
   2151           *beta=pow(alpha,FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth,
   2152             beta,exception));
   2153           return(*beta);
   2154         }
   2155         case '*':
   2156         case ExponentialNotation:
   2157         {
   2158           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth,beta,
   2159             exception);
   2160           return(alpha*(*beta));
   2161         }
   2162         case '/':
   2163         {
   2164           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth,beta,
   2165             exception);
   2166           if (*beta == 0.0)
   2167             {
   2168               (void) ThrowMagickException(exception,GetMagickModule(),
   2169                 OptionError,"DivideByZero","`%s'",expression);
   2170               return(0.0);
   2171             }
   2172           return(alpha/(*beta));
   2173         }
   2174         case '%':
   2175         {
   2176           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth,beta,
   2177             exception);
   2178           *beta=fabs(floor((*beta)+0.5));
   2179           if (*beta == 0.0)
   2180             {
   2181               (void) ThrowMagickException(exception,GetMagickModule(),
   2182                 OptionError,"DivideByZero","`%s'",expression);
   2183               return(0.0);
   2184             }
   2185           return(fmod(alpha,*beta));
   2186         }
   2187         case '+':
   2188         {
   2189           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth,beta,
   2190             exception);
   2191           return(alpha+(*beta));
   2192         }
   2193         case '-':
   2194         {
   2195           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth,beta,
   2196             exception);
   2197           return(alpha-(*beta));
   2198         }
   2199         case LeftShiftOperator:
   2200         {
   2201           gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth,beta,
   2202             exception);
   2203           *beta=(double) ((size_t) (alpha+0.5) << (size_t) (gamma+0.5));
   2204           return(*beta);
   2205         }
   2206         case RightShiftOperator:
   2207         {
   2208           gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth,beta,
   2209             exception);
   2210           *beta=(double) ((size_t) (alpha+0.5) >> (size_t) (gamma+0.5));
   2211           return(*beta);
   2212         }
   2213         case '<':
   2214         {
   2215           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth,beta,
   2216             exception);
   2217           return(alpha < *beta ? 1.0 : 0.0);
   2218         }
   2219         case LessThanEqualOperator:
   2220         {
   2221           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth,beta,
   2222             exception);
   2223           return(alpha <= *beta ? 1.0 : 0.0);
   2224         }
   2225         case '>':
   2226         {
   2227           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth,beta,
   2228             exception);
   2229           return(alpha > *beta ? 1.0 : 0.0);
   2230         }
   2231         case GreaterThanEqualOperator:
   2232         {
   2233           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth,beta,
   2234             exception);
   2235           return(alpha >= *beta ? 1.0 : 0.0);
   2236         }
   2237         case EqualOperator:
   2238         {
   2239           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth,beta,
   2240             exception);
   2241           return(fabs(alpha-(*beta)) < MagickEpsilon ? 1.0 : 0.0);
   2242         }
   2243         case NotEqualOperator:
   2244         {
   2245           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth,beta,
   2246             exception);
   2247           return(fabs(alpha-(*beta)) >= MagickEpsilon ? 1.0 : 0.0);
   2248         }
   2249         case '&':
   2250         {
   2251           gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth,beta,
   2252             exception);
   2253           *beta=(double) ((size_t) (alpha+0.5) & (size_t) (gamma+0.5));
   2254           return(*beta);
   2255         }
   2256         case '|':
   2257         {
   2258           gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth,beta,
   2259             exception);
   2260           *beta=(double) ((size_t) (alpha+0.5) | (size_t) (gamma+0.5));
   2261           return(*beta);
   2262         }
   2263         case LogicalAndOperator:
   2264         {
   2265           p++;
   2266           if (alpha <= 0.0)
   2267             {
   2268               *beta=0.0;
   2269               return(*beta);
   2270             }
   2271           gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,depth,beta,
   2272             exception);
   2273           *beta=(gamma > 0.0) ? 1.0 : 0.0;
   2274           return(*beta);
   2275         }
   2276         case LogicalOrOperator:
   2277         {
   2278           p++;
   2279           if (alpha > 0.0)
   2280             {
   2281              *beta=1.0;
   2282              return(*beta);
   2283             }
   2284           gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,depth,beta,
   2285             exception);
   2286           *beta=(gamma > 0.0) ? 1.0 : 0.0;
   2287           return(*beta);
   2288         }
   2289         case '?':
   2290         {
   2291           (void) CopyMagickString(subexpression,++p,MagickPathExtent);
   2292           q=subexpression;
   2293           p=StringToken(":",&q);
   2294           if (q == (char *) NULL)
   2295             {
   2296               (void) ThrowMagickException(exception,GetMagickModule(),
   2297                 OptionError,"UnableToParseExpression","`%s'",subexpression);
   2298               return(0.0);
   2299             }
   2300           if (fabs(alpha) >= MagickEpsilon)
   2301             gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,depth,beta,
   2302               exception);
   2303           else
   2304             gamma=FxEvaluateSubexpression(fx_info,channel,x,y,q,depth,beta,
   2305               exception);
   2306           return(gamma);
   2307         }
   2308         case '=':
   2309         {
   2310           char
   2311             numeric[MagickPathExtent];
   2312 
   2313           q=subexpression;
   2314           while (isalpha((int) ((unsigned char) *q)) != 0)
   2315             q++;
   2316           if (*q != '\0')
   2317             {
   2318               (void) ThrowMagickException(exception,GetMagickModule(),
   2319                 OptionError,"UnableToParseExpression","`%s'",subexpression);
   2320               return(0.0);
   2321             }
   2322           ClearMagickException(exception);
   2323           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth,beta,
   2324             exception);
   2325           (void) FormatLocaleString(numeric,MagickPathExtent,"%g",*beta);
   2326           (void) DeleteNodeFromSplayTree(fx_info->symbols,subexpression);
   2327           (void) AddValueToSplayTree(fx_info->symbols,ConstantString(
   2328             subexpression),ConstantString(numeric));
   2329           return(*beta);
   2330         }
   2331         case ',':
   2332         {
   2333           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth,beta,
   2334             exception);
   2335           return(alpha);
   2336         }
   2337         case ';':
   2338         {
   2339           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth,beta,
   2340             exception);
   2341           return(*beta);
   2342         }
   2343         default:
   2344         {
   2345           gamma=alpha*FxEvaluateSubexpression(fx_info,channel,x,y,p,depth,beta,
   2346             exception);
   2347           return(gamma);
   2348         }
   2349       }
   2350     }
   2351   if (strchr("(",(int) *expression) != (char *) NULL)
   2352     {
   2353       (*depth)++;
   2354       if (*depth >= FxMaxParenthesisDepth)
   2355         (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
   2356           "ParenthesisNestedTooDeeply","`%s'",expression);
   2357       (void) CopyMagickString(subexpression,expression+1,MagickPathExtent);
   2358       subexpression[strlen(subexpression)-1]='\0';
   2359       gamma=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,depth,
   2360         beta,exception);
   2361       (*depth)--;
   2362       return(gamma);
   2363     }
   2364   switch (*expression)
   2365   {
   2366     case '+':
   2367     {
   2368       gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,depth,beta,
   2369         exception);
   2370       return(1.0*gamma);
   2371     }
   2372     case '-':
   2373     {
   2374       gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,depth,beta,
   2375         exception);
   2376       return(-1.0*gamma);
   2377     }
   2378     case '~':
   2379     {
   2380       gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,depth,beta,
   2381         exception);
   2382       return((~(size_t) (gamma+0.5)));
   2383     }
   2384     case 'A':
   2385     case 'a':
   2386     {
   2387       if (LocaleNCompare(expression,"abs",3) == 0)
   2388         {
   2389           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,depth,
   2390             beta,exception);
   2391           return(fabs(alpha));
   2392         }
   2393 #if defined(MAGICKCORE_HAVE_ACOSH)
   2394       if (LocaleNCompare(expression,"acosh",5) == 0)
   2395         {
   2396           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,depth,
   2397             beta,exception);
   2398           return(acosh(alpha));
   2399         }
   2400 #endif
   2401       if (LocaleNCompare(expression,"acos",4) == 0)
   2402         {
   2403           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,depth,
   2404             beta,exception);
   2405           return(acos(alpha));
   2406         }
   2407 #if defined(MAGICKCORE_HAVE_J1)
   2408       if (LocaleNCompare(expression,"airy",4) == 0)
   2409         {
   2410           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,depth,
   2411             beta,exception);
   2412           if (alpha == 0.0)
   2413             return(1.0);
   2414           gamma=2.0*j1((MagickPI*alpha))/(MagickPI*alpha);
   2415           return(gamma*gamma);
   2416         }
   2417 #endif
   2418 #if defined(MAGICKCORE_HAVE_ASINH)
   2419       if (LocaleNCompare(expression,"asinh",5) == 0)
   2420         {
   2421           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,depth,
   2422             beta,exception);
   2423           return(asinh(alpha));
   2424         }
   2425 #endif
   2426       if (LocaleNCompare(expression,"asin",4) == 0)
   2427         {
   2428           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,depth,
   2429             beta,exception);
   2430           return(asin(alpha));
   2431         }
   2432       if (LocaleNCompare(expression,"alt",3) == 0)
   2433         {
   2434           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,depth,
   2435             beta,exception);
   2436           return(((ssize_t) alpha) & 0x01 ? -1.0 : 1.0);
   2437         }
   2438       if (LocaleNCompare(expression,"atan2",5) == 0)
   2439         {
   2440           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,depth,
   2441             beta,exception);
   2442           return(atan2(alpha,*beta));
   2443         }
   2444 #if defined(MAGICKCORE_HAVE_ATANH)
   2445       if (LocaleNCompare(expression,"atanh",5) == 0)
   2446         {
   2447           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,depth,
   2448             beta,exception);
   2449           return(atanh(alpha));
   2450         }
   2451 #endif
   2452       if (LocaleNCompare(expression,"atan",4) == 0)
   2453         {
   2454           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,depth,
   2455             beta,exception);
   2456           return(atan(alpha));
   2457         }
   2458       if (LocaleCompare(expression,"a") == 0)
   2459         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
   2460       break;
   2461     }
   2462     case 'B':
   2463     case 'b':
   2464     {
   2465       if (LocaleCompare(expression,"b") == 0)
   2466         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
   2467       break;
   2468     }
   2469     case 'C':
   2470     case 'c':
   2471     {
   2472       if (LocaleNCompare(expression,"ceil",4) == 0)
   2473         {
   2474           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,depth,
   2475             beta,exception);
   2476           return(ceil(alpha));
   2477         }
   2478       if (LocaleNCompare(expression,"clamp",5) == 0)
   2479         {
   2480           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,depth,
   2481             beta,exception);
   2482           if (alpha < 0.0)
   2483             return(0.0);
   2484           if (alpha > 1.0)
   2485             return(1.0);
   2486           return(alpha);
   2487         }
   2488       if (LocaleNCompare(expression,"cosh",4) == 0)
   2489         {
   2490           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,depth,
   2491             beta,exception);
   2492           return(cosh(alpha));
   2493         }
   2494       if (LocaleNCompare(expression,"cos",3) == 0)
   2495         {
   2496           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,depth,
   2497             beta,exception);
   2498           return(cos(alpha));
   2499         }
   2500       if (LocaleCompare(expression,"c") == 0)
   2501         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
   2502       break;
   2503     }
   2504     case 'D':
   2505     case 'd':
   2506     {
   2507       if (LocaleNCompare(expression,"debug",5) == 0)
   2508         {
   2509           const char
   2510             *type;
   2511 
   2512           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,depth,
   2513             beta,exception);
   2514           if (fx_info->images->colorspace == CMYKColorspace)
   2515             switch (channel)
   2516             {
   2517               case CyanPixelChannel: type="cyan"; break;
   2518               case MagentaPixelChannel: type="magenta"; break;
   2519               case YellowPixelChannel: type="yellow"; break;
   2520               case AlphaPixelChannel: type="opacity"; break;
   2521               case BlackPixelChannel: type="black"; break;
   2522               default: type="unknown"; break;
   2523             }
   2524           else
   2525             switch (channel)
   2526             {
   2527               case RedPixelChannel: type="red"; break;
   2528               case GreenPixelChannel: type="green"; break;
   2529               case BluePixelChannel: type="blue"; break;
   2530               case AlphaPixelChannel: type="opacity"; break;
   2531               default: type="unknown"; break;
   2532             }
   2533           (void) CopyMagickString(subexpression,expression+6,MagickPathExtent);
   2534           if (strlen(subexpression) > 1)
   2535             subexpression[strlen(subexpression)-1]='\0';
   2536           if (fx_info->file != (FILE *) NULL)
   2537             (void) FormatLocaleFile(fx_info->file,"%s[%.20g,%.20g].%s: "
   2538                "%s=%.*g\n",fx_info->images->filename,(double) x,(double) y,type,
   2539                subexpression,GetMagickPrecision(),alpha);
   2540           return(0.0);
   2541         }
   2542       if (LocaleNCompare(expression,"drc",3) == 0)
   2543         {
   2544           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,depth,
   2545             beta,exception);
   2546           return((alpha/(*beta*(alpha-1.0)+1.0)));
   2547         }
   2548       break;
   2549     }
   2550     case 'E':
   2551     case 'e':
   2552     {
   2553       if (LocaleCompare(expression,"epsilon") == 0)
   2554         return(MagickEpsilon);
   2555 #if defined(MAGICKCORE_HAVE_ERF)
   2556       if (LocaleNCompare(expression,"erf",3) == 0)
   2557         {
   2558           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,depth,
   2559             beta,exception);
   2560           return(erf(alpha));
   2561         }
   2562 #endif
   2563       if (LocaleNCompare(expression,"exp",3) == 0)
   2564         {
   2565           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,depth,
   2566             beta,exception);
   2567           return(exp(alpha));
   2568         }
   2569       if (LocaleCompare(expression,"e") == 0)
   2570         return(2.7182818284590452354);
   2571       break;
   2572     }
   2573     case 'F':
   2574     case 'f':
   2575     {
   2576       if (LocaleNCompare(expression,"floor",5) == 0)
   2577         {
   2578           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,depth,
   2579             beta,exception);
   2580           return(floor(alpha));
   2581         }
   2582       break;
   2583     }
   2584     case 'G':
   2585     case 'g':
   2586     {
   2587       if (LocaleNCompare(expression,"gauss",5) == 0)
   2588         {
   2589           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,depth,
   2590             beta,exception);
   2591           gamma=exp((-alpha*alpha/2.0))/sqrt(2.0*MagickPI);
   2592           return(gamma);
   2593         }
   2594       if (LocaleNCompare(expression,"gcd",3) == 0)
   2595         {
   2596           MagickOffsetType
   2597             gcd;
   2598 
   2599           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,depth,
   2600             beta,exception);
   2601           gcd=FxGCD((MagickOffsetType) (alpha+0.5),(MagickOffsetType) (*beta+
   2602             0.5));
   2603           return(gcd);
   2604         }
   2605       if (LocaleCompare(expression,"g") == 0)
   2606         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
   2607       break;
   2608     }
   2609     case 'H':
   2610     case 'h':
   2611     {
   2612       if (LocaleCompare(expression,"h") == 0)
   2613         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
   2614       if (LocaleCompare(expression,"hue") == 0)
   2615         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
   2616       if (LocaleNCompare(expression,"hypot",5) == 0)
   2617         {
   2618           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,depth,
   2619             beta,exception);
   2620           return(hypot(alpha,*beta));
   2621         }
   2622       break;
   2623     }
   2624     case 'K':
   2625     case 'k':
   2626     {
   2627       if (LocaleCompare(expression,"k") == 0)
   2628         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
   2629       break;
   2630     }
   2631     case 'I':
   2632     case 'i':
   2633     {
   2634       if (LocaleCompare(expression,"intensity") == 0)
   2635         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
   2636       if (LocaleNCompare(expression,"int",3) == 0)
   2637         {
   2638           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,depth,
   2639             beta,exception);
   2640           return(floor(alpha));
   2641         }
   2642       if (LocaleNCompare(expression,"isnan",5) == 0)
   2643         {
   2644           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,depth,
   2645             beta,exception);
   2646           return(!!IsNaN(alpha));
   2647         }
   2648       if (LocaleCompare(expression,"i") == 0)
   2649         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
   2650       break;
   2651     }
   2652     case 'J':
   2653     case 'j':
   2654     {
   2655       if (LocaleCompare(expression,"j") == 0)
   2656         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
   2657 #if defined(MAGICKCORE_HAVE_J0)
   2658       if (LocaleNCompare(expression,"j0",2) == 0)
   2659         {
   2660           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,depth,
   2661             beta,exception);
   2662           return(j0(alpha));
   2663         }
   2664 #endif
   2665 #if defined(MAGICKCORE_HAVE_J1)
   2666       if (LocaleNCompare(expression,"j1",2) == 0)
   2667         {
   2668           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,depth,
   2669             beta,exception);
   2670           return(j1(alpha));
   2671         }
   2672 #endif
   2673 #if defined(MAGICKCORE_HAVE_J1)
   2674       if (LocaleNCompare(expression,"jinc",4) == 0)
   2675         {
   2676           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,depth,
   2677             beta,exception);
   2678           if (alpha == 0.0)
   2679             return(1.0);
   2680           gamma=(2.0*j1((MagickPI*alpha))/(MagickPI*alpha));
   2681           return(gamma);
   2682         }
   2683 #endif
   2684       break;
   2685     }
   2686     case 'L':
   2687     case 'l':
   2688     {
   2689       if (LocaleNCompare(expression,"ln",2) == 0)
   2690         {
   2691           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,depth,
   2692             beta,exception);
   2693           return(log(alpha));
   2694         }
   2695       if (LocaleNCompare(expression,"logtwo",6) == 0)
   2696         {
   2697           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+6,depth,
   2698             beta,exception);
   2699           return(log10(alpha))/log10(2.0);
   2700         }
   2701       if (LocaleNCompare(expression,"log",3) == 0)
   2702         {
   2703           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,depth,
   2704             beta,exception);
   2705           return(log10(alpha));
   2706         }
   2707       if (LocaleCompare(expression,"lightness") == 0)
   2708         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
   2709       break;
   2710     }
   2711     case 'M':
   2712     case 'm':
   2713     {
   2714       if (LocaleCompare(expression,"MaxRGB") == 0)
   2715         return(QuantumRange);
   2716       if (LocaleNCompare(expression,"maxima",6) == 0)
   2717         break;
   2718       if (LocaleNCompare(expression,"max",3) == 0)
   2719         {
   2720           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,depth,
   2721             beta,exception);
   2722           return(alpha > *beta ? alpha : *beta);
   2723         }
   2724       if (LocaleNCompare(expression,"minima",6) == 0)
   2725         break;
   2726       if (LocaleNCompare(expression,"min",3) == 0)
   2727         {
   2728           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,depth,
   2729             beta,exception);
   2730           return(alpha < *beta ? alpha : *beta);
   2731         }
   2732       if (LocaleNCompare(expression,"mod",3) == 0)
   2733         {
   2734           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,depth,
   2735             beta,exception);
   2736           gamma=alpha-floor((alpha/(*beta)))*(*beta);
   2737           return(gamma);
   2738         }
   2739       if (LocaleCompare(expression,"m") == 0)
   2740         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
   2741       break;
   2742     }
   2743     case 'N':
   2744     case 'n':
   2745     {
   2746       if (LocaleNCompare(expression,"not",3) == 0)
   2747         {
   2748           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,depth,
   2749             beta,exception);
   2750           return((alpha < MagickEpsilon));
   2751         }
   2752       if (LocaleCompare(expression,"n") == 0)
   2753         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
   2754       break;
   2755     }
   2756     case 'O':
   2757     case 'o':
   2758     {
   2759       if (LocaleCompare(expression,"Opaque") == 0)
   2760         return(1.0);
   2761       if (LocaleCompare(expression,"o") == 0)
   2762         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
   2763       break;
   2764     }
   2765     case 'P':
   2766     case 'p':
   2767     {
   2768       if (LocaleCompare(expression,"phi") == 0)
   2769         return(MagickPHI);
   2770       if (LocaleCompare(expression,"pi") == 0)
   2771         return(MagickPI);
   2772       if (LocaleNCompare(expression,"pow",3) == 0)
   2773         {
   2774           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,depth,
   2775             beta,exception);
   2776           return(pow(alpha,*beta));
   2777         }
   2778       if (LocaleCompare(expression,"p") == 0)
   2779         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
   2780       break;
   2781     }
   2782     case 'Q':
   2783     case 'q':
   2784     {
   2785       if (LocaleCompare(expression,"QuantumRange") == 0)
   2786         return(QuantumRange);
   2787       if (LocaleCompare(expression,"QuantumScale") == 0)
   2788         return(QuantumScale);
   2789       break;
   2790     }
   2791     case 'R':
   2792     case 'r':
   2793     {
   2794       if (LocaleNCompare(expression,"rand",4) == 0)
   2795         {
   2796 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   2797         #pragma omp critical (MagickCore_FxEvaluateSubexpression)
   2798 #endif
   2799           alpha=GetPseudoRandomValue(fx_info->random_info);
   2800           return(alpha);
   2801         }
   2802       if (LocaleNCompare(expression,"round",5) == 0)
   2803         {
   2804           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,depth,
   2805             beta,exception);
   2806           return(floor(alpha+0.5));
   2807         }
   2808       if (LocaleCompare(expression,"r") == 0)
   2809         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
   2810       break;
   2811     }
   2812     case 'S':
   2813     case 's':
   2814     {
   2815       if (LocaleCompare(expression,"saturation") == 0)
   2816         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
   2817       if (LocaleNCompare(expression,"sign",4) == 0)
   2818         {
   2819           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,depth,
   2820             beta,exception);
   2821           return(alpha < 0.0 ? -1.0 : 1.0);
   2822         }
   2823       if (LocaleNCompare(expression,"sinc",4) == 0)
   2824         {
   2825           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,depth,
   2826             beta,exception);
   2827           if (alpha == 0)
   2828             return(1.0);
   2829           gamma=sin((MagickPI*alpha))/(MagickPI*alpha);
   2830           return(gamma);
   2831         }
   2832       if (LocaleNCompare(expression,"sinh",4) == 0)
   2833         {
   2834           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,depth,
   2835             beta,exception);
   2836           return(sinh(alpha));
   2837         }
   2838       if (LocaleNCompare(expression,"sin",3) == 0)
   2839         {
   2840           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,depth,
   2841             beta,exception);
   2842           return(sin(alpha));
   2843         }
   2844       if (LocaleNCompare(expression,"sqrt",4) == 0)
   2845         {
   2846           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,depth,
   2847             beta,exception);
   2848           return(sqrt(alpha));
   2849         }
   2850       if (LocaleNCompare(expression,"squish",6) == 0)
   2851         {
   2852           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+6,depth,
   2853             beta,exception);
   2854           return((1.0/(1.0+exp(-alpha))));
   2855         }
   2856       if (LocaleCompare(expression,"s") == 0)
   2857         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
   2858       break;
   2859     }
   2860     case 'T':
   2861     case 't':
   2862     {
   2863       if (LocaleNCompare(expression,"tanh",4) == 0)
   2864         {
   2865           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,depth,
   2866             beta,exception);
   2867           return(tanh(alpha));
   2868         }
   2869       if (LocaleNCompare(expression,"tan",3) == 0)
   2870         {
   2871           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,depth,
   2872             beta,exception);
   2873           return(tan(alpha));
   2874         }
   2875       if (LocaleCompare(expression,"Transparent") == 0)
   2876         return(0.0);
   2877       if (LocaleNCompare(expression,"trunc",5) == 0)
   2878         {
   2879           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,depth,
   2880             beta,exception);
   2881           if (alpha >= 0.0)
   2882             return(floor(alpha));
   2883           return(ceil(alpha));
   2884         }
   2885       if (LocaleCompare(expression,"t") == 0)
   2886         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
   2887       break;
   2888     }
   2889     case 'U':
   2890     case 'u':
   2891     {
   2892       if (LocaleCompare(expression,"u") == 0)
   2893         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
   2894       break;
   2895     }
   2896     case 'V':
   2897     case 'v':
   2898     {
   2899       if (LocaleCompare(expression,"v") == 0)
   2900         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
   2901       break;
   2902     }
   2903     case 'W':
   2904     case 'w':
   2905     {
   2906       if (LocaleNCompare(expression,"while",5) == 0)
   2907         {
   2908           do
   2909           {
   2910             alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
   2911               depth,beta,exception);
   2912           } while (fabs(alpha) >= MagickEpsilon);
   2913           return(*beta);
   2914         }
   2915       if (LocaleCompare(expression,"w") == 0)
   2916         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
   2917       break;
   2918     }
   2919     case 'Y':
   2920     case 'y':
   2921     {
   2922       if (LocaleCompare(expression,"y") == 0)
   2923         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
   2924       break;
   2925     }
   2926     case 'Z':
   2927     case 'z':
   2928     {
   2929       if (LocaleCompare(expression,"z") == 0)
   2930         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
   2931       break;
   2932     }
   2933     default:
   2934       break;
   2935   }
   2936   q=(char *) expression;
   2937   alpha=InterpretSiPrefixValue(expression,&q);
   2938   if (q == expression)
   2939     return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
   2940   return(alpha);
   2941 }
   2942 
   2943 MagickPrivate MagickBooleanType FxEvaluateExpression(FxInfo *fx_info,
   2944   double *alpha,ExceptionInfo *exception)
   2945 {
   2946   MagickBooleanType
   2947     status;
   2948 
   2949   status=FxEvaluateChannelExpression(fx_info,GrayPixelChannel,0,0,alpha,
   2950     exception);
   2951   return(status);
   2952 }
   2953 
   2954 MagickExport MagickBooleanType FxPreprocessExpression(FxInfo *fx_info,
   2955   double *alpha,ExceptionInfo *exception)
   2956 {
   2957   FILE
   2958     *file;
   2959 
   2960   MagickBooleanType
   2961     status;
   2962 
   2963   file=fx_info->file;
   2964   fx_info->file=(FILE *) NULL;
   2965   status=FxEvaluateChannelExpression(fx_info,GrayPixelChannel,0,0,alpha,
   2966     exception);
   2967   fx_info->file=file;
   2968   return(status);
   2969 }
   2970 
   2971 MagickPrivate MagickBooleanType FxEvaluateChannelExpression(FxInfo *fx_info,
   2972   const PixelChannel channel,const ssize_t x,const ssize_t y,
   2973   double *alpha,ExceptionInfo *exception)
   2974 {
   2975   double
   2976     beta;
   2977 
   2978   size_t
   2979     depth;
   2980 
   2981   depth=0;
   2982   beta=0.0;
   2983   *alpha=FxEvaluateSubexpression(fx_info,channel,x,y,fx_info->expression,&depth,
   2984     &beta,exception);
   2985   return(exception->severity == OptionError ? MagickFalse : MagickTrue);
   2986 }
   2987 
   2988 
   2989 /*
   2990 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2991 %                                                                             %
   2992 %                                                                             %
   2993 %                                                                             %
   2994 %     F x I m a g e                                                           %
   2995 %                                                                             %
   2996 %                                                                             %
   2997 %                                                                             %
   2998 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2999 %
   3000 %  FxImage() applies a mathematical expression to the specified image.
   3001 %
   3002 %  The format of the FxImage method is:
   3003 %
   3004 %      Image *FxImage(const Image *image,const char *expression,
   3005 %        ExceptionInfo *exception)
   3006 %
   3007 %  A description of each parameter follows:
   3008 %
   3009 %    o image: the image.
   3010 %
   3011 %    o expression: A mathematical expression.
   3012 %
   3013 %    o exception: return any errors or warnings in this structure.
   3014 %
   3015 */
   3016 
   3017 static FxInfo **DestroyFxThreadSet(FxInfo **fx_info)
   3018 {
   3019   register ssize_t
   3020     i;
   3021 
   3022   assert(fx_info != (FxInfo **) NULL);
   3023   for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
   3024     if (fx_info[i] != (FxInfo *) NULL)
   3025       fx_info[i]=DestroyFxInfo(fx_info[i]);
   3026   fx_info=(FxInfo **) RelinquishMagickMemory(fx_info);
   3027   return(fx_info);
   3028 }
   3029 
   3030 static FxInfo **AcquireFxThreadSet(const Image *image,const char *expression,
   3031   ExceptionInfo *exception)
   3032 {
   3033   char
   3034     *fx_expression;
   3035 
   3036   FxInfo
   3037     **fx_info;
   3038 
   3039   double
   3040     alpha;
   3041 
   3042   register ssize_t
   3043     i;
   3044 
   3045   size_t
   3046     number_threads;
   3047 
   3048   number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
   3049   fx_info=(FxInfo **) AcquireQuantumMemory(number_threads,sizeof(*fx_info));
   3050   if (fx_info == (FxInfo **) NULL)
   3051     {
   3052       (void) ThrowMagickException(exception,GetMagickModule(),
   3053         ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
   3054       return((FxInfo **) NULL);
   3055     }
   3056   (void) ResetMagickMemory(fx_info,0,number_threads*sizeof(*fx_info));
   3057   if (*expression != '@')
   3058     fx_expression=ConstantString(expression);
   3059   else
   3060     fx_expression=FileToString(expression+1,~0UL,exception);
   3061   for (i=0; i < (ssize_t) number_threads; i++)
   3062   {
   3063     MagickBooleanType
   3064       status;
   3065 
   3066     fx_info[i]=AcquireFxInfo(image,fx_expression,exception);
   3067     if (fx_info[i] == (FxInfo *) NULL)
   3068       break;
   3069     status=FxPreprocessExpression(fx_info[i],&alpha,exception);
   3070     if (status == MagickFalse)
   3071       break;
   3072   }
   3073   fx_expression=DestroyString(fx_expression);
   3074   if (i < (ssize_t) number_threads)
   3075     fx_info=DestroyFxThreadSet(fx_info);
   3076   return(fx_info);
   3077 }
   3078 
   3079 MagickExport Image *FxImage(const Image *image,const char *expression,
   3080   ExceptionInfo *exception)
   3081 {
   3082 #define FxImageTag  "Fx/Image"
   3083 
   3084   CacheView
   3085     *fx_view,
   3086     *image_view;
   3087 
   3088   FxInfo
   3089     **magick_restrict fx_info;
   3090 
   3091   Image
   3092     *fx_image;
   3093 
   3094   MagickBooleanType
   3095     status;
   3096 
   3097   MagickOffsetType
   3098     progress;
   3099 
   3100   ssize_t
   3101     y;
   3102 
   3103   assert(image != (Image *) NULL);
   3104   assert(image->signature == MagickCoreSignature);
   3105   if (image->debug != MagickFalse)
   3106     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   3107   fx_info=AcquireFxThreadSet(image,expression,exception);
   3108   if (fx_info == (FxInfo **) NULL)
   3109     return((Image *) NULL);
   3110   fx_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
   3111   if (fx_image == (Image *) NULL)
   3112     {
   3113       fx_info=DestroyFxThreadSet(fx_info);
   3114       return((Image *) NULL);
   3115     }
   3116   if (SetImageStorageClass(fx_image,DirectClass,exception) == MagickFalse)
   3117     {
   3118       fx_info=DestroyFxThreadSet(fx_info);
   3119       fx_image=DestroyImage(fx_image);
   3120       return((Image *) NULL);
   3121     }
   3122   /*
   3123     Fx image.
   3124   */
   3125   status=MagickTrue;
   3126   progress=0;
   3127   image_view=AcquireVirtualCacheView(image,exception);
   3128   fx_view=AcquireAuthenticCacheView(fx_image,exception);
   3129 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   3130   #pragma omp parallel for schedule(static,4) shared(progress,status) \
   3131     magick_threads(image,fx_image,fx_image->rows,1)
   3132 #endif
   3133   for (y=0; y < (ssize_t) fx_image->rows; y++)
   3134   {
   3135     const int
   3136       id = GetOpenMPThreadId();
   3137 
   3138     register const Quantum
   3139       *magick_restrict p;
   3140 
   3141     register Quantum
   3142       *magick_restrict q;
   3143 
   3144     register ssize_t
   3145       x;
   3146 
   3147     if (status == MagickFalse)
   3148       continue;
   3149     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
   3150     q=QueueCacheViewAuthenticPixels(fx_view,0,y,fx_image->columns,1,exception);
   3151     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
   3152       {
   3153         status=MagickFalse;
   3154         continue;
   3155       }
   3156     for (x=0; x < (ssize_t) fx_image->columns; x++)
   3157     {
   3158       register ssize_t
   3159         i;
   3160 
   3161       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   3162       {
   3163         double
   3164           alpha;
   3165 
   3166         PixelChannel channel=GetPixelChannelChannel(image,i);
   3167         PixelTrait traits=GetPixelChannelTraits(image,channel);
   3168         PixelTrait fx_traits=GetPixelChannelTraits(fx_image,channel);
   3169         if ((traits == UndefinedPixelTrait) ||
   3170             (fx_traits == UndefinedPixelTrait))
   3171           continue;
   3172         if (((fx_traits & CopyPixelTrait) != 0) ||
   3173             (GetPixelReadMask(image,p) == 0))
   3174           {
   3175             SetPixelChannel(fx_image,channel,p[i],q);
   3176             continue;
   3177           }
   3178         alpha=0.0;
   3179         (void) FxEvaluateChannelExpression(fx_info[id],channel,x,y,&alpha,
   3180           exception);
   3181         q[i]=ClampToQuantum(QuantumRange*alpha);
   3182       }
   3183       p+=GetPixelChannels(image);
   3184       q+=GetPixelChannels(fx_image);
   3185     }
   3186     if (SyncCacheViewAuthenticPixels(fx_view,exception) == MagickFalse)
   3187       status=MagickFalse;
   3188     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   3189       {
   3190         MagickBooleanType
   3191           proceed;
   3192 
   3193 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   3194         #pragma omp critical (MagickCore_FxImage)
   3195 #endif
   3196         proceed=SetImageProgress(image,FxImageTag,progress++,image->rows);
   3197         if (proceed == MagickFalse)
   3198           status=MagickFalse;
   3199       }
   3200   }
   3201   fx_view=DestroyCacheView(fx_view);
   3202   image_view=DestroyCacheView(image_view);
   3203   fx_info=DestroyFxThreadSet(fx_info);
   3204   if (status == MagickFalse)
   3205     fx_image=DestroyImage(fx_image);
   3206   return(fx_image);
   3207 }
   3208 
   3209 
   3210 /*
   3211 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   3212 %                                                                             %
   3213 %                                                                             %
   3214 %                                                                             %
   3215 %     I m p l o d e I m a g e                                                 %
   3216 %                                                                             %
   3217 %                                                                             %
   3218 %                                                                             %
   3219 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   3220 %
   3221 %  ImplodeImage() creates a new image that is a copy of an existing
   3222 %  one with the image pixels "implode" by the specified percentage.  It
   3223 %  allocates the memory necessary for the new Image structure and returns a
   3224 %  pointer to the new image.
   3225 %
   3226 %  The format of the ImplodeImage method is:
   3227 %
   3228 %      Image *ImplodeImage(const Image *image,const double amount,
   3229 %        const PixelInterpolateMethod method,ExceptionInfo *exception)
   3230 %
   3231 %  A description of each parameter follows:
   3232 %
   3233 %    o implode_image: Method ImplodeImage returns a pointer to the image
   3234 %      after it is implode.  A null image is returned if there is a memory
   3235 %      shortage.
   3236 %
   3237 %    o image: the image.
   3238 %
   3239 %    o amount:  Define the extent of the implosion.
   3240 %
   3241 %    o method: the pixel interpolation method.
   3242 %
   3243 %    o exception: return any errors or warnings in this structure.
   3244 %
   3245 */
   3246 MagickExport Image *ImplodeImage(const Image *image,const double amount,
   3247   const PixelInterpolateMethod method,ExceptionInfo *exception)
   3248 {
   3249 #define ImplodeImageTag  "Implode/Image"
   3250 
   3251   CacheView
   3252     *image_view,
   3253     *implode_view,
   3254     *interpolate_view;
   3255 
   3256   Image
   3257     *implode_image;
   3258 
   3259   MagickBooleanType
   3260     status;
   3261 
   3262   MagickOffsetType
   3263     progress;
   3264 
   3265   double
   3266     radius;
   3267 
   3268   PointInfo
   3269     center,
   3270     scale;
   3271 
   3272   ssize_t
   3273     y;
   3274 
   3275   /*
   3276     Initialize implode image attributes.
   3277   */
   3278   assert(image != (Image *) NULL);
   3279   assert(image->signature == MagickCoreSignature);
   3280   if (image->debug != MagickFalse)
   3281     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   3282   assert(exception != (ExceptionInfo *) NULL);
   3283   assert(exception->signature == MagickCoreSignature);
   3284   implode_image=CloneImage(image,image->columns,image->rows,MagickTrue,
   3285     exception);
   3286   if (implode_image == (Image *) NULL)
   3287     return((Image *) NULL);
   3288   if (SetImageStorageClass(implode_image,DirectClass,exception) == MagickFalse)
   3289     {
   3290       implode_image=DestroyImage(implode_image);
   3291       return((Image *) NULL);
   3292     }
   3293   if (implode_image->background_color.alpha != OpaqueAlpha)
   3294     implode_image->alpha_trait=BlendPixelTrait;
   3295   /*
   3296     Compute scaling factor.
   3297   */
   3298   scale.x=1.0;
   3299   scale.y=1.0;
   3300   center.x=0.5*image->columns;
   3301   center.y=0.5*image->rows;
   3302   radius=center.x;
   3303   if (image->columns > image->rows)
   3304     scale.y=(double) image->columns/(double) image->rows;
   3305   else
   3306     if (image->columns < image->rows)
   3307       {
   3308         scale.x=(double) image->rows/(double) image->columns;
   3309         radius=center.y;
   3310       }
   3311   /*
   3312     Implode image.
   3313   */
   3314   status=MagickTrue;
   3315   progress=0;
   3316   image_view=AcquireVirtualCacheView(image,exception);
   3317   interpolate_view=AcquireVirtualCacheView(image,exception);
   3318   implode_view=AcquireAuthenticCacheView(implode_image,exception);
   3319 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   3320   #pragma omp parallel for schedule(static,4) shared(progress,status) \
   3321     magick_threads(image,implode_image,image->rows,1)
   3322 #endif
   3323   for (y=0; y < (ssize_t) image->rows; y++)
   3324   {
   3325     double
   3326       distance;
   3327 
   3328     PointInfo
   3329       delta;
   3330 
   3331     register const Quantum
   3332       *magick_restrict p;
   3333 
   3334     register ssize_t
   3335       x;
   3336 
   3337     register Quantum
   3338       *magick_restrict q;
   3339 
   3340     if (status == MagickFalse)
   3341       continue;
   3342     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
   3343     q=QueueCacheViewAuthenticPixels(implode_view,0,y,implode_image->columns,1,
   3344       exception);
   3345     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
   3346       {
   3347         status=MagickFalse;
   3348         continue;
   3349       }
   3350     delta.y=scale.y*(double) (y-center.y);
   3351     for (x=0; x < (ssize_t) image->columns; x++)
   3352     {
   3353       register ssize_t
   3354         i;
   3355 
   3356       /*
   3357         Determine if the pixel is within an ellipse.
   3358       */
   3359       if (GetPixelReadMask(image,p) == 0)
   3360         {
   3361           SetPixelBackgoundColor(implode_image,q);
   3362           p+=GetPixelChannels(image);
   3363           q+=GetPixelChannels(implode_image);
   3364           continue;
   3365         }
   3366       delta.x=scale.x*(double) (x-center.x);
   3367       distance=delta.x*delta.x+delta.y*delta.y;
   3368       if (distance >= (radius*radius))
   3369         for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   3370         {
   3371           PixelChannel channel=GetPixelChannelChannel(image,i);
   3372           PixelTrait traits=GetPixelChannelTraits(image,channel);
   3373           PixelTrait implode_traits=GetPixelChannelTraits(implode_image,
   3374             channel);
   3375           if ((traits == UndefinedPixelTrait) ||
   3376               (implode_traits == UndefinedPixelTrait))
   3377             continue;
   3378           SetPixelChannel(implode_image,channel,p[i],q);
   3379         }
   3380       else
   3381         {
   3382           double
   3383             factor;
   3384 
   3385           /*
   3386             Implode the pixel.
   3387           */
   3388           factor=1.0;
   3389           if (distance > 0.0)
   3390             factor=pow(sin(MagickPI*sqrt((double) distance)/radius/2),-amount);
   3391           status=InterpolatePixelChannels(image,interpolate_view,implode_image,
   3392             method,(double) (factor*delta.x/scale.x+center.x),(double) (factor*
   3393             delta.y/scale.y+center.y),q,exception);
   3394         }
   3395       p+=GetPixelChannels(image);
   3396       q+=GetPixelChannels(implode_image);
   3397     }
   3398     if (SyncCacheViewAuthenticPixels(implode_view,exception) == MagickFalse)
   3399       status=MagickFalse;
   3400     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   3401       {
   3402         MagickBooleanType
   3403           proceed;
   3404 
   3405 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   3406         #pragma omp critical (MagickCore_ImplodeImage)
   3407 #endif
   3408         proceed=SetImageProgress(image,ImplodeImageTag,progress++,image->rows);
   3409         if (proceed == MagickFalse)
   3410           status=MagickFalse;
   3411       }
   3412   }
   3413   implode_view=DestroyCacheView(implode_view);
   3414   interpolate_view=DestroyCacheView(interpolate_view);
   3415   image_view=DestroyCacheView(image_view);
   3416   if (status == MagickFalse)
   3417     implode_image=DestroyImage(implode_image);
   3418   return(implode_image);
   3419 }
   3420 
   3421 
   3422 /*
   3423 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   3424 %                                                                             %
   3425 %                                                                             %
   3426 %                                                                             %
   3427 %     M o r p h I m a g e s                                                   %
   3428 %                                                                             %
   3429 %                                                                             %
   3430 %                                                                             %
   3431 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   3432 %
   3433 %  The MorphImages() method requires a minimum of two images.  The first
   3434 %  image is transformed into the second by a number of intervening images
   3435 %  as specified by frames.
   3436 %
   3437 %  The format of the MorphImage method is:
   3438 %
   3439 %      Image *MorphImages(const Image *image,const size_t number_frames,
   3440 %        ExceptionInfo *exception)
   3441 %
   3442 %  A description of each parameter follows:
   3443 %
   3444 %    o image: the image.
   3445 %
   3446 %    o number_frames:  Define the number of in-between image to generate.
   3447 %      The more in-between frames, the smoother the morph.
   3448 %
   3449 %    o exception: return any errors or warnings in this structure.
   3450 %
   3451 */
   3452 MagickExport Image *MorphImages(const Image *image,const size_t number_frames,
   3453   ExceptionInfo *exception)
   3454 {
   3455 #define MorphImageTag  "Morph/Image"
   3456 
   3457   double
   3458     alpha,
   3459     beta;
   3460 
   3461   Image
   3462     *morph_image,
   3463     *morph_images;
   3464 
   3465   MagickBooleanType
   3466     status;
   3467 
   3468   MagickOffsetType
   3469     scene;
   3470 
   3471   register const Image
   3472     *next;
   3473 
   3474   register ssize_t
   3475     n;
   3476 
   3477   ssize_t
   3478     y;
   3479 
   3480   /*
   3481     Clone first frame in sequence.
   3482   */
   3483   assert(image != (Image *) NULL);
   3484   assert(image->signature == MagickCoreSignature);
   3485   if (image->debug != MagickFalse)
   3486     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   3487   assert(exception != (ExceptionInfo *) NULL);
   3488   assert(exception->signature == MagickCoreSignature);
   3489   morph_images=CloneImage(image,0,0,MagickTrue,exception);
   3490   if (morph_images == (Image *) NULL)
   3491     return((Image *) NULL);
   3492   if (GetNextImageInList(image) == (Image *) NULL)
   3493     {
   3494       /*
   3495         Morph single image.
   3496       */
   3497       for (n=1; n < (ssize_t) number_frames; n++)
   3498       {
   3499         morph_image=CloneImage(image,0,0,MagickTrue,exception);
   3500         if (morph_image == (Image *) NULL)
   3501           {
   3502             morph_images=DestroyImageList(morph_images);
   3503             return((Image *) NULL);
   3504           }
   3505         AppendImageToList(&morph_images,morph_image);
   3506         if (image->progress_monitor != (MagickProgressMonitor) NULL)
   3507           {
   3508             MagickBooleanType
   3509               proceed;
   3510 
   3511             proceed=SetImageProgress(image,MorphImageTag,(MagickOffsetType) n,
   3512               number_frames);
   3513             if (proceed == MagickFalse)
   3514               status=MagickFalse;
   3515           }
   3516       }
   3517       return(GetFirstImageInList(morph_images));
   3518     }
   3519   /*
   3520     Morph image sequence.
   3521   */
   3522   status=MagickTrue;
   3523   scene=0;
   3524   next=image;
   3525   for ( ; GetNextImageInList(next) != (Image *) NULL; next=GetNextImageInList(next))
   3526   {
   3527     for (n=0; n < (ssize_t) number_frames; n++)
   3528     {
   3529       CacheView
   3530         *image_view,
   3531         *morph_view;
   3532 
   3533       beta=(double) (n+1.0)/(double) (number_frames+1.0);
   3534       alpha=1.0-beta;
   3535       morph_image=ResizeImage(next,(size_t) (alpha*next->columns+beta*
   3536         GetNextImageInList(next)->columns+0.5),(size_t) (alpha*next->rows+beta*
   3537         GetNextImageInList(next)->rows+0.5),next->filter,exception);
   3538       if (morph_image == (Image *) NULL)
   3539         {
   3540           morph_images=DestroyImageList(morph_images);
   3541           return((Image *) NULL);
   3542         }
   3543       status=SetImageStorageClass(morph_image,DirectClass,exception);
   3544       if (status == MagickFalse)
   3545         {
   3546           morph_image=DestroyImage(morph_image);
   3547           return((Image *) NULL);
   3548         }
   3549       AppendImageToList(&morph_images,morph_image);
   3550       morph_images=GetLastImageInList(morph_images);
   3551       morph_image=ResizeImage(GetNextImageInList(next),morph_images->columns,
   3552         morph_images->rows,GetNextImageInList(next)->filter,exception);
   3553       if (morph_image == (Image *) NULL)
   3554         {
   3555           morph_images=DestroyImageList(morph_images);
   3556           return((Image *) NULL);
   3557         }
   3558       image_view=AcquireVirtualCacheView(morph_image,exception);
   3559       morph_view=AcquireAuthenticCacheView(morph_images,exception);
   3560 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   3561       #pragma omp parallel for schedule(static,4) shared(status) \
   3562         magick_threads(morph_image,morph_image,morph_image->rows,1)
   3563 #endif
   3564       for (y=0; y < (ssize_t) morph_images->rows; y++)
   3565       {
   3566         MagickBooleanType
   3567           sync;
   3568 
   3569         register const Quantum
   3570           *magick_restrict p;
   3571 
   3572         register ssize_t
   3573           x;
   3574 
   3575         register Quantum
   3576           *magick_restrict q;
   3577 
   3578         if (status == MagickFalse)
   3579           continue;
   3580         p=GetCacheViewVirtualPixels(image_view,0,y,morph_image->columns,1,
   3581           exception);
   3582         q=GetCacheViewAuthenticPixels(morph_view,0,y,morph_images->columns,1,
   3583           exception);
   3584         if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
   3585           {
   3586             status=MagickFalse;
   3587             continue;
   3588           }
   3589         for (x=0; x < (ssize_t) morph_images->columns; x++)
   3590         {
   3591           register ssize_t
   3592             i;
   3593 
   3594           for (i=0; i < (ssize_t) GetPixelChannels(morph_image); i++)
   3595           {
   3596             PixelChannel channel=GetPixelChannelChannel(morph_image,i);
   3597             PixelTrait traits=GetPixelChannelTraits(morph_image,channel);
   3598             PixelTrait morph_traits=GetPixelChannelTraits(morph_images,channel);
   3599             if ((traits == UndefinedPixelTrait) ||
   3600                 (morph_traits == UndefinedPixelTrait))
   3601               continue;
   3602             if (((morph_traits & CopyPixelTrait) != 0) ||
   3603                 (GetPixelReadMask(morph_images,p) == 0))
   3604               {
   3605                 SetPixelChannel(morph_image,channel,p[i],q);
   3606                 continue;
   3607               }
   3608             SetPixelChannel(morph_image,channel,ClampToQuantum(alpha*
   3609               GetPixelChannel(morph_images,channel,q)+beta*p[i]),q);
   3610           }
   3611           p+=GetPixelChannels(morph_image);
   3612           q+=GetPixelChannels(morph_images);
   3613         }
   3614         sync=SyncCacheViewAuthenticPixels(morph_view,exception);
   3615         if (sync == MagickFalse)
   3616           status=MagickFalse;
   3617       }
   3618       morph_view=DestroyCacheView(morph_view);
   3619       image_view=DestroyCacheView(image_view);
   3620       morph_image=DestroyImage(morph_image);
   3621     }
   3622     if (n < (ssize_t) number_frames)
   3623       break;
   3624     /*
   3625       Clone last frame in sequence.
   3626     */
   3627     morph_image=CloneImage(GetNextImageInList(next),0,0,MagickTrue,exception);
   3628     if (morph_image == (Image *) NULL)
   3629       {
   3630         morph_images=DestroyImageList(morph_images);
   3631         return((Image *) NULL);
   3632       }
   3633     AppendImageToList(&morph_images,morph_image);
   3634     morph_images=GetLastImageInList(morph_images);
   3635     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   3636       {
   3637         MagickBooleanType
   3638           proceed;
   3639 
   3640 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   3641         #pragma omp critical (MagickCore_MorphImages)
   3642 #endif
   3643         proceed=SetImageProgress(image,MorphImageTag,scene,
   3644           GetImageListLength(image));
   3645         if (proceed == MagickFalse)
   3646           status=MagickFalse;
   3647       }
   3648     scene++;
   3649   }
   3650   if (GetNextImageInList(next) != (Image *) NULL)
   3651     {
   3652       morph_images=DestroyImageList(morph_images);
   3653       return((Image *) NULL);
   3654     }
   3655   return(GetFirstImageInList(morph_images));
   3656 }
   3657 
   3658 
   3659 /*
   3660 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   3661 %                                                                             %
   3662 %                                                                             %
   3663 %                                                                             %
   3664 %     P l a s m a I m a g e                                                   %
   3665 %                                                                             %
   3666 %                                                                             %
   3667 %                                                                             %
   3668 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   3669 %
   3670 %  PlasmaImage() initializes an image with plasma fractal values.  The image
   3671 %  must be initialized with a base color and the random number generator
   3672 %  seeded before this method is called.
   3673 %
   3674 %  The format of the PlasmaImage method is:
   3675 %
   3676 %      MagickBooleanType PlasmaImage(Image *image,const SegmentInfo *segment,
   3677 %        size_t attenuate,size_t depth,ExceptionInfo *exception)
   3678 %
   3679 %  A description of each parameter follows:
   3680 %
   3681 %    o image: the image.
   3682 %
   3683 %    o segment:   Define the region to apply plasma fractals values.
   3684 %
   3685 %    o attenuate: Define the plasma attenuation factor.
   3686 %
   3687 %    o depth: Limit the plasma recursion depth.
   3688 %
   3689 %    o exception: return any errors or warnings in this structure.
   3690 %
   3691 */
   3692 
   3693 static inline Quantum PlasmaPixel(RandomInfo *random_info,
   3694   const double pixel,const double noise)
   3695 {
   3696   Quantum
   3697     plasma;
   3698 
   3699   plasma=ClampToQuantum(pixel+noise*GetPseudoRandomValue(random_info)-
   3700     noise/2.0);
   3701   if (plasma <= 0)
   3702     return((Quantum) 0);
   3703   if (plasma >= QuantumRange)
   3704     return(QuantumRange);
   3705   return(plasma);
   3706 }
   3707 
   3708 static MagickBooleanType PlasmaImageProxy(Image *image,CacheView *image_view,
   3709   CacheView *u_view,CacheView *v_view,RandomInfo *random_info,
   3710   const SegmentInfo *segment,size_t attenuate,size_t depth,
   3711   ExceptionInfo *exception)
   3712 {
   3713   double
   3714     plasma;
   3715 
   3716   register const Quantum
   3717     *magick_restrict u,
   3718     *magick_restrict v;
   3719 
   3720   register Quantum
   3721     *magick_restrict q;
   3722 
   3723   register ssize_t
   3724     i;
   3725 
   3726   ssize_t
   3727     x,
   3728     x_mid,
   3729     y,
   3730     y_mid;
   3731 
   3732   if ((fabs(segment->x2-segment->x1) <= MagickEpsilon) &&
   3733       (fabs(segment->y2-segment->y1) <= MagickEpsilon))
   3734     return(MagickTrue);
   3735   if (depth != 0)
   3736     {
   3737       MagickBooleanType
   3738         status;
   3739 
   3740       SegmentInfo
   3741         local_info;
   3742 
   3743       /*
   3744         Divide the area into quadrants and recurse.
   3745       */
   3746       depth--;
   3747       attenuate++;
   3748       x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
   3749       y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
   3750       local_info=(*segment);
   3751       local_info.x2=(double) x_mid;
   3752       local_info.y2=(double) y_mid;
   3753       (void) PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
   3754         &local_info,attenuate,depth,exception);
   3755       local_info=(*segment);
   3756       local_info.y1=(double) y_mid;
   3757       local_info.x2=(double) x_mid;
   3758       (void) PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
   3759         &local_info,attenuate,depth,exception);
   3760       local_info=(*segment);
   3761       local_info.x1=(double) x_mid;
   3762       local_info.y2=(double) y_mid;
   3763       (void) PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
   3764         &local_info,attenuate,depth,exception);
   3765       local_info=(*segment);
   3766       local_info.x1=(double) x_mid;
   3767       local_info.y1=(double) y_mid;
   3768       status=PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
   3769         &local_info,attenuate,depth,exception);
   3770       return(status);
   3771     }
   3772   x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
   3773   y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
   3774   if ((fabs(segment->x1-x_mid) < MagickEpsilon) &&
   3775       (fabs(segment->x2-x_mid) < MagickEpsilon) &&
   3776       (fabs(segment->y1-y_mid) < MagickEpsilon) &&
   3777       (fabs(segment->y2-y_mid) < MagickEpsilon))
   3778     return(MagickFalse);
   3779   /*
   3780     Average pixels and apply plasma.
   3781   */
   3782   plasma=(double) QuantumRange/(2.0*attenuate);
   3783   if ((fabs(segment->x1-x_mid) > MagickEpsilon) ||
   3784       (fabs(segment->x2-x_mid) > MagickEpsilon))
   3785     {
   3786       /*
   3787         Left pixel.
   3788       */
   3789       x=(ssize_t) ceil(segment->x1-0.5);
   3790       u=GetCacheViewVirtualPixels(u_view,x,(ssize_t) ceil(segment->y1-0.5),1,1,
   3791         exception);
   3792       v=GetCacheViewVirtualPixels(v_view,x,(ssize_t) ceil(segment->y2-0.5),1,1,
   3793         exception);
   3794       q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
   3795       if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
   3796           (q == (Quantum *) NULL))
   3797         return(MagickTrue);
   3798       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   3799       {
   3800         PixelChannel channel=GetPixelChannelChannel(image,i);
   3801         PixelTrait traits=GetPixelChannelTraits(image,channel);
   3802         if (traits == UndefinedPixelTrait)
   3803           continue;
   3804         q[i]=PlasmaPixel(random_info,(u[i]+v[i])/2.0,plasma);
   3805       }
   3806       (void) SyncCacheViewAuthenticPixels(image_view,exception);
   3807       if (fabs(segment->x1-segment->x2) > MagickEpsilon)
   3808         {
   3809           /*
   3810             Right pixel.
   3811           */
   3812           x=(ssize_t) ceil(segment->x2-0.5);
   3813           u=GetCacheViewVirtualPixels(u_view,x,(ssize_t) ceil(segment->y1-0.5),
   3814             1,1,exception);
   3815           v=GetCacheViewVirtualPixels(v_view,x,(ssize_t) ceil(segment->y2-0.5),
   3816             1,1,exception);
   3817           q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
   3818           if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
   3819               (q == (Quantum *) NULL))
   3820             return(MagickTrue);
   3821           for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   3822           {
   3823             PixelChannel channel=GetPixelChannelChannel(image,i);
   3824             PixelTrait traits=GetPixelChannelTraits(image,channel);
   3825             if (traits == UndefinedPixelTrait)
   3826               continue;
   3827             q[i]=PlasmaPixel(random_info,(u[i]+v[i])/2.0,plasma);
   3828           }
   3829           (void) SyncCacheViewAuthenticPixels(image_view,exception);
   3830         }
   3831     }
   3832   if ((fabs(segment->y1-y_mid) > MagickEpsilon) ||
   3833       (fabs(segment->y2-y_mid) > MagickEpsilon))
   3834     {
   3835       if ((fabs(segment->x1-x_mid) > MagickEpsilon) ||
   3836           (fabs(segment->y2-y_mid) > MagickEpsilon))
   3837         {
   3838           /*
   3839             Bottom pixel.
   3840           */
   3841           y=(ssize_t) ceil(segment->y2-0.5);
   3842           u=GetCacheViewVirtualPixels(u_view,(ssize_t) ceil(segment->x1-0.5),y,
   3843             1,1,exception);
   3844           v=GetCacheViewVirtualPixels(v_view,(ssize_t) ceil(segment->x2-0.5),y,
   3845             1,1,exception);
   3846           q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
   3847           if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
   3848               (q == (Quantum *) NULL))
   3849             return(MagickTrue);
   3850           for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   3851           {
   3852             PixelChannel channel=GetPixelChannelChannel(image,i);
   3853             PixelTrait traits=GetPixelChannelTraits(image,channel);
   3854             if (traits == UndefinedPixelTrait)
   3855               continue;
   3856             q[i]=PlasmaPixel(random_info,(u[i]+v[i])/2.0,plasma);
   3857           }
   3858           (void) SyncCacheViewAuthenticPixels(image_view,exception);
   3859         }
   3860       if (fabs(segment->y1-segment->y2) > MagickEpsilon)
   3861         {
   3862           /*
   3863             Top pixel.
   3864           */
   3865           y=(ssize_t) ceil(segment->y1-0.5);
   3866           u=GetCacheViewVirtualPixels(u_view,(ssize_t) ceil(segment->x1-0.5),y,
   3867             1,1,exception);
   3868           v=GetCacheViewVirtualPixels(v_view,(ssize_t) ceil(segment->x2-0.5),y,
   3869             1,1,exception);
   3870           q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
   3871           if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
   3872               (q == (Quantum *) NULL))
   3873             return(MagickTrue);
   3874           for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   3875           {
   3876             PixelChannel channel=GetPixelChannelChannel(image,i);
   3877             PixelTrait traits=GetPixelChannelTraits(image,channel);
   3878             if (traits == UndefinedPixelTrait)
   3879               continue;
   3880             q[i]=PlasmaPixel(random_info,(u[i]+v[i])/2.0,plasma);
   3881           }
   3882           (void) SyncCacheViewAuthenticPixels(image_view,exception);
   3883         }
   3884     }
   3885   if ((fabs(segment->x1-segment->x2) > MagickEpsilon) ||
   3886       (fabs(segment->y1-segment->y2) > MagickEpsilon))
   3887     {
   3888       /*
   3889         Middle pixel.
   3890       */
   3891       x=(ssize_t) ceil(segment->x1-0.5);
   3892       y=(ssize_t) ceil(segment->y1-0.5);
   3893       u=GetCacheViewVirtualPixels(u_view,x,y,1,1,exception);
   3894       x=(ssize_t) ceil(segment->x2-0.5);
   3895       y=(ssize_t) ceil(segment->y2-0.5);
   3896       v=GetCacheViewVirtualPixels(v_view,x,y,1,1,exception);
   3897       q=QueueCacheViewAuthenticPixels(image_view,x_mid,y_mid,1,1,exception);
   3898       if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
   3899           (q == (Quantum *) NULL))
   3900         return(MagickTrue);
   3901       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   3902       {
   3903         PixelChannel channel=GetPixelChannelChannel(image,i);
   3904         PixelTrait traits=GetPixelChannelTraits(image,channel);
   3905         if (traits == UndefinedPixelTrait)
   3906           continue;
   3907         q[i]=PlasmaPixel(random_info,(u[i]+v[i])/2.0,plasma);
   3908       }
   3909       (void) SyncCacheViewAuthenticPixels(image_view,exception);
   3910     }
   3911   if ((fabs(segment->x2-segment->x1) < 3.0) &&
   3912       (fabs(segment->y2-segment->y1) < 3.0))
   3913     return(MagickTrue);
   3914   return(MagickFalse);
   3915 }
   3916 
   3917 MagickExport MagickBooleanType PlasmaImage(Image *image,
   3918   const SegmentInfo *segment,size_t attenuate,size_t depth,
   3919   ExceptionInfo *exception)
   3920 {
   3921   CacheView
   3922     *image_view,
   3923     *u_view,
   3924     *v_view;
   3925 
   3926   MagickBooleanType
   3927     status;
   3928 
   3929   RandomInfo
   3930     *random_info;
   3931 
   3932   if (image->debug != MagickFalse)
   3933     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
   3934   assert(image != (Image *) NULL);
   3935   assert(image->signature == MagickCoreSignature);
   3936   if (image->debug != MagickFalse)
   3937     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
   3938   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
   3939     return(MagickFalse);
   3940   image_view=AcquireAuthenticCacheView(image,exception);
   3941   u_view=AcquireVirtualCacheView(image,exception);
   3942   v_view=AcquireVirtualCacheView(image,exception);
   3943   random_info=AcquireRandomInfo();
   3944   status=PlasmaImageProxy(image,image_view,u_view,v_view,random_info,segment,
   3945     attenuate,depth,exception);
   3946   random_info=DestroyRandomInfo(random_info);
   3947   v_view=DestroyCacheView(v_view);
   3948   u_view=DestroyCacheView(u_view);
   3949   image_view=DestroyCacheView(image_view);
   3950   return(status);
   3951 }
   3952 
   3953 
   3954 /*
   3955 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   3956 %                                                                             %
   3957 %                                                                             %
   3958 %                                                                             %
   3959 %   P o l a r o i d I m a g e                                                 %
   3960 %                                                                             %
   3961 %                                                                             %
   3962 %                                                                             %
   3963 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   3964 %
   3965 %  PolaroidImage() simulates a Polaroid picture.
   3966 %
   3967 %  The format of the PolaroidImage method is:
   3968 %
   3969 %      Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
   3970 %        const char *caption,const double angle,
   3971 %        const PixelInterpolateMethod method,ExceptionInfo exception)
   3972 %
   3973 %  A description of each parameter follows:
   3974 %
   3975 %    o image: the image.
   3976 %
   3977 %    o draw_info: the draw info.
   3978 %
   3979 %    o caption: the Polaroid caption.
   3980 %
   3981 %    o angle: Apply the effect along this angle.
   3982 %
   3983 %    o method: the pixel interpolation method.
   3984 %
   3985 %    o exception: return any errors or warnings in this structure.
   3986 %
   3987 */
   3988 MagickExport Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
   3989   const char *caption,const double angle,const PixelInterpolateMethod method,
   3990   ExceptionInfo *exception)
   3991 {
   3992   Image
   3993     *bend_image,
   3994     *caption_image,
   3995     *flop_image,
   3996     *picture_image,
   3997     *polaroid_image,
   3998     *rotate_image,
   3999     *trim_image;
   4000 
   4001   size_t
   4002     height;
   4003 
   4004   ssize_t
   4005     quantum;
   4006 
   4007   /*
   4008     Simulate a Polaroid picture.
   4009   */
   4010   assert(image != (Image *) NULL);
   4011   assert(image->signature == MagickCoreSignature);
   4012   if (image->debug != MagickFalse)
   4013     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   4014   assert(exception != (ExceptionInfo *) NULL);
   4015   assert(exception->signature == MagickCoreSignature);
   4016   quantum=(ssize_t) MagickMax(MagickMax((double) image->columns,(double)
   4017     image->rows)/25.0,10.0);
   4018   height=image->rows+2*quantum;
   4019   caption_image=(Image *) NULL;
   4020   if (caption != (const char *) NULL)
   4021     {
   4022       char
   4023         geometry[MagickPathExtent],
   4024         *text;
   4025 
   4026       DrawInfo
   4027         *annotate_info;
   4028 
   4029       ImageInfo
   4030         *image_info;
   4031 
   4032       MagickBooleanType
   4033         status;
   4034 
   4035       ssize_t
   4036         count;
   4037 
   4038       TypeMetric
   4039         metrics;
   4040 
   4041       /*
   4042         Generate caption image.
   4043       */
   4044       caption_image=CloneImage(image,image->columns,1,MagickTrue,exception);
   4045       if (caption_image == (Image *) NULL)
   4046         return((Image *) NULL);
   4047       image_info=AcquireImageInfo();
   4048       annotate_info=CloneDrawInfo((const ImageInfo *) NULL,draw_info);
   4049       text=InterpretImageProperties(image_info,(Image *) image,caption,
   4050         exception);
   4051       image_info=DestroyImageInfo(image_info);
   4052       (void) CloneString(&annotate_info->text,text);
   4053       count=FormatMagickCaption(caption_image,annotate_info,MagickTrue,&metrics,
   4054         &text,exception);
   4055       status=SetImageExtent(caption_image,image->columns,(size_t) ((count+1)*
   4056         (metrics.ascent-metrics.descent)+0.5),exception);
   4057       if (status == MagickFalse)
   4058         caption_image=DestroyImage(caption_image);
   4059       else
   4060         {
   4061           caption_image->background_color=image->border_color;
   4062           (void) SetImageBackgroundColor(caption_image,exception);
   4063           (void) CloneString(&annotate_info->text,text);
   4064           (void) FormatLocaleString(geometry,MagickPathExtent,"+0+%g",
   4065             metrics.ascent);
   4066           if (annotate_info->gravity == UndefinedGravity)
   4067             (void) CloneString(&annotate_info->geometry,AcquireString(
   4068               geometry));
   4069           (void) AnnotateImage(caption_image,annotate_info,exception);
   4070           height+=caption_image->rows;
   4071         }
   4072       annotate_info=DestroyDrawInfo(annotate_info);
   4073       text=DestroyString(text);
   4074     }
   4075   picture_image=CloneImage(image,image->columns+2*quantum,height,MagickTrue,
   4076     exception);
   4077   if (picture_image == (Image *) NULL)
   4078     {
   4079       if (caption_image != (Image *) NULL)
   4080         caption_image=DestroyImage(caption_image);
   4081       return((Image *) NULL);
   4082     }
   4083   picture_image->background_color=image->border_color;
   4084   (void) SetImageBackgroundColor(picture_image,exception);
   4085   (void) CompositeImage(picture_image,image,OverCompositeOp,MagickTrue,quantum,
   4086     quantum,exception);
   4087   if (caption_image != (Image *) NULL)
   4088     {
   4089       (void) CompositeImage(picture_image,caption_image,OverCompositeOp,
   4090         MagickTrue,quantum,(ssize_t) (image->rows+3*quantum/2),exception);
   4091       caption_image=DestroyImage(caption_image);
   4092     }
   4093   (void) QueryColorCompliance("none",AllCompliance,
   4094     &picture_image->background_color,exception);
   4095   (void) SetImageAlphaChannel(picture_image,OpaqueAlphaChannel,exception);
   4096   rotate_image=RotateImage(picture_image,90.0,exception);
   4097   picture_image=DestroyImage(picture_image);
   4098   if (rotate_image == (Image *) NULL)
   4099     return((Image *) NULL);
   4100   picture_image=rotate_image;
   4101   bend_image=WaveImage(picture_image,0.01*picture_image->rows,2.0*
   4102     picture_image->columns,method,exception);
   4103   picture_image=DestroyImage(picture_image);
   4104   if (bend_image == (Image *) NULL)
   4105     return((Image *) NULL);
   4106   picture_image=bend_image;
   4107   rotate_image=RotateImage(picture_image,-90.0,exception);
   4108   picture_image=DestroyImage(picture_image);
   4109   if (rotate_image == (Image *) NULL)
   4110     return((Image *) NULL);
   4111   picture_image=rotate_image;
   4112   picture_image->background_color=image->background_color;
   4113   polaroid_image=ShadowImage(picture_image,80.0,2.0,quantum/3,quantum/3,
   4114     exception);
   4115   if (polaroid_image == (Image *) NULL)
   4116     {
   4117       picture_image=DestroyImage(picture_image);
   4118       return(picture_image);
   4119     }
   4120   flop_image=FlopImage(polaroid_image,exception);
   4121   polaroid_image=DestroyImage(polaroid_image);
   4122   if (flop_image == (Image *) NULL)
   4123     {
   4124       picture_image=DestroyImage(picture_image);
   4125       return(picture_image);
   4126     }
   4127   polaroid_image=flop_image;
   4128   (void) CompositeImage(polaroid_image,picture_image,OverCompositeOp,
   4129     MagickTrue,(ssize_t) (-0.01*picture_image->columns/2.0),0L,exception);
   4130   picture_image=DestroyImage(picture_image);
   4131   (void) QueryColorCompliance("none",AllCompliance,
   4132     &polaroid_image->background_color,exception);
   4133   rotate_image=RotateImage(polaroid_image,angle,exception);
   4134   polaroid_image=DestroyImage(polaroid_image);
   4135   if (rotate_image == (Image *) NULL)
   4136     return((Image *) NULL);
   4137   polaroid_image=rotate_image;
   4138   trim_image=TrimImage(polaroid_image,exception);
   4139   polaroid_image=DestroyImage(polaroid_image);
   4140   if (trim_image == (Image *) NULL)
   4141     return((Image *) NULL);
   4142   polaroid_image=trim_image;
   4143   return(polaroid_image);
   4144 }
   4145 
   4146 
   4147 /*
   4148 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   4149 %                                                                             %
   4150 %                                                                             %
   4151 %                                                                             %
   4152 %     S e p i a T o n e I m a g e                                             %
   4153 %                                                                             %
   4154 %                                                                             %
   4155 %                                                                             %
   4156 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   4157 %
   4158 %  MagickSepiaToneImage() applies a special effect to the image, similar to the
   4159 %  effect achieved in a photo darkroom by sepia toning.  Threshold ranges from
   4160 %  0 to QuantumRange and is a measure of the extent of the sepia toning.  A
   4161 %  threshold of 80% is a good starting point for a reasonable tone.
   4162 %
   4163 %  The format of the SepiaToneImage method is:
   4164 %
   4165 %      Image *SepiaToneImage(const Image *image,const double threshold,
   4166 %        ExceptionInfo *exception)
   4167 %
   4168 %  A description of each parameter follows:
   4169 %
   4170 %    o image: the image.
   4171 %
   4172 %    o threshold: the tone threshold.
   4173 %
   4174 %    o exception: return any errors or warnings in this structure.
   4175 %
   4176 */
   4177 MagickExport Image *SepiaToneImage(const Image *image,const double threshold,
   4178   ExceptionInfo *exception)
   4179 {
   4180 #define SepiaToneImageTag  "SepiaTone/Image"
   4181 
   4182   CacheView
   4183     *image_view,
   4184     *sepia_view;
   4185 
   4186   Image
   4187     *sepia_image;
   4188 
   4189   MagickBooleanType
   4190     status;
   4191 
   4192   MagickOffsetType
   4193     progress;
   4194 
   4195   ssize_t
   4196     y;
   4197 
   4198   /*
   4199     Initialize sepia-toned image attributes.
   4200   */
   4201   assert(image != (const Image *) NULL);
   4202   assert(image->signature == MagickCoreSignature);
   4203   if (image->debug != MagickFalse)
   4204     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   4205   assert(exception != (ExceptionInfo *) NULL);
   4206   assert(exception->signature == MagickCoreSignature);
   4207   sepia_image=CloneImage(image,0,0,MagickTrue,exception);
   4208   if (sepia_image == (Image *) NULL)
   4209     return((Image *) NULL);
   4210   if (SetImageStorageClass(sepia_image,DirectClass,exception) == MagickFalse)
   4211     {
   4212       sepia_image=DestroyImage(sepia_image);
   4213       return((Image *) NULL);
   4214     }
   4215   /*
   4216     Tone each row of the image.
   4217   */
   4218   status=MagickTrue;
   4219   progress=0;
   4220   image_view=AcquireVirtualCacheView(image,exception);
   4221   sepia_view=AcquireAuthenticCacheView(sepia_image,exception);
   4222 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   4223   #pragma omp parallel for schedule(static,4) shared(progress,status) \
   4224     magick_threads(image,sepia_image,image->rows,1)
   4225 #endif
   4226   for (y=0; y < (ssize_t) image->rows; y++)
   4227   {
   4228     register const Quantum
   4229       *magick_restrict p;
   4230 
   4231     register ssize_t
   4232       x;
   4233 
   4234     register Quantum
   4235       *magick_restrict q;
   4236 
   4237     if (status == MagickFalse)
   4238       continue;
   4239     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
   4240     q=GetCacheViewAuthenticPixels(sepia_view,0,y,sepia_image->columns,1,
   4241       exception);
   4242     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
   4243       {
   4244         status=MagickFalse;
   4245         continue;
   4246       }
   4247     for (x=0; x < (ssize_t) image->columns; x++)
   4248     {
   4249       double
   4250         intensity,
   4251         tone;
   4252 
   4253       intensity=GetPixelIntensity(image,p);
   4254       tone=intensity > threshold ? (double) QuantumRange : intensity+
   4255         (double) QuantumRange-threshold;
   4256       SetPixelRed(sepia_image,ClampToQuantum(tone),q);
   4257       tone=intensity > (7.0*threshold/6.0) ? (double) QuantumRange :
   4258         intensity+(double) QuantumRange-7.0*threshold/6.0;
   4259       SetPixelGreen(sepia_image,ClampToQuantum(tone),q);
   4260       tone=intensity < (threshold/6.0) ? 0 : intensity-threshold/6.0;
   4261       SetPixelBlue(sepia_image,ClampToQuantum(tone),q);
   4262       tone=threshold/7.0;
   4263       if ((double) GetPixelGreen(image,q) < tone)
   4264         SetPixelGreen(sepia_image,ClampToQuantum(tone),q);
   4265       if ((double) GetPixelBlue(image,q) < tone)
   4266         SetPixelBlue(sepia_image,ClampToQuantum(tone),q);
   4267       SetPixelAlpha(sepia_image,GetPixelAlpha(image,p),q);
   4268       p+=GetPixelChannels(image);
   4269       q+=GetPixelChannels(sepia_image);
   4270     }
   4271     if (SyncCacheViewAuthenticPixels(sepia_view,exception) == MagickFalse)
   4272       status=MagickFalse;
   4273     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   4274       {
   4275         MagickBooleanType
   4276           proceed;
   4277 
   4278 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   4279         #pragma omp critical (MagickCore_SepiaToneImage)
   4280 #endif
   4281         proceed=SetImageProgress(image,SepiaToneImageTag,progress++,
   4282           image->rows);
   4283         if (proceed == MagickFalse)
   4284           status=MagickFalse;
   4285       }
   4286   }
   4287   sepia_view=DestroyCacheView(sepia_view);
   4288   image_view=DestroyCacheView(image_view);
   4289   (void) NormalizeImage(sepia_image,exception);
   4290   (void) ContrastImage(sepia_image,MagickTrue,exception);
   4291   if (status == MagickFalse)
   4292     sepia_image=DestroyImage(sepia_image);
   4293   return(sepia_image);
   4294 }
   4295 
   4296 
   4297 /*
   4298 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   4299 %                                                                             %
   4300 %                                                                             %
   4301 %                                                                             %
   4302 %     S h a d o w I m a g e                                                   %
   4303 %                                                                             %
   4304 %                                                                             %
   4305 %                                                                             %
   4306 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   4307 %
   4308 %  ShadowImage() simulates a shadow from the specified image and returns it.
   4309 %
   4310 %  The format of the ShadowImage method is:
   4311 %
   4312 %      Image *ShadowImage(const Image *image,const double alpha,
   4313 %        const double sigma,const ssize_t x_offset,const ssize_t y_offset,
   4314 %        ExceptionInfo *exception)
   4315 %
   4316 %  A description of each parameter follows:
   4317 %
   4318 %    o image: the image.
   4319 %
   4320 %    o alpha: percentage transparency.
   4321 %
   4322 %    o sigma: the standard deviation of the Gaussian, in pixels.
   4323 %
   4324 %    o x_offset: the shadow x-offset.
   4325 %
   4326 %    o y_offset: the shadow y-offset.
   4327 %
   4328 %    o exception: return any errors or warnings in this structure.
   4329 %
   4330 */
   4331 MagickExport Image *ShadowImage(const Image *image,const double alpha,
   4332   const double sigma,const ssize_t x_offset,const ssize_t y_offset,
   4333   ExceptionInfo *exception)
   4334 {
   4335 #define ShadowImageTag  "Shadow/Image"
   4336 
   4337   CacheView
   4338     *image_view;
   4339 
   4340   ChannelType
   4341     channel_mask;
   4342 
   4343   Image
   4344     *border_image,
   4345     *clone_image,
   4346     *shadow_image;
   4347 
   4348   MagickBooleanType
   4349     status;
   4350 
   4351   PixelInfo
   4352     background_color;
   4353 
   4354   RectangleInfo
   4355     border_info;
   4356 
   4357   ssize_t
   4358     y;
   4359 
   4360   assert(image != (Image *) NULL);
   4361   assert(image->signature == MagickCoreSignature);
   4362   if (image->debug != MagickFalse)
   4363     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   4364   assert(exception != (ExceptionInfo *) NULL);
   4365   assert(exception->signature == MagickCoreSignature);
   4366   clone_image=CloneImage(image,0,0,MagickTrue,exception);
   4367   if (clone_image == (Image *) NULL)
   4368     return((Image *) NULL);
   4369   if (IsGrayColorspace(image->colorspace) != MagickFalse)
   4370     (void) SetImageColorspace(clone_image,sRGBColorspace,exception);
   4371   (void) SetImageVirtualPixelMethod(clone_image,EdgeVirtualPixelMethod,
   4372     exception);
   4373   border_info.width=(size_t) floor(2.0*sigma+0.5);
   4374   border_info.height=(size_t) floor(2.0*sigma+0.5);
   4375   border_info.x=0;
   4376   border_info.y=0;
   4377   (void) QueryColorCompliance("none",AllCompliance,&clone_image->border_color,
   4378     exception);
   4379   clone_image->alpha_trait=BlendPixelTrait;
   4380   border_image=BorderImage(clone_image,&border_info,OverCompositeOp,exception);
   4381   clone_image=DestroyImage(clone_image);
   4382   if (border_image == (Image *) NULL)
   4383     return((Image *) NULL);
   4384   if (border_image->alpha_trait == UndefinedPixelTrait)
   4385     (void) SetImageAlphaChannel(border_image,OpaqueAlphaChannel,exception);
   4386   /*
   4387     Shadow image.
   4388   */
   4389   status=MagickTrue;
   4390   background_color=border_image->background_color;
   4391   background_color.alpha_trait=BlendPixelTrait;
   4392   image_view=AcquireAuthenticCacheView(border_image,exception);
   4393   for (y=0; y < (ssize_t) border_image->rows; y++)
   4394   {
   4395     register Quantum
   4396       *magick_restrict q;
   4397 
   4398     register ssize_t
   4399       x;
   4400 
   4401     if (status == MagickFalse)
   4402       continue;
   4403     q=QueueCacheViewAuthenticPixels(image_view,0,y,border_image->columns,1,
   4404       exception);
   4405     if (q == (Quantum *) NULL)
   4406       {
   4407         status=MagickFalse;
   4408         continue;
   4409       }
   4410     for (x=0; x < (ssize_t) border_image->columns; x++)
   4411     {
   4412       if (border_image->alpha_trait != UndefinedPixelTrait)
   4413         background_color.alpha=GetPixelAlpha(border_image,q)*alpha/100.0;
   4414       SetPixelViaPixelInfo(border_image,&background_color,q);
   4415       q+=GetPixelChannels(border_image);
   4416     }
   4417     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
   4418       status=MagickFalse;
   4419   }
   4420   image_view=DestroyCacheView(image_view);
   4421   if (status == MagickFalse)
   4422     {
   4423       border_image=DestroyImage(border_image);
   4424       return((Image *) NULL);
   4425     }
   4426   channel_mask=SetImageChannelMask(border_image,AlphaChannel);
   4427   shadow_image=BlurImage(border_image,0.0,sigma,exception);
   4428   border_image=DestroyImage(border_image);
   4429   if (shadow_image == (Image *) NULL)
   4430     return((Image *) NULL);
   4431   (void) SetPixelChannelMask(shadow_image,channel_mask);
   4432   if (shadow_image->page.width == 0)
   4433     shadow_image->page.width=shadow_image->columns;
   4434   if (shadow_image->page.height == 0)
   4435     shadow_image->page.height=shadow_image->rows;
   4436   shadow_image->page.width+=x_offset-(ssize_t) border_info.width;
   4437   shadow_image->page.height+=y_offset-(ssize_t) border_info.height;
   4438   shadow_image->page.x+=x_offset-(ssize_t) border_info.width;
   4439   shadow_image->page.y+=y_offset-(ssize_t) border_info.height;
   4440   return(shadow_image);
   4441 }
   4442 
   4443 
   4444 /*
   4445 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   4446 %                                                                             %
   4447 %                                                                             %
   4448 %                                                                             %
   4449 %     S k e t c h I m a g e                                                   %
   4450 %                                                                             %
   4451 %                                                                             %
   4452 %                                                                             %
   4453 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   4454 %
   4455 %  SketchImage() simulates a pencil sketch.  We convolve the image with a
   4456 %  Gaussian operator of the given radius and standard deviation (sigma).  For
   4457 %  reasonable results, radius should be larger than sigma.  Use a radius of 0
   4458 %  and SketchImage() selects a suitable radius for you.  Angle gives the angle
   4459 %  of the sketch.
   4460 %
   4461 %  The format of the SketchImage method is:
   4462 %
   4463 %    Image *SketchImage(const Image *image,const double radius,
   4464 %      const double sigma,const double angle,ExceptionInfo *exception)
   4465 %
   4466 %  A description of each parameter follows:
   4467 %
   4468 %    o image: the image.
   4469 %
   4470 %    o radius: the radius of the Gaussian, in pixels, not counting the
   4471 %      center pixel.
   4472 %
   4473 %    o sigma: the standard deviation of the Gaussian, in pixels.
   4474 %
   4475 %    o angle: apply the effect along this angle.
   4476 %
   4477 %    o exception: return any errors or warnings in this structure.
   4478 %
   4479 */
   4480 MagickExport Image *SketchImage(const Image *image,const double radius,
   4481   const double sigma,const double angle,ExceptionInfo *exception)
   4482 {
   4483   CacheView
   4484     *random_view;
   4485 
   4486   Image
   4487     *blend_image,
   4488     *blur_image,
   4489     *dodge_image,
   4490     *random_image,
   4491     *sketch_image;
   4492 
   4493   MagickBooleanType
   4494     status;
   4495 
   4496   RandomInfo
   4497     **magick_restrict random_info;
   4498 
   4499   ssize_t
   4500     y;
   4501 
   4502 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   4503   unsigned long
   4504     key;
   4505 #endif
   4506 
   4507   /*
   4508     Sketch image.
   4509   */
   4510   random_image=CloneImage(image,image->columns << 1,image->rows << 1,
   4511     MagickTrue,exception);
   4512   if (random_image == (Image *) NULL)
   4513     return((Image *) NULL);
   4514   status=MagickTrue;
   4515   random_info=AcquireRandomInfoThreadSet();
   4516   random_view=AcquireAuthenticCacheView(random_image,exception);
   4517 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   4518   key=GetRandomSecretKey(random_info[0]);
   4519   #pragma omp parallel for schedule(static,4) shared(status) \
   4520     magick_threads(random_image,random_image,random_image->rows,key == ~0UL)
   4521 #endif
   4522   for (y=0; y < (ssize_t) random_image->rows; y++)
   4523   {
   4524     const int
   4525       id = GetOpenMPThreadId();
   4526 
   4527     register Quantum
   4528       *magick_restrict q;
   4529 
   4530     register ssize_t
   4531       x;
   4532 
   4533     if (status == MagickFalse)
   4534       continue;
   4535     q=QueueCacheViewAuthenticPixels(random_view,0,y,random_image->columns,1,
   4536       exception);
   4537     if (q == (Quantum *) NULL)
   4538       {
   4539         status=MagickFalse;
   4540         continue;
   4541       }
   4542     for (x=0; x < (ssize_t) random_image->columns; x++)
   4543     {
   4544       double
   4545         value;
   4546 
   4547       register ssize_t
   4548         i;
   4549 
   4550       if (GetPixelReadMask(random_image,q) == 0)
   4551         {
   4552           q+=GetPixelChannels(random_image);
   4553           continue;
   4554         }
   4555       value=GetPseudoRandomValue(random_info[id]);
   4556       for (i=0; i < (ssize_t) GetPixelChannels(random_image); i++)
   4557       {
   4558         PixelChannel channel=GetPixelChannelChannel(image,i);
   4559         PixelTrait traits=GetPixelChannelTraits(image,channel);
   4560         if (traits == UndefinedPixelTrait)
   4561           continue;
   4562         q[i]=ClampToQuantum(QuantumRange*value);
   4563       }
   4564       q+=GetPixelChannels(random_image);
   4565     }
   4566     if (SyncCacheViewAuthenticPixels(random_view,exception) == MagickFalse)
   4567       status=MagickFalse;
   4568   }
   4569   random_view=DestroyCacheView(random_view);
   4570   random_info=DestroyRandomInfoThreadSet(random_info);
   4571   if (status == MagickFalse)
   4572     {
   4573       random_image=DestroyImage(random_image);
   4574       return(random_image);
   4575     }
   4576   blur_image=MotionBlurImage(random_image,radius,sigma,angle,exception);
   4577   random_image=DestroyImage(random_image);
   4578   if (blur_image == (Image *) NULL)
   4579     return((Image *) NULL);
   4580   dodge_image=EdgeImage(blur_image,radius,exception);
   4581   blur_image=DestroyImage(blur_image);
   4582   if (dodge_image == (Image *) NULL)
   4583     return((Image *) NULL);
   4584   (void) NormalizeImage(dodge_image,exception);
   4585   (void) NegateImage(dodge_image,MagickFalse,exception);
   4586   (void) TransformImage(&dodge_image,(char *) NULL,"50%",exception);
   4587   sketch_image=CloneImage(image,0,0,MagickTrue,exception);
   4588   if (sketch_image == (Image *) NULL)
   4589     {
   4590       dodge_image=DestroyImage(dodge_image);
   4591       return((Image *) NULL);
   4592     }
   4593   (void) CompositeImage(sketch_image,dodge_image,ColorDodgeCompositeOp,
   4594     MagickTrue,0,0,exception);
   4595   dodge_image=DestroyImage(dodge_image);
   4596   blend_image=CloneImage(image,0,0,MagickTrue,exception);
   4597   if (blend_image == (Image *) NULL)
   4598     {
   4599       sketch_image=DestroyImage(sketch_image);
   4600       return((Image *) NULL);
   4601     }
   4602   if (blend_image->alpha_trait != BlendPixelTrait)
   4603     (void) SetImageAlpha(blend_image,TransparentAlpha,exception);
   4604   (void) SetImageArtifact(blend_image,"compose:args","20x80");
   4605   (void) CompositeImage(sketch_image,blend_image,BlendCompositeOp,MagickTrue,
   4606     0,0,exception);
   4607   blend_image=DestroyImage(blend_image);
   4608   return(sketch_image);
   4609 }
   4610 
   4611 
   4612 /*
   4613 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   4614 %                                                                             %
   4615 %                                                                             %
   4616 %                                                                             %
   4617 %     S o l a r i z e I m a g e                                               %
   4618 %                                                                             %
   4619 %                                                                             %
   4620 %                                                                             %
   4621 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   4622 %
   4623 %  SolarizeImage() applies a special effect to the image, similar to the effect
   4624 %  achieved in a photo darkroom by selectively exposing areas of photo
   4625 %  sensitive paper to light.  Threshold ranges from 0 to QuantumRange and is a
   4626 %  measure of the extent of the solarization.
   4627 %
   4628 %  The format of the SolarizeImage method is:
   4629 %
   4630 %      MagickBooleanType SolarizeImage(Image *image,const double threshold,
   4631 %        ExceptionInfo *exception)
   4632 %
   4633 %  A description of each parameter follows:
   4634 %
   4635 %    o image: the image.
   4636 %
   4637 %    o threshold:  Define the extent of the solarization.
   4638 %
   4639 %    o exception: return any errors or warnings in this structure.
   4640 %
   4641 */
   4642 MagickExport MagickBooleanType SolarizeImage(Image *image,
   4643   const double threshold,ExceptionInfo *exception)
   4644 {
   4645 #define SolarizeImageTag  "Solarize/Image"
   4646 
   4647   CacheView
   4648     *image_view;
   4649 
   4650   MagickBooleanType
   4651     status;
   4652 
   4653   MagickOffsetType
   4654     progress;
   4655 
   4656   ssize_t
   4657     y;
   4658 
   4659   assert(image != (Image *) NULL);
   4660   assert(image->signature == MagickCoreSignature);
   4661   if (image->debug != MagickFalse)
   4662     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   4663   if (IsGrayColorspace(image->colorspace) != MagickFalse)
   4664     (void) SetImageColorspace(image,sRGBColorspace,exception);
   4665   if (image->storage_class == PseudoClass)
   4666     {
   4667       register ssize_t
   4668         i;
   4669 
   4670       /*
   4671         Solarize colormap.
   4672       */
   4673       for (i=0; i < (ssize_t) image->colors; i++)
   4674       {
   4675         if ((double) image->colormap[i].red > threshold)
   4676           image->colormap[i].red=QuantumRange-image->colormap[i].red;
   4677         if ((double) image->colormap[i].green > threshold)
   4678           image->colormap[i].green=QuantumRange-image->colormap[i].green;
   4679         if ((double) image->colormap[i].blue > threshold)
   4680           image->colormap[i].blue=QuantumRange-image->colormap[i].blue;
   4681       }
   4682     }
   4683   /*
   4684     Solarize image.
   4685   */
   4686   status=MagickTrue;
   4687   progress=0;
   4688   image_view=AcquireAuthenticCacheView(image,exception);
   4689 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   4690   #pragma omp parallel for schedule(static,4) shared(progress,status) \
   4691     magick_threads(image,image,image->rows,1)
   4692 #endif
   4693   for (y=0; y < (ssize_t) image->rows; y++)
   4694   {
   4695     register ssize_t
   4696       x;
   4697 
   4698     register Quantum
   4699       *magick_restrict q;
   4700 
   4701     if (status == MagickFalse)
   4702       continue;
   4703     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
   4704     if (q == (Quantum *) NULL)
   4705       {
   4706         status=MagickFalse;
   4707         continue;
   4708       }
   4709     for (x=0; x < (ssize_t) image->columns; x++)
   4710     {
   4711       register ssize_t
   4712         i;
   4713 
   4714       if (GetPixelReadMask(image,q) == 0)
   4715         {
   4716           q+=GetPixelChannels(image);
   4717           continue;
   4718         }
   4719       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   4720       {
   4721         PixelChannel channel=GetPixelChannelChannel(image,i);
   4722         PixelTrait traits=GetPixelChannelTraits(image,channel);
   4723         if ((traits & UpdatePixelTrait) == 0)
   4724           continue;
   4725         if ((double) q[i] > threshold)
   4726           q[i]=QuantumRange-q[i];
   4727       }
   4728       q+=GetPixelChannels(image);
   4729     }
   4730     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
   4731       status=MagickFalse;
   4732     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   4733       {
   4734         MagickBooleanType
   4735           proceed;
   4736 
   4737 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   4738         #pragma omp critical (MagickCore_SolarizeImage)
   4739 #endif
   4740         proceed=SetImageProgress(image,SolarizeImageTag,progress++,image->rows);
   4741         if (proceed == MagickFalse)
   4742           status=MagickFalse;
   4743       }
   4744   }
   4745   image_view=DestroyCacheView(image_view);
   4746   return(status);
   4747 }
   4748 
   4749 
   4750 /*
   4751 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   4752 %                                                                             %
   4753 %                                                                             %
   4754 %                                                                             %
   4755 %   S t e g a n o I m a g e                                                   %
   4756 %                                                                             %
   4757 %                                                                             %
   4758 %                                                                             %
   4759 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   4760 %
   4761 %  SteganoImage() hides a digital watermark within the image.  Recover
   4762 %  the hidden watermark later to prove that the authenticity of an image.
   4763 %  Offset defines the start position within the image to hide the watermark.
   4764 %
   4765 %  The format of the SteganoImage method is:
   4766 %
   4767 %      Image *SteganoImage(const Image *image,Image *watermark,
   4768 %        ExceptionInfo *exception)
   4769 %
   4770 %  A description of each parameter follows:
   4771 %
   4772 %    o image: the image.
   4773 %
   4774 %    o watermark: the watermark image.
   4775 %
   4776 %    o exception: return any errors or warnings in this structure.
   4777 %
   4778 */
   4779 MagickExport Image *SteganoImage(const Image *image,const Image *watermark,
   4780   ExceptionInfo *exception)
   4781 {
   4782 #define GetBit(alpha,i) ((((size_t) (alpha) >> (size_t) (i)) & 0x01) != 0)
   4783 #define SetBit(alpha,i,set) (Quantum) ((set) != 0 ? (size_t) (alpha) \
   4784   | (one << (size_t) (i)) : (size_t) (alpha) & ~(one << (size_t) (i)))
   4785 #define SteganoImageTag  "Stegano/Image"
   4786 
   4787   CacheView
   4788     *stegano_view,
   4789     *watermark_view;
   4790 
   4791   Image
   4792     *stegano_image;
   4793 
   4794   int
   4795     c;
   4796 
   4797   MagickBooleanType
   4798     status;
   4799 
   4800   PixelInfo
   4801     pixel;
   4802 
   4803   register Quantum
   4804     *q;
   4805 
   4806   register ssize_t
   4807     x;
   4808 
   4809   size_t
   4810     depth,
   4811     one;
   4812 
   4813   ssize_t
   4814     i,
   4815     j,
   4816     k,
   4817     y;
   4818 
   4819   /*
   4820     Initialize steganographic image attributes.
   4821   */
   4822   assert(image != (const Image *) NULL);
   4823   assert(image->signature == MagickCoreSignature);
   4824   if (image->debug != MagickFalse)
   4825     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   4826   assert(watermark != (const Image *) NULL);
   4827   assert(watermark->signature == MagickCoreSignature);
   4828   assert(exception != (ExceptionInfo *) NULL);
   4829   assert(exception->signature == MagickCoreSignature);
   4830   one=1UL;
   4831   stegano_image=CloneImage(image,0,0,MagickTrue,exception);
   4832   if (stegano_image == (Image *) NULL)
   4833     return((Image *) NULL);
   4834   stegano_image->depth=MAGICKCORE_QUANTUM_DEPTH;
   4835   if (SetImageStorageClass(stegano_image,DirectClass,exception) == MagickFalse)
   4836     {
   4837       stegano_image=DestroyImage(stegano_image);
   4838       return((Image *) NULL);
   4839     }
   4840   /*
   4841     Hide watermark in low-order bits of image.
   4842   */
   4843   c=0;
   4844   i=0;
   4845   j=0;
   4846   depth=stegano_image->depth;
   4847   k=stegano_image->offset;
   4848   status=MagickTrue;
   4849   watermark_view=AcquireVirtualCacheView(watermark,exception);
   4850   stegano_view=AcquireAuthenticCacheView(stegano_image,exception);
   4851   for (i=(ssize_t) depth-1; (i >= 0) && (j < (ssize_t) depth); i--)
   4852   {
   4853     for (y=0; (y < (ssize_t) watermark->rows) && (j < (ssize_t) depth); y++)
   4854     {
   4855       for (x=0; (x < (ssize_t) watermark->columns) && (j < (ssize_t) depth); x++)
   4856       {
   4857         ssize_t
   4858           offset;
   4859 
   4860         (void) GetOneCacheViewVirtualPixelInfo(watermark_view,x,y,&pixel,
   4861           exception);
   4862         offset=k/(ssize_t) stegano_image->columns;
   4863         if (offset >= (ssize_t) stegano_image->rows)
   4864           break;
   4865         q=GetCacheViewAuthenticPixels(stegano_view,k % (ssize_t)
   4866           stegano_image->columns,k/(ssize_t) stegano_image->columns,1,1,
   4867           exception);
   4868         if (q == (Quantum *) NULL)
   4869           break;
   4870         switch (c)
   4871         {
   4872           case 0:
   4873           {
   4874             SetPixelRed(stegano_image,SetBit(GetPixelRed(stegano_image,q),j,
   4875               GetBit(GetPixelInfoIntensity(stegano_image,&pixel),i)),q);
   4876             break;
   4877           }
   4878           case 1:
   4879           {
   4880             SetPixelGreen(stegano_image,SetBit(GetPixelGreen(stegano_image,q),j,
   4881               GetBit(GetPixelInfoIntensity(stegano_image,&pixel),i)),q);
   4882             break;
   4883           }
   4884           case 2:
   4885           {
   4886             SetPixelBlue(stegano_image,SetBit(GetPixelBlue(stegano_image,q),j,
   4887               GetBit(GetPixelInfoIntensity(stegano_image,&pixel),i)),q);
   4888             break;
   4889           }
   4890         }
   4891         if (SyncCacheViewAuthenticPixels(stegano_view,exception) == MagickFalse)
   4892           break;
   4893         c++;
   4894         if (c == 3)
   4895           c=0;
   4896         k++;
   4897         if (k == (ssize_t) (stegano_image->columns*stegano_image->columns))
   4898           k=0;
   4899         if (k == stegano_image->offset)
   4900           j++;
   4901       }
   4902     }
   4903     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   4904       {
   4905         MagickBooleanType
   4906           proceed;
   4907 
   4908         proceed=SetImageProgress(image,SteganoImageTag,(MagickOffsetType)
   4909           (depth-i),depth);
   4910         if (proceed == MagickFalse)
   4911           status=MagickFalse;
   4912       }
   4913   }
   4914   stegano_view=DestroyCacheView(stegano_view);
   4915   watermark_view=DestroyCacheView(watermark_view);
   4916   if (status == MagickFalse)
   4917     stegano_image=DestroyImage(stegano_image);
   4918   return(stegano_image);
   4919 }
   4920 
   4921 
   4922 /*
   4923 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   4924 %                                                                             %
   4925 %                                                                             %
   4926 %                                                                             %
   4927 %   S t e r e o A n a g l y p h I m a g e                                     %
   4928 %                                                                             %
   4929 %                                                                             %
   4930 %                                                                             %
   4931 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   4932 %
   4933 %  StereoAnaglyphImage() combines two images and produces a single image that
   4934 %  is the composite of a left and right image of a stereo pair.  Special
   4935 %  red-green stereo glasses are required to view this effect.
   4936 %
   4937 %  The format of the StereoAnaglyphImage method is:
   4938 %
   4939 %      Image *StereoImage(const Image *left_image,const Image *right_image,
   4940 %        ExceptionInfo *exception)
   4941 %      Image *StereoAnaglyphImage(const Image *left_image,
   4942 %        const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
   4943 %        ExceptionInfo *exception)
   4944 %
   4945 %  A description of each parameter follows:
   4946 %
   4947 %    o left_image: the left image.
   4948 %
   4949 %    o right_image: the right image.
   4950 %
   4951 %    o exception: return any errors or warnings in this structure.
   4952 %
   4953 %    o x_offset: amount, in pixels, by which the left image is offset to the
   4954 %      right of the right image.
   4955 %
   4956 %    o y_offset: amount, in pixels, by which the left image is offset to the
   4957 %      bottom of the right image.
   4958 %
   4959 %
   4960 */
   4961 MagickExport Image *StereoImage(const Image *left_image,
   4962   const Image *right_image,ExceptionInfo *exception)
   4963 {
   4964   return(StereoAnaglyphImage(left_image,right_image,0,0,exception));
   4965 }
   4966 
   4967 MagickExport Image *StereoAnaglyphImage(const Image *left_image,
   4968   const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
   4969   ExceptionInfo *exception)
   4970 {
   4971 #define StereoImageTag  "Stereo/Image"
   4972 
   4973   const Image
   4974     *image;
   4975 
   4976   Image
   4977     *stereo_image;
   4978 
   4979   MagickBooleanType
   4980     status;
   4981 
   4982   ssize_t
   4983     y;
   4984 
   4985   assert(left_image != (const Image *) NULL);
   4986   assert(left_image->signature == MagickCoreSignature);
   4987   if (left_image->debug != MagickFalse)
   4988     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
   4989       left_image->filename);
   4990   assert(right_image != (const Image *) NULL);
   4991   assert(right_image->signature == MagickCoreSignature);
   4992   assert(exception != (ExceptionInfo *) NULL);
   4993   assert(exception->signature == MagickCoreSignature);
   4994   assert(right_image != (const Image *) NULL);
   4995   image=left_image;
   4996   if ((left_image->columns != right_image->columns) ||
   4997       (left_image->rows != right_image->rows))
   4998     ThrowImageException(ImageError,"LeftAndRightImageSizesDiffer");
   4999   /*
   5000     Initialize stereo image attributes.
   5001   */
   5002   stereo_image=CloneImage(left_image,left_image->columns,left_image->rows,
   5003     MagickTrue,exception);
   5004   if (stereo_image == (Image *) NULL)
   5005     return((Image *) NULL);
   5006   if (SetImageStorageClass(stereo_image,DirectClass,exception) == MagickFalse)
   5007     {
   5008       stereo_image=DestroyImage(stereo_image);
   5009       return((Image *) NULL);
   5010     }
   5011   (void) SetImageColorspace(stereo_image,sRGBColorspace,exception);
   5012   /*
   5013     Copy left image to red channel and right image to blue channel.
   5014   */
   5015   status=MagickTrue;
   5016   for (y=0; y < (ssize_t) stereo_image->rows; y++)
   5017   {
   5018     register const Quantum
   5019       *magick_restrict p,
   5020       *magick_restrict q;
   5021 
   5022     register ssize_t
   5023       x;
   5024 
   5025     register Quantum
   5026       *magick_restrict r;
   5027 
   5028     p=GetVirtualPixels(left_image,-x_offset,y-y_offset,image->columns,1,
   5029       exception);
   5030     q=GetVirtualPixels(right_image,0,y,right_image->columns,1,exception);
   5031     r=QueueAuthenticPixels(stereo_image,0,y,stereo_image->columns,1,exception);
   5032     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL) ||
   5033         (r == (Quantum *) NULL))
   5034       break;
   5035     for (x=0; x < (ssize_t) stereo_image->columns; x++)
   5036     {
   5037       SetPixelRed(image,GetPixelRed(left_image,p),r);
   5038       SetPixelGreen(image,GetPixelGreen(right_image,q),r);
   5039       SetPixelBlue(image,GetPixelBlue(right_image,q),r);
   5040       if ((GetPixelAlphaTraits(stereo_image) & CopyPixelTrait) != 0)
   5041         SetPixelAlpha(image,(GetPixelAlpha(left_image,p)+
   5042           GetPixelAlpha(right_image,q))/2,r);
   5043       p+=GetPixelChannels(left_image);
   5044       q+=GetPixelChannels(right_image);
   5045       r+=GetPixelChannels(stereo_image);
   5046     }
   5047     if (SyncAuthenticPixels(stereo_image,exception) == MagickFalse)
   5048       break;
   5049     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   5050       {
   5051         MagickBooleanType
   5052           proceed;
   5053 
   5054         proceed=SetImageProgress(image,StereoImageTag,(MagickOffsetType) y,
   5055           stereo_image->rows);
   5056         if (proceed == MagickFalse)
   5057           status=MagickFalse;
   5058       }
   5059   }
   5060   if (status == MagickFalse)
   5061     stereo_image=DestroyImage(stereo_image);
   5062   return(stereo_image);
   5063 }
   5064 
   5065 
   5066 /*
   5067 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   5068 %                                                                             %
   5069 %                                                                             %
   5070 %                                                                             %
   5071 %     S w i r l I m a g e                                                     %
   5072 %                                                                             %
   5073 %                                                                             %
   5074 %                                                                             %
   5075 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   5076 %
   5077 %  SwirlImage() swirls the pixels about the center of the image, where
   5078 %  degrees indicates the sweep of the arc through which each pixel is moved.
   5079 %  You get a more dramatic effect as the degrees move from 1 to 360.
   5080 %
   5081 %  The format of the SwirlImage method is:
   5082 %
   5083 %      Image *SwirlImage(const Image *image,double degrees,
   5084 %        const PixelInterpolateMethod method,ExceptionInfo *exception)
   5085 %
   5086 %  A description of each parameter follows:
   5087 %
   5088 %    o image: the image.
   5089 %
   5090 %    o degrees: Define the tightness of the swirling effect.
   5091 %
   5092 %    o method: the pixel interpolation method.
   5093 %
   5094 %    o exception: return any errors or warnings in this structure.
   5095 %
   5096 */
   5097 MagickExport Image *SwirlImage(const Image *image,double degrees,
   5098   const PixelInterpolateMethod method,ExceptionInfo *exception)
   5099 {
   5100 #define SwirlImageTag  "Swirl/Image"
   5101 
   5102   CacheView
   5103     *image_view,
   5104     *interpolate_view,
   5105     *swirl_view;
   5106 
   5107   Image
   5108     *swirl_image;
   5109 
   5110   MagickBooleanType
   5111     status;
   5112 
   5113   MagickOffsetType
   5114     progress;
   5115 
   5116   double
   5117     radius;
   5118 
   5119   PointInfo
   5120     center,
   5121     scale;
   5122 
   5123   ssize_t
   5124     y;
   5125 
   5126   /*
   5127     Initialize swirl image attributes.
   5128   */
   5129   assert(image != (const Image *) NULL);
   5130   assert(image->signature == MagickCoreSignature);
   5131   if (image->debug != MagickFalse)
   5132     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   5133   assert(exception != (ExceptionInfo *) NULL);
   5134   assert(exception->signature == MagickCoreSignature);
   5135   swirl_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
   5136   if (swirl_image == (Image *) NULL)
   5137     return((Image *) NULL);
   5138   if (SetImageStorageClass(swirl_image,DirectClass,exception) == MagickFalse)
   5139     {
   5140       swirl_image=DestroyImage(swirl_image);
   5141       return((Image *) NULL);
   5142     }
   5143   if (swirl_image->background_color.alpha != OpaqueAlpha)
   5144     swirl_image->alpha_trait=BlendPixelTrait;
   5145   /*
   5146     Compute scaling factor.
   5147   */
   5148   center.x=(double) image->columns/2.0;
   5149   center.y=(double) image->rows/2.0;
   5150   radius=MagickMax(center.x,center.y);
   5151   scale.x=1.0;
   5152   scale.y=1.0;
   5153   if (image->columns > image->rows)
   5154     scale.y=(double) image->columns/(double) image->rows;
   5155   else
   5156     if (image->columns < image->rows)
   5157       scale.x=(double) image->rows/(double) image->columns;
   5158   degrees=(double) DegreesToRadians(degrees);
   5159   /*
   5160     Swirl image.
   5161   */
   5162   status=MagickTrue;
   5163   progress=0;
   5164   image_view=AcquireVirtualCacheView(image,exception);
   5165   interpolate_view=AcquireVirtualCacheView(image,exception);
   5166   swirl_view=AcquireAuthenticCacheView(swirl_image,exception);
   5167 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   5168   #pragma omp parallel for schedule(static,4) shared(progress,status) \
   5169     magick_threads(image,swirl_image,image->rows,1)
   5170 #endif
   5171   for (y=0; y < (ssize_t) image->rows; y++)
   5172   {
   5173     double
   5174       distance;
   5175 
   5176     PointInfo
   5177       delta;
   5178 
   5179     register const Quantum
   5180       *magick_restrict p;
   5181 
   5182     register ssize_t
   5183       x;
   5184 
   5185     register Quantum
   5186       *magick_restrict q;
   5187 
   5188     if (status == MagickFalse)
   5189       continue;
   5190     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
   5191     q=QueueCacheViewAuthenticPixels(swirl_view,0,y,swirl_image->columns,1,
   5192       exception);
   5193     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
   5194       {
   5195         status=MagickFalse;
   5196         continue;
   5197       }
   5198     delta.y=scale.y*(double) (y-center.y);
   5199     for (x=0; x < (ssize_t) image->columns; x++)
   5200     {
   5201       /*
   5202         Determine if the pixel is within an ellipse.
   5203       */
   5204       if (GetPixelReadMask(image,p) == 0)
   5205         {
   5206           SetPixelBackgoundColor(swirl_image,q);
   5207           p+=GetPixelChannels(image);
   5208           q+=GetPixelChannels(swirl_image);
   5209           continue;
   5210         }
   5211       delta.x=scale.x*(double) (x-center.x);
   5212       distance=delta.x*delta.x+delta.y*delta.y;
   5213       if (distance >= (radius*radius))
   5214         {
   5215           register ssize_t
   5216             i;
   5217 
   5218           for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   5219           {
   5220             PixelChannel channel=GetPixelChannelChannel(image,i);
   5221             PixelTrait traits=GetPixelChannelTraits(image,channel);
   5222             PixelTrait swirl_traits=GetPixelChannelTraits(swirl_image,channel);
   5223             if ((traits == UndefinedPixelTrait) ||
   5224                 (swirl_traits == UndefinedPixelTrait))
   5225               continue;
   5226             SetPixelChannel(swirl_image,channel,p[i],q);
   5227           }
   5228         }
   5229       else
   5230         {
   5231           double
   5232             cosine,
   5233             factor,
   5234             sine;
   5235 
   5236           /*
   5237             Swirl the pixel.
   5238           */
   5239           factor=1.0-sqrt((double) distance)/radius;
   5240           sine=sin((double) (degrees*factor*factor));
   5241           cosine=cos((double) (degrees*factor*factor));
   5242           status=InterpolatePixelChannels(image,interpolate_view,swirl_image,
   5243             method,((cosine*delta.x-sine*delta.y)/scale.x+center.x),(double)
   5244             ((sine*delta.x+cosine*delta.y)/scale.y+center.y),q,exception);
   5245         }
   5246       p+=GetPixelChannels(image);
   5247       q+=GetPixelChannels(swirl_image);
   5248     }
   5249     if (SyncCacheViewAuthenticPixels(swirl_view,exception) == MagickFalse)
   5250       status=MagickFalse;
   5251     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   5252       {
   5253         MagickBooleanType
   5254           proceed;
   5255 
   5256 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   5257         #pragma omp critical (MagickCore_SwirlImage)
   5258 #endif
   5259         proceed=SetImageProgress(image,SwirlImageTag,progress++,image->rows);
   5260         if (proceed == MagickFalse)
   5261           status=MagickFalse;
   5262       }
   5263   }
   5264   swirl_view=DestroyCacheView(swirl_view);
   5265   interpolate_view=DestroyCacheView(interpolate_view);
   5266   image_view=DestroyCacheView(image_view);
   5267   if (status == MagickFalse)
   5268     swirl_image=DestroyImage(swirl_image);
   5269   return(swirl_image);
   5270 }
   5271 
   5272 
   5273 /*
   5274 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   5275 %                                                                             %
   5276 %                                                                             %
   5277 %                                                                             %
   5278 %     T i n t I m a g e                                                       %
   5279 %                                                                             %
   5280 %                                                                             %
   5281 %                                                                             %
   5282 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   5283 %
   5284 %  TintImage() applies a color vector to each pixel in the image.  The length
   5285 %  of the vector is 0 for black and white and at its maximum for the midtones.
   5286 %  The vector weighting function is f(x)=(1-(4.0*((x-0.5)*(x-0.5))))
   5287 %
   5288 %  The format of the TintImage method is:
   5289 %
   5290 %      Image *TintImage(const Image *image,const char *blend,
   5291 %        const PixelInfo *tint,ExceptionInfo *exception)
   5292 %
   5293 %  A description of each parameter follows:
   5294 %
   5295 %    o image: the image.
   5296 %
   5297 %    o blend: A color value used for tinting.
   5298 %
   5299 %    o tint: A color value used for tinting.
   5300 %
   5301 %    o exception: return any errors or warnings in this structure.
   5302 %
   5303 */
   5304 MagickExport Image *TintImage(const Image *image,const char *blend,
   5305   const PixelInfo *tint,ExceptionInfo *exception)
   5306 {
   5307 #define TintImageTag  "Tint/Image"
   5308 
   5309   CacheView
   5310     *image_view,
   5311     *tint_view;
   5312 
   5313   double
   5314     intensity;
   5315 
   5316   GeometryInfo
   5317     geometry_info;
   5318 
   5319   Image
   5320     *tint_image;
   5321 
   5322   MagickBooleanType
   5323     status;
   5324 
   5325   MagickOffsetType
   5326     progress;
   5327 
   5328   PixelInfo
   5329     color_vector;
   5330 
   5331   MagickStatusType
   5332     flags;
   5333 
   5334   ssize_t
   5335     y;
   5336 
   5337   /*
   5338     Allocate tint image.
   5339   */
   5340   assert(image != (const Image *) NULL);
   5341   assert(image->signature == MagickCoreSignature);
   5342   if (image->debug != MagickFalse)
   5343     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   5344   assert(exception != (ExceptionInfo *) NULL);
   5345   assert(exception->signature == MagickCoreSignature);
   5346   tint_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
   5347   if (tint_image == (Image *) NULL)
   5348     return((Image *) NULL);
   5349   if (SetImageStorageClass(tint_image,DirectClass,exception) == MagickFalse)
   5350     {
   5351       tint_image=DestroyImage(tint_image);
   5352       return((Image *) NULL);
   5353     }
   5354   if ((IsGrayColorspace(image->colorspace) != MagickFalse) &&
   5355       (IsPixelInfoGray(tint) == MagickFalse))
   5356     (void) SetImageColorspace(tint_image,sRGBColorspace,exception);
   5357   if (blend == (const char *) NULL)
   5358     return(tint_image);
   5359   /*
   5360     Determine RGB values of the color.
   5361   */
   5362   GetPixelInfo(image,&color_vector);
   5363   flags=ParseGeometry(blend,&geometry_info);
   5364   color_vector.red=geometry_info.rho;
   5365   color_vector.green=geometry_info.rho;
   5366   color_vector.blue=geometry_info.rho;
   5367   color_vector.alpha=(MagickRealType) OpaqueAlpha;
   5368   if ((flags & SigmaValue) != 0)
   5369     color_vector.green=geometry_info.sigma;
   5370   if ((flags & XiValue) != 0)
   5371     color_vector.blue=geometry_info.xi;
   5372   if ((flags & PsiValue) != 0)
   5373     color_vector.alpha=geometry_info.psi;
   5374   if (image->colorspace == CMYKColorspace)
   5375     {
   5376       color_vector.black=geometry_info.rho;
   5377       if ((flags & PsiValue) != 0)
   5378         color_vector.black=geometry_info.psi;
   5379       if ((flags & ChiValue) != 0)
   5380         color_vector.alpha=geometry_info.chi;
   5381     }
   5382   intensity=(double) GetPixelInfoIntensity((const Image *) NULL,tint);
   5383   color_vector.red=(double) (color_vector.red*tint->red/100.0-intensity);
   5384   color_vector.green=(double) (color_vector.green*tint->green/100.0-intensity);
   5385   color_vector.blue=(double) (color_vector.blue*tint->blue/100.0-intensity);
   5386   color_vector.black=(double) (color_vector.black*tint->black/100.0-intensity);
   5387   color_vector.alpha=(double) (color_vector.alpha*tint->alpha/100.0-intensity);
   5388   /*
   5389     Tint image.
   5390   */
   5391   status=MagickTrue;
   5392   progress=0;
   5393   image_view=AcquireVirtualCacheView(image,exception);
   5394   tint_view=AcquireAuthenticCacheView(tint_image,exception);
   5395 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   5396   #pragma omp parallel for schedule(static,4) shared(progress,status) \
   5397     magick_threads(image,tint_image,image->rows,1)
   5398 #endif
   5399   for (y=0; y < (ssize_t) image->rows; y++)
   5400   {
   5401     register const Quantum
   5402       *magick_restrict p;
   5403 
   5404     register Quantum
   5405       *magick_restrict q;
   5406 
   5407     register ssize_t
   5408       x;
   5409 
   5410     if (status == MagickFalse)
   5411       continue;
   5412     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
   5413     q=QueueCacheViewAuthenticPixels(tint_view,0,y,tint_image->columns,1,
   5414       exception);
   5415     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
   5416       {
   5417         status=MagickFalse;
   5418         continue;
   5419       }
   5420     for (x=0; x < (ssize_t) image->columns; x++)
   5421     {
   5422       PixelInfo
   5423         pixel;
   5424 
   5425       double
   5426         weight;
   5427 
   5428       register ssize_t
   5429         i;
   5430 
   5431       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   5432       {
   5433         PixelChannel channel=GetPixelChannelChannel(image,i);
   5434         PixelTrait traits=GetPixelChannelTraits(image,channel);
   5435         PixelTrait tint_traits=GetPixelChannelTraits(tint_image,channel);
   5436         if ((traits == UndefinedPixelTrait) ||
   5437             (tint_traits == UndefinedPixelTrait))
   5438           continue;
   5439         if (((tint_traits & CopyPixelTrait) != 0) ||
   5440             (GetPixelReadMask(image,p) == 0))
   5441           {
   5442             SetPixelChannel(tint_image,channel,p[i],q);
   5443             continue;
   5444           }
   5445       }
   5446       GetPixelInfo(image,&pixel);
   5447       weight=QuantumScale*GetPixelRed(image,p)-0.5;
   5448       pixel.red=(double) GetPixelRed(image,p)+color_vector.red*(1.0-(4.0*
   5449         (weight*weight)));
   5450       weight=QuantumScale*GetPixelGreen(image,p)-0.5;
   5451       pixel.green=(double) GetPixelGreen(image,p)+color_vector.green*(1.0-(4.0*
   5452         (weight*weight)));
   5453       weight=QuantumScale*GetPixelBlue(image,p)-0.5;
   5454       pixel.blue=(double) GetPixelBlue(image,p)+color_vector.blue*(1.0-(4.0*
   5455         (weight*weight)));
   5456       weight=QuantumScale*GetPixelBlack(image,p)-0.5;
   5457       pixel.black=(double) GetPixelBlack(image,p)+color_vector.black*(1.0-(4.0*
   5458         (weight*weight)));
   5459       SetPixelViaPixelInfo(tint_image,&pixel,q);
   5460       p+=GetPixelChannels(image);
   5461       q+=GetPixelChannels(tint_image);
   5462     }
   5463     if (SyncCacheViewAuthenticPixels(tint_view,exception) == MagickFalse)
   5464       status=MagickFalse;
   5465     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   5466       {
   5467         MagickBooleanType
   5468           proceed;
   5469 
   5470 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   5471         #pragma omp critical (MagickCore_TintImage)
   5472 #endif
   5473         proceed=SetImageProgress(image,TintImageTag,progress++,image->rows);
   5474         if (proceed == MagickFalse)
   5475           status=MagickFalse;
   5476       }
   5477   }
   5478   tint_view=DestroyCacheView(tint_view);
   5479   image_view=DestroyCacheView(image_view);
   5480   if (status == MagickFalse)
   5481     tint_image=DestroyImage(tint_image);
   5482   return(tint_image);
   5483 }
   5484 
   5485 
   5486 /*
   5487 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   5488 %                                                                             %
   5489 %                                                                             %
   5490 %                                                                             %
   5491 %     V i g n e t t e I m a g e                                               %
   5492 %                                                                             %
   5493 %                                                                             %
   5494 %                                                                             %
   5495 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   5496 %
   5497 %  VignetteImage() softens the edges of the image in vignette style.
   5498 %
   5499 %  The format of the VignetteImage method is:
   5500 %
   5501 %      Image *VignetteImage(const Image *image,const double radius,
   5502 %        const double sigma,const ssize_t x,const ssize_t y,
   5503 %        ExceptionInfo *exception)
   5504 %
   5505 %  A description of each parameter follows:
   5506 %
   5507 %    o image: the image.
   5508 %
   5509 %    o radius: the radius of the pixel neighborhood.
   5510 %
   5511 %    o sigma: the standard deviation of the Gaussian, in pixels.
   5512 %
   5513 %    o x, y:  Define the x and y ellipse offset.
   5514 %
   5515 %    o exception: return any errors or warnings in this structure.
   5516 %
   5517 */
   5518 MagickExport Image *VignetteImage(const Image *image,const double radius,
   5519   const double sigma,const ssize_t x,const ssize_t y,ExceptionInfo *exception)
   5520 {
   5521   char
   5522     ellipse[MagickPathExtent];
   5523 
   5524   DrawInfo
   5525     *draw_info;
   5526 
   5527   Image
   5528     *canvas_image,
   5529     *blur_image,
   5530     *oval_image,
   5531     *vignette_image;
   5532 
   5533   assert(image != (Image *) NULL);
   5534   assert(image->signature == MagickCoreSignature);
   5535   if (image->debug != MagickFalse)
   5536     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   5537   assert(exception != (ExceptionInfo *) NULL);
   5538   assert(exception->signature == MagickCoreSignature);
   5539   canvas_image=CloneImage(image,0,0,MagickTrue,exception);
   5540   if (canvas_image == (Image *) NULL)
   5541     return((Image *) NULL);
   5542   if (SetImageStorageClass(canvas_image,DirectClass,exception) == MagickFalse)
   5543     {
   5544       canvas_image=DestroyImage(canvas_image);
   5545       return((Image *) NULL);
   5546     }
   5547   canvas_image->alpha_trait=BlendPixelTrait;
   5548   oval_image=CloneImage(canvas_image,canvas_image->columns,canvas_image->rows,
   5549     MagickTrue,exception);
   5550   if (oval_image == (Image *) NULL)
   5551     {
   5552       canvas_image=DestroyImage(canvas_image);
   5553       return((Image *) NULL);
   5554     }
   5555   (void) QueryColorCompliance("#000000",AllCompliance,
   5556     &oval_image->background_color,exception);
   5557   (void) SetImageBackgroundColor(oval_image,exception);
   5558   draw_info=CloneDrawInfo((const ImageInfo *) NULL,(const DrawInfo *) NULL);
   5559   (void) QueryColorCompliance("#ffffff",AllCompliance,&draw_info->fill,
   5560     exception);
   5561   (void) QueryColorCompliance("#ffffff",AllCompliance,&draw_info->stroke,
   5562     exception);
   5563   (void) FormatLocaleString(ellipse,MagickPathExtent,"ellipse %g,%g,%g,%g,"
   5564     "0.0,360.0",image->columns/2.0,image->rows/2.0,image->columns/2.0-x,
   5565     image->rows/2.0-y);
   5566   draw_info->primitive=AcquireString(ellipse);
   5567   (void) DrawImage(oval_image,draw_info,exception);
   5568   draw_info=DestroyDrawInfo(draw_info);
   5569   blur_image=BlurImage(oval_image,radius,sigma,exception);
   5570   oval_image=DestroyImage(oval_image);
   5571   if (blur_image == (Image *) NULL)
   5572     {
   5573       canvas_image=DestroyImage(canvas_image);
   5574       return((Image *) NULL);
   5575     }
   5576   blur_image->alpha_trait=UndefinedPixelTrait;
   5577   (void) CompositeImage(canvas_image,blur_image,IntensityCompositeOp,MagickTrue,
   5578     0,0,exception);
   5579   blur_image=DestroyImage(blur_image);
   5580   vignette_image=MergeImageLayers(canvas_image,FlattenLayer,exception);
   5581   canvas_image=DestroyImage(canvas_image);
   5582   if (vignette_image != (Image *) NULL)
   5583     (void) TransformImageColorspace(vignette_image,image->colorspace,exception);
   5584   return(vignette_image);
   5585 }
   5586 
   5587 
   5588 /*
   5589 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   5590 %                                                                             %
   5591 %                                                                             %
   5592 %                                                                             %
   5593 %     W a v e I m a g e                                                       %
   5594 %                                                                             %
   5595 %                                                                             %
   5596 %                                                                             %
   5597 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   5598 %
   5599 %  WaveImage() creates a "ripple" effect in the image by shifting the pixels
   5600 %  vertically along a sine wave whose amplitude and wavelength is specified
   5601 %  by the given parameters.
   5602 %
   5603 %  The format of the WaveImage method is:
   5604 %
   5605 %      Image *WaveImage(const Image *image,const double amplitude,
   5606 %        const double wave_length,const PixelInterpolateMethod method,
   5607 %        ExceptionInfo *exception)
   5608 %
   5609 %  A description of each parameter follows:
   5610 %
   5611 %    o image: the image.
   5612 %
   5613 %    o amplitude, wave_length:  Define the amplitude and wave length of the
   5614 %      sine wave.
   5615 %
   5616 %    o interpolate: the pixel interpolation method.
   5617 %
   5618 %    o exception: return any errors or warnings in this structure.
   5619 %
   5620 */
   5621 MagickExport Image *WaveImage(const Image *image,const double amplitude,
   5622   const double wave_length,const PixelInterpolateMethod method,
   5623   ExceptionInfo *exception)
   5624 {
   5625 #define WaveImageTag  "Wave/Image"
   5626 
   5627   CacheView
   5628     *image_view,
   5629     *wave_view;
   5630 
   5631   Image
   5632     *wave_image;
   5633 
   5634   MagickBooleanType
   5635     status;
   5636 
   5637   MagickOffsetType
   5638     progress;
   5639 
   5640   double
   5641     *sine_map;
   5642 
   5643   register ssize_t
   5644     i;
   5645 
   5646   ssize_t
   5647     y;
   5648 
   5649   /*
   5650     Initialize wave image attributes.
   5651   */
   5652   assert(image != (Image *) NULL);
   5653   assert(image->signature == MagickCoreSignature);
   5654   if (image->debug != MagickFalse)
   5655     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   5656   assert(exception != (ExceptionInfo *) NULL);
   5657   assert(exception->signature == MagickCoreSignature);
   5658   wave_image=CloneImage(image,image->columns,(size_t) (image->rows+2.0*
   5659     fabs(amplitude)),MagickTrue,exception);
   5660   if (wave_image == (Image *) NULL)
   5661     return((Image *) NULL);
   5662   if (SetImageStorageClass(wave_image,DirectClass,exception) == MagickFalse)
   5663     {
   5664       wave_image=DestroyImage(wave_image);
   5665       return((Image *) NULL);
   5666     }
   5667   if (wave_image->background_color.alpha != OpaqueAlpha)
   5668     wave_image->alpha_trait=BlendPixelTrait;
   5669   /*
   5670     Allocate sine map.
   5671   */
   5672   sine_map=(double *) AcquireQuantumMemory((size_t) wave_image->columns,
   5673     sizeof(*sine_map));
   5674   if (sine_map == (double *) NULL)
   5675     {
   5676       wave_image=DestroyImage(wave_image);
   5677       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
   5678     }
   5679   for (i=0; i < (ssize_t) wave_image->columns; i++)
   5680     sine_map[i]=fabs(amplitude)+amplitude*sin((double) ((2.0*MagickPI*i)/
   5681       wave_length));
   5682   /*
   5683     Wave image.
   5684   */
   5685   status=MagickTrue;
   5686   progress=0;
   5687   image_view=AcquireVirtualCacheView(image,exception);
   5688   wave_view=AcquireAuthenticCacheView(wave_image,exception);
   5689   (void) SetCacheViewVirtualPixelMethod(image_view,
   5690     BackgroundVirtualPixelMethod);
   5691 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   5692   #pragma omp parallel for schedule(static,4) shared(progress,status) \
   5693     magick_threads(image,wave_image,wave_image->rows,1)
   5694 #endif
   5695   for (y=0; y < (ssize_t) wave_image->rows; y++)
   5696   {
   5697     register Quantum
   5698       *magick_restrict q;
   5699 
   5700     register ssize_t
   5701       x;
   5702 
   5703     if (status == MagickFalse)
   5704       continue;
   5705     q=QueueCacheViewAuthenticPixels(wave_view,0,y,wave_image->columns,1,
   5706       exception);
   5707     if (q == (Quantum *) NULL)
   5708       {
   5709         status=MagickFalse;
   5710         continue;
   5711       }
   5712     for (x=0; x < (ssize_t) wave_image->columns; x++)
   5713     {
   5714       status=InterpolatePixelChannels(image,image_view,wave_image,method,
   5715         (double) x,(double) (y-sine_map[x]),q,exception);
   5716       q+=GetPixelChannels(wave_image);
   5717     }
   5718     if (SyncCacheViewAuthenticPixels(wave_view,exception) == MagickFalse)
   5719       status=MagickFalse;
   5720     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   5721       {
   5722         MagickBooleanType
   5723           proceed;
   5724 
   5725 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   5726         #pragma omp critical (MagickCore_WaveImage)
   5727 #endif
   5728         proceed=SetImageProgress(image,WaveImageTag,progress++,image->rows);
   5729         if (proceed == MagickFalse)
   5730           status=MagickFalse;
   5731       }
   5732   }
   5733   wave_view=DestroyCacheView(wave_view);
   5734   image_view=DestroyCacheView(image_view);
   5735   sine_map=(double *) RelinquishMagickMemory(sine_map);
   5736   if (status == MagickFalse)
   5737     wave_image=DestroyImage(wave_image);
   5738   return(wave_image);
   5739 }
   5740 
   5741 /*
   5743 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   5744 %                                                                             %
   5745 %                                                                             %
   5746 %                                                                             %
   5747 %     W a v e l e t D e n o i s e I m a g e                                   %
   5748 %                                                                             %
   5749 %                                                                             %
   5750 %                                                                             %
   5751 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   5752 %
   5753 %  WaveletDenoiseImage() removes noise from the image using a wavelet
   5754 %  transform.  The wavelet transform is a fast hierarchical scheme for
   5755 %  processing an image using a set of consecutive lowpass and high_pass filters,
   5756 %  followed by a decimation.  This results in a decomposition into different
   5757 %  scales which can be regarded as different frequency bands, determined by
   5758 %  the mother wavelet.  Adapted from dcraw.c by David Coffin.
   5759 %
   5760 %  The format of the WaveletDenoiseImage method is:
   5761 %
   5762 %      Image *WaveletDenoiseImage(const Image *image,const double threshold,
   5763 %        const double softness,ExceptionInfo *exception)
   5764 %
   5765 %  A description of each parameter follows:
   5766 %
   5767 %    o image: the image.
   5768 %
   5769 %    o threshold: set the threshold for smoothing.
   5770 %
   5771 %    o softness: attenuate the smoothing threshold.
   5772 %
   5773 %    o exception: return any errors or warnings in this structure.
   5774 %
   5775 */
   5776 
   5777 static inline void HatTransform(const float *magick_restrict pixels,
   5778   const size_t stride,const size_t extent,const size_t scale,float *kernel)
   5779 {
   5780   const float
   5781     *magick_restrict p,
   5782     *magick_restrict q,
   5783     *magick_restrict r;
   5784 
   5785   register ssize_t
   5786     i;
   5787 
   5788   p=pixels;
   5789   q=pixels+scale*stride;
   5790   r=pixels+scale*stride;
   5791   for (i=0; i < (ssize_t) scale; i++)
   5792   {
   5793     kernel[i]=0.25f*(*p+(*p)+(*q)+(*r));
   5794     p+=stride;
   5795     q-=stride;
   5796     r+=stride;
   5797   }
   5798   for ( ; i < (ssize_t) (extent-scale); i++)
   5799   {
   5800     kernel[i]=0.25f*(2.0f*(*p)+*(p-scale*stride)+*(p+scale*stride));
   5801     p+=stride;
   5802   }
   5803   q=p-scale*stride;
   5804   r=pixels+stride*(extent-2);
   5805   for ( ; i < (ssize_t) extent; i++)
   5806   {
   5807     kernel[i]=0.25f*(*p+(*p)+(*q)+(*r));
   5808     p+=stride;
   5809     q+=stride;
   5810     r-=stride;
   5811   }
   5812 }
   5813 
   5814 MagickExport Image *WaveletDenoiseImage(const Image *image,
   5815   const double threshold,const double softness,ExceptionInfo *exception)
   5816 {
   5817   CacheView
   5818     *image_view,
   5819     *noise_view;
   5820 
   5821   float
   5822     *kernel,
   5823     *pixels;
   5824 
   5825   Image
   5826     *noise_image;
   5827 
   5828   MagickBooleanType
   5829     status;
   5830 
   5831   MagickSizeType
   5832     number_pixels;
   5833 
   5834   MemoryInfo
   5835     *pixels_info;
   5836 
   5837   ssize_t
   5838     channel;
   5839 
   5840   static const float
   5841     noise_levels[] = { 0.8002f, 0.2735f, 0.1202f, 0.0585f, 0.0291f, 0.0152f,
   5842       0.0080f, 0.0044f };
   5843 
   5844   /*
   5845     Initialize noise image attributes.
   5846   */
   5847   assert(image != (const Image *) NULL);
   5848   assert(image->signature == MagickCoreSignature);
   5849   if (image->debug != MagickFalse)
   5850     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   5851   assert(exception != (ExceptionInfo *) NULL);
   5852   assert(exception->signature == MagickCoreSignature);
   5853 #if defined(MAGICKCORE_OPENCL_SUPPORT)
   5854   noise_image=AccelerateWaveletDenoiseImage(image,threshold,exception);
   5855   if (noise_image != (Image *) NULL)
   5856     return(noise_image);
   5857 #endif
   5858   noise_image=CloneImage(image,0,0,MagickTrue,exception);
   5859   if (noise_image == (Image *) NULL)
   5860     return((Image *) NULL);
   5861   if (SetImageStorageClass(noise_image,DirectClass,exception) == MagickFalse)
   5862     {
   5863       noise_image=DestroyImage(noise_image);
   5864       return((Image *) NULL);
   5865     }
   5866   if (AcquireMagickResource(WidthResource,4*image->columns) == MagickFalse)
   5867     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
   5868   pixels_info=AcquireVirtualMemory(3*image->columns,image->rows*
   5869     sizeof(*pixels));
   5870   kernel=(float *) AcquireQuantumMemory(MagickMax(image->rows,image->columns),
   5871     GetOpenMPMaximumThreads()*sizeof(*kernel));
   5872   if ((pixels_info == (MemoryInfo *) NULL) || (kernel == (float *) NULL))
   5873     {
   5874       if (kernel != (float *) NULL)
   5875         kernel=(float *) RelinquishMagickMemory(kernel);
   5876       if (pixels_info != (MemoryInfo *) NULL)
   5877         pixels_info=RelinquishVirtualMemory(pixels_info);
   5878       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
   5879     }
   5880   pixels=(float *) GetVirtualMemoryBlob(pixels_info);
   5881   status=MagickTrue;
   5882   number_pixels=(MagickSizeType) image->columns*image->rows;
   5883   image_view=AcquireAuthenticCacheView(image,exception);
   5884   noise_view=AcquireAuthenticCacheView(noise_image,exception);
   5885   for (channel=0; channel < (ssize_t) GetPixelChannels(image); channel++)
   5886   {
   5887     register ssize_t
   5888       i;
   5889 
   5890     size_t
   5891       high_pass,
   5892       low_pass;
   5893 
   5894     ssize_t
   5895       level,
   5896       y;
   5897 
   5898     PixelChannel
   5899       pixel_channel;
   5900 
   5901     PixelTrait
   5902       traits;
   5903 
   5904     if (status == MagickFalse)
   5905       continue;
   5906     traits=GetPixelChannelTraits(image,(PixelChannel) channel);
   5907     if (traits == UndefinedPixelTrait)
   5908       continue;
   5909     pixel_channel=GetPixelChannelChannel(image,channel);
   5910     if ((pixel_channel != RedPixelChannel) &&
   5911         (pixel_channel != GreenPixelChannel) &&
   5912         (pixel_channel != BluePixelChannel))
   5913       continue;
   5914     /*
   5915       Copy channel from image to wavelet pixel array.
   5916     */
   5917     i=0;
   5918     for (y=0; y < (ssize_t) image->rows; y++)
   5919     {
   5920       register const Quantum
   5921         *magick_restrict p;
   5922 
   5923       ssize_t
   5924         x;
   5925 
   5926       p=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
   5927       if (p == (const Quantum *) NULL)
   5928         {
   5929           status=MagickFalse;
   5930           break;
   5931         }
   5932       for (x=0; x < (ssize_t) image->columns; x++)
   5933       {
   5934         pixels[i++]=(float) p[channel];
   5935         p+=GetPixelChannels(image);
   5936       }
   5937     }
   5938     /*
   5939       Low pass filter outputs are called approximation kernel & high pass
   5940       filters are referred to as detail kernel. The detail kernel
   5941       have high values in the noisy parts of the signal.
   5942     */
   5943     high_pass=0;
   5944     for (level=0; level < 5; level++)
   5945     {
   5946       double
   5947         magnitude;
   5948 
   5949       ssize_t
   5950         x,
   5951         y;
   5952 
   5953       low_pass=(size_t) (number_pixels*((level & 0x01)+1));
   5954 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   5955       #pragma omp parallel for schedule(static,1) \
   5956         magick_threads(image,image,image->rows,1)
   5957 #endif
   5958       for (y=0; y < (ssize_t) image->rows; y++)
   5959       {
   5960         const int
   5961           id = GetOpenMPThreadId();
   5962 
   5963         register float
   5964           *magick_restrict p,
   5965           *magick_restrict q;
   5966 
   5967         register ssize_t
   5968           x;
   5969 
   5970         p=kernel+id*image->columns;
   5971         q=pixels+y*image->columns;
   5972         HatTransform(q+high_pass,1,image->columns,(size_t) (1 << level),p);
   5973         q+=low_pass;
   5974         for (x=0; x < (ssize_t) image->columns; x++)
   5975           *q++=(*p++);
   5976       }
   5977 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   5978       #pragma omp parallel for schedule(static,1) \
   5979         magick_threads(image,image,image->columns,1)
   5980 #endif
   5981       for (x=0; x < (ssize_t) image->columns; x++)
   5982       {
   5983         const int
   5984           id = GetOpenMPThreadId();
   5985 
   5986         register float
   5987           *magick_restrict p,
   5988           *magick_restrict q;
   5989 
   5990         register ssize_t
   5991           y;
   5992 
   5993         p=kernel+id*image->rows;
   5994         q=pixels+x+low_pass;
   5995         HatTransform(q,image->columns,image->rows,(size_t) (1 << level),p);
   5996         for (y=0; y < (ssize_t) image->rows; y++)
   5997         {
   5998           *q=(*p++);
   5999           q+=image->columns;
   6000         }
   6001       }
   6002       /*
   6003         To threshold, each coefficient is compared to a threshold value and
   6004         attenuated / shrunk by some factor.
   6005       */
   6006       magnitude=threshold*noise_levels[level];
   6007       for (i=0; i < (ssize_t) number_pixels; ++i)
   6008       {
   6009         pixels[high_pass+i]-=pixels[low_pass+i];
   6010         if (pixels[high_pass+i] < -magnitude)
   6011           pixels[high_pass+i]+=magnitude-softness*magnitude;
   6012         else
   6013           if (pixels[high_pass+i] > magnitude)
   6014             pixels[high_pass+i]-=magnitude-softness*magnitude;
   6015           else
   6016             pixels[high_pass+i]*=softness;
   6017         if (high_pass != 0)
   6018           pixels[i]+=pixels[high_pass+i];
   6019       }
   6020       high_pass=low_pass;
   6021     }
   6022     /*
   6023       Reconstruct image from the thresholded wavelet kernel.
   6024     */
   6025     i=0;
   6026     for (y=0; y < (ssize_t) image->rows; y++)
   6027     {
   6028       MagickBooleanType
   6029         sync;
   6030 
   6031       register Quantum
   6032         *magick_restrict q;
   6033 
   6034       register ssize_t
   6035         x;
   6036 
   6037       ssize_t
   6038         offset;
   6039 
   6040       q=GetCacheViewAuthenticPixels(noise_view,0,y,noise_image->columns,1,
   6041         exception);
   6042       if (q == (Quantum *) NULL)
   6043         {
   6044           status=MagickFalse;
   6045           break;
   6046         }
   6047       offset=GetPixelChannelOffset(noise_image,pixel_channel);
   6048       for (x=0; x < (ssize_t) image->columns; x++)
   6049       {
   6050         MagickRealType
   6051           pixel;
   6052 
   6053         pixel=(MagickRealType) pixels[i]+pixels[low_pass+i];
   6054         q[offset]=ClampToQuantum(pixel);
   6055         i++;
   6056         q+=GetPixelChannels(noise_image);
   6057       }
   6058       sync=SyncCacheViewAuthenticPixels(noise_view,exception);
   6059       if (sync == MagickFalse)
   6060         status=MagickFalse;
   6061     }
   6062     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   6063       {
   6064         MagickBooleanType
   6065           proceed;
   6066 
   6067         proceed=SetImageProgress(image,AddNoiseImageTag,(MagickOffsetType)
   6068           channel,GetPixelChannels(image));
   6069         if (proceed == MagickFalse)
   6070           status=MagickFalse;
   6071       }
   6072   }
   6073   noise_view=DestroyCacheView(noise_view);
   6074   image_view=DestroyCacheView(image_view);
   6075   kernel=(float *) RelinquishMagickMemory(kernel);
   6076   pixels_info=RelinquishVirtualMemory(pixels_info);
   6077   if (status == MagickFalse)
   6078     noise_image=DestroyImage(noise_image);
   6079   return(noise_image);
   6080 }
   6081