]> 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 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) && (_OPENMP >= 200203)
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 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 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) && (_OPENMP >= 200203)
961   #pragma omp parallel for 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) && (_OPENMP >= 200203)
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) && (_OPENMP >= 200203)
1476   #pragma omp parallel for 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) && (_OPENMP >= 200203)
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) && (_OPENMP >= 200203)
1708   #pragma omp parallel for 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) && (_OPENMP >= 200203)
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) && (_OPENMP >= 200203)
3544   #pragma omp parallel for 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) && (_OPENMP >= 200203)
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) && (_OPENMP >= 200203)
3747   #pragma omp parallel for 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) && (_OPENMP >= 200203)
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) && (_OPENMP >= 200203)
3969   #pragma omp parallel for 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) && (_OPENMP >= 200203)
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   Image
4520     *recolor_image;
4521
4522   long
4523     progress,
4524     y;
4525
4526   MagickBooleanType
4527     status;
4528
4529   MagickPixelPacket
4530     zero;
4531
4532   register const double
4533     *k;
4534
4535   CacheView
4536     *image_view,
4537     *recolor_view;
4538
4539   /*
4540     Initialize image attributes.
4541   */
4542   assert(image != (Image *) NULL);
4543   assert(image->signature == MagickSignature);
4544   if (image->debug != MagickFalse)
4545     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4546   assert(exception != (ExceptionInfo *) NULL);
4547   assert(exception->signature == MagickSignature);
4548   recolor_image=CloneImage(image,image->columns,image->rows,MagickTrue,
4549     exception);
4550   if (recolor_image == (Image *) NULL)
4551     return((Image *) NULL);
4552   if (SetImageStorageClass(recolor_image,DirectClass) == MagickFalse)
4553     {
4554       InheritException(exception,&recolor_image->exception);
4555       recolor_image=DestroyImage(recolor_image);
4556       return((Image *) NULL);
4557     }
4558   if (image->debug != MagickFalse)
4559     {
4560       char
4561         format[MaxTextExtent],
4562         *message;
4563
4564       long
4565         u,
4566         v;
4567
4568       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
4569         "  Recolor image with %ldx%ld color matrix:",order,order);
4570       message=AcquireString("");
4571       k=color_matrix;
4572       for (v=0; v < (long) order; v++)
4573       {
4574         *message='\0';
4575         (void) FormatMagickString(format,MaxTextExtent,"%ld: ",v);
4576         (void) ConcatenateString(&message,format);
4577         for (u=0; u < (long) order; u++)
4578         {
4579           (void) FormatMagickString(format,MaxTextExtent,"%+f ",*k++);
4580           (void) ConcatenateString(&message,format);
4581         }
4582         (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
4583       }
4584       message=DestroyString(message);
4585     }
4586   /*
4587     Recolor image.
4588   */
4589   status=MagickTrue;
4590   progress=0;
4591   GetMagickPixelPacket(image,&zero);
4592   k=color_matrix;
4593   image_view=AcquireCacheView(image);
4594   recolor_view=AcquireCacheView(recolor_image);
4595 #if defined(MAGICKCORE_OPENMP_SUPPORT) && (_OPENMP >= 200203)
4596   #pragma omp parallel for shared(progress,status)
4597 #endif
4598   for (y=0; y < (long) image->rows; y++)
4599   {
4600     MagickPixelPacket
4601       pixel,
4602       recolor_pixel;
4603
4604     register const IndexPacket
4605       *__restrict indexes;
4606
4607     register const PixelPacket
4608       *__restrict p;
4609
4610     register long
4611       x;
4612
4613     register IndexPacket
4614       *__restrict recolor_indexes;
4615
4616     register PixelPacket
4617       *__restrict q;
4618
4619     if (status == MagickFalse)
4620       continue;
4621     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
4622     q=QueueCacheViewAuthenticPixels(recolor_view,0,y,recolor_image->columns,1,
4623       exception);
4624     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
4625       {
4626         status=MagickFalse;
4627         continue;
4628       }
4629     indexes=GetCacheViewAuthenticIndexQueue(image_view);
4630     recolor_indexes=GetCacheViewAuthenticIndexQueue(recolor_view);
4631     pixel=zero;
4632     recolor_pixel=zero;
4633     for (x=0; x < (long) image->columns; x++)
4634     {
4635       SetMagickPixelPacket(image,p,indexes,&pixel);
4636       SetMagickPixelPacket(recolor_image,p,indexes,&recolor_pixel);
4637       switch (order)
4638       {
4639         case 0:
4640           break;
4641         case 1:
4642         {
4643           recolor_pixel.red=k[0]*pixel.red;
4644           break;
4645         }
4646         case 2:
4647         {
4648           recolor_pixel.red=k[0]*pixel.red+k[1]*pixel.green;
4649           recolor_pixel.green=k[2]*pixel.red+k[3]*pixel.green;
4650           break;
4651         }
4652         case 3:
4653         {
4654           recolor_pixel.red=k[0]*pixel.red+k[1]*pixel.green+k[2]*pixel.blue;
4655           recolor_pixel.green=k[3]*pixel.red+k[4]*pixel.green+k[5]*pixel.blue;
4656           recolor_pixel.blue=k[6]*pixel.red+k[7]*pixel.green+k[8]*pixel.blue;
4657           break;
4658         }
4659         case 4:
4660         {
4661           recolor_pixel.red=k[0]*pixel.red+k[1]*pixel.green+k[2]*pixel.blue+
4662             k[12]*QuantumRange;
4663           recolor_pixel.green=k[4]*pixel.red+k[5]*pixel.green+k[6]*pixel.blue+
4664             k[13]*QuantumRange;
4665           recolor_pixel.blue=k[8]*pixel.red+k[9]*pixel.green+k[10]*pixel.blue+
4666             k[14]*QuantumRange;
4667           break;
4668         }
4669         case 5:
4670         {
4671           recolor_pixel.red=k[0]*pixel.red+k[1]*pixel.green+k[2]*pixel.blue+
4672             k[3]*(QuantumRange-pixel.opacity)+k[20]*QuantumRange;
4673           recolor_pixel.green=k[5]*pixel.red+k[6]*pixel.green+k[7]*pixel.blue+
4674             k[8]*(QuantumRange-pixel.opacity)+k[21]*QuantumRange;
4675           recolor_pixel.blue=k[10]*pixel.red+k[11]*pixel.green+k[12]*pixel.blue+
4676             k[13]*(QuantumRange-pixel.opacity)+k[22]*QuantumRange;
4677           recolor_pixel.opacity=(MagickRealType) QuantumRange-(k[15]*pixel.red+
4678             k[16]*pixel.green+k[17]*pixel.blue+k[18]*(QuantumRange-
4679             pixel.opacity)+k[23]*QuantumRange);
4680           break;
4681         }
4682         default:
4683         {
4684           recolor_pixel.red=k[0]*pixel.red+k[1]*pixel.green+k[2]*pixel.blue+
4685             k[3]*pixel.index+k[4]*((Quantum) QuantumRange-pixel.opacity)+
4686             k[30]*QuantumRange;
4687           recolor_pixel.green=k[6]*pixel.red+k[7]*pixel.green+k[8]*pixel.blue+
4688             k[9]*pixel.index+k[10]*((Quantum) QuantumRange-pixel.opacity)+
4689             k[31]*QuantumRange;
4690           recolor_pixel.blue=k[12]*pixel.red+k[13]*pixel.green+k[14]*pixel.blue+
4691             k[15]*pixel.index+k[16]*((Quantum) QuantumRange-pixel.opacity)+
4692             k[32]*QuantumRange;
4693           if (image->colorspace == CMYKColorspace)
4694             recolor_pixel.index=k[18]*pixel.red+k[19]*pixel.green+k[20]*
4695               pixel.blue+k[21]*pixel.index+k[22]*((Quantum) QuantumRange-
4696               pixel.opacity)+k[33]*QuantumRange;
4697           recolor_pixel.opacity=(MagickRealType) QuantumRange-(k[24]*pixel.red+
4698             k[25]*pixel.green+k[26]*pixel.blue+k[27]*pixel.index+k[28]*
4699             (QuantumRange-pixel.opacity)+k[34]*QuantumRange);
4700           break;
4701         }
4702       }
4703       q->red=RoundToQuantum(recolor_pixel.red);
4704       q->green=RoundToQuantum(recolor_pixel.green);
4705       q->blue=RoundToQuantum(recolor_pixel.blue);
4706       q->opacity=RoundToQuantum(recolor_pixel.opacity);
4707       if (image->colorspace == CMYKColorspace)
4708         recolor_indexes[x]=RoundToQuantum(recolor_pixel.index);
4709       p++;
4710       q++;
4711     }
4712     if (SyncCacheViewAuthenticPixels(recolor_view,exception) == MagickFalse)
4713       status=MagickFalse;
4714     if (image->progress_monitor != (MagickProgressMonitor) NULL)
4715       {
4716         MagickBooleanType
4717           proceed;
4718
4719 #if defined(MAGICKCORE_OPENMP_SUPPORT) && (_OPENMP >= 200203)
4720   #pragma omp critical (MagickCore_RecolorImage)
4721 #endif
4722         proceed=SetImageProgress(image,RecolorImageTag,progress++,image->rows);
4723         if (proceed == MagickFalse)
4724           status=MagickFalse;
4725       }
4726   }
4727   recolor_view=DestroyCacheView(recolor_view);
4728   image_view=DestroyCacheView(image_view);
4729   if (status == MagickFalse)
4730     recolor_image=DestroyImage(recolor_image);
4731   return(recolor_image);
4732 }
4733 \f
4734 /*
4735 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4736 %                                                                             %
4737 %                                                                             %
4738 %                                                                             %
4739 %     S e p i a T o n e I m a g e                                             %
4740 %                                                                             %
4741 %                                                                             %
4742 %                                                                             %
4743 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4744 %
4745 %  MagickSepiaToneImage() applies a special effect to the image, similar to the
4746 %  effect achieved in a photo darkroom by sepia toning.  Threshold ranges from
4747 %  0 to QuantumRange and is a measure of the extent of the sepia toning.  A
4748 %  threshold of 80% is a good starting point for a reasonable tone.
4749 %
4750 %  The format of the SepiaToneImage method is:
4751 %
4752 %      Image *SepiaToneImage(const Image *image,const double threshold,
4753 %        ExceptionInfo *exception)
4754 %
4755 %  A description of each parameter follows:
4756 %
4757 %    o image: the image.
4758 %
4759 %    o threshold: the tone threshold.
4760 %
4761 %    o exception: return any errors or warnings in this structure.
4762 %
4763 */
4764 MagickExport Image *SepiaToneImage(const Image *image,const double threshold,
4765   ExceptionInfo *exception)
4766 {
4767 #define SepiaToneImageTag  "SepiaTone/Image"
4768
4769   Image
4770     *sepia_image;
4771
4772   long
4773     progress,
4774     y;
4775
4776   MagickBooleanType
4777     status;
4778
4779   CacheView
4780     *image_view,
4781     *sepia_view;
4782
4783   /*
4784     Initialize sepia-toned image attributes.
4785   */
4786   assert(image != (const Image *) NULL);
4787   assert(image->signature == MagickSignature);
4788   if (image->debug != MagickFalse)
4789     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4790   assert(exception != (ExceptionInfo *) NULL);
4791   assert(exception->signature == MagickSignature);
4792   sepia_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
4793   if (sepia_image == (Image *) NULL)
4794     return((Image *) NULL);
4795   if (SetImageStorageClass(sepia_image,DirectClass) == MagickFalse)
4796     {
4797       InheritException(exception,&sepia_image->exception);
4798       sepia_image=DestroyImage(sepia_image);
4799       return((Image *) NULL);
4800     }
4801   /*
4802     Tone each row of the image.
4803   */
4804   status=MagickTrue;
4805   progress=0;
4806   image_view=AcquireCacheView(image);
4807   sepia_view=AcquireCacheView(sepia_image);
4808 #if defined(MAGICKCORE_OPENMP_SUPPORT) && (_OPENMP >= 200203)
4809   #pragma omp parallel for shared(progress,status)
4810 #endif
4811   for (y=0; y < (long) image->rows; y++)
4812   {
4813     register const PixelPacket
4814       *__restrict p;
4815
4816     register long
4817       x;
4818
4819     register PixelPacket
4820       *__restrict q;
4821
4822     if (status == MagickFalse)
4823       continue;
4824     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
4825     q=QueueCacheViewAuthenticPixels(sepia_view,0,y,sepia_image->columns,1,
4826       exception);
4827     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
4828       {
4829         status=MagickFalse;
4830         continue;
4831       }
4832     for (x=0; x < (long) image->columns; x++)
4833     {
4834       MagickRealType
4835         intensity,
4836         tone;
4837
4838       intensity=(MagickRealType) PixelIntensityToQuantum(p);
4839       tone=intensity > threshold ? (MagickRealType) QuantumRange : intensity+
4840         (MagickRealType) QuantumRange-threshold;
4841       q->red=RoundToQuantum(tone);
4842       tone=intensity > (7.0*threshold/6.0) ? (MagickRealType) QuantumRange :
4843         intensity+(MagickRealType) QuantumRange-7.0*threshold/6.0;
4844       q->green=RoundToQuantum(tone);
4845       tone=intensity < (threshold/6.0) ? 0 : intensity-threshold/6.0;
4846       q->blue=RoundToQuantum(tone);
4847       tone=threshold/7.0;
4848       if ((MagickRealType) q->green < tone)
4849         q->green=RoundToQuantum(tone);
4850       if ((MagickRealType) q->blue < tone)
4851         q->blue=RoundToQuantum(tone);
4852       p++;
4853       q++;
4854     }
4855     if (SyncCacheViewAuthenticPixels(sepia_view,exception) == MagickFalse)
4856       status=MagickFalse;
4857     if (image->progress_monitor != (MagickProgressMonitor) NULL)
4858       {
4859         MagickBooleanType
4860           proceed;
4861
4862 #if defined(MAGICKCORE_OPENMP_SUPPORT) && (_OPENMP >= 200203)
4863   #pragma omp critical (MagickCore_SepiaToneImage)
4864 #endif
4865         proceed=SetImageProgress(image,SepiaToneImageTag,progress++,
4866           image->rows);
4867         if (proceed == MagickFalse)
4868           status=MagickFalse;
4869       }
4870   }
4871   sepia_view=DestroyCacheView(sepia_view);
4872   image_view=DestroyCacheView(image_view);
4873   (void) NormalizeImage(sepia_image);
4874   (void) ContrastImage(sepia_image,MagickTrue);
4875   if (status == MagickFalse)
4876     sepia_image=DestroyImage(sepia_image);
4877   return(sepia_image);
4878 }
4879 \f
4880 /*
4881 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4882 %                                                                             %
4883 %                                                                             %
4884 %                                                                             %
4885 %     S h a d o w I m a g e                                                   %
4886 %                                                                             %
4887 %                                                                             %
4888 %                                                                             %
4889 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4890 %
4891 %  ShadowImage() simulates a shadow from the specified image and returns it.
4892 %
4893 %  The format of the ShadowImage method is:
4894 %
4895 %      Image *ShadowImage(const Image *image,const double opacity,
4896 %        const double sigma,const long x_offset,const long y_offset,
4897 %        ExceptionInfo *exception)
4898 %
4899 %  A description of each parameter follows:
4900 %
4901 %    o image: the image.
4902 %
4903 %    o opacity: percentage transparency.
4904 %
4905 %    o sigma: the standard deviation of the Gaussian, in pixels.
4906 %
4907 %    o x_offset: the shadow x-offset.
4908 %
4909 %    o y_offset: the shadow y-offset.
4910 %
4911 %    o exception: return any errors or warnings in this structure.
4912 %
4913 */
4914 MagickExport Image *ShadowImage(const Image *image,const double opacity,
4915   const double sigma,const long x_offset,const long y_offset,
4916   ExceptionInfo *exception)
4917 {
4918 #define ShadowImageTag  "Shadow/Image"
4919
4920   Image
4921     *border_image,
4922     *clone_image,
4923     *shadow_image;
4924
4925   long
4926     progress,
4927     y;
4928
4929   MagickBooleanType
4930     status;
4931
4932   RectangleInfo
4933     border_info;
4934
4935   CacheView
4936     *image_view;
4937
4938   assert(image != (Image *) NULL);
4939   assert(image->signature == MagickSignature);
4940   if (image->debug != MagickFalse)
4941     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4942   assert(exception != (ExceptionInfo *) NULL);
4943   assert(exception->signature == MagickSignature);
4944   clone_image=CloneImage(image,0,0,MagickTrue,exception);
4945   if (clone_image == (Image *) NULL)
4946     return((Image *) NULL);
4947   (void) SetImageVirtualPixelMethod(clone_image,EdgeVirtualPixelMethod);
4948   clone_image->compose=OverCompositeOp;
4949   border_info.width=(unsigned long) (2.0*sigma+0.5);
4950   border_info.height=(unsigned long) (2.0*sigma+0.5);
4951   border_info.x=0;
4952   border_info.y=0;
4953   (void) QueryColorDatabase("none",&clone_image->border_color,exception);
4954   border_image=BorderImage(clone_image,&border_info,exception);
4955   clone_image=DestroyImage(clone_image);
4956   if (border_image == (Image *) NULL)
4957     return((Image *) NULL);
4958   if (border_image->matte == MagickFalse)
4959     (void) SetImageAlphaChannel(border_image,OpaqueAlphaChannel);
4960   /*
4961     Shadow image.
4962   */
4963   status=MagickTrue;
4964   progress=0;
4965   image_view=AcquireCacheView(border_image);
4966 #if defined(MAGICKCORE_OPENMP_SUPPORT) && (_OPENMP >= 200203)
4967   #pragma omp parallel for shared(progress,status)
4968 #endif
4969   for (y=0; y < (long) border_image->rows; y++)
4970   {
4971     register long
4972       x;
4973
4974     register PixelPacket
4975       *__restrict q;
4976
4977     if (status == MagickFalse)
4978       continue;
4979     q=GetCacheViewAuthenticPixels(image_view,0,y,border_image->columns,1,
4980       exception);
4981     if (q == (PixelPacket *) NULL)
4982       {
4983         status=MagickFalse;
4984         continue;
4985       }
4986     for (x=0; x < (long) border_image->columns; x++)
4987     {
4988       q->red=border_image->background_color.red;
4989       q->green=border_image->background_color.green;
4990       q->blue=border_image->background_color.blue;
4991       if (border_image->matte == MagickFalse)
4992         q->opacity=border_image->background_color.opacity;
4993       else
4994         q->opacity=RoundToQuantum((MagickRealType) (QuantumRange-(QuantumRange-
4995           q->opacity)*opacity/100.0));
4996       q++;
4997     }
4998     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4999       status=MagickFalse;
5000     if (image->progress_monitor != (MagickProgressMonitor) NULL)
5001       {
5002         MagickBooleanType
5003           proceed;
5004
5005 #if defined(MAGICKCORE_OPENMP_SUPPORT) && (_OPENMP >= 200203)
5006   #pragma omp critical (MagickCore_ShadowImage)
5007 #endif
5008         proceed=SetImageProgress(image,ShadowImageTag,progress++,
5009           border_image->rows);
5010         if (proceed == MagickFalse)
5011           status=MagickFalse;
5012       }
5013   }
5014   image_view=DestroyCacheView(image_view);
5015   shadow_image=BlurImageChannel(border_image,AlphaChannel,0.0,sigma,exception);
5016   border_image=DestroyImage(border_image);
5017   if (shadow_image == (Image *) NULL)
5018     return((Image *) NULL);
5019   if (shadow_image->page.width == 0)
5020     shadow_image->page.width=shadow_image->columns;
5021   if (shadow_image->page.height == 0)
5022     shadow_image->page.height=shadow_image->rows;
5023   shadow_image->page.width+=x_offset-(long) border_info.width;
5024   shadow_image->page.height+=y_offset-(long) border_info.height;
5025   shadow_image->page.x+=x_offset-(long) border_info.width;
5026   shadow_image->page.y+=y_offset-(long) border_info.height;
5027   return(shadow_image);
5028 }
5029 \f
5030 /*
5031 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5032 %                                                                             %
5033 %                                                                             %
5034 %                                                                             %
5035 %     S k e t c h I m a g e                                                   %
5036 %                                                                             %
5037 %                                                                             %
5038 %                                                                             %
5039 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5040 %
5041 %  SketchImage() simulates a pencil sketch.  We convolve the image with a
5042 %  Gaussian operator of the given radius and standard deviation (sigma).  For
5043 %  reasonable results, radius should be larger than sigma.  Use a radius of 0
5044 %  and SketchImage() selects a suitable radius for you.  Angle gives the angle
5045 %  of the sketch.
5046 %
5047 %  The format of the SketchImage method is:
5048 %
5049 %    Image *SketchImage(const Image *image,const double radius,
5050 %      const double sigma,const double angle,ExceptionInfo *exception)
5051 %
5052 %  A description of each parameter follows:
5053 %
5054 %    o image: the image.
5055 %
5056 %    o radius: the radius of the Gaussian, in pixels, not counting
5057 %      the center pixel.
5058 %
5059 %    o sigma: the standard deviation of the Gaussian, in pixels.
5060 %
5061 %    o angle: Apply the effect along this angle.
5062 %
5063 %    o exception: return any errors or warnings in this structure.
5064 %
5065 */
5066 MagickExport Image *SketchImage(const Image *image,const double radius,
5067   const double sigma,const double angle,ExceptionInfo *exception)
5068 {
5069   Image
5070     *blend_image,
5071     *blur_image,
5072     *dodge_image,
5073     *random_image,
5074     *sketch_image;
5075
5076   long
5077     y;
5078
5079   MagickBooleanType
5080     status;
5081
5082   MagickPixelPacket
5083     zero;
5084
5085   RandomInfo
5086     *random_info;
5087
5088   CacheView
5089     *random_view;
5090
5091   /*
5092     Sketch image.
5093   */
5094   random_image=CloneImage(image,image->columns << 1,image->rows << 1,
5095     MagickTrue,exception);
5096   if (random_image == (Image *) NULL)
5097     return((Image *) NULL);
5098   status=MagickTrue;
5099   GetMagickPixelPacket(random_image,&zero);
5100   random_info=AcquireRandomInfo();
5101   random_view=AcquireCacheView(random_image);
5102   for (y=0; y < (long) random_image->rows; y++)
5103   {
5104     MagickPixelPacket
5105       pixel;
5106
5107     register IndexPacket
5108       *__restrict indexes;
5109
5110     register long
5111       x;
5112
5113     register PixelPacket
5114       *__restrict q;
5115
5116     q=QueueCacheViewAuthenticPixels(random_view,0,y,random_image->columns,1,
5117       exception);
5118     if (q == (PixelPacket *) NULL)
5119       {
5120         status=MagickFalse;
5121         continue;
5122       }
5123     indexes=GetCacheViewAuthenticIndexQueue(random_view);
5124     pixel=zero;
5125     for (x=0; x < (long) random_image->columns; x++)
5126     {
5127       pixel.red=(MagickRealType) (QuantumRange*
5128         GetPseudoRandomValue(random_info));
5129       pixel.green=pixel.red;
5130       pixel.blue=pixel.red;
5131       if (image->colorspace == CMYKColorspace)
5132         pixel.index=pixel.red;
5133       SetPixelPacket(random_image,&pixel,q,indexes+x);
5134       q++;
5135     }
5136     if (SyncCacheViewAuthenticPixels(random_view,exception) == MagickFalse)
5137       status=MagickFalse;
5138     if (status == MagickFalse)
5139       break;
5140   }
5141   random_view=DestroyCacheView(random_view);
5142   random_info=DestroyRandomInfo(random_info);
5143   if (status == MagickFalse)
5144     {
5145       random_image=DestroyImage(random_image);
5146       return(random_image);
5147     }
5148   blur_image=MotionBlurImage(random_image,radius,sigma,angle,exception);
5149   random_image=DestroyImage(random_image);
5150   if (blur_image == (Image *) NULL)
5151     return((Image *) NULL);
5152   dodge_image=EdgeImage(blur_image,radius,exception);
5153   blur_image=DestroyImage(blur_image);
5154   if (dodge_image == (Image *) NULL)
5155     return((Image *) NULL);
5156   (void) NormalizeImage(dodge_image);
5157   (void) NegateImage(dodge_image,MagickFalse);
5158   (void) TransformImage(&dodge_image,(char *) NULL,"50%");
5159   sketch_image=CloneImage(image,0,0,MagickTrue,exception);
5160   if (sketch_image == (Image *) NULL)
5161     {
5162       dodge_image=DestroyImage(dodge_image);
5163       return((Image *) NULL);
5164     }
5165   (void) CompositeImage(sketch_image,ColorDodgeCompositeOp,dodge_image,0,0);
5166   dodge_image=DestroyImage(dodge_image);
5167   blend_image=CloneImage(image,0,0,MagickTrue,exception);
5168   if (blend_image == (Image *) NULL)
5169     {
5170       sketch_image=DestroyImage(sketch_image);
5171       return((Image *) NULL);
5172     }
5173   (void) SetImageArtifact(blend_image,"compose:args","20x80");
5174   (void) CompositeImage(sketch_image,BlendCompositeOp,blend_image,0,0);
5175   blend_image=DestroyImage(blend_image);
5176   return(sketch_image);
5177 }
5178 \f
5179 /*
5180 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5181 %                                                                             %
5182 %                                                                             %
5183 %                                                                             %
5184 %     S o l a r i z e I m a g e                                               %
5185 %                                                                             %
5186 %                                                                             %
5187 %                                                                             %
5188 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5189 %
5190 %  SolarizeImage() applies a special effect to the image, similar to the effect
5191 %  achieved in a photo darkroom by selectively exposing areas of photo
5192 %  sensitive paper to light.  Threshold ranges from 0 to QuantumRange and is a
5193 %  measure of the extent of the solarization.
5194 %
5195 %  The format of the SolarizeImage method is:
5196 %
5197 %      MagickBooleanType SolarizeImage(Image *image,const double threshold)
5198 %
5199 %  A description of each parameter follows:
5200 %
5201 %    o image: the image.
5202 %
5203 %    o threshold:  Define the extent of the solarization.
5204 %
5205 */
5206 MagickExport MagickBooleanType SolarizeImage(Image *image,
5207   const double threshold)
5208 {
5209 #define SolarizeImageTag  "Solarize/Image"
5210
5211   ExceptionInfo
5212     *exception;
5213
5214   long
5215     progress,
5216     y;
5217
5218   MagickBooleanType
5219     status;
5220
5221   CacheView
5222     *image_view;
5223
5224   assert(image != (Image *) NULL);
5225   assert(image->signature == MagickSignature);
5226   if (image->debug != MagickFalse)
5227     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5228   if (image->storage_class == PseudoClass)
5229     {
5230       register long
5231         i;
5232
5233       /*
5234         Solarize colormap.
5235       */
5236       for (i=0; i < (long) image->colors; i++)
5237       {
5238         if ((MagickRealType) image->colormap[i].red > threshold)
5239           image->colormap[i].red=(Quantum) QuantumRange-image->colormap[i].red;
5240         if ((MagickRealType) image->colormap[i].green > threshold)
5241           image->colormap[i].green=(Quantum) QuantumRange-
5242             image->colormap[i].green;
5243         if ((MagickRealType) image->colormap[i].blue > threshold)
5244           image->colormap[i].blue=(Quantum) QuantumRange-
5245             image->colormap[i].blue;
5246       }
5247     }
5248   /*
5249     Solarize image.
5250   */
5251   status=MagickTrue;
5252   progress=0;
5253   exception=(&image->exception);
5254   image_view=AcquireCacheView(image);
5255 #if defined(MAGICKCORE_OPENMP_SUPPORT) && (_OPENMP >= 200203)
5256   #pragma omp parallel for shared(progress,status)
5257 #endif
5258   for (y=0; y < (long) image->rows; y++)
5259   {
5260     register long
5261       x;
5262
5263     register PixelPacket
5264       *__restrict q;
5265
5266     if (status == MagickFalse)
5267       continue;
5268     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
5269       exception);
5270     if (q == (PixelPacket *) NULL)
5271       {
5272         status=MagickFalse;
5273         continue;
5274       }
5275     for (x=0; x < (long) image->columns; x++)
5276     {
5277       if ((MagickRealType) q->red > threshold)
5278         q->red=(Quantum) QuantumRange-q->red;
5279       if ((MagickRealType) q->green > threshold)
5280         q->green=(Quantum) QuantumRange-q->green;
5281       if ((MagickRealType) q->blue > threshold)
5282         q->blue=(Quantum) QuantumRange-q->blue;
5283       q++;
5284     }
5285     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5286       status=MagickFalse;
5287     if (image->progress_monitor != (MagickProgressMonitor) NULL)
5288       {
5289         MagickBooleanType
5290           proceed;
5291
5292 #if defined(MAGICKCORE_OPENMP_SUPPORT) && (_OPENMP >= 200203)
5293   #pragma omp critical (MagickCore_SolarizeImage)
5294 #endif
5295         proceed=SetImageProgress(image,SolarizeImageTag,progress++,image->rows);
5296         if (proceed == MagickFalse)
5297           status=MagickFalse;
5298       }
5299   }
5300   image_view=DestroyCacheView(image_view);
5301   return(status);
5302 }
5303 \f
5304 /*
5305 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5306 %                                                                             %
5307 %                                                                             %
5308 %                                                                             %
5309 %   S t e g a n o I m a g e                                                   %
5310 %                                                                             %
5311 %                                                                             %
5312 %                                                                             %
5313 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5314 %
5315 %  SteganoImage() hides a digital watermark within the image.  Recover
5316 %  the hidden watermark later to prove that the authenticity of an image.
5317 %  Offset defines the start position within the image to hide the watermark.
5318 %
5319 %  The format of the SteganoImage method is:
5320 %
5321 %      Image *SteganoImage(const Image *image,Image *watermark,
5322 %        ExceptionInfo *exception)
5323 %
5324 %  A description of each parameter follows:
5325 %
5326 %    o image: the image.
5327 %
5328 %    o watermark: the watermark image.
5329 %
5330 %    o exception: return any errors or warnings in this structure.
5331 %
5332 */
5333 MagickExport Image *SteganoImage(const Image *image,const Image *watermark,
5334   ExceptionInfo *exception)
5335 {
5336 #define GetBit(alpha,i) ((((unsigned long) (alpha) >> (unsigned long) \
5337   (i)) & 0x01) != 0)
5338 #define SetBit(alpha,i,set) (alpha)=(Quantum) ((set) ? (unsigned long) (alpha) \
5339   | (1UL << (unsigned long) (i)) : (unsigned long) (alpha) & \
5340   ~(1UL << (unsigned long) (i)))
5341 #define SteganoImageTag  "Stegano/Image"
5342
5343   Image
5344     *stegano_image;
5345
5346   int
5347     c;
5348
5349   long
5350     i,
5351     j,
5352     k,
5353     y;
5354
5355   MagickBooleanType
5356     status;
5357
5358   PixelPacket
5359     pixel;
5360
5361   register long
5362     x;
5363
5364   register PixelPacket
5365     *q;
5366
5367   unsigned long
5368     depth;
5369
5370   /*
5371     Initialize steganographic image attributes.
5372   */
5373   assert(image != (const Image *) NULL);
5374   assert(image->signature == MagickSignature);
5375   if (image->debug != MagickFalse)
5376     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5377   assert(watermark != (const Image *) NULL);
5378   assert(watermark->signature == MagickSignature);
5379   assert(exception != (ExceptionInfo *) NULL);
5380   assert(exception->signature == MagickSignature);
5381   stegano_image=CloneImage(image,0,0,MagickTrue,exception);
5382   if (stegano_image == (Image *) NULL)
5383     return((Image *) NULL);
5384   if (SetImageStorageClass(stegano_image,DirectClass) == MagickFalse)
5385     {
5386       InheritException(exception,&stegano_image->exception);
5387       stegano_image=DestroyImage(stegano_image);
5388       return((Image *) NULL);
5389     }
5390   stegano_image->depth=MAGICKCORE_QUANTUM_DEPTH;
5391   /*
5392     Hide watermark in low-order bits of image.
5393   */
5394   c=0;
5395   i=0;
5396   j=0;
5397   depth=stegano_image->depth;
5398   k=image->offset;
5399   for (i=(long) depth-1; (i >= 0) && (j < (long) depth); i--)
5400   {
5401     for (y=0; (y < (long) watermark->rows) && (j < (long) depth); y++)
5402     {
5403       for (x=0; (x < (long) watermark->columns) && (j < (long) depth); x++)
5404       {
5405         (void) GetOneVirtualPixel(watermark,x,y,&pixel,exception);
5406         if ((k/(long) stegano_image->columns) >= (long) stegano_image->rows)
5407           break;
5408         q=GetAuthenticPixels(stegano_image,k % (long) stegano_image->columns,
5409           k/(long) stegano_image->columns,1,1,exception);
5410         if (q == (PixelPacket *) NULL)
5411           break;
5412         switch (c)
5413         {
5414           case 0:
5415           {
5416             SetBit(q->red,j,GetBit(PixelIntensityToQuantum(&pixel),i));
5417             break;
5418           }
5419           case 1:
5420           {
5421             SetBit(q->green,j,GetBit(PixelIntensityToQuantum(&pixel),i));
5422             break;
5423           }
5424           case 2:
5425           {
5426             SetBit(q->blue,j,GetBit(PixelIntensityToQuantum(&pixel),i));
5427             break;
5428           }
5429         }
5430         if (SyncAuthenticPixels(stegano_image,exception) == MagickFalse)
5431           break;
5432         c++;
5433         if (c == 3)
5434           c=0;
5435         k++;
5436         if (k == (long) (stegano_image->columns*stegano_image->columns))
5437           k=0;
5438         if (k == image->offset)
5439           j++;
5440       }
5441     }
5442     if ((image->progress_monitor != (MagickProgressMonitor) NULL) &&
5443         (QuantumTick((MagickOffsetType) depth-i,depth) != MagickFalse))
5444       {
5445         status=image->progress_monitor(SteganoImageTag,(MagickOffsetType) depth-
5446           i,depth,image->client_data);
5447         if (status == MagickFalse)
5448           break;
5449       }
5450   }
5451   if (stegano_image->storage_class == PseudoClass)
5452     (void) SyncImage(stegano_image);
5453   return(stegano_image);
5454 }
5455 \f
5456 /*
5457 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5458 %                                                                             %
5459 %                                                                             %
5460 %                                                                             %
5461 %   S t e r e o A n a g l y p h I m a g e                                     %
5462 %                                                                             %
5463 %                                                                             %
5464 %                                                                             %
5465 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5466 %
5467 %  StereoAnaglyphImage() combines two images and produces a single image that
5468 %  is the composite of a left and right image of a stereo pair.  Special
5469 %  red-green stereo glasses are required to view this effect.
5470 %
5471 %  The format of the StereoAnaglyphImage method is:
5472 %
5473 %      Image *StereoImage(const Image *left_image,const Image *right_image,
5474 %        ExceptionInfo *exception)
5475 %      Image *StereoAnaglyphImage(const Image *left_image,
5476 %        const Image *right_image,const long x_offset,const long y_offset,
5477 %        ExceptionInfo *exception)
5478 %
5479 %  A description of each parameter follows:
5480 %
5481 %    o left_image: the left image.
5482 %
5483 %    o right_image: the right image.
5484 %
5485 %    o exception: return any errors or warnings in this structure.
5486 %
5487 %    o x_offset: amount, in pixels, by which the left image is offset to the
5488 %      right of the right image.
5489 %
5490 %    o y_offset: amount, in pixels, by which the left image is offset to the
5491 %      bottom of the right image.
5492 %
5493 %
5494 */
5495 MagickExport Image *StereoImage(const Image *left_image,
5496   const Image *right_image,ExceptionInfo *exception)
5497 {
5498   return(StereoAnaglyphImage(left_image,right_image,0,0,exception));
5499 }
5500
5501 MagickExport Image *StereoAnaglyphImage(const Image *left_image,
5502   const Image *right_image,const long x_offset,const long y_offset,
5503   ExceptionInfo *exception)
5504 {
5505 #define StereoImageTag  "Stereo/Image"
5506
5507   const Image
5508     *image;
5509
5510   Image
5511     *stereo_image;
5512
5513   long
5514     y;
5515
5516   MagickBooleanType
5517     status;
5518
5519   assert(left_image != (const Image *) NULL);
5520   assert(left_image->signature == MagickSignature);
5521   if (left_image->debug != MagickFalse)
5522     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
5523       left_image->filename);
5524   assert(right_image != (const Image *) NULL);
5525   assert(right_image->signature == MagickSignature);
5526   assert(exception != (ExceptionInfo *) NULL);
5527   assert(exception->signature == MagickSignature);
5528   assert(right_image != (const Image *) NULL);
5529   image=left_image;
5530   if ((left_image->columns != right_image->columns) ||
5531       (left_image->rows != right_image->rows))
5532     ThrowImageException(ImageError,"LeftAndRightImageSizesDiffer");
5533   /*
5534     Initialize stereo image attributes.
5535   */
5536   stereo_image=CloneImage(left_image,left_image->columns,left_image->rows,
5537     MagickTrue,exception);
5538   if (stereo_image == (Image *) NULL)
5539     return((Image *) NULL);
5540   if (SetImageStorageClass(stereo_image,DirectClass) == MagickFalse)
5541     {
5542       InheritException(exception,&stereo_image->exception);
5543       stereo_image=DestroyImage(stereo_image);
5544       return((Image *) NULL);
5545     }
5546   /*
5547     Copy left image to red channel and right image to blue channel.
5548   */
5549   for (y=0; y < (long) stereo_image->rows; y++)
5550   {
5551     register const PixelPacket
5552       *__restrict p,
5553       *__restrict q;
5554
5555     register long
5556       x;
5557
5558     register PixelPacket
5559       *__restrict r;
5560
5561     p=GetVirtualPixels(left_image,-x_offset,y-y_offset,image->columns,1,
5562       exception);
5563     q=GetVirtualPixels(right_image,0,y,right_image->columns,1,exception);
5564     r=QueueAuthenticPixels(stereo_image,0,y,stereo_image->columns,1,exception);
5565     if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL) ||
5566         (r == (PixelPacket *) NULL))
5567       break;
5568     for (x=0; x < (long) stereo_image->columns; x++)
5569     {
5570       r->red=p->red;
5571       r->green=q->green;
5572       r->blue=q->blue;
5573       r->opacity=(Quantum) ((p->opacity+q->opacity)/2);
5574       p++;
5575       q++;
5576       r++;
5577     }
5578     if (SyncAuthenticPixels(stereo_image,exception) == MagickFalse)
5579       break;
5580     if ((image->progress_monitor != (MagickProgressMonitor) NULL) &&
5581         (QuantumTick(y,image->rows) != MagickFalse))
5582       {
5583         status=image->progress_monitor(StereoImageTag,y,stereo_image->rows,
5584           stereo_image->client_data);
5585         if (status == MagickFalse)
5586           break;
5587       }
5588   }
5589   return(stereo_image);
5590 }
5591 \f
5592 /*
5593 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5594 %                                                                             %
5595 %                                                                             %
5596 %                                                                             %
5597 %     S w i r l I m a g e                                                     %
5598 %                                                                             %
5599 %                                                                             %
5600 %                                                                             %
5601 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5602 %
5603 %  SwirlImage() swirls the pixels about the center of the image, where
5604 %  degrees indicates the sweep of the arc through which each pixel is moved.
5605 %  You get a more dramatic effect as the degrees move from 1 to 360.
5606 %
5607 %  The format of the SwirlImage method is:
5608 %
5609 %      Image *SwirlImage(const Image *image,double degrees,
5610 %        ExceptionInfo *exception)
5611 %
5612 %  A description of each parameter follows:
5613 %
5614 %    o image: the image.
5615 %
5616 %    o degrees: Define the tightness of the swirling effect.
5617 %
5618 %    o exception: return any errors or warnings in this structure.
5619 %
5620 */
5621 MagickExport Image *SwirlImage(const Image *image,double degrees,
5622   ExceptionInfo *exception)
5623 {
5624 #define SwirlImageTag  "Swirl/Image"
5625
5626   Image
5627     *swirl_image;
5628
5629   long
5630     progress,
5631     y;
5632
5633   MagickBooleanType
5634     status;
5635
5636   MagickPixelPacket
5637     zero;
5638
5639   MagickRealType
5640     radius;
5641
5642   PointInfo
5643     center,
5644     scale;
5645
5646   ResampleFilter
5647     **resample_filter;
5648
5649   CacheView
5650     *image_view,
5651     *swirl_view;
5652
5653   /*
5654     Initialize swirl image attributes.
5655   */
5656   assert(image != (const Image *) NULL);
5657   assert(image->signature == MagickSignature);
5658   if (image->debug != MagickFalse)
5659     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5660   assert(exception != (ExceptionInfo *) NULL);
5661   assert(exception->signature == MagickSignature);
5662   swirl_image=CloneImage(image,0,0,MagickTrue,exception);
5663   if (swirl_image == (Image *) NULL)
5664     return((Image *) NULL);
5665   if (SetImageStorageClass(swirl_image,DirectClass) == MagickFalse)
5666     {
5667       InheritException(exception,&swirl_image->exception);
5668       swirl_image=DestroyImage(swirl_image);
5669       return((Image *) NULL);
5670     }
5671   if (swirl_image->background_color.opacity != OpaqueOpacity)
5672     swirl_image->matte=MagickTrue;
5673   /*
5674     Compute scaling factor.
5675   */
5676   center.x=(double) image->columns/2.0;
5677   center.y=(double) image->rows/2.0;
5678   radius=MagickMax(center.x,center.y);
5679   scale.x=1.0;
5680   scale.y=1.0;
5681   if (image->columns > image->rows)
5682     scale.y=(double) image->columns/(double) image->rows;
5683   else
5684     if (image->columns < image->rows)
5685       scale.x=(double) image->rows/(double) image->columns;
5686   degrees=(double) DegreesToRadians(degrees);
5687   /*
5688     Swirl image.
5689   */
5690   status=MagickTrue;
5691   progress=0;
5692   GetMagickPixelPacket(swirl_image,&zero);
5693   resample_filter=AcquireResampleFilterThreadSet(image,MagickTrue,exception);
5694   image_view=AcquireCacheView(image);
5695   swirl_view=AcquireCacheView(swirl_image);
5696 #if defined(MAGICKCORE_OPENMP_SUPPORT) && (_OPENMP >= 200203)
5697   #pragma omp parallel for shared(progress,status)
5698 #endif
5699   for (y=0; y < (long) image->rows; y++)
5700   {
5701     MagickPixelPacket
5702       pixel;
5703
5704     MagickRealType
5705       distance;
5706
5707     PointInfo
5708       delta;
5709
5710     register IndexPacket
5711       *__restrict swirl_indexes;
5712
5713     register long
5714       id,
5715       x;
5716
5717     register PixelPacket
5718       *__restrict q;
5719
5720     if (status == MagickFalse)
5721       continue;
5722     q=GetCacheViewAuthenticPixels(swirl_view,0,y,swirl_image->columns,1,
5723       exception);
5724     if (q == (PixelPacket *) NULL)
5725       {
5726         status=MagickFalse;
5727         continue;
5728       }
5729     swirl_indexes=GetCacheViewAuthenticIndexQueue(swirl_view);
5730     delta.y=scale.y*(double) (y-center.y);
5731     pixel=zero;
5732     id=GetOpenMPThreadId();
5733     for (x=0; x < (long) image->columns; x++)
5734     {
5735       /*
5736         Determine if the pixel is within an ellipse.
5737       */
5738       delta.x=scale.x*(double) (x-center.x);
5739       distance=delta.x*delta.x+delta.y*delta.y;
5740       if (distance < (radius*radius))
5741         {
5742           MagickRealType
5743             cosine,
5744             factor,
5745             sine;
5746
5747           /*
5748             Swirl the pixel.
5749           */
5750           factor=1.0-sqrt((double) distance)/radius;
5751           sine=sin((double) (degrees*factor*factor));
5752           cosine=cos((double) (degrees*factor*factor));
5753           (void) ResamplePixelColor(resample_filter[id],(double) ((cosine*
5754             delta.x-sine*delta.y)/scale.x+center.x),(double) ((sine*delta.x+
5755             cosine*delta.y)/scale.y+center.y),&pixel);
5756           SetPixelPacket(swirl_image,&pixel,q,swirl_indexes+x);
5757         }
5758       q++;
5759     }
5760     if (SyncCacheViewAuthenticPixels(swirl_view,exception) == MagickFalse)
5761       status=MagickFalse;
5762     if (image->progress_monitor != (MagickProgressMonitor) NULL)
5763       {
5764         MagickBooleanType
5765           proceed;
5766
5767 #if defined(MAGICKCORE_OPENMP_SUPPORT) && (_OPENMP >= 200203)
5768   #pragma omp critical (MagickCore_SwirlImage)
5769 #endif
5770         proceed=SetImageProgress(image,SwirlImageTag,progress++,image->rows);
5771         if (proceed == MagickFalse)
5772           status=MagickFalse;
5773       }
5774   }
5775   swirl_view=DestroyCacheView(swirl_view);
5776   image_view=DestroyCacheView(image_view);
5777   resample_filter=DestroyResampleFilterThreadSet(resample_filter);
5778   if (status == MagickFalse)
5779     swirl_image=DestroyImage(swirl_image);
5780   return(swirl_image);
5781 }
5782 \f
5783 /*
5784 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5785 %                                                                             %
5786 %                                                                             %
5787 %                                                                             %
5788 %     T i n t I m a g e                                                       %
5789 %                                                                             %
5790 %                                                                             %
5791 %                                                                             %
5792 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5793 %
5794 %  TintImage() applies a color vector to each pixel in the image.  The length
5795 %  of the vector is 0 for black and white and at its maximum for the midtones.
5796 %  The vector weighting function is f(x)=(1-(4.0*((x-0.5)*(x-0.5))))
5797 %
5798 %  The format of the TintImage method is:
5799 %
5800 %      Image *TintImage(const Image *image,const char *opacity,
5801 %        const PixelPacket tint,ExceptionInfo *exception)
5802 %
5803 %  A description of each parameter follows:
5804 %
5805 %    o image: the image.
5806 %
5807 %    o opacity: A color value used for tinting.
5808 %
5809 %    o tint: A color value used for tinting.
5810 %
5811 %    o exception: return any errors or warnings in this structure.
5812 %
5813 */
5814 MagickExport Image *TintImage(const Image *image,const char *opacity,
5815   const PixelPacket tint,ExceptionInfo *exception)
5816 {
5817 #define TintImageTag  "Tint/Image"
5818
5819   GeometryInfo
5820     geometry_info;
5821
5822   Image
5823     *tint_image;
5824
5825   long
5826     progress,
5827     y;
5828
5829   MagickBooleanType
5830     status;
5831
5832   MagickStatusType
5833     flags;
5834
5835   MagickPixelPacket
5836     color_vector,
5837     pixel;
5838
5839   CacheView
5840     *image_view,
5841     *tint_view;
5842
5843   /*
5844     Allocate tint image.
5845   */
5846   assert(image != (const Image *) NULL);
5847   assert(image->signature == MagickSignature);
5848   if (image->debug != MagickFalse)
5849     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5850   assert(exception != (ExceptionInfo *) NULL);
5851   assert(exception->signature == MagickSignature);
5852   tint_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
5853   if (tint_image == (Image *) NULL)
5854     return((Image *) NULL);
5855   if (SetImageStorageClass(tint_image,DirectClass) == MagickFalse)
5856     {
5857       InheritException(exception,&tint_image->exception);
5858       tint_image=DestroyImage(tint_image);
5859       return((Image *) NULL);
5860     }
5861   if (opacity == (const char *) NULL)
5862     return(tint_image);
5863   /*
5864     Determine RGB values of the color.
5865   */
5866   flags=ParseGeometry(opacity,&geometry_info);
5867   pixel.red=geometry_info.rho;
5868   if ((flags & SigmaValue) != 0)
5869     pixel.green=geometry_info.sigma;
5870   else
5871     pixel.green=pixel.red;
5872   if ((flags & XiValue) != 0)
5873     pixel.blue=geometry_info.xi;
5874   else
5875     pixel.blue=pixel.red;
5876   if ((flags & PsiValue) != 0)
5877     pixel.opacity=geometry_info.psi;
5878   else
5879     pixel.opacity=(MagickRealType) OpaqueOpacity;
5880   color_vector.red=(MagickRealType) (pixel.red*tint.red/100.0-
5881     PixelIntensity(&tint));
5882   color_vector.green=(MagickRealType) (pixel.green*tint.green/100.0-
5883     PixelIntensity(&tint));
5884   color_vector.blue=(MagickRealType) (pixel.blue*tint.blue/100.0-
5885     PixelIntensity(&tint));
5886   /*
5887     Tint image.
5888   */
5889   status=MagickTrue;
5890   progress=0;
5891   image_view=AcquireCacheView(image);
5892   tint_view=AcquireCacheView(tint_image);
5893 #if defined(MAGICKCORE_OPENMP_SUPPORT) && (_OPENMP >= 200203)
5894   #pragma omp parallel for shared(progress,status)
5895 #endif
5896   for (y=0; y < (long) image->rows; y++)
5897   {
5898     register const PixelPacket
5899       *__restrict p;
5900
5901     register long
5902       x;
5903
5904     register PixelPacket
5905       *__restrict q;
5906
5907     if (status == MagickFalse)
5908       continue;
5909     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
5910     q=QueueCacheViewAuthenticPixels(tint_view,0,y,tint_image->columns,1,
5911       exception);
5912     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
5913       {
5914         status=MagickFalse;
5915         continue;
5916       }
5917     for (x=0; x < (long) image->columns; x++)
5918     {
5919       MagickPixelPacket
5920         pixel;
5921
5922       MagickRealType
5923         weight;
5924
5925       weight=QuantumScale*p->red-0.5;
5926       pixel.red=(MagickRealType) p->red+color_vector.red*(1.0-(4.0*
5927         (weight*weight)));
5928       q->red=RoundToQuantum(pixel.red);
5929       weight=QuantumScale*p->green-0.5;
5930       pixel.green=(MagickRealType) p->green+color_vector.green*(1.0-(4.0*
5931         (weight*weight)));
5932       q->green=RoundToQuantum(pixel.green);
5933       weight=QuantumScale*p->blue-0.5;
5934       pixel.blue=(MagickRealType) p->blue+color_vector.blue*(1.0-(4.0*
5935         (weight*weight)));
5936       q->blue=RoundToQuantum(pixel.blue);
5937       q->opacity=p->opacity;
5938       p++;
5939       q++;
5940     }
5941     if (SyncCacheViewAuthenticPixels(tint_view,exception) == MagickFalse)
5942       status=MagickFalse;
5943     if (image->progress_monitor != (MagickProgressMonitor) NULL)
5944       {
5945         MagickBooleanType
5946           proceed;
5947
5948 #if defined(MAGICKCORE_OPENMP_SUPPORT) && (_OPENMP >= 200203)
5949   #pragma omp critical (MagickCore_TintImage)
5950 #endif
5951         proceed=SetImageProgress(image,TintImageTag,progress++,image->rows);
5952         if (proceed == MagickFalse)
5953           status=MagickFalse;
5954       }
5955   }
5956   tint_view=DestroyCacheView(tint_view);
5957   image_view=DestroyCacheView(image_view);
5958   if (status == MagickFalse)
5959     tint_image=DestroyImage(tint_image);
5960   return(tint_image);
5961 }
5962 \f
5963 /*
5964 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5965 %                                                                             %
5966 %                                                                             %
5967 %                                                                             %
5968 %     V i g n e t t e I m a g e                                               %
5969 %                                                                             %
5970 %                                                                             %
5971 %                                                                             %
5972 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5973 %
5974 %  VignetteImage() softens the edges of the image in vignette style.
5975 %
5976 %  The format of the VignetteImage method is:
5977 %
5978 %      Image *VignetteImage(const Image *image,const double radius,
5979 %        const double sigma,const long x,const long y,ExceptionInfo *exception)
5980 %
5981 %  A description of each parameter follows:
5982 %
5983 %    o image: the image.
5984 %
5985 %    o radius: the radius of the pixel neighborhood.
5986 %
5987 %    o sigma: the standard deviation of the Gaussian, in pixels.
5988 %
5989 %    o x, y:  Define the x and y ellipse offset.
5990 %
5991 %    o exception: return any errors or warnings in this structure.
5992 %
5993 */
5994 MagickExport Image *VignetteImage(const Image *image,const double radius,
5995   const double sigma,const long x,const long y,ExceptionInfo *exception)
5996 {
5997   char
5998     ellipse[MaxTextExtent];
5999
6000   DrawInfo
6001     *draw_info;
6002
6003   Image
6004     *canvas_image,
6005     *blur_image,
6006     *oval_image,
6007     *vignette_image;
6008
6009   assert(image != (Image *) NULL);
6010   assert(image->signature == MagickSignature);
6011   if (image->debug != MagickFalse)
6012     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
6013   assert(exception != (ExceptionInfo *) NULL);
6014   assert(exception->signature == MagickSignature);
6015   canvas_image=CloneImage(image,0,0,MagickTrue,exception);
6016   if (canvas_image == (Image *) NULL)
6017     return((Image *) NULL);
6018   if (SetImageStorageClass(canvas_image,DirectClass) == MagickFalse)
6019     {
6020       InheritException(exception,&canvas_image->exception);
6021       canvas_image=DestroyImage(canvas_image);
6022       return((Image *) NULL);
6023     }
6024   canvas_image->matte=MagickTrue;
6025   oval_image=CloneImage(canvas_image,canvas_image->columns,
6026     canvas_image->rows,MagickTrue,exception);
6027   if (oval_image == (Image *) NULL)
6028     {
6029       canvas_image=DestroyImage(canvas_image);
6030       return((Image *) NULL);
6031     }
6032   (void) QueryColorDatabase("#000000",&oval_image->background_color,exception);
6033   (void) SetImageBackgroundColor(oval_image);
6034   draw_info=CloneDrawInfo((const ImageInfo *) NULL,(const DrawInfo *) NULL);
6035   (void) QueryColorDatabase("#ffffff",&draw_info->fill,exception);
6036   (void) QueryColorDatabase("#ffffff",&draw_info->stroke,exception);
6037   (void) FormatMagickString(ellipse,MaxTextExtent,
6038     "ellipse %g,%g,%g,%g,0.0,360.0",image->columns/2.0,image->rows/2.0,
6039     image->columns/2.0-x,image->rows/2.0-y);
6040   draw_info->primitive=AcquireString(ellipse);
6041   (void) DrawImage(oval_image,draw_info);
6042   draw_info=DestroyDrawInfo(draw_info);
6043   blur_image=BlurImage(oval_image,radius,sigma,exception);
6044   oval_image=DestroyImage(oval_image);
6045   if (blur_image == (Image *) NULL)
6046     {
6047       canvas_image=DestroyImage(canvas_image);
6048       return((Image *) NULL);
6049     }
6050   blur_image->matte=MagickFalse;
6051   (void) CompositeImage(canvas_image,CopyOpacityCompositeOp,blur_image,0,0);
6052   blur_image=DestroyImage(blur_image);
6053   vignette_image=MergeImageLayers(canvas_image,FlattenLayer,exception);
6054   canvas_image=DestroyImage(canvas_image);
6055   return(vignette_image);
6056 }
6057 \f
6058 /*
6059 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6060 %                                                                             %
6061 %                                                                             %
6062 %                                                                             %
6063 %     W a v e I m a g e                                                       %
6064 %                                                                             %
6065 %                                                                             %
6066 %                                                                             %
6067 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6068 %
6069 %  WaveImage() creates a "ripple" effect in the image by shifting the pixels
6070 %  vertically along a sine wave whose amplitude and wavelength is specified
6071 %  by the given parameters.
6072 %
6073 %  The format of the WaveImage method is:
6074 %
6075 %      Image *WaveImage(const Image *image,const double amplitude,
6076 %        const double wave_length,ExceptionInfo *exception)
6077 %
6078 %  A description of each parameter follows:
6079 %
6080 %    o image: the image.
6081 %
6082 %    o amplitude, wave_length:  Define the amplitude and wave length of the
6083 %      sine wave.
6084 %
6085 %    o exception: return any errors or warnings in this structure.
6086 %
6087 */
6088 MagickExport Image *WaveImage(const Image *image,const double amplitude,
6089   const double wave_length,ExceptionInfo *exception)
6090 {
6091 #define WaveImageTag  "Wave/Image"
6092
6093   Image
6094     *wave_image;
6095
6096   long
6097     progress,
6098     y;
6099
6100   MagickBooleanType
6101     status;
6102
6103   MagickPixelPacket
6104     zero;
6105
6106   MagickRealType
6107     *sine_map;
6108
6109   register long
6110     i;
6111
6112   ResampleFilter
6113     **resample_filter;
6114
6115   CacheView
6116     *wave_view;
6117
6118   /*
6119     Initialize wave image attributes.
6120   */
6121   assert(image != (Image *) NULL);
6122   assert(image->signature == MagickSignature);
6123   if (image->debug != MagickFalse)
6124     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
6125   assert(exception != (ExceptionInfo *) NULL);
6126   assert(exception->signature == MagickSignature);
6127   wave_image=CloneImage(image,image->columns,(unsigned long) (image->rows+2.0*
6128     fabs(amplitude)),MagickTrue,exception);
6129   if (wave_image == (Image *) NULL)
6130     return((Image *) NULL);
6131   if (SetImageStorageClass(wave_image,DirectClass) == MagickFalse)
6132     {
6133       InheritException(exception,&wave_image->exception);
6134       wave_image=DestroyImage(wave_image);
6135       return((Image *) NULL);
6136     }
6137   if (wave_image->background_color.opacity != OpaqueOpacity)
6138     wave_image->matte=MagickTrue;
6139   /*
6140     Allocate sine map.
6141   */
6142   sine_map=(MagickRealType *) AcquireQuantumMemory((size_t) wave_image->columns,
6143     sizeof(*sine_map));
6144   if (sine_map == (MagickRealType *) NULL)
6145     {
6146       wave_image=DestroyImage(wave_image);
6147       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
6148     }
6149   for (i=0; i < (long) wave_image->columns; i++)
6150     sine_map[i]=fabs(amplitude)+amplitude*sin((2*MagickPI*i)/wave_length);
6151   /*
6152     Wave image.
6153   */
6154   status=MagickTrue;
6155   progress=0;
6156   GetMagickPixelPacket(wave_image,&zero);
6157   resample_filter=AcquireResampleFilterThreadSet(image,MagickTrue,exception);
6158   wave_view=AcquireCacheView(wave_image);
6159 #if defined(MAGICKCORE_OPENMP_SUPPORT) && (_OPENMP >= 200203)
6160   #pragma omp parallel for shared(progress,status)
6161 #endif
6162   for (y=0; y < (long) wave_image->rows; y++)
6163   {
6164     MagickPixelPacket
6165       pixel;
6166
6167     register IndexPacket
6168       *__restrict indexes;
6169
6170     register long
6171       id,
6172       x;
6173
6174     register PixelPacket
6175       *__restrict q;
6176
6177     if (status == MagickFalse)
6178       continue;
6179     q=QueueCacheViewAuthenticPixels(wave_view,0,y,wave_image->columns,1,
6180       exception);
6181     if (q == (PixelPacket *) NULL)
6182       {
6183         status=MagickFalse;
6184         continue;
6185       }
6186     indexes=GetCacheViewAuthenticIndexQueue(wave_view);
6187     pixel=zero;
6188     id=GetOpenMPThreadId();
6189     (void) SetResampleFilterVirtualPixelMethod(resample_filter[id],
6190       BackgroundVirtualPixelMethod);
6191     for (x=0; x < (long) wave_image->columns; x++)
6192     {
6193       (void) ResamplePixelColor(resample_filter[id],(double) x,(double) (y-
6194         sine_map[x]),&pixel);
6195       SetPixelPacket(wave_image,&pixel,q,indexes+x);
6196       q++;
6197     }
6198     if (SyncCacheViewAuthenticPixels(wave_view,exception) == MagickFalse)
6199       status=MagickFalse;
6200     if (image->progress_monitor != (MagickProgressMonitor) NULL)
6201       {
6202         MagickBooleanType
6203           proceed;
6204
6205 #if defined(MAGICKCORE_OPENMP_SUPPORT) && (_OPENMP >= 200203)
6206   #pragma omp critical (MagickCore_WaveImage)
6207 #endif
6208         proceed=SetImageProgress(image,WaveImageTag,progress++,image->rows);
6209         if (proceed == MagickFalse)
6210           status=MagickFalse;
6211       }
6212   }
6213   wave_view=DestroyCacheView(wave_view);
6214   resample_filter=DestroyResampleFilterThreadSet(resample_filter);
6215   sine_map=(MagickRealType *) RelinquishMagickMemory(sine_map);
6216   if (status == MagickFalse)
6217     wave_image=DestroyImage(wave_image);
6218   return(wave_image);
6219 }