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