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