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