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