]> 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   ChannelType
4132     channel_mask;
4133
4134   Image
4135     *border_image,
4136     *clone_image,
4137     *shadow_image;
4138
4139   MagickBooleanType
4140     status;
4141
4142   MagickOffsetType
4143     progress;
4144
4145   RectangleInfo
4146     border_info;
4147
4148   ssize_t
4149     y;
4150
4151   assert(image != (Image *) NULL);
4152   assert(image->signature == MagickSignature);
4153   if (image->debug != MagickFalse)
4154     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4155   assert(exception != (ExceptionInfo *) NULL);
4156   assert(exception->signature == MagickSignature);
4157   clone_image=CloneImage(image,0,0,MagickTrue,exception);
4158   if (clone_image == (Image *) NULL)
4159     return((Image *) NULL);
4160   (void) SetImageVirtualPixelMethod(clone_image,EdgeVirtualPixelMethod);
4161   clone_image->compose=OverCompositeOp;
4162   border_info.width=(size_t) floor(2.0*sigma+0.5);
4163   border_info.height=(size_t) floor(2.0*sigma+0.5);
4164   border_info.x=0;
4165   border_info.y=0;
4166   (void) QueryColorDatabase("none",&clone_image->border_color,exception);
4167   border_image=BorderImage(clone_image,&border_info,exception);
4168   clone_image=DestroyImage(clone_image);
4169   if (border_image == (Image *) NULL)
4170     return((Image *) NULL);
4171   if (border_image->matte == MagickFalse)
4172     (void) SetImageAlphaChannel(border_image,OpaqueAlphaChannel,exception);
4173   /*
4174     Shadow image.
4175   */
4176   status=MagickTrue;
4177   progress=0;
4178   border_view=AcquireCacheView(border_image);
4179 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4180   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4181 #endif
4182   for (y=0; y < (ssize_t) border_image->rows; y++)
4183   {
4184     register ssize_t
4185       x;
4186
4187     register Quantum
4188       *restrict q;
4189
4190     if (status == MagickFalse)
4191       continue;
4192     q=GetCacheViewAuthenticPixels(border_view,0,y,border_image->columns,1,
4193       exception);
4194     if (q == (const Quantum *) NULL)
4195       {
4196         status=MagickFalse;
4197         continue;
4198       }
4199     for (x=0; x < (ssize_t) border_image->columns; x++)
4200     {
4201       SetPixelRed(border_image,border_image->background_color.red,q);
4202       SetPixelGreen(border_image,border_image->background_color.green,q);
4203       SetPixelBlue(border_image,border_image->background_color.blue,q);
4204       if (border_image->matte == MagickFalse)
4205         SetPixelAlpha(border_image,border_image->background_color.alpha,q);
4206       else
4207         SetPixelAlpha(border_image,ClampToQuantum((MagickRealType)
4208           (GetPixelAlpha(border_image,q)*opacity/100.0)),q);
4209       q+=GetPixelChannels(border_image);
4210     }
4211     if (SyncCacheViewAuthenticPixels(border_view,exception) == MagickFalse)
4212       status=MagickFalse;
4213     if (border_image->progress_monitor != (MagickProgressMonitor) NULL)
4214       {
4215         MagickBooleanType
4216           proceed;
4217
4218 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4219   #pragma omp critical (MagickCore_ShadowImage)
4220 #endif
4221         proceed=SetImageProgress(border_image,ShadowImageTag,progress++,
4222           border_image->rows);
4223         if (proceed == MagickFalse)
4224           status=MagickFalse;
4225       }
4226   }
4227   border_view=DestroyCacheView(border_view);
4228   channel_mask=SetPixelChannelMask(border_image,AlphaChannel);
4229   shadow_image=BlurImage(border_image,0.0,sigma,exception);
4230   (void) SetPixelChannelMap(border_image,channel_mask);
4231   border_image=DestroyImage(border_image);
4232   if (shadow_image == (Image *) NULL)
4233     return((Image *) NULL);
4234   if (shadow_image->page.width == 0)
4235     shadow_image->page.width=shadow_image->columns;
4236   if (shadow_image->page.height == 0)
4237     shadow_image->page.height=shadow_image->rows;
4238   shadow_image->page.width+=x_offset-(ssize_t) border_info.width;
4239   shadow_image->page.height+=y_offset-(ssize_t) border_info.height;
4240   shadow_image->page.x+=x_offset-(ssize_t) border_info.width;
4241   shadow_image->page.y+=y_offset-(ssize_t) border_info.height;
4242   return(shadow_image);
4243 }
4244 \f
4245 /*
4246 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4247 %                                                                             %
4248 %                                                                             %
4249 %                                                                             %
4250 %     S k e t c h I m a g e                                                   %
4251 %                                                                             %
4252 %                                                                             %
4253 %                                                                             %
4254 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4255 %
4256 %  SketchImage() simulates a pencil sketch.  We convolve the image with a
4257 %  Gaussian operator of the given radius and standard deviation (sigma).  For
4258 %  reasonable results, radius should be larger than sigma.  Use a radius of 0
4259 %  and SketchImage() selects a suitable radius for you.  Angle gives the angle
4260 %  of the sketch.
4261 %
4262 %  The format of the SketchImage method is:
4263 %
4264 %    Image *SketchImage(const Image *image,const double radius,
4265 %      const double sigma,const double angle,ExceptionInfo *exception)
4266 %
4267 %  A description of each parameter follows:
4268 %
4269 %    o image: the image.
4270 %
4271 %    o radius: the radius of the Gaussian, in pixels, not counting the
4272 %      center pixel.
4273 %
4274 %    o sigma: the standard deviation of the Gaussian, in pixels.
4275 %
4276 %    o angle: Apply the effect along this angle.
4277 %
4278 %    o exception: return any errors or warnings in this structure.
4279 %
4280 */
4281 MagickExport Image *SketchImage(const Image *image,const double radius,
4282   const double sigma,const double angle,ExceptionInfo *exception)
4283 {
4284   CacheView
4285     *random_view;
4286
4287   Image
4288     *blend_image,
4289     *blur_image,
4290     *dodge_image,
4291     *random_image,
4292     *sketch_image;
4293
4294   MagickBooleanType
4295     status;
4296
4297   PixelInfo
4298     zero;
4299
4300   RandomInfo
4301     **restrict random_info;
4302
4303   ssize_t
4304     y;
4305
4306   /*
4307     Sketch image.
4308   */
4309   random_image=CloneImage(image,image->columns << 1,image->rows << 1,
4310     MagickTrue,exception);
4311   if (random_image == (Image *) NULL)
4312     return((Image *) NULL);
4313   status=MagickTrue;
4314   GetPixelInfo(random_image,&zero);
4315   random_info=AcquireRandomInfoThreadSet();
4316   random_view=AcquireCacheView(random_image);
4317 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4318   #pragma omp parallel for schedule(dynamic,4) shared(status)
4319 #endif
4320   for (y=0; y < (ssize_t) random_image->rows; y++)
4321   {
4322     const int
4323       id = GetOpenMPThreadId();
4324
4325     PixelInfo
4326       pixel;
4327
4328     register ssize_t
4329       x;
4330
4331     register Quantum
4332       *restrict q;
4333
4334     if (status == MagickFalse)
4335       continue;
4336     q=QueueCacheViewAuthenticPixels(random_view,0,y,random_image->columns,1,
4337       exception);
4338     if (q == (const Quantum *) NULL)
4339       {
4340         status=MagickFalse;
4341         continue;
4342       }
4343     pixel=zero;
4344     for (x=0; x < (ssize_t) random_image->columns; x++)
4345     {
4346       pixel.red=(MagickRealType) (QuantumRange*
4347         GetPseudoRandomValue(random_info[id]));
4348       pixel.green=pixel.red;
4349       pixel.blue=pixel.red;
4350       if (image->colorspace == CMYKColorspace)
4351         pixel.black=pixel.red;
4352       SetPixelPixelInfo(random_image,&pixel,q);
4353       q+=GetPixelChannels(random_image);
4354     }
4355     if (SyncCacheViewAuthenticPixels(random_view,exception) == MagickFalse)
4356       status=MagickFalse;
4357   }
4358   random_view=DestroyCacheView(random_view);
4359   random_info=DestroyRandomInfoThreadSet(random_info);
4360   if (status == MagickFalse)
4361     {
4362       random_image=DestroyImage(random_image);
4363       return(random_image);
4364     }
4365   blur_image=MotionBlurImage(random_image,radius,sigma,angle,exception);
4366   random_image=DestroyImage(random_image);
4367   if (blur_image == (Image *) NULL)
4368     return((Image *) NULL);
4369   dodge_image=EdgeImage(blur_image,radius,exception);
4370   blur_image=DestroyImage(blur_image);
4371   if (dodge_image == (Image *) NULL)
4372     return((Image *) NULL);
4373   (void) NormalizeImage(dodge_image,exception);
4374   (void) NegateImage(dodge_image,MagickFalse,exception);
4375   (void) TransformImage(&dodge_image,(char *) NULL,"50%");
4376   sketch_image=CloneImage(image,0,0,MagickTrue,exception);
4377   if (sketch_image == (Image *) NULL)
4378     {
4379       dodge_image=DestroyImage(dodge_image);
4380       return((Image *) NULL);
4381     }
4382   (void) CompositeImage(sketch_image,ColorDodgeCompositeOp,dodge_image,0,0);
4383   dodge_image=DestroyImage(dodge_image);
4384   blend_image=CloneImage(image,0,0,MagickTrue,exception);
4385   if (blend_image == (Image *) NULL)
4386     {
4387       sketch_image=DestroyImage(sketch_image);
4388       return((Image *) NULL);
4389     }
4390   (void) SetImageArtifact(blend_image,"compose:args","20x80");
4391   (void) CompositeImage(sketch_image,BlendCompositeOp,blend_image,0,0);
4392   blend_image=DestroyImage(blend_image);
4393   return(sketch_image);
4394 }
4395 \f
4396 /*
4397 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4398 %                                                                             %
4399 %                                                                             %
4400 %                                                                             %
4401 %     S o l a r i z e I m a g e                                               %
4402 %                                                                             %
4403 %                                                                             %
4404 %                                                                             %
4405 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4406 %
4407 %  SolarizeImage() applies a special effect to the image, similar to the effect
4408 %  achieved in a photo darkroom by selectively exposing areas of photo
4409 %  sensitive paper to light.  Threshold ranges from 0 to QuantumRange and is a
4410 %  measure of the extent of the solarization.
4411 %
4412 %  The format of the SolarizeImage method is:
4413 %
4414 %      MagickBooleanType SolarizeImage(Image *image,const double threshold)
4415 %
4416 %  A description of each parameter follows:
4417 %
4418 %    o image: the image.
4419 %
4420 %    o threshold:  Define the extent of the solarization.
4421 %
4422 */
4423 MagickExport MagickBooleanType SolarizeImage(Image *image,
4424   const double threshold)
4425 {
4426 #define SolarizeImageTag  "Solarize/Image"
4427
4428   CacheView
4429     *image_view;
4430
4431   ExceptionInfo
4432     *exception;
4433
4434   MagickBooleanType
4435     status;
4436
4437   MagickOffsetType
4438     progress;
4439
4440   ssize_t
4441     y;
4442
4443   assert(image != (Image *) NULL);
4444   assert(image->signature == MagickSignature);
4445   if (image->debug != MagickFalse)
4446     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4447   if (image->storage_class == PseudoClass)
4448     {
4449       register ssize_t
4450         i;
4451
4452       /*
4453         Solarize colormap.
4454       */
4455       for (i=0; i < (ssize_t) image->colors; i++)
4456       {
4457         if ((MagickRealType) image->colormap[i].red > threshold)
4458           image->colormap[i].red=(Quantum) QuantumRange-image->colormap[i].red;
4459         if ((MagickRealType) image->colormap[i].green > threshold)
4460           image->colormap[i].green=(Quantum) QuantumRange-
4461             image->colormap[i].green;
4462         if ((MagickRealType) image->colormap[i].blue > threshold)
4463           image->colormap[i].blue=(Quantum) QuantumRange-
4464             image->colormap[i].blue;
4465       }
4466     }
4467   /*
4468     Solarize image.
4469   */
4470   status=MagickTrue;
4471   progress=0;
4472   exception=(&image->exception);
4473   image_view=AcquireCacheView(image);
4474 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4475   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4476 #endif
4477   for (y=0; y < (ssize_t) image->rows; y++)
4478   {
4479     register ssize_t
4480       x;
4481
4482     register Quantum
4483       *restrict q;
4484
4485     if (status == MagickFalse)
4486       continue;
4487     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
4488       exception);
4489     if (q == (const Quantum *) NULL)
4490       {
4491         status=MagickFalse;
4492         continue;
4493       }
4494     for (x=0; x < (ssize_t) image->columns; x++)
4495     {
4496       if ((MagickRealType) GetPixelRed(image,q) > threshold)
4497         SetPixelRed(image,QuantumRange-GetPixelRed(image,q),q);
4498       if ((MagickRealType) GetPixelGreen(image,q) > threshold)
4499         SetPixelGreen(image,QuantumRange-GetPixelGreen(image,q),q);
4500       if ((MagickRealType) GetPixelBlue(image,q) > threshold)
4501         SetPixelBlue(image,QuantumRange-GetPixelBlue(image,q),q);
4502       q+=GetPixelChannels(image);
4503     }
4504     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4505       status=MagickFalse;
4506     if (image->progress_monitor != (MagickProgressMonitor) NULL)
4507       {
4508         MagickBooleanType
4509           proceed;
4510
4511 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4512   #pragma omp critical (MagickCore_SolarizeImage)
4513 #endif
4514         proceed=SetImageProgress(image,SolarizeImageTag,progress++,image->rows);
4515         if (proceed == MagickFalse)
4516           status=MagickFalse;
4517       }
4518   }
4519   image_view=DestroyCacheView(image_view);
4520   return(status);
4521 }
4522 \f
4523 /*
4524 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4525 %                                                                             %
4526 %                                                                             %
4527 %                                                                             %
4528 %   S t e g a n o I m a g e                                                   %
4529 %                                                                             %
4530 %                                                                             %
4531 %                                                                             %
4532 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4533 %
4534 %  SteganoImage() hides a digital watermark within the image.  Recover
4535 %  the hidden watermark later to prove that the authenticity of an image.
4536 %  Offset defines the start position within the image to hide the watermark.
4537 %
4538 %  The format of the SteganoImage method is:
4539 %
4540 %      Image *SteganoImage(const Image *image,Image *watermark,
4541 %        ExceptionInfo *exception)
4542 %
4543 %  A description of each parameter follows:
4544 %
4545 %    o image: the image.
4546 %
4547 %    o watermark: the watermark image.
4548 %
4549 %    o exception: return any errors or warnings in this structure.
4550 %
4551 */
4552 MagickExport Image *SteganoImage(const Image *image,const Image *watermark,
4553   ExceptionInfo *exception)
4554 {
4555 #define GetBit(alpha,i) ((((size_t) (alpha) >> (size_t) (i)) & 0x01) != 0)
4556 #define SetBit(alpha,i,set) (Quantum) ((set) != 0 ? (size_t) (alpha) \
4557   | (one << (size_t) (i)) : (size_t) (alpha) & ~(one << (size_t) (i)))
4558 #define SteganoImageTag  "Stegano/Image"
4559
4560   CacheView
4561     *stegano_view,
4562     *watermark_view;
4563
4564   Image
4565     *stegano_image;
4566
4567   int
4568     c;
4569
4570   MagickBooleanType
4571     status;
4572
4573   PixelPacket
4574     pixel;
4575
4576   register Quantum
4577     *q;
4578
4579   register ssize_t
4580     x;
4581
4582   size_t
4583     depth,
4584     one;
4585
4586   ssize_t
4587     i,
4588     j,
4589     k,
4590     y;
4591
4592   /*
4593     Initialize steganographic image attributes.
4594   */
4595   assert(image != (const Image *) NULL);
4596   assert(image->signature == MagickSignature);
4597   if (image->debug != MagickFalse)
4598     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4599   assert(watermark != (const Image *) NULL);
4600   assert(watermark->signature == MagickSignature);
4601   assert(exception != (ExceptionInfo *) NULL);
4602   assert(exception->signature == MagickSignature);
4603   one=1UL;
4604   stegano_image=CloneImage(image,0,0,MagickTrue,exception);
4605   if (stegano_image == (Image *) NULL)
4606     return((Image *) NULL);
4607   if (SetImageStorageClass(stegano_image,DirectClass,exception) == MagickFalse)
4608     {
4609       stegano_image=DestroyImage(stegano_image);
4610       return((Image *) NULL);
4611     }
4612   stegano_image->depth=MAGICKCORE_QUANTUM_DEPTH;
4613   /*
4614     Hide watermark in low-order bits of image.
4615   */
4616   c=0;
4617   i=0;
4618   j=0;
4619   depth=stegano_image->depth;
4620   k=image->offset;
4621   status=MagickTrue;
4622   watermark_view=AcquireCacheView(watermark);
4623   stegano_view=AcquireCacheView(stegano_image);
4624   for (i=(ssize_t) depth-1; (i >= 0) && (j < (ssize_t) depth); i--)
4625   {
4626     for (y=0; (y < (ssize_t) watermark->rows) && (j < (ssize_t) depth); y++)
4627     {
4628       for (x=0; (x < (ssize_t) watermark->columns) && (j < (ssize_t) depth); x++)
4629       {
4630         (void) GetOneCacheViewVirtualPixel(watermark_view,x,y,&pixel,exception);
4631         if ((k/(ssize_t) stegano_image->columns) >= (ssize_t) stegano_image->rows)
4632           break;
4633         q=GetCacheViewAuthenticPixels(stegano_view,k % (ssize_t)
4634           stegano_image->columns,k/(ssize_t) stegano_image->columns,1,1,
4635           exception);
4636         if (q == (const Quantum *) NULL)
4637           break;
4638         switch (c)
4639         {
4640           case 0:
4641           {
4642             SetPixelRed(image,SetBit(GetPixelRed(image,q),j,GetBit(
4643               GetPixelPacketIntensity(&pixel),i)),q);
4644             break;
4645           }
4646           case 1:
4647           {
4648             SetPixelGreen(image,SetBit(GetPixelGreen(image,q),j,GetBit(
4649               GetPixelPacketIntensity(&pixel),i)),q);
4650             break;
4651           }
4652           case 2:
4653           {
4654             SetPixelBlue(image,SetBit(GetPixelBlue(image,q),j,GetBit(
4655               GetPixelPacketIntensity(&pixel),i)),q);
4656             break;
4657           }
4658         }
4659         if (SyncCacheViewAuthenticPixels(stegano_view,exception) == MagickFalse)
4660           break;
4661         c++;
4662         if (c == 3)
4663           c=0;
4664         k++;
4665         if (k == (ssize_t) (stegano_image->columns*stegano_image->columns))
4666           k=0;
4667         if (k == image->offset)
4668           j++;
4669       }
4670     }
4671     if (image->progress_monitor != (MagickProgressMonitor) NULL)
4672       {
4673         MagickBooleanType
4674           proceed;
4675
4676         proceed=SetImageProgress(image,SteganoImageTag,(MagickOffsetType)
4677           (depth-i),depth);
4678         if (proceed == MagickFalse)
4679           status=MagickFalse;
4680       }
4681   }
4682   stegano_view=DestroyCacheView(stegano_view);
4683   watermark_view=DestroyCacheView(watermark_view);
4684   if (stegano_image->storage_class == PseudoClass)
4685     (void) SyncImage(stegano_image);
4686   if (status == MagickFalse)
4687     {
4688       stegano_image=DestroyImage(stegano_image);
4689       return((Image *) NULL);
4690     }
4691   return(stegano_image);
4692 }
4693 \f
4694 /*
4695 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4696 %                                                                             %
4697 %                                                                             %
4698 %                                                                             %
4699 %   S t e r e o A n a g l y p h I m a g e                                     %
4700 %                                                                             %
4701 %                                                                             %
4702 %                                                                             %
4703 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4704 %
4705 %  StereoAnaglyphImage() combines two images and produces a single image that
4706 %  is the composite of a left and right image of a stereo pair.  Special
4707 %  red-green stereo glasses are required to view this effect.
4708 %
4709 %  The format of the StereoAnaglyphImage method is:
4710 %
4711 %      Image *StereoImage(const Image *left_image,const Image *right_image,
4712 %        ExceptionInfo *exception)
4713 %      Image *StereoAnaglyphImage(const Image *left_image,
4714 %        const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
4715 %        ExceptionInfo *exception)
4716 %
4717 %  A description of each parameter follows:
4718 %
4719 %    o left_image: the left image.
4720 %
4721 %    o right_image: the right image.
4722 %
4723 %    o exception: return any errors or warnings in this structure.
4724 %
4725 %    o x_offset: amount, in pixels, by which the left image is offset to the
4726 %      right of the right image.
4727 %
4728 %    o y_offset: amount, in pixels, by which the left image is offset to the
4729 %      bottom of the right image.
4730 %
4731 %
4732 */
4733 MagickExport Image *StereoImage(const Image *left_image,
4734   const Image *right_image,ExceptionInfo *exception)
4735 {
4736   return(StereoAnaglyphImage(left_image,right_image,0,0,exception));
4737 }
4738
4739 MagickExport Image *StereoAnaglyphImage(const Image *left_image,
4740   const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
4741   ExceptionInfo *exception)
4742 {
4743 #define StereoImageTag  "Stereo/Image"
4744
4745   const Image
4746     *image;
4747
4748   Image
4749     *stereo_image;
4750
4751   MagickBooleanType
4752     status;
4753
4754   ssize_t
4755     y;
4756
4757   assert(left_image != (const Image *) NULL);
4758   assert(left_image->signature == MagickSignature);
4759   if (left_image->debug != MagickFalse)
4760     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4761       left_image->filename);
4762   assert(right_image != (const Image *) NULL);
4763   assert(right_image->signature == MagickSignature);
4764   assert(exception != (ExceptionInfo *) NULL);
4765   assert(exception->signature == MagickSignature);
4766   assert(right_image != (const Image *) NULL);
4767   image=left_image;
4768   if ((left_image->columns != right_image->columns) ||
4769       (left_image->rows != right_image->rows))
4770     ThrowImageException(ImageError,"LeftAndRightImageSizesDiffer");
4771   /*
4772     Initialize stereo image attributes.
4773   */
4774   stereo_image=CloneImage(left_image,left_image->columns,left_image->rows,
4775     MagickTrue,exception);
4776   if (stereo_image == (Image *) NULL)
4777     return((Image *) NULL);
4778   if (SetImageStorageClass(stereo_image,DirectClass,exception) == MagickFalse)
4779     {
4780       stereo_image=DestroyImage(stereo_image);
4781       return((Image *) NULL);
4782     }
4783   /*
4784     Copy left image to red channel and right image to blue channel.
4785   */
4786   status=MagickTrue;
4787   for (y=0; y < (ssize_t) stereo_image->rows; y++)
4788   {
4789     register const Quantum
4790       *restrict p,
4791       *restrict q;
4792
4793     register ssize_t
4794       x;
4795
4796     register Quantum
4797       *restrict r;
4798
4799     p=GetVirtualPixels(left_image,-x_offset,y-y_offset,image->columns,1,
4800       exception);
4801     q=GetVirtualPixels(right_image,0,y,right_image->columns,1,exception);
4802     r=QueueAuthenticPixels(stereo_image,0,y,stereo_image->columns,1,exception);
4803     if ((p == (const Quantum *) NULL) ||
4804         (q == (const Quantum *) NULL) || (r == (Quantum *) NULL))
4805       break;
4806     for (x=0; x < (ssize_t) stereo_image->columns; x++)
4807     {
4808       SetPixelRed(image,GetPixelRed(left_image,p),r);
4809       SetPixelGreen(image,GetPixelGreen(left_image,q),r);
4810       SetPixelBlue(image,GetPixelBlue(left_image,q),r);
4811       SetPixelAlpha(image,(GetPixelAlpha(left_image,p)+
4812         GetPixelAlpha(left_image,q))/2,r);
4813       p+=GetPixelChannels(left_image);
4814       q++;
4815       r++;
4816     }
4817     if (SyncAuthenticPixels(stereo_image,exception) == MagickFalse)
4818       break;
4819     if (image->progress_monitor != (MagickProgressMonitor) NULL)
4820       {
4821         MagickBooleanType
4822           proceed;
4823
4824         proceed=SetImageProgress(image,StereoImageTag,(MagickOffsetType) y,
4825           stereo_image->rows);
4826         if (proceed == MagickFalse)
4827           status=MagickFalse;
4828       }
4829   }
4830   if (status == MagickFalse)
4831     {
4832       stereo_image=DestroyImage(stereo_image);
4833       return((Image *) NULL);
4834     }
4835   return(stereo_image);
4836 }
4837 \f
4838 /*
4839 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4840 %                                                                             %
4841 %                                                                             %
4842 %                                                                             %
4843 %     S w i r l I m a g e                                                     %
4844 %                                                                             %
4845 %                                                                             %
4846 %                                                                             %
4847 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4848 %
4849 %  SwirlImage() swirls the pixels about the center of the image, where
4850 %  degrees indicates the sweep of the arc through which each pixel is moved.
4851 %  You get a more dramatic effect as the degrees move from 1 to 360.
4852 %
4853 %  The format of the SwirlImage method is:
4854 %
4855 %      Image *SwirlImage(const Image *image,double degrees,
4856 %        ExceptionInfo *exception)
4857 %
4858 %  A description of each parameter follows:
4859 %
4860 %    o image: the image.
4861 %
4862 %    o degrees: Define the tightness of the swirling effect.
4863 %
4864 %    o exception: return any errors or warnings in this structure.
4865 %
4866 */
4867 MagickExport Image *SwirlImage(const Image *image,double degrees,
4868   ExceptionInfo *exception)
4869 {
4870 #define SwirlImageTag  "Swirl/Image"
4871
4872   CacheView
4873     *image_view,
4874     *swirl_view;
4875
4876   Image
4877     *swirl_image;
4878
4879   MagickBooleanType
4880     status;
4881
4882   MagickOffsetType
4883     progress;
4884
4885   PixelInfo
4886     zero;
4887
4888   MagickRealType
4889     radius;
4890
4891   PointInfo
4892     center,
4893     scale;
4894
4895   ssize_t
4896     y;
4897
4898   /*
4899     Initialize swirl image attributes.
4900   */
4901   assert(image != (const Image *) NULL);
4902   assert(image->signature == MagickSignature);
4903   if (image->debug != MagickFalse)
4904     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4905   assert(exception != (ExceptionInfo *) NULL);
4906   assert(exception->signature == MagickSignature);
4907   swirl_image=CloneImage(image,0,0,MagickTrue,exception);
4908   if (swirl_image == (Image *) NULL)
4909     return((Image *) NULL);
4910   if (SetImageStorageClass(swirl_image,DirectClass,exception) == MagickFalse)
4911     {
4912       swirl_image=DestroyImage(swirl_image);
4913       return((Image *) NULL);
4914     }
4915   if (swirl_image->background_color.alpha != OpaqueAlpha)
4916     swirl_image->matte=MagickTrue;
4917   /*
4918     Compute scaling factor.
4919   */
4920   center.x=(double) image->columns/2.0;
4921   center.y=(double) image->rows/2.0;
4922   radius=MagickMax(center.x,center.y);
4923   scale.x=1.0;
4924   scale.y=1.0;
4925   if (image->columns > image->rows)
4926     scale.y=(double) image->columns/(double) image->rows;
4927   else
4928     if (image->columns < image->rows)
4929       scale.x=(double) image->rows/(double) image->columns;
4930   degrees=(double) DegreesToRadians(degrees);
4931   /*
4932     Swirl image.
4933   */
4934   status=MagickTrue;
4935   progress=0;
4936   GetPixelInfo(swirl_image,&zero);
4937   image_view=AcquireCacheView(image);
4938   swirl_view=AcquireCacheView(swirl_image);
4939 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4940   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4941 #endif
4942   for (y=0; y < (ssize_t) image->rows; y++)
4943   {
4944     PixelInfo
4945       pixel;
4946
4947     MagickRealType
4948       distance;
4949
4950     PointInfo
4951       delta;
4952
4953     register ssize_t
4954       x;
4955
4956     register Quantum
4957       *restrict q;
4958
4959     if (status == MagickFalse)
4960       continue;
4961     q=GetCacheViewAuthenticPixels(swirl_view,0,y,swirl_image->columns,1,
4962       exception);
4963     if (q == (const Quantum *) NULL)
4964       {
4965         status=MagickFalse;
4966         continue;
4967       }
4968     delta.y=scale.y*(double) (y-center.y);
4969     pixel=zero;
4970     for (x=0; x < (ssize_t) image->columns; x++)
4971     {
4972       /*
4973         Determine if the pixel is within an ellipse.
4974       */
4975       delta.x=scale.x*(double) (x-center.x);
4976       distance=delta.x*delta.x+delta.y*delta.y;
4977       if (distance < (radius*radius))
4978         {
4979           MagickRealType
4980             cosine,
4981             factor,
4982             sine;
4983
4984           /*
4985             Swirl the pixel.
4986           */
4987           factor=1.0-sqrt((double) distance)/radius;
4988           sine=sin((double) (degrees*factor*factor));
4989           cosine=cos((double) (degrees*factor*factor));
4990           (void) InterpolatePixelInfo(image,image_view,
4991             UndefinedInterpolatePixel,(double) ((cosine*delta.x-sine*delta.y)/
4992             scale.x+center.x),(double) ((sine*delta.x+cosine*delta.y)/scale.y+
4993             center.y),&pixel,exception);
4994           SetPixelPixelInfo(swirl_image,&pixel,q);
4995         }
4996       q+=GetPixelChannels(swirl_image);
4997     }
4998     if (SyncCacheViewAuthenticPixels(swirl_view,exception) == MagickFalse)
4999       status=MagickFalse;
5000     if (image->progress_monitor != (MagickProgressMonitor) NULL)
5001       {
5002         MagickBooleanType
5003           proceed;
5004
5005 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5006   #pragma omp critical (MagickCore_SwirlImage)
5007 #endif
5008         proceed=SetImageProgress(image,SwirlImageTag,progress++,image->rows);
5009         if (proceed == MagickFalse)
5010           status=MagickFalse;
5011       }
5012   }
5013   swirl_view=DestroyCacheView(swirl_view);
5014   image_view=DestroyCacheView(image_view);
5015   if (status == MagickFalse)
5016     swirl_image=DestroyImage(swirl_image);
5017   return(swirl_image);
5018 }
5019 \f
5020 /*
5021 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5022 %                                                                             %
5023 %                                                                             %
5024 %                                                                             %
5025 %     T i n t I m a g e                                                       %
5026 %                                                                             %
5027 %                                                                             %
5028 %                                                                             %
5029 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5030 %
5031 %  TintImage() applies a color vector to each pixel in the image.  The length
5032 %  of the vector is 0 for black and white and at its maximum for the midtones.
5033 %  The vector weighting function is f(x)=(1-(4.0*((x-0.5)*(x-0.5))))
5034 %
5035 %  The format of the TintImage method is:
5036 %
5037 %      Image *TintImage(const Image *image,const char *opacity,
5038 %        const PixelPacket tint,ExceptionInfo *exception)
5039 %
5040 %  A description of each parameter follows:
5041 %
5042 %    o image: the image.
5043 %
5044 %    o opacity: A color value used for tinting.
5045 %
5046 %    o tint: A color value used for tinting.
5047 %
5048 %    o exception: return any errors or warnings in this structure.
5049 %
5050 */
5051 MagickExport Image *TintImage(const Image *image,const char *opacity,
5052   const PixelPacket tint,ExceptionInfo *exception)
5053 {
5054 #define TintImageTag  "Tint/Image"
5055
5056   CacheView
5057     *image_view,
5058     *tint_view;
5059
5060   GeometryInfo
5061     geometry_info;
5062
5063   Image
5064     *tint_image;
5065
5066   MagickBooleanType
5067     status;
5068
5069   MagickOffsetType
5070     progress;
5071
5072   PixelInfo
5073     color_vector,
5074     pixel;
5075
5076   MagickStatusType
5077     flags;
5078
5079   ssize_t
5080     y;
5081
5082   /*
5083     Allocate tint image.
5084   */
5085   assert(image != (const Image *) NULL);
5086   assert(image->signature == MagickSignature);
5087   if (image->debug != MagickFalse)
5088     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5089   assert(exception != (ExceptionInfo *) NULL);
5090   assert(exception->signature == MagickSignature);
5091   tint_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
5092   if (tint_image == (Image *) NULL)
5093     return((Image *) NULL);
5094   if (SetImageStorageClass(tint_image,DirectClass,exception) == MagickFalse)
5095     {
5096       tint_image=DestroyImage(tint_image);
5097       return((Image *) NULL);
5098     }
5099   if (opacity == (const char *) NULL)
5100     return(tint_image);
5101   /*
5102     Determine RGB values of the color.
5103   */
5104   flags=ParseGeometry(opacity,&geometry_info);
5105   pixel.red=geometry_info.rho;
5106   if ((flags & SigmaValue) != 0)
5107     pixel.green=geometry_info.sigma;
5108   else
5109     pixel.green=pixel.red;
5110   if ((flags & XiValue) != 0)
5111     pixel.blue=geometry_info.xi;
5112   else
5113     pixel.blue=pixel.red;
5114   if ((flags & PsiValue) != 0)
5115     pixel.alpha=geometry_info.psi;
5116   else
5117     pixel.alpha=(MagickRealType) OpaqueAlpha;
5118   color_vector.red=(MagickRealType) (pixel.red*tint.red/100.0-
5119     GetPixelPacketIntensity(&tint));
5120   color_vector.green=(MagickRealType) (pixel.green*tint.green/100.0-
5121     GetPixelPacketIntensity(&tint));
5122   color_vector.blue=(MagickRealType) (pixel.blue*tint.blue/100.0-
5123     GetPixelPacketIntensity(&tint));
5124   /*
5125     Tint image.
5126   */
5127   status=MagickTrue;
5128   progress=0;
5129   image_view=AcquireCacheView(image);
5130   tint_view=AcquireCacheView(tint_image);
5131 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5132   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
5133 #endif
5134   for (y=0; y < (ssize_t) image->rows; y++)
5135   {
5136     register const Quantum
5137       *restrict p;
5138
5139     register Quantum
5140       *restrict q;
5141
5142     register ssize_t
5143       x;
5144
5145     if (status == MagickFalse)
5146       continue;
5147     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
5148     q=QueueCacheViewAuthenticPixels(tint_view,0,y,tint_image->columns,1,
5149       exception);
5150     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
5151       {
5152         status=MagickFalse;
5153         continue;
5154       }
5155     for (x=0; x < (ssize_t) image->columns; x++)
5156     {
5157       PixelInfo
5158         pixel;
5159
5160       MagickRealType
5161         weight;
5162
5163       weight=QuantumScale*GetPixelRed(image,p)-0.5;
5164       pixel.red=(MagickRealType) GetPixelRed(image,p)+color_vector.red*
5165         (1.0-(4.0*(weight*weight)));
5166       SetPixelRed(tint_image,ClampToQuantum(pixel.red),q);
5167       weight=QuantumScale*GetPixelGreen(image,p)-0.5;
5168       pixel.green=(MagickRealType) GetPixelGreen(image,p)+color_vector.green*
5169         (1.0-(4.0*(weight*weight)));
5170       SetPixelGreen(tint_image,ClampToQuantum(pixel.green),q);
5171       weight=QuantumScale*GetPixelBlue(image,p)-0.5;
5172       pixel.blue=(MagickRealType) GetPixelBlue(image,p)+color_vector.blue*
5173         (1.0-(4.0*(weight*weight)));
5174       SetPixelBlue(tint_image,ClampToQuantum(pixel.blue),q);
5175       SetPixelAlpha(tint_image,GetPixelAlpha(image,p),q);
5176       p+=GetPixelChannels(image);
5177       q+=GetPixelChannels(tint_image);
5178     }
5179     if (SyncCacheViewAuthenticPixels(tint_view,exception) == MagickFalse)
5180       status=MagickFalse;
5181     if (image->progress_monitor != (MagickProgressMonitor) NULL)
5182       {
5183         MagickBooleanType
5184           proceed;
5185
5186 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5187   #pragma omp critical (MagickCore_TintImage)
5188 #endif
5189         proceed=SetImageProgress(image,TintImageTag,progress++,image->rows);
5190         if (proceed == MagickFalse)
5191           status=MagickFalse;
5192       }
5193   }
5194   tint_view=DestroyCacheView(tint_view);
5195   image_view=DestroyCacheView(image_view);
5196   if (status == MagickFalse)
5197     tint_image=DestroyImage(tint_image);
5198   return(tint_image);
5199 }
5200 \f
5201 /*
5202 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5203 %                                                                             %
5204 %                                                                             %
5205 %                                                                             %
5206 %     V i g n e t t e I m a g e                                               %
5207 %                                                                             %
5208 %                                                                             %
5209 %                                                                             %
5210 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5211 %
5212 %  VignetteImage() softens the edges of the image in vignette style.
5213 %
5214 %  The format of the VignetteImage method is:
5215 %
5216 %      Image *VignetteImage(const Image *image,const double radius,
5217 %        const double sigma,const ssize_t x,const ssize_t y,ExceptionInfo *exception)
5218 %
5219 %  A description of each parameter follows:
5220 %
5221 %    o image: the image.
5222 %
5223 %    o radius: the radius of the pixel neighborhood.
5224 %
5225 %    o sigma: the standard deviation of the Gaussian, in pixels.
5226 %
5227 %    o x, y:  Define the x and y ellipse offset.
5228 %
5229 %    o exception: return any errors or warnings in this structure.
5230 %
5231 */
5232 MagickExport Image *VignetteImage(const Image *image,const double radius,
5233   const double sigma,const ssize_t x,const ssize_t y,ExceptionInfo *exception)
5234 {
5235   char
5236     ellipse[MaxTextExtent];
5237
5238   DrawInfo
5239     *draw_info;
5240
5241   Image
5242     *canvas_image,
5243     *blur_image,
5244     *oval_image,
5245     *vignette_image;
5246
5247   assert(image != (Image *) NULL);
5248   assert(image->signature == MagickSignature);
5249   if (image->debug != MagickFalse)
5250     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5251   assert(exception != (ExceptionInfo *) NULL);
5252   assert(exception->signature == MagickSignature);
5253   canvas_image=CloneImage(image,0,0,MagickTrue,exception);
5254   if (canvas_image == (Image *) NULL)
5255     return((Image *) NULL);
5256   if (SetImageStorageClass(canvas_image,DirectClass,exception) == MagickFalse)
5257     {
5258       canvas_image=DestroyImage(canvas_image);
5259       return((Image *) NULL);
5260     }
5261   canvas_image->matte=MagickTrue;
5262   oval_image=CloneImage(canvas_image,canvas_image->columns,
5263     canvas_image->rows,MagickTrue,exception);
5264   if (oval_image == (Image *) NULL)
5265     {
5266       canvas_image=DestroyImage(canvas_image);
5267       return((Image *) NULL);
5268     }
5269   (void) QueryColorDatabase("#000000",&oval_image->background_color,exception);
5270   (void) SetImageBackgroundColor(oval_image);
5271   draw_info=CloneDrawInfo((const ImageInfo *) NULL,(const DrawInfo *) NULL);
5272   (void) QueryColorDatabase("#ffffff",&draw_info->fill,exception);
5273   (void) QueryColorDatabase("#ffffff",&draw_info->stroke,exception);
5274   (void) FormatLocaleString(ellipse,MaxTextExtent,
5275     "ellipse %g,%g,%g,%g,0.0,360.0",image->columns/2.0,
5276     image->rows/2.0,image->columns/2.0-x,image->rows/2.0-y);
5277   draw_info->primitive=AcquireString(ellipse);
5278   (void) DrawImage(oval_image,draw_info);
5279   draw_info=DestroyDrawInfo(draw_info);
5280   blur_image=BlurImage(oval_image,radius,sigma,exception);
5281   oval_image=DestroyImage(oval_image);
5282   if (blur_image == (Image *) NULL)
5283     {
5284       canvas_image=DestroyImage(canvas_image);
5285       return((Image *) NULL);
5286     }
5287   blur_image->matte=MagickFalse;
5288   (void) CompositeImage(canvas_image,CopyOpacityCompositeOp,blur_image,0,0);
5289   blur_image=DestroyImage(blur_image);
5290   vignette_image=MergeImageLayers(canvas_image,FlattenLayer,exception);
5291   canvas_image=DestroyImage(canvas_image);
5292   return(vignette_image);
5293 }
5294 \f
5295 /*
5296 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5297 %                                                                             %
5298 %                                                                             %
5299 %                                                                             %
5300 %     W a v e I m a g e                                                       %
5301 %                                                                             %
5302 %                                                                             %
5303 %                                                                             %
5304 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5305 %
5306 %  WaveImage() creates a "ripple" effect in the image by shifting the pixels
5307 %  vertically along a sine wave whose amplitude and wavelength is specified
5308 %  by the given parameters.
5309 %
5310 %  The format of the WaveImage method is:
5311 %
5312 %      Image *WaveImage(const Image *image,const double amplitude,
5313 %        const double wave_length,ExceptionInfo *exception)
5314 %
5315 %  A description of each parameter follows:
5316 %
5317 %    o image: the image.
5318 %
5319 %    o amplitude, wave_length:  Define the amplitude and wave length of the
5320 %      sine wave.
5321 %
5322 %    o exception: return any errors or warnings in this structure.
5323 %
5324 */
5325 MagickExport Image *WaveImage(const Image *image,const double amplitude,
5326   const double wave_length,ExceptionInfo *exception)
5327 {
5328 #define WaveImageTag  "Wave/Image"
5329
5330   CacheView
5331     *image_view,
5332     *wave_view;
5333
5334   Image
5335     *wave_image;
5336
5337   MagickBooleanType
5338     status;
5339
5340   MagickOffsetType
5341     progress;
5342
5343   PixelInfo
5344     zero;
5345
5346   MagickRealType
5347     *sine_map;
5348
5349   register ssize_t
5350     i;
5351
5352   ssize_t
5353     y;
5354
5355   /*
5356     Initialize wave image attributes.
5357   */
5358   assert(image != (Image *) NULL);
5359   assert(image->signature == MagickSignature);
5360   if (image->debug != MagickFalse)
5361     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5362   assert(exception != (ExceptionInfo *) NULL);
5363   assert(exception->signature == MagickSignature);
5364   wave_image=CloneImage(image,image->columns,(size_t) (image->rows+2.0*
5365     fabs(amplitude)),MagickTrue,exception);
5366   if (wave_image == (Image *) NULL)
5367     return((Image *) NULL);
5368   if (SetImageStorageClass(wave_image,DirectClass,exception) == MagickFalse)
5369     {
5370       wave_image=DestroyImage(wave_image);
5371       return((Image *) NULL);
5372     }
5373   if (wave_image->background_color.alpha != OpaqueAlpha)
5374     wave_image->matte=MagickTrue;
5375   /*
5376     Allocate sine map.
5377   */
5378   sine_map=(MagickRealType *) AcquireQuantumMemory((size_t) wave_image->columns,
5379     sizeof(*sine_map));
5380   if (sine_map == (MagickRealType *) NULL)
5381     {
5382       wave_image=DestroyImage(wave_image);
5383       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
5384     }
5385   for (i=0; i < (ssize_t) wave_image->columns; i++)
5386     sine_map[i]=fabs(amplitude)+amplitude*sin((double) ((2.0*MagickPI*i)/
5387       wave_length));
5388   /*
5389     Wave image.
5390   */
5391   status=MagickTrue;
5392   progress=0;
5393   GetPixelInfo(wave_image,&zero);
5394   image_view=AcquireCacheView(image);
5395   wave_view=AcquireCacheView(wave_image);
5396   (void) SetCacheViewVirtualPixelMethod(image_view,
5397     BackgroundVirtualPixelMethod);
5398 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5399   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
5400 #endif
5401   for (y=0; y < (ssize_t) wave_image->rows; y++)
5402   {
5403     PixelInfo
5404       pixel;
5405
5406     register Quantum
5407       *restrict q;
5408
5409     register ssize_t
5410       x;
5411
5412     if (status == MagickFalse)
5413       continue;
5414     q=QueueCacheViewAuthenticPixels(wave_view,0,y,wave_image->columns,1,
5415       exception);
5416     if (q == (const Quantum *) NULL)
5417       {
5418         status=MagickFalse;
5419         continue;
5420       }
5421     pixel=zero;
5422     for (x=0; x < (ssize_t) wave_image->columns; x++)
5423     {
5424       (void) InterpolatePixelInfo(image,image_view,
5425         UndefinedInterpolatePixel,(double) x,(double) (y-sine_map[x]),&pixel,
5426         exception);
5427       SetPixelPixelInfo(wave_image,&pixel,q);
5428       q+=GetPixelChannels(wave_image);
5429     }
5430     if (SyncCacheViewAuthenticPixels(wave_view,exception) == MagickFalse)
5431       status=MagickFalse;
5432     if (image->progress_monitor != (MagickProgressMonitor) NULL)
5433       {
5434         MagickBooleanType
5435           proceed;
5436
5437 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5438   #pragma omp critical (MagickCore_WaveImage)
5439 #endif
5440         proceed=SetImageProgress(image,WaveImageTag,progress++,image->rows);
5441         if (proceed == MagickFalse)
5442           status=MagickFalse;
5443       }
5444   }
5445   wave_view=DestroyCacheView(wave_view);
5446   image_view=DestroyCacheView(image_view);
5447   sine_map=(MagickRealType *) RelinquishMagickMemory(sine_map);
5448   if (status == MagickFalse)
5449     wave_image=DestroyImage(wave_image);
5450   return(wave_image);
5451 }