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