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