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