Coverage Report

Created: 2026-07-20 07:19

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