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