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