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