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