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