Coverage Report

Created: 2026-06-30 07:12

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.4k
#define MaxTokenLen 100
105
5.99k
#define RpnInit 100
106
16
#define TableExtend 0.1
107
5.99k
#define InitNumOprStack 50
108
8.42k
#define MinValStackSize 100
109
5.99k
#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
780
#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
723k
#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
161k
#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.7k
#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
26.8k
#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
132k
#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
163k
#define NO_CHAN_QUAL      ((PixelChannel) (-1))
618
48.3k
#define THIS_CHANNEL      ((PixelChannel) (-2))
619
79.6k
#define HUE_CHANNEL       ((PixelChannel) (-3))
620
73.2k
#define SAT_CHANNEL       ((PixelChannel) (-4))
621
35.1k
#define LIGHT_CHANNEL     ((PixelChannel) (-5))
622
34.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
132
{
777
132
  if (pc==HUE_CHANNEL || pc==SAT_CHANNEL || pc==LIGHT_CHANNEL || pc==INTENSITY_CHANNEL)
778
17
    return MagickTrue;
779
780
115
  return MagickFalse;
781
132
}
782
783
static MagickBooleanType InitFx (FxInfo * pfx, const Image * img,
784
  MagickBooleanType CalcAllStats, ExceptionInfo *exception)
785
5.99k
{
786
5.99k
  ssize_t i=0;
787
5.99k
  const Image * next;
788
789
5.99k
  pfx->ImgListLen = GetImageListLength (img);
790
5.99k
  pfx->ImgNum = GetImageIndexInList (img);
791
5.99k
  pfx->image = (Image *)img;
792
793
5.99k
  pfx->NeedStats = MagickFalse;
794
5.99k
  pfx->GotStats = MagickFalse;
795
5.99k
  pfx->NeedHsl = MagickFalse;
796
5.99k
  pfx->DebugOpt = IsStringTrue (GetImageArtifact (img, "fx:debug"));
797
5.99k
  pfx->statistics = NULL;
798
5.99k
  pfx->Imgs = NULL;
799
5.99k
  pfx->Images = NULL;
800
5.99k
  pfx->exception = exception;
801
5.99k
  pfx->precision = GetMagickPrecision ();
802
5.99k
  pfx->random_infos = AcquireRandomInfoTLS ();
803
5.99k
  pfx->ContainsDebug = MagickFalse;
804
5.99k
  pfx->runType = (CalcAllStats) ? rtEntireImage : rtCornerOnly;
805
5.99k
  pfx->Imgs = (ImgT *)AcquireQuantumMemory (pfx->ImgListLen, sizeof (ImgT));
806
5.99k
  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.99k
  next = GetFirstImageInList (img);
815
11.9k
  for ( ; next != (Image *) NULL; next=next->next)
816
5.99k
  {
817
5.99k
    ImgT * pimg = &pfx->Imgs[i];
818
5.99k
    pimg->View = AcquireVirtualCacheView (next, pfx->exception);
819
5.99k
    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.99k
    i++;
833
5.99k
  }
834
835
5.99k
  pfx->Images = ImageListToArray (img, pfx->exception);
836
837
5.99k
  return MagickTrue;
838
5.99k
}
839
840
static MagickBooleanType DeInitFx (FxInfo * pfx)
841
5.99k
{
842
5.99k
  ssize_t i;
843
844
5.99k
  if (pfx->Images) pfx->Images = (Image**) RelinquishMagickMemory (pfx->Images);
845
846
5.99k
  if (pfx->Imgs) {
847
11.9k
    for (i = (ssize_t)GetImageListLength(pfx->image); i > 0; i--) {
848
5.99k
      ImgT * pimg = &pfx->Imgs[i-1];
849
5.99k
      pimg->View = DestroyCacheView (pimg->View);
850
5.99k
    }
851
5.99k
    pfx->Imgs=(ImgT *) RelinquishMagickMemory (pfx->Imgs);
852
5.99k
  }
853
5.99k
  pfx->random_infos = DestroyRandomInfoTLS (pfx->random_infos);
854
855
5.99k
  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.99k
  return MagickTrue;
864
5.99k
}
865
866
static ElementTypeE TypeOfOpr (int op)
867
34.0k
{
868
34.0k
  if (op <  oNull) return etOperator;
869
24.2k
  if (op == oNull) return etConstant;
870
12.3k
  if (op <= fNull) return etFunction;
871
7.22k
  if (op <= aNull) return etImgAttr;
872
5.55k
  if (op <= sNull) return etSymbol;
873
3.55k
  if (op <= rNull) return etControl;
874
875
0
  return (ElementTypeE) 0;
876
3.55k
}
877
878
static char * SetPtrShortExp (FxInfo *pfx, char *pExp, size_t len)
879
4.17k
{
880
4.38k
  #define MaxLen 20
881
882
4.17k
  size_t slen;
883
4.17k
  char *p;
884
885
4.17k
  *pfx->ShortExp = '\0';
886
887
4.17k
  if (pExp && len) {
888
4.17k
    slen = CopyMagickString(pfx->ShortExp, pExp, MagickPathExtent);
889
890
4.17k
    if (slen > MaxLen) {
891
208
      (void) CopyMagickString(pfx->ShortExp + MaxLen, "...", 4);
892
208
    }
893
894
4.17k
    p = strchr(pfx->ShortExp, '\n');
895
4.17k
    if (p) (void) CopyMagickString(p, "...", 4);
896
897
4.17k
    p = strchr(pfx->ShortExp, '\r');
898
4.17k
    if (p) (void) CopyMagickString(p, "...", 4);
899
4.17k
  }
900
901
4.17k
  return pfx->ShortExp;
902
4.17k
}
903
904
static char * SetShortExp (FxInfo * pfx)
905
4.15k
{
906
4.15k
  return SetPtrShortExp (pfx, pfx->pex, MaxTokenLen-1);
907
4.15k
}
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.36k
{
914
1.36k
  int i;
915
1.36k
  size_t lenName;
916
1.36k
  lenName = strlen (name);
917
3.03k
  for (i=0; i < pfx->usedUserSymbols; i++) {
918
2.24k
    UserSymbolT *pus = &pfx->UserSymbols[i];
919
2.24k
    if (lenName == pus->len && LocaleNCompare (name, pus->pex, lenName)==0) break;
920
2.24k
  }
921
1.36k
  if (i == pfx->usedUserSymbols) return NULL_ADDRESS;
922
567
  return i;
923
1.36k
}
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
793
{
942
793
  UserSymbolT *pus;
943
793
  if (++pfx->usedUserSymbols >= pfx->numUserSymbols) {
944
0
    if (!ExtendUserSymbols (pfx)) return -1;
945
0
  }
946
793
  pus = &pfx->UserSymbols[pfx->usedUserSymbols-1];
947
793
  pus->pex = pex;
948
793
  pus->len = len;
949
950
793
  return pfx->usedUserSymbols-1;
951
793
}
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.99k
{
995
5.99k
  pfx->numUserSymbols = InitNumUserSymbols;
996
5.99k
  pfx->usedUserSymbols = 0;
997
5.99k
  pfx->UserSymbols = (UserSymbolT*) AcquireMagickMemory ((size_t) pfx->numUserSymbols * sizeof(UserSymbolT));
998
5.99k
  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.99k
  pfx->numElements = RpnInit;
1007
5.99k
  pfx->usedElements = 0;
1008
5.99k
  pfx->Elements = NULL;
1009
1010
5.99k
  pfx->Elements = (ElementT*) AcquireMagickMemory ((size_t) pfx->numElements * sizeof(ElementT));
1011
1012
5.99k
  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.99k
  pfx->usedOprStack = 0;
1021
5.99k
  pfx->maxUsedOprStack = 0;
1022
5.99k
  pfx->numOprStack = InitNumOprStack;
1023
5.99k
  pfx->OperatorStack = (OperatorE*) AcquireMagickMemory ((size_t) pfx->numOprStack * sizeof(OperatorE));
1024
5.99k
  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.99k
  return MagickTrue;
1033
5.99k
}
1034
1035
static MagickBooleanType AllocFxRt (FxInfo * pfx, fxRtT * pfxrt)
1036
4.21k
{
1037
4.21k
  int nRnd;
1038
4.21k
  int i;
1039
4.21k
  pfxrt->random_info = AcquireRandomInfo ();
1040
4.21k
  pfxrt->thisPixel = NULL;
1041
1042
4.21k
  nRnd = 20 + 10 * (int) GetPseudoRandomValue (pfxrt->random_info);
1043
88.5k
  for (i=0; i < nRnd; i++) (void) GetPseudoRandomValue (pfxrt->random_info);;
1044
1045
4.21k
  pfxrt->usedValStack = 0;
1046
4.21k
  pfxrt->numValStack = 2 * pfx->maxUsedOprStack;
1047
4.21k
  if (pfxrt->numValStack < MinValStackSize) pfxrt->numValStack = MinValStackSize;
1048
4.21k
  pfxrt->ValStack = (fxFltType*) AcquireMagickMemory ((size_t) pfxrt->numValStack * sizeof(fxFltType));
1049
4.21k
  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.21k
  pfxrt->UserSymVals = NULL;
1058
1059
4.21k
  if (pfx->usedUserSymbols) {
1060
163
    pfxrt->UserSymVals = (fxFltType*) AcquireMagickMemory ((size_t) pfx->usedUserSymbols * sizeof(fxFltType));
1061
163
    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
461
    for (i = 0; i < pfx->usedUserSymbols; i++) pfxrt->UserSymVals[i] = (fxFltType) 0;
1069
163
  }
1070
1071
4.21k
  return MagickTrue;
1072
4.21k
}
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
25.1k
{
1090
25.1k
  return (op >= oAddEq && op <= oSubSub ? MagickTrue : MagickFalse);
1091
25.1k
}
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.99k
{
1183
5.99k
  pfx->numOprStack = 0;
1184
5.99k
  pfx->usedOprStack = 0;
1185
5.99k
  if (pfx->OperatorStack) pfx->OperatorStack = (OperatorE*) RelinquishMagickMemory (pfx->OperatorStack);
1186
1187
5.99k
  pfx->numElements = 0;
1188
5.99k
  pfx->usedElements = 0;
1189
5.99k
  if (pfx->Elements) pfx->Elements = (ElementT*) RelinquishMagickMemory (pfx->Elements);
1190
1191
5.99k
  pfx->usedUserSymbols = 0;
1192
5.99k
  if (pfx->UserSymbols) pfx->UserSymbols = (UserSymbolT*) RelinquishMagickMemory (pfx->UserSymbols);
1193
5.99k
}
1194
1195
static void DestroyFxRt (fxRtT * pfxrt)
1196
4.21k
{
1197
4.21k
  pfxrt->usedValStack = 0;
1198
4.21k
  if (pfxrt->ValStack) pfxrt->ValStack = (fxFltType*) RelinquishMagickMemory (pfxrt->ValStack);
1199
4.21k
  if (pfxrt->UserSymVals) pfxrt->UserSymVals = (fxFltType*) RelinquishMagickMemory (pfxrt->UserSymVals);
1200
1201
4.21k
  pfxrt->random_info = DestroyRandomInfo (pfxrt->random_info);
1202
4.21k
}
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.6k
{
1214
1215
29.6k
  char * p = pfx->pex;
1216
29.6k
  size_t len = 0;
1217
29.6k
  *pfx->token = '\0';
1218
29.6k
  pfx->lenToken = 0;
1219
29.6k
  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
14.9k
  if (LocaleNCompare (p, "icc-", 4) == 0) {
1227
168
    len = 4;
1228
168
    p += 4;
1229
318
    while (isalpha ((int)*p)) { len++; p++; }
1230
14.7k
  } 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.7k
  } else {
1235
27.2k
    while (isalpha ((int)*p)) { len++; p++; }
1236
14.7k
    if (*p == '_')            { len++; p++; }
1237
14.7k
    while (isalpha ((int)*p)) { len++; p++; }
1238
14.7k
    while (isdigit ((int)*p)) { len++; p++; }
1239
14.7k
  }
1240
14.9k
  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
14.9k
  if (len) {
1248
14.9k
    (void) CopyMagickString (pfx->token, pfx->pex, (len+1<MaxTokenLen)?len+1:MaxTokenLen);
1249
14.9k
  }
1250
1251
14.9k
  pfx->lenToken = strlen (pfx->token);
1252
14.9k
  return len;
1253
29.6k
}
1254
1255
static MagickBooleanType TokenMaybeUserSymbol (FxInfo * pfx)
1256
1.48k
{
1257
1.48k
  char * p = pfx->token;
1258
1.48k
  int i = 0;
1259
4.73k
  while (*p) {
1260
3.30k
    if (!isalpha ((int)*p++)) return MagickFalse;
1261
3.24k
    i++;
1262
3.24k
  }
1263
1.42k
  if (i < 2) return MagickFalse;
1264
1.36k
  return MagickTrue;
1265
1.42k
}
1266
1267
static MagickBooleanType AddElement (FxInfo * pfx, fxFltType val, int oprNum)
1268
34.0k
{
1269
34.0k
  ElementT * pel;
1270
1271
34.0k
  assert (oprNum <= rNull);
1272
1273
34.0k
  if (++pfx->usedElements >= pfx->numElements) {
1274
3
    if (!ExtendRPN (pfx)) return MagickFalse;
1275
3
  }
1276
1277
34.0k
  pel = &pfx->Elements[pfx->usedElements-1];
1278
34.0k
  pel->type = TypeOfOpr (oprNum);
1279
34.0k
  pel->val = val;
1280
34.0k
  pel->val1 = (fxFltType) 0;
1281
34.0k
  pel->val2 = (fxFltType) 0;
1282
34.0k
  pel->operator_index = oprNum;
1283
34.0k
  pel->do_push = MagickTrue;
1284
34.0k
  pel->element_index = 0;
1285
34.0k
  pel->channel_qual = NO_CHAN_QUAL;
1286
34.0k
  pel->img_attr_qual = aNull;
1287
34.0k
  pel->number_dest = 0;
1288
34.0k
  pel->exp_start = NULL;
1289
34.0k
  pel->exp_len = 0;
1290
1291
34.0k
  if (oprNum <= oNull) pel->number_args = Operators[oprNum].number_args;
1292
12.3k
  else if (oprNum <= fNull) pel->number_args = Functions[oprNum-(int) FirstFunc].number_args;
1293
7.22k
  else if (oprNum <= aNull) pel->number_args = 0;
1294
5.55k
  else if (oprNum <= sNull) pel->number_args = 0;
1295
3.55k
  else                      pel->number_args = Controls[oprNum-(int) FirstCont].number_args;
1296
1297
34.0k
  return MagickTrue;
1298
34.0k
}
1299
1300
static MagickBooleanType AddAddressingElement (FxInfo * pfx, int oprNum, int EleNdx)
1301
3.55k
{
1302
3.55k
  ElementT * pel;
1303
3.55k
  if (!AddElement (pfx, (fxFltType) 0, oprNum)) return MagickFalse;
1304
3.55k
  pel = &pfx->Elements[pfx->usedElements-1];
1305
3.55k
  pel->element_index = EleNdx;
1306
3.55k
  if (oprNum == rGoto || oprNum == rGotoChk || oprNum == rIfZeroGoto || oprNum == rIfNotZeroGoto
1307
1.27k
   || oprNum == rZerStk)
1308
2.86k
  {
1309
2.86k
    pel->do_push = MagickFalse;
1310
2.86k
  }
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.55k
  return MagickTrue;
1317
3.55k
}
1318
1319
static MagickBooleanType AddColourElement (FxInfo * pfx, fxFltType val0, fxFltType val1, fxFltType val2)
1320
967
{
1321
967
  ElementT * pel;
1322
967
  if (!AddElement (pfx, val0, oNull)) return MagickFalse;
1323
967
  pel = &pfx->Elements[pfx->usedElements-1];
1324
967
  pel->val1 = val1;
1325
967
  pel->val2 = val2;
1326
967
  pel->type = etColourConstant;
1327
967
  return MagickTrue;
1328
967
}
1329
1330
static inline void SkipSpaces (FxInfo * pfx)
1331
113k
{
1332
113k
  while (isspace ((int)*pfx->pex)) pfx->pex++;
1333
113k
}
1334
1335
static inline char PeekChar (FxInfo * pfx)
1336
15.6k
{
1337
15.6k
  SkipSpaces (pfx);
1338
15.6k
  return *pfx->pex;
1339
15.6k
}
1340
1341
static inline MagickBooleanType PeekStr (FxInfo * pfx, const char * str)
1342
376
{
1343
376
  SkipSpaces (pfx);
1344
1345
376
  return (LocaleNCompare (pfx->pex, str, strlen(str))==0 ? MagickTrue : MagickFalse);
1346
376
}
1347
1348
static MagickBooleanType ExpectChar (FxInfo * pfx, char c)
1349
3.84k
{
1350
3.84k
  if (PeekChar (pfx) != c) {
1351
92
    (void) ThrowMagickException (
1352
92
      pfx->exception, GetMagickModule(), OptionError,
1353
92
      "Expected char", "'%c' at '%s'", c, SetShortExp (pfx));
1354
92
    return MagickFalse;
1355
92
  }
1356
3.75k
  pfx->pex++;
1357
3.75k
  return MagickTrue;
1358
3.84k
}
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.05k
{
1365
2.05k
  int ret=0;
1366
1367
2.05k
  if (*pop != aPage && *pop != aPrintsize && *pop != aRes) return 0;
1368
1369
216
  if (PeekChar (pfx) != '.') return 0;
1370
1371
179
  if (!ExpectChar (pfx, '.')) return 0;
1372
1373
179
  (void) GetToken (pfx);
1374
179
  if (LocaleCompare ("x", pfx->token)==0) ret=1;
1375
119
  else if (LocaleCompare ("y", pfx->token)==0) ret=2;
1376
54
  else if (LocaleCompare ("width", pfx->token)==0) ret=3;
1377
54
  else if (LocaleCompare ("height", pfx->token)==0) ret=4;
1378
1379
179
  if (!ret)
1380
54
    (void) ThrowMagickException (
1381
54
      pfx->exception, GetMagickModule(), OptionError,
1382
54
      "Invalid 'x' or 'y' or 'width' or 'height' token=", "'%s' at '%s'",
1383
54
      pfx->token, SetShortExp(pfx));
1384
1385
179
  if (*pop == aPage) (*pop) = (ImgAttrE) ((int) *pop + ret);
1386
58
  else {
1387
58
    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
58
    } else {
1393
58
      (*pop) = (ImgAttrE) ((int) *pop + ret);
1394
58
    }
1395
58
  }
1396
179
  pfx->pex+=pfx->lenToken;
1397
1398
179
  return ret;
1399
179
}
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
19.3k
{
1417
19.3k
  if (++pfx->usedOprStack >= pfx->numOprStack) {
1418
13
    if (!ExtendOperatorStack (pfx))
1419
0
      return MagickFalse;
1420
13
  }
1421
19.3k
  pfx->OperatorStack[pfx->usedOprStack-1] = (OperatorE) op;
1422
1423
19.3k
  if (pfx->maxUsedOprStack < pfx->usedOprStack)
1424
12.3k
    pfx->maxUsedOprStack = pfx->usedOprStack;
1425
19.3k
  return MagickTrue;
1426
19.3k
}
1427
1428
static OperatorE GetLeadingOp (FxInfo * pfx)
1429
11.3k
{
1430
11.3k
  OperatorE op = oNull;
1431
1432
11.3k
  if      (*pfx->pex == '-') op = oUnaryMinus;
1433
9.86k
  else if (*pfx->pex == '+') op = oUnaryPlus;
1434
8.96k
  else if (*pfx->pex == '~') op = oBitNot;
1435
7.99k
  else if (*pfx->pex == '!') op = oLogNot;
1436
7.30k
  else if (*pfx->pex == '(') op = oOpenParen;
1437
1438
11.3k
  return op;
1439
11.3k
}
1440
1441
static inline MagickBooleanType OprIsUnaryPrefix (OperatorE op)
1442
10.7k
{
1443
10.7k
  return (op == oUnaryMinus || op == oUnaryPlus || op == oBitNot || op == oLogNot ? MagickTrue : MagickFalse);
1444
10.7k
}
1445
1446
static MagickBooleanType TopOprIsUnaryPrefix (FxInfo * pfx)
1447
600
{
1448
600
  if (!pfx->usedOprStack) return MagickFalse;
1449
1450
590
  return OprIsUnaryPrefix (pfx->OperatorStack[pfx->usedOprStack-1]);
1451
600
}
1452
1453
static MagickBooleanType PopOprOpenParen (FxInfo * pfx, OperatorE op)
1454
5.87k
{
1455
1456
5.87k
  if (!pfx->usedOprStack) return MagickFalse;
1457
1458
5.87k
  if (pfx->OperatorStack[pfx->usedOprStack-1] != op) return MagickFalse;
1459
1460
5.86k
  pfx->usedOprStack--;
1461
1462
5.86k
  return MagickTrue;
1463
5.87k
}
1464
1465
static int GetCoordQualifier (FxInfo * pfx, int op)
1466
/* Returns -1 if invalid CoordQualifier, +1 if valid and appropriate.
1467
*/
1468
994
{
1469
994
  if (op != fU && op != fV && op != fS) return -1;
1470
1471
994
  (void) GetToken (pfx);
1472
1473
994
  if (pfx->lenToken != 1) {
1474
294
    return -1;
1475
294
  }
1476
700
  if (*pfx->token != 'p' && *pfx->token != 'P') return -1;
1477
436
  if (!GetFunction (pfx, fP)) return -1;
1478
1479
379
  return 1;
1480
436
}
1481
1482
static PixelChannel GetChannelQualifier (FxInfo * pfx, int op)
1483
1.40k
{
1484
1.40k
  if (op == fU || op == fV || op == fP ||
1485
670
      op == fUP || op == fVP ||
1486
387
      op == fS || (op >= (int) FirstImgAttr && op <= aNull)
1487
1.40k
     )
1488
1.37k
  {
1489
1.37k
    const ChannelT * pch = &Channels[0];
1490
1.37k
    (void) GetToken (pfx);
1491
1492
17.6k
    while (*pch->str) {
1493
16.8k
      if (LocaleCompare (pch->str, pfx->token)==0) {
1494
1495
597
        if (op >= (int) FirstImgAttr && op <= (int) ((OperatorE)aNull) &&
1496
94
              ChanIsVirtual (pch->pixel_channel)
1497
597
           )
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
583
        pfx->pex += pfx->lenToken;
1507
583
        return pch->pixel_channel;
1508
597
      }
1509
16.2k
      pch++;
1510
16.2k
    }
1511
1.37k
  }
1512
810
  return NO_CHAN_QUAL;
1513
1.40k
}
1514
1515
static ImgAttrE GetImgAttrToken (FxInfo * pfx)
1516
6.27k
{
1517
6.27k
  ImgAttrE ia = aNull;
1518
6.27k
  const char * iaStr;
1519
153k
  for (ia = FirstImgAttr; ia < aNull; ia=(ImgAttrE) (ia+1)) {
1520
149k
    iaStr = ImgAttrs[ia-(int) FirstImgAttr].str;
1521
149k
    if (LocaleCompare (iaStr, pfx->token)==0) {
1522
2.05k
      pfx->pex += strlen(pfx->token);
1523
2.05k
      if (ImgAttrs[ia-(int) FirstImgAttr].need_stats != MagickFalse) pfx->NeedStats = MagickTrue;
1524
2.05k
      MaybeXYWH (pfx, &ia);
1525
2.05k
      break;
1526
2.05k
    }
1527
149k
  }
1528
1529
6.27k
  if (ia == aPage || ia == aPrintsize || ia == aRes) {
1530
91
    (void) ThrowMagickException (
1531
91
      pfx->exception, GetMagickModule(), OptionError,
1532
91
      "Attribute", "'%s' needs qualifier at '%s'",
1533
91
      iaStr, SetShortExp(pfx));
1534
91
  }
1535
1536
6.27k
  return ia;
1537
6.27k
}
1538
1539
static ImgAttrE GetImgAttrQualifier (FxInfo * pfx, int op)
1540
450
{
1541
450
  ImgAttrE ia = aNull;
1542
450
  if (op == (OperatorE)fU || op == (OperatorE)fV || op == (OperatorE)fP || op == (OperatorE)fS) {
1543
450
    (void) GetToken (pfx);
1544
450
    if (pfx->lenToken == 0) {
1545
56
      return aNull;
1546
56
    }
1547
394
    ia = GetImgAttrToken (pfx);
1548
394
  }
1549
394
  return ia;
1550
450
}
1551
1552
static MagickBooleanType IsQualifier (FxInfo * pfx)
1553
7.34k
{
1554
7.34k
  if (PeekChar (pfx) == '.') {
1555
1.53k
    pfx->pex++;
1556
1.53k
    return MagickTrue;
1557
1.53k
  }
1558
5.81k
  return MagickFalse;
1559
7.34k
}
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
376
{
1591
376
  if (seconds != NULL) *seconds = SECONDS_ERR;
1592
1593
376
  if (PeekStr (pfx, "%[")) {
1594
211
    int level = 0;
1595
211
    size_t len;
1596
211
    char sProperty [MagickPathExtent];
1597
211
    char * p = pfx->pex + 2;
1598
1599
2.03k
    while (*p) {
1600
1601
2.02k
      if (*p == '[') level++;
1602
1.77k
      else if (*p == ']') {
1603
444
        if (level == 0) break;
1604
244
        level--;
1605
244
      }
1606
1.82k
      p++;
1607
1.82k
    }
1608
211
    if (!*p || level != 0) {
1609
11
      (void) ThrowMagickException (
1610
11
        pfx->exception, GetMagickModule(), OptionError,
1611
11
        "After '%[' expected ']' at", "'%s'",
1612
11
        SetShortExp(pfx));
1613
11
      return -1;
1614
11
    }
1615
1616
200
    len = (size_t) (p - pfx->pex + 1);
1617
200
    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
200
    (void) CopyMagickString (sProperty, pfx->pex, len+1);
1626
200
    sProperty[len] = '\0';
1627
200
    {
1628
200
      char * tailptr;
1629
200
      char * text;
1630
200
      text = InterpretImageProperties (pfx->image->image_info, pfx->image,
1631
200
         sProperty, pfx->exception);
1632
200
      if (!text || !*text) {
1633
76
        text = DestroyString(text);
1634
76
        (void) ThrowMagickException (
1635
76
          pfx->exception, GetMagickModule(), OptionError,
1636
76
          "Unknown property", "'%s' at '%s'",
1637
76
          sProperty, SetShortExp(pfx));
1638
76
        return -1;
1639
76
      }
1640
1641
124
      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
124
      } else {
1655
124
        *val = strtold (text, &tailptr);
1656
124
        if (text == tailptr) {
1657
15
          text = DestroyString(text);
1658
15
          (void) ThrowMagickException (
1659
15
            pfx->exception, GetMagickModule(), OptionError,
1660
15
            "Property", "'%s' text '%s' is not a number at '%s'",
1661
15
            sProperty, text, SetShortExp(pfx));
1662
15
          text = DestroyString(text);
1663
15
          return -1;
1664
15
        }
1665
124
      }
1666
109
      text = DestroyString(text);
1667
109
    }
1668
0
    return ((ssize_t) len);
1669
124
  }
1670
1671
165
  return 0;
1672
376
}
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.21k
{
1681
2.21k
  PixelInfo
1682
2.21k
    colour;
1683
1684
2.21k
  ExceptionInfo
1685
2.21k
    *dummy_exception = AcquireExceptionInfo ();
1686
1687
2.21k
  char
1688
2.21k
    *p;
1689
1690
2.21k
  MagickBooleanType
1691
2.21k
    IsGray,
1692
2.21k
    IsIcc,
1693
2.21k
    IsDev;
1694
1695
2.21k
  char ColSp[MagickPathExtent];
1696
2.21k
  (void) CopyMagickString (ColSp, pfx->token, MaxTokenLen);
1697
2.21k
  p = ColSp + pfx->lenToken - 1;
1698
2.21k
  if (*p == 'a' || *p == 'A') *p = '\0';
1699
1700
2.21k
  (void) GetPixelInfo (pfx->image, &colour);
1701
1702
  /* "gray" is both a colorspace and a named colour. */
1703
1704
2.21k
  IsGray = (LocaleCompare (ColSp, "gray") == 0) ? MagickTrue : MagickFalse;
1705
2.21k
  IsIcc = (LocaleCompare (ColSp, "icc-color") == 0) ? MagickTrue : MagickFalse;
1706
2.21k
  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.21k
  if (!QueryColorCompliance (pfx->token, AllCompliance, &colour, dummy_exception) || IsGray) {
1711
2.16k
    ssize_t type = ParseCommandOption (MagickColorspaceOptions, MagickFalse, ColSp);
1712
2.16k
    if (type >= 0 || IsIcc || IsDev) {
1713
783
      char * q = pfx->pex + pfx->lenToken;
1714
783
      while (isspace((int) ((unsigned char) *q))) q++;
1715
783
      if (*q == '(') {
1716
752
        size_t lenfun;
1717
752
        char sFunc[MagickPathExtent];
1718
4.22k
        while (*q && *q != ')') q++;
1719
752
        if (!*q) {
1720
17
          (void) ThrowMagickException (
1721
17
            pfx->exception, GetMagickModule(), OptionError,
1722
17
            "constant color missing ')'", "at '%s'",
1723
17
            SetShortExp(pfx));
1724
17
          dummy_exception = DestroyExceptionInfo (dummy_exception);
1725
17
          return -1;
1726
17
        }
1727
735
        lenfun = (size_t) (q - pfx->pex + 1);
1728
735
        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
735
        (void) CopyMagickString (sFunc, pfx->pex, lenfun+1);
1737
735
        if (QueryColorCompliance (sFunc, AllCompliance, &colour, dummy_exception)) {
1738
622
          *v0 = QuantumScale*colour.red;
1739
622
          *v1 = QuantumScale*colour.green;
1740
622
          *v2 = QuantumScale*colour.blue;
1741
622
          dummy_exception = DestroyExceptionInfo (dummy_exception);
1742
622
          return (ssize_t)lenfun;
1743
622
        }
1744
735
      } else {
1745
31
        (void) ThrowMagickException (
1746
31
          pfx->exception, GetMagickModule(), OptionError,
1747
31
          "colorspace but not a valid color with '(...)' at", "'%s'",
1748
31
          SetShortExp(pfx));
1749
31
        dummy_exception = DestroyExceptionInfo (dummy_exception);
1750
31
        return -1;
1751
31
      }
1752
783
    }
1753
1.49k
    if (!IsGray) {
1754
1.48k
      dummy_exception = DestroyExceptionInfo (dummy_exception);
1755
1.48k
      return 0;
1756
1.48k
    }
1757
1.49k
  }
1758
1759
58
  *v0 = QuantumScale*colour.red;
1760
58
  *v1 = QuantumScale*colour.green;
1761
58
  *v2 = QuantumScale*colour.blue;
1762
1763
58
  dummy_exception = DestroyExceptionInfo (dummy_exception);
1764
58
  return (ssize_t)strlen (pfx->token);
1765
2.21k
}
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
357
{
1772
357
  char * p;
1773
357
  size_t len;
1774
357
  PixelInfo colour;
1775
1776
357
  if (*pfx->pex != '#') return 0;
1777
1778
  /* find end of hex digits. */
1779
357
  p = pfx->pex + 1;
1780
5.31k
  while (isxdigit ((int)*p)) p++;
1781
357
  if (isalpha ((int)*p)) {
1782
19
    (void) ThrowMagickException (
1783
19
      pfx->exception, GetMagickModule(), OptionError,
1784
19
      "Bad hex number at", "'%s'",
1785
19
      SetShortExp(pfx));
1786
19
    return -1;
1787
19
  }
1788
1789
338
  len = (size_t) (p - pfx->pex);
1790
338
  if (len < 1) return 0;
1791
338
  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
338
  (void) CopyMagickString (pfx->token, pfx->pex, len+1);
1799
1800
338
  (void) GetPixelInfo (pfx->image, &colour);
1801
1802
338
  if (!QueryColorCompliance (pfx->token, AllCompliance, &colour, pfx->exception)) {
1803
51
    (void) ThrowMagickException (
1804
51
      pfx->exception, GetMagickModule(), OptionError,
1805
51
      "QueryColorCompliance rejected", "'%s' at '%s'",
1806
51
      pfx->token, SetShortExp(pfx));
1807
51
    return -1;
1808
51
  }
1809
1810
287
  *v0 = QuantumScale*colour.red;
1811
287
  *v1 = QuantumScale*colour.green;
1812
287
  *v2 = QuantumScale*colour.blue;
1813
1814
287
  return (ssize_t) len;
1815
338
}
1816
1817
static MagickBooleanType GetFunction (FxInfo * pfx, FunctionE fe)
1818
6.39k
{
1819
  /* A function, so get open-parens, n args, close-parens
1820
  */
1821
6.39k
  const char * funStr = Functions[fe-(int) FirstFunc].str;
1822
6.39k
  int nArgs = Functions[fe-(int) FirstFunc].number_args;
1823
6.39k
  char chLimit = ')';
1824
6.39k
  char expChLimit = ')';
1825
6.39k
  const char *strLimit = ",)";
1826
6.39k
  OperatorE pushOp = oOpenParen;
1827
1828
6.39k
  char * pExpStart;
1829
1830
6.39k
  size_t lenExp = 0;
1831
1832
6.39k
  int FndArgs = 0;
1833
6.39k
  int ndx0 = NULL_ADDRESS, ndx1 = NULL_ADDRESS, ndx2 = NULL_ADDRESS, ndx3 = NULL_ADDRESS;
1834
1835
6.39k
  MagickBooleanType coordQual = MagickFalse;
1836
6.39k
  PixelChannel chQual = NO_CHAN_QUAL;
1837
6.39k
  ImgAttrE iaQual = aNull;
1838
1839
6.39k
  pfx->pex += pfx->lenToken;
1840
1841
6.39k
  if (fe == fP) {
1842
2.77k
    char p = PeekChar (pfx);
1843
2.77k
    if (p=='{') {
1844
1.38k
      (void) ExpectChar (pfx, '{');
1845
1.38k
      pushOp = oOpenBrace;
1846
1.38k
      strLimit = ",}";
1847
1.38k
      chLimit = '}';
1848
1.38k
      expChLimit = '}';
1849
1.38k
    } else if (p=='[') {
1850
83
      (void) ExpectChar (pfx, '[');
1851
83
      pushOp = oOpenBracket;
1852
83
      strLimit = ",]";
1853
83
      chLimit = ']';
1854
83
      expChLimit = ']';
1855
1.30k
    } else {
1856
1.30k
      nArgs = 0;
1857
1.30k
      chLimit = ']';
1858
1.30k
      expChLimit = ']';
1859
1.30k
    }
1860
3.62k
  } else if (fe == fU) {
1861
1.46k
    char p = PeekChar (pfx);
1862
1.46k
    if (p=='[') {
1863
497
      (void) ExpectChar (pfx, '[');
1864
497
      pushOp = oOpenBracket;
1865
497
      strLimit = ",]";
1866
497
      chLimit = ']';
1867
497
      expChLimit = ']';
1868
972
    } else {
1869
972
      nArgs = 0;
1870
972
      chLimit = ']';
1871
972
      expChLimit = ']';
1872
972
    }
1873
2.15k
  } else if (fe == fV || fe == fS) {
1874
516
      nArgs = 0;
1875
516
      pushOp = oOpenBracket;
1876
516
      chLimit = ']';
1877
516
      expChLimit = ']';
1878
1.63k
  } else {
1879
1.63k
    if (!ExpectChar (pfx, '(')) return MagickFalse;
1880
1.63k
  }
1881
6.32k
  if (!PushOperatorStack (pfx, (int) pushOp)) return MagickFalse;
1882
1883
6.32k
  pExpStart = pfx->pex;
1884
6.32k
  ndx0 = pfx->usedElements;
1885
6.32k
  if (fe==fDo) {
1886
619
    (void) AddAddressingElement (pfx, rGoto, NULL_ADDRESS); /* address will be ndx1+1 */
1887
619
  }
1888
6.32k
  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.25k
  while (nArgs > 0) {
1913
5.36k
    int FndOne = 0;
1914
5.36k
    if (TranslateStatementList (pfx, strLimit, &chLimit)) {
1915
3.86k
      FndOne = 1;
1916
3.86k
    } else {
1917
1.49k
      if (!*pfx->pex) {
1918
283
        (void) ThrowMagickException (
1919
283
          pfx->exception, GetMagickModule(), OptionError,
1920
283
          "For function", "'%s' expected ')' at '%s'",
1921
283
          funStr, SetShortExp(pfx));
1922
283
        return MagickFalse;
1923
283
      }
1924
      /* Maybe don't break because other expressions may be not empty. */
1925
1.21k
      if (!chLimit) break;
1926
1.05k
      if (fe == fP || fe == fS|| fe == fIf) {
1927
704
        (void) AddElement (pfx, (fxFltType) 0, oNull);
1928
704
        FndOne = 1;
1929
704
      }
1930
1.05k
    }
1931
1932
4.92k
    if (strchr (strLimit, chLimit)==NULL) {
1933
10
      (void) ThrowMagickException (
1934
10
        pfx->exception, GetMagickModule(), OptionError,
1935
10
        "For function", "'%s' expected one of '%s' after expression but found '%c' at '%s'",
1936
10
        funStr, strLimit, chLimit ? chLimit : ' ', SetShortExp(pfx));
1937
10
      return MagickFalse;
1938
10
    }
1939
4.91k
    if (FndOne) {
1940
4.55k
      FndArgs++;
1941
4.55k
      nArgs--;
1942
4.55k
    }
1943
4.91k
    switch (FndArgs) {
1944
3.07k
      case 1:
1945
3.07k
        if (ndx1 != NULL_ADDRESS) {
1946
4
          (void) ThrowMagickException (
1947
4
            pfx->exception, GetMagickModule(), OptionError,
1948
4
            "For function", "'%s' required argument is missing at '%s'",
1949
4
            funStr, SetShortExp(pfx));
1950
4
          return MagickFalse;
1951
4
        }
1952
3.06k
        ndx1 = pfx->usedElements;
1953
3.06k
        if (fe==fWhile || fe==fIf) {
1954
56
          (void) AddAddressingElement (pfx, rIfZeroGoto, NULL_ADDRESS); /* address will be ndx2+1 */
1955
3.01k
        } else if (fe==fDo) {
1956
554
          (void) AddAddressingElement (pfx, rIfZeroGoto, NULL_ADDRESS); /* address will be ndx2+1 */
1957
2.45k
        } else if (fe==fFor) {
1958
39
          pfx->Elements[pfx->usedElements-1].do_push = MagickFalse;
1959
39
        }
1960
3.06k
        break;
1961
1.43k
      case 2:
1962
1.43k
        if (ndx2 != NULL_ADDRESS) {
1963
1
          (void) ThrowMagickException (
1964
1
            pfx->exception, GetMagickModule(), OptionError,
1965
1
            "For function", "'%s' required argument is missing at '%s'",
1966
1
            funStr, SetShortExp(pfx));
1967
1
          return MagickFalse;
1968
1
        }
1969
1.43k
        ndx2 = pfx->usedElements;
1970
1.43k
        if (fe==fWhile) {
1971
0
          pfx->Elements[pfx->usedElements-1].do_push = MagickFalse;
1972
0
          (void) AddAddressingElement (pfx, rGotoChk, ndx0);
1973
1.43k
        } else if (fe==fDo) {
1974
528
          pfx->Elements[pfx->usedElements-1].do_push = MagickFalse;
1975
528
          (void) AddAddressingElement (pfx, rGotoChk, ndx0 + 1);
1976
903
        } else if (fe==fFor) {
1977
22
          (void) AddAddressingElement (pfx, rIfZeroGoto, NULL_ADDRESS); /* address will be ndx3 */
1978
22
          pfx->Elements[pfx->usedElements-1].do_push = MagickTrue; /* we may need return from for() */
1979
22
          (void) AddAddressingElement (pfx, rZerStk, NULL_ADDRESS);
1980
881
        } else if (fe==fIf) {
1981
49
          (void) AddAddressingElement (pfx, rGoto, NULL_ADDRESS); /* address will be ndx3 */
1982
49
        }
1983
1.43k
        break;
1984
58
      case 3:
1985
58
        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
57
        if (fe==fFor) {
1993
13
          pfx->Elements[pfx->usedElements-1].do_push = MagickFalse;
1994
13
          (void) AddAddressingElement (pfx, rGotoChk, ndx1);
1995
13
        }
1996
57
        ndx3 = pfx->usedElements;
1997
57
        break;
1998
348
      default:
1999
348
        break;
2000
4.91k
    }
2001
4.90k
    if (chLimit == expChLimit) {
2002
2.98k
      lenExp = (size_t) (pfx->pex - pExpStart - 1);
2003
2.98k
      break;
2004
2.98k
    }
2005
4.90k
  } /* end while args of a function */
2006
6.03k
  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.03k
  if (fe == fP || fe == fS || fe == fU || fe == fChannel) {
2015
9.26k
    while (FndArgs < Functions[fe-(int) FirstFunc].number_args) {
2016
4.57k
      (void) AddElement (pfx, (fxFltType) 0, oNull);
2017
4.57k
      FndArgs++;
2018
4.57k
    }
2019
4.68k
  }
2020
2021
6.03k
  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.03k
  if (FndArgs < Functions[fe-(int) FirstFunc].number_args) {
2037
95
    (void) ThrowMagickException (
2038
95
      pfx->exception, GetMagickModule(), OptionError,
2039
95
      "For function", "'%s' expected %i arguments, found too few (%i) at '%s'",
2040
95
      funStr, Functions[fe-(int) FirstFunc].number_args, FndArgs, SetShortExp(pfx));
2041
95
    return MagickFalse;
2042
95
  }
2043
5.93k
  if (fe != fS && fe != fV && FndArgs == 0 && Functions[fe-(int) FirstFunc].number_args == 0) {
2044
    /* This is for "rand()" and similar. */
2045
67
    chLimit = expChLimit;
2046
67
    if (!ExpectChar (pfx, ')')) return MagickFalse;
2047
67
  }
2048
2049
5.90k
  if (chLimit != expChLimit) {
2050
93
    (void) ThrowMagickException (
2051
93
      pfx->exception, GetMagickModule(), OptionError,
2052
93
      "For function", "'%s', arguments don't end with '%c' at '%s'",
2053
93
      funStr, expChLimit, SetShortExp(pfx));
2054
93
    return MagickFalse;
2055
93
  }
2056
5.81k
  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.80k
  if (IsQualifier (pfx)) {
2065
2066
1.25k
    if (fe == fU || fe == fV || fe == fS) {
2067
2068
994
      coordQual = (GetCoordQualifier (pfx, (int) fe) == 1) ? MagickTrue : MagickFalse;
2069
2070
994
      if (coordQual) {
2071
2072
        /* Remove last element, which should be fP */
2073
379
        ElementT * pel = &pfx->Elements[pfx->usedElements-1];
2074
379
        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
379
        chQual = pel->channel_qual;
2082
379
        expChLimit = (pel->is_relative) ? ']' : '}';
2083
379
        pfx->usedElements--;
2084
379
        if (fe == fU) fe = fUP;
2085
56
        else if (fe == fV) fe = fVP;
2086
56
        else if (fe == fS) fe = fSP;
2087
379
        funStr = Functions[fe-(int) FirstFunc].str;
2088
379
      }
2089
994
    }
2090
2091
1.25k
    if ( chQual == NO_CHAN_QUAL &&
2092
1.18k
         (fe == fP || fe == fS || fe == fSP || fe == fU || fe == fUP || fe == fV || fe == fVP)
2093
1.25k
       )
2094
1.16k
    {
2095
1.16k
      chQual = GetChannelQualifier (pfx, (int) fe);
2096
1.16k
    }
2097
2098
1.25k
    if (chQual == NO_CHAN_QUAL && (fe == fU || fe == fV || fe == fS)) {
2099
      /* Note: we don't allow "p.mean" etc. */
2100
450
      iaQual = GetImgAttrQualifier (pfx, (int) fe);
2101
450
    }
2102
1.25k
    if (IsQualifier (pfx) && chQual == NO_CHAN_QUAL && iaQual != aNull) {
2103
136
      chQual = GetChannelQualifier (pfx, (int) fe);
2104
136
    }
2105
1.25k
    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.25k
    if (!coordQual && chQual == NO_CHAN_QUAL && iaQual == aNull) {
2113
110
      (void) ThrowMagickException (
2114
110
        pfx->exception, GetMagickModule(), OptionError,
2115
110
        "For function", "'%s', bad qualifier '%s' at '%s'",
2116
110
        funStr, pfx->token, SetShortExp(pfx));
2117
110
      return MagickFalse;
2118
110
    }
2119
1.14k
    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.13k
    if (chQual == HUE_CHANNEL || chQual == SAT_CHANNEL || chQual == LIGHT_CHANNEL) {
2128
167
      pfx->NeedHsl = MagickTrue;
2129
2130
167
      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
167
    }
2138
1.13k
  }
2139
2140
5.67k
  if (iaQual != aNull && chQual != NO_CHAN_QUAL) {
2141
59
    if (ImgAttrs[iaQual-(int) FirstImgAttr].need_stats == MagickFalse) {
2142
21
      (void) ThrowMagickException (
2143
21
        pfx->exception, GetMagickModule(), OptionError,
2144
21
        "Can't have image attribute ", "'%s' with channel qualifier '%s' at '%s'",
2145
21
        ImgAttrs[iaQual-(int) FirstImgAttr].str,
2146
21
        pfx->token, SetShortExp(pfx));
2147
21
      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
59
  }
2159
2160
5.64k
  if (fe==fWhile) {
2161
0
    pfx->Elements[ndx1].element_index = ndx2+1;
2162
5.64k
  } else if (fe==fDo) {
2163
525
    pfx->Elements[ndx0].element_index = ndx1+1;
2164
525
    pfx->Elements[ndx1].element_index = ndx2+1;
2165
5.12k
  } else if (fe==fFor) {
2166
11
    pfx->Elements[ndx2].element_index = ndx3;
2167
5.11k
  } else if (fe==fIf) {
2168
32
    pfx->Elements[ndx1].element_index = ndx2 + 1;
2169
32
    pfx->Elements[ndx2].element_index = ndx3;
2170
5.08k
  } else {
2171
5.08k
    if (fe == fU && iaQual == aNull) {
2172
826
      ElementT * pel = &pfx->Elements[pfx->usedElements-1];
2173
826
      if (pel->type == etConstant && pel->val == 0.0) {
2174
516
        pfx->usedElements--;
2175
516
        fe = fU0;
2176
516
      }
2177
826
    }
2178
5.08k
    (void) AddElement (pfx, (fxFltType) 0, (int) fe);
2179
5.08k
    if (fe == fP || fe == fU  || fe == fU0 || fe == fUP ||
2180
1.16k
        fe == fV || fe == fVP || fe == fS || fe == fSP)
2181
4.41k
    {
2182
4.41k
      ElementT * pel = &pfx->Elements[pfx->usedElements-1];
2183
4.41k
      pel->is_relative = (expChLimit == ']' ? MagickTrue : MagickFalse);
2184
4.41k
      if (chQual >= 0) pel->channel_qual = chQual;
2185
4.41k
      if (iaQual != aNull && (fe == fU || fe == fV || fe == fS)) {
2186
        /* Note: we don't allow "p[2,3].mean" or "p.mean" etc. */
2187
347
        pel->img_attr_qual = iaQual;
2188
347
      }
2189
4.41k
    }
2190
5.08k
  }
2191
2192
5.64k
  if (pExpStart && lenExp) {
2193
2.91k
    ElementT * pel = &pfx->Elements[pfx->usedElements-1];
2194
2.91k
    pel->exp_start = pExpStart;
2195
2.91k
    pel->exp_len = lenExp;
2196
2.91k
  }
2197
2198
5.64k
  if (fe == fDebug)
2199
10
    pfx->ContainsDebug = MagickTrue;
2200
2201
5.64k
  return MagickTrue;
2202
5.67k
}
2203
2204
static MagickBooleanType IsStealth (int op)
2205
11.8k
{
2206
11.8k
  return (op == fU0 || op == fUP || op == fSP || op == fVP ||
2207
11.6k
           (op >= FirstCont && op <= rNull) ? MagickTrue : MagickFalse
2208
11.8k
         );
2209
11.8k
}
2210
2211
static MagickBooleanType GetOperand (
2212
  FxInfo * pfx, MagickBooleanType * UserSymbol, MagickBooleanType * NewUserSymbol, int * UserSymNdx,
2213
  MagickBooleanType * needPopAll)
2214
23.3k
{
2215
2216
23.3k
  *NewUserSymbol = *UserSymbol = MagickFalse;
2217
23.3k
  *UserSymNdx = NULL_ADDRESS;
2218
2219
23.3k
  SkipSpaces (pfx);
2220
23.3k
  if (!*pfx->pex) return MagickFalse;
2221
23.3k
  (void) GetToken (pfx);
2222
2223
23.3k
  if (pfx->lenToken==0) {
2224
2225
    /* Try '(' or unary prefix
2226
    */
2227
11.3k
    OperatorE op = GetLeadingOp (pfx);
2228
11.3k
    if (op==oOpenParen) {
2229
1.12k
      char chLimit = '\0';
2230
1.12k
      if (!PushOperatorStack (pfx, (int) op)) return MagickFalse;
2231
1.12k
      pfx->pex++;
2232
1.12k
      if (!TranslateExpression (pfx, ")", &chLimit, needPopAll)) {
2233
1.04k
        (void) ThrowMagickException (
2234
1.04k
          pfx->exception, GetMagickModule(), OptionError,
2235
1.04k
          "Empty expression in parentheses at", "'%s'",
2236
1.04k
          SetShortExp(pfx));
2237
1.04k
        return MagickFalse;
2238
1.04k
      }
2239
73
      if (chLimit != ')') {
2240
10
        (void) ThrowMagickException (
2241
10
          pfx->exception, GetMagickModule(), OptionError,
2242
10
          "'(' but no ')' at", "'%s'",
2243
10
          SetShortExp(pfx));
2244
10
        return MagickFalse;
2245
10
      }
2246
      /* Top of opr stack should be '('. */
2247
63
      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
63
      return MagickTrue;
2255
10.2k
    } else if (OprIsUnaryPrefix (op)) {
2256
4.02k
      MagickBooleanType operand_ok;
2257
4.02k
      if (!PushOperatorStack (pfx, (int) op)) return MagickFalse;
2258
4.02k
      pfx->pex++;
2259
4.02k
      SkipSpaces (pfx);
2260
4.02k
      if (!*pfx->pex) return MagickFalse;
2261
3.96k
      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.96k
      pfx->teDepth++;
2269
3.96k
      operand_ok=GetOperand (pfx, UserSymbol, NewUserSymbol, UserSymNdx, needPopAll);
2270
3.96k
      pfx->teDepth--;
2271
3.96k
      if (!operand_ok) {
2272
651
        (void) ThrowMagickException (
2273
651
          pfx->exception, GetMagickModule(), OptionError,
2274
651
          "After unary, bad operand at", "'%s'",
2275
651
          SetShortExp(pfx));
2276
651
        return MagickFalse;
2277
651
      }
2278
2279
3.31k
      if (*NewUserSymbol) {
2280
15
        (void) ThrowMagickException (
2281
15
          pfx->exception, GetMagickModule(), OptionError,
2282
15
          "After unary, NewUserSymbol at", "'%s'",
2283
15
          SetShortExp(pfx));
2284
15
        return MagickFalse;
2285
15
      }
2286
2287
3.29k
      if (*UserSymbol) {
2288
36
        (void) AddAddressingElement (pfx, rCopyFrom, *UserSymNdx);
2289
36
        *UserSymNdx = NULL_ADDRESS;
2290
2291
36
        *UserSymbol = MagickFalse;
2292
36
        *NewUserSymbol = MagickFalse;
2293
36
      }
2294
2295
3.29k
      (void) GetToken (pfx);
2296
3.29k
      return MagickTrue;
2297
6.18k
    } else if (*pfx->pex == '#') {
2298
357
      fxFltType v0=0, v1=0, v2=0;
2299
357
      ssize_t lenToken = GetHexColour (pfx, &v0, &v1, &v2);
2300
357
      if (lenToken < 0) {
2301
70
        (void) ThrowMagickException (
2302
70
          pfx->exception, GetMagickModule(), OptionError,
2303
70
          "Bad hex number at", "'%s'",
2304
70
          SetShortExp(pfx));
2305
70
        return MagickFalse;
2306
287
      } else if (lenToken > 0) {
2307
287
        (void) AddColourElement (pfx, v0, v1, v2);
2308
287
        pfx->pex+=lenToken;
2309
287
      }
2310
287
      return MagickTrue;
2311
357
    }
2312
2313
    /* Try a constant number.
2314
    */
2315
5.82k
    {
2316
5.82k
      char * tailptr;
2317
5.82k
      ssize_t lenOptArt;
2318
5.82k
      fxFltType val = strtold (pfx->pex, &tailptr);
2319
5.82k
      if (pfx->pex != tailptr) {
2320
5.45k
        pfx->pex = tailptr;
2321
5.45k
        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
5.18k
          double Pow = 0.0;
2327
5.18k
          const char Prefixes[] = "yzafpnum.kMGTPEZY";
2328
5.18k
          const char * pSi = strchr (Prefixes, *tailptr);
2329
5.18k
          if (pSi && *pSi != '.') Pow = (double) ((pSi - Prefixes) * 3 - 24);
2330
4.49k
          else if (*tailptr == 'c') Pow = -2;
2331
4.44k
          else if (*tailptr == 'h') Pow =  2;
2332
4.40k
          else if (*tailptr == 'k') Pow =  3;
2333
5.18k
          if (Pow != 0.0) {
2334
782
            if (*(++pfx->pex) == 'i') {
2335
24
              val *= pow (2.0, Pow/0.3);
2336
24
              pfx->pex++;
2337
758
            } else {
2338
758
              val *= pow (10.0, Pow);
2339
758
            }
2340
782
          }
2341
5.18k
        }
2342
5.45k
        (void) AddElement (pfx, val, oNull);
2343
5.45k
        return MagickTrue;
2344
5.45k
      }
2345
2346
376
      val = (fxFltType) 0;
2347
376
      lenOptArt = GetProperty (pfx, &val, NULL);
2348
376
      if (lenOptArt < 0) return MagickFalse;
2349
274
      if (lenOptArt > 0) {
2350
109
        (void) AddElement (pfx, val, oNull);
2351
109
        pfx->pex += lenOptArt;
2352
109
        return MagickTrue;
2353
109
      }
2354
274
    }
2355
2356
274
  } /* end of lenToken==0 */
2357
2358
12.2k
  if (pfx->lenToken > 0) {
2359
    /* Try a constant
2360
    */
2361
12.0k
    {
2362
12.0k
      ConstantE ce;
2363
118k
      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
187
          break;
2367
187
        }
2368
107k
      }
2369
2370
12.0k
      if (ce != cNull) {
2371
187
        (void) AddElement (pfx, Constants[ce].val, oNull);
2372
187
        pfx->pex += pfx->lenToken;
2373
187
        return MagickTrue;
2374
187
      }
2375
12.0k
    }
2376
2377
    /* Try a function
2378
    */
2379
11.8k
    {
2380
11.8k
      FunctionE fe;
2381
677k
      for (fe = FirstFunc; fe < fNull; fe=(FunctionE) (fe+1)) {
2382
671k
        const char * feStr = Functions[fe-(int) FirstFunc].str;
2383
671k
        if (LocaleCompare (feStr, pfx->token)==0) {
2384
5.97k
          break;
2385
5.97k
        }
2386
671k
      }
2387
2388
11.8k
      if (fe == fV && pfx->ImgListLen < 2) {
2389
21
        (void) ThrowMagickException (
2390
21
          pfx->exception, GetMagickModule(), OptionError,
2391
21
          "Symbol 'v' but fewer than two images at", "'%s'",
2392
21
          SetShortExp(pfx));
2393
21
        return MagickFalse;
2394
21
      }
2395
2396
11.8k
      if (IsStealth ((int) fe)) {
2397
180
        (void) ThrowMagickException (
2398
180
          pfx->exception, GetMagickModule(), OptionError,
2399
180
          "Function", "'%s' not permitted at '%s'",
2400
180
          pfx->token, SetShortExp(pfx));
2401
180
      }
2402
2403
11.8k
      if (fe == fDo || fe == fFor || fe == fIf || fe == fWhile) {
2404
758
        *needPopAll = MagickTrue;
2405
758
      }
2406
2407
11.8k
      if (fe != fNull) return (GetFunction (pfx, fe));
2408
11.8k
    }
2409
2410
    /* Try image attribute
2411
    */
2412
5.88k
    {
2413
5.88k
      ImgAttrE ia = GetImgAttrToken (pfx);
2414
5.88k
      if (ia != aNull) {
2415
1.66k
        fxFltType val = 0;
2416
1.66k
        (void) AddElement (pfx, val, (int) ia);
2417
2418
1.66k
        if (ImgAttrs[ia-(int) FirstImgAttr].need_stats != MagickFalse) {
2419
289
          if (IsQualifier (pfx)) {
2420
102
            PixelChannel chQual = GetChannelQualifier (pfx, (int) ia);
2421
102
            ElementT * pel;
2422
102
            if (chQual == NO_CHAN_QUAL) {
2423
22
              (void) ThrowMagickException (
2424
22
                pfx->exception, GetMagickModule(), OptionError,
2425
22
                "Bad channel qualifier at", "'%s'",
2426
22
                SetShortExp(pfx));
2427
22
              return MagickFalse;
2428
22
            }
2429
            /* Adjust the element */
2430
80
            pel = &pfx->Elements[pfx->usedElements-1];
2431
80
            pel->channel_qual = chQual;
2432
80
          }
2433
289
        }
2434
1.64k
        return MagickTrue;
2435
1.66k
      }
2436
5.88k
    }
2437
2438
    /* Try symbol
2439
    */
2440
4.21k
    {
2441
4.21k
      SymbolE se;
2442
63.7k
      for (se = FirstSym; se < sNull; se=(SymbolE) (se+1)) {
2443
61.5k
        const char * seStr = Symbols[se-(int) FirstSym].str;
2444
61.5k
        if (LocaleCompare (seStr, pfx->token)==0) {
2445
1.99k
          break;
2446
1.99k
        }
2447
61.5k
      }
2448
4.21k
      if (se != sNull) {
2449
1.99k
        fxFltType val = 0;
2450
1.99k
        (void) AddElement (pfx, val, (int) se);
2451
1.99k
        pfx->pex += pfx->lenToken;
2452
2453
1.99k
        if (se==sHue || se==sSaturation || se==sLightness) pfx->NeedHsl = MagickTrue;
2454
1.99k
        return MagickTrue;
2455
1.99k
      }
2456
4.21k
    }
2457
2458
    /* Try constant colour.
2459
    */
2460
2.21k
    {
2461
2.21k
      fxFltType v0, v1, v2;
2462
2.21k
      ssize_t ColLen = GetConstantColour (pfx, &v0, &v1, &v2);
2463
2.21k
      if (ColLen < 0) return MagickFalse;
2464
2.16k
      if (ColLen > 0) {
2465
680
        (void) AddColourElement (pfx, v0, v1, v2);
2466
680
        pfx->pex+=ColLen;
2467
680
        return MagickTrue;
2468
680
      }
2469
2.16k
    }
2470
2471
    /* Try image artifact.
2472
    */
2473
1.48k
    {
2474
1.48k
      const char *artifact;
2475
1.48k
      artifact = GetImageArtifact (pfx->image, pfx->token);
2476
1.48k
      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.48k
    }
2491
2492
    /* Try user symbols. If it is, don't AddElement yet.
2493
    */
2494
1.48k
    if (TokenMaybeUserSymbol (pfx)) {
2495
1.36k
      *UserSymbol = MagickTrue;
2496
1.36k
      *UserSymNdx = FindUserSymbol (pfx, pfx->token);
2497
1.36k
      if (*UserSymNdx == NULL_ADDRESS) {
2498
793
        *UserSymNdx = AddUserSymbol (pfx, pfx->pex, pfx->lenToken);
2499
793
        *NewUserSymbol = MagickTrue;
2500
793
      } else {
2501
567
      }
2502
1.36k
      pfx->pex += pfx->lenToken;
2503
2504
1.36k
      return MagickTrue;
2505
1.36k
    }
2506
1.48k
  }
2507
2508
293
  (void) ThrowMagickException (
2509
293
    pfx->exception, GetMagickModule(), OptionError,
2510
293
    "Expected operand at", "'%s'",
2511
293
    SetShortExp(pfx));
2512
2513
293
  return MagickFalse;
2514
12.2k
}
2515
2516
static inline MagickBooleanType IsRealOperator (OperatorE op)
2517
8.12k
{
2518
8.12k
  return (op < oOpenParen || op > oCloseBrace) ? MagickTrue : MagickFalse;
2519
8.12k
}
2520
2521
static inline MagickBooleanType ProcessTernaryOpr (FxInfo * pfx, TernaryT * ptern)
2522
/* Ternary operator "... ? ... : ..."
2523
   returns false iff we have exception
2524
*/
2525
7.01k
{
2526
7.01k
  if (pfx->usedOprStack == 0)
2527
10
    return MagickFalse;
2528
7.00k
  if (pfx->OperatorStack[pfx->usedOprStack-1] == oQuery) {
2529
280
    if (ptern->addr_query != NULL_ADDRESS) {
2530
15
      (void) ThrowMagickException (
2531
15
        pfx->exception, GetMagickModule(), OptionError,
2532
15
        "Already have '?' in sub-expression at", "'%s'",
2533
15
        SetShortExp(pfx));
2534
15
      return MagickFalse;
2535
15
    }
2536
265
    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
265
    pfx->usedOprStack--;
2544
265
    ptern->addr_query = pfx->usedElements;
2545
265
    (void) AddAddressingElement (pfx, rIfZeroGoto, NULL_ADDRESS);
2546
    /* address will be one after the Colon address. */
2547
265
  }
2548
6.72k
  else if (pfx->OperatorStack[pfx->usedOprStack-1] == oColon) {
2549
191
    if (ptern->addr_query == NULL_ADDRESS) {
2550
22
      (void) ThrowMagickException (
2551
22
        pfx->exception, GetMagickModule(), OptionError,
2552
22
        "Need '?' in sub-expression at", "'%s'",
2553
22
        SetShortExp(pfx));
2554
22
      return MagickFalse;
2555
22
    }
2556
169
    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
169
    pfx->usedOprStack--;
2564
169
    ptern->addr_colon = pfx->usedElements;
2565
169
    pfx->Elements[pfx->usedElements-1].do_push = MagickTrue;
2566
169
    (void) AddAddressingElement (pfx, rGoto, NULL_ADDRESS);
2567
    /* address will be after the subexpression */
2568
169
  }
2569
6.96k
  return MagickTrue;
2570
7.00k
}
2571
2572
static MagickBooleanType GetOperator (
2573
  FxInfo * pfx,
2574
  MagickBooleanType * Assign, MagickBooleanType * Update, MagickBooleanType * IncrDecr)
2575
8.12k
{
2576
8.12k
  OperatorE op;
2577
8.12k
  size_t len = 0;
2578
8.12k
  MagickBooleanType DoneIt = MagickFalse;
2579
8.12k
  SkipSpaces (pfx);
2580
156k
  for (op = (OperatorE)0; op != oNull; op=(OperatorE) (op+1)) {
2581
155k
    const char * opStr = Operators[op].str;
2582
155k
    len = strlen(opStr);
2583
155k
    if (LocaleNCompare (opStr, pfx->pex, len)==0) {
2584
7.97k
      break;
2585
7.97k
    }
2586
155k
  }
2587
2588
8.12k
  if (!IsRealOperator (op)) {
2589
136
    (void) ThrowMagickException (
2590
136
      pfx->exception, GetMagickModule(), OptionError,
2591
136
      "Not a real operator at", "'%s'",
2592
136
      SetShortExp(pfx));
2593
136
    return MagickFalse;
2594
136
  }
2595
2596
7.98k
  if (op==oNull) {
2597
152
    (void) ThrowMagickException (
2598
152
      pfx->exception, GetMagickModule(), OptionError,
2599
152
      "Expected operator at", "'%s'",
2600
152
      SetShortExp(pfx));
2601
152
    return MagickFalse;
2602
152
  }
2603
2604
7.83k
  *Assign = (op==oAssign) ? MagickTrue : MagickFalse;
2605
7.83k
  *Update = OprInPlace ((int) op);
2606
7.83k
  *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
12.0k
  while (pfx->usedOprStack > 0) {
2614
9.21k
    OperatorE top = pfx->OperatorStack[pfx->usedOprStack-1];
2615
9.21k
    int precTop, precNew;
2616
9.21k
    if (top == oOpenParen || top == oAssign || OprInPlace ((int) top)) break;
2617
7.20k
    precTop = Operators[top].precedence;
2618
7.20k
    precNew = Operators[op].precedence;
2619
    /* Assume left associativity.
2620
       If right assoc, this would be "<=".
2621
    */
2622
7.20k
    if (precTop < precNew) break;
2623
4.24k
    (void) AddElement (pfx, (fxFltType) 0, (int) top);
2624
4.24k
    pfx->usedOprStack--;
2625
4.24k
  }
2626
2627
  /* If new op is close paren, and stack top is open paren,
2628
     remove stack top.
2629
  */
2630
7.83k
  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.83k
  if (!DoneIt) {
2651
7.83k
    if (!PushOperatorStack (pfx, (int) op)) return MagickFalse;
2652
7.83k
  }
2653
2654
7.83k
  pfx->pex += len;
2655
2656
7.83k
  return MagickTrue;
2657
7.83k
}
2658
2659
static MagickBooleanType ResolveTernaryAddresses (FxInfo * pfx, TernaryT * ptern)
2660
9.47k
{
2661
9.47k
  if (ptern->addr_query == NULL_ADDRESS && ptern->addr_colon == NULL_ADDRESS)
2662
9.30k
    return MagickTrue;
2663
2664
170
  if (ptern->addr_query != NULL_ADDRESS && ptern->addr_colon != NULL_ADDRESS) {
2665
142
    pfx->Elements[ptern->addr_query].element_index = ptern->addr_colon + 1;
2666
142
    pfx->Elements[ptern->addr_colon].element_index = pfx->usedElements;
2667
142
    ptern->addr_query = NULL_ADDRESS;
2668
142
    ptern->addr_colon = NULL_ADDRESS;
2669
142
  } else if (ptern->addr_query != NULL_ADDRESS) {
2670
28
      (void) ThrowMagickException (
2671
28
        pfx->exception, GetMagickModule(), OptionError,
2672
28
        "'?' with no corresponding ':'", "'%s' at '%s'",
2673
28
        pfx->token, SetShortExp(pfx));
2674
28
      return MagickFalse;
2675
28
  } 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
142
  return MagickTrue;
2683
170
}
2684
2685
static MagickBooleanType TranslateExpression (
2686
  FxInfo * pfx, const char * strLimit, char * chLimit, MagickBooleanType * needPopAll)
2687
13.8k
{
2688
  /* There should be only one New per expression (oAssign), but can be many Old.
2689
  */
2690
13.8k
  MagickBooleanType UserSymbol, NewUserSymbol;
2691
13.8k
  int UserSymNdx0, UserSymNdx1;
2692
2693
13.8k
  MagickBooleanType
2694
13.8k
    Assign = MagickFalse,
2695
13.8k
    Update = MagickFalse,
2696
13.8k
    IncrDecr = MagickFalse;
2697
2698
13.8k
  int StartEleNdx;
2699
2700
13.8k
  TernaryT ternary;
2701
13.8k
  ternary.addr_query = NULL_ADDRESS;
2702
13.8k
  ternary.addr_colon = NULL_ADDRESS;
2703
2704
13.8k
  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.8k
  pfx->teDepth++;
2712
2713
13.8k
  *chLimit = '\0';
2714
2715
13.8k
  StartEleNdx = pfx->usedElements-1;
2716
13.8k
  if (StartEleNdx < 0) StartEleNdx = 0;
2717
2718
13.8k
  SkipSpaces (pfx);
2719
2720
13.8k
  if (!*pfx->pex) {
2721
64
    pfx->teDepth--;
2722
64
    return MagickFalse;
2723
64
  }
2724
2725
13.7k
  if (strchr(strLimit,*pfx->pex)!=NULL) {
2726
973
    *chLimit = *pfx->pex;
2727
973
    pfx->pex++;
2728
973
    pfx->teDepth--;
2729
2730
973
    return MagickFalse;
2731
973
  }
2732
2733
12.7k
  if (!GetOperand (pfx, &UserSymbol, &NewUserSymbol, &UserSymNdx0, needPopAll)) return MagickFalse;
2734
10.5k
  SkipSpaces (pfx);
2735
2736
  /* Loop through Operator, Operand, Operator, Operand, ...
2737
  */
2738
17.0k
  while (*pfx->pex && (!*strLimit || (strchr(strLimit,*pfx->pex)==NULL))) {
2739
8.12k
    if (!GetOperator (pfx, &Assign, &Update, &IncrDecr)) return MagickFalse;
2740
7.83k
    SkipSpaces (pfx);
2741
7.83k
    if (NewUserSymbol && !Assign) {
2742
14
      (void) ThrowMagickException (
2743
14
        pfx->exception, GetMagickModule(), OptionError,
2744
14
        "Expected assignment after new UserSymbol", "'%s' at '%s'",
2745
14
        pfx->token, SetShortExp(pfx));
2746
14
      return MagickFalse;
2747
14
    }
2748
7.82k
    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.80k
    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.78k
    if (UserSymbol && (Assign || Update) && !IncrDecr) {
2763
2764
814
      if (!TranslateExpression (pfx, strLimit, chLimit, needPopAll)) return MagickFalse;
2765
500
      if (!*pfx->pex) break;
2766
128
      if (!*strLimit) break;
2767
128
      if (strchr(strLimit,*chLimit)!=NULL) break;
2768
128
    }
2769
7.01k
    if (UserSymbol && !Assign && !Update && UserSymNdx0 != NULL_ADDRESS) {
2770
85
      ElementT * pel;
2771
85
      (void) AddAddressingElement (pfx, rCopyFrom, UserSymNdx0);
2772
85
      UserSymNdx0 = NULL_ADDRESS;
2773
85
      pel = &pfx->Elements[pfx->usedElements-1];
2774
85
      pel->do_push = MagickTrue;
2775
85
    }
2776
2777
7.01k
    if (UserSymbol) {
2778
600
      while (TopOprIsUnaryPrefix (pfx)) {
2779
338
        OperatorE op = pfx->OperatorStack[pfx->usedOprStack-1];
2780
338
        (void) AddElement (pfx, (fxFltType) 0, (int) op);
2781
338
        pfx->usedOprStack--;
2782
338
      }
2783
262
    }
2784
2785
7.01k
    if (!ProcessTernaryOpr (pfx, &ternary)) return MagickFalse;
2786
2787
6.96k
    if (ternary.addr_colon != NULL_ADDRESS) {
2788
169
      if (!TranslateExpression (pfx, ",);", chLimit, needPopAll)) return MagickFalse;
2789
142
      break;
2790
169
    }
2791
2792
6.79k
    UserSymbol = NewUserSymbol = MagickFalse;
2793
2794
6.79k
    if ( (!*pfx->pex) || (*strLimit && (strchr(strLimit,*pfx->pex)!=NULL) ) )
2795
158
    {
2796
158
      if (IncrDecr) break;
2797
2798
113
      (void) ThrowMagickException (
2799
113
        pfx->exception, GetMagickModule(), OptionError,
2800
113
        "Expected operand after operator", "at '%s'",
2801
113
        SetShortExp(pfx));
2802
113
      return MagickFalse;
2803
158
    }
2804
2805
6.63k
    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.62k
    if (!GetOperand (pfx, &UserSymbol, &NewUserSymbol, &UserSymNdx1, needPopAll)) {
2814
114
      (void) ThrowMagickException (
2815
114
        pfx->exception, GetMagickModule(), OptionError,
2816
114
        "Expected operand at", "'%s'",
2817
114
        SetShortExp(pfx));
2818
114
      return MagickFalse;
2819
114
    }
2820
6.51k
    SkipSpaces (pfx);
2821
6.51k
    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.49k
    if (UserSymbol && !NewUserSymbol) {
2829
152
      (void) AddAddressingElement (pfx, rCopyFrom, UserSymNdx1);
2830
152
      UserSymNdx1 = NULL_ADDRESS;
2831
152
    }
2832
6.49k
    UserSymNdx0 = UserSymNdx1;
2833
6.49k
  }
2834
2835
9.54k
  if (UserSymbol && !Assign && !Update && UserSymNdx0 != NULL_ADDRESS) {
2836
105
    ElementT * pel;
2837
105
    if (NewUserSymbol) {
2838
54
      (void) ThrowMagickException (
2839
54
        pfx->exception, GetMagickModule(), OptionError,
2840
54
        "NewUserSymbol", "'%s' needs assignment operator at '%s'",
2841
54
        pfx->token, SetShortExp(pfx));
2842
54
      return MagickFalse;
2843
54
    }
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.48k
  if (*pfx->pex && !*chLimit && (strchr(strLimit,*pfx->pex)!=NULL)) {
2850
4.59k
    *chLimit = *pfx->pex;
2851
4.59k
    pfx->pex++;
2852
4.59k
  }
2853
14.1k
  while (pfx->usedOprStack) {
2854
9.52k
    OperatorE op = pfx->OperatorStack[pfx->usedOprStack-1];
2855
9.52k
    if (op == oOpenParen || op == oOpenBracket || op == oOpenBrace) {
2856
4.08k
      break;
2857
4.08k
    }
2858
5.44k
    if ( (op==oAssign && !Assign) || (OprInPlace((int) op) && !Update) ) {
2859
305
      break;
2860
305
    }
2861
5.14k
    pfx->usedOprStack--;
2862
5.14k
    (void) AddElement (pfx, (fxFltType) 0, (int) op);
2863
5.14k
    if (op == oAssign) {
2864
368
      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
365
      pfx->usedElements--;
2874
365
      (void) AddAddressingElement (pfx, rCopyTo, UserSymNdx0);
2875
365
      break;
2876
4.77k
    } else if (OprInPlace ((int) op)) {
2877
141
      if (UserSymNdx0 < 0) {
2878
11
        (void) ThrowMagickException (
2879
11
          pfx->exception, GetMagickModule(), OptionError,
2880
11
          "Operator-in-place to unknown user symbol at", "'%s'",
2881
11
          SetShortExp(pfx));
2882
11
        return MagickFalse;
2883
11
      }
2884
      /* Modify latest element.
2885
      */
2886
130
      pfx->Elements[pfx->usedElements-1].element_index = UserSymNdx0;
2887
130
      break;
2888
141
    }
2889
5.14k
  }
2890
2891
9.47k
  if (ternary.addr_query != NULL_ADDRESS) *needPopAll = MagickTrue;
2892
2893
9.47k
  (void) ResolveTernaryAddresses (pfx, &ternary);
2894
2895
9.47k
  pfx->teDepth--;
2896
2897
9.47k
  if (!pfx->teDepth && *needPopAll) {
2898
568
    (void) AddAddressingElement (pfx, rZerStk, NULL_ADDRESS);
2899
568
    *needPopAll = MagickFalse;
2900
568
  }
2901
2902
9.47k
  if (pfx->exception->severity >= ErrorException)
2903
165
    return MagickFalse;
2904
2905
9.30k
  return MagickTrue;
2906
9.47k
}
2907
2908
2909
static MagickBooleanType TranslateStatement (FxInfo * pfx, char * strLimit, char * chLimit)
2910
11.7k
{
2911
11.7k
  MagickBooleanType NeedPopAll = MagickFalse;
2912
2913
11.7k
  SkipSpaces (pfx);
2914
2915
11.7k
  if (!*pfx->pex) return MagickFalse;
2916
2917
11.7k
  if (!TranslateExpression (pfx, strLimit, chLimit, &NeedPopAll)) {
2918
3.13k
    return MagickFalse;
2919
3.13k
  }
2920
8.59k
  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
567
    ElementT * pel = &pfx->Elements[pfx->usedElements-1];
2926
567
    if (pel->do_push) pel->do_push = MagickFalse;
2927
567
  }
2928
2929
8.59k
  return MagickTrue;
2930
11.7k
}
2931
2932
static MagickBooleanType TranslateStatementList (FxInfo * pfx, const char * strLimit, char * chLimit)
2933
11.3k
{
2934
16.5k
#define MAX_SLIMIT 10
2935
11.3k
  char sLimits[MAX_SLIMIT];
2936
11.3k
  SkipSpaces (pfx);
2937
2938
11.3k
  if (!*pfx->pex) return MagickFalse;
2939
11.2k
  (void) CopyMagickString (sLimits, strLimit, MAX_SLIMIT-1);
2940
2941
11.2k
  if (strchr(strLimit,';')==NULL)
2942
5.31k
    (void) ConcatenateMagickString (sLimits, ";", MAX_SLIMIT);
2943
2944
11.7k
  for (;;) {
2945
11.7k
    if (!TranslateStatement (pfx, sLimits, chLimit)) return MagickFalse;
2946
2947
8.59k
    if (!*pfx->pex) break;
2948
2949
2.49k
    if (*chLimit != ';') {
2950
1.98k
      break;
2951
1.98k
    }
2952
2.49k
  }
2953
2954
8.08k
  if (pfx->exception->severity >= ErrorException)
2955
0
    return MagickFalse;
2956
2957
8.08k
  return MagickTrue;
2958
8.08k
}
2959
2960
/*--------------------------------------------------------------------
2961
   Run-time
2962
*/
2963
2964
static ChannelStatistics *CollectOneImgStats (FxInfo * pfx, Image * img)
2965
2.23k
{
2966
2.23k
  int ch;
2967
2.23k
  ChannelStatistics * cs = GetImageStatistics (img, pfx->exception);
2968
  /* Use RelinquishMagickMemory() somewhere. */
2969
2970
2.23k
  if (cs == (ChannelStatistics *) NULL)
2971
0
    return((ChannelStatistics *) NULL);
2972
2973
147k
  for (ch=0; ch <= (int) MaxPixelChannels; ch++) {
2974
145k
    cs[ch].mean *= QuantumScale;
2975
145k
    cs[ch].median *= QuantumScale;
2976
145k
    cs[ch].maxima *= QuantumScale;
2977
145k
    cs[ch].minima *= QuantumScale;
2978
145k
    cs[ch].standard_deviation *= QuantumScale;
2979
145k
  }
2980
2981
2.23k
  return cs;
2982
2.23k
}
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
450k
{
3013
450k
  if (pfxrt->usedValStack >=pfxrt->numValStack) {
3014
763
    (void) ThrowMagickException (
3015
763
      pfx->exception, GetMagickModule(), OptionError,
3016
763
      "ValStack overflow at addr=", "%i",
3017
763
      addr);
3018
763
    return MagickFalse;
3019
763
  }
3020
3021
450k
  pfxrt->ValStack[pfxrt->usedValStack++] = val;
3022
450k
  return MagickTrue;
3023
450k
}
3024
3025
static inline fxFltType PopVal (FxInfo * pfx, fxRtT * pfxrt, int addr)
3026
401k
{
3027
401k
  if (pfxrt->usedValStack <= 0) {
3028
193
    (void) ThrowMagickException (
3029
193
      pfx->exception, GetMagickModule(), OptionError,
3030
193
      "ValStack underflow at addr=", "%i",
3031
193
      addr);
3032
193
    return (fxFltType) 0;
3033
193
  }
3034
3035
401k
  return pfxrt->ValStack[--pfxrt->usedValStack];
3036
401k
}
3037
3038
static inline fxFltType ImageStat (
3039
  FxInfo * pfx, ssize_t ImgNum, PixelChannel channel, ImgAttrE ia)
3040
4.88k
{
3041
4.88k
  ChannelStatistics * cs = NULL;
3042
4.88k
  fxFltType ret = 0;
3043
4.88k
  MagickBooleanType NeedRelinq = MagickFalse;
3044
3045
4.88k
  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
4.88k
  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
4.88k
  } else if (pfx->NeedStats) {
3061
    /* If we need more than one statistic per pixel, this is inefficient. */
3062
1.57k
    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
1.57k
    cs = CollectOneImgStats (pfx, pfx->Images[ImgNum]);
3069
1.57k
    NeedRelinq = MagickTrue;
3070
1.57k
  }
3071
3072
4.88k
  switch (ia) {
3073
136
    case aDepth:
3074
136
      ret = (fxFltType) GetImageDepth (pfx->Images[ImgNum], pfx->exception);
3075
136
      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
315
    case aMean:
3088
315
      if ((cs != (ChannelStatistics *) NULL) && (channel >= 0))
3089
315
        ret = cs[channel].mean;
3090
315
      break;
3091
151
    case aMedian:
3092
151
      if ((cs != (ChannelStatistics *) NULL) && (channel >= 0))
3093
151
        ret = cs[channel].median;
3094
151
      break;
3095
32
    case aMinima:
3096
32
      if ((cs != (ChannelStatistics *) NULL) && (channel >= 0))
3097
32
        ret = cs[channel].minima;
3098
32
      break;
3099
0
    case aPage:
3100
      /* Do nothing */
3101
0
      break;
3102
334
    case aPageX:
3103
334
      ret = (fxFltType) pfx->Images[ImgNum]->page.x;
3104
334
      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
18
    case aSkewness:
3138
18
      if ((cs != (ChannelStatistics *) NULL) && (channel >= 0))
3139
18
        ret = cs[channel].skewness;
3140
18
      break;
3141
0
    case aStdDev:
3142
0
      if ((cs != (ChannelStatistics *) NULL) && (channel >= 0))
3143
0
        ret = cs[channel].standard_deviation;
3144
0
      break;
3145
378
    case aH:
3146
378
      ret = (fxFltType) pfx->Images[ImgNum]->rows;
3147
378
      break;
3148
558
    case aN:
3149
558
      ret = (fxFltType) pfx->ImgListLen;
3150
558
      break;
3151
521
    case aT: /* image index in list */
3152
521
      ret = (fxFltType) ImgNum;
3153
521
      break;
3154
790
    case aW:
3155
790
      ret = (fxFltType) pfx->Images[ImgNum]->columns;
3156
790
      break;
3157
658
    case aZ:
3158
658
      ret = (fxFltType) GetImageDepth (pfx->Images[ImgNum], pfx->exception);
3159
658
      break;
3160
0
    default:
3161
0
      (void) ThrowMagickException (pfx->exception,GetMagickModule(),OptionError,
3162
0
        "Unknown ia=","%i",ia);
3163
4.88k
  }
3164
4.88k
  if (NeedRelinq) cs = (ChannelStatistics *)RelinquishMagickMemory (cs);
3165
3166
4.88k
  return ret;
3167
4.88k
}
3168
3169
static inline fxFltType FxGcd (fxFltType x, fxFltType y, const size_t depth)
3170
26.9k
{
3171
26.9k
#define FxMaxFunctionDepth  200
3172
3173
26.9k
  if (x < y)
3174
13.2k
    return (FxGcd (y, x, depth+1));
3175
13.7k
  if ((fabs((double) y) < 0.001) || (depth >= FxMaxFunctionDepth))
3176
556
    return (x);
3177
13.2k
  return (FxGcd (y, x-y*floor((double) (x/y)), depth+1));
3178
13.7k
}
3179
3180
static inline ssize_t ChkImgNum (FxInfo * pfx, fxFltType f)
3181
/* Returns -1 if f is too large. */
3182
10.8k
{
3183
10.8k
  ssize_t i = (ssize_t) floor ((double) f + 0.5);
3184
10.8k
  if (i < 0) i += (ssize_t) pfx->ImgListLen;
3185
10.8k
  if (i < 0 || i >= (ssize_t) pfx->ImgListLen) {
3186
2.40k
    (void) ThrowMagickException (
3187
2.40k
      pfx->exception, GetMagickModule(), OptionError,
3188
2.40k
      "ImgNum", "%lu bad for ImgListLen %lu",
3189
2.40k
      (unsigned long) i, (unsigned long) pfx->ImgListLen);
3190
2.40k
    i = -1;
3191
2.40k
  }
3192
10.8k
  return i;
3193
10.8k
}
3194
3195
#define WHICH_ATTR_CHAN \
3196
8.41k
  (pel->channel_qual == NO_CHAN_QUAL) ? CompositePixelChannel : \
3197
8.41k
  (pel->channel_qual == THIS_CHANNEL) ? channel : pel->channel_qual
3198
3199
#define WHICH_NON_ATTR_CHAN \
3200
43.0k
  (pel->channel_qual == NO_CHAN_QUAL || \
3201
43.0k
   pel->channel_qual == THIS_CHANNEL || \
3202
43.0k
   pel->channel_qual == CompositePixelChannel \
3203
43.0k
  ) ? (channel == CompositePixelChannel ? RedPixelChannel: channel) \
3204
43.0k
    : 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
989
    (void) ThrowMagickException (
3224
989
      pfx->exception, GetMagickModule(), OptionError,
3225
989
      "GetHslFlt failure", "%lu %g,%g %i", (unsigned long) ImgNum,
3226
989
      (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
897
{
3241
897
  Image * img = pfx->Images[ImgNum];
3242
3243
897
  double hue=0, saturation=0, lightness=0;
3244
3245
897
  const Quantum * p = GetCacheViewVirtualPixels (pfx->Imgs[ImgNum].View, imgx, imgy, 1, 1, pfx->exception);
3246
897
  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
897
  ConvertRGBToHSL (
3255
897
    GetPixelRed (img, p), GetPixelGreen (img, p), GetPixelBlue (img, p),
3256
897
    &hue, &saturation, &lightness);
3257
3258
897
  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.26k
{
3267
2.26k
  Quantum
3268
2.26k
    quantum_pixel[MaxPixelChannels];
3269
3270
2.26k
  PixelInfo
3271
2.26k
    pixelinf;
3272
3273
2.26k
  Image * img = pfx->Images[ImgNum];
3274
3275
2.26k
  (void) GetPixelInfo (img, &pixelinf);
3276
3277
2.26k
  if (!InterpolatePixelInfo (img, pfx->Imgs[pfx->ImgNum].View, img->interpolate,
3278
2.26k
              (double) fx, (double) fy, &pixelinf, pfx->exception))
3279
486
  {
3280
486
    (void) ThrowMagickException (
3281
486
      pfx->exception, GetMagickModule(), OptionError,
3282
486
      "GetIntensity failure", "%lu %g,%g", (unsigned long) ImgNum,
3283
486
      (double) fx, (double) fy);
3284
486
  }
3285
3286
2.26k
  SetPixelViaPixelInfo (img, &pixelinf, quantum_pixel);
3287
2.26k
  return QuantumScale * GetPixelIntensity (img, quantum_pixel);
3288
2.26k
}
3289
3290
static MagickBooleanType ExecuteRPN (FxInfo * pfx, fxRtT * pfxrt, fxFltType *result,
3291
  const PixelChannel channel, const ssize_t imgx, const ssize_t imgy)
3292
8.03k
{
3293
8.03k
  const Quantum * p = pfxrt->thisPixel;
3294
8.03k
  fxFltType regA=0, regB=0, regC=0, regD=0, regE=0;
3295
8.03k
  Image * img = pfx->image;
3296
8.03k
  ChannelStatistics * cs = NULL;
3297
8.03k
  MagickBooleanType NeedRelinq = MagickFalse;
3298
8.03k
  double hue=0, saturation=0, lightness=0;
3299
8.03k
  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
8.03k
  if (!p) p = GetCacheViewVirtualPixels (
3306
8.03k
    pfx->Imgs[pfx->ImgNum].View, imgx, imgy, 1, 1, pfx->exception);
3307
3308
8.03k
  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
8.03k
  if (pfx->GotStats) {
3317
0
    cs = pfx->statistics[pfx->ImgNum];
3318
8.03k
  } else if (pfx->NeedStats) {
3319
656
    cs = CollectOneImgStats (pfx, pfx->Images[pfx->ImgNum]);
3320
656
    NeedRelinq = MagickTrue;
3321
656
  }
3322
3323
  /*  Following is only for expressions like "saturation", with no image specifier.
3324
  */
3325
8.03k
  if (pfx->NeedHsl) {
3326
309
    ConvertRGBToHSL (
3327
309
      GetPixelRed (img, p), GetPixelGreen (img, p), GetPixelBlue (img, p),
3328
309
      &hue, &saturation, &lightness);
3329
309
  }
3330
3331
579k
  for (i=0; i < pfx->usedElements; i++) {
3332
571k
    ElementT
3333
571k
      *pel;
3334
3335
571k
    if (i < 0) {
3336
0
      (void) ThrowMagickException (
3337
0
        pfx->exception, GetMagickModule(), OptionError,
3338
0
        "Bad run-time address", "%i", i);
3339
0
    }
3340
571k
    pel=&pfx->Elements[i];
3341
571k
    switch (pel->number_args) {
3342
298k
        case 0:
3343
298k
          break;
3344
165k
        case 1:
3345
165k
          regA = PopVal (pfx, pfxrt, i);
3346
165k
          break;
3347
100k
        case 2:
3348
100k
          regB = PopVal (pfx, pfxrt, i);
3349
100k
          regA = PopVal (pfx, pfxrt, i);
3350
100k
          break;
3351
5.60k
        case 3:
3352
5.60k
          regC = PopVal (pfx, pfxrt, i);
3353
5.60k
          regB = PopVal (pfx, pfxrt, i);
3354
5.60k
          regA = PopVal (pfx, pfxrt, i);
3355
5.60k
          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.27k
        case 5:
3363
2.27k
          regE = PopVal (pfx, pfxrt, i);
3364
2.27k
          regD = PopVal (pfx, pfxrt, i);
3365
2.27k
          regC = PopVal (pfx, pfxrt, i);
3366
2.27k
          regB = PopVal (pfx, pfxrt, i);
3367
2.27k
          regA = PopVal (pfx, pfxrt, i);
3368
2.27k
          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
571k
      }
3375
3376
571k
      switch (pel->operator_index) {
3377
435
        case oAddEq:
3378
435
          regA = (pfxrt->UserSymVals[pel->element_index] += regA);
3379
435
          break;
3380
486
        case oSubtractEq:
3381
486
          regA = (pfxrt->UserSymVals[pel->element_index] -= regA);
3382
486
          break;
3383
577
        case oMultiplyEq:
3384
577
          regA = (pfxrt->UserSymVals[pel->element_index] *= regA);
3385
577
          break;
3386
468
        case oDivideEq:
3387
468
          regA = (pfxrt->UserSymVals[pel->element_index] /= regA);
3388
468
          break;
3389
136
        case oPlusPlus:
3390
136
          regA = pfxrt->UserSymVals[pel->element_index]++;
3391
136
          break;
3392
140
        case oSubSub:
3393
140
          regA = pfxrt->UserSymVals[pel->element_index]--;
3394
140
          break;
3395
4.41k
        case oAdd:
3396
4.41k
          regA += regB;
3397
4.41k
          break;
3398
12.1k
        case oSubtract:
3399
12.1k
          regA -= regB;
3400
12.1k
          break;
3401
4.59k
        case oMultiply:
3402
4.59k
          regA *= regB;
3403
4.59k
          break;
3404
5.91k
        case oDivide:
3405
5.91k
          regA /= regB;
3406
5.91k
          break;
3407
9.38k
        case oModulus:
3408
9.38k
          regA = fmod ((double) regA, fabs(floor((double) regB+0.5)));
3409
9.38k
          break;
3410
2.69k
        case oUnaryPlus:
3411
          /* Do nothing. */
3412
2.69k
          break;
3413
14.6k
        case oUnaryMinus:
3414
14.6k
          regA = -regA;
3415
14.6k
          break;
3416
2.73k
        case oLshift:
3417
2.73k
          if (CastDoubleToSizeT((double) regB+0.5) >= (8*sizeof(size_t)))
3418
751
            {
3419
751
              (void) ThrowMagickException ( pfx->exception, GetMagickModule(),
3420
751
                OptionError, "undefined shift", "%g", (double) regB);
3421
751
              regA = (fxFltType) 0.0;
3422
751
              break;
3423
751
            }
3424
1.98k
          regA = (fxFltType) (CastDoubleToSizeT((double) regA+0.5) << CastDoubleToSizeT((double) regB+0.5));
3425
1.98k
          break;
3426
3.08k
        case oRshift:
3427
3.08k
          if (CastDoubleToSizeT((double) regB+0.5) >= (8*sizeof(size_t)))
3428
917
            {
3429
917
              (void) ThrowMagickException ( pfx->exception, GetMagickModule(),
3430
917
                OptionError, "undefined shift", "%g", (double) regB);
3431
917
              regA = (fxFltType) 0.0;
3432
917
              break;
3433
917
            }
3434
2.16k
          regA = (fxFltType) (CastDoubleToSizeT((double) regA+0.5) >> CastDoubleToSizeT((double) regB+0.5));
3435
2.16k
          break;
3436
554
        case oEq:
3437
554
          regA = fabs((double) (regA-regB)) < MagickEpsilon ? 1.0 : 0.0;
3438
554
          break;
3439
836
        case oNotEq:
3440
836
          regA = fabs((double) (regA-regB)) >= MagickEpsilon ? 1.0 : 0.0;
3441
836
          break;
3442
631
        case oLtEq:
3443
631
          regA = (regA <= regB) ? 1.0 : 0.0;
3444
631
          break;
3445
721
        case oGtEq:
3446
721
          regA = (regA >= regB) ? 1.0 : 0.0;
3447
721
          break;
3448
3.18k
        case oLt:
3449
3.18k
          regA = (regA < regB) ? 1.0 : 0.0;
3450
3.18k
          break;
3451
4.55k
        case oGt:
3452
4.55k
          regA = (regA > regB) ? 1.0 : 0.0;
3453
4.55k
          break;
3454
1.05k
        case oLogAnd:
3455
1.05k
          regA = (regA<=0) ? 0.0 : (regB > 0) ? 1.0 : 0.0;
3456
1.05k
          break;
3457
1.33k
        case oLogOr:
3458
1.33k
          regA = (regA>0) ? 1.0 : (regB > 0.0) ? 1.0 : 0.0;
3459
1.33k
          break;
3460
55.1k
        case oLogNot:
3461
55.1k
          regA = (regA==0) ? 1.0 : 0.0;
3462
55.1k
          break;
3463
5.25k
        case oBitAnd:
3464
5.25k
          regA = (fxFltType) (CastDoubleToSizeT((double) regA+0.5) & CastDoubleToSizeT((double) regB+0.5));
3465
5.25k
          break;
3466
7.88k
        case oBitOr:
3467
7.88k
          regA = (fxFltType) (CastDoubleToSizeT((double) regA+0.5) | CastDoubleToSizeT((double) regB+0.5));
3468
7.88k
          break;
3469
30.4k
        case oBitNot:
3470
30.4k
          {
3471
30.4k
            size_t
3472
30.4k
              new_value;
3473
3474
            /* Old fx doesn't add 0.5. */
3475
30.4k
            new_value=~CastDoubleToSizeT((double) regA+0.5);
3476
30.4k
            regA=(fxFltType) new_value;
3477
30.4k
            break;
3478
3.08k
          }
3479
3.56k
        case oPow:
3480
3.56k
          regA = pow ((double) regA, (double) regB);
3481
3.56k
          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
145k
        case oNull: {
3496
145k
          if (pel->type == etColourConstant) {
3497
2.58k
            switch (channel) { default:
3498
1.44k
              case (PixelChannel) 0:
3499
1.44k
                regA = pel->val;
3500
1.44k
                break;
3501
606
              case (PixelChannel) 1:
3502
606
                regA = pel->val1;
3503
606
                break;
3504
529
              case (PixelChannel) 2:
3505
529
                regA = pel->val2;
3506
529
                break;
3507
2.58k
            }
3508
143k
          } else {
3509
143k
            regA = pel->val;
3510
143k
          }
3511
145k
          break;
3512
145k
        }
3513
145k
        case fAbs:
3514
1.72k
          regA = fabs ((double) regA);
3515
1.72k
          break;
3516
0
#if defined(MAGICKCORE_HAVE_ACOSH)
3517
132
        case fAcosh:
3518
132
          regA = acosh ((double) regA);
3519
132
          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
659
        case fAiry:
3526
659
          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
659
          break;
3532
0
#endif
3533
329
        case fAlt:
3534
329
          regA = (fxFltType) (((ssize_t) regA) & 0x01 ? -1.0 : 1.0);
3535
329
          break;
3536
0
#if defined(MAGICKCORE_HAVE_ASINH)
3537
130
        case fAsinh:
3538
130
          regA = asinh ((double) regA);
3539
130
          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
273
        case fAtanh:
3546
273
          regA = atanh ((double) regA);
3547
273
          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.27k
        case fChannel:
3559
2.27k
          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
762
            default: regA = 0.0;
3566
2.27k
          }
3567
2.27k
          break;
3568
2.27k
        case fClamp:
3569
0
          if (regA < 0) regA = 0.0;
3570
0
          else if (regA > 1.0) regA = 1.0;
3571
0
          break;
3572
421
        case fCosh:
3573
421
          regA = cosh ((double) regA);
3574
421
          break;
3575
432
        case fCos:
3576
432
          regA = cos ((double) regA);
3577
432
          break;
3578
19
        case fDebug:
3579
          /* FIXME: debug() should give channel name. */
3580
3581
19
          (void) fprintf (stderr, "%s[%g,%g].[%i]: %s=%.*g\n",
3582
19
                   img->filename, (double) imgx, (double) imgy,
3583
19
                   channel, SetPtrShortExp (pfx, pel->exp_start, (size_t) (pel->exp_len+1)),
3584
19
                   pfx->precision, (double) regA);
3585
19
          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
132
        case fErf:
3591
132
          regA = erf ((double) regA);
3592
132
          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
696
        case fGcd:
3607
696
          if (!IsNaN((double) regA))
3608
556
            regA = FxGcd (regA, regB, 0);
3609
696
          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
648
        case fJinc:
3631
648
          if (regA==0) regA = 1.0;
3632
316
          else regA = 2.0 * __j1((double) (MagickPI*regA))/(MagickPI*regA);
3633
648
          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
603
        case fMax:
3648
603
          regA = (regA > regB) ? regA : regB;
3649
603
          break;
3650
749
        case fMin:
3651
749
          regA = (regA < regB) ? regA : regB;
3652
749
          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
772
        case fNot:
3661
772
          regA = (fxFltType) (regA < MagickEpsilon);
3662
772
          break;
3663
326
        case fPow:
3664
326
          regA = pow ((double) regA, (double) regB);
3665
326
          break;
3666
34
        case fRand: {
3667
#if defined(MAGICKCORE_OPENMP_SUPPORT)
3668
          #pragma omp critical (MagickCore_ExecuteRPN)
3669
#endif
3670
34
          regA = GetPseudoRandomValue (pfxrt->random_info);
3671
34
          break;
3672
2.27k
        }
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
331
        case fSinc:
3680
331
          regA = sin ((double) (MagickPI*regA)) / (MagickPI*regA);
3681
331
          break;
3682
340
        case fSinh:
3683
340
          regA = sinh ((double) regA);
3684
340
          break;
3685
508
        case fSin:
3686
508
          regA = sin ((double) regA);
3687
508
          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
406
        case fTanh:
3695
406
          regA = tanh ((double) regA);
3696
406
          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.19k
        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.19k
          ssize_t ImgNum = ChkImgNum (pfx, regA);
3715
5.19k
          if (ImgNum < 0) break;
3716
4.08k
          regA = (fxFltType) 0;
3717
4.08k
          if (ImgNum == 0) {
3718
4.08k
            Image * pimg = pfx->Images[0];
3719
4.08k
            if (pel->img_attr_qual == aNull) {
3720
1.44k
              if ((int) pel->channel_qual < 0) {
3721
1.12k
                if (pel->channel_qual == NO_CHAN_QUAL || pel->channel_qual == THIS_CHANNEL) {
3722
780
                  if (pfx->ImgNum==0) {
3723
780
                    regA = QuantumScale * (double) p[pimg->channel_map[WHICH_NON_ATTR_CHAN].offset];
3724
780
                  } 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
780
                } 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.12k
              } else {
3744
316
                if (pfx->ImgNum==0) {
3745
316
                  regA = QuantumScale * (double) p[pimg->channel_map[WHICH_NON_ATTR_CHAN].offset];
3746
316
                } 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
316
              }
3758
2.64k
            } else {
3759
              /* we have an image attribute */
3760
2.64k
              regA = ImageStat (pfx, 0, WHICH_ATTR_CHAN, pel->img_attr_qual);
3761
2.64k
            }
3762
4.08k
          } 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
3.74k
          break;
3794
4.08k
        }
3795
8.79k
        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.79k
          Image * pimg = pfx->Images[0];
3800
8.79k
          if ((int) pel->channel_qual < 0) {
3801
8.41k
            if (pel->channel_qual == NO_CHAN_QUAL || pel->channel_qual == THIS_CHANNEL) {
3802
3803
7.84k
              if (pfx->ImgNum==0) {
3804
7.84k
                regA = QuantumScale * (double) p[pimg->channel_map[WHICH_NON_ATTR_CHAN].offset];
3805
7.84k
              } 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.84k
            } else if (pel->channel_qual == HUE_CHANNEL || pel->channel_qual == SAT_CHANNEL ||
3818
432
                       pel->channel_qual == LIGHT_CHANNEL) {
3819
432
              regA = GetHslInt (pfx, 0, imgx, imgy, pel->channel_qual);
3820
432
              break;
3821
432
            } else if (pel->channel_qual == INTENSITY_CHANNEL) {
3822
145
              regA = GetIntensity (pfx, 0, (fxFltType) imgx, (fxFltType) imgy);
3823
145
            }
3824
8.41k
          } else {
3825
375
            if (pfx->ImgNum==0) {
3826
375
              regA = QuantumScale * (double) p[pimg->channel_map[WHICH_NON_ATTR_CHAN].offset];
3827
375
            } 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
375
          }
3839
8.36k
          break;
3840
8.79k
        }
3841
8.36k
        case fUP: {
3842
          /* 3 args are: ImgNum, x, y */
3843
5.60k
          ssize_t ImgNum = ChkImgNum (pfx, regA);
3844
5.60k
          fxFltType fx, fy;
3845
3846
5.60k
          if (ImgNum < 0) break;
3847
3848
4.30k
          if (pel->is_relative) {
3849
1.34k
            fx = imgx + regB;
3850
1.34k
            fy = imgy + regC;
3851
2.96k
          } else {
3852
2.96k
            fx = regB;
3853
2.96k
            fy = regC;
3854
2.96k
          }
3855
3856
4.30k
          if ((int) pel->channel_qual < 0) {
3857
3.69k
            if (pel->channel_qual == HUE_CHANNEL || pel->channel_qual == SAT_CHANNEL
3858
2.21k
             || pel->channel_qual == LIGHT_CHANNEL) {
3859
1.48k
              regA = GetHslFlt (pfx, ImgNum, fx, fy, pel->channel_qual);
3860
1.48k
              break;
3861
2.21k
            } else if (pel->channel_qual == INTENSITY_CHANNEL) {
3862
584
              regA = GetIntensity (pfx, ImgNum, fx, fy);
3863
584
              break;
3864
584
            }
3865
3.69k
          }
3866
3867
2.24k
          {
3868
2.24k
            double v;
3869
2.24k
            Image * imUP = pfx->Images[ImgNum];
3870
2.24k
            if (! InterpolatePixelChannel (imUP, pfx->Imgs[ImgNum].View, WHICH_NON_ATTR_CHAN,
3871
2.24k
                    imUP->interpolate, (double) fx, (double) fy, &v, pfx->exception))
3872
575
            {
3873
575
              (void) ThrowMagickException (
3874
575
                pfx->exception, GetMagickModule(), OptionError,
3875
575
                "fUP can't get interpolate", "%lu", (unsigned long) ImgNum);
3876
575
              break;
3877
575
            }
3878
1.66k
            regA = v * QuantumScale;
3879
1.66k
          }
3880
3881
0
          break;
3882
2.24k
        }
3883
10.0k
        case fS:
3884
10.0k
        case fV: {
3885
          /* No args. */
3886
10.0k
          ssize_t ImgNum = 1;
3887
10.0k
          if (pel->operator_index == fS) ImgNum = pfx->ImgNum;
3888
3889
10.0k
          if (pel->img_attr_qual == aNull) {
3890
7.76k
            const Quantum * pv = GetCacheViewVirtualPixels (
3891
7.76k
                                   pfx->Imgs[ImgNum].View, imgx, imgy, 1,1, pfx->exception);
3892
7.76k
            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
7.76k
            if ((int) pel->channel_qual < 0) {
3900
7.12k
              if (pel->channel_qual == HUE_CHANNEL || pel->channel_qual == SAT_CHANNEL ||
3901
6.98k
                  pel->channel_qual == LIGHT_CHANNEL) {
3902
137
                regA = GetHslInt (pfx, ImgNum, imgx, imgy, pel->channel_qual);
3903
137
                break;
3904
6.98k
              } else if (pel->channel_qual == INTENSITY_CHANNEL) {
3905
338
                regA = GetIntensity (pfx, ImgNum, (double) imgx, (double) imgy);
3906
338
                break;
3907
338
              }
3908
7.12k
            }
3909
3910
7.29k
            regA = QuantumScale * (double)
3911
7.29k
              pv[pfx->Images[ImgNum]->channel_map[WHICH_NON_ATTR_CHAN].offset];
3912
7.29k
          } else {
3913
2.24k
            regA = ImageStat (pfx, ImgNum, WHICH_ATTR_CHAN, pel->img_attr_qual);
3914
2.24k
          }
3915
3916
9.53k
          break;
3917
10.0k
        }
3918
25.2k
        case fP:
3919
25.7k
        case fSP:
3920
25.7k
        case fVP: {
3921
          /* 2 args are: x, y */
3922
25.7k
          fxFltType fx, fy;
3923
25.7k
          ssize_t ImgNum = pfx->ImgNum;
3924
25.7k
          if (pel->operator_index == fVP) ImgNum = 1;
3925
25.7k
          if (pel->is_relative) {
3926
17.6k
            fx = imgx + regA;
3927
17.6k
            fy = imgy + regB;
3928
17.6k
          } else {
3929
8.04k
            fx = regA;
3930
8.04k
            fy = regB;
3931
8.04k
          }
3932
25.7k
          if ((int) pel->channel_qual < 0) {
3933
25.2k
            if (pel->channel_qual == HUE_CHANNEL || pel->channel_qual == SAT_CHANNEL ||
3934
24.5k
                pel->channel_qual == LIGHT_CHANNEL) {
3935
753
              regA = GetHslFlt (pfx, ImgNum, fx, fy, pel->channel_qual);
3936
753
              break;
3937
24.5k
            } else if (pel->channel_qual == INTENSITY_CHANNEL) {
3938
798
              regA = GetIntensity (pfx, ImgNum, fx, fy);
3939
798
              break;
3940
798
            }
3941
25.2k
          }
3942
3943
24.1k
          {
3944
24.1k
            double v;
3945
3946
24.1k
            if (! InterpolatePixelChannel (pfx->Images[ImgNum], pfx->Imgs[ImgNum].View,
3947
24.1k
                                           WHICH_NON_ATTR_CHAN, pfx->Images[ImgNum]->interpolate,
3948
24.1k
                                           (double) fx, (double) fy, &v, pfx->exception)
3949
24.1k
                                          )
3950
3.65k
            {
3951
3.65k
              (void) ThrowMagickException (
3952
3.65k
                pfx->exception, GetMagickModule(), OptionError,
3953
3.65k
                "fSP or fVP can't get interp", "%lu", (unsigned long) ImgNum);
3954
3.65k
              break;
3955
3.65k
            }
3956
20.5k
            regA = v * (fxFltType)QuantumScale;
3957
20.5k
          }
3958
3959
0
          break;
3960
24.1k
        }
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.02k
        case aMaxima:
3974
1.02k
          if ((cs != (ChannelStatistics *) NULL) && (channel > 0))
3975
692
            regA = cs[WHICH_ATTR_CHAN].maxima;
3976
1.02k
          break;
3977
1.20k
        case aMean:
3978
1.20k
          if ((cs != (ChannelStatistics *) NULL) && (channel > 0))
3979
817
            regA = cs[WHICH_ATTR_CHAN].mean;
3980
1.20k
          break;
3981
1.13k
        case aMedian:
3982
1.13k
          if ((cs != (ChannelStatistics *) NULL) && (channel > 0))
3983
728
            regA = cs[WHICH_ATTR_CHAN].median;
3984
1.13k
          break;
3985
1.01k
        case aMinima:
3986
1.01k
          if ((cs != (ChannelStatistics *) NULL) && (channel > 0))
3987
669
            regA = cs[WHICH_ATTR_CHAN].minima;
3988
1.01k
          break;
3989
0
        case aPage:
3990
0
          break;
3991
442
        case aPageX:
3992
442
          regA = (fxFltType) img->page.x;
3993
442
          break;
3994
537
        case aPageY:
3995
537
          regA = (fxFltType) img->page.y;
3996
537
          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
307
        case aPrintsizeX:
4006
307
          regA = (fxFltType) MagickSafeReciprocal (img->resolution.x) * img->columns;
4007
307
          break;
4008
337
        case aPrintsizeY:
4009
337
          regA = (fxFltType) MagickSafeReciprocal (img->resolution.y) * img->rows;
4010
337
          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
905
        case aSkewness:
4023
905
          if ((cs != (ChannelStatistics *) NULL) && (channel > 0))
4024
620
            regA = cs[WHICH_ATTR_CHAN].skewness;
4025
905
          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
5.75k
        case aH: /* image->rows */
4031
5.75k
          regA = (fxFltType) img->rows;
4032
5.75k
          break;
4033
3.21k
        case aN: /* image list length */
4034
3.21k
          regA = (fxFltType) pfx->ImgListLen;
4035
3.21k
          break;
4036
6.52k
        case aT: /* image index in list */
4037
6.52k
          regA = (fxFltType) pfx->ImgNum;
4038
6.52k
          break;
4039
3.58k
        case aW: /* image->columns */
4040
3.58k
          regA = (fxFltType) img->columns;
4041
3.58k
          break;
4042
14.9k
        case aZ: /* image depth */
4043
14.9k
          regA = (fxFltType) GetImageDepth (img, pfx->exception);
4044
14.9k
          break;
4045
0
        case aNull:
4046
0
          break;
4047
521
        case sHue: /* of conversion to HSL */
4048
521
          regA = hue;
4049
521
          break;
4050
381
        case sIntensity:
4051
381
          regA = GetIntensity (pfx, pfx->ImgNum, (double) imgx, (double) imgy);
4052
381
          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
5.63k
        case sA: /* alpha */
4066
5.63k
          regA = QuantumScale * (double) GetPixelAlpha (img, p);
4067
5.63k
          break;
4068
4.32k
        case sB: /* blue */
4069
4.32k
          regA = QuantumScale * (double) GetPixelBlue (img, p);
4070
4.32k
          break;
4071
3.81k
        case sC: /* red (ie cyan) */
4072
3.81k
          regA = QuantumScale * (double) GetPixelCyan (img, p);
4073
3.81k
          break;
4074
7.08k
        case sG: /* green */
4075
7.08k
          regA = QuantumScale * (double) GetPixelGreen (img, p);
4076
7.08k
          break;
4077
2.71k
        case sI: /* current x-coordinate */
4078
2.71k
          regA = (fxFltType) imgx;
4079
2.71k
          break;
4080
1.90k
        case sJ: /* current y-coordinate */
4081
1.90k
          regA = (fxFltType) imgy;
4082
1.90k
          break;
4083
4.86k
        case sK: /* black of CMYK */
4084
4.86k
          regA = QuantumScale * (double) GetPixelBlack (img, p);
4085
4.86k
          break;
4086
2.21k
        case sM: /* green (ie magenta) */
4087
2.21k
          regA = QuantumScale * (double) GetPixelGreen (img, p);
4088
2.21k
          break;
4089
4.51k
        case sO: /* alpha */
4090
4.51k
          regA = QuantumScale * (double) GetPixelAlpha (img, p);
4091
4.51k
          break;
4092
6.68k
        case sR:
4093
6.68k
          regA = QuantumScale * (double) GetPixelRed (img, p);
4094
6.68k
          break;
4095
4.09k
        case sY:
4096
4.09k
          regA = QuantumScale * (double) GetPixelYellow (img, p);
4097
4.09k
          break;
4098
0
        case sNull:
4099
0
          break;
4100
4101
3.13k
        case rGoto:
4102
3.13k
          assert (pel->element_index >= 0);
4103
3.13k
          i = pel->element_index-1; /* -1 because 'for' loop will increment. */
4104
3.13k
          break;
4105
38.4k
        case rGotoChk:
4106
38.4k
          assert (pel->element_index >= 0);
4107
38.4k
          i = pel->element_index-1; /* -1 because 'for' loop will increment. */
4108
38.4k
          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
38.4k
          break;
4114
40.8k
        case rIfZeroGoto:
4115
40.8k
          assert (pel->element_index >= 0);
4116
40.8k
          if (fabs((double) regA) < MagickEpsilon) i = pel->element_index-1;
4117
40.8k
          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.57k
        case rCopyFrom:
4123
1.57k
          assert (pel->element_index >= 0);
4124
1.57k
          regA = pfxrt->UserSymVals[pel->element_index];
4125
1.57k
          break;
4126
3.77k
        case rCopyTo:
4127
3.77k
          assert (pel->element_index >= 0);
4128
3.77k
          pfxrt->UserSymVals[pel->element_index] = regA;
4129
3.77k
          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
571k
    }
4143
571k
    if (pel->do_push)
4144
450k
      if (!PushVal (pfx, pfxrt, regA, i)) break;
4145
571k
  }
4146
4147
8.03k
  if (pfxrt->usedValStack > 0) regA = PopVal (pfx, pfxrt, 9999);
4148
4149
8.03k
  *result = regA;
4150
4151
8.03k
  if (NeedRelinq) cs = (ChannelStatistics *)RelinquishMagickMemory (cs);
4152
4153
8.03k
  if (pfx->exception->severity >= ErrorException)
4154
2.31k
    return MagickFalse;
4155
4156
5.72k
  if (pfxrt->usedValStack != 0) {
4157
341
      (void) ThrowMagickException (
4158
341
        pfx->exception, GetMagickModule(), OptionError,
4159
341
        "ValStack not empty", "(%i)", pfxrt->usedValStack);
4160
341
    return MagickFalse;
4161
341
  }
4162
4163
5.38k
  return MagickTrue;
4164
5.72k
}
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
8.03k
{
4173
8.03k
  const int
4174
8.03k
    id = GetOpenMPThreadId();
4175
4176
8.03k
  fxFltType ret;
4177
4178
8.03k
  assert (pfx != NULL);
4179
8.03k
  assert (pfx->image != NULL);
4180
8.03k
  assert (pfx->Images != NULL);
4181
8.03k
  assert (pfx->Imgs != NULL);
4182
8.03k
  assert (pfx->fxrts != NULL);
4183
4184
8.03k
  pfx->fxrts[id].thisPixel = NULL;
4185
4186
4187
8.03k
  if (!ExecuteRPN (pfx, &pfx->fxrts[id], &ret, channel, x, y)) {
4188
2.65k
    (void) ThrowMagickException (
4189
2.65k
      exception, GetMagickModule(), OptionError,
4190
2.65k
      "ExecuteRPN failed", " ");
4191
2.65k
    return MagickFalse;
4192
2.65k
  }
4193
4194
5.38k
  *result = (double) ret;
4195
4196
5.38k
  return MagickTrue;
4197
8.03k
}
4198
4199
static FxInfo *AcquireFxInfoPrivate (const Image * images, const char * expression,
4200
  MagickBooleanType CalcAllStats, ExceptionInfo *exception)
4201
5.99k
{
4202
5.99k
  char chLimit;
4203
4204
5.99k
  FxInfo * pfx = (FxInfo*) AcquireCriticalMemory (sizeof (*pfx));
4205
4206
5.99k
  memset (pfx, 0, sizeof (*pfx));
4207
4208
5.99k
  if (!InitFx (pfx, images, CalcAllStats, exception)) {
4209
0
    pfx = (FxInfo*) RelinquishMagickMemory(pfx);
4210
0
    return NULL;
4211
0
  }
4212
4213
5.99k
  if (!BuildRPN (pfx)) {
4214
0
    (void) DeInitFx (pfx);
4215
0
    pfx = (FxInfo*) RelinquishMagickMemory(pfx);
4216
0
    return((FxInfo *) NULL);
4217
0
  }
4218
4219
5.99k
  if ((*expression == '@') && (strlen(expression) > 1))
4220
94
    pfx->expression=FileToString(expression,~0UL,exception);
4221
5.99k
  if (pfx->expression == (char *) NULL)
4222
5.96k
    pfx->expression=ConstantString(expression);
4223
5.99k
  pfx->pex = (char *) pfx->expression;
4224
4225
5.99k
  pfx->teDepth = 0;
4226
5.99k
  if (!TranslateStatementList (pfx, ";", &chLimit)) {
4227
1.77k
    (void) DestroyRPN (pfx);
4228
1.77k
    pfx->expression = DestroyString (pfx->expression);
4229
1.77k
    pfx->pex = NULL;
4230
1.77k
    (void) DeInitFx (pfx);
4231
1.77k
    pfx = (FxInfo*) RelinquishMagickMemory(pfx);
4232
1.77k
    return NULL;
4233
1.77k
  }
4234
4235
4.22k
  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.22k
  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.21k
  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.21k
  if (pfx->DebugOpt) {
4275
0
    DumpTables (stderr);
4276
0
    DumpUserSymbols (pfx, stderr);
4277
0
    (void) DumpRPN (pfx, stderr);
4278
0
  }
4279
4280
4.21k
  {
4281
4.21k
    size_t number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
4282
4.21k
    ssize_t t;
4283
4284
4.21k
    pfx->fxrts = (fxRtT *)AcquireQuantumMemory (number_threads, sizeof(fxRtT));
4285
4.21k
    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.43k
    for (t=0; t < (ssize_t) number_threads; t++) {
4298
4.21k
      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.21k
    }
4318
4.21k
  }
4319
4.21k
  return pfx;
4320
4.21k
}
4321
4322
FxInfo *AcquireFxInfo (const Image * images, const char * expression, ExceptionInfo *exception)
4323
5.99k
{
4324
5.99k
  return AcquireFxInfoPrivate (images, expression, MagickFalse, exception);
4325
5.99k
}
4326
4327
FxInfo *DestroyFxInfo (FxInfo * pfx)
4328
4.21k
{
4329
4.21k
  ssize_t t;
4330
4331
4.21k
  assert (pfx != NULL);
4332
4.21k
  assert (pfx->image != NULL);
4333
4.21k
  assert (pfx->Images != NULL);
4334
4.21k
  assert (pfx->Imgs != NULL);
4335
4.21k
  assert (pfx->fxrts != NULL);
4336
4337
8.43k
  for (t=0; t < (ssize_t) GetMagickResourceLimit(ThreadResource); t++) {
4338
4.21k
    DestroyFxRt (&pfx->fxrts[t]);
4339
4.21k
  }
4340
4.21k
  pfx->fxrts = (fxRtT *) RelinquishMagickMemory (pfx->fxrts);
4341
4342
4.21k
  DestroyRPN (pfx);
4343
4344
4.21k
  pfx->expression = DestroyString (pfx->expression);
4345
4.21k
  pfx->pex = NULL;
4346
4347
4.21k
  (void) DeInitFx (pfx);
4348
4349
4.21k
  pfx = (FxInfo*) RelinquishMagickMemory(pfx);
4350
4351
4.21k
  return NULL;
4352
4.21k
}
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
}