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
141 SplayTreeInfo
142 *colors,
143 *symbols;
144
145 CacheView
146 **view;
147
148 RandomInfo
149 *random_info;
150
151 ExceptionInfo
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
569 MagickPixelPacket
570 pixel;
571
572 PointInfo
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 {
732 MagickPixelPacket
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 {
811 GeometryInfo
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(MagickSafeReciprocal(image->x_resolution)*image->columns);
1055 if (LocaleCompare(symbol,"printsize.y") == 0)
1056 return(MagickSafeReciprocal(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(alpha/(*beta));
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/(*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/(*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(log10(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(log10(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 if (*beta == 0.0)
2436 FxReturn(0.0);
2437 FxReturn(alpha-floor((double) (alpha/(*beta))*(*beta)));
2438 }
2439 if (LocaleCompare(expression,"m") == 0)
2440 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2441 break;
2442 }
2443 case 'N':
2444 case 'n':
2445 {
2446 if (IsFxFunction(expression,"not",3) != MagickFalse)
2447 {
2448 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2449 depth+1,beta,exception);
2450 FxReturn((double) (alpha < MagickEpsilon));
2451 }
2452 if (LocaleCompare(expression,"n") == 0)
2453 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2454 break;
2455 }
2456 case 'O':
2457 case 'o':
2458 {
2459 if (LocaleCompare(expression,"Opaque") == 0)
2460 FxReturn(1.0);
2461 if (LocaleCompare(expression,"o") == 0)
2462 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2463 break;
2464 }
2465 case 'P':
2466 case 'p':
2467 {
2468 if (LocaleCompare(expression,"phi") == 0)
2469 FxReturn(MagickPHI);
2470 if (LocaleCompare(expression,"pi") == 0)
2471 FxReturn(MagickPI);
2472 if (IsFxFunction(expression,"pow",3) != MagickFalse)
2473 {
2474 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2475 depth+1,beta,exception);
2476 FxReturn(pow(alpha,*beta));
2477 }
2478 if (LocaleCompare(expression,"p") == 0)
2479 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2480 break;
2481 }
2482 case 'Q':
2483 case 'q':
2484 {
2485 if (LocaleCompare(expression,"QuantumRange") == 0)
2486 FxReturn((double) QuantumRange);
2487 if (LocaleCompare(expression,"QuantumScale") == 0)
2488 FxReturn(QuantumScale);
2489 break;
2490 }
2491 case 'R':
2492 case 'r':
2493 {
2494 if (IsFxFunction(expression,"rand",4) != MagickFalse)
2495 {
2496 double
2497 alpha;
2498
2499#if defined(MAGICKCORE_OPENMP_SUPPORT)
2500 #pragma omp critical (MagickCore_FxEvaluateSubexpression)
2501#endif
2502 alpha=GetPseudoRandomValue(fx_info->random_info);
2503 FxReturn(alpha);
2504 }
2505 if (IsFxFunction(expression,"round",5) != MagickFalse)
2506 {
2507 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2508 depth+1,beta,exception);
2509 if ((alpha-floor(alpha)) < (ceil(alpha)-alpha))
2510 FxReturn(floor(alpha));
2511 FxReturn(ceil(alpha));
2512 }
2513 if (LocaleCompare(expression,"r") == 0)
2514 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2515 break;
2516 }
2517 case 'S':
2518 case 's':
2519 {
2520 if (LocaleCompare(expression,"saturation") == 0)
2521 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2522 if (IsFxFunction(expression,"sign",4) != MagickFalse)
2523 {
2524 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2525 depth+1,beta,exception);
2526 FxReturn(alpha < 0.0 ? -1.0 : 1.0);
2527 }
2528 if (IsFxFunction(expression,"sinc",4) != MagickFalse)
2529 {
2530 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2531 depth+1,beta,exception);
2532 if (alpha == 0)
2533 FxReturn(1.0);
2534 FxReturn(sin((MagickPI*alpha))/(MagickPI*alpha));
2535 }
2536 if (IsFxFunction(expression,"sinh",4) != MagickFalse)
2537 {
2538 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2539 depth+1,beta,exception);
2540 FxReturn(sinh(alpha));
2541 }
2542 if (IsFxFunction(expression,"sin",3) != MagickFalse)
2543 {
2544 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2545 depth+1,beta,exception);
2546 FxReturn(sin(alpha));
2547 }
2548 if (IsFxFunction(expression,"sqrt",4) != MagickFalse)
2549 {
2550 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2551 depth+1,beta,exception);
2552 FxReturn(sqrt(alpha));
2553 }
2554 if (IsFxFunction(expression,"squish",6) != MagickFalse)
2555 {
2556 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+6,
2557 depth+1,beta,exception);
2558 FxReturn((1.0/(1.0+exp(-alpha))));
2559 }
2560 if (LocaleCompare(expression,"s") == 0)
2561 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2562 break;
2563 }
2564 case 'T':
2565 case 't':
2566 {
2567 if (IsFxFunction(expression,"tanh",4) != MagickFalse)
2568 {
2569 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2570 depth+1,beta,exception);
2571 FxReturn(tanh(alpha));
2572 }
2573 if (IsFxFunction(expression,"tan",3) != MagickFalse)
2574 {
2575 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2576 depth+1,beta,exception);
2577 FxReturn(tan(alpha));
2578 }
2579 if (LocaleCompare(expression,"Transparent") == 0)
2580 FxReturn(0.0);
2581 if (IsFxFunction(expression,"trunc",5) != MagickFalse)
2582 {
2583 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2584 depth+1,beta,exception);
2585 if (alpha >= 0.0)
2586 FxReturn(floor(alpha));
2587 FxReturn(ceil(alpha));
2588 }
2589 if (LocaleCompare(expression,"t") == 0)
2590 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2591 break;
2592 }
2593 case 'U':
2594 case 'u':
2595 {
2596 if (LocaleCompare(expression,"u") == 0)
2597 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2598 break;
2599 }
2600 case 'V':
2601 case 'v':
2602 {
2603 if (LocaleCompare(expression,"v") == 0)
2604 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2605 break;
2606 }
2607 case 'W':
2608 case 'w':
2609 {
2610 if (IsFxFunction(expression,"while",5) != MagickFalse)
2611 {
2612 size_t
2613 length;
2614
2615 /*
2616 Parse while(condition,expression).
2617 */
2618 length=CopyMagickString(subexpression,expression+6,
2619 MagickPathExtent-1);
2620 if (length != 0)
2621 subexpression[length-1]='\0';
2622 FxParseConditional(subexpression,',',p,q);
2623 for (alpha=0.0; ; )
2624 {
2625 if (IsImageTTLExpired(fx_info->images) != MagickFalse)
2626 (void) ThrowMagickException(exception,GetMagickModule(),
2627 ResourceLimitFatalError,"TimeLimitExceeded","`%s'",
2628 fx_info->images->filename);
2629 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,depth+1,beta,
2630 exception);
2631 if (fabs(gamma) < MagickEpsilon)
2632 break;
2633 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,q+1,depth+1,beta,
2634 exception);
2635 }
2636 FxReturn(alpha);
2637 }
2638 if (LocaleCompare(expression,"w") == 0)
2639 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2640 break;
2641 }
2642 case 'Y':
2643 case 'y':
2644 {
2645 if (LocaleCompare(expression,"y") == 0)
2646 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2647 break;
2648 }
2649 case 'Z':
2650 case 'z':
2651 {
2652 if (LocaleCompare(expression,"z") == 0)
2653 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2654 break;
2655 }
2656 default:
2657 break;
2658 }
2659 q=(char *) expression;
2660 alpha=InterpretSiPrefixValue(expression,&q);
2661 if (q == expression)
2662 alpha=FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception);
2663 if (*q == ')')
2664 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
2665 "UnbalancedParenthesis","`%s'",expression);
2666 FxReturn(alpha);
2667}
2668
2669MagickExport MagickBooleanType FxEvaluateExpression(FxInfo *fx_info,
2670 double *alpha,ExceptionInfo *exception)
2671{
2672 MagickBooleanType
2673 status;
2674
2675 status=FxEvaluateChannelExpression(fx_info,GrayChannel,0,0,alpha,exception);
2676 return(status);
2677}
2678
2679MagickExport MagickBooleanType FxPreprocessExpression(FxInfo *fx_info,
2680 double *alpha,ExceptionInfo *exception)
2681{
2682 FILE
2683 *file;
2684
2685 MagickBooleanType
2686 status;
2687
2688 file=fx_info->file;
2689 fx_info->file=(FILE *) NULL;
2690 status=FxEvaluateChannelExpression(fx_info,GrayChannel,0,0,alpha,exception);
2691 fx_info->file=file;
2692 return(status);
2693}
2694
2695MagickExport MagickBooleanType FxEvaluateChannelExpression(FxInfo *fx_info,
2696 const ChannelType channel,const ssize_t x,const ssize_t y,double *alpha,
2697 ExceptionInfo *exception)
2698{
2699 double
2700 beta;
2701
2702 beta=0.0;
2703 *alpha=FxEvaluateSubexpression(fx_info,channel,x,y,fx_info->expression,0,
2704 &beta,exception);
2705 return(exception->severity == OptionError ? MagickFalse : MagickTrue);
2706}
2707
2708/*
2709%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2710% %
2711% %
2712% %
2713% F x I m a g e %
2714% %
2715% %
2716% %
2717%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2718%
2719% FxImage() applies a mathematical expression to the specified image.
2720%
2721% The format of the FxImage method is:
2722%
2723% Image *FxImage(const Image *image,const char *expression,
2724% ExceptionInfo *exception)
2725% Image *FxImageChannel(const Image *image,const ChannelType channel,
2726% const char *expression,ExceptionInfo *exception)
2727%
2728% A description of each parameter follows:
2729%
2730% o image: the image.
2731%
2732% o channel: the channel.
2733%
2734% o expression: A mathematical expression.
2735%
2736% o exception: return any errors or warnings in this structure.
2737%
2738*/
2739
2740static FxInfo **DestroyFxTLS(FxInfo **fx_info)
2741{
2742 ssize_t
2743 i;
2744
2745 assert(fx_info != (FxInfo **) NULL);
2746 for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
2747 if (fx_info[i] != (FxInfo *) NULL)
2748 fx_info[i]=DestroyFxInfo(fx_info[i]);
2749 fx_info=(FxInfo **) RelinquishMagickMemory(fx_info);
2750 return(fx_info);
2751}
2752
2753static FxInfo **AcquireFxTLS(const Image *image,const char *expression,
2754 ExceptionInfo *exception)
2755{
2756 char
2757 *fx_expression;
2758
2759 double
2760 alpha;
2761
2762 FxInfo
2763 **fx_info;
2764
2765 ssize_t
2766 i;
2767
2768 size_t
2769 number_threads;
2770
2771 number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
2772 fx_info=(FxInfo **) AcquireQuantumMemory(number_threads,sizeof(*fx_info));
2773 if (fx_info == (FxInfo **) NULL)
2774 {
2775 (void) ThrowMagickException(exception,GetMagickModule(),
2776 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2777 return((FxInfo **) NULL);
2778 }
2779 (void) memset(fx_info,0,number_threads*sizeof(*fx_info));
2780 if (*expression != '@')
2781 fx_expression=ConstantString(expression);
2782 else
2783 fx_expression=FileToString(expression,~0UL,exception);
2784 for (i=0; i < (ssize_t) number_threads; i++)
2785 {
2786 MagickBooleanType
2787 status;
2788
2789 fx_info[i]=AcquireFxInfo(image,fx_expression);
2790 if (fx_info[i] == (FxInfo *) NULL)
2791 break;
2792 status=FxPreprocessExpression(fx_info[i],&alpha,exception);
2793 if (status == MagickFalse)
2794 break;
2795 }
2796 fx_expression=DestroyString(fx_expression);
2797 if (i < (ssize_t) number_threads)
2798 fx_info=DestroyFxTLS(fx_info);
2799 return(fx_info);
2800}
2801
2802MagickExport Image *FxImage(const Image *image,const char *expression,
2803 ExceptionInfo *exception)
2804{
2805 Image
2806 *fx_image;
2807
2808 fx_image=FxImageChannel(image,GrayChannel,expression,exception);
2809 return(fx_image);
2810}
2811
2812MagickExport Image *FxImageChannel(const Image *image,const ChannelType channel,
2813 const char *expression,ExceptionInfo *exception)
2814{
2815#define FxImageTag "Fx/Image"
2816
2817 CacheView
2818 *fx_view;
2819
2820 FxInfo
2821 **magick_restrict fx_info;
2822
2823 Image
2824 *fx_image;
2825
2826 MagickBooleanType
2827 status;
2828
2829 MagickOffsetType
2830 progress;
2831
2832 ssize_t
2833 y;
2834
2835 assert(image != (Image *) NULL);
2836 assert(image->signature == MagickCoreSignature);
2837 if (IsEventLogging() != MagickFalse)
2838 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2839 if (expression == (const char *) NULL)
2840 return(CloneImage(image,0,0,MagickTrue,exception));
2841 fx_info=AcquireFxTLS(image,expression,exception);
2842 if (fx_info == (FxInfo **) NULL)
2843 return((Image *) NULL);
2844 fx_image=CloneImage(image,0,0,MagickTrue,exception);
2845 if (fx_image == (Image *) NULL)
2846 {
2847 fx_info=DestroyFxTLS(fx_info);
2848 return((Image *) NULL);
2849 }
2850 if (SetImageStorageClass(fx_image,DirectClass) == MagickFalse)
2851 {
2852 InheritException(exception,&fx_image->exception);
2853 fx_info=DestroyFxTLS(fx_info);
2854 fx_image=DestroyImage(fx_image);
2855 return((Image *) NULL);
2856 }
2857 /*
2858 Fx image.
2859 */
2860 status=MagickTrue;
2861 progress=0;
2862 fx_view=AcquireAuthenticCacheView(fx_image,exception);
2863#if defined(MAGICKCORE_OPENMP_SUPPORT)
2864 #pragma omp parallel for schedule(dynamic) shared(progress,status) \
2865 magick_number_threads(image,fx_image,fx_image->rows, \
2866 GlobExpression(fx_info[0]->expression,"*debug(*",MagickTrue) == 0 ? 1 : 0)
2867#endif
2868 for (y=0; y < (ssize_t) fx_image->rows; y++)
2869 {
2870 const int
2871 id = GetOpenMPThreadId();
2872
2873 double
2874 alpha;
2875
2876 IndexPacket
2877 *magick_restrict fx_indexes;
2878
2879 ssize_t
2880 x;
2881
2882 PixelPacket
2883 *magick_restrict q;
2884
2885 if (status == MagickFalse)
2886 continue;
2887 q=GetCacheViewAuthenticPixels(fx_view,0,y,fx_image->columns,1,exception);
2888 if (q == (PixelPacket *) NULL)
2889 {
2890 status=MagickFalse;
2891 continue;
2892 }
2893 fx_indexes=GetCacheViewAuthenticIndexQueue(fx_view);
2894 alpha=0.0;
2895 for (x=0; x < (ssize_t) fx_image->columns; x++)
2896 {
2897 if ((channel & RedChannel) != 0)
2898 {
2899 (void) FxEvaluateChannelExpression(fx_info[id],RedChannel,x,y,
2900 &alpha,exception);
2901 SetPixelRed(q,ClampToQuantum((MagickRealType) QuantumRange*alpha));
2902 }
2903 if ((channel & GreenChannel) != 0)
2904 {
2905 (void) FxEvaluateChannelExpression(fx_info[id],GreenChannel,x,y,
2906 &alpha,exception);
2907 SetPixelGreen(q,ClampToQuantum((MagickRealType) QuantumRange*alpha));
2908 }
2909 if ((channel & BlueChannel) != 0)
2910 {
2911 (void) FxEvaluateChannelExpression(fx_info[id],BlueChannel,x,y,
2912 &alpha,exception);
2913 SetPixelBlue(q,ClampToQuantum((MagickRealType) QuantumRange*alpha));
2914 }
2915 if ((channel & OpacityChannel) != 0)
2916 {
2917 (void) FxEvaluateChannelExpression(fx_info[id],OpacityChannel,x,y,
2918 &alpha,exception);
2919 if (image->matte == MagickFalse)
2920 SetPixelOpacity(q,ClampToQuantum((MagickRealType) QuantumRange*
2921 alpha));
2922 else
2923 SetPixelOpacity(q,ClampToQuantum((MagickRealType) QuantumRange-
2924 (MagickRealType) QuantumRange*alpha));
2925 }
2926 if (((channel & IndexChannel) != 0) &&
2927 (fx_image->colorspace == CMYKColorspace))
2928 {
2929 (void) FxEvaluateChannelExpression(fx_info[id],IndexChannel,x,y,
2930 &alpha,exception);
2931 SetPixelIndex(fx_indexes+x,ClampToQuantum((MagickRealType)
2932 QuantumRange*alpha));
2933 }
2934 q++;
2935 }
2936 if (SyncCacheViewAuthenticPixels(fx_view,exception) == MagickFalse)
2937 status=MagickFalse;
2938 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2939 {
2940 MagickBooleanType
2941 proceed;
2942
2943#if defined(MAGICKCORE_OPENMP_SUPPORT)
2944 #pragma omp atomic
2945#endif
2946 progress++;
2947 proceed=SetImageProgress(image,FxImageTag,progress,image->rows);
2948 if (proceed == MagickFalse)
2949 status=MagickFalse;
2950 }
2951 }
2952 fx_view=DestroyCacheView(fx_view);
2953 fx_info=DestroyFxTLS(fx_info);
2954 if (status == MagickFalse)
2955 fx_image=DestroyImage(fx_image);
2956 return(fx_image);
2957}
Definition fx.c:131