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