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