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