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