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