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