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