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