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