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