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