]> 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 %        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 exception: return any errors or warnings in this structure.
3112 %
3113 */
3114 MagickExport Image *ImplodeImage(const Image *image,const double amount,
3115   ExceptionInfo *exception)
3116 {
3117 #define ImplodeImageTag  "Implode/Image"
3118
3119   CacheView
3120     *image_view,
3121     *implode_view;
3122
3123   Image
3124     *implode_image;
3125
3126   MagickBooleanType
3127     status;
3128
3129   MagickOffsetType
3130     progress;
3131
3132   PixelInfo
3133     zero;
3134
3135   MagickRealType
3136     radius;
3137
3138   PointInfo
3139     center,
3140     scale;
3141
3142   ssize_t
3143     y;
3144
3145   /*
3146     Initialize implode image attributes.
3147   */
3148   assert(image != (Image *) NULL);
3149   assert(image->signature == MagickSignature);
3150   if (image->debug != MagickFalse)
3151     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3152   assert(exception != (ExceptionInfo *) NULL);
3153   assert(exception->signature == MagickSignature);
3154   implode_image=CloneImage(image,0,0,MagickTrue,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   GetPixelInfo(implode_image,&zero);
3186   image_view=AcquireCacheView(image);
3187   implode_view=AcquireCacheView(implode_image);
3188 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3189   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
3190 #endif
3191   for (y=0; y < (ssize_t) image->rows; y++)
3192   {
3193     PixelInfo
3194       pixel;
3195
3196     MagickRealType
3197       distance;
3198
3199     PointInfo
3200       delta;
3201
3202     register ssize_t
3203       x;
3204
3205     register Quantum
3206       *restrict q;
3207
3208     if (status == MagickFalse)
3209       continue;
3210     q=GetCacheViewAuthenticPixels(implode_view,0,y,implode_image->columns,1,
3211       exception);
3212     if (q == (Quantum *) NULL)
3213       {
3214         status=MagickFalse;
3215         continue;
3216       }
3217     delta.y=scale.y*(double) (y-center.y);
3218     pixel=zero;
3219     for (x=0; x < (ssize_t) image->columns; x++)
3220     {
3221       /*
3222         Determine if the pixel is within an ellipse.
3223       */
3224       delta.x=scale.x*(double) (x-center.x);
3225       distance=delta.x*delta.x+delta.y*delta.y;
3226       if (distance < (radius*radius))
3227         {
3228           double
3229             factor;
3230
3231           /*
3232             Implode the pixel.
3233           */
3234           factor=1.0;
3235           if (distance > 0.0)
3236             factor=pow(sin((double) (MagickPI*sqrt((double) distance)/
3237               radius/2)),-amount);
3238           (void) InterpolatePixelInfo(image,image_view,
3239             UndefinedInterpolatePixel,(double) (factor*delta.x/scale.x+
3240             center.x),(double) (factor*delta.y/scale.y+center.y),&pixel,
3241             exception);
3242           SetPixelPixelInfo(implode_image,&pixel,q);
3243         }
3244       q+=GetPixelChannels(implode_image);
3245     }
3246     if (SyncCacheViewAuthenticPixels(implode_view,exception) == MagickFalse)
3247       status=MagickFalse;
3248     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3249       {
3250         MagickBooleanType
3251           proceed;
3252
3253 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3254   #pragma omp critical (MagickCore_ImplodeImage)
3255 #endif
3256         proceed=SetImageProgress(image,ImplodeImageTag,progress++,image->rows);
3257         if (proceed == MagickFalse)
3258           status=MagickFalse;
3259       }
3260   }
3261   implode_view=DestroyCacheView(implode_view);
3262   image_view=DestroyCacheView(image_view);
3263   if (status == MagickFalse)
3264     implode_image=DestroyImage(implode_image);
3265   return(implode_image);
3266 }
3267 \f
3268 /*
3269 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3270 %                                                                             %
3271 %                                                                             %
3272 %                                                                             %
3273 %     M o r p h I m a g e s                                                   %
3274 %                                                                             %
3275 %                                                                             %
3276 %                                                                             %
3277 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3278 %
3279 %  The MorphImages() method requires a minimum of two images.  The first
3280 %  image is transformed into the second by a number of intervening images
3281 %  as specified by frames.
3282 %
3283 %  The format of the MorphImage method is:
3284 %
3285 %      Image *MorphImages(const Image *image,const size_t number_frames,
3286 %        ExceptionInfo *exception)
3287 %
3288 %  A description of each parameter follows:
3289 %
3290 %    o image: the image.
3291 %
3292 %    o number_frames:  Define the number of in-between image to generate.
3293 %      The more in-between frames, the smoother the morph.
3294 %
3295 %    o exception: return any errors or warnings in this structure.
3296 %
3297 */
3298 MagickExport Image *MorphImages(const Image *image,
3299   const size_t number_frames,ExceptionInfo *exception)
3300 {
3301 #define MorphImageTag  "Morph/Image"
3302
3303   Image
3304     *morph_image,
3305     *morph_images;
3306
3307   MagickBooleanType
3308     status;
3309
3310   MagickOffsetType
3311     scene;
3312
3313   MagickRealType
3314     alpha,
3315     beta;
3316
3317   register const Image
3318     *next;
3319
3320   register ssize_t
3321     i;
3322
3323   ssize_t
3324     y;
3325
3326   /*
3327     Clone first frame in sequence.
3328   */
3329   assert(image != (Image *) NULL);
3330   assert(image->signature == MagickSignature);
3331   if (image->debug != MagickFalse)
3332     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3333   assert(exception != (ExceptionInfo *) NULL);
3334   assert(exception->signature == MagickSignature);
3335   morph_images=CloneImage(image,0,0,MagickTrue,exception);
3336   if (morph_images == (Image *) NULL)
3337     return((Image *) NULL);
3338   if (GetNextImageInList(image) == (Image *) NULL)
3339     {
3340       /*
3341         Morph single image.
3342       */
3343       for (i=1; i < (ssize_t) number_frames; i++)
3344       {
3345         morph_image=CloneImage(image,0,0,MagickTrue,exception);
3346         if (morph_image == (Image *) NULL)
3347           {
3348             morph_images=DestroyImageList(morph_images);
3349             return((Image *) NULL);
3350           }
3351         AppendImageToList(&morph_images,morph_image);
3352         if (image->progress_monitor != (MagickProgressMonitor) NULL)
3353           {
3354             MagickBooleanType
3355               proceed;
3356
3357             proceed=SetImageProgress(image,MorphImageTag,(MagickOffsetType) i,
3358               number_frames);
3359             if (proceed == MagickFalse)
3360               status=MagickFalse;
3361           }
3362       }
3363       return(GetFirstImageInList(morph_images));
3364     }
3365   /*
3366     Morph image sequence.
3367   */
3368   status=MagickTrue;
3369   scene=0;
3370   next=image;
3371   for ( ; GetNextImageInList(next) != (Image *) NULL; next=GetNextImageInList(next))
3372   {
3373     for (i=0; i < (ssize_t) number_frames; i++)
3374     {
3375       CacheView
3376         *image_view,
3377         *morph_view;
3378
3379       beta=(MagickRealType) (i+1.0)/(MagickRealType) (number_frames+1.0);
3380       alpha=1.0-beta;
3381       morph_image=ResizeImage(next,(size_t) (alpha*next->columns+beta*
3382         GetNextImageInList(next)->columns+0.5),(size_t) (alpha*
3383         next->rows+beta*GetNextImageInList(next)->rows+0.5),
3384         next->filter,next->blur,exception);
3385       if (morph_image == (Image *) NULL)
3386         {
3387           morph_images=DestroyImageList(morph_images);
3388           return((Image *) NULL);
3389         }
3390       if (SetImageStorageClass(morph_image,DirectClass,exception) == MagickFalse)
3391         {
3392           morph_image=DestroyImage(morph_image);
3393           return((Image *) NULL);
3394         }
3395       AppendImageToList(&morph_images,morph_image);
3396       morph_images=GetLastImageInList(morph_images);
3397       morph_image=ResizeImage(GetNextImageInList(next),morph_images->columns,
3398         morph_images->rows,GetNextImageInList(next)->filter,
3399         GetNextImageInList(next)->blur,exception);
3400       if (morph_image == (Image *) NULL)
3401         {
3402           morph_images=DestroyImageList(morph_images);
3403           return((Image *) NULL);
3404         }
3405       image_view=AcquireCacheView(morph_image);
3406       morph_view=AcquireCacheView(morph_images);
3407 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3408   #pragma omp parallel for schedule(dynamic,4) shared(status)
3409 #endif
3410       for (y=0; y < (ssize_t) morph_images->rows; y++)
3411       {
3412         MagickBooleanType
3413           sync;
3414
3415         register const Quantum
3416           *restrict p;
3417
3418         register ssize_t
3419           x;
3420
3421         register Quantum
3422           *restrict q;
3423
3424         if (status == MagickFalse)
3425           continue;
3426         p=GetCacheViewVirtualPixels(image_view,0,y,morph_image->columns,1,
3427           exception);
3428         q=GetCacheViewAuthenticPixels(morph_view,0,y,morph_images->columns,1,
3429           exception);
3430         if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3431           {
3432             status=MagickFalse;
3433             continue;
3434           }
3435         for (x=0; x < (ssize_t) morph_images->columns; x++)
3436         {
3437           SetPixelRed(morph_images,ClampToQuantum(alpha*
3438             GetPixelRed(morph_images,q)+beta*GetPixelRed(morph_image,p)),q);
3439           SetPixelGreen(morph_images,ClampToQuantum(alpha*
3440             GetPixelGreen(morph_images,q)+beta*GetPixelGreen(morph_image,p)),q);
3441           SetPixelBlue(morph_images,ClampToQuantum(alpha*
3442             GetPixelBlue(morph_images,q)+beta*GetPixelBlue(morph_image,p)),q);
3443           SetPixelAlpha(morph_images,ClampToQuantum(alpha*
3444             GetPixelAlpha(morph_images,q)+beta*GetPixelAlpha(morph_image,p)),q);
3445           if ((morph_image->colorspace == CMYKColorspace) &&
3446               (morph_images->colorspace == CMYKColorspace))
3447             SetPixelBlack(morph_images,ClampToQuantum(alpha*
3448               GetPixelBlack(morph_images,q)+beta*GetPixelBlack(morph_image,p)),
3449               q);
3450           p+=GetPixelChannels(morph_image);
3451           q+=GetPixelChannels(morph_images);
3452         }
3453         sync=SyncCacheViewAuthenticPixels(morph_view,exception);
3454         if (sync == MagickFalse)
3455           status=MagickFalse;
3456       }
3457       morph_view=DestroyCacheView(morph_view);
3458       image_view=DestroyCacheView(image_view);
3459       morph_image=DestroyImage(morph_image);
3460     }
3461     if (i < (ssize_t) number_frames)
3462       break;
3463     /*
3464       Clone last frame in sequence.
3465     */
3466     morph_image=CloneImage(GetNextImageInList(next),0,0,MagickTrue,exception);
3467     if (morph_image == (Image *) NULL)
3468       {
3469         morph_images=DestroyImageList(morph_images);
3470         return((Image *) NULL);
3471       }
3472     AppendImageToList(&morph_images,morph_image);
3473     morph_images=GetLastImageInList(morph_images);
3474     if (image->progress_monitor != (MagickProgressMonitor) NULL)
3475       {
3476         MagickBooleanType
3477           proceed;
3478
3479 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3480   #pragma omp critical (MagickCore_MorphImages)
3481 #endif
3482         proceed=SetImageProgress(image,MorphImageTag,scene,
3483           GetImageListLength(image));
3484         if (proceed == MagickFalse)
3485           status=MagickFalse;
3486       }
3487     scene++;
3488   }
3489   if (GetNextImageInList(next) != (Image *) NULL)
3490     {
3491       morph_images=DestroyImageList(morph_images);
3492       return((Image *) NULL);
3493     }
3494   return(GetFirstImageInList(morph_images));
3495 }
3496 \f
3497 /*
3498 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3499 %                                                                             %
3500 %                                                                             %
3501 %                                                                             %
3502 %     P l a s m a I m a g e                                                   %
3503 %                                                                             %
3504 %                                                                             %
3505 %                                                                             %
3506 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3507 %
3508 %  PlasmaImage() initializes an image with plasma fractal values.  The image
3509 %  must be initialized with a base color and the random number generator
3510 %  seeded before this method is called.
3511 %
3512 %  The format of the PlasmaImage method is:
3513 %
3514 %      MagickBooleanType PlasmaImage(Image *image,const SegmentInfo *segment,
3515 %        size_t attenuate,size_t depth,ExceptionInfo *exception)
3516 %
3517 %  A description of each parameter follows:
3518 %
3519 %    o image: the image.
3520 %
3521 %    o segment:   Define the region to apply plasma fractals values.
3522 %
3523 %    o attenuate: Define the plasma attenuation factor.
3524 %
3525 %    o depth: Limit the plasma recursion depth.
3526 %
3527 %    o exception: return any errors or warnings in this structure.
3528 %
3529 */
3530
3531 static inline Quantum PlasmaPixel(RandomInfo *random_info,
3532   const MagickRealType pixel,const MagickRealType noise)
3533 {
3534   Quantum
3535     plasma;
3536
3537   plasma=ClampToQuantum(pixel+noise*GetPseudoRandomValue(random_info)-
3538     noise/2.0);
3539   return(plasma);
3540 }
3541
3542 static MagickBooleanType PlasmaImageProxy(Image *image,
3543   CacheView *image_view,RandomInfo *random_info,const SegmentInfo *segment,
3544   size_t attenuate,size_t depth,ExceptionInfo *exception)
3545 {
3546   MagickRealType
3547     plasma;
3548
3549   PixelPacket
3550     u,
3551     v;
3552
3553   ssize_t
3554     x,
3555     x_mid,
3556     y,
3557     y_mid;
3558
3559   if (((segment->x2-segment->x1) == 0.0) && ((segment->y2-segment->y1) == 0.0))
3560     return(MagickTrue);
3561   if (depth != 0)
3562     {
3563       SegmentInfo
3564         local_info;
3565
3566       /*
3567         Divide the area into quadrants and recurse.
3568       */
3569       depth--;
3570       attenuate++;
3571       x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3572       y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
3573       local_info=(*segment);
3574       local_info.x2=(double) x_mid;
3575       local_info.y2=(double) y_mid;
3576       (void) PlasmaImageProxy(image,image_view,random_info,&local_info,
3577         attenuate,depth,exception);
3578       local_info=(*segment);
3579       local_info.y1=(double) y_mid;
3580       local_info.x2=(double) x_mid;
3581       (void) PlasmaImageProxy(image,image_view,random_info,&local_info,
3582         attenuate,depth,exception);
3583       local_info=(*segment);
3584       local_info.x1=(double) x_mid;
3585       local_info.y2=(double) y_mid;
3586       (void) PlasmaImageProxy(image,image_view,random_info,&local_info,
3587         attenuate,depth,exception);
3588       local_info=(*segment);
3589       local_info.x1=(double) x_mid;
3590       local_info.y1=(double) y_mid;
3591       return(PlasmaImageProxy(image,image_view,random_info,&local_info,
3592         attenuate,depth,exception));
3593     }
3594   x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3595   y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
3596   if ((segment->x1 == (double) x_mid) && (segment->x2 == (double) x_mid) &&
3597       (segment->y1 == (double) y_mid) && (segment->y2 == (double) y_mid))
3598     return(MagickFalse);
3599   /*
3600     Average pixels and apply plasma.
3601   */
3602   plasma=(MagickRealType) QuantumRange/(2.0*attenuate);
3603   if ((segment->x1 != (double) x_mid) || (segment->x2 != (double) x_mid))
3604     {
3605       register Quantum
3606         *restrict q;
3607
3608       /*
3609         Left pixel.
3610       */
3611       x=(ssize_t) ceil(segment->x1-0.5);
3612       (void) GetOneCacheViewVirtualPixel(image_view,x,(ssize_t)
3613         ceil(segment->y1-0.5),&u,exception);
3614       (void) GetOneCacheViewVirtualPixel(image_view,x,(ssize_t)
3615         ceil(segment->y2-0.5),&v,exception);
3616       q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
3617       if (q == (Quantum *) NULL)
3618         return(MagickTrue);
3619       SetPixelRed(image,PlasmaPixel(random_info,(MagickRealType)
3620         (u.red+v.red)/2.0,plasma),q);
3621       SetPixelGreen(image,PlasmaPixel(random_info,(MagickRealType)
3622         (u.green+v.green)/2.0,plasma),q);
3623       SetPixelBlue(image,PlasmaPixel(random_info,(MagickRealType)
3624         (u.blue+v.blue)/2.0,plasma),q);
3625       (void) SyncCacheViewAuthenticPixels(image_view,exception);
3626       if (segment->x1 != segment->x2)
3627         {
3628           /*
3629             Right pixel.
3630           */
3631           x=(ssize_t) ceil(segment->x2-0.5);
3632           (void) GetOneCacheViewVirtualPixel(image_view,x,(ssize_t)
3633             ceil(segment->y1-0.5),&u,exception);
3634           (void) GetOneCacheViewVirtualPixel(image_view,x,(ssize_t)
3635             ceil(segment->y2-0.5),&v,exception);
3636           q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
3637           if (q == (Quantum *) NULL)
3638             return(MagickTrue);
3639           SetPixelRed(image,PlasmaPixel(random_info,(MagickRealType)
3640             (u.red+v.red)/2.0,plasma),q);
3641           SetPixelGreen(image,PlasmaPixel(random_info,(MagickRealType)
3642             (u.green+v.green)/2.0,plasma),q);
3643           SetPixelBlue(image,PlasmaPixel(random_info,(MagickRealType)
3644             (u.blue+v.blue)/2.0,plasma),q);
3645           (void) SyncCacheViewAuthenticPixels(image_view,exception);
3646         }
3647     }
3648   if ((segment->y1 != (double) y_mid) || (segment->y2 != (double) y_mid))
3649     {
3650       if ((segment->x1 != (double) x_mid) || (segment->y2 != (double) y_mid))
3651         {
3652           register Quantum
3653             *restrict q;
3654
3655           /*
3656             Bottom pixel.
3657           */
3658           y=(ssize_t) ceil(segment->y2-0.5);
3659           (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
3660             ceil(segment->x1-0.5),y,&u,exception);
3661           (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
3662             ceil(segment->x2-0.5),y,&v,exception);
3663           q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
3664           if (q == (Quantum *) NULL)
3665             return(MagickTrue);
3666           SetPixelRed(image,PlasmaPixel(random_info,(MagickRealType)
3667             (u.red+v.red)/2.0,plasma),q);
3668           SetPixelGreen(image,PlasmaPixel(random_info,(MagickRealType)
3669             (u.green+v.green)/2.0,plasma),q);
3670           SetPixelBlue(image,PlasmaPixel(random_info,(MagickRealType)
3671             (u.blue+v.blue)/2.0,plasma),q);
3672           (void) SyncCacheViewAuthenticPixels(image_view,exception);
3673         }
3674       if (segment->y1 != segment->y2)
3675         {
3676           register Quantum
3677             *restrict q;
3678
3679           /*
3680             Top pixel.
3681           */
3682           y=(ssize_t) ceil(segment->y1-0.5);
3683           (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
3684             ceil(segment->x1-0.5),y,&u,exception);
3685           (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t)
3686             ceil(segment->x2-0.5),y,&v,exception);
3687           q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
3688           if (q == (Quantum *) NULL)
3689             return(MagickTrue);
3690           SetPixelRed(image,PlasmaPixel(random_info,(MagickRealType)
3691             (u.red+v.red)/2.0,plasma),q);
3692           SetPixelGreen(image,PlasmaPixel(random_info,(MagickRealType)
3693             (u.green+v.green)/2.0,plasma),q);
3694           SetPixelBlue(image,PlasmaPixel(random_info,(MagickRealType)
3695             (u.blue+v.blue)/2.0,plasma),q);
3696           (void) SyncCacheViewAuthenticPixels(image_view,exception);
3697         }
3698     }
3699   if ((segment->x1 != segment->x2) || (segment->y1 != segment->y2))
3700     {
3701       register Quantum
3702         *restrict q;
3703
3704       /*
3705         Middle pixel.
3706       */
3707       x=(ssize_t) ceil(segment->x1-0.5);
3708       y=(ssize_t) ceil(segment->y1-0.5);
3709       (void) GetOneVirtualPixel(image,x,y,&u,exception);
3710       x=(ssize_t) ceil(segment->x2-0.5);
3711       y=(ssize_t) ceil(segment->y2-0.5);
3712       (void) GetOneCacheViewVirtualPixel(image_view,x,y,&v,exception);
3713       q=QueueCacheViewAuthenticPixels(image_view,x_mid,y_mid,1,1,exception);
3714       if (q == (Quantum *) NULL)
3715         return(MagickTrue);
3716       SetPixelRed(image,PlasmaPixel(random_info,(MagickRealType)
3717         (u.red+v.red)/2.0,plasma),q);
3718       SetPixelGreen(image,PlasmaPixel(random_info,(MagickRealType)
3719         (u.green+v.green)/2.0,plasma),q);
3720       SetPixelBlue(image,PlasmaPixel(random_info,(MagickRealType)
3721         (u.blue+v.blue)/2.0,plasma),q);
3722       (void) SyncCacheViewAuthenticPixels(image_view,exception);
3723     }
3724   if (((segment->x2-segment->x1) < 3.0) && ((segment->y2-segment->y1) < 3.0))
3725     return(MagickTrue);
3726   return(MagickFalse);
3727 }
3728 \f
3729 MagickExport MagickBooleanType PlasmaImage(Image *image,
3730   const SegmentInfo *segment,size_t attenuate,size_t depth,
3731   ExceptionInfo *exception)
3732 {
3733   CacheView
3734     *image_view;
3735
3736   MagickBooleanType
3737     status;
3738
3739   RandomInfo
3740     *random_info;
3741
3742   if (image->debug != MagickFalse)
3743     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3744   assert(image != (Image *) NULL);
3745   assert(image->signature == MagickSignature);
3746   if (image->debug != MagickFalse)
3747     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3748   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
3749     return(MagickFalse);
3750   image_view=AcquireCacheView(image);
3751   random_info=AcquireRandomInfo();
3752   status=PlasmaImageProxy(image,image_view,random_info,segment,attenuate,depth,
3753     exception);
3754   random_info=DestroyRandomInfo(random_info);
3755   image_view=DestroyCacheView(image_view);
3756   return(status);
3757 }
3758 \f
3759 /*
3760 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3761 %                                                                             %
3762 %                                                                             %
3763 %                                                                             %
3764 %   P o l a r o i d I m a g e                                                 %
3765 %                                                                             %
3766 %                                                                             %
3767 %                                                                             %
3768 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3769 %
3770 %  PolaroidImage() simulates a Polaroid picture.
3771 %
3772 %  The format of the AnnotateImage method is:
3773 %
3774 %      Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
3775 %        const double angle,const PixelInterpolateMethod method,
3776 %        ExceptionInfo exception)
3777 %
3778 %  A description of each parameter follows:
3779 %
3780 %    o image: the image.
3781 %
3782 %    o draw_info: the draw info.
3783 %
3784 %    o angle: Apply the effect along this angle.
3785 %
3786 %    o method: the pixel interpolation method.
3787 %
3788 %    o exception: return any errors or warnings in this structure.
3789 %
3790 */
3791 MagickExport Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
3792   const double angle,const PixelInterpolateMethod method,
3793   ExceptionInfo *exception)
3794 {
3795   const char
3796     *value;
3797
3798   Image
3799     *bend_image,
3800     *caption_image,
3801     *flop_image,
3802     *picture_image,
3803     *polaroid_image,
3804     *rotate_image,
3805     *trim_image;
3806
3807   size_t
3808     height;
3809
3810   ssize_t
3811     quantum;
3812
3813   /*
3814     Simulate a Polaroid picture.
3815   */
3816   assert(image != (Image *) NULL);
3817   assert(image->signature == MagickSignature);
3818   if (image->debug != MagickFalse)
3819     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3820   assert(exception != (ExceptionInfo *) NULL);
3821   assert(exception->signature == MagickSignature);
3822   quantum=(ssize_t) MagickMax(MagickMax((double) image->columns,(double)
3823     image->rows)/25.0,10.0);
3824   height=image->rows+2*quantum;
3825   caption_image=(Image *) NULL;
3826   value=GetImageProperty(image,"Caption");
3827   if (value != (const char *) NULL)
3828     {
3829       char
3830         *caption,
3831         geometry[MaxTextExtent];
3832
3833       DrawInfo
3834         *annotate_info;
3835
3836       MagickBooleanType
3837         status;
3838
3839       ssize_t
3840         count;
3841
3842       TypeMetric
3843         metrics;
3844
3845       /*
3846         Generate caption image.
3847       */
3848       caption_image=CloneImage(image,image->columns,1,MagickTrue,exception);
3849       if (caption_image == (Image *) NULL)
3850         return((Image *) NULL);
3851       annotate_info=CloneDrawInfo((const ImageInfo *) NULL,draw_info);
3852       caption=InterpretImageProperties((ImageInfo *) NULL,(Image *) image,
3853         value,exception);
3854       (void) CloneString(&annotate_info->text,caption);
3855       count=FormatMagickCaption(caption_image,annotate_info,MagickTrue,&metrics,
3856         &caption,exception);
3857       status=SetImageExtent(caption_image,image->columns,(size_t)
3858         ((count+1)*(metrics.ascent-metrics.descent)+0.5),exception);
3859       if (status == MagickFalse)
3860         caption_image=DestroyImage(caption_image);
3861       else
3862         {
3863           caption_image->background_color=image->border_color;
3864           (void) SetImageBackgroundColor(caption_image);
3865           (void) CloneString(&annotate_info->text,caption);
3866           (void) FormatLocaleString(geometry,MaxTextExtent,"+0+%g",
3867             metrics.ascent);
3868           if (annotate_info->gravity == UndefinedGravity)
3869             (void) CloneString(&annotate_info->geometry,AcquireString(
3870               geometry));
3871           (void) AnnotateImage(caption_image,annotate_info,exception);
3872           height+=caption_image->rows;
3873         }
3874       annotate_info=DestroyDrawInfo(annotate_info);
3875       caption=DestroyString(caption);
3876     }
3877   picture_image=CloneImage(image,image->columns+2*quantum,height,MagickTrue,
3878     exception);
3879   if (picture_image == (Image *) NULL)
3880     {
3881       if (caption_image != (Image *) NULL)
3882         caption_image=DestroyImage(caption_image);
3883       return((Image *) NULL);
3884     }
3885   picture_image->background_color=image->border_color;
3886   (void) SetImageBackgroundColor(picture_image);
3887   (void) CompositeImage(picture_image,OverCompositeOp,image,quantum,quantum);
3888   if (caption_image != (Image *) NULL)
3889     {
3890       (void) CompositeImage(picture_image,OverCompositeOp,caption_image,
3891         quantum,(ssize_t) (image->rows+3*quantum/2));
3892       caption_image=DestroyImage(caption_image);
3893     }
3894   (void) QueryColorDatabase("none",&picture_image->background_color,exception);
3895   (void) SetImageAlphaChannel(picture_image,OpaqueAlphaChannel,exception);
3896   rotate_image=RotateImage(picture_image,90.0,exception);
3897   picture_image=DestroyImage(picture_image);
3898   if (rotate_image == (Image *) NULL)
3899     return((Image *) NULL);
3900   picture_image=rotate_image;
3901   bend_image=WaveImage(picture_image,0.01*picture_image->rows,2.0*
3902     picture_image->columns,method,exception);
3903   picture_image=DestroyImage(picture_image);
3904   if (bend_image == (Image *) NULL)
3905     return((Image *) NULL);
3906   InheritException(&bend_image->exception,exception);
3907   picture_image=bend_image;
3908   rotate_image=RotateImage(picture_image,-90.0,exception);
3909   picture_image=DestroyImage(picture_image);
3910   if (rotate_image == (Image *) NULL)
3911     return((Image *) NULL);
3912   picture_image=rotate_image;
3913   picture_image->background_color=image->background_color;
3914   polaroid_image=ShadowImage(picture_image,80.0,2.0,quantum/3,quantum/3,
3915     exception);
3916   if (polaroid_image == (Image *) NULL)
3917     {
3918       picture_image=DestroyImage(picture_image);
3919       return(picture_image);
3920     }
3921   flop_image=FlopImage(polaroid_image,exception);
3922   polaroid_image=DestroyImage(polaroid_image);
3923   if (flop_image == (Image *) NULL)
3924     {
3925       picture_image=DestroyImage(picture_image);
3926       return(picture_image);
3927     }
3928   polaroid_image=flop_image;
3929   (void) CompositeImage(polaroid_image,OverCompositeOp,picture_image,
3930     (ssize_t) (-0.01*picture_image->columns/2.0),0L);
3931   picture_image=DestroyImage(picture_image);
3932   (void) QueryColorDatabase("none",&polaroid_image->background_color,exception);
3933   rotate_image=RotateImage(polaroid_image,angle,exception);
3934   polaroid_image=DestroyImage(polaroid_image);
3935   if (rotate_image == (Image *) NULL)
3936     return((Image *) NULL);
3937   polaroid_image=rotate_image;
3938   trim_image=TrimImage(polaroid_image,exception);
3939   polaroid_image=DestroyImage(polaroid_image);
3940   if (trim_image == (Image *) NULL)
3941     return((Image *) NULL);
3942   polaroid_image=trim_image;
3943   return(polaroid_image);
3944 }
3945 \f
3946 /*
3947 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3948 %                                                                             %
3949 %                                                                             %
3950 %                                                                             %
3951 %     S e p i a T o n e I m a g e                                             %
3952 %                                                                             %
3953 %                                                                             %
3954 %                                                                             %
3955 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3956 %
3957 %  MagickSepiaToneImage() applies a special effect to the image, similar to the
3958 %  effect achieved in a photo darkroom by sepia toning.  Threshold ranges from
3959 %  0 to QuantumRange and is a measure of the extent of the sepia toning.  A
3960 %  threshold of 80% is a good starting point for a reasonable tone.
3961 %
3962 %  The format of the SepiaToneImage method is:
3963 %
3964 %      Image *SepiaToneImage(const Image *image,const double threshold,
3965 %        ExceptionInfo *exception)
3966 %
3967 %  A description of each parameter follows:
3968 %
3969 %    o image: the image.
3970 %
3971 %    o threshold: the tone threshold.
3972 %
3973 %    o exception: return any errors or warnings in this structure.
3974 %
3975 */
3976 MagickExport Image *SepiaToneImage(const Image *image,const double threshold,
3977   ExceptionInfo *exception)
3978 {
3979 #define SepiaToneImageTag  "SepiaTone/Image"
3980
3981   CacheView
3982     *image_view,
3983     *sepia_view;
3984
3985   Image
3986     *sepia_image;
3987
3988   MagickBooleanType
3989     status;
3990
3991   MagickOffsetType
3992     progress;
3993
3994   ssize_t
3995     y;
3996
3997   /*
3998     Initialize sepia-toned image attributes.
3999   */
4000   assert(image != (const Image *) NULL);
4001   assert(image->signature == MagickSignature);
4002   if (image->debug != MagickFalse)
4003     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4004   assert(exception != (ExceptionInfo *) NULL);
4005   assert(exception->signature == MagickSignature);
4006   sepia_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
4007   if (sepia_image == (Image *) NULL)
4008     return((Image *) NULL);
4009   if (SetImageStorageClass(sepia_image,DirectClass,exception) == MagickFalse)
4010     {
4011       sepia_image=DestroyImage(sepia_image);
4012       return((Image *) NULL);
4013     }
4014   /*
4015     Tone each row of the image.
4016   */
4017   status=MagickTrue;
4018   progress=0;
4019   image_view=AcquireCacheView(image);
4020   sepia_view=AcquireCacheView(sepia_image);
4021 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4022   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4023 #endif
4024   for (y=0; y < (ssize_t) image->rows; y++)
4025   {
4026     register const Quantum
4027       *restrict p;
4028
4029     register ssize_t
4030       x;
4031
4032     register Quantum
4033       *restrict q;
4034
4035     if (status == MagickFalse)
4036       continue;
4037     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
4038     q=QueueCacheViewAuthenticPixels(sepia_view,0,y,sepia_image->columns,1,
4039       exception);
4040     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
4041       {
4042         status=MagickFalse;
4043         continue;
4044       }
4045     for (x=0; x < (ssize_t) image->columns; x++)
4046     {
4047       MagickRealType
4048         intensity,
4049         tone;
4050
4051       intensity=(MagickRealType) GetPixelIntensity(image,p);
4052       tone=intensity > threshold ? (MagickRealType) QuantumRange : intensity+
4053         (MagickRealType) QuantumRange-threshold;
4054       SetPixelRed(sepia_image,ClampToQuantum(tone),q);
4055       tone=intensity > (7.0*threshold/6.0) ? (MagickRealType) QuantumRange :
4056         intensity+(MagickRealType) QuantumRange-7.0*threshold/6.0;
4057       SetPixelGreen(sepia_image,ClampToQuantum(tone),q);
4058       tone=intensity < (threshold/6.0) ? 0 : intensity-threshold/6.0;
4059       SetPixelBlue(sepia_image,ClampToQuantum(tone),q);
4060       tone=threshold/7.0;
4061       if ((MagickRealType) GetPixelGreen(image,q) < tone)
4062         SetPixelGreen(sepia_image,ClampToQuantum(tone),q);
4063       if ((MagickRealType) GetPixelBlue(image,q) < tone)
4064         SetPixelBlue(sepia_image,ClampToQuantum(tone),q);
4065       p+=GetPixelChannels(image);
4066       q+=GetPixelChannels(sepia_image);
4067     }
4068     if (SyncCacheViewAuthenticPixels(sepia_view,exception) == MagickFalse)
4069       status=MagickFalse;
4070     if (image->progress_monitor != (MagickProgressMonitor) NULL)
4071       {
4072         MagickBooleanType
4073           proceed;
4074
4075 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4076   #pragma omp critical (MagickCore_SepiaToneImage)
4077 #endif
4078         proceed=SetImageProgress(image,SepiaToneImageTag,progress++,
4079           image->rows);
4080         if (proceed == MagickFalse)
4081           status=MagickFalse;
4082       }
4083   }
4084   sepia_view=DestroyCacheView(sepia_view);
4085   image_view=DestroyCacheView(image_view);
4086   (void) NormalizeImage(sepia_image,exception);
4087   (void) ContrastImage(sepia_image,MagickTrue,exception);
4088   if (status == MagickFalse)
4089     sepia_image=DestroyImage(sepia_image);
4090   return(sepia_image);
4091 }
4092 \f
4093 /*
4094 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4095 %                                                                             %
4096 %                                                                             %
4097 %                                                                             %
4098 %     S h a d o w I m a g e                                                   %
4099 %                                                                             %
4100 %                                                                             %
4101 %                                                                             %
4102 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4103 %
4104 %  ShadowImage() simulates a shadow from the specified image and returns it.
4105 %
4106 %  The format of the ShadowImage method is:
4107 %
4108 %      Image *ShadowImage(const Image *image,const double opacity,
4109 %        const double sigma,const ssize_t x_offset,const ssize_t y_offset,
4110 %        ExceptionInfo *exception)
4111 %
4112 %  A description of each parameter follows:
4113 %
4114 %    o image: the image.
4115 %
4116 %    o opacity: percentage transparency.
4117 %
4118 %    o sigma: the standard deviation of the Gaussian, in pixels.
4119 %
4120 %    o x_offset: the shadow x-offset.
4121 %
4122 %    o y_offset: the shadow y-offset.
4123 %
4124 %    o exception: return any errors or warnings in this structure.
4125 %
4126 */
4127 MagickExport Image *ShadowImage(const Image *image,const double opacity,
4128   const double sigma,const ssize_t x_offset,const ssize_t y_offset,
4129   ExceptionInfo *exception)
4130 {
4131 #define ShadowImageTag  "Shadow/Image"
4132
4133   CacheView
4134     *border_view;
4135
4136   ChannelType
4137     channel_mask;
4138
4139   Image
4140     *border_image,
4141     *clone_image,
4142     *shadow_image;
4143
4144   MagickBooleanType
4145     status;
4146
4147   MagickOffsetType
4148     progress;
4149
4150   RectangleInfo
4151     border_info;
4152
4153   ssize_t
4154     y;
4155
4156   assert(image != (Image *) NULL);
4157   assert(image->signature == MagickSignature);
4158   if (image->debug != MagickFalse)
4159     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4160   assert(exception != (ExceptionInfo *) NULL);
4161   assert(exception->signature == MagickSignature);
4162   clone_image=CloneImage(image,0,0,MagickTrue,exception);
4163   if (clone_image == (Image *) NULL)
4164     return((Image *) NULL);
4165   (void) SetImageVirtualPixelMethod(clone_image,EdgeVirtualPixelMethod);
4166   clone_image->compose=OverCompositeOp;
4167   border_info.width=(size_t) floor(2.0*sigma+0.5);
4168   border_info.height=(size_t) floor(2.0*sigma+0.5);
4169   border_info.x=0;
4170   border_info.y=0;
4171   (void) QueryColorDatabase("none",&clone_image->border_color,exception);
4172   border_image=BorderImage(clone_image,&border_info,exception);
4173   clone_image=DestroyImage(clone_image);
4174   if (border_image == (Image *) NULL)
4175     return((Image *) NULL);
4176   if (border_image->matte == MagickFalse)
4177     (void) SetImageAlphaChannel(border_image,OpaqueAlphaChannel,exception);
4178   /*
4179     Shadow image.
4180   */
4181   status=MagickTrue;
4182   progress=0;
4183   border_view=AcquireCacheView(border_image);
4184 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4185   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4186 #endif
4187   for (y=0; y < (ssize_t) border_image->rows; y++)
4188   {
4189     register ssize_t
4190       x;
4191
4192     register Quantum
4193       *restrict q;
4194
4195     if (status == MagickFalse)
4196       continue;
4197     q=GetCacheViewAuthenticPixels(border_view,0,y,border_image->columns,1,
4198       exception);
4199     if (q == (Quantum *) NULL)
4200       {
4201         status=MagickFalse;
4202         continue;
4203       }
4204     for (x=0; x < (ssize_t) border_image->columns; x++)
4205     {
4206       SetPixelRed(border_image,border_image->background_color.red,q);
4207       SetPixelGreen(border_image,border_image->background_color.green,q);
4208       SetPixelBlue(border_image,border_image->background_color.blue,q);
4209       if (border_image->matte == MagickFalse)
4210         SetPixelAlpha(border_image,border_image->background_color.alpha,q);
4211       else
4212         SetPixelAlpha(border_image,ClampToQuantum((MagickRealType)
4213           (GetPixelAlpha(border_image,q)*opacity/100.0)),q);
4214       q+=GetPixelChannels(border_image);
4215     }
4216     if (SyncCacheViewAuthenticPixels(border_view,exception) == MagickFalse)
4217       status=MagickFalse;
4218     if (border_image->progress_monitor != (MagickProgressMonitor) NULL)
4219       {
4220         MagickBooleanType
4221           proceed;
4222
4223 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4224   #pragma omp critical (MagickCore_ShadowImage)
4225 #endif
4226         proceed=SetImageProgress(border_image,ShadowImageTag,progress++,
4227           border_image->rows);
4228         if (proceed == MagickFalse)
4229           status=MagickFalse;
4230       }
4231   }
4232   border_view=DestroyCacheView(border_view);
4233   channel_mask=SetPixelChannelMask(border_image,AlphaChannel);
4234   shadow_image=BlurImage(border_image,0.0,sigma,image->bias,exception);
4235   (void) SetPixelChannelMap(border_image,channel_mask);
4236   border_image=DestroyImage(border_image);
4237   if (shadow_image == (Image *) NULL)
4238     return((Image *) NULL);
4239   if (shadow_image->page.width == 0)
4240     shadow_image->page.width=shadow_image->columns;
4241   if (shadow_image->page.height == 0)
4242     shadow_image->page.height=shadow_image->rows;
4243   shadow_image->page.width+=x_offset-(ssize_t) border_info.width;
4244   shadow_image->page.height+=y_offset-(ssize_t) border_info.height;
4245   shadow_image->page.x+=x_offset-(ssize_t) border_info.width;
4246   shadow_image->page.y+=y_offset-(ssize_t) border_info.height;
4247   return(shadow_image);
4248 }
4249 \f
4250 /*
4251 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4252 %                                                                             %
4253 %                                                                             %
4254 %                                                                             %
4255 %     S k e t c h I m a g e                                                   %
4256 %                                                                             %
4257 %                                                                             %
4258 %                                                                             %
4259 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4260 %
4261 %  SketchImage() simulates a pencil sketch.  We convolve the image with a
4262 %  Gaussian operator of the given radius and standard deviation (sigma).  For
4263 %  reasonable results, radius should be larger than sigma.  Use a radius of 0
4264 %  and SketchImage() selects a suitable radius for you.  Angle gives the angle
4265 %  of the sketch.
4266 %
4267 %  The format of the SketchImage method is:
4268 %
4269 %    Image *SketchImage(const Image *image,const double radius,
4270 %      const double sigma,const double angle,const double bias,
4271 %      ExceptionInfo *exception)
4272 %
4273 %  A description of each parameter follows:
4274 %
4275 %    o image: the image.
4276 %
4277 %    o radius: the radius of the Gaussian, in pixels, not counting the
4278 %      center pixel.
4279 %
4280 %    o sigma: the standard deviation of the Gaussian, in pixels.
4281 %
4282 %    o angle: apply the effect along this angle.
4283 %
4284 %    o bias: the bias.
4285 %
4286 %    o exception: return any errors or warnings in this structure.
4287 %
4288 */
4289 MagickExport Image *SketchImage(const Image *image,const double radius,
4290   const double sigma,const double angle,const double bias,
4291   ExceptionInfo *exception)
4292 {
4293   CacheView
4294     *random_view;
4295
4296   Image
4297     *blend_image,
4298     *blur_image,
4299     *dodge_image,
4300     *random_image,
4301     *sketch_image;
4302
4303   MagickBooleanType
4304     status;
4305
4306   PixelInfo
4307     zero;
4308
4309   RandomInfo
4310     **restrict random_info;
4311
4312   ssize_t
4313     y;
4314
4315   /*
4316     Sketch image.
4317   */
4318   random_image=CloneImage(image,image->columns << 1,image->rows << 1,
4319     MagickTrue,exception);
4320   if (random_image == (Image *) NULL)
4321     return((Image *) NULL);
4322   status=MagickTrue;
4323   GetPixelInfo(random_image,&zero);
4324   random_info=AcquireRandomInfoThreadSet();
4325   random_view=AcquireCacheView(random_image);
4326 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4327   #pragma omp parallel for schedule(dynamic,4) shared(status)
4328 #endif
4329   for (y=0; y < (ssize_t) random_image->rows; y++)
4330   {
4331     const int
4332       id = GetOpenMPThreadId();
4333
4334     PixelInfo
4335       pixel;
4336
4337     register ssize_t
4338       x;
4339
4340     register Quantum
4341       *restrict q;
4342
4343     if (status == MagickFalse)
4344       continue;
4345     q=QueueCacheViewAuthenticPixels(random_view,0,y,random_image->columns,1,
4346       exception);
4347     if (q == (Quantum *) NULL)
4348       {
4349         status=MagickFalse;
4350         continue;
4351       }
4352     pixel=zero;
4353     for (x=0; x < (ssize_t) random_image->columns; x++)
4354     {
4355       pixel.red=(MagickRealType) (QuantumRange*
4356         GetPseudoRandomValue(random_info[id]));
4357       pixel.green=pixel.red;
4358       pixel.blue=pixel.red;
4359       if (image->colorspace == CMYKColorspace)
4360         pixel.black=pixel.red;
4361       SetPixelPixelInfo(random_image,&pixel,q);
4362       q+=GetPixelChannels(random_image);
4363     }
4364     if (SyncCacheViewAuthenticPixels(random_view,exception) == MagickFalse)
4365       status=MagickFalse;
4366   }
4367   random_view=DestroyCacheView(random_view);
4368   random_info=DestroyRandomInfoThreadSet(random_info);
4369   if (status == MagickFalse)
4370     {
4371       random_image=DestroyImage(random_image);
4372       return(random_image);
4373     }
4374   blur_image=MotionBlurImage(random_image,radius,sigma,angle,bias,exception);
4375   random_image=DestroyImage(random_image);
4376   if (blur_image == (Image *) NULL)
4377     return((Image *) NULL);
4378   dodge_image=EdgeImage(blur_image,radius,sigma,exception);
4379   blur_image=DestroyImage(blur_image);
4380   if (dodge_image == (Image *) NULL)
4381     return((Image *) NULL);
4382   (void) NormalizeImage(dodge_image,exception);
4383   (void) NegateImage(dodge_image,MagickFalse,exception);
4384   (void) TransformImage(&dodge_image,(char *) NULL,"50%");
4385   sketch_image=CloneImage(image,0,0,MagickTrue,exception);
4386   if (sketch_image == (Image *) NULL)
4387     {
4388       dodge_image=DestroyImage(dodge_image);
4389       return((Image *) NULL);
4390     }
4391   (void) CompositeImage(sketch_image,ColorDodgeCompositeOp,dodge_image,0,0);
4392   dodge_image=DestroyImage(dodge_image);
4393   blend_image=CloneImage(image,0,0,MagickTrue,exception);
4394   if (blend_image == (Image *) NULL)
4395     {
4396       sketch_image=DestroyImage(sketch_image);
4397       return((Image *) NULL);
4398     }
4399   (void) SetImageArtifact(blend_image,"compose:args","20x80");
4400   (void) CompositeImage(sketch_image,BlendCompositeOp,blend_image,0,0);
4401   blend_image=DestroyImage(blend_image);
4402   return(sketch_image);
4403 }
4404 \f
4405 /*
4406 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4407 %                                                                             %
4408 %                                                                             %
4409 %                                                                             %
4410 %     S o l a r i z e I m a g e                                               %
4411 %                                                                             %
4412 %                                                                             %
4413 %                                                                             %
4414 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4415 %
4416 %  SolarizeImage() applies a special effect to the image, similar to the effect
4417 %  achieved in a photo darkroom by selectively exposing areas of photo
4418 %  sensitive paper to light.  Threshold ranges from 0 to QuantumRange and is a
4419 %  measure of the extent of the solarization.
4420 %
4421 %  The format of the SolarizeImage method is:
4422 %
4423 %      MagickBooleanType SolarizeImage(Image *image,const double threshold,
4424 %        ExceptionInfo *exception)
4425 %
4426 %  A description of each parameter follows:
4427 %
4428 %    o image: the image.
4429 %
4430 %    o threshold:  Define the extent of the solarization.
4431 %
4432 %    o exception: return any errors or warnings in this structure.
4433 %
4434 */
4435 MagickExport MagickBooleanType SolarizeImage(Image *image,
4436   const double threshold,ExceptionInfo *exception)
4437 {
4438 #define SolarizeImageTag  "Solarize/Image"
4439
4440   CacheView
4441     *image_view;
4442
4443   MagickBooleanType
4444     status;
4445
4446   MagickOffsetType
4447     progress;
4448
4449   ssize_t
4450     y;
4451
4452   assert(image != (Image *) NULL);
4453   assert(image->signature == MagickSignature);
4454   if (image->debug != MagickFalse)
4455     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4456   if (image->storage_class == PseudoClass)
4457     {
4458       register ssize_t
4459         i;
4460
4461       /*
4462         Solarize colormap.
4463       */
4464       for (i=0; i < (ssize_t) image->colors; i++)
4465       {
4466         if ((MagickRealType) image->colormap[i].red > threshold)
4467           image->colormap[i].red=(Quantum) QuantumRange-image->colormap[i].red;
4468         if ((MagickRealType) image->colormap[i].green > threshold)
4469           image->colormap[i].green=(Quantum) QuantumRange-
4470             image->colormap[i].green;
4471         if ((MagickRealType) image->colormap[i].blue > threshold)
4472           image->colormap[i].blue=(Quantum) QuantumRange-
4473             image->colormap[i].blue;
4474       }
4475     }
4476   /*
4477     Solarize image.
4478   */
4479   status=MagickTrue;
4480   progress=0;
4481   image_view=AcquireCacheView(image);
4482 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4483   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4484 #endif
4485   for (y=0; y < (ssize_t) image->rows; y++)
4486   {
4487     register ssize_t
4488       x;
4489
4490     register Quantum
4491       *restrict q;
4492
4493     if (status == MagickFalse)
4494       continue;
4495     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
4496     if (q == (Quantum *) NULL)
4497       {
4498         status=MagickFalse;
4499         continue;
4500       }
4501     for (x=0; x < (ssize_t) image->columns; x++)
4502     {
4503       if ((MagickRealType) GetPixelRed(image,q) > threshold)
4504         SetPixelRed(image,QuantumRange-GetPixelRed(image,q),q);
4505       if ((MagickRealType) GetPixelGreen(image,q) > threshold)
4506         SetPixelGreen(image,QuantumRange-GetPixelGreen(image,q),q);
4507       if ((MagickRealType) GetPixelBlue(image,q) > threshold)
4508         SetPixelBlue(image,QuantumRange-GetPixelBlue(image,q),q);
4509       q+=GetPixelChannels(image);
4510     }
4511     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4512       status=MagickFalse;
4513     if (image->progress_monitor != (MagickProgressMonitor) NULL)
4514       {
4515         MagickBooleanType
4516           proceed;
4517
4518 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4519   #pragma omp critical (MagickCore_SolarizeImage)
4520 #endif
4521         proceed=SetImageProgress(image,SolarizeImageTag,progress++,image->rows);
4522         if (proceed == MagickFalse)
4523           status=MagickFalse;
4524       }
4525   }
4526   image_view=DestroyCacheView(image_view);
4527   return(status);
4528 }
4529 \f
4530 /*
4531 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4532 %                                                                             %
4533 %                                                                             %
4534 %                                                                             %
4535 %   S t e g a n o I m a g e                                                   %
4536 %                                                                             %
4537 %                                                                             %
4538 %                                                                             %
4539 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4540 %
4541 %  SteganoImage() hides a digital watermark within the image.  Recover
4542 %  the hidden watermark later to prove that the authenticity of an image.
4543 %  Offset defines the start position within the image to hide the watermark.
4544 %
4545 %  The format of the SteganoImage method is:
4546 %
4547 %      Image *SteganoImage(const Image *image,Image *watermark,
4548 %        ExceptionInfo *exception)
4549 %
4550 %  A description of each parameter follows:
4551 %
4552 %    o image: the image.
4553 %
4554 %    o watermark: the watermark image.
4555 %
4556 %    o exception: return any errors or warnings in this structure.
4557 %
4558 */
4559 MagickExport Image *SteganoImage(const Image *image,const Image *watermark,
4560   ExceptionInfo *exception)
4561 {
4562 #define GetBit(alpha,i) ((((size_t) (alpha) >> (size_t) (i)) & 0x01) != 0)
4563 #define SetBit(alpha,i,set) (Quantum) ((set) != 0 ? (size_t) (alpha) \
4564   | (one << (size_t) (i)) : (size_t) (alpha) & ~(one << (size_t) (i)))
4565 #define SteganoImageTag  "Stegano/Image"
4566
4567   CacheView
4568     *stegano_view,
4569     *watermark_view;
4570
4571   Image
4572     *stegano_image;
4573
4574   int
4575     c;
4576
4577   MagickBooleanType
4578     status;
4579
4580   PixelPacket
4581     pixel;
4582
4583   register Quantum
4584     *q;
4585
4586   register ssize_t
4587     x;
4588
4589   size_t
4590     depth,
4591     one;
4592
4593   ssize_t
4594     i,
4595     j,
4596     k,
4597     y;
4598
4599   /*
4600     Initialize steganographic image attributes.
4601   */
4602   assert(image != (const Image *) NULL);
4603   assert(image->signature == MagickSignature);
4604   if (image->debug != MagickFalse)
4605     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4606   assert(watermark != (const Image *) NULL);
4607   assert(watermark->signature == MagickSignature);
4608   assert(exception != (ExceptionInfo *) NULL);
4609   assert(exception->signature == MagickSignature);
4610   one=1UL;
4611   stegano_image=CloneImage(image,0,0,MagickTrue,exception);
4612   if (stegano_image == (Image *) NULL)
4613     return((Image *) NULL);
4614   if (SetImageStorageClass(stegano_image,DirectClass,exception) == MagickFalse)
4615     {
4616       stegano_image=DestroyImage(stegano_image);
4617       return((Image *) NULL);
4618     }
4619   stegano_image->depth=MAGICKCORE_QUANTUM_DEPTH;
4620   /*
4621     Hide watermark in low-order bits of image.
4622   */
4623   c=0;
4624   i=0;
4625   j=0;
4626   depth=stegano_image->depth;
4627   k=image->offset;
4628   status=MagickTrue;
4629   watermark_view=AcquireCacheView(watermark);
4630   stegano_view=AcquireCacheView(stegano_image);
4631   for (i=(ssize_t) depth-1; (i >= 0) && (j < (ssize_t) depth); i--)
4632   {
4633     for (y=0; (y < (ssize_t) watermark->rows) && (j < (ssize_t) depth); y++)
4634     {
4635       for (x=0; (x < (ssize_t) watermark->columns) && (j < (ssize_t) depth); x++)
4636       {
4637         (void) GetOneCacheViewVirtualPixel(watermark_view,x,y,&pixel,exception);
4638         if ((k/(ssize_t) stegano_image->columns) >= (ssize_t) stegano_image->rows)
4639           break;
4640         q=GetCacheViewAuthenticPixels(stegano_view,k % (ssize_t)
4641           stegano_image->columns,k/(ssize_t) stegano_image->columns,1,1,
4642           exception);
4643         if (q == (Quantum *) NULL)
4644           break;
4645         switch (c)
4646         {
4647           case 0:
4648           {
4649             SetPixelRed(image,SetBit(GetPixelRed(image,q),j,GetBit(
4650               GetPixelPacketIntensity(&pixel),i)),q);
4651             break;
4652           }
4653           case 1:
4654           {
4655             SetPixelGreen(image,SetBit(GetPixelGreen(image,q),j,GetBit(
4656               GetPixelPacketIntensity(&pixel),i)),q);
4657             break;
4658           }
4659           case 2:
4660           {
4661             SetPixelBlue(image,SetBit(GetPixelBlue(image,q),j,GetBit(
4662               GetPixelPacketIntensity(&pixel),i)),q);
4663             break;
4664           }
4665         }
4666         if (SyncCacheViewAuthenticPixels(stegano_view,exception) == MagickFalse)
4667           break;
4668         c++;
4669         if (c == 3)
4670           c=0;
4671         k++;
4672         if (k == (ssize_t) (stegano_image->columns*stegano_image->columns))
4673           k=0;
4674         if (k == image->offset)
4675           j++;
4676       }
4677     }
4678     if (image->progress_monitor != (MagickProgressMonitor) NULL)
4679       {
4680         MagickBooleanType
4681           proceed;
4682
4683         proceed=SetImageProgress(image,SteganoImageTag,(MagickOffsetType)
4684           (depth-i),depth);
4685         if (proceed == MagickFalse)
4686           status=MagickFalse;
4687       }
4688   }
4689   stegano_view=DestroyCacheView(stegano_view);
4690   watermark_view=DestroyCacheView(watermark_view);
4691   if (stegano_image->storage_class == PseudoClass)
4692     (void) SyncImage(stegano_image);
4693   if (status == MagickFalse)
4694     {
4695       stegano_image=DestroyImage(stegano_image);
4696       return((Image *) NULL);
4697     }
4698   return(stegano_image);
4699 }
4700 \f
4701 /*
4702 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4703 %                                                                             %
4704 %                                                                             %
4705 %                                                                             %
4706 %   S t e r e o A n a g l y p h I m a g e                                     %
4707 %                                                                             %
4708 %                                                                             %
4709 %                                                                             %
4710 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4711 %
4712 %  StereoAnaglyphImage() combines two images and produces a single image that
4713 %  is the composite of a left and right image of a stereo pair.  Special
4714 %  red-green stereo glasses are required to view this effect.
4715 %
4716 %  The format of the StereoAnaglyphImage method is:
4717 %
4718 %      Image *StereoImage(const Image *left_image,const Image *right_image,
4719 %        ExceptionInfo *exception)
4720 %      Image *StereoAnaglyphImage(const Image *left_image,
4721 %        const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
4722 %        ExceptionInfo *exception)
4723 %
4724 %  A description of each parameter follows:
4725 %
4726 %    o left_image: the left image.
4727 %
4728 %    o right_image: the right image.
4729 %
4730 %    o exception: return any errors or warnings in this structure.
4731 %
4732 %    o x_offset: amount, in pixels, by which the left image is offset to the
4733 %      right of the right image.
4734 %
4735 %    o y_offset: amount, in pixels, by which the left image is offset to the
4736 %      bottom of the right image.
4737 %
4738 %
4739 */
4740 MagickExport Image *StereoImage(const Image *left_image,
4741   const Image *right_image,ExceptionInfo *exception)
4742 {
4743   return(StereoAnaglyphImage(left_image,right_image,0,0,exception));
4744 }
4745
4746 MagickExport Image *StereoAnaglyphImage(const Image *left_image,
4747   const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
4748   ExceptionInfo *exception)
4749 {
4750 #define StereoImageTag  "Stereo/Image"
4751
4752   const Image
4753     *image;
4754
4755   Image
4756     *stereo_image;
4757
4758   MagickBooleanType
4759     status;
4760
4761   ssize_t
4762     y;
4763
4764   assert(left_image != (const Image *) NULL);
4765   assert(left_image->signature == MagickSignature);
4766   if (left_image->debug != MagickFalse)
4767     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4768       left_image->filename);
4769   assert(right_image != (const Image *) NULL);
4770   assert(right_image->signature == MagickSignature);
4771   assert(exception != (ExceptionInfo *) NULL);
4772   assert(exception->signature == MagickSignature);
4773   assert(right_image != (const Image *) NULL);
4774   image=left_image;
4775   if ((left_image->columns != right_image->columns) ||
4776       (left_image->rows != right_image->rows))
4777     ThrowImageException(ImageError,"LeftAndRightImageSizesDiffer");
4778   /*
4779     Initialize stereo image attributes.
4780   */
4781   stereo_image=CloneImage(left_image,left_image->columns,left_image->rows,
4782     MagickTrue,exception);
4783   if (stereo_image == (Image *) NULL)
4784     return((Image *) NULL);
4785   if (SetImageStorageClass(stereo_image,DirectClass,exception) == MagickFalse)
4786     {
4787       stereo_image=DestroyImage(stereo_image);
4788       return((Image *) NULL);
4789     }
4790   /*
4791     Copy left image to red channel and right image to blue channel.
4792   */
4793   status=MagickTrue;
4794   for (y=0; y < (ssize_t) stereo_image->rows; y++)
4795   {
4796     register const Quantum
4797       *restrict p,
4798       *restrict q;
4799
4800     register ssize_t
4801       x;
4802
4803     register Quantum
4804       *restrict r;
4805
4806     p=GetVirtualPixels(left_image,-x_offset,y-y_offset,image->columns,1,
4807       exception);
4808     q=GetVirtualPixels(right_image,0,y,right_image->columns,1,exception);
4809     r=QueueAuthenticPixels(stereo_image,0,y,stereo_image->columns,1,exception);
4810     if ((p == (const Quantum *) NULL) ||
4811         (q == (Quantum *) NULL) || (r == (Quantum *) NULL))
4812       break;
4813     for (x=0; x < (ssize_t) stereo_image->columns; x++)
4814     {
4815       SetPixelRed(image,GetPixelRed(left_image,p),r);
4816       SetPixelGreen(image,GetPixelGreen(left_image,q),r);
4817       SetPixelBlue(image,GetPixelBlue(left_image,q),r);
4818       SetPixelAlpha(image,(GetPixelAlpha(left_image,p)+
4819         GetPixelAlpha(left_image,q))/2,r);
4820       p+=GetPixelChannels(left_image);
4821       q++;
4822       r++;
4823     }
4824     if (SyncAuthenticPixels(stereo_image,exception) == MagickFalse)
4825       break;
4826     if (image->progress_monitor != (MagickProgressMonitor) NULL)
4827       {
4828         MagickBooleanType
4829           proceed;
4830
4831         proceed=SetImageProgress(image,StereoImageTag,(MagickOffsetType) y,
4832           stereo_image->rows);
4833         if (proceed == MagickFalse)
4834           status=MagickFalse;
4835       }
4836   }
4837   if (status == MagickFalse)
4838     {
4839       stereo_image=DestroyImage(stereo_image);
4840       return((Image *) NULL);
4841     }
4842   return(stereo_image);
4843 }
4844 \f
4845 /*
4846 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4847 %                                                                             %
4848 %                                                                             %
4849 %                                                                             %
4850 %     S w i r l I m a g e                                                     %
4851 %                                                                             %
4852 %                                                                             %
4853 %                                                                             %
4854 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4855 %
4856 %  SwirlImage() swirls the pixels about the center of the image, where
4857 %  degrees indicates the sweep of the arc through which each pixel is moved.
4858 %  You get a more dramatic effect as the degrees move from 1 to 360.
4859 %
4860 %  The format of the SwirlImage method is:
4861 %
4862 %      Image *SwirlImage(const Image *image,double degrees,
4863 %        ExceptionInfo *exception)
4864 %
4865 %  A description of each parameter follows:
4866 %
4867 %    o image: the image.
4868 %
4869 %    o degrees: Define the tightness of the swirling effect.
4870 %
4871 %    o exception: return any errors or warnings in this structure.
4872 %
4873 */
4874 MagickExport Image *SwirlImage(const Image *image,double degrees,
4875   ExceptionInfo *exception)
4876 {
4877 #define SwirlImageTag  "Swirl/Image"
4878
4879   CacheView
4880     *image_view,
4881     *swirl_view;
4882
4883   Image
4884     *swirl_image;
4885
4886   MagickBooleanType
4887     status;
4888
4889   MagickOffsetType
4890     progress;
4891
4892   PixelInfo
4893     zero;
4894
4895   MagickRealType
4896     radius;
4897
4898   PointInfo
4899     center,
4900     scale;
4901
4902   ssize_t
4903     y;
4904
4905   /*
4906     Initialize swirl image attributes.
4907   */
4908   assert(image != (const Image *) NULL);
4909   assert(image->signature == MagickSignature);
4910   if (image->debug != MagickFalse)
4911     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4912   assert(exception != (ExceptionInfo *) NULL);
4913   assert(exception->signature == MagickSignature);
4914   swirl_image=CloneImage(image,0,0,MagickTrue,exception);
4915   if (swirl_image == (Image *) NULL)
4916     return((Image *) NULL);
4917   if (SetImageStorageClass(swirl_image,DirectClass,exception) == MagickFalse)
4918     {
4919       swirl_image=DestroyImage(swirl_image);
4920       return((Image *) NULL);
4921     }
4922   if (swirl_image->background_color.alpha != OpaqueAlpha)
4923     swirl_image->matte=MagickTrue;
4924   /*
4925     Compute scaling factor.
4926   */
4927   center.x=(double) image->columns/2.0;
4928   center.y=(double) image->rows/2.0;
4929   radius=MagickMax(center.x,center.y);
4930   scale.x=1.0;
4931   scale.y=1.0;
4932   if (image->columns > image->rows)
4933     scale.y=(double) image->columns/(double) image->rows;
4934   else
4935     if (image->columns < image->rows)
4936       scale.x=(double) image->rows/(double) image->columns;
4937   degrees=(double) DegreesToRadians(degrees);
4938   /*
4939     Swirl image.
4940   */
4941   status=MagickTrue;
4942   progress=0;
4943   GetPixelInfo(swirl_image,&zero);
4944   image_view=AcquireCacheView(image);
4945   swirl_view=AcquireCacheView(swirl_image);
4946 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4947   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
4948 #endif
4949   for (y=0; y < (ssize_t) image->rows; y++)
4950   {
4951     PixelInfo
4952       pixel;
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     pixel=zero;
4977     for (x=0; x < (ssize_t) image->columns; x++)
4978     {
4979       /*
4980         Determine if the pixel is within an ellipse.
4981       */
4982       delta.x=scale.x*(double) (x-center.x);
4983       distance=delta.x*delta.x+delta.y*delta.y;
4984       if (distance < (radius*radius))
4985         {
4986           MagickRealType
4987             cosine,
4988             factor,
4989             sine;
4990
4991           /*
4992             Swirl the pixel.
4993           */
4994           factor=1.0-sqrt((double) distance)/radius;
4995           sine=sin((double) (degrees*factor*factor));
4996           cosine=cos((double) (degrees*factor*factor));
4997           (void) InterpolatePixelInfo(image,image_view,
4998             UndefinedInterpolatePixel,(double) ((cosine*delta.x-sine*delta.y)/
4999             scale.x+center.x),(double) ((sine*delta.x+cosine*delta.y)/scale.y+
5000             center.y),&pixel,exception);
5001           SetPixelPixelInfo(swirl_image,&pixel,q);
5002         }
5003       q+=GetPixelChannels(swirl_image);
5004     }
5005     if (SyncCacheViewAuthenticPixels(swirl_view,exception) == MagickFalse)
5006       status=MagickFalse;
5007     if (image->progress_monitor != (MagickProgressMonitor) NULL)
5008       {
5009         MagickBooleanType
5010           proceed;
5011
5012 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5013   #pragma omp critical (MagickCore_SwirlImage)
5014 #endif
5015         proceed=SetImageProgress(image,SwirlImageTag,progress++,image->rows);
5016         if (proceed == MagickFalse)
5017           status=MagickFalse;
5018       }
5019   }
5020   swirl_view=DestroyCacheView(swirl_view);
5021   image_view=DestroyCacheView(image_view);
5022   if (status == MagickFalse)
5023     swirl_image=DestroyImage(swirl_image);
5024   return(swirl_image);
5025 }
5026 \f
5027 /*
5028 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5029 %                                                                             %
5030 %                                                                             %
5031 %                                                                             %
5032 %     T i n t I m a g e                                                       %
5033 %                                                                             %
5034 %                                                                             %
5035 %                                                                             %
5036 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5037 %
5038 %  TintImage() applies a color vector to each pixel in the image.  The length
5039 %  of the vector is 0 for black and white and at its maximum for the midtones.
5040 %  The vector weighting function is f(x)=(1-(4.0*((x-0.5)*(x-0.5))))
5041 %
5042 %  The format of the TintImage method is:
5043 %
5044 %      Image *TintImage(const Image *image,const char *opacity,
5045 %        const PixelInfo *tint,ExceptionInfo *exception)
5046 %
5047 %  A description of each parameter follows:
5048 %
5049 %    o image: the image.
5050 %
5051 %    o opacity: A color value used for tinting.
5052 %
5053 %    o tint: A color value used for tinting.
5054 %
5055 %    o exception: return any errors or warnings in this structure.
5056 %
5057 */
5058 MagickExport Image *TintImage(const Image *image,const char *opacity,
5059   const PixelInfo *tint,ExceptionInfo *exception)
5060 {
5061 #define TintImageTag  "Tint/Image"
5062
5063   CacheView
5064     *image_view,
5065     *tint_view;
5066
5067   GeometryInfo
5068     geometry_info;
5069
5070   Image
5071     *tint_image;
5072
5073   MagickBooleanType
5074     status;
5075
5076   MagickOffsetType
5077     progress;
5078
5079   MagickRealType
5080     intensity;
5081
5082   PixelInfo
5083     color_vector,
5084     pixel;
5085
5086   MagickStatusType
5087     flags;
5088
5089   ssize_t
5090     y;
5091
5092   /*
5093     Allocate tint image.
5094   */
5095   assert(image != (const Image *) NULL);
5096   assert(image->signature == MagickSignature);
5097   if (image->debug != MagickFalse)
5098     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5099   assert(exception != (ExceptionInfo *) NULL);
5100   assert(exception->signature == MagickSignature);
5101   tint_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
5102   if (tint_image == (Image *) NULL)
5103     return((Image *) NULL);
5104   if (SetImageStorageClass(tint_image,DirectClass,exception) == MagickFalse)
5105     {
5106       tint_image=DestroyImage(tint_image);
5107       return((Image *) NULL);
5108     }
5109   if (opacity == (const char *) NULL)
5110     return(tint_image);
5111   /*
5112     Determine RGB values of the color.
5113   */
5114   GetPixelInfo(image,&pixel);
5115   flags=ParseGeometry(opacity,&geometry_info);
5116   pixel.red=geometry_info.rho;
5117   if ((flags & SigmaValue) != 0)
5118     pixel.green=geometry_info.sigma;
5119   else
5120     pixel.green=pixel.red;
5121   if ((flags & XiValue) != 0)
5122     pixel.blue=geometry_info.xi;
5123   else
5124     pixel.blue=pixel.red;
5125   if ((flags & PsiValue) != 0)
5126     pixel.alpha=geometry_info.psi;
5127   else
5128     pixel.alpha=(MagickRealType) OpaqueAlpha;
5129   intensity=(MagickRealType) GetPixelInfoIntensity(tint);
5130   color_vector.red=(MagickRealType) (pixel.red*tint->red/100.0-intensity);
5131   color_vector.green=(MagickRealType) (pixel.green*tint->green/100.0-intensity);
5132   color_vector.blue=(MagickRealType) (pixel.blue*tint->blue/100.0-intensity);
5133   color_vector.black=(MagickRealType) (pixel.black*tint->black/100.0-intensity);
5134   color_vector.alpha=(MagickRealType) (pixel.alpha*tint->alpha/100.0-intensity);
5135   /*
5136     Tint image.
5137   */
5138   status=MagickTrue;
5139   progress=0;
5140   image_view=AcquireCacheView(image);
5141   tint_view=AcquireCacheView(tint_image);
5142 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5143   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
5144 #endif
5145   for (y=0; y < (ssize_t) image->rows; y++)
5146   {
5147     register const Quantum
5148       *restrict p;
5149
5150     register Quantum
5151       *restrict q;
5152
5153     register ssize_t
5154       x;
5155
5156     if (status == MagickFalse)
5157       continue;
5158     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
5159     q=QueueCacheViewAuthenticPixels(tint_view,0,y,tint_image->columns,1,
5160       exception);
5161     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
5162       {
5163         status=MagickFalse;
5164         continue;
5165       }
5166     for (x=0; x < (ssize_t) image->columns; x++)
5167     {
5168       PixelInfo
5169         pixel;
5170
5171       MagickRealType
5172         weight;
5173
5174       if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
5175         {
5176           weight=QuantumScale*GetPixelRed(image,p)-0.5;
5177           pixel.red=(MagickRealType) GetPixelRed(image,p)+
5178             color_vector.red*(1.0-(4.0*(weight*weight)));
5179           SetPixelRed(tint_image,ClampToQuantum(pixel.red),q);
5180         }
5181       if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
5182         {
5183           weight=QuantumScale*GetPixelGreen(image,p)-0.5;
5184           pixel.green=(MagickRealType) GetPixelGreen(image,p)+
5185             color_vector.green*(1.0-(4.0*(weight*weight)));
5186           SetPixelGreen(tint_image,ClampToQuantum(pixel.green),q);
5187         }
5188       if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
5189         {
5190           weight=QuantumScale*GetPixelBlue(image,p)-0.5;
5191           pixel.blue=(MagickRealType) GetPixelBlue(image,p)+
5192             color_vector.blue*(1.0-(4.0*(weight*weight)));
5193           SetPixelBlue(tint_image,ClampToQuantum(pixel.blue),q);
5194         }
5195       if ((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0)
5196         {
5197           weight=QuantumScale*GetPixelBlack(image,p)-0.5;
5198           pixel.black=(MagickRealType) GetPixelBlack(image,p)+
5199             color_vector.black*(1.0-(4.0*(weight*weight)));
5200           SetPixelBlack(tint_image,ClampToQuantum(pixel.black),q);
5201         }
5202       if ((GetPixelAlphaTraits(image) & CopyPixelTrait) != 0)
5203         SetPixelAlpha(tint_image,GetPixelAlpha(image,p),q);
5204       p+=GetPixelChannels(image);
5205       q+=GetPixelChannels(tint_image);
5206     }
5207     if (SyncCacheViewAuthenticPixels(tint_view,exception) == MagickFalse)
5208       status=MagickFalse;
5209     if (image->progress_monitor != (MagickProgressMonitor) NULL)
5210       {
5211         MagickBooleanType
5212           proceed;
5213
5214 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5215   #pragma omp critical (MagickCore_TintImage)
5216 #endif
5217         proceed=SetImageProgress(image,TintImageTag,progress++,image->rows);
5218         if (proceed == MagickFalse)
5219           status=MagickFalse;
5220       }
5221   }
5222   tint_view=DestroyCacheView(tint_view);
5223   image_view=DestroyCacheView(image_view);
5224   if (status == MagickFalse)
5225     tint_image=DestroyImage(tint_image);
5226   return(tint_image);
5227 }
5228 \f
5229 /*
5230 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5231 %                                                                             %
5232 %                                                                             %
5233 %                                                                             %
5234 %     V i g n e t t e I m a g e                                               %
5235 %                                                                             %
5236 %                                                                             %
5237 %                                                                             %
5238 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5239 %
5240 %  VignetteImage() softens the edges of the image in vignette style.
5241 %
5242 %  The format of the VignetteImage method is:
5243 %
5244 %      Image *VignetteImage(const Image *image,const double radius,
5245 %        const double sigma,const ssize_t x,const ssize_t y,
5246 %        ExceptionInfo *exception)
5247 %
5248 %  A description of each parameter follows:
5249 %
5250 %    o image: the image.
5251 %
5252 %    o radius: the radius of the pixel neighborhood.
5253 %
5254 %    o sigma: the standard deviation of the Gaussian, in pixels.
5255 %
5256 %    o x, y:  Define the x and y ellipse offset.
5257 %
5258 %    o exception: return any errors or warnings in this structure.
5259 %
5260 */
5261 MagickExport Image *VignetteImage(const Image *image,const double radius,
5262   const double sigma,const ssize_t x,const ssize_t y,ExceptionInfo *exception)
5263 {
5264   char
5265     ellipse[MaxTextExtent];
5266
5267   DrawInfo
5268     *draw_info;
5269
5270   Image
5271     *canvas_image,
5272     *blur_image,
5273     *oval_image,
5274     *vignette_image;
5275
5276   assert(image != (Image *) NULL);
5277   assert(image->signature == MagickSignature);
5278   if (image->debug != MagickFalse)
5279     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5280   assert(exception != (ExceptionInfo *) NULL);
5281   assert(exception->signature == MagickSignature);
5282   canvas_image=CloneImage(image,0,0,MagickTrue,exception);
5283   if (canvas_image == (Image *) NULL)
5284     return((Image *) NULL);
5285   if (SetImageStorageClass(canvas_image,DirectClass,exception) == MagickFalse)
5286     {
5287       canvas_image=DestroyImage(canvas_image);
5288       return((Image *) NULL);
5289     }
5290   canvas_image->matte=MagickTrue;
5291   oval_image=CloneImage(canvas_image,canvas_image->columns,
5292     canvas_image->rows,MagickTrue,exception);
5293   if (oval_image == (Image *) NULL)
5294     {
5295       canvas_image=DestroyImage(canvas_image);
5296       return((Image *) NULL);
5297     }
5298   (void) QueryColorDatabase("#000000",&oval_image->background_color,exception);
5299   (void) SetImageBackgroundColor(oval_image);
5300   draw_info=CloneDrawInfo((const ImageInfo *) NULL,(const DrawInfo *) NULL);
5301   (void) QueryColorDatabase("#ffffff",&draw_info->fill,exception);
5302   (void) QueryColorDatabase("#ffffff",&draw_info->stroke,exception);
5303   (void) FormatLocaleString(ellipse,MaxTextExtent,
5304     "ellipse %g,%g,%g,%g,0.0,360.0",image->columns/2.0,
5305     image->rows/2.0,image->columns/2.0-x,image->rows/2.0-y);
5306   draw_info->primitive=AcquireString(ellipse);
5307   (void) DrawImage(oval_image,draw_info,exception);
5308   draw_info=DestroyDrawInfo(draw_info);
5309   blur_image=BlurImage(oval_image,radius,sigma,image->bias,exception);
5310   oval_image=DestroyImage(oval_image);
5311   if (blur_image == (Image *) NULL)
5312     {
5313       canvas_image=DestroyImage(canvas_image);
5314       return((Image *) NULL);
5315     }
5316   blur_image->matte=MagickFalse;
5317   (void) CompositeImage(canvas_image,CopyOpacityCompositeOp,blur_image,0,0);
5318   blur_image=DestroyImage(blur_image);
5319   vignette_image=MergeImageLayers(canvas_image,FlattenLayer,exception);
5320   canvas_image=DestroyImage(canvas_image);
5321   return(vignette_image);
5322 }
5323 \f
5324 /*
5325 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5326 %                                                                             %
5327 %                                                                             %
5328 %                                                                             %
5329 %     W a v e I m a g e                                                       %
5330 %                                                                             %
5331 %                                                                             %
5332 %                                                                             %
5333 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5334 %
5335 %  WaveImage() creates a "ripple" effect in the image by shifting the pixels
5336 %  vertically along a sine wave whose amplitude and wavelength is specified
5337 %  by the given parameters.
5338 %
5339 %  The format of the WaveImage method is:
5340 %
5341 %      Image *WaveImage(const Image *image,const double amplitude,
5342 %        const double wave_length,const PixelInterpolateMethod method,
5343 %        ExceptionInfo *exception)
5344 %
5345 %  A description of each parameter follows:
5346 %
5347 %    o image: the image.
5348 %
5349 %    o amplitude, wave_length:  Define the amplitude and wave length of the
5350 %      sine wave.
5351 %
5352 %    o interpolate: the pixel interpolation method.
5353 %
5354 %    o exception: return any errors or warnings in this structure.
5355 %
5356 */
5357 MagickExport Image *WaveImage(const Image *image,const double amplitude,
5358   const double wave_length,const PixelInterpolateMethod method,
5359   ExceptionInfo *exception)
5360 {
5361 #define WaveImageTag  "Wave/Image"
5362
5363   CacheView
5364     *image_view,
5365     *wave_view;
5366
5367   Image
5368     *wave_image;
5369
5370   MagickBooleanType
5371     status;
5372
5373   MagickOffsetType
5374     progress;
5375
5376   MagickRealType
5377     *sine_map;
5378
5379   register ssize_t
5380     i;
5381
5382   ssize_t
5383     y;
5384
5385   /*
5386     Initialize wave image attributes.
5387   */
5388   assert(image != (Image *) NULL);
5389   assert(image->signature == MagickSignature);
5390   if (image->debug != MagickFalse)
5391     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5392   assert(exception != (ExceptionInfo *) NULL);
5393   assert(exception->signature == MagickSignature);
5394   wave_image=CloneImage(image,image->columns,(size_t) (image->rows+2.0*
5395     fabs(amplitude)),MagickTrue,exception);
5396   if (wave_image == (Image *) NULL)
5397     return((Image *) NULL);
5398   if (SetImageStorageClass(wave_image,DirectClass,exception) == MagickFalse)
5399     {
5400       wave_image=DestroyImage(wave_image);
5401       return((Image *) NULL);
5402     }
5403   if (wave_image->background_color.alpha != OpaqueAlpha)
5404     wave_image->matte=MagickTrue;
5405   /*
5406     Allocate sine map.
5407   */
5408   sine_map=(MagickRealType *) AcquireQuantumMemory((size_t) wave_image->columns,
5409     sizeof(*sine_map));
5410   if (sine_map == (MagickRealType *) NULL)
5411     {
5412       wave_image=DestroyImage(wave_image);
5413       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
5414     }
5415   for (i=0; i < (ssize_t) wave_image->columns; i++)
5416     sine_map[i]=fabs(amplitude)+amplitude*sin((double) ((2.0*MagickPI*i)/
5417       wave_length));
5418   /*
5419     Wave image.
5420   */
5421   status=MagickTrue;
5422   progress=0;
5423   image_view=AcquireCacheView(image);
5424   wave_view=AcquireCacheView(wave_image);
5425   (void) SetCacheViewVirtualPixelMethod(image_view,
5426     BackgroundVirtualPixelMethod);
5427 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5428   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
5429 #endif
5430   for (y=0; y < (ssize_t) wave_image->rows; y++)
5431   {
5432     register Quantum
5433       *restrict q;
5434
5435     register ssize_t
5436       x;
5437
5438     if (status == MagickFalse)
5439       continue;
5440     q=QueueCacheViewAuthenticPixels(wave_view,0,y,wave_image->columns,1,
5441       exception);
5442     if (q == (Quantum *) NULL)
5443       {
5444         status=MagickFalse;
5445         continue;
5446       }
5447     for (x=0; x < (ssize_t) wave_image->columns; x++)
5448     {
5449       status=InterpolatePixelChannels(image,image_view,wave_image,method,
5450         (double) x,(double) (y-sine_map[x]),q,exception);
5451       q+=GetPixelChannels(wave_image);
5452     }
5453     if (SyncCacheViewAuthenticPixels(wave_view,exception) == MagickFalse)
5454       status=MagickFalse;
5455     if (image->progress_monitor != (MagickProgressMonitor) NULL)
5456       {
5457         MagickBooleanType
5458           proceed;
5459
5460 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5461   #pragma omp critical (MagickCore_WaveImage)
5462 #endif
5463         proceed=SetImageProgress(image,WaveImageTag,progress++,image->rows);
5464         if (proceed == MagickFalse)
5465           status=MagickFalse;
5466       }
5467   }
5468   wave_view=DestroyCacheView(wave_view);
5469   image_view=DestroyCacheView(image_view);
5470   sine_map=(MagickRealType *) RelinquishMagickMemory(sine_map);
5471   if (status == MagickFalse)
5472     wave_image=DestroyImage(wave_image);
5473   return(wave_image);
5474 }