MagickCore 7.1.1
Convert, Edit, Or Compose Bitmap Images
Loading...
Searching...
No Matches
fx.c
1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% FFFFF X X %
7% F X X %
8% FFF X %
9% F X X %
10% F X X %
11% %
12% %
13% MagickCore Image Special Effects Methods %
14% %
15% Software Design %
16% snibgo (Alan Gibson) %
17% January 2022 %
18% %
19% %
20% %
21% Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization %
22% dedicated to making software imaging solutions freely available. %
23% %
24% You may not use this file except in compliance with the License. You may %
25% obtain a copy of the License at %
26% %
27% https://imagemagick.org/script/license.php %
28% %
29% Unless required by applicable law or agreed to in writing, software %
30% distributed under the License is distributed on an "AS IS" BASIS, %
31% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32% See the License for the specific language governing permissions and %
33% limitations under the License. %
34% %
35%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36%
37%
38%
39*/
40
41/*
42 Include declarations.
43*/
44#include "MagickCore/studio.h"
45#include "MagickCore/accelerate-private.h"
46#include "MagickCore/annotate.h"
47#include "MagickCore/artifact.h"
48#include "MagickCore/attribute.h"
49#include "MagickCore/cache.h"
50#include "MagickCore/cache-view.h"
51#include "MagickCore/channel.h"
52#include "MagickCore/color.h"
53#include "MagickCore/color-private.h"
54#include "MagickCore/colorspace-private.h"
55#include "MagickCore/composite.h"
56#include "MagickCore/decorate.h"
57#include "MagickCore/distort.h"
58#include "MagickCore/draw.h"
59#include "MagickCore/effect.h"
60#include "MagickCore/enhance.h"
61#include "MagickCore/exception.h"
62#include "MagickCore/exception-private.h"
63#include "MagickCore/fx.h"
64#include "MagickCore/fx-private.h"
65#include "MagickCore/gem.h"
66#include "MagickCore/gem-private.h"
67#include "MagickCore/geometry.h"
68#include "MagickCore/layer.h"
69#include "MagickCore/list.h"
70#include "MagickCore/log.h"
71#include "MagickCore/image.h"
72#include "MagickCore/image-private.h"
73#include "MagickCore/magick.h"
74#include "MagickCore/memory_.h"
75#include "MagickCore/memory-private.h"
76#include "MagickCore/monitor.h"
77#include "MagickCore/monitor-private.h"
78#include "MagickCore/option.h"
79#include "MagickCore/pixel.h"
80#include "MagickCore/pixel-accessor.h"
81#include "MagickCore/policy.h"
82#include "MagickCore/property.h"
83#include "MagickCore/quantum.h"
84#include "MagickCore/quantum-private.h"
85#include "MagickCore/random_.h"
86#include "MagickCore/random-private.h"
87#include "MagickCore/resample.h"
88#include "MagickCore/resample-private.h"
89#include "MagickCore/resize.h"
90#include "MagickCore/resource_.h"
91#include "MagickCore/splay-tree.h"
92#include "MagickCore/statistic.h"
93#include "MagickCore/statistic-private.h"
94#include "MagickCore/string_.h"
95#include "MagickCore/thread-private.h"
96#include "MagickCore/threshold.h"
97#include "MagickCore/timer-private.h"
98#include "MagickCore/token.h"
99#include "MagickCore/transform.h"
100#include "MagickCore/transform-private.h"
101#include "MagickCore/utility.h"
102
103
104#define MaxTokenLen 100
105#define RpnInit 100
106#define TableExtend 0.1
107#define InitNumOprStack 50
108#define MinValStackSize 100
109#define InitNumUserSymbols 50
110
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 if (!PushOperatorStack (pfx, (int) op)) return MagickFalse;
2174 pfx->pex++;
2175 SkipSpaces (pfx);
2176 if (!*pfx->pex) return MagickFalse;
2177
2178 if (!GetOperand (pfx, UserSymbol, NewUserSymbol, UserSymNdx, needPopAll)) {
2179 (void) ThrowMagickException (
2180 pfx->exception, GetMagickModule(), OptionError,
2181 "After unary, bad operand at", "'%s'",
2182 SetShortExp(pfx));
2183 return MagickFalse;
2184 }
2185
2186 if (*NewUserSymbol) {
2187 (void) ThrowMagickException (
2188 pfx->exception, GetMagickModule(), OptionError,
2189 "After unary, NewUserSymbol at", "'%s'",
2190 SetShortExp(pfx));
2191 return MagickFalse;
2192 }
2193
2194 if (*UserSymbol) {
2195 (void) AddAddressingElement (pfx, rCopyFrom, *UserSymNdx);
2196 *UserSymNdx = NULL_ADDRESS;
2197
2198 *UserSymbol = MagickFalse;
2199 *NewUserSymbol = MagickFalse;
2200 }
2201
2202 (void) GetToken (pfx);
2203 return MagickTrue;
2204 } else if (*pfx->pex == '#') {
2205 fxFltType v0=0, v1=0, v2=0;
2206 ssize_t lenToken = GetHexColour (pfx, &v0, &v1, &v2);
2207 if (lenToken < 0) {
2208 (void) ThrowMagickException (
2209 pfx->exception, GetMagickModule(), OptionError,
2210 "Bad hex number at", "'%s'",
2211 SetShortExp(pfx));
2212 return MagickFalse;
2213 } else if (lenToken > 0) {
2214 (void) AddColourElement (pfx, v0, v1, v2);
2215 pfx->pex+=lenToken;
2216 }
2217 return MagickTrue;
2218 }
2219
2220 /* Try a constant number.
2221 */
2222 {
2223 char * tailptr;
2224 ssize_t lenOptArt;
2225 fxFltType val = strtold (pfx->pex, &tailptr);
2226 if (pfx->pex != tailptr) {
2227 pfx->pex = tailptr;
2228 if (*tailptr) {
2229 /* Could have "prefix" K, Ki, M etc.
2230 See https://en.wikipedia.org/wiki/Metric_prefix
2231 and https://en.wikipedia.org/wiki/Binary_prefix
2232 */
2233 double Pow = 0.0;
2234 const char Prefixes[] = "yzafpnum.kMGTPEZY";
2235 const char * pSi = strchr (Prefixes, *tailptr);
2236 if (pSi && *pSi != '.') Pow = (double) ((pSi - Prefixes) * 3 - 24);
2237 else if (*tailptr == 'c') Pow = -2;
2238 else if (*tailptr == 'h') Pow = 2;
2239 else if (*tailptr == 'k') Pow = 3;
2240 if (Pow != 0.0) {
2241 if (*(++pfx->pex) == 'i') {
2242 val *= pow (2.0, Pow/0.3);
2243 pfx->pex++;
2244 } else {
2245 val *= pow (10.0, Pow);
2246 }
2247 }
2248 }
2249 (void) AddElement (pfx, val, oNull);
2250 return MagickTrue;
2251 }
2252
2253 val = (fxFltType) 0;
2254 lenOptArt = GetProperty (pfx, &val);
2255 if (lenOptArt < 0) return MagickFalse;
2256 if (lenOptArt > 0) {
2257 (void) AddElement (pfx, val, oNull);
2258 pfx->pex += lenOptArt;
2259 return MagickTrue;
2260 }
2261 }
2262
2263 } /* end of lenToken==0 */
2264
2265 if (pfx->lenToken > 0) {
2266 /* Try a constant
2267 */
2268 {
2269 ConstantE ce;
2270 for (ce = (ConstantE)0; ce < cNull; ce=(ConstantE) (ce+1)) {
2271 const char * ceStr = Constants[ce].str;
2272 if (LocaleCompare (ceStr, pfx->token)==0) {
2273 break;
2274 }
2275 }
2276
2277 if (ce != cNull) {
2278 (void) AddElement (pfx, Constants[ce].val, oNull);
2279 pfx->pex += pfx->lenToken;
2280 return MagickTrue;
2281 }
2282 }
2283
2284 /* Try a function
2285 */
2286 {
2287 FunctionE fe;
2288 for (fe = FirstFunc; fe < fNull; fe=(FunctionE) (fe+1)) {
2289 const char * feStr = Functions[fe-(int) FirstFunc].str;
2290 if (LocaleCompare (feStr, pfx->token)==0) {
2291 break;
2292 }
2293 }
2294
2295 if (fe == fV && pfx->ImgListLen < 2) {
2296 (void) ThrowMagickException (
2297 pfx->exception, GetMagickModule(), OptionError,
2298 "Symbol 'v' but fewer than two images at", "'%s'",
2299 SetShortExp(pfx));
2300 return MagickFalse;
2301 }
2302
2303 if (IsStealth ((int) fe)) {
2304 (void) ThrowMagickException (
2305 pfx->exception, GetMagickModule(), OptionError,
2306 "Function", "'%s' not permitted at '%s'",
2307 pfx->token, SetShortExp(pfx));
2308 }
2309
2310 if (fe == fDo || fe == fFor || fe == fIf || fe == fWhile) {
2311 *needPopAll = MagickTrue;
2312 }
2313
2314 if (fe != fNull) return (GetFunction (pfx, fe));
2315 }
2316
2317 /* Try image attribute
2318 */
2319 {
2320 ImgAttrE ia = GetImgAttrToken (pfx);
2321 if (ia != aNull) {
2322 fxFltType val = 0;
2323 (void) AddElement (pfx, val, (int) ia);
2324
2325 if (ImgAttrs[ia-(int) FirstImgAttr].need_stats != MagickFalse) {
2326 if (IsQualifier (pfx)) {
2327 PixelChannel chQual = GetChannelQualifier (pfx, (int) ia);
2328 ElementT * pel;
2329 if (chQual == NO_CHAN_QUAL) {
2330 (void) ThrowMagickException (
2331 pfx->exception, GetMagickModule(), OptionError,
2332 "Bad channel qualifier at", "'%s'",
2333 SetShortExp(pfx));
2334 return MagickFalse;
2335 }
2336 /* Adjust the element */
2337 pel = &pfx->Elements[pfx->usedElements-1];
2338 pel->channel_qual = chQual;
2339 }
2340 }
2341 return MagickTrue;
2342 }
2343 }
2344
2345 /* Try symbol
2346 */
2347 {
2348 SymbolE se;
2349 for (se = FirstSym; se < sNull; se=(SymbolE) (se+1)) {
2350 const char * seStr = Symbols[se-(int) FirstSym].str;
2351 if (LocaleCompare (seStr, pfx->token)==0) {
2352 break;
2353 }
2354 }
2355 if (se != sNull) {
2356 fxFltType val = 0;
2357 (void) AddElement (pfx, val, (int) se);
2358 pfx->pex += pfx->lenToken;
2359
2360 if (se==sHue || se==sSaturation || se==sLightness) pfx->NeedHsl = MagickTrue;
2361 return MagickTrue;
2362 }
2363 }
2364
2365 /* Try constant colour.
2366 */
2367 {
2368 fxFltType v0, v1, v2;
2369 ssize_t ColLen = GetConstantColour (pfx, &v0, &v1, &v2);
2370 if (ColLen < 0) return MagickFalse;
2371 if (ColLen > 0) {
2372 (void) AddColourElement (pfx, v0, v1, v2);
2373 pfx->pex+=ColLen;
2374 return MagickTrue;
2375 }
2376 }
2377
2378 /* Try image artifact.
2379 */
2380 {
2381 const char *artifact;
2382 artifact = GetImageArtifact (pfx->image, pfx->token);
2383 if (artifact != (const char *) NULL) {
2384 char * tailptr;
2385 fxFltType val = strtold (artifact, &tailptr);
2386 if (pfx->token == tailptr) {
2387 (void) ThrowMagickException (
2388 pfx->exception, GetMagickModule(), OptionError,
2389 "Artifact", "'%s' has value '%s', not a number, at '%s'",
2390 pfx->token, artifact, SetShortExp(pfx));
2391 return MagickFalse;
2392 }
2393 (void) AddElement (pfx, val, oNull);
2394 pfx->pex+=pfx->lenToken;
2395 return MagickTrue;
2396 }
2397 }
2398
2399 /* Try user symbols. If it is, don't AddElement yet.
2400 */
2401 if (TokenMaybeUserSymbol (pfx)) {
2402 *UserSymbol = MagickTrue;
2403 *UserSymNdx = FindUserSymbol (pfx, pfx->token);
2404 if (*UserSymNdx == NULL_ADDRESS) {
2405 *UserSymNdx = AddUserSymbol (pfx, pfx->pex, pfx->lenToken);
2406 *NewUserSymbol = MagickTrue;
2407 } else {
2408 }
2409 pfx->pex += pfx->lenToken;
2410
2411 return MagickTrue;
2412 }
2413 }
2414
2415 (void) ThrowMagickException (
2416 pfx->exception, GetMagickModule(), OptionError,
2417 "Expected operand at", "'%s'",
2418 SetShortExp(pfx));
2419
2420 return MagickFalse;
2421}
2422
2423static inline MagickBooleanType IsRealOperator (OperatorE op)
2424{
2425 return (op < oOpenParen || op > oCloseBrace) ? MagickTrue : MagickFalse;
2426}
2427
2428static inline MagickBooleanType ProcessTernaryOpr (FxInfo * pfx, TernaryT * ptern)
2429/* Ternary operator "... ? ... : ..."
2430 returns false iff we have exception
2431*/
2432{
2433 if (pfx->usedOprStack == 0)
2434 return MagickFalse;
2435 if (pfx->OperatorStack[pfx->usedOprStack-1] == oQuery) {
2436 if (ptern->addr_query != NULL_ADDRESS) {
2437 (void) ThrowMagickException (
2438 pfx->exception, GetMagickModule(), OptionError,
2439 "Already have '?' in sub-expression at", "'%s'",
2440 SetShortExp(pfx));
2441 return MagickFalse;
2442 }
2443 if (ptern->addr_colon != NULL_ADDRESS) {
2444 (void) ThrowMagickException (
2445 pfx->exception, GetMagickModule(), OptionError,
2446 "Already have ':' in sub-expression at", "'%s'",
2447 SetShortExp(pfx));
2448 return MagickFalse;
2449 }
2450 pfx->usedOprStack--;
2451 ptern->addr_query = pfx->usedElements;
2452 (void) AddAddressingElement (pfx, rIfZeroGoto, NULL_ADDRESS);
2453 /* address will be one after the Colon address. */
2454 }
2455 else if (pfx->OperatorStack[pfx->usedOprStack-1] == oColon) {
2456 if (ptern->addr_query == NULL_ADDRESS) {
2457 (void) ThrowMagickException (
2458 pfx->exception, GetMagickModule(), OptionError,
2459 "Need '?' in sub-expression at", "'%s'",
2460 SetShortExp(pfx));
2461 return MagickFalse;
2462 }
2463 if (ptern->addr_colon != NULL_ADDRESS) {
2464 (void) ThrowMagickException (
2465 pfx->exception, GetMagickModule(), OptionError,
2466 "Already have ':' in sub-expression at", "'%s'",
2467 SetShortExp(pfx));
2468 return MagickFalse;
2469 }
2470 pfx->usedOprStack--;
2471 ptern->addr_colon = pfx->usedElements;
2472 pfx->Elements[pfx->usedElements-1].do_push = MagickTrue;
2473 (void) AddAddressingElement (pfx, rGoto, NULL_ADDRESS);
2474 /* address will be after the subexpression */
2475 }
2476 return MagickTrue;
2477}
2478
2479static MagickBooleanType GetOperator (
2480 FxInfo * pfx,
2481 MagickBooleanType * Assign, MagickBooleanType * Update, MagickBooleanType * IncrDecr)
2482{
2483 OperatorE op;
2484 size_t len = 0;
2485 MagickBooleanType DoneIt = MagickFalse;
2486 SkipSpaces (pfx);
2487 for (op = (OperatorE)0; op != oNull; op=(OperatorE) (op+1)) {
2488 const char * opStr = Operators[op].str;
2489 len = strlen(opStr);
2490 if (LocaleNCompare (opStr, pfx->pex, len)==0) {
2491 break;
2492 }
2493 }
2494
2495 if (!IsRealOperator (op)) {
2496 (void) ThrowMagickException (
2497 pfx->exception, GetMagickModule(), OptionError,
2498 "Not a real operator at", "'%s'",
2499 SetShortExp(pfx));
2500 return MagickFalse;
2501 }
2502
2503 if (op==oNull) {
2504 (void) ThrowMagickException (
2505 pfx->exception, GetMagickModule(), OptionError,
2506 "Expected operator at", "'%s'",
2507 SetShortExp(pfx));
2508 return MagickFalse;
2509 }
2510
2511 *Assign = (op==oAssign) ? MagickTrue : MagickFalse;
2512 *Update = OprInPlace ((int) op);
2513 *IncrDecr = (op == oPlusPlus || op == oSubSub) ? MagickTrue : MagickFalse;
2514
2515 /* while top of OperatorStack is not empty and is not open-parens or assign,
2516 and top of OperatorStack is higher precedence than new op,
2517 then move top of OperatorStack to Element list.
2518 */
2519
2520 while (pfx->usedOprStack > 0) {
2521 OperatorE top = pfx->OperatorStack[pfx->usedOprStack-1];
2522 int precTop, precNew;
2523 if (top == oOpenParen || top == oAssign || OprInPlace ((int) top)) break;
2524 precTop = Operators[top].precedence;
2525 precNew = Operators[op].precedence;
2526 /* Assume left associativity.
2527 If right assoc, this would be "<=".
2528 */
2529 if (precTop < precNew) break;
2530 (void) AddElement (pfx, (fxFltType) 0, (int) top);
2531 pfx->usedOprStack--;
2532 }
2533
2534 /* If new op is close paren, and stack top is open paren,
2535 remove stack top.
2536 */
2537 if (op==oCloseParen) {
2538 if (pfx->usedOprStack == 0) {
2539 (void) ThrowMagickException (
2540 pfx->exception, GetMagickModule(), OptionError,
2541 "Found ')' but nothing on stack at", "'%s'",
2542 SetShortExp(pfx));
2543 return MagickFalse;
2544 }
2545
2546 if (pfx->OperatorStack[pfx->usedOprStack-1] != oOpenParen) {
2547 (void) ThrowMagickException (
2548 pfx->exception, GetMagickModule(), OptionError,
2549 "Found ')' but no '(' on stack at", "'%s'",
2550 SetShortExp(pfx));
2551 return MagickFalse;
2552 }
2553 pfx->usedOprStack--;
2554 DoneIt = MagickTrue;
2555 }
2556
2557 if (!DoneIt) {
2558 if (!PushOperatorStack (pfx, (int) op)) return MagickFalse;
2559 }
2560
2561 pfx->pex += len;
2562
2563 return MagickTrue;
2564}
2565
2566static MagickBooleanType ResolveTernaryAddresses (FxInfo * pfx, TernaryT * ptern)
2567{
2568 if (ptern->addr_query == NULL_ADDRESS && ptern->addr_colon == NULL_ADDRESS)
2569 return MagickTrue;
2570
2571 if (ptern->addr_query != NULL_ADDRESS && ptern->addr_colon != NULL_ADDRESS) {
2572 pfx->Elements[ptern->addr_query].element_index = ptern->addr_colon + 1;
2573 pfx->Elements[ptern->addr_colon].element_index = pfx->usedElements;
2574 ptern->addr_query = NULL_ADDRESS;
2575 ptern->addr_colon = NULL_ADDRESS;
2576 } else if (ptern->addr_query != NULL_ADDRESS) {
2577 (void) ThrowMagickException (
2578 pfx->exception, GetMagickModule(), OptionError,
2579 "'?' with no corresponding ':'", "'%s' at '%s'",
2580 pfx->token, SetShortExp(pfx));
2581 return MagickFalse;
2582 } else if (ptern->addr_colon != NULL_ADDRESS) {
2583 (void) ThrowMagickException (
2584 pfx->exception, GetMagickModule(), OptionError,
2585 "':' with no corresponding '?'", "'%s' at '%s'",
2586 pfx->token, SetShortExp(pfx));
2587 return MagickFalse;
2588 }
2589 return MagickTrue;
2590}
2591
2592static MagickBooleanType TranslateExpression (
2593 FxInfo * pfx, const char * strLimit, char * chLimit, MagickBooleanType * needPopAll)
2594{
2595 /* There should be only one New per expression (oAssign), but can be many Old.
2596 */
2597 MagickBooleanType UserSymbol, NewUserSymbol;
2598 int UserSymNdx0, UserSymNdx1;
2599
2600 MagickBooleanType
2601 Assign = MagickFalse,
2602 Update = MagickFalse,
2603 IncrDecr = MagickFalse;
2604
2605 int StartEleNdx;
2606
2607 TernaryT ternary;
2608 ternary.addr_query = NULL_ADDRESS;
2609 ternary.addr_colon = NULL_ADDRESS;
2610
2611 pfx->teDepth++;
2612
2613 *chLimit = '\0';
2614
2615 StartEleNdx = pfx->usedElements-1;
2616 if (StartEleNdx < 0) StartEleNdx = 0;
2617
2618 SkipSpaces (pfx);
2619
2620 if (!*pfx->pex) {
2621 pfx->teDepth--;
2622 return MagickFalse;
2623 }
2624
2625 if (strchr(strLimit,*pfx->pex)!=NULL) {
2626 *chLimit = *pfx->pex;
2627 pfx->pex++;
2628 pfx->teDepth--;
2629
2630 return MagickFalse;
2631 }
2632
2633 if (!GetOperand (pfx, &UserSymbol, &NewUserSymbol, &UserSymNdx0, needPopAll)) return MagickFalse;
2634 SkipSpaces (pfx);
2635
2636 /* Loop through Operator, Operand, Operator, Operand, ...
2637 */
2638 while (*pfx->pex && (!*strLimit || (strchr(strLimit,*pfx->pex)==NULL))) {
2639 if (!GetOperator (pfx, &Assign, &Update, &IncrDecr)) return MagickFalse;
2640 SkipSpaces (pfx);
2641 if (NewUserSymbol && !Assign) {
2642 (void) ThrowMagickException (
2643 pfx->exception, GetMagickModule(), OptionError,
2644 "Expected assignment after new UserSymbol", "'%s' at '%s'",
2645 pfx->token, SetShortExp(pfx));
2646 return MagickFalse;
2647 }
2648 if (!UserSymbol && Assign) {
2649 (void) ThrowMagickException (
2650 pfx->exception, GetMagickModule(), OptionError,
2651 "Attempted assignment to non-UserSymbol", "'%s' at '%s'",
2652 pfx->token, SetShortExp(pfx));
2653 return MagickFalse;
2654 }
2655 if (!UserSymbol && Update) {
2656 (void) ThrowMagickException (
2657 pfx->exception, GetMagickModule(), OptionError,
2658 "Attempted update to non-UserSymbol", "'%s' at '%s'",
2659 pfx->token, SetShortExp(pfx));
2660 return MagickFalse;
2661 }
2662 if (UserSymbol && (Assign || Update) && !IncrDecr) {
2663
2664 if (!TranslateExpression (pfx, strLimit, chLimit, needPopAll)) return MagickFalse;
2665 if (!*pfx->pex) break;
2666 if (!*strLimit) break;
2667 if (strchr(strLimit,*chLimit)!=NULL) break;
2668 }
2669 if (UserSymbol && !Assign && !Update && UserSymNdx0 != NULL_ADDRESS) {
2670 ElementT * pel;
2671 (void) AddAddressingElement (pfx, rCopyFrom, UserSymNdx0);
2672 UserSymNdx0 = NULL_ADDRESS;
2673 pel = &pfx->Elements[pfx->usedElements-1];
2674 pel->do_push = MagickTrue;
2675 }
2676
2677 if (UserSymbol) {
2678 while (TopOprIsUnaryPrefix (pfx)) {
2679 OperatorE op = pfx->OperatorStack[pfx->usedOprStack-1];
2680 (void) AddElement (pfx, (fxFltType) 0, (int) op);
2681 pfx->usedOprStack--;
2682 }
2683 }
2684
2685 if (!ProcessTernaryOpr (pfx, &ternary)) return MagickFalse;
2686
2687 if (ternary.addr_colon != NULL_ADDRESS) {
2688 if (!TranslateExpression (pfx, ",);", chLimit, needPopAll)) return MagickFalse;
2689 break;
2690 }
2691
2692 UserSymbol = NewUserSymbol = MagickFalse;
2693
2694 if ( (!*pfx->pex) || (*strLimit && (strchr(strLimit,*pfx->pex)!=NULL) ) )
2695 {
2696 if (IncrDecr) break;
2697
2698 (void) ThrowMagickException (
2699 pfx->exception, GetMagickModule(), OptionError,
2700 "Expected operand after operator", "at '%s'",
2701 SetShortExp(pfx));
2702 return MagickFalse;
2703 }
2704
2705 if (IncrDecr) {
2706 (void) ThrowMagickException (
2707 pfx->exception, GetMagickModule(), OptionError,
2708 "'++' and '--' must be the final operators in an expression at", "'%s'",
2709 SetShortExp(pfx));
2710 return MagickFalse;
2711 }
2712
2713 if (!GetOperand (pfx, &UserSymbol, &NewUserSymbol, &UserSymNdx1, needPopAll)) {
2714 (void) ThrowMagickException (
2715 pfx->exception, GetMagickModule(), OptionError,
2716 "Expected operand at", "'%s'",
2717 SetShortExp(pfx));
2718 return MagickFalse;
2719 }
2720 SkipSpaces (pfx);
2721 if (NewUserSymbol && !Assign) {
2722 (void) ThrowMagickException (
2723 pfx->exception, GetMagickModule(), OptionError,
2724 "NewUserSymbol", "'%s' after non-assignment operator at '%s'",
2725 pfx->token, SetShortExp(pfx));
2726 return MagickFalse;
2727 }
2728 if (UserSymbol && !NewUserSymbol) {
2729 (void) AddAddressingElement (pfx, rCopyFrom, UserSymNdx1);
2730 UserSymNdx1 = NULL_ADDRESS;
2731 }
2732 UserSymNdx0 = UserSymNdx1;
2733 }
2734
2735 if (UserSymbol && !Assign && !Update && UserSymNdx0 != NULL_ADDRESS) {
2736 ElementT * pel;
2737 if (NewUserSymbol) {
2738 (void) ThrowMagickException (
2739 pfx->exception, GetMagickModule(), OptionError,
2740 "NewUserSymbol", "'%s' needs assignment operator at '%s'",
2741 pfx->token, SetShortExp(pfx));
2742 return MagickFalse;
2743 }
2744 (void) AddAddressingElement (pfx, rCopyFrom, UserSymNdx0);
2745 pel = &pfx->Elements[pfx->usedElements-1];
2746 pel->do_push = MagickTrue;
2747 }
2748
2749 if (*pfx->pex && !*chLimit && (strchr(strLimit,*pfx->pex)!=NULL)) {
2750 *chLimit = *pfx->pex;
2751 pfx->pex++;
2752 }
2753 while (pfx->usedOprStack) {
2754 OperatorE op = pfx->OperatorStack[pfx->usedOprStack-1];
2755 if (op == oOpenParen || op == oOpenBracket || op == oOpenBrace) {
2756 break;
2757 }
2758 if ( (op==oAssign && !Assign) || (OprInPlace((int) op) && !Update) ) {
2759 break;
2760 }
2761 pfx->usedOprStack--;
2762 (void) AddElement (pfx, (fxFltType) 0, (int) op);
2763 if (op == oAssign) {
2764 if (UserSymNdx0 < 0) {
2765 (void) ThrowMagickException (
2766 pfx->exception, GetMagickModule(), OptionError,
2767 "Assignment to unknown user symbol at", "'%s'",
2768 SetShortExp(pfx));
2769 return MagickFalse;
2770 }
2771 /* Adjust last element, by deletion and add.
2772 */
2773 pfx->usedElements--;
2774 (void) AddAddressingElement (pfx, rCopyTo, UserSymNdx0);
2775 break;
2776 } else if (OprInPlace ((int) op)) {
2777 if (UserSymNdx0 < 0) {
2778 (void) ThrowMagickException (
2779 pfx->exception, GetMagickModule(), OptionError,
2780 "Operator-in-place to unknown user symbol at", "'%s'",
2781 SetShortExp(pfx));
2782 return MagickFalse;
2783 }
2784 /* Modify latest element.
2785 */
2786 pfx->Elements[pfx->usedElements-1].element_index = UserSymNdx0;
2787 break;
2788 }
2789 }
2790
2791 if (ternary.addr_query != NULL_ADDRESS) *needPopAll = MagickTrue;
2792
2793 (void) ResolveTernaryAddresses (pfx, &ternary);
2794
2795 pfx->teDepth--;
2796
2797 if (!pfx->teDepth && *needPopAll) {
2798 (void) AddAddressingElement (pfx, rZerStk, NULL_ADDRESS);
2799 *needPopAll = MagickFalse;
2800 }
2801
2802 if (pfx->exception->severity >= ErrorException)
2803 return MagickFalse;
2804
2805 return MagickTrue;
2806}
2807
2808
2809static MagickBooleanType TranslateStatement (FxInfo * pfx, char * strLimit, char * chLimit)
2810{
2811 MagickBooleanType NeedPopAll = MagickFalse;
2812
2813 SkipSpaces (pfx);
2814
2815 if (!*pfx->pex) return MagickFalse;
2816
2817 if (!TranslateExpression (pfx, strLimit, chLimit, &NeedPopAll)) {
2818 return MagickFalse;
2819 }
2820 if (pfx->usedElements && *chLimit==';') {
2821 /* FIXME: not necessarily the last element,
2822 but the last _executed_ element, eg "goto" in a "for()".,
2823 Pending a fix, we will use rZerStk.
2824 */
2825 ElementT * pel = &pfx->Elements[pfx->usedElements-1];
2826 if (pel->do_push) pel->do_push = MagickFalse;
2827 }
2828
2829 return MagickTrue;
2830}
2831
2832static MagickBooleanType TranslateStatementList (FxInfo * pfx, const char * strLimit, char * chLimit)
2833{
2834#define MAX_SLIMIT 10
2835 char sLimits[MAX_SLIMIT];
2836 SkipSpaces (pfx);
2837
2838 if (!*pfx->pex) return MagickFalse;
2839 (void) CopyMagickString (sLimits, strLimit, MAX_SLIMIT-1);
2840
2841 if (strchr(strLimit,';')==NULL)
2842 (void) ConcatenateMagickString (sLimits, ";", MAX_SLIMIT);
2843
2844 for (;;) {
2845 if (!TranslateStatement (pfx, sLimits, chLimit)) return MagickFalse;
2846
2847 if (!*pfx->pex) break;
2848
2849 if (*chLimit != ';') {
2850 break;
2851 }
2852 }
2853
2854 if (pfx->exception->severity >= ErrorException)
2855 return MagickFalse;
2856
2857 return MagickTrue;
2858}
2859
2860/*--------------------------------------------------------------------
2861 Run-time
2862*/
2863
2864static ChannelStatistics *CollectOneImgStats (FxInfo * pfx, Image * img)
2865{
2866 int ch;
2867 ChannelStatistics * cs = GetImageStatistics (img, pfx->exception);
2868 /* Use RelinquishMagickMemory() somewhere. */
2869
2870 if (cs == (ChannelStatistics *) NULL)
2871 return((ChannelStatistics *) NULL);
2872
2873 for (ch=0; ch <= (int) MaxPixelChannels; ch++) {
2874 cs[ch].mean *= QuantumScale;
2875 cs[ch].median *= QuantumScale;
2876 cs[ch].maxima *= QuantumScale;
2877 cs[ch].minima *= QuantumScale;
2878 cs[ch].standard_deviation *= QuantumScale;
2879 }
2880
2881 return cs;
2882}
2883
2884static MagickBooleanType CollectStatistics (FxInfo * pfx)
2885{
2886 Image * img = GetFirstImageInList (pfx->image);
2887
2888 size_t imgNum=0;
2889
2890 pfx->statistics = (ChannelStatistics**) AcquireMagickMemory ((size_t) pfx->ImgListLen * sizeof (ChannelStatistics *));
2891 if (!pfx->statistics) {
2892 (void) ThrowMagickException (
2893 pfx->exception, GetMagickModule(), ResourceLimitFatalError,
2894 "Statistics", "%lu",
2895 (unsigned long) pfx->ImgListLen);
2896 return MagickFalse;
2897 }
2898
2899 for (;;) {
2900 pfx->statistics[imgNum] = CollectOneImgStats (pfx, img);
2901
2902 if (++imgNum == pfx->ImgListLen) break;
2903 img = GetNextImageInList (img);
2904 assert (img != (Image *) NULL);
2905 }
2906 pfx->GotStats = MagickTrue;
2907
2908 return MagickTrue;
2909}
2910
2911static inline MagickBooleanType PushVal (FxInfo * pfx, fxRtT * pfxrt, fxFltType val, int addr)
2912{
2913 if (pfxrt->usedValStack >=pfxrt->numValStack) {
2914 (void) ThrowMagickException (
2915 pfx->exception, GetMagickModule(), OptionError,
2916 "ValStack overflow at addr=", "%i",
2917 addr);
2918 return MagickFalse;
2919 }
2920
2921 pfxrt->ValStack[pfxrt->usedValStack++] = val;
2922 return MagickTrue;
2923}
2924
2925static inline fxFltType PopVal (FxInfo * pfx, fxRtT * pfxrt, int addr)
2926{
2927 if (pfxrt->usedValStack <= 0) {
2928 (void) ThrowMagickException (
2929 pfx->exception, GetMagickModule(), OptionError,
2930 "ValStack underflow at addr=", "%i",
2931 addr);
2932 return (fxFltType) 0;
2933 }
2934
2935 return pfxrt->ValStack[--pfxrt->usedValStack];
2936}
2937
2938static inline fxFltType ImageStat (
2939 FxInfo * pfx, ssize_t ImgNum, PixelChannel channel, ImgAttrE ia)
2940{
2941 ChannelStatistics * cs = NULL;
2942 fxFltType ret = 0;
2943 MagickBooleanType NeedRelinq = MagickFalse;
2944
2945 if (ImgNum < 0)
2946 {
2947 (void) ThrowMagickException(pfx->exception,GetMagickModule(),
2948 OptionError,"NoSuchImage","%lu",(unsigned long) ImgNum);
2949 ImgNum=0;
2950 }
2951
2952 if (pfx->GotStats) {
2953 if ((channel < 0) || (channel > MaxPixelChannels))
2954 {
2955 (void) ThrowMagickException(pfx->exception,GetMagickModule(),
2956 OptionError,"NoSuchImageChannel","%i",channel);
2957 channel=(PixelChannel) 0;
2958 }
2959 cs = pfx->statistics[ImgNum];
2960 } else if (pfx->NeedStats) {
2961 /* If we need more than one statistic per pixel, this is inefficient. */
2962 if ((channel < 0) || (channel > MaxPixelChannels))
2963 {
2964 (void) ThrowMagickException(pfx->exception,GetMagickModule(),
2965 OptionError,"NoSuchImageChannel","%i",channel);
2966 channel=(PixelChannel) 0;
2967 }
2968 cs = CollectOneImgStats (pfx, pfx->Images[ImgNum]);
2969 NeedRelinq = MagickTrue;
2970 }
2971
2972 switch (ia) {
2973 case aDepth:
2974 ret = (fxFltType) GetImageDepth (pfx->Images[ImgNum], pfx->exception);
2975 break;
2976 case aExtent:
2977 ret = (fxFltType) GetBlobSize (pfx->image);
2978 break;
2979 case aKurtosis:
2980 if ((cs != (ChannelStatistics *) NULL) && (channel >= 0))
2981 ret = cs[channel].kurtosis;
2982 break;
2983 case aMaxima:
2984 if ((cs != (ChannelStatistics *) NULL) && (channel >= 0))
2985 ret = cs[channel].maxima;
2986 break;
2987 case aMean:
2988 if ((cs != (ChannelStatistics *) NULL) && (channel >= 0))
2989 ret = cs[channel].mean;
2990 break;
2991 case aMedian:
2992 if ((cs != (ChannelStatistics *) NULL) && (channel >= 0))
2993 ret = cs[channel].median;
2994 break;
2995 case aMinima:
2996 if ((cs != (ChannelStatistics *) NULL) && (channel >= 0))
2997 ret = cs[channel].minima;
2998 break;
2999 case aPage:
3000 /* Do nothing */
3001 break;
3002 case aPageX:
3003 ret = (fxFltType) pfx->Images[ImgNum]->page.x;
3004 break;
3005 case aPageY:
3006 ret = (fxFltType) pfx->Images[ImgNum]->page.y;
3007 break;
3008 case aPageWid:
3009 ret = (fxFltType) pfx->Images[ImgNum]->page.width;
3010 break;
3011 case aPageHt:
3012 ret = (fxFltType) pfx->Images[ImgNum]->page.height;
3013 break;
3014 case aPrintsize:
3015 /* Do nothing */
3016 break;
3017 case aPrintsizeX:
3018 ret = (fxFltType) PerceptibleReciprocal (pfx->Images[ImgNum]->resolution.x)
3019 * pfx->Images[ImgNum]->columns;
3020 break;
3021 case aPrintsizeY:
3022 ret = (fxFltType) PerceptibleReciprocal (pfx->Images[ImgNum]->resolution.y)
3023 * pfx->Images[ImgNum]->rows;
3024 break;
3025 case aQuality:
3026 ret = (fxFltType) pfx->Images[ImgNum]->quality;
3027 break;
3028 case aRes:
3029 /* Do nothing */
3030 break;
3031 case aResX:
3032 ret = pfx->Images[ImgNum]->resolution.x;
3033 break;
3034 case aResY:
3035 ret = pfx->Images[ImgNum]->resolution.y;
3036 break;
3037 case aSkewness:
3038 if ((cs != (ChannelStatistics *) NULL) && (channel >= 0))
3039 ret = cs[channel].skewness;
3040 break;
3041 case aStdDev:
3042 if ((cs != (ChannelStatistics *) NULL) && (channel >= 0))
3043 ret = cs[channel].standard_deviation;
3044 break;
3045 case aH:
3046 ret = (fxFltType) pfx->Images[ImgNum]->rows;
3047 break;
3048 case aN:
3049 ret = (fxFltType) pfx->ImgListLen;
3050 break;
3051 case aT: /* image index in list */
3052 ret = (fxFltType) ImgNum;
3053 break;
3054 case aW:
3055 ret = (fxFltType) pfx->Images[ImgNum]->columns;
3056 break;
3057 case aZ:
3058 ret = (fxFltType) GetImageDepth (pfx->Images[ImgNum], pfx->exception);
3059 break;
3060 default:
3061 (void) ThrowMagickException (pfx->exception,GetMagickModule(),OptionError,
3062 "Unknown ia=","%i",ia);
3063 }
3064 if (NeedRelinq) cs = (ChannelStatistics *)RelinquishMagickMemory (cs);
3065
3066 return ret;
3067}
3068
3069static inline fxFltType FxGcd (fxFltType x, fxFltType y, const size_t depth)
3070{
3071#define FxMaxFunctionDepth 200
3072
3073 if (x < y)
3074 return (FxGcd (y, x, depth+1));
3075 if ((fabs((double) y) < 0.001) || (depth >= FxMaxFunctionDepth))
3076 return (x);
3077 return (FxGcd (y, x-y*floor((double) (x/y)), depth+1));
3078}
3079
3080static inline ssize_t ChkImgNum (FxInfo * pfx, fxFltType f)
3081/* Returns -1 if f is too large. */
3082{
3083 ssize_t i = (ssize_t) floor ((double) f + 0.5);
3084 if (i < 0) i += (ssize_t) pfx->ImgListLen;
3085 if (i < 0 || i >= (ssize_t) pfx->ImgListLen) {
3086 (void) ThrowMagickException (
3087 pfx->exception, GetMagickModule(), OptionError,
3088 "ImgNum", "%lu bad for ImgListLen %lu",
3089 (unsigned long) i, (unsigned long) pfx->ImgListLen);
3090 i = -1;
3091 }
3092 return i;
3093}
3094
3095#define WHICH_ATTR_CHAN \
3096 (pel->channel_qual == NO_CHAN_QUAL) ? CompositePixelChannel : \
3097 (pel->channel_qual == THIS_CHANNEL) ? channel : pel->channel_qual
3098
3099#define WHICH_NON_ATTR_CHAN \
3100 (pel->channel_qual == NO_CHAN_QUAL || \
3101 pel->channel_qual == THIS_CHANNEL || \
3102 pel->channel_qual == CompositePixelChannel \
3103 ) ? (channel == CompositePixelChannel ? RedPixelChannel: channel) \
3104 : pel->channel_qual
3105
3106static fxFltType GetHslFlt (FxInfo * pfx, ssize_t ImgNum, const fxFltType fx, const fxFltType fy,
3107 PixelChannel channel)
3108{
3109 Image * img = pfx->Images[ImgNum];
3110
3111 double red, green, blue;
3112 double hue=0, saturation=0, lightness=0;
3113
3114 MagickBooleanType okay = MagickTrue;
3115 if(!InterpolatePixelChannel (img, pfx->Imgs[ImgNum].View, RedPixelChannel, img->interpolate,
3116 (double) fx, (double) fy, &red, pfx->exception)) okay = MagickFalse;
3117 if(!InterpolatePixelChannel (img, pfx->Imgs[ImgNum].View, GreenPixelChannel, img->interpolate,
3118 (double) fx, (double) fy, &green, pfx->exception)) okay = MagickFalse;
3119 if(!InterpolatePixelChannel (img, pfx->Imgs[ImgNum].View, BluePixelChannel, img->interpolate,
3120 (double) fx, (double) fy, &blue, pfx->exception)) okay = MagickFalse;
3121
3122 if (!okay)
3123 (void) ThrowMagickException (
3124 pfx->exception, GetMagickModule(), OptionError,
3125 "GetHslFlt failure", "%lu %g,%g %i", (unsigned long) ImgNum,
3126 (double) fx, (double) fy, channel);
3127
3128 ConvertRGBToHSL (
3129 red, green, blue,
3130 &hue, &saturation, &lightness);
3131
3132 if (channel == HUE_CHANNEL) return hue;
3133 if (channel == SAT_CHANNEL) return saturation;
3134 if (channel == LIGHT_CHANNEL) return lightness;
3135
3136 return 0.0;
3137}
3138
3139static fxFltType GetHslInt (FxInfo * pfx, ssize_t ImgNum, const ssize_t imgx, const ssize_t imgy, PixelChannel channel)
3140{
3141 Image * img = pfx->Images[ImgNum];
3142
3143 double hue=0, saturation=0, lightness=0;
3144
3145 const Quantum * p = GetCacheViewVirtualPixels (pfx->Imgs[ImgNum].View, imgx, imgy, 1, 1, pfx->exception);
3146 if (p == (const Quantum *) NULL)
3147 {
3148 (void) ThrowMagickException (pfx->exception,GetMagickModule(),
3149 OptionError,"GetHslInt failure","%lu %li,%li %i",(unsigned long) ImgNum,
3150 (long) imgx,(long) imgy,channel);
3151 return(0.0);
3152 }
3153
3154 ConvertRGBToHSL (
3155 GetPixelRed (img, p), GetPixelGreen (img, p), GetPixelBlue (img, p),
3156 &hue, &saturation, &lightness);
3157
3158 if (channel == HUE_CHANNEL) return hue;
3159 if (channel == SAT_CHANNEL) return saturation;
3160 if (channel == LIGHT_CHANNEL) return lightness;
3161
3162 return 0.0;
3163}
3164
3165static inline fxFltType GetIntensity (FxInfo * pfx, ssize_t ImgNum, const fxFltType fx, const fxFltType fy)
3166{
3167 Quantum
3168 quantum_pixel[MaxPixelChannels];
3169
3170 PixelInfo
3171 pixelinf;
3172
3173 Image * img = pfx->Images[ImgNum];
3174
3175 (void) GetPixelInfo (img, &pixelinf);
3176
3177 if (!InterpolatePixelInfo (img, pfx->Imgs[pfx->ImgNum].View, img->interpolate,
3178 (double) fx, (double) fy, &pixelinf, pfx->exception))
3179 {
3180 (void) ThrowMagickException (
3181 pfx->exception, GetMagickModule(), OptionError,
3182 "GetIntensity failure", "%lu %g,%g", (unsigned long) ImgNum,
3183 (double) fx, (double) fy);
3184 }
3185
3186 SetPixelViaPixelInfo (img, &pixelinf, quantum_pixel);
3187 return QuantumScale * GetPixelIntensity (img, quantum_pixel);
3188}
3189
3190static MagickBooleanType ExecuteRPN (FxInfo * pfx, fxRtT * pfxrt, fxFltType *result,
3191 const PixelChannel channel, const ssize_t imgx, const ssize_t imgy)
3192{
3193 const Quantum * p = pfxrt->thisPixel;
3194 fxFltType regA=0, regB=0, regC=0, regD=0, regE=0;
3195 Image * img = pfx->image;
3196 ChannelStatistics * cs = NULL;
3197 MagickBooleanType NeedRelinq = MagickFalse;
3198 double hue=0, saturation=0, lightness=0;
3199 int i;
3200
3201 /* For -fx, this sets p to ImgNum 0.
3202 for %[fx:...], this sets p to the current image.
3203 Similarly img.
3204 */
3205 if (!p) p = GetCacheViewVirtualPixels (
3206 pfx->Imgs[pfx->ImgNum].View, imgx, imgy, 1, 1, pfx->exception);
3207
3208 if (p == (const Quantum *) NULL)
3209 {
3210 (void) ThrowMagickException (pfx->exception,GetMagickModule(),
3211 OptionError,"Can't get virtual pixels","%lu %li,%li",(unsigned long)
3212 pfx->ImgNum,(long) imgx,(long) imgy);
3213 return(MagickFalse);
3214 }
3215
3216 if (pfx->GotStats) {
3217 cs = pfx->statistics[pfx->ImgNum];
3218 } else if (pfx->NeedStats) {
3219 cs = CollectOneImgStats (pfx, pfx->Images[pfx->ImgNum]);
3220 NeedRelinq = MagickTrue;
3221 }
3222
3223 /* Following is only for expressions like "saturation", with no image specifier.
3224 */
3225 if (pfx->NeedHsl) {
3226 ConvertRGBToHSL (
3227 GetPixelRed (img, p), GetPixelGreen (img, p), GetPixelBlue (img, p),
3228 &hue, &saturation, &lightness);
3229 }
3230
3231 for (i=0; i < pfx->usedElements; i++) {
3232 ElementT
3233 *pel;
3234
3235 if (i < 0) {
3236 (void) ThrowMagickException (
3237 pfx->exception, GetMagickModule(), OptionError,
3238 "Bad run-time address", "%i", i);
3239 }
3240 pel=&pfx->Elements[i];
3241 switch (pel->number_args) {
3242 case 0:
3243 break;
3244 case 1:
3245 regA = PopVal (pfx, pfxrt, i);
3246 break;
3247 case 2:
3248 regB = PopVal (pfx, pfxrt, i);
3249 regA = PopVal (pfx, pfxrt, i);
3250 break;
3251 case 3:
3252 regC = PopVal (pfx, pfxrt, i);
3253 regB = PopVal (pfx, pfxrt, i);
3254 regA = PopVal (pfx, pfxrt, i);
3255 break;
3256 case 4:
3257 regD = PopVal (pfx, pfxrt, i);
3258 regC = PopVal (pfx, pfxrt, i);
3259 regB = PopVal (pfx, pfxrt, i);
3260 regA = PopVal (pfx, pfxrt, i);
3261 break;
3262 case 5:
3263 regE = PopVal (pfx, pfxrt, i);
3264 regD = PopVal (pfx, pfxrt, i);
3265 regC = PopVal (pfx, pfxrt, i);
3266 regB = PopVal (pfx, pfxrt, i);
3267 regA = PopVal (pfx, pfxrt, i);
3268 break;
3269 default:
3270 (void) ThrowMagickException (
3271 pfx->exception, GetMagickModule(), OptionError,
3272 "Too many args:", "%i", pel->number_args);
3273 break;
3274 }
3275
3276 switch (pel->operator_index) {
3277 case oAddEq:
3278 regA = (pfxrt->UserSymVals[pel->element_index] += regA);
3279 break;
3280 case oSubtractEq:
3281 regA = (pfxrt->UserSymVals[pel->element_index] -= regA);
3282 break;
3283 case oMultiplyEq:
3284 regA = (pfxrt->UserSymVals[pel->element_index] *= regA);
3285 break;
3286 case oDivideEq:
3287 regA = (pfxrt->UserSymVals[pel->element_index] *= PerceptibleReciprocal((double)regA));
3288 break;
3289 case oPlusPlus:
3290 regA = pfxrt->UserSymVals[pel->element_index]++;
3291 break;
3292 case oSubSub:
3293 regA = pfxrt->UserSymVals[pel->element_index]--;
3294 break;
3295 case oAdd:
3296 regA += regB;
3297 break;
3298 case oSubtract:
3299 regA -= regB;
3300 break;
3301 case oMultiply:
3302 regA *= regB;
3303 break;
3304 case oDivide:
3305 regA *= PerceptibleReciprocal((double)regB);
3306 break;
3307 case oModulus:
3308 regA = fmod ((double) regA, fabs(floor((double) regB+0.5)));
3309 break;
3310 case oUnaryPlus:
3311 /* Do nothing. */
3312 break;
3313 case oUnaryMinus:
3314 regA = -regA;
3315 break;
3316 case oLshift:
3317 if ((size_t) (regB+0.5) >= (8*sizeof(size_t)))
3318 {
3319 (void) ThrowMagickException ( pfx->exception, GetMagickModule(),
3320 OptionError, "undefined shift", "%g", (double) regB);
3321 regA = (fxFltType) 0.0;
3322 break;
3323 }
3324 regA = (fxFltType) ((size_t)(regA+0.5) << (size_t)(regB+0.5));
3325 break;
3326 case oRshift:
3327 if ((size_t) (regB+0.5) >= (8*sizeof(size_t)))
3328 {
3329 (void) ThrowMagickException ( pfx->exception, GetMagickModule(),
3330 OptionError, "undefined shift", "%g", (double) regB);
3331 regA = (fxFltType) 0.0;
3332 break;
3333 }
3334 regA = (fxFltType) ((size_t)(regA+0.5) >> (size_t)(regB+0.5));
3335 break;
3336 case oEq:
3337 regA = fabs((double) (regA-regB)) < MagickEpsilon ? 1.0 : 0.0;
3338 break;
3339 case oNotEq:
3340 regA = fabs((double) (regA-regB)) >= MagickEpsilon ? 1.0 : 0.0;
3341 break;
3342 case oLtEq:
3343 regA = (regA <= regB) ? 1.0 : 0.0;
3344 break;
3345 case oGtEq:
3346 regA = (regA >= regB) ? 1.0 : 0.0;
3347 break;
3348 case oLt:
3349 regA = (regA < regB) ? 1.0 : 0.0;
3350 break;
3351 case oGt:
3352 regA = (regA > regB) ? 1.0 : 0.0;
3353 break;
3354 case oLogAnd:
3355 regA = (regA<=0) ? 0.0 : (regB > 0) ? 1.0 : 0.0;
3356 break;
3357 case oLogOr:
3358 regA = (regA>0) ? 1.0 : (regB > 0.0) ? 1.0 : 0.0;
3359 break;
3360 case oLogNot:
3361 regA = (regA==0) ? 1.0 : 0.0;
3362 break;
3363 case oBitAnd:
3364 regA = (fxFltType) ((size_t)(regA+0.5) & (size_t)(regB+0.5));
3365 break;
3366 case oBitOr:
3367 regA = (fxFltType) ((size_t)(regA+0.5) | (size_t)(regB+0.5));
3368 break;
3369 case oBitNot:
3370 /* Old fx doesn't add 0.5. */
3371 regA = (fxFltType) (~(size_t)(regA+0.5));
3372 break;
3373 case oPow:
3374 regA = pow ((double) regA, (double) regB);
3375 break;
3376 case oQuery:
3377 case oColon:
3378 break;
3379 case oOpenParen:
3380 case oCloseParen:
3381 case oOpenBracket:
3382 case oCloseBracket:
3383 case oOpenBrace:
3384 case oCloseBrace:
3385 break;
3386 case oAssign:
3387 pel->val = regA;
3388 break;
3389 case oNull: {
3390 if (pel->type == etColourConstant) {
3391 switch (channel) { default:
3392 case (PixelChannel) 0:
3393 regA = pel->val;
3394 break;
3395 case (PixelChannel) 1:
3396 regA = pel->val1;
3397 break;
3398 case (PixelChannel) 2:
3399 regA = pel->val2;
3400 break;
3401 }
3402 } else {
3403 regA = pel->val;
3404 }
3405 break;
3406 }
3407 case fAbs:
3408 regA = fabs ((double) regA);
3409 break;
3410#if defined(MAGICKCORE_HAVE_ACOSH)
3411 case fAcosh:
3412 regA = acosh ((double) regA);
3413 break;
3414#endif
3415 case fAcos:
3416 regA = acos ((double) regA);
3417 break;
3418#if defined(MAGICKCORE_HAVE_J1)
3419 case fAiry:
3420 if (regA==0) regA = 1.0;
3421 else {
3422 fxFltType gamma = 2.0 * j1 ((MagickPI*regA)) / (MagickPI*regA);
3423 regA = gamma * gamma;
3424 }
3425 break;
3426#endif
3427 case fAlt:
3428 regA = (fxFltType) (((ssize_t) regA) & 0x01 ? -1.0 : 1.0);
3429 break;
3430#if defined(MAGICKCORE_HAVE_ASINH)
3431 case fAsinh:
3432 regA = asinh ((double) regA);
3433 break;
3434#endif
3435 case fAsin:
3436 regA = asin ((double) regA);
3437 break;
3438#if defined(MAGICKCORE_HAVE_ATANH)
3439 case fAtanh:
3440 regA = atanh ((double) regA);
3441 break;
3442#endif
3443 case fAtan2:
3444 regA = atan2 ((double) regA, (double) regB);
3445 break;
3446 case fAtan:
3447 regA = atan ((double) regA);
3448 break;
3449 case fCeil:
3450 regA = ceil ((double) regA);
3451 break;
3452 case fChannel:
3453 switch (channel) {
3454 case (PixelChannel) 0: break;
3455 case (PixelChannel) 1: regA = regB; break;
3456 case (PixelChannel) 2: regA = regC; break;
3457 case (PixelChannel) 3: regA = regD; break;
3458 case (PixelChannel) 4: regA = regE; break;
3459 default: regA = 0.0;
3460 }
3461 break;
3462 case fClamp:
3463 if (regA < 0) regA = 0.0;
3464 else if (regA > 1.0) regA = 1.0;
3465 break;
3466 case fCosh:
3467 regA = cosh ((double) regA);
3468 break;
3469 case fCos:
3470 regA = cos ((double) regA);
3471 break;
3472 case fDebug:
3473 /* FIXME: debug() should give channel name. */
3474
3475 (void) fprintf (stderr, "%s[%g,%g].[%i]: %s=%.*g\n",
3476 img->filename, (double) imgx, (double) imgy,
3477 channel, SetPtrShortExp (pfx, pel->exp_start, (size_t) (pel->exp_len+1)),
3478 pfx->precision, (double) regA);
3479 break;
3480 case fDrc:
3481 regA = regA / (regB*(regA-1.0) + 1.0);
3482 break;
3483#if defined(MAGICKCORE_HAVE_ERF)
3484 case fErf:
3485 regA = erf ((double) regA);
3486 break;
3487#endif
3488 case fExp:
3489 regA = exp ((double) regA);
3490 break;
3491 case fFloor:
3492 regA = floor ((double) regA);
3493 break;
3494 case fGauss:
3495 regA = exp((double) (-regA*regA/2.0))/sqrt(2.0*MagickPI);
3496 break;
3497 case fGcd:
3498 if (!IsNaN(regA))
3499 regA = FxGcd (regA, regB, 0);
3500 break;
3501 case fHypot:
3502 regA = hypot ((double) regA, (double) regB);
3503 break;
3504 case fInt:
3505 regA = floor ((double) regA);
3506 break;
3507 case fIsnan:
3508 regA = (fxFltType) (!!IsNaN (regA));
3509 break;
3510#if defined(MAGICKCORE_HAVE_J0)
3511 case fJ0:
3512 regA = j0 ((double) regA);
3513 break;
3514#endif
3515#if defined(MAGICKCORE_HAVE_J1)
3516 case fJ1:
3517 regA = j1 ((double) regA);
3518 break;
3519#endif
3520#if defined(MAGICKCORE_HAVE_J1)
3521 case fJinc:
3522 if (regA==0) regA = 1.0;
3523 else regA = 2.0 * j1 ((MagickPI*regA))/(MagickPI*regA);
3524 break;
3525#endif
3526 case fLn:
3527 regA = log ((double) regA);
3528 break;
3529 case fLogtwo:
3530 regA = MagickLog10((double) regA) / log10(2.0);
3531 break;
3532 case fLog:
3533 regA = MagickLog10 ((double) regA);
3534 break;
3535 case fMax:
3536 regA = (regA > regB) ? regA : regB;
3537 break;
3538 case fMin:
3539 regA = (regA < regB) ? regA : regB;
3540 break;
3541 case fMod:
3542 regA = regA - floor((double) (regA*PerceptibleReciprocal((double) regB)))*regB;
3543 break;
3544 case fNot:
3545 regA = (fxFltType) (regA < MagickEpsilon);
3546 break;
3547 case fPow:
3548 regA = pow ((double) regA, (double) regB);
3549 break;
3550 case fRand: {
3551#if defined(MAGICKCORE_OPENMP_SUPPORT)
3552 #pragma omp critical (MagickCore_ExecuteRPN)
3553#endif
3554 regA = GetPseudoRandomValue (pfxrt->random_info);
3555 break;
3556 }
3557 case fRound:
3558 regA = floor ((double) regA + 0.5);
3559 break;
3560 case fSign:
3561 regA = (regA < 0) ? -1.0 : 1.0;
3562 break;
3563 case fSinc:
3564 regA = sin ((double) (MagickPI*regA)) / (MagickPI*regA);
3565 break;
3566 case fSinh:
3567 regA = sinh ((double) regA);
3568 break;
3569 case fSin:
3570 regA = sin ((double) regA);
3571 break;
3572 case fSqrt:
3573 regA = sqrt ((double) regA);
3574 break;
3575 case fSquish:
3576 regA = 1.0 / (1.0 + exp ((double) -regA));
3577 break;
3578 case fTanh:
3579 regA = tanh ((double) regA);
3580 break;
3581 case fTan:
3582 regA = tan ((double) regA);
3583 break;
3584 case fTrunc:
3585 if (regA >= 0) regA = floor ((double) regA);
3586 else regA = ceil ((double) regA);
3587 break;
3588
3589 case fDo:
3590 case fFor:
3591 case fIf:
3592 case fWhile:
3593 break;
3594 case fU: {
3595 /* Note: 1 value is available, index into image list.
3596 May have ImgAttr qualifier or channel qualifier or both.
3597 */
3598 ssize_t ImgNum = ChkImgNum (pfx, regA);
3599 if (ImgNum < 0) break;
3600 regA = (fxFltType) 0;
3601 if (ImgNum == 0) {
3602 Image * pimg = pfx->Images[0];
3603 if (pel->img_attr_qual == aNull) {
3604 if ((int) pel->channel_qual < 0) {
3605 if (pel->channel_qual == NO_CHAN_QUAL || pel->channel_qual == THIS_CHANNEL) {
3606 if (pfx->ImgNum==0) {
3607 regA = QuantumScale * (double) p[pimg->channel_map[WHICH_NON_ATTR_CHAN].offset];
3608 } else {
3609 const Quantum * pv = GetCacheViewVirtualPixels (
3610 pfx->Imgs[0].View, imgx, imgy, 1,1, pfx->exception);
3611 if (!pv) {
3612 (void) ThrowMagickException (
3613 pfx->exception, GetMagickModule(), OptionError,
3614 "fU can't get cache", "%lu", (unsigned long) ImgNum);
3615 break;
3616 }
3617 regA = QuantumScale * (double) pv[pimg->channel_map[WHICH_NON_ATTR_CHAN].offset];
3618 }
3619 } else if (pel->channel_qual == HUE_CHANNEL || pel->channel_qual == SAT_CHANNEL ||
3620 pel->channel_qual == LIGHT_CHANNEL) {
3621 regA = GetHslInt (pfx, ImgNum, imgx, imgy, pel->channel_qual);
3622 break;
3623 } else if (pel->channel_qual == INTENSITY_CHANNEL) {
3624 regA = GetIntensity (pfx, 0, (double) imgx, (double) imgy);
3625 break;
3626 }
3627 } else {
3628 if (pfx->ImgNum==0) {
3629 regA = QuantumScale * (double) p[pimg->channel_map[WHICH_NON_ATTR_CHAN].offset];
3630 } else {
3631 const Quantum * pv = GetCacheViewVirtualPixels (
3632 pfx->Imgs[0].View, imgx, imgy, 1,1, pfx->exception);
3633 if (!pv) {
3634 (void) ThrowMagickException (
3635 pfx->exception, GetMagickModule(), OptionError,
3636 "fU can't get cache", "%lu", (unsigned long) ImgNum);
3637 break;
3638 }
3639 regA = QuantumScale * (double) pv[pimg->channel_map[WHICH_NON_ATTR_CHAN].offset];
3640 }
3641 }
3642 } else {
3643 /* we have an image attribute */
3644 regA = ImageStat (pfx, 0, WHICH_ATTR_CHAN, pel->img_attr_qual);
3645 }
3646 } else {
3647 /* We have non-zero ImgNum. */
3648 if (pel->img_attr_qual == aNull) {
3649 const Quantum * pv;
3650 if ((int) pel->channel_qual < 0) {
3651 if (pel->channel_qual == HUE_CHANNEL || pel->channel_qual == SAT_CHANNEL ||
3652 pel->channel_qual == LIGHT_CHANNEL)
3653 {
3654 regA = GetHslInt (pfx, ImgNum, imgx, imgy, pel->channel_qual);
3655 break;
3656 } else if (pel->channel_qual == INTENSITY_CHANNEL)
3657 {
3658 regA = GetIntensity (pfx, ImgNum, (fxFltType) imgx, (fxFltType) imgy);
3659 break;
3660 }
3661 }
3662
3663 pv = GetCacheViewVirtualPixels (
3664 pfx->Imgs[ImgNum].View, imgx, imgy, 1,1, pfx->exception);
3665 if (!pv) {
3666 (void) ThrowMagickException (
3667 pfx->exception, GetMagickModule(), OptionError,
3668 "fU can't get cache", "%lu", (unsigned long) ImgNum);
3669 break;
3670 }
3671 regA = QuantumScale * (double)
3672 pv[pfx->Images[ImgNum]->channel_map[WHICH_NON_ATTR_CHAN].offset];
3673 } else {
3674 regA = ImageStat (pfx, ImgNum, WHICH_ATTR_CHAN, pel->img_attr_qual);
3675 }
3676 }
3677 break;
3678 }
3679 case fU0: {
3680 /* No args. No image attribute. We may have a ChannelQual.
3681 If called from %[fx:...], ChannelQual will be CompositePixelChannel.
3682 */
3683 Image * pimg = pfx->Images[0];
3684 if ((int) pel->channel_qual < 0) {
3685 if (pel->channel_qual == NO_CHAN_QUAL || pel->channel_qual == THIS_CHANNEL) {
3686
3687 if (pfx->ImgNum==0) {
3688 regA = QuantumScale * (double) p[pimg->channel_map[WHICH_NON_ATTR_CHAN].offset];
3689 } else {
3690 const Quantum * pv = GetCacheViewVirtualPixels (
3691 pfx->Imgs[0].View, imgx, imgy, 1,1, pfx->exception);
3692 if (!pv) {
3693 (void) ThrowMagickException (
3694 pfx->exception, GetMagickModule(), OptionError,
3695 "fU0 can't get cache", "%i", 0);
3696 break;
3697 }
3698 regA = QuantumScale * (double) pv[pimg->channel_map[WHICH_NON_ATTR_CHAN].offset];
3699 }
3700
3701 } else if (pel->channel_qual == HUE_CHANNEL || pel->channel_qual == SAT_CHANNEL ||
3702 pel->channel_qual == LIGHT_CHANNEL) {
3703 regA = GetHslInt (pfx, 0, imgx, imgy, pel->channel_qual);
3704 break;
3705 } else if (pel->channel_qual == INTENSITY_CHANNEL) {
3706 regA = GetIntensity (pfx, 0, (fxFltType) imgx, (fxFltType) imgy);
3707 }
3708 } else {
3709 if (pfx->ImgNum==0) {
3710 regA = QuantumScale * (double) p[pimg->channel_map[WHICH_NON_ATTR_CHAN].offset];
3711 } else {
3712 const Quantum * pv = GetCacheViewVirtualPixels (
3713 pfx->Imgs[0].View, imgx, imgy, 1,1, pfx->exception);
3714 if (!pv) {
3715 (void) ThrowMagickException (
3716 pfx->exception, GetMagickModule(), OptionError,
3717 "fU0 can't get cache", "%i", 0);
3718 break;
3719 }
3720 regA = QuantumScale * (double) pv[pimg->channel_map[WHICH_NON_ATTR_CHAN].offset];
3721 }
3722 }
3723 break;
3724 }
3725 case fUP: {
3726 /* 3 args are: ImgNum, x, y */
3727 ssize_t ImgNum = ChkImgNum (pfx, regA);
3728 fxFltType fx, fy;
3729
3730 if (ImgNum < 0) break;
3731
3732 if (pel->is_relative) {
3733 fx = imgx + regB;
3734 fy = imgy + regC;
3735 } else {
3736 fx = regB;
3737 fy = regC;
3738 }
3739
3740 if ((int) pel->channel_qual < 0) {
3741 if (pel->channel_qual == HUE_CHANNEL || pel->channel_qual == SAT_CHANNEL
3742 || pel->channel_qual == LIGHT_CHANNEL) {
3743 regA = GetHslFlt (pfx, ImgNum, fx, fy, pel->channel_qual);
3744 break;
3745 } else if (pel->channel_qual == INTENSITY_CHANNEL) {
3746 regA = GetIntensity (pfx, ImgNum, fx, fy);
3747 break;
3748 }
3749 }
3750
3751 {
3752 double v;
3753 Image * imUP = pfx->Images[ImgNum];
3754 if (! InterpolatePixelChannel (imUP, pfx->Imgs[ImgNum].View, WHICH_NON_ATTR_CHAN,
3755 imUP->interpolate, (double) fx, (double) fy, &v, pfx->exception))
3756 {
3757 (void) ThrowMagickException (
3758 pfx->exception, GetMagickModule(), OptionError,
3759 "fUP can't get interpolate", "%lu", (unsigned long) ImgNum);
3760 break;
3761 }
3762 regA = v * QuantumScale;
3763 }
3764
3765 break;
3766 }
3767 case fS:
3768 case fV: {
3769 /* No args. */
3770 ssize_t ImgNum = 1;
3771 if (pel->operator_index == fS) ImgNum = pfx->ImgNum;
3772
3773 if (pel->img_attr_qual == aNull) {
3774 const Quantum * pv = GetCacheViewVirtualPixels (
3775 pfx->Imgs[ImgNum].View, imgx, imgy, 1,1, pfx->exception);
3776 if (!pv) {
3777 (void) ThrowMagickException (
3778 pfx->exception, GetMagickModule(), OptionError,
3779 "fV can't get cache", "%lu", (unsigned long) ImgNum);
3780 break;
3781 }
3782
3783 if ((int) pel->channel_qual < 0) {
3784 if (pel->channel_qual == HUE_CHANNEL || pel->channel_qual == SAT_CHANNEL ||
3785 pel->channel_qual == LIGHT_CHANNEL) {
3786 regA = GetHslInt (pfx, ImgNum, imgx, imgy, pel->channel_qual);
3787 break;
3788 } else if (pel->channel_qual == INTENSITY_CHANNEL) {
3789 regA = GetIntensity (pfx, ImgNum, (double) imgx, (double) imgy);
3790 break;
3791 }
3792 }
3793
3794 regA = QuantumScale * (double)
3795 pv[pfx->Images[ImgNum]->channel_map[WHICH_NON_ATTR_CHAN].offset];
3796 } else {
3797 regA = ImageStat (pfx, ImgNum, WHICH_ATTR_CHAN, pel->img_attr_qual);
3798 }
3799
3800 break;
3801 }
3802 case fP:
3803 case fSP:
3804 case fVP: {
3805 /* 2 args are: x, y */
3806 fxFltType fx, fy;
3807 ssize_t ImgNum = pfx->ImgNum;
3808 if (pel->operator_index == fVP) ImgNum = 1;
3809 if (pel->is_relative) {
3810 fx = imgx + regA;
3811 fy = imgy + regB;
3812 } else {
3813 fx = regA;
3814 fy = regB;
3815 }
3816 if ((int) pel->channel_qual < 0) {
3817 if (pel->channel_qual == HUE_CHANNEL || pel->channel_qual == SAT_CHANNEL ||
3818 pel->channel_qual == LIGHT_CHANNEL) {
3819 regA = GetHslFlt (pfx, ImgNum, fx, fy, pel->channel_qual);
3820 break;
3821 } else if (pel->channel_qual == INTENSITY_CHANNEL) {
3822 regA = GetIntensity (pfx, ImgNum, fx, fy);
3823 break;
3824 }
3825 }
3826
3827 {
3828 double v;
3829
3830 if (! InterpolatePixelChannel (pfx->Images[ImgNum], pfx->Imgs[ImgNum].View,
3831 WHICH_NON_ATTR_CHAN, pfx->Images[ImgNum]->interpolate,
3832 (double) fx, (double) fy, &v, pfx->exception)
3833 )
3834 {
3835 (void) ThrowMagickException (
3836 pfx->exception, GetMagickModule(), OptionError,
3837 "fSP or fVP can't get interp", "%lu", (unsigned long) ImgNum);
3838 break;
3839 }
3840 regA = v * (fxFltType)QuantumScale;
3841 }
3842
3843 break;
3844 }
3845 case fNull:
3846 break;
3847 case aDepth:
3848 regA = (fxFltType) GetImageDepth (img, pfx->exception);
3849 break;
3850 case aExtent:
3851 regA = (fxFltType) img->extent;
3852 break;
3853 case aKurtosis:
3854 if ((cs != (ChannelStatistics *) NULL) && (channel > 0))
3855 regA = cs[WHICH_ATTR_CHAN].kurtosis;
3856 break;
3857 case aMaxima:
3858 if ((cs != (ChannelStatistics *) NULL) && (channel > 0))
3859 regA = cs[WHICH_ATTR_CHAN].maxima;
3860 break;
3861 case aMean:
3862 if ((cs != (ChannelStatistics *) NULL) && (channel > 0))
3863 regA = cs[WHICH_ATTR_CHAN].mean;
3864 break;
3865 case aMedian:
3866 if ((cs != (ChannelStatistics *) NULL) && (channel > 0))
3867 regA = cs[WHICH_ATTR_CHAN].median;
3868 break;
3869 case aMinima:
3870 if ((cs != (ChannelStatistics *) NULL) && (channel > 0))
3871 regA = cs[WHICH_ATTR_CHAN].minima;
3872 break;
3873 case aPage:
3874 break;
3875 case aPageX:
3876 regA = (fxFltType) img->page.x;
3877 break;
3878 case aPageY:
3879 regA = (fxFltType) img->page.y;
3880 break;
3881 case aPageWid:
3882 regA = (fxFltType) img->page.width;
3883 break;
3884 case aPageHt:
3885 regA = (fxFltType) img->page.height;
3886 break;
3887 case aPrintsize:
3888 break;
3889 case aPrintsizeX:
3890 regA = (fxFltType) PerceptibleReciprocal (img->resolution.x) * img->columns;
3891 break;
3892 case aPrintsizeY:
3893 regA = (fxFltType) PerceptibleReciprocal (img->resolution.y) * img->rows;
3894 break;
3895 case aQuality:
3896 regA = (fxFltType) img->quality;
3897 break;
3898 case aRes:
3899 break;
3900 case aResX:
3901 regA = (fxFltType) img->resolution.x;
3902 break;
3903 case aResY:
3904 regA = (fxFltType) img->resolution.y;
3905 break;
3906 case aSkewness:
3907 if ((cs != (ChannelStatistics *) NULL) && (channel > 0))
3908 regA = cs[WHICH_ATTR_CHAN].skewness;
3909 break;
3910 case aStdDev:
3911 if ((cs != (ChannelStatistics *) NULL) && (channel > 0))
3912 regA = cs[WHICH_ATTR_CHAN].standard_deviation;
3913 break;
3914 case aH: /* image->rows */
3915 regA = (fxFltType) img->rows;
3916 break;
3917 case aN: /* image list length */
3918 regA = (fxFltType) pfx->ImgListLen;
3919 break;
3920 case aT: /* image index in list */
3921 regA = (fxFltType) pfx->ImgNum;
3922 break;
3923 case aW: /* image->columns */
3924 regA = (fxFltType) img->columns;
3925 break;
3926 case aZ: /* image depth */
3927 regA = (fxFltType) GetImageDepth (img, pfx->exception);
3928 break;
3929 case aNull:
3930 break;
3931 case sHue: /* of conversion to HSL */
3932 regA = hue;
3933 break;
3934 case sIntensity:
3935 regA = GetIntensity (pfx, pfx->ImgNum, (double) imgx, (double) imgy);
3936 break;
3937 case sLightness: /* of conversion to HSL */
3938 regA = lightness;
3939 break;
3940 case sLuma: /* calculation */
3941 case sLuminance: /* as Luma */
3942 regA = QuantumScale * (0.212656 * (double) GetPixelRed (img,p) +
3943 0.715158 * (double) GetPixelGreen (img,p) +
3944 0.072186 * (double) GetPixelBlue (img,p));
3945 break;
3946 case sSaturation: /* from conversion to HSL */
3947 regA = saturation;
3948 break;
3949 case sA: /* alpha */
3950 regA = QuantumScale * (double) GetPixelAlpha (img, p);
3951 break;
3952 case sB: /* blue */
3953 regA = QuantumScale * (double) GetPixelBlue (img, p);
3954 break;
3955 case sC: /* red (ie cyan) */
3956 regA = QuantumScale * (double) GetPixelCyan (img, p);
3957 break;
3958 case sG: /* green */
3959 regA = QuantumScale * (double) GetPixelGreen (img, p);
3960 break;
3961 case sI: /* current x-coordinate */
3962 regA = (fxFltType) imgx;
3963 break;
3964 case sJ: /* current y-coordinate */
3965 regA = (fxFltType) imgy;
3966 break;
3967 case sK: /* black of CMYK */
3968 regA = QuantumScale * (double) GetPixelBlack (img, p);
3969 break;
3970 case sM: /* green (ie magenta) */
3971 regA = QuantumScale * (double) GetPixelGreen (img, p);
3972 break;
3973 case sO: /* alpha */
3974 regA = QuantumScale * (double) GetPixelAlpha (img, p);
3975 break;
3976 case sR:
3977 regA = QuantumScale * (double) GetPixelRed (img, p);
3978 break;
3979 case sY:
3980 regA = QuantumScale * (double) GetPixelYellow (img, p);
3981 break;
3982 case sNull:
3983 break;
3984
3985 case rGoto:
3986 assert (pel->element_index >= 0);
3987 i = pel->element_index-1; /* -1 because 'for' loop will increment. */
3988 break;
3989 case rGotoChk:
3990 assert (pel->element_index >= 0);
3991 i = pel->element_index-1; /* -1 because 'for' loop will increment. */
3992 if (IsImageTTLExpired(img) != MagickFalse) {
3993 i = pfx->usedElements-1; /* Do no more opcodes. */
3994 (void) ThrowMagickException (pfx->exception, GetMagickModule(),
3995 ResourceLimitFatalError, "TimeLimitExceeded", "`%s'", img->filename);
3996 }
3997 break;
3998 case rIfZeroGoto:
3999 assert (pel->element_index >= 0);
4000 if (fabs((double) regA) < MagickEpsilon) i = pel->element_index-1;
4001 break;
4002 case rIfNotZeroGoto:
4003 assert (pel->element_index >= 0);
4004 if (fabs((double) regA) > MagickEpsilon) i = pel->element_index-1;
4005 break;
4006 case rCopyFrom:
4007 assert (pel->element_index >= 0);
4008 regA = pfxrt->UserSymVals[pel->element_index];
4009 break;
4010 case rCopyTo:
4011 assert (pel->element_index >= 0);
4012 pfxrt->UserSymVals[pel->element_index] = regA;
4013 break;
4014 case rZerStk:
4015 pfxrt->usedValStack = 0;
4016 break;
4017 case rNull:
4018 break;
4019
4020 default:
4021 (void) ThrowMagickException (
4022 pfx->exception, GetMagickModule(), OptionError,
4023 "pel->oprNum", "%i '%s' not yet implemented",
4024 (int)pel->operator_index, OprStr(pel->operator_index));
4025 break;
4026 }
4027 if (pel->do_push)
4028 if (!PushVal (pfx, pfxrt, regA, i)) break;
4029 }
4030
4031 if (pfxrt->usedValStack > 0) regA = PopVal (pfx, pfxrt, 9999);
4032
4033 *result = regA;
4034
4035 if (NeedRelinq) cs = (ChannelStatistics *)RelinquishMagickMemory (cs);
4036
4037 if (pfx->exception->severity >= ErrorException)
4038 return MagickFalse;
4039
4040 if (pfxrt->usedValStack != 0) {
4041 (void) ThrowMagickException (
4042 pfx->exception, GetMagickModule(), OptionError,
4043 "ValStack not empty", "(%i)", pfxrt->usedValStack);
4044 return MagickFalse;
4045 }
4046
4047 return MagickTrue;
4048}
4049
4050/* Following is substitute for FxEvaluateChannelExpression().
4051*/
4052MagickPrivate MagickBooleanType FxEvaluateChannelExpression (
4053 FxInfo *pfx,
4054 const PixelChannel channel, const ssize_t x, const ssize_t y,
4055 double *result, ExceptionInfo *exception)
4056{
4057 const int
4058 id = GetOpenMPThreadId();
4059
4060 fxFltType ret;
4061
4062 assert (pfx != NULL);
4063 assert (pfx->image != NULL);
4064 assert (pfx->Images != NULL);
4065 assert (pfx->Imgs != NULL);
4066 assert (pfx->fxrts != NULL);
4067
4068 pfx->fxrts[id].thisPixel = NULL;
4069
4070 if (!ExecuteRPN (pfx, &pfx->fxrts[id], &ret, channel, x, y)) {
4071 (void) ThrowMagickException (
4072 exception, GetMagickModule(), OptionError,
4073 "ExecuteRPN failed", " ");
4074 return MagickFalse;
4075 }
4076
4077 *result = (double) ret;
4078
4079 return MagickTrue;
4080}
4081
4082static FxInfo *AcquireFxInfoPrivate (const Image * images, const char * expression,
4083 MagickBooleanType CalcAllStats, ExceptionInfo *exception)
4084{
4085 char chLimit;
4086
4087 FxInfo * pfx = (FxInfo*) AcquireCriticalMemory (sizeof (*pfx));
4088
4089 memset (pfx, 0, sizeof (*pfx));
4090
4091 if (!InitFx (pfx, images, CalcAllStats, exception)) {
4092 pfx = (FxInfo*) RelinquishMagickMemory(pfx);
4093 return NULL;
4094 }
4095
4096 if (!BuildRPN (pfx)) {
4097 (void) DeInitFx (pfx);
4098 pfx = (FxInfo*) RelinquishMagickMemory(pfx);
4099 return((FxInfo *) NULL);
4100 }
4101
4102 if ((*expression == '@') && (strlen(expression) > 1))
4103 pfx->expression=FileToString(expression,~0UL,exception);
4104 if (pfx->expression == (char *) NULL)
4105 pfx->expression=ConstantString(expression);
4106 pfx->pex = (char *) pfx->expression;
4107
4108 pfx->teDepth = 0;
4109 if (!TranslateStatementList (pfx, ";", &chLimit)) {
4110 (void) DestroyRPN (pfx);
4111 pfx->expression = DestroyString (pfx->expression);
4112 pfx->pex = NULL;
4113 (void) DeInitFx (pfx);
4114 pfx = (FxInfo*) RelinquishMagickMemory(pfx);
4115 return NULL;
4116 }
4117
4118 if (pfx->teDepth) {
4119 (void) ThrowMagickException (
4120 pfx->exception, GetMagickModule(), OptionError,
4121 "Translate expression depth", "(%i) not 0",
4122 pfx->teDepth);
4123
4124 (void) DestroyRPN (pfx);
4125 pfx->expression = DestroyString (pfx->expression);
4126 pfx->pex = NULL;
4127 (void) DeInitFx (pfx);
4128 pfx = (FxInfo*) RelinquishMagickMemory(pfx);
4129 return NULL;
4130 }
4131
4132 if (chLimit != '\0' && chLimit != ';') {
4133 (void) ThrowMagickException (
4134 pfx->exception, GetMagickModule(), OptionError,
4135 "AcquireFxInfo: TranslateExpression did not exhaust input", "(chLimit=%i) at'%s'",
4136 (int)chLimit, pfx->pex);
4137
4138 (void) DestroyRPN (pfx);
4139 pfx->expression = DestroyString (pfx->expression);
4140 pfx->pex = NULL;
4141 (void) DeInitFx (pfx);
4142 pfx = (FxInfo*) RelinquishMagickMemory(pfx);
4143 return NULL;
4144 }
4145
4146 if (pfx->NeedStats && pfx->runType == rtEntireImage && !pfx->statistics) {
4147 if (!CollectStatistics (pfx)) {
4148 (void) DestroyRPN (pfx);
4149 pfx->expression = DestroyString (pfx->expression);
4150 pfx->pex = NULL;
4151 (void) DeInitFx (pfx);
4152 pfx = (FxInfo*) RelinquishMagickMemory(pfx);
4153 return NULL;
4154 }
4155 }
4156
4157 if (pfx->DebugOpt) {
4158 DumpTables (stderr);
4159 DumpUserSymbols (pfx, stderr);
4160 (void) DumpRPN (pfx, stderr);
4161 }
4162
4163 {
4164 size_t number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
4165 ssize_t t;
4166
4167 pfx->fxrts = (fxRtT *)AcquireQuantumMemory (number_threads, sizeof(fxRtT));
4168 if (!pfx->fxrts) {
4169 (void) ThrowMagickException (
4170 pfx->exception, GetMagickModule(), ResourceLimitFatalError,
4171 "fxrts", "%lu",
4172 (unsigned long) number_threads);
4173 (void) DestroyRPN (pfx);
4174 pfx->expression = DestroyString (pfx->expression);
4175 pfx->pex = NULL;
4176 (void) DeInitFx (pfx);
4177 pfx = (FxInfo*) RelinquishMagickMemory(pfx);
4178 return NULL;
4179 }
4180 for (t=0; t < (ssize_t) number_threads; t++) {
4181 if (!AllocFxRt (pfx, &pfx->fxrts[t])) {
4182 (void) ThrowMagickException (
4183 pfx->exception, GetMagickModule(), ResourceLimitFatalError,
4184 "AllocFxRt t=", "%g",
4185 (double) t);
4186 {
4187 ssize_t t2;
4188 for (t2 = t-1; t2 >= 0; t2--) {
4189 DestroyFxRt (&pfx->fxrts[t]);
4190 }
4191 }
4192 pfx->fxrts = (fxRtT *) RelinquishMagickMemory (pfx->fxrts);
4193 (void) DestroyRPN (pfx);
4194 pfx->expression = DestroyString (pfx->expression);
4195 pfx->pex = NULL;
4196 (void) DeInitFx (pfx);
4197 pfx = (FxInfo*) RelinquishMagickMemory(pfx);
4198 return NULL;
4199 }
4200 }
4201 }
4202 return pfx;
4203}
4204
4205FxInfo *AcquireFxInfo (const Image * images, const char * expression, ExceptionInfo *exception)
4206{
4207 return AcquireFxInfoPrivate (images, expression, MagickFalse, exception);
4208}
4209
4210FxInfo *DestroyFxInfo (FxInfo * pfx)
4211{
4212 ssize_t t;
4213
4214 assert (pfx != NULL);
4215 assert (pfx->image != NULL);
4216 assert (pfx->Images != NULL);
4217 assert (pfx->Imgs != NULL);
4218 assert (pfx->fxrts != NULL);
4219
4220 for (t=0; t < (ssize_t) GetMagickResourceLimit(ThreadResource); t++) {
4221 DestroyFxRt (&pfx->fxrts[t]);
4222 }
4223 pfx->fxrts = (fxRtT *) RelinquishMagickMemory (pfx->fxrts);
4224
4225 DestroyRPN (pfx);
4226
4227 pfx->expression = DestroyString (pfx->expression);
4228 pfx->pex = NULL;
4229
4230 (void) DeInitFx (pfx);
4231
4232 pfx = (FxInfo*) RelinquishMagickMemory(pfx);
4233
4234 return NULL;
4235}
4236
4237/* Following is substitute for FxImage().
4238*/
4239MagickExport Image *FxImage(const Image *image,const char *expression,
4240 ExceptionInfo *exception)
4241{
4242#define FxImageTag "FxNew/Image"
4243
4244 CacheView
4245 *fx_view,
4246 *image_view;
4247
4248 Image
4249 *fx_image;
4250
4251 MagickBooleanType
4252 status;
4253
4254 MagickOffsetType
4255 progress;
4256
4257 ssize_t
4258 y;
4259
4260 FxInfo
4261 *pfx;
4262
4263 assert(image != (Image *) NULL);
4264 assert(image->signature == MagickCoreSignature);
4265 if (IsEventLogging() != MagickFalse)
4266 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4267 if (expression == (const char *) NULL)
4268 return(CloneImage(image,0,0,MagickTrue,exception));
4269 fx_image=CloneImage(image,0,0,MagickTrue,exception);
4270 if (!fx_image) return NULL;
4271 if (SetImageStorageClass(fx_image,DirectClass,exception) == MagickFalse) {
4272 fx_image=DestroyImage(fx_image);
4273 return NULL;
4274 }
4275
4276 pfx = AcquireFxInfoPrivate (image, expression, MagickTrue, exception);
4277
4278 if (!pfx) {
4279 fx_image=DestroyImage(fx_image);
4280 return NULL;
4281 }
4282
4283 assert (pfx->image != NULL);
4284 assert (pfx->Images != NULL);
4285 assert (pfx->Imgs != NULL);
4286 assert (pfx->fxrts != NULL);
4287
4288 status=MagickTrue;
4289 progress=0;
4290 image_view = AcquireVirtualCacheView (image, pfx->exception);
4291 fx_view = AcquireAuthenticCacheView (fx_image, pfx->exception);
4292#if defined(MAGICKCORE_OPENMP_SUPPORT)
4293 #pragma omp parallel for schedule(dynamic) shared(progress,status) \
4294 magick_number_threads(image,fx_image,fx_image->rows, \
4295 pfx->ContainsDebug ? 0 : 1)
4296#endif
4297 for (y=0; y < (ssize_t) fx_image->rows; y++)
4298 {
4299 const int
4300 id = GetOpenMPThreadId();
4301
4302 const Quantum
4303 *magick_restrict p;
4304
4305 Quantum
4306 *magick_restrict q;
4307
4308 ssize_t
4309 x;
4310
4311 fxFltType
4312 result = 0.0;
4313
4314 if (status == MagickFalse)
4315 continue;
4316 p = GetCacheViewVirtualPixels (image_view, 0, y, image->columns, 1, pfx->exception);
4317 q = QueueCacheViewAuthenticPixels (fx_view, 0, y, fx_image->columns, 1, pfx->exception);
4318 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) {
4319 status=MagickFalse;
4320 continue;
4321 }
4322 for (x=0; x < (ssize_t) fx_image->columns; x++) {
4323 ssize_t i;
4324
4325 pfx->fxrts[id].thisPixel = (Quantum *)p;
4326
4327 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4328 {
4329 PixelChannel channel = GetPixelChannelChannel (image, i);
4330 PixelTrait traits = GetPixelChannelTraits (image, channel);
4331 PixelTrait fx_traits = GetPixelChannelTraits (fx_image, channel);
4332 if ((traits == UndefinedPixelTrait) ||
4333 (fx_traits == UndefinedPixelTrait))
4334 continue;
4335 if ((fx_traits & CopyPixelTrait) != 0) {
4336 SetPixelChannel (fx_image, channel, p[i], q);
4337 continue;
4338 }
4339
4340 if (!ExecuteRPN (pfx, &pfx->fxrts[id], &result, channel, x, y)) {
4341 status=MagickFalse;
4342 break;
4343 }
4344
4345 q[i] = ClampToQuantum ((MagickRealType) (QuantumRange*result));
4346 }
4347 p+=(ptrdiff_t) GetPixelChannels (image);
4348 q+=(ptrdiff_t) GetPixelChannels (fx_image);
4349 }
4350 if (SyncCacheViewAuthenticPixels(fx_view, pfx->exception) == MagickFalse)
4351 status=MagickFalse;
4352 if (image->progress_monitor != (MagickProgressMonitor) NULL)
4353 {
4354 MagickBooleanType
4355 proceed;
4356
4357#if defined(MAGICKCORE_OPENMP_SUPPORT)
4358 #pragma omp atomic
4359#endif
4360 progress++;
4361 proceed = SetImageProgress (image, FxImageTag, progress, image->rows);
4362 if (proceed == MagickFalse)
4363 status=MagickFalse;
4364 }
4365 }
4366
4367 fx_view = DestroyCacheView (fx_view);
4368 image_view = DestroyCacheView (image_view);
4369
4370 /* Before destroying the user symbol values, dump them to stderr.
4371 */
4372 if (pfx->DebugOpt && pfx->usedUserSymbols) {
4373 int t, i;
4374 char UserSym[MagickPathExtent];
4375 fprintf (stderr, "User symbols (%i):\n", pfx->usedUserSymbols);
4376 for (t=0; t < (int) GetMagickResourceLimit(ThreadResource); t++) {
4377 for (i = 0; i < (int) pfx->usedUserSymbols; i++) {
4378 fprintf (stderr, "th=%i us=%i '%s': %.*Lg\n",
4379 t, i, NameOfUserSym (pfx, i, UserSym), pfx->precision, pfx->fxrts[t].UserSymVals[i]);
4380 }
4381 }
4382 }
4383
4384 if ((status == MagickFalse) || (pfx->exception->severity >= ErrorException))
4385 fx_image=DestroyImage(fx_image);
4386
4387 pfx=DestroyFxInfo(pfx);
4388
4389 return(fx_image);
4390}
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