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