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