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