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