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