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