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