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