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 % Cristy %
17 % October 1996 %
18 % %
19 % %
20 % %
21 % Copyright 1999-2021 ImageMagick Studio LLC, a non-profit organization %
22 % dedicated to making software imaging solutions freely available. %
23 % %
24 % You may not use this file except in compliance with the License. You may %
25 % obtain a copy of the License at %
26 % %
27 % https://imagemagick.org/script/license.php %
28 % %
29 % Unless required by applicable law or agreed to in writing, software %
30 % distributed under the License is distributed on an "AS IS" BASIS, %
31 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32 % See the License for the specific language governing permissions and %
33 % limitations under the License. %
34 % %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36 %
37 %
38 %
39 */
40
41 /*
42 Include declarations.
43 */
44 #include "MagickCore/studio.h"
45 #include "MagickCore/accelerate-private.h"
46 #include "MagickCore/annotate.h"
47 #include "MagickCore/artifact.h"
48 #include "MagickCore/attribute.h"
49 #include "MagickCore/cache.h"
50 #include "MagickCore/cache-view.h"
51 #include "MagickCore/channel.h"
52 #include "MagickCore/color.h"
53 #include "MagickCore/color-private.h"
54 #include "MagickCore/colorspace-private.h"
55 #include "MagickCore/composite.h"
56 #include "MagickCore/decorate.h"
57 #include "MagickCore/distort.h"
58 #include "MagickCore/draw.h"
59 #include "MagickCore/effect.h"
60 #include "MagickCore/enhance.h"
61 #include "MagickCore/exception.h"
62 #include "MagickCore/exception-private.h"
63 #include "MagickCore/fx.h"
64 #include "MagickCore/fx-private.h"
65 #include "MagickCore/gem.h"
66 #include "MagickCore/gem-private.h"
67 #include "MagickCore/geometry.h"
68 #include "MagickCore/layer.h"
69 #include "MagickCore/list.h"
70 #include "MagickCore/log.h"
71 #include "MagickCore/image.h"
72 #include "MagickCore/image-private.h"
73 #include "MagickCore/magick.h"
74 #include "MagickCore/memory_.h"
75 #include "MagickCore/memory-private.h"
76 #include "MagickCore/monitor.h"
77 #include "MagickCore/monitor-private.h"
78 #include "MagickCore/option.h"
79 #include "MagickCore/pixel.h"
80 #include "MagickCore/pixel-accessor.h"
81 #include "MagickCore/property.h"
82 #include "MagickCore/quantum.h"
83 #include "MagickCore/quantum-private.h"
84 #include "MagickCore/random_.h"
85 #include "MagickCore/random-private.h"
86 #include "MagickCore/resample.h"
87 #include "MagickCore/resample-private.h"
88 #include "MagickCore/resize.h"
89 #include "MagickCore/resource_.h"
90 #include "MagickCore/splay-tree.h"
91 #include "MagickCore/statistic.h"
92 #include "MagickCore/string_.h"
93 #include "MagickCore/string-private.h"
94 #include "MagickCore/thread-private.h"
95 #include "MagickCore/threshold.h"
96 #include "MagickCore/token.h"
97 #include "MagickCore/transform.h"
98 #include "MagickCore/transform-private.h"
99 #include "MagickCore/utility.h"
100
101 /*
102 Typedef declarations.
103 */
104 typedef enum
105 {
106 BitwiseAndAssignmentOperator = 0xd9U,
107 BitwiseOrAssignmentOperator,
108 LeftShiftAssignmentOperator,
109 RightShiftAssignmentOperator,
110 PowerAssignmentOperator,
111 ModuloAssignmentOperator,
112 PlusAssignmentOperator,
113 SubtractAssignmentOperator,
114 MultiplyAssignmentOperator,
115 DivideAssignmentOperator,
116 IncrementAssignmentOperator,
117 DecrementAssignmentOperator,
118 LeftShiftOperator,
119 RightShiftOperator,
120 LessThanEqualOperator,
121 GreaterThanEqualOperator,
122 EqualOperator,
123 NotEqualOperator,
124 LogicalAndOperator,
125 LogicalOrOperator,
126 ExponentialNotation
127 } FxOperator;
128
129 struct _FxInfo
130 {
131 const Image
132 *images;
133
134 char
135 *expression;
136
137 FILE
138 *file;
139
140 SplayTreeInfo
141 *colors,
142 *symbols;
143
144 CacheView
145 **view;
146
147 RandomInfo
148 *random_info;
149
150 ExceptionInfo
151 *exception;
152 };
153
154 /*
155 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
156 % %
157 % %
158 % %
159 + A c q u i r e F x I n f o %
160 % %
161 % %
162 % %
163 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
164 %
165 % AcquireFxInfo() allocates the FxInfo structure.
166 %
167 % The format of the AcquireFxInfo method is:
168 %
169 % FxInfo *AcquireFxInfo(Image *images,const char *expression,
170 % ExceptionInfo *exception)
171 %
172 % A description of each parameter follows:
173 %
174 % o images: the image sequence.
175 %
176 % o expression: the expression.
177 %
178 % o exception: return any errors or warnings in this structure.
179 %
180 */
AcquireFxInfo(const Image * images,const char * expression,ExceptionInfo * exception)181 MagickPrivate FxInfo *AcquireFxInfo(const Image *images,const char *expression,
182 ExceptionInfo *exception)
183 {
184 const Image
185 *next;
186
187 FxInfo
188 *fx_info;
189
190 ssize_t
191 i;
192
193 unsigned char
194 fx_op[2];
195
196 fx_info=(FxInfo *) AcquireCriticalMemory(sizeof(*fx_info));
197 (void) memset(fx_info,0,sizeof(*fx_info));
198 fx_info->exception=AcquireExceptionInfo();
199 fx_info->images=images;
200 fx_info->colors=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
201 RelinquishMagickMemory);
202 fx_info->symbols=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
203 RelinquishMagickMemory);
204 fx_info->view=(CacheView **) AcquireQuantumMemory(GetImageListLength(
205 fx_info->images),sizeof(*fx_info->view));
206 if (fx_info->view == (CacheView **) NULL)
207 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
208 i=0;
209 next=GetFirstImageInList(fx_info->images);
210 for ( ; next != (Image *) NULL; next=next->next)
211 {
212 fx_info->view[i]=AcquireVirtualCacheView(next,exception);
213 i++;
214 }
215 fx_info->random_info=AcquireRandomInfo();
216 fx_info->expression=ConstantString(expression);
217 fx_info->file=stderr;
218 /*
219 Convert compound to simple operators.
220 */
221 fx_op[1]='\0';
222 *fx_op=(unsigned char) BitwiseAndAssignmentOperator;
223 (void) SubstituteString(&fx_info->expression,"&=",(char *) fx_op);
224 *fx_op=(unsigned char) BitwiseOrAssignmentOperator;
225 (void) SubstituteString(&fx_info->expression,"|=",(char *) fx_op);
226 *fx_op=(unsigned char) LeftShiftAssignmentOperator;
227 (void) SubstituteString(&fx_info->expression,"<<=",(char *) fx_op);
228 *fx_op=(unsigned char) RightShiftAssignmentOperator;
229 (void) SubstituteString(&fx_info->expression,">>=",(char *) fx_op);
230 *fx_op=(unsigned char) PowerAssignmentOperator;
231 (void) SubstituteString(&fx_info->expression,"^=",(char *) fx_op);
232 *fx_op=(unsigned char) ModuloAssignmentOperator;
233 (void) SubstituteString(&fx_info->expression,"%=",(char *) fx_op);
234 *fx_op=(unsigned char) PlusAssignmentOperator;
235 (void) SubstituteString(&fx_info->expression,"+=",(char *) fx_op);
236 *fx_op=(unsigned char) SubtractAssignmentOperator;
237 (void) SubstituteString(&fx_info->expression,"-=",(char *) fx_op);
238 *fx_op=(unsigned char) MultiplyAssignmentOperator;
239 (void) SubstituteString(&fx_info->expression,"*=",(char *) fx_op);
240 *fx_op=(unsigned char) DivideAssignmentOperator;
241 (void) SubstituteString(&fx_info->expression,"/=",(char *) fx_op);
242 *fx_op=(unsigned char) IncrementAssignmentOperator;
243 (void) SubstituteString(&fx_info->expression,"++",(char *) fx_op);
244 *fx_op=(unsigned char) DecrementAssignmentOperator;
245 (void) SubstituteString(&fx_info->expression,"--",(char *) fx_op);
246 *fx_op=(unsigned char) LeftShiftOperator;
247 (void) SubstituteString(&fx_info->expression,"<<",(char *) fx_op);
248 *fx_op=(unsigned char) RightShiftOperator;
249 (void) SubstituteString(&fx_info->expression,">>",(char *) fx_op);
250 *fx_op=(unsigned char) LessThanEqualOperator;
251 (void) SubstituteString(&fx_info->expression,"<=",(char *) fx_op);
252 *fx_op=(unsigned char) GreaterThanEqualOperator;
253 (void) SubstituteString(&fx_info->expression,">=",(char *) fx_op);
254 *fx_op=(unsigned char) EqualOperator;
255 (void) SubstituteString(&fx_info->expression,"==",(char *) fx_op);
256 *fx_op=(unsigned char) NotEqualOperator;
257 (void) SubstituteString(&fx_info->expression,"!=",(char *) fx_op);
258 *fx_op=(unsigned char) LogicalAndOperator;
259 (void) SubstituteString(&fx_info->expression,"&&",(char *) fx_op);
260 *fx_op=(unsigned char) LogicalOrOperator;
261 (void) SubstituteString(&fx_info->expression,"||",(char *) fx_op);
262 *fx_op=(unsigned char) ExponentialNotation;
263 (void) SubstituteString(&fx_info->expression,"**",(char *) fx_op);
264 /*
265 Force right-to-left associativity for unary negation.
266 */
267 (void) SubstituteString(&fx_info->expression,"-","-1.0*");
268 (void) SubstituteString(&fx_info->expression,"^-1.0*","^-");
269 (void) SubstituteString(&fx_info->expression,"E-1.0*","E-");
270 (void) SubstituteString(&fx_info->expression,"e-1.0*","e-");
271 (void) SubstituteString(&fx_info->expression," ",""); /* compact string */
272 return(fx_info);
273 }
274
275 /*
276 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
277 % %
278 % %
279 % %
280 + D e s t r o y F x I n f o %
281 % %
282 % %
283 % %
284 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
285 %
286 % DestroyFxInfo() deallocates memory associated with an FxInfo structure.
287 %
288 % The format of the DestroyFxInfo method is:
289 %
290 % ImageInfo *DestroyFxInfo(ImageInfo *fx_info)
291 %
292 % A description of each parameter follows:
293 %
294 % o fx_info: the fx info.
295 %
296 */
DestroyFxInfo(FxInfo * fx_info)297 MagickPrivate FxInfo *DestroyFxInfo(FxInfo *fx_info)
298 {
299 ssize_t
300 i;
301
302 fx_info->exception=DestroyExceptionInfo(fx_info->exception);
303 fx_info->expression=DestroyString(fx_info->expression);
304 fx_info->symbols=DestroySplayTree(fx_info->symbols);
305 fx_info->colors=DestroySplayTree(fx_info->colors);
306 for (i=(ssize_t) GetImageListLength(fx_info->images)-1; i >= 0; i--)
307 fx_info->view[i]=DestroyCacheView(fx_info->view[i]);
308 fx_info->view=(CacheView **) RelinquishMagickMemory(fx_info->view);
309 fx_info->random_info=DestroyRandomInfo(fx_info->random_info);
310 fx_info=(FxInfo *) RelinquishMagickMemory(fx_info);
311 return(fx_info);
312 }
313
314 /*
315 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
316 % %
317 % %
318 % %
319 + F x E v a l u a t e C h a n n e l E x p r e s s i o n %
320 % %
321 % %
322 % %
323 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
324 %
325 % FxEvaluateChannelExpression() evaluates an expression and returns the
326 % results.
327 %
328 % The format of the FxEvaluateExpression method is:
329 %
330 % double FxEvaluateChannelExpression(FxInfo *fx_info,
331 % const PixelChannel channel,const ssize_t x,const ssize_t y,
332 % double *alpha,Exceptioninfo *exception)
333 % double FxEvaluateExpression(FxInfo *fx_info,
334 % double *alpha,Exceptioninfo *exception)
335 %
336 % A description of each parameter follows:
337 %
338 % o fx_info: the fx info.
339 %
340 % o channel: the channel.
341 %
342 % o x,y: the pixel position.
343 %
344 % o alpha: the result.
345 %
346 % o exception: return any errors or warnings in this structure.
347 %
348 */
349
GetFxSymbolValue(FxInfo * magick_restrict fx_info,const char * symbol)350 static inline const double *GetFxSymbolValue(FxInfo *magick_restrict fx_info,
351 const char *symbol)
352 {
353 return((const double *) GetValueFromSplayTree(fx_info->symbols,symbol));
354 }
355
SetFxSymbolValue(FxInfo * magick_restrict fx_info,const char * magick_restrict symbol,double const value)356 static inline MagickBooleanType SetFxSymbolValue(
357 FxInfo *magick_restrict fx_info,const char *magick_restrict symbol,
358 double const value)
359 {
360 double
361 *object;
362
363 object=(double *) GetValueFromSplayTree(fx_info->symbols,symbol);
364 if (object != (double *) NULL)
365 {
366 *object=value;
367 return(MagickTrue);
368 }
369 object=(double *) AcquireMagickMemory(sizeof(*object));
370 if (object == (double *) NULL)
371 {
372 (void) ThrowMagickException(fx_info->exception,GetMagickModule(),
373 ResourceLimitError,"MemoryAllocationFailed","`%s'",
374 fx_info->images->filename);
375 return(MagickFalse);
376 }
377 *object=value;
378 return(AddValueToSplayTree(fx_info->symbols,ConstantString(symbol),object));
379 }
380
FxChannelStatistics(FxInfo * fx_info,Image * image,PixelChannel channel,const char * symbol,ExceptionInfo * exception)381 static double FxChannelStatistics(FxInfo *fx_info,Image *image,
382 PixelChannel channel,const char *symbol,ExceptionInfo *exception)
383 {
384 ChannelType
385 channel_mask;
386
387 char
388 key[MagickPathExtent];
389
390 const double
391 *value;
392
393 double
394 statistic;
395
396 const char
397 *p;
398
399 channel_mask=UndefinedChannel;
400 for (p=symbol; (*p != '.') && (*p != '\0'); p++) ;
401 if (*p == '.')
402 {
403 ssize_t
404 option;
405
406 option=ParseCommandOption(MagickPixelChannelOptions,MagickTrue,p+1);
407 if (option >= 0)
408 {
409 channel=(PixelChannel) option;
410 channel_mask=SetPixelChannelMask(image,(ChannelType)
411 (1UL << channel));
412 }
413 }
414 (void) FormatLocaleString(key,MagickPathExtent,"%p.%.20g.%s",(void *) image,
415 (double) channel,symbol);
416 value=GetFxSymbolValue(fx_info,key);
417 if (value != (const double *) NULL)
418 {
419 if (channel_mask != UndefinedChannel)
420 (void) SetPixelChannelMask(image,channel_mask);
421 return(QuantumScale*(*value));
422 }
423 statistic=0.0;
424 if (LocaleNCompare(symbol,"depth",5) == 0)
425 {
426 size_t
427 depth;
428
429 depth=GetImageDepth(image,exception);
430 statistic=(double) depth;
431 }
432 if (LocaleNCompare(symbol,"kurtosis",8) == 0)
433 {
434 double
435 kurtosis,
436 skewness;
437
438 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
439 statistic=kurtosis;
440 }
441 if (LocaleNCompare(symbol,"maxima",6) == 0)
442 {
443 double
444 maxima,
445 minima;
446
447 (void) GetImageRange(image,&minima,&maxima,exception);
448 statistic=maxima;
449 }
450 if (LocaleNCompare(symbol,"mean",4) == 0)
451 {
452 double
453 mean,
454 standard_deviation;
455
456 (void) GetImageMean(image,&mean,&standard_deviation,exception);
457 statistic=mean;
458 }
459 if (LocaleNCompare(symbol,"median",6) == 0)
460 {
461 double
462 median;
463
464 (void) GetImageMedian(image,&median,exception);
465 statistic=median;
466 }
467 if (LocaleNCompare(symbol,"minima",6) == 0)
468 {
469 double
470 maxima,
471 minima;
472
473 (void) GetImageRange(image,&minima,&maxima,exception);
474 statistic=minima;
475 }
476 if (LocaleNCompare(symbol,"skewness",8) == 0)
477 {
478 double
479 kurtosis,
480 skewness;
481
482 (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
483 statistic=skewness;
484 }
485 if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
486 {
487 double
488 mean,
489 standard_deviation;
490
491 (void) GetImageMean(image,&mean,&standard_deviation,exception);
492 statistic=standard_deviation;
493 }
494 if (channel_mask != UndefinedChannel)
495 (void) SetPixelChannelMask(image,channel_mask);
496 if (SetFxSymbolValue(fx_info,key,statistic) == MagickFalse)
497 return(0.0);
498 return(QuantumScale*statistic);
499 }
500
501 static double
502 FxEvaluateSubexpression(FxInfo *,const PixelChannel,const ssize_t,
503 const ssize_t,const char *,const size_t,double *,ExceptionInfo *);
504
IsFxFunction(const char * expression,const char * name,const size_t length)505 static inline MagickBooleanType IsFxFunction(const char *expression,
506 const char *name,const size_t length)
507 {
508 int
509 c;
510
511 size_t
512 i;
513
514 for (i=0; i <= length; i++)
515 if (expression[i] == '\0')
516 return(MagickFalse);
517 c=expression[length];
518 if ((LocaleNCompare(expression,name,length) == 0) &&
519 ((isspace((int) ((unsigned char) c)) == 0) || (c == '(')))
520 return(MagickTrue);
521 return(MagickFalse);
522 }
523
FxGCD(const double alpha,const double beta)524 static inline double FxGCD(const double alpha,const double beta)
525 {
526 if (alpha < beta)
527 return(FxGCD(beta,alpha));
528 if (fabs(beta) < 0.001)
529 return(alpha);
530 return(FxGCD(beta,alpha-beta*floor(alpha/beta)));
531 }
532
FxSubexpression(const char * expression,ExceptionInfo * exception)533 static inline const char *FxSubexpression(const char *expression,
534 ExceptionInfo *exception)
535 {
536 const char
537 *subexpression;
538
539 ssize_t
540 level;
541
542 level=0;
543 subexpression=expression;
544 while ((*subexpression != '\0') &&
545 ((level != 1) || (strchr(")",(int) *subexpression) == (char *) NULL)))
546 {
547 if (strchr("(",(int) *subexpression) != (char *) NULL)
548 level++;
549 else
550 if (strchr(")",(int) *subexpression) != (char *) NULL)
551 level--;
552 subexpression++;
553 }
554 if (*subexpression == '\0')
555 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
556 "UnbalancedParenthesis","`%s'",expression);
557 return(subexpression);
558 }
559
FxGetSymbol(FxInfo * fx_info,const PixelChannel channel,const ssize_t x,const ssize_t y,const char * expression,const size_t depth,ExceptionInfo * exception)560 static double FxGetSymbol(FxInfo *fx_info,const PixelChannel channel,
561 const ssize_t x,const ssize_t y,const char *expression,const size_t depth,
562 ExceptionInfo *exception)
563 {
564 char
565 *q,
566 symbol[MagickPathExtent];
567
568 const char
569 *artifact,
570 *p;
571
572 const double
573 *value;
574
575 double
576 alpha,
577 beta;
578
579 Image
580 *image;
581
582 MagickBooleanType
583 status;
584
585 PixelInfo
586 pixel;
587
588 PointInfo
589 point;
590
591 ssize_t
592 i;
593
594 size_t
595 level;
596
597 p=expression;
598 i=GetImageIndexInList(fx_info->images);
599 level=0;
600 point.x=(double) x;
601 point.y=(double) y;
602 if (isalpha((int) ((unsigned char) *(p+1))) == 0)
603 {
604 char
605 *subexpression;
606
607 subexpression=AcquireString(expression);
608 if (strchr("suv",(int) *p) != (char *) NULL)
609 {
610 switch (*p)
611 {
612 case 's':
613 default:
614 {
615 i=GetImageIndexInList(fx_info->images);
616 break;
617 }
618 case 'u': i=0; break;
619 case 'v': i=1; break;
620 }
621 p++;
622 if (*p == '[')
623 {
624 level++;
625 q=subexpression;
626 for (p++; *p != '\0'; )
627 {
628 if (*p == '[')
629 level++;
630 else
631 if (*p == ']')
632 {
633 level--;
634 if (level == 0)
635 break;
636 }
637 *q++=(*p++);
638 }
639 *q='\0';
640 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
641 depth,&beta,exception);
642 i=(ssize_t) alpha;
643 if (*p != '\0')
644 p++;
645 }
646 if (*p == '.')
647 p++;
648 }
649 if ((*p == 'p') && (isalpha((int) ((unsigned char) *(p+1))) == 0))
650 {
651 p++;
652 if (*p == '{')
653 {
654 level++;
655 q=subexpression;
656 for (p++; *p != '\0'; )
657 {
658 if (*p == '{')
659 level++;
660 else
661 if (*p == '}')
662 {
663 level--;
664 if (level == 0)
665 break;
666 }
667 *q++=(*p++);
668 }
669 *q='\0';
670 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
671 depth,&beta,exception);
672 point.x=alpha;
673 point.y=beta;
674 if (*p != '\0')
675 p++;
676 }
677 else
678 if (*p == '[')
679 {
680 level++;
681 q=subexpression;
682 for (p++; *p != '\0'; )
683 {
684 if (*p == '[')
685 level++;
686 else
687 if (*p == ']')
688 {
689 level--;
690 if (level == 0)
691 break;
692 }
693 *q++=(*p++);
694 }
695 *q='\0';
696 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
697 depth,&beta,exception);
698 point.x+=alpha;
699 point.y+=beta;
700 if (*p != '\0')
701 p++;
702 }
703 if (*p == '.')
704 p++;
705 }
706 subexpression=DestroyString(subexpression);
707 }
708 image=GetImageFromList(fx_info->images,i);
709 if (image == (Image *) NULL)
710 {
711 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
712 "NoSuchImage","`%s'",expression);
713 return(0.0);
714 }
715 i=GetImageIndexInList(image);
716 GetPixelInfo(image,&pixel);
717 status=InterpolatePixelInfo(image,fx_info->view[i],image->interpolate,
718 point.x,point.y,&pixel,exception);
719 (void) status;
720 if ((*p != '\0') && (*(p+1) != '\0') && (*(p+2) != '\0') &&
721 (LocaleCompare(p,"intensity") != 0) && (LocaleCompare(p,"luma") != 0) &&
722 (LocaleCompare(p,"luminance") != 0) && (LocaleCompare(p,"hue") != 0) &&
723 (LocaleCompare(p,"saturation") != 0) &&
724 (LocaleCompare(p,"lightness") != 0))
725 {
726 char
727 name[MagickPathExtent];
728
729 size_t
730 length;
731
732 (void) CopyMagickString(name,p,MagickPathExtent);
733 length=strlen(name);
734 for (q=name+length-1; q > name; q--)
735 {
736 if (*q == ')')
737 break;
738 if (*q == '.')
739 {
740 *q='\0';
741 break;
742 }
743 }
744 q=name;
745 if ((*q != '\0') && (*(q+1) != '\0') && (*(q+2) != '\0') &&
746 (GetFxSymbolValue(fx_info,name) == (const double *) NULL))
747 {
748 PixelInfo
749 *color;
750
751 color=(PixelInfo *) GetValueFromSplayTree(fx_info->colors,name);
752 if (color != (PixelInfo *) NULL)
753 {
754 pixel=(*color);
755 p+=length;
756 }
757 else
758 {
759 MagickBooleanType
760 status;
761
762 status=QueryColorCompliance(name,AllCompliance,&pixel,
763 fx_info->exception);
764 if (status != MagickFalse)
765 {
766 (void) AddValueToSplayTree(fx_info->colors,
767 ConstantString(name),ClonePixelInfo(&pixel));
768 p+=length;
769 }
770 }
771 }
772 }
773 (void) CopyMagickString(symbol,p,MagickPathExtent);
774 StripString(symbol);
775 if (*symbol == '\0')
776 {
777 switch (channel)
778 {
779 case RedPixelChannel: return(QuantumScale*pixel.red);
780 case GreenPixelChannel: return(QuantumScale*pixel.green);
781 case BluePixelChannel: return(QuantumScale*pixel.blue);
782 case BlackPixelChannel:
783 {
784 if (image->colorspace != CMYKColorspace)
785 {
786 (void) ThrowMagickException(exception,GetMagickModule(),
787 ImageError,"ColorSeparatedImageRequired","`%s'",
788 image->filename);
789 return(0.0);
790 }
791 return(QuantumScale*pixel.black);
792 }
793 case AlphaPixelChannel:
794 {
795 if (pixel.alpha_trait == UndefinedPixelTrait)
796 return(1.0);
797 alpha=(double) (QuantumScale*pixel.alpha);
798 return(alpha);
799 }
800 case CompositePixelChannel:
801 {
802 Quantum
803 quantum_pixel[MaxPixelChannels];
804
805 SetPixelViaPixelInfo(image,&pixel,quantum_pixel);
806 return(QuantumScale*GetPixelIntensity(image,quantum_pixel));
807 }
808 case IndexPixelChannel:
809 return(0.0);
810 default:
811 break;
812 }
813 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
814 "UnableToParseExpression","`%s'",p);
815 return(0.0);
816 }
817 switch (*symbol)
818 {
819 case 'A':
820 case 'a':
821 {
822 if (LocaleCompare(symbol,"a") == 0)
823 return((QuantumScale*pixel.alpha));
824 break;
825 }
826 case 'B':
827 case 'b':
828 {
829 if (LocaleCompare(symbol,"b") == 0)
830 return(QuantumScale*pixel.blue);
831 break;
832 }
833 case 'C':
834 case 'c':
835 {
836 if (IsFxFunction(symbol,"channel",7) != MagickFalse)
837 {
838 GeometryInfo
839 channel_info;
840
841 MagickStatusType
842 flags;
843
844 flags=ParseGeometry(symbol+7,&channel_info);
845 if (image->colorspace == CMYKColorspace)
846 switch (channel)
847 {
848 case CyanPixelChannel:
849 {
850 if ((flags & RhoValue) == 0)
851 return(0.0);
852 return(channel_info.rho);
853 }
854 case MagentaPixelChannel:
855 {
856 if ((flags & SigmaValue) == 0)
857 return(0.0);
858 return(channel_info.sigma);
859 }
860 case YellowPixelChannel:
861 {
862 if ((flags & XiValue) == 0)
863 return(0.0);
864 return(channel_info.xi);
865 }
866 case BlackPixelChannel:
867 {
868 if ((flags & PsiValue) == 0)
869 return(0.0);
870 return(channel_info.psi);
871 }
872 case AlphaPixelChannel:
873 {
874 if ((flags & ChiValue) == 0)
875 return(0.0);
876 return(channel_info.chi);
877 }
878 default:
879 return(0.0);
880 }
881 switch (channel)
882 {
883 case RedPixelChannel:
884 {
885 if ((flags & RhoValue) == 0)
886 return(0.0);
887 return(channel_info.rho);
888 }
889 case GreenPixelChannel:
890 {
891 if ((flags & SigmaValue) == 0)
892 return(0.0);
893 return(channel_info.sigma);
894 }
895 case BluePixelChannel:
896 {
897 if ((flags & XiValue) == 0)
898 return(0.0);
899 return(channel_info.xi);
900 }
901 case BlackPixelChannel:
902 {
903 if ((flags & ChiValue) == 0)
904 return(0.0);
905 return(channel_info.chi);
906 }
907 case AlphaPixelChannel:
908 {
909 if ((flags & PsiValue) == 0)
910 return(0.0);
911 return(channel_info.psi);
912 }
913 default:
914 return(0.0);
915 }
916 }
917 if (LocaleCompare(symbol,"c") == 0)
918 return(QuantumScale*pixel.red);
919 break;
920 }
921 case 'D':
922 case 'd':
923 {
924 if (LocaleNCompare(symbol,"depth",5) == 0)
925 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
926 break;
927 }
928 case 'E':
929 case 'e':
930 {
931 if (LocaleCompare(symbol,"extent") == 0)
932 {
933 if (image->extent != 0)
934 return((double) image->extent);
935 return((double) GetBlobSize(image));
936 }
937 break;
938 }
939 case 'G':
940 case 'g':
941 {
942 if (LocaleCompare(symbol,"g") == 0)
943 return(QuantumScale*pixel.green);
944 break;
945 }
946 case 'K':
947 case 'k':
948 {
949 if (LocaleNCompare(symbol,"kurtosis",8) == 0)
950 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
951 if (LocaleCompare(symbol,"k") == 0)
952 {
953 if (image->colorspace != CMYKColorspace)
954 {
955 (void) ThrowMagickException(exception,GetMagickModule(),
956 OptionError,"ColorSeparatedImageRequired","`%s'",
957 image->filename);
958 return(0.0);
959 }
960 return(QuantumScale*pixel.black);
961 }
962 break;
963 }
964 case 'H':
965 case 'h':
966 {
967 if (LocaleCompare(symbol,"h") == 0)
968 return((double) image->rows);
969 if (LocaleCompare(symbol,"hue") == 0)
970 {
971 double
972 hue,
973 lightness,
974 saturation;
975
976 ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
977 &lightness);
978 return(hue);
979 }
980 break;
981 }
982 case 'I':
983 case 'i':
984 {
985 if ((LocaleCompare(symbol,"image.depth") == 0) ||
986 (LocaleCompare(symbol,"image.minima") == 0) ||
987 (LocaleCompare(symbol,"image.maxima") == 0) ||
988 (LocaleCompare(symbol,"image.mean") == 0) ||
989 (LocaleCompare(symbol,"image.kurtosis") == 0) ||
990 (LocaleCompare(symbol,"image.skewness") == 0) ||
991 (LocaleCompare(symbol,"image.standard_deviation") == 0))
992 return(FxChannelStatistics(fx_info,image,channel,symbol+6,exception));
993 if (LocaleCompare(symbol,"image.resolution.x") == 0)
994 return(image->resolution.x);
995 if (LocaleCompare(symbol,"image.resolution.y") == 0)
996 return(image->resolution.y);
997 if (LocaleCompare(symbol,"intensity") == 0)
998 {
999 Quantum
1000 quantum_pixel[MaxPixelChannels];
1001
1002 SetPixelViaPixelInfo(image,&pixel,quantum_pixel);
1003 return(QuantumScale*GetPixelIntensity(image,quantum_pixel));
1004 }
1005 if (LocaleCompare(symbol,"i") == 0)
1006 return((double) x);
1007 break;
1008 }
1009 case 'J':
1010 case 'j':
1011 {
1012 if (LocaleCompare(symbol,"j") == 0)
1013 return((double) y);
1014 break;
1015 }
1016 case 'L':
1017 case 'l':
1018 {
1019 if (LocaleCompare(symbol,"lightness") == 0)
1020 {
1021 double
1022 hue,
1023 lightness,
1024 saturation;
1025
1026 ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
1027 &lightness);
1028 return(lightness);
1029 }
1030 if (LocaleCompare(symbol,"luma") == 0)
1031 {
1032 double
1033 luma;
1034
1035 luma=0.212656*pixel.red+0.715158*pixel.green+0.072186*pixel.blue;
1036 return(QuantumScale*luma);
1037 }
1038 if (LocaleCompare(symbol,"luminance") == 0)
1039 {
1040 double
1041 luminence;
1042
1043 luminence=0.212656*pixel.red+0.715158*pixel.green+0.072186*pixel.blue;
1044 return(QuantumScale*luminence);
1045 }
1046 break;
1047 }
1048 case 'M':
1049 case 'm':
1050 {
1051 if (LocaleNCompare(symbol,"maxima",6) == 0)
1052 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1053 if (LocaleNCompare(symbol,"mean",4) == 0)
1054 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1055 if (LocaleNCompare(symbol,"median",6) == 0)
1056 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1057 if (LocaleNCompare(symbol,"minima",6) == 0)
1058 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1059 if (LocaleCompare(symbol,"m") == 0)
1060 return(QuantumScale*pixel.green);
1061 break;
1062 }
1063 case 'N':
1064 case 'n':
1065 {
1066 if (LocaleCompare(symbol,"n") == 0)
1067 return((double) GetImageListLength(fx_info->images));
1068 break;
1069 }
1070 case 'O':
1071 case 'o':
1072 {
1073 if (LocaleCompare(symbol,"o") == 0)
1074 return(QuantumScale*pixel.alpha);
1075 break;
1076 }
1077 case 'P':
1078 case 'p':
1079 {
1080 if (LocaleCompare(symbol,"page.height") == 0)
1081 return((double) image->page.height);
1082 if (LocaleCompare(symbol,"page.width") == 0)
1083 return((double) image->page.width);
1084 if (LocaleCompare(symbol,"page.x") == 0)
1085 return((double) image->page.x);
1086 if (LocaleCompare(symbol,"page.y") == 0)
1087 return((double) image->page.y);
1088 if (LocaleCompare(symbol,"printsize.x") == 0)
1089 return(PerceptibleReciprocal(image->resolution.x)*image->columns);
1090 if (LocaleCompare(symbol,"printsize.y") == 0)
1091 return(PerceptibleReciprocal(image->resolution.y)*image->rows);
1092 break;
1093 }
1094 case 'Q':
1095 case 'q':
1096 {
1097 if (LocaleCompare(symbol,"quality") == 0)
1098 return((double) image->quality);
1099 break;
1100 }
1101 case 'R':
1102 case 'r':
1103 {
1104 if (LocaleCompare(symbol,"resolution.x") == 0)
1105 return(image->resolution.x);
1106 if (LocaleCompare(symbol,"resolution.y") == 0)
1107 return(image->resolution.y);
1108 if (LocaleCompare(symbol,"r") == 0)
1109 return(QuantumScale*pixel.red);
1110 break;
1111 }
1112 case 'S':
1113 case 's':
1114 {
1115 if (LocaleCompare(symbol,"saturation") == 0)
1116 {
1117 double
1118 hue,
1119 lightness,
1120 saturation;
1121
1122 ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
1123 &lightness);
1124 return(saturation);
1125 }
1126 if (LocaleNCompare(symbol,"skewness",8) == 0)
1127 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1128 if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1129 return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1130 break;
1131 }
1132 case 'T':
1133 case 't':
1134 {
1135 if (LocaleCompare(symbol,"t") == 0)
1136 return((double) GetImageIndexInList(fx_info->images));
1137 break;
1138 }
1139 case 'W':
1140 case 'w':
1141 {
1142 if (LocaleCompare(symbol,"w") == 0)
1143 return((double) image->columns);
1144 break;
1145 }
1146 case 'Y':
1147 case 'y':
1148 {
1149 if (LocaleCompare(symbol,"y") == 0)
1150 return(QuantumScale*pixel.blue);
1151 break;
1152 }
1153 case 'Z':
1154 case 'z':
1155 {
1156 if (LocaleCompare(symbol,"z") == 0)
1157 return((double) GetImageDepth(image,fx_info->exception));
1158 break;
1159 }
1160 default:
1161 break;
1162 }
1163 value=GetFxSymbolValue(fx_info,symbol);
1164 if (value != (const double *) NULL)
1165 return(*value);
1166 artifact=GetImageArtifact(image,symbol);
1167 if (artifact != (const char *) NULL)
1168 return(StringToDouble(artifact,(char **) NULL));
1169 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1170 "UndefinedVariable","`%s'",symbol);
1171 (void) SetFxSymbolValue(fx_info,symbol,0.0);
1172 return(0.0);
1173 }
1174
FxOperatorPrecedence(const char * expression,ExceptionInfo * exception)1175 static const char *FxOperatorPrecedence(const char *expression,
1176 ExceptionInfo *exception)
1177 {
1178 typedef enum
1179 {
1180 UndefinedPrecedence,
1181 NullPrecedence,
1182 BitwiseComplementPrecedence,
1183 ExponentPrecedence,
1184 ExponentialNotationPrecedence,
1185 MultiplyPrecedence,
1186 AdditionPrecedence,
1187 ShiftPrecedence,
1188 RelationalPrecedence,
1189 EquivalencyPrecedence,
1190 BitwiseAndPrecedence,
1191 BitwiseOrPrecedence,
1192 LogicalAndPrecedence,
1193 LogicalOrPrecedence,
1194 TernaryPrecedence,
1195 AssignmentPrecedence,
1196 CommaPrecedence,
1197 SeparatorPrecedence
1198 } FxPrecedence;
1199
1200 FxPrecedence
1201 precedence,
1202 target;
1203
1204 const char
1205 *subexpression;
1206
1207 int
1208 c;
1209
1210 size_t
1211 level;
1212
1213 c=(-1);
1214 level=0;
1215 subexpression=(const char *) NULL;
1216 target=NullPrecedence;
1217 while ((c != '\0') && (*expression != '\0'))
1218 {
1219 precedence=UndefinedPrecedence;
1220 if ((isspace((int) ((unsigned char) *expression)) != 0) || (c == (int) '@'))
1221 {
1222 expression++;
1223 continue;
1224 }
1225 switch (*expression)
1226 {
1227 case 'A':
1228 case 'a':
1229 {
1230 #if defined(MAGICKCORE_HAVE_ACOSH)
1231 if (IsFxFunction(expression,"acosh",5) != MagickFalse)
1232 {
1233 expression+=5;
1234 break;
1235 }
1236 #endif
1237 #if defined(MAGICKCORE_HAVE_ASINH)
1238 if (IsFxFunction(expression,"asinh",5) != MagickFalse)
1239 {
1240 expression+=5;
1241 break;
1242 }
1243 #endif
1244 #if defined(MAGICKCORE_HAVE_ATANH)
1245 if (IsFxFunction(expression,"atanh",5) != MagickFalse)
1246 {
1247 expression+=5;
1248 break;
1249 }
1250 #endif
1251 if (IsFxFunction(expression,"atan2",5) != MagickFalse)
1252 {
1253 expression+=5;
1254 break;
1255 }
1256 break;
1257 }
1258 case 'E':
1259 case 'e':
1260 {
1261 if ((isdigit((int) ((unsigned char) c)) != 0) &&
1262 ((LocaleNCompare(expression,"E+",2) == 0) ||
1263 (LocaleNCompare(expression,"E-",2) == 0)))
1264 {
1265 expression+=2; /* scientific notation */
1266 break;
1267 }
1268 }
1269 case 'J':
1270 case 'j':
1271 {
1272 if ((IsFxFunction(expression,"j0",2) != MagickFalse) ||
1273 (IsFxFunction(expression,"j1",2) != MagickFalse))
1274 {
1275 expression+=2;
1276 break;
1277 }
1278 break;
1279 }
1280 case '#':
1281 {
1282 while (isxdigit((int) ((unsigned char) *(expression+1))) != 0)
1283 expression++;
1284 break;
1285 }
1286 default:
1287 break;
1288 }
1289 if ((c == (int) '{') || (c == (int) '['))
1290 level++;
1291 else
1292 if ((c == (int) '}') || (c == (int) ']'))
1293 level--;
1294 if (level == 0)
1295 switch ((unsigned char) *expression)
1296 {
1297 case '~':
1298 case '!':
1299 {
1300 precedence=BitwiseComplementPrecedence;
1301 break;
1302 }
1303 case '^':
1304 case '@':
1305 {
1306 precedence=ExponentPrecedence;
1307 break;
1308 }
1309 default:
1310 {
1311 if (((c != 0) && ((isdigit((int) ((unsigned char) c)) != 0) ||
1312 (strchr(")",c) != (char *) NULL))) &&
1313 (((islower((int) ((unsigned char) *expression)) != 0) ||
1314 (strchr("(",(int) ((unsigned char) *expression)) != (char *) NULL)) ||
1315 ((isdigit((int) ((unsigned char) c)) == 0) &&
1316 (isdigit((int) ((unsigned char) *expression)) != 0))) &&
1317 (strchr("xy",(int) ((unsigned char) *expression)) == (char *) NULL))
1318 precedence=MultiplyPrecedence;
1319 break;
1320 }
1321 case '*':
1322 case '/':
1323 case '%':
1324 {
1325 precedence=MultiplyPrecedence;
1326 break;
1327 }
1328 case '+':
1329 case '-':
1330 {
1331 if ((strchr("(+-/*%:&^|<>~,",c) == (char *) NULL) ||
1332 (isalpha((int) ((unsigned char) c)) != 0))
1333 precedence=AdditionPrecedence;
1334 break;
1335 }
1336 case BitwiseAndAssignmentOperator:
1337 case BitwiseOrAssignmentOperator:
1338 case LeftShiftAssignmentOperator:
1339 case RightShiftAssignmentOperator:
1340 case PowerAssignmentOperator:
1341 case ModuloAssignmentOperator:
1342 case PlusAssignmentOperator:
1343 case SubtractAssignmentOperator:
1344 case MultiplyAssignmentOperator:
1345 case DivideAssignmentOperator:
1346 case IncrementAssignmentOperator:
1347 case DecrementAssignmentOperator:
1348 {
1349 precedence=AssignmentPrecedence;
1350 break;
1351 }
1352 case LeftShiftOperator:
1353 case RightShiftOperator:
1354 {
1355 precedence=ShiftPrecedence;
1356 break;
1357 }
1358 case '<':
1359 case LessThanEqualOperator:
1360 case GreaterThanEqualOperator:
1361 case '>':
1362 {
1363 precedence=RelationalPrecedence;
1364 break;
1365 }
1366 case EqualOperator:
1367 case NotEqualOperator:
1368 {
1369 precedence=EquivalencyPrecedence;
1370 break;
1371 }
1372 case '&':
1373 {
1374 precedence=BitwiseAndPrecedence;
1375 break;
1376 }
1377 case '|':
1378 {
1379 precedence=BitwiseOrPrecedence;
1380 break;
1381 }
1382 case LogicalAndOperator:
1383 {
1384 precedence=LogicalAndPrecedence;
1385 break;
1386 }
1387 case LogicalOrOperator:
1388 {
1389 precedence=LogicalOrPrecedence;
1390 break;
1391 }
1392 case ExponentialNotation:
1393 {
1394 precedence=ExponentialNotationPrecedence;
1395 break;
1396 }
1397 case ':':
1398 case '?':
1399 {
1400 precedence=TernaryPrecedence;
1401 break;
1402 }
1403 case '=':
1404 {
1405 precedence=AssignmentPrecedence;
1406 break;
1407 }
1408 case ',':
1409 {
1410 precedence=CommaPrecedence;
1411 break;
1412 }
1413 case ';':
1414 {
1415 precedence=SeparatorPrecedence;
1416 break;
1417 }
1418 }
1419 if ((precedence == BitwiseComplementPrecedence) ||
1420 (precedence == TernaryPrecedence) ||
1421 (precedence == AssignmentPrecedence))
1422 {
1423 if (precedence > target)
1424 {
1425 /*
1426 Right-to-left associativity.
1427 */
1428 target=precedence;
1429 subexpression=expression;
1430 }
1431 }
1432 else
1433 if (precedence >= target)
1434 {
1435 /*
1436 Left-to-right associativity.
1437 */
1438 target=precedence;
1439 subexpression=expression;
1440 }
1441 if (strchr("(",(int) *expression) != (char *) NULL)
1442 expression=FxSubexpression(expression,exception);
1443 c=(int) (*expression++);
1444 }
1445 return(subexpression);
1446 }
1447
FxEvaluateSubexpression(FxInfo * fx_info,const PixelChannel channel,const ssize_t x,const ssize_t y,const char * expression,const size_t depth,double * beta,ExceptionInfo * exception)1448 static double FxEvaluateSubexpression(FxInfo *fx_info,
1449 const PixelChannel channel,const ssize_t x,const ssize_t y,
1450 const char *expression,const size_t depth,double *beta,
1451 ExceptionInfo *exception)
1452 {
1453 #define FxMaxParenthesisDepth 58
1454 #define FxMaxSubexpressionDepth 200
1455 #define FxReturn(value) \
1456 { \
1457 subexpression=DestroyString(subexpression); \
1458 return(value); \
1459 }
1460 #define FxParseConditional(subexpression,sentinal,p,q) \
1461 { \
1462 p=subexpression; \
1463 for (q=(char *) p; (*q != (sentinal)) && (*q != '\0'); q++) \
1464 if (*q == '(') \
1465 { \
1466 for (q++; (*q != ')') && (*q != '\0'); q++); \
1467 if (*q == '\0') \
1468 break; \
1469 } \
1470 if (*q == '\0') \
1471 { \
1472 (void) ThrowMagickException(exception,GetMagickModule(), \
1473 OptionError,"UnableToParseExpression","`%s'",subexpression); \
1474 FxReturn(0.0); \
1475 } \
1476 if (strlen(q) == 1) \
1477 *(q+1)='\0'; \
1478 *q='\0'; \
1479 }
1480
1481 char
1482 *q,
1483 *subexpression;
1484
1485 double
1486 alpha,
1487 gamma,
1488 sans,
1489 value;
1490
1491 const char
1492 *p;
1493
1494 *beta=0.0;
1495 sans=0.0;
1496 subexpression=AcquireString(expression);
1497 *subexpression='\0';
1498 if (depth > FxMaxSubexpressionDepth)
1499 {
1500 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1501 "UnableToParseExpression","`%s'",expression);
1502 FxReturn(0.0);
1503 }
1504 if (exception->severity >= ErrorException)
1505 FxReturn(0.0);
1506 while (isspace((int) ((unsigned char) *expression)) != 0)
1507 expression++;
1508 if (*expression == '\0')
1509 FxReturn(0.0);
1510 p=FxOperatorPrecedence(expression,exception);
1511 if (p != (const char *) NULL)
1512 {
1513 (void) CopyMagickString(subexpression,expression,(size_t)
1514 (p-expression+1));
1515 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,depth+1,
1516 beta,exception);
1517 switch ((unsigned char) *p)
1518 {
1519 case '~':
1520 {
1521 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1522 exception);
1523 *beta=(double) (~(size_t) *beta);
1524 FxReturn(*beta);
1525 }
1526 case '!':
1527 {
1528 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1529 exception);
1530 FxReturn(*beta == 0.0 ? 1.0 : 0.0);
1531 }
1532 case '^':
1533 {
1534 *beta=pow(alpha,FxEvaluateSubexpression(fx_info,channel,x,y,++p,
1535 depth+1,beta,exception));
1536 FxReturn(*beta);
1537 }
1538 case '*':
1539 case ExponentialNotation:
1540 {
1541 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1542 exception);
1543 FxReturn(alpha*(*beta));
1544 }
1545 case '/':
1546 {
1547 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1548 exception);
1549 FxReturn(PerceptibleReciprocal(*beta)*alpha);
1550 }
1551 case '%':
1552 {
1553 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1554 exception);
1555 FxReturn(fmod(alpha,*beta));
1556 }
1557 case '+':
1558 {
1559 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1560 exception);
1561 FxReturn(alpha+(*beta));
1562 }
1563 case '-':
1564 {
1565 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1566 exception);
1567 FxReturn(alpha-(*beta));
1568 }
1569 case BitwiseAndAssignmentOperator:
1570 {
1571 q=subexpression;
1572 while (isalpha((int) ((unsigned char) *q)) != 0)
1573 q++;
1574 if (*q != '\0')
1575 {
1576 (void) ThrowMagickException(exception,GetMagickModule(),
1577 OptionError,"UnableToParseExpression","`%s'",subexpression);
1578 FxReturn(0.0);
1579 }
1580 ClearMagickException(exception);
1581 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1582 exception);
1583 value=(double) ((size_t) (alpha+0.5) & (size_t) (*beta+0.5));
1584 if (SetFxSymbolValue(fx_info,subexpression,value) == MagickFalse)
1585 return(0.0);
1586 FxReturn(*beta);
1587 }
1588 case BitwiseOrAssignmentOperator:
1589 {
1590 q=subexpression;
1591 while (isalpha((int) ((unsigned char) *q)) != 0)
1592 q++;
1593 if (*q != '\0')
1594 {
1595 (void) ThrowMagickException(exception,GetMagickModule(),
1596 OptionError,"UnableToParseExpression","`%s'",subexpression);
1597 FxReturn(0.0);
1598 }
1599 ClearMagickException(exception);
1600 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1601 exception);
1602 value=(double) ((size_t) (alpha+0.5) | (size_t) (*beta+0.5));
1603 if (SetFxSymbolValue(fx_info,subexpression,value) == MagickFalse)
1604 return(0.0);
1605 FxReturn(*beta);
1606 }
1607 case LeftShiftAssignmentOperator:
1608 {
1609 q=subexpression;
1610 while (isalpha((int) ((unsigned char) *q)) != 0)
1611 q++;
1612 if (*q != '\0')
1613 {
1614 (void) ThrowMagickException(exception,GetMagickModule(),
1615 OptionError,"UnableToParseExpression","`%s'",subexpression);
1616 FxReturn(0.0);
1617 }
1618 ClearMagickException(exception);
1619 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1620 exception);
1621 if ((size_t) (*beta+0.5) >= (8*sizeof(size_t)))
1622 {
1623 (void) ThrowMagickException(exception,GetMagickModule(),
1624 OptionError,"ShiftCountOverflow","`%s'",subexpression);
1625 FxReturn(0.0);
1626 }
1627 value=(double) ((size_t) (alpha+0.5) << (size_t) (*beta+0.5));
1628 if (SetFxSymbolValue(fx_info,subexpression,value) == MagickFalse)
1629 return(0.0);
1630 FxReturn(*beta);
1631 }
1632 case RightShiftAssignmentOperator:
1633 {
1634 q=subexpression;
1635 while (isalpha((int) ((unsigned char) *q)) != 0)
1636 q++;
1637 if (*q != '\0')
1638 {
1639 (void) ThrowMagickException(exception,GetMagickModule(),
1640 OptionError,"UnableToParseExpression","`%s'",subexpression);
1641 FxReturn(0.0);
1642 }
1643 ClearMagickException(exception);
1644 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1645 exception);
1646 if ((size_t) (*beta+0.5) >= (8*sizeof(size_t)))
1647 {
1648 (void) ThrowMagickException(exception,GetMagickModule(),
1649 OptionError,"ShiftCountOverflow","`%s'",subexpression);
1650 FxReturn(0.0);
1651 }
1652 value=(double) ((size_t) (alpha+0.5) >> (size_t) (*beta+0.5));
1653 if (SetFxSymbolValue(fx_info,subexpression,value) == MagickFalse)
1654 return(0.0);
1655 FxReturn(*beta);
1656 }
1657 case PowerAssignmentOperator:
1658 {
1659 q=subexpression;
1660 while (isalpha((int) ((unsigned char) *q)) != 0)
1661 q++;
1662 if (*q != '\0')
1663 {
1664 (void) ThrowMagickException(exception,GetMagickModule(),
1665 OptionError,"UnableToParseExpression","`%s'",subexpression);
1666 FxReturn(0.0);
1667 }
1668 ClearMagickException(exception);
1669 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1670 exception);
1671 value=pow(alpha,*beta);
1672 if (SetFxSymbolValue(fx_info,subexpression,value) == MagickFalse)
1673 return(0.0);
1674 FxReturn(*beta);
1675 }
1676 case ModuloAssignmentOperator:
1677 {
1678 q=subexpression;
1679 while (isalpha((int) ((unsigned char) *q)) != 0)
1680 q++;
1681 if (*q != '\0')
1682 {
1683 (void) ThrowMagickException(exception,GetMagickModule(),
1684 OptionError,"UnableToParseExpression","`%s'",subexpression);
1685 FxReturn(0.0);
1686 }
1687 ClearMagickException(exception);
1688 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1689 exception);
1690 value=fmod(alpha,*beta);
1691 if (SetFxSymbolValue(fx_info,subexpression,value) == MagickFalse)
1692 return(0.0);
1693 FxReturn(*beta);
1694 }
1695 case PlusAssignmentOperator:
1696 {
1697 q=subexpression;
1698 while (isalpha((int) ((unsigned char) *q)) != 0)
1699 q++;
1700 if (*q != '\0')
1701 {
1702 (void) ThrowMagickException(exception,GetMagickModule(),
1703 OptionError,"UnableToParseExpression","`%s'",subexpression);
1704 FxReturn(0.0);
1705 }
1706 ClearMagickException(exception);
1707 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1708 exception);
1709 value=alpha+(*beta);
1710 if (SetFxSymbolValue(fx_info,subexpression,value) == MagickFalse)
1711 return(0.0);
1712 FxReturn(*beta);
1713 }
1714 case SubtractAssignmentOperator:
1715 {
1716 q=subexpression;
1717 while (isalpha((int) ((unsigned char) *q)) != 0)
1718 q++;
1719 if (*q != '\0')
1720 {
1721 (void) ThrowMagickException(exception,GetMagickModule(),
1722 OptionError,"UnableToParseExpression","`%s'",subexpression);
1723 FxReturn(0.0);
1724 }
1725 ClearMagickException(exception);
1726 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1727 exception);
1728 value=alpha-(*beta);
1729 if (SetFxSymbolValue(fx_info,subexpression,value) == MagickFalse)
1730 return(0.0);
1731 FxReturn(*beta);
1732 }
1733 case MultiplyAssignmentOperator:
1734 {
1735 q=subexpression;
1736 while (isalpha((int) ((unsigned char) *q)) != 0)
1737 q++;
1738 if (*q != '\0')
1739 {
1740 (void) ThrowMagickException(exception,GetMagickModule(),
1741 OptionError,"UnableToParseExpression","`%s'",subexpression);
1742 FxReturn(0.0);
1743 }
1744 ClearMagickException(exception);
1745 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1746 exception);
1747 value=alpha*(*beta);
1748 if (SetFxSymbolValue(fx_info,subexpression,value) == MagickFalse)
1749 return(0.0);
1750 FxReturn(*beta);
1751 }
1752 case DivideAssignmentOperator:
1753 {
1754 q=subexpression;
1755 while (isalpha((int) ((unsigned char) *q)) != 0)
1756 q++;
1757 if (*q != '\0')
1758 {
1759 (void) ThrowMagickException(exception,GetMagickModule(),
1760 OptionError,"UnableToParseExpression","`%s'",subexpression);
1761 FxReturn(0.0);
1762 }
1763 ClearMagickException(exception);
1764 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1765 exception);
1766 value=alpha*PerceptibleReciprocal(*beta);
1767 if (SetFxSymbolValue(fx_info,subexpression,value) == MagickFalse)
1768 return(0.0);
1769 FxReturn(*beta);
1770 }
1771 case IncrementAssignmentOperator:
1772 {
1773 if (*subexpression == '\0')
1774 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1775 exception);
1776 value=alpha+1.0;
1777 if (*subexpression == '\0')
1778 {
1779 if (SetFxSymbolValue(fx_info,p,value) == MagickFalse)
1780 return(0.0);
1781 }
1782 else
1783 if (SetFxSymbolValue(fx_info,subexpression,value) == MagickFalse)
1784 return(0.0);
1785 FxReturn(*beta);
1786 }
1787 case DecrementAssignmentOperator:
1788 {
1789 if (*subexpression == '\0')
1790 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1791 exception);
1792 value=alpha-1.0;
1793 if (*subexpression == '\0')
1794 {
1795 if (SetFxSymbolValue(fx_info,p,value) == MagickFalse)
1796 return(0.0);
1797 }
1798 else
1799 if (SetFxSymbolValue(fx_info,subexpression,value) == MagickFalse)
1800 return(0.0);
1801 FxReturn(*beta);
1802 }
1803 case LeftShiftOperator:
1804 {
1805 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1806 exception);
1807 if ((size_t) (gamma+0.5) >= (8*sizeof(size_t)))
1808 {
1809 (void) ThrowMagickException(exception,GetMagickModule(),
1810 OptionError,"ShiftCountOverflow","`%s'",subexpression);
1811 FxReturn(0.0);
1812 }
1813 *beta=(double) ((size_t) (alpha+0.5) << (size_t) (gamma+0.5));
1814 FxReturn(*beta);
1815 }
1816 case RightShiftOperator:
1817 {
1818 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1819 exception);
1820 if ((size_t) (gamma+0.5) >= (8*sizeof(size_t)))
1821 {
1822 (void) ThrowMagickException(exception,GetMagickModule(),
1823 OptionError,"ShiftCountOverflow","`%s'",subexpression);
1824 FxReturn(0.0);
1825 }
1826 *beta=(double) ((size_t) (alpha+0.5) >> (size_t) (gamma+0.5));
1827 FxReturn(*beta);
1828 }
1829 case '<':
1830 {
1831 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1832 exception);
1833 FxReturn(alpha < *beta ? 1.0 : 0.0);
1834 }
1835 case LessThanEqualOperator:
1836 {
1837 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1838 exception);
1839 FxReturn(alpha <= *beta ? 1.0 : 0.0);
1840 }
1841 case '>':
1842 {
1843 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1844 exception);
1845 FxReturn(alpha > *beta ? 1.0 : 0.0);
1846 }
1847 case GreaterThanEqualOperator:
1848 {
1849 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1850 exception);
1851 FxReturn(alpha >= *beta ? 1.0 : 0.0);
1852 }
1853 case EqualOperator:
1854 {
1855 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1856 exception);
1857 FxReturn(fabs(alpha-(*beta)) < MagickEpsilon ? 1.0 : 0.0);
1858 }
1859 case NotEqualOperator:
1860 {
1861 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1862 exception);
1863 FxReturn(fabs(alpha-(*beta)) >= MagickEpsilon ? 1.0 : 0.0);
1864 }
1865 case '&':
1866 {
1867 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1868 exception);
1869 *beta=(double) ((size_t) (alpha+0.5) & (size_t) (gamma+0.5));
1870 FxReturn(*beta);
1871 }
1872 case '|':
1873 {
1874 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1875 exception);
1876 *beta=(double) ((size_t) (alpha+0.5) | (size_t) (gamma+0.5));
1877 FxReturn(*beta);
1878 }
1879 case LogicalAndOperator:
1880 {
1881 p++;
1882 if (alpha <= 0.0)
1883 {
1884 *beta=0.0;
1885 FxReturn(*beta);
1886 }
1887 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,depth+1,beta,
1888 exception);
1889 *beta=(gamma > 0.0) ? 1.0 : 0.0;
1890 FxReturn(*beta);
1891 }
1892 case LogicalOrOperator:
1893 {
1894 p++;
1895 if (alpha > 0.0)
1896 {
1897 *beta=1.0;
1898 FxReturn(*beta);
1899 }
1900 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,depth+1,beta,
1901 exception);
1902 *beta=(gamma > 0.0) ? 1.0 : 0.0;
1903 FxReturn(*beta);
1904 }
1905 case '?':
1906 {
1907 (void) CopyMagickString(subexpression,++p,MagickPathExtent-1);
1908 FxParseConditional(subexpression,':',p,q);
1909 if (fabs(alpha) >= MagickEpsilon)
1910 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,depth+1,beta,
1911 exception);
1912 else
1913 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,q+1,depth+1,beta,
1914 exception);
1915 FxReturn(gamma);
1916 }
1917 case '=':
1918 {
1919 q=subexpression;
1920 while (isalpha((int) ((unsigned char) *q)) != 0)
1921 q++;
1922 if (*q != '\0')
1923 {
1924 (void) ThrowMagickException(exception,GetMagickModule(),
1925 OptionError,"UnableToParseExpression","`%s'",subexpression);
1926 FxReturn(0.0);
1927 }
1928 ClearMagickException(exception);
1929 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1930 exception);
1931 value=(*beta);
1932 if (SetFxSymbolValue(fx_info,subexpression,value) == MagickFalse)
1933 return(0.0);
1934 FxReturn(*beta);
1935 }
1936 case ',':
1937 {
1938 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1939 exception);
1940 FxReturn(alpha);
1941 }
1942 case ';':
1943 {
1944 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1945 exception);
1946 FxReturn(*beta);
1947 }
1948 default:
1949 {
1950 gamma=alpha*FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,
1951 beta,exception);
1952 FxReturn(gamma);
1953 }
1954 }
1955 }
1956 if (strchr("(",(int) *expression) != (char *) NULL)
1957 {
1958 size_t
1959 length;
1960
1961 if (depth >= FxMaxParenthesisDepth)
1962 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1963 "ParenthesisNestedTooDeeply","`%s'",expression);
1964 length=CopyMagickString(subexpression,expression+1,MagickPathExtent);
1965 if (length != 0)
1966 subexpression[length-1]='\0';
1967 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,depth+1,
1968 beta,exception);
1969 FxReturn(gamma);
1970 }
1971 switch (*expression)
1972 {
1973 case '+':
1974 {
1975 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,depth+1,
1976 beta,exception);
1977 FxReturn(1.0*gamma);
1978 }
1979 case '-':
1980 {
1981 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,depth+1,
1982 beta,exception);
1983 FxReturn(-1.0*gamma);
1984 }
1985 case '~':
1986 {
1987 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,depth+1,
1988 beta,exception);
1989 FxReturn((double) (~(size_t) (gamma+0.5)));
1990 }
1991 case 'A':
1992 case 'a':
1993 {
1994 if (IsFxFunction(expression,"abs",3) != MagickFalse)
1995 {
1996 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
1997 depth+1,beta,exception);
1998 FxReturn(fabs(alpha));
1999 }
2000 #if defined(MAGICKCORE_HAVE_ACOSH)
2001 if (IsFxFunction(expression,"acosh",5) != MagickFalse)
2002 {
2003 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2004 depth+1,beta,exception);
2005 FxReturn(acosh(alpha));
2006 }
2007 #endif
2008 if (IsFxFunction(expression,"acos",4) != MagickFalse)
2009 {
2010 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2011 depth+1,beta,exception);
2012 FxReturn(acos(alpha));
2013 }
2014 #if defined(MAGICKCORE_HAVE_J1)
2015 if (IsFxFunction(expression,"airy",4) != MagickFalse)
2016 {
2017 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2018 depth+1,beta,exception);
2019 if (alpha == 0.0)
2020 FxReturn(1.0);
2021 gamma=2.0*j1((MagickPI*alpha))/(MagickPI*alpha);
2022 FxReturn(gamma*gamma);
2023 }
2024 #endif
2025 #if defined(MAGICKCORE_HAVE_ASINH)
2026 if (IsFxFunction(expression,"asinh",5) != MagickFalse)
2027 {
2028 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2029 depth+1,beta,exception);
2030 FxReturn(asinh(alpha));
2031 }
2032 #endif
2033 if (IsFxFunction(expression,"asin",4) != MagickFalse)
2034 {
2035 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2036 depth+1,beta,exception);
2037 FxReturn(asin(alpha));
2038 }
2039 if (IsFxFunction(expression,"alt",3) != MagickFalse)
2040 {
2041 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2042 depth+1,beta,exception);
2043 FxReturn(((ssize_t) alpha) & 0x01 ? -1.0 : 1.0);
2044 }
2045 if (IsFxFunction(expression,"atan2",5) != MagickFalse)
2046 {
2047 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2048 depth+1,beta,exception);
2049 FxReturn(atan2(alpha,*beta));
2050 }
2051 #if defined(MAGICKCORE_HAVE_ATANH)
2052 if (IsFxFunction(expression,"atanh",5) != MagickFalse)
2053 {
2054 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2055 depth+1,beta,exception);
2056 FxReturn(atanh(alpha));
2057 }
2058 #endif
2059 if (IsFxFunction(expression,"atan",4) != MagickFalse)
2060 {
2061 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2062 depth+1,beta,exception);
2063 FxReturn(atan(alpha));
2064 }
2065 if (LocaleCompare(expression,"a") == 0)
2066 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2067 break;
2068 }
2069 case 'B':
2070 case 'b':
2071 {
2072 if (LocaleCompare(expression,"b") == 0)
2073 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2074 break;
2075 }
2076 case 'C':
2077 case 'c':
2078 {
2079 if (IsFxFunction(expression,"ceil",4) != MagickFalse)
2080 {
2081 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2082 depth+1,beta,exception);
2083 FxReturn(ceil(alpha));
2084 }
2085 if (IsFxFunction(expression,"clamp",5) != MagickFalse)
2086 {
2087 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2088 depth+1,beta,exception);
2089 if (alpha < 0.0)
2090 FxReturn(0.0);
2091 if (alpha > 1.0)
2092 FxReturn(1.0);
2093 FxReturn(alpha);
2094 }
2095 if (IsFxFunction(expression,"cosh",4) != MagickFalse)
2096 {
2097 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2098 depth+1,beta,exception);
2099 FxReturn(cosh(alpha));
2100 }
2101 if (IsFxFunction(expression,"cos",3) != MagickFalse)
2102 {
2103 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2104 depth+1,beta,exception);
2105 FxReturn(cos(alpha));
2106 }
2107 if (LocaleCompare(expression,"c") == 0)
2108 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2109 break;
2110 }
2111 case 'D':
2112 case 'd':
2113 {
2114 if (IsFxFunction(expression,"debug",5) != MagickFalse)
2115 {
2116 const char
2117 *type;
2118
2119 size_t
2120 length;
2121
2122 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2123 depth+1,beta,exception);
2124 switch (fx_info->images->colorspace)
2125 {
2126 case CMYKColorspace:
2127 {
2128 switch (channel)
2129 {
2130 case CyanPixelChannel: type="cyan"; break;
2131 case MagentaPixelChannel: type="magenta"; break;
2132 case YellowPixelChannel: type="yellow"; break;
2133 case AlphaPixelChannel: type="alpha"; break;
2134 case BlackPixelChannel: type="black"; break;
2135 default: type="unknown"; break;
2136 }
2137 break;
2138 }
2139 case GRAYColorspace:
2140 {
2141 switch (channel)
2142 {
2143 case RedPixelChannel: type="gray"; break;
2144 case AlphaPixelChannel: type="alpha"; break;
2145 default: type="unknown"; break;
2146 }
2147 break;
2148 }
2149 default:
2150 {
2151 switch (channel)
2152 {
2153 case RedPixelChannel: type="red"; break;
2154 case GreenPixelChannel: type="green"; break;
2155 case BluePixelChannel: type="blue"; break;
2156 case AlphaPixelChannel: type="alpha"; break;
2157 default: type="unknown"; break;
2158 }
2159 break;
2160 }
2161 }
2162 *subexpression='\0';
2163 length=1;
2164 if (strlen(expression) > 6)
2165 length=CopyMagickString(subexpression,expression+6,
2166 MagickPathExtent);
2167 if (length != 0)
2168 subexpression[length-1]='\0';
2169 if (fx_info->file != (FILE *) NULL)
2170 (void) FormatLocaleFile(fx_info->file,"%s[%.20g,%.20g].%s: "
2171 "%s=%.*g\n",fx_info->images->filename,(double) x,(double) y,type,
2172 subexpression,GetMagickPrecision(),alpha);
2173 FxReturn(alpha);
2174 }
2175 if (IsFxFunction(expression,"do",2) != MagickFalse)
2176 {
2177 size_t
2178 length;
2179
2180 /*
2181 Parse do(expression,condition test).
2182 */
2183 length=CopyMagickString(subexpression,expression+3,
2184 MagickPathExtent-1);
2185 if (length != 0)
2186 subexpression[length-1]='\0';
2187 FxParseConditional(subexpression,',',p,q);
2188 for (alpha=0.0; ; )
2189 {
2190 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,q+1,depth+1,beta,
2191 exception);
2192 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,depth+1,&sans,
2193 exception);
2194 if (fabs(gamma) < MagickEpsilon)
2195 break;
2196 }
2197 FxReturn(alpha);
2198 }
2199 if (IsFxFunction(expression,"drc",3) != MagickFalse)
2200 {
2201 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2202 depth+1,beta,exception);
2203 FxReturn((alpha/(*beta*(alpha-1.0)+1.0)));
2204 }
2205 break;
2206 }
2207 case 'E':
2208 case 'e':
2209 {
2210 if (LocaleCompare(expression,"epsilon") == 0)
2211 FxReturn(MagickEpsilon);
2212 #if defined(MAGICKCORE_HAVE_ERF)
2213 if (IsFxFunction(expression,"erf",3) != MagickFalse)
2214 {
2215 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2216 depth+1,beta,exception);
2217 FxReturn(erf(alpha));
2218 }
2219 #endif
2220 if (IsFxFunction(expression,"exp",3) != MagickFalse)
2221 {
2222 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2223 depth+1,beta,exception);
2224 FxReturn(exp(alpha));
2225 }
2226 if (LocaleCompare(expression,"e") == 0)
2227 FxReturn(2.7182818284590452354);
2228 break;
2229 }
2230 case 'F':
2231 case 'f':
2232 {
2233 if (IsFxFunction(expression,"floor",5) != MagickFalse)
2234 {
2235 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2236 depth+1,beta,exception);
2237 FxReturn(floor(alpha));
2238 }
2239 if (IsFxFunction(expression,"for",3) != MagickFalse)
2240 {
2241 double
2242 sans = 0.0;
2243
2244 size_t
2245 length;
2246
2247 /*
2248 Parse for(initialization, condition test, expression).
2249 */
2250 length=CopyMagickString(subexpression,expression+4,
2251 MagickPathExtent-1);
2252 if (length != 0)
2253 subexpression[length-1]='\0';
2254 FxParseConditional(subexpression,',',p,q);
2255 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,p,depth+1,&sans,
2256 exception);
2257 (void) CopyMagickString(subexpression,q+1,MagickPathExtent-1);
2258 FxParseConditional(subexpression,',',p,q);
2259 for (alpha=0.0; ; )
2260 {
2261 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,depth+1,&sans,
2262 exception);
2263 if (fabs(gamma) < MagickEpsilon)
2264 break;
2265 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,q+1,depth+1,beta,
2266 exception);
2267 }
2268 FxReturn(alpha);
2269 }
2270 break;
2271 }
2272 case 'G':
2273 case 'g':
2274 {
2275 if (IsFxFunction(expression,"gauss",5) != MagickFalse)
2276 {
2277 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2278 depth+1,beta,exception);
2279 FxReturn(exp((-alpha*alpha/2.0))/sqrt(2.0*MagickPI));
2280 }
2281 if (IsFxFunction(expression,"gcd",3) != MagickFalse)
2282 {
2283 double
2284 gcd;
2285
2286 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2287 depth+1,beta,exception);
2288 if (IsNaN(alpha))
2289 FxReturn(alpha);
2290 gcd=FxGCD(alpha,*beta);
2291 FxReturn(gcd);
2292 }
2293 if (LocaleCompare(expression,"g") == 0)
2294 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2295 break;
2296 }
2297 case 'H':
2298 case 'h':
2299 {
2300 if (LocaleCompare(expression,"h") == 0)
2301 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2302 if (LocaleCompare(expression,"hue") == 0)
2303 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2304 if (IsFxFunction(expression,"hypot",5) != MagickFalse)
2305 {
2306 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2307 depth+1,beta,exception);
2308 FxReturn(hypot(alpha,*beta));
2309 }
2310 break;
2311 }
2312 case 'K':
2313 case 'k':
2314 {
2315 if (LocaleCompare(expression,"k") == 0)
2316 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2317 break;
2318 }
2319 case 'I':
2320 case 'i':
2321 {
2322 if (IsFxFunction(expression,"if",2) != MagickFalse)
2323 {
2324 double
2325 sans = 0.0;
2326
2327 size_t
2328 length;
2329
2330 /*
2331 Parse if(condition test, true-expression, false-expression).
2332 */
2333 length=CopyMagickString(subexpression,expression+3,
2334 MagickPathExtent-1);
2335 if (length != 0)
2336 subexpression[length-1]='\0';
2337 FxParseConditional(subexpression,',',p,q);
2338 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,p,depth+1,&sans,
2339 exception);
2340 (void) CopyMagickString(subexpression,q+1,MagickPathExtent-1);
2341 FxParseConditional(subexpression,',',p,q);
2342 if (fabs(alpha) >= MagickEpsilon)
2343 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,p,depth+1,beta,
2344 exception);
2345 else
2346 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,q+1,depth+1,beta,
2347 exception);
2348 FxReturn(alpha);
2349 }
2350 if (LocaleCompare(expression,"intensity") == 0)
2351 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2352 if (IsFxFunction(expression,"int",3) != MagickFalse)
2353 {
2354 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2355 depth+1,beta,exception);
2356 FxReturn(floor(alpha));
2357 }
2358 if (IsFxFunction(expression,"isnan",5) != MagickFalse)
2359 {
2360 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2361 depth+1,beta,exception);
2362 FxReturn((double) !!IsNaN(alpha));
2363 }
2364 if (LocaleCompare(expression,"i") == 0)
2365 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2366 break;
2367 }
2368 case 'J':
2369 case 'j':
2370 {
2371 if (LocaleCompare(expression,"j") == 0)
2372 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2373 #if defined(MAGICKCORE_HAVE_J0)
2374 if (IsFxFunction(expression,"j0",2) != MagickFalse)
2375 {
2376 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,
2377 depth+1,beta,exception);
2378 FxReturn(j0(alpha));
2379 }
2380 #endif
2381 #if defined(MAGICKCORE_HAVE_J1)
2382 if (IsFxFunction(expression,"j1",2) != MagickFalse)
2383 {
2384 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,
2385 depth+1,beta,exception);
2386 FxReturn(j1(alpha));
2387 }
2388 #endif
2389 #if defined(MAGICKCORE_HAVE_J1)
2390 if (IsFxFunction(expression,"jinc",4) != MagickFalse)
2391 {
2392 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2393 depth+1,beta,exception);
2394 if (alpha == 0.0)
2395 FxReturn(1.0);
2396 FxReturn((2.0*j1((MagickPI*alpha))/(MagickPI*alpha)));
2397 }
2398 #endif
2399 break;
2400 }
2401 case 'L':
2402 case 'l':
2403 {
2404 if (IsFxFunction(expression,"ln",2) != MagickFalse)
2405 {
2406 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,
2407 depth+1,beta,exception);
2408 FxReturn(log(alpha));
2409 }
2410 if (IsFxFunction(expression,"logtwo",6) != MagickFalse)
2411 {
2412 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+6,
2413 depth+1,beta,exception);
2414 FxReturn(log10(alpha)/log10(2.0));
2415 }
2416 if (IsFxFunction(expression,"log",3) != MagickFalse)
2417 {
2418 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2419 depth+1,beta,exception);
2420 FxReturn(log10(alpha));
2421 }
2422 if (LocaleCompare(expression,"lightness") == 0)
2423 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2424 break;
2425 }
2426 case 'M':
2427 case 'm':
2428 {
2429 if (LocaleCompare(expression,"MaxRGB") == 0)
2430 FxReturn(QuantumRange);
2431 if (LocaleNCompare(expression,"maxima",6) == 0)
2432 break;
2433 if (IsFxFunction(expression,"max",3) != MagickFalse)
2434 {
2435 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2436 depth+1,beta,exception);
2437 FxReturn(alpha > *beta ? alpha : *beta);
2438 }
2439 if (LocaleNCompare(expression,"minima",6) == 0)
2440 break;
2441 if (IsFxFunction(expression,"min",3) != MagickFalse)
2442 {
2443 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2444 depth+1,beta,exception);
2445 FxReturn(alpha < *beta ? alpha : *beta);
2446 }
2447 if (IsFxFunction(expression,"mod",3) != MagickFalse)
2448 {
2449 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2450 depth+1,beta,exception);
2451 FxReturn(alpha-floor((alpha*PerceptibleReciprocal(*beta)))*(*beta));
2452 }
2453 if (LocaleCompare(expression,"m") == 0)
2454 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2455 break;
2456 }
2457 case 'N':
2458 case 'n':
2459 {
2460 if (IsFxFunction(expression,"not",3) != MagickFalse)
2461 {
2462 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2463 depth+1,beta,exception);
2464 FxReturn((double) (alpha < MagickEpsilon));
2465 }
2466 if (LocaleCompare(expression,"n") == 0)
2467 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2468 break;
2469 }
2470 case 'O':
2471 case 'o':
2472 {
2473 if (LocaleCompare(expression,"Opaque") == 0)
2474 FxReturn(1.0);
2475 if (LocaleCompare(expression,"o") == 0)
2476 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2477 break;
2478 }
2479 case 'P':
2480 case 'p':
2481 {
2482 if (LocaleCompare(expression,"phi") == 0)
2483 FxReturn(MagickPHI);
2484 if (LocaleCompare(expression,"pi") == 0)
2485 FxReturn(MagickPI);
2486 if (IsFxFunction(expression,"pow",3) != MagickFalse)
2487 {
2488 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2489 depth+1,beta,exception);
2490 FxReturn(pow(alpha,*beta));
2491 }
2492 if (LocaleCompare(expression,"p") == 0)
2493 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2494 break;
2495 }
2496 case 'Q':
2497 case 'q':
2498 {
2499 if (LocaleCompare(expression,"QuantumRange") == 0)
2500 FxReturn(QuantumRange);
2501 if (LocaleCompare(expression,"QuantumScale") == 0)
2502 FxReturn(QuantumScale);
2503 break;
2504 }
2505 case 'R':
2506 case 'r':
2507 {
2508 if (IsFxFunction(expression,"rand",4) != MagickFalse)
2509 {
2510 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2511 #pragma omp critical (MagickCore_FxEvaluateSubexpression)
2512 #endif
2513 alpha=GetPseudoRandomValue(fx_info->random_info);
2514 FxReturn(alpha);
2515 }
2516 if (IsFxFunction(expression,"round",5) != MagickFalse)
2517 {
2518 /*
2519 Round the fraction to nearest integer.
2520 */
2521 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2522 depth+1,beta,exception);
2523 if ((alpha-floor(alpha)) < (ceil(alpha)-alpha))
2524 FxReturn(floor(alpha));
2525 FxReturn(ceil(alpha));
2526 }
2527 if (LocaleCompare(expression,"r") == 0)
2528 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2529 break;
2530 }
2531 case 'S':
2532 case 's':
2533 {
2534 if (LocaleCompare(expression,"saturation") == 0)
2535 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2536 if (IsFxFunction(expression,"sign",4) != MagickFalse)
2537 {
2538 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2539 depth+1,beta,exception);
2540 FxReturn(alpha < 0.0 ? -1.0 : 1.0);
2541 }
2542 if (IsFxFunction(expression,"sinc",4) != MagickFalse)
2543 {
2544 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2545 depth+1,beta,exception);
2546 if (alpha == 0)
2547 FxReturn(1.0);
2548 FxReturn(sin((MagickPI*alpha))/(MagickPI*alpha));
2549 }
2550 if (IsFxFunction(expression,"sinh",4) != MagickFalse)
2551 {
2552 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2553 depth+1,beta,exception);
2554 FxReturn(sinh(alpha));
2555 }
2556 if (IsFxFunction(expression,"sin",3) != MagickFalse)
2557 {
2558 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2559 depth+1,beta,exception);
2560 FxReturn(sin(alpha));
2561 }
2562 if (IsFxFunction(expression,"sqrt",4) != MagickFalse)
2563 {
2564 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2565 depth+1,beta,exception);
2566 FxReturn(sqrt(alpha));
2567 }
2568 if (IsFxFunction(expression,"squish",6) != MagickFalse)
2569 {
2570 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+6,
2571 depth+1,beta,exception);
2572 FxReturn((1.0/(1.0+exp(-alpha))));
2573 }
2574 if (LocaleCompare(expression,"s") == 0)
2575 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2576 break;
2577 }
2578 case 'T':
2579 case 't':
2580 {
2581 if (IsFxFunction(expression,"tanh",4) != MagickFalse)
2582 {
2583 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2584 depth+1,beta,exception);
2585 FxReturn(tanh(alpha));
2586 }
2587 if (IsFxFunction(expression,"tan",3) != MagickFalse)
2588 {
2589 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2590 depth+1,beta,exception);
2591 FxReturn(tan(alpha));
2592 }
2593 if (LocaleCompare(expression,"Transparent") == 0)
2594 FxReturn(0.0);
2595 if (IsFxFunction(expression,"trunc",5) != MagickFalse)
2596 {
2597 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2598 depth+1,beta,exception);
2599 if (alpha >= 0.0)
2600 FxReturn(floor(alpha));
2601 FxReturn(ceil(alpha));
2602 }
2603 if (LocaleCompare(expression,"t") == 0)
2604 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2605 break;
2606 }
2607 case 'U':
2608 case 'u':
2609 {
2610 if (LocaleCompare(expression,"u") == 0)
2611 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2612 break;
2613 }
2614 case 'V':
2615 case 'v':
2616 {
2617 if (LocaleCompare(expression,"v") == 0)
2618 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2619 break;
2620 }
2621 case 'W':
2622 case 'w':
2623 {
2624 if (IsFxFunction(expression,"while",5) != MagickFalse)
2625 {
2626 size_t
2627 length;
2628
2629 /*
2630 Parse while(condition test, expression).
2631 */
2632 length=CopyMagickString(subexpression,expression+6,
2633 MagickPathExtent-1);
2634 if (length != 0)
2635 subexpression[length-1]='\0';
2636 FxParseConditional(subexpression,',',p,q);
2637 for (alpha=0.0; ; )
2638 {
2639 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,depth+1,&sans,
2640 exception);
2641 if (fabs(gamma) < MagickEpsilon)
2642 break;
2643 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,q+1,depth+1,
2644 beta,exception);
2645 }
2646 FxReturn(alpha);
2647 }
2648 if (LocaleCompare(expression,"w") == 0)
2649 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2650 break;
2651 }
2652 case 'Y':
2653 case 'y':
2654 {
2655 if (LocaleCompare(expression,"y") == 0)
2656 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2657 break;
2658 }
2659 case 'Z':
2660 case 'z':
2661 {
2662 if (LocaleCompare(expression,"z") == 0)
2663 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2664 break;
2665 }
2666 default:
2667 break;
2668 }
2669 subexpression=DestroyString(subexpression);
2670 q=(char *) expression;
2671 alpha=InterpretSiPrefixValue(expression,&q);
2672 if (q == expression)
2673 alpha=FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception);
2674 FxReturn(alpha);
2675 }
2676
FxEvaluateExpression(FxInfo * fx_info,double * alpha,ExceptionInfo * exception)2677 MagickPrivate MagickBooleanType FxEvaluateExpression(FxInfo *fx_info,
2678 double *alpha,ExceptionInfo *exception)
2679 {
2680 MagickBooleanType
2681 status;
2682
2683 status=FxEvaluateChannelExpression(fx_info,GrayPixelChannel,0,0,alpha,
2684 exception);
2685 return(status);
2686 }
2687
FxPreprocessExpression(FxInfo * fx_info,double * alpha,ExceptionInfo * exception)2688 MagickExport MagickBooleanType FxPreprocessExpression(FxInfo *fx_info,
2689 double *alpha,ExceptionInfo *exception)
2690 {
2691 FILE
2692 *file;
2693
2694 MagickBooleanType
2695 status;
2696
2697 file=fx_info->file;
2698 fx_info->file=(FILE *) NULL;
2699 status=FxEvaluateChannelExpression(fx_info,GrayPixelChannel,0,0,alpha,
2700 exception);
2701 fx_info->file=file;
2702 return(status);
2703 }
2704
FxEvaluateChannelExpression(FxInfo * fx_info,const PixelChannel channel,const ssize_t x,const ssize_t y,double * alpha,ExceptionInfo * exception)2705 MagickPrivate MagickBooleanType FxEvaluateChannelExpression(FxInfo *fx_info,
2706 const PixelChannel channel,const ssize_t x,const ssize_t y,
2707 double *alpha,ExceptionInfo *exception)
2708 {
2709 double
2710 beta;
2711
2712 beta=0.0;
2713 *alpha=FxEvaluateSubexpression(fx_info,channel,x,y,fx_info->expression,0,
2714 &beta,exception);
2715 return(exception->severity == OptionError ? MagickFalse : MagickTrue);
2716 }
2717
2718 /*
2719 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2720 % %
2721 % %
2722 % %
2723 % F x I m a g e %
2724 % %
2725 % %
2726 % %
2727 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2728 %
2729 % FxImage() applies a mathematical expression to the specified image.
2730 %
2731 % The format of the FxImage method is:
2732 %
2733 % Image *FxImage(const Image *image,const char *expression,
2734 % ExceptionInfo *exception)
2735 %
2736 % A description of each parameter follows:
2737 %
2738 % o image: the image.
2739 %
2740 % o expression: A mathematical expression.
2741 %
2742 % o exception: return any errors or warnings in this structure.
2743 %
2744 */
2745
DestroyFxThreadSet(FxInfo ** fx_info)2746 static FxInfo **DestroyFxThreadSet(FxInfo **fx_info)
2747 {
2748 ssize_t
2749 i;
2750
2751 assert(fx_info != (FxInfo **) NULL);
2752 for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
2753 if (fx_info[i] != (FxInfo *) NULL)
2754 fx_info[i]=DestroyFxInfo(fx_info[i]);
2755 fx_info=(FxInfo **) RelinquishMagickMemory(fx_info);
2756 return(fx_info);
2757 }
2758
AcquireFxThreadSet(const Image * image,const char * expression,ExceptionInfo * exception)2759 static FxInfo **AcquireFxThreadSet(const Image *image,const char *expression,
2760 ExceptionInfo *exception)
2761 {
2762 char
2763 *fx_expression;
2764
2765 double
2766 alpha;
2767
2768 FxInfo
2769 **fx_info;
2770
2771 ssize_t
2772 i;
2773
2774 size_t
2775 number_threads;
2776
2777 number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
2778 fx_info=(FxInfo **) AcquireQuantumMemory(number_threads,sizeof(*fx_info));
2779 if (fx_info == (FxInfo **) NULL)
2780 {
2781 (void) ThrowMagickException(exception,GetMagickModule(),
2782 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2783 return((FxInfo **) NULL);
2784 }
2785 (void) memset(fx_info,0,number_threads*sizeof(*fx_info));
2786 if (*expression != '@')
2787 fx_expression=ConstantString(expression);
2788 else
2789 fx_expression=FileToString(expression+1,~0UL,exception);
2790 for (i=0; i < (ssize_t) number_threads; i++)
2791 {
2792 MagickBooleanType
2793 status;
2794
2795 fx_info[i]=AcquireFxInfo(image,fx_expression,exception);
2796 if (fx_info[i] == (FxInfo *) NULL)
2797 break;
2798 status=FxPreprocessExpression(fx_info[i],&alpha,exception);
2799 if (status == MagickFalse)
2800 break;
2801 }
2802 fx_expression=DestroyString(fx_expression);
2803 if (i < (ssize_t) number_threads)
2804 fx_info=DestroyFxThreadSet(fx_info);
2805 return(fx_info);
2806 }
2807
FxImage(const Image * image,const char * expression,ExceptionInfo * exception)2808 MagickExport Image *FxImage(const Image *image,const char *expression,
2809 ExceptionInfo *exception)
2810 {
2811 #define FxImageTag "Fx/Image"
2812
2813 CacheView
2814 *fx_view,
2815 *image_view;
2816
2817 FxInfo
2818 **magick_restrict fx_info;
2819
2820 Image
2821 *fx_image;
2822
2823 MagickBooleanType
2824 status;
2825
2826 MagickOffsetType
2827 progress;
2828
2829 ssize_t
2830 y;
2831
2832 assert(image != (Image *) NULL);
2833 assert(image->signature == MagickCoreSignature);
2834 if (image->debug != MagickFalse)
2835 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2836 if (expression == (const char *) NULL)
2837 return(CloneImage(image,0,0,MagickTrue,exception));
2838 fx_info=AcquireFxThreadSet(image,expression,exception);
2839 if (fx_info == (FxInfo **) NULL)
2840 return((Image *) NULL);
2841 fx_image=CloneImage(image,0,0,MagickTrue,exception);
2842 if (fx_image == (Image *) NULL)
2843 {
2844 fx_info=DestroyFxThreadSet(fx_info);
2845 return((Image *) NULL);
2846 }
2847 if (SetImageStorageClass(fx_image,DirectClass,exception) == MagickFalse)
2848 {
2849 fx_info=DestroyFxThreadSet(fx_info);
2850 fx_image=DestroyImage(fx_image);
2851 return((Image *) NULL);
2852 }
2853 /*
2854 Fx image.
2855 */
2856 status=MagickTrue;
2857 progress=0;
2858 image_view=AcquireVirtualCacheView(image,exception);
2859 fx_view=AcquireAuthenticCacheView(fx_image,exception);
2860 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2861 #pragma omp parallel for schedule(dynamic) shared(progress,status) \
2862 magick_number_threads(image,fx_image,fx_image->rows, \
2863 GlobExpression(fx_info[0]->expression,"debug(",MagickTrue) == 0 ? 1 : 0)
2864 #endif
2865 for (y=0; y < (ssize_t) fx_image->rows; y++)
2866 {
2867 const int
2868 id = GetOpenMPThreadId();
2869
2870 const Quantum
2871 *magick_restrict p;
2872
2873 Quantum
2874 *magick_restrict q;
2875
2876 ssize_t
2877 x;
2878
2879 if (status == MagickFalse)
2880 continue;
2881 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2882 q=QueueCacheViewAuthenticPixels(fx_view,0,y,fx_image->columns,1,exception);
2883 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2884 {
2885 status=MagickFalse;
2886 continue;
2887 }
2888 for (x=0; x < (ssize_t) fx_image->columns; x++)
2889 {
2890 ssize_t
2891 i;
2892
2893 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2894 {
2895 double
2896 alpha;
2897
2898 PixelChannel channel = GetPixelChannelChannel(image,i);
2899 PixelTrait traits = GetPixelChannelTraits(image,channel);
2900 PixelTrait fx_traits=GetPixelChannelTraits(fx_image,channel);
2901 if ((traits == UndefinedPixelTrait) ||
2902 (fx_traits == UndefinedPixelTrait))
2903 continue;
2904 if ((fx_traits & CopyPixelTrait) != 0)
2905 {
2906 SetPixelChannel(fx_image,channel,p[i],q);
2907 continue;
2908 }
2909 alpha=0.0;
2910 (void) FxEvaluateChannelExpression(fx_info[id],channel,x,y,&alpha,
2911 exception);
2912 q[i]=ClampToQuantum(QuantumRange*alpha);
2913 }
2914 p+=GetPixelChannels(image);
2915 q+=GetPixelChannels(fx_image);
2916 }
2917 if (SyncCacheViewAuthenticPixels(fx_view,exception) == MagickFalse)
2918 status=MagickFalse;
2919 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2920 {
2921 MagickBooleanType
2922 proceed;
2923
2924 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2925 #pragma omp atomic
2926 #endif
2927 progress++;
2928 proceed=SetImageProgress(image,FxImageTag,progress,image->rows);
2929 if (proceed == MagickFalse)
2930 status=MagickFalse;
2931 }
2932 }
2933 fx_view=DestroyCacheView(fx_view);
2934 image_view=DestroyCacheView(image_view);
2935 fx_info=DestroyFxThreadSet(fx_info);
2936 if (status == MagickFalse)
2937 fx_image=DestroyImage(fx_image);
2938 return(fx_image);
2939 }
2940