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