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