Coverage Report

Created: 2026-02-14 07:11

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