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