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