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/license/ %
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) (~CastDoubleToSizeT(*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) (CastDoubleToSizeT(alpha+0.5) & CastDoubleToSizeT(*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) (CastDoubleToSizeT(alpha+0.5) |
1574 CastDoubleToSizeT(*beta+0.5));
1575 if (SetFxSymbolValue(fx_info,subexpression,value) == MagickFalse)
1576 return(0.0);
1577 FxReturn(*beta);
1578 }
1579 case LeftShiftAssignmentOperator:
1580 {
1581 q=subexpression;
1582 while (isalpha((int) ((unsigned char) *q)) != 0)
1583 q++;
1584 if (*q != '\0')
1585 {
1586 (void) ThrowMagickException(exception,GetMagickModule(),
1587 OptionError,"UnableToParseExpression","`%s'",subexpression);
1588 FxReturn(0.0);
1589 }
1590 ClearMagickException(exception);
1591 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1592 exception);
1593 if (CastDoubleToSizeT(*beta+0.5) >= (8*sizeof(size_t)))
1594 {
1595 (void) ThrowMagickException(exception,GetMagickModule(),
1596 OptionError,"ShiftCountOverflow","`%s'",subexpression);
1597 FxReturn(0.0);
1598 }
1599 value=(double) (CastDoubleToSizeT(alpha+0.5) <<
1600 CastDoubleToSizeT(*beta+0.5));
1601 if (SetFxSymbolValue(fx_info,subexpression,value) == MagickFalse)
1602 return(0.0);
1603 FxReturn(*beta);
1604 }
1605 case RightShiftAssignmentOperator:
1606 {
1607 q=subexpression;
1608 while (isalpha((int) ((unsigned char) *q)) != 0)
1609 q++;
1610 if (*q != '\0')
1611 {
1612 (void) ThrowMagickException(exception,GetMagickModule(),
1613 OptionError,"UnableToParseExpression","`%s'",subexpression);
1614 FxReturn(0.0);
1615 }
1616 ClearMagickException(exception);
1617 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1618 exception);
1619 if (CastDoubleToSizeT(*beta+0.5) >= (8*sizeof(size_t)))
1620 {
1621 (void) ThrowMagickException(exception,GetMagickModule(),
1622 OptionError,"ShiftCountOverflow","`%s'",subexpression);
1623 FxReturn(0.0);
1624 }
1625 value=(double) (CastDoubleToSizeT(alpha+0.5) >>
1626 CastDoubleToSizeT(*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/(*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 (CastDoubleToSizeT(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) (CastDoubleToSizeT(alpha+0.5) <<
1788 CastDoubleToSizeT(gamma+0.5));
1789 FxReturn(*beta);
1790 }
1791 case RightShiftOperator:
1792 {
1793 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1794 exception);
1795 if (CastDoubleToSizeT(gamma+0.5) >= (8*sizeof(size_t)))
1796 {
1797 (void) ThrowMagickException(exception,GetMagickModule(),
1798 OptionError,"ShiftCountOverflow","`%s'",subexpression);
1799 FxReturn(0.0);
1800 }
1801 *beta=(double) (CastDoubleToSizeT(alpha+0.5) >> CastDoubleToSizeT(gamma+0.5));
1802 FxReturn(*beta);
1803 }
1804 case '<':
1805 {
1806 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1807 exception);
1808 FxReturn(alpha < *beta ? 1.0 : 0.0);
1809 }
1810 case LessThanEqualOperator:
1811 {
1812 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1813 exception);
1814 FxReturn(alpha <= *beta ? 1.0 : 0.0);
1815 }
1816 case '>':
1817 {
1818 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1819 exception);
1820 FxReturn(alpha > *beta ? 1.0 : 0.0);
1821 }
1822 case GreaterThanEqualOperator:
1823 {
1824 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1825 exception);
1826 FxReturn(alpha >= *beta ? 1.0 : 0.0);
1827 }
1828 case EqualOperator:
1829 {
1830 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1831 exception);
1832 FxReturn(fabs(alpha-(*beta)) < MagickEpsilon ? 1.0 : 0.0);
1833 }
1834 case NotEqualOperator:
1835 {
1836 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1837 exception);
1838 FxReturn(fabs(alpha-(*beta)) >= MagickEpsilon ? 1.0 : 0.0);
1839 }
1840 case '&':
1841 {
1842 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1843 exception);
1844 *beta=(double) (CastDoubleToSizeT(alpha+0.5) & CastDoubleToSizeT(gamma+0.5));
1845 FxReturn(*beta);
1846 }
1847 case '|':
1848 {
1849 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1850 exception);
1851 *beta=(double) (CastDoubleToSizeT(alpha+0.5) | CastDoubleToSizeT(gamma+0.5));
1852 FxReturn(*beta);
1853 }
1854 case LogicalAndOperator:
1855 {
1856 p++;
1857 if (alpha <= 0.0)
1858 {
1859 *beta=0.0;
1860 FxReturn(*beta);
1861 }
1862 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,depth+1,beta,
1863 exception);
1864 *beta=(gamma > 0.0) ? 1.0 : 0.0;
1865 FxReturn(*beta);
1866 }
1867 case LogicalOrOperator:
1868 {
1869 p++;
1870 if (alpha > 0.0)
1871 {
1872 *beta=1.0;
1873 FxReturn(*beta);
1874 }
1875 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,depth+1,beta,
1876 exception);
1877 *beta=(gamma > 0.0) ? 1.0 : 0.0;
1878 FxReturn(*beta);
1879 }
1880 case '?':
1881 {
1882 double
1883 gamma;
1884
1885 (void) CopyMagickString(subexpression,++p,MaxTextExtent-1);
1886 FxParseConditional(subexpression,':',p,q);
1887 if (fabs(alpha) >= MagickEpsilon)
1888 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,depth+1,beta,
1889 exception);
1890 else
1891 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,q+1,depth+1,beta,
1892 exception);
1893 FxReturn(gamma);
1894 }
1895 case '=':
1896 {
1897 q=subexpression;
1898 while (isalpha((int) ((unsigned char) *q)) != 0)
1899 q++;
1900 if (*q != '\0')
1901 {
1902 (void) ThrowMagickException(exception,GetMagickModule(),
1903 OptionError,"UnableToParseExpression","`%s'",subexpression);
1904 FxReturn(0.0);
1905 }
1906 ClearMagickException(exception);
1907 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1908 exception);
1909 value=(*beta);
1910 if (SetFxSymbolValue(fx_info,subexpression,value) == MagickFalse)
1911 return(0.0);
1912 FxReturn(*beta);
1913 }
1914 case ',':
1915 {
1916 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1917 exception);
1918 FxReturn(alpha);
1919 }
1920 case ';':
1921 {
1922 *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
1923 exception);
1924 if (*p == '\0')
1925 FxReturn(alpha);
1926 FxReturn(*beta);
1927 }
1928 default:
1929 {
1930 gamma=alpha*FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,
1931 beta,exception);
1932 FxReturn(gamma);
1933 }
1934 }
1935 }
1936 if (strchr("(",(int) *expression) != (char *) NULL)
1937 {
1938 size_t
1939 length;
1940
1941 if (depth >= FxMaxParenthesisDepth)
1942 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1943 "ParenthesisNestedTooDeeply","`%s'",expression);
1944 length=CopyMagickString(subexpression,expression+1,MaxTextExtent);
1945 if (length != 0)
1946 subexpression[length-1]='\0';
1947 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,depth+1,
1948 beta,exception);
1949 FxReturn(gamma);
1950 }
1951 switch (*expression)
1952 {
1953 case '+':
1954 {
1955 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,depth+1,
1956 beta,exception);
1957 FxReturn(1.0*gamma);
1958 }
1959 case '-':
1960 {
1961 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,depth+1,
1962 beta,exception);
1963 FxReturn(-1.0*gamma);
1964 }
1965 case '~':
1966 {
1967 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,depth+1,
1968 beta,exception);
1969 FxReturn((double) (~CastDoubleToSizeT(gamma+0.5)));
1970 }
1971 case 'A':
1972 case 'a':
1973 {
1974 if (IsFxFunction(expression,"abs",3) != MagickFalse)
1975 {
1976 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
1977 depth+1,beta,exception);
1978 FxReturn(fabs(alpha));
1979 }
1980#if defined(MAGICKCORE_HAVE_ACOSH)
1981 if (IsFxFunction(expression,"acosh",5) != MagickFalse)
1982 {
1983 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
1984 depth+1,beta,exception);
1985 FxReturn(acosh(alpha));
1986 }
1987#endif
1988 if (IsFxFunction(expression,"acos",4) != MagickFalse)
1989 {
1990 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
1991 depth+1,beta,exception);
1992 FxReturn(acos(alpha));
1993 }
1994#if defined(MAGICKCORE_HAVE_J1)
1995 if (IsFxFunction(expression,"airy",4) != MagickFalse)
1996 {
1997 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
1998 depth+1,beta,exception);
1999 if (alpha == 0.0)
2000 FxReturn(1.0);
2001 gamma=2.0*j1((MagickPI*alpha))/(MagickPI*alpha);
2002 FxReturn(gamma*gamma);
2003 }
2004#endif
2005#if defined(MAGICKCORE_HAVE_ASINH)
2006 if (IsFxFunction(expression,"asinh",5) != MagickFalse)
2007 {
2008 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2009 depth+1,beta,exception);
2010 FxReturn(asinh(alpha));
2011 }
2012#endif
2013 if (IsFxFunction(expression,"asin",4) != MagickFalse)
2014 {
2015 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2016 depth+1,beta,exception);
2017 FxReturn(asin(alpha));
2018 }
2019 if (IsFxFunction(expression,"alt",3) != MagickFalse)
2020 {
2021 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2022 depth+1,beta,exception);
2023 FxReturn(((ssize_t) alpha) & 0x01 ? -1.0 : 1.0);
2024 }
2025 if (IsFxFunction(expression,"atan2",5) != MagickFalse)
2026 {
2027 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2028 depth+1,beta,exception);
2029 FxReturn(atan2(alpha,*beta));
2030 }
2031#if defined(MAGICKCORE_HAVE_ATANH)
2032 if (IsFxFunction(expression,"atanh",5) != MagickFalse)
2033 {
2034 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2035 depth+1,beta,exception);
2036 FxReturn(atanh(alpha));
2037 }
2038#endif
2039 if (IsFxFunction(expression,"atan",4) != MagickFalse)
2040 {
2041 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2042 depth+1,beta,exception);
2043 FxReturn(atan(alpha));
2044 }
2045 if (LocaleCompare(expression,"a") == 0)
2046 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2047 break;
2048 }
2049 case 'B':
2050 case 'b':
2051 {
2052 if (LocaleCompare(expression,"b") == 0)
2053 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2054 break;
2055 }
2056 case 'C':
2057 case 'c':
2058 {
2059 if (IsFxFunction(expression,"ceil",4) != MagickFalse)
2060 {
2061 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2062 depth+1,beta,exception);
2063 FxReturn(ceil(alpha));
2064 }
2065 if (IsFxFunction(expression,"clamp",5) != MagickFalse)
2066 {
2067 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2068 depth+1,beta,exception);
2069 if (alpha < 0.0)
2070 FxReturn(0.0);
2071 if (alpha > 1.0)
2072 FxReturn(1.0);
2073 FxReturn(alpha);
2074 }
2075 if (IsFxFunction(expression,"cosh",4) != MagickFalse)
2076 {
2077 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2078 depth+1,beta,exception);
2079 FxReturn(cosh(alpha));
2080 }
2081 if (IsFxFunction(expression,"cos",3) != MagickFalse)
2082 {
2083 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2084 depth+1,beta,exception);
2085 FxReturn(cos(alpha));
2086 }
2087 if (LocaleCompare(expression,"c") == 0)
2088 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2089 break;
2090 }
2091 case 'D':
2092 case 'd':
2093 {
2094 if (IsFxFunction(expression,"debug",5) != MagickFalse)
2095 {
2096 const char
2097 *type;
2098
2099 size_t
2100 length;
2101
2102 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2103 depth+1,beta,exception);
2104 switch (fx_info->images->colorspace)
2105 {
2106 case CMYKColorspace:
2107 {
2108 switch (channel)
2109 {
2110 case CyanChannel: type="cyan"; break;
2111 case MagentaChannel: type="magenta"; break;
2112 case YellowChannel: type="yellow"; break;
2113 case AlphaChannel: type="alpha"; break;
2114 case BlackChannel: type="black"; break;
2115 default: type="unknown"; break;
2116 }
2117 break;
2118 }
2119 case GRAYColorspace:
2120 {
2121 switch (channel)
2122 {
2123 case RedChannel: type="gray"; break;
2124 case AlphaChannel: type="alpha"; break;
2125 default: type="unknown"; break;
2126 }
2127 break;
2128 }
2129 default:
2130 {
2131 switch (channel)
2132 {
2133 case RedChannel: type="red"; break;
2134 case GreenChannel: type="green"; break;
2135 case BlueChannel: type="blue"; break;
2136 case AlphaChannel: type="alpha"; break;
2137 default: type="unknown"; break;
2138 }
2139 break;
2140 }
2141 }
2142 *subexpression='\0';
2143 length=1;
2144 if (strlen(expression) > 6)
2145 length=CopyMagickString(subexpression,expression+6,MaxTextExtent);
2146 if (length != 0)
2147 subexpression[length-1]='\0';
2148 if (fx_info->file != (FILE *) NULL)
2149 (void) FormatLocaleFile(fx_info->file,
2150 "%s[%.20g,%.20g].%s: %s=%.*g\n",fx_info->images->filename,
2151 (double) x,(double) y,type,subexpression,GetMagickPrecision(),
2152 (double) alpha);
2153 FxReturn(alpha);
2154 }
2155 if (IsFxFunction(expression,"do",2) != MagickFalse)
2156 {
2157 size_t
2158 length;
2159
2160 /*
2161 Parse do(expression,condition test).
2162 */
2163 length=CopyMagickString(subexpression,expression+6,
2164 MagickPathExtent-1);
2165 if (length != 0)
2166 subexpression[length-1]='\0';
2167 FxParseConditional(subexpression,',',p,q);
2168 for (alpha=0.0; ; )
2169 {
2170 if (IsImageTTLExpired(fx_info->images) != MagickFalse)
2171 (void) ThrowMagickException(exception,GetMagickModule(),
2172 ResourceLimitFatalError,"TimeLimitExceeded","`%s'",
2173 fx_info->images->filename);
2174 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,q+1,depth+1,beta,
2175 exception);
2176 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,depth+1,&sans,
2177 exception);
2178 if (fabs(gamma) < MagickEpsilon)
2179 break;
2180 }
2181 FxReturn(alpha);
2182 }
2183 if (IsFxFunction(expression,"drc",3) != MagickFalse)
2184 {
2185 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2186 depth+1,beta,exception);
2187 FxReturn(alpha/(*beta*(alpha-1.0)+1.0));
2188 }
2189 break;
2190 }
2191 case 'E':
2192 case 'e':
2193 {
2194 if (LocaleCompare(expression,"epsilon") == 0)
2195 FxReturn(MagickEpsilon);
2196#if defined(MAGICKCORE_HAVE_ERF)
2197 if (IsFxFunction(expression,"erf",3) != MagickFalse)
2198 {
2199 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2200 depth+1,beta,exception);
2201 FxReturn(erf(alpha));
2202 }
2203#endif
2204 if (IsFxFunction(expression,"exp",3) != MagickFalse)
2205 {
2206 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2207 depth+1,beta,exception);
2208 FxReturn(exp(alpha));
2209 }
2210 if (LocaleCompare(expression,"e") == 0)
2211 FxReturn(2.7182818284590452354);
2212 break;
2213 }
2214 case 'F':
2215 case 'f':
2216 {
2217 if (IsFxFunction(expression,"floor",5) != MagickFalse)
2218 {
2219 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2220 depth+1,beta,exception);
2221 FxReturn(floor(alpha));
2222 }
2223 if (IsFxFunction(expression,"for",3) != MagickFalse)
2224 {
2225 double
2226 sans = 0.0;
2227
2228 size_t
2229 length;
2230
2231 /*
2232 Parse for(initialization, condition test, expression).
2233 */
2234 length=CopyMagickString(subexpression,expression+4,
2235 MagickPathExtent-1);
2236 if (length != 0)
2237 subexpression[length-1]='\0';
2238 FxParseConditional(subexpression,',',p,q);
2239 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,p,depth+1,&sans,
2240 exception);
2241 (void) CopyMagickString(subexpression,q+1,MagickPathExtent-1);
2242 FxParseConditional(subexpression,',',p,q);
2243 for (alpha=0.0; ; )
2244 {
2245 if (IsImageTTLExpired(fx_info->images) != MagickFalse)
2246 (void) ThrowMagickException(exception,GetMagickModule(),
2247 ResourceLimitFatalError,"TimeLimitExceeded","`%s'",
2248 fx_info->images->filename);
2249 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,depth+1,&sans,
2250 exception);
2251 if (fabs(gamma) < MagickEpsilon)
2252 break;
2253 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,q+1,depth+1,beta,
2254 exception);
2255 }
2256 FxReturn(alpha);
2257 }
2258 break;
2259 }
2260 case 'G':
2261 case 'g':
2262 {
2263 if (IsFxFunction(expression,"gauss",5) != MagickFalse)
2264 {
2265 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2266 depth+1,beta,exception);
2267 FxReturn(exp((-alpha*alpha/2.0))/sqrt(2.0*MagickPI));
2268 }
2269 if (IsFxFunction(expression,"gcd",3) != MagickFalse)
2270 {
2271 double
2272 gcd;
2273
2274 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2275 depth+1,beta,exception);
2276 if (IsNaN(alpha))
2277 FxReturn(alpha);
2278 gcd=FxGCD(alpha,*beta,0);
2279 FxReturn(gcd);
2280 }
2281 if (LocaleCompare(expression,"g") == 0)
2282 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2283 break;
2284 }
2285 case 'H':
2286 case 'h':
2287 {
2288 if (LocaleCompare(expression,"h") == 0)
2289 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2290 if (LocaleCompare(expression,"hue") == 0)
2291 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2292 if (IsFxFunction(expression,"hypot",5) != MagickFalse)
2293 {
2294 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2295 depth+1,beta,exception);
2296 FxReturn(hypot(alpha,*beta));
2297 }
2298 break;
2299 }
2300 case 'K':
2301 case 'k':
2302 {
2303 if (LocaleCompare(expression,"k") == 0)
2304 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2305 break;
2306 }
2307 case 'I':
2308 case 'i':
2309 {
2310 if (IsFxFunction(expression,"if",2) != MagickFalse)
2311 {
2312 double
2313 sans = 0.0;
2314
2315 size_t
2316 length;
2317
2318 /*
2319 Parse if(condition test, true-expression, false-expression).
2320 */
2321 length=CopyMagickString(subexpression,expression+3,
2322 MagickPathExtent-1);
2323 if (length != 0)
2324 subexpression[length-1]='\0';
2325 FxParseConditional(subexpression,',',p,q);
2326 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,p,depth+1,&sans,
2327 exception);
2328 (void) CopyMagickString(subexpression,q+1,MagickPathExtent-1);
2329 FxParseConditional(subexpression,',',p,q);
2330 if (fabs(alpha) >= MagickEpsilon)
2331 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,p,depth+1,beta,
2332 exception);
2333 else
2334 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,q+1,depth+1,beta,
2335 exception);
2336 FxReturn(alpha);
2337 }
2338 if (LocaleCompare(expression,"intensity") == 0)
2339 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2340 if (IsFxFunction(expression,"int",3) != MagickFalse)
2341 {
2342 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2343 depth+1,beta,exception);
2344 FxReturn(floor(alpha));
2345 }
2346 if (IsFxFunction(expression,"isnan",5) != MagickFalse)
2347 {
2348 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2349 depth+1,beta,exception);
2350 FxReturn((double) !!IsNaN(alpha));
2351 }
2352 if (LocaleCompare(expression,"i") == 0)
2353 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2354 break;
2355 }
2356 case 'J':
2357 case 'j':
2358 {
2359 if (LocaleCompare(expression,"j") == 0)
2360 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2361#if defined(MAGICKCORE_HAVE_J0)
2362 if (IsFxFunction(expression,"j0",2) != MagickFalse)
2363 {
2364 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,
2365 depth+1,beta,exception);
2366 FxReturn(j0(alpha));
2367 }
2368#endif
2369#if defined(MAGICKCORE_HAVE_J1)
2370 if (IsFxFunction(expression,"j1",2) != MagickFalse)
2371 {
2372 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,
2373 depth+1,beta,exception);
2374 FxReturn(j1(alpha));
2375 }
2376#endif
2377#if defined(MAGICKCORE_HAVE_J1)
2378 if (IsFxFunction(expression,"jinc",4) != MagickFalse)
2379 {
2380 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2381 depth+1,beta,exception);
2382 if (alpha == 0.0)
2383 FxReturn(1.0);
2384 FxReturn((2.0*j1((MagickPI*alpha))/(MagickPI*alpha)));
2385 }
2386#endif
2387 break;
2388 }
2389 case 'L':
2390 case 'l':
2391 {
2392 if (IsFxFunction(expression,"ln",2) != MagickFalse)
2393 {
2394 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,
2395 depth+1,beta,exception);
2396 FxReturn(log(alpha));
2397 }
2398 if (IsFxFunction(expression,"logtwo",6) != MagickFalse)
2399 {
2400 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+6,
2401 depth+1,beta,exception);
2402 FxReturn(log10(alpha)/log10(2.0));
2403 }
2404 if (IsFxFunction(expression,"log",3) != MagickFalse)
2405 {
2406 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2407 depth+1,beta,exception);
2408 FxReturn(log10(alpha));
2409 }
2410 if (LocaleCompare(expression,"lightness") == 0)
2411 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2412 break;
2413 }
2414 case 'M':
2415 case 'm':
2416 {
2417 if (LocaleCompare(expression,"MaxRGB") == 0)
2418 FxReturn((double) QuantumRange);
2419 if (LocaleNCompare(expression,"maxima",6) == 0)
2420 break;
2421 if (IsFxFunction(expression,"max",3) != MagickFalse)
2422 {
2423 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2424 depth+1,beta,exception);
2425 FxReturn(alpha > *beta ? alpha : *beta);
2426 }
2427 if (LocaleNCompare(expression,"minima",6) == 0)
2428 break;
2429 if (IsFxFunction(expression,"min",3) != MagickFalse)
2430 {
2431 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2432 depth+1,beta,exception);
2433 FxReturn(alpha < *beta ? alpha : *beta);
2434 }
2435 if (IsFxFunction(expression,"mod",3) != MagickFalse)
2436 {
2437 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2438 depth+1,beta,exception);
2439 if (*beta == 0.0)
2440 FxReturn(0.0);
2441 FxReturn(alpha-floor((double) (alpha/(*beta))*(*beta)));
2442 }
2443 if (LocaleCompare(expression,"m") == 0)
2444 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2445 break;
2446 }
2447 case 'N':
2448 case 'n':
2449 {
2450 if (IsFxFunction(expression,"not",3) != MagickFalse)
2451 {
2452 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2453 depth+1,beta,exception);
2454 FxReturn((double) (alpha < MagickEpsilon));
2455 }
2456 if (LocaleCompare(expression,"n") == 0)
2457 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2458 break;
2459 }
2460 case 'O':
2461 case 'o':
2462 {
2463 if (LocaleCompare(expression,"Opaque") == 0)
2464 FxReturn(1.0);
2465 if (LocaleCompare(expression,"o") == 0)
2466 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2467 break;
2468 }
2469 case 'P':
2470 case 'p':
2471 {
2472 if (LocaleCompare(expression,"phi") == 0)
2473 FxReturn(MagickPHI);
2474 if (LocaleCompare(expression,"pi") == 0)
2475 FxReturn(MagickPI);
2476 if (IsFxFunction(expression,"pow",3) != MagickFalse)
2477 {
2478 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2479 depth+1,beta,exception);
2480 FxReturn(pow(alpha,*beta));
2481 }
2482 if (LocaleCompare(expression,"p") == 0)
2483 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2484 break;
2485 }
2486 case 'Q':
2487 case 'q':
2488 {
2489 if (LocaleCompare(expression,"QuantumRange") == 0)
2490 FxReturn((double) QuantumRange);
2491 if (LocaleCompare(expression,"QuantumScale") == 0)
2492 FxReturn(QuantumScale);
2493 break;
2494 }
2495 case 'R':
2496 case 'r':
2497 {
2498 if (IsFxFunction(expression,"rand",4) != MagickFalse)
2499 {
2500 double
2501 alpha;
2502
2503#if defined(MAGICKCORE_OPENMP_SUPPORT)
2504 #pragma omp critical (MagickCore_FxEvaluateSubexpression)
2505#endif
2506 alpha=GetPseudoRandomValue(fx_info->random_info);
2507 FxReturn(alpha);
2508 }
2509 if (IsFxFunction(expression,"round",5) != MagickFalse)
2510 {
2511 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2512 depth+1,beta,exception);
2513 if ((alpha-floor(alpha)) < (ceil(alpha)-alpha))
2514 FxReturn(floor(alpha));
2515 FxReturn(ceil(alpha));
2516 }
2517 if (LocaleCompare(expression,"r") == 0)
2518 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2519 break;
2520 }
2521 case 'S':
2522 case 's':
2523 {
2524 if (LocaleCompare(expression,"saturation") == 0)
2525 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2526 if (IsFxFunction(expression,"sign",4) != MagickFalse)
2527 {
2528 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2529 depth+1,beta,exception);
2530 FxReturn(alpha < 0.0 ? -1.0 : 1.0);
2531 }
2532 if (IsFxFunction(expression,"sinc",4) != MagickFalse)
2533 {
2534 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2535 depth+1,beta,exception);
2536 if (alpha == 0)
2537 FxReturn(1.0);
2538 FxReturn(sin((MagickPI*alpha))/(MagickPI*alpha));
2539 }
2540 if (IsFxFunction(expression,"sinh",4) != MagickFalse)
2541 {
2542 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2543 depth+1,beta,exception);
2544 FxReturn(sinh(alpha));
2545 }
2546 if (IsFxFunction(expression,"sin",3) != MagickFalse)
2547 {
2548 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2549 depth+1,beta,exception);
2550 FxReturn(sin(alpha));
2551 }
2552 if (IsFxFunction(expression,"sqrt",4) != MagickFalse)
2553 {
2554 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2555 depth+1,beta,exception);
2556 FxReturn(sqrt(alpha));
2557 }
2558 if (IsFxFunction(expression,"squish",6) != MagickFalse)
2559 {
2560 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+6,
2561 depth+1,beta,exception);
2562 FxReturn((1.0/(1.0+exp(-alpha))));
2563 }
2564 if (LocaleCompare(expression,"s") == 0)
2565 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2566 break;
2567 }
2568 case 'T':
2569 case 't':
2570 {
2571 if (IsFxFunction(expression,"tanh",4) != MagickFalse)
2572 {
2573 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2574 depth+1,beta,exception);
2575 FxReturn(tanh(alpha));
2576 }
2577 if (IsFxFunction(expression,"tan",3) != MagickFalse)
2578 {
2579 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2580 depth+1,beta,exception);
2581 FxReturn(tan(alpha));
2582 }
2583 if (LocaleCompare(expression,"Transparent") == 0)
2584 FxReturn(0.0);
2585 if (IsFxFunction(expression,"trunc",5) != MagickFalse)
2586 {
2587 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2588 depth+1,beta,exception);
2589 if (alpha >= 0.0)
2590 FxReturn(floor(alpha));
2591 FxReturn(ceil(alpha));
2592 }
2593 if (LocaleCompare(expression,"t") == 0)
2594 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2595 break;
2596 }
2597 case 'U':
2598 case 'u':
2599 {
2600 if (LocaleCompare(expression,"u") == 0)
2601 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2602 break;
2603 }
2604 case 'V':
2605 case 'v':
2606 {
2607 if (LocaleCompare(expression,"v") == 0)
2608 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2609 break;
2610 }
2611 case 'W':
2612 case 'w':
2613 {
2614 if (IsFxFunction(expression,"while",5) != MagickFalse)
2615 {
2616 size_t
2617 length;
2618
2619 /*
2620 Parse while(condition,expression).
2621 */
2622 length=CopyMagickString(subexpression,expression+6,
2623 MagickPathExtent-1);
2624 if (length != 0)
2625 subexpression[length-1]='\0';
2626 FxParseConditional(subexpression,',',p,q);
2627 for (alpha=0.0; ; )
2628 {
2629 if (IsImageTTLExpired(fx_info->images) != MagickFalse)
2630 (void) ThrowMagickException(exception,GetMagickModule(),
2631 ResourceLimitFatalError,"TimeLimitExceeded","`%s'",
2632 fx_info->images->filename);
2633 gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,depth+1,beta,
2634 exception);
2635 if (fabs(gamma) < MagickEpsilon)
2636 break;
2637 alpha=FxEvaluateSubexpression(fx_info,channel,x,y,q+1,depth+1,beta,
2638 exception);
2639 }
2640 FxReturn(alpha);
2641 }
2642 if (LocaleCompare(expression,"w") == 0)
2643 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2644 break;
2645 }
2646 case 'Y':
2647 case 'y':
2648 {
2649 if (LocaleCompare(expression,"y") == 0)
2650 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2651 break;
2652 }
2653 case 'Z':
2654 case 'z':
2655 {
2656 if (LocaleCompare(expression,"z") == 0)
2657 FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2658 break;
2659 }
2660 default:
2661 break;
2662 }
2663 q=(char *) expression;
2664 alpha=InterpretSiPrefixValue(expression,&q);
2665 if (q == expression)
2666 alpha=FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception);
2667 if (*q == ')')
2668 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
2669 "UnbalancedParenthesis","`%s'",expression);
2670 FxReturn(alpha);
2671}
2672
2673MagickExport MagickBooleanType FxEvaluateExpression(FxInfo *fx_info,
2674 double *alpha,ExceptionInfo *exception)
2675{
2676 MagickBooleanType
2677 status;
2678
2679 status=FxEvaluateChannelExpression(fx_info,GrayChannel,0,0,alpha,exception);
2680 return(status);
2681}
2682
2683MagickExport MagickBooleanType FxPreprocessExpression(FxInfo *fx_info,
2684 double *alpha,ExceptionInfo *exception)
2685{
2686 FILE
2687 *file;
2688
2689 MagickBooleanType
2690 status;
2691
2692 file=fx_info->file;
2693 fx_info->file=(FILE *) NULL;
2694 status=FxEvaluateChannelExpression(fx_info,GrayChannel,0,0,alpha,exception);
2695 fx_info->file=file;
2696 return(status);
2697}
2698
2699MagickExport MagickBooleanType FxEvaluateChannelExpression(FxInfo *fx_info,
2700 const ChannelType channel,const ssize_t x,const ssize_t y,double *alpha,
2701 ExceptionInfo *exception)
2702{
2703 double
2704 beta;
2705
2706 beta=0.0;
2707 *alpha=FxEvaluateSubexpression(fx_info,channel,x,y,fx_info->expression,0,
2708 &beta,exception);
2709 return(exception->severity == OptionError ? MagickFalse : MagickTrue);
2710}
2711
2712/*
2713%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2714% %
2715% %
2716% %
2717% F x I m a g e %
2718% %
2719% %
2720% %
2721%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2722%
2723% FxImage() applies a mathematical expression to the specified image.
2724%
2725% The format of the FxImage method is:
2726%
2727% Image *FxImage(const Image *image,const char *expression,
2728% ExceptionInfo *exception)
2729% Image *FxImageChannel(const Image *image,const ChannelType channel,
2730% const char *expression,ExceptionInfo *exception)
2731%
2732% A description of each parameter follows:
2733%
2734% o image: the image.
2735%
2736% o channel: the channel.
2737%
2738% o expression: A mathematical expression.
2739%
2740% o exception: return any errors or warnings in this structure.
2741%
2742*/
2743
2744static FxInfo **DestroyFxTLS(FxInfo **fx_info)
2745{
2746 ssize_t
2747 i;
2748
2749 assert(fx_info != (FxInfo **) NULL);
2750 for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
2751 if (fx_info[i] != (FxInfo *) NULL)
2752 fx_info[i]=DestroyFxInfo(fx_info[i]);
2753 fx_info=(FxInfo **) RelinquishMagickMemory(fx_info);
2754 return(fx_info);
2755}
2756
2757static FxInfo **AcquireFxTLS(const Image *image,const char *expression,
2758 ExceptionInfo *exception)
2759{
2760 char
2761 *fx_expression;
2762
2763 double
2764 alpha;
2765
2766 FxInfo
2767 **fx_info;
2768
2769 ssize_t
2770 i;
2771
2772 size_t
2773 number_threads;
2774
2775 number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
2776 fx_info=(FxInfo **) AcquireQuantumMemory(number_threads,sizeof(*fx_info));
2777 if (fx_info == (FxInfo **) NULL)
2778 {
2779 (void) ThrowMagickException(exception,GetMagickModule(),
2780 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2781 return((FxInfo **) NULL);
2782 }
2783 (void) memset(fx_info,0,number_threads*sizeof(*fx_info));
2784 if (*expression != '@')
2785 fx_expression=ConstantString(expression);
2786 else
2787 fx_expression=FileToString(expression,~0UL,exception);
2788 for (i=0; i < (ssize_t) number_threads; i++)
2789 {
2790 MagickBooleanType
2791 status;
2792
2793 fx_info[i]=AcquireFxInfo(image,fx_expression);
2794 if (fx_info[i] == (FxInfo *) NULL)
2795 break;
2796 status=FxPreprocessExpression(fx_info[i],&alpha,exception);
2797 if (status == MagickFalse)
2798 break;
2799 }
2800 fx_expression=DestroyString(fx_expression);
2801 if (i < (ssize_t) number_threads)
2802 fx_info=DestroyFxTLS(fx_info);
2803 return(fx_info);
2804}
2805
2806MagickExport Image *FxImage(const Image *image,const char *expression,
2807 ExceptionInfo *exception)
2808{
2809 Image
2810 *fx_image;
2811
2812 fx_image=FxImageChannel(image,GrayChannel,expression,exception);
2813 return(fx_image);
2814}
2815
2816MagickExport Image *FxImageChannel(const Image *image,const ChannelType channel,
2817 const char *expression,ExceptionInfo *exception)
2818{
2819#define FxImageTag "Fx/Image"
2820
2821 CacheView
2822 *fx_view;
2823
2824 FxInfo
2825 **magick_restrict fx_info;
2826
2827 Image
2828 *fx_image;
2829
2830 MagickBooleanType
2831 status;
2832
2833 MagickOffsetType
2834 progress;
2835
2836 ssize_t
2837 y;
2838
2839 assert(image != (Image *) NULL);
2840 assert(image->signature == MagickCoreSignature);
2841 if (IsEventLogging() != MagickFalse)
2842 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2843 if (expression == (const char *) NULL)
2844 return(CloneImage(image,0,0,MagickTrue,exception));
2845 fx_info=AcquireFxTLS(image,expression,exception);
2846 if (fx_info == (FxInfo **) NULL)
2847 return((Image *) NULL);
2848 fx_image=CloneImage(image,0,0,MagickTrue,exception);
2849 if (fx_image == (Image *) NULL)
2850 {
2851 fx_info=DestroyFxTLS(fx_info);
2852 return((Image *) NULL);
2853 }
2854 if (SetImageStorageClass(fx_image,DirectClass) == MagickFalse)
2855 {
2856 InheritException(exception,&fx_image->exception);
2857 fx_info=DestroyFxTLS(fx_info);
2858 fx_image=DestroyImage(fx_image);
2859 return((Image *) NULL);
2860 }
2861 /*
2862 Fx image.
2863 */
2864 status=MagickTrue;
2865 progress=0;
2866 fx_view=AcquireAuthenticCacheView(fx_image,exception);
2867#if defined(MAGICKCORE_OPENMP_SUPPORT)
2868 #pragma omp parallel for schedule(dynamic) shared(progress,status) \
2869 magick_number_threads(image,fx_image,fx_image->rows, \
2870 GlobExpression(fx_info[0]->expression,"*debug(*",MagickTrue) == 0 ? 1 : 0)
2871#endif
2872 for (y=0; y < (ssize_t) fx_image->rows; y++)
2873 {
2874 const int
2875 id = GetOpenMPThreadId();
2876
2877 double
2878 alpha;
2879
2880 IndexPacket
2881 *magick_restrict fx_indexes;
2882
2883 ssize_t
2884 x;
2885
2886 PixelPacket
2887 *magick_restrict q;
2888
2889 if (status == MagickFalse)
2890 continue;
2891 q=GetCacheViewAuthenticPixels(fx_view,0,y,fx_image->columns,1,exception);
2892 if (q == (PixelPacket *) NULL)
2893 {
2894 status=MagickFalse;
2895 continue;
2896 }
2897 fx_indexes=GetCacheViewAuthenticIndexQueue(fx_view);
2898 alpha=0.0;
2899 for (x=0; x < (ssize_t) fx_image->columns; x++)
2900 {
2901 if ((channel & RedChannel) != 0)
2902 {
2903 (void) FxEvaluateChannelExpression(fx_info[id],RedChannel,x,y,
2904 &alpha,exception);
2905 SetPixelRed(q,ClampToQuantum((MagickRealType) QuantumRange*alpha));
2906 }
2907 if ((channel & GreenChannel) != 0)
2908 {
2909 (void) FxEvaluateChannelExpression(fx_info[id],GreenChannel,x,y,
2910 &alpha,exception);
2911 SetPixelGreen(q,ClampToQuantum((MagickRealType) QuantumRange*alpha));
2912 }
2913 if ((channel & BlueChannel) != 0)
2914 {
2915 (void) FxEvaluateChannelExpression(fx_info[id],BlueChannel,x,y,
2916 &alpha,exception);
2917 SetPixelBlue(q,ClampToQuantum((MagickRealType) QuantumRange*alpha));
2918 }
2919 if ((channel & OpacityChannel) != 0)
2920 {
2921 (void) FxEvaluateChannelExpression(fx_info[id],OpacityChannel,x,y,
2922 &alpha,exception);
2923 if (image->matte == MagickFalse)
2924 SetPixelOpacity(q,ClampToQuantum((MagickRealType) QuantumRange*
2925 alpha));
2926 else
2927 SetPixelOpacity(q,ClampToQuantum((MagickRealType) QuantumRange-
2928 (MagickRealType) QuantumRange*alpha));
2929 }
2930 if (((channel & IndexChannel) != 0) &&
2931 (fx_image->colorspace == CMYKColorspace))
2932 {
2933 (void) FxEvaluateChannelExpression(fx_info[id],IndexChannel,x,y,
2934 &alpha,exception);
2935 SetPixelIndex(fx_indexes+x,ClampToQuantum((MagickRealType)
2936 QuantumRange*alpha));
2937 }
2938 q++;
2939 }
2940 if (SyncCacheViewAuthenticPixels(fx_view,exception) == MagickFalse)
2941 status=MagickFalse;
2942 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2943 {
2944 MagickBooleanType
2945 proceed;
2946
2947#if defined(MAGICKCORE_OPENMP_SUPPORT)
2948 #pragma omp atomic
2949#endif
2950 progress++;
2951 proceed=SetImageProgress(image,FxImageTag,progress,image->rows);
2952 if (proceed == MagickFalse)
2953 status=MagickFalse;
2954 }
2955 }
2956 fx_view=DestroyCacheView(fx_view);
2957 fx_info=DestroyFxTLS(fx_info);
2958 if (status == MagickFalse)
2959 fx_image=DestroyImage(fx_image);
2960 return(fx_image);
2961}
Definition fx.c:131