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