]> granicus.if.org Git - imagemagick/blob - magick/fx.c
another small bug fix
[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-2010 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         default:
1522           break;
1523       }
1524       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1525         "UnableToParseExpression","`%s'",p);
1526       return(0.0);
1527     }
1528   switch (*symbol)
1529   {
1530     case 'A':
1531     case 'a':
1532     {
1533       if (LocaleCompare(symbol,"a") == 0)
1534         return((MagickRealType) (QuantumScale*GetAlphaPixelComponent(&pixel)));
1535       break;
1536     }
1537     case 'B':
1538     case 'b':
1539     {
1540       if (LocaleCompare(symbol,"b") == 0)
1541         return(QuantumScale*pixel.blue);
1542       break;
1543     }
1544     case 'C':
1545     case 'c':
1546     {
1547       if (LocaleNCompare(symbol,"channel",7) == 0)
1548         {
1549           GeometryInfo
1550             channel_info;
1551
1552           MagickStatusType
1553             flags;
1554
1555           flags=ParseGeometry(symbol+7,&channel_info);
1556           if (image->colorspace == CMYKColorspace)
1557             switch (channel)
1558             {
1559               case CyanChannel:
1560               {
1561                 if ((flags & RhoValue) == 0)
1562                   return(0.0);
1563                 return(channel_info.rho);
1564               }
1565               case MagentaChannel:
1566               {
1567                 if ((flags & SigmaValue) == 0)
1568                   return(0.0);
1569                 return(channel_info.sigma);
1570               }
1571               case YellowChannel:
1572               {
1573                 if ((flags & XiValue) == 0)
1574                   return(0.0);
1575                 return(channel_info.xi);
1576               }
1577               case BlackChannel:
1578               {
1579                 if ((flags & PsiValue) == 0)
1580                   return(0.0);
1581                 return(channel_info.psi);
1582               }
1583               case OpacityChannel:
1584               {
1585                 if ((flags & ChiValue) == 0)
1586                   return(0.0);
1587                 return(channel_info.chi);
1588               }
1589               default:
1590                 return(0.0);
1591             }
1592           switch (channel)
1593           {
1594             case RedChannel:
1595             {
1596               if ((flags & RhoValue) == 0)
1597                 return(0.0);
1598               return(channel_info.rho);
1599             }
1600             case GreenChannel:
1601             {
1602               if ((flags & SigmaValue) == 0)
1603                 return(0.0);
1604               return(channel_info.sigma);
1605             }
1606             case BlueChannel:
1607             {
1608               if ((flags & XiValue) == 0)
1609                 return(0.0);
1610               return(channel_info.xi);
1611             }
1612             case OpacityChannel:
1613             {
1614               if ((flags & PsiValue) == 0)
1615                 return(0.0);
1616               return(channel_info.psi);
1617             }
1618             case IndexChannel:
1619             {
1620               if ((flags & ChiValue) == 0)
1621                 return(0.0);
1622               return(channel_info.chi);
1623             }
1624             default:
1625               return(0.0);
1626           }
1627           return(0.0);
1628         }
1629       if (LocaleCompare(symbol,"c") == 0)
1630         return(QuantumScale*pixel.red);
1631       break;
1632     }
1633     case 'D':
1634     case 'd':
1635     {
1636       if (LocaleNCompare(symbol,"depth",5) == 0)
1637         return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1638       break;
1639     }
1640     case 'G':
1641     case 'g':
1642     {
1643       if (LocaleCompare(symbol,"g") == 0)
1644         return(QuantumScale*pixel.green);
1645       break;
1646     }
1647     case 'K':
1648     case 'k':
1649     {
1650       if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1651         return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1652       if (LocaleCompare(symbol,"k") == 0)
1653         {
1654           if (image->colorspace != CMYKColorspace)
1655             {
1656               (void) ThrowMagickException(exception,GetMagickModule(),
1657                 OptionError,"ColorSeparatedImageRequired","`%s'",
1658                 image->filename);
1659               return(0.0);
1660             }
1661           return(QuantumScale*pixel.index);
1662         }
1663       break;
1664     }
1665     case 'H':
1666     case 'h':
1667     {
1668       if (LocaleCompare(symbol,"h") == 0)
1669         return((MagickRealType) image->rows);
1670       if (LocaleCompare(symbol,"hue") == 0)
1671         {
1672           double
1673             hue,
1674             lightness,
1675             saturation;
1676
1677           ConvertRGBToHSL(ClampToQuantum(pixel.red),ClampToQuantum(pixel.green),
1678             ClampToQuantum(pixel.blue),&hue,&saturation,&lightness);
1679           return(hue);
1680         }
1681       break;
1682     }
1683     case 'I':
1684     case 'i':
1685     {
1686       if ((LocaleCompare(symbol,"image.depth") == 0) ||
1687           (LocaleCompare(symbol,"image.minima") == 0) ||
1688           (LocaleCompare(symbol,"image.maxima") == 0) ||
1689           (LocaleCompare(symbol,"image.mean") == 0) ||
1690           (LocaleCompare(symbol,"image.kurtosis") == 0) ||
1691           (LocaleCompare(symbol,"image.skewness") == 0) ||
1692           (LocaleCompare(symbol,"image.standard_deviation") == 0))
1693         return(FxChannelStatistics(fx_info,image,channel,symbol+6,exception));
1694       if (LocaleCompare(symbol,"image.resolution.x") == 0)
1695         return(image->x_resolution);
1696       if (LocaleCompare(symbol,"image.resolution.y") == 0)
1697         return(image->y_resolution);
1698       if (LocaleCompare(symbol,"intensity") == 0)
1699         return(QuantumScale*MagickPixelIntensityToQuantum(&pixel));
1700       if (LocaleCompare(symbol,"i") == 0)
1701         return((MagickRealType) x);
1702       break;
1703     }
1704     case 'J':
1705     case 'j':
1706     {
1707       if (LocaleCompare(symbol,"j") == 0)
1708         return((MagickRealType) y);
1709       break;
1710     }
1711     case 'L':
1712     case 'l':
1713     {
1714       if (LocaleCompare(symbol,"lightness") == 0)
1715         {
1716           double
1717             hue,
1718             lightness,
1719             saturation;
1720
1721           ConvertRGBToHSL(ClampToQuantum(pixel.red),ClampToQuantum(pixel.green),
1722             ClampToQuantum(pixel.blue),&hue,&saturation,&lightness);
1723           return(lightness);
1724         }
1725       if (LocaleCompare(symbol,"luminance") == 0)
1726         {
1727           double
1728             luminence;
1729
1730           luminence=0.2126*pixel.red+0.7152*pixel.green+0.0722*pixel.blue;
1731           return(QuantumScale*luminence);
1732         }
1733       break;
1734     }
1735     case 'M':
1736     case 'm':
1737     {
1738       if (LocaleNCompare(symbol,"maxima",6) == 0)
1739         return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1740       if (LocaleNCompare(symbol,"mean",4) == 0)
1741         return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1742       if (LocaleNCompare(symbol,"minima",6) == 0)
1743         return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1744       if (LocaleCompare(symbol,"m") == 0)
1745         return(QuantumScale*pixel.blue);
1746       break;
1747     }
1748     case 'N':
1749     case 'n':
1750     {
1751       if (LocaleCompare(symbol,"n") == 0)
1752         return((MagickRealType) GetImageListLength(fx_info->images));
1753       break;
1754     }
1755     case 'O':
1756     case 'o':
1757     {
1758       if (LocaleCompare(symbol,"o") == 0)
1759         return(QuantumScale*pixel.opacity);
1760       break;
1761     }
1762     case 'P':
1763     case 'p':
1764     {
1765       if (LocaleCompare(symbol,"page.height") == 0)
1766         return((MagickRealType) image->page.height);
1767       if (LocaleCompare(symbol,"page.width") == 0)
1768         return((MagickRealType) image->page.width);
1769       if (LocaleCompare(symbol,"page.x") == 0)
1770         return((MagickRealType) image->page.x);
1771       if (LocaleCompare(symbol,"page.y") == 0)
1772         return((MagickRealType) image->page.y);
1773       break;
1774     }
1775     case 'R':
1776     case 'r':
1777     {
1778       if (LocaleCompare(symbol,"resolution.x") == 0)
1779         return(image->x_resolution);
1780       if (LocaleCompare(symbol,"resolution.y") == 0)
1781         return(image->y_resolution);
1782       if (LocaleCompare(symbol,"r") == 0)
1783         return(QuantumScale*pixel.red);
1784       break;
1785     }
1786     case 'S':
1787     case 's':
1788     {
1789       if (LocaleCompare(symbol,"saturation") == 0)
1790         {
1791           double
1792             hue,
1793             lightness,
1794             saturation;
1795
1796           ConvertRGBToHSL(ClampToQuantum(pixel.red),ClampToQuantum(pixel.green),
1797             ClampToQuantum(pixel.blue),&hue,&saturation,&lightness);
1798           return(saturation);
1799         }
1800       if (LocaleNCompare(symbol,"skewness",8) == 0)
1801         return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1802       if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1803         return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1804       break;
1805     }
1806     case 'T':
1807     case 't':
1808     {
1809       if (LocaleCompare(symbol,"t") == 0)
1810         return((MagickRealType) fx_info->images->scene);
1811       break;
1812     }
1813     case 'W':
1814     case 'w':
1815     {
1816       if (LocaleCompare(symbol,"w") == 0)
1817         return((MagickRealType) image->columns);
1818       break;
1819     }
1820     case 'Y':
1821     case 'y':
1822     {
1823       if (LocaleCompare(symbol,"y") == 0)
1824         return(QuantumScale*pixel.green);
1825       break;
1826     }
1827     case 'Z':
1828     case 'z':
1829     {
1830       if (LocaleCompare(symbol,"z") == 0)
1831         {
1832           MagickRealType
1833             depth;
1834
1835           depth=(MagickRealType) GetImageChannelDepth(image,channel,
1836             fx_info->exception);
1837           return(depth);
1838         }
1839       break;
1840     }
1841     default:
1842       break;
1843   }
1844   value=(const char *) GetValueFromSplayTree(fx_info->symbols,symbol);
1845   if (value != (const char *) NULL)
1846     return((MagickRealType) StringToDouble(value));
1847   (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1848     "UnableToParseExpression","`%s'",symbol);
1849   return(0.0);
1850 }
1851
1852 static const char *FxOperatorPrecedence(const char *expression,
1853   ExceptionInfo *exception)
1854 {
1855   typedef enum
1856   {
1857     UndefinedPrecedence,
1858     NullPrecedence,
1859     BitwiseComplementPrecedence,
1860     ExponentPrecedence,
1861     ExponentialNotationPrecedence,
1862     MultiplyPrecedence,
1863     AdditionPrecedence,
1864     ShiftPrecedence,
1865     RelationalPrecedence,
1866     EquivalencyPrecedence,
1867     BitwiseAndPrecedence,
1868     BitwiseOrPrecedence,
1869     LogicalAndPrecedence,
1870     LogicalOrPrecedence,
1871     TernaryPrecedence,
1872     AssignmentPrecedence,
1873     CommaPrecedence,
1874     SeparatorPrecedence
1875   } FxPrecedence;
1876
1877   FxPrecedence
1878     precedence,
1879     target;
1880
1881   register const char
1882     *subexpression;
1883
1884   register int
1885     c;
1886
1887   size_t
1888     level;
1889
1890   c=0;
1891   level=0;
1892   subexpression=(const char *) NULL;
1893   target=NullPrecedence;
1894   while (*expression != '\0')
1895   {
1896     precedence=UndefinedPrecedence;
1897     if ((isspace((int) ((char) *expression)) != 0) || (c == (int) '@'))
1898       {
1899         expression++;
1900         continue;
1901       }
1902     switch (*expression)
1903     {
1904       case 'A':
1905       case 'a':
1906       {
1907         if (LocaleNCompare(expression,"atan2",5) == 0)
1908           {
1909             expression+=5;
1910             break;
1911           }
1912         break;
1913       }
1914       case 'J':
1915       case 'j':
1916       {
1917         if ((LocaleNCompare(expression,"j0",2) == 0) ||
1918             (LocaleNCompare(expression,"j1",2) == 0))
1919           {
1920             expression+=2;
1921             break;
1922           }
1923         break;
1924       }
1925       case '#':
1926       {
1927         while (isxdigit((int) ((unsigned char) *(expression+1))) != 0)
1928           expression++;
1929         break;
1930       }
1931       default:
1932         break;
1933     }
1934     if ((c == (int) '{') || (c == (int) '['))
1935       level++;
1936     else
1937       if ((c == (int) '}') || (c == (int) ']'))
1938         level--;
1939     if (level == 0)
1940       switch ((unsigned char) *expression)
1941       {
1942         case '~':
1943         case '!':
1944         {
1945           precedence=BitwiseComplementPrecedence;
1946           break;
1947         }
1948         case '^':
1949         case '@':
1950         {
1951           precedence=ExponentPrecedence;
1952           break;
1953         }
1954         default:
1955         {
1956           if (((c != 0) && ((isdigit((int) ((char) c)) != 0) ||
1957                (strchr(")",c) != (char *) NULL))) &&
1958               (((islower((int) ((char) *expression)) != 0) ||
1959                (strchr("(",(int) *expression) != (char *) NULL)) ||
1960                ((isdigit((int) ((char) c)) == 0) &&
1961                 (isdigit((int) ((char) *expression)) != 0))) &&
1962               (strchr("xy",(int) *expression) == (char *) NULL))
1963             precedence=MultiplyPrecedence;
1964           break;
1965         }
1966         case '*':
1967         case '/':
1968         case '%':
1969         {
1970           precedence=MultiplyPrecedence;
1971           break;
1972         }
1973         case '+':
1974         case '-':
1975         {
1976           if ((strchr("(+-/*%:&^|<>~,",c) == (char *) NULL) ||
1977               (isalpha(c) != 0))
1978             precedence=AdditionPrecedence;
1979           break;
1980         }
1981         case LeftShiftOperator:
1982         case RightShiftOperator:
1983         {
1984           precedence=ShiftPrecedence;
1985           break;
1986         }
1987         case '<':
1988         case LessThanEqualOperator:
1989         case GreaterThanEqualOperator:
1990         case '>':
1991         {
1992           precedence=RelationalPrecedence;
1993           break;
1994         }
1995         case EqualOperator:
1996         case NotEqualOperator:
1997         {
1998           precedence=EquivalencyPrecedence;
1999           break;
2000         }
2001         case '&':
2002         {
2003           precedence=BitwiseAndPrecedence;
2004           break;
2005         }
2006         case '|':
2007         {
2008           precedence=BitwiseOrPrecedence;
2009           break;
2010         }
2011         case LogicalAndOperator:
2012         {
2013           precedence=LogicalAndPrecedence;
2014           break;
2015         }
2016         case LogicalOrOperator:
2017         {
2018           precedence=LogicalOrPrecedence;
2019           break;
2020         }
2021         case ExponentialNotation:
2022         {
2023           precedence=ExponentialNotationPrecedence;
2024           break;
2025         }
2026         case ':':
2027         case '?':
2028         {
2029           precedence=TernaryPrecedence;
2030           break;
2031         }
2032         case '=':
2033         {
2034           precedence=AssignmentPrecedence;
2035           break;
2036         }
2037         case ',':
2038         {
2039           precedence=CommaPrecedence;
2040           break;
2041         }
2042         case ';':
2043         {
2044           precedence=SeparatorPrecedence;
2045           break;
2046         }
2047       }
2048     if ((precedence == BitwiseComplementPrecedence) ||
2049         (precedence == TernaryPrecedence) ||
2050         (precedence == AssignmentPrecedence))
2051       {
2052         if (precedence > target)
2053           {
2054             /*
2055               Right-to-left associativity.
2056             */
2057             target=precedence;
2058             subexpression=expression;
2059           }
2060       }
2061     else
2062       if (precedence >= target)
2063         {
2064           /*
2065             Left-to-right associativity.
2066           */
2067           target=precedence;
2068           subexpression=expression;
2069         }
2070     if (strchr("(",(int) *expression) != (char *) NULL)
2071       expression=FxSubexpression(expression,exception);
2072     c=(int) (*expression++);
2073   }
2074   return(subexpression);
2075 }
2076
2077 static MagickRealType FxEvaluateSubexpression(FxInfo *fx_info,
2078   const ChannelType channel,const ssize_t x,const ssize_t y,
2079   const char *expression,MagickRealType *beta,ExceptionInfo *exception)
2080 {
2081   char
2082     *q,
2083     subexpression[MaxTextExtent];
2084
2085   MagickRealType
2086     alpha,
2087     gamma;
2088
2089   register const char
2090     *p;
2091
2092   *beta=0.0;
2093   if (exception->severity != UndefinedException)
2094     return(0.0);
2095   while (isspace((int) *expression) != 0)
2096     expression++;
2097   if (*expression == '\0')
2098     {
2099       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
2100         "MissingExpression","`%s'",expression);
2101       return(0.0);
2102     }
2103   *subexpression='\0';
2104   p=FxOperatorPrecedence(expression,exception);
2105   if (p != (const char *) NULL)
2106     {
2107       (void) CopyMagickString(subexpression,expression,(size_t)
2108         (p-expression+1));
2109       alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2110         exception);
2111       switch ((unsigned char) *p)
2112       {
2113         case '~':
2114         {
2115           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2116           *beta=(MagickRealType) (~(size_t) *beta);
2117           return(*beta);
2118         }
2119         case '!':
2120         {
2121           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2122           return(*beta == 0.0 ? 1.0 : 0.0);
2123         }
2124         case '^':
2125         {
2126           *beta=pow((double) alpha,(double) FxEvaluateSubexpression(fx_info,
2127             channel,x,y,++p,beta,exception));
2128           return(*beta);
2129         }
2130         case '*':
2131         case ExponentialNotation:
2132         {
2133           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2134           return(alpha*(*beta));
2135         }
2136         case '/':
2137         {
2138           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2139           if (*beta == 0.0)
2140             {
2141               if (exception->severity == UndefinedException)
2142                 (void) ThrowMagickException(exception,GetMagickModule(),
2143                   OptionError,"DivideByZero","`%s'",expression);
2144               return(0.0);
2145             }
2146           return(alpha/(*beta));
2147         }
2148         case '%':
2149         {
2150           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2151           *beta=fabs(floor(((double) *beta)+0.5));
2152           if (*beta == 0.0)
2153             {
2154               (void) ThrowMagickException(exception,GetMagickModule(),
2155                 OptionError,"DivideByZero","`%s'",expression);
2156               return(0.0);
2157             }
2158           return(fmod((double) alpha,(double) *beta));
2159         }
2160         case '+':
2161         {
2162           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2163           return(alpha+(*beta));
2164         }
2165         case '-':
2166         {
2167           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2168           return(alpha-(*beta));
2169         }
2170         case LeftShiftOperator:
2171         {
2172           gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2173           *beta=(MagickRealType) ((size_t) (alpha+0.5) << (size_t)
2174             (gamma+0.5));
2175           return(*beta);
2176         }
2177         case RightShiftOperator:
2178         {
2179           gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2180           *beta=(MagickRealType) ((size_t) (alpha+0.5) >> (size_t)
2181             (gamma+0.5));
2182           return(*beta);
2183         }
2184         case '<':
2185         {
2186           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2187           return(alpha < *beta ? 1.0 : 0.0);
2188         }
2189         case LessThanEqualOperator:
2190         {
2191           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2192           return(alpha <= *beta ? 1.0 : 0.0);
2193         }
2194         case '>':
2195         {
2196           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2197           return(alpha > *beta ? 1.0 : 0.0);
2198         }
2199         case GreaterThanEqualOperator:
2200         {
2201           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2202           return(alpha >= *beta ? 1.0 : 0.0);
2203         }
2204         case EqualOperator:
2205         {
2206           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2207           return(fabs(alpha-(*beta)) <= MagickEpsilon ? 1.0 : 0.0);
2208         }
2209         case NotEqualOperator:
2210         {
2211           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2212           return(fabs(alpha-(*beta)) > MagickEpsilon ? 1.0 : 0.0);
2213         }
2214         case '&':
2215         {
2216           gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2217           *beta=(MagickRealType) ((size_t) (alpha+0.5) & (size_t)
2218             (gamma+0.5));
2219           return(*beta);
2220         }
2221         case '|':
2222         {
2223           gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2224           *beta=(MagickRealType) ((size_t) (alpha+0.5) | (size_t)
2225             (gamma+0.5));
2226           return(*beta);
2227         }
2228         case LogicalAndOperator:
2229         {
2230           gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2231           *beta=(alpha > 0.0) && (gamma > 0.0) ? 1.0 : 0.0;
2232           return(*beta);
2233         }
2234         case LogicalOrOperator:
2235         {
2236           gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2237           *beta=(alpha > 0.0) || (gamma > 0.0) ? 1.0 : 0.0;
2238           return(*beta);
2239         }
2240         case '?':
2241         {
2242           MagickRealType
2243             gamma;
2244
2245           (void) CopyMagickString(subexpression,++p,MaxTextExtent);
2246           q=subexpression;
2247           p=StringToken(":",&q);
2248           if (q == (char *) NULL)
2249             {
2250               (void) ThrowMagickException(exception,GetMagickModule(),
2251                 OptionError,"UnableToParseExpression","`%s'",subexpression);
2252               return(0.0);
2253             }
2254           if (fabs((double) alpha) > MagickEpsilon)
2255             gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,exception);
2256           else
2257             gamma=FxEvaluateSubexpression(fx_info,channel,x,y,q,beta,exception);
2258           return(gamma);
2259         }
2260         case '=':
2261         {
2262           char
2263             numeric[MaxTextExtent];
2264
2265           q=subexpression;
2266           while (isalpha((int) ((unsigned char) *q)) != 0)
2267             q++;
2268           if (*q != '\0')
2269             {
2270               (void) ThrowMagickException(exception,GetMagickModule(),
2271                 OptionError,"UnableToParseExpression","`%s'",subexpression);
2272               return(0.0);
2273             }
2274           ClearMagickException(exception);
2275           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2276           (void) FormatMagickString(numeric,MaxTextExtent,"%g",(double)
2277             *beta);
2278           (void) DeleteNodeFromSplayTree(fx_info->symbols,subexpression);
2279           (void) AddValueToSplayTree(fx_info->symbols,ConstantString(
2280             subexpression),ConstantString(numeric));
2281           return(*beta);
2282         }
2283         case ',':
2284         {
2285           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2286           return(alpha);
2287         }
2288         case ';':
2289         {
2290           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2291           return(*beta);
2292         }
2293         default:
2294         {
2295           gamma=alpha*FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,
2296             exception);
2297           return(gamma);
2298         }
2299       }
2300     }
2301   if (strchr("(",(int) *expression) != (char *) NULL)
2302     {
2303       (void) CopyMagickString(subexpression,expression+1,MaxTextExtent);
2304       subexpression[strlen(subexpression)-1]='\0';
2305       gamma=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2306         exception);
2307       return(gamma);
2308     }
2309   switch (*expression)
2310   {
2311     case '+':
2312     {
2313       gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2314         exception);
2315       return(1.0*gamma);
2316     }
2317     case '-':
2318     {
2319       gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2320         exception);
2321       return(-1.0*gamma);
2322     }
2323     case '~':
2324     {
2325       gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2326         exception);
2327       return((MagickRealType) (~(size_t) (gamma+0.5)));
2328     }
2329     case 'A':
2330     case 'a':
2331     {
2332       if (LocaleNCompare(expression,"abs",3) == 0)
2333         {
2334           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2335             exception);
2336           return((MagickRealType) fabs((double) alpha));
2337         }
2338       if (LocaleNCompare(expression,"acos",4) == 0)
2339         {
2340           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2341             exception);
2342           return((MagickRealType) acos((double) alpha));
2343         }
2344 #if defined(MAGICKCORE_HAVE_J1)
2345       if (LocaleNCompare(expression,"airy",4) == 0)
2346         {
2347           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2348             exception);
2349           if (alpha == 0.0)
2350             return(1.0);
2351           gamma=2.0*j1((double) (MagickPI*alpha))/(MagickPI*alpha);
2352           return(gamma*gamma);
2353         }
2354 #endif
2355       if (LocaleNCompare(expression,"asin",4) == 0)
2356         {
2357           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2358             exception);
2359           return((MagickRealType) asin((double) alpha));
2360         }
2361       if (LocaleNCompare(expression,"alt",3) == 0)
2362         {
2363           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2364             exception);
2365           return(((ssize_t) alpha) & 0x01 ? -1.0 : 1.0);
2366         }
2367       if (LocaleNCompare(expression,"atan2",5) == 0)
2368         {
2369           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2370             exception);
2371           return((MagickRealType) atan2((double) alpha,(double) *beta));
2372         }
2373       if (LocaleNCompare(expression,"atan",4) == 0)
2374         {
2375           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2376             exception);
2377           return((MagickRealType) atan((double) alpha));
2378         }
2379       if (LocaleCompare(expression,"a") == 0)
2380         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2381       break;
2382     }
2383     case 'B':
2384     case 'b':
2385     {
2386       if (LocaleCompare(expression,"b") == 0)
2387         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2388       break;
2389     }
2390     case 'C':
2391     case 'c':
2392     {
2393       if (LocaleNCompare(expression,"ceil",4) == 0)
2394         {
2395           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2396             exception);
2397           return((MagickRealType) ceil((double) alpha));
2398         }
2399       if (LocaleNCompare(expression,"cosh",4) == 0)
2400         {
2401           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2402             exception);
2403           return((MagickRealType) cosh((double) alpha));
2404         }
2405       if (LocaleNCompare(expression,"cos",3) == 0)
2406         {
2407           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2408             exception);
2409           return((MagickRealType) cos((double) alpha));
2410         }
2411       if (LocaleCompare(expression,"c") == 0)
2412         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2413       break;
2414     }
2415     case 'D':
2416     case 'd':
2417     {
2418       if (LocaleNCompare(expression,"debug",5) == 0)
2419         {
2420           const char
2421             *type;
2422
2423           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2424             exception);
2425           if (fx_info->images->colorspace == CMYKColorspace)
2426             switch (channel)
2427             {
2428               case CyanChannel: type="cyan"; break;
2429               case MagentaChannel: type="magenta"; break;
2430               case YellowChannel: type="yellow"; break;
2431               case OpacityChannel: type="opacity"; break;
2432               case BlackChannel: type="black"; break;
2433               default: type="unknown"; break;
2434             }
2435           else
2436             switch (channel)
2437             {
2438               case RedChannel: type="red"; break;
2439               case GreenChannel: type="green"; break;
2440               case BlueChannel: type="blue"; break;
2441               case OpacityChannel: type="opacity"; break;
2442               default: type="unknown"; break;
2443             }
2444           (void) CopyMagickString(subexpression,expression+6,MaxTextExtent);
2445           if (strlen(subexpression) > 1)
2446             subexpression[strlen(subexpression)-1]='\0';
2447           if (fx_info->file != (FILE *) NULL)
2448             (void) fprintf(fx_info->file,"%s[%.20g,%.20g].%s: %s=%.*g\n",
2449               fx_info->images->filename,(double) x,(double) y,type,
2450               subexpression,GetMagickPrecision(),(double) alpha);
2451           return(0.0);
2452         }
2453       break;
2454     }
2455     case 'E':
2456     case 'e':
2457     {
2458       if (LocaleCompare(expression,"epsilon") == 0)
2459         return((MagickRealType) MagickEpsilon);
2460       if (LocaleNCompare(expression,"exp",3) == 0)
2461         {
2462           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2463             exception);
2464           return((MagickRealType) exp((double) alpha));
2465         }
2466       if (LocaleCompare(expression,"e") == 0)
2467         return((MagickRealType) 2.7182818284590452354);
2468       break;
2469     }
2470     case 'F':
2471     case 'f':
2472     {
2473       if (LocaleNCompare(expression,"floor",5) == 0)
2474         {
2475           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2476             exception);
2477           return((MagickRealType) floor((double) alpha));
2478         }
2479       break;
2480     }
2481     case 'G':
2482     case 'g':
2483     {
2484       if (LocaleCompare(expression,"g") == 0)
2485         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2486       break;
2487     }
2488     case 'H':
2489     case 'h':
2490     {
2491       if (LocaleCompare(expression,"h") == 0)
2492         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2493       if (LocaleCompare(expression,"hue") == 0)
2494         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2495       if (LocaleNCompare(expression,"hypot",5) == 0)
2496         {
2497           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2498             exception);
2499           return((MagickRealType) hypot((double) alpha,(double) *beta));
2500         }
2501       break;
2502     }
2503     case 'K':
2504     case 'k':
2505     {
2506       if (LocaleCompare(expression,"k") == 0)
2507         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2508       break;
2509     }
2510     case 'I':
2511     case 'i':
2512     {
2513       if (LocaleCompare(expression,"intensity") == 0)
2514         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2515       if (LocaleNCompare(expression,"int",3) == 0)
2516         {
2517           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2518             exception);
2519           return((MagickRealType) floor(alpha));
2520         }
2521       if (LocaleCompare(expression,"i") == 0)
2522         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2523       break;
2524     }
2525     case 'J':
2526     case 'j':
2527     {
2528       if (LocaleCompare(expression,"j") == 0)
2529         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2530 #if defined(MAGICKCORE_HAVE_J0)
2531       if (LocaleNCompare(expression,"j0",2) == 0)
2532         {
2533           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2534             exception);
2535           return((MagickRealType) j0((double) alpha));
2536         }
2537 #endif
2538 #if defined(MAGICKCORE_HAVE_J1)
2539       if (LocaleNCompare(expression,"j1",2) == 0)
2540         {
2541           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2542             exception);
2543           return((MagickRealType) j1((double) alpha));
2544         }
2545 #endif
2546 #if defined(MAGICKCORE_HAVE_J1)
2547       if (LocaleNCompare(expression,"jinc",4) == 0)
2548         {
2549           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2550             exception);
2551           if (alpha == 0.0)
2552             return(1.0);
2553           gamma=(MagickRealType) (2.0*j1((double) (MagickPI*alpha))/
2554             (MagickPI*alpha));
2555           return(gamma);
2556         }
2557 #endif
2558       break;
2559     }
2560     case 'L':
2561     case 'l':
2562     {
2563       if (LocaleNCompare(expression,"ln",2) == 0)
2564         {
2565           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2566             exception);
2567           return((MagickRealType) log((double) alpha));
2568         }
2569       if (LocaleNCompare(expression,"logtwo",6) == 0)
2570         {
2571           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+6,beta,
2572             exception);
2573           return((MagickRealType) log10((double) alpha))/log10(2.0);
2574         }
2575       if (LocaleNCompare(expression,"log",3) == 0)
2576         {
2577           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2578             exception);
2579           return((MagickRealType) log10((double) alpha));
2580         }
2581       if (LocaleCompare(expression,"lightness") == 0)
2582         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2583       break;
2584     }
2585     case 'M':
2586     case 'm':
2587     {
2588       if (LocaleCompare(expression,"MaxRGB") == 0)
2589         return((MagickRealType) QuantumRange);
2590       if (LocaleNCompare(expression,"maxima",6) == 0)
2591         break;
2592       if (LocaleNCompare(expression,"max",3) == 0)
2593         return(FxMax(fx_info,channel,x,y,expression+3,exception));
2594       if (LocaleNCompare(expression,"minima",6) == 0)
2595         break;
2596       if (LocaleNCompare(expression,"min",3) == 0)
2597         return(FxMin(fx_info,channel,x,y,expression+3,exception));
2598       if (LocaleNCompare(expression,"mod",3) == 0)
2599         {
2600           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2601             exception);
2602           return((MagickRealType) fmod((double) alpha,(double) *beta));
2603         }
2604       if (LocaleCompare(expression,"m") == 0)
2605         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2606       break;
2607     }
2608     case 'N':
2609     case 'n':
2610     {
2611       if (LocaleCompare(expression,"n") == 0)
2612         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2613       break;
2614     }
2615     case 'O':
2616     case 'o':
2617     {
2618       if (LocaleCompare(expression,"Opaque") == 0)
2619         return(1.0);
2620       if (LocaleCompare(expression,"o") == 0)
2621         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2622       break;
2623     }
2624     case 'P':
2625     case 'p':
2626     {
2627       if (LocaleCompare(expression,"pi") == 0)
2628         return((MagickRealType) MagickPI);
2629       if (LocaleNCompare(expression,"pow",3) == 0)
2630         {
2631           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2632             exception);
2633           return((MagickRealType) pow((double) alpha,(double) *beta));
2634         }
2635       if (LocaleCompare(expression,"p") == 0)
2636         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2637       break;
2638     }
2639     case 'Q':
2640     case 'q':
2641     {
2642       if (LocaleCompare(expression,"QuantumRange") == 0)
2643         return((MagickRealType) QuantumRange);
2644       if (LocaleCompare(expression,"QuantumScale") == 0)
2645         return((MagickRealType) QuantumScale);
2646       break;
2647     }
2648     case 'R':
2649     case 'r':
2650     {
2651       if (LocaleNCompare(expression,"rand",4) == 0)
2652         return((MagickRealType) GetPseudoRandomValue(fx_info->random_info));
2653       if (LocaleNCompare(expression,"round",5) == 0)
2654         {
2655           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2656             exception);
2657           return((MagickRealType) floor((double) alpha+0.5));
2658         }
2659       if (LocaleCompare(expression,"r") == 0)
2660         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2661       break;
2662     }
2663     case 'S':
2664     case 's':
2665     {
2666       if (LocaleCompare(expression,"saturation") == 0)
2667         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2668       if (LocaleNCompare(expression,"sign",4) == 0)
2669         {
2670           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2671             exception);
2672           return(alpha < 0.0 ? -1.0 : 1.0);
2673         }
2674       if (LocaleNCompare(expression,"sinc",4) == 0)
2675         {
2676           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2677             exception);
2678           if (alpha == 0)
2679             return(1.0);
2680           gamma=(MagickRealType) (sin((double) (MagickPI*alpha))/
2681             (MagickPI*alpha));
2682           return(gamma);
2683         }
2684       if (LocaleNCompare(expression,"sinh",4) == 0)
2685         {
2686           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2687             exception);
2688           return((MagickRealType) sinh((double) alpha));
2689         }
2690       if (LocaleNCompare(expression,"sin",3) == 0)
2691         {
2692           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2693             exception);
2694           return((MagickRealType) sin((double) alpha));
2695         }
2696       if (LocaleNCompare(expression,"sqrt",4) == 0)
2697         {
2698           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2699             exception);
2700           return((MagickRealType) sqrt((double) alpha));
2701         }
2702       if (LocaleCompare(expression,"s") == 0)
2703         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2704       break;
2705     }
2706     case 'T':
2707     case 't':
2708     {
2709       if (LocaleNCompare(expression,"tanh",4) == 0)
2710         {
2711           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2712             exception);
2713           return((MagickRealType) tanh((double) alpha));
2714         }
2715       if (LocaleNCompare(expression,"tan",3) == 0)
2716         {
2717           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2718             exception);
2719           return((MagickRealType) tan((double) alpha));
2720         }
2721       if (LocaleCompare(expression,"Transparent") == 0)
2722         return(0.0);
2723       if (LocaleNCompare(expression,"trunc",5) == 0)
2724         {
2725           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2726             exception);
2727           if (alpha >= 0.0)
2728             return((MagickRealType) floor((double) alpha));
2729           return((MagickRealType) ceil((double) alpha));
2730         }
2731       if (LocaleCompare(expression,"t") == 0)
2732         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2733       break;
2734     }
2735     case 'U':
2736     case 'u':
2737     {
2738       if (LocaleCompare(expression,"u") == 0)
2739         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2740       break;
2741     }
2742     case 'V':
2743     case 'v':
2744     {
2745       if (LocaleCompare(expression,"v") == 0)
2746         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2747       break;
2748     }
2749     case 'W':
2750     case 'w':
2751     {
2752       if (LocaleCompare(expression,"w") == 0)
2753         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2754       break;
2755     }
2756     case 'Y':
2757     case 'y':
2758     {
2759       if (LocaleCompare(expression,"y") == 0)
2760         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2761       break;
2762     }
2763     case 'Z':
2764     case 'z':
2765     {
2766       if (LocaleCompare(expression,"z") == 0)
2767         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2768       break;
2769     }
2770     default:
2771       break;
2772   }
2773   q=(char *) expression;
2774   alpha=strtod(expression,&q);
2775   if (q == expression)
2776     return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2777   return(alpha);
2778 }
2779
2780 MagickExport MagickBooleanType FxEvaluateExpression(FxInfo *fx_info,
2781   MagickRealType *alpha,ExceptionInfo *exception)
2782 {
2783   MagickBooleanType
2784     status;
2785
2786   status=FxEvaluateChannelExpression(fx_info,GrayChannel,0,0,alpha,exception);
2787   return(status);
2788 }
2789
2790 MagickExport MagickBooleanType FxPreprocessExpression(FxInfo *fx_info,
2791   MagickRealType *alpha,ExceptionInfo *exception)
2792 {
2793   FILE
2794     *file;
2795
2796   MagickBooleanType
2797     status;
2798
2799   file=fx_info->file;
2800   fx_info->file=(FILE *) NULL;
2801   status=FxEvaluateChannelExpression(fx_info,GrayChannel,0,0,alpha,exception);
2802   fx_info->file=file;
2803   return(status);
2804 }
2805
2806 MagickExport MagickBooleanType FxEvaluateChannelExpression(FxInfo *fx_info,
2807   const ChannelType channel,const ssize_t x,const ssize_t y,
2808   MagickRealType *alpha,ExceptionInfo *exception)
2809 {
2810   MagickRealType
2811     beta;
2812
2813   beta=0.0;
2814   *alpha=FxEvaluateSubexpression(fx_info,channel,x,y,fx_info->expression,&beta,
2815     exception);
2816   return(exception->severity == OptionError ? MagickFalse : MagickTrue);
2817 }
2818 \f
2819 /*
2820 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2821 %                                                                             %
2822 %                                                                             %
2823 %                                                                             %
2824 %     F x I m a g e                                                           %
2825 %                                                                             %
2826 %                                                                             %
2827 %                                                                             %
2828 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2829 %
2830 %  FxImage() applies a mathematical expression to the specified image.
2831 %
2832 %  The format of the FxImage method is:
2833 %
2834 %      Image *FxImage(const Image *image,const char *expression,
2835 %        ExceptionInfo *exception)
2836 %      Image *FxImageChannel(const Image *image,const ChannelType channel,
2837 %        const char *expression,ExceptionInfo *exception)
2838 %
2839 %  A description of each parameter follows:
2840 %
2841 %    o image: the image.
2842 %
2843 %    o channel: the channel.
2844 %
2845 %    o expression: A mathematical expression.
2846 %
2847 %    o exception: return any errors or warnings in this structure.
2848 %
2849 */
2850
2851 static FxInfo **DestroyFxThreadSet(FxInfo **fx_info)
2852 {
2853   register ssize_t
2854     i;
2855
2856   assert(fx_info != (FxInfo **) NULL);
2857   for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
2858     if (fx_info[i] != (FxInfo *) NULL)
2859       fx_info[i]=DestroyFxInfo(fx_info[i]);
2860   fx_info=(FxInfo **) RelinquishMagickMemory(fx_info);
2861   return(fx_info);
2862 }
2863
2864 static FxInfo **AcquireFxThreadSet(const Image *image,const char *expression,
2865   ExceptionInfo *exception)
2866 {
2867   char
2868     *fx_expression;
2869
2870   FxInfo
2871     **fx_info;
2872
2873   MagickRealType
2874     alpha;
2875
2876   register ssize_t
2877     i;
2878
2879   size_t
2880     number_threads;
2881
2882   number_threads=GetOpenMPMaximumThreads();
2883   fx_info=(FxInfo **) AcquireQuantumMemory(number_threads,sizeof(*fx_info));
2884   if (fx_info == (FxInfo **) NULL)
2885     return((FxInfo **) NULL);
2886   (void) ResetMagickMemory(fx_info,0,number_threads*sizeof(*fx_info));
2887   if (*expression != '@')
2888     fx_expression=ConstantString(expression);
2889   else
2890     fx_expression=FileToString(expression+1,~0,exception);
2891   for (i=0; i < (ssize_t) number_threads; i++)
2892   {
2893     fx_info[i]=AcquireFxInfo(image,fx_expression);
2894     if (fx_info[i] == (FxInfo *) NULL)
2895       return(DestroyFxThreadSet(fx_info));
2896     (void) FxPreprocessExpression(fx_info[i],&alpha,fx_info[i]->exception);
2897   }
2898   fx_expression=DestroyString(fx_expression);
2899   return(fx_info);
2900 }
2901
2902 MagickExport Image *FxImage(const Image *image,const char *expression,
2903   ExceptionInfo *exception)
2904 {
2905   Image
2906     *fx_image;
2907
2908   fx_image=FxImageChannel(image,GrayChannel,expression,exception);
2909   return(fx_image);
2910 }
2911
2912 MagickExport Image *FxImageChannel(const Image *image,const ChannelType channel,
2913   const char *expression,ExceptionInfo *exception)
2914 {
2915 #define FxImageTag  "Fx/Image"
2916
2917   CacheView
2918     *fx_view;
2919
2920   FxInfo
2921     **restrict fx_info;
2922
2923   Image
2924     *fx_image;
2925
2926   MagickBooleanType
2927     status;
2928
2929   MagickOffsetType
2930     progress;
2931
2932   MagickRealType
2933     alpha;
2934
2935   ssize_t
2936     y;
2937
2938   assert(image != (Image *) NULL);
2939   assert(image->signature == MagickSignature);
2940   if (image->debug != MagickFalse)
2941     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2942   fx_image=CloneImage(image,0,0,MagickTrue,exception);
2943   if (fx_image == (Image *) NULL)
2944     return((Image *) NULL);
2945   if (SetImageStorageClass(fx_image,DirectClass) == MagickFalse)
2946     {
2947       InheritException(exception,&fx_image->exception);
2948       fx_image=DestroyImage(fx_image);
2949       return((Image *) NULL);
2950     }
2951   fx_info=AcquireFxThreadSet(image,expression,exception);
2952   if (fx_info == (FxInfo **) NULL)
2953     {
2954       fx_image=DestroyImage(fx_image);
2955       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2956     }
2957   status=FxPreprocessExpression(fx_info[0],&alpha,exception);
2958   if (status == MagickFalse)
2959     {
2960       fx_image=DestroyImage(fx_image);
2961       fx_info=DestroyFxThreadSet(fx_info);
2962       return((Image *) NULL);
2963     }
2964   /*
2965     Fx image.
2966   */
2967   status=MagickTrue;
2968   progress=0;
2969   fx_view=AcquireCacheView(fx_image);
2970 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2971   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2972 #endif
2973   for (y=0; y < (ssize_t) fx_image->rows; y++)
2974   {
2975     const int
2976       id = GetOpenMPThreadId();
2977
2978     MagickRealType
2979       alpha;
2980
2981     register IndexPacket
2982       *restrict fx_indexes;
2983
2984     register ssize_t
2985       x;
2986
2987     register PixelPacket
2988       *restrict q;
2989
2990     if (status == MagickFalse)
2991       continue;
2992     q=GetCacheViewAuthenticPixels(fx_view,0,y,fx_image->columns,1,exception);
2993     if (q == (PixelPacket *) NULL)
2994       {
2995         status=MagickFalse;
2996         continue;
2997       }
2998     fx_indexes=GetCacheViewAuthenticIndexQueue(fx_view);
2999     alpha=0.0;
3000     for (x=0; x < (ssize_t) fx_image->columns; x++)
3001     {
3002       if ((channel & RedChannel) != 0)
3003         {
3004           (void) FxEvaluateChannelExpression(fx_info[id],RedChannel,x,y,
3005             &alpha,exception);
3006           q->red=ClampToQuantum((MagickRealType) QuantumRange*alpha);
3007         }
3008       if ((channel & GreenChannel) != 0)
3009         {
3010           (void) FxEvaluateChannelExpression(fx_info[id],GreenChannel,x,y,
3011             &alpha,exception);
3012           q->green=ClampToQuantum((MagickRealType) QuantumRange*alpha);
3013         }
3014       if ((channel & BlueChannel) != 0)
3015         {
3016           (void) FxEvaluateChannelExpression(fx_info[id],BlueChannel,x,y,
3017             &alpha,exception);
3018           q->blue=ClampToQuantum((MagickRealType) QuantumRange*alpha);
3019         }
3020       if ((channel & OpacityChannel) != 0)
3021         {
3022           (void) FxEvaluateChannelExpression(fx_info[id],OpacityChannel,x,y,
3023             &alpha,exception);
3024           if (image->matte == MagickFalse)
3025             q->opacity=ClampToQuantum((MagickRealType) QuantumRange*alpha);
3026           else
3027             q->opacity=ClampToQuantum((MagickRealType) (QuantumRange-
3028               QuantumRange*alpha));
3029         }
3030       if (((channel & IndexChannel) != 0) &&
3031           (fx_image->colorspace == CMYKColorspace))
3032         {
3033           (void) FxEvaluateChannelExpression(fx_info[id],IndexChannel,x,y,
3034             &alpha,exception);
3035           fx_indexes[x]=(IndexPacket) ClampToQuantum((MagickRealType)
3036             QuantumRange*alpha);
3037         }
3038       q++;
3039     }
3040     if (SyncCacheViewAuthenticPixels(fx_view,exception) == MagickFalse)
3041       status=MagickFalse;
3042     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3043       {
3044         MagickBooleanType
3045           proceed;
3046
3047 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3048   #pragma omp critical (MagickCore_FxImageChannel)
3049 #endif
3050         proceed=SetImageProgress(image,FxImageTag,progress++,image->rows);
3051         if (proceed == MagickFalse)
3052           status=MagickFalse;
3053       }
3054   }
3055   fx_image->matte=fx_info[0]->matte;
3056   fx_view=DestroyCacheView(fx_view);
3057   fx_info=DestroyFxThreadSet(fx_info);
3058   if (status == MagickFalse)
3059     fx_image=DestroyImage(fx_image);
3060   return(fx_image);
3061 }
3062 \f
3063 /*
3064 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3065 %                                                                             %
3066 %                                                                             %
3067 %                                                                             %
3068 %     I m p l o d e I m a g e                                                 %
3069 %                                                                             %
3070 %                                                                             %
3071 %                                                                             %
3072 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3073 %
3074 %  ImplodeImage() creates a new image that is a copy of an existing
3075 %  one with the image pixels "implode" by the specified percentage.  It
3076 %  allocates the memory necessary for the new Image structure and returns a
3077 %  pointer to the new image.
3078 %
3079 %  The format of the ImplodeImage method is:
3080 %
3081 %      Image *ImplodeImage(const Image *image,const double amount,
3082 %        ExceptionInfo *exception)
3083 %
3084 %  A description of each parameter follows:
3085 %
3086 %    o implode_image: Method ImplodeImage returns a pointer to the image
3087 %      after it is implode.  A null image is returned if there is a memory
3088 %      shortage.
3089 %
3090 %    o image: the image.
3091 %
3092 %    o amount:  Define the extent of the implosion.
3093 %
3094 %    o exception: return any errors or warnings in this structure.
3095 %
3096 */
3097 MagickExport Image *ImplodeImage(const Image *image,const double amount,
3098   ExceptionInfo *exception)
3099 {
3100 #define ImplodeImageTag  "Implode/Image"
3101
3102   CacheView
3103     *image_view,
3104     *implode_view;
3105
3106   Image
3107     *implode_image;
3108
3109   MagickBooleanType
3110     status;
3111
3112   MagickOffsetType
3113     progress;
3114
3115   MagickPixelPacket
3116     zero;
3117
3118   MagickRealType
3119     radius;
3120
3121   PointInfo
3122     center,
3123     scale;
3124
3125   ResampleFilter
3126     **restrict resample_filter;
3127
3128   ssize_t
3129     y;
3130
3131   /*
3132     Initialize implode image attributes.
3133   */
3134   assert(image != (Image *) NULL);
3135   assert(image->signature == MagickSignature);
3136   if (image->debug != MagickFalse)
3137     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3138   assert(exception != (ExceptionInfo *) NULL);
3139   assert(exception->signature == MagickSignature);
3140   implode_image=CloneImage(image,0,0,MagickTrue,exception);
3141   if (implode_image == (Image *) NULL)
3142     return((Image *) NULL);
3143   if (SetImageStorageClass(implode_image,DirectClass) == MagickFalse)
3144     {
3145       InheritException(exception,&implode_image->exception);
3146       implode_image=DestroyImage(implode_image);
3147       return((Image *) NULL);
3148     }
3149   if (implode_image->background_color.opacity != OpaqueOpacity)
3150     implode_image->matte=MagickTrue;
3151   /*
3152     Compute scaling factor.
3153   */
3154   scale.x=1.0;
3155   scale.y=1.0;
3156   center.x=0.5*image->columns;
3157   center.y=0.5*image->rows;
3158   radius=center.x;
3159   if (image->columns > image->rows)
3160     scale.y=(double) image->columns/(double) image->rows;
3161   else
3162     if (image->columns < image->rows)
3163       {
3164         scale.x=(double) image->rows/(double) image->columns;
3165         radius=center.y;
3166       }
3167   /*
3168     Implode image.
3169   */
3170   status=MagickTrue;
3171   progress=0;
3172   GetMagickPixelPacket(implode_image,&zero);
3173   resample_filter=AcquireResampleFilterThreadSet(image,
3174     UndefinedVirtualPixelMethod,MagickTrue,exception);
3175   image_view=AcquireCacheView(image);
3176   implode_view=AcquireCacheView(implode_image);
3177 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3178   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3179 #endif
3180   for (y=0; y < (ssize_t) image->rows; y++)
3181   {
3182     const int
3183       id = GetOpenMPThreadId();
3184
3185     MagickPixelPacket
3186       pixel;
3187
3188     MagickRealType
3189       distance;
3190
3191     PointInfo
3192       delta;
3193
3194     register IndexPacket
3195       *restrict implode_indexes;
3196
3197     register ssize_t
3198       x;
3199
3200     register PixelPacket
3201       *restrict q;
3202
3203     if (status == MagickFalse)
3204       continue;
3205     q=GetCacheViewAuthenticPixels(implode_view,0,y,implode_image->columns,1,
3206       exception);
3207     if (q == (PixelPacket *) NULL)
3208       {
3209         status=MagickFalse;
3210         continue;
3211       }
3212     implode_indexes=GetCacheViewAuthenticIndexQueue(implode_view);
3213     delta.y=scale.y*(double) (y-center.y);
3214     pixel=zero;
3215     for (x=0; x < (ssize_t) image->columns; x++)
3216     {
3217       /*
3218         Determine if the pixel is within an ellipse.
3219       */
3220       delta.x=scale.x*(double) (x-center.x);
3221       distance=delta.x*delta.x+delta.y*delta.y;
3222       if (distance < (radius*radius))
3223         {
3224           double
3225             factor;
3226
3227           /*
3228             Implode the pixel.
3229           */
3230           factor=1.0;
3231           if (distance > 0.0)
3232             factor=pow(sin((double) (MagickPI*sqrt((double) distance)/
3233               radius/2)),-amount);
3234           (void) ResamplePixelColor(resample_filter[id],(double)
3235             (factor*delta.x/scale.x+center.x),(double) (factor*delta.y/
3236             scale.y+center.y),&pixel);
3237           SetPixelPacket(implode_image,&pixel,q,implode_indexes+x);
3238         }
3239       q++;
3240     }
3241     if (SyncCacheViewAuthenticPixels(implode_view,exception) == MagickFalse)
3242       status=MagickFalse;
3243     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3244       {
3245         MagickBooleanType
3246           proceed;
3247
3248 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3249   #pragma omp critical (MagickCore_ImplodeImage)
3250 #endif
3251         proceed=SetImageProgress(image,ImplodeImageTag,progress++,image->rows);
3252         if (proceed == MagickFalse)
3253           status=MagickFalse;
3254       }
3255   }
3256   implode_view=DestroyCacheView(implode_view);
3257   image_view=DestroyCacheView(image_view);
3258   resample_filter=DestroyResampleFilterThreadSet(resample_filter);
3259   if (status == MagickFalse)
3260     implode_image=DestroyImage(implode_image);
3261   return(implode_image);
3262 }
3263 \f
3264 /*
3265 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3266 %                                                                             %
3267 %                                                                             %
3268 %                                                                             %
3269 %     M o r p h I m a g e s                                                   %
3270 %                                                                             %
3271 %                                                                             %
3272 %                                                                             %
3273 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3274 %
3275 %  The MorphImages() method requires a minimum of two images.  The first
3276 %  image is transformed into the second by a number of intervening images
3277 %  as specified by frames.
3278 %
3279 %  The format of the MorphImage method is:
3280 %
3281 %      Image *MorphImages(const Image *image,const size_t number_frames,
3282 %        ExceptionInfo *exception)
3283 %
3284 %  A description of each parameter follows:
3285 %
3286 %    o image: the image.
3287 %
3288 %    o number_frames:  Define the number of in-between image to generate.
3289 %      The more in-between frames, the smoother the morph.
3290 %
3291 %    o exception: return any errors or warnings in this structure.
3292 %
3293 */
3294 MagickExport Image *MorphImages(const Image *image,
3295   const size_t number_frames,ExceptionInfo *exception)
3296 {
3297 #define MorphImageTag  "Morph/Image"
3298
3299   Image
3300     *morph_image,
3301     *morph_images;
3302
3303   ssize_t
3304     y;
3305
3306   MagickOffsetType
3307     scene;
3308
3309   MagickRealType
3310     alpha,
3311     beta;
3312
3313   register const Image
3314     *next;
3315
3316   register ssize_t
3317     i;
3318
3319   MagickBooleanType
3320     status;
3321
3322   /*
3323     Clone first frame in sequence.
3324   */
3325   assert(image != (Image *) NULL);
3326   assert(image->signature == MagickSignature);
3327   if (image->debug != MagickFalse)
3328     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3329   assert(exception != (ExceptionInfo *) NULL);
3330   assert(exception->signature == MagickSignature);
3331   morph_images=CloneImage(image,0,0,MagickTrue,exception);
3332   if (morph_images == (Image *) NULL)
3333     return((Image *) NULL);
3334   if (GetNextImageInList(image) == (Image *) NULL)
3335     {
3336       /*
3337         Morph single image.
3338       */
3339       for (i=1; i < (ssize_t) number_frames; i++)
3340       {
3341         morph_image=CloneImage(image,0,0,MagickTrue,exception);
3342         if (morph_image == (Image *) NULL)
3343           {
3344             morph_images=DestroyImageList(morph_images);
3345             return((Image *) NULL);
3346           }
3347         AppendImageToList(&morph_images,morph_image);
3348         if (image->progress_monitor != (MagickProgressMonitor) NULL)
3349           {
3350             MagickBooleanType
3351               proceed;
3352
3353             proceed=SetImageProgress(image,MorphImageTag,(MagickOffsetType) i,
3354               number_frames);
3355             if (proceed == MagickFalse)
3356               status=MagickFalse;
3357           }
3358       }
3359       return(GetFirstImageInList(morph_images));
3360     }
3361   /*
3362     Morph image sequence.
3363   */
3364   status=MagickTrue;
3365   scene=0;
3366   next=image;
3367   for ( ; GetNextImageInList(next) != (Image *) NULL; next=GetNextImageInList(next))
3368   {
3369     for (i=0; i < (ssize_t) number_frames; i++)
3370     {
3371       CacheView
3372         *image_view,
3373         *morph_view;
3374
3375       beta=(MagickRealType) (i+1.0)/(MagickRealType) (number_frames+1.0);
3376       alpha=1.0-beta;
3377       morph_image=ResizeImage(next,(size_t) (alpha*next->columns+beta*
3378         GetNextImageInList(next)->columns+0.5),(size_t) (alpha*
3379         next->rows+beta*GetNextImageInList(next)->rows+0.5),
3380         next->filter,next->blur,exception);
3381       if (morph_image == (Image *) NULL)
3382         {
3383           morph_images=DestroyImageList(morph_images);
3384           return((Image *) NULL);
3385         }
3386       if (SetImageStorageClass(morph_image,DirectClass) == MagickFalse)
3387         {
3388           InheritException(exception,&morph_image->exception);
3389           morph_image=DestroyImage(morph_image);
3390           return((Image *) NULL);
3391         }
3392       AppendImageToList(&morph_images,morph_image);
3393       morph_images=GetLastImageInList(morph_images);
3394       morph_image=ResizeImage(GetNextImageInList(next),morph_images->columns,
3395         morph_images->rows,GetNextImageInList(next)->filter,
3396         GetNextImageInList(next)->blur,exception);
3397       if (morph_image == (Image *) NULL)
3398         {
3399           morph_images=DestroyImageList(morph_images);
3400           return((Image *) NULL);
3401         }
3402       image_view=AcquireCacheView(morph_image);
3403       morph_view=AcquireCacheView(morph_images);
3404 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3405   #pragma omp parallel for schedule(dynamic,4) shared(status)
3406 #endif
3407       for (y=0; y < (ssize_t) morph_images->rows; y++)
3408       {
3409         MagickBooleanType
3410           sync;
3411
3412         register const PixelPacket
3413           *restrict p;
3414
3415         register ssize_t
3416           x;
3417
3418         register PixelPacket
3419           *restrict q;
3420
3421         if (status == MagickFalse)
3422           continue;
3423         p=GetCacheViewVirtualPixels(image_view,0,y,morph_image->columns,1,
3424           exception);
3425         q=GetCacheViewAuthenticPixels(morph_view,0,y,morph_images->columns,1,
3426           exception);
3427         if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
3428           {
3429             status=MagickFalse;
3430             continue;
3431           }
3432         for (x=0; x < (ssize_t) morph_images->columns; x++)
3433         {
3434           q->red=ClampToQuantum(alpha*q->red+beta*GetRedPixelComponent(p));
3435           q->green=ClampToQuantum(alpha*q->green+beta*
3436             GetGreenPixelComponent(p));
3437           q->blue=ClampToQuantum(alpha*q->blue+beta*GetBluePixelComponent(p));
3438           q->opacity=ClampToQuantum(alpha*q->opacity+beta*
3439             GetOpacityPixelComponent(p));
3440           p++;
3441           q++;
3442         }
3443         sync=SyncCacheViewAuthenticPixels(morph_view,exception);
3444         if (sync == MagickFalse)
3445           status=MagickFalse;
3446       }
3447       morph_view=DestroyCacheView(morph_view);
3448       image_view=DestroyCacheView(image_view);
3449       morph_image=DestroyImage(morph_image);
3450     }
3451     if (i < (ssize_t) number_frames)
3452       break;
3453     /*
3454       Clone last frame in sequence.
3455     */
3456     morph_image=CloneImage(GetNextImageInList(next),0,0,MagickTrue,exception);
3457     if (morph_image == (Image *) NULL)
3458       {
3459         morph_images=DestroyImageList(morph_images);
3460         return((Image *) NULL);
3461       }
3462     AppendImageToList(&morph_images,morph_image);
3463     morph_images=GetLastImageInList(morph_images);
3464     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3465       {
3466         MagickBooleanType
3467           proceed;
3468
3469 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3470   #pragma omp critical (MagickCore_MorphImages)
3471 #endif
3472         proceed=SetImageProgress(image,MorphImageTag,scene,
3473           GetImageListLength(image));
3474         if (proceed == MagickFalse)
3475           status=MagickFalse;
3476       }
3477     scene++;
3478   }
3479   if (GetNextImageInList(next) != (Image *) NULL)
3480     {
3481       morph_images=DestroyImageList(morph_images);
3482       return((Image *) NULL);
3483     }
3484   return(GetFirstImageInList(morph_images));
3485 }
3486 \f
3487 /*
3488 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3489 %                                                                             %
3490 %                                                                             %
3491 %                                                                             %
3492 %     P l a s m a I m a g e                                                   %
3493 %                                                                             %
3494 %                                                                             %
3495 %                                                                             %
3496 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3497 %
3498 %  PlasmaImage() initializes an image with plasma fractal values.  The image
3499 %  must be initialized with a base color and the random number generator
3500 %  seeded before this method is called.
3501 %
3502 %  The format of the PlasmaImage method is:
3503 %
3504 %      MagickBooleanType PlasmaImage(Image *image,const SegmentInfo *segment,
3505 %        size_t attenuate,size_t depth)
3506 %
3507 %  A description of each parameter follows:
3508 %
3509 %    o image: the image.
3510 %
3511 %    o segment:   Define the region to apply plasma fractals values.
3512 %
3513 %    o attenuate: Define the plasma attenuation factor.
3514 %
3515 %    o depth: Limit the plasma recursion depth.
3516 %
3517 */
3518
3519 static inline Quantum PlasmaPixel(RandomInfo *random_info,
3520   const MagickRealType pixel,const MagickRealType noise)
3521 {
3522   Quantum
3523     plasma;
3524
3525   plasma=ClampToQuantum(pixel+noise*GetPseudoRandomValue(random_info)-
3526     noise/2.0);
3527   return(plasma);
3528 }
3529
3530 MagickExport MagickBooleanType PlasmaImageProxy(Image *image,
3531   CacheView *image_view,RandomInfo *random_info,const SegmentInfo *segment,
3532   size_t attenuate,size_t depth)
3533 {
3534   ExceptionInfo
3535     *exception;
3536
3537   ssize_t
3538     x,
3539     x_mid,
3540     y,
3541     y_mid;
3542
3543   MagickRealType
3544     plasma;
3545
3546   PixelPacket
3547     u,
3548     v;
3549
3550   if (((segment->x2-segment->x1) == 0.0) && ((segment->y2-segment->y1) == 0.0))
3551     return(MagickTrue);
3552   if (depth != 0)
3553     {
3554       SegmentInfo
3555         local_info;
3556
3557       /*
3558         Divide the area into quadrants and recurse.
3559       */
3560       depth--;
3561       attenuate++;
3562       x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3563       y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
3564       local_info=(*segment);
3565       local_info.x2=(double) x_mid;
3566       local_info.y2=(double) y_mid;
3567       (void) PlasmaImageProxy(image,image_view,random_info,&local_info,
3568         attenuate,depth);
3569       local_info=(*segment);
3570       local_info.y1=(double) y_mid;
3571       local_info.x2=(double) x_mid;
3572       (void) PlasmaImageProxy(image,image_view,random_info,&local_info,
3573         attenuate,depth);
3574       local_info=(*segment);
3575       local_info.x1=(double) x_mid;
3576       local_info.y2=(double) y_mid;
3577       (void) PlasmaImageProxy(image,image_view,random_info,&local_info,
3578         attenuate,depth);
3579       local_info=(*segment);
3580       local_info.x1=(double) x_mid;
3581       local_info.y1=(double) y_mid;
3582       return(PlasmaImageProxy(image,image_view,random_info,&local_info,
3583         attenuate,depth));
3584     }
3585   x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3586   y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
3587   if ((segment->x1 == (double) x_mid) && (segment->x2 == (double) x_mid) &&
3588       (segment->y1 == (double) y_mid) && (segment->y2 == (double) y_mid))
3589     return(MagickFalse);
3590   /*
3591     Average pixels and apply plasma.
3592   */
3593   exception=(&image->exception);
3594   plasma=(MagickRealType) QuantumRange/(2.0*attenuate);
3595   if ((segment->x1 != (double) x_mid) || (segment->x2 != (double) x_mid))
3596     {
3597       register PixelPacket
3598         *restrict q;
3599
3600       /*
3601         Left pixel.
3602       */
3603       x=(ssize_t) ceil(segment->x1-0.5);
3604       (void) GetOneCacheViewVirtualPixel(image_view,x,(ssize_t)
3605         ceil(segment->y1-0.5),&u,exception);
3606       (void) GetOneCacheViewVirtualPixel(image_view,x,(ssize_t)
3607         ceil(segment->y2-0.5),&v,exception);
3608       q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
3609       if (q == (PixelPacket *) NULL)
3610         return(MagickTrue);
3611       q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
3612         plasma);
3613       q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/2.0,
3614         plasma);
3615       q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
3616         plasma);
3617       (void) SyncCacheViewAuthenticPixels(image_view,exception);
3618       if (segment->x1 != segment->x2)
3619         {
3620           /*
3621             Right pixel.
3622           */
3623           x=(ssize_t) ceil(segment->x2-0.5);
3624           (void) GetOneCacheViewVirtualPixel(image_view,x,(ssize_t)
3625             ceil(segment->y1-0.5),&u,exception);
3626           (void) GetOneCacheViewVirtualPixel(image_view,x,(ssize_t)
3627             ceil(segment->y2-0.5),&v,exception);
3628           q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
3629           if (q == (PixelPacket *) NULL)
3630             return(MagickTrue);
3631           q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
3632             plasma);
3633           q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/
3634             2.0,plasma);
3635           q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
3636             plasma);
3637           (void) SyncCacheViewAuthenticPixels(image_view,exception);
3638         }
3639     }
3640   if ((segment->y1 != (double) y_mid) || (segment->y2 != (double) y_mid))
3641     {
3642       if ((segment->x1 != (double) x_mid) || (segment->y2 != (double) y_mid))
3643         {
3644           register PixelPacket
3645             *restrict q;
3646
3647           /*
3648             Bottom pixel.
3649           */
3650           y=(ssize_t) ceil(segment->y2-0.5);
3651           (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
3652             ceil(segment->x1-0.5),y,&u,exception);
3653           (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
3654             ceil(segment->x2-0.5),y,&v,exception);
3655           q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
3656           if (q == (PixelPacket *) NULL)
3657             return(MagickTrue);
3658           q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
3659             plasma);
3660           q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/
3661             2.0,plasma);
3662           q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
3663             plasma);
3664           (void) SyncCacheViewAuthenticPixels(image_view,exception);
3665         }
3666       if (segment->y1 != segment->y2)
3667         {
3668           register PixelPacket
3669             *restrict q;
3670
3671           /*
3672             Top pixel.
3673           */
3674           y=(ssize_t) ceil(segment->y1-0.5);
3675           (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
3676             ceil(segment->x1-0.5),y,&u,exception);
3677           (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
3678             ceil(segment->x2-0.5),y,&v,exception);
3679           q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
3680           if (q == (PixelPacket *) NULL)
3681             return(MagickTrue);
3682           q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
3683             plasma);
3684           q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/
3685             2.0,plasma);
3686           q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
3687             plasma);
3688           (void) SyncCacheViewAuthenticPixels(image_view,exception);
3689         }
3690     }
3691   if ((segment->x1 != segment->x2) || (segment->y1 != segment->y2))
3692     {
3693       register PixelPacket
3694         *restrict q;
3695
3696       /*
3697         Middle pixel.
3698       */
3699       x=(ssize_t) ceil(segment->x1-0.5);
3700       y=(ssize_t) ceil(segment->y1-0.5);
3701       (void) GetOneVirtualPixel(image,x,y,&u,exception);
3702       x=(ssize_t) ceil(segment->x2-0.5);
3703       y=(ssize_t) ceil(segment->y2-0.5);
3704       (void) GetOneCacheViewVirtualPixel(image_view,x,y,&v,exception);
3705       q=QueueCacheViewAuthenticPixels(image_view,x_mid,y_mid,1,1,exception);
3706       if (q == (PixelPacket *) NULL)
3707         return(MagickTrue);
3708       q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
3709         plasma);
3710       q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/2.0,
3711         plasma);
3712       q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
3713         plasma);
3714       (void) SyncCacheViewAuthenticPixels(image_view,exception);
3715     }
3716   if (((segment->x2-segment->x1) < 3.0) && ((segment->y2-segment->y1) < 3.0))
3717     return(MagickTrue);
3718   return(MagickFalse);
3719 }
3720 \f
3721 MagickExport MagickBooleanType PlasmaImage(Image *image,
3722   const SegmentInfo *segment,size_t attenuate,size_t depth)
3723 {
3724   CacheView
3725     *image_view;
3726
3727   MagickBooleanType
3728     status;
3729
3730   RandomInfo
3731     *random_info;
3732
3733   if (image->debug != MagickFalse)
3734     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3735   assert(image != (Image *) NULL);
3736   assert(image->signature == MagickSignature);
3737   if (image->debug != MagickFalse)
3738     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3739   if (SetImageStorageClass(image,DirectClass) == MagickFalse)
3740     return(MagickFalse);
3741   image_view=AcquireCacheView(image);
3742   random_info=AcquireRandomInfo();
3743   status=PlasmaImageProxy(image,image_view,random_info,segment,attenuate,depth);
3744   random_info=DestroyRandomInfo(random_info);
3745   image_view=DestroyCacheView(image_view);
3746   return(status);
3747 }
3748 \f
3749 /*
3750 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3751 %                                                                             %
3752 %                                                                             %
3753 %                                                                             %
3754 %   P o l a r o i d I m a g e                                                 %
3755 %                                                                             %
3756 %                                                                             %
3757 %                                                                             %
3758 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3759 %
3760 %  PolaroidImage() simulates a Polaroid picture.
3761 %
3762 %  The format of the AnnotateImage method is:
3763 %
3764 %      Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
3765 %        const double angle,ExceptionInfo exception)
3766 %
3767 %  A description of each parameter follows:
3768 %
3769 %    o image: the image.
3770 %
3771 %    o draw_info: the draw info.
3772 %
3773 %    o angle: Apply the effect along this angle.
3774 %
3775 %    o exception: return any errors or warnings in this structure.
3776 %
3777 */
3778 MagickExport Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
3779   const double angle,ExceptionInfo *exception)
3780 {
3781   const char
3782     *value;
3783
3784   ssize_t
3785     quantum;
3786
3787   Image
3788     *bend_image,
3789     *caption_image,
3790     *flop_image,
3791     *picture_image,
3792     *polaroid_image,
3793     *rotate_image,
3794     *trim_image;
3795
3796   size_t
3797     height;
3798
3799   /*
3800     Simulate a Polaroid picture.
3801   */
3802   assert(image != (Image *) NULL);
3803   assert(image->signature == MagickSignature);
3804   if (image->debug != MagickFalse)
3805     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3806   assert(exception != (ExceptionInfo *) NULL);
3807   assert(exception->signature == MagickSignature);
3808   quantum=(ssize_t) MagickMax(MagickMax((double) image->columns,(double)
3809     image->rows)/25.0,10.0);
3810   height=image->rows+2*quantum;
3811   caption_image=(Image *) NULL;
3812   value=GetImageProperty(image,"Caption");
3813   if (value != (const char *) NULL)
3814     {
3815       char
3816         *caption,
3817         geometry[MaxTextExtent];
3818
3819       DrawInfo
3820         *annotate_info;
3821
3822       ssize_t
3823         count;
3824
3825       MagickBooleanType
3826         status;
3827
3828       TypeMetric
3829         metrics;
3830
3831       /*
3832         Generate caption image.
3833       */
3834       caption_image=CloneImage(image,image->columns,1,MagickTrue,exception);
3835       if (caption_image == (Image *) NULL)
3836         return((Image *) NULL);
3837       annotate_info=CloneDrawInfo((const ImageInfo *) NULL,draw_info);
3838       caption=InterpretImageProperties((ImageInfo *) NULL,(Image *) image,
3839         value);
3840       (void) CloneString(&annotate_info->text,caption);
3841       count=FormatMagickCaption(caption_image,annotate_info,MagickTrue,&metrics,
3842         &caption);
3843       status=SetImageExtent(caption_image,image->columns,(size_t)
3844         ((count+1)*(metrics.ascent-metrics.descent)+0.5));
3845       if (status == MagickFalse)
3846         caption_image=DestroyImage(caption_image);
3847       else
3848         {
3849           caption_image->background_color=image->border_color;
3850           (void) SetImageBackgroundColor(caption_image);
3851           (void) CloneString(&annotate_info->text,caption);
3852           (void) FormatMagickString(geometry,MaxTextExtent,"+0+%g",
3853             metrics.ascent);
3854           if (annotate_info->gravity == UndefinedGravity)
3855             (void) CloneString(&annotate_info->geometry,AcquireString(
3856               geometry));
3857           (void) AnnotateImage(caption_image,annotate_info);
3858           height+=caption_image->rows;
3859         }
3860       annotate_info=DestroyDrawInfo(annotate_info);
3861       caption=DestroyString(caption);
3862     }
3863   picture_image=CloneImage(image,image->columns+2*quantum,height,MagickTrue,
3864     exception);
3865   if (picture_image == (Image *) NULL)
3866     {
3867       if (caption_image != (Image *) NULL)
3868         caption_image=DestroyImage(caption_image);
3869       return((Image *) NULL);
3870     }
3871   picture_image->background_color=image->border_color;
3872   (void) SetImageBackgroundColor(picture_image);
3873   (void) CompositeImage(picture_image,OverCompositeOp,image,quantum,quantum);
3874   if (caption_image != (Image *) NULL)
3875     {
3876       (void) CompositeImage(picture_image,OverCompositeOp,caption_image,
3877         quantum,(ssize_t) (image->rows+3*quantum/2));
3878       caption_image=DestroyImage(caption_image);
3879     }
3880   (void) QueryColorDatabase("none",&picture_image->background_color,exception);
3881   (void) SetImageAlphaChannel(picture_image,OpaqueAlphaChannel);
3882   rotate_image=RotateImage(picture_image,90.0,exception);
3883   picture_image=DestroyImage(picture_image);
3884   if (rotate_image == (Image *) NULL)
3885     return((Image *) NULL);
3886   picture_image=rotate_image;
3887   bend_image=WaveImage(picture_image,0.01*picture_image->rows,2.0*
3888     picture_image->columns,exception);
3889   picture_image=DestroyImage(picture_image);
3890   if (bend_image == (Image *) NULL)
3891     return((Image *) NULL);
3892   InheritException(&bend_image->exception,exception);
3893   picture_image=bend_image;
3894   rotate_image=RotateImage(picture_image,-90.0,exception);
3895   picture_image=DestroyImage(picture_image);
3896   if (rotate_image == (Image *) NULL)
3897     return((Image *) NULL);
3898   picture_image=rotate_image;
3899   picture_image->background_color=image->background_color;
3900   polaroid_image=ShadowImage(picture_image,80.0,2.0,quantum/3,quantum/3,
3901     exception);
3902   if (polaroid_image == (Image *) NULL)
3903     {
3904       picture_image=DestroyImage(picture_image);
3905       return(picture_image);
3906     }
3907   flop_image=FlopImage(polaroid_image,exception);
3908   polaroid_image=DestroyImage(polaroid_image);
3909   if (flop_image == (Image *) NULL)
3910     {
3911       picture_image=DestroyImage(picture_image);
3912       return(picture_image);
3913     }
3914   polaroid_image=flop_image;
3915   (void) CompositeImage(polaroid_image,OverCompositeOp,picture_image,
3916     (ssize_t) (-0.01*picture_image->columns/2.0),0L);
3917   picture_image=DestroyImage(picture_image);
3918   (void) QueryColorDatabase("none",&polaroid_image->background_color,exception);
3919   rotate_image=RotateImage(polaroid_image,angle,exception);
3920   polaroid_image=DestroyImage(polaroid_image);
3921   if (rotate_image == (Image *) NULL)
3922     return((Image *) NULL);
3923   polaroid_image=rotate_image;
3924   trim_image=TrimImage(polaroid_image,exception);
3925   polaroid_image=DestroyImage(polaroid_image);
3926   if (trim_image == (Image *) NULL)
3927     return((Image *) NULL);
3928   polaroid_image=trim_image;
3929   return(polaroid_image);
3930 }
3931 \f
3932 /*
3933 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3934 %                                                                             %
3935 %                                                                             %
3936 %                                                                             %
3937 %     S e p i a T o n e I m a g e                                             %
3938 %                                                                             %
3939 %                                                                             %
3940 %                                                                             %
3941 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3942 %
3943 %  MagickSepiaToneImage() applies a special effect to the image, similar to the
3944 %  effect achieved in a photo darkroom by sepia toning.  Threshold ranges from
3945 %  0 to QuantumRange and is a measure of the extent of the sepia toning.  A
3946 %  threshold of 80% is a good starting point for a reasonable tone.
3947 %
3948 %  The format of the SepiaToneImage method is:
3949 %
3950 %      Image *SepiaToneImage(const Image *image,const double threshold,
3951 %        ExceptionInfo *exception)
3952 %
3953 %  A description of each parameter follows:
3954 %
3955 %    o image: the image.
3956 %
3957 %    o threshold: the tone threshold.
3958 %
3959 %    o exception: return any errors or warnings in this structure.
3960 %
3961 */
3962 MagickExport Image *SepiaToneImage(const Image *image,const double threshold,
3963   ExceptionInfo *exception)
3964 {
3965 #define SepiaToneImageTag  "SepiaTone/Image"
3966
3967   CacheView
3968     *image_view,
3969     *sepia_view;
3970
3971   Image
3972     *sepia_image;
3973
3974   MagickBooleanType
3975     status;
3976
3977   MagickOffsetType
3978     progress;
3979
3980   ssize_t
3981     y;
3982
3983   /*
3984     Initialize sepia-toned image attributes.
3985   */
3986   assert(image != (const Image *) NULL);
3987   assert(image->signature == MagickSignature);
3988   if (image->debug != MagickFalse)
3989     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3990   assert(exception != (ExceptionInfo *) NULL);
3991   assert(exception->signature == MagickSignature);
3992   sepia_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
3993   if (sepia_image == (Image *) NULL)
3994     return((Image *) NULL);
3995   if (SetImageStorageClass(sepia_image,DirectClass) == MagickFalse)
3996     {
3997       InheritException(exception,&sepia_image->exception);
3998       sepia_image=DestroyImage(sepia_image);
3999       return((Image *) NULL);
4000     }
4001   /*
4002     Tone each row of the image.
4003   */
4004   status=MagickTrue;
4005   progress=0;
4006   image_view=AcquireCacheView(image);
4007   sepia_view=AcquireCacheView(sepia_image);
4008 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4009   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4010 #endif
4011   for (y=0; y < (ssize_t) image->rows; y++)
4012   {
4013     register const PixelPacket
4014       *restrict p;
4015
4016     register ssize_t
4017       x;
4018
4019     register PixelPacket
4020       *restrict q;
4021
4022     if (status == MagickFalse)
4023       continue;
4024     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
4025     q=QueueCacheViewAuthenticPixels(sepia_view,0,y,sepia_image->columns,1,
4026       exception);
4027     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
4028       {
4029         status=MagickFalse;
4030         continue;
4031       }
4032     for (x=0; x < (ssize_t) image->columns; x++)
4033     {
4034       MagickRealType
4035         intensity,
4036         tone;
4037
4038       intensity=(MagickRealType) PixelIntensityToQuantum(p);
4039       tone=intensity > threshold ? (MagickRealType) QuantumRange : intensity+
4040         (MagickRealType) QuantumRange-threshold;
4041       q->red=ClampToQuantum(tone);
4042       tone=intensity > (7.0*threshold/6.0) ? (MagickRealType) QuantumRange :
4043         intensity+(MagickRealType) QuantumRange-7.0*threshold/6.0;
4044       q->green=ClampToQuantum(tone);
4045       tone=intensity < (threshold/6.0) ? 0 : intensity-threshold/6.0;
4046       q->blue=ClampToQuantum(tone);
4047       tone=threshold/7.0;
4048       if ((MagickRealType) q->green < tone)
4049         q->green=ClampToQuantum(tone);
4050       if ((MagickRealType) q->blue < tone)
4051         q->blue=ClampToQuantum(tone);
4052       p++;
4053       q++;
4054     }
4055     if (SyncCacheViewAuthenticPixels(sepia_view,exception) == MagickFalse)
4056       status=MagickFalse;
4057     if (image->progress_monitor != (MagickProgressMonitor) NULL)
4058       {
4059         MagickBooleanType
4060           proceed;
4061
4062 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4063   #pragma omp critical (MagickCore_SepiaToneImage)
4064 #endif
4065         proceed=SetImageProgress(image,SepiaToneImageTag,progress++,
4066           image->rows);
4067         if (proceed == MagickFalse)
4068           status=MagickFalse;
4069       }
4070   }
4071   sepia_view=DestroyCacheView(sepia_view);
4072   image_view=DestroyCacheView(image_view);
4073   (void) NormalizeImage(sepia_image);
4074   (void) ContrastImage(sepia_image,MagickTrue);
4075   if (status == MagickFalse)
4076     sepia_image=DestroyImage(sepia_image);
4077   return(sepia_image);
4078 }
4079 \f
4080 /*
4081 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4082 %                                                                             %
4083 %                                                                             %
4084 %                                                                             %
4085 %     S h a d o w I m a g e                                                   %
4086 %                                                                             %
4087 %                                                                             %
4088 %                                                                             %
4089 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4090 %
4091 %  ShadowImage() simulates a shadow from the specified image and returns it.
4092 %
4093 %  The format of the ShadowImage method is:
4094 %
4095 %      Image *ShadowImage(const Image *image,const double opacity,
4096 %        const double sigma,const ssize_t x_offset,const ssize_t y_offset,
4097 %        ExceptionInfo *exception)
4098 %
4099 %  A description of each parameter follows:
4100 %
4101 %    o image: the image.
4102 %
4103 %    o opacity: percentage transparency.
4104 %
4105 %    o sigma: the standard deviation of the Gaussian, in pixels.
4106 %
4107 %    o x_offset: the shadow x-offset.
4108 %
4109 %    o y_offset: the shadow y-offset.
4110 %
4111 %    o exception: return any errors or warnings in this structure.
4112 %
4113 */
4114 MagickExport Image *ShadowImage(const Image *image,const double opacity,
4115   const double sigma,const ssize_t x_offset,const ssize_t y_offset,
4116   ExceptionInfo *exception)
4117 {
4118 #define ShadowImageTag  "Shadow/Image"
4119
4120   CacheView
4121     *image_view;
4122
4123   Image
4124     *border_image,
4125     *clone_image,
4126     *shadow_image;
4127
4128   MagickBooleanType
4129     status;
4130
4131   MagickOffsetType
4132     progress;
4133
4134   RectangleInfo
4135     border_info;
4136
4137   ssize_t
4138     y;
4139
4140   assert(image != (Image *) NULL);
4141   assert(image->signature == MagickSignature);
4142   if (image->debug != MagickFalse)
4143     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4144   assert(exception != (ExceptionInfo *) NULL);
4145   assert(exception->signature == MagickSignature);
4146   clone_image=CloneImage(image,0,0,MagickTrue,exception);
4147   if (clone_image == (Image *) NULL)
4148     return((Image *) NULL);
4149   (void) SetImageVirtualPixelMethod(clone_image,EdgeVirtualPixelMethod);
4150   clone_image->compose=OverCompositeOp;
4151   border_info.width=(size_t) floor(2.0*sigma+0.5);
4152   border_info.height=(size_t) floor(2.0*sigma+0.5);
4153   border_info.x=0;
4154   border_info.y=0;
4155   (void) QueryColorDatabase("none",&clone_image->border_color,exception);
4156   border_image=BorderImage(clone_image,&border_info,exception);
4157   clone_image=DestroyImage(clone_image);
4158   if (border_image == (Image *) NULL)
4159     return((Image *) NULL);
4160   if (border_image->matte == MagickFalse)
4161     (void) SetImageAlphaChannel(border_image,OpaqueAlphaChannel);
4162   /*
4163     Shadow image.
4164   */
4165   status=MagickTrue;
4166   progress=0;
4167   image_view=AcquireCacheView(border_image);
4168 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4169   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4170 #endif
4171   for (y=0; y < (ssize_t) border_image->rows; y++)
4172   {
4173     register ssize_t
4174       x;
4175
4176     register PixelPacket
4177       *restrict q;
4178
4179     if (status == MagickFalse)
4180       continue;
4181     q=GetCacheViewAuthenticPixels(image_view,0,y,border_image->columns,1,
4182       exception);
4183     if (q == (PixelPacket *) NULL)
4184       {
4185         status=MagickFalse;
4186         continue;
4187       }
4188     for (x=0; x < (ssize_t) border_image->columns; x++)
4189     {
4190       q->red=border_image->background_color.red;
4191       q->green=border_image->background_color.green;
4192       q->blue=border_image->background_color.blue;
4193       if (border_image->matte == MagickFalse)
4194         q->opacity=border_image->background_color.opacity;
4195       else
4196         q->opacity=ClampToQuantum((MagickRealType) (QuantumRange-
4197           GetAlphaPixelComponent(q)*opacity/100.0));
4198       q++;
4199     }
4200     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4201       status=MagickFalse;
4202     if (image->progress_monitor != (MagickProgressMonitor) NULL)
4203       {
4204         MagickBooleanType
4205           proceed;
4206
4207 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4208   #pragma omp critical (MagickCore_ShadowImage)
4209 #endif
4210         proceed=SetImageProgress(image,ShadowImageTag,progress++,
4211           border_image->rows);
4212         if (proceed == MagickFalse)
4213           status=MagickFalse;
4214       }
4215   }
4216   image_view=DestroyCacheView(image_view);
4217   shadow_image=BlurImageChannel(border_image,AlphaChannel,0.0,sigma,exception);
4218   border_image=DestroyImage(border_image);
4219   if (shadow_image == (Image *) NULL)
4220     return((Image *) NULL);
4221   if (shadow_image->page.width == 0)
4222     shadow_image->page.width=shadow_image->columns;
4223   if (shadow_image->page.height == 0)
4224     shadow_image->page.height=shadow_image->rows;
4225   shadow_image->page.width+=x_offset-(ssize_t) border_info.width;
4226   shadow_image->page.height+=y_offset-(ssize_t) border_info.height;
4227   shadow_image->page.x+=x_offset-(ssize_t) border_info.width;
4228   shadow_image->page.y+=y_offset-(ssize_t) border_info.height;
4229   return(shadow_image);
4230 }
4231 \f
4232 /*
4233 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4234 %                                                                             %
4235 %                                                                             %
4236 %                                                                             %
4237 %     S k e t c h I m a g e                                                   %
4238 %                                                                             %
4239 %                                                                             %
4240 %                                                                             %
4241 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4242 %
4243 %  SketchImage() simulates a pencil sketch.  We convolve the image with a
4244 %  Gaussian operator of the given radius and standard deviation (sigma).  For
4245 %  reasonable results, radius should be larger than sigma.  Use a radius of 0
4246 %  and SketchImage() selects a suitable radius for you.  Angle gives the angle
4247 %  of the sketch.
4248 %
4249 %  The format of the SketchImage method is:
4250 %
4251 %    Image *SketchImage(const Image *image,const double radius,
4252 %      const double sigma,const double angle,ExceptionInfo *exception)
4253 %
4254 %  A description of each parameter follows:
4255 %
4256 %    o image: the image.
4257 %
4258 %    o radius: the radius of the Gaussian, in pixels, not counting
4259 %      the center pixel.
4260 %
4261 %    o sigma: the standard deviation of the Gaussian, in pixels.
4262 %
4263 %    o angle: Apply the effect along this angle.
4264 %
4265 %    o exception: return any errors or warnings in this structure.
4266 %
4267 */
4268 MagickExport Image *SketchImage(const Image *image,const double radius,
4269   const double sigma,const double angle,ExceptionInfo *exception)
4270 {
4271   CacheView
4272     *random_view;
4273
4274   Image
4275     *blend_image,
4276     *blur_image,
4277     *dodge_image,
4278     *random_image,
4279     *sketch_image;
4280
4281   ssize_t
4282     y;
4283
4284   MagickBooleanType
4285     status;
4286
4287   MagickPixelPacket
4288     zero;
4289
4290   RandomInfo
4291     **restrict random_info;
4292
4293   /*
4294     Sketch image.
4295   */
4296   random_image=CloneImage(image,image->columns << 1,image->rows << 1,
4297     MagickTrue,exception);
4298   if (random_image == (Image *) NULL)
4299     return((Image *) NULL);
4300   status=MagickTrue;
4301   GetMagickPixelPacket(random_image,&zero);
4302   random_info=AcquireRandomInfoThreadSet();
4303   random_view=AcquireCacheView(random_image);
4304 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4305   #pragma omp parallel for schedule(dynamic,4) shared(status)
4306 #endif
4307   for (y=0; y < (ssize_t) random_image->rows; y++)
4308   {
4309     const int
4310       id = GetOpenMPThreadId();
4311
4312     MagickPixelPacket
4313       pixel;
4314
4315     register IndexPacket
4316       *restrict indexes;
4317
4318     register ssize_t
4319       x;
4320
4321     register PixelPacket
4322       *restrict q;
4323
4324     if (status == MagickFalse)
4325       continue;
4326     q=QueueCacheViewAuthenticPixels(random_view,0,y,random_image->columns,1,
4327       exception);
4328     if (q == (PixelPacket *) NULL)
4329       {
4330         status=MagickFalse;
4331         continue;
4332       }
4333     indexes=GetCacheViewAuthenticIndexQueue(random_view);
4334     pixel=zero;
4335     for (x=0; x < (ssize_t) random_image->columns; x++)
4336     {
4337       pixel.red=(MagickRealType) (QuantumRange*
4338         GetPseudoRandomValue(random_info[id]));
4339       pixel.green=pixel.red;
4340       pixel.blue=pixel.red;
4341       if (image->colorspace == CMYKColorspace)
4342         pixel.index=pixel.red;
4343       SetPixelPacket(random_image,&pixel,q,indexes+x);
4344       q++;
4345     }
4346     if (SyncCacheViewAuthenticPixels(random_view,exception) == MagickFalse)
4347       status=MagickFalse;
4348   }
4349   random_view=DestroyCacheView(random_view);
4350   random_info=DestroyRandomInfoThreadSet(random_info);
4351   if (status == MagickFalse)
4352     {
4353       random_image=DestroyImage(random_image);
4354       return(random_image);
4355     }
4356   blur_image=MotionBlurImage(random_image,radius,sigma,angle,exception);
4357   random_image=DestroyImage(random_image);
4358   if (blur_image == (Image *) NULL)
4359     return((Image *) NULL);
4360   dodge_image=EdgeImage(blur_image,radius,exception);
4361   blur_image=DestroyImage(blur_image);
4362   if (dodge_image == (Image *) NULL)
4363     return((Image *) NULL);
4364   (void) NormalizeImage(dodge_image);
4365   (void) NegateImage(dodge_image,MagickFalse);
4366   (void) TransformImage(&dodge_image,(char *) NULL,"50%");
4367   sketch_image=CloneImage(image,0,0,MagickTrue,exception);
4368   if (sketch_image == (Image *) NULL)
4369     {
4370       dodge_image=DestroyImage(dodge_image);
4371       return((Image *) NULL);
4372     }
4373   (void) CompositeImage(sketch_image,ColorDodgeCompositeOp,dodge_image,0,0);
4374   dodge_image=DestroyImage(dodge_image);
4375   blend_image=CloneImage(image,0,0,MagickTrue,exception);
4376   if (blend_image == (Image *) NULL)
4377     {
4378       sketch_image=DestroyImage(sketch_image);
4379       return((Image *) NULL);
4380     }
4381   (void) SetImageArtifact(blend_image,"compose:args","20x80");
4382   (void) CompositeImage(sketch_image,BlendCompositeOp,blend_image,0,0);
4383   blend_image=DestroyImage(blend_image);
4384   return(sketch_image);
4385 }
4386 \f
4387 /*
4388 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4389 %                                                                             %
4390 %                                                                             %
4391 %                                                                             %
4392 %     S o l a r i z e I m a g e                                               %
4393 %                                                                             %
4394 %                                                                             %
4395 %                                                                             %
4396 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4397 %
4398 %  SolarizeImage() applies a special effect to the image, similar to the effect
4399 %  achieved in a photo darkroom by selectively exposing areas of photo
4400 %  sensitive paper to light.  Threshold ranges from 0 to QuantumRange and is a
4401 %  measure of the extent of the solarization.
4402 %
4403 %  The format of the SolarizeImage method is:
4404 %
4405 %      MagickBooleanType SolarizeImage(Image *image,const double threshold)
4406 %
4407 %  A description of each parameter follows:
4408 %
4409 %    o image: the image.
4410 %
4411 %    o threshold:  Define the extent of the solarization.
4412 %
4413 */
4414 MagickExport MagickBooleanType SolarizeImage(Image *image,
4415   const double threshold)
4416 {
4417 #define SolarizeImageTag  "Solarize/Image"
4418
4419   CacheView
4420     *image_view;
4421
4422   ExceptionInfo
4423     *exception;
4424
4425   MagickBooleanType
4426     status;
4427
4428   MagickOffsetType
4429     progress;
4430
4431   ssize_t
4432     y;
4433
4434   assert(image != (Image *) NULL);
4435   assert(image->signature == MagickSignature);
4436   if (image->debug != MagickFalse)
4437     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4438   if (image->storage_class == PseudoClass)
4439     {
4440       register ssize_t
4441         i;
4442
4443       /*
4444         Solarize colormap.
4445       */
4446       for (i=0; i < (ssize_t) image->colors; i++)
4447       {
4448         if ((MagickRealType) image->colormap[i].red > threshold)
4449           image->colormap[i].red=(Quantum) QuantumRange-image->colormap[i].red;
4450         if ((MagickRealType) image->colormap[i].green > threshold)
4451           image->colormap[i].green=(Quantum) QuantumRange-
4452             image->colormap[i].green;
4453         if ((MagickRealType) image->colormap[i].blue > threshold)
4454           image->colormap[i].blue=(Quantum) QuantumRange-
4455             image->colormap[i].blue;
4456       }
4457     }
4458   /*
4459     Solarize image.
4460   */
4461   status=MagickTrue;
4462   progress=0;
4463   exception=(&image->exception);
4464   image_view=AcquireCacheView(image);
4465 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4466   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4467 #endif
4468   for (y=0; y < (ssize_t) image->rows; y++)
4469   {
4470     register ssize_t
4471       x;
4472
4473     register PixelPacket
4474       *restrict q;
4475
4476     if (status == MagickFalse)
4477       continue;
4478     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
4479       exception);
4480     if (q == (PixelPacket *) NULL)
4481       {
4482         status=MagickFalse;
4483         continue;
4484       }
4485     for (x=0; x < (ssize_t) image->columns; x++)
4486     {
4487       if ((MagickRealType) q->red > threshold)
4488         q->red=(Quantum) QuantumRange-q->red;
4489       if ((MagickRealType) q->green > threshold)
4490         q->green=(Quantum) QuantumRange-q->green;
4491       if ((MagickRealType) q->blue > threshold)
4492         q->blue=(Quantum) QuantumRange-q->blue;
4493       q++;
4494     }
4495     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4496       status=MagickFalse;
4497     if (image->progress_monitor != (MagickProgressMonitor) NULL)
4498       {
4499         MagickBooleanType
4500           proceed;
4501
4502 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4503   #pragma omp critical (MagickCore_SolarizeImage)
4504 #endif
4505         proceed=SetImageProgress(image,SolarizeImageTag,progress++,image->rows);
4506         if (proceed == MagickFalse)
4507           status=MagickFalse;
4508       }
4509   }
4510   image_view=DestroyCacheView(image_view);
4511   return(status);
4512 }
4513 \f
4514 /*
4515 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4516 %                                                                             %
4517 %                                                                             %
4518 %                                                                             %
4519 %   S t e g a n o I m a g e                                                   %
4520 %                                                                             %
4521 %                                                                             %
4522 %                                                                             %
4523 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4524 %
4525 %  SteganoImage() hides a digital watermark within the image.  Recover
4526 %  the hidden watermark later to prove that the authenticity of an image.
4527 %  Offset defines the start position within the image to hide the watermark.
4528 %
4529 %  The format of the SteganoImage method is:
4530 %
4531 %      Image *SteganoImage(const Image *image,Image *watermark,
4532 %        ExceptionInfo *exception)
4533 %
4534 %  A description of each parameter follows:
4535 %
4536 %    o image: the image.
4537 %
4538 %    o watermark: the watermark image.
4539 %
4540 %    o exception: return any errors or warnings in this structure.
4541 %
4542 */
4543 MagickExport Image *SteganoImage(const Image *image,const Image *watermark,
4544   ExceptionInfo *exception)
4545 {
4546 #define GetBit(alpha,i) ((((size_t) (alpha) >> (size_t) (i)) & 0x01) != 0)
4547 #define SetBit(alpha,i,set) (alpha)=(Quantum) ((set) != 0 ? (size_t) (alpha) \
4548   | (one << (size_t) (i)) : (size_t) (alpha) & ~(one << (size_t) (i)))
4549 #define SteganoImageTag  "Stegano/Image"
4550
4551   CacheView
4552     *stegano_view,
4553     *watermark_view;
4554
4555   Image
4556     *stegano_image;
4557
4558   int
4559     c;
4560
4561   MagickBooleanType
4562     status;
4563
4564   PixelPacket
4565     pixel;
4566
4567   register PixelPacket
4568     *q;
4569
4570   register ssize_t
4571     x;
4572
4573   size_t
4574     depth,
4575     one;
4576
4577   ssize_t
4578     i,
4579     j,
4580     k,
4581     y;
4582
4583   /*
4584     Initialize steganographic image attributes.
4585   */
4586   assert(image != (const Image *) NULL);
4587   assert(image->signature == MagickSignature);
4588   if (image->debug != MagickFalse)
4589     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4590   assert(watermark != (const Image *) NULL);
4591   assert(watermark->signature == MagickSignature);
4592   assert(exception != (ExceptionInfo *) NULL);
4593   assert(exception->signature == MagickSignature);
4594   one=1UL;
4595   stegano_image=CloneImage(image,0,0,MagickTrue,exception);
4596   if (stegano_image == (Image *) NULL)
4597     return((Image *) NULL);
4598   if (SetImageStorageClass(stegano_image,DirectClass) == MagickFalse)
4599     {
4600       InheritException(exception,&stegano_image->exception);
4601       stegano_image=DestroyImage(stegano_image);
4602       return((Image *) NULL);
4603     }
4604   stegano_image->depth=MAGICKCORE_QUANTUM_DEPTH;
4605   /*
4606     Hide watermark in low-order bits of image.
4607   */
4608   c=0;
4609   i=0;
4610   j=0;
4611   depth=stegano_image->depth;
4612   k=image->offset;
4613   watermark_view=AcquireCacheView(watermark);
4614   stegano_view=AcquireCacheView(stegano_image);
4615   for (i=(ssize_t) depth-1; (i >= 0) && (j < (ssize_t) depth); i--)
4616   {
4617     for (y=0; (y < (ssize_t) watermark->rows) && (j < (ssize_t) depth); y++)
4618     {
4619       for (x=0; (x < (ssize_t) watermark->columns) && (j < (ssize_t) depth); x++)
4620       {
4621         (void) GetOneCacheViewVirtualPixel(watermark_view,x,y,&pixel,exception);
4622         if ((k/(ssize_t) stegano_image->columns) >= (ssize_t) stegano_image->rows)
4623           break;
4624         q=GetCacheViewAuthenticPixels(stegano_view,k % (ssize_t)
4625           stegano_image->columns,k/(ssize_t) stegano_image->columns,1,1,
4626           exception);
4627         if (q == (PixelPacket *) NULL)
4628           break;
4629         switch (c)
4630         {
4631           case 0:
4632           {
4633             SetBit(q->red,j,GetBit(PixelIntensityToQuantum(&pixel),i));
4634             break;
4635           }
4636           case 1:
4637           {
4638             SetBit(q->green,j,GetBit(PixelIntensityToQuantum(&pixel),i));
4639             break;
4640           }
4641           case 2:
4642           {
4643             SetBit(q->blue,j,GetBit(PixelIntensityToQuantum(&pixel),i));
4644             break;
4645           }
4646         }
4647         if (SyncCacheViewAuthenticPixels(stegano_view,exception) == MagickFalse)
4648           break;
4649         c++;
4650         if (c == 3)
4651           c=0;
4652         k++;
4653         if (k == (ssize_t) (stegano_image->columns*stegano_image->columns))
4654           k=0;
4655         if (k == image->offset)
4656           j++;
4657       }
4658     }
4659     if (image->progress_monitor != (MagickProgressMonitor) NULL)
4660       {
4661         MagickBooleanType
4662           proceed;
4663
4664         proceed=SetImageProgress(image,SteganoImageTag,(MagickOffsetType)
4665           (depth-i),depth);
4666         if (proceed == MagickFalse)
4667           status=MagickFalse;
4668       }
4669   }
4670   stegano_view=DestroyCacheView(stegano_view);
4671   watermark_view=DestroyCacheView(watermark_view);
4672   if (stegano_image->storage_class == PseudoClass)
4673     (void) SyncImage(stegano_image);
4674   return(stegano_image);
4675 }
4676 \f
4677 /*
4678 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4679 %                                                                             %
4680 %                                                                             %
4681 %                                                                             %
4682 %   S t e r e o A n a g l y p h I m a g e                                     %
4683 %                                                                             %
4684 %                                                                             %
4685 %                                                                             %
4686 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4687 %
4688 %  StereoAnaglyphImage() combines two images and produces a single image that
4689 %  is the composite of a left and right image of a stereo pair.  Special
4690 %  red-green stereo glasses are required to view this effect.
4691 %
4692 %  The format of the StereoAnaglyphImage method is:
4693 %
4694 %      Image *StereoImage(const Image *left_image,const Image *right_image,
4695 %        ExceptionInfo *exception)
4696 %      Image *StereoAnaglyphImage(const Image *left_image,
4697 %        const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
4698 %        ExceptionInfo *exception)
4699 %
4700 %  A description of each parameter follows:
4701 %
4702 %    o left_image: the left image.
4703 %
4704 %    o right_image: the right image.
4705 %
4706 %    o exception: return any errors or warnings in this structure.
4707 %
4708 %    o x_offset: amount, in pixels, by which the left image is offset to the
4709 %      right of the right image.
4710 %
4711 %    o y_offset: amount, in pixels, by which the left image is offset to the
4712 %      bottom of the right image.
4713 %
4714 %
4715 */
4716 MagickExport Image *StereoImage(const Image *left_image,
4717   const Image *right_image,ExceptionInfo *exception)
4718 {
4719   return(StereoAnaglyphImage(left_image,right_image,0,0,exception));
4720 }
4721
4722 MagickExport Image *StereoAnaglyphImage(const Image *left_image,
4723   const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
4724   ExceptionInfo *exception)
4725 {
4726 #define StereoImageTag  "Stereo/Image"
4727
4728   const Image
4729     *image;
4730
4731   Image
4732     *stereo_image;
4733
4734   ssize_t
4735     y;
4736
4737   MagickBooleanType
4738     status;
4739
4740   assert(left_image != (const Image *) NULL);
4741   assert(left_image->signature == MagickSignature);
4742   if (left_image->debug != MagickFalse)
4743     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4744       left_image->filename);
4745   assert(right_image != (const Image *) NULL);
4746   assert(right_image->signature == MagickSignature);
4747   assert(exception != (ExceptionInfo *) NULL);
4748   assert(exception->signature == MagickSignature);
4749   assert(right_image != (const Image *) NULL);
4750   image=left_image;
4751   if ((left_image->columns != right_image->columns) ||
4752       (left_image->rows != right_image->rows))
4753     ThrowImageException(ImageError,"LeftAndRightImageSizesDiffer");
4754   /*
4755     Initialize stereo image attributes.
4756   */
4757   stereo_image=CloneImage(left_image,left_image->columns,left_image->rows,
4758     MagickTrue,exception);
4759   if (stereo_image == (Image *) NULL)
4760     return((Image *) NULL);
4761   if (SetImageStorageClass(stereo_image,DirectClass) == MagickFalse)
4762     {
4763       InheritException(exception,&stereo_image->exception);
4764       stereo_image=DestroyImage(stereo_image);
4765       return((Image *) NULL);
4766     }
4767   /*
4768     Copy left image to red channel and right image to blue channel.
4769   */
4770   for (y=0; y < (ssize_t) stereo_image->rows; y++)
4771   {
4772     register const PixelPacket
4773       *restrict p,
4774       *restrict q;
4775
4776     register ssize_t
4777       x;
4778
4779     register PixelPacket
4780       *restrict r;
4781
4782     p=GetVirtualPixels(left_image,-x_offset,y-y_offset,image->columns,1,
4783       exception);
4784     q=GetVirtualPixels(right_image,0,y,right_image->columns,1,exception);
4785     r=QueueAuthenticPixels(stereo_image,0,y,stereo_image->columns,1,exception);
4786     if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL) ||
4787         (r == (PixelPacket *) NULL))
4788       break;
4789     for (x=0; x < (ssize_t) stereo_image->columns; x++)
4790     {
4791       r->red=GetRedPixelComponent(p);
4792       r->green=q->green;
4793       r->blue=q->blue;
4794       r->opacity=(Quantum) ((p->opacity+q->opacity)/2);
4795       p++;
4796       q++;
4797       r++;
4798     }
4799     if (SyncAuthenticPixels(stereo_image,exception) == MagickFalse)
4800       break;
4801     if (image->progress_monitor != (MagickProgressMonitor) NULL)
4802       {
4803         MagickBooleanType
4804           proceed;
4805
4806         proceed=SetImageProgress(image,StereoImageTag,(MagickOffsetType) y,
4807           stereo_image->rows);
4808         if (proceed == MagickFalse)
4809           status=MagickFalse;
4810       }
4811   }
4812   return(stereo_image);
4813 }
4814 \f
4815 /*
4816 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4817 %                                                                             %
4818 %                                                                             %
4819 %                                                                             %
4820 %     S w i r l I m a g e                                                     %
4821 %                                                                             %
4822 %                                                                             %
4823 %                                                                             %
4824 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4825 %
4826 %  SwirlImage() swirls the pixels about the center of the image, where
4827 %  degrees indicates the sweep of the arc through which each pixel is moved.
4828 %  You get a more dramatic effect as the degrees move from 1 to 360.
4829 %
4830 %  The format of the SwirlImage method is:
4831 %
4832 %      Image *SwirlImage(const Image *image,double degrees,
4833 %        ExceptionInfo *exception)
4834 %
4835 %  A description of each parameter follows:
4836 %
4837 %    o image: the image.
4838 %
4839 %    o degrees: Define the tightness of the swirling effect.
4840 %
4841 %    o exception: return any errors or warnings in this structure.
4842 %
4843 */
4844 MagickExport Image *SwirlImage(const Image *image,double degrees,
4845   ExceptionInfo *exception)
4846 {
4847 #define SwirlImageTag  "Swirl/Image"
4848
4849   CacheView
4850     *image_view,
4851     *swirl_view;
4852
4853   Image
4854     *swirl_image;
4855
4856   MagickBooleanType
4857     status;
4858
4859   MagickOffsetType
4860     progress;
4861
4862   MagickPixelPacket
4863     zero;
4864
4865   MagickRealType
4866     radius;
4867
4868   PointInfo
4869     center,
4870     scale;
4871
4872   ResampleFilter
4873     **restrict resample_filter;
4874
4875   ssize_t
4876     y;
4877
4878   /*
4879     Initialize swirl image attributes.
4880   */
4881   assert(image != (const Image *) NULL);
4882   assert(image->signature == MagickSignature);
4883   if (image->debug != MagickFalse)
4884     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4885   assert(exception != (ExceptionInfo *) NULL);
4886   assert(exception->signature == MagickSignature);
4887   swirl_image=CloneImage(image,0,0,MagickTrue,exception);
4888   if (swirl_image == (Image *) NULL)
4889     return((Image *) NULL);
4890   if (SetImageStorageClass(swirl_image,DirectClass) == MagickFalse)
4891     {
4892       InheritException(exception,&swirl_image->exception);
4893       swirl_image=DestroyImage(swirl_image);
4894       return((Image *) NULL);
4895     }
4896   if (swirl_image->background_color.opacity != OpaqueOpacity)
4897     swirl_image->matte=MagickTrue;
4898   /*
4899     Compute scaling factor.
4900   */
4901   center.x=(double) image->columns/2.0;
4902   center.y=(double) image->rows/2.0;
4903   radius=MagickMax(center.x,center.y);
4904   scale.x=1.0;
4905   scale.y=1.0;
4906   if (image->columns > image->rows)
4907     scale.y=(double) image->columns/(double) image->rows;
4908   else
4909     if (image->columns < image->rows)
4910       scale.x=(double) image->rows/(double) image->columns;
4911   degrees=(double) DegreesToRadians(degrees);
4912   /*
4913     Swirl image.
4914   */
4915   status=MagickTrue;
4916   progress=0;
4917   GetMagickPixelPacket(swirl_image,&zero);
4918   resample_filter=AcquireResampleFilterThreadSet(image,
4919     UndefinedVirtualPixelMethod,MagickTrue,exception);
4920   image_view=AcquireCacheView(image);
4921   swirl_view=AcquireCacheView(swirl_image);
4922 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4923   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4924 #endif
4925   for (y=0; y < (ssize_t) image->rows; y++)
4926   {
4927     const int
4928       id = GetOpenMPThreadId();
4929
4930     MagickPixelPacket
4931       pixel;
4932
4933     MagickRealType
4934       distance;
4935
4936     PointInfo
4937       delta;
4938
4939     register IndexPacket
4940       *restrict swirl_indexes;
4941
4942     register ssize_t
4943       x;
4944
4945     register PixelPacket
4946       *restrict q;
4947
4948     if (status == MagickFalse)
4949       continue;
4950     q=GetCacheViewAuthenticPixels(swirl_view,0,y,swirl_image->columns,1,
4951       exception);
4952     if (q == (PixelPacket *) NULL)
4953       {
4954         status=MagickFalse;
4955         continue;
4956       }
4957     swirl_indexes=GetCacheViewAuthenticIndexQueue(swirl_view);
4958     delta.y=scale.y*(double) (y-center.y);
4959     pixel=zero;
4960     for (x=0; x < (ssize_t) image->columns; x++)
4961     {
4962       /*
4963         Determine if the pixel is within an ellipse.
4964       */
4965       delta.x=scale.x*(double) (x-center.x);
4966       distance=delta.x*delta.x+delta.y*delta.y;
4967       if (distance < (radius*radius))
4968         {
4969           MagickRealType
4970             cosine,
4971             factor,
4972             sine;
4973
4974           /*
4975             Swirl the pixel.
4976           */
4977           factor=1.0-sqrt((double) distance)/radius;
4978           sine=sin((double) (degrees*factor*factor));
4979           cosine=cos((double) (degrees*factor*factor));
4980           (void) ResamplePixelColor(resample_filter[id],(double) ((cosine*
4981             delta.x-sine*delta.y)/scale.x+center.x),(double) ((sine*delta.x+
4982             cosine*delta.y)/scale.y+center.y),&pixel);
4983           SetPixelPacket(swirl_image,&pixel,q,swirl_indexes+x);
4984         }
4985       q++;
4986     }
4987     if (SyncCacheViewAuthenticPixels(swirl_view,exception) == MagickFalse)
4988       status=MagickFalse;
4989     if (image->progress_monitor != (MagickProgressMonitor) NULL)
4990       {
4991         MagickBooleanType
4992           proceed;
4993
4994 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4995   #pragma omp critical (MagickCore_SwirlImage)
4996 #endif
4997         proceed=SetImageProgress(image,SwirlImageTag,progress++,image->rows);
4998         if (proceed == MagickFalse)
4999           status=MagickFalse;
5000       }
5001   }
5002   swirl_view=DestroyCacheView(swirl_view);
5003   image_view=DestroyCacheView(image_view);
5004   resample_filter=DestroyResampleFilterThreadSet(resample_filter);
5005   if (status == MagickFalse)
5006     swirl_image=DestroyImage(swirl_image);
5007   return(swirl_image);
5008 }
5009 \f
5010 /*
5011 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5012 %                                                                             %
5013 %                                                                             %
5014 %                                                                             %
5015 %     T i n t I m a g e                                                       %
5016 %                                                                             %
5017 %                                                                             %
5018 %                                                                             %
5019 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5020 %
5021 %  TintImage() applies a color vector to each pixel in the image.  The length
5022 %  of the vector is 0 for black and white and at its maximum for the midtones.
5023 %  The vector weighting function is f(x)=(1-(4.0*((x-0.5)*(x-0.5))))
5024 %
5025 %  The format of the TintImage method is:
5026 %
5027 %      Image *TintImage(const Image *image,const char *opacity,
5028 %        const PixelPacket tint,ExceptionInfo *exception)
5029 %
5030 %  A description of each parameter follows:
5031 %
5032 %    o image: the image.
5033 %
5034 %    o opacity: A color value used for tinting.
5035 %
5036 %    o tint: A color value used for tinting.
5037 %
5038 %    o exception: return any errors or warnings in this structure.
5039 %
5040 */
5041 MagickExport Image *TintImage(const Image *image,const char *opacity,
5042   const PixelPacket tint,ExceptionInfo *exception)
5043 {
5044 #define TintImageTag  "Tint/Image"
5045
5046   CacheView
5047     *image_view,
5048     *tint_view;
5049
5050   GeometryInfo
5051     geometry_info;
5052
5053   Image
5054     *tint_image;
5055
5056   MagickBooleanType
5057     status;
5058
5059   MagickOffsetType
5060     progress;
5061
5062   MagickPixelPacket
5063     color_vector,
5064     pixel;
5065
5066   MagickStatusType
5067     flags;
5068
5069   ssize_t
5070     y;
5071
5072   /*
5073     Allocate tint image.
5074   */
5075   assert(image != (const Image *) NULL);
5076   assert(image->signature == MagickSignature);
5077   if (image->debug != MagickFalse)
5078     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5079   assert(exception != (ExceptionInfo *) NULL);
5080   assert(exception->signature == MagickSignature);
5081   tint_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
5082   if (tint_image == (Image *) NULL)
5083     return((Image *) NULL);
5084   if (SetImageStorageClass(tint_image,DirectClass) == MagickFalse)
5085     {
5086       InheritException(exception,&tint_image->exception);
5087       tint_image=DestroyImage(tint_image);
5088       return((Image *) NULL);
5089     }
5090   if (opacity == (const char *) NULL)
5091     return(tint_image);
5092   /*
5093     Determine RGB values of the color.
5094   */
5095   flags=ParseGeometry(opacity,&geometry_info);
5096   pixel.red=geometry_info.rho;
5097   if ((flags & SigmaValue) != 0)
5098     pixel.green=geometry_info.sigma;
5099   else
5100     pixel.green=pixel.red;
5101   if ((flags & XiValue) != 0)
5102     pixel.blue=geometry_info.xi;
5103   else
5104     pixel.blue=pixel.red;
5105   if ((flags & PsiValue) != 0)
5106     pixel.opacity=geometry_info.psi;
5107   else
5108     pixel.opacity=(MagickRealType) OpaqueOpacity;
5109   color_vector.red=(MagickRealType) (pixel.red*tint.red/100.0-
5110     PixelIntensity(&tint));
5111   color_vector.green=(MagickRealType) (pixel.green*tint.green/100.0-
5112     PixelIntensity(&tint));
5113   color_vector.blue=(MagickRealType) (pixel.blue*tint.blue/100.0-
5114     PixelIntensity(&tint));
5115   /*
5116     Tint image.
5117   */
5118   status=MagickTrue;
5119   progress=0;
5120   image_view=AcquireCacheView(image);
5121   tint_view=AcquireCacheView(tint_image);
5122 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5123   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
5124 #endif
5125   for (y=0; y < (ssize_t) image->rows; y++)
5126   {
5127     register const PixelPacket
5128       *restrict p;
5129
5130     register ssize_t
5131       x;
5132
5133     register PixelPacket
5134       *restrict q;
5135
5136     if (status == MagickFalse)
5137       continue;
5138     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
5139     q=QueueCacheViewAuthenticPixels(tint_view,0,y,tint_image->columns,1,
5140       exception);
5141     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
5142       {
5143         status=MagickFalse;
5144         continue;
5145       }
5146     for (x=0; x < (ssize_t) image->columns; x++)
5147     {
5148       MagickPixelPacket
5149         pixel;
5150
5151       MagickRealType
5152         weight;
5153
5154       weight=QuantumScale*p->red-0.5;
5155       pixel.red=(MagickRealType) p->red+color_vector.red*(1.0-(4.0*
5156         (weight*weight)));
5157       SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
5158       weight=QuantumScale*p->green-0.5;
5159       pixel.green=(MagickRealType) p->green+color_vector.green*(1.0-(4.0*
5160         (weight*weight)));
5161       SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
5162       weight=QuantumScale*p->blue-0.5;
5163       pixel.blue=(MagickRealType) p->blue+color_vector.blue*(1.0-(4.0*
5164         (weight*weight)));
5165       SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
5166       SetOpacityPixelComponent(q,GetOpacityPixelComponent(p));
5167       p++;
5168       q++;
5169     }
5170     if (SyncCacheViewAuthenticPixels(tint_view,exception) == MagickFalse)
5171       status=MagickFalse;
5172     if (image->progress_monitor != (MagickProgressMonitor) NULL)
5173       {
5174         MagickBooleanType
5175           proceed;
5176
5177 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5178   #pragma omp critical (MagickCore_TintImage)
5179 #endif
5180         proceed=SetImageProgress(image,TintImageTag,progress++,image->rows);
5181         if (proceed == MagickFalse)
5182           status=MagickFalse;
5183       }
5184   }
5185   tint_view=DestroyCacheView(tint_view);
5186   image_view=DestroyCacheView(image_view);
5187   if (status == MagickFalse)
5188     tint_image=DestroyImage(tint_image);
5189   return(tint_image);
5190 }
5191 \f
5192 /*
5193 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5194 %                                                                             %
5195 %                                                                             %
5196 %                                                                             %
5197 %     V i g n e t t e I m a g e                                               %
5198 %                                                                             %
5199 %                                                                             %
5200 %                                                                             %
5201 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5202 %
5203 %  VignetteImage() softens the edges of the image in vignette style.
5204 %
5205 %  The format of the VignetteImage method is:
5206 %
5207 %      Image *VignetteImage(const Image *image,const double radius,
5208 %        const double sigma,const ssize_t x,const ssize_t y,ExceptionInfo *exception)
5209 %
5210 %  A description of each parameter follows:
5211 %
5212 %    o image: the image.
5213 %
5214 %    o radius: the radius of the pixel neighborhood.
5215 %
5216 %    o sigma: the standard deviation of the Gaussian, in pixels.
5217 %
5218 %    o x, y:  Define the x and y ellipse offset.
5219 %
5220 %    o exception: return any errors or warnings in this structure.
5221 %
5222 */
5223 MagickExport Image *VignetteImage(const Image *image,const double radius,
5224   const double sigma,const ssize_t x,const ssize_t y,ExceptionInfo *exception)
5225 {
5226   char
5227     ellipse[MaxTextExtent];
5228
5229   DrawInfo
5230     *draw_info;
5231
5232   Image
5233     *canvas_image,
5234     *blur_image,
5235     *oval_image,
5236     *vignette_image;
5237
5238   assert(image != (Image *) NULL);
5239   assert(image->signature == MagickSignature);
5240   if (image->debug != MagickFalse)
5241     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5242   assert(exception != (ExceptionInfo *) NULL);
5243   assert(exception->signature == MagickSignature);
5244   canvas_image=CloneImage(image,0,0,MagickTrue,exception);
5245   if (canvas_image == (Image *) NULL)
5246     return((Image *) NULL);
5247   if (SetImageStorageClass(canvas_image,DirectClass) == MagickFalse)
5248     {
5249       InheritException(exception,&canvas_image->exception);
5250       canvas_image=DestroyImage(canvas_image);
5251       return((Image *) NULL);
5252     }
5253   canvas_image->matte=MagickTrue;
5254   oval_image=CloneImage(canvas_image,canvas_image->columns,
5255     canvas_image->rows,MagickTrue,exception);
5256   if (oval_image == (Image *) NULL)
5257     {
5258       canvas_image=DestroyImage(canvas_image);
5259       return((Image *) NULL);
5260     }
5261   (void) QueryColorDatabase("#000000",&oval_image->background_color,exception);
5262   (void) SetImageBackgroundColor(oval_image);
5263   draw_info=CloneDrawInfo((const ImageInfo *) NULL,(const DrawInfo *) NULL);
5264   (void) QueryColorDatabase("#ffffff",&draw_info->fill,exception);
5265   (void) QueryColorDatabase("#ffffff",&draw_info->stroke,exception);
5266   (void) FormatMagickString(ellipse,MaxTextExtent,
5267     "ellipse %g,%g,%g,%g,0.0,360.0",image->columns/2.0,
5268     image->rows/2.0,image->columns/2.0-x,image->rows/2.0-y);
5269   draw_info->primitive=AcquireString(ellipse);
5270   (void) DrawImage(oval_image,draw_info);
5271   draw_info=DestroyDrawInfo(draw_info);
5272   blur_image=BlurImage(oval_image,radius,sigma,exception);
5273   oval_image=DestroyImage(oval_image);
5274   if (blur_image == (Image *) NULL)
5275     {
5276       canvas_image=DestroyImage(canvas_image);
5277       return((Image *) NULL);
5278     }
5279   blur_image->matte=MagickFalse;
5280   (void) CompositeImage(canvas_image,CopyOpacityCompositeOp,blur_image,0,0);
5281   blur_image=DestroyImage(blur_image);
5282   vignette_image=MergeImageLayers(canvas_image,FlattenLayer,exception);
5283   canvas_image=DestroyImage(canvas_image);
5284   return(vignette_image);
5285 }
5286 \f
5287 /*
5288 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5289 %                                                                             %
5290 %                                                                             %
5291 %                                                                             %
5292 %     W a v e I m a g e                                                       %
5293 %                                                                             %
5294 %                                                                             %
5295 %                                                                             %
5296 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5297 %
5298 %  WaveImage() creates a "ripple" effect in the image by shifting the pixels
5299 %  vertically along a sine wave whose amplitude and wavelength is specified
5300 %  by the given parameters.
5301 %
5302 %  The format of the WaveImage method is:
5303 %
5304 %      Image *WaveImage(const Image *image,const double amplitude,
5305 %        const double wave_length,ExceptionInfo *exception)
5306 %
5307 %  A description of each parameter follows:
5308 %
5309 %    o image: the image.
5310 %
5311 %    o amplitude, wave_length:  Define the amplitude and wave length of the
5312 %      sine wave.
5313 %
5314 %    o exception: return any errors or warnings in this structure.
5315 %
5316 */
5317 MagickExport Image *WaveImage(const Image *image,const double amplitude,
5318   const double wave_length,ExceptionInfo *exception)
5319 {
5320 #define WaveImageTag  "Wave/Image"
5321
5322   CacheView
5323     *wave_view;
5324
5325   Image
5326     *wave_image;
5327
5328   MagickBooleanType
5329     status;
5330
5331   MagickOffsetType
5332     progress;
5333
5334   MagickPixelPacket
5335     zero;
5336
5337   MagickRealType
5338     *sine_map;
5339
5340   register ssize_t
5341     i;
5342
5343   ResampleFilter
5344     **restrict resample_filter;
5345
5346   ssize_t
5347     y;
5348
5349   /*
5350     Initialize wave image attributes.
5351   */
5352   assert(image != (Image *) NULL);
5353   assert(image->signature == MagickSignature);
5354   if (image->debug != MagickFalse)
5355     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5356   assert(exception != (ExceptionInfo *) NULL);
5357   assert(exception->signature == MagickSignature);
5358   wave_image=CloneImage(image,image->columns,(size_t) (image->rows+2.0*
5359     fabs(amplitude)),MagickTrue,exception);
5360   if (wave_image == (Image *) NULL)
5361     return((Image *) NULL);
5362   if (SetImageStorageClass(wave_image,DirectClass) == MagickFalse)
5363     {
5364       InheritException(exception,&wave_image->exception);
5365       wave_image=DestroyImage(wave_image);
5366       return((Image *) NULL);
5367     }
5368   if (wave_image->background_color.opacity != OpaqueOpacity)
5369     wave_image->matte=MagickTrue;
5370   /*
5371     Allocate sine map.
5372   */
5373   sine_map=(MagickRealType *) AcquireQuantumMemory((size_t) wave_image->columns,
5374     sizeof(*sine_map));
5375   if (sine_map == (MagickRealType *) NULL)
5376     {
5377       wave_image=DestroyImage(wave_image);
5378       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
5379     }
5380   for (i=0; i < (ssize_t) wave_image->columns; i++)
5381     sine_map[i]=fabs(amplitude)+amplitude*sin((double) ((2.0*MagickPI*i)/
5382       wave_length));
5383   /*
5384     Wave image.
5385   */
5386   status=MagickTrue;
5387   progress=0;
5388   GetMagickPixelPacket(wave_image,&zero);
5389   resample_filter=AcquireResampleFilterThreadSet(image,
5390     BackgroundVirtualPixelMethod,MagickTrue,exception);
5391   wave_view=AcquireCacheView(wave_image);
5392 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5393   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
5394 #endif
5395   for (y=0; y < (ssize_t) wave_image->rows; y++)
5396   {
5397     const int
5398       id = GetOpenMPThreadId();
5399
5400     MagickPixelPacket
5401       pixel;
5402
5403     register IndexPacket
5404       *restrict indexes;
5405
5406     register ssize_t
5407       x;
5408
5409     register PixelPacket
5410       *restrict q;
5411
5412     if (status == MagickFalse)
5413       continue;
5414     q=QueueCacheViewAuthenticPixels(wave_view,0,y,wave_image->columns,1,
5415       exception);
5416     if (q == (PixelPacket *) NULL)
5417       {
5418         status=MagickFalse;
5419         continue;
5420       }
5421     indexes=GetCacheViewAuthenticIndexQueue(wave_view);
5422     pixel=zero;
5423     for (x=0; x < (ssize_t) wave_image->columns; x++)
5424     {
5425       (void) ResamplePixelColor(resample_filter[id],(double) x,(double) (y-
5426         sine_map[x]),&pixel);
5427       SetPixelPacket(wave_image,&pixel,q,indexes+x);
5428       q++;
5429     }
5430     if (SyncCacheViewAuthenticPixels(wave_view,exception) == MagickFalse)
5431       status=MagickFalse;
5432     if (image->progress_monitor != (MagickProgressMonitor) NULL)
5433       {
5434         MagickBooleanType
5435           proceed;
5436
5437 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5438   #pragma omp critical (MagickCore_WaveImage)
5439 #endif
5440         proceed=SetImageProgress(image,WaveImageTag,progress++,image->rows);
5441         if (proceed == MagickFalse)
5442           status=MagickFalse;
5443       }
5444   }
5445   wave_view=DestroyCacheView(wave_view);
5446   resample_filter=DestroyResampleFilterThreadSet(resample_filter);
5447   sine_map=(MagickRealType *) RelinquishMagickMemory(sine_map);
5448   if (status == MagickFalse)
5449     wave_image=DestroyImage(wave_image);
5450   return(wave_image);
5451 }