]> 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/gem-private.h"
62 #include "MagickCore/geometry.h"
63 #include "MagickCore/layer.h"
64 #include "MagickCore/list.h"
65 #include "MagickCore/log.h"
66 #include "MagickCore/image.h"
67 #include "MagickCore/image-private.h"
68 #include "MagickCore/magick.h"
69 #include "MagickCore/memory_.h"
70 #include "MagickCore/monitor.h"
71 #include "MagickCore/monitor-private.h"
72 #include "MagickCore/option.h"
73 #include "MagickCore/pixel.h"
74 #include "MagickCore/pixel-accessor.h"
75 #include "MagickCore/property.h"
76 #include "MagickCore/quantum.h"
77 #include "MagickCore/quantum-private.h"
78 #include "MagickCore/random_.h"
79 #include "MagickCore/random-private.h"
80 #include "MagickCore/resample.h"
81 #include "MagickCore/resample-private.h"
82 #include "MagickCore/resize.h"
83 #include "MagickCore/shear.h"
84 #include "MagickCore/splay-tree.h"
85 #include "MagickCore/statistic.h"
86 #include "MagickCore/string_.h"
87 #include "MagickCore/string-private.h"
88 #include "MagickCore/thread-private.h"
89 #include "MagickCore/transform.h"
90 #include "MagickCore/utility.h"
91 \f
92 /*
93   Define declarations.
94 */
95 #define LeftShiftOperator 0xf5
96 #define RightShiftOperator 0xf6
97 #define LessThanEqualOperator 0xf7
98 #define GreaterThanEqualOperator 0xf8
99 #define EqualOperator 0xf9
100 #define NotEqualOperator 0xfa
101 #define LogicalAndOperator 0xfb
102 #define LogicalOrOperator 0xfc
103 #define ExponentialNotation 0xfd
104
105 struct _FxInfo
106 {
107   const Image
108     *images;
109
110   char
111     *expression;
112
113   FILE
114     *file;
115
116   SplayTreeInfo
117     *colors,
118     *symbols;
119
120   CacheView
121     **view;
122
123   RandomInfo
124     *random_info;
125
126   ExceptionInfo
127     *exception;
128 };
129 \f
130 /*
131 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
132 %                                                                             %
133 %                                                                             %
134 %                                                                             %
135 +   A c q u i r e F x I n f o                                                 %
136 %                                                                             %
137 %                                                                             %
138 %                                                                             %
139 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
140 %
141 %  AcquireFxInfo() allocates the FxInfo structure.
142 %
143 %  The format of the AcquireFxInfo method is:
144 %
145 %      FxInfo *AcquireFxInfo(Image *image,const char *expression)
146 %
147 %  A description of each parameter follows:
148 %
149 %    o image: the image.
150 %
151 %    o expression: the expression.
152 %
153 */
154 MagickPrivate FxInfo *AcquireFxInfo(const Image *image,const char *expression)
155 {
156   char
157     fx_op[2];
158
159   const Image
160     *next;
161
162   FxInfo
163     *fx_info;
164
165   register ssize_t
166     i;
167
168   fx_info=(FxInfo *) AcquireMagickMemory(sizeof(*fx_info));
169   if (fx_info == (FxInfo *) NULL)
170     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
171   (void) ResetMagickMemory(fx_info,0,sizeof(*fx_info));
172   fx_info->exception=AcquireExceptionInfo();
173   fx_info->images=image;
174   fx_info->colors=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
175     RelinquishMagickMemory);
176   fx_info->symbols=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
177     RelinquishMagickMemory);
178   fx_info->view=(CacheView **) AcquireQuantumMemory(GetImageListLength(
179     fx_info->images),sizeof(*fx_info->view));
180   if (fx_info->view == (CacheView **) NULL)
181     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
182   i=0;
183   next=GetFirstImageInList(fx_info->images);
184   for ( ; next != (Image *) NULL; next=next->next)
185   {
186     fx_info->view[i]=AcquireCacheView(next);
187     i++;
188   }
189   fx_info->random_info=AcquireRandomInfo();
190   fx_info->expression=ConstantString(expression);
191   fx_info->file=stderr;
192   (void) SubstituteString(&fx_info->expression," ","");  /* compact string */
193   /*
194     Force right-to-left associativity for unary negation.
195   */
196   (void) SubstituteString(&fx_info->expression,"-","-1.0*");
197   if ((strstr(fx_info->expression,"e+") != (char *) NULL) ||
198       (strstr(fx_info->expression,"e-") != (char *) NULL))
199     {
200       /*
201         Convert scientific notation.
202       */
203       (void) SubstituteString(&fx_info->expression,"0e+","0**10^");
204       (void) SubstituteString(&fx_info->expression,"1e+","1**10^");
205       (void) SubstituteString(&fx_info->expression,"2e+","2**10^");
206       (void) SubstituteString(&fx_info->expression,"3e+","3**10^");
207       (void) SubstituteString(&fx_info->expression,"4e+","4**10^");
208       (void) SubstituteString(&fx_info->expression,"5e+","5**10^");
209       (void) SubstituteString(&fx_info->expression,"6e+","6**10^");
210       (void) SubstituteString(&fx_info->expression,"7e+","7**10^");
211       (void) SubstituteString(&fx_info->expression,"8e+","8**10^");
212       (void) SubstituteString(&fx_info->expression,"9e+","9**10^");
213       (void) SubstituteString(&fx_info->expression,"0e-1.0*","0**10^-");
214       (void) SubstituteString(&fx_info->expression,"1e-1.0*","1**10^-");
215       (void) SubstituteString(&fx_info->expression,"2e-1.0*","2**10^-");
216       (void) SubstituteString(&fx_info->expression,"3e-1.0*","3**10^-");
217       (void) SubstituteString(&fx_info->expression,"4e-1.0*","4**10^-");
218       (void) SubstituteString(&fx_info->expression,"5e-1.0*","5**10^-");
219       (void) SubstituteString(&fx_info->expression,"6e-1.0*","6**10^-");
220       (void) SubstituteString(&fx_info->expression,"7e-1.0*","7**10^-");
221       (void) SubstituteString(&fx_info->expression,"8e-1.0*","8**10^-");
222       (void) SubstituteString(&fx_info->expression,"9e-1.0*","9**10^-");
223     }
224   if ((strstr(fx_info->expression,"E+") != (char *) NULL) ||
225       (strstr(fx_info->expression,"E-") != (char *) NULL))
226     {
227       /*
228         Convert scientific notation.
229       */
230       (void) SubstituteString(&fx_info->expression,"0E+","0**10^");
231       (void) SubstituteString(&fx_info->expression,"1E+","1**10^");
232       (void) SubstituteString(&fx_info->expression,"2E+","2**10^");
233       (void) SubstituteString(&fx_info->expression,"3E+","3**10^");
234       (void) SubstituteString(&fx_info->expression,"4E+","4**10^");
235       (void) SubstituteString(&fx_info->expression,"5E+","5**10^");
236       (void) SubstituteString(&fx_info->expression,"6E+","6**10^");
237       (void) SubstituteString(&fx_info->expression,"7E+","7**10^");
238       (void) SubstituteString(&fx_info->expression,"8E+","8**10^");
239       (void) SubstituteString(&fx_info->expression,"9E+","9**10^");
240       (void) SubstituteString(&fx_info->expression,"0E-1.0*","0**10^-");
241       (void) SubstituteString(&fx_info->expression,"1E-1.0*","1**10^-");
242       (void) SubstituteString(&fx_info->expression,"2E-1.0*","2**10^-");
243       (void) SubstituteString(&fx_info->expression,"3E-1.0*","3**10^-");
244       (void) SubstituteString(&fx_info->expression,"4E-1.0*","4**10^-");
245       (void) SubstituteString(&fx_info->expression,"5E-1.0*","5**10^-");
246       (void) SubstituteString(&fx_info->expression,"6E-1.0*","6**10^-");
247       (void) SubstituteString(&fx_info->expression,"7E-1.0*","7**10^-");
248       (void) SubstituteString(&fx_info->expression,"8E-1.0*","8**10^-");
249       (void) SubstituteString(&fx_info->expression,"9E-1.0*","9**10^-");
250     }
251   /*
252     Convert complex to simple operators.
253   */
254   fx_op[1]='\0';
255   *fx_op=(char) LeftShiftOperator;
256   (void) SubstituteString(&fx_info->expression,"<<",fx_op);
257   *fx_op=(char) RightShiftOperator;
258   (void) SubstituteString(&fx_info->expression,">>",fx_op);
259   *fx_op=(char) LessThanEqualOperator;
260   (void) SubstituteString(&fx_info->expression,"<=",fx_op);
261   *fx_op=(char) GreaterThanEqualOperator;
262   (void) SubstituteString(&fx_info->expression,">=",fx_op);
263   *fx_op=(char) EqualOperator;
264   (void) SubstituteString(&fx_info->expression,"==",fx_op);
265   *fx_op=(char) NotEqualOperator;
266   (void) SubstituteString(&fx_info->expression,"!=",fx_op);
267   *fx_op=(char) LogicalAndOperator;
268   (void) SubstituteString(&fx_info->expression,"&&",fx_op);
269   *fx_op=(char) LogicalOrOperator;
270   (void) SubstituteString(&fx_info->expression,"||",fx_op);
271   *fx_op=(char) ExponentialNotation;
272   (void) SubstituteString(&fx_info->expression,"**",fx_op);
273   return(fx_info);
274 }
275 \f
276 /*
277 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
278 %                                                                             %
279 %                                                                             %
280 %                                                                             %
281 %     A d d N o i s e I m a g e                                               %
282 %                                                                             %
283 %                                                                             %
284 %                                                                             %
285 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
286 %
287 %  AddNoiseImage() adds random noise to the image.
288 %
289 %  The format of the AddNoiseImage method is:
290 %
291 %      Image *AddNoiseImage(const Image *image,const NoiseType noise_type,
292 %        ExceptionInfo *exception)
293 %
294 %  A description of each parameter follows:
295 %
296 %    o image: the image.
297 %
298 %    o channel: the channel type.
299 %
300 %    o noise_type:  The type of noise: Uniform, Gaussian, Multiplicative,
301 %      Impulse, Laplacian, or Poisson.
302 %
303 %    o exception: return any errors or warnings in this structure.
304 %
305 */
306 MagickExport Image *AddNoiseImage(const Image *image,const NoiseType noise_type,  ExceptionInfo *exception)
307 {
308 #define AddNoiseImageTag  "AddNoise/Image"
309
310   CacheView
311     *image_view,
312     *noise_view;
313
314   const char
315     *option;
316
317   Image
318     *noise_image;
319
320   MagickBooleanType
321     status;
322
323   MagickOffsetType
324     progress;
325
326   MagickRealType
327     attenuate;
328
329   RandomInfo
330     **restrict random_info;
331
332   ssize_t
333     y;
334
335   /*
336     Initialize noise image attributes.
337   */
338   assert(image != (const Image *) NULL);
339   assert(image->signature == MagickSignature);
340   if (image->debug != MagickFalse)
341     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
342   assert(exception != (ExceptionInfo *) NULL);
343   assert(exception->signature == MagickSignature);
344   noise_image=CloneImage(image,0,0,MagickTrue,exception);
345   if (noise_image == (Image *) NULL)
346     return((Image *) NULL);
347   if (SetImageStorageClass(noise_image,DirectClass,exception) == MagickFalse)
348     {
349       noise_image=DestroyImage(noise_image);
350       return((Image *) NULL);
351     }
352   /*
353     Add noise in each row.
354   */
355   attenuate=1.0;
356   option=GetImageArtifact(image,"attenuate");
357   if (option != (char *) NULL)
358     attenuate=InterpretLocaleValue(option,(char **) NULL);
359   status=MagickTrue;
360   progress=0;
361   random_info=AcquireRandomInfoThreadSet();
362   image_view=AcquireCacheView(image);
363   noise_view=AcquireCacheView(noise_image);
364 #if defined(MAGICKCORE_OPENMP_SUPPORT)
365   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
366 #endif
367   for (y=0; y < (ssize_t) image->rows; y++)
368   {
369     const int
370       id = GetOpenMPThreadId();
371
372     MagickBooleanType
373       sync;
374
375     register const Quantum
376       *restrict p;
377
378     register ssize_t
379       x;
380
381     register Quantum
382       *restrict q;
383
384     if (status == MagickFalse)
385       continue;
386     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
387     q=GetCacheViewAuthenticPixels(noise_view,0,y,noise_image->columns,1,
388       exception);
389     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
390       {
391         status=MagickFalse;
392         continue;
393       }
394     for (x=0; x < (ssize_t) image->columns; x++)
395     {
396       if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
397         SetPixelRed(noise_image,ClampToQuantum(GenerateDifferentialNoise(
398           random_info[id],GetPixelRed(image,p),noise_type,attenuate)),q);
399       if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
400         SetPixelGreen(noise_image,ClampToQuantum(GenerateDifferentialNoise(
401           random_info[id],GetPixelGreen(image,p),noise_type,attenuate)),q);
402       if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
403         SetPixelBlue(noise_image,ClampToQuantum(GenerateDifferentialNoise(
404           random_info[id],GetPixelBlue(image,p),noise_type,attenuate)),q);
405       if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
406           (image->colorspace == CMYKColorspace))
407         SetPixelBlack(noise_image,ClampToQuantum(GenerateDifferentialNoise(
408           random_info[id],GetPixelBlack(image,p),noise_type,attenuate)),q);
409       if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
410         SetPixelAlpha(noise_image,ClampToQuantum(GenerateDifferentialNoise(
411           random_info[id],GetPixelAlpha(image,p),noise_type,attenuate)),q);
412       p+=GetPixelChannels(image);
413       q+=GetPixelChannels(noise_image);
414     }
415     sync=SyncCacheViewAuthenticPixels(noise_view,exception);
416     if (sync == MagickFalse)
417       status=MagickFalse;
418     if (image->progress_monitor != (MagickProgressMonitor) NULL)
419       {
420         MagickBooleanType
421           proceed;
422
423 #if defined(MAGICKCORE_OPENMP_SUPPORT)
424         #pragma omp critical (MagickCore_AddNoiseImage)
425 #endif
426         proceed=SetImageProgress(image,AddNoiseImageTag,progress++,
427           image->rows);
428         if (proceed == MagickFalse)
429           status=MagickFalse;
430       }
431   }
432   noise_view=DestroyCacheView(noise_view);
433   image_view=DestroyCacheView(image_view);
434   random_info=DestroyRandomInfoThreadSet(random_info);
435   if (status == MagickFalse)
436     noise_image=DestroyImage(noise_image);
437   return(noise_image);
438 }
439 \f
440 /*
441 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
442 %                                                                             %
443 %                                                                             %
444 %                                                                             %
445 %     B l u e S h i f t I m a g e                                             %
446 %                                                                             %
447 %                                                                             %
448 %                                                                             %
449 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
450 %
451 %  BlueShiftImage() mutes the colors of the image to simulate a scene at
452 %  nighttime in the moonlight.
453 %
454 %  The format of the BlueShiftImage method is:
455 %
456 %      Image *BlueShiftImage(const Image *image,const double factor,
457 %        ExceptionInfo *exception)
458 %
459 %  A description of each parameter follows:
460 %
461 %    o image: the image.
462 %
463 %    o factor: the shift factor.
464 %
465 %    o exception: return any errors or warnings in this structure.
466 %
467 */
468 MagickExport Image *BlueShiftImage(const Image *image,const double factor,
469   ExceptionInfo *exception)
470 {
471 #define BlueShiftImageTag  "BlueShift/Image"
472
473   CacheView
474     *image_view,
475     *shift_view;
476
477   Image
478     *shift_image;
479
480   MagickBooleanType
481     status;
482
483   MagickOffsetType
484     progress;
485
486   ssize_t
487     y;
488
489   /*
490     Allocate blue shift image.
491   */
492   assert(image != (const Image *) NULL);
493   assert(image->signature == MagickSignature);
494   if (image->debug != MagickFalse)
495     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
496   assert(exception != (ExceptionInfo *) NULL);
497   assert(exception->signature == MagickSignature);
498   shift_image=CloneImage(image,image->columns,image->rows,MagickTrue,
499     exception);
500   if (shift_image == (Image *) NULL)
501     return((Image *) NULL);
502   if (SetImageStorageClass(shift_image,DirectClass,exception) == MagickFalse)
503     {
504       shift_image=DestroyImage(shift_image);
505       return((Image *) NULL);
506     }
507   /*
508     Blue-shift DirectClass image.
509   */
510   status=MagickTrue;
511   progress=0;
512   image_view=AcquireCacheView(image);
513   shift_view=AcquireCacheView(shift_image);
514 #if defined(MAGICKCORE_OPENMP_SUPPORT)
515   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
516 #endif
517   for (y=0; y < (ssize_t) image->rows; y++)
518   {
519     MagickBooleanType
520       sync;
521
522     PixelInfo
523       pixel;
524
525     Quantum
526       quantum;
527
528     register const Quantum
529       *restrict p;
530
531     register ssize_t
532       x;
533
534     register Quantum
535       *restrict q;
536
537     if (status == MagickFalse)
538       continue;
539     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
540     q=QueueCacheViewAuthenticPixels(shift_view,0,y,shift_image->columns,1,
541       exception);
542     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
543       {
544         status=MagickFalse;
545         continue;
546       }
547     for (x=0; x < (ssize_t) image->columns; x++)
548     {
549       quantum=GetPixelRed(image,p);
550       if (GetPixelGreen(image,p) < quantum)
551         quantum=GetPixelGreen(image,p);
552       if (GetPixelBlue(image,p) < quantum)
553         quantum=GetPixelBlue(image,p);
554       pixel.red=0.5*(GetPixelRed(image,p)+factor*quantum);
555       pixel.green=0.5*(GetPixelGreen(image,p)+factor*quantum);
556       pixel.blue=0.5*(GetPixelBlue(image,p)+factor*quantum);
557       quantum=GetPixelRed(image,p);
558       if (GetPixelGreen(image,p) > quantum)
559         quantum=GetPixelGreen(image,p);
560       if (GetPixelBlue(image,p) > quantum)
561         quantum=GetPixelBlue(image,p);
562       pixel.red=0.5*(pixel.red+factor*quantum);
563       pixel.green=0.5*(pixel.green+factor*quantum);
564       pixel.blue=0.5*(pixel.blue+factor*quantum);
565       SetPixelRed(shift_image,ClampToQuantum(pixel.red),q);
566       SetPixelGreen(shift_image,ClampToQuantum(pixel.green),q);
567       SetPixelBlue(shift_image,ClampToQuantum(pixel.blue),q);
568       p+=GetPixelChannels(image);
569       q+=GetPixelChannels(shift_image);
570     }
571     sync=SyncCacheViewAuthenticPixels(shift_view,exception);
572     if (sync == MagickFalse)
573       status=MagickFalse;
574     if (image->progress_monitor != (MagickProgressMonitor) NULL)
575       {
576         MagickBooleanType
577           proceed;
578
579 #if defined(MAGICKCORE_OPENMP_SUPPORT)
580   #pragma omp critical (MagickCore_BlueShiftImage)
581 #endif
582         proceed=SetImageProgress(image,BlueShiftImageTag,progress++,
583           image->rows);
584         if (proceed == MagickFalse)
585           status=MagickFalse;
586       }
587   }
588   image_view=DestroyCacheView(image_view);
589   shift_view=DestroyCacheView(shift_view);
590   if (status == MagickFalse)
591     shift_image=DestroyImage(shift_image);
592   return(shift_image);
593 }
594 \f
595 /*
596 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
597 %                                                                             %
598 %                                                                             %
599 %                                                                             %
600 %     C h a r c o a l I m a g e                                               %
601 %                                                                             %
602 %                                                                             %
603 %                                                                             %
604 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
605 %
606 %  CharcoalImage() creates a new image that is a copy of an existing one with
607 %  the edge highlighted.  It allocates the memory necessary for the new Image
608 %  structure and returns a pointer to the new image.
609 %
610 %  The format of the CharcoalImage method is:
611 %
612 %      Image *CharcoalImage(const Image *image,const double radius,
613 %        const double sigma,ExceptionInfo *exception)
614 %
615 %  A description of each parameter follows:
616 %
617 %    o image: the image.
618 %
619 %    o radius: the radius of the pixel neighborhood.
620 %
621 %    o sigma: the standard deviation of the Gaussian, in pixels.
622 %
623 %    o exception: return any errors or warnings in this structure.
624 %
625 */
626 MagickExport Image *CharcoalImage(const Image *image,const double radius,
627   const double sigma,ExceptionInfo *exception)
628 {
629   Image
630     *charcoal_image,
631     *clone_image,
632     *edge_image;
633
634   assert(image != (Image *) NULL);
635   assert(image->signature == MagickSignature);
636   if (image->debug != MagickFalse)
637     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
638   assert(exception != (ExceptionInfo *) NULL);
639   assert(exception->signature == MagickSignature);
640   clone_image=CloneImage(image,0,0,MagickTrue,exception);
641   if (clone_image == (Image *) NULL)
642     return((Image *) NULL);
643   (void) SetImageType(clone_image,GrayscaleType,exception);
644   edge_image=EdgeImage(clone_image,radius,exception);
645   clone_image=DestroyImage(clone_image);
646   if (edge_image == (Image *) NULL)
647     return((Image *) NULL);
648   charcoal_image=BlurImage(edge_image,radius,sigma,exception);
649   edge_image=DestroyImage(edge_image);
650   if (charcoal_image == (Image *) NULL)
651     return((Image *) NULL);
652   (void) NormalizeImage(charcoal_image,exception);
653   (void) NegateImage(charcoal_image,MagickFalse,exception);
654   (void) SetImageType(charcoal_image,GrayscaleType,exception);
655   return(charcoal_image);
656 }
657 \f
658 /*
659 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
660 %                                                                             %
661 %                                                                             %
662 %                                                                             %
663 %     C o l o r i z e I m a g e                                               %
664 %                                                                             %
665 %                                                                             %
666 %                                                                             %
667 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
668 %
669 %  ColorizeImage() blends the fill color with each pixel in the image.
670 %  A percentage blend is specified with opacity.  Control the application
671 %  of different color components by specifying a different percentage for
672 %  each component (e.g. 90/100/10 is 90% red, 100% green, and 10% blue).
673 %
674 %  The format of the ColorizeImage method is:
675 %
676 %      Image *ColorizeImage(const Image *image,const char *opacity,
677 %        const PixelPacket colorize,ExceptionInfo *exception)
678 %
679 %  A description of each parameter follows:
680 %
681 %    o image: the image.
682 %
683 %    o opacity:  A character string indicating the level of opacity as a
684 %      percentage.
685 %
686 %    o colorize: A color value.
687 %
688 %    o exception: return any errors or warnings in this structure.
689 %
690 */
691 MagickExport Image *ColorizeImage(const Image *image,const char *opacity,
692   const PixelPacket colorize,ExceptionInfo *exception)
693 {
694 #define ColorizeImageTag  "Colorize/Image"
695
696   CacheView
697     *colorize_view,
698     *image_view;
699
700   GeometryInfo
701     geometry_info;
702
703   Image
704     *colorize_image;
705
706   MagickBooleanType
707     status;
708
709   MagickOffsetType
710     progress;
711
712   PixelInfo
713     pixel;
714
715   MagickStatusType
716     flags;
717
718   ssize_t
719     y;
720
721   /*
722     Allocate colorized image.
723   */
724   assert(image != (const Image *) NULL);
725   assert(image->signature == MagickSignature);
726   if (image->debug != MagickFalse)
727     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
728   assert(exception != (ExceptionInfo *) NULL);
729   assert(exception->signature == MagickSignature);
730   colorize_image=CloneImage(image,image->columns,image->rows,MagickTrue,
731     exception);
732   if (colorize_image == (Image *) NULL)
733     return((Image *) NULL);
734   if (SetImageStorageClass(colorize_image,DirectClass,exception) == MagickFalse)
735     {
736       colorize_image=DestroyImage(colorize_image);
737       return((Image *) NULL);
738     }
739   if (opacity == (const char *) NULL)
740     return(colorize_image);
741   /*
742     Determine RGB values of the pen color.
743   */
744   flags=ParseGeometry(opacity,&geometry_info);
745   pixel.red=geometry_info.rho;
746   pixel.green=geometry_info.rho;
747   pixel.blue=geometry_info.rho;
748   pixel.alpha=(MagickRealType) OpaqueAlpha;
749   if ((flags & SigmaValue) != 0)
750     pixel.green=geometry_info.sigma;
751   if ((flags & XiValue) != 0)
752     pixel.blue=geometry_info.xi;
753   if ((flags & PsiValue) != 0)
754     pixel.alpha=geometry_info.psi;
755   /*
756     Colorize DirectClass image.
757   */
758   status=MagickTrue;
759   progress=0;
760   image_view=AcquireCacheView(image);
761   colorize_view=AcquireCacheView(colorize_image);
762 #if defined(MAGICKCORE_OPENMP_SUPPORT)
763   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
764 #endif
765   for (y=0; y < (ssize_t) image->rows; y++)
766   {
767     MagickBooleanType
768       sync;
769
770     register const Quantum
771       *restrict p;
772
773     register ssize_t
774       x;
775
776     register Quantum
777       *restrict q;
778
779     if (status == MagickFalse)
780       continue;
781     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
782     q=QueueCacheViewAuthenticPixels(colorize_view,0,y,colorize_image->columns,1,
783       exception);
784     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
785       {
786         status=MagickFalse;
787         continue;
788       }
789     for (x=0; x < (ssize_t) image->columns; x++)
790     {
791       SetPixelRed(colorize_image,ClampToQuantum((GetPixelRed(image,p)*
792         (100.0-pixel.red)+colorize.red*pixel.red)/100.0),q);
793       SetPixelGreen(colorize_image,ClampToQuantum((GetPixelGreen(image,p)*
794         (100.0-pixel.green)+colorize.green*pixel.green)/100.0),q);
795       SetPixelBlue(colorize_image,ClampToQuantum((GetPixelBlue(image,p)*
796         (100.0-pixel.blue)+colorize.blue*pixel.blue)/100.0),q);
797       SetPixelAlpha(colorize_image,ClampToQuantum((GetPixelAlpha(image,p)*
798         (100.0-pixel.alpha)+colorize.alpha*pixel.alpha)/100.0),q);
799       p+=GetPixelChannels(image);
800       q+=GetPixelChannels(colorize_image);
801     }
802     sync=SyncCacheViewAuthenticPixels(colorize_view,exception);
803     if (sync == MagickFalse)
804       status=MagickFalse;
805     if (image->progress_monitor != (MagickProgressMonitor) NULL)
806       {
807         MagickBooleanType
808           proceed;
809
810 #if defined(MAGICKCORE_OPENMP_SUPPORT)
811   #pragma omp critical (MagickCore_ColorizeImage)
812 #endif
813         proceed=SetImageProgress(image,ColorizeImageTag,progress++,image->rows);
814         if (proceed == MagickFalse)
815           status=MagickFalse;
816       }
817   }
818   image_view=DestroyCacheView(image_view);
819   colorize_view=DestroyCacheView(colorize_view);
820   if (status == MagickFalse)
821     colorize_image=DestroyImage(colorize_image);
822   return(colorize_image);
823 }
824 \f
825 /*
826 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
827 %                                                                             %
828 %                                                                             %
829 %                                                                             %
830 %     C o l o r M a t r i x I m a g e                                         %
831 %                                                                             %
832 %                                                                             %
833 %                                                                             %
834 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
835 %
836 %  ColorMatrixImage() applies color transformation to an image. This method
837 %  permits saturation changes, hue rotation, luminance to alpha, and various
838 %  other effects.  Although variable-sized transformation matrices can be used,
839 %  typically one uses a 5x5 matrix for an RGBA image and a 6x6 for CMYKA
840 %  (or RGBA with offsets).  The matrix is similar to those used by Adobe Flash
841 %  except offsets are in column 6 rather than 5 (in support of CMYKA images)
842 %  and offsets are normalized (divide Flash offset by 255).
843 %
844 %  The format of the ColorMatrixImage method is:
845 %
846 %      Image *ColorMatrixImage(const Image *image,
847 %        const KernelInfo *color_matrix,ExceptionInfo *exception)
848 %
849 %  A description of each parameter follows:
850 %
851 %    o image: the image.
852 %
853 %    o color_matrix:  the color matrix.
854 %
855 %    o exception: return any errors or warnings in this structure.
856 %
857 */
858 MagickExport Image *ColorMatrixImage(const Image *image,
859   const KernelInfo *color_matrix,ExceptionInfo *exception)
860 {
861 #define ColorMatrixImageTag  "ColorMatrix/Image"
862
863   CacheView
864     *color_view,
865     *image_view;
866
867   double
868     ColorMatrix[6][6] =
869     {
870       { 1.0, 0.0, 0.0, 0.0, 0.0, 0.0 },
871       { 0.0, 1.0, 0.0, 0.0, 0.0, 0.0 },
872       { 0.0, 0.0, 1.0, 0.0, 0.0, 0.0 },
873       { 0.0, 0.0, 0.0, 1.0, 0.0, 0.0 },
874       { 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 },
875       { 0.0, 0.0, 0.0, 0.0, 0.0, 1.0 }
876     };
877
878   Image
879     *color_image;
880
881   MagickBooleanType
882     status;
883
884   MagickOffsetType
885     progress;
886
887   register ssize_t
888     i;
889
890   ssize_t
891     u,
892     v,
893     y;
894
895   /*
896     Create color matrix.
897   */
898   assert(image != (Image *) NULL);
899   assert(image->signature == MagickSignature);
900   if (image->debug != MagickFalse)
901     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
902   assert(exception != (ExceptionInfo *) NULL);
903   assert(exception->signature == MagickSignature);
904   i=0;
905   for (v=0; v < (ssize_t) color_matrix->height; v++)
906     for (u=0; u < (ssize_t) color_matrix->width; u++)
907     {
908       if ((v < 6) && (u < 6))
909         ColorMatrix[v][u]=color_matrix->values[i];
910       i++;
911     }
912   /*
913     Initialize color image.
914   */
915   color_image=CloneImage(image,0,0,MagickTrue,exception);
916   if (color_image == (Image *) NULL)
917     return((Image *) NULL);
918   if (SetImageStorageClass(color_image,DirectClass,exception) == MagickFalse)
919     {
920       color_image=DestroyImage(color_image);
921       return((Image *) NULL);
922     }
923   if (image->debug != MagickFalse)
924     {
925       char
926         format[MaxTextExtent],
927         *message;
928
929       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
930         "  ColorMatrix image with color matrix:");
931       message=AcquireString("");
932       for (v=0; v < 6; v++)
933       {
934         *message='\0';
935         (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) v);
936         (void) ConcatenateString(&message,format);
937         for (u=0; u < 6; u++)
938         {
939           (void) FormatLocaleString(format,MaxTextExtent,"%+f ",
940             ColorMatrix[v][u]);
941           (void) ConcatenateString(&message,format);
942         }
943         (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
944       }
945       message=DestroyString(message);
946     }
947   /*
948     ColorMatrix image.
949   */
950   status=MagickTrue;
951   progress=0;
952   image_view=AcquireCacheView(image);
953   color_view=AcquireCacheView(color_image);
954 #if defined(MAGICKCORE_OPENMP_SUPPORT)
955   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
956 #endif
957   for (y=0; y < (ssize_t) image->rows; y++)
958   {
959     MagickRealType
960       pixel;
961
962     register const Quantum
963       *restrict p;
964
965     register Quantum
966       *restrict q;
967
968     register ssize_t
969       x;
970
971     if (status == MagickFalse)
972       continue;
973     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
974     q=GetCacheViewAuthenticPixels(color_view,0,y,color_image->columns,1,
975       exception);
976     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
977       {
978         status=MagickFalse;
979         continue;
980       }
981     for (x=0; x < (ssize_t) image->columns; x++)
982     {
983       register ssize_t
984         v;
985
986       size_t
987         height;
988
989       height=color_matrix->height > 6 ? 6UL : color_matrix->height;
990       for (v=0; v < (ssize_t) height; v++)
991       {
992         pixel=ColorMatrix[v][0]*GetPixelRed(image,p)+ColorMatrix[v][1]*
993           GetPixelGreen(image,p)+ColorMatrix[v][2]*GetPixelBlue(image,p);
994         if (image->colorspace == CMYKColorspace)
995           pixel+=ColorMatrix[v][3]*GetPixelBlack(image,p);
996         if (image->matte != MagickFalse)
997           pixel+=ColorMatrix[v][4]*GetPixelAlpha(image,p);
998         pixel+=QuantumRange*ColorMatrix[v][5];
999         switch (v)
1000         {
1001           case 0: SetPixelRed(color_image,ClampToQuantum(pixel),q); break;
1002           case 1: SetPixelGreen(color_image,ClampToQuantum(pixel),q); break;
1003           case 2: SetPixelBlue(color_image,ClampToQuantum(pixel),q); break;
1004           case 3:
1005           {
1006             if (image->colorspace == CMYKColorspace)
1007               SetPixelBlack(color_image,ClampToQuantum(pixel),q);
1008             break;
1009           }
1010           case 4:
1011           {
1012             if (image->matte != MagickFalse)
1013               SetPixelAlpha(color_image,ClampToQuantum(pixel),q);
1014             break;
1015           }
1016         }
1017       }
1018       p+=GetPixelChannels(image);
1019       q+=GetPixelChannels(color_image);
1020     }
1021     if (SyncCacheViewAuthenticPixels(color_view,exception) == MagickFalse)
1022       status=MagickFalse;
1023     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1024       {
1025         MagickBooleanType
1026           proceed;
1027
1028 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1029   #pragma omp critical (MagickCore_ColorMatrixImage)
1030 #endif
1031         proceed=SetImageProgress(image,ColorMatrixImageTag,progress++,
1032           image->rows);
1033         if (proceed == MagickFalse)
1034           status=MagickFalse;
1035       }
1036   }
1037   color_view=DestroyCacheView(color_view);
1038   image_view=DestroyCacheView(image_view);
1039   if (status == MagickFalse)
1040     color_image=DestroyImage(color_image);
1041   return(color_image);
1042 }
1043 \f
1044 /*
1045 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1046 %                                                                             %
1047 %                                                                             %
1048 %                                                                             %
1049 +   D e s t r o y F x I n f o                                                 %
1050 %                                                                             %
1051 %                                                                             %
1052 %                                                                             %
1053 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1054 %
1055 %  DestroyFxInfo() deallocates memory associated with an FxInfo structure.
1056 %
1057 %  The format of the DestroyFxInfo method is:
1058 %
1059 %      ImageInfo *DestroyFxInfo(ImageInfo *fx_info)
1060 %
1061 %  A description of each parameter follows:
1062 %
1063 %    o fx_info: the fx info.
1064 %
1065 */
1066 MagickPrivate FxInfo *DestroyFxInfo(FxInfo *fx_info)
1067 {
1068   register ssize_t
1069     i;
1070
1071   fx_info->exception=DestroyExceptionInfo(fx_info->exception);
1072   fx_info->expression=DestroyString(fx_info->expression);
1073   fx_info->symbols=DestroySplayTree(fx_info->symbols);
1074   fx_info->colors=DestroySplayTree(fx_info->colors);
1075   for (i=(ssize_t) GetImageListLength(fx_info->images)-1; i >= 0; i--)
1076     fx_info->view[i]=DestroyCacheView(fx_info->view[i]);
1077   fx_info->view=(CacheView **) RelinquishMagickMemory(fx_info->view);
1078   fx_info->random_info=DestroyRandomInfo(fx_info->random_info);
1079   fx_info=(FxInfo *) RelinquishMagickMemory(fx_info);
1080   return(fx_info);
1081 }
1082 \f
1083 /*
1084 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1085 %                                                                             %
1086 %                                                                             %
1087 %                                                                             %
1088 +     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                   %
1089 %                                                                             %
1090 %                                                                             %
1091 %                                                                             %
1092 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1093 %
1094 %  FxEvaluateChannelExpression() evaluates an expression and returns the
1095 %  results.
1096 %
1097 %  The format of the FxEvaluateExpression method is:
1098 %
1099 %      MagickRealType FxEvaluateChannelExpression(FxInfo *fx_info,
1100 %        const PixelChannel channel,const ssize_t x,const ssize_t y,
1101 %        MagickRealType *alpha,Exceptioninfo *exception)
1102 %      MagickRealType FxEvaluateExpression(FxInfo *fx_info,
1103 %        MagickRealType *alpha,Exceptioninfo *exception)
1104 %
1105 %  A description of each parameter follows:
1106 %
1107 %    o fx_info: the fx info.
1108 %
1109 %    o channel: the channel.
1110 %
1111 %    o x,y: the pixel position.
1112 %
1113 %    o alpha: the result.
1114 %
1115 %    o exception: return any errors or warnings in this structure.
1116 %
1117 */
1118
1119 static inline double MagickMax(const double x,const double y)
1120 {
1121   if (x > y)
1122     return(x);
1123   return(y);
1124 }
1125
1126 static inline double MagickMin(const double x,const double y)
1127 {
1128   if (x < y)
1129     return(x);
1130   return(y);
1131 }
1132
1133 static MagickRealType FxChannelStatistics(FxInfo *fx_info,const Image *image,
1134   PixelChannel channel,const char *symbol,ExceptionInfo *exception)
1135 {
1136   char
1137     key[MaxTextExtent],
1138     statistic[MaxTextExtent];
1139
1140   const char
1141     *value;
1142
1143   register const char
1144     *p;
1145
1146   for (p=symbol; (*p != '.') && (*p != '\0'); p++) ;
1147   if (*p == '.')
1148     switch (*++p)  /* e.g. depth.r */
1149     {
1150       case 'r': channel=RedPixelChannel; break;
1151       case 'g': channel=GreenPixelChannel; break;
1152       case 'b': channel=BluePixelChannel; break;
1153       case 'c': channel=CyanPixelChannel; break;
1154       case 'm': channel=MagentaPixelChannel; break;
1155       case 'y': channel=YellowPixelChannel; break;
1156       case 'k': channel=BlackPixelChannel; break;
1157       default: break;
1158     }
1159   (void) FormatLocaleString(key,MaxTextExtent,"%p.%.20g.%s",(void *) image,
1160     (double) channel,symbol);
1161   value=(const char *) GetValueFromSplayTree(fx_info->symbols,key);
1162   if (value != (const char *) NULL)
1163     return(QuantumScale*InterpretLocaleValue(value,(char **) NULL));
1164   (void) DeleteNodeFromSplayTree(fx_info->symbols,key);
1165   if (LocaleNCompare(symbol,"depth",5) == 0)
1166     {
1167       size_t
1168         depth;
1169
1170       depth=GetImageDepth(image,exception);
1171       (void) FormatLocaleString(statistic,MaxTextExtent,"%.20g",(double) depth);
1172     }
1173   if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1174     {
1175       double
1176         kurtosis,
1177         skewness;
1178
1179       (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
1180       (void) FormatLocaleString(statistic,MaxTextExtent,"%g",kurtosis);
1181     }
1182   if (LocaleNCompare(symbol,"maxima",6) == 0)
1183     {
1184       double
1185         maxima,
1186         minima;
1187
1188       (void) GetImageRange(image,&minima,&maxima,exception);
1189       (void) FormatLocaleString(statistic,MaxTextExtent,"%g",maxima);
1190     }
1191   if (LocaleNCompare(symbol,"mean",4) == 0)
1192     {
1193       double
1194         mean,
1195         standard_deviation;
1196
1197       (void) GetImageMean(image,&mean,&standard_deviation,exception);
1198       (void) FormatLocaleString(statistic,MaxTextExtent,"%g",mean);
1199     }
1200   if (LocaleNCompare(symbol,"minima",6) == 0)
1201     {
1202       double
1203         maxima,
1204         minima;
1205
1206       (void) GetImageRange(image,&minima,&maxima,exception);
1207       (void) FormatLocaleString(statistic,MaxTextExtent,"%g",minima);
1208     }
1209   if (LocaleNCompare(symbol,"skewness",8) == 0)
1210     {
1211       double
1212         kurtosis,
1213         skewness;
1214
1215       (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
1216       (void) FormatLocaleString(statistic,MaxTextExtent,"%g",skewness);
1217     }
1218   if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1219     {
1220       double
1221         mean,
1222         standard_deviation;
1223
1224       (void) GetImageMean(image,&mean,&standard_deviation,exception);
1225       (void) FormatLocaleString(statistic,MaxTextExtent,"%g",
1226         standard_deviation);
1227     }
1228   (void) AddValueToSplayTree(fx_info->symbols,ConstantString(key),
1229     ConstantString(statistic));
1230   return(QuantumScale*InterpretLocaleValue(statistic,(char **) NULL));
1231 }
1232
1233 static MagickRealType
1234   FxEvaluateSubexpression(FxInfo *,const PixelChannel,const ssize_t,
1235     const ssize_t,const char *,MagickRealType *,ExceptionInfo *);
1236
1237 static inline MagickRealType FxMax(FxInfo *fx_info,const PixelChannel channel,
1238   const ssize_t x,const ssize_t y,const char *expression,
1239   ExceptionInfo *exception)
1240 {
1241   MagickRealType
1242     alpha,
1243     beta;
1244
1245   alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression,&beta,exception);
1246   return((MagickRealType) MagickMax((double) alpha,(double) beta));
1247 }
1248
1249 static inline MagickRealType FxMin(FxInfo *fx_info,PixelChannel channel,
1250   const ssize_t x,const ssize_t y,const char *expression,
1251   ExceptionInfo *exception)
1252 {
1253   MagickRealType
1254     alpha,
1255     beta;
1256
1257   alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression,&beta,exception);
1258   return((MagickRealType) MagickMin((double) alpha,(double) beta));
1259 }
1260
1261 static inline const char *FxSubexpression(const char *expression,
1262   ExceptionInfo *exception)
1263 {
1264   const char
1265     *subexpression;
1266
1267   register ssize_t
1268     level;
1269
1270   level=0;
1271   subexpression=expression;
1272   while ((*subexpression != '\0') &&
1273          ((level != 1) || (strchr(")",(int) *subexpression) == (char *) NULL)))
1274   {
1275     if (strchr("(",(int) *subexpression) != (char *) NULL)
1276       level++;
1277     else
1278       if (strchr(")",(int) *subexpression) != (char *) NULL)
1279         level--;
1280     subexpression++;
1281   }
1282   if (*subexpression == '\0')
1283     (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1284       "UnbalancedParenthesis","`%s'",expression);
1285   return(subexpression);
1286 }
1287
1288 static MagickRealType FxGetSymbol(FxInfo *fx_info,const PixelChannel channel,
1289   const ssize_t x,const ssize_t y,const char *expression,
1290   ExceptionInfo *exception)
1291 {
1292   char
1293     *q,
1294     subexpression[MaxTextExtent],
1295     symbol[MaxTextExtent];
1296
1297   const char
1298     *p,
1299     *value;
1300
1301   Image
1302     *image;
1303
1304   PixelInfo
1305     pixel;
1306
1307   MagickRealType
1308     alpha,
1309     beta;
1310
1311   PointInfo
1312     point;
1313
1314   register ssize_t
1315     i;
1316
1317   size_t
1318     length;
1319
1320   size_t
1321     level;
1322
1323   p=expression;
1324   i=GetImageIndexInList(fx_info->images);
1325   level=0;
1326   point.x=(double) x;
1327   point.y=(double) y;
1328   if (isalpha((int) *(p+1)) == 0)
1329     {
1330       if (strchr("suv",(int) *p) != (char *) NULL)
1331         {
1332           switch (*p)
1333           {
1334             case 's':
1335             default:
1336             {
1337               i=GetImageIndexInList(fx_info->images);
1338               break;
1339             }
1340             case 'u': i=0; break;
1341             case 'v': i=1; break;
1342           }
1343           p++;
1344           if (*p == '[')
1345             {
1346               level++;
1347               q=subexpression;
1348               for (p++; *p != '\0'; )
1349               {
1350                 if (*p == '[')
1351                   level++;
1352                 else
1353                   if (*p == ']')
1354                     {
1355                       level--;
1356                       if (level == 0)
1357                         break;
1358                     }
1359                 *q++=(*p++);
1360               }
1361               *q='\0';
1362               alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1363                 &beta,exception);
1364               i=(ssize_t) (alpha+0.5);
1365               p++;
1366             }
1367           if (*p == '.')
1368             p++;
1369         }
1370       if ((isalpha((int) *(p+1)) == 0) && (*p == 'p'))
1371         {
1372           p++;
1373           if (*p == '{')
1374             {
1375               level++;
1376               q=subexpression;
1377               for (p++; *p != '\0'; )
1378               {
1379                 if (*p == '{')
1380                   level++;
1381                 else
1382                   if (*p == '}')
1383                     {
1384                       level--;
1385                       if (level == 0)
1386                         break;
1387                     }
1388                 *q++=(*p++);
1389               }
1390               *q='\0';
1391               alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1392                 &beta,exception);
1393               point.x=alpha;
1394               point.y=beta;
1395               p++;
1396             }
1397           else
1398             if (*p == '[')
1399               {
1400                 level++;
1401                 q=subexpression;
1402                 for (p++; *p != '\0'; )
1403                 {
1404                   if (*p == '[')
1405                     level++;
1406                   else
1407                     if (*p == ']')
1408                       {
1409                         level--;
1410                         if (level == 0)
1411                           break;
1412                       }
1413                   *q++=(*p++);
1414                 }
1415                 *q='\0';
1416                 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1417                   &beta,exception);
1418                 point.x+=alpha;
1419                 point.y+=beta;
1420                 p++;
1421               }
1422           if (*p == '.')
1423             p++;
1424         }
1425     }
1426   length=GetImageListLength(fx_info->images);
1427   while (i < 0)
1428     i+=(ssize_t) length;
1429   i%=length;
1430   image=GetImageFromList(fx_info->images,i);
1431   if (image == (Image *) NULL)
1432     {
1433       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1434         "NoSuchImage","`%s'",expression);
1435       return(0.0);
1436     }
1437   GetPixelInfo(image,&pixel);
1438   (void) InterpolatePixelInfo(image,fx_info->view[i],image->interpolate,
1439     point.x,point.y,&pixel,exception);
1440   if ((strlen(p) > 2) &&
1441       (LocaleCompare(p,"intensity") != 0) &&
1442       (LocaleCompare(p,"luminance") != 0) &&
1443       (LocaleCompare(p,"hue") != 0) &&
1444       (LocaleCompare(p,"saturation") != 0) &&
1445       (LocaleCompare(p,"lightness") != 0))
1446     {
1447       char
1448         name[MaxTextExtent];
1449
1450       (void) CopyMagickString(name,p,MaxTextExtent);
1451       for (q=name+(strlen(name)-1); q > name; q--)
1452       {
1453         if (*q == ')')
1454           break;
1455         if (*q == '.')
1456           {
1457             *q='\0';
1458             break;
1459           }
1460       }
1461       if ((strlen(name) > 2) &&
1462           (GetValueFromSplayTree(fx_info->symbols,name) == (const char *) NULL))
1463         {
1464           PixelInfo
1465             *color;
1466
1467           color=(PixelInfo *) GetValueFromSplayTree(fx_info->colors,name);
1468           if (color != (PixelInfo *) NULL)
1469             {
1470               pixel=(*color);
1471               p+=strlen(name);
1472             }
1473           else
1474             if (QueryMagickColor(name,&pixel,fx_info->exception) != MagickFalse)
1475               {
1476                 (void) AddValueToSplayTree(fx_info->colors,ConstantString(name),
1477                   ClonePixelInfo(&pixel));
1478                 p+=strlen(name);
1479               }
1480         }
1481     }
1482   (void) CopyMagickString(symbol,p,MaxTextExtent);
1483   StripString(symbol);
1484   if (*symbol == '\0')
1485     {
1486       switch (channel)
1487       {
1488         case RedPixelChannel: return(QuantumScale*pixel.red);
1489         case GreenPixelChannel: return(QuantumScale*pixel.green);
1490         case BluePixelChannel: return(QuantumScale*pixel.blue);
1491         case BlackPixelChannel:
1492         {
1493           if (image->colorspace != CMYKColorspace)
1494             {
1495               (void) ThrowMagickException(exception,GetMagickModule(),
1496                 OptionError,"ColorSeparatedImageRequired","`%s'",
1497                 image->filename);
1498               return(0.0);
1499             }
1500           return(QuantumScale*pixel.black);
1501         }
1502         case AlphaPixelChannel:
1503         {
1504           MagickRealType
1505             alpha;
1506
1507           if (pixel.matte == MagickFalse)
1508             return(1.0);
1509           alpha=(MagickRealType) (QuantumScale*pixel.alpha);
1510           return(alpha);
1511         }
1512         case IntensityPixelChannel:
1513         {
1514           return(QuantumScale*GetPixelInfoIntensity(&pixel));
1515         }
1516         default:
1517           break;
1518       }
1519       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1520         "UnableToParseExpression","`%s'",p);
1521       return(0.0);
1522     }
1523   switch (*symbol)
1524   {
1525     case 'A':
1526     case 'a':
1527     {
1528       if (LocaleCompare(symbol,"a") == 0)
1529         return((MagickRealType) (QuantumScale*pixel.alpha));
1530       break;
1531     }
1532     case 'B':
1533     case 'b':
1534     {
1535       if (LocaleCompare(symbol,"b") == 0)
1536         return(QuantumScale*pixel.blue);
1537       break;
1538     }
1539     case 'C':
1540     case 'c':
1541     {
1542       if (LocaleNCompare(symbol,"channel",7) == 0)
1543         {
1544           GeometryInfo
1545             channel_info;
1546
1547           MagickStatusType
1548             flags;
1549
1550           flags=ParseGeometry(symbol+7,&channel_info);
1551           if (image->colorspace == CMYKColorspace)
1552             switch (channel)
1553             {
1554               case CyanPixelChannel:
1555               {
1556                 if ((flags & RhoValue) == 0)
1557                   return(0.0);
1558                 return(channel_info.rho);
1559               }
1560               case MagentaPixelChannel:
1561               {
1562                 if ((flags & SigmaValue) == 0)
1563                   return(0.0);
1564                 return(channel_info.sigma);
1565               }
1566               case YellowPixelChannel:
1567               {
1568                 if ((flags & XiValue) == 0)
1569                   return(0.0);
1570                 return(channel_info.xi);
1571               }
1572               case BlackPixelChannel:
1573               {
1574                 if ((flags & PsiValue) == 0)
1575                   return(0.0);
1576                 return(channel_info.psi);
1577               }
1578               case AlphaPixelChannel:
1579               {
1580                 if ((flags & ChiValue) == 0)
1581                   return(0.0);
1582                 return(channel_info.chi);
1583               }
1584               default:
1585                 return(0.0);
1586             }
1587           switch (channel)
1588           {
1589             case RedPixelChannel:
1590             {
1591               if ((flags & RhoValue) == 0)
1592                 return(0.0);
1593               return(channel_info.rho);
1594             }
1595             case GreenPixelChannel:
1596             {
1597               if ((flags & SigmaValue) == 0)
1598                 return(0.0);
1599               return(channel_info.sigma);
1600             }
1601             case BluePixelChannel:
1602             {
1603               if ((flags & XiValue) == 0)
1604                 return(0.0);
1605               return(channel_info.xi);
1606             }
1607             case BlackPixelChannel:
1608             {
1609               if ((flags & ChiValue) == 0)
1610                 return(0.0);
1611               return(channel_info.chi);
1612             }
1613             case AlphaPixelChannel:
1614             {
1615               if ((flags & PsiValue) == 0)
1616                 return(0.0);
1617               return(channel_info.psi);
1618             }
1619             default:
1620               return(0.0);
1621           }
1622           return(0.0);
1623         }
1624       if (LocaleCompare(symbol,"c") == 0)
1625         return(QuantumScale*pixel.red);
1626       break;
1627     }
1628     case 'D':
1629     case 'd':
1630     {
1631       if (LocaleNCompare(symbol,"depth",5) == 0)
1632         return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1633       break;
1634     }
1635     case 'G':
1636     case 'g':
1637     {
1638       if (LocaleCompare(symbol,"g") == 0)
1639         return(QuantumScale*pixel.green);
1640       break;
1641     }
1642     case 'K':
1643     case 'k':
1644     {
1645       if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1646         return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1647       if (LocaleCompare(symbol,"k") == 0)
1648         {
1649           if (image->colorspace != CMYKColorspace)
1650             {
1651               (void) ThrowMagickException(exception,GetMagickModule(),
1652                 OptionError,"ColorSeparatedImageRequired","`%s'",
1653                 image->filename);
1654               return(0.0);
1655             }
1656           return(QuantumScale*pixel.black);
1657         }
1658       break;
1659     }
1660     case 'H':
1661     case 'h':
1662     {
1663       if (LocaleCompare(symbol,"h") == 0)
1664         return((MagickRealType) image->rows);
1665       if (LocaleCompare(symbol,"hue") == 0)
1666         {
1667           double
1668             hue,
1669             lightness,
1670             saturation;
1671
1672           ConvertRGBToHSL(ClampToQuantum(pixel.red),ClampToQuantum(pixel.green),
1673             ClampToQuantum(pixel.blue),&hue,&saturation,&lightness);
1674           return(hue);
1675         }
1676       break;
1677     }
1678     case 'I':
1679     case 'i':
1680     {
1681       if ((LocaleCompare(symbol,"image.depth") == 0) ||
1682           (LocaleCompare(symbol,"image.minima") == 0) ||
1683           (LocaleCompare(symbol,"image.maxima") == 0) ||
1684           (LocaleCompare(symbol,"image.mean") == 0) ||
1685           (LocaleCompare(symbol,"image.kurtosis") == 0) ||
1686           (LocaleCompare(symbol,"image.skewness") == 0) ||
1687           (LocaleCompare(symbol,"image.standard_deviation") == 0))
1688         return(FxChannelStatistics(fx_info,image,channel,symbol+6,exception));
1689       if (LocaleCompare(symbol,"image.resolution.x") == 0)
1690         return(image->x_resolution);
1691       if (LocaleCompare(symbol,"image.resolution.y") == 0)
1692         return(image->y_resolution);
1693       if (LocaleCompare(symbol,"intensity") == 0)
1694         return(QuantumScale*GetPixelInfoIntensity(&pixel));
1695       if (LocaleCompare(symbol,"i") == 0)
1696         return((MagickRealType) x);
1697       break;
1698     }
1699     case 'J':
1700     case 'j':
1701     {
1702       if (LocaleCompare(symbol,"j") == 0)
1703         return((MagickRealType) y);
1704       break;
1705     }
1706     case 'L':
1707     case 'l':
1708     {
1709       if (LocaleCompare(symbol,"lightness") == 0)
1710         {
1711           double
1712             hue,
1713             lightness,
1714             saturation;
1715
1716           ConvertRGBToHSL(ClampToQuantum(pixel.red),ClampToQuantum(pixel.green),
1717             ClampToQuantum(pixel.blue),&hue,&saturation,&lightness);
1718           return(lightness);
1719         }
1720       if (LocaleCompare(symbol,"luminance") == 0)
1721         {
1722           double
1723             luminence;
1724
1725           luminence=0.2126*pixel.red+0.7152*pixel.green+0.0722*pixel.blue;
1726           return(QuantumScale*luminence);
1727         }
1728       break;
1729     }
1730     case 'M':
1731     case 'm':
1732     {
1733       if (LocaleNCompare(symbol,"maxima",6) == 0)
1734         return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1735       if (LocaleNCompare(symbol,"mean",4) == 0)
1736         return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1737       if (LocaleNCompare(symbol,"minima",6) == 0)
1738         return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1739       if (LocaleCompare(symbol,"m") == 0)
1740         return(QuantumScale*pixel.blue);
1741       break;
1742     }
1743     case 'N':
1744     case 'n':
1745     {
1746       if (LocaleCompare(symbol,"n") == 0)
1747         return((MagickRealType) GetImageListLength(fx_info->images));
1748       break;
1749     }
1750     case 'O':
1751     case 'o':
1752     {
1753       if (LocaleCompare(symbol,"o") == 0)
1754         return(QuantumScale*pixel.alpha);
1755       break;
1756     }
1757     case 'P':
1758     case 'p':
1759     {
1760       if (LocaleCompare(symbol,"page.height") == 0)
1761         return((MagickRealType) image->page.height);
1762       if (LocaleCompare(symbol,"page.width") == 0)
1763         return((MagickRealType) image->page.width);
1764       if (LocaleCompare(symbol,"page.x") == 0)
1765         return((MagickRealType) image->page.x);
1766       if (LocaleCompare(symbol,"page.y") == 0)
1767         return((MagickRealType) image->page.y);
1768       break;
1769     }
1770     case 'R':
1771     case 'r':
1772     {
1773       if (LocaleCompare(symbol,"resolution.x") == 0)
1774         return(image->x_resolution);
1775       if (LocaleCompare(symbol,"resolution.y") == 0)
1776         return(image->y_resolution);
1777       if (LocaleCompare(symbol,"r") == 0)
1778         return(QuantumScale*pixel.red);
1779       break;
1780     }
1781     case 'S':
1782     case 's':
1783     {
1784       if (LocaleCompare(symbol,"saturation") == 0)
1785         {
1786           double
1787             hue,
1788             lightness,
1789             saturation;
1790
1791           ConvertRGBToHSL(ClampToQuantum(pixel.red),ClampToQuantum(pixel.green),
1792             ClampToQuantum(pixel.blue),&hue,&saturation,&lightness);
1793           return(saturation);
1794         }
1795       if (LocaleNCompare(symbol,"skewness",8) == 0)
1796         return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1797       if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1798         return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1799       break;
1800     }
1801     case 'T':
1802     case 't':
1803     {
1804       if (LocaleCompare(symbol,"t") == 0)
1805         return((MagickRealType) GetImageIndexInList(fx_info->images));
1806       break;
1807     }
1808     case 'W':
1809     case 'w':
1810     {
1811       if (LocaleCompare(symbol,"w") == 0)
1812         return((MagickRealType) image->columns);
1813       break;
1814     }
1815     case 'Y':
1816     case 'y':
1817     {
1818       if (LocaleCompare(symbol,"y") == 0)
1819         return(QuantumScale*pixel.green);
1820       break;
1821     }
1822     case 'Z':
1823     case 'z':
1824     {
1825       if (LocaleCompare(symbol,"z") == 0)
1826         {
1827           MagickRealType
1828             depth;
1829
1830           depth=(MagickRealType) GetImageDepth(image,fx_info->exception);
1831           return(depth);
1832         }
1833       break;
1834     }
1835     default:
1836       break;
1837   }
1838   value=(const char *) GetValueFromSplayTree(fx_info->symbols,symbol);
1839   if (value != (const char *) NULL)
1840     return((MagickRealType) InterpretLocaleValue(value,(char **) NULL));
1841   (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1842     "UnableToParseExpression","`%s'",symbol);
1843   return(0.0);
1844 }
1845
1846 static const char *FxOperatorPrecedence(const char *expression,
1847   ExceptionInfo *exception)
1848 {
1849   typedef enum
1850   {
1851     UndefinedPrecedence,
1852     NullPrecedence,
1853     BitwiseComplementPrecedence,
1854     ExponentPrecedence,
1855     ExponentialNotationPrecedence,
1856     MultiplyPrecedence,
1857     AdditionPrecedence,
1858     ShiftPrecedence,
1859     RelationalPrecedence,
1860     EquivalencyPrecedence,
1861     BitwiseAndPrecedence,
1862     BitwiseOrPrecedence,
1863     LogicalAndPrecedence,
1864     LogicalOrPrecedence,
1865     TernaryPrecedence,
1866     AssignmentPrecedence,
1867     CommaPrecedence,
1868     SeparatorPrecedence
1869   } FxPrecedence;
1870
1871   FxPrecedence
1872     precedence,
1873     target;
1874
1875   register const char
1876     *subexpression;
1877
1878   register int
1879     c;
1880
1881   size_t
1882     level;
1883
1884   c=0;
1885   level=0;
1886   subexpression=(const char *) NULL;
1887   target=NullPrecedence;
1888   while (*expression != '\0')
1889   {
1890     precedence=UndefinedPrecedence;
1891     if ((isspace((int) ((char) *expression)) != 0) || (c == (int) '@'))
1892       {
1893         expression++;
1894         continue;
1895       }
1896     switch (*expression)
1897     {
1898       case 'A':
1899       case 'a':
1900       {
1901 #if defined(MAGICKCORE_HAVE_ACOSH)
1902         if (LocaleNCompare(expression,"acosh",5) == 0)
1903           {
1904             expression+=5;
1905             break;
1906           }
1907 #endif
1908 #if defined(MAGICKCORE_HAVE_ASINH)
1909         if (LocaleNCompare(expression,"asinh",5) == 0)
1910           {
1911             expression+=5;
1912             break;
1913           }
1914 #endif
1915 #if defined(MAGICKCORE_HAVE_ATANH)
1916         if (LocaleNCompare(expression,"atanh",5) == 0)
1917           {
1918             expression+=5;
1919             break;
1920           }
1921 #endif
1922         break;
1923       }
1924       case 'J':
1925       case 'j':
1926       {
1927         if ((LocaleNCompare(expression,"j0",2) == 0) ||
1928             (LocaleNCompare(expression,"j1",2) == 0))
1929           {
1930             expression+=2;
1931             break;
1932           }
1933         break;
1934       }
1935       case '#':
1936       {
1937         while (isxdigit((int) ((unsigned char) *(expression+1))) != 0)
1938           expression++;
1939         break;
1940       }
1941       default:
1942         break;
1943     }
1944     if ((c == (int) '{') || (c == (int) '['))
1945       level++;
1946     else
1947       if ((c == (int) '}') || (c == (int) ']'))
1948         level--;
1949     if (level == 0)
1950       switch ((unsigned char) *expression)
1951       {
1952         case '~':
1953         case '!':
1954         {
1955           precedence=BitwiseComplementPrecedence;
1956           break;
1957         }
1958         case '^':
1959         case '@':
1960         {
1961           precedence=ExponentPrecedence;
1962           break;
1963         }
1964         default:
1965         {
1966           if (((c != 0) && ((isdigit((int) ((char) c)) != 0) ||
1967                (strchr(")",c) != (char *) NULL))) &&
1968               (((islower((int) ((char) *expression)) != 0) ||
1969                (strchr("(",(int) *expression) != (char *) NULL)) ||
1970                ((isdigit((int) ((char) c)) == 0) &&
1971                 (isdigit((int) ((char) *expression)) != 0))) &&
1972               (strchr("xy",(int) *expression) == (char *) NULL))
1973             precedence=MultiplyPrecedence;
1974           break;
1975         }
1976         case '*':
1977         case '/':
1978         case '%':
1979         {
1980           precedence=MultiplyPrecedence;
1981           break;
1982         }
1983         case '+':
1984         case '-':
1985         {
1986           if ((strchr("(+-/*%:&^|<>~,",c) == (char *) NULL) ||
1987               (isalpha(c) != 0))
1988             precedence=AdditionPrecedence;
1989           break;
1990         }
1991         case LeftShiftOperator:
1992         case RightShiftOperator:
1993         {
1994           precedence=ShiftPrecedence;
1995           break;
1996         }
1997         case '<':
1998         case LessThanEqualOperator:
1999         case GreaterThanEqualOperator:
2000         case '>':
2001         {
2002           precedence=RelationalPrecedence;
2003           break;
2004         }
2005         case EqualOperator:
2006         case NotEqualOperator:
2007         {
2008           precedence=EquivalencyPrecedence;
2009           break;
2010         }
2011         case '&':
2012         {
2013           precedence=BitwiseAndPrecedence;
2014           break;
2015         }
2016         case '|':
2017         {
2018           precedence=BitwiseOrPrecedence;
2019           break;
2020         }
2021         case LogicalAndOperator:
2022         {
2023           precedence=LogicalAndPrecedence;
2024           break;
2025         }
2026         case LogicalOrOperator:
2027         {
2028           precedence=LogicalOrPrecedence;
2029           break;
2030         }
2031         case ExponentialNotation:
2032         {
2033           precedence=ExponentialNotationPrecedence;
2034           break;
2035         }
2036         case ':':
2037         case '?':
2038         {
2039           precedence=TernaryPrecedence;
2040           break;
2041         }
2042         case '=':
2043         {
2044           precedence=AssignmentPrecedence;
2045           break;
2046         }
2047         case ',':
2048         {
2049           precedence=CommaPrecedence;
2050           break;
2051         }
2052         case ';':
2053         {
2054           precedence=SeparatorPrecedence;
2055           break;
2056         }
2057       }
2058     if ((precedence == BitwiseComplementPrecedence) ||
2059         (precedence == TernaryPrecedence) ||
2060         (precedence == AssignmentPrecedence))
2061       {
2062         if (precedence > target)
2063           {
2064             /*
2065               Right-to-left associativity.
2066             */
2067             target=precedence;
2068             subexpression=expression;
2069           }
2070       }
2071     else
2072       if (precedence >= target)
2073         {
2074           /*
2075             Left-to-right associativity.
2076           */
2077           target=precedence;
2078           subexpression=expression;
2079         }
2080     if (strchr("(",(int) *expression) != (char *) NULL)
2081       expression=FxSubexpression(expression,exception);
2082     c=(int) (*expression++);
2083   }
2084   return(subexpression);
2085 }
2086
2087 static MagickRealType FxEvaluateSubexpression(FxInfo *fx_info,
2088   const PixelChannel channel,const ssize_t x,const ssize_t y,
2089   const char *expression,MagickRealType *beta,ExceptionInfo *exception)
2090 {
2091   char
2092     *q,
2093     subexpression[MaxTextExtent];
2094
2095   MagickRealType
2096     alpha,
2097     gamma;
2098
2099   register const char
2100     *p;
2101
2102   *beta=0.0;
2103   if (exception->severity != UndefinedException)
2104     return(0.0);
2105   while (isspace((int) *expression) != 0)
2106     expression++;
2107   if (*expression == '\0')
2108     {
2109       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
2110         "MissingExpression","`%s'",expression);
2111       return(0.0);
2112     }
2113   *subexpression='\0';
2114   p=FxOperatorPrecedence(expression,exception);
2115   if (p != (const char *) NULL)
2116     {
2117       (void) CopyMagickString(subexpression,expression,(size_t)
2118         (p-expression+1));
2119       alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2120         exception);
2121       switch ((unsigned char) *p)
2122       {
2123         case '~':
2124         {
2125           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2126           *beta=(MagickRealType) (~(size_t) *beta);
2127           return(*beta);
2128         }
2129         case '!':
2130         {
2131           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2132           return(*beta == 0.0 ? 1.0 : 0.0);
2133         }
2134         case '^':
2135         {
2136           *beta=pow((double) alpha,(double) FxEvaluateSubexpression(fx_info,
2137             channel,x,y,++p,beta,exception));
2138           return(*beta);
2139         }
2140         case '*':
2141         case ExponentialNotation:
2142         {
2143           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2144           return(alpha*(*beta));
2145         }
2146         case '/':
2147         {
2148           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2149           if (*beta == 0.0)
2150             {
2151               if (exception->severity == UndefinedException)
2152                 (void) ThrowMagickException(exception,GetMagickModule(),
2153                   OptionError,"DivideByZero","`%s'",expression);
2154               return(0.0);
2155             }
2156           return(alpha/(*beta));
2157         }
2158         case '%':
2159         {
2160           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2161           *beta=fabs(floor(((double) *beta)+0.5));
2162           if (*beta == 0.0)
2163             {
2164               (void) ThrowMagickException(exception,GetMagickModule(),
2165                 OptionError,"DivideByZero","`%s'",expression);
2166               return(0.0);
2167             }
2168           return(fmod((double) alpha,(double) *beta));
2169         }
2170         case '+':
2171         {
2172           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2173           return(alpha+(*beta));
2174         }
2175         case '-':
2176         {
2177           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2178           return(alpha-(*beta));
2179         }
2180         case LeftShiftOperator:
2181         {
2182           gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2183           *beta=(MagickRealType) ((size_t) (alpha+0.5) << (size_t)
2184             (gamma+0.5));
2185           return(*beta);
2186         }
2187         case RightShiftOperator:
2188         {
2189           gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2190           *beta=(MagickRealType) ((size_t) (alpha+0.5) >> (size_t)
2191             (gamma+0.5));
2192           return(*beta);
2193         }
2194         case '<':
2195         {
2196           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2197           return(alpha < *beta ? 1.0 : 0.0);
2198         }
2199         case LessThanEqualOperator:
2200         {
2201           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2202           return(alpha <= *beta ? 1.0 : 0.0);
2203         }
2204         case '>':
2205         {
2206           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2207           return(alpha > *beta ? 1.0 : 0.0);
2208         }
2209         case GreaterThanEqualOperator:
2210         {
2211           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2212           return(alpha >= *beta ? 1.0 : 0.0);
2213         }
2214         case EqualOperator:
2215         {
2216           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2217           return(fabs(alpha-(*beta)) <= MagickEpsilon ? 1.0 : 0.0);
2218         }
2219         case NotEqualOperator:
2220         {
2221           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2222           return(fabs(alpha-(*beta)) > MagickEpsilon ? 1.0 : 0.0);
2223         }
2224         case '&':
2225         {
2226           gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2227           *beta=(MagickRealType) ((size_t) (alpha+0.5) & (size_t)
2228             (gamma+0.5));
2229           return(*beta);
2230         }
2231         case '|':
2232         {
2233           gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2234           *beta=(MagickRealType) ((size_t) (alpha+0.5) | (size_t)
2235             (gamma+0.5));
2236           return(*beta);
2237         }
2238         case LogicalAndOperator:
2239         {
2240           gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2241           *beta=(alpha > 0.0) && (gamma > 0.0) ? 1.0 : 0.0;
2242           return(*beta);
2243         }
2244         case LogicalOrOperator:
2245         {
2246           gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2247           *beta=(alpha > 0.0) || (gamma > 0.0) ? 1.0 : 0.0;
2248           return(*beta);
2249         }
2250         case '?':
2251         {
2252           MagickRealType
2253             gamma;
2254
2255           (void) CopyMagickString(subexpression,++p,MaxTextExtent);
2256           q=subexpression;
2257           p=StringToken(":",&q);
2258           if (q == (char *) NULL)
2259             {
2260               (void) ThrowMagickException(exception,GetMagickModule(),
2261                 OptionError,"UnableToParseExpression","`%s'",subexpression);
2262               return(0.0);
2263             }
2264           if (fabs((double) alpha) > MagickEpsilon)
2265             gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,exception);
2266           else
2267             gamma=FxEvaluateSubexpression(fx_info,channel,x,y,q,beta,exception);
2268           return(gamma);
2269         }
2270         case '=':
2271         {
2272           char
2273             numeric[MaxTextExtent];
2274
2275           q=subexpression;
2276           while (isalpha((int) ((unsigned char) *q)) != 0)
2277             q++;
2278           if (*q != '\0')
2279             {
2280               (void) ThrowMagickException(exception,GetMagickModule(),
2281                 OptionError,"UnableToParseExpression","`%s'",subexpression);
2282               return(0.0);
2283             }
2284           ClearMagickException(exception);
2285           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2286           (void) FormatLocaleString(numeric,MaxTextExtent,"%g",(double)
2287             *beta);
2288           (void) DeleteNodeFromSplayTree(fx_info->symbols,subexpression);
2289           (void) AddValueToSplayTree(fx_info->symbols,ConstantString(
2290             subexpression),ConstantString(numeric));
2291           return(*beta);
2292         }
2293         case ',':
2294         {
2295           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2296           return(alpha);
2297         }
2298         case ';':
2299         {
2300           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
2301           return(*beta);
2302         }
2303         default:
2304         {
2305           gamma=alpha*FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,
2306             exception);
2307           return(gamma);
2308         }
2309       }
2310     }
2311   if (strchr("(",(int) *expression) != (char *) NULL)
2312     {
2313       (void) CopyMagickString(subexpression,expression+1,MaxTextExtent);
2314       subexpression[strlen(subexpression)-1]='\0';
2315       gamma=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
2316         exception);
2317       return(gamma);
2318     }
2319   switch (*expression)
2320   {
2321     case '+':
2322     {
2323       gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2324         exception);
2325       return(1.0*gamma);
2326     }
2327     case '-':
2328     {
2329       gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2330         exception);
2331       return(-1.0*gamma);
2332     }
2333     case '~':
2334     {
2335       gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
2336         exception);
2337       return((MagickRealType) (~(size_t) (gamma+0.5)));
2338     }
2339     case 'A':
2340     case 'a':
2341     {
2342       if (LocaleNCompare(expression,"abs",3) == 0)
2343         {
2344           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2345             exception);
2346           return((MagickRealType) fabs((double) alpha));
2347         }
2348 #if defined(MAGICKCORE_HAVE_ACOSH)
2349       if (LocaleNCompare(expression,"acosh",5) == 0)
2350         {
2351           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2352             exception);
2353           return((MagickRealType) acosh((double) alpha));
2354         }
2355 #endif
2356       if (LocaleNCompare(expression,"acos",4) == 0)
2357         {
2358           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2359             exception);
2360           return((MagickRealType) acos((double) alpha));
2361         }
2362 #if defined(MAGICKCORE_HAVE_J1)
2363       if (LocaleNCompare(expression,"airy",4) == 0)
2364         {
2365           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2366             exception);
2367           if (alpha == 0.0)
2368             return(1.0);
2369           gamma=2.0*j1((double) (MagickPI*alpha))/(MagickPI*alpha);
2370           return(gamma*gamma);
2371         }
2372 #endif
2373 #if defined(MAGICKCORE_HAVE_ASINH)
2374       if (LocaleNCompare(expression,"asinh",5) == 0)
2375         {
2376           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2377             exception);
2378           return((MagickRealType) asinh((double) alpha));
2379         }
2380 #endif
2381       if (LocaleNCompare(expression,"asin",4) == 0)
2382         {
2383           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2384             exception);
2385           return((MagickRealType) asin((double) alpha));
2386         }
2387       if (LocaleNCompare(expression,"alt",3) == 0)
2388         {
2389           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2390             exception);
2391           return(((ssize_t) alpha) & 0x01 ? -1.0 : 1.0);
2392         }
2393       if (LocaleNCompare(expression,"atan2",5) == 0)
2394         {
2395           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2396             exception);
2397           return((MagickRealType) atan2((double) alpha,(double) *beta));
2398         }
2399 #if defined(MAGICKCORE_HAVE_ATANH)
2400       if (LocaleNCompare(expression,"atanh",5) == 0)
2401         {
2402           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2403             exception);
2404           return((MagickRealType) atanh((double) alpha));
2405         }
2406 #endif
2407       if (LocaleNCompare(expression,"atan",4) == 0)
2408         {
2409           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2410             exception);
2411           return((MagickRealType) atan((double) alpha));
2412         }
2413       if (LocaleCompare(expression,"a") == 0)
2414         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2415       break;
2416     }
2417     case 'B':
2418     case 'b':
2419     {
2420       if (LocaleCompare(expression,"b") == 0)
2421         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2422       break;
2423     }
2424     case 'C':
2425     case 'c':
2426     {
2427       if (LocaleNCompare(expression,"ceil",4) == 0)
2428         {
2429           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2430             exception);
2431           return((MagickRealType) ceil((double) alpha));
2432         }
2433       if (LocaleNCompare(expression,"cosh",4) == 0)
2434         {
2435           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2436             exception);
2437           return((MagickRealType) cosh((double) alpha));
2438         }
2439       if (LocaleNCompare(expression,"cos",3) == 0)
2440         {
2441           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2442             exception);
2443           return((MagickRealType) cos((double) alpha));
2444         }
2445       if (LocaleCompare(expression,"c") == 0)
2446         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2447       break;
2448     }
2449     case 'D':
2450     case 'd':
2451     {
2452       if (LocaleNCompare(expression,"debug",5) == 0)
2453         {
2454           const char
2455             *type;
2456
2457           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2458             exception);
2459           if (fx_info->images->colorspace == CMYKColorspace)
2460             switch (channel)
2461             {
2462               case CyanPixelChannel: type="cyan"; break;
2463               case MagentaPixelChannel: type="magenta"; break;
2464               case YellowPixelChannel: type="yellow"; break;
2465               case AlphaPixelChannel: type="opacity"; break;
2466               case BlackPixelChannel: type="black"; break;
2467               default: type="unknown"; break;
2468             }
2469           else
2470             switch (channel)
2471             {
2472               case RedPixelChannel: type="red"; break;
2473               case GreenPixelChannel: type="green"; break;
2474               case BluePixelChannel: type="blue"; break;
2475               case AlphaPixelChannel: type="opacity"; break;
2476               default: type="unknown"; break;
2477             }
2478           (void) CopyMagickString(subexpression,expression+6,MaxTextExtent);
2479           if (strlen(subexpression) > 1)
2480             subexpression[strlen(subexpression)-1]='\0';
2481           if (fx_info->file != (FILE *) NULL)
2482             (void) FormatLocaleFile(fx_info->file,
2483               "%s[%.20g,%.20g].%s: %s=%.*g\n",fx_info->images->filename,
2484                (double) x,(double) y,type,subexpression,GetMagickPrecision(),
2485                (double) alpha);
2486           return(0.0);
2487         }
2488       break;
2489     }
2490     case 'E':
2491     case 'e':
2492     {
2493       if (LocaleCompare(expression,"epsilon") == 0)
2494         return((MagickRealType) MagickEpsilon);
2495       if (LocaleNCompare(expression,"exp",3) == 0)
2496         {
2497           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2498             exception);
2499           return((MagickRealType) exp((double) alpha));
2500         }
2501       if (LocaleCompare(expression,"e") == 0)
2502         return((MagickRealType) 2.7182818284590452354);
2503       break;
2504     }
2505     case 'F':
2506     case 'f':
2507     {
2508       if (LocaleNCompare(expression,"floor",5) == 0)
2509         {
2510           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2511             exception);
2512           return((MagickRealType) floor((double) alpha));
2513         }
2514       break;
2515     }
2516     case 'G':
2517     case 'g':
2518     {
2519       if (LocaleCompare(expression,"g") == 0)
2520         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2521       break;
2522     }
2523     case 'H':
2524     case 'h':
2525     {
2526       if (LocaleCompare(expression,"h") == 0)
2527         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2528       if (LocaleCompare(expression,"hue") == 0)
2529         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2530       if (LocaleNCompare(expression,"hypot",5) == 0)
2531         {
2532           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2533             exception);
2534           return((MagickRealType) hypot((double) alpha,(double) *beta));
2535         }
2536       break;
2537     }
2538     case 'K':
2539     case 'k':
2540     {
2541       if (LocaleCompare(expression,"k") == 0)
2542         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2543       break;
2544     }
2545     case 'I':
2546     case 'i':
2547     {
2548       if (LocaleCompare(expression,"intensity") == 0)
2549         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2550       if (LocaleNCompare(expression,"int",3) == 0)
2551         {
2552           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2553             exception);
2554           return((MagickRealType) floor(alpha));
2555         }
2556       if (LocaleCompare(expression,"i") == 0)
2557         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2558       break;
2559     }
2560     case 'J':
2561     case 'j':
2562     {
2563       if (LocaleCompare(expression,"j") == 0)
2564         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2565 #if defined(MAGICKCORE_HAVE_J0)
2566       if (LocaleNCompare(expression,"j0",2) == 0)
2567         {
2568           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2569             exception);
2570           return((MagickRealType) j0((double) alpha));
2571         }
2572 #endif
2573 #if defined(MAGICKCORE_HAVE_J1)
2574       if (LocaleNCompare(expression,"j1",2) == 0)
2575         {
2576           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2577             exception);
2578           return((MagickRealType) j1((double) alpha));
2579         }
2580 #endif
2581 #if defined(MAGICKCORE_HAVE_J1)
2582       if (LocaleNCompare(expression,"jinc",4) == 0)
2583         {
2584           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2585             exception);
2586           if (alpha == 0.0)
2587             return(1.0);
2588           gamma=(MagickRealType) (2.0*j1((double) (MagickPI*alpha))/
2589             (MagickPI*alpha));
2590           return(gamma);
2591         }
2592 #endif
2593       break;
2594     }
2595     case 'L':
2596     case 'l':
2597     {
2598       if (LocaleNCompare(expression,"ln",2) == 0)
2599         {
2600           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2601             exception);
2602           return((MagickRealType) log((double) alpha));
2603         }
2604       if (LocaleNCompare(expression,"logtwo",6) == 0)
2605         {
2606           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+6,beta,
2607             exception);
2608           return((MagickRealType) log10((double) alpha))/log10(2.0);
2609         }
2610       if (LocaleNCompare(expression,"log",3) == 0)
2611         {
2612           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2613             exception);
2614           return((MagickRealType) log10((double) alpha));
2615         }
2616       if (LocaleCompare(expression,"lightness") == 0)
2617         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2618       break;
2619     }
2620     case 'M':
2621     case 'm':
2622     {
2623       if (LocaleCompare(expression,"MaxRGB") == 0)
2624         return((MagickRealType) QuantumRange);
2625       if (LocaleNCompare(expression,"maxima",6) == 0)
2626         break;
2627       if (LocaleNCompare(expression,"max",3) == 0)
2628         return(FxMax(fx_info,channel,x,y,expression+3,exception));
2629       if (LocaleNCompare(expression,"minima",6) == 0)
2630         break;
2631       if (LocaleNCompare(expression,"min",3) == 0)
2632         return(FxMin(fx_info,channel,x,y,expression+3,exception));
2633       if (LocaleNCompare(expression,"mod",3) == 0)
2634         {
2635           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2636             exception);
2637           return((MagickRealType) fmod((double) alpha,(double) *beta));
2638         }
2639       if (LocaleCompare(expression,"m") == 0)
2640         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2641       break;
2642     }
2643     case 'N':
2644     case 'n':
2645     {
2646       if (LocaleCompare(expression,"n") == 0)
2647         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2648       break;
2649     }
2650     case 'O':
2651     case 'o':
2652     {
2653       if (LocaleCompare(expression,"Opaque") == 0)
2654         return(1.0);
2655       if (LocaleCompare(expression,"o") == 0)
2656         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2657       break;
2658     }
2659     case 'P':
2660     case 'p':
2661     {
2662       if (LocaleCompare(expression,"pi") == 0)
2663         return((MagickRealType) MagickPI);
2664       if (LocaleNCompare(expression,"pow",3) == 0)
2665         {
2666           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2667             exception);
2668           return((MagickRealType) pow((double) alpha,(double) *beta));
2669         }
2670       if (LocaleCompare(expression,"p") == 0)
2671         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2672       break;
2673     }
2674     case 'Q':
2675     case 'q':
2676     {
2677       if (LocaleCompare(expression,"QuantumRange") == 0)
2678         return((MagickRealType) QuantumRange);
2679       if (LocaleCompare(expression,"QuantumScale") == 0)
2680         return((MagickRealType) QuantumScale);
2681       break;
2682     }
2683     case 'R':
2684     case 'r':
2685     {
2686       if (LocaleNCompare(expression,"rand",4) == 0)
2687         return((MagickRealType) GetPseudoRandomValue(fx_info->random_info));
2688       if (LocaleNCompare(expression,"round",5) == 0)
2689         {
2690           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2691             exception);
2692           return((MagickRealType) floor((double) alpha+0.5));
2693         }
2694       if (LocaleCompare(expression,"r") == 0)
2695         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2696       break;
2697     }
2698     case 'S':
2699     case 's':
2700     {
2701       if (LocaleCompare(expression,"saturation") == 0)
2702         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2703       if (LocaleNCompare(expression,"sign",4) == 0)
2704         {
2705           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2706             exception);
2707           return(alpha < 0.0 ? -1.0 : 1.0);
2708         }
2709       if (LocaleNCompare(expression,"sinc",4) == 0)
2710         {
2711           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2712             exception);
2713           if (alpha == 0)
2714             return(1.0);
2715           gamma=(MagickRealType) (sin((double) (MagickPI*alpha))/
2716             (MagickPI*alpha));
2717           return(gamma);
2718         }
2719       if (LocaleNCompare(expression,"sinh",4) == 0)
2720         {
2721           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2722             exception);
2723           return((MagickRealType) sinh((double) alpha));
2724         }
2725       if (LocaleNCompare(expression,"sin",3) == 0)
2726         {
2727           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2728             exception);
2729           return((MagickRealType) sin((double) alpha));
2730         }
2731       if (LocaleNCompare(expression,"sqrt",4) == 0)
2732         {
2733           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2734             exception);
2735           return((MagickRealType) sqrt((double) alpha));
2736         }
2737       if (LocaleCompare(expression,"s") == 0)
2738         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2739       break;
2740     }
2741     case 'T':
2742     case 't':
2743     {
2744       if (LocaleNCompare(expression,"tanh",4) == 0)
2745         {
2746           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2747             exception);
2748           return((MagickRealType) tanh((double) alpha));
2749         }
2750       if (LocaleNCompare(expression,"tan",3) == 0)
2751         {
2752           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2753             exception);
2754           return((MagickRealType) tan((double) alpha));
2755         }
2756       if (LocaleCompare(expression,"Transparent") == 0)
2757         return(0.0);
2758       if (LocaleNCompare(expression,"trunc",5) == 0)
2759         {
2760           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2761             exception);
2762           if (alpha >= 0.0)
2763             return((MagickRealType) floor((double) alpha));
2764           return((MagickRealType) ceil((double) alpha));
2765         }
2766       if (LocaleCompare(expression,"t") == 0)
2767         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2768       break;
2769     }
2770     case 'U':
2771     case 'u':
2772     {
2773       if (LocaleCompare(expression,"u") == 0)
2774         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2775       break;
2776     }
2777     case 'V':
2778     case 'v':
2779     {
2780       if (LocaleCompare(expression,"v") == 0)
2781         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2782       break;
2783     }
2784     case 'W':
2785     case 'w':
2786     {
2787       if (LocaleCompare(expression,"w") == 0)
2788         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2789       break;
2790     }
2791     case 'Y':
2792     case 'y':
2793     {
2794       if (LocaleCompare(expression,"y") == 0)
2795         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2796       break;
2797     }
2798     case 'Z':
2799     case 'z':
2800     {
2801       if (LocaleCompare(expression,"z") == 0)
2802         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2803       break;
2804     }
2805     default:
2806       break;
2807   }
2808   q=(char *) expression;
2809   alpha=InterpretLocaleValue(expression,&q);
2810   if (q == expression)
2811     return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2812   return(alpha);
2813 }
2814
2815 MagickPrivate MagickBooleanType FxEvaluateExpression(FxInfo *fx_info,
2816   MagickRealType *alpha,ExceptionInfo *exception)
2817 {
2818   MagickBooleanType
2819     status;
2820
2821   status=FxEvaluateChannelExpression(fx_info,GrayPixelChannel,0,0,alpha,
2822     exception);
2823   return(status);
2824 }
2825
2826 MagickExport MagickBooleanType FxPreprocessExpression(FxInfo *fx_info,
2827   MagickRealType *alpha,ExceptionInfo *exception)
2828 {
2829   FILE
2830     *file;
2831
2832   MagickBooleanType
2833     status;
2834
2835   file=fx_info->file;
2836   fx_info->file=(FILE *) NULL;
2837   status=FxEvaluateChannelExpression(fx_info,GrayPixelChannel,0,0,alpha,
2838     exception);
2839   fx_info->file=file;
2840   return(status);
2841 }
2842
2843 MagickPrivate MagickBooleanType FxEvaluateChannelExpression(FxInfo *fx_info,
2844   const PixelChannel channel,const ssize_t x,const ssize_t y,
2845   MagickRealType *alpha,ExceptionInfo *exception)
2846 {
2847   MagickRealType
2848     beta;
2849
2850   beta=0.0;
2851   *alpha=FxEvaluateSubexpression(fx_info,channel,x,y,fx_info->expression,&beta,
2852     exception);
2853   return(exception->severity == OptionError ? MagickFalse : MagickTrue);
2854 }
2855 \f
2856 /*
2857 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2858 %                                                                             %
2859 %                                                                             %
2860 %                                                                             %
2861 %     F x I m a g e                                                           %
2862 %                                                                             %
2863 %                                                                             %
2864 %                                                                             %
2865 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2866 %
2867 %  FxImage() applies a mathematical expression to the specified image.
2868 %
2869 %  The format of the FxImage method is:
2870 %
2871 %      Image *FxImage(const Image *image,const char *expression,
2872 %        ExceptionInfo *exception)
2873 %
2874 %  A description of each parameter follows:
2875 %
2876 %    o image: the image.
2877 %
2878 %    o expression: A mathematical expression.
2879 %
2880 %    o exception: return any errors or warnings in this structure.
2881 %
2882 */
2883
2884 static FxInfo **DestroyFxThreadSet(FxInfo **fx_info)
2885 {
2886   register ssize_t
2887     i;
2888
2889   assert(fx_info != (FxInfo **) NULL);
2890   for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
2891     if (fx_info[i] != (FxInfo *) NULL)
2892       fx_info[i]=DestroyFxInfo(fx_info[i]);
2893   fx_info=(FxInfo **) RelinquishMagickMemory(fx_info);
2894   return(fx_info);
2895 }
2896
2897 static FxInfo **AcquireFxThreadSet(const Image *image,const char *expression,
2898   ExceptionInfo *exception)
2899 {
2900   char
2901     *fx_expression;
2902
2903   FxInfo
2904     **fx_info;
2905
2906   MagickRealType
2907     alpha;
2908
2909   register ssize_t
2910     i;
2911
2912   size_t
2913     number_threads;
2914
2915   number_threads=GetOpenMPMaximumThreads();
2916   fx_info=(FxInfo **) AcquireQuantumMemory(number_threads,sizeof(*fx_info));
2917   if (fx_info == (FxInfo **) NULL)
2918     return((FxInfo **) NULL);
2919   (void) ResetMagickMemory(fx_info,0,number_threads*sizeof(*fx_info));
2920   if (*expression != '@')
2921     fx_expression=ConstantString(expression);
2922   else
2923     fx_expression=FileToString(expression+1,~0,exception);
2924   for (i=0; i < (ssize_t) number_threads; i++)
2925   {
2926     fx_info[i]=AcquireFxInfo(image,fx_expression);
2927     if (fx_info[i] == (FxInfo *) NULL)
2928       return(DestroyFxThreadSet(fx_info));
2929     (void) FxPreprocessExpression(fx_info[i],&alpha,fx_info[i]->exception);
2930   }
2931   fx_expression=DestroyString(fx_expression);
2932   return(fx_info);
2933 }
2934
2935 MagickExport Image *FxImage(const Image *image,const char *expression,
2936   ExceptionInfo *exception)
2937 {
2938 #define FxImageTag  "Fx/Image"
2939
2940   CacheView
2941     *fx_view,
2942     *image_view;
2943
2944   FxInfo
2945     **restrict fx_info;
2946
2947   Image
2948     *fx_image;
2949
2950   MagickBooleanType
2951     status;
2952
2953   MagickOffsetType
2954     progress;
2955
2956   MagickRealType
2957     alpha;
2958
2959   ssize_t
2960     y;
2961
2962   assert(image != (Image *) NULL);
2963   assert(image->signature == MagickSignature);
2964   if (image->debug != MagickFalse)
2965     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2966   fx_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
2967   if (fx_image == (Image *) NULL)
2968     return((Image *) NULL);
2969   if (SetImageStorageClass(fx_image,DirectClass,exception) == MagickFalse)
2970     {
2971       fx_image=DestroyImage(fx_image);
2972       return((Image *) NULL);
2973     }
2974   fx_info=AcquireFxThreadSet(image,expression,exception);
2975   if (fx_info == (FxInfo **) NULL)
2976     {
2977       fx_image=DestroyImage(fx_image);
2978       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2979     }
2980   status=FxPreprocessExpression(fx_info[0],&alpha,exception);
2981   if (status == MagickFalse)
2982     {
2983       fx_image=DestroyImage(fx_image);
2984       fx_info=DestroyFxThreadSet(fx_info);
2985       return((Image *) NULL);
2986     }
2987   /*
2988     Fx image.
2989   */
2990   status=MagickTrue;
2991   progress=0;
2992   image_view=AcquireCacheView(image);
2993   fx_view=AcquireCacheView(fx_image);
2994 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2995   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2996 #endif
2997   for (y=0; y < (ssize_t) fx_image->rows; y++)
2998   {
2999     const int
3000       id = GetOpenMPThreadId();
3001
3002     register const Quantum
3003       *restrict p;
3004
3005     register Quantum
3006       *restrict q;
3007
3008     register ssize_t
3009       x;
3010
3011     if (status == MagickFalse)
3012       continue;
3013     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3014     q=GetCacheViewAuthenticPixels(fx_view,0,y,fx_image->columns,1,exception);
3015     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3016       {
3017         status=MagickFalse;
3018         continue;
3019       }
3020     for (x=0; x < (ssize_t) fx_image->columns; x++)
3021     {
3022       register ssize_t
3023         i;
3024
3025       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3026       {
3027         MagickRealType
3028           alpha;
3029
3030         PixelChannel
3031           channel;
3032
3033         PixelTrait
3034           fx_traits,
3035           traits;
3036
3037         traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
3038         channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
3039         fx_traits=GetPixelChannelMapTraits(fx_image,channel);
3040         if ((traits == UndefinedPixelTrait) ||
3041             (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 == (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,ExceptionInfo *exception)
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 %    o exception: return any errors or warnings in this structure.
3527 %
3528 */
3529
3530 static inline Quantum PlasmaPixel(RandomInfo *random_info,
3531   const MagickRealType pixel,const MagickRealType noise)
3532 {
3533   Quantum
3534     plasma;
3535
3536   plasma=ClampToQuantum(pixel+noise*GetPseudoRandomValue(random_info)-
3537     noise/2.0);
3538   return(plasma);
3539 }
3540
3541 static MagickBooleanType PlasmaImageProxy(Image *image,
3542   CacheView *image_view,RandomInfo *random_info,const SegmentInfo *segment,
3543   size_t attenuate,size_t depth,ExceptionInfo *exception)
3544 {
3545   MagickRealType
3546     plasma;
3547
3548   PixelPacket
3549     u,
3550     v;
3551
3552   ssize_t
3553     x,
3554     x_mid,
3555     y,
3556     y_mid;
3557
3558   if (((segment->x2-segment->x1) == 0.0) && ((segment->y2-segment->y1) == 0.0))
3559     return(MagickTrue);
3560   if (depth != 0)
3561     {
3562       SegmentInfo
3563         local_info;
3564
3565       /*
3566         Divide the area into quadrants and recurse.
3567       */
3568       depth--;
3569       attenuate++;
3570       x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3571       y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
3572       local_info=(*segment);
3573       local_info.x2=(double) x_mid;
3574       local_info.y2=(double) y_mid;
3575       (void) PlasmaImageProxy(image,image_view,random_info,&local_info,
3576         attenuate,depth,exception);
3577       local_info=(*segment);
3578       local_info.y1=(double) y_mid;
3579       local_info.x2=(double) x_mid;
3580       (void) PlasmaImageProxy(image,image_view,random_info,&local_info,
3581         attenuate,depth,exception);
3582       local_info=(*segment);
3583       local_info.x1=(double) x_mid;
3584       local_info.y2=(double) y_mid;
3585       (void) PlasmaImageProxy(image,image_view,random_info,&local_info,
3586         attenuate,depth,exception);
3587       local_info=(*segment);
3588       local_info.x1=(double) x_mid;
3589       local_info.y1=(double) y_mid;
3590       return(PlasmaImageProxy(image,image_view,random_info,&local_info,
3591         attenuate,depth,exception));
3592     }
3593   x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3594   y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
3595   if ((segment->x1 == (double) x_mid) && (segment->x2 == (double) x_mid) &&
3596       (segment->y1 == (double) y_mid) && (segment->y2 == (double) y_mid))
3597     return(MagickFalse);
3598   /*
3599     Average pixels and apply plasma.
3600   */
3601   plasma=(MagickRealType) QuantumRange/(2.0*attenuate);
3602   if ((segment->x1 != (double) x_mid) || (segment->x2 != (double) x_mid))
3603     {
3604       register Quantum
3605         *restrict q;
3606
3607       /*
3608         Left pixel.
3609       */
3610       x=(ssize_t) ceil(segment->x1-0.5);
3611       (void) GetOneCacheViewVirtualPixel(image_view,x,(ssize_t)
3612         ceil(segment->y1-0.5),&u,exception);
3613       (void) GetOneCacheViewVirtualPixel(image_view,x,(ssize_t)
3614         ceil(segment->y2-0.5),&v,exception);
3615       q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
3616       if (q == (Quantum *) NULL)
3617         return(MagickTrue);
3618       SetPixelRed(image,PlasmaPixel(random_info,(MagickRealType)
3619         (u.red+v.red)/2.0,plasma),q);
3620       SetPixelGreen(image,PlasmaPixel(random_info,(MagickRealType)
3621         (u.green+v.green)/2.0,plasma),q);
3622       SetPixelBlue(image,PlasmaPixel(random_info,(MagickRealType)
3623         (u.blue+v.blue)/2.0,plasma),q);
3624       (void) SyncCacheViewAuthenticPixels(image_view,exception);
3625       if (segment->x1 != segment->x2)
3626         {
3627           /*
3628             Right pixel.
3629           */
3630           x=(ssize_t) ceil(segment->x2-0.5);
3631           (void) GetOneCacheViewVirtualPixel(image_view,x,(ssize_t)
3632             ceil(segment->y1-0.5),&u,exception);
3633           (void) GetOneCacheViewVirtualPixel(image_view,x,(ssize_t)
3634             ceil(segment->y2-0.5),&v,exception);
3635           q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
3636           if (q == (Quantum *) NULL)
3637             return(MagickTrue);
3638           SetPixelRed(image,PlasmaPixel(random_info,(MagickRealType)
3639             (u.red+v.red)/2.0,plasma),q);
3640           SetPixelGreen(image,PlasmaPixel(random_info,(MagickRealType)
3641             (u.green+v.green)/2.0,plasma),q);
3642           SetPixelBlue(image,PlasmaPixel(random_info,(MagickRealType)
3643             (u.blue+v.blue)/2.0,plasma),q);
3644           (void) SyncCacheViewAuthenticPixels(image_view,exception);
3645         }
3646     }
3647   if ((segment->y1 != (double) y_mid) || (segment->y2 != (double) y_mid))
3648     {
3649       if ((segment->x1 != (double) x_mid) || (segment->y2 != (double) y_mid))
3650         {
3651           register Quantum
3652             *restrict q;
3653
3654           /*
3655             Bottom pixel.
3656           */
3657           y=(ssize_t) ceil(segment->y2-0.5);
3658           (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
3659             ceil(segment->x1-0.5),y,&u,exception);
3660           (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
3661             ceil(segment->x2-0.5),y,&v,exception);
3662           q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
3663           if (q == (Quantum *) NULL)
3664             return(MagickTrue);
3665           SetPixelRed(image,PlasmaPixel(random_info,(MagickRealType)
3666             (u.red+v.red)/2.0,plasma),q);
3667           SetPixelGreen(image,PlasmaPixel(random_info,(MagickRealType)
3668             (u.green+v.green)/2.0,plasma),q);
3669           SetPixelBlue(image,PlasmaPixel(random_info,(MagickRealType)
3670             (u.blue+v.blue)/2.0,plasma),q);
3671           (void) SyncCacheViewAuthenticPixels(image_view,exception);
3672         }
3673       if (segment->y1 != segment->y2)
3674         {
3675           register Quantum
3676             *restrict q;
3677
3678           /*
3679             Top pixel.
3680           */
3681           y=(ssize_t) ceil(segment->y1-0.5);
3682           (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
3683             ceil(segment->x1-0.5),y,&u,exception);
3684           (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
3685             ceil(segment->x2-0.5),y,&v,exception);
3686           q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
3687           if (q == (Quantum *) NULL)
3688             return(MagickTrue);
3689           SetPixelRed(image,PlasmaPixel(random_info,(MagickRealType)
3690             (u.red+v.red)/2.0,plasma),q);
3691           SetPixelGreen(image,PlasmaPixel(random_info,(MagickRealType)
3692             (u.green+v.green)/2.0,plasma),q);
3693           SetPixelBlue(image,PlasmaPixel(random_info,(MagickRealType)
3694             (u.blue+v.blue)/2.0,plasma),q);
3695           (void) SyncCacheViewAuthenticPixels(image_view,exception);
3696         }
3697     }
3698   if ((segment->x1 != segment->x2) || (segment->y1 != segment->y2))
3699     {
3700       register Quantum
3701         *restrict q;
3702
3703       /*
3704         Middle pixel.
3705       */
3706       x=(ssize_t) ceil(segment->x1-0.5);
3707       y=(ssize_t) ceil(segment->y1-0.5);
3708       (void) GetOneVirtualPixel(image,x,y,&u,exception);
3709       x=(ssize_t) ceil(segment->x2-0.5);
3710       y=(ssize_t) ceil(segment->y2-0.5);
3711       (void) GetOneCacheViewVirtualPixel(image_view,x,y,&v,exception);
3712       q=QueueCacheViewAuthenticPixels(image_view,x_mid,y_mid,1,1,exception);
3713       if (q == (Quantum *) NULL)
3714         return(MagickTrue);
3715       SetPixelRed(image,PlasmaPixel(random_info,(MagickRealType)
3716         (u.red+v.red)/2.0,plasma),q);
3717       SetPixelGreen(image,PlasmaPixel(random_info,(MagickRealType)
3718         (u.green+v.green)/2.0,plasma),q);
3719       SetPixelBlue(image,PlasmaPixel(random_info,(MagickRealType)
3720         (u.blue+v.blue)/2.0,plasma),q);
3721       (void) SyncCacheViewAuthenticPixels(image_view,exception);
3722     }
3723   if (((segment->x2-segment->x1) < 3.0) && ((segment->y2-segment->y1) < 3.0))
3724     return(MagickTrue);
3725   return(MagickFalse);
3726 }
3727 \f
3728 MagickExport MagickBooleanType PlasmaImage(Image *image,
3729   const SegmentInfo *segment,size_t attenuate,size_t depth,
3730   ExceptionInfo *exception)
3731 {
3732   CacheView
3733     *image_view;
3734
3735   MagickBooleanType
3736     status;
3737
3738   RandomInfo
3739     *random_info;
3740
3741   if (image->debug != MagickFalse)
3742     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3743   assert(image != (Image *) NULL);
3744   assert(image->signature == MagickSignature);
3745   if (image->debug != MagickFalse)
3746     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3747   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
3748     return(MagickFalse);
3749   image_view=AcquireCacheView(image);
3750   random_info=AcquireRandomInfo();
3751   status=PlasmaImageProxy(image,image_view,random_info,segment,attenuate,depth,
3752     exception);
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,exception);
3849       (void) CloneString(&annotate_info->text,caption);
3850       count=FormatMagickCaption(caption_image,annotate_info,MagickTrue,&metrics,
3851         &caption,exception);
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,exception);
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 == (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 == (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 %        ExceptionInfo *exception)
4416 %
4417 %  A description of each parameter follows:
4418 %
4419 %    o image: the image.
4420 %
4421 %    o threshold:  Define the extent of the solarization.
4422 %
4423 %    o exception: return any errors or warnings in this structure.
4424 %
4425 */
4426 MagickExport MagickBooleanType SolarizeImage(Image *image,
4427   const double threshold,ExceptionInfo *exception)
4428 {
4429 #define SolarizeImageTag  "Solarize/Image"
4430
4431   CacheView
4432     *image_view;
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   image_view=AcquireCacheView(image);
4473 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4474   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4475 #endif
4476   for (y=0; y < (ssize_t) image->rows; y++)
4477   {
4478     register ssize_t
4479       x;
4480
4481     register Quantum
4482       *restrict q;
4483
4484     if (status == MagickFalse)
4485       continue;
4486     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
4487     if (q == (Quantum *) NULL)
4488       {
4489         status=MagickFalse;
4490         continue;
4491       }
4492     for (x=0; x < (ssize_t) image->columns; x++)
4493     {
4494       if ((MagickRealType) GetPixelRed(image,q) > threshold)
4495         SetPixelRed(image,QuantumRange-GetPixelRed(image,q),q);
4496       if ((MagickRealType) GetPixelGreen(image,q) > threshold)
4497         SetPixelGreen(image,QuantumRange-GetPixelGreen(image,q),q);
4498       if ((MagickRealType) GetPixelBlue(image,q) > threshold)
4499         SetPixelBlue(image,QuantumRange-GetPixelBlue(image,q),q);
4500       q+=GetPixelChannels(image);
4501     }
4502     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4503       status=MagickFalse;
4504     if (image->progress_monitor != (MagickProgressMonitor) NULL)
4505       {
4506         MagickBooleanType
4507           proceed;
4508
4509 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4510   #pragma omp critical (MagickCore_SolarizeImage)
4511 #endif
4512         proceed=SetImageProgress(image,SolarizeImageTag,progress++,image->rows);
4513         if (proceed == MagickFalse)
4514           status=MagickFalse;
4515       }
4516   }
4517   image_view=DestroyCacheView(image_view);
4518   return(status);
4519 }
4520 \f
4521 /*
4522 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4523 %                                                                             %
4524 %                                                                             %
4525 %                                                                             %
4526 %   S t e g a n o I m a g e                                                   %
4527 %                                                                             %
4528 %                                                                             %
4529 %                                                                             %
4530 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4531 %
4532 %  SteganoImage() hides a digital watermark within the image.  Recover
4533 %  the hidden watermark later to prove that the authenticity of an image.
4534 %  Offset defines the start position within the image to hide the watermark.
4535 %
4536 %  The format of the SteganoImage method is:
4537 %
4538 %      Image *SteganoImage(const Image *image,Image *watermark,
4539 %        ExceptionInfo *exception)
4540 %
4541 %  A description of each parameter follows:
4542 %
4543 %    o image: the image.
4544 %
4545 %    o watermark: the watermark image.
4546 %
4547 %    o exception: return any errors or warnings in this structure.
4548 %
4549 */
4550 MagickExport Image *SteganoImage(const Image *image,const Image *watermark,
4551   ExceptionInfo *exception)
4552 {
4553 #define GetBit(alpha,i) ((((size_t) (alpha) >> (size_t) (i)) & 0x01) != 0)
4554 #define SetBit(alpha,i,set) (Quantum) ((set) != 0 ? (size_t) (alpha) \
4555   | (one << (size_t) (i)) : (size_t) (alpha) & ~(one << (size_t) (i)))
4556 #define SteganoImageTag  "Stegano/Image"
4557
4558   CacheView
4559     *stegano_view,
4560     *watermark_view;
4561
4562   Image
4563     *stegano_image;
4564
4565   int
4566     c;
4567
4568   MagickBooleanType
4569     status;
4570
4571   PixelPacket
4572     pixel;
4573
4574   register Quantum
4575     *q;
4576
4577   register ssize_t
4578     x;
4579
4580   size_t
4581     depth,
4582     one;
4583
4584   ssize_t
4585     i,
4586     j,
4587     k,
4588     y;
4589
4590   /*
4591     Initialize steganographic image attributes.
4592   */
4593   assert(image != (const Image *) NULL);
4594   assert(image->signature == MagickSignature);
4595   if (image->debug != MagickFalse)
4596     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4597   assert(watermark != (const Image *) NULL);
4598   assert(watermark->signature == MagickSignature);
4599   assert(exception != (ExceptionInfo *) NULL);
4600   assert(exception->signature == MagickSignature);
4601   one=1UL;
4602   stegano_image=CloneImage(image,0,0,MagickTrue,exception);
4603   if (stegano_image == (Image *) NULL)
4604     return((Image *) NULL);
4605   if (SetImageStorageClass(stegano_image,DirectClass,exception) == MagickFalse)
4606     {
4607       stegano_image=DestroyImage(stegano_image);
4608       return((Image *) NULL);
4609     }
4610   stegano_image->depth=MAGICKCORE_QUANTUM_DEPTH;
4611   /*
4612     Hide watermark in low-order bits of image.
4613   */
4614   c=0;
4615   i=0;
4616   j=0;
4617   depth=stegano_image->depth;
4618   k=image->offset;
4619   status=MagickTrue;
4620   watermark_view=AcquireCacheView(watermark);
4621   stegano_view=AcquireCacheView(stegano_image);
4622   for (i=(ssize_t) depth-1; (i >= 0) && (j < (ssize_t) depth); i--)
4623   {
4624     for (y=0; (y < (ssize_t) watermark->rows) && (j < (ssize_t) depth); y++)
4625     {
4626       for (x=0; (x < (ssize_t) watermark->columns) && (j < (ssize_t) depth); x++)
4627       {
4628         (void) GetOneCacheViewVirtualPixel(watermark_view,x,y,&pixel,exception);
4629         if ((k/(ssize_t) stegano_image->columns) >= (ssize_t) stegano_image->rows)
4630           break;
4631         q=GetCacheViewAuthenticPixels(stegano_view,k % (ssize_t)
4632           stegano_image->columns,k/(ssize_t) stegano_image->columns,1,1,
4633           exception);
4634         if (q == (Quantum *) NULL)
4635           break;
4636         switch (c)
4637         {
4638           case 0:
4639           {
4640             SetPixelRed(image,SetBit(GetPixelRed(image,q),j,GetBit(
4641               GetPixelPacketIntensity(&pixel),i)),q);
4642             break;
4643           }
4644           case 1:
4645           {
4646             SetPixelGreen(image,SetBit(GetPixelGreen(image,q),j,GetBit(
4647               GetPixelPacketIntensity(&pixel),i)),q);
4648             break;
4649           }
4650           case 2:
4651           {
4652             SetPixelBlue(image,SetBit(GetPixelBlue(image,q),j,GetBit(
4653               GetPixelPacketIntensity(&pixel),i)),q);
4654             break;
4655           }
4656         }
4657         if (SyncCacheViewAuthenticPixels(stegano_view,exception) == MagickFalse)
4658           break;
4659         c++;
4660         if (c == 3)
4661           c=0;
4662         k++;
4663         if (k == (ssize_t) (stegano_image->columns*stegano_image->columns))
4664           k=0;
4665         if (k == image->offset)
4666           j++;
4667       }
4668     }
4669     if (image->progress_monitor != (MagickProgressMonitor) NULL)
4670       {
4671         MagickBooleanType
4672           proceed;
4673
4674         proceed=SetImageProgress(image,SteganoImageTag,(MagickOffsetType)
4675           (depth-i),depth);
4676         if (proceed == MagickFalse)
4677           status=MagickFalse;
4678       }
4679   }
4680   stegano_view=DestroyCacheView(stegano_view);
4681   watermark_view=DestroyCacheView(watermark_view);
4682   if (stegano_image->storage_class == PseudoClass)
4683     (void) SyncImage(stegano_image);
4684   if (status == MagickFalse)
4685     {
4686       stegano_image=DestroyImage(stegano_image);
4687       return((Image *) NULL);
4688     }
4689   return(stegano_image);
4690 }
4691 \f
4692 /*
4693 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4694 %                                                                             %
4695 %                                                                             %
4696 %                                                                             %
4697 %   S t e r e o A n a g l y p h I m a g e                                     %
4698 %                                                                             %
4699 %                                                                             %
4700 %                                                                             %
4701 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4702 %
4703 %  StereoAnaglyphImage() combines two images and produces a single image that
4704 %  is the composite of a left and right image of a stereo pair.  Special
4705 %  red-green stereo glasses are required to view this effect.
4706 %
4707 %  The format of the StereoAnaglyphImage method is:
4708 %
4709 %      Image *StereoImage(const Image *left_image,const Image *right_image,
4710 %        ExceptionInfo *exception)
4711 %      Image *StereoAnaglyphImage(const Image *left_image,
4712 %        const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
4713 %        ExceptionInfo *exception)
4714 %
4715 %  A description of each parameter follows:
4716 %
4717 %    o left_image: the left image.
4718 %
4719 %    o right_image: the right image.
4720 %
4721 %    o exception: return any errors or warnings in this structure.
4722 %
4723 %    o x_offset: amount, in pixels, by which the left image is offset to the
4724 %      right of the right image.
4725 %
4726 %    o y_offset: amount, in pixels, by which the left image is offset to the
4727 %      bottom of the right image.
4728 %
4729 %
4730 */
4731 MagickExport Image *StereoImage(const Image *left_image,
4732   const Image *right_image,ExceptionInfo *exception)
4733 {
4734   return(StereoAnaglyphImage(left_image,right_image,0,0,exception));
4735 }
4736
4737 MagickExport Image *StereoAnaglyphImage(const Image *left_image,
4738   const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
4739   ExceptionInfo *exception)
4740 {
4741 #define StereoImageTag  "Stereo/Image"
4742
4743   const Image
4744     *image;
4745
4746   Image
4747     *stereo_image;
4748
4749   MagickBooleanType
4750     status;
4751
4752   ssize_t
4753     y;
4754
4755   assert(left_image != (const Image *) NULL);
4756   assert(left_image->signature == MagickSignature);
4757   if (left_image->debug != MagickFalse)
4758     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4759       left_image->filename);
4760   assert(right_image != (const Image *) NULL);
4761   assert(right_image->signature == MagickSignature);
4762   assert(exception != (ExceptionInfo *) NULL);
4763   assert(exception->signature == MagickSignature);
4764   assert(right_image != (const Image *) NULL);
4765   image=left_image;
4766   if ((left_image->columns != right_image->columns) ||
4767       (left_image->rows != right_image->rows))
4768     ThrowImageException(ImageError,"LeftAndRightImageSizesDiffer");
4769   /*
4770     Initialize stereo image attributes.
4771   */
4772   stereo_image=CloneImage(left_image,left_image->columns,left_image->rows,
4773     MagickTrue,exception);
4774   if (stereo_image == (Image *) NULL)
4775     return((Image *) NULL);
4776   if (SetImageStorageClass(stereo_image,DirectClass,exception) == MagickFalse)
4777     {
4778       stereo_image=DestroyImage(stereo_image);
4779       return((Image *) NULL);
4780     }
4781   /*
4782     Copy left image to red channel and right image to blue channel.
4783   */
4784   status=MagickTrue;
4785   for (y=0; y < (ssize_t) stereo_image->rows; y++)
4786   {
4787     register const Quantum
4788       *restrict p,
4789       *restrict q;
4790
4791     register ssize_t
4792       x;
4793
4794     register Quantum
4795       *restrict r;
4796
4797     p=GetVirtualPixels(left_image,-x_offset,y-y_offset,image->columns,1,
4798       exception);
4799     q=GetVirtualPixels(right_image,0,y,right_image->columns,1,exception);
4800     r=QueueAuthenticPixels(stereo_image,0,y,stereo_image->columns,1,exception);
4801     if ((p == (const Quantum *) NULL) ||
4802         (q == (Quantum *) NULL) || (r == (Quantum *) NULL))
4803       break;
4804     for (x=0; x < (ssize_t) stereo_image->columns; x++)
4805     {
4806       SetPixelRed(image,GetPixelRed(left_image,p),r);
4807       SetPixelGreen(image,GetPixelGreen(left_image,q),r);
4808       SetPixelBlue(image,GetPixelBlue(left_image,q),r);
4809       SetPixelAlpha(image,(GetPixelAlpha(left_image,p)+
4810         GetPixelAlpha(left_image,q))/2,r);
4811       p+=GetPixelChannels(left_image);
4812       q++;
4813       r++;
4814     }
4815     if (SyncAuthenticPixels(stereo_image,exception) == MagickFalse)
4816       break;
4817     if (image->progress_monitor != (MagickProgressMonitor) NULL)
4818       {
4819         MagickBooleanType
4820           proceed;
4821
4822         proceed=SetImageProgress(image,StereoImageTag,(MagickOffsetType) y,
4823           stereo_image->rows);
4824         if (proceed == MagickFalse)
4825           status=MagickFalse;
4826       }
4827   }
4828   if (status == MagickFalse)
4829     {
4830       stereo_image=DestroyImage(stereo_image);
4831       return((Image *) NULL);
4832     }
4833   return(stereo_image);
4834 }
4835 \f
4836 /*
4837 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4838 %                                                                             %
4839 %                                                                             %
4840 %                                                                             %
4841 %     S w i r l I m a g e                                                     %
4842 %                                                                             %
4843 %                                                                             %
4844 %                                                                             %
4845 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4846 %
4847 %  SwirlImage() swirls the pixels about the center of the image, where
4848 %  degrees indicates the sweep of the arc through which each pixel is moved.
4849 %  You get a more dramatic effect as the degrees move from 1 to 360.
4850 %
4851 %  The format of the SwirlImage method is:
4852 %
4853 %      Image *SwirlImage(const Image *image,double degrees,
4854 %        ExceptionInfo *exception)
4855 %
4856 %  A description of each parameter follows:
4857 %
4858 %    o image: the image.
4859 %
4860 %    o degrees: Define the tightness of the swirling effect.
4861 %
4862 %    o exception: return any errors or warnings in this structure.
4863 %
4864 */
4865 MagickExport Image *SwirlImage(const Image *image,double degrees,
4866   ExceptionInfo *exception)
4867 {
4868 #define SwirlImageTag  "Swirl/Image"
4869
4870   CacheView
4871     *image_view,
4872     *swirl_view;
4873
4874   Image
4875     *swirl_image;
4876
4877   MagickBooleanType
4878     status;
4879
4880   MagickOffsetType
4881     progress;
4882
4883   PixelInfo
4884     zero;
4885
4886   MagickRealType
4887     radius;
4888
4889   PointInfo
4890     center,
4891     scale;
4892
4893   ssize_t
4894     y;
4895
4896   /*
4897     Initialize swirl image attributes.
4898   */
4899   assert(image != (const Image *) NULL);
4900   assert(image->signature == MagickSignature);
4901   if (image->debug != MagickFalse)
4902     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4903   assert(exception != (ExceptionInfo *) NULL);
4904   assert(exception->signature == MagickSignature);
4905   swirl_image=CloneImage(image,0,0,MagickTrue,exception);
4906   if (swirl_image == (Image *) NULL)
4907     return((Image *) NULL);
4908   if (SetImageStorageClass(swirl_image,DirectClass,exception) == MagickFalse)
4909     {
4910       swirl_image=DestroyImage(swirl_image);
4911       return((Image *) NULL);
4912     }
4913   if (swirl_image->background_color.alpha != OpaqueAlpha)
4914     swirl_image->matte=MagickTrue;
4915   /*
4916     Compute scaling factor.
4917   */
4918   center.x=(double) image->columns/2.0;
4919   center.y=(double) image->rows/2.0;
4920   radius=MagickMax(center.x,center.y);
4921   scale.x=1.0;
4922   scale.y=1.0;
4923   if (image->columns > image->rows)
4924     scale.y=(double) image->columns/(double) image->rows;
4925   else
4926     if (image->columns < image->rows)
4927       scale.x=(double) image->rows/(double) image->columns;
4928   degrees=(double) DegreesToRadians(degrees);
4929   /*
4930     Swirl image.
4931   */
4932   status=MagickTrue;
4933   progress=0;
4934   GetPixelInfo(swirl_image,&zero);
4935   image_view=AcquireCacheView(image);
4936   swirl_view=AcquireCacheView(swirl_image);
4937 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4938   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4939 #endif
4940   for (y=0; y < (ssize_t) image->rows; y++)
4941   {
4942     PixelInfo
4943       pixel;
4944
4945     MagickRealType
4946       distance;
4947
4948     PointInfo
4949       delta;
4950
4951     register ssize_t
4952       x;
4953
4954     register Quantum
4955       *restrict q;
4956
4957     if (status == MagickFalse)
4958       continue;
4959     q=GetCacheViewAuthenticPixels(swirl_view,0,y,swirl_image->columns,1,
4960       exception);
4961     if (q == (Quantum *) NULL)
4962       {
4963         status=MagickFalse;
4964         continue;
4965       }
4966     delta.y=scale.y*(double) (y-center.y);
4967     pixel=zero;
4968     for (x=0; x < (ssize_t) image->columns; x++)
4969     {
4970       /*
4971         Determine if the pixel is within an ellipse.
4972       */
4973       delta.x=scale.x*(double) (x-center.x);
4974       distance=delta.x*delta.x+delta.y*delta.y;
4975       if (distance < (radius*radius))
4976         {
4977           MagickRealType
4978             cosine,
4979             factor,
4980             sine;
4981
4982           /*
4983             Swirl the pixel.
4984           */
4985           factor=1.0-sqrt((double) distance)/radius;
4986           sine=sin((double) (degrees*factor*factor));
4987           cosine=cos((double) (degrees*factor*factor));
4988           (void) InterpolatePixelInfo(image,image_view,
4989             UndefinedInterpolatePixel,(double) ((cosine*delta.x-sine*delta.y)/
4990             scale.x+center.x),(double) ((sine*delta.x+cosine*delta.y)/scale.y+
4991             center.y),&pixel,exception);
4992           SetPixelPixelInfo(swirl_image,&pixel,q);
4993         }
4994       q+=GetPixelChannels(swirl_image);
4995     }
4996     if (SyncCacheViewAuthenticPixels(swirl_view,exception) == MagickFalse)
4997       status=MagickFalse;
4998     if (image->progress_monitor != (MagickProgressMonitor) NULL)
4999       {
5000         MagickBooleanType
5001           proceed;
5002
5003 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5004   #pragma omp critical (MagickCore_SwirlImage)
5005 #endif
5006         proceed=SetImageProgress(image,SwirlImageTag,progress++,image->rows);
5007         if (proceed == MagickFalse)
5008           status=MagickFalse;
5009       }
5010   }
5011   swirl_view=DestroyCacheView(swirl_view);
5012   image_view=DestroyCacheView(image_view);
5013   if (status == MagickFalse)
5014     swirl_image=DestroyImage(swirl_image);
5015   return(swirl_image);
5016 }
5017 \f
5018 /*
5019 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5020 %                                                                             %
5021 %                                                                             %
5022 %                                                                             %
5023 %     T i n t I m a g e                                                       %
5024 %                                                                             %
5025 %                                                                             %
5026 %                                                                             %
5027 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5028 %
5029 %  TintImage() applies a color vector to each pixel in the image.  The length
5030 %  of the vector is 0 for black and white and at its maximum for the midtones.
5031 %  The vector weighting function is f(x)=(1-(4.0*((x-0.5)*(x-0.5))))
5032 %
5033 %  The format of the TintImage method is:
5034 %
5035 %      Image *TintImage(const Image *image,const char *opacity,
5036 %        const PixelPacket tint,ExceptionInfo *exception)
5037 %
5038 %  A description of each parameter follows:
5039 %
5040 %    o image: the image.
5041 %
5042 %    o opacity: A color value used for tinting.
5043 %
5044 %    o tint: A color value used for tinting.
5045 %
5046 %    o exception: return any errors or warnings in this structure.
5047 %
5048 */
5049 MagickExport Image *TintImage(const Image *image,const char *opacity,
5050   const PixelPacket tint,ExceptionInfo *exception)
5051 {
5052 #define TintImageTag  "Tint/Image"
5053
5054   CacheView
5055     *image_view,
5056     *tint_view;
5057
5058   GeometryInfo
5059     geometry_info;
5060
5061   Image
5062     *tint_image;
5063
5064   MagickBooleanType
5065     status;
5066
5067   MagickOffsetType
5068     progress;
5069
5070   PixelInfo
5071     color_vector,
5072     pixel;
5073
5074   MagickStatusType
5075     flags;
5076
5077   ssize_t
5078     y;
5079
5080   /*
5081     Allocate tint image.
5082   */
5083   assert(image != (const Image *) NULL);
5084   assert(image->signature == MagickSignature);
5085   if (image->debug != MagickFalse)
5086     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5087   assert(exception != (ExceptionInfo *) NULL);
5088   assert(exception->signature == MagickSignature);
5089   tint_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
5090   if (tint_image == (Image *) NULL)
5091     return((Image *) NULL);
5092   if (SetImageStorageClass(tint_image,DirectClass,exception) == MagickFalse)
5093     {
5094       tint_image=DestroyImage(tint_image);
5095       return((Image *) NULL);
5096     }
5097   if (opacity == (const char *) NULL)
5098     return(tint_image);
5099   /*
5100     Determine RGB values of the color.
5101   */
5102   flags=ParseGeometry(opacity,&geometry_info);
5103   pixel.red=geometry_info.rho;
5104   if ((flags & SigmaValue) != 0)
5105     pixel.green=geometry_info.sigma;
5106   else
5107     pixel.green=pixel.red;
5108   if ((flags & XiValue) != 0)
5109     pixel.blue=geometry_info.xi;
5110   else
5111     pixel.blue=pixel.red;
5112   if ((flags & PsiValue) != 0)
5113     pixel.alpha=geometry_info.psi;
5114   else
5115     pixel.alpha=(MagickRealType) OpaqueAlpha;
5116   color_vector.red=(MagickRealType) (pixel.red*tint.red/100.0-
5117     GetPixelPacketIntensity(&tint));
5118   color_vector.green=(MagickRealType) (pixel.green*tint.green/100.0-
5119     GetPixelPacketIntensity(&tint));
5120   color_vector.blue=(MagickRealType) (pixel.blue*tint.blue/100.0-
5121     GetPixelPacketIntensity(&tint));
5122   /*
5123     Tint image.
5124   */
5125   status=MagickTrue;
5126   progress=0;
5127   image_view=AcquireCacheView(image);
5128   tint_view=AcquireCacheView(tint_image);
5129 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5130   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
5131 #endif
5132   for (y=0; y < (ssize_t) image->rows; y++)
5133   {
5134     register const Quantum
5135       *restrict p;
5136
5137     register Quantum
5138       *restrict q;
5139
5140     register ssize_t
5141       x;
5142
5143     if (status == MagickFalse)
5144       continue;
5145     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
5146     q=QueueCacheViewAuthenticPixels(tint_view,0,y,tint_image->columns,1,
5147       exception);
5148     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
5149       {
5150         status=MagickFalse;
5151         continue;
5152       }
5153     for (x=0; x < (ssize_t) image->columns; x++)
5154     {
5155       PixelInfo
5156         pixel;
5157
5158       MagickRealType
5159         weight;
5160
5161       weight=QuantumScale*GetPixelRed(image,p)-0.5;
5162       pixel.red=(MagickRealType) GetPixelRed(image,p)+color_vector.red*
5163         (1.0-(4.0*(weight*weight)));
5164       SetPixelRed(tint_image,ClampToQuantum(pixel.red),q);
5165       weight=QuantumScale*GetPixelGreen(image,p)-0.5;
5166       pixel.green=(MagickRealType) GetPixelGreen(image,p)+color_vector.green*
5167         (1.0-(4.0*(weight*weight)));
5168       SetPixelGreen(tint_image,ClampToQuantum(pixel.green),q);
5169       weight=QuantumScale*GetPixelBlue(image,p)-0.5;
5170       pixel.blue=(MagickRealType) GetPixelBlue(image,p)+color_vector.blue*
5171         (1.0-(4.0*(weight*weight)));
5172       SetPixelBlue(tint_image,ClampToQuantum(pixel.blue),q);
5173       SetPixelAlpha(tint_image,GetPixelAlpha(image,p),q);
5174       p+=GetPixelChannels(image);
5175       q+=GetPixelChannels(tint_image);
5176     }
5177     if (SyncCacheViewAuthenticPixels(tint_view,exception) == MagickFalse)
5178       status=MagickFalse;
5179     if (image->progress_monitor != (MagickProgressMonitor) NULL)
5180       {
5181         MagickBooleanType
5182           proceed;
5183
5184 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5185   #pragma omp critical (MagickCore_TintImage)
5186 #endif
5187         proceed=SetImageProgress(image,TintImageTag,progress++,image->rows);
5188         if (proceed == MagickFalse)
5189           status=MagickFalse;
5190       }
5191   }
5192   tint_view=DestroyCacheView(tint_view);
5193   image_view=DestroyCacheView(image_view);
5194   if (status == MagickFalse)
5195     tint_image=DestroyImage(tint_image);
5196   return(tint_image);
5197 }
5198 \f
5199 /*
5200 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5201 %                                                                             %
5202 %                                                                             %
5203 %                                                                             %
5204 %     V i g n e t t e I m a g e                                               %
5205 %                                                                             %
5206 %                                                                             %
5207 %                                                                             %
5208 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5209 %
5210 %  VignetteImage() softens the edges of the image in vignette style.
5211 %
5212 %  The format of the VignetteImage method is:
5213 %
5214 %      Image *VignetteImage(const Image *image,const double radius,
5215 %        const double sigma,const ssize_t x,const ssize_t y,ExceptionInfo *exception)
5216 %
5217 %  A description of each parameter follows:
5218 %
5219 %    o image: the image.
5220 %
5221 %    o radius: the radius of the pixel neighborhood.
5222 %
5223 %    o sigma: the standard deviation of the Gaussian, in pixels.
5224 %
5225 %    o x, y:  Define the x and y ellipse offset.
5226 %
5227 %    o exception: return any errors or warnings in this structure.
5228 %
5229 */
5230 MagickExport Image *VignetteImage(const Image *image,const double radius,
5231   const double sigma,const ssize_t x,const ssize_t y,ExceptionInfo *exception)
5232 {
5233   char
5234     ellipse[MaxTextExtent];
5235
5236   DrawInfo
5237     *draw_info;
5238
5239   Image
5240     *canvas_image,
5241     *blur_image,
5242     *oval_image,
5243     *vignette_image;
5244
5245   assert(image != (Image *) NULL);
5246   assert(image->signature == MagickSignature);
5247   if (image->debug != MagickFalse)
5248     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5249   assert(exception != (ExceptionInfo *) NULL);
5250   assert(exception->signature == MagickSignature);
5251   canvas_image=CloneImage(image,0,0,MagickTrue,exception);
5252   if (canvas_image == (Image *) NULL)
5253     return((Image *) NULL);
5254   if (SetImageStorageClass(canvas_image,DirectClass,exception) == MagickFalse)
5255     {
5256       canvas_image=DestroyImage(canvas_image);
5257       return((Image *) NULL);
5258     }
5259   canvas_image->matte=MagickTrue;
5260   oval_image=CloneImage(canvas_image,canvas_image->columns,
5261     canvas_image->rows,MagickTrue,exception);
5262   if (oval_image == (Image *) NULL)
5263     {
5264       canvas_image=DestroyImage(canvas_image);
5265       return((Image *) NULL);
5266     }
5267   (void) QueryColorDatabase("#000000",&oval_image->background_color,exception);
5268   (void) SetImageBackgroundColor(oval_image);
5269   draw_info=CloneDrawInfo((const ImageInfo *) NULL,(const DrawInfo *) NULL);
5270   (void) QueryColorDatabase("#ffffff",&draw_info->fill,exception);
5271   (void) QueryColorDatabase("#ffffff",&draw_info->stroke,exception);
5272   (void) FormatLocaleString(ellipse,MaxTextExtent,
5273     "ellipse %g,%g,%g,%g,0.0,360.0",image->columns/2.0,
5274     image->rows/2.0,image->columns/2.0-x,image->rows/2.0-y);
5275   draw_info->primitive=AcquireString(ellipse);
5276   (void) DrawImage(oval_image,draw_info,exception);
5277   draw_info=DestroyDrawInfo(draw_info);
5278   blur_image=BlurImage(oval_image,radius,sigma,exception);
5279   oval_image=DestroyImage(oval_image);
5280   if (blur_image == (Image *) NULL)
5281     {
5282       canvas_image=DestroyImage(canvas_image);
5283       return((Image *) NULL);
5284     }
5285   blur_image->matte=MagickFalse;
5286   (void) CompositeImage(canvas_image,CopyOpacityCompositeOp,blur_image,0,0);
5287   blur_image=DestroyImage(blur_image);
5288   vignette_image=MergeImageLayers(canvas_image,FlattenLayer,exception);
5289   canvas_image=DestroyImage(canvas_image);
5290   return(vignette_image);
5291 }
5292 \f
5293 /*
5294 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5295 %                                                                             %
5296 %                                                                             %
5297 %                                                                             %
5298 %     W a v e I m a g e                                                       %
5299 %                                                                             %
5300 %                                                                             %
5301 %                                                                             %
5302 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5303 %
5304 %  WaveImage() creates a "ripple" effect in the image by shifting the pixels
5305 %  vertically along a sine wave whose amplitude and wavelength is specified
5306 %  by the given parameters.
5307 %
5308 %  The format of the WaveImage method is:
5309 %
5310 %      Image *WaveImage(const Image *image,const double amplitude,
5311 %        const double wave_length,ExceptionInfo *exception)
5312 %
5313 %  A description of each parameter follows:
5314 %
5315 %    o image: the image.
5316 %
5317 %    o amplitude, wave_length:  Define the amplitude and wave length of the
5318 %      sine wave.
5319 %
5320 %    o exception: return any errors or warnings in this structure.
5321 %
5322 */
5323 MagickExport Image *WaveImage(const Image *image,const double amplitude,
5324   const double wave_length,ExceptionInfo *exception)
5325 {
5326 #define WaveImageTag  "Wave/Image"
5327
5328   CacheView
5329     *image_view,
5330     *wave_view;
5331
5332   Image
5333     *wave_image;
5334
5335   MagickBooleanType
5336     status;
5337
5338   MagickOffsetType
5339     progress;
5340
5341   PixelInfo
5342     zero;
5343
5344   MagickRealType
5345     *sine_map;
5346
5347   register ssize_t
5348     i;
5349
5350   ssize_t
5351     y;
5352
5353   /*
5354     Initialize wave image attributes.
5355   */
5356   assert(image != (Image *) NULL);
5357   assert(image->signature == MagickSignature);
5358   if (image->debug != MagickFalse)
5359     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5360   assert(exception != (ExceptionInfo *) NULL);
5361   assert(exception->signature == MagickSignature);
5362   wave_image=CloneImage(image,image->columns,(size_t) (image->rows+2.0*
5363     fabs(amplitude)),MagickTrue,exception);
5364   if (wave_image == (Image *) NULL)
5365     return((Image *) NULL);
5366   if (SetImageStorageClass(wave_image,DirectClass,exception) == MagickFalse)
5367     {
5368       wave_image=DestroyImage(wave_image);
5369       return((Image *) NULL);
5370     }
5371   if (wave_image->background_color.alpha != OpaqueAlpha)
5372     wave_image->matte=MagickTrue;
5373   /*
5374     Allocate sine map.
5375   */
5376   sine_map=(MagickRealType *) AcquireQuantumMemory((size_t) wave_image->columns,
5377     sizeof(*sine_map));
5378   if (sine_map == (MagickRealType *) NULL)
5379     {
5380       wave_image=DestroyImage(wave_image);
5381       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
5382     }
5383   for (i=0; i < (ssize_t) wave_image->columns; i++)
5384     sine_map[i]=fabs(amplitude)+amplitude*sin((double) ((2.0*MagickPI*i)/
5385       wave_length));
5386   /*
5387     Wave image.
5388   */
5389   status=MagickTrue;
5390   progress=0;
5391   GetPixelInfo(wave_image,&zero);
5392   image_view=AcquireCacheView(image);
5393   wave_view=AcquireCacheView(wave_image);
5394   (void) SetCacheViewVirtualPixelMethod(image_view,
5395     BackgroundVirtualPixelMethod);
5396 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5397   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
5398 #endif
5399   for (y=0; y < (ssize_t) wave_image->rows; y++)
5400   {
5401     PixelInfo
5402       pixel;
5403
5404     register Quantum
5405       *restrict q;
5406
5407     register ssize_t
5408       x;
5409
5410     if (status == MagickFalse)
5411       continue;
5412     q=QueueCacheViewAuthenticPixels(wave_view,0,y,wave_image->columns,1,
5413       exception);
5414     if (q == (Quantum *) NULL)
5415       {
5416         status=MagickFalse;
5417         continue;
5418       }
5419     pixel=zero;
5420     for (x=0; x < (ssize_t) wave_image->columns; x++)
5421     {
5422       (void) InterpolatePixelInfo(image,image_view,
5423         UndefinedInterpolatePixel,(double) x,(double) (y-sine_map[x]),&pixel,
5424         exception);
5425       SetPixelPixelInfo(wave_image,&pixel,q);
5426       q+=GetPixelChannels(wave_image);
5427     }
5428     if (SyncCacheViewAuthenticPixels(wave_view,exception) == MagickFalse)
5429       status=MagickFalse;
5430     if (image->progress_monitor != (MagickProgressMonitor) NULL)
5431       {
5432         MagickBooleanType
5433           proceed;
5434
5435 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5436   #pragma omp critical (MagickCore_WaveImage)
5437 #endif
5438         proceed=SetImageProgress(image,WaveImageTag,progress++,image->rows);
5439         if (proceed == MagickFalse)
5440           status=MagickFalse;
5441       }
5442   }
5443   wave_view=DestroyCacheView(wave_view);
5444   image_view=DestroyCacheView(image_view);
5445   sine_map=(MagickRealType *) RelinquishMagickMemory(sine_map);
5446   if (status == MagickFalse)
5447     wave_image=DestroyImage(wave_image);
5448   return(wave_image);
5449 }