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