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