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