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