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