MagickCore  7.1.1-43
Convert, Edit, Or Compose Bitmap Images
fx.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 % %
4 % %
5 % %
6 % FFFFF X X %
7 % F X X %
8 % FFF X %
9 % F X X %
10 % F X X %
11 % %
12 % %
13 % MagickCore Image Special Effects Methods %
14 % %
15 % Software Design %
16 % snibgo (Alan Gibson) %
17 % January 2022 %
18 % %
19 % %
20 % %
21 % Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization %
22 % dedicated to making software imaging solutions freely available. %
23 % %
24 % You may not use this file except in compliance with the License. You may %
25 % obtain a copy of the License at %
26 % %
27 % https://imagemagick.org/script/license.php %
28 % %
29 % Unless required by applicable law or agreed to in writing, software %
30 % distributed under the License is distributed on an "AS IS" BASIS, %
31 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32 % See the License for the specific language governing permissions and %
33 % limitations under the License. %
34 % %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36 %
37 %
38 %
39 */
40 
41 /*
42  Include declarations.
43 */
44 #include "MagickCore/studio.h"
45 #include "MagickCore/accelerate-private.h"
46 #include "MagickCore/annotate.h"
47 #include "MagickCore/artifact.h"
48 #include "MagickCore/attribute.h"
49 #include "MagickCore/cache.h"
50 #include "MagickCore/cache-view.h"
51 #include "MagickCore/channel.h"
52 #include "MagickCore/color.h"
53 #include "MagickCore/color-private.h"
54 #include "MagickCore/colorspace-private.h"
55 #include "MagickCore/composite.h"
56 #include "MagickCore/decorate.h"
57 #include "MagickCore/distort.h"
58 #include "MagickCore/draw.h"
59 #include "MagickCore/effect.h"
60 #include "MagickCore/enhance.h"
61 #include "MagickCore/exception.h"
62 #include "MagickCore/exception-private.h"
63 #include "MagickCore/fx.h"
64 #include "MagickCore/fx-private.h"
65 #include "MagickCore/gem.h"
66 #include "MagickCore/gem-private.h"
67 #include "MagickCore/geometry.h"
68 #include "MagickCore/layer.h"
69 #include "MagickCore/list.h"
70 #include "MagickCore/log.h"
71 #include "MagickCore/image.h"
72 #include "MagickCore/image-private.h"
73 #include "MagickCore/magick.h"
74 #include "MagickCore/memory_.h"
75 #include "MagickCore/memory-private.h"
76 #include "MagickCore/monitor.h"
77 #include "MagickCore/monitor-private.h"
78 #include "MagickCore/option.h"
79 #include "MagickCore/pixel.h"
80 #include "MagickCore/pixel-accessor.h"
81 #include "MagickCore/policy.h"
82 #include "MagickCore/property.h"
83 #include "MagickCore/quantum.h"
84 #include "MagickCore/quantum-private.h"
85 #include "MagickCore/random_.h"
86 #include "MagickCore/random-private.h"
87 #include "MagickCore/resample.h"
88 #include "MagickCore/resample-private.h"
89 #include "MagickCore/resize.h"
90 #include "MagickCore/resource_.h"
91 #include "MagickCore/splay-tree.h"
92 #include "MagickCore/statistic.h"
93 #include "MagickCore/statistic-private.h"
94 #include "MagickCore/string_.h"
95 #include "MagickCore/thread-private.h"
96 #include "MagickCore/threshold.h"
97 #include "MagickCore/timer-private.h"
98 #include "MagickCore/token.h"
99 #include "MagickCore/transform.h"
100 #include "MagickCore/transform-private.h"
101 #include "MagickCore/utility.h"
102 
103 
104 #define MaxTokenLen 100
105 #define RpnInit 100
106 #define TableExtend 0.1
107 #define InitNumOprStack 50
108 #define MinValStackSize 100
109 #define InitNumUserSymbols 50
110 
111 typedef long double fxFltType;
112 
113 typedef enum {
114  oAddEq,
115  oSubtractEq,
116  oMultiplyEq,
117  oDivideEq,
118  oPlusPlus,
119  oSubSub,
120  oAdd,
121  oSubtract,
122  oMultiply,
123  oDivide,
124  oModulus,
125  oUnaryPlus,
126  oUnaryMinus,
127  oLshift,
128  oRshift,
129  oEq,
130  oNotEq,
131  oLtEq,
132  oGtEq,
133  oLt,
134  oGt,
135  oLogAnd,
136  oLogOr,
137  oLogNot,
138  oBitAnd,
139  oBitOr,
140  oBitNot,
141  oPow,
142  oQuery,
143  oColon,
144  oOpenParen,
145  oCloseParen,
146  oOpenBracket,
147  oCloseBracket,
148  oOpenBrace,
149  oCloseBrace,
150  oAssign,
151  oNull
152 } OperatorE;
153 
154 typedef struct {
155  OperatorE
156  op;
157 
158  const char *
159  str;
160 
161  int
162  precedence, /* Higher number is higher precedence */
163  number_args;
164 } OperatorT;
165 
166 static const OperatorT Operators[] = {
167  {oAddEq, "+=", 12, 1},
168  {oSubtractEq, "-=", 12, 1},
169  {oMultiplyEq, "*=", 13, 1},
170  {oDivideEq, "/=", 13, 1},
171  {oPlusPlus, "++", 12, 0},
172  {oSubSub, "--", 12, 0},
173  {oAdd, "+", 12, 2},
174  {oSubtract, "-", 12, 2},
175  {oMultiply, "*", 13, 2},
176  {oDivide, "/", 13, 2},
177  {oModulus, "%", 13, 2},
178  {oUnaryPlus, "+", 14, 1},
179  {oUnaryMinus, "-", 14, 1},
180  {oLshift, "<<", 11, 2},
181  {oRshift, ">>", 11, 2},
182  {oEq, "==", 9, 2},
183  {oNotEq, "!=", 9, 2},
184  {oLtEq, "<=", 10, 2},
185  {oGtEq, ">=", 10, 2},
186  {oLt, "<", 10, 2},
187  {oGt, ">", 10, 2},
188  {oLogAnd, "&&", 6, 2},
189  {oLogOr, "||", 5, 2},
190  {oLogNot, "!", 16, 1},
191  {oBitAnd, "&", 8, 2},
192  {oBitOr, "|", 7, 2},
193  {oBitNot, "~", 16, 1},
194  {oPow, "^", 15, 2},
195  {oQuery, "?", 4, 1},
196  {oColon, ":", 4, 1},
197  {oOpenParen, "(", 0, 0},
198  {oCloseParen, ")", 0, 0},
199  {oOpenBracket, "[", 0, 0},
200  {oCloseBracket,"]", 0, 0},
201  {oOpenBrace, "{", 0, 0},
202  {oCloseBrace, "}", 0, 0},
203  {oAssign, "=", 3, 1},
204  {oNull, "onull", 17, 0}
205 };
206 
207 typedef enum {
208  cEpsilon,
209  cE,
210  cOpaque,
211  cPhi,
212  cPi,
213  cQuantumRange,
214  cQuantumScale,
215  cTransparent,
216  cMaxRgb,
217  cNull
218 } ConstantE;
219 
220 typedef struct {
221  ConstantE
222  cons;
223 
224  fxFltType
225  val;
226 
227  const char
228  *str;
229 } ConstantT;
230 
231 static const ConstantT Constants[] = {
232  {cEpsilon, MagickEpsilon, "epsilon"},
233  {cE, 2.7182818284590452354, "e"},
234  {cOpaque, 1.0, "opaque"},
235  {cPhi, MagickPHI, "phi"},
236  {cPi, MagickPI, "pi"},
237  {cQuantumRange, QuantumRange, "quantumrange"},
238  {cQuantumScale, QuantumScale, "quantumscale"},
239  {cTransparent, 0.0, "transparent"},
240  {cMaxRgb, QuantumRange, "MaxRGB"},
241  {cNull, 0.0, "cnull"}
242 };
243 
244 #define FirstFunc ((FunctionE) (oNull+1))
245 
246 typedef enum {
247  fAbs = oNull+1,
248 #if defined(MAGICKCORE_HAVE_ACOSH)
249  fAcosh,
250 #endif
251  fAcos,
252 #if defined(MAGICKCORE_HAVE_J1)
253  fAiry,
254 #endif
255  fAlt,
256 #if defined(MAGICKCORE_HAVE_ASINH)
257  fAsinh,
258 #endif
259  fAsin,
260 #if defined(MAGICKCORE_HAVE_ATANH)
261  fAtanh,
262 #endif
263  fAtan2,
264  fAtan,
265  fCeil,
266  fChannel,
267  fClamp,
268  fCosh,
269  fCos,
270  fDebug,
271  fDrc,
272 #if defined(MAGICKCORE_HAVE_ERF)
273  fErf,
274 #endif
275  fExp,
276  fFloor,
277  fGauss,
278  fGcd,
279  fHypot,
280  fInt,
281  fIsnan,
282 #if defined(MAGICKCORE_HAVE_J0)
283  fJ0,
284 #endif
285 #if defined(MAGICKCORE_HAVE_J1)
286  fJ1,
287 #endif
288 #if defined(MAGICKCORE_HAVE_J1)
289  fJinc,
290 #endif
291  fLn,
292  fLogtwo,
293  fLog,
294  fMax,
295  fMin,
296  fMod,
297  fNot,
298  fPow,
299  fRand,
300  fRound,
301  fSign,
302  fSinc,
303  fSinh,
304  fSin,
305  fSqrt,
306  fSquish,
307  fTanh,
308  fTan,
309  fTrunc,
310  fDo,
311  fFor,
312  fIf,
313  fWhile,
314  fU,
315  fU0,
316  fUP,
317  fS,
318  fV,
319  fP,
320  fSP,
321  fVP,
322 
323  fNull
324 } FunctionE;
325 
326 typedef struct {
327  FunctionE
328  func;
329 
330  const char
331  *str;
332 
333  int
334  number_args;
335 } FunctionT;
336 
337 static const FunctionT Functions[] = {
338  {fAbs, "abs" , 1},
339 #if defined(MAGICKCORE_HAVE_ACOSH)
340  {fAcosh, "acosh" , 1},
341 #endif
342  {fAcos, "acos" , 1},
343 #if defined(MAGICKCORE_HAVE_J1)
344  {fAiry, "airy" , 1},
345 #endif
346  {fAlt, "alt" , 1},
347 #if defined(MAGICKCORE_HAVE_ASINH)
348  {fAsinh, "asinh" , 1},
349 #endif
350  {fAsin, "asin" , 1},
351 #if defined(MAGICKCORE_HAVE_ATANH)
352  {fAtanh, "atanh" , 1},
353 #endif
354  {fAtan2, "atan2" , 2},
355  {fAtan, "atan" , 1},
356  {fCeil, "ceil" , 1},
357  {fChannel, "channel", 5}, /* Special case: allow zero to five arguments. */
358  {fClamp, "clamp" , 1},
359  {fCosh, "cosh" , 1},
360  {fCos, "cos" , 1},
361  {fDebug, "debug" , 1},
362  {fDrc, "drc" , 2},
363 #if defined(MAGICKCORE_HAVE_ERF)
364  {fErf, "erf" , 1},
365 #endif
366  {fExp, "exp" , 1},
367  {fFloor, "floor" , 1},
368  {fGauss, "gauss" , 1},
369  {fGcd, "gcd" , 2},
370  {fHypot, "hypot" , 2},
371  {fInt, "int" , 1},
372  {fIsnan, "isnan" , 1},
373 #if defined(MAGICKCORE_HAVE_J0)
374  {fJ0, "j0" , 1},
375 #endif
376 #if defined(MAGICKCORE_HAVE_J1)
377  {fJ1, "j1" , 1},
378 #endif
379 #if defined(MAGICKCORE_HAVE_J1)
380  {fJinc, "jinc" , 1},
381 #endif
382  {fLn, "ln" , 1},
383  {fLogtwo, "logtwo", 1},
384  {fLog, "log" , 1},
385  {fMax, "max" , 2},
386  {fMin, "min" , 2},
387  {fMod, "mod" , 2},
388  {fNot, "not" , 1},
389  {fPow, "pow" , 2},
390  {fRand, "rand" , 0},
391  {fRound, "round" , 1},
392  {fSign, "sign" , 1},
393  {fSinc, "sinc" , 1},
394  {fSinh, "sinh" , 1},
395  {fSin, "sin" , 1},
396  {fSqrt, "sqrt" , 1},
397  {fSquish, "squish", 1},
398  {fTanh, "tanh" , 1},
399  {fTan, "tan" , 1},
400  {fTrunc, "trunc" , 1},
401  {fDo, "do", 2},
402  {fFor, "for", 3},
403  {fIf, "if", 3},
404  {fWhile, "while", 2},
405  {fU, "u", 1},
406  {fU0, "u0", 0},
407  {fUP, "up", 3},
408  {fS, "s", 0},
409  {fV, "v", 0},
410  {fP, "p", 2},
411  {fSP, "sp", 2},
412  {fVP, "vp", 2},
413 
414  {fNull, "fnull" , 0}
415 };
416 
417 #define FirstImgAttr ((ImgAttrE) (fNull+1))
418 
419 typedef enum {
420  aDepth = fNull+1,
421  aExtent,
422  aKurtosis,
423  aMaxima,
424  aMean,
425  aMedian,
426  aMinima,
427  aPage,
428  aPageX,
429  aPageY,
430  aPageWid,
431  aPageHt,
432  aPrintsize,
433  aPrintsizeX,
434  aPrintsizeY,
435  aQuality,
436  aRes,
437  aResX,
438  aResY,
439  aSkewness,
440  aStdDev,
441  aH,
442  aN,
443  aT,
444  aW,
445  aZ,
446  aNull
447 } ImgAttrE;
448 
449 typedef struct {
450  ImgAttrE
451  attr;
452 
453  const char
454  *str;
455 
456  MagickBooleanType
457  need_stats;
458 } ImgAttrT;
459 
460 static const ImgAttrT ImgAttrs[] = {
461  {aDepth, "depth", MagickTrue},
462  {aExtent, "extent", MagickFalse},
463  {aKurtosis, "kurtosis", MagickTrue},
464  {aMaxima, "maxima", MagickTrue},
465  {aMean, "mean", MagickTrue},
466  {aMedian, "median", MagickTrue},
467  {aMinima, "minima", MagickTrue},
468  {aPage, "page", MagickFalse},
469  {aPageX, "page.x", MagickFalse},
470  {aPageY, "page.y", MagickFalse},
471  {aPageWid, "page.width", MagickFalse},
472  {aPageHt, "page.height", MagickFalse},
473  {aPrintsize, "printsize", MagickFalse},
474  {aPrintsizeX, "printsize.x", MagickFalse},
475  {aPrintsizeY, "printsize.y", MagickFalse},
476  {aQuality, "quality", MagickFalse},
477  {aRes, "resolution", MagickFalse},
478  {aResX, "resolution.x", MagickFalse},
479  {aResY, "resolution.y", MagickFalse},
480  {aSkewness, "skewness", MagickTrue},
481  {aStdDev, "standard_deviation", MagickTrue},
482  {aH, "h", MagickFalse},
483  {aN, "n", MagickFalse},
484  {aT, "t", MagickFalse},
485  {aW, "w", MagickFalse},
486  {aZ, "z", MagickFalse},
487  {aNull, "anull", MagickFalse},
488  {aNull, "anull", MagickFalse},
489  {aNull, "anull", MagickFalse},
490  {aNull, "anull", MagickFalse}
491 };
492 
493 #define FirstSym ((SymbolE) (aNull+1))
494 
495 typedef enum {
496  sHue = aNull+1,
497  sIntensity,
498  sLightness,
499  sLuma,
500  sLuminance,
501  sSaturation,
502  sA,
503  sB,
504  sC,
505  sG,
506  sI,
507  sJ,
508  sK,
509  sM,
510  sO,
511  sR,
512  sY,
513  sNull
514 } SymbolE;
515 
516 typedef struct {
517  SymbolE
518  sym;
519 
520  const char
521  *str;
522 } SymbolT;
523 
524 static const SymbolT Symbols[] = {
525  {sHue, "hue"},
526  {sIntensity, "intensity"},
527  {sLightness, "lightness"},
528  {sLuma, "luma"},
529  {sLuminance, "luminance"},
530  {sSaturation, "saturation"},
531  {sA, "a"},
532  {sB, "b"},
533  {sC, "c"},
534  {sG, "g"},
535  {sI, "i"},
536  {sJ, "j"},
537  {sK, "k"},
538  {sM, "m"},
539  {sO, "o"},
540  {sR, "r"},
541  {sY, "y"},
542  {sNull, "snull"}
543 };
544 /*
545  There is no way to access new value of pixels. This might be a future enhancement, eg "q".
546  fP, oU and oV can have channel qualifier such as "u.r".
547  For meta channels, we might also allow numbered channels eg "u.2" or "u.16".
548  ... or have extra argument to p[].
549 */
550 
551 #define FirstCont (sNull+1)
552 
553 /* Run-time controls are in the RPN, not explicitly in the input string. */
554 typedef enum {
555  rGoto = FirstCont,
556  rGotoChk,
557  rIfZeroGoto,
558  rIfNotZeroGoto,
559  rCopyFrom,
560  rCopyTo,
561  rZerStk,
562  rNull
563 } ControlE;
564 
565 typedef struct {
566  ControlE
567  cont;
568 
569  const char
570  *str;
571 
572  int
573  number_args;
574 } ControlT;
575 
576 static const ControlT Controls[] = {
577  {rGoto, "goto", 0},
578  {rGotoChk, "gotochk", 0},
579  {rIfZeroGoto, "ifzerogoto", 1},
580  {rIfNotZeroGoto, "ifnotzerogoto", 1},
581  {rCopyFrom, "copyfrom", 0},
582  {rCopyTo, "copyto", 1},
583  {rZerStk, "zerstk", 0},
584  {rNull, "rnull", 0}
585 };
586 
587 #define NULL_ADDRESS -2
588 
589 typedef struct {
590  int
591  addr_query,
592  addr_colon;
593 } TernaryT;
594 
595 typedef struct {
596  const char
597  *str;
598 
599  PixelChannel
600  pixel_channel;
601 } ChannelT;
602 
603 #define NO_CHAN_QUAL ((PixelChannel) (-1))
604 #define THIS_CHANNEL ((PixelChannel) (-2))
605 #define HUE_CHANNEL ((PixelChannel) (-3))
606 #define SAT_CHANNEL ((PixelChannel) (-4))
607 #define LIGHT_CHANNEL ((PixelChannel) (-5))
608 #define INTENSITY_CHANNEL ((PixelChannel) (-6))
609 
610 static const ChannelT Channels[] = {
611  {"r", RedPixelChannel},
612  {"g", GreenPixelChannel},
613  {"b", BluePixelChannel},
614  {"c", CyanPixelChannel},
615  {"m", MagentaPixelChannel},
616  {"y", YellowPixelChannel},
617  {"k", BlackPixelChannel},
618  {"a", AlphaPixelChannel},
619  {"o", AlphaPixelChannel},
620  {"hue", HUE_CHANNEL},
621  {"saturation", SAT_CHANNEL},
622  {"lightness", LIGHT_CHANNEL},
623  {"intensity", INTENSITY_CHANNEL},
624  {"all", CompositePixelChannel},
625  {"this", THIS_CHANNEL},
626  {"", NO_CHAN_QUAL}
627 };
628 
629 /* The index into UserSymbols is also the index into run-time UserSymVals.
630 */
631 typedef struct {
632  char
633  *pex;
634 
635  size_t
636  len;
637 } UserSymbolT;
638 
639 typedef enum {
640  etOperator,
641  etConstant,
642  etFunction,
643  etImgAttr,
644  etSymbol,
645  etColourConstant,
646  etControl
647 } ElementTypeE;
648 
649 static const char * sElementTypes[] = {
650  "Operator",
651  "Constant",
652  "Function",
653  "ImgAttr",
654  "Symbol",
655  "ColConst",
656  "Control"
657 };
658 
659 typedef struct {
660  char
661  *exp_start;
662 
663  ElementTypeE
664  type;
665 
666  fxFltType
667  val,
668  val1,
669  val2;
670 
671  ImgAttrE
672  img_attr_qual;
673 
674  int
675  element_index,
676  number_args,
677  number_dest, /* Number of Elements that "goto" this element */
678  operator_index;
679 
680  MagickBooleanType
681  do_push,
682  is_relative;
683 
684  PixelChannel
685  channel_qual;
686 
687  size_t
688  exp_len;
689 } ElementT;
690 
691 typedef enum {
692  rtUnknown,
693  rtEntireImage,
694  rtCornerOnly
695 } RunTypeE;
696 
697 typedef struct {
698  CacheView *View;
699  /* Other per-image metadata could go here. */
700 } ImgT;
701 
702 typedef struct {
703  RandomInfo * magick_restrict random_info;
704  int numValStack;
705  int usedValStack;
706  fxFltType * ValStack;
707  fxFltType * UserSymVals;
708  Quantum * thisPixel;
709 } fxRtT;
710 
711 struct _FxInfo {
712  Image * image;
713  size_t ImgListLen;
714  ssize_t ImgNum;
715  MagickBooleanType NeedStats;
716  MagickBooleanType GotStats;
717  MagickBooleanType NeedHsl;
718  MagickBooleanType DebugOpt; /* Whether "-debug" option is in effect */
719  MagickBooleanType ContainsDebug; /* Whether expression contains "debug ()" function */
720  char * expression;
721  char * pex;
722  char ShortExp[MagickPathExtent]; /* for reporting */
723  int teDepth;
724  char token[MagickPathExtent];
725  size_t lenToken;
726  int numElements;
727  int usedElements;
728  ElementT * Elements; /* Elements is read-only at runtime. */
729  int numUserSymbols;
730  int usedUserSymbols;
731  UserSymbolT * UserSymbols;
732  int numOprStack;
733  int usedOprStack;
734  int maxUsedOprStack;
735  OperatorE * OperatorStack;
736  ChannelStatistics ** statistics;
737  int precision;
738  RunTypeE runType;
739 
740  RandomInfo
741  **magick_restrict random_infos;
742 
743  ImgT * Imgs;
744  Image ** Images;
745 
746  ExceptionInfo * exception;
747 
748  fxRtT * fxrts;
749 };
750 
751 /* Forward declarations for recursion.
752 */
753 static MagickBooleanType TranslateStatementList
754  (FxInfo * pfx, const char * strLimit, char * chLimit);
755 
756 static MagickBooleanType TranslateExpression
757  (FxInfo * pfx, const char * strLimit, char * chLimit, MagickBooleanType * needPopAll);
758 
759 static MagickBooleanType GetFunction (FxInfo * pfx, FunctionE fe);
760 
761 static inline MagickBooleanType ChanIsVirtual (PixelChannel pc)
762 {
763  if (pc==HUE_CHANNEL || pc==SAT_CHANNEL || pc==LIGHT_CHANNEL || pc==INTENSITY_CHANNEL)
764  return MagickTrue;
765 
766  return MagickFalse;
767 }
768 
769 static MagickBooleanType InitFx (FxInfo * pfx, const Image * img,
770  MagickBooleanType CalcAllStats, ExceptionInfo *exception)
771 {
772  ssize_t i=0;
773  const Image * next;
774 
775  pfx->ImgListLen = GetImageListLength (img);
776  pfx->ImgNum = GetImageIndexInList (img);
777  pfx->image = (Image *)img;
778 
779  pfx->NeedStats = MagickFalse;
780  pfx->GotStats = MagickFalse;
781  pfx->NeedHsl = MagickFalse;
782  pfx->DebugOpt = IsStringTrue (GetImageArtifact (img, "fx:debug"));
783  pfx->statistics = NULL;
784  pfx->Imgs = NULL;
785  pfx->Images = NULL;
786  pfx->exception = exception;
787  pfx->precision = GetMagickPrecision ();
788  pfx->random_infos = AcquireRandomInfoTLS ();
789  pfx->ContainsDebug = MagickFalse;
790  pfx->runType = (CalcAllStats) ? rtEntireImage : rtCornerOnly;
791  pfx->Imgs = (ImgT *)AcquireQuantumMemory (pfx->ImgListLen, sizeof (ImgT));
792  if (!pfx->Imgs) {
793  (void) ThrowMagickException (
794  pfx->exception, GetMagickModule(), ResourceLimitFatalError,
795  "Imgs", "%lu",
796  (unsigned long) pfx->ImgListLen);
797  return MagickFalse;
798  }
799 
800  next = GetFirstImageInList (img);
801  for ( ; next != (Image *) NULL; next=next->next)
802  {
803  ImgT * pimg = &pfx->Imgs[i];
804  pimg->View = AcquireVirtualCacheView (next, pfx->exception);
805  if (!pimg->View) {
806  (void) ThrowMagickException (
807  pfx->exception, GetMagickModule(), ResourceLimitFatalError,
808  "View", "[%li]",
809  (long) i);
810  /* dealloc any done so far, and Imgs */
811  for ( ; i > 0; i--) {
812  pimg = &pfx->Imgs[i-1];
813  pimg->View = DestroyCacheView (pimg->View);
814  }
815  pfx->Imgs=(ImgT *) RelinquishMagickMemory (pfx->Imgs);
816  return MagickFalse;
817  }
818  i++;
819  }
820 
821  pfx->Images = ImageListToArray (img, pfx->exception);
822 
823  return MagickTrue;
824 }
825 
826 static MagickBooleanType DeInitFx (FxInfo * pfx)
827 {
828  ssize_t i;
829 
830  if (pfx->Images) pfx->Images = (Image**) RelinquishMagickMemory (pfx->Images);
831 
832  if (pfx->Imgs) {
833  for (i = (ssize_t)GetImageListLength(pfx->image); i > 0; i--) {
834  ImgT * pimg = &pfx->Imgs[i-1];
835  pimg->View = DestroyCacheView (pimg->View);
836  }
837  pfx->Imgs=(ImgT *) RelinquishMagickMemory (pfx->Imgs);
838  }
839  pfx->random_infos = DestroyRandomInfoTLS (pfx->random_infos);
840 
841  if (pfx->statistics) {
842  for (i = (ssize_t)GetImageListLength(pfx->image); i > 0; i--) {
843  pfx->statistics[i-1]=(ChannelStatistics *) RelinquishMagickMemory (pfx->statistics[i-1]);
844  }
845 
846  pfx->statistics = (ChannelStatistics**) RelinquishMagickMemory(pfx->statistics);
847  }
848 
849  return MagickTrue;
850 }
851 
852 static ElementTypeE TypeOfOpr (int op)
853 {
854  if (op < oNull) return etOperator;
855  if (op == oNull) return etConstant;
856  if (op <= fNull) return etFunction;
857  if (op <= aNull) return etImgAttr;
858  if (op <= sNull) return etSymbol;
859  if (op <= rNull) return etControl;
860 
861  return (ElementTypeE) 0;
862 }
863 
864 static char * SetPtrShortExp (FxInfo * pfx, char * pExp, size_t len)
865 {
866  #define MaxLen 20
867 
868  size_t slen;
869  char * p;
870 
871  *pfx->ShortExp = '\0';
872 
873  if (pExp && len) {
874  slen = CopyMagickString (pfx->ShortExp, pExp, len);
875  if (slen > MaxLen) {
876  (void) CopyMagickString (pfx->ShortExp+MaxLen, "...", 4);
877  }
878  p = strchr (pfx->ShortExp, '\n');
879  if (p) (void) CopyMagickString (p, "...", 4);
880  p = strchr (pfx->ShortExp, '\r');
881  if (p) (void) CopyMagickString (p, "...", 4);
882  }
883  return pfx->ShortExp;
884 }
885 
886 static char * SetShortExp (FxInfo * pfx)
887 {
888  return SetPtrShortExp (pfx, pfx->pex, MaxTokenLen-1);
889 }
890 
891 static int FindUserSymbol (FxInfo * pfx, char * name)
892 /* returns index into pfx->UserSymbols, and thus into pfxrt->UserSymVals,
893  or NULL_ADDRESS if not found.
894 */
895 {
896  int i;
897  size_t lenName;
898  lenName = strlen (name);
899  for (i=0; i < pfx->usedUserSymbols; i++) {
900  UserSymbolT *pus = &pfx->UserSymbols[i];
901  if (lenName == pus->len && LocaleNCompare (name, pus->pex, lenName)==0) break;
902  }
903  if (i == pfx->usedUserSymbols) return NULL_ADDRESS;
904  return i;
905 }
906 
907 static MagickBooleanType ExtendUserSymbols (FxInfo * pfx)
908 {
909  pfx->numUserSymbols = (int) ceil (pfx->numUserSymbols * (1 + TableExtend));
910  pfx->UserSymbols = (UserSymbolT*) ResizeMagickMemory (pfx->UserSymbols, (size_t) pfx->numUserSymbols * sizeof(UserSymbolT));
911  if (!pfx->UserSymbols) {
912  (void) ThrowMagickException (
913  pfx->exception, GetMagickModule(), ResourceLimitFatalError,
914  "UserSymbols", "%i",
915  pfx->numUserSymbols);
916  return MagickFalse;
917  }
918 
919  return MagickTrue;
920 }
921 
922 static int AddUserSymbol (FxInfo * pfx, char * pex, size_t len)
923 {
924  UserSymbolT *pus;
925  if (++pfx->usedUserSymbols >= pfx->numUserSymbols) {
926  if (!ExtendUserSymbols (pfx)) return -1;
927  }
928  pus = &pfx->UserSymbols[pfx->usedUserSymbols-1];
929  pus->pex = pex;
930  pus->len = len;
931 
932  return pfx->usedUserSymbols-1;
933 }
934 
935 static void DumpTables (FILE * fh)
936 {
937 
938  int i;
939  for (i=0; i <= rNull; i++) {
940  const char * str = "";
941  if ( i < oNull) str = Operators[i].str;
942  if (i >= (int) FirstFunc && i < fNull) str = Functions[i-(int) FirstFunc].str;
943  if (i >= (int) FirstImgAttr && i < aNull) str = ImgAttrs[i-(int) FirstImgAttr].str;
944  if (i >= (int) FirstSym && i < sNull) str = Symbols[i-(int) FirstSym].str;
945  if (i >= (int) FirstCont && i < rNull) str = Controls[i-(int) FirstCont].str;
946  if (i==0 ) fprintf (stderr, "Operators:\n ");
947  else if (i==oNull) fprintf (stderr, "\nFunctions:\n ");
948  else if (i==fNull) fprintf (stderr, "\nImage attributes:\n ");
949  else if (i==aNull) fprintf (stderr, "\nSymbols:\n ");
950  else if (i==sNull) fprintf (stderr, "\nControls:\n ");
951  fprintf (fh, " %s", str);
952  }
953  fprintf (fh, "\n");
954 }
955 
956 static char * NameOfUserSym (FxInfo * pfx, int ndx, char * buf)
957 {
958  UserSymbolT * pus;
959  assert (ndx >= 0 && ndx < pfx->usedUserSymbols);
960  pus = &pfx->UserSymbols[ndx];
961  (void) CopyMagickString (buf, pus->pex, pus->len+1);
962  return buf;
963 }
964 
965 static void DumpUserSymbols (FxInfo * pfx, FILE * fh)
966 {
967  char UserSym[MagickPathExtent];
968  int i;
969  fprintf (fh, "UserSymbols (%i)\n", pfx->usedUserSymbols);
970  for (i=0; i < pfx->usedUserSymbols; i++) {
971  fprintf (fh, " %i: '%s'\n", i, NameOfUserSym (pfx, i, UserSym));
972  }
973 }
974 
975 static MagickBooleanType BuildRPN (FxInfo * pfx)
976 {
977  pfx->numUserSymbols = InitNumUserSymbols;
978  pfx->usedUserSymbols = 0;
979  pfx->UserSymbols = (UserSymbolT*) AcquireMagickMemory ((size_t) pfx->numUserSymbols * sizeof(UserSymbolT));
980  if (!pfx->UserSymbols) {
981  (void) ThrowMagickException (
982  pfx->exception, GetMagickModule(), ResourceLimitFatalError,
983  "UserSymbols", "%i",
984  pfx->numUserSymbols);
985  return MagickFalse;
986  }
987 
988  pfx->numElements = RpnInit;
989  pfx->usedElements = 0;
990  pfx->Elements = NULL;
991 
992  pfx->Elements = (ElementT*) AcquireMagickMemory ((size_t) pfx->numElements * sizeof(ElementT));
993 
994  if (!pfx->Elements) {
995  (void) ThrowMagickException (
996  pfx->exception, GetMagickModule(), ResourceLimitFatalError,
997  "Elements", "%i",
998  pfx->numElements);
999  return MagickFalse;
1000  }
1001 
1002  pfx->usedOprStack = 0;
1003  pfx->maxUsedOprStack = 0;
1004  pfx->numOprStack = InitNumOprStack;
1005  pfx->OperatorStack = (OperatorE*) AcquireMagickMemory ((size_t) pfx->numOprStack * sizeof(OperatorE));
1006  if (!pfx->OperatorStack) {
1007  (void) ThrowMagickException (
1008  pfx->exception, GetMagickModule(), ResourceLimitFatalError,
1009  "OperatorStack", "%i",
1010  pfx->numOprStack);
1011  return MagickFalse;
1012  }
1013 
1014  return MagickTrue;
1015 }
1016 
1017 static MagickBooleanType AllocFxRt (FxInfo * pfx, fxRtT * pfxrt)
1018 {
1019  int nRnd;
1020  int i;
1021  pfxrt->random_info = AcquireRandomInfo ();
1022  pfxrt->thisPixel = NULL;
1023 
1024  nRnd = 20 + 10 * (int) GetPseudoRandomValue (pfxrt->random_info);
1025  for (i=0; i < nRnd; i++) (void) GetPseudoRandomValue (pfxrt->random_info);;
1026 
1027  pfxrt->usedValStack = 0;
1028  pfxrt->numValStack = 2 * pfx->maxUsedOprStack;
1029  if (pfxrt->numValStack < MinValStackSize) pfxrt->numValStack = MinValStackSize;
1030  pfxrt->ValStack = (fxFltType*) AcquireMagickMemory ((size_t) pfxrt->numValStack * sizeof(fxFltType));
1031  if (!pfxrt->ValStack) {
1032  (void) ThrowMagickException (
1033  pfx->exception, GetMagickModule(), ResourceLimitFatalError,
1034  "ValStack", "%i",
1035  pfxrt->numValStack);
1036  return MagickFalse;
1037  }
1038 
1039  pfxrt->UserSymVals = NULL;
1040 
1041  if (pfx->usedUserSymbols) {
1042  pfxrt->UserSymVals = (fxFltType*) AcquireMagickMemory ((size_t) pfx->usedUserSymbols * sizeof(fxFltType));
1043  if (!pfxrt->UserSymVals) {
1044  (void) ThrowMagickException (
1045  pfx->exception, GetMagickModule(), ResourceLimitFatalError,
1046  "UserSymVals", "%i",
1047  pfx->usedUserSymbols);
1048  return MagickFalse;
1049  }
1050  for (i = 0; i < pfx->usedUserSymbols; i++) pfxrt->UserSymVals[i] = (fxFltType) 0;
1051  }
1052 
1053  return MagickTrue;
1054 }
1055 
1056 static MagickBooleanType ExtendRPN (FxInfo * pfx)
1057 {
1058  pfx->numElements = (int) ceil (pfx->numElements * (1 + TableExtend));
1059  pfx->Elements = (ElementT*) ResizeMagickMemory (pfx->Elements, (size_t) pfx->numElements * sizeof(ElementT));
1060  if (!pfx->Elements) {
1061  (void) ThrowMagickException (
1062  pfx->exception, GetMagickModule(), ResourceLimitFatalError,
1063  "Elements", "%i",
1064  pfx->numElements);
1065  return MagickFalse;
1066  }
1067  return MagickTrue;
1068 }
1069 
1070 static inline MagickBooleanType OprInPlace (int op)
1071 {
1072  return (op >= oAddEq && op <= oSubSub ? MagickTrue : MagickFalse);
1073 }
1074 
1075 static const char * OprStr (int oprNum)
1076 {
1077  const char * str;
1078  if (oprNum < 0) str = "bad OprStr";
1079  else if (oprNum <= oNull) str = Operators[oprNum].str;
1080  else if (oprNum <= fNull) str = Functions[oprNum-(int) FirstFunc].str;
1081  else if (oprNum <= aNull) str = ImgAttrs[oprNum-(int) FirstImgAttr].str;
1082  else if (oprNum <= sNull) str = Symbols[oprNum-(int) FirstSym].str;
1083  else if (oprNum <= rNull) str = Controls[oprNum-(int) FirstCont].str;
1084  else {
1085  str = "bad OprStr";
1086  }
1087  return str;
1088 }
1089 
1090 static MagickBooleanType DumpRPN (FxInfo * pfx, FILE * fh)
1091 {
1092  int i;
1093 
1094  fprintf (fh, "DumpRPN:");
1095  fprintf (fh, " numElements=%i", pfx->numElements);
1096  fprintf (fh, " usedElements=%i", pfx->usedElements);
1097  fprintf (fh, " maxUsedOprStack=%i", pfx->maxUsedOprStack);
1098  fprintf (fh, " ImgListLen=%g", (double) pfx->ImgListLen);
1099  fprintf (fh, " NeedStats=%s", pfx->NeedStats ? "yes" : "no");
1100  fprintf (fh, " GotStats=%s", pfx->GotStats ? "yes" : "no");
1101  fprintf (fh, " NeedHsl=%s\n", pfx->NeedHsl ? "yes" : "no");
1102  if (pfx->runType==rtEntireImage) fprintf (stderr, "EntireImage");
1103  else if (pfx->runType==rtCornerOnly) fprintf (stderr, "CornerOnly");
1104  fprintf (fh, "\n");
1105 
1106 
1107  for (i=0; i < pfx->usedElements; i++) {
1108  ElementT * pel = &pfx->Elements[i];
1109  pel->number_dest = 0;
1110  }
1111  for (i=0; i < pfx->usedElements; i++) {
1112  ElementT * pel = &pfx->Elements[i];
1113  if (pel->operator_index == rGoto || pel->operator_index == rGotoChk || pel->operator_index == rIfZeroGoto || pel->operator_index == rIfNotZeroGoto) {
1114  if (pel->element_index >= 0 && pel->element_index < pfx->numElements) {
1115  ElementT * pelDest = &pfx->Elements[pel->element_index];
1116  pelDest->number_dest++;
1117  }
1118  }
1119  }
1120  for (i=0; i < pfx->usedElements; i++) {
1121  char UserSym[MagickPathExtent];
1122 
1123  ElementT * pel = &pfx->Elements[i];
1124  const char * str = OprStr (pel->operator_index);
1125  const char *sRelAbs = "";
1126 
1127  if (pel->operator_index == fP || pel->operator_index == fUP || pel->operator_index == fVP || pel->operator_index == fSP)
1128  sRelAbs = pel->is_relative ? "[]" : "{}";
1129 
1130  if (pel->type == etColourConstant)
1131  fprintf (fh, " %i: %s vals=%.*Lg,%.*Lg,%.*Lg '%s%s' nArgs=%i ndx=%i %s",
1132  i, sElementTypes[pel->type],
1133  pfx->precision, pel->val, pfx->precision, pel->val1, pfx->precision, pel->val2,
1134  str, sRelAbs, pel->number_args, pel->element_index,
1135  pel->do_push ? "push" : "NO push");
1136  else
1137  fprintf (fh, " %i: %s val=%.*Lg '%s%s' nArgs=%i ndx=%i %s",
1138  i, sElementTypes[pel->type], pfx->precision, pel->val, str, sRelAbs,
1139  pel->number_args, pel->element_index,
1140  pel->do_push ? "push" : "NO push");
1141 
1142  if (pel->img_attr_qual != aNull)
1143  fprintf (fh, " ia=%s", OprStr((int) pel->img_attr_qual));
1144 
1145  if (pel->channel_qual != NO_CHAN_QUAL) {
1146  if (pel->channel_qual == THIS_CHANNEL) fprintf (stderr, " ch=this");
1147  else fprintf (stderr, " ch=%i", pel->channel_qual);
1148  }
1149 
1150  if (pel->operator_index == rCopyTo) {
1151  fprintf (fh, " CopyTo ==> %s", NameOfUserSym (pfx, pel->element_index, UserSym));
1152  } else if (pel->operator_index == rCopyFrom) {
1153  fprintf (fh, " CopyFrom <== %s", NameOfUserSym (pfx, pel->element_index, UserSym));
1154  } else if (OprInPlace (pel->operator_index)) {
1155  fprintf (fh, " <==> %s", NameOfUserSym (pfx, pel->element_index, UserSym));
1156  }
1157  if (pel->number_dest > 0) fprintf (fh, " <==dest(%i)", pel->number_dest);
1158  fprintf (fh, "\n");
1159  }
1160  return MagickTrue;
1161 }
1162 
1163 static void DestroyRPN (FxInfo * pfx)
1164 {
1165  pfx->numOprStack = 0;
1166  pfx->usedOprStack = 0;
1167  if (pfx->OperatorStack) pfx->OperatorStack = (OperatorE*) RelinquishMagickMemory (pfx->OperatorStack);
1168 
1169  pfx->numElements = 0;
1170  pfx->usedElements = 0;
1171  if (pfx->Elements) pfx->Elements = (ElementT*) RelinquishMagickMemory (pfx->Elements);
1172 
1173  pfx->usedUserSymbols = 0;
1174  if (pfx->UserSymbols) pfx->UserSymbols = (UserSymbolT*) RelinquishMagickMemory (pfx->UserSymbols);
1175 }
1176 
1177 static void DestroyFxRt (fxRtT * pfxrt)
1178 {
1179  pfxrt->usedValStack = 0;
1180  if (pfxrt->ValStack) pfxrt->ValStack = (fxFltType*) RelinquishMagickMemory (pfxrt->ValStack);
1181  if (pfxrt->UserSymVals) pfxrt->UserSymVals = (fxFltType*) RelinquishMagickMemory (pfxrt->UserSymVals);
1182 
1183  pfxrt->random_info = DestroyRandomInfo (pfxrt->random_info);
1184 }
1185 
1186 static size_t GetToken (FxInfo * pfx)
1187 /* Returns length of token that starts with an alpha,
1188  or 0 if it isn't a token that starts with an alpha.
1189  j0 and j1 have trailing digit.
1190  Also colours like "gray47" have more trailing digits.
1191  After initial alpha(s) also allow single "_", eg "standard_deviation".
1192  Does not advance pfx->pex.
1193  This splits "mean.r" etc.
1194 */
1195 {
1196 
1197  char * p = pfx->pex;
1198  size_t len = 0;
1199  *pfx->token = '\0';
1200  pfx->lenToken = 0;
1201  if (!isalpha((int)*p)) return 0;
1202 
1203  /* Regard strings that start "icc-" or "device-",
1204  followed by any number of alphas,
1205  as a token.
1206  */
1207 
1208  if (LocaleNCompare (p, "icc-", 4) == 0) {
1209  len = 4;
1210  p += 4;
1211  while (isalpha ((int)*p)) { len++; p++; }
1212  } else if (LocaleNCompare (p, "device-", 7) == 0) {
1213  len = 7;
1214  p += 7;
1215  while (isalpha ((int)*p)) { len++; p++; }
1216  } else {
1217  while (isalpha ((int)*p)) { len++; p++; }
1218  if (*p == '_') { len++; p++; }
1219  while (isalpha ((int)*p)) { len++; p++; }
1220  while (isdigit ((int)*p)) { len++; p++; }
1221  }
1222  if (len >= MaxTokenLen) {
1223  (void) ThrowMagickException (
1224  pfx->exception, GetMagickModule(), OptionError,
1225  "GetToken: too long", "%g at '%s'",
1226  (double) len, SetShortExp(pfx));
1227  len = MaxTokenLen;
1228  }
1229  if (len) {
1230  (void) CopyMagickString (pfx->token, pfx->pex, (len+1<MaxTokenLen)?len+1:MaxTokenLen);
1231  }
1232 
1233  pfx->lenToken = strlen (pfx->token);
1234  return len;
1235 }
1236 
1237 static MagickBooleanType TokenMaybeUserSymbol (FxInfo * pfx)
1238 {
1239  char * p = pfx->token;
1240  int i = 0;
1241  while (*p) {
1242  if (!isalpha ((int)*p++)) return MagickFalse;
1243  i++;
1244  }
1245  if (i < 2) return MagickFalse;
1246  return MagickTrue;
1247 }
1248 
1249 static MagickBooleanType AddElement (FxInfo * pfx, fxFltType val, int oprNum)
1250 {
1251  ElementT * pel;
1252 
1253  assert (oprNum <= rNull);
1254 
1255  if (++pfx->usedElements >= pfx->numElements) {
1256  if (!ExtendRPN (pfx)) return MagickFalse;
1257  }
1258 
1259  pel = &pfx->Elements[pfx->usedElements-1];
1260  pel->type = TypeOfOpr (oprNum);
1261  pel->val = val;
1262  pel->val1 = (fxFltType) 0;
1263  pel->val2 = (fxFltType) 0;
1264  pel->operator_index = oprNum;
1265  pel->do_push = MagickTrue;
1266  pel->element_index = 0;
1267  pel->channel_qual = NO_CHAN_QUAL;
1268  pel->img_attr_qual = aNull;
1269  pel->number_dest = 0;
1270  pel->exp_start = NULL;
1271  pel->exp_len = 0;
1272 
1273  if (oprNum <= oNull) pel->number_args = Operators[oprNum].number_args;
1274  else if (oprNum <= fNull) pel->number_args = Functions[oprNum-(int) FirstFunc].number_args;
1275  else if (oprNum <= aNull) pel->number_args = 0;
1276  else if (oprNum <= sNull) pel->number_args = 0;
1277  else pel->number_args = Controls[oprNum-(int) FirstCont].number_args;
1278 
1279  return MagickTrue;
1280 }
1281 
1282 static MagickBooleanType AddAddressingElement (FxInfo * pfx, int oprNum, int EleNdx)
1283 {
1284  ElementT * pel;
1285  if (!AddElement (pfx, (fxFltType) 0, oprNum)) return MagickFalse;
1286  pel = &pfx->Elements[pfx->usedElements-1];
1287  pel->element_index = EleNdx;
1288  if (oprNum == rGoto || oprNum == rGotoChk || oprNum == rIfZeroGoto || oprNum == rIfNotZeroGoto
1289  || oprNum == rZerStk)
1290  {
1291  pel->do_push = MagickFalse;
1292  }
1293 
1294  /* Note: for() may or may not need pushing,
1295  depending on whether the value is needed, eg "for(...)+2" or debug(for(...)).
1296  */
1297 
1298  return MagickTrue;
1299 }
1300 
1301 static MagickBooleanType AddColourElement (FxInfo * pfx, fxFltType val0, fxFltType val1, fxFltType val2)
1302 {
1303  ElementT * pel;
1304  if (!AddElement (pfx, val0, oNull)) return MagickFalse;
1305  pel = &pfx->Elements[pfx->usedElements-1];
1306  pel->val1 = val1;
1307  pel->val2 = val2;
1308  pel->type = etColourConstant;
1309  return MagickTrue;
1310 }
1311 
1312 static inline void SkipSpaces (FxInfo * pfx)
1313 {
1314  while (isspace ((int)*pfx->pex)) pfx->pex++;
1315 }
1316 
1317 static inline char PeekChar (FxInfo * pfx)
1318 {
1319  SkipSpaces (pfx);
1320  return *pfx->pex;
1321 }
1322 
1323 static inline MagickBooleanType PeekStr (FxInfo * pfx, const char * str)
1324 {
1325  SkipSpaces (pfx);
1326 
1327  return (LocaleNCompare (pfx->pex, str, strlen(str))==0 ? MagickTrue : MagickFalse);
1328 }
1329 
1330 static MagickBooleanType ExpectChar (FxInfo * pfx, char c)
1331 {
1332  if (PeekChar (pfx) != c) {
1333  (void) ThrowMagickException (
1334  pfx->exception, GetMagickModule(), OptionError,
1335  "Expected char", "'%c' at '%s'", c, SetShortExp (pfx));
1336  return MagickFalse;
1337  }
1338  pfx->pex++;
1339  return MagickTrue;
1340 }
1341 
1342 static int MaybeXYWH (FxInfo * pfx, ImgAttrE * pop)
1343 /* If ".x" or ".y" or ".width" or ".height" increments *pop and returns 1 to 4 .
1344  Otherwise returns 0.
1345 */
1346 {
1347  int ret=0;
1348 
1349  if (*pop != aPage && *pop != aPrintsize && *pop != aRes) return 0;
1350 
1351  if (PeekChar (pfx) != '.') return 0;
1352 
1353  if (!ExpectChar (pfx, '.')) return 0;
1354 
1355  (void) GetToken (pfx);
1356  if (LocaleCompare ("x", pfx->token)==0) ret=1;
1357  else if (LocaleCompare ("y", pfx->token)==0) ret=2;
1358  else if (LocaleCompare ("width", pfx->token)==0) ret=3;
1359  else if (LocaleCompare ("height", pfx->token)==0) ret=4;
1360 
1361  if (!ret)
1362  (void) ThrowMagickException (
1363  pfx->exception, GetMagickModule(), OptionError,
1364  "Invalid 'x' or 'y' or 'width' or 'height' token=", "'%s' at '%s'",
1365  pfx->token, SetShortExp(pfx));
1366 
1367  if (*pop == aPage) (*pop) = (ImgAttrE) ((int) *pop + ret);
1368  else {
1369  if (ret > 2) {
1370  (void) ThrowMagickException (
1371  pfx->exception, GetMagickModule(), OptionError,
1372  "Invalid 'width' or 'height' token=", "'%s' at '%s'",
1373  pfx->token, SetShortExp(pfx));
1374  } else {
1375  (*pop) = (ImgAttrE) ((int) *pop + ret);
1376  }
1377  }
1378  pfx->pex+=pfx->lenToken;
1379 
1380  return ret;
1381 }
1382 
1383 static MagickBooleanType ExtendOperatorStack (FxInfo * pfx)
1384 {
1385  pfx->numOprStack = (int) ceil (pfx->numOprStack * (1 + TableExtend));
1386  pfx->OperatorStack = (OperatorE*) ResizeMagickMemory (pfx->OperatorStack, (size_t) pfx->numOprStack * sizeof(OperatorE));
1387  if (!pfx->OperatorStack) {
1388  (void) ThrowMagickException (
1389  pfx->exception, GetMagickModule(), ResourceLimitFatalError,
1390  "OprStack", "%i",
1391  pfx->numOprStack);
1392  return MagickFalse;
1393  }
1394  return MagickTrue;
1395 }
1396 
1397 static MagickBooleanType PushOperatorStack (FxInfo * pfx, int op)
1398 {
1399  if (++pfx->usedOprStack >= pfx->numOprStack) {
1400  if (!ExtendOperatorStack (pfx))
1401  return MagickFalse;
1402  }
1403  pfx->OperatorStack[pfx->usedOprStack-1] = (OperatorE) op;
1404 
1405  if (pfx->maxUsedOprStack < pfx->usedOprStack)
1406  pfx->maxUsedOprStack = pfx->usedOprStack;
1407  return MagickTrue;
1408 }
1409 
1410 static OperatorE GetLeadingOp (FxInfo * pfx)
1411 {
1412  OperatorE op = oNull;
1413 
1414  if (*pfx->pex == '-') op = oUnaryMinus;
1415  else if (*pfx->pex == '+') op = oUnaryPlus;
1416  else if (*pfx->pex == '~') op = oBitNot;
1417  else if (*pfx->pex == '!') op = oLogNot;
1418  else if (*pfx->pex == '(') op = oOpenParen;
1419 
1420  return op;
1421 }
1422 
1423 static inline MagickBooleanType OprIsUnaryPrefix (OperatorE op)
1424 {
1425  return (op == oUnaryMinus || op == oUnaryPlus || op == oBitNot || op == oLogNot ? MagickTrue : MagickFalse);
1426 }
1427 
1428 static MagickBooleanType TopOprIsUnaryPrefix (FxInfo * pfx)
1429 {
1430  if (!pfx->usedOprStack) return MagickFalse;
1431 
1432  return OprIsUnaryPrefix (pfx->OperatorStack[pfx->usedOprStack-1]);
1433 }
1434 
1435 static MagickBooleanType PopOprOpenParen (FxInfo * pfx, OperatorE op)
1436 {
1437 
1438  if (!pfx->usedOprStack) return MagickFalse;
1439 
1440  if (pfx->OperatorStack[pfx->usedOprStack-1] != op) return MagickFalse;
1441 
1442  pfx->usedOprStack--;
1443 
1444  return MagickTrue;
1445 }
1446 
1447 static int GetCoordQualifier (FxInfo * pfx, int op)
1448 /* Returns -1 if invalid CoordQualifier, +1 if valid and appropriate.
1449 */
1450 {
1451  if (op != fU && op != fV && op != fS) return -1;
1452 
1453  (void) GetToken (pfx);
1454 
1455  if (pfx->lenToken != 1) {
1456  return -1;
1457  }
1458  if (*pfx->token != 'p' && *pfx->token != 'P') return -1;
1459  if (!GetFunction (pfx, fP)) return -1;
1460 
1461  return 1;
1462 }
1463 
1464 static PixelChannel GetChannelQualifier (FxInfo * pfx, int op)
1465 {
1466  if (op == fU || op == fV || op == fP ||
1467  op == fUP || op == fVP ||
1468  op == fS || (op >= (int) FirstImgAttr && op <= aNull)
1469  )
1470  {
1471  const ChannelT * pch = &Channels[0];
1472  (void) GetToken (pfx);
1473 
1474  while (*pch->str) {
1475  if (LocaleCompare (pch->str, pfx->token)==0) {
1476 
1477  if (op >= (int) FirstImgAttr && op <= (int) ((OperatorE)aNull) &&
1478  ChanIsVirtual (pch->pixel_channel)
1479  )
1480  {
1481  (void) ThrowMagickException (
1482  pfx->exception, GetMagickModule(), OptionError,
1483  "Can't have image attribute with channel qualifier at", "'%s' at '%s'",
1484  pfx->token, SetShortExp(pfx));
1485  return NO_CHAN_QUAL;
1486  }
1487 
1488  pfx->pex += pfx->lenToken;
1489  return pch->pixel_channel;
1490  }
1491  pch++;
1492  }
1493  }
1494  return NO_CHAN_QUAL;
1495 }
1496 
1497 static ImgAttrE GetImgAttrToken (FxInfo * pfx)
1498 {
1499  ImgAttrE ia = aNull;
1500  const char * iaStr;
1501  for (ia = FirstImgAttr; ia < aNull; ia=(ImgAttrE) (ia+1)) {
1502  iaStr = ImgAttrs[ia-(int) FirstImgAttr].str;
1503  if (LocaleCompare (iaStr, pfx->token)==0) {
1504  pfx->pex += strlen(pfx->token);
1505  if (ImgAttrs[ia-(int) FirstImgAttr].need_stats != MagickFalse) pfx->NeedStats = MagickTrue;
1506  MaybeXYWH (pfx, &ia);
1507  break;
1508  }
1509  }
1510 
1511  if (ia == aPage || ia == aPrintsize || ia == aRes) {
1512  (void) ThrowMagickException (
1513  pfx->exception, GetMagickModule(), OptionError,
1514  "Attribute", "'%s' needs qualifier at '%s'",
1515  iaStr, SetShortExp(pfx));
1516  }
1517 
1518  return ia;
1519 }
1520 
1521 static ImgAttrE GetImgAttrQualifier (FxInfo * pfx, int op)
1522 {
1523  ImgAttrE ia = aNull;
1524  if (op == (OperatorE)fU || op == (OperatorE)fV || op == (OperatorE)fP || op == (OperatorE)fS) {
1525  (void) GetToken (pfx);
1526  if (pfx->lenToken == 0) {
1527  return aNull;
1528  }
1529  ia = GetImgAttrToken (pfx);
1530  }
1531  return ia;
1532 }
1533 
1534 static MagickBooleanType IsQualifier (FxInfo * pfx)
1535 {
1536  if (PeekChar (pfx) == '.') {
1537  pfx->pex++;
1538  return MagickTrue;
1539  }
1540  return MagickFalse;
1541 }
1542 
1543 static ssize_t GetProperty (FxInfo * pfx, fxFltType *val)
1544 /* returns number of character to swallow.
1545  "-1" means invalid input
1546  "0" means no relevant input (don't swallow, but not an error)
1547 */
1548 {
1549  if (PeekStr (pfx, "%[")) {
1550  int level = 0;
1551  size_t len;
1552  char sProperty [MagickPathExtent];
1553  char * p = pfx->pex + 2;
1554 
1555  while (*p) {
1556 
1557  if (*p == '[') level++;
1558  else if (*p == ']') {
1559  if (level == 0) break;
1560  level--;
1561  }
1562  p++;
1563  }
1564  if (!*p || level != 0) {
1565  (void) ThrowMagickException (
1566  pfx->exception, GetMagickModule(), OptionError,
1567  "After '%[' expected ']' at", "'%s'",
1568  SetShortExp(pfx));
1569  return -1;
1570  }
1571 
1572  len = (size_t) (p - pfx->pex + 1);
1573  if (len > MaxTokenLen) {
1574  (void) ThrowMagickException (
1575  pfx->exception, GetMagickModule(), OptionError,
1576  "Too much text between '%[' and ']' at", "'%s'",
1577  SetShortExp(pfx));
1578  return -1;
1579  }
1580 
1581  (void) CopyMagickString (sProperty, pfx->pex, len+1);
1582  sProperty[len] = '\0';
1583  {
1584  char * tailptr;
1585  char * text;
1586  text = InterpretImageProperties (pfx->image->image_info, pfx->image,
1587  sProperty, pfx->exception);
1588  if (!text || !*text) {
1589  text = DestroyString(text);
1590  (void) ThrowMagickException (
1591  pfx->exception, GetMagickModule(), OptionError,
1592  "Unknown property", "'%s' at '%s'",
1593  sProperty, SetShortExp(pfx));
1594  return -1;
1595  }
1596 
1597  *val = strtold (text, &tailptr);
1598  if (text == tailptr) {
1599  text = DestroyString(text);
1600  (void) ThrowMagickException (
1601  pfx->exception, GetMagickModule(), OptionError,
1602  "Property", "'%s' text '%s' is not a number at '%s'",
1603  sProperty, text, SetShortExp(pfx));
1604  return -1;
1605  }
1606 
1607  text = DestroyString(text);
1608  }
1609  return ((ssize_t) len);
1610  }
1611 
1612  return 0;
1613 }
1614 
1615 static inline ssize_t GetConstantColour (FxInfo * pfx, fxFltType *v0, fxFltType *v1, fxFltType *v2)
1616 /* Finds named colour such as "blue" and colorspace function such as "lab(10,20,30)".
1617  Returns number of characters to swallow.
1618  Return -1 means apparently a constant colour, but with an error.
1619  Return 0 means not a constant colour, but not an error.
1620 */
1621 {
1622  PixelInfo
1623  colour;
1624 
1626  *dummy_exception = AcquireExceptionInfo ();
1627 
1628  char
1629  *p;
1630 
1631  MagickBooleanType
1632  IsGray,
1633  IsIcc,
1634  IsDev;
1635 
1636  char ColSp[MagickPathExtent];
1637  (void) CopyMagickString (ColSp, pfx->token, MaxTokenLen);
1638  p = ColSp + pfx->lenToken - 1;
1639  if (*p == 'a' || *p == 'A') *p = '\0';
1640 
1641  (void) GetPixelInfo (pfx->image, &colour);
1642 
1643  /* "gray" is both a colorspace and a named colour. */
1644 
1645  IsGray = (LocaleCompare (ColSp, "gray") == 0) ? MagickTrue : MagickFalse;
1646  IsIcc = (LocaleCompare (ColSp, "icc-color") == 0) ? MagickTrue : MagickFalse;
1647  IsDev = (LocaleNCompare (ColSp, "device-", 7) == 0) ? MagickTrue : MagickFalse;
1648 
1649  /* QueryColorCompliance will raise a warning if it isn't a colour, so we discard any exceptions.
1650  */
1651  if (!QueryColorCompliance (pfx->token, AllCompliance, &colour, dummy_exception) || IsGray) {
1652  ssize_t type = ParseCommandOption (MagickColorspaceOptions, MagickFalse, ColSp);
1653  if (type >= 0 || IsIcc || IsDev) {
1654  char * q = pfx->pex + pfx->lenToken;
1655  while (isspace((int) ((unsigned char) *q))) q++;
1656  if (*q == '(') {
1657  size_t lenfun;
1658  char sFunc[MagickPathExtent];
1659  while (*q && *q != ')') q++;
1660  if (!*q) {
1661  (void) ThrowMagickException (
1662  pfx->exception, GetMagickModule(), OptionError,
1663  "constant color missing ')'", "at '%s'",
1664  SetShortExp(pfx));
1665  dummy_exception = DestroyExceptionInfo (dummy_exception);
1666  return -1;
1667  }
1668  lenfun = (size_t) (q - pfx->pex + 1);
1669  if (lenfun > MaxTokenLen) {
1670  (void) ThrowMagickException (
1671  pfx->exception, GetMagickModule(), OptionError,
1672  "lenfun too long", "'%lu' at '%s'",
1673  (unsigned long) lenfun, SetShortExp(pfx));
1674  dummy_exception = DestroyExceptionInfo (dummy_exception);
1675  return -1;
1676  }
1677  (void) CopyMagickString (sFunc, pfx->pex, lenfun+1);
1678  if (QueryColorCompliance (sFunc, AllCompliance, &colour, dummy_exception)) {
1679  *v0 = QuantumScale*colour.red;
1680  *v1 = QuantumScale*colour.green;
1681  *v2 = QuantumScale*colour.blue;
1682  dummy_exception = DestroyExceptionInfo (dummy_exception);
1683  return (ssize_t)lenfun;
1684  }
1685  } else {
1686  (void) ThrowMagickException (
1687  pfx->exception, GetMagickModule(), OptionError,
1688  "colorspace but not a valid color with '(...)' at", "'%s'",
1689  SetShortExp(pfx));
1690  dummy_exception = DestroyExceptionInfo (dummy_exception);
1691  return -1;
1692  }
1693  }
1694  if (!IsGray) {
1695  dummy_exception = DestroyExceptionInfo (dummy_exception);
1696  return 0;
1697  }
1698  }
1699 
1700  *v0 = QuantumScale*colour.red;
1701  *v1 = QuantumScale*colour.green;
1702  *v2 = QuantumScale*colour.blue;
1703 
1704  dummy_exception = DestroyExceptionInfo (dummy_exception);
1705  return (ssize_t)strlen (pfx->token);
1706 }
1707 
1708 static inline ssize_t GetHexColour (FxInfo * pfx, fxFltType *v0, fxFltType *v1, fxFltType *v2)
1709 /* Returns number of characters to swallow.
1710  Negative return means it starts with '#', but invalid hex number.
1711 */
1712 {
1713  char * p;
1714  size_t len;
1715  PixelInfo colour;
1716 
1717  if (*pfx->pex != '#') return 0;
1718 
1719  /* find end of hex digits. */
1720  p = pfx->pex + 1;
1721  while (isxdigit ((int)*p)) p++;
1722  if (isalpha ((int)*p)) {
1723  (void) ThrowMagickException (
1724  pfx->exception, GetMagickModule(), OptionError,
1725  "Bad hex number at", "'%s'",
1726  SetShortExp(pfx));
1727  return -1;
1728  }
1729 
1730  len = (size_t) (p - pfx->pex);
1731  if (len < 1) return 0;
1732  if (len >= MaxTokenLen) {
1733  (void) ThrowMagickException (
1734  pfx->exception, GetMagickModule(), OptionError,
1735  "Hex colour too long at", "'%s'",
1736  SetShortExp(pfx));
1737  return -1;
1738  }
1739  (void) CopyMagickString (pfx->token, pfx->pex, len+1);
1740 
1741  (void) GetPixelInfo (pfx->image, &colour);
1742 
1743  if (!QueryColorCompliance (pfx->token, AllCompliance, &colour, pfx->exception)) {
1744  (void) ThrowMagickException (
1745  pfx->exception, GetMagickModule(), OptionError,
1746  "QueryColorCompliance rejected", "'%s' at '%s'",
1747  pfx->token, SetShortExp(pfx));
1748  return -1;
1749  }
1750 
1751  *v0 = QuantumScale*colour.red;
1752  *v1 = QuantumScale*colour.green;
1753  *v2 = QuantumScale*colour.blue;
1754 
1755  return (ssize_t) len;
1756 }
1757 
1758 static MagickBooleanType GetFunction (FxInfo * pfx, FunctionE fe)
1759 {
1760  /* A function, so get open-parens, n args, close-parens
1761  */
1762  const char * funStr = Functions[fe-(int) FirstFunc].str;
1763  int nArgs = Functions[fe-(int) FirstFunc].number_args;
1764  char chLimit = ')';
1765  char expChLimit = ')';
1766  const char *strLimit = ",)";
1767  OperatorE pushOp = oOpenParen;
1768 
1769  char * pExpStart;
1770 
1771  size_t lenExp = 0;
1772 
1773  int FndArgs = 0;
1774  int ndx0 = NULL_ADDRESS, ndx1 = NULL_ADDRESS, ndx2 = NULL_ADDRESS, ndx3 = NULL_ADDRESS;
1775 
1776  MagickBooleanType coordQual = MagickFalse;
1777  PixelChannel chQual = NO_CHAN_QUAL;
1778  ImgAttrE iaQual = aNull;
1779 
1780  pfx->pex += pfx->lenToken;
1781 
1782  if (fe == fP) {
1783  char p = PeekChar (pfx);
1784  if (p=='{') {
1785  (void) ExpectChar (pfx, '{');
1786  pushOp = oOpenBrace;
1787  strLimit = ",}";
1788  chLimit = '}';
1789  expChLimit = '}';
1790  } else if (p=='[') {
1791  (void) ExpectChar (pfx, '[');
1792  pushOp = oOpenBracket;
1793  strLimit = ",]";
1794  chLimit = ']';
1795  expChLimit = ']';
1796  } else {
1797  nArgs = 0;
1798  chLimit = ']';
1799  expChLimit = ']';
1800  }
1801  } else if (fe == fU) {
1802  char p = PeekChar (pfx);
1803  if (p=='[') {
1804  (void) ExpectChar (pfx, '[');
1805  pushOp = oOpenBracket;
1806  strLimit = ",]";
1807  chLimit = ']';
1808  expChLimit = ']';
1809  } else {
1810  nArgs = 0;
1811  chLimit = ']';
1812  expChLimit = ']';
1813  }
1814  } else if (fe == fV || fe == fS) {
1815  nArgs = 0;
1816  pushOp = oOpenBracket;
1817  chLimit = ']';
1818  expChLimit = ']';
1819  } else {
1820  if (!ExpectChar (pfx, '(')) return MagickFalse;
1821  }
1822  if (!PushOperatorStack (pfx, (int) pushOp)) return MagickFalse;
1823 
1824  pExpStart = pfx->pex;
1825  ndx0 = pfx->usedElements;
1826  if (fe==fDo) {
1827  (void) AddAddressingElement (pfx, rGoto, NULL_ADDRESS); /* address will be ndx1+1 */
1828  }
1829  while (nArgs > 0) {
1830  int FndOne = 0;
1831  if (TranslateStatementList (pfx, strLimit, &chLimit)) {
1832  FndOne = 1;
1833  } else {
1834  if (!*pfx->pex) {
1835  (void) ThrowMagickException (
1836  pfx->exception, GetMagickModule(), OptionError,
1837  "For function", "'%s' expected ')' at '%s'",
1838  funStr, SetShortExp(pfx));
1839  return MagickFalse;
1840  }
1841  /* Maybe don't break because other expressions may be not empty. */
1842  if (!chLimit) break;
1843  if (fe == fP || fe == fS|| fe == fIf) {
1844  (void) AddElement (pfx, (fxFltType) 0, oNull);
1845  FndOne = 1;
1846  }
1847  }
1848 
1849  if (strchr (strLimit, chLimit)==NULL) {
1850  (void) ThrowMagickException (
1851  pfx->exception, GetMagickModule(), OptionError,
1852  "For function", "'%s' expected one of '%s' after expression but found '%c' at '%s'",
1853  funStr, strLimit, chLimit ? chLimit : ' ', SetShortExp(pfx));
1854  return MagickFalse;
1855  }
1856  if (FndOne) {
1857  FndArgs++;
1858  nArgs--;
1859  }
1860  switch (FndArgs) {
1861  case 1:
1862  if (ndx1 != NULL_ADDRESS) {
1863  (void) ThrowMagickException (
1864  pfx->exception, GetMagickModule(), OptionError,
1865  "For function", "'%s' required argument is missing at '%s'",
1866  funStr, SetShortExp(pfx));
1867  return MagickFalse;
1868  }
1869  ndx1 = pfx->usedElements;
1870  if (fe==fWhile || fe==fIf) {
1871  (void) AddAddressingElement (pfx, rIfZeroGoto, NULL_ADDRESS); /* address will be ndx2+1 */
1872  } else if (fe==fDo) {
1873  (void) AddAddressingElement (pfx, rIfZeroGoto, NULL_ADDRESS); /* address will be ndx2+1 */
1874  } else if (fe==fFor) {
1875  pfx->Elements[pfx->usedElements-1].do_push = MagickFalse;
1876  }
1877  break;
1878  case 2:
1879  if (ndx2 != NULL_ADDRESS) {
1880  (void) ThrowMagickException (
1881  pfx->exception, GetMagickModule(), OptionError,
1882  "For function", "'%s' required argument is missing at '%s'",
1883  funStr, SetShortExp(pfx));
1884  return MagickFalse;
1885  }
1886  ndx2 = pfx->usedElements;
1887  if (fe==fWhile) {
1888  pfx->Elements[pfx->usedElements-1].do_push = MagickFalse;
1889  (void) AddAddressingElement (pfx, rGotoChk, ndx0);
1890  } else if (fe==fDo) {
1891  pfx->Elements[pfx->usedElements-1].do_push = MagickFalse;
1892  (void) AddAddressingElement (pfx, rGotoChk, ndx0 + 1);
1893  } else if (fe==fFor) {
1894  (void) AddAddressingElement (pfx, rIfZeroGoto, NULL_ADDRESS); /* address will be ndx3 */
1895  pfx->Elements[pfx->usedElements-1].do_push = MagickTrue; /* we may need return from for() */
1896  (void) AddAddressingElement (pfx, rZerStk, NULL_ADDRESS);
1897  } else if (fe==fIf) {
1898  (void) AddAddressingElement (pfx, rGoto, NULL_ADDRESS); /* address will be ndx3 */
1899  }
1900  break;
1901  case 3:
1902  if (ndx3 != NULL_ADDRESS) {
1903  (void) ThrowMagickException (
1904  pfx->exception, GetMagickModule(), OptionError,
1905  "For function", "'%s' required argument is missing at '%s'",
1906  funStr, SetShortExp(pfx));
1907  return MagickFalse;
1908  }
1909  if (fe==fFor) {
1910  pfx->Elements[pfx->usedElements-1].do_push = MagickFalse;
1911  (void) AddAddressingElement (pfx, rGotoChk, ndx1);
1912  }
1913  ndx3 = pfx->usedElements;
1914  break;
1915  default:
1916  break;
1917  }
1918  if (chLimit == expChLimit) {
1919  lenExp = (size_t) (pfx->pex - pExpStart - 1);
1920  break;
1921  }
1922  } /* end while args of a function */
1923  if (chLimit && chLimit != expChLimit && chLimit != ',' ) {
1924  (void) ThrowMagickException (
1925  pfx->exception, GetMagickModule(), OptionError,
1926  "For function", "'%s' expected '%c', found '%c' at '%s'",
1927  funStr, expChLimit, chLimit ? chLimit : ' ', SetShortExp(pfx));
1928  return MagickFalse;
1929  }
1930 
1931  if (fe == fP || fe == fS || fe == fU || fe == fChannel) {
1932  while (FndArgs < Functions[fe-(int) FirstFunc].number_args) {
1933  (void) AddElement (pfx, (fxFltType) 0, oNull);
1934  FndArgs++;
1935  }
1936  }
1937 
1938  if (FndArgs > Functions[fe-(int) FirstFunc].number_args)
1939  {
1940  if (fe==fChannel) {
1941  (void) ThrowMagickException (
1942  pfx->exception, GetMagickModule(), OptionError,
1943  "For function", "'%s' expected up to %i arguments, found '%i' at '%s'",
1944  funStr, Functions[fe-(int) FirstFunc].number_args, FndArgs, SetShortExp(pfx));
1945  } else {
1946  (void) ThrowMagickException (
1947  pfx->exception, GetMagickModule(), OptionError,
1948  "For function", "'%s' expected %i arguments, found '%i' at '%s'",
1949  funStr, Functions[fe-(int) FirstFunc].number_args, FndArgs, SetShortExp(pfx));
1950  }
1951  return MagickFalse;
1952  }
1953  if (FndArgs < Functions[fe-(int) FirstFunc].number_args) {
1954  (void) ThrowMagickException (
1955  pfx->exception, GetMagickModule(), OptionError,
1956  "For function", "'%s' expected %i arguments, found too few (%i) at '%s'",
1957  funStr, Functions[fe-(int) FirstFunc].number_args, FndArgs, SetShortExp(pfx));
1958  return MagickFalse;
1959  }
1960  if (fe != fS && fe != fV && FndArgs == 0 && Functions[fe-(int) FirstFunc].number_args == 0) {
1961  /* This is for "rand()" and similar. */
1962  chLimit = expChLimit;
1963  if (!ExpectChar (pfx, ')')) return MagickFalse;
1964  }
1965 
1966  if (chLimit != expChLimit) {
1967  (void) ThrowMagickException (
1968  pfx->exception, GetMagickModule(), OptionError,
1969  "For function", "'%s', arguments don't end with '%c' at '%s'",
1970  funStr, expChLimit, SetShortExp(pfx));
1971  return MagickFalse;
1972  }
1973  if (!PopOprOpenParen (pfx, pushOp)) {
1974  (void) ThrowMagickException (
1975  pfx->exception, GetMagickModule(), OptionError,
1976  "Bug: For function", "'%s' tos not '%s' at '%s'",
1977  funStr, Operators[pushOp].str, SetShortExp(pfx));
1978  return MagickFalse;
1979  }
1980 
1981  if (IsQualifier (pfx)) {
1982 
1983  if (fe == fU || fe == fV || fe == fS) {
1984 
1985  coordQual = (GetCoordQualifier (pfx, (int) fe) == 1) ? MagickTrue : MagickFalse;
1986 
1987  if (coordQual) {
1988 
1989  /* Remove last element, which should be fP */
1990  ElementT * pel = &pfx->Elements[pfx->usedElements-1];
1991  if (pel->operator_index != fP) {
1992  (void) ThrowMagickException (
1993  pfx->exception, GetMagickModule(), OptionError,
1994  "Bug: For function", "'%s' last element not 'p' at '%s'",
1995  funStr, SetShortExp(pfx));
1996  return MagickFalse;
1997  }
1998  chQual = pel->channel_qual;
1999  expChLimit = (pel->is_relative) ? ']' : '}';
2000  pfx->usedElements--;
2001  if (fe == fU) fe = fUP;
2002  else if (fe == fV) fe = fVP;
2003  else if (fe == fS) fe = fSP;
2004  funStr = Functions[fe-(int) FirstFunc].str;
2005  }
2006  }
2007 
2008  if ( chQual == NO_CHAN_QUAL &&
2009  (fe == fP || fe == fS || fe == fSP || fe == fU || fe == fUP || fe == fV || fe == fVP)
2010  )
2011  {
2012  chQual = GetChannelQualifier (pfx, (int) fe);
2013  }
2014 
2015  if (chQual == NO_CHAN_QUAL && (fe == fU || fe == fV || fe == fS)) {
2016  /* Note: we don't allow "p.mean" etc. */
2017  iaQual = GetImgAttrQualifier (pfx, (int) fe);
2018  }
2019  if (IsQualifier (pfx) && chQual == NO_CHAN_QUAL && iaQual != aNull) {
2020  chQual = GetChannelQualifier (pfx, (int) fe);
2021  }
2022  if (coordQual && iaQual != aNull) {
2023  (void) ThrowMagickException (
2024  pfx->exception, GetMagickModule(), OptionError,
2025  "For function", "'%s', can't have qualifiers 'p' and image attribute '%s' at '%s'",
2026  funStr, pfx->token, SetShortExp(pfx));
2027  return MagickFalse;
2028  }
2029  if (!coordQual && chQual == NO_CHAN_QUAL && iaQual == aNull) {
2030  (void) ThrowMagickException (
2031  pfx->exception, GetMagickModule(), OptionError,
2032  "For function", "'%s', bad qualifier '%s' at '%s'",
2033  funStr, pfx->token, SetShortExp(pfx));
2034  return MagickFalse;
2035  }
2036  if (!coordQual && chQual == CompositePixelChannel && iaQual == aNull) {
2037  (void) ThrowMagickException (
2038  pfx->exception, GetMagickModule(), OptionError,
2039  "For function", "'%s', bad composite qualifier '%s' at '%s'",
2040  funStr, pfx->token, SetShortExp(pfx));
2041  return MagickFalse;
2042  }
2043 
2044  if (chQual == HUE_CHANNEL || chQual == SAT_CHANNEL || chQual == LIGHT_CHANNEL) {
2045  pfx->NeedHsl = MagickTrue;
2046 
2047  if (iaQual >= FirstImgAttr && iaQual < aNull) {
2048  (void) ThrowMagickException (
2049  pfx->exception, GetMagickModule(), OptionError,
2050  "Can't have image attribute with HLS qualifier at", "'%s'",
2051  SetShortExp(pfx));
2052  return MagickFalse;
2053  }
2054  }
2055  }
2056 
2057  if (iaQual != aNull && chQual != NO_CHAN_QUAL) {
2058  if (ImgAttrs[iaQual-(int) FirstImgAttr].need_stats == MagickFalse) {
2059  (void) ThrowMagickException (
2060  pfx->exception, GetMagickModule(), OptionError,
2061  "Can't have image attribute ", "'%s' with channel qualifier '%s' at '%s'",
2062  ImgAttrs[iaQual-(int) FirstImgAttr].str,
2063  pfx->token, SetShortExp(pfx));
2064  return MagickFalse;
2065  } else {
2066  if (ChanIsVirtual (chQual)) {
2067  (void) ThrowMagickException (
2068  pfx->exception, GetMagickModule(), OptionError,
2069  "Can't have statistical image attribute ", "'%s' with virtual channel qualifier '%s' at '%s'",
2070  ImgAttrs[iaQual-(int) FirstImgAttr].str,
2071  pfx->token, SetShortExp(pfx));
2072  return MagickFalse;
2073  }
2074  }
2075  }
2076 
2077  if (fe==fWhile) {
2078  pfx->Elements[ndx1].element_index = ndx2+1;
2079  } else if (fe==fDo) {
2080  pfx->Elements[ndx0].element_index = ndx1+1;
2081  pfx->Elements[ndx1].element_index = ndx2+1;
2082  } else if (fe==fFor) {
2083  pfx->Elements[ndx2].element_index = ndx3;
2084  } else if (fe==fIf) {
2085  pfx->Elements[ndx1].element_index = ndx2 + 1;
2086  pfx->Elements[ndx2].element_index = ndx3;
2087  } else {
2088  if (fe == fU && iaQual == aNull) {
2089  ElementT * pel = &pfx->Elements[pfx->usedElements-1];
2090  if (pel->type == etConstant && pel->val == 0.0) {
2091  pfx->usedElements--;
2092  fe = fU0;
2093  }
2094  }
2095  (void) AddElement (pfx, (fxFltType) 0, (int) fe);
2096  if (fe == fP || fe == fU || fe == fU0 || fe == fUP ||
2097  fe == fV || fe == fVP || fe == fS || fe == fSP)
2098  {
2099  ElementT * pel = &pfx->Elements[pfx->usedElements-1];
2100  pel->is_relative = (expChLimit == ']' ? MagickTrue : MagickFalse);
2101  if (chQual >= 0) pel->channel_qual = chQual;
2102  if (iaQual != aNull && (fe == fU || fe == fV || fe == fS)) {
2103  /* Note: we don't allow "p[2,3].mean" or "p.mean" etc. */
2104  pel->img_attr_qual = iaQual;
2105  }
2106  }
2107  }
2108 
2109  if (pExpStart && lenExp) {
2110  ElementT * pel = &pfx->Elements[pfx->usedElements-1];
2111  pel->exp_start = pExpStart;
2112  pel->exp_len = lenExp;
2113  }
2114 
2115  if (fe == fDebug)
2116  pfx->ContainsDebug = MagickTrue;
2117 
2118  return MagickTrue;
2119 }
2120 
2121 static MagickBooleanType IsStealth (int op)
2122 {
2123  return (op == fU0 || op == fUP || op == fSP || op == fVP ||
2124  (op >= FirstCont && op <= rNull) ? MagickTrue : MagickFalse
2125  );
2126 }
2127 
2128 static MagickBooleanType GetOperand (
2129  FxInfo * pfx, MagickBooleanType * UserSymbol, MagickBooleanType * NewUserSymbol, int * UserSymNdx,
2130  MagickBooleanType * needPopAll)
2131 {
2132 
2133  *NewUserSymbol = *UserSymbol = MagickFalse;
2134  *UserSymNdx = NULL_ADDRESS;
2135 
2136  SkipSpaces (pfx);
2137  if (!*pfx->pex) return MagickFalse;
2138  (void) GetToken (pfx);
2139 
2140  if (pfx->lenToken==0) {
2141 
2142  /* Try '(' or unary prefix
2143  */
2144  OperatorE op = GetLeadingOp (pfx);
2145  if (op==oOpenParen) {
2146  char chLimit = '\0';
2147  if (!PushOperatorStack (pfx, (int) op)) return MagickFalse;
2148  pfx->pex++;
2149  if (!TranslateExpression (pfx, ")", &chLimit, needPopAll)) {
2150  (void) ThrowMagickException (
2151  pfx->exception, GetMagickModule(), OptionError,
2152  "Empty expression in parentheses at", "'%s'",
2153  SetShortExp(pfx));
2154  return MagickFalse;
2155  }
2156  if (chLimit != ')') {
2157  (void) ThrowMagickException (
2158  pfx->exception, GetMagickModule(), OptionError,
2159  "'(' but no ')' at", "'%s'",
2160  SetShortExp(pfx));
2161  return MagickFalse;
2162  }
2163  /* Top of opr stack should be '('. */
2164  if (!PopOprOpenParen (pfx, oOpenParen)) {
2165  (void) ThrowMagickException (
2166  pfx->exception, GetMagickModule(), OptionError,
2167  "Bug: tos not '(' at", "'%s'",
2168  SetShortExp(pfx));
2169  return MagickFalse;
2170  }
2171  return MagickTrue;
2172  } else if (OprIsUnaryPrefix (op)) {
2173  if (!PushOperatorStack (pfx, (int) op)) return MagickFalse;
2174  pfx->pex++;
2175  SkipSpaces (pfx);
2176  if (!*pfx->pex) return MagickFalse;
2177 
2178  if (!GetOperand (pfx, UserSymbol, NewUserSymbol, UserSymNdx, needPopAll)) {
2179  (void) ThrowMagickException (
2180  pfx->exception, GetMagickModule(), OptionError,
2181  "After unary, bad operand at", "'%s'",
2182  SetShortExp(pfx));
2183  return MagickFalse;
2184  }
2185 
2186  if (*NewUserSymbol) {
2187  (void) ThrowMagickException (
2188  pfx->exception, GetMagickModule(), OptionError,
2189  "After unary, NewUserSymbol at", "'%s'",
2190  SetShortExp(pfx));
2191  return MagickFalse;
2192  }
2193 
2194  if (*UserSymbol) {
2195  (void) AddAddressingElement (pfx, rCopyFrom, *UserSymNdx);
2196  *UserSymNdx = NULL_ADDRESS;
2197 
2198  *UserSymbol = MagickFalse;
2199  *NewUserSymbol = MagickFalse;
2200  }
2201 
2202  (void) GetToken (pfx);
2203  return MagickTrue;
2204  } else if (*pfx->pex == '#') {
2205  fxFltType v0=0, v1=0, v2=0;
2206  ssize_t lenToken = GetHexColour (pfx, &v0, &v1, &v2);
2207  if (lenToken < 0) {
2208  (void) ThrowMagickException (
2209  pfx->exception, GetMagickModule(), OptionError,
2210  "Bad hex number at", "'%s'",
2211  SetShortExp(pfx));
2212  return MagickFalse;
2213  } else if (lenToken > 0) {
2214  (void) AddColourElement (pfx, v0, v1, v2);
2215  pfx->pex+=lenToken;
2216  }
2217  return MagickTrue;
2218  }
2219 
2220  /* Try a constant number.
2221  */
2222  {
2223  char * tailptr;
2224  ssize_t lenOptArt;
2225  fxFltType val = strtold (pfx->pex, &tailptr);
2226  if (pfx->pex != tailptr) {
2227  pfx->pex = tailptr;
2228  if (*tailptr) {
2229  /* Could have "prefix" K, Ki, M etc.
2230  See https://en.wikipedia.org/wiki/Metric_prefix
2231  and https://en.wikipedia.org/wiki/Binary_prefix
2232  */
2233  double Pow = 0.0;
2234  const char Prefixes[] = "yzafpnum.kMGTPEZY";
2235  const char * pSi = strchr (Prefixes, *tailptr);
2236  if (pSi && *pSi != '.') Pow = (double) ((pSi - Prefixes) * 3 - 24);
2237  else if (*tailptr == 'c') Pow = -2;
2238  else if (*tailptr == 'h') Pow = 2;
2239  else if (*tailptr == 'k') Pow = 3;
2240  if (Pow != 0.0) {
2241  if (*(++pfx->pex) == 'i') {
2242  val *= pow (2.0, Pow/0.3);
2243  pfx->pex++;
2244  } else {
2245  val *= pow (10.0, Pow);
2246  }
2247  }
2248  }
2249  (void) AddElement (pfx, val, oNull);
2250  return MagickTrue;
2251  }
2252 
2253  val = (fxFltType) 0;
2254  lenOptArt = GetProperty (pfx, &val);
2255  if (lenOptArt < 0) return MagickFalse;
2256  if (lenOptArt > 0) {
2257  (void) AddElement (pfx, val, oNull);
2258  pfx->pex += lenOptArt;
2259  return MagickTrue;
2260  }
2261  }
2262 
2263  } /* end of lenToken==0 */
2264 
2265  if (pfx->lenToken > 0) {
2266  /* Try a constant
2267  */
2268  {
2269  ConstantE ce;
2270  for (ce = (ConstantE)0; ce < cNull; ce=(ConstantE) (ce+1)) {
2271  const char * ceStr = Constants[ce].str;
2272  if (LocaleCompare (ceStr, pfx->token)==0) {
2273  break;
2274  }
2275  }
2276 
2277  if (ce != cNull) {
2278  (void) AddElement (pfx, Constants[ce].val, oNull);
2279  pfx->pex += pfx->lenToken;
2280  return MagickTrue;
2281  }
2282  }
2283 
2284  /* Try a function
2285  */
2286  {
2287  FunctionE fe;
2288  for (fe = FirstFunc; fe < fNull; fe=(FunctionE) (fe+1)) {
2289  const char * feStr = Functions[fe-(int) FirstFunc].str;
2290  if (LocaleCompare (feStr, pfx->token)==0) {
2291  break;
2292  }
2293  }
2294 
2295  if (fe == fV && pfx->ImgListLen < 2) {
2296  (void) ThrowMagickException (
2297  pfx->exception, GetMagickModule(), OptionError,
2298  "Symbol 'v' but fewer than two images at", "'%s'",
2299  SetShortExp(pfx));
2300  return MagickFalse;
2301  }
2302 
2303  if (IsStealth ((int) fe)) {
2304  (void) ThrowMagickException (
2305  pfx->exception, GetMagickModule(), OptionError,
2306  "Function", "'%s' not permitted at '%s'",
2307  pfx->token, SetShortExp(pfx));
2308  }
2309 
2310  if (fe == fDo || fe == fFor || fe == fIf || fe == fWhile) {
2311  *needPopAll = MagickTrue;
2312  }
2313 
2314  if (fe != fNull) return (GetFunction (pfx, fe));
2315  }
2316 
2317  /* Try image attribute
2318  */
2319  {
2320  ImgAttrE ia = GetImgAttrToken (pfx);
2321  if (ia != aNull) {
2322  fxFltType val = 0;
2323  (void) AddElement (pfx, val, (int) ia);
2324 
2325  if (ImgAttrs[ia-(int) FirstImgAttr].need_stats != MagickFalse) {
2326  if (IsQualifier (pfx)) {
2327  PixelChannel chQual = GetChannelQualifier (pfx, (int) ia);
2328  ElementT * pel;
2329  if (chQual == NO_CHAN_QUAL) {
2330  (void) ThrowMagickException (
2331  pfx->exception, GetMagickModule(), OptionError,
2332  "Bad channel qualifier at", "'%s'",
2333  SetShortExp(pfx));
2334  return MagickFalse;
2335  }
2336  /* Adjust the element */
2337  pel = &pfx->Elements[pfx->usedElements-1];
2338  pel->channel_qual = chQual;
2339  }
2340  }
2341  return MagickTrue;
2342  }
2343  }
2344 
2345  /* Try symbol
2346  */
2347  {
2348  SymbolE se;
2349  for (se = FirstSym; se < sNull; se=(SymbolE) (se+1)) {
2350  const char * seStr = Symbols[se-(int) FirstSym].str;
2351  if (LocaleCompare (seStr, pfx->token)==0) {
2352  break;
2353  }
2354  }
2355  if (se != sNull) {
2356  fxFltType val = 0;
2357  (void) AddElement (pfx, val, (int) se);
2358  pfx->pex += pfx->lenToken;
2359 
2360  if (se==sHue || se==sSaturation || se==sLightness) pfx->NeedHsl = MagickTrue;
2361  return MagickTrue;
2362  }
2363  }
2364 
2365  /* Try constant colour.
2366  */
2367  {
2368  fxFltType v0, v1, v2;
2369  ssize_t ColLen = GetConstantColour (pfx, &v0, &v1, &v2);
2370  if (ColLen < 0) return MagickFalse;
2371  if (ColLen > 0) {
2372  (void) AddColourElement (pfx, v0, v1, v2);
2373  pfx->pex+=ColLen;
2374  return MagickTrue;
2375  }
2376  }
2377 
2378  /* Try image artifact.
2379  */
2380  {
2381  const char *artifact;
2382  artifact = GetImageArtifact (pfx->image, pfx->token);
2383  if (artifact != (const char *) NULL) {
2384  char * tailptr;
2385  fxFltType val = strtold (artifact, &tailptr);
2386  if (pfx->token == tailptr) {
2387  (void) ThrowMagickException (
2388  pfx->exception, GetMagickModule(), OptionError,
2389  "Artifact", "'%s' has value '%s', not a number, at '%s'",
2390  pfx->token, artifact, SetShortExp(pfx));
2391  return MagickFalse;
2392  }
2393  (void) AddElement (pfx, val, oNull);
2394  pfx->pex+=pfx->lenToken;
2395  return MagickTrue;
2396  }
2397  }
2398 
2399  /* Try user symbols. If it is, don't AddElement yet.
2400  */
2401  if (TokenMaybeUserSymbol (pfx)) {
2402  *UserSymbol = MagickTrue;
2403  *UserSymNdx = FindUserSymbol (pfx, pfx->token);
2404  if (*UserSymNdx == NULL_ADDRESS) {
2405  *UserSymNdx = AddUserSymbol (pfx, pfx->pex, pfx->lenToken);
2406  *NewUserSymbol = MagickTrue;
2407  } else {
2408  }
2409  pfx->pex += pfx->lenToken;
2410 
2411  return MagickTrue;
2412  }
2413  }
2414 
2415  (void) ThrowMagickException (
2416  pfx->exception, GetMagickModule(), OptionError,
2417  "Expected operand at", "'%s'",
2418  SetShortExp(pfx));
2419 
2420  return MagickFalse;
2421 }
2422 
2423 static inline MagickBooleanType IsRealOperator (OperatorE op)
2424 {
2425  return (op < oOpenParen || op > oCloseBrace) ? MagickTrue : MagickFalse;
2426 }
2427 
2428 static inline MagickBooleanType ProcessTernaryOpr (FxInfo * pfx, TernaryT * ptern)
2429 /* Ternary operator "... ? ... : ..."
2430  returns false iff we have exception
2431 */
2432 {
2433  if (pfx->usedOprStack == 0)
2434  return MagickFalse;
2435  if (pfx->OperatorStack[pfx->usedOprStack-1] == oQuery) {
2436  if (ptern->addr_query != NULL_ADDRESS) {
2437  (void) ThrowMagickException (
2438  pfx->exception, GetMagickModule(), OptionError,
2439  "Already have '?' in sub-expression at", "'%s'",
2440  SetShortExp(pfx));
2441  return MagickFalse;
2442  }
2443  if (ptern->addr_colon != NULL_ADDRESS) {
2444  (void) ThrowMagickException (
2445  pfx->exception, GetMagickModule(), OptionError,
2446  "Already have ':' in sub-expression at", "'%s'",
2447  SetShortExp(pfx));
2448  return MagickFalse;
2449  }
2450  pfx->usedOprStack--;
2451  ptern->addr_query = pfx->usedElements;
2452  (void) AddAddressingElement (pfx, rIfZeroGoto, NULL_ADDRESS);
2453  /* address will be one after the Colon address. */
2454  }
2455  else if (pfx->OperatorStack[pfx->usedOprStack-1] == oColon) {
2456  if (ptern->addr_query == NULL_ADDRESS) {
2457  (void) ThrowMagickException (
2458  pfx->exception, GetMagickModule(), OptionError,
2459  "Need '?' in sub-expression at", "'%s'",
2460  SetShortExp(pfx));
2461  return MagickFalse;
2462  }
2463  if (ptern->addr_colon != NULL_ADDRESS) {
2464  (void) ThrowMagickException (
2465  pfx->exception, GetMagickModule(), OptionError,
2466  "Already have ':' in sub-expression at", "'%s'",
2467  SetShortExp(pfx));
2468  return MagickFalse;
2469  }
2470  pfx->usedOprStack--;
2471  ptern->addr_colon = pfx->usedElements;
2472  pfx->Elements[pfx->usedElements-1].do_push = MagickTrue;
2473  (void) AddAddressingElement (pfx, rGoto, NULL_ADDRESS);
2474  /* address will be after the subexpression */
2475  }
2476  return MagickTrue;
2477 }
2478 
2479 static MagickBooleanType GetOperator (
2480  FxInfo * pfx,
2481  MagickBooleanType * Assign, MagickBooleanType * Update, MagickBooleanType * IncrDecr)
2482 {
2483  OperatorE op;
2484  size_t len = 0;
2485  MagickBooleanType DoneIt = MagickFalse;
2486  SkipSpaces (pfx);
2487  for (op = (OperatorE)0; op != oNull; op=(OperatorE) (op+1)) {
2488  const char * opStr = Operators[op].str;
2489  len = strlen(opStr);
2490  if (LocaleNCompare (opStr, pfx->pex, len)==0) {
2491  break;
2492  }
2493  }
2494 
2495  if (!IsRealOperator (op)) {
2496  (void) ThrowMagickException (
2497  pfx->exception, GetMagickModule(), OptionError,
2498  "Not a real operator at", "'%s'",
2499  SetShortExp(pfx));
2500  return MagickFalse;
2501  }
2502 
2503  if (op==oNull) {
2504  (void) ThrowMagickException (
2505  pfx->exception, GetMagickModule(), OptionError,
2506  "Expected operator at", "'%s'",
2507  SetShortExp(pfx));
2508  return MagickFalse;
2509  }
2510 
2511  *Assign = (op==oAssign) ? MagickTrue : MagickFalse;
2512  *Update = OprInPlace ((int) op);
2513  *IncrDecr = (op == oPlusPlus || op == oSubSub) ? MagickTrue : MagickFalse;
2514 
2515  /* while top of OperatorStack is not empty and is not open-parens or assign,
2516  and top of OperatorStack is higher precedence than new op,
2517  then move top of OperatorStack to Element list.
2518  */
2519 
2520  while (pfx->usedOprStack > 0) {
2521  OperatorE top = pfx->OperatorStack[pfx->usedOprStack-1];
2522  int precTop, precNew;
2523  if (top == oOpenParen || top == oAssign || OprInPlace ((int) top)) break;
2524  precTop = Operators[top].precedence;
2525  precNew = Operators[op].precedence;
2526  /* Assume left associativity.
2527  If right assoc, this would be "<=".
2528  */
2529  if (precTop < precNew) break;
2530  (void) AddElement (pfx, (fxFltType) 0, (int) top);
2531  pfx->usedOprStack--;
2532  }
2533 
2534  /* If new op is close paren, and stack top is open paren,
2535  remove stack top.
2536  */
2537  if (op==oCloseParen) {
2538  if (pfx->usedOprStack == 0) {
2539  (void) ThrowMagickException (
2540  pfx->exception, GetMagickModule(), OptionError,
2541  "Found ')' but nothing on stack at", "'%s'",
2542  SetShortExp(pfx));
2543  return MagickFalse;
2544  }
2545 
2546  if (pfx->OperatorStack[pfx->usedOprStack-1] != oOpenParen) {
2547  (void) ThrowMagickException (
2548  pfx->exception, GetMagickModule(), OptionError,
2549  "Found ')' but no '(' on stack at", "'%s'",
2550  SetShortExp(pfx));
2551  return MagickFalse;
2552  }
2553  pfx->usedOprStack--;
2554  DoneIt = MagickTrue;
2555  }
2556 
2557  if (!DoneIt) {
2558  if (!PushOperatorStack (pfx, (int) op)) return MagickFalse;
2559  }
2560 
2561  pfx->pex += len;
2562 
2563  return MagickTrue;
2564 }
2565 
2566 static MagickBooleanType ResolveTernaryAddresses (FxInfo * pfx, TernaryT * ptern)
2567 {
2568  if (ptern->addr_query == NULL_ADDRESS && ptern->addr_colon == NULL_ADDRESS)
2569  return MagickTrue;
2570 
2571  if (ptern->addr_query != NULL_ADDRESS && ptern->addr_colon != NULL_ADDRESS) {
2572  pfx->Elements[ptern->addr_query].element_index = ptern->addr_colon + 1;
2573  pfx->Elements[ptern->addr_colon].element_index = pfx->usedElements;
2574  ptern->addr_query = NULL_ADDRESS;
2575  ptern->addr_colon = NULL_ADDRESS;
2576  } else if (ptern->addr_query != NULL_ADDRESS) {
2577  (void) ThrowMagickException (
2578  pfx->exception, GetMagickModule(), OptionError,
2579  "'?' with no corresponding ':'", "'%s' at '%s'",
2580  pfx->token, SetShortExp(pfx));
2581  return MagickFalse;
2582  } else if (ptern->addr_colon != NULL_ADDRESS) {
2583  (void) ThrowMagickException (
2584  pfx->exception, GetMagickModule(), OptionError,
2585  "':' with no corresponding '?'", "'%s' at '%s'",
2586  pfx->token, SetShortExp(pfx));
2587  return MagickFalse;
2588  }
2589  return MagickTrue;
2590 }
2591 
2592 static MagickBooleanType TranslateExpression (
2593  FxInfo * pfx, const char * strLimit, char * chLimit, MagickBooleanType * needPopAll)
2594 {
2595  /* There should be only one New per expression (oAssign), but can be many Old.
2596  */
2597  MagickBooleanType UserSymbol, NewUserSymbol;
2598  int UserSymNdx0, UserSymNdx1;
2599 
2600  MagickBooleanType
2601  Assign = MagickFalse,
2602  Update = MagickFalse,
2603  IncrDecr = MagickFalse;
2604 
2605  int StartEleNdx;
2606 
2607  TernaryT ternary;
2608  ternary.addr_query = NULL_ADDRESS;
2609  ternary.addr_colon = NULL_ADDRESS;
2610 
2611  pfx->teDepth++;
2612 
2613  *chLimit = '\0';
2614 
2615  StartEleNdx = pfx->usedElements-1;
2616  if (StartEleNdx < 0) StartEleNdx = 0;
2617 
2618  SkipSpaces (pfx);
2619 
2620  if (!*pfx->pex) {
2621  pfx->teDepth--;
2622  return MagickFalse;
2623  }
2624 
2625  if (strchr(strLimit,*pfx->pex)!=NULL) {
2626  *chLimit = *pfx->pex;
2627  pfx->pex++;
2628  pfx->teDepth--;
2629 
2630  return MagickFalse;
2631  }
2632 
2633  if (!GetOperand (pfx, &UserSymbol, &NewUserSymbol, &UserSymNdx0, needPopAll)) return MagickFalse;
2634  SkipSpaces (pfx);
2635 
2636  /* Loop through Operator, Operand, Operator, Operand, ...
2637  */
2638  while (*pfx->pex && (!*strLimit || (strchr(strLimit,*pfx->pex)==NULL))) {
2639  if (!GetOperator (pfx, &Assign, &Update, &IncrDecr)) return MagickFalse;
2640  SkipSpaces (pfx);
2641  if (NewUserSymbol && !Assign) {
2642  (void) ThrowMagickException (
2643  pfx->exception, GetMagickModule(), OptionError,
2644  "Expected assignment after new UserSymbol", "'%s' at '%s'",
2645  pfx->token, SetShortExp(pfx));
2646  return MagickFalse;
2647  }
2648  if (!UserSymbol && Assign) {
2649  (void) ThrowMagickException (
2650  pfx->exception, GetMagickModule(), OptionError,
2651  "Attempted assignment to non-UserSymbol", "'%s' at '%s'",
2652  pfx->token, SetShortExp(pfx));
2653  return MagickFalse;
2654  }
2655  if (!UserSymbol && Update) {
2656  (void) ThrowMagickException (
2657  pfx->exception, GetMagickModule(), OptionError,
2658  "Attempted update to non-UserSymbol", "'%s' at '%s'",
2659  pfx->token, SetShortExp(pfx));
2660  return MagickFalse;
2661  }
2662  if (UserSymbol && (Assign || Update) && !IncrDecr) {
2663 
2664  if (!TranslateExpression (pfx, strLimit, chLimit, needPopAll)) return MagickFalse;
2665  if (!*pfx->pex) break;
2666  if (!*strLimit) break;
2667  if (strchr(strLimit,*chLimit)!=NULL) break;
2668  }
2669  if (UserSymbol && !Assign && !Update && UserSymNdx0 != NULL_ADDRESS) {
2670  ElementT * pel;
2671  (void) AddAddressingElement (pfx, rCopyFrom, UserSymNdx0);
2672  UserSymNdx0 = NULL_ADDRESS;
2673  pel = &pfx->Elements[pfx->usedElements-1];
2674  pel->do_push = MagickTrue;
2675  }
2676 
2677  if (UserSymbol) {
2678  while (TopOprIsUnaryPrefix (pfx)) {
2679  OperatorE op = pfx->OperatorStack[pfx->usedOprStack-1];
2680  (void) AddElement (pfx, (fxFltType) 0, (int) op);
2681  pfx->usedOprStack--;
2682  }
2683  }
2684 
2685  if (!ProcessTernaryOpr (pfx, &ternary)) return MagickFalse;
2686 
2687  if (ternary.addr_colon != NULL_ADDRESS) {
2688  if (!TranslateExpression (pfx, ",);", chLimit, needPopAll)) return MagickFalse;
2689  break;
2690  }
2691 
2692  UserSymbol = NewUserSymbol = MagickFalse;
2693 
2694  if ( (!*pfx->pex) || (*strLimit && (strchr(strLimit,*pfx->pex)!=NULL) ) )
2695  {
2696  if (IncrDecr) break;
2697 
2698  (void) ThrowMagickException (
2699  pfx->exception, GetMagickModule(), OptionError,
2700  "Expected operand after operator", "at '%s'",
2701  SetShortExp(pfx));
2702  return MagickFalse;
2703  }
2704 
2705  if (IncrDecr) {
2706  (void) ThrowMagickException (
2707  pfx->exception, GetMagickModule(), OptionError,
2708  "'++' and '--' must be the final operators in an expression at", "'%s'",
2709  SetShortExp(pfx));
2710  return MagickFalse;
2711  }
2712 
2713  if (!GetOperand (pfx, &UserSymbol, &NewUserSymbol, &UserSymNdx1, needPopAll)) {
2714  (void) ThrowMagickException (
2715  pfx->exception, GetMagickModule(), OptionError,
2716  "Expected operand at", "'%s'",
2717  SetShortExp(pfx));
2718  return MagickFalse;
2719  }
2720  SkipSpaces (pfx);
2721  if (NewUserSymbol && !Assign) {
2722  (void) ThrowMagickException (
2723  pfx->exception, GetMagickModule(), OptionError,
2724  "NewUserSymbol", "'%s' after non-assignment operator at '%s'",
2725  pfx->token, SetShortExp(pfx));
2726  return MagickFalse;
2727  }
2728  if (UserSymbol && !NewUserSymbol) {
2729  (void) AddAddressingElement (pfx, rCopyFrom, UserSymNdx1);
2730  UserSymNdx1 = NULL_ADDRESS;
2731  }
2732  UserSymNdx0 = UserSymNdx1;
2733  }
2734 
2735  if (UserSymbol && !Assign && !Update && UserSymNdx0 != NULL_ADDRESS) {
2736  ElementT * pel;
2737  if (NewUserSymbol) {
2738  (void) ThrowMagickException (
2739  pfx->exception, GetMagickModule(), OptionError,
2740  "NewUserSymbol", "'%s' needs assignment operator at '%s'",
2741  pfx->token, SetShortExp(pfx));
2742  return MagickFalse;
2743  }
2744  (void) AddAddressingElement (pfx, rCopyFrom, UserSymNdx0);
2745  pel = &pfx->Elements[pfx->usedElements-1];
2746  pel->do_push = MagickTrue;
2747  }
2748 
2749  if (*pfx->pex && !*chLimit && (strchr(strLimit,*pfx->pex)!=NULL)) {
2750  *chLimit = *pfx->pex;
2751  pfx->pex++;
2752  }
2753  while (pfx->usedOprStack) {
2754  OperatorE op = pfx->OperatorStack[pfx->usedOprStack-1];
2755  if (op == oOpenParen || op == oOpenBracket || op == oOpenBrace) {
2756  break;
2757  }
2758  if ( (op==oAssign && !Assign) || (OprInPlace((int) op) && !Update) ) {
2759  break;
2760  }
2761  pfx->usedOprStack--;
2762  (void) AddElement (pfx, (fxFltType) 0, (int) op);
2763  if (op == oAssign) {
2764  if (UserSymNdx0 < 0) {
2765  (void) ThrowMagickException (
2766  pfx->exception, GetMagickModule(), OptionError,
2767  "Assignment to unknown user symbol at", "'%s'",
2768  SetShortExp(pfx));
2769  return MagickFalse;
2770  }
2771  /* Adjust last element, by deletion and add.
2772  */
2773  pfx->usedElements--;
2774  (void) AddAddressingElement (pfx, rCopyTo, UserSymNdx0);
2775  break;
2776  } else if (OprInPlace ((int) op)) {
2777  if (UserSymNdx0 < 0) {
2778  (void) ThrowMagickException (
2779  pfx->exception, GetMagickModule(), OptionError,
2780  "Operator-in-place to unknown user symbol at", "'%s'",
2781  SetShortExp(pfx));
2782  return MagickFalse;
2783  }
2784  /* Modify latest element.
2785  */
2786  pfx->Elements[pfx->usedElements-1].element_index = UserSymNdx0;
2787  break;
2788  }
2789  }
2790 
2791  if (ternary.addr_query != NULL_ADDRESS) *needPopAll = MagickTrue;
2792 
2793  (void) ResolveTernaryAddresses (pfx, &ternary);
2794 
2795  pfx->teDepth--;
2796 
2797  if (!pfx->teDepth && *needPopAll) {
2798  (void) AddAddressingElement (pfx, rZerStk, NULL_ADDRESS);
2799  *needPopAll = MagickFalse;
2800  }
2801 
2802  if (pfx->exception->severity >= ErrorException)
2803  return MagickFalse;
2804 
2805  return MagickTrue;
2806 }
2807 
2808 
2809 static MagickBooleanType TranslateStatement (FxInfo * pfx, char * strLimit, char * chLimit)
2810 {
2811  MagickBooleanType NeedPopAll = MagickFalse;
2812 
2813  SkipSpaces (pfx);
2814 
2815  if (!*pfx->pex) return MagickFalse;
2816 
2817  if (!TranslateExpression (pfx, strLimit, chLimit, &NeedPopAll)) {
2818  return MagickFalse;
2819  }
2820  if (pfx->usedElements && *chLimit==';') {
2821  /* FIXME: not necessarily the last element,
2822  but the last _executed_ element, eg "goto" in a "for()".,
2823  Pending a fix, we will use rZerStk.
2824  */
2825  ElementT * pel = &pfx->Elements[pfx->usedElements-1];
2826  if (pel->do_push) pel->do_push = MagickFalse;
2827  }
2828 
2829  return MagickTrue;
2830 }
2831 
2832 static MagickBooleanType TranslateStatementList (FxInfo * pfx, const char * strLimit, char * chLimit)
2833 {
2834 #define MAX_SLIMIT 10
2835  char sLimits[MAX_SLIMIT];
2836  SkipSpaces (pfx);
2837 
2838  if (!*pfx->pex) return MagickFalse;
2839  (void) CopyMagickString (sLimits, strLimit, MAX_SLIMIT-1);
2840 
2841  if (strchr(strLimit,';')==NULL)
2842  (void) ConcatenateMagickString (sLimits, ";", MAX_SLIMIT);
2843 
2844  for (;;) {
2845  if (!TranslateStatement (pfx, sLimits, chLimit)) return MagickFalse;
2846 
2847  if (!*pfx->pex) break;
2848 
2849  if (*chLimit != ';') {
2850  break;
2851  }
2852  }
2853 
2854  if (pfx->exception->severity >= ErrorException)
2855  return MagickFalse;
2856 
2857  return MagickTrue;
2858 }
2859 
2860 /*--------------------------------------------------------------------
2861  Run-time
2862 */
2863 
2864 static ChannelStatistics *CollectOneImgStats (FxInfo * pfx, Image * img)
2865 {
2866  int ch;
2867  ChannelStatistics * cs = GetImageStatistics (img, pfx->exception);
2868  /* Use RelinquishMagickMemory() somewhere. */
2869 
2870  if (cs == (ChannelStatistics *) NULL)
2871  return((ChannelStatistics *) NULL);
2872 
2873  for (ch=0; ch <= (int) MaxPixelChannels; ch++) {
2874  cs[ch].mean *= QuantumScale;
2875  cs[ch].median *= QuantumScale;
2876  cs[ch].maxima *= QuantumScale;
2877  cs[ch].minima *= QuantumScale;
2878  cs[ch].standard_deviation *= QuantumScale;
2879  }
2880 
2881  return cs;
2882 }
2883 
2884 static MagickBooleanType CollectStatistics (FxInfo * pfx)
2885 {
2886  Image * img = GetFirstImageInList (pfx->image);
2887 
2888  size_t imgNum=0;
2889 
2890  pfx->statistics = (ChannelStatistics**) AcquireMagickMemory ((size_t) pfx->ImgListLen * sizeof (ChannelStatistics *));
2891  if (!pfx->statistics) {
2892  (void) ThrowMagickException (
2893  pfx->exception, GetMagickModule(), ResourceLimitFatalError,
2894  "Statistics", "%lu",
2895  (unsigned long) pfx->ImgListLen);
2896  return MagickFalse;
2897  }
2898 
2899  for (;;) {
2900  pfx->statistics[imgNum] = CollectOneImgStats (pfx, img);
2901 
2902  if (++imgNum == pfx->ImgListLen) break;
2903  img = GetNextImageInList (img);
2904  assert (img != (Image *) NULL);
2905  }
2906  pfx->GotStats = MagickTrue;
2907 
2908  return MagickTrue;
2909 }
2910 
2911 static inline MagickBooleanType PushVal (FxInfo * pfx, fxRtT * pfxrt, fxFltType val, int addr)
2912 {
2913  if (pfxrt->usedValStack >=pfxrt->numValStack) {
2914  (void) ThrowMagickException (
2915  pfx->exception, GetMagickModule(), OptionError,
2916  "ValStack overflow at addr=", "%i",
2917  addr);
2918  return MagickFalse;
2919  }
2920 
2921  pfxrt->ValStack[pfxrt->usedValStack++] = val;
2922  return MagickTrue;
2923 }
2924 
2925 static inline fxFltType PopVal (FxInfo * pfx, fxRtT * pfxrt, int addr)
2926 {
2927  if (pfxrt->usedValStack <= 0) {
2928  (void) ThrowMagickException (
2929  pfx->exception, GetMagickModule(), OptionError,
2930  "ValStack underflow at addr=", "%i",
2931  addr);
2932  return (fxFltType) 0;
2933  }
2934 
2935  return pfxrt->ValStack[--pfxrt->usedValStack];
2936 }
2937 
2938 static inline fxFltType ImageStat (
2939  FxInfo * pfx, ssize_t ImgNum, PixelChannel channel, ImgAttrE ia)
2940 {
2941  ChannelStatistics * cs = NULL;
2942  fxFltType ret = 0;
2943  MagickBooleanType NeedRelinq = MagickFalse;
2944 
2945  if (ImgNum < 0)
2946  {
2947  (void) ThrowMagickException(pfx->exception,GetMagickModule(),
2948  OptionError,"NoSuchImage","%lu",(unsigned long) ImgNum);
2949  ImgNum=0;
2950  }
2951 
2952  if (pfx->GotStats) {
2953  if ((channel < 0) || (channel > MaxPixelChannels))
2954  {
2955  (void) ThrowMagickException(pfx->exception,GetMagickModule(),
2956  OptionError,"NoSuchImageChannel","%i",channel);
2957  channel=(PixelChannel) 0;
2958  }
2959  cs = pfx->statistics[ImgNum];
2960  } else if (pfx->NeedStats) {
2961  /* If we need more than one statistic per pixel, this is inefficient. */
2962  if ((channel < 0) || (channel > MaxPixelChannels))
2963  {
2964  (void) ThrowMagickException(pfx->exception,GetMagickModule(),
2965  OptionError,"NoSuchImageChannel","%i",channel);
2966  channel=(PixelChannel) 0;
2967  }
2968  cs = CollectOneImgStats (pfx, pfx->Images[ImgNum]);
2969  NeedRelinq = MagickTrue;
2970  }
2971 
2972  switch (ia) {
2973  case aDepth:
2974  ret = (fxFltType) GetImageDepth (pfx->Images[ImgNum], pfx->exception);
2975  break;
2976  case aExtent:
2977  ret = (fxFltType) GetBlobSize (pfx->image);
2978  break;
2979  case aKurtosis:
2980  if ((cs != (ChannelStatistics *) NULL) && (channel >= 0))
2981  ret = cs[channel].kurtosis;
2982  break;
2983  case aMaxima:
2984  if ((cs != (ChannelStatistics *) NULL) && (channel >= 0))
2985  ret = cs[channel].maxima;
2986  break;
2987  case aMean:
2988  if ((cs != (ChannelStatistics *) NULL) && (channel >= 0))
2989  ret = cs[channel].mean;
2990  break;
2991  case aMedian:
2992  if ((cs != (ChannelStatistics *) NULL) && (channel >= 0))
2993  ret = cs[channel].median;
2994  break;
2995  case aMinima:
2996  if ((cs != (ChannelStatistics *) NULL) && (channel >= 0))
2997  ret = cs[channel].minima;
2998  break;
2999  case aPage:
3000  /* Do nothing */
3001  break;
3002  case aPageX:
3003  ret = (fxFltType) pfx->Images[ImgNum]->page.x;
3004  break;
3005  case aPageY:
3006  ret = (fxFltType) pfx->Images[ImgNum]->page.y;
3007  break;
3008  case aPageWid:
3009  ret = (fxFltType) pfx->Images[ImgNum]->page.width;
3010  break;
3011  case aPageHt:
3012  ret = (fxFltType) pfx->Images[ImgNum]->page.height;
3013  break;
3014  case aPrintsize:
3015  /* Do nothing */
3016  break;
3017  case aPrintsizeX:
3018  ret = (fxFltType) PerceptibleReciprocal (pfx->Images[ImgNum]->resolution.x)
3019  * pfx->Images[ImgNum]->columns;
3020  break;
3021  case aPrintsizeY:
3022  ret = (fxFltType) PerceptibleReciprocal (pfx->Images[ImgNum]->resolution.y)
3023  * pfx->Images[ImgNum]->rows;
3024  break;
3025  case aQuality:
3026  ret = (fxFltType) pfx->Images[ImgNum]->quality;
3027  break;
3028  case aRes:
3029  /* Do nothing */
3030  break;
3031  case aResX:
3032  ret = pfx->Images[ImgNum]->resolution.x;
3033  break;
3034  case aResY:
3035  ret = pfx->Images[ImgNum]->resolution.y;
3036  break;
3037  case aSkewness:
3038  if ((cs != (ChannelStatistics *) NULL) && (channel >= 0))
3039  ret = cs[channel].skewness;
3040  break;
3041  case aStdDev:
3042  if ((cs != (ChannelStatistics *) NULL) && (channel >= 0))
3043  ret = cs[channel].standard_deviation;
3044  break;
3045  case aH:
3046  ret = (fxFltType) pfx->Images[ImgNum]->rows;
3047  break;
3048  case aN:
3049  ret = (fxFltType) pfx->ImgListLen;
3050  break;
3051  case aT: /* image index in list */
3052  ret = (fxFltType) ImgNum;
3053  break;
3054  case aW:
3055  ret = (fxFltType) pfx->Images[ImgNum]->columns;
3056  break;
3057  case aZ:
3058  ret = (fxFltType) GetImageDepth (pfx->Images[ImgNum], pfx->exception);
3059  break;
3060  default:
3061  (void) ThrowMagickException (pfx->exception,GetMagickModule(),OptionError,
3062  "Unknown ia=","%i",ia);
3063  }
3064  if (NeedRelinq) cs = (ChannelStatistics *)RelinquishMagickMemory (cs);
3065 
3066  return ret;
3067 }
3068 
3069 static inline fxFltType FxGcd (fxFltType x, fxFltType y, const size_t depth)
3070 {
3071 #define FxMaxFunctionDepth 200
3072 
3073  if (x < y)
3074  return (FxGcd (y, x, depth+1));
3075  if ((fabs((double) y) < 0.001) || (depth >= FxMaxFunctionDepth))
3076  return (x);
3077  return (FxGcd (y, x-y*floor((double) (x/y)), depth+1));
3078 }
3079 
3080 static inline ssize_t ChkImgNum (FxInfo * pfx, fxFltType f)
3081 /* Returns -1 if f is too large. */
3082 {
3083  ssize_t i = (ssize_t) floor ((double) f + 0.5);
3084  if (i < 0) i += (ssize_t) pfx->ImgListLen;
3085  if (i < 0 || i >= (ssize_t) pfx->ImgListLen) {
3086  (void) ThrowMagickException (
3087  pfx->exception, GetMagickModule(), OptionError,
3088  "ImgNum", "%lu bad for ImgListLen %lu",
3089  (unsigned long) i, (unsigned long) pfx->ImgListLen);
3090  i = -1;
3091  }
3092  return i;
3093 }
3094 
3095 #define WHICH_ATTR_CHAN \
3096  (pel->channel_qual == NO_CHAN_QUAL) ? CompositePixelChannel : \
3097  (pel->channel_qual == THIS_CHANNEL) ? channel : pel->channel_qual
3098 
3099 #define WHICH_NON_ATTR_CHAN \
3100  (pel->channel_qual == NO_CHAN_QUAL || \
3101  pel->channel_qual == THIS_CHANNEL || \
3102  pel->channel_qual == CompositePixelChannel \
3103  ) ? (channel == CompositePixelChannel ? RedPixelChannel: channel) \
3104  : pel->channel_qual
3105 
3106 static fxFltType GetHslFlt (FxInfo * pfx, ssize_t ImgNum, const fxFltType fx, const fxFltType fy,
3107  PixelChannel channel)
3108 {
3109  Image * img = pfx->Images[ImgNum];
3110 
3111  double red, green, blue;
3112  double hue=0, saturation=0, lightness=0;
3113 
3114  MagickBooleanType okay = MagickTrue;
3115  if(!InterpolatePixelChannel (img, pfx->Imgs[ImgNum].View, RedPixelChannel, img->interpolate,
3116  (double) fx, (double) fy, &red, pfx->exception)) okay = MagickFalse;
3117  if(!InterpolatePixelChannel (img, pfx->Imgs[ImgNum].View, GreenPixelChannel, img->interpolate,
3118  (double) fx, (double) fy, &green, pfx->exception)) okay = MagickFalse;
3119  if(!InterpolatePixelChannel (img, pfx->Imgs[ImgNum].View, BluePixelChannel, img->interpolate,
3120  (double) fx, (double) fy, &blue, pfx->exception)) okay = MagickFalse;
3121 
3122  if (!okay)
3123  (void) ThrowMagickException (
3124  pfx->exception, GetMagickModule(), OptionError,
3125  "GetHslFlt failure", "%lu %g,%g %i", (unsigned long) ImgNum,
3126  (double) fx, (double) fy, channel);
3127 
3128  ConvertRGBToHSL (
3129  red, green, blue,
3130  &hue, &saturation, &lightness);
3131 
3132  if (channel == HUE_CHANNEL) return hue;
3133  if (channel == SAT_CHANNEL) return saturation;
3134  if (channel == LIGHT_CHANNEL) return lightness;
3135 
3136  return 0.0;
3137 }
3138 
3139 static fxFltType GetHslInt (FxInfo * pfx, ssize_t ImgNum, const ssize_t imgx, const ssize_t imgy, PixelChannel channel)
3140 {
3141  Image * img = pfx->Images[ImgNum];
3142 
3143  double hue=0, saturation=0, lightness=0;
3144 
3145  const Quantum * p = GetCacheViewVirtualPixels (pfx->Imgs[ImgNum].View, imgx, imgy, 1, 1, pfx->exception);
3146  if (p == (const Quantum *) NULL)
3147  {
3148  (void) ThrowMagickException (pfx->exception,GetMagickModule(),
3149  OptionError,"GetHslInt failure","%lu %li,%li %i",(unsigned long) ImgNum,
3150  (long) imgx,(long) imgy,channel);
3151  return(0.0);
3152  }
3153 
3154  ConvertRGBToHSL (
3155  GetPixelRed (img, p), GetPixelGreen (img, p), GetPixelBlue (img, p),
3156  &hue, &saturation, &lightness);
3157 
3158  if (channel == HUE_CHANNEL) return hue;
3159  if (channel == SAT_CHANNEL) return saturation;
3160  if (channel == LIGHT_CHANNEL) return lightness;
3161 
3162  return 0.0;
3163 }
3164 
3165 static inline fxFltType GetIntensity (FxInfo * pfx, ssize_t ImgNum, const fxFltType fx, const fxFltType fy)
3166 {
3167  Quantum
3168  quantum_pixel[MaxPixelChannels];
3169 
3170  PixelInfo
3171  pixelinf;
3172 
3173  Image * img = pfx->Images[ImgNum];
3174 
3175  (void) GetPixelInfo (img, &pixelinf);
3176 
3177  if (!InterpolatePixelInfo (img, pfx->Imgs[pfx->ImgNum].View, img->interpolate,
3178  (double) fx, (double) fy, &pixelinf, pfx->exception))
3179  {
3180  (void) ThrowMagickException (
3181  pfx->exception, GetMagickModule(), OptionError,
3182  "GetIntensity failure", "%lu %g,%g", (unsigned long) ImgNum,
3183  (double) fx, (double) fy);
3184  }
3185 
3186  SetPixelViaPixelInfo (img, &pixelinf, quantum_pixel);
3187  return QuantumScale * GetPixelIntensity (img, quantum_pixel);
3188 }
3189 
3190 static MagickBooleanType ExecuteRPN (FxInfo * pfx, fxRtT * pfxrt, fxFltType *result,
3191  const PixelChannel channel, const ssize_t imgx, const ssize_t imgy)
3192 {
3193  const Quantum * p = pfxrt->thisPixel;
3194  fxFltType regA=0, regB=0, regC=0, regD=0, regE=0;
3195  Image * img = pfx->image;
3196  ChannelStatistics * cs = NULL;
3197  MagickBooleanType NeedRelinq = MagickFalse;
3198  double hue=0, saturation=0, lightness=0;
3199  int i;
3200 
3201  /* For -fx, this sets p to ImgNum 0.
3202  for %[fx:...], this sets p to the current image.
3203  Similarly img.
3204  */
3205  if (!p) p = GetCacheViewVirtualPixels (
3206  pfx->Imgs[pfx->ImgNum].View, imgx, imgy, 1, 1, pfx->exception);
3207 
3208  if (p == (const Quantum *) NULL)
3209  {
3210  (void) ThrowMagickException (pfx->exception,GetMagickModule(),
3211  OptionError,"Can't get virtual pixels","%lu %li,%li",(unsigned long)
3212  pfx->ImgNum,(long) imgx,(long) imgy);
3213  return(MagickFalse);
3214  }
3215 
3216  if (pfx->GotStats) {
3217  cs = pfx->statistics[pfx->ImgNum];
3218  } else if (pfx->NeedStats) {
3219  cs = CollectOneImgStats (pfx, pfx->Images[pfx->ImgNum]);
3220  NeedRelinq = MagickTrue;
3221  }
3222 
3223  /* Following is only for expressions like "saturation", with no image specifier.
3224  */
3225  if (pfx->NeedHsl) {
3226  ConvertRGBToHSL (
3227  GetPixelRed (img, p), GetPixelGreen (img, p), GetPixelBlue (img, p),
3228  &hue, &saturation, &lightness);
3229  }
3230 
3231  for (i=0; i < pfx->usedElements; i++) {
3232  ElementT
3233  *pel;
3234 
3235  if (i < 0) {
3236  (void) ThrowMagickException (
3237  pfx->exception, GetMagickModule(), OptionError,
3238  "Bad run-time address", "%i", i);
3239  }
3240  pel=&pfx->Elements[i];
3241  switch (pel->number_args) {
3242  case 0:
3243  break;
3244  case 1:
3245  regA = PopVal (pfx, pfxrt, i);
3246  break;
3247  case 2:
3248  regB = PopVal (pfx, pfxrt, i);
3249  regA = PopVal (pfx, pfxrt, i);
3250  break;
3251  case 3:
3252  regC = PopVal (pfx, pfxrt, i);
3253  regB = PopVal (pfx, pfxrt, i);
3254  regA = PopVal (pfx, pfxrt, i);
3255  break;
3256  case 4:
3257  regD = PopVal (pfx, pfxrt, i);
3258  regC = PopVal (pfx, pfxrt, i);
3259  regB = PopVal (pfx, pfxrt, i);
3260  regA = PopVal (pfx, pfxrt, i);
3261  break;
3262  case 5:
3263  regE = PopVal (pfx, pfxrt, i);
3264  regD = PopVal (pfx, pfxrt, i);
3265  regC = PopVal (pfx, pfxrt, i);
3266  regB = PopVal (pfx, pfxrt, i);
3267  regA = PopVal (pfx, pfxrt, i);
3268  break;
3269  default:
3270  (void) ThrowMagickException (
3271  pfx->exception, GetMagickModule(), OptionError,
3272  "Too many args:", "%i", pel->number_args);
3273  break;
3274  }
3275 
3276  switch (pel->operator_index) {
3277  case oAddEq:
3278  regA = (pfxrt->UserSymVals[pel->element_index] += regA);
3279  break;
3280  case oSubtractEq:
3281  regA = (pfxrt->UserSymVals[pel->element_index] -= regA);
3282  break;
3283  case oMultiplyEq:
3284  regA = (pfxrt->UserSymVals[pel->element_index] *= regA);
3285  break;
3286  case oDivideEq:
3287  regA = (pfxrt->UserSymVals[pel->element_index] *= PerceptibleReciprocal((double)regA));
3288  break;
3289  case oPlusPlus:
3290  regA = pfxrt->UserSymVals[pel->element_index]++;
3291  break;
3292  case oSubSub:
3293  regA = pfxrt->UserSymVals[pel->element_index]--;
3294  break;
3295  case oAdd:
3296  regA += regB;
3297  break;
3298  case oSubtract:
3299  regA -= regB;
3300  break;
3301  case oMultiply:
3302  regA *= regB;
3303  break;
3304  case oDivide:
3305  regA *= PerceptibleReciprocal((double)regB);
3306  break;
3307  case oModulus:
3308  regA = fmod ((double) regA, fabs(floor((double) regB+0.5)));
3309  break;
3310  case oUnaryPlus:
3311  /* Do nothing. */
3312  break;
3313  case oUnaryMinus:
3314  regA = -regA;
3315  break;
3316  case oLshift:
3317  if ((size_t) (regB+0.5) >= (8*sizeof(size_t)))
3318  {
3319  (void) ThrowMagickException ( pfx->exception, GetMagickModule(),
3320  OptionError, "undefined shift", "%g", (double) regB);
3321  regA = (fxFltType) 0.0;
3322  break;
3323  }
3324  regA = (fxFltType) ((size_t)(regA+0.5) << (size_t)(regB+0.5));
3325  break;
3326  case oRshift:
3327  if ((size_t) (regB+0.5) >= (8*sizeof(size_t)))
3328  {
3329  (void) ThrowMagickException ( pfx->exception, GetMagickModule(),
3330  OptionError, "undefined shift", "%g", (double) regB);
3331  regA = (fxFltType) 0.0;
3332  break;
3333  }
3334  regA = (fxFltType) ((size_t)(regA+0.5) >> (size_t)(regB+0.5));
3335  break;
3336  case oEq:
3337  regA = fabs((double) (regA-regB)) < MagickEpsilon ? 1.0 : 0.0;
3338  break;
3339  case oNotEq:
3340  regA = fabs((double) (regA-regB)) >= MagickEpsilon ? 1.0 : 0.0;
3341  break;
3342  case oLtEq:
3343  regA = (regA <= regB) ? 1.0 : 0.0;
3344  break;
3345  case oGtEq:
3346  regA = (regA >= regB) ? 1.0 : 0.0;
3347  break;
3348  case oLt:
3349  regA = (regA < regB) ? 1.0 : 0.0;
3350  break;
3351  case oGt:
3352  regA = (regA > regB) ? 1.0 : 0.0;
3353  break;
3354  case oLogAnd:
3355  regA = (regA<=0) ? 0.0 : (regB > 0) ? 1.0 : 0.0;
3356  break;
3357  case oLogOr:
3358  regA = (regA>0) ? 1.0 : (regB > 0.0) ? 1.0 : 0.0;
3359  break;
3360  case oLogNot:
3361  regA = (regA==0) ? 1.0 : 0.0;
3362  break;
3363  case oBitAnd:
3364  regA = (fxFltType) ((size_t)(regA+0.5) & (size_t)(regB+0.5));
3365  break;
3366  case oBitOr:
3367  regA = (fxFltType) ((size_t)(regA+0.5) | (size_t)(regB+0.5));
3368  break;
3369  case oBitNot:
3370  /* Old fx doesn't add 0.5. */
3371  regA = (fxFltType) (~(size_t)(regA+0.5));
3372  break;
3373  case oPow:
3374  regA = pow ((double) regA, (double) regB);
3375  break;
3376  case oQuery:
3377  case oColon:
3378  break;
3379  case oOpenParen:
3380  case oCloseParen:
3381  case oOpenBracket:
3382  case oCloseBracket:
3383  case oOpenBrace:
3384  case oCloseBrace:
3385  break;
3386  case oAssign:
3387  pel->val = regA;
3388  break;
3389  case oNull: {
3390  if (pel->type == etColourConstant) {
3391  switch (channel) { default:
3392  case (PixelChannel) 0:
3393  regA = pel->val;
3394  break;
3395  case (PixelChannel) 1:
3396  regA = pel->val1;
3397  break;
3398  case (PixelChannel) 2:
3399  regA = pel->val2;
3400  break;
3401  }
3402  } else {
3403  regA = pel->val;
3404  }
3405  break;
3406  }
3407  case fAbs:
3408  regA = fabs ((double) regA);
3409  break;
3410 #if defined(MAGICKCORE_HAVE_ACOSH)
3411  case fAcosh:
3412  regA = acosh ((double) regA);
3413  break;
3414 #endif
3415  case fAcos:
3416  regA = acos ((double) regA);
3417  break;
3418 #if defined(MAGICKCORE_HAVE_J1)
3419  case fAiry:
3420  if (regA==0) regA = 1.0;
3421  else {
3422  fxFltType gamma = 2.0 * j1 ((MagickPI*regA)) / (MagickPI*regA);
3423  regA = gamma * gamma;
3424  }
3425  break;
3426 #endif
3427  case fAlt:
3428  regA = (fxFltType) (((ssize_t) regA) & 0x01 ? -1.0 : 1.0);
3429  break;
3430 #if defined(MAGICKCORE_HAVE_ASINH)
3431  case fAsinh:
3432  regA = asinh ((double) regA);
3433  break;
3434 #endif
3435  case fAsin:
3436  regA = asin ((double) regA);
3437  break;
3438 #if defined(MAGICKCORE_HAVE_ATANH)
3439  case fAtanh:
3440  regA = atanh ((double) regA);
3441  break;
3442 #endif
3443  case fAtan2:
3444  regA = atan2 ((double) regA, (double) regB);
3445  break;
3446  case fAtan:
3447  regA = atan ((double) regA);
3448  break;
3449  case fCeil:
3450  regA = ceil ((double) regA);
3451  break;
3452  case fChannel:
3453  switch (channel) {
3454  case (PixelChannel) 0: break;
3455  case (PixelChannel) 1: regA = regB; break;
3456  case (PixelChannel) 2: regA = regC; break;
3457  case (PixelChannel) 3: regA = regD; break;
3458  case (PixelChannel) 4: regA = regE; break;
3459  default: regA = 0.0;
3460  }
3461  break;
3462  case fClamp:
3463  if (regA < 0) regA = 0.0;
3464  else if (regA > 1.0) regA = 1.0;
3465  break;
3466  case fCosh:
3467  regA = cosh ((double) regA);
3468  break;
3469  case fCos:
3470  regA = cos ((double) regA);
3471  break;
3472  case fDebug:
3473  /* FIXME: debug() should give channel name. */
3474 
3475  (void) fprintf (stderr, "%s[%g,%g].[%i]: %s=%.*g\n",
3476  img->filename, (double) imgx, (double) imgy,
3477  channel, SetPtrShortExp (pfx, pel->exp_start, (size_t) (pel->exp_len+1)),
3478  pfx->precision, (double) regA);
3479  break;
3480  case fDrc:
3481  regA = regA / (regB*(regA-1.0) + 1.0);
3482  break;
3483 #if defined(MAGICKCORE_HAVE_ERF)
3484  case fErf:
3485  regA = erf ((double) regA);
3486  break;
3487 #endif
3488  case fExp:
3489  regA = exp ((double) regA);
3490  break;
3491  case fFloor:
3492  regA = floor ((double) regA);
3493  break;
3494  case fGauss:
3495  regA = exp((double) (-regA*regA/2.0))/sqrt(2.0*MagickPI);
3496  break;
3497  case fGcd:
3498  if (!IsNaN(regA))
3499  regA = FxGcd (regA, regB, 0);
3500  break;
3501  case fHypot:
3502  regA = hypot ((double) regA, (double) regB);
3503  break;
3504  case fInt:
3505  regA = floor ((double) regA);
3506  break;
3507  case fIsnan:
3508  regA = (fxFltType) (!!IsNaN (regA));
3509  break;
3510 #if defined(MAGICKCORE_HAVE_J0)
3511  case fJ0:
3512  regA = j0 ((double) regA);
3513  break;
3514 #endif
3515 #if defined(MAGICKCORE_HAVE_J1)
3516  case fJ1:
3517  regA = j1 ((double) regA);
3518  break;
3519 #endif
3520 #if defined(MAGICKCORE_HAVE_J1)
3521  case fJinc:
3522  if (regA==0) regA = 1.0;
3523  else regA = 2.0 * j1 ((MagickPI*regA))/(MagickPI*regA);
3524  break;
3525 #endif
3526  case fLn:
3527  regA = log ((double) regA);
3528  break;
3529  case fLogtwo:
3530  regA = MagickLog10((double) regA) / log10(2.0);
3531  break;
3532  case fLog:
3533  regA = MagickLog10 ((double) regA);
3534  break;
3535  case fMax:
3536  regA = (regA > regB) ? regA : regB;
3537  break;
3538  case fMin:
3539  regA = (regA < regB) ? regA : regB;
3540  break;
3541  case fMod:
3542  regA = regA - floor((double) (regA*PerceptibleReciprocal((double) regB)))*regB;
3543  break;
3544  case fNot:
3545  regA = (fxFltType) (regA < MagickEpsilon);
3546  break;
3547  case fPow:
3548  regA = pow ((double) regA, (double) regB);
3549  break;
3550  case fRand: {
3551 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3552  #pragma omp critical (MagickCore_ExecuteRPN)
3553 #endif
3554  regA = GetPseudoRandomValue (pfxrt->random_info);
3555  break;
3556  }
3557  case fRound:
3558  regA = floor ((double) regA + 0.5);
3559  break;
3560  case fSign:
3561  regA = (regA < 0) ? -1.0 : 1.0;
3562  break;
3563  case fSinc:
3564  regA = sin ((double) (MagickPI*regA)) / (MagickPI*regA);
3565  break;
3566  case fSinh:
3567  regA = sinh ((double) regA);
3568  break;
3569  case fSin:
3570  regA = sin ((double) regA);
3571  break;
3572  case fSqrt:
3573  regA = sqrt ((double) regA);
3574  break;
3575  case fSquish:
3576  regA = 1.0 / (1.0 + exp ((double) -regA));
3577  break;
3578  case fTanh:
3579  regA = tanh ((double) regA);
3580  break;
3581  case fTan:
3582  regA = tan ((double) regA);
3583  break;
3584  case fTrunc:
3585  if (regA >= 0) regA = floor ((double) regA);
3586  else regA = ceil ((double) regA);
3587  break;
3588 
3589  case fDo:
3590  case fFor:
3591  case fIf:
3592  case fWhile:
3593  break;
3594  case fU: {
3595  /* Note: 1 value is available, index into image list.
3596  May have ImgAttr qualifier or channel qualifier or both.
3597  */
3598  ssize_t ImgNum = ChkImgNum (pfx, regA);
3599  if (ImgNum < 0) break;
3600  regA = (fxFltType) 0;
3601  if (ImgNum == 0) {
3602  Image * pimg = pfx->Images[0];
3603  if (pel->img_attr_qual == aNull) {
3604  if ((int) pel->channel_qual < 0) {
3605  if (pel->channel_qual == NO_CHAN_QUAL || pel->channel_qual == THIS_CHANNEL) {
3606  if (pfx->ImgNum==0) {
3607  regA = QuantumScale * (double) p[pimg->channel_map[WHICH_NON_ATTR_CHAN].offset];
3608  } else {
3609  const Quantum * pv = GetCacheViewVirtualPixels (
3610  pfx->Imgs[0].View, imgx, imgy, 1,1, pfx->exception);
3611  if (!pv) {
3612  (void) ThrowMagickException (
3613  pfx->exception, GetMagickModule(), OptionError,
3614  "fU can't get cache", "%lu", (unsigned long) ImgNum);
3615  break;
3616  }
3617  regA = QuantumScale * (double) pv[pimg->channel_map[WHICH_NON_ATTR_CHAN].offset];
3618  }
3619  } else if (pel->channel_qual == HUE_CHANNEL || pel->channel_qual == SAT_CHANNEL ||
3620  pel->channel_qual == LIGHT_CHANNEL) {
3621  regA = GetHslInt (pfx, ImgNum, imgx, imgy, pel->channel_qual);
3622  break;
3623  } else if (pel->channel_qual == INTENSITY_CHANNEL) {
3624  regA = GetIntensity (pfx, 0, (double) imgx, (double) imgy);
3625  break;
3626  }
3627  } else {
3628  if (pfx->ImgNum==0) {
3629  regA = QuantumScale * (double) p[pimg->channel_map[WHICH_NON_ATTR_CHAN].offset];
3630  } else {
3631  const Quantum * pv = GetCacheViewVirtualPixels (
3632  pfx->Imgs[0].View, imgx, imgy, 1,1, pfx->exception);
3633  if (!pv) {
3634  (void) ThrowMagickException (
3635  pfx->exception, GetMagickModule(), OptionError,
3636  "fU can't get cache", "%lu", (unsigned long) ImgNum);
3637  break;
3638  }
3639  regA = QuantumScale * (double) pv[pimg->channel_map[WHICH_NON_ATTR_CHAN].offset];
3640  }
3641  }
3642  } else {
3643  /* we have an image attribute */
3644  regA = ImageStat (pfx, 0, WHICH_ATTR_CHAN, pel->img_attr_qual);
3645  }
3646  } else {
3647  /* We have non-zero ImgNum. */
3648  if (pel->img_attr_qual == aNull) {
3649  const Quantum * pv;
3650  if ((int) pel->channel_qual < 0) {
3651  if (pel->channel_qual == HUE_CHANNEL || pel->channel_qual == SAT_CHANNEL ||
3652  pel->channel_qual == LIGHT_CHANNEL)
3653  {
3654  regA = GetHslInt (pfx, ImgNum, imgx, imgy, pel->channel_qual);
3655  break;
3656  } else if (pel->channel_qual == INTENSITY_CHANNEL)
3657  {
3658  regA = GetIntensity (pfx, ImgNum, (fxFltType) imgx, (fxFltType) imgy);
3659  break;
3660  }
3661  }
3662 
3663  pv = GetCacheViewVirtualPixels (
3664  pfx->Imgs[ImgNum].View, imgx, imgy, 1,1, pfx->exception);
3665  if (!pv) {
3666  (void) ThrowMagickException (
3667  pfx->exception, GetMagickModule(), OptionError,
3668  "fU can't get cache", "%lu", (unsigned long) ImgNum);
3669  break;
3670  }
3671  regA = QuantumScale * (double)
3672  pv[pfx->Images[ImgNum]->channel_map[WHICH_NON_ATTR_CHAN].offset];
3673  } else {
3674  regA = ImageStat (pfx, ImgNum, WHICH_ATTR_CHAN, pel->img_attr_qual);
3675  }
3676  }
3677  break;
3678  }
3679  case fU0: {
3680  /* No args. No image attribute. We may have a ChannelQual.
3681  If called from %[fx:...], ChannelQual will be CompositePixelChannel.
3682  */
3683  Image * pimg = pfx->Images[0];
3684  if ((int) pel->channel_qual < 0) {
3685  if (pel->channel_qual == NO_CHAN_QUAL || pel->channel_qual == THIS_CHANNEL) {
3686 
3687  if (pfx->ImgNum==0) {
3688  regA = QuantumScale * (double) p[pimg->channel_map[WHICH_NON_ATTR_CHAN].offset];
3689  } else {
3690  const Quantum * pv = GetCacheViewVirtualPixels (
3691  pfx->Imgs[0].View, imgx, imgy, 1,1, pfx->exception);
3692  if (!pv) {
3693  (void) ThrowMagickException (
3694  pfx->exception, GetMagickModule(), OptionError,
3695  "fU0 can't get cache", "%i", 0);
3696  break;
3697  }
3698  regA = QuantumScale * (double) pv[pimg->channel_map[WHICH_NON_ATTR_CHAN].offset];
3699  }
3700 
3701  } else if (pel->channel_qual == HUE_CHANNEL || pel->channel_qual == SAT_CHANNEL ||
3702  pel->channel_qual == LIGHT_CHANNEL) {
3703  regA = GetHslInt (pfx, 0, imgx, imgy, pel->channel_qual);
3704  break;
3705  } else if (pel->channel_qual == INTENSITY_CHANNEL) {
3706  regA = GetIntensity (pfx, 0, (fxFltType) imgx, (fxFltType) imgy);
3707  }
3708  } else {
3709  if (pfx->ImgNum==0) {
3710  regA = QuantumScale * (double) p[pimg->channel_map[WHICH_NON_ATTR_CHAN].offset];
3711  } else {
3712  const Quantum * pv = GetCacheViewVirtualPixels (
3713  pfx->Imgs[0].View, imgx, imgy, 1,1, pfx->exception);
3714  if (!pv) {
3715  (void) ThrowMagickException (
3716  pfx->exception, GetMagickModule(), OptionError,
3717  "fU0 can't get cache", "%i", 0);
3718  break;
3719  }
3720  regA = QuantumScale * (double) pv[pimg->channel_map[WHICH_NON_ATTR_CHAN].offset];
3721  }
3722  }
3723  break;
3724  }
3725  case fUP: {
3726  /* 3 args are: ImgNum, x, y */
3727  ssize_t ImgNum = ChkImgNum (pfx, regA);
3728  fxFltType fx, fy;
3729 
3730  if (ImgNum < 0) break;
3731 
3732  if (pel->is_relative) {
3733  fx = imgx + regB;
3734  fy = imgy + regC;
3735  } else {
3736  fx = regB;
3737  fy = regC;
3738  }
3739 
3740  if ((int) pel->channel_qual < 0) {
3741  if (pel->channel_qual == HUE_CHANNEL || pel->channel_qual == SAT_CHANNEL
3742  || pel->channel_qual == LIGHT_CHANNEL) {
3743  regA = GetHslFlt (pfx, ImgNum, fx, fy, pel->channel_qual);
3744  break;
3745  } else if (pel->channel_qual == INTENSITY_CHANNEL) {
3746  regA = GetIntensity (pfx, ImgNum, fx, fy);
3747  break;
3748  }
3749  }
3750 
3751  {
3752  double v;
3753  Image * imUP = pfx->Images[ImgNum];
3754  if (! InterpolatePixelChannel (imUP, pfx->Imgs[ImgNum].View, WHICH_NON_ATTR_CHAN,
3755  imUP->interpolate, (double) fx, (double) fy, &v, pfx->exception))
3756  {
3757  (void) ThrowMagickException (
3758  pfx->exception, GetMagickModule(), OptionError,
3759  "fUP can't get interpolate", "%lu", (unsigned long) ImgNum);
3760  break;
3761  }
3762  regA = v * QuantumScale;
3763  }
3764 
3765  break;
3766  }
3767  case fS:
3768  case fV: {
3769  /* No args. */
3770  ssize_t ImgNum = 1;
3771  if (pel->operator_index == fS) ImgNum = pfx->ImgNum;
3772 
3773  if (pel->img_attr_qual == aNull) {
3774  const Quantum * pv = GetCacheViewVirtualPixels (
3775  pfx->Imgs[ImgNum].View, imgx, imgy, 1,1, pfx->exception);
3776  if (!pv) {
3777  (void) ThrowMagickException (
3778  pfx->exception, GetMagickModule(), OptionError,
3779  "fV can't get cache", "%lu", (unsigned long) ImgNum);
3780  break;
3781  }
3782 
3783  if ((int) pel->channel_qual < 0) {
3784  if (pel->channel_qual == HUE_CHANNEL || pel->channel_qual == SAT_CHANNEL ||
3785  pel->channel_qual == LIGHT_CHANNEL) {
3786  regA = GetHslInt (pfx, ImgNum, imgx, imgy, pel->channel_qual);
3787  break;
3788  } else if (pel->channel_qual == INTENSITY_CHANNEL) {
3789  regA = GetIntensity (pfx, ImgNum, (double) imgx, (double) imgy);
3790  break;
3791  }
3792  }
3793 
3794  regA = QuantumScale * (double)
3795  pv[pfx->Images[ImgNum]->channel_map[WHICH_NON_ATTR_CHAN].offset];
3796  } else {
3797  regA = ImageStat (pfx, ImgNum, WHICH_ATTR_CHAN, pel->img_attr_qual);
3798  }
3799 
3800  break;
3801  }
3802  case fP:
3803  case fSP:
3804  case fVP: {
3805  /* 2 args are: x, y */
3806  fxFltType fx, fy;
3807  ssize_t ImgNum = pfx->ImgNum;
3808  if (pel->operator_index == fVP) ImgNum = 1;
3809  if (pel->is_relative) {
3810  fx = imgx + regA;
3811  fy = imgy + regB;
3812  } else {
3813  fx = regA;
3814  fy = regB;
3815  }
3816  if ((int) pel->channel_qual < 0) {
3817  if (pel->channel_qual == HUE_CHANNEL || pel->channel_qual == SAT_CHANNEL ||
3818  pel->channel_qual == LIGHT_CHANNEL) {
3819  regA = GetHslFlt (pfx, ImgNum, fx, fy, pel->channel_qual);
3820  break;
3821  } else if (pel->channel_qual == INTENSITY_CHANNEL) {
3822  regA = GetIntensity (pfx, ImgNum, fx, fy);
3823  break;
3824  }
3825  }
3826 
3827  {
3828  double v;
3829 
3830  if (! InterpolatePixelChannel (pfx->Images[ImgNum], pfx->Imgs[ImgNum].View,
3831  WHICH_NON_ATTR_CHAN, pfx->Images[ImgNum]->interpolate,
3832  (double) fx, (double) fy, &v, pfx->exception)
3833  )
3834  {
3835  (void) ThrowMagickException (
3836  pfx->exception, GetMagickModule(), OptionError,
3837  "fSP or fVP can't get interp", "%lu", (unsigned long) ImgNum);
3838  break;
3839  }
3840  regA = v * (fxFltType)QuantumScale;
3841  }
3842 
3843  break;
3844  }
3845  case fNull:
3846  break;
3847  case aDepth:
3848  regA = (fxFltType) GetImageDepth (img, pfx->exception);
3849  break;
3850  case aExtent:
3851  regA = (fxFltType) img->extent;
3852  break;
3853  case aKurtosis:
3854  if ((cs != (ChannelStatistics *) NULL) && (channel > 0))
3855  regA = cs[WHICH_ATTR_CHAN].kurtosis;
3856  break;
3857  case aMaxima:
3858  if ((cs != (ChannelStatistics *) NULL) && (channel > 0))
3859  regA = cs[WHICH_ATTR_CHAN].maxima;
3860  break;
3861  case aMean:
3862  if ((cs != (ChannelStatistics *) NULL) && (channel > 0))
3863  regA = cs[WHICH_ATTR_CHAN].mean;
3864  break;
3865  case aMedian:
3866  if ((cs != (ChannelStatistics *) NULL) && (channel > 0))
3867  regA = cs[WHICH_ATTR_CHAN].median;
3868  break;
3869  case aMinima:
3870  if ((cs != (ChannelStatistics *) NULL) && (channel > 0))
3871  regA = cs[WHICH_ATTR_CHAN].minima;
3872  break;
3873  case aPage:
3874  break;
3875  case aPageX:
3876  regA = (fxFltType) img->page.x;
3877  break;
3878  case aPageY:
3879  regA = (fxFltType) img->page.y;
3880  break;
3881  case aPageWid:
3882  regA = (fxFltType) img->page.width;
3883  break;
3884  case aPageHt:
3885  regA = (fxFltType) img->page.height;
3886  break;
3887  case aPrintsize:
3888  break;
3889  case aPrintsizeX:
3890  regA = (fxFltType) PerceptibleReciprocal (img->resolution.x) * img->columns;
3891  break;
3892  case aPrintsizeY:
3893  regA = (fxFltType) PerceptibleReciprocal (img->resolution.y) * img->rows;
3894  break;
3895  case aQuality:
3896  regA = (fxFltType) img->quality;
3897  break;
3898  case aRes:
3899  break;
3900  case aResX:
3901  regA = (fxFltType) img->resolution.x;
3902  break;
3903  case aResY:
3904  regA = (fxFltType) img->resolution.y;
3905  break;
3906  case aSkewness:
3907  if ((cs != (ChannelStatistics *) NULL) && (channel > 0))
3908  regA = cs[WHICH_ATTR_CHAN].skewness;
3909  break;
3910  case aStdDev:
3911  if ((cs != (ChannelStatistics *) NULL) && (channel > 0))
3912  regA = cs[WHICH_ATTR_CHAN].standard_deviation;
3913  break;
3914  case aH: /* image->rows */
3915  regA = (fxFltType) img->rows;
3916  break;
3917  case aN: /* image list length */
3918  regA = (fxFltType) pfx->ImgListLen;
3919  break;
3920  case aT: /* image index in list */
3921  regA = (fxFltType) pfx->ImgNum;
3922  break;
3923  case aW: /* image->columns */
3924  regA = (fxFltType) img->columns;
3925  break;
3926  case aZ: /* image depth */
3927  regA = (fxFltType) GetImageDepth (img, pfx->exception);
3928  break;
3929  case aNull:
3930  break;
3931  case sHue: /* of conversion to HSL */
3932  regA = hue;
3933  break;
3934  case sIntensity:
3935  regA = GetIntensity (pfx, pfx->ImgNum, (double) imgx, (double) imgy);
3936  break;
3937  case sLightness: /* of conversion to HSL */
3938  regA = lightness;
3939  break;
3940  case sLuma: /* calculation */
3941  case sLuminance: /* as Luma */
3942  regA = QuantumScale * (0.212656 * (double) GetPixelRed (img,p) +
3943  0.715158 * (double) GetPixelGreen (img,p) +
3944  0.072186 * (double) GetPixelBlue (img,p));
3945  break;
3946  case sSaturation: /* from conversion to HSL */
3947  regA = saturation;
3948  break;
3949  case sA: /* alpha */
3950  regA = QuantumScale * (double) GetPixelAlpha (img, p);
3951  break;
3952  case sB: /* blue */
3953  regA = QuantumScale * (double) GetPixelBlue (img, p);
3954  break;
3955  case sC: /* red (ie cyan) */
3956  regA = QuantumScale * (double) GetPixelCyan (img, p);
3957  break;
3958  case sG: /* green */
3959  regA = QuantumScale * (double) GetPixelGreen (img, p);
3960  break;
3961  case sI: /* current x-coordinate */
3962  regA = (fxFltType) imgx;
3963  break;
3964  case sJ: /* current y-coordinate */
3965  regA = (fxFltType) imgy;
3966  break;
3967  case sK: /* black of CMYK */
3968  regA = QuantumScale * (double) GetPixelBlack (img, p);
3969  break;
3970  case sM: /* green (ie magenta) */
3971  regA = QuantumScale * (double) GetPixelGreen (img, p);
3972  break;
3973  case sO: /* alpha */
3974  regA = QuantumScale * (double) GetPixelAlpha (img, p);
3975  break;
3976  case sR:
3977  regA = QuantumScale * (double) GetPixelRed (img, p);
3978  break;
3979  case sY:
3980  regA = QuantumScale * (double) GetPixelYellow (img, p);
3981  break;
3982  case sNull:
3983  break;
3984 
3985  case rGoto:
3986  assert (pel->element_index >= 0);
3987  i = pel->element_index-1; /* -1 because 'for' loop will increment. */
3988  break;
3989  case rGotoChk:
3990  assert (pel->element_index >= 0);
3991  i = pel->element_index-1; /* -1 because 'for' loop will increment. */
3992  if (IsImageTTLExpired(img) != MagickFalse) {
3993  i = pfx->usedElements-1; /* Do no more opcodes. */
3994  (void) ThrowMagickException (pfx->exception, GetMagickModule(),
3995  ResourceLimitFatalError, "TimeLimitExceeded", "`%s'", img->filename);
3996  }
3997  break;
3998  case rIfZeroGoto:
3999  assert (pel->element_index >= 0);
4000  if (fabs((double) regA) < MagickEpsilon) i = pel->element_index-1;
4001  break;
4002  case rIfNotZeroGoto:
4003  assert (pel->element_index >= 0);
4004  if (fabs((double) regA) > MagickEpsilon) i = pel->element_index-1;
4005  break;
4006  case rCopyFrom:
4007  assert (pel->element_index >= 0);
4008  regA = pfxrt->UserSymVals[pel->element_index];
4009  break;
4010  case rCopyTo:
4011  assert (pel->element_index >= 0);
4012  pfxrt->UserSymVals[pel->element_index] = regA;
4013  break;
4014  case rZerStk:
4015  pfxrt->usedValStack = 0;
4016  break;
4017  case rNull:
4018  break;
4019 
4020  default:
4021  (void) ThrowMagickException (
4022  pfx->exception, GetMagickModule(), OptionError,
4023  "pel->oprNum", "%i '%s' not yet implemented",
4024  (int)pel->operator_index, OprStr(pel->operator_index));
4025  break;
4026  }
4027  if (pel->do_push)
4028  if (!PushVal (pfx, pfxrt, regA, i)) break;
4029  }
4030 
4031  if (pfxrt->usedValStack > 0) regA = PopVal (pfx, pfxrt, 9999);
4032 
4033  *result = regA;
4034 
4035  if (NeedRelinq) cs = (ChannelStatistics *)RelinquishMagickMemory (cs);
4036 
4037  if (pfx->exception->severity >= ErrorException)
4038  return MagickFalse;
4039 
4040  if (pfxrt->usedValStack != 0) {
4041  (void) ThrowMagickException (
4042  pfx->exception, GetMagickModule(), OptionError,
4043  "ValStack not empty", "(%i)", pfxrt->usedValStack);
4044  return MagickFalse;
4045  }
4046 
4047  return MagickTrue;
4048 }
4049 
4050 /* Following is substitute for FxEvaluateChannelExpression().
4051 */
4052 MagickPrivate MagickBooleanType FxEvaluateChannelExpression (
4053  FxInfo *pfx,
4054  const PixelChannel channel, const ssize_t x, const ssize_t y,
4055  double *result, ExceptionInfo *exception)
4056 {
4057  const int
4058  id = GetOpenMPThreadId();
4059 
4060  fxFltType ret;
4061 
4062  assert (pfx != NULL);
4063  assert (pfx->image != NULL);
4064  assert (pfx->Images != NULL);
4065  assert (pfx->Imgs != NULL);
4066  assert (pfx->fxrts != NULL);
4067 
4068  pfx->fxrts[id].thisPixel = NULL;
4069 
4070  if (!ExecuteRPN (pfx, &pfx->fxrts[id], &ret, channel, x, y)) {
4071  (void) ThrowMagickException (
4072  exception, GetMagickModule(), OptionError,
4073  "ExecuteRPN failed", " ");
4074  return MagickFalse;
4075  }
4076 
4077  *result = (double) ret;
4078 
4079  return MagickTrue;
4080 }
4081 
4082 static FxInfo *AcquireFxInfoPrivate (const Image * images, const char * expression,
4083  MagickBooleanType CalcAllStats, ExceptionInfo *exception)
4084 {
4085  char chLimit;
4086 
4087  FxInfo * pfx = (FxInfo*) AcquireCriticalMemory (sizeof (*pfx));
4088 
4089  memset (pfx, 0, sizeof (*pfx));
4090 
4091  if (!InitFx (pfx, images, CalcAllStats, exception)) {
4092  pfx = (FxInfo*) RelinquishMagickMemory(pfx);
4093  return NULL;
4094  }
4095 
4096  if (!BuildRPN (pfx)) {
4097  (void) DeInitFx (pfx);
4098  pfx = (FxInfo*) RelinquishMagickMemory(pfx);
4099  return((FxInfo *) NULL);
4100  }
4101 
4102  if ((*expression == '@') && (strlen(expression) > 1))
4103  pfx->expression=FileToString(expression,~0UL,exception);
4104  if (pfx->expression == (char *) NULL)
4105  pfx->expression=ConstantString(expression);
4106  pfx->pex = (char *) pfx->expression;
4107 
4108  pfx->teDepth = 0;
4109  if (!TranslateStatementList (pfx, ";", &chLimit)) {
4110  (void) DestroyRPN (pfx);
4111  pfx->expression = DestroyString (pfx->expression);
4112  pfx->pex = NULL;
4113  (void) DeInitFx (pfx);
4114  pfx = (FxInfo*) RelinquishMagickMemory(pfx);
4115  return NULL;
4116  }
4117 
4118  if (pfx->teDepth) {
4119  (void) ThrowMagickException (
4120  pfx->exception, GetMagickModule(), OptionError,
4121  "Translate expression depth", "(%i) not 0",
4122  pfx->teDepth);
4123 
4124  (void) DestroyRPN (pfx);
4125  pfx->expression = DestroyString (pfx->expression);
4126  pfx->pex = NULL;
4127  (void) DeInitFx (pfx);
4128  pfx = (FxInfo*) RelinquishMagickMemory(pfx);
4129  return NULL;
4130  }
4131 
4132  if (chLimit != '\0' && chLimit != ';') {
4133  (void) ThrowMagickException (
4134  pfx->exception, GetMagickModule(), OptionError,
4135  "AcquireFxInfo: TranslateExpression did not exhaust input", "(chLimit=%i) at'%s'",
4136  (int)chLimit, pfx->pex);
4137 
4138  (void) DestroyRPN (pfx);
4139  pfx->expression = DestroyString (pfx->expression);
4140  pfx->pex = NULL;
4141  (void) DeInitFx (pfx);
4142  pfx = (FxInfo*) RelinquishMagickMemory(pfx);
4143  return NULL;
4144  }
4145 
4146  if (pfx->NeedStats && pfx->runType == rtEntireImage && !pfx->statistics) {
4147  if (!CollectStatistics (pfx)) {
4148  (void) DestroyRPN (pfx);
4149  pfx->expression = DestroyString (pfx->expression);
4150  pfx->pex = NULL;
4151  (void) DeInitFx (pfx);
4152  pfx = (FxInfo*) RelinquishMagickMemory(pfx);
4153  return NULL;
4154  }
4155  }
4156 
4157  if (pfx->DebugOpt) {
4158  DumpTables (stderr);
4159  DumpUserSymbols (pfx, stderr);
4160  (void) DumpRPN (pfx, stderr);
4161  }
4162 
4163  {
4164  size_t number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
4165  ssize_t t;
4166 
4167  pfx->fxrts = (fxRtT *)AcquireQuantumMemory (number_threads, sizeof(fxRtT));
4168  if (!pfx->fxrts) {
4169  (void) ThrowMagickException (
4170  pfx->exception, GetMagickModule(), ResourceLimitFatalError,
4171  "fxrts", "%lu",
4172  (unsigned long) number_threads);
4173  (void) DestroyRPN (pfx);
4174  pfx->expression = DestroyString (pfx->expression);
4175  pfx->pex = NULL;
4176  (void) DeInitFx (pfx);
4177  pfx = (FxInfo*) RelinquishMagickMemory(pfx);
4178  return NULL;
4179  }
4180  for (t=0; t < (ssize_t) number_threads; t++) {
4181  if (!AllocFxRt (pfx, &pfx->fxrts[t])) {
4182  (void) ThrowMagickException (
4183  pfx->exception, GetMagickModule(), ResourceLimitFatalError,
4184  "AllocFxRt t=", "%g",
4185  (double) t);
4186  {
4187  ssize_t t2;
4188  for (t2 = t-1; t2 >= 0; t2--) {
4189  DestroyFxRt (&pfx->fxrts[t]);
4190  }
4191  }
4192  pfx->fxrts = (fxRtT *) RelinquishMagickMemory (pfx->fxrts);
4193  (void) DestroyRPN (pfx);
4194  pfx->expression = DestroyString (pfx->expression);
4195  pfx->pex = NULL;
4196  (void) DeInitFx (pfx);
4197  pfx = (FxInfo*) RelinquishMagickMemory(pfx);
4198  return NULL;
4199  }
4200  }
4201  }
4202  return pfx;
4203 }
4204 
4205 FxInfo *AcquireFxInfo (const Image * images, const char * expression, ExceptionInfo *exception)
4206 {
4207  return AcquireFxInfoPrivate (images, expression, MagickFalse, exception);
4208 }
4209 
4210 FxInfo *DestroyFxInfo (FxInfo * pfx)
4211 {
4212  ssize_t t;
4213 
4214  assert (pfx != NULL);
4215  assert (pfx->image != NULL);
4216  assert (pfx->Images != NULL);
4217  assert (pfx->Imgs != NULL);
4218  assert (pfx->fxrts != NULL);
4219 
4220  for (t=0; t < (ssize_t) GetMagickResourceLimit(ThreadResource); t++) {
4221  DestroyFxRt (&pfx->fxrts[t]);
4222  }
4223  pfx->fxrts = (fxRtT *) RelinquishMagickMemory (pfx->fxrts);
4224 
4225  DestroyRPN (pfx);
4226 
4227  pfx->expression = DestroyString (pfx->expression);
4228  pfx->pex = NULL;
4229 
4230  (void) DeInitFx (pfx);
4231 
4232  pfx = (FxInfo*) RelinquishMagickMemory(pfx);
4233 
4234  return NULL;
4235 }
4236 
4237 /* Following is substitute for FxImage().
4238 */
4239 MagickExport Image *FxImage(const Image *image,const char *expression,
4240  ExceptionInfo *exception)
4241 {
4242 #define FxImageTag "FxNew/Image"
4243 
4244  CacheView
4245  *fx_view,
4246  *image_view;
4247 
4248  Image
4249  *fx_image;
4250 
4251  MagickBooleanType
4252  status;
4253 
4254  MagickOffsetType
4255  progress;
4256 
4257  ssize_t
4258  y;
4259 
4260  FxInfo
4261  *pfx;
4262 
4263  assert(image != (Image *) NULL);
4264  assert(image->signature == MagickCoreSignature);
4265  if (IsEventLogging() != MagickFalse)
4266  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4267  if (expression == (const char *) NULL)
4268  return(CloneImage(image,0,0,MagickTrue,exception));
4269  fx_image=CloneImage(image,0,0,MagickTrue,exception);
4270  if (!fx_image) return NULL;
4271  if (SetImageStorageClass(fx_image,DirectClass,exception) == MagickFalse) {
4272  fx_image=DestroyImage(fx_image);
4273  return NULL;
4274  }
4275 
4276  pfx = AcquireFxInfoPrivate (image, expression, MagickTrue, exception);
4277 
4278  if (!pfx) {
4279  fx_image=DestroyImage(fx_image);
4280  return NULL;
4281  }
4282 
4283  assert (pfx->image != NULL);
4284  assert (pfx->Images != NULL);
4285  assert (pfx->Imgs != NULL);
4286  assert (pfx->fxrts != NULL);
4287 
4288  status=MagickTrue;
4289  progress=0;
4290  image_view = AcquireVirtualCacheView (image, pfx->exception);
4291  fx_view = AcquireAuthenticCacheView (fx_image, pfx->exception);
4292 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4293  #pragma omp parallel for schedule(dynamic) shared(progress,status) \
4294  magick_number_threads(image,fx_image,fx_image->rows, \
4295  pfx->ContainsDebug ? 0 : 1)
4296 #endif
4297  for (y=0; y < (ssize_t) fx_image->rows; y++)
4298  {
4299  const int
4300  id = GetOpenMPThreadId();
4301 
4302  const Quantum
4303  *magick_restrict p;
4304 
4305  Quantum
4306  *magick_restrict q;
4307 
4308  ssize_t
4309  x;
4310 
4311  fxFltType
4312  result = 0.0;
4313 
4314  if (status == MagickFalse)
4315  continue;
4316  p = GetCacheViewVirtualPixels (image_view, 0, y, image->columns, 1, pfx->exception);
4317  q = QueueCacheViewAuthenticPixels (fx_view, 0, y, fx_image->columns, 1, pfx->exception);
4318  if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) {
4319  status=MagickFalse;
4320  continue;
4321  }
4322  for (x=0; x < (ssize_t) fx_image->columns; x++) {
4323  ssize_t i;
4324 
4325  pfx->fxrts[id].thisPixel = (Quantum *)p;
4326 
4327  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4328  {
4329  PixelChannel channel = GetPixelChannelChannel (image, i);
4330  PixelTrait traits = GetPixelChannelTraits (image, channel);
4331  PixelTrait fx_traits = GetPixelChannelTraits (fx_image, channel);
4332  if ((traits == UndefinedPixelTrait) ||
4333  (fx_traits == UndefinedPixelTrait))
4334  continue;
4335  if ((fx_traits & CopyPixelTrait) != 0) {
4336  SetPixelChannel (fx_image, channel, p[i], q);
4337  continue;
4338  }
4339 
4340  if (!ExecuteRPN (pfx, &pfx->fxrts[id], &result, channel, x, y)) {
4341  status=MagickFalse;
4342  break;
4343  }
4344 
4345  q[i] = ClampToQuantum ((MagickRealType) (QuantumRange*result));
4346  }
4347  p+=(ptrdiff_t) GetPixelChannels (image);
4348  q+=(ptrdiff_t) GetPixelChannels (fx_image);
4349  }
4350  if (SyncCacheViewAuthenticPixels(fx_view, pfx->exception) == MagickFalse)
4351  status=MagickFalse;
4352  if (image->progress_monitor != (MagickProgressMonitor) NULL)
4353  {
4354  MagickBooleanType
4355  proceed;
4356 
4357 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4358  #pragma omp atomic
4359 #endif
4360  progress++;
4361  proceed = SetImageProgress (image, FxImageTag, progress, image->rows);
4362  if (proceed == MagickFalse)
4363  status=MagickFalse;
4364  }
4365  }
4366 
4367  fx_view = DestroyCacheView (fx_view);
4368  image_view = DestroyCacheView (image_view);
4369 
4370  /* Before destroying the user symbol values, dump them to stderr.
4371  */
4372  if (pfx->DebugOpt && pfx->usedUserSymbols) {
4373  int t, i;
4374  char UserSym[MagickPathExtent];
4375  fprintf (stderr, "User symbols (%i):\n", pfx->usedUserSymbols);
4376  for (t=0; t < (int) GetMagickResourceLimit(ThreadResource); t++) {
4377  for (i = 0; i < (int) pfx->usedUserSymbols; i++) {
4378  fprintf (stderr, "th=%i us=%i '%s': %.*Lg\n",
4379  t, i, NameOfUserSym (pfx, i, UserSym), pfx->precision, pfx->fxrts[t].UserSymVals[i]);
4380  }
4381  }
4382  }
4383 
4384  if ((status == MagickFalse) || (pfx->exception->severity >= ErrorException))
4385  fx_image=DestroyImage(fx_image);
4386 
4387  pfx=DestroyFxInfo(pfx);
4388 
4389  return(fx_image);
4390 }
ElementT
Definition: fx.c:659
ImgAttrT
Definition: fx.c:449
fxRtT
Definition: fx.c:702
_CacheView
Definition: cache-view.c:65
SymbolT
Definition: fx.c:516
UserSymbolT
Definition: fx.c:631
_Image
Definition: image.h:131
_PixelInfo
Definition: pixel.h:181
ImgT
Definition: fx.c:697
_FxInfo
Definition: fx.c:711
ChannelT
Definition: fx.c:595
OperatorT
Definition: fx.c:154
TernaryT
Definition: fx.c:589
_ExceptionInfo
Definition: exception.h:101
ControlT
Definition: fx.c:565
FunctionT
Definition: fx.c:326
ConstantT
Definition: fx.c:220
_ChannelStatistics
Definition: statistic.h:29
_RandomInfo
Definition: random.c:83