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