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