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