• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                                 FFFFF  X   X                                %
7 %                                 F       X X                                 %
8 %                                 FFF      X                                  %
9 %                                 F       X X                                 %
10 %                                 F      X   X                                %
11 %                                                                             %
12 %                                                                             %
13 %                   MagickCore Image Special Effects Methods                  %
14 %                                                                             %
15 %                               Software Design                               %
16 %                                    Cristy                                   %
17 %                                 October 1996                                %
18 %                                                                             %
19 %                                                                             %
20 %                                                                             %
21 %  Copyright 1999-2021 ImageMagick Studio LLC, a non-profit organization      %
22 %  dedicated to making software imaging solutions freely available.           %
23 %                                                                             %
24 %  You may not use this file except in compliance with the License.  You may  %
25 %  obtain a copy of the License at                                            %
26 %                                                                             %
27 %    https://imagemagick.org/script/license.php                               %
28 %                                                                             %
29 %  Unless required by applicable law or agreed to in writing, software        %
30 %  distributed under the License is distributed on an "AS IS" BASIS,          %
31 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
32 %  See the License for the specific language governing permissions and        %
33 %  limitations under the License.                                             %
34 %                                                                             %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36 %
37 %
38 %
39 */
40 
41 /*
42   Include declarations.
43 */
44 #include "MagickCore/studio.h"
45 #include "MagickCore/accelerate-private.h"
46 #include "MagickCore/annotate.h"
47 #include "MagickCore/artifact.h"
48 #include "MagickCore/attribute.h"
49 #include "MagickCore/cache.h"
50 #include "MagickCore/cache-view.h"
51 #include "MagickCore/channel.h"
52 #include "MagickCore/color.h"
53 #include "MagickCore/color-private.h"
54 #include "MagickCore/colorspace-private.h"
55 #include "MagickCore/composite.h"
56 #include "MagickCore/decorate.h"
57 #include "MagickCore/distort.h"
58 #include "MagickCore/draw.h"
59 #include "MagickCore/effect.h"
60 #include "MagickCore/enhance.h"
61 #include "MagickCore/exception.h"
62 #include "MagickCore/exception-private.h"
63 #include "MagickCore/fx.h"
64 #include "MagickCore/fx-private.h"
65 #include "MagickCore/gem.h"
66 #include "MagickCore/gem-private.h"
67 #include "MagickCore/geometry.h"
68 #include "MagickCore/layer.h"
69 #include "MagickCore/list.h"
70 #include "MagickCore/log.h"
71 #include "MagickCore/image.h"
72 #include "MagickCore/image-private.h"
73 #include "MagickCore/magick.h"
74 #include "MagickCore/memory_.h"
75 #include "MagickCore/memory-private.h"
76 #include "MagickCore/monitor.h"
77 #include "MagickCore/monitor-private.h"
78 #include "MagickCore/option.h"
79 #include "MagickCore/pixel.h"
80 #include "MagickCore/pixel-accessor.h"
81 #include "MagickCore/property.h"
82 #include "MagickCore/quantum.h"
83 #include "MagickCore/quantum-private.h"
84 #include "MagickCore/random_.h"
85 #include "MagickCore/random-private.h"
86 #include "MagickCore/resample.h"
87 #include "MagickCore/resample-private.h"
88 #include "MagickCore/resize.h"
89 #include "MagickCore/resource_.h"
90 #include "MagickCore/splay-tree.h"
91 #include "MagickCore/statistic.h"
92 #include "MagickCore/string_.h"
93 #include "MagickCore/string-private.h"
94 #include "MagickCore/thread-private.h"
95 #include "MagickCore/threshold.h"
96 #include "MagickCore/token.h"
97 #include "MagickCore/transform.h"
98 #include "MagickCore/transform-private.h"
99 #include "MagickCore/utility.h"
100 
101 /*
102   Typedef declarations.
103 */
104 typedef enum
105 {
106   BitwiseAndAssignmentOperator = 0xd9U,
107   BitwiseOrAssignmentOperator,
108   LeftShiftAssignmentOperator,
109   RightShiftAssignmentOperator,
110   PowerAssignmentOperator,
111   ModuloAssignmentOperator,
112   PlusAssignmentOperator,
113   SubtractAssignmentOperator,
114   MultiplyAssignmentOperator,
115   DivideAssignmentOperator,
116   IncrementAssignmentOperator,
117   DecrementAssignmentOperator,
118   LeftShiftOperator,
119   RightShiftOperator,
120   LessThanEqualOperator,
121   GreaterThanEqualOperator,
122   EqualOperator,
123   NotEqualOperator,
124   LogicalAndOperator,
125   LogicalOrOperator,
126   ExponentialNotation
127 } FxOperator;
128 
129 struct _FxInfo
130 {
131   const Image
132     *images;
133 
134   char
135     *expression;
136 
137   FILE
138     *file;
139 
140   SplayTreeInfo
141     *colors,
142     *symbols;
143 
144   CacheView
145     **view;
146 
147   RandomInfo
148     *random_info;
149 
150   ExceptionInfo
151     *exception;
152 };
153 
154 /*
155 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
156 %                                                                             %
157 %                                                                             %
158 %                                                                             %
159 +   A c q u i r e F x I n f o                                                 %
160 %                                                                             %
161 %                                                                             %
162 %                                                                             %
163 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
164 %
165 %  AcquireFxInfo() allocates the FxInfo structure.
166 %
167 %  The format of the AcquireFxInfo method is:
168 %
169 %      FxInfo *AcquireFxInfo(Image *images,const char *expression,
170 %        ExceptionInfo *exception)
171 %
172 %  A description of each parameter follows:
173 %
174 %    o images: the image sequence.
175 %
176 %    o expression: the expression.
177 %
178 %    o exception: return any errors or warnings in this structure.
179 %
180 */
AcquireFxInfo(const Image * images,const char * expression,ExceptionInfo * exception)181 MagickPrivate FxInfo *AcquireFxInfo(const Image *images,const char *expression,
182   ExceptionInfo *exception)
183 {
184   const Image
185     *next;
186 
187   FxInfo
188     *fx_info;
189 
190   ssize_t
191     i;
192 
193   unsigned char
194     fx_op[2];
195 
196   fx_info=(FxInfo *) AcquireCriticalMemory(sizeof(*fx_info));
197   (void) memset(fx_info,0,sizeof(*fx_info));
198   fx_info->exception=AcquireExceptionInfo();
199   fx_info->images=images;
200   fx_info->colors=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
201     RelinquishMagickMemory);
202   fx_info->symbols=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
203     RelinquishMagickMemory);
204   fx_info->view=(CacheView **) AcquireQuantumMemory(GetImageListLength(
205     fx_info->images),sizeof(*fx_info->view));
206   if (fx_info->view == (CacheView **) NULL)
207     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
208   i=0;
209   next=GetFirstImageInList(fx_info->images);
210   for ( ; next != (Image *) NULL; next=next->next)
211   {
212     fx_info->view[i]=AcquireVirtualCacheView(next,exception);
213     i++;
214   }
215   fx_info->random_info=AcquireRandomInfo();
216   fx_info->expression=ConstantString(expression);
217   fx_info->file=stderr;
218   /*
219     Convert compound to simple operators.
220   */
221   fx_op[1]='\0';
222   *fx_op=(unsigned char) BitwiseAndAssignmentOperator;
223   (void) SubstituteString(&fx_info->expression,"&=",(char *) fx_op);
224   *fx_op=(unsigned char) BitwiseOrAssignmentOperator;
225   (void) SubstituteString(&fx_info->expression,"|=",(char *) fx_op);
226   *fx_op=(unsigned char) LeftShiftAssignmentOperator;
227   (void) SubstituteString(&fx_info->expression,"<<=",(char *) fx_op);
228   *fx_op=(unsigned char) RightShiftAssignmentOperator;
229   (void) SubstituteString(&fx_info->expression,">>=",(char *) fx_op);
230   *fx_op=(unsigned char) PowerAssignmentOperator;
231   (void) SubstituteString(&fx_info->expression,"^=",(char *) fx_op);
232   *fx_op=(unsigned char) ModuloAssignmentOperator;
233   (void) SubstituteString(&fx_info->expression,"%=",(char *) fx_op);
234   *fx_op=(unsigned char) PlusAssignmentOperator;
235   (void) SubstituteString(&fx_info->expression,"+=",(char *) fx_op);
236   *fx_op=(unsigned char) SubtractAssignmentOperator;
237   (void) SubstituteString(&fx_info->expression,"-=",(char *) fx_op);
238   *fx_op=(unsigned char) MultiplyAssignmentOperator;
239   (void) SubstituteString(&fx_info->expression,"*=",(char *) fx_op);
240   *fx_op=(unsigned char) DivideAssignmentOperator;
241   (void) SubstituteString(&fx_info->expression,"/=",(char *) fx_op);
242   *fx_op=(unsigned char) IncrementAssignmentOperator;
243   (void) SubstituteString(&fx_info->expression,"++",(char *) fx_op);
244   *fx_op=(unsigned char) DecrementAssignmentOperator;
245   (void) SubstituteString(&fx_info->expression,"--",(char *) fx_op);
246   *fx_op=(unsigned char) LeftShiftOperator;
247   (void) SubstituteString(&fx_info->expression,"<<",(char *) fx_op);
248   *fx_op=(unsigned char) RightShiftOperator;
249   (void) SubstituteString(&fx_info->expression,">>",(char *) fx_op);
250   *fx_op=(unsigned char) LessThanEqualOperator;
251   (void) SubstituteString(&fx_info->expression,"<=",(char *) fx_op);
252   *fx_op=(unsigned char) GreaterThanEqualOperator;
253   (void) SubstituteString(&fx_info->expression,">=",(char *) fx_op);
254   *fx_op=(unsigned char) EqualOperator;
255   (void) SubstituteString(&fx_info->expression,"==",(char *) fx_op);
256   *fx_op=(unsigned char) NotEqualOperator;
257   (void) SubstituteString(&fx_info->expression,"!=",(char *) fx_op);
258   *fx_op=(unsigned char) LogicalAndOperator;
259   (void) SubstituteString(&fx_info->expression,"&&",(char *) fx_op);
260   *fx_op=(unsigned char) LogicalOrOperator;
261   (void) SubstituteString(&fx_info->expression,"||",(char *) fx_op);
262   *fx_op=(unsigned char) ExponentialNotation;
263   (void) SubstituteString(&fx_info->expression,"**",(char *) fx_op);
264   /*
265     Force right-to-left associativity for unary negation.
266   */
267   (void) SubstituteString(&fx_info->expression,"-","-1.0*");
268   (void) SubstituteString(&fx_info->expression,"^-1.0*","^-");
269   (void) SubstituteString(&fx_info->expression,"E-1.0*","E-");
270   (void) SubstituteString(&fx_info->expression,"e-1.0*","e-");
271   (void) SubstituteString(&fx_info->expression," ","");  /* compact string */
272   return(fx_info);
273 }
274 
275 /*
276 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
277 %                                                                             %
278 %                                                                             %
279 %                                                                             %
280 +   D e s t r o y F x I n f o                                                 %
281 %                                                                             %
282 %                                                                             %
283 %                                                                             %
284 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
285 %
286 %  DestroyFxInfo() deallocates memory associated with an FxInfo structure.
287 %
288 %  The format of the DestroyFxInfo method is:
289 %
290 %      ImageInfo *DestroyFxInfo(ImageInfo *fx_info)
291 %
292 %  A description of each parameter follows:
293 %
294 %    o fx_info: the fx info.
295 %
296 */
DestroyFxInfo(FxInfo * fx_info)297 MagickPrivate FxInfo *DestroyFxInfo(FxInfo *fx_info)
298 {
299   ssize_t
300     i;
301 
302   fx_info->exception=DestroyExceptionInfo(fx_info->exception);
303   fx_info->expression=DestroyString(fx_info->expression);
304   fx_info->symbols=DestroySplayTree(fx_info->symbols);
305   fx_info->colors=DestroySplayTree(fx_info->colors);
306   for (i=(ssize_t) GetImageListLength(fx_info->images)-1; i >= 0; i--)
307     fx_info->view[i]=DestroyCacheView(fx_info->view[i]);
308   fx_info->view=(CacheView **) RelinquishMagickMemory(fx_info->view);
309   fx_info->random_info=DestroyRandomInfo(fx_info->random_info);
310   fx_info=(FxInfo *) RelinquishMagickMemory(fx_info);
311   return(fx_info);
312 }
313 
314 /*
315 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
316 %                                                                             %
317 %                                                                             %
318 %                                                                             %
319 +     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                   %
320 %                                                                             %
321 %                                                                             %
322 %                                                                             %
323 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
324 %
325 %  FxEvaluateChannelExpression() evaluates an expression and returns the
326 %  results.
327 %
328 %  The format of the FxEvaluateExpression method is:
329 %
330 %      double FxEvaluateChannelExpression(FxInfo *fx_info,
331 %        const PixelChannel channel,const ssize_t x,const ssize_t y,
332 %        double *alpha,Exceptioninfo *exception)
333 %      double FxEvaluateExpression(FxInfo *fx_info,
334 %        double *alpha,Exceptioninfo *exception)
335 %
336 %  A description of each parameter follows:
337 %
338 %    o fx_info: the fx info.
339 %
340 %    o channel: the channel.
341 %
342 %    o x,y: the pixel position.
343 %
344 %    o alpha: the result.
345 %
346 %    o exception: return any errors or warnings in this structure.
347 %
348 */
349 
GetFxSymbolValue(FxInfo * magick_restrict fx_info,const char * symbol)350 static inline const double *GetFxSymbolValue(FxInfo *magick_restrict fx_info,
351   const char *symbol)
352 {
353   return((const double *) GetValueFromSplayTree(fx_info->symbols,symbol));
354 }
355 
SetFxSymbolValue(FxInfo * magick_restrict fx_info,const char * magick_restrict symbol,double const value)356 static inline MagickBooleanType SetFxSymbolValue(
357   FxInfo *magick_restrict fx_info,const char *magick_restrict symbol,
358   double const value)
359 {
360   double
361     *object;
362 
363   object=(double *) GetValueFromSplayTree(fx_info->symbols,symbol);
364   if (object != (double *) NULL)
365     {
366       *object=value;
367       return(MagickTrue);
368     }
369   object=(double *) AcquireMagickMemory(sizeof(*object));
370   if (object == (double *) NULL)
371     {
372       (void) ThrowMagickException(fx_info->exception,GetMagickModule(),
373         ResourceLimitError,"MemoryAllocationFailed","`%s'",
374         fx_info->images->filename);
375       return(MagickFalse);
376     }
377   *object=value;
378   return(AddValueToSplayTree(fx_info->symbols,ConstantString(symbol),object));
379 }
380 
FxChannelStatistics(FxInfo * fx_info,Image * image,PixelChannel channel,const char * symbol,ExceptionInfo * exception)381 static double FxChannelStatistics(FxInfo *fx_info,Image *image,
382   PixelChannel channel,const char *symbol,ExceptionInfo *exception)
383 {
384   ChannelType
385     channel_mask;
386 
387   char
388     key[MagickPathExtent];
389 
390   const double
391     *value;
392 
393   double
394     statistic;
395 
396   const char
397     *p;
398 
399   channel_mask=UndefinedChannel;
400   for (p=symbol; (*p != '.') && (*p != '\0'); p++) ;
401   if (*p == '.')
402     {
403       ssize_t
404         option;
405 
406       option=ParseCommandOption(MagickPixelChannelOptions,MagickTrue,p+1);
407       if (option >= 0)
408         {
409           channel=(PixelChannel) option;
410           channel_mask=SetPixelChannelMask(image,(ChannelType)
411             (1UL << channel));
412         }
413     }
414   (void) FormatLocaleString(key,MagickPathExtent,"%p.%.20g.%s",(void *) image,
415     (double) channel,symbol);
416   value=GetFxSymbolValue(fx_info,key);
417   if (value != (const double *) NULL)
418     {
419       if (channel_mask != UndefinedChannel)
420         (void) SetPixelChannelMask(image,channel_mask);
421       return(QuantumScale*(*value));
422     }
423   statistic=0.0;
424   if (LocaleNCompare(symbol,"depth",5) == 0)
425     {
426       size_t
427         depth;
428 
429       depth=GetImageDepth(image,exception);
430       statistic=(double) depth;
431     }
432   if (LocaleNCompare(symbol,"kurtosis",8) == 0)
433     {
434       double
435         kurtosis,
436         skewness;
437 
438       (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
439       statistic=kurtosis;
440     }
441   if (LocaleNCompare(symbol,"maxima",6) == 0)
442     {
443       double
444         maxima,
445         minima;
446 
447       (void) GetImageRange(image,&minima,&maxima,exception);
448       statistic=maxima;
449     }
450   if (LocaleNCompare(symbol,"mean",4) == 0)
451     {
452       double
453         mean,
454         standard_deviation;
455 
456       (void) GetImageMean(image,&mean,&standard_deviation,exception);
457       statistic=mean;
458     }
459   if (LocaleNCompare(symbol,"median",6) == 0)
460     {
461       double
462         median;
463 
464       (void) GetImageMedian(image,&median,exception);
465       statistic=median;
466     }
467   if (LocaleNCompare(symbol,"minima",6) == 0)
468     {
469       double
470         maxima,
471         minima;
472 
473       (void) GetImageRange(image,&minima,&maxima,exception);
474       statistic=minima;
475     }
476   if (LocaleNCompare(symbol,"skewness",8) == 0)
477     {
478       double
479         kurtosis,
480         skewness;
481 
482       (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
483       statistic=skewness;
484     }
485   if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
486     {
487       double
488         mean,
489         standard_deviation;
490 
491       (void) GetImageMean(image,&mean,&standard_deviation,exception);
492       statistic=standard_deviation;
493     }
494   if (channel_mask != UndefinedChannel)
495     (void) SetPixelChannelMask(image,channel_mask);
496   if (SetFxSymbolValue(fx_info,key,statistic) == MagickFalse)
497     return(0.0);
498   return(QuantumScale*statistic);
499 }
500 
501 static double
502   FxEvaluateSubexpression(FxInfo *,const PixelChannel,const ssize_t,
503     const ssize_t,const char *,const size_t,double *,ExceptionInfo *);
504 
IsFxFunction(const char * expression,const char * name,const size_t length)505 static inline MagickBooleanType IsFxFunction(const char *expression,
506   const char *name,const size_t length)
507 {
508   int
509     c;
510 
511   size_t
512     i;
513 
514   for (i=0; i <= length; i++)
515     if (expression[i] == '\0')
516       return(MagickFalse);
517   c=expression[length];
518   if ((LocaleNCompare(expression,name,length) == 0) &&
519       ((isspace((int) ((unsigned char) c)) == 0) || (c == '(')))
520     return(MagickTrue);
521   return(MagickFalse);
522 }
523 
FxGCD(const double alpha,const double beta)524 static inline double FxGCD(const double alpha,const double beta)
525 {
526   if (alpha < beta)
527     return(FxGCD(beta,alpha));
528   if (fabs(beta) < 0.001)
529     return(alpha);
530   return(FxGCD(beta,alpha-beta*floor(alpha/beta)));
531 }
532 
FxSubexpression(const char * expression,ExceptionInfo * exception)533 static inline const char *FxSubexpression(const char *expression,
534   ExceptionInfo *exception)
535 {
536   const char
537     *subexpression;
538 
539   ssize_t
540     level;
541 
542   level=0;
543   subexpression=expression;
544   while ((*subexpression != '\0') &&
545          ((level != 1) || (strchr(")",(int) *subexpression) == (char *) NULL)))
546   {
547     if (strchr("(",(int) *subexpression) != (char *) NULL)
548       level++;
549     else
550       if (strchr(")",(int) *subexpression) != (char *) NULL)
551         level--;
552     subexpression++;
553   }
554   if (*subexpression == '\0')
555     (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
556       "UnbalancedParenthesis","`%s'",expression);
557   return(subexpression);
558 }
559 
FxGetSymbol(FxInfo * fx_info,const PixelChannel channel,const ssize_t x,const ssize_t y,const char * expression,const size_t depth,ExceptionInfo * exception)560 static double FxGetSymbol(FxInfo *fx_info,const PixelChannel channel,
561   const ssize_t x,const ssize_t y,const char *expression,const size_t depth,
562   ExceptionInfo *exception)
563 {
564   char
565     *q,
566     symbol[MagickPathExtent];
567 
568   const char
569     *artifact,
570     *p;
571 
572   const double
573     *value;
574 
575   double
576     alpha,
577     beta;
578 
579   Image
580     *image;
581 
582   MagickBooleanType
583     status;
584 
585   PixelInfo
586     pixel;
587 
588   PointInfo
589     point;
590 
591   ssize_t
592     i;
593 
594   size_t
595     level;
596 
597   p=expression;
598   i=GetImageIndexInList(fx_info->images);
599   level=0;
600   point.x=(double) x;
601   point.y=(double) y;
602   if (isalpha((int) ((unsigned char) *(p+1))) == 0)
603     {
604       char
605         *subexpression;
606 
607       subexpression=AcquireString(expression);
608       if (strchr("suv",(int) *p) != (char *) NULL)
609         {
610           switch (*p)
611           {
612             case 's':
613             default:
614             {
615               i=GetImageIndexInList(fx_info->images);
616               break;
617             }
618             case 'u': i=0; break;
619             case 'v': i=1; break;
620           }
621           p++;
622           if (*p == '[')
623             {
624               level++;
625               q=subexpression;
626               for (p++; *p != '\0'; )
627               {
628                 if (*p == '[')
629                   level++;
630                 else
631                   if (*p == ']')
632                     {
633                       level--;
634                       if (level == 0)
635                         break;
636                     }
637                 *q++=(*p++);
638               }
639               *q='\0';
640               alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
641                 depth,&beta,exception);
642               i=(ssize_t) alpha;
643               if (*p != '\0')
644                 p++;
645             }
646           if (*p == '.')
647             p++;
648         }
649       if ((*p == 'p') && (isalpha((int) ((unsigned char) *(p+1))) == 0))
650         {
651           p++;
652           if (*p == '{')
653             {
654               level++;
655               q=subexpression;
656               for (p++; *p != '\0'; )
657               {
658                 if (*p == '{')
659                   level++;
660                 else
661                   if (*p == '}')
662                     {
663                       level--;
664                       if (level == 0)
665                         break;
666                     }
667                 *q++=(*p++);
668               }
669               *q='\0';
670               alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
671                 depth,&beta,exception);
672               point.x=alpha;
673               point.y=beta;
674               if (*p != '\0')
675                 p++;
676             }
677           else
678             if (*p == '[')
679               {
680                 level++;
681                 q=subexpression;
682                 for (p++; *p != '\0'; )
683                 {
684                   if (*p == '[')
685                     level++;
686                   else
687                     if (*p == ']')
688                       {
689                         level--;
690                         if (level == 0)
691                           break;
692                       }
693                   *q++=(*p++);
694                 }
695                 *q='\0';
696                 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
697                   depth,&beta,exception);
698                 point.x+=alpha;
699                 point.y+=beta;
700                 if (*p != '\0')
701                   p++;
702               }
703           if (*p == '.')
704             p++;
705         }
706       subexpression=DestroyString(subexpression);
707     }
708   image=GetImageFromList(fx_info->images,i);
709   if (image == (Image *) NULL)
710     {
711       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
712         "NoSuchImage","`%s'",expression);
713       return(0.0);
714     }
715   i=GetImageIndexInList(image);
716   GetPixelInfo(image,&pixel);
717   status=InterpolatePixelInfo(image,fx_info->view[i],image->interpolate,
718     point.x,point.y,&pixel,exception);
719   (void) status;
720   if ((*p != '\0') && (*(p+1) != '\0') && (*(p+2) != '\0') &&
721       (LocaleCompare(p,"intensity") != 0) && (LocaleCompare(p,"luma") != 0) &&
722       (LocaleCompare(p,"luminance") != 0) && (LocaleCompare(p,"hue") != 0) &&
723       (LocaleCompare(p,"saturation") != 0) &&
724       (LocaleCompare(p,"lightness") != 0))
725     {
726       char
727         name[MagickPathExtent];
728 
729       size_t
730         length;
731 
732       (void) CopyMagickString(name,p,MagickPathExtent);
733       length=strlen(name);
734       for (q=name+length-1; q > name; q--)
735       {
736         if (*q == ')')
737           break;
738         if (*q == '.')
739           {
740             *q='\0';
741             break;
742           }
743       }
744       q=name;
745       if ((*q != '\0') && (*(q+1) != '\0') && (*(q+2) != '\0') &&
746           (GetFxSymbolValue(fx_info,name) == (const double *) NULL))
747         {
748           PixelInfo
749             *color;
750 
751           color=(PixelInfo *) GetValueFromSplayTree(fx_info->colors,name);
752           if (color != (PixelInfo *) NULL)
753             {
754               pixel=(*color);
755               p+=length;
756             }
757           else
758             {
759               MagickBooleanType
760                 status;
761 
762               status=QueryColorCompliance(name,AllCompliance,&pixel,
763                 fx_info->exception);
764               if (status != MagickFalse)
765                 {
766                   (void) AddValueToSplayTree(fx_info->colors,
767                     ConstantString(name),ClonePixelInfo(&pixel));
768                   p+=length;
769                 }
770             }
771         }
772     }
773   (void) CopyMagickString(symbol,p,MagickPathExtent);
774   StripString(symbol);
775   if (*symbol == '\0')
776     {
777       switch (channel)
778       {
779         case RedPixelChannel: return(QuantumScale*pixel.red);
780         case GreenPixelChannel: return(QuantumScale*pixel.green);
781         case BluePixelChannel: return(QuantumScale*pixel.blue);
782         case BlackPixelChannel:
783         {
784           if (image->colorspace != CMYKColorspace)
785             {
786               (void) ThrowMagickException(exception,GetMagickModule(),
787                 ImageError,"ColorSeparatedImageRequired","`%s'",
788                 image->filename);
789               return(0.0);
790             }
791           return(QuantumScale*pixel.black);
792         }
793         case AlphaPixelChannel:
794         {
795           if (pixel.alpha_trait == UndefinedPixelTrait)
796             return(1.0);
797           alpha=(double) (QuantumScale*pixel.alpha);
798           return(alpha);
799         }
800         case CompositePixelChannel:
801         {
802           Quantum
803             quantum_pixel[MaxPixelChannels];
804 
805           SetPixelViaPixelInfo(image,&pixel,quantum_pixel);
806           return(QuantumScale*GetPixelIntensity(image,quantum_pixel));
807         }
808         case IndexPixelChannel:
809           return(0.0);
810         default:
811           break;
812       }
813       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
814         "UnableToParseExpression","`%s'",p);
815       return(0.0);
816     }
817   switch (*symbol)
818   {
819     case 'A':
820     case 'a':
821     {
822       if (LocaleCompare(symbol,"a") == 0)
823         return((QuantumScale*pixel.alpha));
824       break;
825     }
826     case 'B':
827     case 'b':
828     {
829       if (LocaleCompare(symbol,"b") == 0)
830         return(QuantumScale*pixel.blue);
831       break;
832     }
833     case 'C':
834     case 'c':
835     {
836       if (IsFxFunction(symbol,"channel",7) != MagickFalse)
837         {
838           GeometryInfo
839             channel_info;
840 
841           MagickStatusType
842             flags;
843 
844           flags=ParseGeometry(symbol+7,&channel_info);
845           if (image->colorspace == CMYKColorspace)
846             switch (channel)
847             {
848               case CyanPixelChannel:
849               {
850                 if ((flags & RhoValue) == 0)
851                   return(0.0);
852                 return(channel_info.rho);
853               }
854               case MagentaPixelChannel:
855               {
856                 if ((flags & SigmaValue) == 0)
857                   return(0.0);
858                 return(channel_info.sigma);
859               }
860               case YellowPixelChannel:
861               {
862                 if ((flags & XiValue) == 0)
863                   return(0.0);
864                 return(channel_info.xi);
865               }
866               case BlackPixelChannel:
867               {
868                 if ((flags & PsiValue) == 0)
869                   return(0.0);
870                 return(channel_info.psi);
871               }
872               case AlphaPixelChannel:
873               {
874                 if ((flags & ChiValue) == 0)
875                   return(0.0);
876                 return(channel_info.chi);
877               }
878               default:
879                 return(0.0);
880             }
881           switch (channel)
882           {
883             case RedPixelChannel:
884             {
885               if ((flags & RhoValue) == 0)
886                 return(0.0);
887               return(channel_info.rho);
888             }
889             case GreenPixelChannel:
890             {
891               if ((flags & SigmaValue) == 0)
892                 return(0.0);
893               return(channel_info.sigma);
894             }
895             case BluePixelChannel:
896             {
897               if ((flags & XiValue) == 0)
898                 return(0.0);
899               return(channel_info.xi);
900             }
901             case BlackPixelChannel:
902             {
903               if ((flags & ChiValue) == 0)
904                 return(0.0);
905               return(channel_info.chi);
906             }
907             case AlphaPixelChannel:
908             {
909               if ((flags & PsiValue) == 0)
910                 return(0.0);
911               return(channel_info.psi);
912             }
913             default:
914               return(0.0);
915           }
916         }
917       if (LocaleCompare(symbol,"c") == 0)
918         return(QuantumScale*pixel.red);
919       break;
920     }
921     case 'D':
922     case 'd':
923     {
924       if (LocaleNCompare(symbol,"depth",5) == 0)
925         return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
926       break;
927     }
928     case 'E':
929     case 'e':
930     {
931       if (LocaleCompare(symbol,"extent") == 0)
932         {
933           if (image->extent != 0)
934             return((double) image->extent);
935           return((double) GetBlobSize(image));
936         }
937       break;
938     }
939     case 'G':
940     case 'g':
941     {
942       if (LocaleCompare(symbol,"g") == 0)
943         return(QuantumScale*pixel.green);
944       break;
945     }
946     case 'K':
947     case 'k':
948     {
949       if (LocaleNCompare(symbol,"kurtosis",8) == 0)
950         return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
951       if (LocaleCompare(symbol,"k") == 0)
952         {
953           if (image->colorspace != CMYKColorspace)
954             {
955               (void) ThrowMagickException(exception,GetMagickModule(),
956                 OptionError,"ColorSeparatedImageRequired","`%s'",
957                 image->filename);
958               return(0.0);
959             }
960           return(QuantumScale*pixel.black);
961         }
962       break;
963     }
964     case 'H':
965     case 'h':
966     {
967       if (LocaleCompare(symbol,"h") == 0)
968         return((double) image->rows);
969       if (LocaleCompare(symbol,"hue") == 0)
970         {
971           double
972             hue,
973             lightness,
974             saturation;
975 
976           ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
977             &lightness);
978           return(hue);
979         }
980       break;
981     }
982     case 'I':
983     case 'i':
984     {
985       if ((LocaleCompare(symbol,"image.depth") == 0) ||
986           (LocaleCompare(symbol,"image.minima") == 0) ||
987           (LocaleCompare(symbol,"image.maxima") == 0) ||
988           (LocaleCompare(symbol,"image.mean") == 0) ||
989           (LocaleCompare(symbol,"image.kurtosis") == 0) ||
990           (LocaleCompare(symbol,"image.skewness") == 0) ||
991           (LocaleCompare(symbol,"image.standard_deviation") == 0))
992         return(FxChannelStatistics(fx_info,image,channel,symbol+6,exception));
993       if (LocaleCompare(symbol,"image.resolution.x") == 0)
994         return(image->resolution.x);
995       if (LocaleCompare(symbol,"image.resolution.y") == 0)
996         return(image->resolution.y);
997       if (LocaleCompare(symbol,"intensity") == 0)
998         {
999           Quantum
1000             quantum_pixel[MaxPixelChannels];
1001 
1002           SetPixelViaPixelInfo(image,&pixel,quantum_pixel);
1003           return(QuantumScale*GetPixelIntensity(image,quantum_pixel));
1004         }
1005       if (LocaleCompare(symbol,"i") == 0)
1006         return((double) x);
1007       break;
1008     }
1009     case 'J':
1010     case 'j':
1011     {
1012       if (LocaleCompare(symbol,"j") == 0)
1013         return((double) y);
1014       break;
1015     }
1016     case 'L':
1017     case 'l':
1018     {
1019       if (LocaleCompare(symbol,"lightness") == 0)
1020         {
1021           double
1022             hue,
1023             lightness,
1024             saturation;
1025 
1026           ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
1027             &lightness);
1028           return(lightness);
1029         }
1030       if (LocaleCompare(symbol,"luma") == 0)
1031         {
1032           double
1033             luma;
1034 
1035           luma=0.212656*pixel.red+0.715158*pixel.green+0.072186*pixel.blue;
1036           return(QuantumScale*luma);
1037         }
1038       if (LocaleCompare(symbol,"luminance") == 0)
1039         {
1040           double
1041             luminence;
1042 
1043           luminence=0.212656*pixel.red+0.715158*pixel.green+0.072186*pixel.blue;
1044           return(QuantumScale*luminence);
1045         }
1046       break;
1047     }
1048     case 'M':
1049     case 'm':
1050     {
1051       if (LocaleNCompare(symbol,"maxima",6) == 0)
1052         return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1053       if (LocaleNCompare(symbol,"mean",4) == 0)
1054         return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1055       if (LocaleNCompare(symbol,"median",6) == 0)
1056         return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1057       if (LocaleNCompare(symbol,"minima",6) == 0)
1058         return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1059       if (LocaleCompare(symbol,"m") == 0)
1060         return(QuantumScale*pixel.green);
1061       break;
1062     }
1063     case 'N':
1064     case 'n':
1065     {
1066       if (LocaleCompare(symbol,"n") == 0)
1067         return((double) GetImageListLength(fx_info->images));
1068       break;
1069     }
1070     case 'O':
1071     case 'o':
1072     {
1073       if (LocaleCompare(symbol,"o") == 0)
1074         return(QuantumScale*pixel.alpha);
1075       break;
1076     }
1077     case 'P':
1078     case 'p':
1079     {
1080       if (LocaleCompare(symbol,"page.height") == 0)
1081         return((double) image->page.height);
1082       if (LocaleCompare(symbol,"page.width") == 0)
1083         return((double) image->page.width);
1084       if (LocaleCompare(symbol,"page.x") == 0)
1085         return((double) image->page.x);
1086       if (LocaleCompare(symbol,"page.y") == 0)
1087         return((double) image->page.y);
1088       if (LocaleCompare(symbol,"printsize.x") == 0)
1089         return(PerceptibleReciprocal(image->resolution.x)*image->columns);
1090       if (LocaleCompare(symbol,"printsize.y") == 0)
1091         return(PerceptibleReciprocal(image->resolution.y)*image->rows);
1092       break;
1093     }
1094     case 'Q':
1095     case 'q':
1096     {
1097       if (LocaleCompare(symbol,"quality") == 0)
1098         return((double) image->quality);
1099       break;
1100     }
1101     case 'R':
1102     case 'r':
1103     {
1104       if (LocaleCompare(symbol,"resolution.x") == 0)
1105         return(image->resolution.x);
1106       if (LocaleCompare(symbol,"resolution.y") == 0)
1107         return(image->resolution.y);
1108       if (LocaleCompare(symbol,"r") == 0)
1109         return(QuantumScale*pixel.red);
1110       break;
1111     }
1112     case 'S':
1113     case 's':
1114     {
1115       if (LocaleCompare(symbol,"saturation") == 0)
1116         {
1117           double
1118             hue,
1119             lightness,
1120             saturation;
1121 
1122           ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
1123             &lightness);
1124           return(saturation);
1125         }
1126       if (LocaleNCompare(symbol,"skewness",8) == 0)
1127         return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1128       if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1129         return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1130       break;
1131     }
1132     case 'T':
1133     case 't':
1134     {
1135       if (LocaleCompare(symbol,"t") == 0)
1136         return((double) GetImageIndexInList(fx_info->images));
1137       break;
1138     }
1139     case 'W':
1140     case 'w':
1141     {
1142       if (LocaleCompare(symbol,"w") == 0)
1143         return((double) image->columns);
1144       break;
1145     }
1146     case 'Y':
1147     case 'y':
1148     {
1149       if (LocaleCompare(symbol,"y") == 0)
1150         return(QuantumScale*pixel.blue);
1151       break;
1152     }
1153     case 'Z':
1154     case 'z':
1155     {
1156       if (LocaleCompare(symbol,"z") == 0)
1157         return((double) GetImageDepth(image,fx_info->exception));
1158       break;
1159     }
1160     default:
1161       break;
1162   }
1163   value=GetFxSymbolValue(fx_info,symbol);
1164   if (value != (const double *) NULL)
1165     return(*value);
1166   artifact=GetImageArtifact(image,symbol);
1167   if (artifact != (const char *) NULL)
1168     return(StringToDouble(artifact,(char **) NULL));
1169   (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1170     "UndefinedVariable","`%s'",symbol);
1171   (void) SetFxSymbolValue(fx_info,symbol,0.0);
1172   return(0.0);
1173 }
1174 
FxOperatorPrecedence(const char * expression,ExceptionInfo * exception)1175 static const char *FxOperatorPrecedence(const char *expression,
1176   ExceptionInfo *exception)
1177 {
1178   typedef enum
1179   {
1180     UndefinedPrecedence,
1181     NullPrecedence,
1182     BitwiseComplementPrecedence,
1183     ExponentPrecedence,
1184     ExponentialNotationPrecedence,
1185     MultiplyPrecedence,
1186     AdditionPrecedence,
1187     ShiftPrecedence,
1188     RelationalPrecedence,
1189     EquivalencyPrecedence,
1190     BitwiseAndPrecedence,
1191     BitwiseOrPrecedence,
1192     LogicalAndPrecedence,
1193     LogicalOrPrecedence,
1194     TernaryPrecedence,
1195     AssignmentPrecedence,
1196     CommaPrecedence,
1197     SeparatorPrecedence
1198   } FxPrecedence;
1199 
1200   FxPrecedence
1201     precedence,
1202     target;
1203 
1204   const char
1205     *subexpression;
1206 
1207   int
1208     c;
1209 
1210   size_t
1211     level;
1212 
1213   c=(-1);
1214   level=0;
1215   subexpression=(const char *) NULL;
1216   target=NullPrecedence;
1217   while ((c != '\0') && (*expression != '\0'))
1218   {
1219     precedence=UndefinedPrecedence;
1220     if ((isspace((int) ((unsigned char) *expression)) != 0) || (c == (int) '@'))
1221       {
1222         expression++;
1223         continue;
1224       }
1225     switch (*expression)
1226     {
1227       case 'A':
1228       case 'a':
1229       {
1230 #if defined(MAGICKCORE_HAVE_ACOSH)
1231         if (IsFxFunction(expression,"acosh",5) != MagickFalse)
1232           {
1233             expression+=5;
1234             break;
1235           }
1236 #endif
1237 #if defined(MAGICKCORE_HAVE_ASINH)
1238         if (IsFxFunction(expression,"asinh",5) != MagickFalse)
1239           {
1240             expression+=5;
1241             break;
1242           }
1243 #endif
1244 #if defined(MAGICKCORE_HAVE_ATANH)
1245         if (IsFxFunction(expression,"atanh",5) != MagickFalse)
1246           {
1247             expression+=5;
1248             break;
1249           }
1250 #endif
1251         if (IsFxFunction(expression,"atan2",5) != MagickFalse)
1252           {
1253             expression+=5;
1254             break;
1255           }
1256         break;
1257       }
1258       case 'E':
1259       case 'e':
1260       {
1261         if ((isdigit((int) ((unsigned char) c)) != 0) &&
1262             ((LocaleNCompare(expression,"E+",2) == 0) ||
1263              (LocaleNCompare(expression,"E-",2) == 0)))
1264           {
1265             expression+=2;  /* scientific notation */
1266             break;
1267           }
1268       }
1269       case 'J':
1270       case 'j':
1271       {
1272         if ((IsFxFunction(expression,"j0",2) != MagickFalse) ||
1273             (IsFxFunction(expression,"j1",2) != MagickFalse))
1274           {
1275             expression+=2;
1276             break;
1277           }
1278         break;
1279       }
1280       case '#':
1281       {
1282         while (isxdigit((int) ((unsigned char) *(expression+1))) != 0)
1283           expression++;
1284         break;
1285       }
1286       default:
1287         break;
1288     }
1289     if ((c == (int) '{') || (c == (int) '['))
1290       level++;
1291     else
1292       if ((c == (int) '}') || (c == (int) ']'))
1293         level--;
1294     if (level == 0)
1295       switch ((unsigned char) *expression)
1296       {
1297         case '~':
1298         case '!':
1299         {
1300           precedence=BitwiseComplementPrecedence;
1301           break;
1302         }
1303         case '^':
1304         case '@':
1305         {
1306           precedence=ExponentPrecedence;
1307           break;
1308         }
1309         default:
1310         {
1311           if (((c != 0) && ((isdigit((int) ((unsigned char) c)) != 0) ||
1312                (strchr(")",c) != (char *) NULL))) &&
1313               (((islower((int) ((unsigned char) *expression)) != 0) ||
1314                (strchr("(",(int) ((unsigned char) *expression)) != (char *) NULL)) ||
1315                ((isdigit((int) ((unsigned char) c)) == 0) &&
1316                 (isdigit((int) ((unsigned char) *expression)) != 0))) &&
1317               (strchr("xy",(int) ((unsigned char) *expression)) == (char *) NULL))
1318             precedence=MultiplyPrecedence;
1319           break;
1320         }
1321         case '*':
1322         case '/':
1323         case '%':
1324         {
1325           precedence=MultiplyPrecedence;
1326           break;
1327         }
1328         case '+':
1329         case '-':
1330         {
1331           if ((strchr("(+-/*%:&^|<>~,",c) == (char *) NULL) ||
1332               (isalpha((int) ((unsigned char) c)) != 0))
1333             precedence=AdditionPrecedence;
1334           break;
1335         }
1336         case BitwiseAndAssignmentOperator:
1337         case BitwiseOrAssignmentOperator:
1338         case LeftShiftAssignmentOperator:
1339         case RightShiftAssignmentOperator:
1340         case PowerAssignmentOperator:
1341         case ModuloAssignmentOperator:
1342         case PlusAssignmentOperator:
1343         case SubtractAssignmentOperator:
1344         case MultiplyAssignmentOperator:
1345         case DivideAssignmentOperator:
1346         case IncrementAssignmentOperator:
1347         case DecrementAssignmentOperator:
1348         {
1349           precedence=AssignmentPrecedence;
1350           break;
1351         }
1352         case LeftShiftOperator:
1353         case RightShiftOperator:
1354         {
1355           precedence=ShiftPrecedence;
1356           break;
1357         }
1358         case '<':
1359         case LessThanEqualOperator:
1360         case GreaterThanEqualOperator:
1361         case '>':
1362         {
1363           precedence=RelationalPrecedence;
1364           break;
1365         }
1366         case EqualOperator:
1367         case NotEqualOperator:
1368         {
1369           precedence=EquivalencyPrecedence;
1370           break;
1371         }
1372         case '&':
1373         {
1374           precedence=BitwiseAndPrecedence;
1375           break;
1376         }
1377         case '|':
1378         {
1379           precedence=BitwiseOrPrecedence;
1380           break;
1381         }
1382         case LogicalAndOperator:
1383         {
1384           precedence=LogicalAndPrecedence;
1385           break;
1386         }
1387         case LogicalOrOperator:
1388         {
1389           precedence=LogicalOrPrecedence;
1390           break;
1391         }
1392         case ExponentialNotation:
1393         {
1394           precedence=ExponentialNotationPrecedence;
1395           break;
1396         }
1397         case ':':
1398         case '?':
1399         {
1400           precedence=TernaryPrecedence;
1401           break;
1402         }
1403         case '=':
1404         {
1405           precedence=AssignmentPrecedence;
1406           break;
1407         }
1408         case ',':
1409         {
1410           precedence=CommaPrecedence;
1411           break;
1412         }
1413         case ';':
1414         {
1415           precedence=SeparatorPrecedence;
1416           break;
1417         }
1418       }
1419     if ((precedence == BitwiseComplementPrecedence) ||
1420         (precedence == TernaryPrecedence) ||
1421         (precedence == AssignmentPrecedence))
1422       {
1423         if (precedence > target)
1424           {
1425             /*
1426               Right-to-left associativity.
1427             */
1428             target=precedence;
1429             subexpression=expression;
1430           }
1431       }
1432     else
1433       if (precedence >= target)
1434         {
1435           /*
1436             Left-to-right associativity.
1437           */
1438           target=precedence;
1439           subexpression=expression;
1440         }
1441     if (strchr("(",(int) *expression) != (char *) NULL)
1442       expression=FxSubexpression(expression,exception);
1443     c=(int) (*expression++);
1444   }
1445   return(subexpression);
1446 }
1447 
FxEvaluateSubexpression(FxInfo * fx_info,const PixelChannel channel,const ssize_t x,const ssize_t y,const char * expression,const size_t depth,double * beta,ExceptionInfo * exception)1448 static double FxEvaluateSubexpression(FxInfo *fx_info,
1449   const PixelChannel channel,const ssize_t x,const ssize_t y,
1450   const char *expression,const size_t depth,double *beta,
1451   ExceptionInfo *exception)
1452 {
1453 #define FxMaxParenthesisDepth  58
1454 #define FxMaxSubexpressionDepth  200
1455 #define FxReturn(value) \
1456 { \
1457   subexpression=DestroyString(subexpression); \
1458   return(value); \
1459 }
1460 #define FxParseConditional(subexpression,sentinal,p,q) \
1461 { \
1462   p=subexpression; \
1463   for (q=(char *) p; (*q != (sentinal)) && (*q != '\0'); q++) \
1464     if (*q == '(') \
1465       { \
1466         for (q++; (*q != ')') && (*q != '\0'); q++); \
1467         if (*q == '\0') \
1468           break; \
1469       } \
1470   if (*q == '\0') \
1471     { \
1472       (void) ThrowMagickException(exception,GetMagickModule(), \
1473         OptionError,"UnableToParseExpression","`%s'",subexpression); \
1474       FxReturn(0.0); \
1475     } \
1476   if (strlen(q) == 1) \
1477     *(q+1)='\0'; \
1478   *q='\0'; \
1479 }
1480 
1481   char
1482     *q,
1483     *subexpression;
1484 
1485   double
1486     alpha,
1487     gamma,
1488     sans,
1489     value;
1490 
1491   const char
1492     *p;
1493 
1494   *beta=0.0;
1495   sans=0.0;
1496   subexpression=AcquireString(expression);
1497   *subexpression='\0';
1498   if (depth > FxMaxSubexpressionDepth)
1499     {
1500       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1501         "UnableToParseExpression","`%s'",expression);
1502       FxReturn(0.0);
1503     }
1504   if (exception->severity >= ErrorException)
1505     FxReturn(0.0);
1506   while (isspace((int) ((unsigned char) *expression)) != 0)
1507     expression++;
1508   if (*expression == '\0')
1509     FxReturn(0.0);
1510   p=FxOperatorPrecedence(expression,exception);
1511   if (p != (const char *) NULL)
1512     {
1513       (void) CopyMagickString(subexpression,expression,(size_t)
1514         (p-expression+1));
1515       alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,depth+1,
1516         beta,exception);
1517       switch ((unsigned char) *p)
1518       {
1519         case '~':
1520         {
1521           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1522             exception);
1523           *beta=(double) (~(size_t) *beta);
1524           FxReturn(*beta);
1525         }
1526         case '!':
1527         {
1528           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1529             exception);
1530           FxReturn(*beta == 0.0 ? 1.0 : 0.0);
1531         }
1532         case '^':
1533         {
1534           *beta=pow(alpha,FxEvaluateSubexpression(fx_info,channel,x,y,++p,
1535             depth+1,beta,exception));
1536           FxReturn(*beta);
1537         }
1538         case '*':
1539         case ExponentialNotation:
1540         {
1541           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1542             exception);
1543           FxReturn(alpha*(*beta));
1544         }
1545         case '/':
1546         {
1547           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1548             exception);
1549           FxReturn(PerceptibleReciprocal(*beta)*alpha);
1550         }
1551         case '%':
1552         {
1553           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1554             exception);
1555           FxReturn(fmod(alpha,*beta));
1556         }
1557         case '+':
1558         {
1559           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1560             exception);
1561           FxReturn(alpha+(*beta));
1562         }
1563         case '-':
1564         {
1565           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1566             exception);
1567           FxReturn(alpha-(*beta));
1568         }
1569         case BitwiseAndAssignmentOperator:
1570         {
1571           q=subexpression;
1572           while (isalpha((int) ((unsigned char) *q)) != 0)
1573             q++;
1574           if (*q != '\0')
1575             {
1576               (void) ThrowMagickException(exception,GetMagickModule(),
1577                 OptionError,"UnableToParseExpression","`%s'",subexpression);
1578               FxReturn(0.0);
1579             }
1580           ClearMagickException(exception);
1581           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1582             exception);
1583           value=(double) ((size_t) (alpha+0.5) & (size_t) (*beta+0.5));
1584           if (SetFxSymbolValue(fx_info,subexpression,value) == MagickFalse)
1585             return(0.0);
1586           FxReturn(*beta);
1587         }
1588         case BitwiseOrAssignmentOperator:
1589         {
1590           q=subexpression;
1591           while (isalpha((int) ((unsigned char) *q)) != 0)
1592             q++;
1593           if (*q != '\0')
1594             {
1595               (void) ThrowMagickException(exception,GetMagickModule(),
1596                 OptionError,"UnableToParseExpression","`%s'",subexpression);
1597               FxReturn(0.0);
1598             }
1599           ClearMagickException(exception);
1600           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1601             exception);
1602           value=(double) ((size_t) (alpha+0.5) | (size_t) (*beta+0.5));
1603           if (SetFxSymbolValue(fx_info,subexpression,value) == MagickFalse)
1604             return(0.0);
1605           FxReturn(*beta);
1606         }
1607         case LeftShiftAssignmentOperator:
1608         {
1609           q=subexpression;
1610           while (isalpha((int) ((unsigned char) *q)) != 0)
1611             q++;
1612           if (*q != '\0')
1613             {
1614               (void) ThrowMagickException(exception,GetMagickModule(),
1615                 OptionError,"UnableToParseExpression","`%s'",subexpression);
1616               FxReturn(0.0);
1617             }
1618           ClearMagickException(exception);
1619           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1620             exception);
1621           if ((size_t) (*beta+0.5) >= (8*sizeof(size_t)))
1622             {
1623               (void) ThrowMagickException(exception,GetMagickModule(),
1624                 OptionError,"ShiftCountOverflow","`%s'",subexpression);
1625               FxReturn(0.0);
1626             }
1627           value=(double) ((size_t) (alpha+0.5) << (size_t) (*beta+0.5));
1628           if (SetFxSymbolValue(fx_info,subexpression,value) == MagickFalse)
1629             return(0.0);
1630           FxReturn(*beta);
1631         }
1632         case RightShiftAssignmentOperator:
1633         {
1634           q=subexpression;
1635           while (isalpha((int) ((unsigned char) *q)) != 0)
1636             q++;
1637           if (*q != '\0')
1638             {
1639               (void) ThrowMagickException(exception,GetMagickModule(),
1640                 OptionError,"UnableToParseExpression","`%s'",subexpression);
1641               FxReturn(0.0);
1642             }
1643           ClearMagickException(exception);
1644           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1645             exception);
1646           if ((size_t) (*beta+0.5) >= (8*sizeof(size_t)))
1647             {
1648               (void) ThrowMagickException(exception,GetMagickModule(),
1649                 OptionError,"ShiftCountOverflow","`%s'",subexpression);
1650               FxReturn(0.0);
1651             }
1652           value=(double) ((size_t) (alpha+0.5) >> (size_t) (*beta+0.5));
1653           if (SetFxSymbolValue(fx_info,subexpression,value) == MagickFalse)
1654             return(0.0);
1655           FxReturn(*beta);
1656         }
1657         case PowerAssignmentOperator:
1658         {
1659           q=subexpression;
1660           while (isalpha((int) ((unsigned char) *q)) != 0)
1661             q++;
1662           if (*q != '\0')
1663             {
1664               (void) ThrowMagickException(exception,GetMagickModule(),
1665                 OptionError,"UnableToParseExpression","`%s'",subexpression);
1666               FxReturn(0.0);
1667             }
1668           ClearMagickException(exception);
1669           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1670             exception);
1671           value=pow(alpha,*beta);
1672           if (SetFxSymbolValue(fx_info,subexpression,value) == MagickFalse)
1673             return(0.0);
1674           FxReturn(*beta);
1675         }
1676         case ModuloAssignmentOperator:
1677         {
1678           q=subexpression;
1679           while (isalpha((int) ((unsigned char) *q)) != 0)
1680             q++;
1681           if (*q != '\0')
1682             {
1683               (void) ThrowMagickException(exception,GetMagickModule(),
1684                 OptionError,"UnableToParseExpression","`%s'",subexpression);
1685               FxReturn(0.0);
1686             }
1687           ClearMagickException(exception);
1688           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1689             exception);
1690           value=fmod(alpha,*beta);
1691           if (SetFxSymbolValue(fx_info,subexpression,value) == MagickFalse)
1692             return(0.0);
1693           FxReturn(*beta);
1694         }
1695         case PlusAssignmentOperator:
1696         {
1697           q=subexpression;
1698           while (isalpha((int) ((unsigned char) *q)) != 0)
1699             q++;
1700           if (*q != '\0')
1701             {
1702               (void) ThrowMagickException(exception,GetMagickModule(),
1703                 OptionError,"UnableToParseExpression","`%s'",subexpression);
1704               FxReturn(0.0);
1705             }
1706           ClearMagickException(exception);
1707           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1708             exception);
1709           value=alpha+(*beta);
1710           if (SetFxSymbolValue(fx_info,subexpression,value) == MagickFalse)
1711             return(0.0);
1712           FxReturn(*beta);
1713         }
1714         case SubtractAssignmentOperator:
1715         {
1716           q=subexpression;
1717           while (isalpha((int) ((unsigned char) *q)) != 0)
1718             q++;
1719           if (*q != '\0')
1720             {
1721               (void) ThrowMagickException(exception,GetMagickModule(),
1722                 OptionError,"UnableToParseExpression","`%s'",subexpression);
1723               FxReturn(0.0);
1724             }
1725           ClearMagickException(exception);
1726           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1727             exception);
1728           value=alpha-(*beta);
1729           if (SetFxSymbolValue(fx_info,subexpression,value) == MagickFalse)
1730             return(0.0);
1731           FxReturn(*beta);
1732         }
1733         case MultiplyAssignmentOperator:
1734         {
1735           q=subexpression;
1736           while (isalpha((int) ((unsigned char) *q)) != 0)
1737             q++;
1738           if (*q != '\0')
1739             {
1740               (void) ThrowMagickException(exception,GetMagickModule(),
1741                 OptionError,"UnableToParseExpression","`%s'",subexpression);
1742               FxReturn(0.0);
1743             }
1744           ClearMagickException(exception);
1745           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1746             exception);
1747           value=alpha*(*beta);
1748           if (SetFxSymbolValue(fx_info,subexpression,value) == MagickFalse)
1749             return(0.0);
1750           FxReturn(*beta);
1751         }
1752         case DivideAssignmentOperator:
1753         {
1754           q=subexpression;
1755           while (isalpha((int) ((unsigned char) *q)) != 0)
1756             q++;
1757           if (*q != '\0')
1758             {
1759               (void) ThrowMagickException(exception,GetMagickModule(),
1760                 OptionError,"UnableToParseExpression","`%s'",subexpression);
1761               FxReturn(0.0);
1762             }
1763           ClearMagickException(exception);
1764           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1765             exception);
1766           value=alpha*PerceptibleReciprocal(*beta);
1767           if (SetFxSymbolValue(fx_info,subexpression,value) == MagickFalse)
1768             return(0.0);
1769           FxReturn(*beta);
1770         }
1771         case IncrementAssignmentOperator:
1772         {
1773           if (*subexpression == '\0')
1774             alpha=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1775               exception);
1776           value=alpha+1.0;
1777           if (*subexpression == '\0')
1778             {
1779               if (SetFxSymbolValue(fx_info,p,value) == MagickFalse)
1780                 return(0.0);
1781             }
1782           else
1783             if (SetFxSymbolValue(fx_info,subexpression,value) == MagickFalse)
1784               return(0.0);
1785           FxReturn(*beta);
1786         }
1787         case DecrementAssignmentOperator:
1788         {
1789           if (*subexpression == '\0')
1790             alpha=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1791               exception);
1792           value=alpha-1.0;
1793           if (*subexpression == '\0')
1794             {
1795               if (SetFxSymbolValue(fx_info,p,value) == MagickFalse)
1796                 return(0.0);
1797             }
1798           else
1799             if (SetFxSymbolValue(fx_info,subexpression,value) == MagickFalse)
1800               return(0.0);
1801           FxReturn(*beta);
1802         }
1803         case LeftShiftOperator:
1804         {
1805           gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1806             exception);
1807           if ((size_t) (gamma+0.5) >= (8*sizeof(size_t)))
1808             {
1809               (void) ThrowMagickException(exception,GetMagickModule(),
1810                 OptionError,"ShiftCountOverflow","`%s'",subexpression);
1811               FxReturn(0.0);
1812             }
1813           *beta=(double) ((size_t) (alpha+0.5) << (size_t) (gamma+0.5));
1814           FxReturn(*beta);
1815         }
1816         case RightShiftOperator:
1817         {
1818           gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1819             exception);
1820           if ((size_t) (gamma+0.5) >= (8*sizeof(size_t)))
1821             {
1822               (void) ThrowMagickException(exception,GetMagickModule(),
1823                 OptionError,"ShiftCountOverflow","`%s'",subexpression);
1824               FxReturn(0.0);
1825             }
1826           *beta=(double) ((size_t) (alpha+0.5) >> (size_t) (gamma+0.5));
1827           FxReturn(*beta);
1828         }
1829         case '<':
1830         {
1831           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1832             exception);
1833           FxReturn(alpha < *beta ? 1.0 : 0.0);
1834         }
1835         case LessThanEqualOperator:
1836         {
1837           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1838             exception);
1839           FxReturn(alpha <= *beta ? 1.0 : 0.0);
1840         }
1841         case '>':
1842         {
1843           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1844             exception);
1845           FxReturn(alpha > *beta ? 1.0 : 0.0);
1846         }
1847         case GreaterThanEqualOperator:
1848         {
1849           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1850             exception);
1851           FxReturn(alpha >= *beta ? 1.0 : 0.0);
1852         }
1853         case EqualOperator:
1854         {
1855           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1856             exception);
1857           FxReturn(fabs(alpha-(*beta)) < MagickEpsilon ? 1.0 : 0.0);
1858         }
1859         case NotEqualOperator:
1860         {
1861           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1862             exception);
1863           FxReturn(fabs(alpha-(*beta)) >= MagickEpsilon ? 1.0 : 0.0);
1864         }
1865         case '&':
1866         {
1867           gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1868             exception);
1869           *beta=(double) ((size_t) (alpha+0.5) & (size_t) (gamma+0.5));
1870           FxReturn(*beta);
1871         }
1872         case '|':
1873         {
1874           gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1875             exception);
1876           *beta=(double) ((size_t) (alpha+0.5) | (size_t) (gamma+0.5));
1877           FxReturn(*beta);
1878         }
1879         case LogicalAndOperator:
1880         {
1881           p++;
1882           if (alpha <= 0.0)
1883             {
1884               *beta=0.0;
1885               FxReturn(*beta);
1886             }
1887           gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,depth+1,beta,
1888             exception);
1889           *beta=(gamma > 0.0) ? 1.0 : 0.0;
1890           FxReturn(*beta);
1891         }
1892         case LogicalOrOperator:
1893         {
1894           p++;
1895           if (alpha > 0.0)
1896             {
1897              *beta=1.0;
1898              FxReturn(*beta);
1899             }
1900           gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,depth+1,beta,
1901             exception);
1902           *beta=(gamma > 0.0) ? 1.0 : 0.0;
1903           FxReturn(*beta);
1904         }
1905         case '?':
1906         {
1907           (void) CopyMagickString(subexpression,++p,MagickPathExtent-1);
1908           FxParseConditional(subexpression,':',p,q);
1909           if (fabs(alpha) >= MagickEpsilon)
1910             gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,depth+1,beta,
1911               exception);
1912           else
1913             gamma=FxEvaluateSubexpression(fx_info,channel,x,y,q+1,depth+1,beta,
1914               exception);
1915           FxReturn(gamma);
1916         }
1917         case '=':
1918         {
1919           q=subexpression;
1920           while (isalpha((int) ((unsigned char) *q)) != 0)
1921             q++;
1922           if (*q != '\0')
1923             {
1924               (void) ThrowMagickException(exception,GetMagickModule(),
1925                 OptionError,"UnableToParseExpression","`%s'",subexpression);
1926               FxReturn(0.0);
1927             }
1928           ClearMagickException(exception);
1929           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1930             exception);
1931           value=(*beta);
1932           if (SetFxSymbolValue(fx_info,subexpression,value) == MagickFalse)
1933             return(0.0);
1934           FxReturn(*beta);
1935         }
1936         case ',':
1937         {
1938           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1939             exception);
1940           FxReturn(alpha);
1941         }
1942         case ';':
1943         {
1944           *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1945             exception);
1946           FxReturn(*beta);
1947         }
1948         default:
1949         {
1950           gamma=alpha*FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,
1951             beta,exception);
1952           FxReturn(gamma);
1953         }
1954       }
1955     }
1956   if (strchr("(",(int) *expression) != (char *) NULL)
1957     {
1958       size_t
1959         length;
1960 
1961       if (depth >= FxMaxParenthesisDepth)
1962         (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1963           "ParenthesisNestedTooDeeply","`%s'",expression);
1964       length=CopyMagickString(subexpression,expression+1,MagickPathExtent);
1965       if (length != 0)
1966         subexpression[length-1]='\0';
1967       gamma=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,depth+1,
1968         beta,exception);
1969       FxReturn(gamma);
1970     }
1971   switch (*expression)
1972   {
1973     case '+':
1974     {
1975       gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,depth+1,
1976         beta,exception);
1977       FxReturn(1.0*gamma);
1978     }
1979     case '-':
1980     {
1981       gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,depth+1,
1982         beta,exception);
1983       FxReturn(-1.0*gamma);
1984     }
1985     case '~':
1986     {
1987       gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,depth+1,
1988         beta,exception);
1989       FxReturn((double) (~(size_t) (gamma+0.5)));
1990     }
1991     case 'A':
1992     case 'a':
1993     {
1994       if (IsFxFunction(expression,"abs",3) != MagickFalse)
1995         {
1996           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
1997             depth+1,beta,exception);
1998           FxReturn(fabs(alpha));
1999         }
2000 #if defined(MAGICKCORE_HAVE_ACOSH)
2001       if (IsFxFunction(expression,"acosh",5) != MagickFalse)
2002         {
2003           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2004             depth+1,beta,exception);
2005           FxReturn(acosh(alpha));
2006         }
2007 #endif
2008       if (IsFxFunction(expression,"acos",4) != MagickFalse)
2009         {
2010           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2011             depth+1,beta,exception);
2012           FxReturn(acos(alpha));
2013         }
2014 #if defined(MAGICKCORE_HAVE_J1)
2015       if (IsFxFunction(expression,"airy",4) != MagickFalse)
2016         {
2017           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2018             depth+1,beta,exception);
2019           if (alpha == 0.0)
2020             FxReturn(1.0);
2021           gamma=2.0*j1((MagickPI*alpha))/(MagickPI*alpha);
2022           FxReturn(gamma*gamma);
2023         }
2024 #endif
2025 #if defined(MAGICKCORE_HAVE_ASINH)
2026       if (IsFxFunction(expression,"asinh",5) != MagickFalse)
2027         {
2028           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2029             depth+1,beta,exception);
2030           FxReturn(asinh(alpha));
2031         }
2032 #endif
2033       if (IsFxFunction(expression,"asin",4) != MagickFalse)
2034         {
2035           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2036             depth+1,beta,exception);
2037           FxReturn(asin(alpha));
2038         }
2039       if (IsFxFunction(expression,"alt",3) != MagickFalse)
2040         {
2041           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2042             depth+1,beta,exception);
2043           FxReturn(((ssize_t) alpha) & 0x01 ? -1.0 : 1.0);
2044         }
2045       if (IsFxFunction(expression,"atan2",5) != MagickFalse)
2046         {
2047           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2048             depth+1,beta,exception);
2049           FxReturn(atan2(alpha,*beta));
2050         }
2051 #if defined(MAGICKCORE_HAVE_ATANH)
2052       if (IsFxFunction(expression,"atanh",5) != MagickFalse)
2053         {
2054           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2055             depth+1,beta,exception);
2056           FxReturn(atanh(alpha));
2057         }
2058 #endif
2059       if (IsFxFunction(expression,"atan",4) != MagickFalse)
2060         {
2061           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2062             depth+1,beta,exception);
2063           FxReturn(atan(alpha));
2064         }
2065       if (LocaleCompare(expression,"a") == 0)
2066         FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2067       break;
2068     }
2069     case 'B':
2070     case 'b':
2071     {
2072       if (LocaleCompare(expression,"b") == 0)
2073         FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2074       break;
2075     }
2076     case 'C':
2077     case 'c':
2078     {
2079       if (IsFxFunction(expression,"ceil",4) != MagickFalse)
2080         {
2081           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2082             depth+1,beta,exception);
2083           FxReturn(ceil(alpha));
2084         }
2085       if (IsFxFunction(expression,"clamp",5) != MagickFalse)
2086         {
2087           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2088             depth+1,beta,exception);
2089           if (alpha < 0.0)
2090             FxReturn(0.0);
2091           if (alpha > 1.0)
2092             FxReturn(1.0);
2093           FxReturn(alpha);
2094         }
2095       if (IsFxFunction(expression,"cosh",4) != MagickFalse)
2096         {
2097           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2098             depth+1,beta,exception);
2099           FxReturn(cosh(alpha));
2100         }
2101       if (IsFxFunction(expression,"cos",3) != MagickFalse)
2102         {
2103           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2104             depth+1,beta,exception);
2105           FxReturn(cos(alpha));
2106         }
2107       if (LocaleCompare(expression,"c") == 0)
2108         FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2109       break;
2110     }
2111     case 'D':
2112     case 'd':
2113     {
2114       if (IsFxFunction(expression,"debug",5) != MagickFalse)
2115         {
2116           const char
2117             *type;
2118 
2119           size_t
2120             length;
2121 
2122           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2123             depth+1,beta,exception);
2124           switch (fx_info->images->colorspace)
2125           {
2126             case CMYKColorspace:
2127             {
2128               switch (channel)
2129               {
2130                 case CyanPixelChannel: type="cyan"; break;
2131                 case MagentaPixelChannel: type="magenta"; break;
2132                 case YellowPixelChannel: type="yellow"; break;
2133                 case AlphaPixelChannel: type="alpha"; break;
2134                 case BlackPixelChannel: type="black"; break;
2135                 default: type="unknown"; break;
2136               }
2137               break;
2138             }
2139             case GRAYColorspace:
2140             {
2141               switch (channel)
2142               {
2143                 case RedPixelChannel: type="gray"; break;
2144                 case AlphaPixelChannel: type="alpha"; break;
2145                 default: type="unknown"; break;
2146               }
2147               break;
2148             }
2149             default:
2150             {
2151               switch (channel)
2152               {
2153                 case RedPixelChannel: type="red"; break;
2154                 case GreenPixelChannel: type="green"; break;
2155                 case BluePixelChannel: type="blue"; break;
2156                 case AlphaPixelChannel: type="alpha"; break;
2157                 default: type="unknown"; break;
2158               }
2159               break;
2160             }
2161           }
2162           *subexpression='\0';
2163           length=1;
2164           if (strlen(expression) > 6)
2165             length=CopyMagickString(subexpression,expression+6,
2166               MagickPathExtent);
2167           if (length != 0)
2168             subexpression[length-1]='\0';
2169           if (fx_info->file != (FILE *) NULL)
2170             (void) FormatLocaleFile(fx_info->file,"%s[%.20g,%.20g].%s: "
2171               "%s=%.*g\n",fx_info->images->filename,(double) x,(double) y,type,
2172               subexpression,GetMagickPrecision(),alpha);
2173           FxReturn(alpha);
2174         }
2175       if (IsFxFunction(expression,"do",2) != MagickFalse)
2176         {
2177           size_t
2178             length;
2179 
2180           /*
2181             Parse do(expression,condition test).
2182           */
2183           length=CopyMagickString(subexpression,expression+3,
2184             MagickPathExtent-1);
2185           if (length != 0)
2186             subexpression[length-1]='\0';
2187           FxParseConditional(subexpression,',',p,q);
2188           for (alpha=0.0; ; )
2189           {
2190             alpha=FxEvaluateSubexpression(fx_info,channel,x,y,q+1,depth+1,beta,
2191               exception);
2192             gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,depth+1,&sans,
2193               exception);
2194             if (fabs(gamma) < MagickEpsilon)
2195               break;
2196           }
2197           FxReturn(alpha);
2198         }
2199       if (IsFxFunction(expression,"drc",3) != MagickFalse)
2200         {
2201           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2202             depth+1,beta,exception);
2203           FxReturn((alpha/(*beta*(alpha-1.0)+1.0)));
2204         }
2205       break;
2206     }
2207     case 'E':
2208     case 'e':
2209     {
2210       if (LocaleCompare(expression,"epsilon") == 0)
2211         FxReturn(MagickEpsilon);
2212 #if defined(MAGICKCORE_HAVE_ERF)
2213       if (IsFxFunction(expression,"erf",3) != MagickFalse)
2214         {
2215           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2216             depth+1,beta,exception);
2217           FxReturn(erf(alpha));
2218         }
2219 #endif
2220       if (IsFxFunction(expression,"exp",3) != MagickFalse)
2221         {
2222           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2223             depth+1,beta,exception);
2224           FxReturn(exp(alpha));
2225         }
2226       if (LocaleCompare(expression,"e") == 0)
2227         FxReturn(2.7182818284590452354);
2228       break;
2229     }
2230     case 'F':
2231     case 'f':
2232     {
2233       if (IsFxFunction(expression,"floor",5) != MagickFalse)
2234         {
2235           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2236             depth+1,beta,exception);
2237           FxReturn(floor(alpha));
2238         }
2239       if (IsFxFunction(expression,"for",3) != MagickFalse)
2240         {
2241           double
2242             sans = 0.0;
2243 
2244           size_t
2245             length;
2246 
2247           /*
2248             Parse for(initialization, condition test, expression).
2249           */
2250           length=CopyMagickString(subexpression,expression+4,
2251             MagickPathExtent-1);
2252           if (length != 0)
2253             subexpression[length-1]='\0';
2254           FxParseConditional(subexpression,',',p,q);
2255           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,p,depth+1,&sans,
2256             exception);
2257           (void) CopyMagickString(subexpression,q+1,MagickPathExtent-1);
2258           FxParseConditional(subexpression,',',p,q);
2259           for (alpha=0.0; ; )
2260           {
2261             gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,depth+1,&sans,
2262               exception);
2263             if (fabs(gamma) < MagickEpsilon)
2264               break;
2265             alpha=FxEvaluateSubexpression(fx_info,channel,x,y,q+1,depth+1,beta,
2266               exception);
2267           }
2268           FxReturn(alpha);
2269         }
2270       break;
2271     }
2272     case 'G':
2273     case 'g':
2274     {
2275       if (IsFxFunction(expression,"gauss",5) != MagickFalse)
2276         {
2277           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2278             depth+1,beta,exception);
2279           FxReturn(exp((-alpha*alpha/2.0))/sqrt(2.0*MagickPI));
2280         }
2281       if (IsFxFunction(expression,"gcd",3) != MagickFalse)
2282         {
2283           double
2284             gcd;
2285 
2286           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2287             depth+1,beta,exception);
2288           if (IsNaN(alpha))
2289             FxReturn(alpha);
2290           gcd=FxGCD(alpha,*beta);
2291           FxReturn(gcd);
2292         }
2293       if (LocaleCompare(expression,"g") == 0)
2294         FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2295       break;
2296     }
2297     case 'H':
2298     case 'h':
2299     {
2300       if (LocaleCompare(expression,"h") == 0)
2301         FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2302       if (LocaleCompare(expression,"hue") == 0)
2303         FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2304       if (IsFxFunction(expression,"hypot",5) != MagickFalse)
2305         {
2306           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2307             depth+1,beta,exception);
2308           FxReturn(hypot(alpha,*beta));
2309         }
2310       break;
2311     }
2312     case 'K':
2313     case 'k':
2314     {
2315       if (LocaleCompare(expression,"k") == 0)
2316         FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2317       break;
2318     }
2319     case 'I':
2320     case 'i':
2321     {
2322       if (IsFxFunction(expression,"if",2) != MagickFalse)
2323         {
2324           double
2325             sans = 0.0;
2326 
2327           size_t
2328             length;
2329 
2330           /*
2331             Parse if(condition test, true-expression, false-expression).
2332           */
2333           length=CopyMagickString(subexpression,expression+3,
2334             MagickPathExtent-1);
2335           if (length != 0)
2336             subexpression[length-1]='\0';
2337           FxParseConditional(subexpression,',',p,q);
2338           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,p,depth+1,&sans,
2339             exception);
2340           (void) CopyMagickString(subexpression,q+1,MagickPathExtent-1);
2341           FxParseConditional(subexpression,',',p,q);
2342           if (fabs(alpha) >= MagickEpsilon)
2343             alpha=FxEvaluateSubexpression(fx_info,channel,x,y,p,depth+1,beta,
2344               exception);
2345           else
2346             alpha=FxEvaluateSubexpression(fx_info,channel,x,y,q+1,depth+1,beta,
2347               exception);
2348           FxReturn(alpha);
2349         }
2350       if (LocaleCompare(expression,"intensity") == 0)
2351         FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2352       if (IsFxFunction(expression,"int",3) != MagickFalse)
2353         {
2354           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2355             depth+1,beta,exception);
2356           FxReturn(floor(alpha));
2357         }
2358       if (IsFxFunction(expression,"isnan",5) != MagickFalse)
2359         {
2360           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2361             depth+1,beta,exception);
2362           FxReturn((double) !!IsNaN(alpha));
2363         }
2364       if (LocaleCompare(expression,"i") == 0)
2365         FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2366       break;
2367     }
2368     case 'J':
2369     case 'j':
2370     {
2371       if (LocaleCompare(expression,"j") == 0)
2372         FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2373 #if defined(MAGICKCORE_HAVE_J0)
2374       if (IsFxFunction(expression,"j0",2) != MagickFalse)
2375         {
2376           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,
2377             depth+1,beta,exception);
2378           FxReturn(j0(alpha));
2379         }
2380 #endif
2381 #if defined(MAGICKCORE_HAVE_J1)
2382       if (IsFxFunction(expression,"j1",2) != MagickFalse)
2383         {
2384           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,
2385             depth+1,beta,exception);
2386           FxReturn(j1(alpha));
2387         }
2388 #endif
2389 #if defined(MAGICKCORE_HAVE_J1)
2390       if (IsFxFunction(expression,"jinc",4) != MagickFalse)
2391         {
2392           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2393             depth+1,beta,exception);
2394           if (alpha == 0.0)
2395             FxReturn(1.0);
2396           FxReturn((2.0*j1((MagickPI*alpha))/(MagickPI*alpha)));
2397         }
2398 #endif
2399       break;
2400     }
2401     case 'L':
2402     case 'l':
2403     {
2404       if (IsFxFunction(expression,"ln",2) != MagickFalse)
2405         {
2406           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,
2407             depth+1,beta,exception);
2408           FxReturn(log(alpha));
2409         }
2410       if (IsFxFunction(expression,"logtwo",6) != MagickFalse)
2411         {
2412           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+6,
2413             depth+1,beta,exception);
2414           FxReturn(log10(alpha)/log10(2.0));
2415         }
2416       if (IsFxFunction(expression,"log",3) != MagickFalse)
2417         {
2418           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2419             depth+1,beta,exception);
2420           FxReturn(log10(alpha));
2421         }
2422       if (LocaleCompare(expression,"lightness") == 0)
2423         FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2424       break;
2425     }
2426     case 'M':
2427     case 'm':
2428     {
2429       if (LocaleCompare(expression,"MaxRGB") == 0)
2430         FxReturn(QuantumRange);
2431       if (LocaleNCompare(expression,"maxima",6) == 0)
2432         break;
2433       if (IsFxFunction(expression,"max",3) != MagickFalse)
2434         {
2435           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2436             depth+1,beta,exception);
2437           FxReturn(alpha > *beta ? alpha : *beta);
2438         }
2439       if (LocaleNCompare(expression,"minima",6) == 0)
2440         break;
2441       if (IsFxFunction(expression,"min",3) != MagickFalse)
2442         {
2443           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2444             depth+1,beta,exception);
2445           FxReturn(alpha < *beta ? alpha : *beta);
2446         }
2447       if (IsFxFunction(expression,"mod",3) != MagickFalse)
2448         {
2449           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2450             depth+1,beta,exception);
2451           FxReturn(alpha-floor((alpha*PerceptibleReciprocal(*beta)))*(*beta));
2452         }
2453       if (LocaleCompare(expression,"m") == 0)
2454         FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2455       break;
2456     }
2457     case 'N':
2458     case 'n':
2459     {
2460       if (IsFxFunction(expression,"not",3) != MagickFalse)
2461         {
2462           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2463             depth+1,beta,exception);
2464           FxReturn((double) (alpha < MagickEpsilon));
2465         }
2466       if (LocaleCompare(expression,"n") == 0)
2467         FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2468       break;
2469     }
2470     case 'O':
2471     case 'o':
2472     {
2473       if (LocaleCompare(expression,"Opaque") == 0)
2474         FxReturn(1.0);
2475       if (LocaleCompare(expression,"o") == 0)
2476         FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2477       break;
2478     }
2479     case 'P':
2480     case 'p':
2481     {
2482       if (LocaleCompare(expression,"phi") == 0)
2483         FxReturn(MagickPHI);
2484       if (LocaleCompare(expression,"pi") == 0)
2485         FxReturn(MagickPI);
2486       if (IsFxFunction(expression,"pow",3) != MagickFalse)
2487         {
2488           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2489             depth+1,beta,exception);
2490           FxReturn(pow(alpha,*beta));
2491         }
2492       if (LocaleCompare(expression,"p") == 0)
2493         FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2494       break;
2495     }
2496     case 'Q':
2497     case 'q':
2498     {
2499       if (LocaleCompare(expression,"QuantumRange") == 0)
2500         FxReturn(QuantumRange);
2501       if (LocaleCompare(expression,"QuantumScale") == 0)
2502         FxReturn(QuantumScale);
2503       break;
2504     }
2505     case 'R':
2506     case 'r':
2507     {
2508       if (IsFxFunction(expression,"rand",4) != MagickFalse)
2509         {
2510 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2511         #pragma omp critical (MagickCore_FxEvaluateSubexpression)
2512 #endif
2513           alpha=GetPseudoRandomValue(fx_info->random_info);
2514           FxReturn(alpha);
2515         }
2516       if (IsFxFunction(expression,"round",5) != MagickFalse)
2517         {
2518           /*
2519             Round the fraction to nearest integer.
2520           */
2521           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2522             depth+1,beta,exception);
2523           if ((alpha-floor(alpha)) < (ceil(alpha)-alpha))
2524             FxReturn(floor(alpha));
2525           FxReturn(ceil(alpha));
2526         }
2527       if (LocaleCompare(expression,"r") == 0)
2528         FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2529       break;
2530     }
2531     case 'S':
2532     case 's':
2533     {
2534       if (LocaleCompare(expression,"saturation") == 0)
2535         FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2536       if (IsFxFunction(expression,"sign",4) != MagickFalse)
2537         {
2538           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2539             depth+1,beta,exception);
2540           FxReturn(alpha < 0.0 ? -1.0 : 1.0);
2541         }
2542       if (IsFxFunction(expression,"sinc",4) != MagickFalse)
2543         {
2544           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2545             depth+1,beta,exception);
2546           if (alpha == 0)
2547             FxReturn(1.0);
2548           FxReturn(sin((MagickPI*alpha))/(MagickPI*alpha));
2549         }
2550       if (IsFxFunction(expression,"sinh",4) != MagickFalse)
2551         {
2552           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2553             depth+1,beta,exception);
2554           FxReturn(sinh(alpha));
2555         }
2556       if (IsFxFunction(expression,"sin",3) != MagickFalse)
2557         {
2558           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2559             depth+1,beta,exception);
2560           FxReturn(sin(alpha));
2561         }
2562       if (IsFxFunction(expression,"sqrt",4) != MagickFalse)
2563         {
2564           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2565             depth+1,beta,exception);
2566           FxReturn(sqrt(alpha));
2567         }
2568       if (IsFxFunction(expression,"squish",6) != MagickFalse)
2569         {
2570           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+6,
2571             depth+1,beta,exception);
2572           FxReturn((1.0/(1.0+exp(-alpha))));
2573         }
2574       if (LocaleCompare(expression,"s") == 0)
2575         FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2576       break;
2577     }
2578     case 'T':
2579     case 't':
2580     {
2581       if (IsFxFunction(expression,"tanh",4) != MagickFalse)
2582         {
2583           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2584             depth+1,beta,exception);
2585           FxReturn(tanh(alpha));
2586         }
2587       if (IsFxFunction(expression,"tan",3) != MagickFalse)
2588         {
2589           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2590             depth+1,beta,exception);
2591           FxReturn(tan(alpha));
2592         }
2593       if (LocaleCompare(expression,"Transparent") == 0)
2594         FxReturn(0.0);
2595       if (IsFxFunction(expression,"trunc",5) != MagickFalse)
2596         {
2597           alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2598             depth+1,beta,exception);
2599           if (alpha >= 0.0)
2600             FxReturn(floor(alpha));
2601           FxReturn(ceil(alpha));
2602         }
2603       if (LocaleCompare(expression,"t") == 0)
2604         FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2605       break;
2606     }
2607     case 'U':
2608     case 'u':
2609     {
2610       if (LocaleCompare(expression,"u") == 0)
2611         FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2612       break;
2613     }
2614     case 'V':
2615     case 'v':
2616     {
2617       if (LocaleCompare(expression,"v") == 0)
2618         FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2619       break;
2620     }
2621     case 'W':
2622     case 'w':
2623     {
2624       if (IsFxFunction(expression,"while",5) != MagickFalse)
2625         {
2626           size_t
2627             length;
2628 
2629           /*
2630             Parse while(condition test, expression).
2631           */
2632           length=CopyMagickString(subexpression,expression+6,
2633             MagickPathExtent-1);
2634           if (length != 0)
2635             subexpression[length-1]='\0';
2636           FxParseConditional(subexpression,',',p,q);
2637           for (alpha=0.0; ; )
2638           {
2639             gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,depth+1,&sans,
2640               exception);
2641             if (fabs(gamma) < MagickEpsilon)
2642               break;
2643             alpha=FxEvaluateSubexpression(fx_info,channel,x,y,q+1,depth+1,
2644               beta,exception);
2645           }
2646           FxReturn(alpha);
2647         }
2648       if (LocaleCompare(expression,"w") == 0)
2649         FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2650       break;
2651     }
2652     case 'Y':
2653     case 'y':
2654     {
2655       if (LocaleCompare(expression,"y") == 0)
2656         FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2657       break;
2658     }
2659     case 'Z':
2660     case 'z':
2661     {
2662       if (LocaleCompare(expression,"z") == 0)
2663         FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2664       break;
2665     }
2666     default:
2667       break;
2668   }
2669   subexpression=DestroyString(subexpression);
2670   q=(char *) expression;
2671   alpha=InterpretSiPrefixValue(expression,&q);
2672   if (q == expression)
2673     alpha=FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception);
2674   FxReturn(alpha);
2675 }
2676 
FxEvaluateExpression(FxInfo * fx_info,double * alpha,ExceptionInfo * exception)2677 MagickPrivate MagickBooleanType FxEvaluateExpression(FxInfo *fx_info,
2678   double *alpha,ExceptionInfo *exception)
2679 {
2680   MagickBooleanType
2681     status;
2682 
2683   status=FxEvaluateChannelExpression(fx_info,GrayPixelChannel,0,0,alpha,
2684     exception);
2685   return(status);
2686 }
2687 
FxPreprocessExpression(FxInfo * fx_info,double * alpha,ExceptionInfo * exception)2688 MagickExport MagickBooleanType FxPreprocessExpression(FxInfo *fx_info,
2689   double *alpha,ExceptionInfo *exception)
2690 {
2691   FILE
2692     *file;
2693 
2694   MagickBooleanType
2695     status;
2696 
2697   file=fx_info->file;
2698   fx_info->file=(FILE *) NULL;
2699   status=FxEvaluateChannelExpression(fx_info,GrayPixelChannel,0,0,alpha,
2700     exception);
2701   fx_info->file=file;
2702   return(status);
2703 }
2704 
FxEvaluateChannelExpression(FxInfo * fx_info,const PixelChannel channel,const ssize_t x,const ssize_t y,double * alpha,ExceptionInfo * exception)2705 MagickPrivate MagickBooleanType FxEvaluateChannelExpression(FxInfo *fx_info,
2706   const PixelChannel channel,const ssize_t x,const ssize_t y,
2707   double *alpha,ExceptionInfo *exception)
2708 {
2709   double
2710     beta;
2711 
2712   beta=0.0;
2713   *alpha=FxEvaluateSubexpression(fx_info,channel,x,y,fx_info->expression,0,
2714     &beta,exception);
2715   return(exception->severity == OptionError ? MagickFalse : MagickTrue);
2716 }
2717 
2718 /*
2719 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2720 %                                                                             %
2721 %                                                                             %
2722 %                                                                             %
2723 %     F x I m a g e                                                           %
2724 %                                                                             %
2725 %                                                                             %
2726 %                                                                             %
2727 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2728 %
2729 %  FxImage() applies a mathematical expression to the specified image.
2730 %
2731 %  The format of the FxImage method is:
2732 %
2733 %      Image *FxImage(const Image *image,const char *expression,
2734 %        ExceptionInfo *exception)
2735 %
2736 %  A description of each parameter follows:
2737 %
2738 %    o image: the image.
2739 %
2740 %    o expression: A mathematical expression.
2741 %
2742 %    o exception: return any errors or warnings in this structure.
2743 %
2744 */
2745 
DestroyFxThreadSet(FxInfo ** fx_info)2746 static FxInfo **DestroyFxThreadSet(FxInfo **fx_info)
2747 {
2748   ssize_t
2749     i;
2750 
2751   assert(fx_info != (FxInfo **) NULL);
2752   for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
2753     if (fx_info[i] != (FxInfo *) NULL)
2754       fx_info[i]=DestroyFxInfo(fx_info[i]);
2755   fx_info=(FxInfo **) RelinquishMagickMemory(fx_info);
2756   return(fx_info);
2757 }
2758 
AcquireFxThreadSet(const Image * image,const char * expression,ExceptionInfo * exception)2759 static FxInfo **AcquireFxThreadSet(const Image *image,const char *expression,
2760   ExceptionInfo *exception)
2761 {
2762   char
2763     *fx_expression;
2764 
2765   double
2766     alpha;
2767 
2768   FxInfo
2769     **fx_info;
2770 
2771   ssize_t
2772     i;
2773 
2774   size_t
2775     number_threads;
2776 
2777   number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
2778   fx_info=(FxInfo **) AcquireQuantumMemory(number_threads,sizeof(*fx_info));
2779   if (fx_info == (FxInfo **) NULL)
2780     {
2781       (void) ThrowMagickException(exception,GetMagickModule(),
2782         ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2783       return((FxInfo **) NULL);
2784     }
2785   (void) memset(fx_info,0,number_threads*sizeof(*fx_info));
2786   if (*expression != '@')
2787     fx_expression=ConstantString(expression);
2788   else
2789     fx_expression=FileToString(expression+1,~0UL,exception);
2790   for (i=0; i < (ssize_t) number_threads; i++)
2791   {
2792     MagickBooleanType
2793       status;
2794 
2795     fx_info[i]=AcquireFxInfo(image,fx_expression,exception);
2796     if (fx_info[i] == (FxInfo *) NULL)
2797       break;
2798     status=FxPreprocessExpression(fx_info[i],&alpha,exception);
2799     if (status == MagickFalse)
2800       break;
2801   }
2802   fx_expression=DestroyString(fx_expression);
2803   if (i < (ssize_t) number_threads)
2804     fx_info=DestroyFxThreadSet(fx_info);
2805   return(fx_info);
2806 }
2807 
FxImage(const Image * image,const char * expression,ExceptionInfo * exception)2808 MagickExport Image *FxImage(const Image *image,const char *expression,
2809   ExceptionInfo *exception)
2810 {
2811 #define FxImageTag  "Fx/Image"
2812 
2813   CacheView
2814     *fx_view,
2815     *image_view;
2816 
2817   FxInfo
2818     **magick_restrict fx_info;
2819 
2820   Image
2821     *fx_image;
2822 
2823   MagickBooleanType
2824     status;
2825 
2826   MagickOffsetType
2827     progress;
2828 
2829   ssize_t
2830     y;
2831 
2832   assert(image != (Image *) NULL);
2833   assert(image->signature == MagickCoreSignature);
2834   if (image->debug != MagickFalse)
2835     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2836   if (expression == (const char *) NULL)
2837     return(CloneImage(image,0,0,MagickTrue,exception));
2838   fx_info=AcquireFxThreadSet(image,expression,exception);
2839   if (fx_info == (FxInfo **) NULL)
2840     return((Image *) NULL);
2841   fx_image=CloneImage(image,0,0,MagickTrue,exception);
2842   if (fx_image == (Image *) NULL)
2843     {
2844       fx_info=DestroyFxThreadSet(fx_info);
2845       return((Image *) NULL);
2846     }
2847   if (SetImageStorageClass(fx_image,DirectClass,exception) == MagickFalse)
2848     {
2849       fx_info=DestroyFxThreadSet(fx_info);
2850       fx_image=DestroyImage(fx_image);
2851       return((Image *) NULL);
2852     }
2853   /*
2854     Fx image.
2855   */
2856   status=MagickTrue;
2857   progress=0;
2858   image_view=AcquireVirtualCacheView(image,exception);
2859   fx_view=AcquireAuthenticCacheView(fx_image,exception);
2860 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2861   #pragma omp parallel for schedule(dynamic) shared(progress,status) \
2862     magick_number_threads(image,fx_image,fx_image->rows, \
2863       GlobExpression(fx_info[0]->expression,"debug(",MagickTrue) == 0 ? 1 : 0)
2864 #endif
2865   for (y=0; y < (ssize_t) fx_image->rows; y++)
2866   {
2867     const int
2868       id = GetOpenMPThreadId();
2869 
2870     const Quantum
2871       *magick_restrict p;
2872 
2873     Quantum
2874       *magick_restrict q;
2875 
2876     ssize_t
2877       x;
2878 
2879     if (status == MagickFalse)
2880       continue;
2881     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2882     q=QueueCacheViewAuthenticPixels(fx_view,0,y,fx_image->columns,1,exception);
2883     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2884       {
2885         status=MagickFalse;
2886         continue;
2887       }
2888     for (x=0; x < (ssize_t) fx_image->columns; x++)
2889     {
2890       ssize_t
2891         i;
2892 
2893       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2894       {
2895         double
2896           alpha;
2897 
2898         PixelChannel channel = GetPixelChannelChannel(image,i);
2899         PixelTrait traits = GetPixelChannelTraits(image,channel);
2900         PixelTrait fx_traits=GetPixelChannelTraits(fx_image,channel);
2901         if ((traits == UndefinedPixelTrait) ||
2902             (fx_traits == UndefinedPixelTrait))
2903           continue;
2904         if ((fx_traits & CopyPixelTrait) != 0)
2905           {
2906             SetPixelChannel(fx_image,channel,p[i],q);
2907             continue;
2908           }
2909         alpha=0.0;
2910         (void) FxEvaluateChannelExpression(fx_info[id],channel,x,y,&alpha,
2911           exception);
2912         q[i]=ClampToQuantum(QuantumRange*alpha);
2913       }
2914       p+=GetPixelChannels(image);
2915       q+=GetPixelChannels(fx_image);
2916     }
2917     if (SyncCacheViewAuthenticPixels(fx_view,exception) == MagickFalse)
2918       status=MagickFalse;
2919     if (image->progress_monitor != (MagickProgressMonitor) NULL)
2920       {
2921         MagickBooleanType
2922           proceed;
2923 
2924 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2925         #pragma omp atomic
2926 #endif
2927         progress++;
2928         proceed=SetImageProgress(image,FxImageTag,progress,image->rows);
2929         if (proceed == MagickFalse)
2930           status=MagickFalse;
2931       }
2932   }
2933   fx_view=DestroyCacheView(fx_view);
2934   image_view=DestroyCacheView(image_view);
2935   fx_info=DestroyFxThreadSet(fx_info);
2936   if (status == MagickFalse)
2937     fx_image=DestroyImage(fx_image);
2938   return(fx_image);
2939 }
2940