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