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