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