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