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