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