]> 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(1.0);
2283           gamma=(MagickRealType) (2.0*j1((double) (MagickPI*alpha))/
2284             (MagickPI*alpha));
2285           return(gamma);
2286         }
2287       break;
2288     }
2289     case 'L':
2290     case 'l':
2291     {
2292       if (LocaleNCompare(expression,"ln",2) == 0)
2293         {
2294           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
2295             exception);
2296           return((MagickRealType) log((double) alpha));
2297         }
2298       if (LocaleNCompare(expression,"logtwo",4) == 0)
2299         {
2300           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2301             exception);
2302           return((MagickRealType) log10((double) alpha))/log10(2.0);
2303         }
2304       if (LocaleNCompare(expression,"log",3) == 0)
2305         {
2306           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2307             exception);
2308           return((MagickRealType) log10((double) alpha));
2309         }
2310       if (LocaleCompare(expression,"lightness") == 0)
2311         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2312       break;
2313     }
2314     case 'M':
2315     case 'm':
2316     {
2317       if (LocaleCompare(expression,"MaxRGB") == 0)
2318         return((MagickRealType) QuantumRange);
2319       if (LocaleNCompare(expression,"maxima",6) == 0)
2320         break;
2321       if (LocaleNCompare(expression,"max",3) == 0)
2322         return(FxMax(fx_info,channel,x,y,expression+3,exception));
2323       if (LocaleNCompare(expression,"minima",6) == 0)
2324         break;
2325       if (LocaleNCompare(expression,"min",3) == 0)
2326         return(FxMin(fx_info,channel,x,y,expression+3,exception));
2327       if (LocaleNCompare(expression,"mod",3) == 0)
2328         {
2329           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2330             exception);
2331           return((MagickRealType) fmod((double) alpha,(double) *beta));
2332         }
2333       if (LocaleCompare(expression,"m") == 0)
2334         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2335       break;
2336     }
2337     case 'N':
2338     case 'n':
2339     {
2340       if (LocaleCompare(expression,"n") == 0)
2341         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2342       break;
2343     }
2344     case 'O':
2345     case 'o':
2346     {
2347       if (LocaleCompare(expression,"Opaque") == 0)
2348         return(1.0);
2349       if (LocaleCompare(expression,"o") == 0)
2350         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2351       break;
2352     }
2353     case 'P':
2354     case 'p':
2355     {
2356       if (LocaleCompare(expression,"pi") == 0)
2357         return((MagickRealType) MagickPI);
2358       if (LocaleNCompare(expression,"pow",3) == 0)
2359         {
2360           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2361             exception);
2362           return((MagickRealType) pow((double) alpha,(double) *beta));
2363         }
2364       if (LocaleCompare(expression,"p") == 0)
2365         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2366       break;
2367     }
2368     case 'Q':
2369     case 'q':
2370     {
2371       if (LocaleCompare(expression,"QuantumRange") == 0)
2372         return((MagickRealType) QuantumRange);
2373       if (LocaleCompare(expression,"QuantumScale") == 0)
2374         return((MagickRealType) QuantumScale);
2375       break;
2376     }
2377     case 'R':
2378     case 'r':
2379     {
2380       if (LocaleNCompare(expression,"rand",4) == 0)
2381         return((MagickRealType) GetPseudoRandomValue(fx_info->random_info));
2382       if (LocaleNCompare(expression,"round",5) == 0)
2383         {
2384           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
2385             exception);
2386           if (alpha >= 0.0)
2387             return((MagickRealType) floor((double) alpha+0.5));
2388           return((MagickRealType) ceil((double) alpha-0.5));
2389         }
2390       if (LocaleCompare(expression,"r") == 0)
2391         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2392       break;
2393     }
2394     case 'S':
2395     case 's':
2396     {
2397       if (LocaleCompare(expression,"saturation") == 0)
2398         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2399       if (LocaleNCompare(expression,"sign",4) == 0)
2400         {
2401           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2402             exception);
2403           return(alpha < 0.0 ? -1.0 : 1.0);
2404         }
2405       if (LocaleNCompare(expression,"sinc",4) == 0)
2406         {
2407           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2408             exception);
2409           if (alpha == 0)
2410             return(1.0);
2411           gamma=(MagickRealType) (sin((double) (MagickPI*alpha))/
2412             (MagickPI*alpha));
2413           return(gamma);
2414         }
2415       if (LocaleNCompare(expression,"sinh",4) == 0)
2416         {
2417           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2418             exception);
2419           return((MagickRealType) sinh((double) alpha));
2420         }
2421       if (LocaleNCompare(expression,"sin",3) == 0)
2422         {
2423           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2424             exception);
2425           return((MagickRealType) sin((double) alpha));
2426         }
2427       if (LocaleNCompare(expression,"sqrt",4) == 0)
2428         {
2429           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2430             exception);
2431           return((MagickRealType) sqrt((double) alpha));
2432         }
2433       if (LocaleCompare(expression,"s") == 0)
2434         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2435       break;
2436     }
2437     case 'T':
2438     case 't':
2439     {
2440       if (LocaleNCompare(expression,"tanh",4) == 0)
2441         {
2442           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
2443             exception);
2444           return((MagickRealType) tanh((double) alpha));
2445         }
2446       if (LocaleNCompare(expression,"tan",3) == 0)
2447         {
2448           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
2449             exception);
2450           return((MagickRealType) tan((double) alpha));
2451         }
2452       if (LocaleCompare(expression,"Transparent") == 0)
2453         return(0.0);
2454       if (LocaleCompare(expression,"t") == 0)
2455         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2456       break;
2457     }
2458     case 'U':
2459     case 'u':
2460     {
2461       if (LocaleCompare(expression,"u") == 0)
2462         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2463       break;
2464     }
2465     case 'V':
2466     case 'v':
2467     {
2468       if (LocaleCompare(expression,"v") == 0)
2469         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2470       break;
2471     }
2472     case 'W':
2473     case 'w':
2474     {
2475       if (LocaleCompare(expression,"w") == 0)
2476         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2477       break;
2478     }
2479     case 'Y':
2480     case 'y':
2481     {
2482       if (LocaleCompare(expression,"y") == 0)
2483         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2484       break;
2485     }
2486     case 'Z':
2487     case 'z':
2488     {
2489       if (LocaleCompare(expression,"z") == 0)
2490         return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2491       break;
2492     }
2493     default:
2494       break;
2495   }
2496   q=(char *) expression;
2497   alpha=strtod(expression,&q);
2498   if (q == expression)
2499     return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
2500   return(alpha);
2501 }
2502
2503 MagickExport MagickBooleanType FxEvaluateExpression(FxInfo *fx_info,
2504   MagickRealType *alpha,ExceptionInfo *exception)
2505 {
2506   MagickBooleanType
2507     status;
2508
2509   status=FxEvaluateChannelExpression(fx_info,GrayChannel,0,0,alpha,exception);
2510   return(status);
2511 }
2512
2513 MagickExport MagickBooleanType FxPreprocessExpression(FxInfo *fx_info,
2514   MagickRealType *alpha,ExceptionInfo *exception)
2515 {
2516   FILE
2517     *file;
2518
2519   MagickBooleanType
2520     status;
2521
2522   file=fx_info->file;
2523   fx_info->file=(FILE *) NULL;
2524   status=FxEvaluateChannelExpression(fx_info,GrayChannel,0,0,alpha,exception);
2525   fx_info->file=file;
2526   return(status);
2527 }
2528
2529 MagickExport MagickBooleanType FxEvaluateChannelExpression(FxInfo *fx_info,
2530   const ChannelType channel,const long x,const long y,MagickRealType *alpha,
2531   ExceptionInfo *exception)
2532 {
2533   MagickRealType
2534     beta;
2535
2536   beta=0.0;
2537   *alpha=FxEvaluateSubexpression(fx_info,channel,x,y,fx_info->expression,&beta,
2538     exception);
2539   return(exception->severity == OptionError ? MagickFalse : MagickTrue);
2540 }
2541 \f
2542 /*
2543 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2544 %                                                                             %
2545 %                                                                             %
2546 %                                                                             %
2547 %     F x I m a g e                                                           %
2548 %                                                                             %
2549 %                                                                             %
2550 %                                                                             %
2551 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2552 %
2553 %  FxImage() applies a mathematical expression to the specified image.
2554 %
2555 %  The format of the FxImage method is:
2556 %
2557 %      Image *FxImage(const Image *image,const char *expression,
2558 %        ExceptionInfo *exception)
2559 %      Image *FxImageChannel(const Image *image,const ChannelType channel,
2560 %        const char *expression,ExceptionInfo *exception)
2561 %
2562 %  A description of each parameter follows:
2563 %
2564 %    o image: the image.
2565 %
2566 %    o channel: the channel.
2567 %
2568 %    o expression: A mathematical expression.
2569 %
2570 %    o exception: return any errors or warnings in this structure.
2571 %
2572 */
2573
2574 static FxInfo **DestroyFxThreadSet(FxInfo **fx_info)
2575 {
2576   register long
2577     i;
2578
2579   assert(fx_info != (FxInfo **) NULL);
2580   for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
2581     if (fx_info[i] != (FxInfo *) NULL)
2582       fx_info[i]=DestroyFxInfo(fx_info[i]);
2583   fx_info=(FxInfo **) RelinquishAlignedMemory(fx_info);
2584   return(fx_info);
2585 }
2586
2587 static FxInfo **AcquireFxThreadSet(const Image *image,const char *expression,
2588   ExceptionInfo *exception)
2589 {
2590   char
2591     *fx_expression;
2592
2593   FxInfo
2594     **fx_info;
2595
2596   MagickRealType
2597     alpha;
2598
2599   register long
2600     i;
2601
2602   unsigned long
2603     number_threads;
2604
2605   number_threads=GetOpenMPMaximumThreads();
2606   fx_info=(FxInfo **) AcquireAlignedMemory(number_threads,sizeof(*fx_info));
2607   if (fx_info == (FxInfo **) NULL)
2608     return((FxInfo **) NULL);
2609   (void) ResetMagickMemory(fx_info,0,number_threads*sizeof(*fx_info));
2610   if (*expression != '@')
2611     fx_expression=ConstantString(expression);
2612   else
2613     fx_expression=FileToString(expression+1,~0,exception);
2614   for (i=0; i < (long) number_threads; i++)
2615   {
2616     fx_info[i]=AcquireFxInfo(image,fx_expression);
2617     if (fx_info[i] == (FxInfo *) NULL)
2618       return(DestroyFxThreadSet(fx_info));
2619     (void) FxPreprocessExpression(fx_info[i],&alpha,fx_info[i]->exception);
2620   }
2621   fx_expression=DestroyString(fx_expression);
2622   return(fx_info);
2623 }
2624
2625 MagickExport Image *FxImage(const Image *image,const char *expression,
2626   ExceptionInfo *exception)
2627 {
2628   Image
2629     *fx_image;
2630
2631   fx_image=FxImageChannel(image,GrayChannel,expression,exception);
2632   return(fx_image);
2633 }
2634
2635 MagickExport Image *FxImageChannel(const Image *image,const ChannelType channel,
2636   const char *expression,ExceptionInfo *exception)
2637 {
2638 #define FxImageTag  "Fx/Image"
2639
2640   CacheView
2641     *fx_view;
2642
2643   FxInfo
2644     **restrict fx_info;
2645
2646   Image
2647     *fx_image;
2648
2649   long
2650     progress,
2651     y;
2652
2653   MagickBooleanType
2654     status;
2655
2656   MagickRealType
2657     alpha;
2658
2659   assert(image != (Image *) NULL);
2660   assert(image->signature == MagickSignature);
2661   if (image->debug != MagickFalse)
2662     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2663   fx_image=CloneImage(image,0,0,MagickTrue,exception);
2664   if (fx_image == (Image *) NULL)
2665     return((Image *) NULL);
2666   if (SetImageStorageClass(fx_image,DirectClass) == MagickFalse)
2667     {
2668       InheritException(exception,&fx_image->exception);
2669       fx_image=DestroyImage(fx_image);
2670       return((Image *) NULL);
2671     }
2672   fx_info=AcquireFxThreadSet(image,expression,exception);
2673   if (fx_info == (FxInfo **) NULL)
2674     {
2675       fx_image=DestroyImage(fx_image);
2676       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
2677     }
2678   status=FxPreprocessExpression(fx_info[0],&alpha,exception);
2679   if (status == MagickFalse)
2680     {
2681       fx_image=DestroyImage(fx_image);
2682       fx_info=DestroyFxThreadSet(fx_info);
2683       return((Image *) NULL);
2684     }
2685   /*
2686     Fx image.
2687   */
2688   status=MagickTrue;
2689   progress=0;
2690   fx_view=AcquireCacheView(fx_image);
2691 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2692   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2693 #endif
2694   for (y=0; y < (long) fx_image->rows; y++)
2695   {
2696     MagickRealType
2697       alpha;
2698
2699     register IndexPacket
2700       *restrict fx_indexes;
2701
2702     register long
2703       id,
2704       x;
2705
2706     register PixelPacket
2707       *restrict q;
2708
2709     if (status == MagickFalse)
2710       continue;
2711     q=GetCacheViewAuthenticPixels(fx_view,0,y,fx_image->columns,1,exception);
2712     if (q == (PixelPacket *) NULL)
2713       {
2714         status=MagickFalse;
2715         continue;
2716       }
2717     fx_indexes=GetCacheViewAuthenticIndexQueue(fx_view);
2718     id=GetOpenMPThreadId();
2719     alpha=0.0;
2720     for (x=0; x < (long) fx_image->columns; x++)
2721     {
2722       if ((channel & RedChannel) != 0)
2723         {
2724           (void) FxEvaluateChannelExpression(fx_info[id],RedChannel,x,y,
2725             &alpha,exception);
2726           q->red=ClampToQuantum((MagickRealType) QuantumRange*alpha);
2727         }
2728       if ((channel & GreenChannel) != 0)
2729         {
2730           (void) FxEvaluateChannelExpression(fx_info[id],GreenChannel,x,y,
2731             &alpha,exception);
2732           q->green=ClampToQuantum((MagickRealType) QuantumRange*alpha);
2733         }
2734       if ((channel & BlueChannel) != 0)
2735         {
2736           (void) FxEvaluateChannelExpression(fx_info[id],BlueChannel,x,y,
2737             &alpha,exception);
2738           q->blue=ClampToQuantum((MagickRealType) QuantumRange*alpha);
2739         }
2740       if ((channel & OpacityChannel) != 0)
2741         {
2742           (void) FxEvaluateChannelExpression(fx_info[id],OpacityChannel,x,y,
2743             &alpha,exception);
2744           if (image->matte == MagickFalse)
2745             q->opacity=ClampToQuantum((MagickRealType) QuantumRange*alpha);
2746           else
2747             q->opacity=ClampToQuantum((MagickRealType) (QuantumRange-
2748               QuantumRange*alpha));
2749         }
2750       if (((channel & IndexChannel) != 0) &&
2751           (fx_image->colorspace == CMYKColorspace))
2752         {
2753           (void) FxEvaluateChannelExpression(fx_info[id],IndexChannel,x,y,
2754             &alpha,exception);
2755           fx_indexes[x]=(IndexPacket) ClampToQuantum((MagickRealType)
2756             QuantumRange*alpha);
2757         }
2758       q++;
2759     }
2760     if (SyncCacheViewAuthenticPixels(fx_view,exception) == MagickFalse)
2761       status=MagickFalse;
2762     if (image->progress_monitor != (MagickProgressMonitor) NULL)
2763       {
2764         MagickBooleanType
2765           proceed;
2766
2767 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2768   #pragma omp critical (MagickCore_FxImageChannel)
2769 #endif
2770         proceed=SetImageProgress(image,FxImageTag,progress++,image->rows);
2771         if (proceed == MagickFalse)
2772           status=MagickFalse;
2773       }
2774   }
2775   fx_image->matte=fx_info[0]->matte;
2776   fx_view=DestroyCacheView(fx_view);
2777   fx_info=DestroyFxThreadSet(fx_info);
2778   if (status == MagickFalse)
2779     fx_image=DestroyImage(fx_image);
2780   return(fx_image);
2781 }
2782 \f
2783 /*
2784 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2785 %                                                                             %
2786 %                                                                             %
2787 %                                                                             %
2788 %     I m p l o d e I m a g e                                                 %
2789 %                                                                             %
2790 %                                                                             %
2791 %                                                                             %
2792 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2793 %
2794 %  ImplodeImage() creates a new image that is a copy of an existing
2795 %  one with the image pixels "implode" by the specified percentage.  It
2796 %  allocates the memory necessary for the new Image structure and returns a
2797 %  pointer to the new image.
2798 %
2799 %  The format of the ImplodeImage method is:
2800 %
2801 %      Image *ImplodeImage(const Image *image,const double amount,
2802 %        ExceptionInfo *exception)
2803 %
2804 %  A description of each parameter follows:
2805 %
2806 %    o implode_image: Method ImplodeImage returns a pointer to the image
2807 %      after it is implode.  A null image is returned if there is a memory
2808 %      shortage.
2809 %
2810 %    o image: the image.
2811 %
2812 %    o amount:  Define the extent of the implosion.
2813 %
2814 %    o exception: return any errors or warnings in this structure.
2815 %
2816 */
2817 MagickExport Image *ImplodeImage(const Image *image,const double amount,
2818   ExceptionInfo *exception)
2819 {
2820 #define ImplodeImageTag  "Implode/Image"
2821
2822   CacheView
2823     *image_view,
2824     *implode_view;
2825
2826   Image
2827     *implode_image;
2828
2829   long
2830     progress,
2831     y;
2832
2833   MagickBooleanType
2834     status;
2835
2836   MagickPixelPacket
2837     zero;
2838
2839   MagickRealType
2840     radius;
2841
2842   PointInfo
2843     center,
2844     scale;
2845
2846   ResampleFilter
2847     **restrict resample_filter;
2848
2849   /*
2850     Initialize implode image attributes.
2851   */
2852   assert(image != (Image *) NULL);
2853   assert(image->signature == MagickSignature);
2854   if (image->debug != MagickFalse)
2855     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2856   assert(exception != (ExceptionInfo *) NULL);
2857   assert(exception->signature == MagickSignature);
2858   implode_image=CloneImage(image,0,0,MagickTrue,exception);
2859   if (implode_image == (Image *) NULL)
2860     return((Image *) NULL);
2861   if (SetImageStorageClass(implode_image,DirectClass) == MagickFalse)
2862     {
2863       InheritException(exception,&implode_image->exception);
2864       implode_image=DestroyImage(implode_image);
2865       return((Image *) NULL);
2866     }
2867   if (implode_image->background_color.opacity != OpaqueOpacity)
2868     implode_image->matte=MagickTrue;
2869   /*
2870     Compute scaling factor.
2871   */
2872   scale.x=1.0;
2873   scale.y=1.0;
2874   center.x=0.5*image->columns;
2875   center.y=0.5*image->rows;
2876   radius=center.x;
2877   if (image->columns > image->rows)
2878     scale.y=(double) image->columns/(double) image->rows;
2879   else
2880     if (image->columns < image->rows)
2881       {
2882         scale.x=(double) image->rows/(double) image->columns;
2883         radius=center.y;
2884       }
2885   /*
2886     Implode image.
2887   */
2888   status=MagickTrue;
2889   progress=0;
2890   GetMagickPixelPacket(implode_image,&zero);
2891   resample_filter=AcquireResampleFilterThreadSet(image,
2892     UndefinedVirtualPixelMethod,MagickTrue,exception);
2893   image_view=AcquireCacheView(image);
2894   implode_view=AcquireCacheView(implode_image);
2895 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2896   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2897 #endif
2898   for (y=0; y < (long) image->rows; y++)
2899   {
2900     MagickPixelPacket
2901       pixel;
2902
2903     MagickRealType
2904       distance;
2905
2906     PointInfo
2907       delta;
2908
2909     register IndexPacket
2910       *restrict implode_indexes;
2911
2912     register long
2913       id,
2914       x;
2915
2916     register PixelPacket
2917       *restrict q;
2918
2919     if (status == MagickFalse)
2920       continue;
2921     q=GetCacheViewAuthenticPixels(implode_view,0,y,implode_image->columns,1,
2922       exception);
2923     if (q == (PixelPacket *) NULL)
2924       {
2925         status=MagickFalse;
2926         continue;
2927       }
2928     implode_indexes=GetCacheViewAuthenticIndexQueue(implode_view);
2929     delta.y=scale.y*(double) (y-center.y);
2930     pixel=zero;
2931     id=GetOpenMPThreadId();
2932     for (x=0; x < (long) image->columns; x++)
2933     {
2934       /*
2935         Determine if the pixel is within an ellipse.
2936       */
2937       delta.x=scale.x*(double) (x-center.x);
2938       distance=delta.x*delta.x+delta.y*delta.y;
2939       if (distance < (radius*radius))
2940         {
2941           double
2942             factor;
2943
2944           /*
2945             Implode the pixel.
2946           */
2947           factor=1.0;
2948           if (distance > 0.0)
2949             factor=pow(sin((double) (MagickPI*sqrt((double) distance)/
2950               radius/2)),-amount);
2951           (void) ResamplePixelColor(resample_filter[id],(double)
2952             (factor*delta.x/scale.x+center.x),(double) (factor*delta.y/
2953             scale.y+center.y),&pixel);
2954           SetPixelPacket(implode_image,&pixel,q,implode_indexes+x);
2955         }
2956       q++;
2957     }
2958     if (SyncCacheViewAuthenticPixels(implode_view,exception) == MagickFalse)
2959       status=MagickFalse;
2960     if (image->progress_monitor != (MagickProgressMonitor) NULL)
2961       {
2962         MagickBooleanType
2963           proceed;
2964
2965 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2966   #pragma omp critical (MagickCore_ImplodeImage)
2967 #endif
2968         proceed=SetImageProgress(image,ImplodeImageTag,progress++,image->rows);
2969         if (proceed == MagickFalse)
2970           status=MagickFalse;
2971       }
2972   }
2973   implode_view=DestroyCacheView(implode_view);
2974   image_view=DestroyCacheView(image_view);
2975   resample_filter=DestroyResampleFilterThreadSet(resample_filter);
2976   if (status == MagickFalse)
2977     implode_image=DestroyImage(implode_image);
2978   return(implode_image);
2979 }
2980 \f
2981 /*
2982 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2983 %                                                                             %
2984 %                                                                             %
2985 %                                                                             %
2986 %     M o r p h I m a g e s                                                   %
2987 %                                                                             %
2988 %                                                                             %
2989 %                                                                             %
2990 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2991 %
2992 %  The MorphImages() method requires a minimum of two images.  The first
2993 %  image is transformed into the second by a number of intervening images
2994 %  as specified by frames.
2995 %
2996 %  The format of the MorphImage method is:
2997 %
2998 %      Image *MorphImages(const Image *image,const unsigned long number_frames,
2999 %        ExceptionInfo *exception)
3000 %
3001 %  A description of each parameter follows:
3002 %
3003 %    o image: the image.
3004 %
3005 %    o number_frames:  Define the number of in-between image to generate.
3006 %      The more in-between frames, the smoother the morph.
3007 %
3008 %    o exception: return any errors or warnings in this structure.
3009 %
3010 */
3011 MagickExport Image *MorphImages(const Image *image,
3012   const unsigned long number_frames,ExceptionInfo *exception)
3013 {
3014 #define MorphImageTag  "Morph/Image"
3015
3016   Image
3017     *morph_image,
3018     *morph_images;
3019
3020   long
3021     y;
3022
3023   MagickOffsetType
3024     scene;
3025
3026   MagickRealType
3027     alpha,
3028     beta;
3029
3030   register const Image
3031     *next;
3032
3033   register long
3034     i;
3035
3036   MagickBooleanType
3037     status;
3038
3039   /*
3040     Clone first frame in sequence.
3041   */
3042   assert(image != (Image *) NULL);
3043   assert(image->signature == MagickSignature);
3044   if (image->debug != MagickFalse)
3045     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3046   assert(exception != (ExceptionInfo *) NULL);
3047   assert(exception->signature == MagickSignature);
3048   morph_images=CloneImage(image,0,0,MagickTrue,exception);
3049   if (morph_images == (Image *) NULL)
3050     return((Image *) NULL);
3051   if (GetNextImageInList(image) == (Image *) NULL)
3052     {
3053       /*
3054         Morph single image.
3055       */
3056       for (i=1; i < (long) number_frames; i++)
3057       {
3058         morph_image=CloneImage(image,0,0,MagickTrue,exception);
3059         if (morph_image == (Image *) NULL)
3060           {
3061             morph_images=DestroyImageList(morph_images);
3062             return((Image *) NULL);
3063           }
3064         AppendImageToList(&morph_images,morph_image);
3065         if (image->progress_monitor != (MagickProgressMonitor) NULL)
3066           {
3067             MagickBooleanType
3068               proceed;
3069
3070             proceed=SetImageProgress(image,MorphImageTag,i,number_frames);
3071             if (proceed == MagickFalse)
3072               status=MagickFalse;
3073           }
3074       }
3075       return(GetFirstImageInList(morph_images));
3076     }
3077   /*
3078     Morph image sequence.
3079   */
3080   status=MagickTrue;
3081   scene=0;
3082   next=image;
3083   for ( ; GetNextImageInList(next) != (Image *) NULL; next=GetNextImageInList(next))
3084   {
3085     for (i=0; i < (long) number_frames; i++)
3086     {
3087       CacheView
3088         *image_view,
3089         *morph_view;
3090
3091       beta=(MagickRealType) (i+1.0)/(MagickRealType) (number_frames+1.0);
3092       alpha=1.0-beta;
3093       morph_image=ZoomImage(next,(unsigned long) (alpha*next->columns+beta*
3094         GetNextImageInList(next)->columns+0.5),(unsigned long) (alpha*
3095         next->rows+beta*GetNextImageInList(next)->rows+0.5),exception);
3096       if (morph_image == (Image *) NULL)
3097         {
3098           morph_images=DestroyImageList(morph_images);
3099           return((Image *) NULL);
3100         }
3101       if (SetImageStorageClass(morph_image,DirectClass) == MagickFalse)
3102         {
3103           InheritException(exception,&morph_image->exception);
3104           morph_image=DestroyImage(morph_image);
3105           return((Image *) NULL);
3106         }
3107       AppendImageToList(&morph_images,morph_image);
3108       morph_images=GetLastImageInList(morph_images);
3109       morph_image=ZoomImage(GetNextImageInList(next),morph_images->columns,
3110         morph_images->rows,exception);
3111       if (morph_image == (Image *) NULL)
3112         {
3113           morph_images=DestroyImageList(morph_images);
3114           return((Image *) NULL);
3115         }
3116       image_view=AcquireCacheView(morph_image);
3117       morph_view=AcquireCacheView(morph_images);
3118 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3119   #pragma omp parallel for schedule(dynamic,4) shared(status)
3120 #endif
3121       for (y=0; y < (long) morph_images->rows; y++)
3122       {
3123         MagickBooleanType
3124           sync;
3125
3126         register const PixelPacket
3127           *restrict p;
3128
3129         register long
3130           x;
3131
3132         register PixelPacket
3133           *restrict q;
3134
3135         if (status == MagickFalse)
3136           continue;
3137         p=GetCacheViewVirtualPixels(image_view,0,y,morph_image->columns,1,
3138           exception);
3139         q=GetCacheViewAuthenticPixels(morph_view,0,y,morph_images->columns,1,
3140           exception);
3141         if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
3142           {
3143             status=MagickFalse;
3144             continue;
3145           }
3146         for (x=0; x < (long) morph_images->columns; x++)
3147         {
3148           q->red=ClampToQuantum(alpha*q->red+beta*GetRedPixelComponent(p));
3149           q->green=ClampToQuantum(alpha*q->green+beta*
3150             GetGreenPixelComponent(p));
3151           q->blue=ClampToQuantum(alpha*q->blue+beta*GetBluePixelComponent(p));
3152           q->opacity=ClampToQuantum(alpha*q->opacity+beta*
3153             GetOpacityPixelComponent(p));
3154           p++;
3155           q++;
3156         }
3157         sync=SyncCacheViewAuthenticPixels(morph_view,exception);
3158         if (sync == MagickFalse)
3159           status=MagickFalse;
3160       }
3161       morph_view=DestroyCacheView(morph_view);
3162       image_view=DestroyCacheView(image_view);
3163       morph_image=DestroyImage(morph_image);
3164     }
3165     if (i < (long) number_frames)
3166       break;
3167     /*
3168       Clone last frame in sequence.
3169     */
3170     morph_image=CloneImage(GetNextImageInList(next),0,0,MagickTrue,exception);
3171     if (morph_image == (Image *) NULL)
3172       {
3173         morph_images=DestroyImageList(morph_images);
3174         return((Image *) NULL);
3175       }
3176     AppendImageToList(&morph_images,morph_image);
3177     morph_images=GetLastImageInList(morph_images);
3178     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3179       {
3180         MagickBooleanType
3181           proceed;
3182
3183 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3184   #pragma omp critical (MagickCore_MorphImages)
3185 #endif
3186         proceed=SetImageProgress(image,MorphImageTag,scene,
3187           GetImageListLength(image));
3188         if (proceed == MagickFalse)
3189           status=MagickFalse;
3190       }
3191     scene++;
3192   }
3193   if (GetNextImageInList(next) != (Image *) NULL)
3194     {
3195       morph_images=DestroyImageList(morph_images);
3196       return((Image *) NULL);
3197     }
3198   return(GetFirstImageInList(morph_images));
3199 }
3200 \f
3201 /*
3202 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3203 %                                                                             %
3204 %                                                                             %
3205 %                                                                             %
3206 %     P l a s m a I m a g e                                                   %
3207 %                                                                             %
3208 %                                                                             %
3209 %                                                                             %
3210 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3211 %
3212 %  PlasmaImage() initializes an image with plasma fractal values.  The image
3213 %  must be initialized with a base color and the random number generator
3214 %  seeded before this method is called.
3215 %
3216 %  The format of the PlasmaImage method is:
3217 %
3218 %      MagickBooleanType PlasmaImage(Image *image,const SegmentInfo *segment,
3219 %        unsigned long attenuate,unsigned long depth)
3220 %
3221 %  A description of each parameter follows:
3222 %
3223 %    o image: the image.
3224 %
3225 %    o segment:   Define the region to apply plasma fractals values.
3226 %
3227 %    o attenuate: Define the plasmattenuation factor.
3228 %
3229 %    o depth: Limit the plasma recursion depth.
3230 %
3231 */
3232
3233 static inline Quantum PlasmaPixel(RandomInfo *random_info,
3234   const MagickRealType pixel,const MagickRealType noise)
3235 {
3236   Quantum
3237     plasma;
3238
3239   plasma=ClampToQuantum(pixel+noise*GetPseudoRandomValue(random_info)-
3240     noise/2.0);
3241   return(plasma);
3242 }
3243
3244 MagickExport MagickBooleanType PlasmaImageProxy(Image *image,
3245   RandomInfo *random_info,const SegmentInfo *segment,unsigned long attenuate,
3246   unsigned long depth)
3247 {
3248   ExceptionInfo
3249     *exception;
3250
3251   long
3252     x,
3253     x_mid,
3254     y,
3255     y_mid;
3256
3257   MagickRealType
3258     plasma;
3259
3260   PixelPacket
3261     u,
3262     v;
3263
3264   if (((segment->x2-segment->x1) == 0.0) && ((segment->y2-segment->y1) == 0.0))
3265     return(MagickTrue);
3266   if (depth != 0)
3267     {
3268       SegmentInfo
3269         local_info;
3270
3271       /*
3272         Divide the area into quadrants and recurse.
3273       */
3274       depth--;
3275       attenuate++;
3276       x_mid=(long) ceil(segment->x1+segment->x2-0.5)/2;
3277       y_mid=(long) ceil(segment->y1+segment->y2-0.5)/2;
3278       local_info=(*segment);
3279       local_info.x2=(double) x_mid;
3280       local_info.y2=(double) y_mid;
3281       (void) PlasmaImageProxy(image,random_info,&local_info,attenuate,depth);
3282       local_info=(*segment);
3283       local_info.y1=(double) y_mid;
3284       local_info.x2=(double) x_mid;
3285       (void) PlasmaImageProxy(image,random_info,&local_info,attenuate,depth);
3286       local_info=(*segment);
3287       local_info.x1=(double) x_mid;
3288       local_info.y2=(double) y_mid;
3289       (void) PlasmaImageProxy(image,random_info,&local_info,attenuate,depth);
3290       local_info=(*segment);
3291       local_info.x1=(double) x_mid;
3292       local_info.y1=(double) y_mid;
3293       return(PlasmaImageProxy(image,random_info,&local_info,attenuate,depth));
3294     }
3295   if (SetImageStorageClass(image,DirectClass) == MagickFalse)
3296     return(MagickFalse);
3297   x_mid=(long) ceil(segment->x1+segment->x2-0.5)/2;
3298   y_mid=(long) ceil(segment->y1+segment->y2-0.5)/2;
3299   if ((segment->x1 == (double) x_mid) && (segment->x2 == (double) x_mid) &&
3300       (segment->y1 == (double) y_mid) && (segment->y2 == (double) y_mid))
3301     return(MagickFalse);
3302   /*
3303     Average pixels and apply plasma.
3304   */
3305   exception=(&image->exception);
3306   plasma=(MagickRealType) QuantumRange/(2.0*attenuate);
3307   if ((segment->x1 != (double) x_mid) || (segment->x2 != (double) x_mid))
3308     {
3309       register PixelPacket
3310         *restrict q;
3311
3312       /*
3313         Left pixel.
3314       */
3315       x=(long) ceil(segment->x1-0.5);
3316       (void) GetOneVirtualPixel(image,x,(long) ceil(segment->y1-0.5),&u,
3317         exception);
3318       (void) GetOneVirtualPixel(image,x,(long) ceil(segment->y2-0.5),&v,
3319         exception);
3320       q=QueueAuthenticPixels(image,x,y_mid,1,1,exception);
3321       if (q == (PixelPacket *) NULL)
3322         return(MagickTrue);
3323       q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
3324         plasma);
3325       q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/2.0,
3326         plasma);
3327       q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
3328         plasma);
3329       (void) SyncAuthenticPixels(image,exception);
3330       if (segment->x1 != segment->x2)
3331         {
3332           /*
3333             Right pixel.
3334           */
3335           x=(long) ceil(segment->x2-0.5);
3336           (void) GetOneVirtualPixel(image,x,(long) ceil(segment->y1-0.5),&u,
3337             exception);
3338           (void) GetOneVirtualPixel(image,x,(long) ceil(segment->y2-0.5),&v,
3339             exception);
3340           q=QueueAuthenticPixels(image,x,y_mid,1,1,exception);
3341           if (q == (PixelPacket *) NULL)
3342             return(MagickTrue);
3343           q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
3344             plasma);
3345           q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/
3346             2.0,plasma);
3347           q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
3348              plasma);
3349           (void) SyncAuthenticPixels(image,exception);
3350         }
3351     }
3352   if ((segment->y1 != (double) y_mid) || (segment->y2 != (double) y_mid))
3353     {
3354       if ((segment->x1 != (double) x_mid) || (segment->y2 != (double) y_mid))
3355         {
3356           register PixelPacket
3357             *restrict q;
3358
3359           /*
3360             Bottom pixel.
3361           */
3362           y=(long) ceil(segment->y2-0.5);
3363           (void) GetOneVirtualPixel(image,(long) ceil(segment->x1-0.5),y,&u,
3364             exception);
3365           (void) GetOneVirtualPixel(image,(long) ceil(segment->x2-0.5),y,&v,
3366             exception);
3367           q=QueueAuthenticPixels(image,x_mid,y,1,1,exception);
3368           if (q == (PixelPacket *) NULL)
3369             return(MagickTrue);
3370           q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
3371             plasma);
3372           q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/
3373             2.0,plasma);
3374           q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
3375             plasma);
3376           (void) SyncAuthenticPixels(image,exception);
3377         }
3378       if (segment->y1 != segment->y2)
3379         {
3380           register PixelPacket
3381             *restrict q;
3382
3383           /*
3384             Top pixel.
3385           */
3386           y=(long) ceil(segment->y1-0.5);
3387           (void) GetOneVirtualPixel(image,(long) ceil(segment->x1-0.5),y,&u,
3388             exception);
3389           (void) GetOneVirtualPixel(image,(long) ceil(segment->x2-0.5),y,&v,
3390             exception);
3391           q=QueueAuthenticPixels(image,x_mid,y,1,1,exception);
3392           if (q == (PixelPacket *) NULL)
3393             return(MagickTrue);
3394           q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
3395             plasma);
3396           q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/
3397             2.0,plasma);
3398           q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
3399             plasma);
3400           (void) SyncAuthenticPixels(image,exception);
3401         }
3402     }
3403   if ((segment->x1 != segment->x2) || (segment->y1 != segment->y2))
3404     {
3405       register PixelPacket
3406         *restrict q;
3407
3408       /*
3409         Middle pixel.
3410       */
3411       x=(long) ceil(segment->x1-0.5);
3412       y=(long) ceil(segment->y1-0.5);
3413       (void) GetOneVirtualPixel(image,x,y,&u,exception);
3414       x=(long) ceil(segment->x2-0.5);
3415       y=(long) ceil(segment->y2-0.5);
3416       (void) GetOneVirtualPixel(image,x,y,&v,exception);
3417       q=QueueAuthenticPixels(image,x_mid,y_mid,1,1,exception);
3418       if (q == (PixelPacket *) NULL)
3419         return(MagickTrue);
3420       q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
3421         plasma);
3422       q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/2.0,
3423         plasma);
3424       q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
3425         plasma);
3426       (void) SyncAuthenticPixels(image,exception);
3427     }
3428   if (((segment->x2-segment->x1) < 3.0) && ((segment->y2-segment->y1) < 3.0))
3429     return(MagickTrue);
3430   return(MagickFalse);
3431 }
3432 \f
3433 MagickExport MagickBooleanType PlasmaImage(Image *image,
3434   const SegmentInfo *segment,unsigned long attenuate,unsigned long depth)
3435 {
3436   MagickBooleanType
3437     status;
3438
3439   RandomInfo
3440     *random_info;
3441
3442   if (image->debug != MagickFalse)
3443     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3444   assert(image != (Image *) NULL);
3445   assert(image->signature == MagickSignature);
3446   if (image->debug != MagickFalse)
3447     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3448   random_info=AcquireRandomInfo();
3449   status=PlasmaImageProxy(image,random_info,segment,attenuate,depth);
3450   random_info=DestroyRandomInfo(random_info);
3451   return(status);
3452 }
3453 \f
3454 /*
3455 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3456 %                                                                             %
3457 %                                                                             %
3458 %                                                                             %
3459 %   P o l a r o i d I m a g e                                                 %
3460 %                                                                             %
3461 %                                                                             %
3462 %                                                                             %
3463 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3464 %
3465 %  PolaroidImage() simulates a Polaroid picture.
3466 %
3467 %  The format of the AnnotateImage method is:
3468 %
3469 %      Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
3470 %        const double angle,ExceptionInfo exception)
3471 %
3472 %  A description of each parameter follows:
3473 %
3474 %    o image: the image.
3475 %
3476 %    o draw_info: the draw info.
3477 %
3478 %    o angle: Apply the effect along this angle.
3479 %
3480 %    o exception: return any errors or warnings in this structure.
3481 %
3482 */
3483 MagickExport Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
3484   const double angle,ExceptionInfo *exception)
3485 {
3486   const char
3487     *value;
3488
3489   long
3490     quantum;
3491
3492   Image
3493     *bend_image,
3494     *caption_image,
3495     *flop_image,
3496     *picture_image,
3497     *polaroid_image,
3498     *rotate_image,
3499     *trim_image;
3500
3501   unsigned long
3502     height;
3503
3504   /*
3505     Simulate a Polaroid picture.
3506   */
3507   assert(image != (Image *) NULL);
3508   assert(image->signature == MagickSignature);
3509   if (image->debug != MagickFalse)
3510     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3511   assert(exception != (ExceptionInfo *) NULL);
3512   assert(exception->signature == MagickSignature);
3513   quantum=(long) MagickMax(MagickMax((double) image->columns,(double)
3514     image->rows)/25.0,10.0);
3515   height=image->rows+2*quantum;
3516   caption_image=(Image *) NULL;
3517   value=GetImageProperty(image,"Caption");
3518   if (value != (const char *) NULL)
3519     {
3520       char
3521         *caption,
3522         geometry[MaxTextExtent];
3523
3524       DrawInfo
3525         *annotate_info;
3526
3527       long
3528         count;
3529
3530       MagickBooleanType
3531         status;
3532
3533       TypeMetric
3534         metrics;
3535
3536       /*
3537         Generate caption image.
3538       */
3539       caption_image=CloneImage(image,image->columns,1,MagickTrue,exception);
3540       if (caption_image == (Image *) NULL)
3541         return((Image *) NULL);
3542       annotate_info=CloneDrawInfo((const ImageInfo *) NULL,draw_info);
3543       caption=InterpretImageProperties((ImageInfo *) NULL,(Image *) image,
3544         value);
3545       (void) CloneString(&annotate_info->text,caption);
3546       count=FormatMagickCaption(caption_image,annotate_info,&metrics,&caption);
3547       status=SetImageExtent(caption_image,image->columns,(unsigned long)
3548         ((count+1)*(metrics.ascent-metrics.descent)+0.5));
3549       if (status == MagickFalse)
3550         caption_image=DestroyImage(caption_image);
3551       else
3552         {
3553           caption_image->background_color=image->border_color;
3554           (void) SetImageBackgroundColor(caption_image);
3555           (void) CloneString(&annotate_info->text,caption);
3556           (void) FormatMagickString(geometry,MaxTextExtent,"+0+%g",
3557             metrics.ascent);
3558           if (annotate_info->gravity == UndefinedGravity)
3559             (void) CloneString(&annotate_info->geometry,AcquireString(
3560               geometry));
3561           (void) AnnotateImage(caption_image,annotate_info);
3562           height+=caption_image->rows;
3563         }
3564       annotate_info=DestroyDrawInfo(annotate_info);
3565       caption=DestroyString(caption);
3566     }
3567   picture_image=CloneImage(image,image->columns+2*quantum,height,MagickTrue,
3568     exception);
3569   if (picture_image == (Image *) NULL)
3570     {
3571       if (caption_image != (Image *) NULL)
3572         caption_image=DestroyImage(caption_image);
3573       return((Image *) NULL);
3574     }
3575   picture_image->background_color=image->border_color;
3576   (void) SetImageBackgroundColor(picture_image);
3577   (void) CompositeImage(picture_image,OverCompositeOp,image,quantum,quantum);
3578   if (caption_image != (Image *) NULL)
3579     {
3580       (void) CompositeImage(picture_image,OverCompositeOp,caption_image,
3581         quantum,(long) (image->rows+3*quantum/2));
3582       caption_image=DestroyImage(caption_image);
3583     }
3584   (void) QueryColorDatabase("none",&picture_image->background_color,exception);
3585   (void) SetImageAlphaChannel(picture_image,OpaqueAlphaChannel);
3586   rotate_image=RotateImage(picture_image,90.0,exception);
3587   picture_image=DestroyImage(picture_image);
3588   if (rotate_image == (Image *) NULL)
3589     return((Image *) NULL);
3590   picture_image=rotate_image;
3591   bend_image=WaveImage(picture_image,0.01*picture_image->rows,2.0*
3592     picture_image->columns,exception);
3593   picture_image=DestroyImage(picture_image);
3594   if (bend_image == (Image *) NULL)
3595     return((Image *) NULL);
3596   InheritException(&bend_image->exception,exception);
3597   picture_image=bend_image;
3598   rotate_image=RotateImage(picture_image,-90.0,exception);
3599   picture_image=DestroyImage(picture_image);
3600   if (rotate_image == (Image *) NULL)
3601     return((Image *) NULL);
3602   picture_image=rotate_image;
3603   picture_image->background_color=image->background_color;
3604   polaroid_image=ShadowImage(picture_image,80.0,2.0,quantum/3,quantum/3,
3605     exception);
3606   if (polaroid_image == (Image *) NULL)
3607     {
3608       picture_image=DestroyImage(picture_image);
3609       return(picture_image);
3610     }
3611   flop_image=FlopImage(polaroid_image,exception);
3612   polaroid_image=DestroyImage(polaroid_image);
3613   if (flop_image == (Image *) NULL)
3614     {
3615       picture_image=DestroyImage(picture_image);
3616       return(picture_image);
3617     }
3618   polaroid_image=flop_image;
3619   (void) CompositeImage(polaroid_image,OverCompositeOp,picture_image,
3620     (long) (-0.01*picture_image->columns/2.0),0L);
3621   picture_image=DestroyImage(picture_image);
3622   (void) QueryColorDatabase("none",&polaroid_image->background_color,exception);
3623   rotate_image=RotateImage(polaroid_image,angle,exception);
3624   polaroid_image=DestroyImage(polaroid_image);
3625   if (rotate_image == (Image *) NULL)
3626     return((Image *) NULL);
3627   polaroid_image=rotate_image;
3628   trim_image=TrimImage(polaroid_image,exception);
3629   polaroid_image=DestroyImage(polaroid_image);
3630   if (trim_image == (Image *) NULL)
3631     return((Image *) NULL);
3632   polaroid_image=trim_image;
3633   return(polaroid_image);
3634 }
3635 \f
3636 /*
3637 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3638 %                                                                             %
3639 %                                                                             %
3640 %                                                                             %
3641 %     R e c o l o r I m a g e                                                 %
3642 %                                                                             %
3643 %                                                                             %
3644 %                                                                             %
3645 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3646 %
3647 %  RecolorImage() translate, scale, shear, or rotate image colors.  Although
3648 %  you can use variable sized matrices, typically you use a 5 x 5 for an RGBA
3649 %  image and a 6x6 for CMYKA.  Populate the last row with normalized values to
3650 %  translate.
3651 %
3652 %  The format of the RecolorImage method is:
3653 %
3654 %      Image *RecolorImage(const Image *image,const unsigned long order,
3655 %        const double *color_matrix,ExceptionInfo *exception)
3656 %
3657 %  A description of each parameter follows:
3658 %
3659 %    o image: the image.
3660 %
3661 %    o order: the number of columns and rows in the recolor matrix.
3662 %
3663 %    o color_matrix: An array of double representing the recolor matrix.
3664 %
3665 %    o exception: return any errors or warnings in this structure.
3666 %
3667 */
3668 MagickExport Image *RecolorImage(const Image *image,const unsigned long order,
3669   const double *color_matrix,ExceptionInfo *exception)
3670 {
3671 #define RecolorImageTag  "Recolor/Image"
3672
3673   CacheView
3674     *image_view,
3675     *recolor_view;
3676
3677   Image
3678     *recolor_image;
3679
3680   long
3681     progress,
3682     y;
3683
3684   MagickBooleanType
3685     status;
3686
3687   /*
3688     Initialize image attributes.
3689   */
3690   assert(image != (Image *) NULL);
3691   assert(image->signature == MagickSignature);
3692   if (image->debug != MagickFalse)
3693     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3694   assert(exception != (ExceptionInfo *) NULL);
3695   assert(exception->signature == MagickSignature);
3696   recolor_image=CloneImage(image,0,0,MagickTrue,exception);
3697   if (recolor_image == (Image *) NULL)
3698     return((Image *) NULL);
3699   if (SetImageStorageClass(recolor_image,DirectClass) == MagickFalse)
3700     {
3701       InheritException(exception,&recolor_image->exception);
3702       recolor_image=DestroyImage(recolor_image);
3703       return((Image *) NULL);
3704     }
3705   if (image->debug != MagickFalse)
3706     {
3707       char
3708         format[MaxTextExtent],
3709         *message;
3710
3711       long
3712         u,
3713         v;
3714
3715       register const double
3716         *k;
3717
3718       (void) LogMagickEvent(TransformEvent,GetMagickModule(),
3719         "  Recolor image with %ldx%ld color matrix:",order,order);
3720       message=AcquireString("");
3721       k=color_matrix;
3722       for (v=0; v < (long) order; v++)
3723       {
3724         *message='\0';
3725         (void) FormatMagickString(format,MaxTextExtent,"%ld: ",v);
3726         (void) ConcatenateString(&message,format);
3727         for (u=0; u < (long) order; u++)
3728         {
3729           (void) FormatMagickString(format,MaxTextExtent,"%+f ",*k++);
3730           (void) ConcatenateString(&message,format);
3731         }
3732         (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
3733       }
3734       message=DestroyString(message);
3735     }
3736   /*
3737     Recolor image.
3738   */
3739   status=MagickTrue;
3740   progress=0;
3741   image_view=AcquireCacheView(image);
3742   recolor_view=AcquireCacheView(recolor_image);
3743 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3744   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3745 #endif
3746   for (y=0; y < (long) image->rows; y++)
3747   {
3748     register const double
3749       *restrict k;
3750
3751     register const IndexPacket
3752       *restrict indexes;
3753
3754     register const PixelPacket
3755       *restrict p;
3756
3757     register long
3758       x;
3759
3760     register IndexPacket
3761       *restrict recolor_indexes;
3762
3763     register PixelPacket
3764       *restrict q;
3765
3766     if (status == MagickFalse)
3767       continue;
3768     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3769     q=GetCacheViewAuthenticPixels(recolor_view,0,y,recolor_image->columns,1,
3770       exception);
3771     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
3772       {
3773         status=MagickFalse;
3774         continue;
3775       }
3776     indexes=GetCacheViewVirtualIndexQueue(image_view);
3777     recolor_indexes=GetCacheViewAuthenticIndexQueue(recolor_view);
3778     k=color_matrix;
3779     switch (order)
3780     {
3781       case 0:
3782         break;
3783       case 1:
3784       {
3785         for (x=0; x < (long) image->columns; x++)
3786         {
3787           q->red=ClampToQuantum(k[0]*GetRedPixelComponent(p));
3788           p++;
3789           q++;
3790         }
3791         break;
3792       }
3793       case 2:
3794       {
3795         for (x=0; x < (long) image->columns; x++)
3796         {
3797           q->red=ClampToQuantum(k[0]*p->red+k[1]*GetGreenPixelComponent(p));
3798           q->green=ClampToQuantum(k[2]*p->red+k[3]*GetGreenPixelComponent(p));
3799           p++;
3800           q++;
3801         }
3802         break;
3803       }
3804       case 3:
3805       {
3806         for (x=0; x < (long) image->columns; x++)
3807         {
3808           q->red=ClampToQuantum(k[0]*p->red+k[1]*p->green+k[2]*GetBluePixelComponent(p));
3809           q->green=ClampToQuantum(k[3]*p->red+k[4]*p->green+k[5]*GetBluePixelComponent(p));
3810           q->blue=ClampToQuantum(k[6]*p->red+k[7]*p->green+k[8]*GetBluePixelComponent(p));
3811           p++;
3812           q++;
3813         }
3814         break;
3815       }
3816       case 4:
3817       {
3818         for (x=0; x < (long) image->columns; x++)
3819         {
3820           q->red=ClampToQuantum(k[0]*p->red+k[1]*p->green+k[2]*p->blue+
3821             k[12]*QuantumRange);
3822           q->green=ClampToQuantum(k[4]*p->red+k[5]*p->green+k[6]*p->blue+
3823             k[13]*QuantumRange);
3824           q->blue=ClampToQuantum(k[8]*p->red+k[9]*p->green+k[10]*p->blue+
3825             k[14]*QuantumRange);
3826           p++;
3827           q++;
3828         }
3829         break;
3830       }
3831       case 5:
3832       {
3833         for (x=0; x < (long) image->columns; x++)
3834         {
3835           q->red=ClampToQuantum(k[0]*p->red+k[1]*p->green+k[2]*p->blue+
3836             k[3]*(GetAlphaPixelComponent(p))+k[20]*QuantumRange);
3837           q->green=ClampToQuantum(k[5]*p->red+k[6]*p->green+k[7]*p->blue+
3838             k[8]*(GetAlphaPixelComponent(p))+k[21]*QuantumRange);
3839           q->blue=ClampToQuantum(k[10]*p->red+k[11]*p->green+k[12]*p->blue+
3840             k[13]*(GetAlphaPixelComponent(p))+k[22]*QuantumRange);
3841           if (image->matte != MagickFalse)
3842             q->opacity=ClampToQuantum((MagickRealType) QuantumRange-(k[15]*
3843               p->red+k[16]*p->green+k[17]*p->blue+k[18]*
3844               GetAlphaPixelComponent(p)+k[23]*QuantumRange));
3845           p++;
3846           q++;
3847         }
3848         break;
3849       }
3850       default:
3851       {
3852         for (x=0; x < (long) image->columns; x++)
3853         {
3854           q->red=ClampToQuantum(k[0]*p->red+k[1]*p->green+k[2]*p->blue+
3855             k[3]*indexes[x]+k[4]*((Quantum) GetAlphaPixelComponent(p))+
3856             k[30]*QuantumRange);
3857           q->green=ClampToQuantum(k[6]*p->red+k[7]*p->green+k[8]*p->blue+
3858             k[9]*indexes[x]+k[10]*((Quantum) GetAlphaPixelComponent(p))+
3859             k[31]*QuantumRange);
3860           q->blue=ClampToQuantum(k[12]*p->red+k[13]*p->green+k[14]*p->blue+
3861             k[15]*indexes[x]+k[16]*((Quantum) GetAlphaPixelComponent(p))+
3862             k[32]*QuantumRange);
3863           if (image->matte != MagickFalse)
3864             q->opacity=ClampToQuantum((MagickRealType) QuantumRange-(k[24]*
3865               p->red+k[25]*p->green+k[26]*p->blue+k[27]*indexes[x]+
3866               k[28]*(GetAlphaPixelComponent(p))+k[34]*QuantumRange));
3867           if (image->colorspace == CMYKColorspace)
3868             recolor_indexes[x]=ClampToQuantum(k[18]*p->red+k[19]*p->green+k[20]*
3869               p->blue+k[21]*indexes[x]+k[22]*((Quantum) QuantumRange-
3870               GetOpacityPixelComponent(p))+k[33]*QuantumRange);
3871           p++;
3872           q++;
3873         }
3874         break;
3875       }
3876     }
3877     if (SyncCacheViewAuthenticPixels(recolor_view,exception) == MagickFalse)
3878       status=MagickFalse;
3879     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3880       {
3881         MagickBooleanType
3882           proceed;
3883
3884 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3885   #pragma omp critical (MagickCore_RecolorImage)
3886 #endif
3887         proceed=SetImageProgress(image,RecolorImageTag,progress++,image->rows);
3888         if (proceed == MagickFalse)
3889           status=MagickFalse;
3890       }
3891   }
3892   recolor_view=DestroyCacheView(recolor_view);
3893   image_view=DestroyCacheView(image_view);
3894   if (status == MagickFalse)
3895     recolor_image=DestroyImage(recolor_image);
3896   return(recolor_image);
3897 }
3898 \f
3899 /*
3900 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3901 %                                                                             %
3902 %                                                                             %
3903 %                                                                             %
3904 %     S e p i a T o n e I m a g e                                             %
3905 %                                                                             %
3906 %                                                                             %
3907 %                                                                             %
3908 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3909 %
3910 %  MagickSepiaToneImage() applies a special effect to the image, similar to the
3911 %  effect achieved in a photo darkroom by sepia toning.  Threshold ranges from
3912 %  0 to QuantumRange and is a measure of the extent of the sepia toning.  A
3913 %  threshold of 80% is a good starting point for a reasonable tone.
3914 %
3915 %  The format of the SepiaToneImage method is:
3916 %
3917 %      Image *SepiaToneImage(const Image *image,const double threshold,
3918 %        ExceptionInfo *exception)
3919 %
3920 %  A description of each parameter follows:
3921 %
3922 %    o image: the image.
3923 %
3924 %    o threshold: the tone threshold.
3925 %
3926 %    o exception: return any errors or warnings in this structure.
3927 %
3928 */
3929 MagickExport Image *SepiaToneImage(const Image *image,const double threshold,
3930   ExceptionInfo *exception)
3931 {
3932 #define SepiaToneImageTag  "SepiaTone/Image"
3933
3934   CacheView
3935     *image_view,
3936     *sepia_view;
3937
3938   Image
3939     *sepia_image;
3940
3941   long
3942     progress,
3943     y;
3944
3945   MagickBooleanType
3946     status;
3947
3948   /*
3949     Initialize sepia-toned image attributes.
3950   */
3951   assert(image != (const Image *) NULL);
3952   assert(image->signature == MagickSignature);
3953   if (image->debug != MagickFalse)
3954     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3955   assert(exception != (ExceptionInfo *) NULL);
3956   assert(exception->signature == MagickSignature);
3957   sepia_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
3958   if (sepia_image == (Image *) NULL)
3959     return((Image *) NULL);
3960   if (SetImageStorageClass(sepia_image,DirectClass) == MagickFalse)
3961     {
3962       InheritException(exception,&sepia_image->exception);
3963       sepia_image=DestroyImage(sepia_image);
3964       return((Image *) NULL);
3965     }
3966   /*
3967     Tone each row of the image.
3968   */
3969   status=MagickTrue;
3970   progress=0;
3971   image_view=AcquireCacheView(image);
3972   sepia_view=AcquireCacheView(sepia_image);
3973 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3974   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3975 #endif
3976   for (y=0; y < (long) image->rows; y++)
3977   {
3978     register const PixelPacket
3979       *restrict p;
3980
3981     register long
3982       x;
3983
3984     register PixelPacket
3985       *restrict q;
3986
3987     if (status == MagickFalse)
3988       continue;
3989     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3990     q=QueueCacheViewAuthenticPixels(sepia_view,0,y,sepia_image->columns,1,
3991       exception);
3992     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
3993       {
3994         status=MagickFalse;
3995         continue;
3996       }
3997     for (x=0; x < (long) image->columns; x++)
3998     {
3999       MagickRealType
4000         intensity,
4001         tone;
4002
4003       intensity=(MagickRealType) PixelIntensityToQuantum(p);
4004       tone=intensity > threshold ? (MagickRealType) QuantumRange : intensity+
4005         (MagickRealType) QuantumRange-threshold;
4006       q->red=ClampToQuantum(tone);
4007       tone=intensity > (7.0*threshold/6.0) ? (MagickRealType) QuantumRange :
4008         intensity+(MagickRealType) QuantumRange-7.0*threshold/6.0;
4009       q->green=ClampToQuantum(tone);
4010       tone=intensity < (threshold/6.0) ? 0 : intensity-threshold/6.0;
4011       q->blue=ClampToQuantum(tone);
4012       tone=threshold/7.0;
4013       if ((MagickRealType) q->green < tone)
4014         q->green=ClampToQuantum(tone);
4015       if ((MagickRealType) q->blue < tone)
4016         q->blue=ClampToQuantum(tone);
4017       p++;
4018       q++;
4019     }
4020     if (SyncCacheViewAuthenticPixels(sepia_view,exception) == MagickFalse)
4021       status=MagickFalse;
4022     if (image->progress_monitor != (MagickProgressMonitor) NULL)
4023       {
4024         MagickBooleanType
4025           proceed;
4026
4027 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4028   #pragma omp critical (MagickCore_SepiaToneImage)
4029 #endif
4030         proceed=SetImageProgress(image,SepiaToneImageTag,progress++,
4031           image->rows);
4032         if (proceed == MagickFalse)
4033           status=MagickFalse;
4034       }
4035   }
4036   sepia_view=DestroyCacheView(sepia_view);
4037   image_view=DestroyCacheView(image_view);
4038   (void) NormalizeImage(sepia_image);
4039   (void) ContrastImage(sepia_image,MagickTrue);
4040   if (status == MagickFalse)
4041     sepia_image=DestroyImage(sepia_image);
4042   return(sepia_image);
4043 }
4044 \f
4045 /*
4046 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4047 %                                                                             %
4048 %                                                                             %
4049 %                                                                             %
4050 %     S h a d o w I m a g e                                                   %
4051 %                                                                             %
4052 %                                                                             %
4053 %                                                                             %
4054 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4055 %
4056 %  ShadowImage() simulates a shadow from the specified image and returns it.
4057 %
4058 %  The format of the ShadowImage method is:
4059 %
4060 %      Image *ShadowImage(const Image *image,const double opacity,
4061 %        const double sigma,const long x_offset,const long y_offset,
4062 %        ExceptionInfo *exception)
4063 %
4064 %  A description of each parameter follows:
4065 %
4066 %    o image: the image.
4067 %
4068 %    o opacity: percentage transparency.
4069 %
4070 %    o sigma: the standard deviation of the Gaussian, in pixels.
4071 %
4072 %    o x_offset: the shadow x-offset.
4073 %
4074 %    o y_offset: the shadow y-offset.
4075 %
4076 %    o exception: return any errors or warnings in this structure.
4077 %
4078 */
4079 MagickExport Image *ShadowImage(const Image *image,const double opacity,
4080   const double sigma,const long x_offset,const long y_offset,
4081   ExceptionInfo *exception)
4082 {
4083 #define ShadowImageTag  "Shadow/Image"
4084
4085   CacheView
4086     *image_view;
4087
4088   Image
4089     *border_image,
4090     *clone_image,
4091     *shadow_image;
4092
4093   long
4094     progress,
4095     y;
4096
4097   MagickBooleanType
4098     status;
4099
4100   RectangleInfo
4101     border_info;
4102
4103   assert(image != (Image *) NULL);
4104   assert(image->signature == MagickSignature);
4105   if (image->debug != MagickFalse)
4106     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4107   assert(exception != (ExceptionInfo *) NULL);
4108   assert(exception->signature == MagickSignature);
4109   clone_image=CloneImage(image,0,0,MagickTrue,exception);
4110   if (clone_image == (Image *) NULL)
4111     return((Image *) NULL);
4112   (void) SetImageVirtualPixelMethod(clone_image,EdgeVirtualPixelMethod);
4113   clone_image->compose=OverCompositeOp;
4114   border_info.width=(unsigned long) floor(2.0*sigma+0.5);
4115   border_info.height=(unsigned long) floor(2.0*sigma+0.5);
4116   border_info.x=0;
4117   border_info.y=0;
4118   (void) QueryColorDatabase("none",&clone_image->border_color,exception);
4119   border_image=BorderImage(clone_image,&border_info,exception);
4120   clone_image=DestroyImage(clone_image);
4121   if (border_image == (Image *) NULL)
4122     return((Image *) NULL);
4123   if (border_image->matte == MagickFalse)
4124     (void) SetImageAlphaChannel(border_image,OpaqueAlphaChannel);
4125   /*
4126     Shadow image.
4127   */
4128   status=MagickTrue;
4129   progress=0;
4130   image_view=AcquireCacheView(border_image);
4131 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4132   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4133 #endif
4134   for (y=0; y < (long) border_image->rows; y++)
4135   {
4136     register long
4137       x;
4138
4139     register PixelPacket
4140       *restrict q;
4141
4142     if (status == MagickFalse)
4143       continue;
4144     q=GetCacheViewAuthenticPixels(image_view,0,y,border_image->columns,1,
4145       exception);
4146     if (q == (PixelPacket *) NULL)
4147       {
4148         status=MagickFalse;
4149         continue;
4150       }
4151     for (x=0; x < (long) border_image->columns; x++)
4152     {
4153       q->red=border_image->background_color.red;
4154       q->green=border_image->background_color.green;
4155       q->blue=border_image->background_color.blue;
4156       if (border_image->matte == MagickFalse)
4157         q->opacity=border_image->background_color.opacity;
4158       else
4159         q->opacity=ClampToQuantum((MagickRealType) (QuantumRange-
4160           GetAlphaPixelComponent(q)*opacity/100.0));
4161       q++;
4162     }
4163     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4164       status=MagickFalse;
4165     if (image->progress_monitor != (MagickProgressMonitor) NULL)
4166       {
4167         MagickBooleanType
4168           proceed;
4169
4170 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4171   #pragma omp critical (MagickCore_ShadowImage)
4172 #endif
4173         proceed=SetImageProgress(image,ShadowImageTag,progress++,
4174           border_image->rows);
4175         if (proceed == MagickFalse)
4176           status=MagickFalse;
4177       }
4178   }
4179   image_view=DestroyCacheView(image_view);
4180   shadow_image=BlurImageChannel(border_image,AlphaChannel,0.0,sigma,exception);
4181   border_image=DestroyImage(border_image);
4182   if (shadow_image == (Image *) NULL)
4183     return((Image *) NULL);
4184   if (shadow_image->page.width == 0)
4185     shadow_image->page.width=shadow_image->columns;
4186   if (shadow_image->page.height == 0)
4187     shadow_image->page.height=shadow_image->rows;
4188   shadow_image->page.width+=x_offset-(long) border_info.width;
4189   shadow_image->page.height+=y_offset-(long) border_info.height;
4190   shadow_image->page.x+=x_offset-(long) border_info.width;
4191   shadow_image->page.y+=y_offset-(long) border_info.height;
4192   return(shadow_image);
4193 }
4194 \f
4195 /*
4196 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4197 %                                                                             %
4198 %                                                                             %
4199 %                                                                             %
4200 %     S k e t c h I m a g e                                                   %
4201 %                                                                             %
4202 %                                                                             %
4203 %                                                                             %
4204 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4205 %
4206 %  SketchImage() simulates a pencil sketch.  We convolve the image with a
4207 %  Gaussian operator of the given radius and standard deviation (sigma).  For
4208 %  reasonable results, radius should be larger than sigma.  Use a radius of 0
4209 %  and SketchImage() selects a suitable radius for you.  Angle gives the angle
4210 %  of the sketch.
4211 %
4212 %  The format of the SketchImage method is:
4213 %
4214 %    Image *SketchImage(const Image *image,const double radius,
4215 %      const double sigma,const double angle,ExceptionInfo *exception)
4216 %
4217 %  A description of each parameter follows:
4218 %
4219 %    o image: the image.
4220 %
4221 %    o radius: the radius of the Gaussian, in pixels, not counting
4222 %      the center pixel.
4223 %
4224 %    o sigma: the standard deviation of the Gaussian, in pixels.
4225 %
4226 %    o angle: Apply the effect along this angle.
4227 %
4228 %    o exception: return any errors or warnings in this structure.
4229 %
4230 */
4231 MagickExport Image *SketchImage(const Image *image,const double radius,
4232   const double sigma,const double angle,ExceptionInfo *exception)
4233 {
4234   CacheView
4235     *random_view;
4236
4237   Image
4238     *blend_image,
4239     *blur_image,
4240     *dodge_image,
4241     *random_image,
4242     *sketch_image;
4243
4244   long
4245     y;
4246
4247   MagickBooleanType
4248     status;
4249
4250   MagickPixelPacket
4251     zero;
4252
4253   RandomInfo
4254     **restrict random_info;
4255
4256   /*
4257     Sketch image.
4258   */
4259   random_image=CloneImage(image,image->columns << 1,image->rows << 1,
4260     MagickTrue,exception);
4261   if (random_image == (Image *) NULL)
4262     return((Image *) NULL);
4263   status=MagickTrue;
4264   GetMagickPixelPacket(random_image,&zero);
4265   random_info=AcquireRandomInfoThreadSet();
4266   random_view=AcquireCacheView(random_image);
4267 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4268   #pragma omp parallel for schedule(dynamic,4) shared(status)
4269 #endif
4270   for (y=0; y < (long) random_image->rows; y++)
4271   {
4272     MagickPixelPacket
4273       pixel;
4274
4275     register IndexPacket
4276       *restrict indexes;
4277
4278     register long
4279       id,
4280       x;
4281
4282     register PixelPacket
4283       *restrict q;
4284
4285     if (status == MagickFalse)
4286       continue;
4287     q=QueueCacheViewAuthenticPixels(random_view,0,y,random_image->columns,1,
4288       exception);
4289     if (q == (PixelPacket *) NULL)
4290       {
4291         status=MagickFalse;
4292         continue;
4293       }
4294     indexes=GetCacheViewAuthenticIndexQueue(random_view);
4295     pixel=zero;
4296     id=GetOpenMPThreadId();
4297     for (x=0; x < (long) random_image->columns; x++)
4298     {
4299       pixel.red=(MagickRealType) (QuantumRange*
4300         GetPseudoRandomValue(random_info[id]));
4301       pixel.green=pixel.red;
4302       pixel.blue=pixel.red;
4303       if (image->colorspace == CMYKColorspace)
4304         pixel.index=pixel.red;
4305       SetPixelPacket(random_image,&pixel,q,indexes+x);
4306       q++;
4307     }
4308     if (SyncCacheViewAuthenticPixels(random_view,exception) == MagickFalse)
4309       status=MagickFalse;
4310   }
4311   random_view=DestroyCacheView(random_view);
4312   random_info=DestroyRandomInfoThreadSet(random_info);
4313   if (status == MagickFalse)
4314     {
4315       random_image=DestroyImage(random_image);
4316       return(random_image);
4317     }
4318   blur_image=MotionBlurImage(random_image,radius,sigma,angle,exception);
4319   random_image=DestroyImage(random_image);
4320   if (blur_image == (Image *) NULL)
4321     return((Image *) NULL);
4322   dodge_image=EdgeImage(blur_image,radius,exception);
4323   blur_image=DestroyImage(blur_image);
4324   if (dodge_image == (Image *) NULL)
4325     return((Image *) NULL);
4326   (void) NormalizeImage(dodge_image);
4327   (void) NegateImage(dodge_image,MagickFalse);
4328   (void) TransformImage(&dodge_image,(char *) NULL,"50%");
4329   sketch_image=CloneImage(image,0,0,MagickTrue,exception);
4330   if (sketch_image == (Image *) NULL)
4331     {
4332       dodge_image=DestroyImage(dodge_image);
4333       return((Image *) NULL);
4334     }
4335   (void) CompositeImage(sketch_image,ColorDodgeCompositeOp,dodge_image,0,0);
4336   dodge_image=DestroyImage(dodge_image);
4337   blend_image=CloneImage(image,0,0,MagickTrue,exception);
4338   if (blend_image == (Image *) NULL)
4339     {
4340       sketch_image=DestroyImage(sketch_image);
4341       return((Image *) NULL);
4342     }
4343   (void) SetImageArtifact(blend_image,"compose:args","20x80");
4344   (void) CompositeImage(sketch_image,BlendCompositeOp,blend_image,0,0);
4345   blend_image=DestroyImage(blend_image);
4346   return(sketch_image);
4347 }
4348 \f
4349 /*
4350 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4351 %                                                                             %
4352 %                                                                             %
4353 %                                                                             %
4354 %     S o l a r i z e I m a g e                                               %
4355 %                                                                             %
4356 %                                                                             %
4357 %                                                                             %
4358 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4359 %
4360 %  SolarizeImage() applies a special effect to the image, similar to the effect
4361 %  achieved in a photo darkroom by selectively exposing areas of photo
4362 %  sensitive paper to light.  Threshold ranges from 0 to QuantumRange and is a
4363 %  measure of the extent of the solarization.
4364 %
4365 %  The format of the SolarizeImage method is:
4366 %
4367 %      MagickBooleanType SolarizeImage(Image *image,const double threshold)
4368 %
4369 %  A description of each parameter follows:
4370 %
4371 %    o image: the image.
4372 %
4373 %    o threshold:  Define the extent of the solarization.
4374 %
4375 */
4376 MagickExport MagickBooleanType SolarizeImage(Image *image,
4377   const double threshold)
4378 {
4379 #define SolarizeImageTag  "Solarize/Image"
4380
4381   CacheView
4382     *image_view;
4383
4384   ExceptionInfo
4385     *exception;
4386
4387   long
4388     progress,
4389     y;
4390
4391   MagickBooleanType
4392     status;
4393
4394   assert(image != (Image *) NULL);
4395   assert(image->signature == MagickSignature);
4396   if (image->debug != MagickFalse)
4397     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4398   if (image->storage_class == PseudoClass)
4399     {
4400       register long
4401         i;
4402
4403       /*
4404         Solarize colormap.
4405       */
4406       for (i=0; i < (long) image->colors; i++)
4407       {
4408         if ((MagickRealType) image->colormap[i].red > threshold)
4409           image->colormap[i].red=(Quantum) QuantumRange-image->colormap[i].red;
4410         if ((MagickRealType) image->colormap[i].green > threshold)
4411           image->colormap[i].green=(Quantum) QuantumRange-
4412             image->colormap[i].green;
4413         if ((MagickRealType) image->colormap[i].blue > threshold)
4414           image->colormap[i].blue=(Quantum) QuantumRange-
4415             image->colormap[i].blue;
4416       }
4417     }
4418   /*
4419     Solarize image.
4420   */
4421   status=MagickTrue;
4422   progress=0;
4423   exception=(&image->exception);
4424   image_view=AcquireCacheView(image);
4425 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4426   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4427 #endif
4428   for (y=0; y < (long) image->rows; y++)
4429   {
4430     register long
4431       x;
4432
4433     register PixelPacket
4434       *restrict q;
4435
4436     if (status == MagickFalse)
4437       continue;
4438     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
4439       exception);
4440     if (q == (PixelPacket *) NULL)
4441       {
4442         status=MagickFalse;
4443         continue;
4444       }
4445     for (x=0; x < (long) image->columns; x++)
4446     {
4447       if ((MagickRealType) q->red > threshold)
4448         q->red=(Quantum) QuantumRange-q->red;
4449       if ((MagickRealType) q->green > threshold)
4450         q->green=(Quantum) QuantumRange-q->green;
4451       if ((MagickRealType) q->blue > threshold)
4452         q->blue=(Quantum) QuantumRange-q->blue;
4453       q++;
4454     }
4455     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4456       status=MagickFalse;
4457     if (image->progress_monitor != (MagickProgressMonitor) NULL)
4458       {
4459         MagickBooleanType
4460           proceed;
4461
4462 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4463   #pragma omp critical (MagickCore_SolarizeImage)
4464 #endif
4465         proceed=SetImageProgress(image,SolarizeImageTag,progress++,image->rows);
4466         if (proceed == MagickFalse)
4467           status=MagickFalse;
4468       }
4469   }
4470   image_view=DestroyCacheView(image_view);
4471   return(status);
4472 }
4473 \f
4474 /*
4475 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4476 %                                                                             %
4477 %                                                                             %
4478 %                                                                             %
4479 %   S t e g a n o I m a g e                                                   %
4480 %                                                                             %
4481 %                                                                             %
4482 %                                                                             %
4483 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4484 %
4485 %  SteganoImage() hides a digital watermark within the image.  Recover
4486 %  the hidden watermark later to prove that the authenticity of an image.
4487 %  Offset defines the start position within the image to hide the watermark.
4488 %
4489 %  The format of the SteganoImage method is:
4490 %
4491 %      Image *SteganoImage(const Image *image,Image *watermark,
4492 %        ExceptionInfo *exception)
4493 %
4494 %  A description of each parameter follows:
4495 %
4496 %    o image: the image.
4497 %
4498 %    o watermark: the watermark image.
4499 %
4500 %    o exception: return any errors or warnings in this structure.
4501 %
4502 */
4503 MagickExport Image *SteganoImage(const Image *image,const Image *watermark,
4504   ExceptionInfo *exception)
4505 {
4506 #define GetBit(alpha,i) ((((unsigned long) (alpha) >> (unsigned long) \
4507   (i)) & 0x01) != 0)
4508 #define SetBit(alpha,i,set) (alpha)=(Quantum) ((set) ? (unsigned long) (alpha) \
4509   | (1UL << (unsigned long) (i)) : (unsigned long) (alpha) & \
4510   ~(1UL << (unsigned long) (i)))
4511 #define SteganoImageTag  "Stegano/Image"
4512
4513   Image
4514     *stegano_image;
4515
4516   int
4517     c;
4518
4519   long
4520     i,
4521     j,
4522     k,
4523     y;
4524
4525   MagickBooleanType
4526     status;
4527
4528   PixelPacket
4529     pixel;
4530
4531   register long
4532     x;
4533
4534   register PixelPacket
4535     *q;
4536
4537   unsigned long
4538     depth;
4539
4540   /*
4541     Initialize steganographic image attributes.
4542   */
4543   assert(image != (const Image *) NULL);
4544   assert(image->signature == MagickSignature);
4545   if (image->debug != MagickFalse)
4546     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4547   assert(watermark != (const Image *) NULL);
4548   assert(watermark->signature == MagickSignature);
4549   assert(exception != (ExceptionInfo *) NULL);
4550   assert(exception->signature == MagickSignature);
4551   stegano_image=CloneImage(image,0,0,MagickTrue,exception);
4552   if (stegano_image == (Image *) NULL)
4553     return((Image *) NULL);
4554   if (SetImageStorageClass(stegano_image,DirectClass) == MagickFalse)
4555     {
4556       InheritException(exception,&stegano_image->exception);
4557       stegano_image=DestroyImage(stegano_image);
4558       return((Image *) NULL);
4559     }
4560   stegano_image->depth=MAGICKCORE_QUANTUM_DEPTH;
4561   /*
4562     Hide watermark in low-order bits of image.
4563   */
4564   c=0;
4565   i=0;
4566   j=0;
4567   depth=stegano_image->depth;
4568   k=image->offset;
4569   for (i=(long) depth-1; (i >= 0) && (j < (long) depth); i--)
4570   {
4571     for (y=0; (y < (long) watermark->rows) && (j < (long) depth); y++)
4572     {
4573       for (x=0; (x < (long) watermark->columns) && (j < (long) depth); x++)
4574       {
4575         (void) GetOneVirtualPixel(watermark,x,y,&pixel,exception);
4576         if ((k/(long) stegano_image->columns) >= (long) stegano_image->rows)
4577           break;
4578         q=GetAuthenticPixels(stegano_image,k % (long) stegano_image->columns,
4579           k/(long) stegano_image->columns,1,1,exception);
4580         if (q == (PixelPacket *) NULL)
4581           break;
4582         switch (c)
4583         {
4584           case 0:
4585           {
4586             SetBit(q->red,j,GetBit(PixelIntensityToQuantum(&pixel),i));
4587             break;
4588           }
4589           case 1:
4590           {
4591             SetBit(q->green,j,GetBit(PixelIntensityToQuantum(&pixel),i));
4592             break;
4593           }
4594           case 2:
4595           {
4596             SetBit(q->blue,j,GetBit(PixelIntensityToQuantum(&pixel),i));
4597             break;
4598           }
4599         }
4600         if (SyncAuthenticPixels(stegano_image,exception) == MagickFalse)
4601           break;
4602         c++;
4603         if (c == 3)
4604           c=0;
4605         k++;
4606         if (k == (long) (stegano_image->columns*stegano_image->columns))
4607           k=0;
4608         if (k == image->offset)
4609           j++;
4610       }
4611     }
4612     if (image->progress_monitor != (MagickProgressMonitor) NULL)
4613       {
4614         MagickBooleanType
4615           proceed;
4616
4617         proceed=SetImageProgress(image,SteganoImageTag,(MagickOffsetType)
4618           (depth-i),depth);
4619         if (proceed == MagickFalse)
4620           status=MagickFalse;
4621       }
4622   }
4623   if (stegano_image->storage_class == PseudoClass)
4624     (void) SyncImage(stegano_image);
4625   return(stegano_image);
4626 }
4627 \f
4628 /*
4629 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4630 %                                                                             %
4631 %                                                                             %
4632 %                                                                             %
4633 %   S t e r e o A n a g l y p h I m a g e                                     %
4634 %                                                                             %
4635 %                                                                             %
4636 %                                                                             %
4637 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4638 %
4639 %  StereoAnaglyphImage() combines two images and produces a single image that
4640 %  is the composite of a left and right image of a stereo pair.  Special
4641 %  red-green stereo glasses are required to view this effect.
4642 %
4643 %  The format of the StereoAnaglyphImage method is:
4644 %
4645 %      Image *StereoImage(const Image *left_image,const Image *right_image,
4646 %        ExceptionInfo *exception)
4647 %      Image *StereoAnaglyphImage(const Image *left_image,
4648 %        const Image *right_image,const long x_offset,const long y_offset,
4649 %        ExceptionInfo *exception)
4650 %
4651 %  A description of each parameter follows:
4652 %
4653 %    o left_image: the left image.
4654 %
4655 %    o right_image: the right image.
4656 %
4657 %    o exception: return any errors or warnings in this structure.
4658 %
4659 %    o x_offset: amount, in pixels, by which the left image is offset to the
4660 %      right of the right image.
4661 %
4662 %    o y_offset: amount, in pixels, by which the left image is offset to the
4663 %      bottom of the right image.
4664 %
4665 %
4666 */
4667 MagickExport Image *StereoImage(const Image *left_image,
4668   const Image *right_image,ExceptionInfo *exception)
4669 {
4670   return(StereoAnaglyphImage(left_image,right_image,0,0,exception));
4671 }
4672
4673 MagickExport Image *StereoAnaglyphImage(const Image *left_image,
4674   const Image *right_image,const long x_offset,const long y_offset,
4675   ExceptionInfo *exception)
4676 {
4677 #define StereoImageTag  "Stereo/Image"
4678
4679   const Image
4680     *image;
4681
4682   Image
4683     *stereo_image;
4684
4685   long
4686     y;
4687
4688   MagickBooleanType
4689     status;
4690
4691   assert(left_image != (const Image *) NULL);
4692   assert(left_image->signature == MagickSignature);
4693   if (left_image->debug != MagickFalse)
4694     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4695       left_image->filename);
4696   assert(right_image != (const Image *) NULL);
4697   assert(right_image->signature == MagickSignature);
4698   assert(exception != (ExceptionInfo *) NULL);
4699   assert(exception->signature == MagickSignature);
4700   assert(right_image != (const Image *) NULL);
4701   image=left_image;
4702   if ((left_image->columns != right_image->columns) ||
4703       (left_image->rows != right_image->rows))
4704     ThrowImageException(ImageError,"LeftAndRightImageSizesDiffer");
4705   /*
4706     Initialize stereo image attributes.
4707   */
4708   stereo_image=CloneImage(left_image,left_image->columns,left_image->rows,
4709     MagickTrue,exception);
4710   if (stereo_image == (Image *) NULL)
4711     return((Image *) NULL);
4712   if (SetImageStorageClass(stereo_image,DirectClass) == MagickFalse)
4713     {
4714       InheritException(exception,&stereo_image->exception);
4715       stereo_image=DestroyImage(stereo_image);
4716       return((Image *) NULL);
4717     }
4718   /*
4719     Copy left image to red channel and right image to blue channel.
4720   */
4721   for (y=0; y < (long) stereo_image->rows; y++)
4722   {
4723     register const PixelPacket
4724       *restrict p,
4725       *restrict q;
4726
4727     register long
4728       x;
4729
4730     register PixelPacket
4731       *restrict r;
4732
4733     p=GetVirtualPixels(left_image,-x_offset,y-y_offset,image->columns,1,
4734       exception);
4735     q=GetVirtualPixels(right_image,0,y,right_image->columns,1,exception);
4736     r=QueueAuthenticPixels(stereo_image,0,y,stereo_image->columns,1,exception);
4737     if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL) ||
4738         (r == (PixelPacket *) NULL))
4739       break;
4740     for (x=0; x < (long) stereo_image->columns; x++)
4741     {
4742       r->red=GetRedPixelComponent(p);
4743       r->green=q->green;
4744       r->blue=q->blue;
4745       r->opacity=(Quantum) ((p->opacity+q->opacity)/2);
4746       p++;
4747       q++;
4748       r++;
4749     }
4750     if (SyncAuthenticPixels(stereo_image,exception) == MagickFalse)
4751       break;
4752     if (image->progress_monitor != (MagickProgressMonitor) NULL)
4753       {
4754         MagickBooleanType
4755           proceed;
4756
4757         proceed=SetImageProgress(image,StereoImageTag,y,stereo_image->rows);
4758         if (proceed == MagickFalse)
4759           status=MagickFalse;
4760       }
4761   }
4762   return(stereo_image);
4763 }
4764 \f
4765 /*
4766 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4767 %                                                                             %
4768 %                                                                             %
4769 %                                                                             %
4770 %     S w i r l I m a g e                                                     %
4771 %                                                                             %
4772 %                                                                             %
4773 %                                                                             %
4774 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4775 %
4776 %  SwirlImage() swirls the pixels about the center of the image, where
4777 %  degrees indicates the sweep of the arc through which each pixel is moved.
4778 %  You get a more dramatic effect as the degrees move from 1 to 360.
4779 %
4780 %  The format of the SwirlImage method is:
4781 %
4782 %      Image *SwirlImage(const Image *image,double degrees,
4783 %        ExceptionInfo *exception)
4784 %
4785 %  A description of each parameter follows:
4786 %
4787 %    o image: the image.
4788 %
4789 %    o degrees: Define the tightness of the swirling effect.
4790 %
4791 %    o exception: return any errors or warnings in this structure.
4792 %
4793 */
4794 MagickExport Image *SwirlImage(const Image *image,double degrees,
4795   ExceptionInfo *exception)
4796 {
4797 #define SwirlImageTag  "Swirl/Image"
4798
4799   CacheView
4800     *image_view,
4801     *swirl_view;
4802
4803   Image
4804     *swirl_image;
4805
4806   long
4807     progress,
4808     y;
4809
4810   MagickBooleanType
4811     status;
4812
4813   MagickPixelPacket
4814     zero;
4815
4816   MagickRealType
4817     radius;
4818
4819   PointInfo
4820     center,
4821     scale;
4822
4823   ResampleFilter
4824     **restrict resample_filter;
4825
4826   /*
4827     Initialize swirl image attributes.
4828   */
4829   assert(image != (const Image *) NULL);
4830   assert(image->signature == MagickSignature);
4831   if (image->debug != MagickFalse)
4832     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4833   assert(exception != (ExceptionInfo *) NULL);
4834   assert(exception->signature == MagickSignature);
4835   swirl_image=CloneImage(image,0,0,MagickTrue,exception);
4836   if (swirl_image == (Image *) NULL)
4837     return((Image *) NULL);
4838   if (SetImageStorageClass(swirl_image,DirectClass) == MagickFalse)
4839     {
4840       InheritException(exception,&swirl_image->exception);
4841       swirl_image=DestroyImage(swirl_image);
4842       return((Image *) NULL);
4843     }
4844   if (swirl_image->background_color.opacity != OpaqueOpacity)
4845     swirl_image->matte=MagickTrue;
4846   /*
4847     Compute scaling factor.
4848   */
4849   center.x=(double) image->columns/2.0;
4850   center.y=(double) image->rows/2.0;
4851   radius=MagickMax(center.x,center.y);
4852   scale.x=1.0;
4853   scale.y=1.0;
4854   if (image->columns > image->rows)
4855     scale.y=(double) image->columns/(double) image->rows;
4856   else
4857     if (image->columns < image->rows)
4858       scale.x=(double) image->rows/(double) image->columns;
4859   degrees=(double) DegreesToRadians(degrees);
4860   /*
4861     Swirl image.
4862   */
4863   status=MagickTrue;
4864   progress=0;
4865   GetMagickPixelPacket(swirl_image,&zero);
4866   resample_filter=AcquireResampleFilterThreadSet(image,
4867     UndefinedVirtualPixelMethod,MagickTrue,exception);
4868   image_view=AcquireCacheView(image);
4869   swirl_view=AcquireCacheView(swirl_image);
4870 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4871   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4872 #endif
4873   for (y=0; y < (long) image->rows; y++)
4874   {
4875     MagickPixelPacket
4876       pixel;
4877
4878     MagickRealType
4879       distance;
4880
4881     PointInfo
4882       delta;
4883
4884     register IndexPacket
4885       *restrict swirl_indexes;
4886
4887     register long
4888       id,
4889       x;
4890
4891     register PixelPacket
4892       *restrict q;
4893
4894     if (status == MagickFalse)
4895       continue;
4896     q=GetCacheViewAuthenticPixels(swirl_view,0,y,swirl_image->columns,1,
4897       exception);
4898     if (q == (PixelPacket *) NULL)
4899       {
4900         status=MagickFalse;
4901         continue;
4902       }
4903     swirl_indexes=GetCacheViewAuthenticIndexQueue(swirl_view);
4904     delta.y=scale.y*(double) (y-center.y);
4905     pixel=zero;
4906     id=GetOpenMPThreadId();
4907     for (x=0; x < (long) image->columns; x++)
4908     {
4909       /*
4910         Determine if the pixel is within an ellipse.
4911       */
4912       delta.x=scale.x*(double) (x-center.x);
4913       distance=delta.x*delta.x+delta.y*delta.y;
4914       if (distance < (radius*radius))
4915         {
4916           MagickRealType
4917             cosine,
4918             factor,
4919             sine;
4920
4921           /*
4922             Swirl the pixel.
4923           */
4924           factor=1.0-sqrt((double) distance)/radius;
4925           sine=sin((double) (degrees*factor*factor));
4926           cosine=cos((double) (degrees*factor*factor));
4927           (void) ResamplePixelColor(resample_filter[id],(double) ((cosine*
4928             delta.x-sine*delta.y)/scale.x+center.x),(double) ((sine*delta.x+
4929             cosine*delta.y)/scale.y+center.y),&pixel);
4930           SetPixelPacket(swirl_image,&pixel,q,swirl_indexes+x);
4931         }
4932       q++;
4933     }
4934     if (SyncCacheViewAuthenticPixels(swirl_view,exception) == MagickFalse)
4935       status=MagickFalse;
4936     if (image->progress_monitor != (MagickProgressMonitor) NULL)
4937       {
4938         MagickBooleanType
4939           proceed;
4940
4941 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4942   #pragma omp critical (MagickCore_SwirlImage)
4943 #endif
4944         proceed=SetImageProgress(image,SwirlImageTag,progress++,image->rows);
4945         if (proceed == MagickFalse)
4946           status=MagickFalse;
4947       }
4948   }
4949   swirl_view=DestroyCacheView(swirl_view);
4950   image_view=DestroyCacheView(image_view);
4951   resample_filter=DestroyResampleFilterThreadSet(resample_filter);
4952   if (status == MagickFalse)
4953     swirl_image=DestroyImage(swirl_image);
4954   return(swirl_image);
4955 }
4956 \f
4957 /*
4958 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4959 %                                                                             %
4960 %                                                                             %
4961 %                                                                             %
4962 %     T i n t I m a g e                                                       %
4963 %                                                                             %
4964 %                                                                             %
4965 %                                                                             %
4966 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4967 %
4968 %  TintImage() applies a color vector to each pixel in the image.  The length
4969 %  of the vector is 0 for black and white and at its maximum for the midtones.
4970 %  The vector weighting function is f(x)=(1-(4.0*((x-0.5)*(x-0.5))))
4971 %
4972 %  The format of the TintImage method is:
4973 %
4974 %      Image *TintImage(const Image *image,const char *opacity,
4975 %        const PixelPacket tint,ExceptionInfo *exception)
4976 %
4977 %  A description of each parameter follows:
4978 %
4979 %    o image: the image.
4980 %
4981 %    o opacity: A color value used for tinting.
4982 %
4983 %    o tint: A color value used for tinting.
4984 %
4985 %    o exception: return any errors or warnings in this structure.
4986 %
4987 */
4988 MagickExport Image *TintImage(const Image *image,const char *opacity,
4989   const PixelPacket tint,ExceptionInfo *exception)
4990 {
4991 #define TintImageTag  "Tint/Image"
4992
4993   CacheView
4994     *image_view,
4995     *tint_view;
4996
4997   GeometryInfo
4998     geometry_info;
4999
5000   Image
5001     *tint_image;
5002
5003   long
5004     progress,
5005     y;
5006
5007   MagickBooleanType
5008     status;
5009
5010   MagickStatusType
5011     flags;
5012
5013   MagickPixelPacket
5014     color_vector,
5015     pixel;
5016
5017   /*
5018     Allocate tint image.
5019   */
5020   assert(image != (const Image *) NULL);
5021   assert(image->signature == MagickSignature);
5022   if (image->debug != MagickFalse)
5023     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5024   assert(exception != (ExceptionInfo *) NULL);
5025   assert(exception->signature == MagickSignature);
5026   tint_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
5027   if (tint_image == (Image *) NULL)
5028     return((Image *) NULL);
5029   if (SetImageStorageClass(tint_image,DirectClass) == MagickFalse)
5030     {
5031       InheritException(exception,&tint_image->exception);
5032       tint_image=DestroyImage(tint_image);
5033       return((Image *) NULL);
5034     }
5035   if (opacity == (const char *) NULL)
5036     return(tint_image);
5037   /*
5038     Determine RGB values of the color.
5039   */
5040   flags=ParseGeometry(opacity,&geometry_info);
5041   pixel.red=geometry_info.rho;
5042   if ((flags & SigmaValue) != 0)
5043     pixel.green=geometry_info.sigma;
5044   else
5045     pixel.green=pixel.red;
5046   if ((flags & XiValue) != 0)
5047     pixel.blue=geometry_info.xi;
5048   else
5049     pixel.blue=pixel.red;
5050   if ((flags & PsiValue) != 0)
5051     pixel.opacity=geometry_info.psi;
5052   else
5053     pixel.opacity=(MagickRealType) OpaqueOpacity;
5054   color_vector.red=(MagickRealType) (pixel.red*tint.red/100.0-
5055     PixelIntensity(&tint));
5056   color_vector.green=(MagickRealType) (pixel.green*tint.green/100.0-
5057     PixelIntensity(&tint));
5058   color_vector.blue=(MagickRealType) (pixel.blue*tint.blue/100.0-
5059     PixelIntensity(&tint));
5060   /*
5061     Tint image.
5062   */
5063   status=MagickTrue;
5064   progress=0;
5065   image_view=AcquireCacheView(image);
5066   tint_view=AcquireCacheView(tint_image);
5067 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5068   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
5069 #endif
5070   for (y=0; y < (long) image->rows; y++)
5071   {
5072     register const PixelPacket
5073       *restrict p;
5074
5075     register long
5076       x;
5077
5078     register PixelPacket
5079       *restrict q;
5080
5081     if (status == MagickFalse)
5082       continue;
5083     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
5084     q=QueueCacheViewAuthenticPixels(tint_view,0,y,tint_image->columns,1,
5085       exception);
5086     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
5087       {
5088         status=MagickFalse;
5089         continue;
5090       }
5091     for (x=0; x < (long) image->columns; x++)
5092     {
5093       MagickPixelPacket
5094         pixel;
5095
5096       MagickRealType
5097         weight;
5098
5099       weight=QuantumScale*p->red-0.5;
5100       pixel.red=(MagickRealType) p->red+color_vector.red*(1.0-(4.0*
5101         (weight*weight)));
5102       SetRedPixelComponent(q,ClampRedPixelComponent(&pixel));
5103       weight=QuantumScale*p->green-0.5;
5104       pixel.green=(MagickRealType) p->green+color_vector.green*(1.0-(4.0*
5105         (weight*weight)));
5106       SetGreenPixelComponent(q,ClampGreenPixelComponent(&pixel));
5107       weight=QuantumScale*p->blue-0.5;
5108       pixel.blue=(MagickRealType) p->blue+color_vector.blue*(1.0-(4.0*
5109         (weight*weight)));
5110       SetBluePixelComponent(q,ClampBluePixelComponent(&pixel));
5111       SetOpacityPixelComponent(q,GetOpacityPixelComponent(p));
5112       p++;
5113       q++;
5114     }
5115     if (SyncCacheViewAuthenticPixels(tint_view,exception) == MagickFalse)
5116       status=MagickFalse;
5117     if (image->progress_monitor != (MagickProgressMonitor) NULL)
5118       {
5119         MagickBooleanType
5120           proceed;
5121
5122 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5123   #pragma omp critical (MagickCore_TintImage)
5124 #endif
5125         proceed=SetImageProgress(image,TintImageTag,progress++,image->rows);
5126         if (proceed == MagickFalse)
5127           status=MagickFalse;
5128       }
5129   }
5130   tint_view=DestroyCacheView(tint_view);
5131   image_view=DestroyCacheView(image_view);
5132   if (status == MagickFalse)
5133     tint_image=DestroyImage(tint_image);
5134   return(tint_image);
5135 }
5136 \f
5137 /*
5138 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5139 %                                                                             %
5140 %                                                                             %
5141 %                                                                             %
5142 %     V i g n e t t e I m a g e                                               %
5143 %                                                                             %
5144 %                                                                             %
5145 %                                                                             %
5146 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5147 %
5148 %  VignetteImage() softens the edges of the image in vignette style.
5149 %
5150 %  The format of the VignetteImage method is:
5151 %
5152 %      Image *VignetteImage(const Image *image,const double radius,
5153 %        const double sigma,const long x,const long y,ExceptionInfo *exception)
5154 %
5155 %  A description of each parameter follows:
5156 %
5157 %    o image: the image.
5158 %
5159 %    o radius: the radius of the pixel neighborhood.
5160 %
5161 %    o sigma: the standard deviation of the Gaussian, in pixels.
5162 %
5163 %    o x, y:  Define the x and y ellipse offset.
5164 %
5165 %    o exception: return any errors or warnings in this structure.
5166 %
5167 */
5168 MagickExport Image *VignetteImage(const Image *image,const double radius,
5169   const double sigma,const long x,const long y,ExceptionInfo *exception)
5170 {
5171   char
5172     ellipse[MaxTextExtent];
5173
5174   DrawInfo
5175     *draw_info;
5176
5177   Image
5178     *canvas_image,
5179     *blur_image,
5180     *oval_image,
5181     *vignette_image;
5182
5183   assert(image != (Image *) NULL);
5184   assert(image->signature == MagickSignature);
5185   if (image->debug != MagickFalse)
5186     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5187   assert(exception != (ExceptionInfo *) NULL);
5188   assert(exception->signature == MagickSignature);
5189   canvas_image=CloneImage(image,0,0,MagickTrue,exception);
5190   if (canvas_image == (Image *) NULL)
5191     return((Image *) NULL);
5192   if (SetImageStorageClass(canvas_image,DirectClass) == MagickFalse)
5193     {
5194       InheritException(exception,&canvas_image->exception);
5195       canvas_image=DestroyImage(canvas_image);
5196       return((Image *) NULL);
5197     }
5198   canvas_image->matte=MagickTrue;
5199   oval_image=CloneImage(canvas_image,canvas_image->columns,
5200     canvas_image->rows,MagickTrue,exception);
5201   if (oval_image == (Image *) NULL)
5202     {
5203       canvas_image=DestroyImage(canvas_image);
5204       return((Image *) NULL);
5205     }
5206   (void) QueryColorDatabase("#000000",&oval_image->background_color,exception);
5207   (void) SetImageBackgroundColor(oval_image);
5208   draw_info=CloneDrawInfo((const ImageInfo *) NULL,(const DrawInfo *) NULL);
5209   (void) QueryColorDatabase("#ffffff",&draw_info->fill,exception);
5210   (void) QueryColorDatabase("#ffffff",&draw_info->stroke,exception);
5211   (void) FormatMagickString(ellipse,MaxTextExtent,
5212     "ellipse %g,%g,%g,%g,0.0,360.0",image->columns/2.0,
5213     image->rows/2.0,image->columns/2.0-x,image->rows/2.0-y);
5214   draw_info->primitive=AcquireString(ellipse);
5215   (void) DrawImage(oval_image,draw_info);
5216   draw_info=DestroyDrawInfo(draw_info);
5217   blur_image=BlurImage(oval_image,radius,sigma,exception);
5218   oval_image=DestroyImage(oval_image);
5219   if (blur_image == (Image *) NULL)
5220     {
5221       canvas_image=DestroyImage(canvas_image);
5222       return((Image *) NULL);
5223     }
5224   blur_image->matte=MagickFalse;
5225   (void) CompositeImage(canvas_image,CopyOpacityCompositeOp,blur_image,0,0);
5226   blur_image=DestroyImage(blur_image);
5227   vignette_image=MergeImageLayers(canvas_image,FlattenLayer,exception);
5228   canvas_image=DestroyImage(canvas_image);
5229   return(vignette_image);
5230 }
5231 \f
5232 /*
5233 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5234 %                                                                             %
5235 %                                                                             %
5236 %                                                                             %
5237 %     W a v e I m a g e                                                       %
5238 %                                                                             %
5239 %                                                                             %
5240 %                                                                             %
5241 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5242 %
5243 %  WaveImage() creates a "ripple" effect in the image by shifting the pixels
5244 %  vertically along a sine wave whose amplitude and wavelength is specified
5245 %  by the given parameters.
5246 %
5247 %  The format of the WaveImage method is:
5248 %
5249 %      Image *WaveImage(const Image *image,const double amplitude,
5250 %        const double wave_length,ExceptionInfo *exception)
5251 %
5252 %  A description of each parameter follows:
5253 %
5254 %    o image: the image.
5255 %
5256 %    o amplitude, wave_length:  Define the amplitude and wave length of the
5257 %      sine wave.
5258 %
5259 %    o exception: return any errors or warnings in this structure.
5260 %
5261 */
5262 MagickExport Image *WaveImage(const Image *image,const double amplitude,
5263   const double wave_length,ExceptionInfo *exception)
5264 {
5265 #define WaveImageTag  "Wave/Image"
5266
5267   CacheView
5268     *wave_view;
5269
5270   Image
5271     *wave_image;
5272
5273   long
5274     progress,
5275     y;
5276
5277   MagickBooleanType
5278     status;
5279
5280   MagickPixelPacket
5281     zero;
5282
5283   MagickRealType
5284     *sine_map;
5285
5286   register long
5287     i;
5288
5289   ResampleFilter
5290     **restrict resample_filter;
5291
5292   /*
5293     Initialize wave image attributes.
5294   */
5295   assert(image != (Image *) NULL);
5296   assert(image->signature == MagickSignature);
5297   if (image->debug != MagickFalse)
5298     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5299   assert(exception != (ExceptionInfo *) NULL);
5300   assert(exception->signature == MagickSignature);
5301   wave_image=CloneImage(image,image->columns,(unsigned long) (image->rows+2.0*
5302     fabs(amplitude)),MagickTrue,exception);
5303   if (wave_image == (Image *) NULL)
5304     return((Image *) NULL);
5305   if (SetImageStorageClass(wave_image,DirectClass) == MagickFalse)
5306     {
5307       InheritException(exception,&wave_image->exception);
5308       wave_image=DestroyImage(wave_image);
5309       return((Image *) NULL);
5310     }
5311   if (wave_image->background_color.opacity != OpaqueOpacity)
5312     wave_image->matte=MagickTrue;
5313   /*
5314     Allocate sine map.
5315   */
5316   sine_map=(MagickRealType *) AcquireQuantumMemory((size_t) wave_image->columns,
5317     sizeof(*sine_map));
5318   if (sine_map == (MagickRealType *) NULL)
5319     {
5320       wave_image=DestroyImage(wave_image);
5321       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
5322     }
5323   for (i=0; i < (long) wave_image->columns; i++)
5324     sine_map[i]=fabs(amplitude)+amplitude*sin((2*MagickPI*i)/wave_length);
5325   /*
5326     Wave image.
5327   */
5328   status=MagickTrue;
5329   progress=0;
5330   GetMagickPixelPacket(wave_image,&zero);
5331   resample_filter=AcquireResampleFilterThreadSet(image,
5332     BackgroundVirtualPixelMethod,MagickTrue,exception);
5333   wave_view=AcquireCacheView(wave_image);
5334 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5335   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
5336 #endif
5337   for (y=0; y < (long) wave_image->rows; y++)
5338   {
5339     MagickPixelPacket
5340       pixel;
5341
5342     register IndexPacket
5343       *restrict indexes;
5344
5345     register long
5346       id,
5347       x;
5348
5349     register PixelPacket
5350       *restrict q;
5351
5352     if (status == MagickFalse)
5353       continue;
5354     q=QueueCacheViewAuthenticPixels(wave_view,0,y,wave_image->columns,1,
5355       exception);
5356     if (q == (PixelPacket *) NULL)
5357       {
5358         status=MagickFalse;
5359         continue;
5360       }
5361     indexes=GetCacheViewAuthenticIndexQueue(wave_view);
5362     pixel=zero;
5363     id=GetOpenMPThreadId();
5364     for (x=0; x < (long) wave_image->columns; x++)
5365     {
5366       (void) ResamplePixelColor(resample_filter[id],(double) x,(double) (y-
5367         sine_map[x]),&pixel);
5368       SetPixelPacket(wave_image,&pixel,q,indexes+x);
5369       q++;
5370     }
5371     if (SyncCacheViewAuthenticPixels(wave_view,exception) == MagickFalse)
5372       status=MagickFalse;
5373     if (image->progress_monitor != (MagickProgressMonitor) NULL)
5374       {
5375         MagickBooleanType
5376           proceed;
5377
5378 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5379   #pragma omp critical (MagickCore_WaveImage)
5380 #endif
5381         proceed=SetImageProgress(image,WaveImageTag,progress++,image->rows);
5382         if (proceed == MagickFalse)
5383           status=MagickFalse;
5384       }
5385   }
5386   wave_view=DestroyCacheView(wave_view);
5387   resample_filter=DestroyResampleFilterThreadSet(resample_filter);
5388   sine_map=(MagickRealType *) RelinquishMagickMemory(sine_map);
5389   if (status == MagickFalse)
5390     wave_image=DestroyImage(wave_image);
5391   return(wave_image);
5392 }