MagickCore  7.0.3
fx.c
Go to the documentation of this file.
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-2019 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"
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"
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"
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"
73 #include "MagickCore/magick.h"
74 #include "MagickCore/memory_.h"
76 #include "MagickCore/monitor.h"
78 #include "MagickCore/option.h"
79 #include "MagickCore/pixel.h"
81 #include "MagickCore/property.h"
82 #include "MagickCore/quantum.h"
84 #include "MagickCore/random_.h"
86 #include "MagickCore/resample.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"
95 #include "MagickCore/threshold.h"
96 #include "MagickCore/transform.h"
98 #include "MagickCore/utility.h"
99 
100 /*
101  Define declarations.
102 */
103 #define LeftShiftOperator 0xf5U
104 #define RightShiftOperator 0xf6U
105 #define LessThanEqualOperator 0xf7U
106 #define GreaterThanEqualOperator 0xf8U
107 #define EqualOperator 0xf9U
108 #define NotEqualOperator 0xfaU
109 #define LogicalAndOperator 0xfbU
110 #define LogicalOrOperator 0xfcU
111 #define ExponentialNotation 0xfdU
112 
113 struct _FxInfo
114 {
115  const Image
117 
118  char
120 
121  FILE
123 
126  *symbols;
127 
128  CacheView
129  **view;
130 
131  RandomInfo
133 
136 };
137 
138 /*
139 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
140 % %
141 % %
142 % %
143 + A c q u i r e F x I n f o %
144 % %
145 % %
146 % %
147 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
148 %
149 % AcquireFxInfo() allocates the FxInfo structure.
150 %
151 % The format of the AcquireFxInfo method is:
152 %
153 % FxInfo *AcquireFxInfo(Image *images,const char *expression,
154 % ExceptionInfo *exception)
155 %
156 % A description of each parameter follows:
157 %
158 % o images: the image sequence.
159 %
160 % o expression: the expression.
161 %
162 % o exception: return any errors or warnings in this structure.
163 %
164 */
165 MagickPrivate FxInfo *AcquireFxInfo(const Image *images,const char *expression,
166  ExceptionInfo *exception)
167 {
168  char
169  fx_op[2];
170 
171  const Image
172  *next;
173 
174  FxInfo
175  *fx_info;
176 
177  register ssize_t
178  i;
179 
180  fx_info=(FxInfo *) AcquireCriticalMemory(sizeof(*fx_info));
181  (void) memset(fx_info,0,sizeof(*fx_info));
182  fx_info->exception=AcquireExceptionInfo();
183  fx_info->images=images;
189  fx_info->images),sizeof(*fx_info->view));
190  if (fx_info->view == (CacheView **) NULL)
191  ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
192  i=0;
193  next=GetFirstImageInList(fx_info->images);
194  for ( ; next != (Image *) NULL; next=next->next)
195  {
196  fx_info->view[i]=AcquireVirtualCacheView(next,exception);
197  i++;
198  }
199  fx_info->random_info=AcquireRandomInfo();
200  fx_info->expression=ConstantString(expression);
201  fx_info->file=stderr;
202  (void) SubstituteString(&fx_info->expression," ",""); /* compact string */
203  /*
204  Force right-to-left associativity for unary negation.
205  */
206  (void) SubstituteString(&fx_info->expression,"-","-1.0*");
207  (void) SubstituteString(&fx_info->expression,"^-1.0*","^-");
208  (void) SubstituteString(&fx_info->expression,"E-1.0*","E-");
209  (void) SubstituteString(&fx_info->expression,"e-1.0*","e-");
210  /*
211  Convert compound to simple operators.
212  */
213  fx_op[1]='\0';
214  *fx_op=(char) LeftShiftOperator;
215  (void) SubstituteString(&fx_info->expression,"<<",fx_op);
216  *fx_op=(char) RightShiftOperator;
217  (void) SubstituteString(&fx_info->expression,">>",fx_op);
218  *fx_op=(char) LessThanEqualOperator;
219  (void) SubstituteString(&fx_info->expression,"<=",fx_op);
220  *fx_op=(char) GreaterThanEqualOperator;
221  (void) SubstituteString(&fx_info->expression,">=",fx_op);
222  *fx_op=(char) EqualOperator;
223  (void) SubstituteString(&fx_info->expression,"==",fx_op);
224  *fx_op=(char) NotEqualOperator;
225  (void) SubstituteString(&fx_info->expression,"!=",fx_op);
226  *fx_op=(char) LogicalAndOperator;
227  (void) SubstituteString(&fx_info->expression,"&&",fx_op);
228  *fx_op=(char) LogicalOrOperator;
229  (void) SubstituteString(&fx_info->expression,"||",fx_op);
230  *fx_op=(char) ExponentialNotation;
231  (void) SubstituteString(&fx_info->expression,"**",fx_op);
232  return(fx_info);
233 }
234 
235 /*
236 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
237 % %
238 % %
239 % %
240 % A d d N o i s e I m a g e %
241 % %
242 % %
243 % %
244 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
245 %
246 % AddNoiseImage() adds random noise to the image.
247 %
248 % The format of the AddNoiseImage method is:
249 %
250 % Image *AddNoiseImage(const Image *image,const NoiseType noise_type,
251 % const double attenuate,ExceptionInfo *exception)
252 %
253 % A description of each parameter follows:
254 %
255 % o image: the image.
256 %
257 % o channel: the channel type.
258 %
259 % o noise_type: The type of noise: Uniform, Gaussian, Multiplicative,
260 % Impulse, Laplacian, or Poisson.
261 %
262 % o attenuate: attenuate the random distribution.
263 %
264 % o exception: return any errors or warnings in this structure.
265 %
266 */
267 MagickExport Image *AddNoiseImage(const Image *image,const NoiseType noise_type,
268  const double attenuate,ExceptionInfo *exception)
269 {
270 #define AddNoiseImageTag "AddNoise/Image"
271 
272  CacheView
273  *image_view,
274  *noise_view;
275 
276  Image
277  *noise_image;
278 
280  status;
281 
283  progress;
284 
285  RandomInfo
287 
288  ssize_t
289  y;
290 
291 #if defined(MAGICKCORE_OPENMP_SUPPORT)
292  unsigned long
293  key;
294 #endif
295 
296  /*
297  Initialize noise image attributes.
298  */
299  assert(image != (const Image *) NULL);
300  assert(image->signature == MagickCoreSignature);
301  if (image->debug != MagickFalse)
302  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
303  assert(exception != (ExceptionInfo *) NULL);
304  assert(exception->signature == MagickCoreSignature);
305 #if defined(MAGICKCORE_OPENCL_SUPPORT)
306  noise_image=AccelerateAddNoiseImage(image,noise_type,attenuate,exception);
307  if (noise_image != (Image *) NULL)
308  return(noise_image);
309 #endif
310  noise_image=CloneImage(image,0,0,MagickTrue,exception);
311  if (noise_image == (Image *) NULL)
312  return((Image *) NULL);
313  if (SetImageStorageClass(noise_image,DirectClass,exception) == MagickFalse)
314  {
315  noise_image=DestroyImage(noise_image);
316  return((Image *) NULL);
317  }
318  /*
319  Add noise in each row.
320  */
321  status=MagickTrue;
322  progress=0;
323  random_info=AcquireRandomInfoThreadSet();
324  image_view=AcquireVirtualCacheView(image,exception);
325  noise_view=AcquireAuthenticCacheView(noise_image,exception);
326 #if defined(MAGICKCORE_OPENMP_SUPPORT)
327  key=GetRandomSecretKey(random_info[0]);
328  #pragma omp parallel for schedule(static) shared(progress,status) \
329  magick_number_threads(image,noise_image,image->rows,key == ~0UL)
330 #endif
331  for (y=0; y < (ssize_t) image->rows; y++)
332  {
333  const int
334  id = GetOpenMPThreadId();
335 
337  sync;
338 
339  register const Quantum
340  *magick_restrict p;
341 
342  register ssize_t
343  x;
344 
345  register Quantum
346  *magick_restrict q;
347 
348  if (status == MagickFalse)
349  continue;
350  p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
351  q=QueueCacheViewAuthenticPixels(noise_view,0,y,noise_image->columns,1,
352  exception);
353  if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
354  {
355  status=MagickFalse;
356  continue;
357  }
358  for (x=0; x < (ssize_t) image->columns; x++)
359  {
360  register ssize_t
361  i;
362 
363  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
364  {
365  PixelChannel channel = GetPixelChannelChannel(image,i);
366  PixelTrait traits = GetPixelChannelTraits(image,channel);
367  PixelTrait noise_traits=GetPixelChannelTraits(noise_image,channel);
368  if ((traits == UndefinedPixelTrait) ||
369  (noise_traits == UndefinedPixelTrait))
370  continue;
371  if ((noise_traits & CopyPixelTrait) != 0)
372  {
373  SetPixelChannel(noise_image,channel,p[i],q);
374  continue;
375  }
376  SetPixelChannel(noise_image,channel,ClampToQuantum(
377  GenerateDifferentialNoise(random_info[id],p[i],noise_type,attenuate)),
378  q);
379  }
380  p+=GetPixelChannels(image);
381  q+=GetPixelChannels(noise_image);
382  }
383  sync=SyncCacheViewAuthenticPixels(noise_view,exception);
384  if (sync == MagickFalse)
385  status=MagickFalse;
386  if (image->progress_monitor != (MagickProgressMonitor) NULL)
387  {
389  proceed;
390 
391 #if defined(MAGICKCORE_OPENMP_SUPPORT)
392  #pragma omp atomic
393 #endif
394  progress++;
395  proceed=SetImageProgress(image,AddNoiseImageTag,progress,image->rows);
396  if (proceed == MagickFalse)
397  status=MagickFalse;
398  }
399  }
400  noise_view=DestroyCacheView(noise_view);
401  image_view=DestroyCacheView(image_view);
402  random_info=DestroyRandomInfoThreadSet(random_info);
403  if (status == MagickFalse)
404  noise_image=DestroyImage(noise_image);
405  return(noise_image);
406 }
407 
408 /*
409 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
410 % %
411 % %
412 % %
413 % B l u e S h i f t I m a g e %
414 % %
415 % %
416 % %
417 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
418 %
419 % BlueShiftImage() mutes the colors of the image to simulate a scene at
420 % nighttime in the moonlight.
421 %
422 % The format of the BlueShiftImage method is:
423 %
424 % Image *BlueShiftImage(const Image *image,const double factor,
425 % ExceptionInfo *exception)
426 %
427 % A description of each parameter follows:
428 %
429 % o image: the image.
430 %
431 % o factor: the shift factor.
432 %
433 % o exception: return any errors or warnings in this structure.
434 %
435 */
436 MagickExport Image *BlueShiftImage(const Image *image,const double factor,
437  ExceptionInfo *exception)
438 {
439 #define BlueShiftImageTag "BlueShift/Image"
440 
441  CacheView
442  *image_view,
443  *shift_view;
444 
445  Image
446  *shift_image;
447 
449  status;
450 
452  progress;
453 
454  ssize_t
455  y;
456 
457  /*
458  Allocate blue shift image.
459  */
460  assert(image != (const Image *) NULL);
461  assert(image->signature == MagickCoreSignature);
462  if (image->debug != MagickFalse)
463  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
464  assert(exception != (ExceptionInfo *) NULL);
465  assert(exception->signature == MagickCoreSignature);
466  shift_image=CloneImage(image,0,0,MagickTrue,exception);
467  if (shift_image == (Image *) NULL)
468  return((Image *) NULL);
469  if (SetImageStorageClass(shift_image,DirectClass,exception) == MagickFalse)
470  {
471  shift_image=DestroyImage(shift_image);
472  return((Image *) NULL);
473  }
474  /*
475  Blue-shift DirectClass image.
476  */
477  status=MagickTrue;
478  progress=0;
479  image_view=AcquireVirtualCacheView(image,exception);
480  shift_view=AcquireAuthenticCacheView(shift_image,exception);
481 #if defined(MAGICKCORE_OPENMP_SUPPORT)
482  #pragma omp parallel for schedule(static) shared(progress,status) \
483  magick_number_threads(image,shift_image,image->rows,1)
484 #endif
485  for (y=0; y < (ssize_t) image->rows; y++)
486  {
488  sync;
489 
490  PixelInfo
491  pixel;
492 
493  Quantum
494  quantum;
495 
496  register const Quantum
497  *magick_restrict p;
498 
499  register ssize_t
500  x;
501 
502  register Quantum
503  *magick_restrict q;
504 
505  if (status == MagickFalse)
506  continue;
507  p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
508  q=QueueCacheViewAuthenticPixels(shift_view,0,y,shift_image->columns,1,
509  exception);
510  if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
511  {
512  status=MagickFalse;
513  continue;
514  }
515  for (x=0; x < (ssize_t) image->columns; x++)
516  {
517  quantum=GetPixelRed(image,p);
518  if (GetPixelGreen(image,p) < quantum)
519  quantum=GetPixelGreen(image,p);
520  if (GetPixelBlue(image,p) < quantum)
521  quantum=GetPixelBlue(image,p);
522  pixel.red=0.5*(GetPixelRed(image,p)+factor*quantum);
523  pixel.green=0.5*(GetPixelGreen(image,p)+factor*quantum);
524  pixel.blue=0.5*(GetPixelBlue(image,p)+factor*quantum);
525  quantum=GetPixelRed(image,p);
526  if (GetPixelGreen(image,p) > quantum)
527  quantum=GetPixelGreen(image,p);
528  if (GetPixelBlue(image,p) > quantum)
529  quantum=GetPixelBlue(image,p);
530  pixel.red=0.5*(pixel.red+factor*quantum);
531  pixel.green=0.5*(pixel.green+factor*quantum);
532  pixel.blue=0.5*(pixel.blue+factor*quantum);
533  SetPixelRed(shift_image,ClampToQuantum(pixel.red),q);
534  SetPixelGreen(shift_image,ClampToQuantum(pixel.green),q);
535  SetPixelBlue(shift_image,ClampToQuantum(pixel.blue),q);
536  p+=GetPixelChannels(image);
537  q+=GetPixelChannels(shift_image);
538  }
539  sync=SyncCacheViewAuthenticPixels(shift_view,exception);
540  if (sync == MagickFalse)
541  status=MagickFalse;
542  if (image->progress_monitor != (MagickProgressMonitor) NULL)
543  {
545  proceed;
546 
547 #if defined(MAGICKCORE_OPENMP_SUPPORT)
548  #pragma omp atomic
549 #endif
550  progress++;
551  proceed=SetImageProgress(image,BlueShiftImageTag,progress,image->rows);
552  if (proceed == MagickFalse)
553  status=MagickFalse;
554  }
555  }
556  image_view=DestroyCacheView(image_view);
557  shift_view=DestroyCacheView(shift_view);
558  if (status == MagickFalse)
559  shift_image=DestroyImage(shift_image);
560  return(shift_image);
561 }
562 
563 /*
564 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
565 % %
566 % %
567 % %
568 % C h a r c o a l I m a g e %
569 % %
570 % %
571 % %
572 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
573 %
574 % CharcoalImage() creates a new image that is a copy of an existing one with
575 % the edge highlighted. It allocates the memory necessary for the new Image
576 % structure and returns a pointer to the new image.
577 %
578 % The format of the CharcoalImage method is:
579 %
580 % Image *CharcoalImage(const Image *image,const double radius,
581 % const double sigma,ExceptionInfo *exception)
582 %
583 % A description of each parameter follows:
584 %
585 % o image: the image.
586 %
587 % o radius: the radius of the pixel neighborhood.
588 %
589 % o sigma: the standard deviation of the Gaussian, in pixels.
590 %
591 % o exception: return any errors or warnings in this structure.
592 %
593 */
594 MagickExport Image *CharcoalImage(const Image *image,const double radius,
595  const double sigma,ExceptionInfo *exception)
596 {
597  Image
598  *charcoal_image,
599  *edge_image;
600 
602  status;
603 
604  assert(image != (Image *) NULL);
605  assert(image->signature == MagickCoreSignature);
606  if (image->debug != MagickFalse)
607  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
608  assert(exception != (ExceptionInfo *) NULL);
609  assert(exception->signature == MagickCoreSignature);
610  edge_image=EdgeImage(image,radius,exception);
611  if (edge_image == (Image *) NULL)
612  return((Image *) NULL);
613  charcoal_image=(Image *) NULL;
614  status=ClampImage(edge_image,exception);
615  if (status != MagickFalse)
616  charcoal_image=BlurImage(edge_image,radius,sigma,exception);
617  edge_image=DestroyImage(edge_image);
618  if (charcoal_image == (Image *) NULL)
619  return((Image *) NULL);
620  status=NormalizeImage(charcoal_image,exception);
621  if (status != MagickFalse)
622  status=NegateImage(charcoal_image,MagickFalse,exception);
623  if (status != MagickFalse)
624  status=GrayscaleImage(charcoal_image,image->intensity,exception);
625  if (status == MagickFalse)
626  charcoal_image=DestroyImage(charcoal_image);
627  return(charcoal_image);
628 }
629 
630 /*
631 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
632 % %
633 % %
634 % %
635 % C o l o r i z e I m a g e %
636 % %
637 % %
638 % %
639 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
640 %
641 % ColorizeImage() blends the fill color with each pixel in the image.
642 % A percentage blend is specified with opacity. Control the application
643 % of different color components by specifying a different percentage for
644 % each component (e.g. 90/100/10 is 90% red, 100% green, and 10% blue).
645 %
646 % The format of the ColorizeImage method is:
647 %
648 % Image *ColorizeImage(const Image *image,const char *blend,
649 % const PixelInfo *colorize,ExceptionInfo *exception)
650 %
651 % A description of each parameter follows:
652 %
653 % o image: the image.
654 %
655 % o blend: A character string indicating the level of blending as a
656 % percentage.
657 %
658 % o colorize: A color value.
659 %
660 % o exception: return any errors or warnings in this structure.
661 %
662 */
663 MagickExport Image *ColorizeImage(const Image *image,const char *blend,
664  const PixelInfo *colorize,ExceptionInfo *exception)
665 {
666 #define ColorizeImageTag "Colorize/Image"
667 #define Colorize(pixel,blend_percentage,colorize) \
668  (((pixel)*(100.0-(blend_percentage))+(colorize)*(blend_percentage))/100.0)
669 
670  CacheView
671  *image_view;
672 
674  geometry_info;
675 
676  Image
677  *colorize_image;
678 
680  status;
681 
683  progress;
684 
686  flags;
687 
688  PixelInfo
689  blend_percentage;
690 
691  ssize_t
692  y;
693 
694  /*
695  Allocate colorized image.
696  */
697  assert(image != (const Image *) NULL);
698  assert(image->signature == MagickCoreSignature);
699  if (image->debug != MagickFalse)
700  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
701  assert(exception != (ExceptionInfo *) NULL);
702  assert(exception->signature == MagickCoreSignature);
703  colorize_image=CloneImage(image,0,0,MagickTrue,exception);
704  if (colorize_image == (Image *) NULL)
705  return((Image *) NULL);
706  if (SetImageStorageClass(colorize_image,DirectClass,exception) == MagickFalse)
707  {
708  colorize_image=DestroyImage(colorize_image);
709  return((Image *) NULL);
710  }
711  if ((IsGrayColorspace(colorize_image->colorspace) != MagickFalse) ||
712  (IsPixelInfoGray(colorize) != MagickFalse))
713  (void) SetImageColorspace(colorize_image,sRGBColorspace,exception);
714  if ((colorize_image->alpha_trait == UndefinedPixelTrait) &&
715  (colorize->alpha_trait != UndefinedPixelTrait))
716  (void) SetImageAlpha(colorize_image,OpaqueAlpha,exception);
717  if (blend == (const char *) NULL)
718  return(colorize_image);
719  GetPixelInfo(colorize_image,&blend_percentage);
720  flags=ParseGeometry(blend,&geometry_info);
721  blend_percentage.red=geometry_info.rho;
722  blend_percentage.green=geometry_info.rho;
723  blend_percentage.blue=geometry_info.rho;
724  blend_percentage.black=geometry_info.rho;
725  blend_percentage.alpha=(MagickRealType) TransparentAlpha;
726  if ((flags & SigmaValue) != 0)
727  blend_percentage.green=geometry_info.sigma;
728  if ((flags & XiValue) != 0)
729  blend_percentage.blue=geometry_info.xi;
730  if ((flags & PsiValue) != 0)
731  blend_percentage.alpha=geometry_info.psi;
732  if (blend_percentage.colorspace == CMYKColorspace)
733  {
734  if ((flags & PsiValue) != 0)
735  blend_percentage.black=geometry_info.psi;
736  if ((flags & ChiValue) != 0)
737  blend_percentage.alpha=geometry_info.chi;
738  }
739  /*
740  Colorize DirectClass image.
741  */
742  status=MagickTrue;
743  progress=0;
744  image_view=AcquireVirtualCacheView(colorize_image,exception);
745 #if defined(MAGICKCORE_OPENMP_SUPPORT)
746  #pragma omp parallel for schedule(static) shared(progress,status) \
747  magick_number_threads(colorize_image,colorize_image,colorize_image->rows,1)
748 #endif
749  for (y=0; y < (ssize_t) colorize_image->rows; y++)
750  {
752  sync;
753 
754  register Quantum
755  *magick_restrict q;
756 
757  register ssize_t
758  x;
759 
760  if (status == MagickFalse)
761  continue;
762  q=GetCacheViewAuthenticPixels(image_view,0,y,colorize_image->columns,1,
763  exception);
764  if (q == (Quantum *) NULL)
765  {
766  status=MagickFalse;
767  continue;
768  }
769  for (x=0; x < (ssize_t) colorize_image->columns; x++)
770  {
771  register ssize_t
772  i;
773 
774  for (i=0; i < (ssize_t) GetPixelChannels(colorize_image); i++)
775  {
776  PixelTrait traits = GetPixelChannelTraits(colorize_image,
777  (PixelChannel) i);
778  if (traits == UndefinedPixelTrait)
779  continue;
780  if ((traits & CopyPixelTrait) != 0)
781  continue;
782  SetPixelChannel(colorize_image,(PixelChannel) i,ClampToQuantum(
783  Colorize(q[i],GetPixelInfoChannel(&blend_percentage,(PixelChannel) i),
784  GetPixelInfoChannel(colorize,(PixelChannel) i))),q);
785  }
786  q+=GetPixelChannels(colorize_image);
787  }
788  sync=SyncCacheViewAuthenticPixels(image_view,exception);
789  if (sync == MagickFalse)
790  status=MagickFalse;
791  if (image->progress_monitor != (MagickProgressMonitor) NULL)
792  {
794  proceed;
795 
796 #if defined(MAGICKCORE_OPENMP_SUPPORT)
797  #pragma omp atomic
798 #endif
799  progress++;
800  proceed=SetImageProgress(image,ColorizeImageTag,progress,
801  colorize_image->rows);
802  if (proceed == MagickFalse)
803  status=MagickFalse;
804  }
805  }
806  image_view=DestroyCacheView(image_view);
807  if (status == MagickFalse)
808  colorize_image=DestroyImage(colorize_image);
809  return(colorize_image);
810 }
811 
812 /*
813 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
814 % %
815 % %
816 % %
817 % C o l o r M a t r i x I m a g e %
818 % %
819 % %
820 % %
821 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
822 %
823 % ColorMatrixImage() applies color transformation to an image. This method
824 % permits saturation changes, hue rotation, luminance to alpha, and various
825 % other effects. Although variable-sized transformation matrices can be used,
826 % typically one uses a 5x5 matrix for an RGBA image and a 6x6 for CMYKA
827 % (or RGBA with offsets). The matrix is similar to those used by Adobe Flash
828 % except offsets are in column 6 rather than 5 (in support of CMYKA images)
829 % and offsets are normalized (divide Flash offset by 255).
830 %
831 % The format of the ColorMatrixImage method is:
832 %
833 % Image *ColorMatrixImage(const Image *image,
834 % const KernelInfo *color_matrix,ExceptionInfo *exception)
835 %
836 % A description of each parameter follows:
837 %
838 % o image: the image.
839 %
840 % o color_matrix: the color matrix.
841 %
842 % o exception: return any errors or warnings in this structure.
843 %
844 */
845 /* FUTURE: modify to make use of a MagickMatrix Mutliply function
846  That should be provided in "matrix.c"
847  (ASIDE: actually distorts should do this too but currently doesn't)
848 */
849 
851  const KernelInfo *color_matrix,ExceptionInfo *exception)
852 {
853 #define ColorMatrixImageTag "ColorMatrix/Image"
854 
855  CacheView
856  *color_view,
857  *image_view;
858 
859  double
860  ColorMatrix[6][6] =
861  {
862  { 1.0, 0.0, 0.0, 0.0, 0.0, 0.0 },
863  { 0.0, 1.0, 0.0, 0.0, 0.0, 0.0 },
864  { 0.0, 0.0, 1.0, 0.0, 0.0, 0.0 },
865  { 0.0, 0.0, 0.0, 1.0, 0.0, 0.0 },
866  { 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 },
867  { 0.0, 0.0, 0.0, 0.0, 0.0, 1.0 }
868  };
869 
870  Image
871  *color_image;
872 
874  status;
875 
877  progress;
878 
879  register ssize_t
880  i;
881 
882  ssize_t
883  u,
884  v,
885  y;
886 
887  /*
888  Map given color_matrix, into a 6x6 matrix RGBKA and a constant
889  */
890  assert(image != (Image *) NULL);
891  assert(image->signature == MagickCoreSignature);
892  if (image->debug != MagickFalse)
893  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
894  assert(exception != (ExceptionInfo *) NULL);
895  assert(exception->signature == MagickCoreSignature);
896  i=0;
897  for (v=0; v < (ssize_t) color_matrix->height; v++)
898  for (u=0; u < (ssize_t) color_matrix->width; u++)
899  {
900  if ((v < 6) && (u < 6))
901  ColorMatrix[v][u]=color_matrix->values[i];
902  i++;
903  }
904  /*
905  Initialize color image.
906  */
907  color_image=CloneImage(image,0,0,MagickTrue,exception);
908  if (color_image == (Image *) NULL)
909  return((Image *) NULL);
910  if (SetImageStorageClass(color_image,DirectClass,exception) == MagickFalse)
911  {
912  color_image=DestroyImage(color_image);
913  return((Image *) NULL);
914  }
915  if (image->debug != MagickFalse)
916  {
917  char
918  format[MagickPathExtent],
919  *message;
920 
922  " ColorMatrix image with color matrix:");
923  message=AcquireString("");
924  for (v=0; v < 6; v++)
925  {
926  *message='\0';
927  (void) FormatLocaleString(format,MagickPathExtent,"%.20g: ",(double) v);
928  (void) ConcatenateString(&message,format);
929  for (u=0; u < 6; u++)
930  {
931  (void) FormatLocaleString(format,MagickPathExtent,"%+f ",
932  ColorMatrix[v][u]);
933  (void) ConcatenateString(&message,format);
934  }
935  (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
936  }
937  message=DestroyString(message);
938  }
939  /*
940  Apply the ColorMatrix to image.
941  */
942  status=MagickTrue;
943  progress=0;
944  image_view=AcquireVirtualCacheView(image,exception);
945  color_view=AcquireAuthenticCacheView(color_image,exception);
946 #if defined(MAGICKCORE_OPENMP_SUPPORT)
947  #pragma omp parallel for schedule(static) shared(progress,status) \
948  magick_number_threads(image,color_image,image->rows,1)
949 #endif
950  for (y=0; y < (ssize_t) image->rows; y++)
951  {
952  PixelInfo
953  pixel;
954 
955  register const Quantum
956  *magick_restrict p;
957 
958  register Quantum
959  *magick_restrict q;
960 
961  register ssize_t
962  x;
963 
964  if (status == MagickFalse)
965  continue;
966  p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
967  q=GetCacheViewAuthenticPixels(color_view,0,y,color_image->columns,1,
968  exception);
969  if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
970  {
971  status=MagickFalse;
972  continue;
973  }
974  GetPixelInfo(image,&pixel);
975  for (x=0; x < (ssize_t) image->columns; x++)
976  {
977  register ssize_t
978  v;
979 
980  size_t
981  height;
982 
983  GetPixelInfoPixel(image,p,&pixel);
984  height=color_matrix->height > 6 ? 6UL : color_matrix->height;
985  for (v=0; v < (ssize_t) height; v++)
986  {
987  double
988  sum;
989 
990  sum=ColorMatrix[v][0]*GetPixelRed(image,p)+ColorMatrix[v][1]*
991  GetPixelGreen(image,p)+ColorMatrix[v][2]*GetPixelBlue(image,p);
992  if (image->colorspace == CMYKColorspace)
993  sum+=ColorMatrix[v][3]*GetPixelBlack(image,p);
994  if (image->alpha_trait != UndefinedPixelTrait)
995  sum+=ColorMatrix[v][4]*GetPixelAlpha(image,p);
996  sum+=QuantumRange*ColorMatrix[v][5];
997  switch (v)
998  {
999  case 0: pixel.red=sum; break;
1000  case 1: pixel.green=sum; break;
1001  case 2: pixel.blue=sum; break;
1002  case 3: pixel.black=sum; break;
1003  case 4: pixel.alpha=sum; break;
1004  default: break;
1005  }
1006  }
1007  SetPixelViaPixelInfo(color_image,&pixel,q);
1008  p+=GetPixelChannels(image);
1009  q+=GetPixelChannels(color_image);
1010  }
1011  if (SyncCacheViewAuthenticPixels(color_view,exception) == MagickFalse)
1012  status=MagickFalse;
1013  if (image->progress_monitor != (MagickProgressMonitor) NULL)
1014  {
1016  proceed;
1017 
1018 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1019  #pragma omp atomic
1020 #endif
1021  progress++;
1022  proceed=SetImageProgress(image,ColorMatrixImageTag,progress,
1023  image->rows);
1024  if (proceed == MagickFalse)
1025  status=MagickFalse;
1026  }
1027  }
1028  color_view=DestroyCacheView(color_view);
1029  image_view=DestroyCacheView(image_view);
1030  if (status == MagickFalse)
1031  color_image=DestroyImage(color_image);
1032  return(color_image);
1033 }
1034 
1035 /*
1036 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1037 % %
1038 % %
1039 % %
1040 + D e s t r o y F x I n f o %
1041 % %
1042 % %
1043 % %
1044 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1045 %
1046 % DestroyFxInfo() deallocates memory associated with an FxInfo structure.
1047 %
1048 % The format of the DestroyFxInfo method is:
1049 %
1050 % ImageInfo *DestroyFxInfo(ImageInfo *fx_info)
1051 %
1052 % A description of each parameter follows:
1053 %
1054 % o fx_info: the fx info.
1055 %
1056 */
1058 {
1059  register ssize_t
1060  i;
1061 
1062  fx_info->exception=DestroyExceptionInfo(fx_info->exception);
1063  fx_info->expression=DestroyString(fx_info->expression);
1064  fx_info->symbols=DestroySplayTree(fx_info->symbols);
1065  fx_info->colors=DestroySplayTree(fx_info->colors);
1066  for (i=(ssize_t) GetImageListLength(fx_info->images)-1; i >= 0; i--)
1067  fx_info->view[i]=DestroyCacheView(fx_info->view[i]);
1068  fx_info->view=(CacheView **) RelinquishMagickMemory(fx_info->view);
1069  fx_info->random_info=DestroyRandomInfo(fx_info->random_info);
1070  fx_info=(FxInfo *) RelinquishMagickMemory(fx_info);
1071  return(fx_info);
1072 }
1073 
1074 /*
1075 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1076 % %
1077 % %
1078 % %
1079 + 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 %
1080 % %
1081 % %
1082 % %
1083 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1084 %
1085 % FxEvaluateChannelExpression() evaluates an expression and returns the
1086 % results.
1087 %
1088 % The format of the FxEvaluateExpression method is:
1089 %
1090 % double FxEvaluateChannelExpression(FxInfo *fx_info,
1091 % const PixelChannel channel,const ssize_t x,const ssize_t y,
1092 % double *alpha,Exceptioninfo *exception)
1093 % double FxEvaluateExpression(FxInfo *fx_info,
1094 % double *alpha,Exceptioninfo *exception)
1095 %
1096 % A description of each parameter follows:
1097 %
1098 % o fx_info: the fx info.
1099 %
1100 % o channel: the channel.
1101 %
1102 % o x,y: the pixel position.
1103 %
1104 % o alpha: the result.
1105 %
1106 % o exception: return any errors or warnings in this structure.
1107 %
1108 */
1109 
1110 static double FxChannelStatistics(FxInfo *fx_info,Image *image,
1111  PixelChannel channel,const char *symbol,ExceptionInfo *exception)
1112 {
1113  ChannelType
1114  channel_mask;
1115 
1116  char
1117  key[MagickPathExtent],
1118  statistic[MagickPathExtent];
1119 
1120  const char
1121  *value;
1122 
1123  register const char
1124  *p;
1125 
1126  channel_mask=UndefinedChannel;
1127  for (p=symbol; (*p != '.') && (*p != '\0'); p++) ;
1128  if (*p == '.')
1129  {
1130  ssize_t
1131  option;
1132 
1134  if (option >= 0)
1135  {
1136  channel=(PixelChannel) option;
1137  channel_mask=SetPixelChannelMask(image,(ChannelType)
1138  (1UL << channel));
1139  }
1140  }
1141  (void) FormatLocaleString(key,MagickPathExtent,"%p.%.20g.%s",(void *) image,
1142  (double) channel,symbol);
1143  value=(const char *) GetValueFromSplayTree(fx_info->symbols,key);
1144  if (value != (const char *) NULL)
1145  {
1146  if (channel_mask != UndefinedChannel)
1147  (void) SetPixelChannelMask(image,channel_mask);
1148  return(QuantumScale*StringToDouble(value,(char **) NULL));
1149  }
1150  (void) DeleteNodeFromSplayTree(fx_info->symbols,key);
1151  if (LocaleNCompare(symbol,"depth",5) == 0)
1152  {
1153  size_t
1154  depth;
1155 
1156  depth=GetImageDepth(image,exception);
1157  (void) FormatLocaleString(statistic,MagickPathExtent,"%.20g",(double)
1158  depth);
1159  }
1160  if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1161  {
1162  double
1163  kurtosis,
1164  skewness;
1165 
1166  (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
1167  (void) FormatLocaleString(statistic,MagickPathExtent,"%.20g",kurtosis);
1168  }
1169  if (LocaleNCompare(symbol,"maxima",6) == 0)
1170  {
1171  double
1172  maxima,
1173  minima;
1174 
1175  (void) GetImageRange(image,&minima,&maxima,exception);
1176  (void) FormatLocaleString(statistic,MagickPathExtent,"%.20g",maxima);
1177  }
1178  if (LocaleNCompare(symbol,"mean",4) == 0)
1179  {
1180  double
1181  mean,
1182  standard_deviation;
1183 
1184  (void) GetImageMean(image,&mean,&standard_deviation,exception);
1185  (void) FormatLocaleString(statistic,MagickPathExtent,"%.20g",mean);
1186  }
1187  if (LocaleNCompare(symbol,"minima",6) == 0)
1188  {
1189  double
1190  maxima,
1191  minima;
1192 
1193  (void) GetImageRange(image,&minima,&maxima,exception);
1194  (void) FormatLocaleString(statistic,MagickPathExtent,"%.20g",minima);
1195  }
1196  if (LocaleNCompare(symbol,"skewness",8) == 0)
1197  {
1198  double
1199  kurtosis,
1200  skewness;
1201 
1202  (void) GetImageKurtosis(image,&kurtosis,&skewness,exception);
1203  (void) FormatLocaleString(statistic,MagickPathExtent,"%.20g",skewness);
1204  }
1205  if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1206  {
1207  double
1208  mean,
1209  standard_deviation;
1210 
1211  (void) GetImageMean(image,&mean,&standard_deviation,exception);
1212  (void) FormatLocaleString(statistic,MagickPathExtent,"%.20g",
1213  standard_deviation);
1214  }
1215  if (channel_mask != UndefinedChannel)
1216  (void) SetPixelChannelMask(image,channel_mask);
1217  (void) AddValueToSplayTree(fx_info->symbols,ConstantString(key),
1218  ConstantString(statistic));
1219  return(QuantumScale*StringToDouble(statistic,(char **) NULL));
1220 }
1221 
1222 static double
1223  FxEvaluateSubexpression(FxInfo *,const PixelChannel,const ssize_t,
1224  const ssize_t,const char *,const size_t,double *,ExceptionInfo *);
1225 
1226 static inline MagickBooleanType IsFxFunction(const char *expression,
1227  const char *name,const size_t length)
1228 {
1229  int
1230  c;
1231 
1232  c=name[length];
1233  if ((LocaleNCompare(expression,name,length) == 0) &&
1234  ((isspace(c) == 0) || (c == '(')))
1235  return(MagickTrue);
1236  return(MagickFalse);
1237 }
1238 
1240 {
1241  if (beta != 0)
1242  return(FxGCD(beta,alpha % beta));
1243  return(alpha);
1244 }
1245 
1246 static inline const char *FxSubexpression(const char *expression,
1247  ExceptionInfo *exception)
1248 {
1249  const char
1250  *subexpression;
1251 
1252  register ssize_t
1253  level;
1254 
1255  level=0;
1256  subexpression=expression;
1257  while ((*subexpression != '\0') &&
1258  ((level != 1) || (strchr(")",(int) *subexpression) == (char *) NULL)))
1259  {
1260  if (strchr("(",(int) *subexpression) != (char *) NULL)
1261  level++;
1262  else
1263  if (strchr(")",(int) *subexpression) != (char *) NULL)
1264  level--;
1265  subexpression++;
1266  }
1267  if (*subexpression == '\0')
1269  "UnbalancedParenthesis","`%s'",expression);
1270  return(subexpression);
1271 }
1272 
1273 static double FxGetSymbol(FxInfo *fx_info,const PixelChannel channel,
1274  const ssize_t x,const ssize_t y,const char *expression,const size_t depth,
1275  ExceptionInfo *exception)
1276 {
1277  char
1278  *q,
1279  symbol[MagickPathExtent];
1280 
1281  const char
1282  *p,
1283  *value;
1284 
1285  Image
1286  *image;
1287 
1289  status;
1290 
1291  PixelInfo
1292  pixel;
1293 
1294  double
1295  alpha,
1296  beta;
1297 
1298  PointInfo
1299  point;
1300 
1301  register ssize_t
1302  i;
1303 
1304  size_t
1305  level;
1306 
1307  p=expression;
1308  i=GetImageIndexInList(fx_info->images);
1309  level=0;
1310  point.x=(double) x;
1311  point.y=(double) y;
1312  if (isalpha((int) ((unsigned char) *(p+1))) == 0)
1313  {
1314  char
1315  *subexpression;
1316 
1317  subexpression=AcquireString(expression);
1318  if (strchr("suv",(int) *p) != (char *) NULL)
1319  {
1320  switch (*p)
1321  {
1322  case 's':
1323  default:
1324  {
1325  i=GetImageIndexInList(fx_info->images);
1326  break;
1327  }
1328  case 'u': i=0; break;
1329  case 'v': i=1; break;
1330  }
1331  p++;
1332  if (*p == '[')
1333  {
1334  level++;
1335  q=subexpression;
1336  for (p++; *p != '\0'; )
1337  {
1338  if (*p == '[')
1339  level++;
1340  else
1341  if (*p == ']')
1342  {
1343  level--;
1344  if (level == 0)
1345  break;
1346  }
1347  *q++=(*p++);
1348  }
1349  *q='\0';
1350  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1351  depth,&beta,exception);
1352  i=(ssize_t) alpha;
1353  if (*p != '\0')
1354  p++;
1355  }
1356  if (*p == '.')
1357  p++;
1358  }
1359  if ((*p == 'p') && (isalpha((int) ((unsigned char) *(p+1))) == 0))
1360  {
1361  p++;
1362  if (*p == '{')
1363  {
1364  level++;
1365  q=subexpression;
1366  for (p++; *p != '\0'; )
1367  {
1368  if (*p == '{')
1369  level++;
1370  else
1371  if (*p == '}')
1372  {
1373  level--;
1374  if (level == 0)
1375  break;
1376  }
1377  *q++=(*p++);
1378  }
1379  *q='\0';
1380  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1381  depth,&beta,exception);
1382  point.x=alpha;
1383  point.y=beta;
1384  if (*p != '\0')
1385  p++;
1386  }
1387  else
1388  if (*p == '[')
1389  {
1390  level++;
1391  q=subexpression;
1392  for (p++; *p != '\0'; )
1393  {
1394  if (*p == '[')
1395  level++;
1396  else
1397  if (*p == ']')
1398  {
1399  level--;
1400  if (level == 0)
1401  break;
1402  }
1403  *q++=(*p++);
1404  }
1405  *q='\0';
1406  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
1407  depth,&beta,exception);
1408  point.x+=alpha;
1409  point.y+=beta;
1410  if (*p != '\0')
1411  p++;
1412  }
1413  if (*p == '.')
1414  p++;
1415  }
1416  subexpression=DestroyString(subexpression);
1417  }
1418  image=GetImageFromList(fx_info->images,i);
1419  if (image == (Image *) NULL)
1420  {
1422  "NoSuchImage","`%s'",expression);
1423  return(0.0);
1424  }
1425  i=GetImageIndexInList(image);
1426  GetPixelInfo(image,&pixel);
1427  status=InterpolatePixelInfo(image,fx_info->view[i],image->interpolate,
1428  point.x,point.y,&pixel,exception);
1429  (void) status;
1430  if ((strlen(p) > 2) && (LocaleCompare(p,"intensity") != 0) &&
1431  (LocaleCompare(p,"luma") != 0) && (LocaleCompare(p,"luminance") != 0) &&
1432  (LocaleCompare(p,"hue") != 0) && (LocaleCompare(p,"saturation") != 0) &&
1433  (LocaleCompare(p,"lightness") != 0))
1434  {
1435  char
1436  name[MagickPathExtent];
1437 
1438  (void) CopyMagickString(name,p,MagickPathExtent);
1439  for (q=name+(strlen(name)-1); q > name; q--)
1440  {
1441  if (*q == ')')
1442  break;
1443  if (*q == '.')
1444  {
1445  *q='\0';
1446  break;
1447  }
1448  }
1449  if ((strlen(name) > 2) &&
1450  (GetValueFromSplayTree(fx_info->symbols,name) == (const char *) NULL))
1451  {
1452  PixelInfo
1453  *color;
1454 
1455  color=(PixelInfo *) GetValueFromSplayTree(fx_info->colors,name);
1456  if (color != (PixelInfo *) NULL)
1457  {
1458  pixel=(*color);
1459  p+=strlen(name);
1460  }
1461  else
1462  {
1464  status;
1465 
1466  status=QueryColorCompliance(name,AllCompliance,&pixel,
1467  fx_info->exception);
1468  if (status != MagickFalse)
1469  {
1470  (void) AddValueToSplayTree(fx_info->colors,ConstantString(
1471  name),ClonePixelInfo(&pixel));
1472  p+=strlen(name);
1473  }
1474  }
1475  }
1476  }
1477  (void) CopyMagickString(symbol,p,MagickPathExtent);
1478  StripString(symbol);
1479  if (*symbol == '\0')
1480  {
1481  switch (channel)
1482  {
1483  case RedPixelChannel: return(QuantumScale*pixel.red);
1484  case GreenPixelChannel: return(QuantumScale*pixel.green);
1485  case BluePixelChannel: return(QuantumScale*pixel.blue);
1486  case BlackPixelChannel:
1487  {
1488  if (image->colorspace != CMYKColorspace)
1489  {
1490  (void) ThrowMagickException(exception,GetMagickModule(),
1491  ImageError,"ColorSeparatedImageRequired","`%s'",
1492  image->filename);
1493  return(0.0);
1494  }
1495  return(QuantumScale*pixel.black);
1496  }
1497  case AlphaPixelChannel:
1498  {
1499  if (pixel.alpha_trait == UndefinedPixelTrait)
1500  return(1.0);
1501  alpha=(double) (QuantumScale*pixel.alpha);
1502  return(alpha);
1503  }
1504  case CompositePixelChannel:
1505  {
1506  Quantum
1507  quantum_pixel[MaxPixelChannels];
1508 
1509  SetPixelViaPixelInfo(image,&pixel,quantum_pixel);
1510  return(QuantumScale*GetPixelIntensity(image,quantum_pixel));
1511  }
1512  case IndexPixelChannel:
1513  return(0.0);
1514  default:
1515  break;
1516  }
1518  "UnableToParseExpression","`%s'",p);
1519  return(0.0);
1520  }
1521  switch (*symbol)
1522  {
1523  case 'A':
1524  case 'a':
1525  {
1526  if (LocaleCompare(symbol,"a") == 0)
1527  return((QuantumScale*pixel.alpha));
1528  break;
1529  }
1530  case 'B':
1531  case 'b':
1532  {
1533  if (LocaleCompare(symbol,"b") == 0)
1534  return(QuantumScale*pixel.blue);
1535  break;
1536  }
1537  case 'C':
1538  case 'c':
1539  {
1540  if (IsFxFunction(symbol,"channel",7) != MagickFalse)
1541  {
1542  GeometryInfo
1543  channel_info;
1544 
1546  flags;
1547 
1548  flags=ParseGeometry(symbol+7,&channel_info);
1549  if (image->colorspace == CMYKColorspace)
1550  switch (channel)
1551  {
1552  case CyanPixelChannel:
1553  {
1554  if ((flags & RhoValue) == 0)
1555  return(0.0);
1556  return(channel_info.rho);
1557  }
1558  case MagentaPixelChannel:
1559  {
1560  if ((flags & SigmaValue) == 0)
1561  return(0.0);
1562  return(channel_info.sigma);
1563  }
1564  case YellowPixelChannel:
1565  {
1566  if ((flags & XiValue) == 0)
1567  return(0.0);
1568  return(channel_info.xi);
1569  }
1570  case BlackPixelChannel:
1571  {
1572  if ((flags & PsiValue) == 0)
1573  return(0.0);
1574  return(channel_info.psi);
1575  }
1576  case AlphaPixelChannel:
1577  {
1578  if ((flags & ChiValue) == 0)
1579  return(0.0);
1580  return(channel_info.chi);
1581  }
1582  default:
1583  return(0.0);
1584  }
1585  switch (channel)
1586  {
1587  case RedPixelChannel:
1588  {
1589  if ((flags & RhoValue) == 0)
1590  return(0.0);
1591  return(channel_info.rho);
1592  }
1593  case GreenPixelChannel:
1594  {
1595  if ((flags & SigmaValue) == 0)
1596  return(0.0);
1597  return(channel_info.sigma);
1598  }
1599  case BluePixelChannel:
1600  {
1601  if ((flags & XiValue) == 0)
1602  return(0.0);
1603  return(channel_info.xi);
1604  }
1605  case BlackPixelChannel:
1606  {
1607  if ((flags & ChiValue) == 0)
1608  return(0.0);
1609  return(channel_info.chi);
1610  }
1611  case AlphaPixelChannel:
1612  {
1613  if ((flags & PsiValue) == 0)
1614  return(0.0);
1615  return(channel_info.psi);
1616  }
1617  default:
1618  return(0.0);
1619  }
1620  }
1621  if (LocaleCompare(symbol,"c") == 0)
1622  return(QuantumScale*pixel.red);
1623  break;
1624  }
1625  case 'D':
1626  case 'd':
1627  {
1628  if (LocaleNCompare(symbol,"depth",5) == 0)
1629  return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1630  break;
1631  }
1632  case 'E':
1633  case 'e':
1634  {
1635  if (LocaleCompare(symbol,"extent") == 0)
1636  {
1637  if (image->extent != 0)
1638  return((double) image->extent);
1639  return((double) GetBlobSize(image));
1640  }
1641  break;
1642  }
1643  case 'G':
1644  case 'g':
1645  {
1646  if (LocaleCompare(symbol,"g") == 0)
1647  return(QuantumScale*pixel.green);
1648  break;
1649  }
1650  case 'K':
1651  case 'k':
1652  {
1653  if (LocaleNCompare(symbol,"kurtosis",8) == 0)
1654  return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1655  if (LocaleCompare(symbol,"k") == 0)
1656  {
1657  if (image->colorspace != CMYKColorspace)
1658  {
1659  (void) ThrowMagickException(exception,GetMagickModule(),
1660  OptionError,"ColorSeparatedImageRequired","`%s'",
1661  image->filename);
1662  return(0.0);
1663  }
1664  return(QuantumScale*pixel.black);
1665  }
1666  break;
1667  }
1668  case 'H':
1669  case 'h':
1670  {
1671  if (LocaleCompare(symbol,"h") == 0)
1672  return((double) image->rows);
1673  if (LocaleCompare(symbol,"hue") == 0)
1674  {
1675  double
1676  hue,
1677  lightness,
1678  saturation;
1679 
1680  ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
1681  &lightness);
1682  return(hue);
1683  }
1684  break;
1685  }
1686  case 'I':
1687  case 'i':
1688  {
1689  if ((LocaleCompare(symbol,"image.depth") == 0) ||
1690  (LocaleCompare(symbol,"image.minima") == 0) ||
1691  (LocaleCompare(symbol,"image.maxima") == 0) ||
1692  (LocaleCompare(symbol,"image.mean") == 0) ||
1693  (LocaleCompare(symbol,"image.kurtosis") == 0) ||
1694  (LocaleCompare(symbol,"image.skewness") == 0) ||
1695  (LocaleCompare(symbol,"image.standard_deviation") == 0))
1696  return(FxChannelStatistics(fx_info,image,channel,symbol+6,exception));
1697  if (LocaleCompare(symbol,"image.resolution.x") == 0)
1698  return(image->resolution.x);
1699  if (LocaleCompare(symbol,"image.resolution.y") == 0)
1700  return(image->resolution.y);
1701  if (LocaleCompare(symbol,"intensity") == 0)
1702  {
1703  Quantum
1704  quantum_pixel[MaxPixelChannels];
1705 
1706  SetPixelViaPixelInfo(image,&pixel,quantum_pixel);
1707  return(QuantumScale*GetPixelIntensity(image,quantum_pixel));
1708  }
1709  if (LocaleCompare(symbol,"i") == 0)
1710  return((double) x);
1711  break;
1712  }
1713  case 'J':
1714  case 'j':
1715  {
1716  if (LocaleCompare(symbol,"j") == 0)
1717  return((double) y);
1718  break;
1719  }
1720  case 'L':
1721  case 'l':
1722  {
1723  if (LocaleCompare(symbol,"lightness") == 0)
1724  {
1725  double
1726  hue,
1727  lightness,
1728  saturation;
1729 
1730  ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
1731  &lightness);
1732  return(lightness);
1733  }
1734  if (LocaleCompare(symbol,"luma") == 0)
1735  {
1736  double
1737  luma;
1738 
1739  luma=0.212656*pixel.red+0.715158*pixel.green+0.072186*pixel.blue;
1740  return(QuantumScale*luma);
1741  }
1742  if (LocaleCompare(symbol,"luminance") == 0)
1743  {
1744  double
1745  luminence;
1746 
1747  luminence=0.212656*pixel.red+0.715158*pixel.green+0.072186*pixel.blue;
1748  return(QuantumScale*luminence);
1749  }
1750  break;
1751  }
1752  case 'M':
1753  case 'm':
1754  {
1755  if (LocaleNCompare(symbol,"maxima",6) == 0)
1756  return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1757  if (LocaleNCompare(symbol,"mean",4) == 0)
1758  return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1759  if (LocaleNCompare(symbol,"minima",6) == 0)
1760  return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1761  if (LocaleCompare(symbol,"m") == 0)
1762  return(QuantumScale*pixel.green);
1763  break;
1764  }
1765  case 'N':
1766  case 'n':
1767  {
1768  if (LocaleCompare(symbol,"n") == 0)
1769  return((double) GetImageListLength(fx_info->images));
1770  break;
1771  }
1772  case 'O':
1773  case 'o':
1774  {
1775  if (LocaleCompare(symbol,"o") == 0)
1776  return(QuantumScale*pixel.alpha);
1777  break;
1778  }
1779  case 'P':
1780  case 'p':
1781  {
1782  if (LocaleCompare(symbol,"page.height") == 0)
1783  return((double) image->page.height);
1784  if (LocaleCompare(symbol,"page.width") == 0)
1785  return((double) image->page.width);
1786  if (LocaleCompare(symbol,"page.x") == 0)
1787  return((double) image->page.x);
1788  if (LocaleCompare(symbol,"page.y") == 0)
1789  return((double) image->page.y);
1790  if (LocaleCompare(symbol,"printsize.x") == 0)
1791  return(PerceptibleReciprocal(image->resolution.x)*image->columns);
1792  if (LocaleCompare(symbol,"printsize.y") == 0)
1793  return(PerceptibleReciprocal(image->resolution.y)*image->rows);
1794  break;
1795  }
1796  case 'Q':
1797  case 'q':
1798  {
1799  if (LocaleCompare(symbol,"quality") == 0)
1800  return((double) image->quality);
1801  break;
1802  }
1803  case 'R':
1804  case 'r':
1805  {
1806  if (LocaleCompare(symbol,"resolution.x") == 0)
1807  return(image->resolution.x);
1808  if (LocaleCompare(symbol,"resolution.y") == 0)
1809  return(image->resolution.y);
1810  if (LocaleCompare(symbol,"r") == 0)
1811  return(QuantumScale*pixel.red);
1812  break;
1813  }
1814  case 'S':
1815  case 's':
1816  {
1817  if (LocaleCompare(symbol,"saturation") == 0)
1818  {
1819  double
1820  hue,
1821  lightness,
1822  saturation;
1823 
1824  ConvertRGBToHSL(pixel.red,pixel.green,pixel.blue,&hue,&saturation,
1825  &lightness);
1826  return(saturation);
1827  }
1828  if (LocaleNCompare(symbol,"skewness",8) == 0)
1829  return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1830  if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
1831  return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
1832  break;
1833  }
1834  case 'T':
1835  case 't':
1836  {
1837  if (LocaleCompare(symbol,"t") == 0)
1838  return((double) GetImageIndexInList(fx_info->images));
1839  break;
1840  }
1841  case 'W':
1842  case 'w':
1843  {
1844  if (LocaleCompare(symbol,"w") == 0)
1845  return((double) image->columns);
1846  break;
1847  }
1848  case 'Y':
1849  case 'y':
1850  {
1851  if (LocaleCompare(symbol,"y") == 0)
1852  return(QuantumScale*pixel.blue);
1853  break;
1854  }
1855  case 'Z':
1856  case 'z':
1857  {
1858  if (LocaleCompare(symbol,"z") == 0)
1859  return((double) GetImageDepth(image,fx_info->exception));
1860  break;
1861  }
1862  default:
1863  break;
1864  }
1865  value=(const char *) GetValueFromSplayTree(fx_info->symbols,symbol);
1866  if (value != (const char *) NULL)
1867  return(StringToDouble(value,(char **) NULL));
1869  "UnableToParseExpression","`%s'",symbol);
1870  return(0.0);
1871 }
1872 
1873 static const char *FxOperatorPrecedence(const char *expression,
1874  ExceptionInfo *exception)
1875 {
1876  typedef enum
1877  {
1878  UndefinedPrecedence,
1879  NullPrecedence,
1880  BitwiseComplementPrecedence,
1881  ExponentPrecedence,
1882  ExponentialNotationPrecedence,
1883  MultiplyPrecedence,
1884  AdditionPrecedence,
1885  ShiftPrecedence,
1886  RelationalPrecedence,
1887  EquivalencyPrecedence,
1888  BitwiseAndPrecedence,
1889  BitwiseOrPrecedence,
1890  LogicalAndPrecedence,
1891  LogicalOrPrecedence,
1892  TernaryPrecedence,
1893  AssignmentPrecedence,
1894  CommaPrecedence,
1895  SeparatorPrecedence
1896  } FxPrecedence;
1897 
1898  FxPrecedence
1899  precedence,
1900  target;
1901 
1902  register const char
1903  *subexpression;
1904 
1905  register int
1906  c;
1907 
1908  size_t
1909  level;
1910 
1911  c=(-1);
1912  level=0;
1913  subexpression=(const char *) NULL;
1914  target=NullPrecedence;
1915  while ((c != '\0') && (*expression != '\0'))
1916  {
1917  precedence=UndefinedPrecedence;
1918  if ((isspace((int) ((unsigned char) *expression)) != 0) || (c == (int) '@'))
1919  {
1920  expression++;
1921  continue;
1922  }
1923  switch (*expression)
1924  {
1925  case 'A':
1926  case 'a':
1927  {
1928 #if defined(MAGICKCORE_HAVE_ACOSH)
1929  if (IsFxFunction(expression,"acosh",5) != MagickFalse)
1930  {
1931  expression+=5;
1932  break;
1933  }
1934 #endif
1935 #if defined(MAGICKCORE_HAVE_ASINH)
1936  if (IsFxFunction(expression,"asinh",5) != MagickFalse)
1937  {
1938  expression+=5;
1939  break;
1940  }
1941 #endif
1942 #if defined(MAGICKCORE_HAVE_ATANH)
1943  if (IsFxFunction(expression,"atanh",5) != MagickFalse)
1944  {
1945  expression+=5;
1946  break;
1947  }
1948 #endif
1949  if (IsFxFunction(expression,"atan2",5) != MagickFalse)
1950  {
1951  expression+=5;
1952  break;
1953  }
1954  break;
1955  }
1956  case 'E':
1957  case 'e':
1958  {
1959  if ((isdigit(c) != 0) &&
1960  ((LocaleNCompare(expression,"E+",2) == 0) ||
1961  (LocaleNCompare(expression,"E-",2) == 0)))
1962  {
1963  expression+=2; /* scientific notation */
1964  break;
1965  }
1966  }
1967  case 'J':
1968  case 'j':
1969  {
1970  if ((IsFxFunction(expression,"j0",2) != MagickFalse) ||
1971  (IsFxFunction(expression,"j1",2) != MagickFalse))
1972  {
1973  expression+=2;
1974  break;
1975  }
1976  break;
1977  }
1978  case '#':
1979  {
1980  while (isxdigit((int) ((unsigned char) *(expression+1))) != 0)
1981  expression++;
1982  break;
1983  }
1984  default:
1985  break;
1986  }
1987  if ((c == (int) '{') || (c == (int) '['))
1988  level++;
1989  else
1990  if ((c == (int) '}') || (c == (int) ']'))
1991  level--;
1992  if (level == 0)
1993  switch ((unsigned char) *expression)
1994  {
1995  case '~':
1996  case '!':
1997  {
1998  precedence=BitwiseComplementPrecedence;
1999  break;
2000  }
2001  case '^':
2002  case '@':
2003  {
2004  precedence=ExponentPrecedence;
2005  break;
2006  }
2007  default:
2008  {
2009  if (((c != 0) && ((isdigit(c) != 0) ||
2010  (strchr(")",c) != (char *) NULL))) &&
2011  (((islower((int) ((unsigned char) *expression)) != 0) ||
2012  (strchr("(",(int) ((unsigned char) *expression)) != (char *) NULL)) ||
2013  ((isdigit(c) == 0) &&
2014  (isdigit((int) ((unsigned char) *expression)) != 0))) &&
2015  (strchr("xy",(int) ((unsigned char) *expression)) == (char *) NULL))
2016  precedence=MultiplyPrecedence;
2017  break;
2018  }
2019  case '*':
2020  case '/':
2021  case '%':
2022  {
2023  precedence=MultiplyPrecedence;
2024  break;
2025  }
2026  case '+':
2027  case '-':
2028  {
2029  if ((strchr("(+-/*%:&^|<>~,",c) == (char *) NULL) ||
2030  (isalpha(c) != 0))
2031  precedence=AdditionPrecedence;
2032  break;
2033  }
2034  case LeftShiftOperator:
2035  case RightShiftOperator:
2036  {
2037  precedence=ShiftPrecedence;
2038  break;
2039  }
2040  case '<':
2041  case LessThanEqualOperator:
2043  case '>':
2044  {
2045  precedence=RelationalPrecedence;
2046  break;
2047  }
2048  case EqualOperator:
2049  case NotEqualOperator:
2050  {
2051  precedence=EquivalencyPrecedence;
2052  break;
2053  }
2054  case '&':
2055  {
2056  precedence=BitwiseAndPrecedence;
2057  break;
2058  }
2059  case '|':
2060  {
2061  precedence=BitwiseOrPrecedence;
2062  break;
2063  }
2064  case LogicalAndOperator:
2065  {
2066  precedence=LogicalAndPrecedence;
2067  break;
2068  }
2069  case LogicalOrOperator:
2070  {
2071  precedence=LogicalOrPrecedence;
2072  break;
2073  }
2074  case ExponentialNotation:
2075  {
2076  precedence=ExponentialNotationPrecedence;
2077  break;
2078  }
2079  case ':':
2080  case '?':
2081  {
2082  precedence=TernaryPrecedence;
2083  break;
2084  }
2085  case '=':
2086  {
2087  precedence=AssignmentPrecedence;
2088  break;
2089  }
2090  case ',':
2091  {
2092  precedence=CommaPrecedence;
2093  break;
2094  }
2095  case ';':
2096  {
2097  precedence=SeparatorPrecedence;
2098  break;
2099  }
2100  }
2101  if ((precedence == BitwiseComplementPrecedence) ||
2102  (precedence == TernaryPrecedence) ||
2103  (precedence == AssignmentPrecedence))
2104  {
2105  if (precedence > target)
2106  {
2107  /*
2108  Right-to-left associativity.
2109  */
2110  target=precedence;
2111  subexpression=expression;
2112  }
2113  }
2114  else
2115  if (precedence >= target)
2116  {
2117  /*
2118  Left-to-right associativity.
2119  */
2120  target=precedence;
2121  subexpression=expression;
2122  }
2123  if (strchr("(",(int) *expression) != (char *) NULL)
2124  expression=FxSubexpression(expression,exception);
2125  c=(int) (*expression++);
2126  }
2127  return(subexpression);
2128 }
2129 
2130 static double FxEvaluateSubexpression(FxInfo *fx_info,
2131  const PixelChannel channel,const ssize_t x,const ssize_t y,
2132  const char *expression,const size_t depth,double *beta,
2133  ExceptionInfo *exception)
2134 {
2135 #define FxMaxParenthesisDepth 58
2136 #define FxMaxSubexpressionDepth 200
2137 #define FxReturn(value) \
2138 { \
2139  subexpression=DestroyString(subexpression); \
2140  return(value); \
2141 }
2142 
2143  char
2144  *q,
2145  *subexpression;
2146 
2147  double
2148  alpha,
2149  gamma;
2150 
2151  register const char
2152  *p;
2153 
2154  *beta=0.0;
2155  subexpression=AcquireString(expression);
2156  *subexpression='\0';
2157  if (depth > FxMaxSubexpressionDepth)
2158  {
2160  "UnableToParseExpression","`%s'",expression);
2161  FxReturn(0.0);
2162  }
2163  if (exception->severity >= ErrorException)
2164  FxReturn(0.0);
2165  while (isspace((int) ((unsigned char) *expression)) != 0)
2166  expression++;
2167  if (*expression == '\0')
2168  FxReturn(0.0);
2169  p=FxOperatorPrecedence(expression,exception);
2170  if (p != (const char *) NULL)
2171  {
2172  (void) CopyMagickString(subexpression,expression,(size_t)
2173  (p-expression+1));
2174  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,depth+1,
2175  beta,exception);
2176  switch ((unsigned char) *p)
2177  {
2178  case '~':
2179  {
2180  *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
2181  exception);
2182  *beta=(double) (~(size_t) *beta);
2183  FxReturn(*beta);
2184  }
2185  case '!':
2186  {
2187  *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
2188  exception);
2189  FxReturn(*beta == 0.0 ? 1.0 : 0.0);
2190  }
2191  case '^':
2192  {
2193  *beta=pow(alpha,FxEvaluateSubexpression(fx_info,channel,x,y,++p,
2194  depth+1,beta,exception));
2195  FxReturn(*beta);
2196  }
2197  case '*':
2198  case ExponentialNotation:
2199  {
2200  *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
2201  exception);
2202  FxReturn(alpha*(*beta));
2203  }
2204  case '/':
2205  {
2206  *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
2207  exception);
2208  FxReturn(PerceptibleReciprocal(*beta)*alpha);
2209  }
2210  case '%':
2211  {
2212  *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
2213  exception);
2214  *beta=fabs(floor((*beta)+0.5));
2215  FxReturn(fmod(alpha,*beta));
2216  }
2217  case '+':
2218  {
2219  *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
2220  exception);
2221  FxReturn(alpha+(*beta));
2222  }
2223  case '-':
2224  {
2225  *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
2226  exception);
2227  FxReturn(alpha-(*beta));
2228  }
2229  case LeftShiftOperator:
2230  {
2231  gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
2232  exception);
2233  if ((size_t) (gamma+0.5) >= (8*sizeof(size_t)))
2234  {
2235  (void) ThrowMagickException(exception,GetMagickModule(),
2236  OptionError,"ShiftCountOverflow","`%s'",subexpression);
2237  FxReturn(0.0);
2238  }
2239  *beta=(double) ((size_t) (alpha+0.5) << (size_t) (gamma+0.5));
2240  FxReturn(*beta);
2241  }
2242  case RightShiftOperator:
2243  {
2244  gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
2245  exception);
2246  if ((size_t) (gamma+0.5) >= (8*sizeof(size_t)))
2247  {
2248  (void) ThrowMagickException(exception,GetMagickModule(),
2249  OptionError,"ShiftCountOverflow","`%s'",subexpression);
2250  FxReturn(0.0);
2251  }
2252  *beta=(double) ((size_t) (alpha+0.5) >> (size_t) (gamma+0.5));
2253  FxReturn(*beta);
2254  }
2255  case '<':
2256  {
2257  *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
2258  exception);
2259  FxReturn(alpha < *beta ? 1.0 : 0.0);
2260  }
2261  case LessThanEqualOperator:
2262  {
2263  *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
2264  exception);
2265  FxReturn(alpha <= *beta ? 1.0 : 0.0);
2266  }
2267  case '>':
2268  {
2269  *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
2270  exception);
2271  FxReturn(alpha > *beta ? 1.0 : 0.0);
2272  }
2274  {
2275  *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
2276  exception);
2277  FxReturn(alpha >= *beta ? 1.0 : 0.0);
2278  }
2279  case EqualOperator:
2280  {
2281  *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
2282  exception);
2283  FxReturn(fabs(alpha-(*beta)) < MagickEpsilon ? 1.0 : 0.0);
2284  }
2285  case NotEqualOperator:
2286  {
2287  *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
2288  exception);
2289  FxReturn(fabs(alpha-(*beta)) >= MagickEpsilon ? 1.0 : 0.0);
2290  }
2291  case '&':
2292  {
2293  gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
2294  exception);
2295  *beta=(double) ((size_t) (alpha+0.5) & (size_t) (gamma+0.5));
2296  FxReturn(*beta);
2297  }
2298  case '|':
2299  {
2300  gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
2301  exception);
2302  *beta=(double) ((size_t) (alpha+0.5) | (size_t) (gamma+0.5));
2303  FxReturn(*beta);
2304  }
2305  case LogicalAndOperator:
2306  {
2307  p++;
2308  if (alpha <= 0.0)
2309  {
2310  *beta=0.0;
2311  FxReturn(*beta);
2312  }
2313  gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,depth+1,beta,
2314  exception);
2315  *beta=(gamma > 0.0) ? 1.0 : 0.0;
2316  FxReturn(*beta);
2317  }
2318  case LogicalOrOperator:
2319  {
2320  p++;
2321  if (alpha > 0.0)
2322  {
2323  *beta=1.0;
2324  FxReturn(*beta);
2325  }
2326  gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,depth+1,beta,
2327  exception);
2328  *beta=(gamma > 0.0) ? 1.0 : 0.0;
2329  FxReturn(*beta);
2330  }
2331  case '?':
2332  {
2333  (void) CopyMagickString(subexpression,++p,MagickPathExtent);
2334  q=subexpression;
2335  p=StringToken(":",&q);
2336  if (q == (char *) NULL)
2337  {
2338  (void) ThrowMagickException(exception,GetMagickModule(),
2339  OptionError,"UnableToParseExpression","`%s'",subexpression);
2340  FxReturn(0.0);
2341  }
2342  if (fabs(alpha) >= MagickEpsilon)
2343  gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,depth+1,beta,
2344  exception);
2345  else
2346  gamma=FxEvaluateSubexpression(fx_info,channel,x,y,q,depth+1,beta,
2347  exception);
2348  FxReturn(gamma);
2349  }
2350  case '=':
2351  {
2352  char
2353  numeric[MagickPathExtent];
2354 
2355  q=subexpression;
2356  while (isalpha((int) ((unsigned char) *q)) != 0)
2357  q++;
2358  if (*q != '\0')
2359  {
2360  (void) ThrowMagickException(exception,GetMagickModule(),
2361  OptionError,"UnableToParseExpression","`%s'",subexpression);
2362  FxReturn(0.0);
2363  }
2364  ClearMagickException(exception);
2365  *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
2366  exception);
2367  (void) FormatLocaleString(numeric,MagickPathExtent,"%.20g",*beta);
2368  (void) DeleteNodeFromSplayTree(fx_info->symbols,subexpression);
2369  (void) AddValueToSplayTree(fx_info->symbols,ConstantString(
2370  subexpression),ConstantString(numeric));
2371  FxReturn(*beta);
2372  }
2373  case ',':
2374  {
2375  *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
2376  exception);
2377  FxReturn(alpha);
2378  }
2379  case ';':
2380  {
2381  *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,beta,
2382  exception);
2383  FxReturn(*beta);
2384  }
2385  default:
2386  {
2387  gamma=alpha*FxEvaluateSubexpression(fx_info,channel,x,y,++p,depth+1,
2388  beta,exception);
2389  FxReturn(gamma);
2390  }
2391  }
2392  }
2393  if (strchr("(",(int) *expression) != (char *) NULL)
2394  {
2395  if (depth >= FxMaxParenthesisDepth)
2397  "ParenthesisNestedTooDeeply","`%s'",expression);
2398  (void) CopyMagickString(subexpression,expression+1,MagickPathExtent);
2399  if (strlen(subexpression) != 0)
2400  subexpression[strlen(subexpression)-1]='\0';
2401  gamma=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,depth+1,
2402  beta,exception);
2403  FxReturn(gamma);
2404  }
2405  switch (*expression)
2406  {
2407  case '+':
2408  {
2409  gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,depth+1,
2410  beta,exception);
2411  FxReturn(1.0*gamma);
2412  }
2413  case '-':
2414  {
2415  gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,depth+1,
2416  beta,exception);
2417  FxReturn(-1.0*gamma);
2418  }
2419  case '~':
2420  {
2421  gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,depth+1,
2422  beta,exception);
2423  FxReturn((double) (~(size_t) (gamma+0.5)));
2424  }
2425  case 'A':
2426  case 'a':
2427  {
2428  if (IsFxFunction(expression,"abs",3) != MagickFalse)
2429  {
2430  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2431  depth+1,beta,exception);
2432  FxReturn(fabs(alpha));
2433  }
2434 #if defined(MAGICKCORE_HAVE_ACOSH)
2435  if (IsFxFunction(expression,"acosh",5) != MagickFalse)
2436  {
2437  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2438  depth+1,beta,exception);
2439  FxReturn(acosh(alpha));
2440  }
2441 #endif
2442  if (IsFxFunction(expression,"acos",4) != MagickFalse)
2443  {
2444  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2445  depth+1,beta,exception);
2446  FxReturn(acos(alpha));
2447  }
2448 #if defined(MAGICKCORE_HAVE_J1)
2449  if (IsFxFunction(expression,"airy",4) != MagickFalse)
2450  {
2451  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2452  depth+1,beta,exception);
2453  if (alpha == 0.0)
2454  FxReturn(1.0);
2455  gamma=2.0*j1((MagickPI*alpha))/(MagickPI*alpha);
2456  FxReturn(gamma*gamma);
2457  }
2458 #endif
2459 #if defined(MAGICKCORE_HAVE_ASINH)
2460  if (IsFxFunction(expression,"asinh",5) != MagickFalse)
2461  {
2462  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2463  depth+1,beta,exception);
2464  FxReturn(asinh(alpha));
2465  }
2466 #endif
2467  if (IsFxFunction(expression,"asin",4) != MagickFalse)
2468  {
2469  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2470  depth+1,beta,exception);
2471  FxReturn(asin(alpha));
2472  }
2473  if (IsFxFunction(expression,"alt",3) != MagickFalse)
2474  {
2475  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2476  depth+1,beta,exception);
2477  FxReturn(((ssize_t) alpha) & 0x01 ? -1.0 : 1.0);
2478  }
2479  if (IsFxFunction(expression,"atan2",5) != MagickFalse)
2480  {
2481  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2482  depth+1,beta,exception);
2483  FxReturn(atan2(alpha,*beta));
2484  }
2485 #if defined(MAGICKCORE_HAVE_ATANH)
2486  if (IsFxFunction(expression,"atanh",5) != MagickFalse)
2487  {
2488  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2489  depth+1,beta,exception);
2490  FxReturn(atanh(alpha));
2491  }
2492 #endif
2493  if (IsFxFunction(expression,"atan",4) != MagickFalse)
2494  {
2495  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2496  depth+1,beta,exception);
2497  FxReturn(atan(alpha));
2498  }
2499  if (LocaleCompare(expression,"a") == 0)
2500  FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2501  break;
2502  }
2503  case 'B':
2504  case 'b':
2505  {
2506  if (LocaleCompare(expression,"b") == 0)
2507  FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2508  break;
2509  }
2510  case 'C':
2511  case 'c':
2512  {
2513  if (IsFxFunction(expression,"ceil",4) != MagickFalse)
2514  {
2515  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2516  depth+1,beta,exception);
2517  FxReturn(ceil(alpha));
2518  }
2519  if (IsFxFunction(expression,"clamp",5) != MagickFalse)
2520  {
2521  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2522  depth+1,beta,exception);
2523  if (alpha < 0.0)
2524  FxReturn(0.0);
2525  if (alpha > 1.0)
2526  FxReturn(1.0);
2527  FxReturn(alpha);
2528  }
2529  if (IsFxFunction(expression,"cosh",4) != MagickFalse)
2530  {
2531  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2532  depth+1,beta,exception);
2533  FxReturn(cosh(alpha));
2534  }
2535  if (IsFxFunction(expression,"cos",3) != MagickFalse)
2536  {
2537  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2538  depth+1,beta,exception);
2539  FxReturn(cos(alpha));
2540  }
2541  if (LocaleCompare(expression,"c") == 0)
2542  FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2543  break;
2544  }
2545  case 'D':
2546  case 'd':
2547  {
2548  if (IsFxFunction(expression,"debug",5) != MagickFalse)
2549  {
2550  const char
2551  *type;
2552 
2553  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2554  depth+1,beta,exception);
2555  if (fx_info->images->colorspace == CMYKColorspace)
2556  switch (channel)
2557  {
2558  case CyanPixelChannel: type="cyan"; break;
2559  case MagentaPixelChannel: type="magenta"; break;
2560  case YellowPixelChannel: type="yellow"; break;
2561  case AlphaPixelChannel: type="opacity"; break;
2562  case BlackPixelChannel: type="black"; break;
2563  default: type="unknown"; break;
2564  }
2565  else
2566  switch (channel)
2567  {
2568  case RedPixelChannel: type="red"; break;
2569  case GreenPixelChannel: type="green"; break;
2570  case BluePixelChannel: type="blue"; break;
2571  case AlphaPixelChannel: type="opacity"; break;
2572  default: type="unknown"; break;
2573  }
2574  *subexpression='\0';
2575  if (strlen(expression) > 6)
2576  (void) CopyMagickString(subexpression,expression+6,
2578  if (strlen(subexpression) > 1)
2579  subexpression[strlen(subexpression)-1]='\0';
2580  if (fx_info->file != (FILE *) NULL)
2581  (void) FormatLocaleFile(fx_info->file,"%s[%.20g,%.20g].%s: "
2582  "%s=%.*g\n",fx_info->images->filename,(double) x,(double) y,type,
2583  subexpression,GetMagickPrecision(),alpha);
2584  FxReturn(0.0);
2585  }
2586  if (IsFxFunction(expression,"drc",3) != MagickFalse)
2587  {
2588  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2589  depth+1,beta,exception);
2590  FxReturn((alpha/(*beta*(alpha-1.0)+1.0)));
2591  }
2592  break;
2593  }
2594  case 'E':
2595  case 'e':
2596  {
2597  if (LocaleCompare(expression,"epsilon") == 0)
2599 #if defined(MAGICKCORE_HAVE_ERF)
2600  if (IsFxFunction(expression,"erf",3) != MagickFalse)
2601  {
2602  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2603  depth+1,beta,exception);
2604  FxReturn(erf(alpha));
2605  }
2606 #endif
2607  if (IsFxFunction(expression,"exp",3) != MagickFalse)
2608  {
2609  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2610  depth+1,beta,exception);
2611  FxReturn(exp(alpha));
2612  }
2613  if (LocaleCompare(expression,"e") == 0)
2614  FxReturn(2.7182818284590452354);
2615  break;
2616  }
2617  case 'F':
2618  case 'f':
2619  {
2620  if (IsFxFunction(expression,"floor",5) != MagickFalse)
2621  {
2622  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2623  depth+1,beta,exception);
2624  FxReturn(floor(alpha));
2625  }
2626  break;
2627  }
2628  case 'G':
2629  case 'g':
2630  {
2631  if (IsFxFunction(expression,"gauss",5) != MagickFalse)
2632  {
2633  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2634  depth+1,beta,exception);
2635  gamma=exp((-alpha*alpha/2.0))/sqrt(2.0*MagickPI);
2636  FxReturn(gamma);
2637  }
2638  if (IsFxFunction(expression,"gcd",3) != MagickFalse)
2639  {
2641  gcd;
2642 
2643  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2644  depth+1,beta,exception);
2645  gcd=FxGCD((MagickOffsetType) (alpha+0.5),(MagickOffsetType) (*beta+
2646  0.5));
2647  FxReturn((double) gcd);
2648  }
2649  if (LocaleCompare(expression,"g") == 0)
2650  FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2651  break;
2652  }
2653  case 'H':
2654  case 'h':
2655  {
2656  if (LocaleCompare(expression,"h") == 0)
2657  FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2658  if (LocaleCompare(expression,"hue") == 0)
2659  FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2660  if (IsFxFunction(expression,"hypot",5) != MagickFalse)
2661  {
2662  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2663  depth+1,beta,exception);
2664  FxReturn(hypot(alpha,*beta));
2665  }
2666  break;
2667  }
2668  case 'K':
2669  case 'k':
2670  {
2671  if (LocaleCompare(expression,"k") == 0)
2672  FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2673  break;
2674  }
2675  case 'I':
2676  case 'i':
2677  {
2678  if (LocaleCompare(expression,"intensity") == 0)
2679  FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2680  if (IsFxFunction(expression,"int",3) != MagickFalse)
2681  {
2682  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2683  depth+1,beta,exception);
2684  FxReturn(floor(alpha));
2685  }
2686  if (IsFxFunction(expression,"isnan",5) != MagickFalse)
2687  {
2688  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2689  depth+1,beta,exception);
2690  FxReturn((double) !!IsNaN(alpha));
2691  }
2692  if (LocaleCompare(expression,"i") == 0)
2693  FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2694  break;
2695  }
2696  case 'J':
2697  case 'j':
2698  {
2699  if (LocaleCompare(expression,"j") == 0)
2700  FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2701 #if defined(MAGICKCORE_HAVE_J0)
2702  if (IsFxFunction(expression,"j0",2) != MagickFalse)
2703  {
2704  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,
2705  depth+1,beta,exception);
2706  FxReturn(j0(alpha));
2707  }
2708 #endif
2709 #if defined(MAGICKCORE_HAVE_J1)
2710  if (IsFxFunction(expression,"j1",2) != MagickFalse)
2711  {
2712  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,
2713  depth+1,beta,exception);
2714  FxReturn(j1(alpha));
2715  }
2716 #endif
2717 #if defined(MAGICKCORE_HAVE_J1)
2718  if (IsFxFunction(expression,"jinc",4) != MagickFalse)
2719  {
2720  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2721  depth+1,beta,exception);
2722  if (alpha == 0.0)
2723  FxReturn(1.0);
2724  gamma=(2.0*j1((MagickPI*alpha))/(MagickPI*alpha));
2725  FxReturn(gamma);
2726  }
2727 #endif
2728  break;
2729  }
2730  case 'L':
2731  case 'l':
2732  {
2733  if (IsFxFunction(expression,"ln",2) != MagickFalse)
2734  {
2735  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,
2736  depth+1,beta,exception);
2737  FxReturn(log(alpha));
2738  }
2739  if (IsFxFunction(expression,"logtwo",6) != MagickFalse)
2740  {
2741  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+6,
2742  depth+1,beta,exception);
2743  FxReturn(log10(alpha)/log10(2.0));
2744  }
2745  if (IsFxFunction(expression,"log",3) != MagickFalse)
2746  {
2747  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2748  depth+1,beta,exception);
2749  FxReturn(log10(alpha));
2750  }
2751  if (LocaleCompare(expression,"lightness") == 0)
2752  FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2753  break;
2754  }
2755  case 'M':
2756  case 'm':
2757  {
2758  if (LocaleCompare(expression,"MaxRGB") == 0)
2760  if (LocaleNCompare(expression,"maxima",6) == 0)
2761  break;
2762  if (IsFxFunction(expression,"max",3) != MagickFalse)
2763  {
2764  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2765  depth+1,beta,exception);
2766  FxReturn(alpha > *beta ? alpha : *beta);
2767  }
2768  if (LocaleNCompare(expression,"minima",6) == 0)
2769  break;
2770  if (IsFxFunction(expression,"min",3) != MagickFalse)
2771  {
2772  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2773  depth+1,beta,exception);
2774  FxReturn(alpha < *beta ? alpha : *beta);
2775  }
2776  if (IsFxFunction(expression,"mod",3) != MagickFalse)
2777  {
2778  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2779  depth+1,beta,exception);
2780  gamma=alpha-floor((alpha*PerceptibleReciprocal(*beta)))*(*beta);
2781  FxReturn(gamma);
2782  }
2783  if (LocaleCompare(expression,"m") == 0)
2784  FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2785  break;
2786  }
2787  case 'N':
2788  case 'n':
2789  {
2790  if (IsFxFunction(expression,"not",3) != MagickFalse)
2791  {
2792  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2793  depth+1,beta,exception);
2794  FxReturn((double) (alpha < MagickEpsilon));
2795  }
2796  if (LocaleCompare(expression,"n") == 0)
2797  FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2798  break;
2799  }
2800  case 'O':
2801  case 'o':
2802  {
2803  if (LocaleCompare(expression,"Opaque") == 0)
2804  FxReturn(1.0);
2805  if (LocaleCompare(expression,"o") == 0)
2806  FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2807  break;
2808  }
2809  case 'P':
2810  case 'p':
2811  {
2812  if (LocaleCompare(expression,"phi") == 0)
2814  if (LocaleCompare(expression,"pi") == 0)
2815  FxReturn(MagickPI);
2816  if (IsFxFunction(expression,"pow",3) != MagickFalse)
2817  {
2818  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2819  depth+1,beta,exception);
2820  FxReturn(pow(alpha,*beta));
2821  }
2822  if (LocaleCompare(expression,"p") == 0)
2823  FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2824  break;
2825  }
2826  case 'Q':
2827  case 'q':
2828  {
2829  if (LocaleCompare(expression,"QuantumRange") == 0)
2831  if (LocaleCompare(expression,"QuantumScale") == 0)
2833  break;
2834  }
2835  case 'R':
2836  case 'r':
2837  {
2838  if (IsFxFunction(expression,"rand",4) != MagickFalse)
2839  {
2840 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2841  #pragma omp critical (MagickCore_FxEvaluateSubexpression)
2842 #endif
2843  alpha=GetPseudoRandomValue(fx_info->random_info);
2844  FxReturn(alpha);
2845  }
2846  if (IsFxFunction(expression,"round",5) != MagickFalse)
2847  {
2848  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2849  depth+1,beta,exception);
2850  FxReturn(floor(alpha+0.5));
2851  }
2852  if (LocaleCompare(expression,"r") == 0)
2853  FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2854  break;
2855  }
2856  case 'S':
2857  case 's':
2858  {
2859  if (LocaleCompare(expression,"saturation") == 0)
2860  FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2861  if (IsFxFunction(expression,"sign",4) != MagickFalse)
2862  {
2863  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2864  depth+1,beta,exception);
2865  FxReturn(alpha < 0.0 ? -1.0 : 1.0);
2866  }
2867  if (IsFxFunction(expression,"sinc",4) != MagickFalse)
2868  {
2869  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2870  depth+1,beta,exception);
2871  if (alpha == 0)
2872  FxReturn(1.0);
2873  gamma=sin((MagickPI*alpha))/(MagickPI*alpha);
2874  FxReturn(gamma);
2875  }
2876  if (IsFxFunction(expression,"sinh",4) != MagickFalse)
2877  {
2878  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2879  depth+1,beta,exception);
2880  FxReturn(sinh(alpha));
2881  }
2882  if (IsFxFunction(expression,"sin",3) != MagickFalse)
2883  {
2884  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2885  depth+1,beta,exception);
2886  FxReturn(sin(alpha));
2887  }
2888  if (IsFxFunction(expression,"sqrt",4) != MagickFalse)
2889  {
2890  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2891  depth+1,beta,exception);
2892  FxReturn(sqrt(alpha));
2893  }
2894  if (IsFxFunction(expression,"squish",6) != MagickFalse)
2895  {
2896  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+6,
2897  depth+1,beta,exception);
2898  FxReturn((1.0/(1.0+exp(-alpha))));
2899  }
2900  if (LocaleCompare(expression,"s") == 0)
2901  FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2902  break;
2903  }
2904  case 'T':
2905  case 't':
2906  {
2907  if (IsFxFunction(expression,"tanh",4) != MagickFalse)
2908  {
2909  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,
2910  depth+1,beta,exception);
2911  FxReturn(tanh(alpha));
2912  }
2913  if (IsFxFunction(expression,"tan",3) != MagickFalse)
2914  {
2915  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,
2916  depth+1,beta,exception);
2917  FxReturn(tan(alpha));
2918  }
2919  if (LocaleCompare(expression,"Transparent") == 0)
2920  FxReturn(0.0);
2921  if (IsFxFunction(expression,"trunc",5) != MagickFalse)
2922  {
2923  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,
2924  depth+1,beta,exception);
2925  if (alpha >= 0.0)
2926  FxReturn(floor(alpha));
2927  FxReturn(ceil(alpha));
2928  }
2929  if (LocaleCompare(expression,"t") == 0)
2930  FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2931  break;
2932  }
2933  case 'U':
2934  case 'u':
2935  {
2936  if (LocaleCompare(expression,"u") == 0)
2937  FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2938  break;
2939  }
2940  case 'V':
2941  case 'v':
2942  {
2943  if (LocaleCompare(expression,"v") == 0)
2944  FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2945  break;
2946  }
2947  case 'W':
2948  case 'w':
2949  {
2950  if (IsFxFunction(expression,"while",5) != MagickFalse)
2951  {
2952  /*
2953  Parse while(condition,expression).
2954  */
2955  (void) CopyMagickString(subexpression,expression+5,MagickPathExtent);
2956  q=subexpression;
2957  p=StringToken(",",&q);
2958  if ((p == (char *) NULL) || (strlen(p) < 1) || (q == (char *) NULL))
2959  {
2960  (void) ThrowMagickException(exception,GetMagickModule(),
2961  OptionError,"UnableToParseExpression","`%s'",expression);
2962  FxReturn(0.0);
2963  }
2964  for ( ; ; )
2965  {
2966  double sans = 0.0;
2967  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,p+1,depth+1,&sans,
2968  exception);
2969  if (fabs(alpha) < MagickEpsilon)
2970  FxReturn(*beta);
2971  alpha=FxEvaluateSubexpression(fx_info,channel,x,y,q,depth+1,beta,
2972  exception);
2973  }
2974  }
2975  if (LocaleCompare(expression,"w") == 0)
2976  FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2977  break;
2978  }
2979  case 'Y':
2980  case 'y':
2981  {
2982  if (LocaleCompare(expression,"y") == 0)
2983  FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2984  break;
2985  }
2986  case 'Z':
2987  case 'z':
2988  {
2989  if (LocaleCompare(expression,"z") == 0)
2990  FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
2991  break;
2992  }
2993  default:
2994  break;
2995  }
2996  subexpression=DestroyString(subexpression);
2997  q=(char *) expression;
2998  alpha=InterpretSiPrefixValue(expression,&q);
2999  if (q == expression)
3000  FxReturn(FxGetSymbol(fx_info,channel,x,y,expression,depth+1,exception));
3001  FxReturn(alpha);
3002 }
3003 
3005  double *alpha,ExceptionInfo *exception)
3006 {
3008  status;
3009 
3010  status=FxEvaluateChannelExpression(fx_info,GrayPixelChannel,0,0,alpha,
3011  exception);
3012  return(status);
3013 }
3014 
3016  double *alpha,ExceptionInfo *exception)
3017 {
3018  FILE
3019  *file;
3020 
3022  status;
3023 
3024  file=fx_info->file;
3025  fx_info->file=(FILE *) NULL;
3026  status=FxEvaluateChannelExpression(fx_info,GrayPixelChannel,0,0,alpha,
3027  exception);
3028  fx_info->file=file;
3029  return(status);
3030 }
3031 
3033  const PixelChannel channel,const ssize_t x,const ssize_t y,
3034  double *alpha,ExceptionInfo *exception)
3035 {
3036  double
3037  beta;
3038 
3039  beta=0.0;
3040  *alpha=FxEvaluateSubexpression(fx_info,channel,x,y,fx_info->expression,0,
3041  &beta,exception);
3042  return(exception->severity == OptionError ? MagickFalse : MagickTrue);
3043 }
3044 
3045 /*
3046 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3047 % %
3048 % %
3049 % %
3050 % F x I m a g e %
3051 % %
3052 % %
3053 % %
3054 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3055 %
3056 % FxImage() applies a mathematical expression to the specified image.
3057 %
3058 % The format of the FxImage method is:
3059 %
3060 % Image *FxImage(const Image *image,const char *expression,
3061 % ExceptionInfo *exception)
3062 %
3063 % A description of each parameter follows:
3064 %
3065 % o image: the image.
3066 %
3067 % o expression: A mathematical expression.
3068 %
3069 % o exception: return any errors or warnings in this structure.
3070 %
3071 */
3072 
3073 static FxInfo **DestroyFxThreadSet(FxInfo **fx_info)
3074 {
3075  register ssize_t
3076  i;
3077 
3078  assert(fx_info != (FxInfo **) NULL);
3079  for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
3080  if (fx_info[i] != (FxInfo *) NULL)
3081  fx_info[i]=DestroyFxInfo(fx_info[i]);
3082  fx_info=(FxInfo **) RelinquishMagickMemory(fx_info);
3083  return(fx_info);
3084 }
3085 
3086 static FxInfo **AcquireFxThreadSet(const Image *image,const char *expression,
3087  ExceptionInfo *exception)
3088 {
3089  char
3090  *fx_expression;
3091 
3092  FxInfo
3093  **fx_info;
3094 
3095  double
3096  alpha;
3097 
3098  register ssize_t
3099  i;
3100 
3101  size_t
3102  number_threads;
3103 
3104  number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
3105  fx_info=(FxInfo **) AcquireQuantumMemory(number_threads,sizeof(*fx_info));
3106  if (fx_info == (FxInfo **) NULL)
3107  {
3108  (void) ThrowMagickException(exception,GetMagickModule(),
3109  ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
3110  return((FxInfo **) NULL);
3111  }
3112  (void) memset(fx_info,0,number_threads*sizeof(*fx_info));
3113  if (*expression != '@')
3114  fx_expression=ConstantString(expression);
3115  else
3116  fx_expression=FileToString(expression+1,~0UL,exception);
3117  for (i=0; i < (ssize_t) number_threads; i++)
3118  {
3120  status;
3121 
3122  fx_info[i]=AcquireFxInfo(image,fx_expression,exception);
3123  if (fx_info[i] == (FxInfo *) NULL)
3124  break;
3125  status=FxPreprocessExpression(fx_info[i],&alpha,exception);
3126  if (status == MagickFalse)
3127  break;
3128  }
3129  fx_expression=DestroyString(fx_expression);
3130  if (i < (ssize_t) number_threads)
3131  fx_info=DestroyFxThreadSet(fx_info);
3132  return(fx_info);
3133 }
3134 
3135 MagickExport Image *FxImage(const Image *image,const char *expression,
3136  ExceptionInfo *exception)
3137 {
3138 #define FxImageTag "Fx/Image"
3139 
3140  CacheView
3141  *fx_view,
3142  *image_view;
3143 
3144  FxInfo
3145  **magick_restrict fx_info;
3146 
3147  Image
3148  *fx_image;
3149 
3151  status;
3152 
3154  progress;
3155 
3156  ssize_t
3157  y;
3158 
3159  assert(image != (Image *) NULL);
3160  assert(image->signature == MagickCoreSignature);
3161  if (image->debug != MagickFalse)
3162  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3163  if (expression == (const char *) NULL)
3164  return(CloneImage(image,0,0,MagickTrue,exception));
3165  fx_info=AcquireFxThreadSet(image,expression,exception);
3166  if (fx_info == (FxInfo **) NULL)
3167  return((Image *) NULL);
3168  fx_image=CloneImage(image,0,0,MagickTrue,exception);
3169  if (fx_image == (Image *) NULL)
3170  {
3171  fx_info=DestroyFxThreadSet(fx_info);
3172  return((Image *) NULL);
3173  }
3174  if (SetImageStorageClass(fx_image,DirectClass,exception) == MagickFalse)
3175  {
3176  fx_info=DestroyFxThreadSet(fx_info);
3177  fx_image=DestroyImage(fx_image);
3178  return((Image *) NULL);
3179  }
3180  /*
3181  Fx image.
3182  */
3183  status=MagickTrue;
3184  progress=0;
3185  image_view=AcquireVirtualCacheView(image,exception);
3186  fx_view=AcquireAuthenticCacheView(fx_image,exception);
3187 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3188  #pragma omp parallel for schedule(static) shared(progress,status) \
3189  magick_number_threads(image,fx_image,fx_image->rows,1)
3190 #endif
3191  for (y=0; y < (ssize_t) fx_image->rows; y++)
3192  {
3193  const int
3194  id = GetOpenMPThreadId();
3195 
3196  register const Quantum
3197  *magick_restrict p;
3198 
3199  register Quantum
3200  *magick_restrict q;
3201 
3202  register ssize_t
3203  x;
3204 
3205  if (status == MagickFalse)
3206  continue;
3207  p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3208  q=QueueCacheViewAuthenticPixels(fx_view,0,y,fx_image->columns,1,exception);
3209  if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3210  {
3211  status=MagickFalse;
3212  continue;
3213  }
3214  for (x=0; x < (ssize_t) fx_image->columns; x++)
3215  {
3216  register ssize_t
3217  i;
3218 
3219  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3220  {
3221  double
3222  alpha;
3223 
3224  PixelChannel channel = GetPixelChannelChannel(image,i);
3225  PixelTrait traits = GetPixelChannelTraits(image,channel);
3226  PixelTrait fx_traits=GetPixelChannelTraits(fx_image,channel);
3227  if ((traits == UndefinedPixelTrait) ||
3228  (fx_traits == UndefinedPixelTrait))
3229  continue;
3230  if ((fx_traits & CopyPixelTrait) != 0)
3231  {
3232  SetPixelChannel(fx_image,channel,p[i],q);
3233  continue;
3234  }
3235  alpha=0.0;
3236  (void) FxEvaluateChannelExpression(fx_info[id],channel,x,y,&alpha,
3237  exception);
3238  q[i]=ClampToQuantum(QuantumRange*alpha);
3239  }
3240  p+=GetPixelChannels(image);
3241  q+=GetPixelChannels(fx_image);
3242  }
3243  if (SyncCacheViewAuthenticPixels(fx_view,exception) == MagickFalse)
3244  status=MagickFalse;
3245  if (image->progress_monitor != (MagickProgressMonitor) NULL)
3246  {
3248  proceed;
3249 
3250 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3251  #pragma omp atomic
3252 #endif
3253  progress++;
3254  proceed=SetImageProgress(image,FxImageTag,progress,image->rows);
3255  if (proceed == MagickFalse)
3256  status=MagickFalse;
3257  }
3258  }
3259  fx_view=DestroyCacheView(fx_view);
3260  image_view=DestroyCacheView(image_view);
3261  fx_info=DestroyFxThreadSet(fx_info);
3262  if (status == MagickFalse)
3263  fx_image=DestroyImage(fx_image);
3264  return(fx_image);
3265 }
3266 
3267 /*
3268 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3269 % %
3270 % %
3271 % %
3272 % I m p l o d e I m a g e %
3273 % %
3274 % %
3275 % %
3276 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3277 %
3278 % ImplodeImage() creates a new image that is a copy of an existing
3279 % one with the image pixels "implode" by the specified percentage. It
3280 % allocates the memory necessary for the new Image structure and returns a
3281 % pointer to the new image.
3282 %
3283 % The format of the ImplodeImage method is:
3284 %
3285 % Image *ImplodeImage(const Image *image,const double amount,
3286 % const PixelInterpolateMethod method,ExceptionInfo *exception)
3287 %
3288 % A description of each parameter follows:
3289 %
3290 % o implode_image: Method ImplodeImage returns a pointer to the image
3291 % after it is implode. A null image is returned if there is a memory
3292 % shortage.
3293 %
3294 % o image: the image.
3295 %
3296 % o amount: Define the extent of the implosion.
3297 %
3298 % o method: the pixel interpolation method.
3299 %
3300 % o exception: return any errors or warnings in this structure.
3301 %
3302 */
3303 MagickExport Image *ImplodeImage(const Image *image,const double amount,
3304  const PixelInterpolateMethod method,ExceptionInfo *exception)
3305 {
3306 #define ImplodeImageTag "Implode/Image"
3307 
3308  CacheView
3309  *canvas_view,
3310  *implode_view,
3311  *interpolate_view;
3312 
3313  double
3314  radius;
3315 
3316  Image
3317  *canvas_image,
3318  *implode_image;
3319 
3321  status;
3322 
3324  progress;
3325 
3326  PointInfo
3327  center,
3328  scale;
3329 
3330  ssize_t
3331  y;
3332 
3333  /*
3334  Initialize implode image attributes.
3335  */
3336  assert(image != (Image *) NULL);
3337  assert(image->signature == MagickCoreSignature);
3338  if (image->debug != MagickFalse)
3339  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3340  assert(exception != (ExceptionInfo *) NULL);
3341  assert(exception->signature == MagickCoreSignature);
3342  canvas_image=CloneImage(image,0,0,MagickTrue,exception);
3343  if (canvas_image == (Image *) NULL)
3344  return((Image *) NULL);
3345  if ((canvas_image->alpha_trait == UndefinedPixelTrait) &&
3346  (canvas_image->background_color.alpha != OpaqueAlpha))
3347  (void) SetImageAlphaChannel(canvas_image,OpaqueAlphaChannel,exception);
3348  implode_image=CloneImage(canvas_image,0,0,MagickTrue,exception);
3349  if (implode_image == (Image *) NULL)
3350  {
3351  canvas_image=DestroyImage(canvas_image);
3352  return((Image *) NULL);
3353  }
3354  if (SetImageStorageClass(implode_image,DirectClass,exception) == MagickFalse)
3355  {
3356  canvas_image=DestroyImage(canvas_image);
3357  implode_image=DestroyImage(implode_image);
3358  return((Image *) NULL);
3359  }
3360  /*
3361  Compute scaling factor.
3362  */
3363  scale.x=1.0;
3364  scale.y=1.0;
3365  center.x=0.5*canvas_image->columns;
3366  center.y=0.5*canvas_image->rows;
3367  radius=center.x;
3368  if (canvas_image->columns > canvas_image->rows)
3369  scale.y=(double) canvas_image->columns/(double) canvas_image->rows;
3370  else
3371  if (canvas_image->columns < canvas_image->rows)
3372  {
3373  scale.x=(double) canvas_image->rows/(double) canvas_image->columns;
3374  radius=center.y;
3375  }
3376  /*
3377  Implode image.
3378  */
3379  status=MagickTrue;
3380  progress=0;
3381  canvas_view=AcquireVirtualCacheView(canvas_image,exception);
3382  interpolate_view=AcquireVirtualCacheView(canvas_image,exception);
3383  implode_view=AcquireAuthenticCacheView(implode_image,exception);
3384 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3385  #pragma omp parallel for schedule(static) shared(progress,status) \
3386  magick_number_threads(canvas_image,implode_image,canvas_image->rows,1)
3387 #endif
3388  for (y=0; y < (ssize_t) canvas_image->rows; y++)
3389  {
3390  double
3391  distance;
3392 
3393  PointInfo
3394  delta;
3395 
3396  register const Quantum
3397  *magick_restrict p;
3398 
3399  register ssize_t
3400  x;
3401 
3402  register Quantum
3403  *magick_restrict q;
3404 
3405  if (status == MagickFalse)
3406  continue;
3407  p=GetCacheViewVirtualPixels(canvas_view,0,y,canvas_image->columns,1,
3408  exception);
3409  q=QueueCacheViewAuthenticPixels(implode_view,0,y,implode_image->columns,1,
3410  exception);
3411  if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3412  {
3413  status=MagickFalse;
3414  continue;
3415  }
3416  delta.y=scale.y*(double) (y-center.y);
3417  for (x=0; x < (ssize_t) canvas_image->columns; x++)
3418  {
3419  register ssize_t
3420  i;
3421 
3422  /*
3423  Determine if the pixel is within an ellipse.
3424  */
3425  delta.x=scale.x*(double) (x-center.x);
3426  distance=delta.x*delta.x+delta.y*delta.y;
3427  if (distance >= (radius*radius))
3428  for (i=0; i < (ssize_t) GetPixelChannels(canvas_image); i++)
3429  {
3430  PixelChannel channel = GetPixelChannelChannel(canvas_image,i);
3431  PixelTrait traits = GetPixelChannelTraits(canvas_image,channel);
3432  PixelTrait implode_traits = GetPixelChannelTraits(implode_image,
3433  channel);
3434  if ((traits == UndefinedPixelTrait) ||
3435  (implode_traits == UndefinedPixelTrait))
3436  continue;
3437  SetPixelChannel(implode_image,channel,p[i],q);
3438  }
3439  else
3440  {
3441  double
3442  factor;
3443 
3444  /*
3445  Implode the pixel.
3446  */
3447  factor=1.0;
3448  if (distance > 0.0)
3449  factor=pow(sin(MagickPI*sqrt((double) distance)/radius/2),-amount);
3450  status=InterpolatePixelChannels(canvas_image,interpolate_view,
3451  implode_image,method,(double) (factor*delta.x/scale.x+center.x),
3452  (double) (factor*delta.y/scale.y+center.y),q,exception);
3453  if (status == MagickFalse)
3454  break;
3455  }
3456  p+=GetPixelChannels(canvas_image);
3457  q+=GetPixelChannels(implode_image);
3458  }
3459  if (SyncCacheViewAuthenticPixels(implode_view,exception) == MagickFalse)
3460  status=MagickFalse;
3461  if (canvas_image->progress_monitor != (MagickProgressMonitor) NULL)
3462  {
3464  proceed;
3465 
3466 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3467  #pragma omp atomic
3468 #endif
3469  progress++;
3470  proceed=SetImageProgress(canvas_image,ImplodeImageTag,progress,
3471  canvas_image->rows);
3472  if (proceed == MagickFalse)
3473  status=MagickFalse;
3474  }
3475  }
3476  implode_view=DestroyCacheView(implode_view);
3477  interpolate_view=DestroyCacheView(interpolate_view);
3478  canvas_view=DestroyCacheView(canvas_view);
3479  canvas_image=DestroyImage(canvas_image);
3480  if (status == MagickFalse)
3481  implode_image=DestroyImage(implode_image);
3482  return(implode_image);
3483 }
3484 
3485 /*
3486 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3487 % %
3488 % %
3489 % %
3490 % M o r p h I m a g e s %
3491 % %
3492 % %
3493 % %
3494 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3495 %
3496 % The MorphImages() method requires a minimum of two images. The first
3497 % image is transformed into the second by a number of intervening images
3498 % as specified by frames.
3499 %
3500 % The format of the MorphImage method is:
3501 %
3502 % Image *MorphImages(const Image *image,const size_t number_frames,
3503 % ExceptionInfo *exception)
3504 %
3505 % A description of each parameter follows:
3506 %
3507 % o image: the image.
3508 %
3509 % o number_frames: Define the number of in-between image to generate.
3510 % The more in-between frames, the smoother the morph.
3511 %
3512 % o exception: return any errors or warnings in this structure.
3513 %
3514 */
3515 MagickExport Image *MorphImages(const Image *image,const size_t number_frames,
3516  ExceptionInfo *exception)
3517 {
3518 #define MorphImageTag "Morph/Image"
3519 
3520  double
3521  alpha,
3522  beta;
3523 
3524  Image
3525  *morph_image,
3526  *morph_images;
3527 
3529  status;
3530 
3532  scene;
3533 
3534  register const Image
3535  *next;
3536 
3537  register ssize_t
3538  n;
3539 
3540  ssize_t
3541  y;
3542 
3543  /*
3544  Clone first frame in sequence.
3545  */
3546  assert(image != (Image *) NULL);
3547  assert(image->signature == MagickCoreSignature);
3548  if (image->debug != MagickFalse)
3549  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3550  assert(exception != (ExceptionInfo *) NULL);
3551  assert(exception->signature == MagickCoreSignature);
3552  morph_images=CloneImage(image,0,0,MagickTrue,exception);
3553  if (morph_images == (Image *) NULL)
3554  return((Image *) NULL);
3555  if (GetNextImageInList(image) == (Image *) NULL)
3556  {
3557  /*
3558  Morph single image.
3559  */
3560  for (n=1; n < (ssize_t) number_frames; n++)
3561  {
3562  morph_image=CloneImage(image,0,0,MagickTrue,exception);
3563  if (morph_image == (Image *) NULL)
3564  {
3565  morph_images=DestroyImageList(morph_images);
3566  return((Image *) NULL);
3567  }
3568  AppendImageToList(&morph_images,morph_image);
3569  if (image->progress_monitor != (MagickProgressMonitor) NULL)
3570  {
3572  proceed;
3573 
3575  number_frames);
3576  if (proceed == MagickFalse)
3577  status=MagickFalse;
3578  }
3579  }
3580  return(GetFirstImageInList(morph_images));
3581  }
3582  /*
3583  Morph image sequence.
3584  */
3585  status=MagickTrue;
3586  scene=0;
3587  next=image;
3588  for ( ; GetNextImageInList(next) != (Image *) NULL; next=GetNextImageInList(next))
3589  {
3590  for (n=0; n < (ssize_t) number_frames; n++)
3591  {
3592  CacheView
3593  *image_view,
3594  *morph_view;
3595 
3596  beta=(double) (n+1.0)/(double) (number_frames+1.0);
3597  alpha=1.0-beta;
3598  morph_image=ResizeImage(next,(size_t) (alpha*next->columns+beta*
3599  GetNextImageInList(next)->columns+0.5),(size_t) (alpha*next->rows+beta*
3600  GetNextImageInList(next)->rows+0.5),next->filter,exception);
3601  if (morph_image == (Image *) NULL)
3602  {
3603  morph_images=DestroyImageList(morph_images);
3604  return((Image *) NULL);
3605  }
3606  status=SetImageStorageClass(morph_image,DirectClass,exception);
3607  if (status == MagickFalse)
3608  {
3609  morph_image=DestroyImage(morph_image);
3610  return((Image *) NULL);
3611  }
3612  AppendImageToList(&morph_images,morph_image);
3613  morph_images=GetLastImageInList(morph_images);
3614  morph_image=ResizeImage(GetNextImageInList(next),morph_images->columns,
3615  morph_images->rows,GetNextImageInList(next)->filter,exception);
3616  if (morph_image == (Image *) NULL)
3617  {
3618  morph_images=DestroyImageList(morph_images);
3619  return((Image *) NULL);
3620  }
3621  image_view=AcquireVirtualCacheView(morph_image,exception);
3622  morph_view=AcquireAuthenticCacheView(morph_images,exception);
3623 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3624  #pragma omp parallel for schedule(static) shared(status) \
3625  magick_number_threads(morph_image,morph_image,morph_image->rows,1)
3626 #endif
3627  for (y=0; y < (ssize_t) morph_images->rows; y++)
3628  {
3630  sync;
3631 
3632  register const Quantum
3633  *magick_restrict p;
3634 
3635  register ssize_t
3636  x;
3637 
3638  register Quantum
3639  *magick_restrict q;
3640 
3641  if (status == MagickFalse)
3642  continue;
3643  p=GetCacheViewVirtualPixels(image_view,0,y,morph_image->columns,1,
3644  exception);
3645  q=GetCacheViewAuthenticPixels(morph_view,0,y,morph_images->columns,1,
3646  exception);
3647  if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3648  {
3649  status=MagickFalse;
3650  continue;
3651  }
3652  for (x=0; x < (ssize_t) morph_images->columns; x++)
3653  {
3654  register ssize_t
3655  i;
3656 
3657  for (i=0; i < (ssize_t) GetPixelChannels(morph_image); i++)
3658  {
3659  PixelChannel channel = GetPixelChannelChannel(morph_image,i);
3660  PixelTrait traits = GetPixelChannelTraits(morph_image,channel);
3661  PixelTrait morph_traits=GetPixelChannelTraits(morph_images,channel);
3662  if ((traits == UndefinedPixelTrait) ||
3663  (morph_traits == UndefinedPixelTrait))
3664  continue;
3665  if ((morph_traits & CopyPixelTrait) != 0)
3666  {
3667  SetPixelChannel(morph_image,channel,p[i],q);
3668  continue;
3669  }
3670  SetPixelChannel(morph_image,channel,ClampToQuantum(alpha*
3671  GetPixelChannel(morph_images,channel,q)+beta*p[i]),q);
3672  }
3673  p+=GetPixelChannels(morph_image);
3674  q+=GetPixelChannels(morph_images);
3675  }
3676  sync=SyncCacheViewAuthenticPixels(morph_view,exception);
3677  if (sync == MagickFalse)
3678  status=MagickFalse;
3679  }
3680  morph_view=DestroyCacheView(morph_view);
3681  image_view=DestroyCacheView(image_view);
3682  morph_image=DestroyImage(morph_image);
3683  }
3684  if (n < (ssize_t) number_frames)
3685  break;
3686  /*
3687  Clone last frame in sequence.
3688  */
3689  morph_image=CloneImage(GetNextImageInList(next),0,0,MagickTrue,exception);
3690  if (morph_image == (Image *) NULL)
3691  {
3692  morph_images=DestroyImageList(morph_images);
3693  return((Image *) NULL);
3694  }
3695  AppendImageToList(&morph_images,morph_image);
3696  morph_images=GetLastImageInList(morph_images);
3697  if (image->progress_monitor != (MagickProgressMonitor) NULL)
3698  {
3700  proceed;
3701 
3702  proceed=SetImageProgress(image,MorphImageTag,scene,
3703  GetImageListLength(image));
3704  if (proceed == MagickFalse)
3705  status=MagickFalse;
3706  }
3707  scene++;
3708  }
3709  if (GetNextImageInList(next) != (Image *) NULL)
3710  {
3711  morph_images=DestroyImageList(morph_images);
3712  return((Image *) NULL);
3713  }
3714  return(GetFirstImageInList(morph_images));
3715 }
3716 
3717 /*
3718 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3719 % %
3720 % %
3721 % %
3722 % P l a s m a I m a g e %
3723 % %
3724 % %
3725 % %
3726 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3727 %
3728 % PlasmaImage() initializes an image with plasma fractal values. The image
3729 % must be initialized with a base color and the random number generator
3730 % seeded before this method is called.
3731 %
3732 % The format of the PlasmaImage method is:
3733 %
3734 % MagickBooleanType PlasmaImage(Image *image,const SegmentInfo *segment,
3735 % size_t attenuate,size_t depth,ExceptionInfo *exception)
3736 %
3737 % A description of each parameter follows:
3738 %
3739 % o image: the image.
3740 %
3741 % o segment: Define the region to apply plasma fractals values.
3742 %
3743 % o attenuate: Define the plasma attenuation factor.
3744 %
3745 % o depth: Limit the plasma recursion depth.
3746 %
3747 % o exception: return any errors or warnings in this structure.
3748 %
3749 */
3750 
3752  const double pixel,const double noise)
3753 {
3754  Quantum
3755  plasma;
3756 
3757  plasma=ClampToQuantum(pixel+noise*GetPseudoRandomValue(random_info)-
3758  noise/2.0);
3759  if (plasma <= 0)
3760  return((Quantum) 0);
3761  if (plasma >= QuantumRange)
3762  return(QuantumRange);
3763  return(plasma);
3764 }
3765 
3767  CacheView *u_view,CacheView *v_view,RandomInfo *random_info,
3768  const SegmentInfo *segment,size_t attenuate,size_t depth,
3769  ExceptionInfo *exception)
3770 {
3771  double
3772  plasma;
3773 
3774  register const Quantum
3775  *magick_restrict u,
3776  *magick_restrict v;
3777 
3778  register Quantum
3779  *magick_restrict q;
3780 
3781  register ssize_t
3782  i;
3783 
3784  ssize_t
3785  x,
3786  x_mid,
3787  y,
3788  y_mid;
3789 
3790  if ((fabs(segment->x2-segment->x1) <= MagickEpsilon) &&
3791  (fabs(segment->y2-segment->y1) <= MagickEpsilon))
3792  return(MagickTrue);
3793  if (depth != 0)
3794  {
3796  status;
3797 
3798  SegmentInfo
3799  local_info;
3800 
3801  /*
3802  Divide the area into quadrants and recurse.
3803  */
3804  depth--;
3805  attenuate++;
3806  x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3807  y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
3808  local_info=(*segment);
3809  local_info.x2=(double) x_mid;
3810  local_info.y2=(double) y_mid;
3811  (void) PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3812  &local_info,attenuate,depth,exception);
3813  local_info=(*segment);
3814  local_info.y1=(double) y_mid;
3815  local_info.x2=(double) x_mid;
3816  (void) PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3817  &local_info,attenuate,depth,exception);
3818  local_info=(*segment);
3819  local_info.x1=(double) x_mid;
3820  local_info.y2=(double) y_mid;
3821  (void) PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3822  &local_info,attenuate,depth,exception);
3823  local_info=(*segment);
3824  local_info.x1=(double) x_mid;
3825  local_info.y1=(double) y_mid;
3826  status=PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
3827  &local_info,attenuate,depth,exception);
3828  return(status);
3829  }
3830  x_mid=(ssize_t) ceil((segment->x1+segment->x2)/2-0.5);
3831  y_mid=(ssize_t) ceil((segment->y1+segment->y2)/2-0.5);
3832  if ((fabs(segment->x1-x_mid) < MagickEpsilon) &&
3833  (fabs(segment->x2-x_mid) < MagickEpsilon) &&
3834  (fabs(segment->y1-y_mid) < MagickEpsilon) &&
3835  (fabs(segment->y2-y_mid) < MagickEpsilon))
3836  return(MagickFalse);
3837  /*
3838  Average pixels and apply plasma.
3839  */
3840  plasma=(double) QuantumRange/(2.0*attenuate);
3841  if ((fabs(segment->x1-x_mid) > MagickEpsilon) ||
3842  (fabs(segment->x2-x_mid) > MagickEpsilon))
3843  {
3844  /*
3845  Left pixel.
3846  */
3847  x=(ssize_t) ceil(segment->x1-0.5);
3848  u=GetCacheViewVirtualPixels(u_view,x,(ssize_t) ceil(segment->y1-0.5),1,1,
3849  exception);
3850  v=GetCacheViewVirtualPixels(v_view,x,(ssize_t) ceil(segment->y2-0.5),1,1,
3851  exception);
3852  q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
3853  if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3854  (q == (Quantum *) NULL))
3855  return(MagickTrue);
3856  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3857  {
3858  PixelChannel channel = GetPixelChannelChannel(image,i);
3859  PixelTrait traits = GetPixelChannelTraits(image,channel);
3860  if (traits == UndefinedPixelTrait)
3861  continue;
3862  q[i]=PlasmaPixel(random_info,(u[i]+v[i])/2.0,plasma);
3863  }
3864  (void) SyncCacheViewAuthenticPixels(image_view,exception);
3865  if (fabs(segment->x1-segment->x2) > MagickEpsilon)
3866  {
3867  /*
3868  Right pixel.
3869  */
3870  x=(ssize_t) ceil(segment->x2-0.5);
3871  u=GetCacheViewVirtualPixels(u_view,x,(ssize_t) ceil(segment->y1-0.5),
3872  1,1,exception);
3873  v=GetCacheViewVirtualPixels(v_view,x,(ssize_t) ceil(segment->y2-0.5),
3874  1,1,exception);
3875  q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
3876  if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3877  (q == (Quantum *) NULL))
3878  return(MagickTrue);
3879  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3880  {
3881  PixelChannel channel = GetPixelChannelChannel(image,i);
3882  PixelTrait traits = GetPixelChannelTraits(image,channel);
3883  if (traits == UndefinedPixelTrait)
3884  continue;
3885  q[i]=PlasmaPixel(random_info,(u[i]+v[i])/2.0,plasma);
3886  }
3887  (void) SyncCacheViewAuthenticPixels(image_view,exception);
3888  }
3889  }
3890  if ((fabs(segment->y1-y_mid) > MagickEpsilon) ||
3891  (fabs(segment->y2-y_mid) > MagickEpsilon))
3892  {
3893  if ((fabs(segment->x1-x_mid) > MagickEpsilon) ||
3894  (fabs(segment->y2-y_mid) > MagickEpsilon))
3895  {
3896  /*
3897  Bottom pixel.
3898  */
3899  y=(ssize_t) ceil(segment->y2-0.5);
3900  u=GetCacheViewVirtualPixels(u_view,(ssize_t) ceil(segment->x1-0.5),y,
3901  1,1,exception);
3902  v=GetCacheViewVirtualPixels(v_view,(ssize_t) ceil(segment->x2-0.5),y,
3903  1,1,exception);
3904  q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
3905  if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3906  (q == (Quantum *) NULL))
3907  return(MagickTrue);
3908  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3909  {
3910  PixelChannel channel = GetPixelChannelChannel(image,i);
3911  PixelTrait traits = GetPixelChannelTraits(image,channel);
3912  if (traits == UndefinedPixelTrait)
3913  continue;
3914  q[i]=PlasmaPixel(random_info,(u[i]+v[i])/2.0,plasma);
3915  }
3916  (void) SyncCacheViewAuthenticPixels(image_view,exception);
3917  }
3918  if (fabs(segment->y1-segment->y2) > MagickEpsilon)
3919  {
3920  /*
3921  Top pixel.
3922  */
3923  y=(ssize_t) ceil(segment->y1-0.5);
3924  u=GetCacheViewVirtualPixels(u_view,(ssize_t) ceil(segment->x1-0.5),y,
3925  1,1,exception);
3926  v=GetCacheViewVirtualPixels(v_view,(ssize_t) ceil(segment->x2-0.5),y,
3927  1,1,exception);
3928  q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
3929  if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3930  (q == (Quantum *) NULL))
3931  return(MagickTrue);
3932  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3933  {
3934  PixelChannel channel = GetPixelChannelChannel(image,i);
3935  PixelTrait traits = GetPixelChannelTraits(image,channel);
3936  if (traits == UndefinedPixelTrait)
3937  continue;
3938  q[i]=PlasmaPixel(random_info,(u[i]+v[i])/2.0,plasma);
3939  }
3940  (void) SyncCacheViewAuthenticPixels(image_view,exception);
3941  }
3942  }
3943  if ((fabs(segment->x1-segment->x2) > MagickEpsilon) ||
3944  (fabs(segment->y1-segment->y2) > MagickEpsilon))
3945  {
3946  /*
3947  Middle pixel.
3948  */
3949  x=(ssize_t) ceil(segment->x1-0.5);
3950  y=(ssize_t) ceil(segment->y1-0.5);
3951  u=GetCacheViewVirtualPixels(u_view,x,y,1,1,exception);
3952  x=(ssize_t) ceil(segment->x2-0.5);
3953  y=(ssize_t) ceil(segment->y2-0.5);
3954  v=GetCacheViewVirtualPixels(v_view,x,y,1,1,exception);
3955  q=QueueCacheViewAuthenticPixels(image_view,x_mid,y_mid,1,1,exception);
3956  if ((u == (const Quantum *) NULL) || (v == (const Quantum *) NULL) ||
3957  (q == (Quantum *) NULL))
3958  return(MagickTrue);
3959  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3960  {
3961  PixelChannel channel = GetPixelChannelChannel(image,i);
3962  PixelTrait traits = GetPixelChannelTraits(image,channel);
3963  if (traits == UndefinedPixelTrait)
3964  continue;
3965  q[i]=PlasmaPixel(random_info,(u[i]+v[i])/2.0,plasma);
3966  }
3967  (void) SyncCacheViewAuthenticPixels(image_view,exception);
3968  }
3969  if ((fabs(segment->x2-segment->x1) < 3.0) &&
3970  (fabs(segment->y2-segment->y1) < 3.0))
3971  return(MagickTrue);
3972  return(MagickFalse);
3973 }
3974 
3976  const SegmentInfo *segment,size_t attenuate,size_t depth,
3977  ExceptionInfo *exception)
3978 {
3979  CacheView
3980  *image_view,
3981  *u_view,
3982  *v_view;
3983 
3985  status;
3986 
3987  RandomInfo
3988  *random_info;
3989 
3990  assert(image != (Image *) NULL);
3991  if (image->debug != MagickFalse)
3992  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3993  assert(image->signature == MagickCoreSignature);
3994  if (image->debug != MagickFalse)
3995  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3996  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
3997  return(MagickFalse);
3998  image_view=AcquireAuthenticCacheView(image,exception);
3999  u_view=AcquireVirtualCacheView(image,exception);
4000  v_view=AcquireVirtualCacheView(image,exception);
4001  random_info=AcquireRandomInfo();
4002  status=PlasmaImageProxy(image,image_view,u_view,v_view,random_info,segment,
4003  attenuate,depth,exception);
4004  random_info=DestroyRandomInfo(random_info);
4005  v_view=DestroyCacheView(v_view);
4006  u_view=DestroyCacheView(u_view);
4007  image_view=DestroyCacheView(image_view);
4008  return(status);
4009 }
4010 
4011 /*
4012 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4013 % %
4014 % %
4015 % %
4016 % P o l a r o i d I m a g e %
4017 % %
4018 % %
4019 % %
4020 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4021 %
4022 % PolaroidImage() simulates a Polaroid picture.
4023 %
4024 % The format of the PolaroidImage method is:
4025 %
4026 % Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
4027 % const char *caption,const double angle,
4028 % const PixelInterpolateMethod method,ExceptionInfo exception)
4029 %
4030 % A description of each parameter follows:
4031 %
4032 % o image: the image.
4033 %
4034 % o draw_info: the draw info.
4035 %
4036 % o caption: the Polaroid caption.
4037 %
4038 % o angle: Apply the effect along this angle.
4039 %
4040 % o method: the pixel interpolation method.
4041 %
4042 % o exception: return any errors or warnings in this structure.
4043 %
4044 */
4045 MagickExport Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
4046  const char *caption,const double angle,const PixelInterpolateMethod method,
4047  ExceptionInfo *exception)
4048 {
4049  Image
4050  *bend_image,
4051  *caption_image,
4052  *flop_image,
4053  *picture_image,
4054  *polaroid_image,
4055  *rotate_image,
4056  *trim_image;
4057 
4058  size_t
4059  height;
4060 
4061  ssize_t
4062  quantum;
4063 
4064  /*
4065  Simulate a Polaroid picture.
4066  */
4067  assert(image != (Image *) NULL);
4068  assert(image->signature == MagickCoreSignature);
4069  if (image->debug != MagickFalse)
4070  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4071  assert(exception != (ExceptionInfo *) NULL);
4072  assert(exception->signature == MagickCoreSignature);
4073  quantum=(ssize_t) MagickMax(MagickMax((double) image->columns,(double)
4074  image->rows)/25.0,10.0);
4075  height=image->rows+2*quantum;
4076  caption_image=(Image *) NULL;
4077  if (caption != (const char *) NULL)
4078  {
4079  char
4080  *text;
4081 
4082  /*
4083  Generate caption image.
4084  */
4085  caption_image=CloneImage(image,image->columns,1,MagickTrue,exception);
4086  if (caption_image == (Image *) NULL)
4087  return((Image *) NULL);
4088  text=InterpretImageProperties((ImageInfo *) NULL,(Image *) image,caption,
4089  exception);
4090  if (text != (char *) NULL)
4091  {
4092  char
4093  geometry[MagickPathExtent];
4094 
4095  DrawInfo
4096  *annotate_info;
4097 
4099  status;
4100 
4101  ssize_t
4102  count;
4103 
4104  TypeMetric
4105  metrics;
4106 
4107  annotate_info=CloneDrawInfo((const ImageInfo *) NULL,draw_info);
4108  (void) CloneString(&annotate_info->text,text);
4109  count=FormatMagickCaption(caption_image,annotate_info,MagickTrue,
4110  &metrics,&text,exception);
4111  status=SetImageExtent(caption_image,image->columns,(size_t)
4112  ((count+1)*(metrics.ascent-metrics.descent)+0.5),exception);
4113  if (status == MagickFalse)
4114  caption_image=DestroyImage(caption_image);
4115  else
4116  {
4117  caption_image->background_color=image->border_color;
4118  (void) SetImageBackgroundColor(caption_image,exception);
4119  (void) CloneString(&annotate_info->text,text);
4120  (void) FormatLocaleString(geometry,MagickPathExtent,"+0+%.20g",
4121  metrics.ascent);
4122  if (annotate_info->gravity == UndefinedGravity)
4123  (void) CloneString(&annotate_info->geometry,AcquireString(
4124  geometry));
4125  (void) AnnotateImage(caption_image,annotate_info,exception);
4126  height+=caption_image->rows;
4127  }
4128  annotate_info=DestroyDrawInfo(annotate_info);
4129  text=DestroyString(text);
4130  }
4131  }
4132  picture_image=CloneImage(image,image->columns+2*quantum,height,MagickTrue,
4133  exception);
4134  if (picture_image == (Image *) NULL)
4135  {
4136  if (caption_image != (Image *) NULL)
4137  caption_image=DestroyImage(caption_image);
4138  return((Image *) NULL);
4139  }
4140  picture_image->background_color=image->border_color;
4141  (void) SetImageBackgroundColor(picture_image,exception);
4142  (void) CompositeImage(picture_image,image,OverCompositeOp,MagickTrue,quantum,
4143  quantum,exception);
4144  if (caption_image != (Image *) NULL)
4145  {
4146  (void) CompositeImage(picture_image,caption_image,OverCompositeOp,
4147  MagickTrue,quantum,(ssize_t) (image->rows+3*quantum/2),exception);
4148  caption_image=DestroyImage(caption_image);
4149  }
4150  (void) QueryColorCompliance("none",AllCompliance,
4151  &picture_image->background_color,exception);
4152  (void) SetImageAlphaChannel(picture_image,OpaqueAlphaChannel,exception);
4153  rotate_image=RotateImage(picture_image,90.0,exception);
4154  picture_image=DestroyImage(picture_image);
4155  if (rotate_image == (Image *) NULL)
4156  return((Image *) NULL);
4157  picture_image=rotate_image;
4158  bend_image=WaveImage(picture_image,0.01*picture_image->rows,2.0*
4159  picture_image->columns,method,exception);
4160  picture_image=DestroyImage(picture_image);
4161  if (bend_image == (Image *) NULL)
4162  return((Image *) NULL);
4163  picture_image=bend_image;
4164  rotate_image=RotateImage(picture_image,-90.0,exception);
4165  picture_image=DestroyImage(picture_image);
4166  if (rotate_image == (Image *) NULL)
4167  return((Image *) NULL);
4168  picture_image=rotate_image;
4169  picture_image->background_color=image->background_color;
4170  polaroid_image=ShadowImage(picture_image,80.0,2.0,quantum/3,quantum/3,
4171  exception);
4172  if (polaroid_image == (Image *) NULL)
4173  {
4174  picture_image=DestroyImage(picture_image);
4175  return(picture_image);
4176  }
4177  flop_image=FlopImage(polaroid_image,exception);
4178  polaroid_image=DestroyImage(polaroid_image);
4179  if (flop_image == (Image *) NULL)
4180  {
4181  picture_image=DestroyImage(picture_image);
4182  return(picture_image);
4183  }
4184  polaroid_image=flop_image;
4185  (void) CompositeImage(polaroid_image,picture_image,OverCompositeOp,
4186  MagickTrue,(ssize_t) (-0.01*picture_image->columns/2.0),0L,exception);
4187  picture_image=DestroyImage(picture_image);
4188  (void) QueryColorCompliance("none",AllCompliance,
4189  &polaroid_image->background_color,exception);
4190  rotate_image=RotateImage(polaroid_image,angle,exception);
4191  polaroid_image=DestroyImage(polaroid_image);
4192  if (rotate_image == (Image *) NULL)
4193  return((Image *) NULL);
4194  polaroid_image=rotate_image;
4195  trim_image=TrimImage(polaroid_image,exception);
4196  polaroid_image=DestroyImage(polaroid_image);
4197  if (trim_image == (Image *) NULL)
4198  return((Image *) NULL);
4199  polaroid_image=trim_image;
4200  return(polaroid_image);
4201 }
4202 
4203 /*
4204 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4205 % %
4206 % %
4207 % %
4208 % S e p i a T o n e I m a g e %
4209 % %
4210 % %
4211 % %
4212 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4213 %
4214 % MagickSepiaToneImage() applies a special effect to the image, similar to the
4215 % effect achieved in a photo darkroom by sepia toning. Threshold ranges from
4216 % 0 to QuantumRange and is a measure of the extent of the sepia toning. A
4217 % threshold of 80% is a good starting point for a reasonable tone.
4218 %
4219 % The format of the SepiaToneImage method is:
4220 %
4221 % Image *SepiaToneImage(const Image *image,const double threshold,
4222 % ExceptionInfo *exception)
4223 %
4224 % A description of each parameter follows:
4225 %
4226 % o image: the image.
4227 %
4228 % o threshold: the tone threshold.
4229 %
4230 % o exception: return any errors or warnings in this structure.
4231 %
4232 */
4233 MagickExport Image *SepiaToneImage(const Image *image,const double threshold,
4234  ExceptionInfo *exception)
4235 {
4236 #define SepiaToneImageTag "SepiaTone/Image"
4237 
4238  CacheView
4239  *image_view,
4240  *sepia_view;
4241 
4242  Image
4243  *sepia_image;
4244 
4246  status;
4247 
4249  progress;
4250 
4251  ssize_t
4252  y;
4253 
4254  /*
4255  Initialize sepia-toned image attributes.
4256  */
4257  assert(image != (const Image *) NULL);
4258  assert(image->signature == MagickCoreSignature);
4259  if (image->debug != MagickFalse)
4260  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4261  assert(exception != (ExceptionInfo *) NULL);
4262  assert(exception->signature == MagickCoreSignature);
4263  sepia_image=CloneImage(image,0,0,MagickTrue,exception);
4264  if (sepia_image == (Image *) NULL)
4265  return((Image *) NULL);
4266  if (SetImageStorageClass(sepia_image,DirectClass,exception) == MagickFalse)
4267  {
4268  sepia_image=DestroyImage(sepia_image);
4269  return((Image *) NULL);
4270  }
4271  /*
4272  Tone each row of the image.
4273  */
4274  status=MagickTrue;
4275  progress=0;
4276  image_view=AcquireVirtualCacheView(image,exception);
4277  sepia_view=AcquireAuthenticCacheView(sepia_image,exception);
4278 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4279  #pragma omp parallel for schedule(static) shared(progress,status) \
4280  magick_number_threads(image,sepia_image,image->rows,1)
4281 #endif
4282  for (y=0; y < (ssize_t) image->rows; y++)
4283  {
4284  register const Quantum
4285  *magick_restrict p;
4286 
4287  register ssize_t
4288  x;
4289 
4290  register Quantum
4291  *magick_restrict q;
4292 
4293  if (status == MagickFalse)
4294  continue;
4295  p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
4296  q=GetCacheViewAuthenticPixels(sepia_view,0,y,sepia_image->columns,1,
4297  exception);
4298  if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
4299  {
4300  status=MagickFalse;
4301  continue;
4302  }
4303  for (x=0; x < (ssize_t) image->columns; x++)
4304  {
4305  double
4306  intensity,
4307  tone;
4308 
4309  intensity=GetPixelIntensity(image,p);
4310  tone=intensity > threshold ? (double) QuantumRange : intensity+
4311  (double) QuantumRange-threshold;
4312  SetPixelRed(sepia_image,ClampToQuantum(tone),q);
4313  tone=intensity > (7.0*threshold/6.0) ? (double) QuantumRange :
4314  intensity+(double) QuantumRange-7.0*threshold/6.0;
4315  SetPixelGreen(sepia_image,ClampToQuantum(tone),q);
4316  tone=intensity < (threshold/6.0) ? 0 : intensity-threshold/6.0;
4317  SetPixelBlue(sepia_image,ClampToQuantum(tone),q);
4318  tone=threshold/7.0;
4319  if ((double) GetPixelGreen(image,q) < tone)
4320  SetPixelGreen(sepia_image,ClampToQuantum(tone),q);
4321  if ((double) GetPixelBlue(image,q) < tone)
4322  SetPixelBlue(sepia_image,ClampToQuantum(tone),q);
4323  SetPixelAlpha(sepia_image,GetPixelAlpha(image,p),q);
4324  p+=GetPixelChannels(image);
4325  q+=GetPixelChannels(sepia_image);
4326  }
4327  if (SyncCacheViewAuthenticPixels(sepia_view,exception) == MagickFalse)
4328  status=MagickFalse;
4329  if (image->progress_monitor != (MagickProgressMonitor) NULL)
4330  {
4332  proceed;
4333 
4334 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4335  #pragma omp atomic
4336 #endif
4337  progress++;
4338  proceed=SetImageProgress(image,SepiaToneImageTag,progress,image->rows);
4339  if (proceed == MagickFalse)
4340  status=MagickFalse;
4341  }
4342  }
4343  sepia_view=DestroyCacheView(sepia_view);
4344  image_view=DestroyCacheView(image_view);
4345  (void) NormalizeImage(sepia_image,exception);
4346  (void) ContrastImage(sepia_image,MagickTrue,exception);
4347  if (status == MagickFalse)
4348  sepia_image=DestroyImage(sepia_image);
4349  return(sepia_image);
4350 }
4351 
4352 /*
4353 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4354 % %
4355 % %
4356 % %
4357 % S h a d o w I m a g e %
4358 % %
4359 % %
4360 % %
4361 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4362 %
4363 % ShadowImage() simulates a shadow from the specified image and returns it.
4364 %
4365 % The format of the ShadowImage method is:
4366 %
4367 % Image *ShadowImage(const Image *image,const double alpha,
4368 % const double sigma,const ssize_t x_offset,const ssize_t y_offset,
4369 % ExceptionInfo *exception)
4370 %
4371 % A description of each parameter follows:
4372 %
4373 % o image: the image.
4374 %
4375 % o alpha: percentage transparency.
4376 %
4377 % o sigma: the standard deviation of the Gaussian, in pixels.
4378 %
4379 % o x_offset: the shadow x-offset.
4380 %
4381 % o y_offset: the shadow y-offset.
4382 %
4383 % o exception: return any errors or warnings in this structure.
4384 %
4385 */
4386 MagickExport Image *ShadowImage(const Image *image,const double alpha,
4387  const double sigma,const ssize_t x_offset,const ssize_t y_offset,
4388  ExceptionInfo *exception)
4389 {
4390 #define ShadowImageTag "Shadow/Image"
4391 
4392  CacheView
4393  *image_view;
4394 
4395  ChannelType
4396  channel_mask;
4397 
4398  Image
4399  *border_image,
4400  *clone_image,
4401  *shadow_image;
4402 
4404  status;
4405 
4406  PixelInfo
4407  background_color;
4408 
4410  border_info;
4411 
4412  ssize_t
4413  y;
4414 
4415  assert(image != (Image *) NULL);
4416  assert(image->signature == MagickCoreSignature);
4417  if (image->debug != MagickFalse)
4418  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4419  assert(exception != (ExceptionInfo *) NULL);
4420  assert(exception->signature == MagickCoreSignature);
4421  clone_image=CloneImage(image,0,0,MagickTrue,exception);
4422  if (clone_image == (Image *) NULL)
4423  return((Image *) NULL);
4424  if (IsGrayColorspace(image->colorspace) != MagickFalse)
4425  (void) SetImageColorspace(clone_image,sRGBColorspace,exception);
4427  exception);
4428  border_info.width=(size_t) floor(2.0*sigma+0.5);
4429  border_info.height=(size_t) floor(2.0*sigma+0.5);
4430  border_info.x=0;
4431  border_info.y=0;
4432  (void) QueryColorCompliance("none",AllCompliance,&clone_image->border_color,
4433  exception);
4434  clone_image->alpha_trait=BlendPixelTrait;
4435  border_image=BorderImage(clone_image,&border_info,OverCompositeOp,exception);
4436  clone_image=DestroyImage(clone_image);
4437  if (border_image == (Image *) NULL)
4438  return((Image *) NULL);
4439  if (border_image->alpha_trait == UndefinedPixelTrait)
4440  (void) SetImageAlphaChannel(border_image,OpaqueAlphaChannel,exception);
4441  /*
4442  Shadow image.
4443  */
4444  status=MagickTrue;
4445  background_color=border_image->background_color;
4446  background_color.alpha_trait=BlendPixelTrait;
4447  image_view=AcquireAuthenticCacheView(border_image,exception);
4448  for (y=0; y < (ssize_t) border_image->rows; y++)
4449  {
4450  register Quantum
4451  *magick_restrict q;
4452 
4453  register ssize_t
4454  x;
4455 
4456  if (status == MagickFalse)
4457  continue;
4458  q=QueueCacheViewAuthenticPixels(image_view,0,y,border_image->columns,1,
4459  exception);
4460  if (q == (Quantum *) NULL)
4461  {
4462  status=MagickFalse;
4463  continue;
4464  }
4465  for (x=0; x < (ssize_t) border_image->columns; x++)
4466  {
4467  if (border_image->alpha_trait != UndefinedPixelTrait)
4468  background_color.alpha=GetPixelAlpha(border_image,q)*alpha/100.0;
4469  SetPixelViaPixelInfo(border_image,&background_color,q);
4470  q+=GetPixelChannels(border_image);
4471  }
4472  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4473  status=MagickFalse;
4474  }
4475  image_view=DestroyCacheView(image_view);
4476  if (status == MagickFalse)
4477  {
4478  border_image=DestroyImage(border_image);
4479  return((Image *) NULL);
4480  }
4481  channel_mask=SetImageChannelMask(border_image,AlphaChannel);
4482  shadow_image=BlurImage(border_image,0.0,sigma,exception);
4483  border_image=DestroyImage(border_image);
4484  if (shadow_image == (Image *) NULL)
4485  return((Image *) NULL);
4486  (void) SetPixelChannelMask(shadow_image,channel_mask);
4487  if (shadow_image->page.width == 0)
4488  shadow_image->page.width=shadow_image->columns;
4489  if (shadow_image->page.height == 0)
4490  shadow_image->page.height=shadow_image->rows;
4491  shadow_image->page.width+=x_offset-(ssize_t) border_info.width;
4492  shadow_image->page.height+=y_offset-(ssize_t) border_info.height;
4493  shadow_image->page.x+=x_offset-(ssize_t) border_info.width;
4494  shadow_image->page.y+=y_offset-(ssize_t) border_info.height;
4495  return(shadow_image);
4496 }
4497 
4498 /*
4499 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4500 % %
4501 % %
4502 % %
4503 % S k e t c h I m a g e %
4504 % %
4505 % %
4506 % %
4507 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4508 %
4509 % SketchImage() simulates a pencil sketch. We convolve the image with a
4510 % Gaussian operator of the given radius and standard deviation (sigma). For
4511 % reasonable results, radius should be larger than sigma. Use a radius of 0
4512 % and SketchImage() selects a suitable radius for you. Angle gives the angle
4513 % of the sketch.
4514 %
4515 % The format of the SketchImage method is:
4516 %
4517 % Image *SketchImage(const Image *image,const double radius,
4518 % const double sigma,const double angle,ExceptionInfo *exception)
4519 %
4520 % A description of each parameter follows:
4521 %
4522 % o image: the image.
4523 %
4524 % o radius: the radius of the Gaussian, in pixels, not counting the
4525 % center pixel.
4526 %
4527 % o sigma: the standard deviation of the Gaussian, in pixels.
4528 %
4529 % o angle: apply the effect along this angle.
4530 %
4531 % o exception: return any errors or warnings in this structure.
4532 %
4533 */
4534 MagickExport Image *SketchImage(const Image *image,const double radius,
4535  const double sigma,const double angle,ExceptionInfo *exception)
4536 {
4537  CacheView
4538  *random_view;
4539 
4540  Image
4541  *blend_image,
4542  *blur_image,
4543  *dodge_image,
4544  *random_image,
4545  *sketch_image;
4546 
4548  status;
4549 
4550  RandomInfo
4552 
4553  ssize_t
4554  y;
4555 
4556 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4557  unsigned long
4558  key;
4559 #endif
4560 
4561  /*
4562  Sketch image.
4563  */
4564  random_image=CloneImage(image,image->columns << 1,image->rows << 1,
4565  MagickTrue,exception);
4566  if (random_image == (Image *) NULL)
4567  return((Image *) NULL);
4568  status=MagickTrue;
4569  random_info=AcquireRandomInfoThreadSet();
4570  random_view=AcquireAuthenticCacheView(random_image,exception);
4571 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4572  key=GetRandomSecretKey(random_info[0]);
4573  #pragma omp parallel for schedule(static) shared(status) \
4574  magick_number_threads(random_image,random_image,random_image->rows,key == ~0UL)
4575 #endif
4576  for (y=0; y < (ssize_t) random_image->rows; y++)
4577  {
4578  const int
4579  id = GetOpenMPThreadId();
4580 
4581  register Quantum
4582  *magick_restrict q;
4583 
4584  register ssize_t
4585  x;
4586 
4587  if (status == MagickFalse)
4588  continue;
4589  q=QueueCacheViewAuthenticPixels(random_view,0,y,random_image->columns,1,
4590  exception);
4591  if (q == (Quantum *) NULL)
4592  {
4593  status=MagickFalse;
4594  continue;
4595  }
4596  for (x=0; x < (ssize_t) random_image->columns; x++)
4597  {
4598  double
4599  value;
4600 
4601  register ssize_t
4602  i;
4603 
4604  value=GetPseudoRandomValue(random_info[id]);
4605  for (i=0; i < (ssize_t) GetPixelChannels(random_image); i++)
4606  {
4607  PixelChannel channel = GetPixelChannelChannel(image,i);
4608  PixelTrait traits = GetPixelChannelTraits(image,channel);
4609  if (traits == UndefinedPixelTrait)
4610  continue;
4611  q[i]=ClampToQuantum(QuantumRange*value);
4612  }
4613  q+=GetPixelChannels(random_image);
4614  }
4615  if (SyncCacheViewAuthenticPixels(random_view,exception) == MagickFalse)
4616  status=MagickFalse;
4617  }
4618  random_view=DestroyCacheView(random_view);
4619  random_info=DestroyRandomInfoThreadSet(random_info);
4620  if (status == MagickFalse)
4621  {
4622  random_image=DestroyImage(random_image);
4623  return(random_image);
4624  }
4625  blur_image=MotionBlurImage(random_image,radius,sigma,angle,exception);
4626  random_image=DestroyImage(random_image);
4627  if (blur_image == (Image *) NULL)
4628  return((Image *) NULL);
4629  dodge_image=EdgeImage(blur_image,radius,exception);
4630  blur_image=DestroyImage(blur_image);
4631  if (dodge_image == (Image *) NULL)
4632  return((Image *) NULL);
4633  status=ClampImage(dodge_image,exception);
4634  if (status != MagickFalse)
4635  status=NormalizeImage(dodge_image,exception);
4636  if (status != MagickFalse)
4637  status=NegateImage(dodge_image,MagickFalse,exception);
4638  if (status != MagickFalse)
4639  status=TransformImage(&dodge_image,(char *) NULL,"50%",exception);
4640  sketch_image=CloneImage(image,0,0,MagickTrue,exception);
4641  if (sketch_image == (Image *) NULL)
4642  {
4643  dodge_image=DestroyImage(dodge_image);
4644  return((Image *) NULL);
4645  }
4646  (void) CompositeImage(sketch_image,dodge_image,ColorDodgeCompositeOp,
4647  MagickTrue,0,0,exception);
4648  dodge_image=DestroyImage(dodge_image);
4649  blend_image=CloneImage(image,0,0,MagickTrue,exception);
4650  if (blend_image == (Image *) NULL)
4651  {
4652  sketch_image=DestroyImage(sketch_image);
4653  return((Image *) NULL);
4654  }
4655  if (blend_image->alpha_trait != BlendPixelTrait)
4656  (void) SetImageAlpha(blend_image,TransparentAlpha,exception);
4657  (void) SetImageArtifact(blend_image,"compose:args","20x80");
4658  (void) CompositeImage(sketch_image,blend_image,BlendCompositeOp,MagickTrue,
4659  0,0,exception);
4660  blend_image=DestroyImage(blend_image);
4661  return(sketch_image);
4662 }
4663 
4664 /*
4665 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4666 % %
4667 % %
4668 % %
4669 % S o l a r i z e I m a g e %
4670 % %
4671 % %
4672 % %
4673 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4674 %
4675 % SolarizeImage() applies a special effect to the image, similar to the effect
4676 % achieved in a photo darkroom by selectively exposing areas of photo
4677 % sensitive paper to light. Threshold ranges from 0 to QuantumRange and is a
4678 % measure of the extent of the solarization.
4679 %
4680 % The format of the SolarizeImage method is:
4681 %
4682 % MagickBooleanType SolarizeImage(Image *image,const double threshold,
4683 % ExceptionInfo *exception)
4684 %
4685 % A description of each parameter follows:
4686 %
4687 % o image: the image.
4688 %
4689 % o threshold: Define the extent of the solarization.
4690 %
4691 % o exception: return any errors or warnings in this structure.
4692 %
4693 */
4695  const double threshold,ExceptionInfo *exception)
4696 {
4697 #define SolarizeImageTag "Solarize/Image"
4698 
4699  CacheView
4700  *image_view;
4701 
4703  status;
4704 
4706  progress;
4707 
4708  ssize_t
4709  y;
4710 
4711  assert(image != (Image *) NULL);
4712  assert(image->signature == MagickCoreSignature);
4713  if (image->debug != MagickFalse)
4714  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4715  if (IsGrayColorspace(image->colorspace) != MagickFalse)
4716  (void) SetImageColorspace(image,sRGBColorspace,exception);
4717  if (image->storage_class == PseudoClass)
4718  {
4719  register ssize_t
4720  i;
4721 
4722  /*
4723  Solarize colormap.
4724  */
4725  for (i=0; i < (ssize_t) image->colors; i++)
4726  {
4727  if ((double) image->colormap[i].red > threshold)
4728  image->colormap[i].red=QuantumRange-image->colormap[i].red;
4729  if ((double) image->colormap[i].green > threshold)
4730  image->colormap[i].green=QuantumRange-image->colormap[i].green;
4731  if ((double) image->colormap[i].blue > threshold)
4732  image->colormap[i].blue=QuantumRange-image->colormap[i].blue;
4733  }
4734  }
4735  /*
4736  Solarize image.
4737  */
4738  status=MagickTrue;
4739  progress=0;
4740  image_view=AcquireAuthenticCacheView(image,exception);
4741 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4742  #pragma omp parallel for schedule(static) shared(progress,status) \
4743  magick_number_threads(image,image,image->rows,1)
4744 #endif
4745  for (y=0; y < (ssize_t) image->rows; y++)
4746  {
4747  register ssize_t
4748  x;
4749 
4750  register Quantum
4751  *magick_restrict q;
4752 
4753  if (status == MagickFalse)
4754  continue;
4755  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
4756  if (q == (Quantum *) NULL)
4757  {
4758  status=MagickFalse;
4759  continue;
4760  }
4761  for (x=0; x < (ssize_t) image->columns; x++)
4762  {
4763  register ssize_t
4764  i;
4765 
4766  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
4767  {
4768  PixelChannel channel = GetPixelChannelChannel(image,i);
4769  PixelTrait traits = GetPixelChannelTraits(image,channel);
4770  if ((traits & UpdatePixelTrait) == 0)
4771  continue;
4772  if ((double) q[i] > threshold)
4773  q[i]=QuantumRange-q[i];
4774  }
4775  q+=GetPixelChannels(image);
4776  }
4777  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4778  status=MagickFalse;
4779  if (image->progress_monitor != (MagickProgressMonitor) NULL)
4780  {
4782  proceed;
4783 
4784 #if defined(MAGICKCORE_OPENMP_SUPPORT)
4785  #pragma omp atomic
4786 #endif
4787  progress++;
4788  proceed=SetImageProgress(image,SolarizeImageTag,progress,image->rows);
4789  if (proceed == MagickFalse)
4790  status=MagickFalse;
4791  }
4792  }
4793  image_view=DestroyCacheView(image_view);
4794  return(status);
4795 }
4796 
4797 /*
4798 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4799 % %
4800 % %
4801 % %
4802 % S t e g a n o I m a g e %
4803 % %
4804 % %
4805 % %
4806 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4807 %
4808 % SteganoImage() hides a digital watermark within the image. Recover
4809 % the hidden watermark later to prove that the authenticity of an image.
4810 % Offset defines the start position within the image to hide the watermark.
4811 %
4812 % The format of the SteganoImage method is:
4813 %
4814 % Image *SteganoImage(const Image *image,Image *watermark,
4815 % ExceptionInfo *exception)
4816 %
4817 % A description of each parameter follows:
4818 %
4819 % o image: the image.
4820 %
4821 % o watermark: the watermark image.
4822 %
4823 % o exception: return any errors or warnings in this structure.
4824 %
4825 */
4826 MagickExport Image *SteganoImage(const Image *image,const Image *watermark,
4827  ExceptionInfo *exception)
4828 {
4829 #define GetBit(alpha,i) ((((size_t) (alpha) >> (size_t) (i)) & 0x01) != 0)
4830 #define SetBit(alpha,i,set) (Quantum) ((set) != 0 ? (size_t) (alpha) \
4831  | (one << (size_t) (i)) : (size_t) (alpha) & ~(one << (size_t) (i)))
4832 #define SteganoImageTag "Stegano/Image"
4833 
4834  CacheView
4835  *stegano_view,
4836  *watermark_view;
4837 
4838  Image
4839  *stegano_image;
4840 
4841  int
4842  c;
4843 
4845  status;
4846 
4847  PixelInfo
4848  pixel;
4849 
4850  register Quantum
4851  *q;
4852 
4853  register ssize_t
4854  x;
4855 
4856  size_t
4857  depth,
4858  one;
4859 
4860  ssize_t
4861  i,
4862  j,
4863  k,
4864  y;
4865 
4866  /*
4867  Initialize steganographic image attributes.
4868  */
4869  assert(image != (const Image *) NULL);
4870  assert(image->signature == MagickCoreSignature);
4871  if (image->debug != MagickFalse)
4872  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
4873  assert(watermark != (const Image *) NULL);
4874  assert(watermark->signature == MagickCoreSignature);
4875  assert(exception != (ExceptionInfo *) NULL);
4876  assert(exception->signature == MagickCoreSignature);
4877  one=1UL;
4878  stegano_image=CloneImage(image,0,0,MagickTrue,exception);
4879  if (stegano_image == (Image *) NULL)
4880  return((Image *) NULL);
4881  stegano_image->depth=MAGICKCORE_QUANTUM_DEPTH;
4882  if (SetImageStorageClass(stegano_image,DirectClass,exception) == MagickFalse)
4883  {
4884  stegano_image=DestroyImage(stegano_image);
4885  return((Image *) NULL);
4886  }
4887  /*
4888  Hide watermark in low-order bits of image.
4889  */
4890  c=0;
4891  i=0;
4892  j=0;
4893  depth=stegano_image->depth;
4894  k=stegano_image->offset;
4895  status=MagickTrue;
4896  watermark_view=AcquireVirtualCacheView(watermark,exception);
4897  stegano_view=AcquireAuthenticCacheView(stegano_image,exception);
4898  for (i=(ssize_t) depth-1; (i >= 0) && (j < (ssize_t) depth); i--)
4899  {
4900  for (y=0; (y < (ssize_t) watermark->rows) && (j < (ssize_t) depth); y++)
4901  {
4902  for (x=0; (x < (ssize_t) watermark->columns) && (j < (ssize_t) depth); x++)
4903  {
4904  ssize_t
4905  offset;
4906 
4907  (void) GetOneCacheViewVirtualPixelInfo(watermark_view,x,y,&pixel,
4908  exception);
4909  offset=k/(ssize_t) stegano_image->columns;
4910  if (offset >= (ssize_t) stegano_image->rows)
4911  break;
4912  q=GetCacheViewAuthenticPixels(stegano_view,k % (ssize_t)
4913  stegano_image->columns,k/(ssize_t) stegano_image->columns,1,1,
4914  exception);
4915  if (q == (Quantum *) NULL)
4916  break;
4917  switch (c)
4918  {
4919  case 0:
4920  {
4921  SetPixelRed(stegano_image,SetBit(GetPixelRed(stegano_image,q),j,
4922  GetBit(GetPixelInfoIntensity(stegano_image,&pixel),i)),q);
4923  break;
4924  }
4925  case 1:
4926  {
4927  SetPixelGreen(stegano_image,SetBit(GetPixelGreen(stegano_image,q),j,
4928  GetBit(GetPixelInfoIntensity(stegano_image,&pixel),i)),q);
4929  break;
4930  }
4931  case 2:
4932  {
4933  SetPixelBlue(stegano_image,SetBit(GetPixelBlue(stegano_image,q),j,
4934  GetBit(GetPixelInfoIntensity(stegano_image,&pixel),i)),q);
4935  break;
4936  }
4937  }
4938  if (SyncCacheViewAuthenticPixels(stegano_view,exception) == MagickFalse)
4939  break;
4940  c++;
4941  if (c == 3)
4942  c=0;
4943  k++;
4944  if (k == (ssize_t) (stegano_image->columns*stegano_image->columns))
4945  k=0;
4946  if (k == stegano_image->offset)
4947  j++;
4948  }
4949  }
4950  if (image->progress_monitor != (MagickProgressMonitor) NULL)
4951  {
4953  proceed;
4954 
4956  (depth-i),depth);
4957  if (proceed == MagickFalse)
4958  status=MagickFalse;
4959  }
4960  }
4961  stegano_view=DestroyCacheView(stegano_view);
4962  watermark_view=DestroyCacheView(watermark_view);
4963  if (status == MagickFalse)
4964  stegano_image=DestroyImage(stegano_image);
4965  return(stegano_image);
4966 }
4967 
4968 /*
4969 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4970 % %
4971 % %
4972 % %
4973 % S t e r e o A n a g l y p h I m a g e %
4974 % %
4975 % %
4976 % %
4977 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4978 %
4979 % StereoAnaglyphImage() combines two images and produces a single image that
4980 % is the composite of a left and right image of a stereo pair. Special
4981 % red-green stereo glasses are required to view this effect.
4982 %
4983 % The format of the StereoAnaglyphImage method is:
4984 %
4985 % Image *StereoImage(const Image *left_image,const Image *right_image,
4986 % ExceptionInfo *exception)
4987 % Image *StereoAnaglyphImage(const Image *left_image,
4988 % const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
4989 % ExceptionInfo *exception)
4990 %
4991 % A description of each parameter follows:
4992 %
4993 % o left_image: the left image.
4994 %
4995 % o right_image: the right image.
4996 %
4997 % o exception: return any errors or warnings in this structure.
4998 %
4999 % o x_offset: amount, in pixels, by which the left image is offset to the
5000 % right of the right image.
5001 %
5002 % o y_offset: amount, in pixels, by which the left image is offset to the
5003 % bottom of the right image.
5004 %
5005 %
5006 */
5008  const Image *right_image,ExceptionInfo *exception)
5009 {
5010  return(StereoAnaglyphImage(left_image,right_image,0,0,exception));
5011 }
5012 
5014  const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
5015  ExceptionInfo *exception)
5016 {
5017 #define StereoImageTag "Stereo/Image"
5018 
5019  const Image
5020  *image;
5021 
5022  Image
5023  *stereo_image;
5024 
5026  status;
5027 
5028  ssize_t
5029  y;
5030 
5031  assert(left_image != (const Image *) NULL);
5032  assert(left_image->signature == MagickCoreSignature);
5033  if (left_image->debug != MagickFalse)
5035  left_image->filename);
5036  assert(right_image != (const Image *) NULL);
5037  assert(right_image->signature == MagickCoreSignature);
5038  assert(exception != (ExceptionInfo *) NULL);
5039  assert(exception->signature == MagickCoreSignature);
5040  image=left_image;
5041  if ((left_image->columns != right_image->columns) ||
5042  (left_image->rows != right_image->rows))
5043  ThrowImageException(ImageError,"LeftAndRightImageSizesDiffer");
5044  /*
5045  Initialize stereo image attributes.
5046  */
5047  stereo_image=CloneImage(left_image,left_image->columns,left_image->rows,
5048  MagickTrue,exception);
5049  if (stereo_image == (Image *) NULL)
5050  return((Image *) NULL);
5051  if (SetImageStorageClass(stereo_image,DirectClass,exception) == MagickFalse)
5052  {
5053  stereo_image=DestroyImage(stereo_image);
5054  return((Image *) NULL);
5055  }
5056  (void) SetImageColorspace(stereo_image,sRGBColorspace,exception);
5057  /*
5058  Copy left image to red channel and right image to blue channel.
5059  */
5060  status=MagickTrue;
5061  for (y=0; y < (ssize_t) stereo_image->rows; y++)
5062  {
5063  register const Quantum
5064  *magick_restrict p,
5065  *magick_restrict q;
5066 
5067  register ssize_t
5068  x;
5069 
5070  register Quantum
5071  *magick_restrict r;
5072 
5073  p=GetVirtualPixels(left_image,-x_offset,y-y_offset,image->columns,1,
5074  exception);
5075  q=GetVirtualPixels(right_image,0,y,right_image->columns,1,exception);
5076  r=QueueAuthenticPixels(stereo_image,0,y,stereo_image->columns,1,exception);
5077  if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL) ||
5078  (r == (Quantum *) NULL))
5079  break;
5080  for (x=0; x < (ssize_t) stereo_image->columns; x++)
5081  {
5082  SetPixelRed(stereo_image,GetPixelRed(left_image,p),r);
5083  SetPixelGreen(stereo_image,GetPixelGreen(right_image,q),r);
5084  SetPixelBlue(stereo_image,GetPixelBlue(right_image,q),r);
5085  if ((GetPixelAlphaTraits(stereo_image) & CopyPixelTrait) != 0)
5086  SetPixelAlpha(stereo_image,(GetPixelAlpha(left_image,p)+
5087  GetPixelAlpha(right_image,q))/2,r);
5088  p+=GetPixelChannels(left_image);
5089  q+=GetPixelChannels(right_image);
5090  r+=GetPixelChannels(stereo_image);
5091  }
5092  if (SyncAuthenticPixels(stereo_image,exception) == MagickFalse)
5093  break;
5094  if (image->progress_monitor != (MagickProgressMonitor) NULL)
5095  {
5097  proceed;
5098 
5100  stereo_image->rows);
5101  if (proceed == MagickFalse)
5102  status=MagickFalse;
5103  }
5104  }
5105  if (status == MagickFalse)
5106  stereo_image=DestroyImage(stereo_image);
5107  return(stereo_image);
5108 }
5109 
5110 /*
5111 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5112 % %
5113 % %
5114 % %
5115 % S w i r l I m a g e %
5116 % %
5117 % %
5118 % %
5119 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5120 %
5121 % SwirlImage() swirls the pixels about the center of the image, where
5122 % degrees indicates the sweep of the arc through which each pixel is moved.
5123 % You get a more dramatic effect as the degrees move from 1 to 360.
5124 %
5125 % The format of the SwirlImage method is:
5126 %
5127 % Image *SwirlImage(const Image *image,double degrees,
5128 % const PixelInterpolateMethod method,ExceptionInfo *exception)
5129 %
5130 % A description of each parameter follows:
5131 %
5132 % o image: the image.
5133 %
5134 % o degrees: Define the tightness of the swirling effect.
5135 %
5136 % o method: the pixel interpolation method.
5137 %
5138 % o exception: return any errors or warnings in this structure.
5139 %
5140 */
5141 MagickExport Image *SwirlImage(const Image *image,double degrees,
5142  const PixelInterpolateMethod method,ExceptionInfo *exception)
5143 {
5144 #define SwirlImageTag "Swirl/Image"
5145 
5146  CacheView
5147  *canvas_view,
5148  *interpolate_view,
5149  *swirl_view;
5150 
5151  double
5152  radius;
5153 
5154  Image
5155  *canvas_image,
5156  *swirl_image;
5157 
5159  status;
5160 
5162  progress;
5163 
5164  PointInfo
5165  center,
5166  scale;
5167 
5168  ssize_t
5169  y;
5170 
5171  /*
5172  Initialize swirl image attributes.
5173  */
5174  assert(image != (const Image *) NULL);
5175  assert(image->signature == MagickCoreSignature);
5176  if (image->debug != MagickFalse)
5177  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5178  assert(exception != (ExceptionInfo *) NULL);
5179  assert(exception->signature == MagickCoreSignature);
5180  canvas_image=CloneImage(image,0,0,MagickTrue,exception);
5181  if (canvas_image == (Image *) NULL)
5182  return((Image *) NULL);
5183  swirl_image=CloneImage(canvas_image,0,0,MagickTrue,exception);
5184  if (swirl_image == (Image *) NULL)
5185  {
5186  canvas_image=DestroyImage(canvas_image);
5187  return((Image *) NULL);
5188  }
5189  if (SetImageStorageClass(swirl_image,DirectClass,exception) == MagickFalse)
5190  {
5191  canvas_image=DestroyImage(canvas_image);
5192  swirl_image=DestroyImage(swirl_image);
5193  return((Image *) NULL);
5194  }
5195  if (swirl_image->background_color.alpha_trait != UndefinedPixelTrait)
5196  (void) SetImageAlphaChannel(swirl_image,OnAlphaChannel,exception);
5197  /*
5198  Compute scaling factor.
5199  */
5200  center.x=(double) canvas_image->columns/2.0;
5201  center.y=(double) canvas_image->rows/2.0;
5202  radius=MagickMax(center.x,center.y);
5203  scale.x=1.0;
5204  scale.y=1.0;
5205  if (canvas_image->columns > canvas_image->rows)
5206  scale.y=(double) canvas_image->columns/(double) canvas_image->rows;
5207  else
5208  if (canvas_image->columns < canvas_image->rows)
5209  scale.x=(double) canvas_image->rows/(double) canvas_image->columns;
5210  degrees=(double) DegreesToRadians(degrees);
5211  /*
5212  Swirl image.
5213  */
5214  status=MagickTrue;
5215  progress=0;
5216  canvas_view=AcquireVirtualCacheView(canvas_image,exception);
5217  interpolate_view=AcquireVirtualCacheView(image,exception);
5218  swirl_view=AcquireAuthenticCacheView(swirl_image,exception);
5219 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5220  #pragma omp parallel for schedule(static) shared(progress,status) \
5221  magick_number_threads(canvas_image,swirl_image,canvas_image->rows,1)
5222 #endif
5223  for (y=0; y < (ssize_t) canvas_image->rows; y++)
5224  {
5225  double
5226  distance;
5227 
5228  PointInfo
5229  delta;
5230 
5231  register const Quantum
5232  *magick_restrict p;
5233 
5234  register ssize_t
5235  x;
5236 
5237  register Quantum
5238  *magick_restrict q;
5239 
5240  if (status == MagickFalse)
5241  continue;
5242  p=GetCacheViewVirtualPixels(canvas_view,0,y,canvas_image->columns,1,
5243  exception);
5244  q=QueueCacheViewAuthenticPixels(swirl_view,0,y,swirl_image->columns,1,
5245  exception);
5246  if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
5247  {
5248  status=MagickFalse;
5249  continue;
5250  }
5251  delta.y=scale.y*(double) (y-center.y);
5252  for (x=0; x < (ssize_t) canvas_image->columns; x++)
5253  {
5254  /*
5255  Determine if the pixel is within an ellipse.
5256  */
5257  delta.x=scale.x*(double) (x-center.x);
5258  distance=delta.x*delta.x+delta.y*delta.y;
5259  if (distance >= (radius*radius))
5260  {
5261  register ssize_t
5262  i;
5263 
5264  for (i=0; i < (ssize_t) GetPixelChannels(canvas_image); i++)
5265  {
5266  PixelChannel channel = GetPixelChannelChannel(canvas_image,i);
5267  PixelTrait traits = GetPixelChannelTraits(canvas_image,channel);
5268  PixelTrait swirl_traits = GetPixelChannelTraits(swirl_image,
5269  channel);
5270  if ((traits == UndefinedPixelTrait) ||
5271  (swirl_traits == UndefinedPixelTrait))
5272  continue;
5273  SetPixelChannel(swirl_image,channel,p[i],q);
5274  }
5275  }
5276  else
5277  {
5278  double
5279  cosine,
5280  factor,
5281  sine;
5282 
5283  /*
5284  Swirl the pixel.
5285  */
5286  factor=1.0-sqrt((double) distance)/radius;
5287  sine=sin((double) (degrees*factor*factor));
5288  cosine=cos((double) (degrees*factor*factor));
5289  status=InterpolatePixelChannels(canvas_image,interpolate_view,
5290  swirl_image,method,((cosine*delta.x-sine*delta.y)/scale.x+center.x),
5291  (double) ((sine*delta.x+cosine*delta.y)/scale.y+center.y),q,
5292  exception);
5293  if (status == MagickFalse)
5294  break;
5295  }
5296  p+=GetPixelChannels(canvas_image);
5297  q+=GetPixelChannels(swirl_image);
5298  }
5299  if (SyncCacheViewAuthenticPixels(swirl_view,exception) == MagickFalse)
5300  status=MagickFalse;
5301  if (canvas_image->progress_monitor != (MagickProgressMonitor) NULL)
5302  {
5304  proceed;
5305 
5306 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5307  #pragma omp atomic
5308 #endif
5309  progress++;
5310  proceed=SetImageProgress(canvas_image,SwirlImageTag,progress,
5311  canvas_image->rows);
5312  if (proceed == MagickFalse)
5313  status=MagickFalse;
5314  }
5315  }
5316  swirl_view=DestroyCacheView(swirl_view);
5317  interpolate_view=DestroyCacheView(interpolate_view);
5318  canvas_view=DestroyCacheView(canvas_view);
5319  canvas_image=DestroyImage(canvas_image);
5320  if (status == MagickFalse)
5321  swirl_image=DestroyImage(swirl_image);
5322  return(swirl_image);
5323 }
5324 
5325 /*
5326 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5327 % %
5328 % %
5329 % %
5330 % T i n t I m a g e %
5331 % %
5332 % %
5333 % %
5334 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5335 %
5336 % TintImage() applies a color vector to each pixel in the image. The length
5337 % of the vector is 0 for black and white and at its maximum for the midtones.
5338 % The vector weighting function is f(x)=(1-(4.0*((x-0.5)*(x-0.5))))
5339 %
5340 % The format of the TintImage method is:
5341 %
5342 % Image *TintImage(const Image *image,const char *blend,
5343 % const PixelInfo *tint,ExceptionInfo *exception)
5344 %
5345 % A description of each parameter follows:
5346 %
5347 % o image: the image.
5348 %
5349 % o blend: A color value used for tinting.
5350 %
5351 % o tint: A color value used for tinting.
5352 %
5353 % o exception: return any errors or warnings in this structure.
5354 %
5355 */
5356 MagickExport Image *TintImage(const Image *image,const char *blend,
5357  const PixelInfo *tint,ExceptionInfo *exception)
5358 {
5359 #define TintImageTag "Tint/Image"
5360 
5361  CacheView
5362  *image_view,
5363  *tint_view;
5364 
5365  double
5366  intensity;
5367 
5368  GeometryInfo
5369  geometry_info;
5370 
5371  Image
5372  *tint_image;
5373 
5375  status;
5376 
5378  progress;
5379 
5380  PixelInfo
5381  color_vector;
5382 
5384  flags;
5385 
5386  ssize_t
5387  y;
5388 
5389  /*
5390  Allocate tint image.
5391  */
5392  assert(image != (const Image *) NULL);
5393  assert(image->signature == MagickCoreSignature);
5394  if (image->debug != MagickFalse)
5395  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5396  assert(exception != (ExceptionInfo *) NULL);
5397  assert(exception->signature == MagickCoreSignature);
5398  tint_image=CloneImage(image,0,0,MagickTrue,exception);
5399  if (tint_image == (Image *) NULL)
5400  return((Image *) NULL);
5401  if (SetImageStorageClass(tint_image,DirectClass,exception) == MagickFalse)
5402  {
5403  tint_image=DestroyImage(tint_image);
5404  return((Image *) NULL);
5405  }
5406  if ((IsGrayColorspace(image->colorspace) != MagickFalse) &&
5407  (IsPixelInfoGray(tint) == MagickFalse))
5408  (void) SetImageColorspace(tint_image,sRGBColorspace,exception);
5409  if (blend == (const char *) NULL)
5410  return(tint_image);
5411  /*
5412  Determine RGB values of the color.
5413  */
5414  GetPixelInfo(image,&color_vector);
5415  flags=ParseGeometry(blend,&geometry_info);
5416  color_vector.red=geometry_info.rho;
5417  color_vector.green=geometry_info.rho;
5418  color_vector.blue=geometry_info.rho;
5419  color_vector.alpha=(MagickRealType) OpaqueAlpha;
5420  if ((flags & SigmaValue) != 0)
5421  color_vector.green=geometry_info.sigma;
5422  if ((flags & XiValue) != 0)
5423  color_vector.blue=geometry_info.xi;
5424  if ((flags & PsiValue) != 0)
5425  color_vector.alpha=geometry_info.psi;
5426  if (image->colorspace == CMYKColorspace)
5427  {
5428  color_vector.black=geometry_info.rho;
5429  if ((flags & PsiValue) != 0)
5430  color_vector.black=geometry_info.psi;
5431  if ((flags & ChiValue) != 0)
5432  color_vector.alpha=geometry_info.chi;
5433  }
5434  intensity=(double) GetPixelInfoIntensity((const Image *) NULL,tint);
5435  color_vector.red=(double) (color_vector.red*tint->red/100.0-intensity);
5436  color_vector.green=(double) (color_vector.green*tint->green/100.0-intensity);
5437  color_vector.blue=(double) (color_vector.blue*tint->blue/100.0-intensity);
5438  color_vector.black=(double) (color_vector.black*tint->black/100.0-intensity);
5439  color_vector.alpha=(double) (color_vector.alpha*tint->alpha/100.0-intensity);
5440  /*
5441  Tint image.
5442  */
5443  status=MagickTrue;
5444  progress=0;
5445  image_view=AcquireVirtualCacheView(image,exception);
5446  tint_view=AcquireAuthenticCacheView(tint_image,exception);
5447 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5448  #pragma omp parallel for schedule(static) shared(progress,status) \
5449  magick_number_threads(image,tint_image,image->rows,1)
5450 #endif
5451  for (y=0; y < (ssize_t) image->rows; y++)
5452  {
5453  register const Quantum
5454  *magick_restrict p;
5455 
5456  register Quantum
5457  *magick_restrict q;
5458 
5459  register ssize_t
5460  x;
5461 
5462  if (status == MagickFalse)
5463  continue;
5464  p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
5465  q=QueueCacheViewAuthenticPixels(tint_view,0,y,tint_image->columns,1,
5466  exception);
5467  if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
5468  {
5469  status=MagickFalse;
5470  continue;
5471  }
5472  for (x=0; x < (ssize_t) image->columns; x++)
5473  {
5474  PixelInfo
5475  pixel;
5476 
5477  double
5478  weight;
5479 
5480  GetPixelInfo(image,&pixel);
5481  weight=QuantumScale*GetPixelRed(image,p)-0.5;
5482  pixel.red=(MagickRealType) GetPixelRed(image,p)+color_vector.red*
5483  (1.0-(4.0*(weight*weight)));
5484  weight=QuantumScale*GetPixelGreen(image,p)-0.5;
5485  pixel.green=(MagickRealType) GetPixelGreen(image,p)+color_vector.green*
5486  (1.0-(4.0*(weight*weight)));
5487  weight=QuantumScale*GetPixelBlue(image,p)-0.5;
5488  pixel.blue=(MagickRealType) GetPixelBlue(image,p)+color_vector.blue*
5489  (1.0-(4.0*(weight*weight)));
5490  weight=QuantumScale*GetPixelBlack(image,p)-0.5;
5491  pixel.black=(MagickRealType) GetPixelBlack(image,p)+color_vector.black*
5492  (1.0-(4.0*(weight*weight)));
5493  pixel.alpha=(MagickRealType) GetPixelAlpha(image,p);
5494  SetPixelViaPixelInfo(tint_image,&pixel,q);
5495  p+=GetPixelChannels(image);
5496  q+=GetPixelChannels(tint_image);
5497  }
5498  if (SyncCacheViewAuthenticPixels(tint_view,exception) == MagickFalse)
5499  status=MagickFalse;
5500  if (image->progress_monitor != (MagickProgressMonitor) NULL)
5501  {
5503  proceed;
5504 
5505 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5506  #pragma omp atomic
5507 #endif
5508  progress++;
5509  proceed=SetImageProgress(image,TintImageTag,progress,image->rows);
5510  if (proceed == MagickFalse)
5511  status=MagickFalse;
5512  }
5513  }
5514  tint_view=DestroyCacheView(tint_view);
5515  image_view=DestroyCacheView(image_view);
5516  if (status == MagickFalse)
5517  tint_image=DestroyImage(tint_image);
5518  return(tint_image);
5519 }
5520 
5521 /*
5522 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5523 % %
5524 % %
5525 % %
5526 % V i g n e t t e I m a g e %
5527 % %
5528 % %
5529 % %
5530 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5531 %
5532 % VignetteImage() softens the edges of the image in vignette style.
5533 %
5534 % The format of the VignetteImage method is:
5535 %
5536 % Image *VignetteImage(const Image *image,const double radius,
5537 % const double sigma,const ssize_t x,const ssize_t y,
5538 % ExceptionInfo *exception)
5539 %
5540 % A description of each parameter follows:
5541 %
5542 % o image: the image.
5543 %
5544 % o radius: the radius of the pixel neighborhood.
5545 %
5546 % o sigma: the standard deviation of the Gaussian, in pixels.
5547 %
5548 % o x, y: Define the x and y ellipse offset.
5549 %
5550 % o exception: return any errors or warnings in this structure.
5551 %
5552 */
5553 MagickExport Image *VignetteImage(const Image *image,const double radius,
5554  const double sigma,const ssize_t x,const ssize_t y,ExceptionInfo *exception)
5555 {
5556  char
5557  ellipse[MagickPathExtent];
5558 
5559  DrawInfo
5560  *draw_info;
5561 
5562  Image
5563  *canvas,
5564  *blur_image,
5565  *oval_image,
5566  *vignette_image;
5567 
5568  assert(image != (Image *) NULL);
5569  assert(image->signature == MagickCoreSignature);
5570  if (image->debug != MagickFalse)
5571  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5572  assert(exception != (ExceptionInfo *) NULL);
5573  assert(exception->signature == MagickCoreSignature);
5574  canvas=CloneImage(image,0,0,MagickTrue,exception);
5575  if (canvas == (Image *) NULL)
5576  return((Image *) NULL);
5577  if (SetImageStorageClass(canvas,DirectClass,exception) == MagickFalse)
5578  {
5579  canvas=DestroyImage(canvas);
5580  return((Image *) NULL);
5581  }
5582  canvas->alpha_trait=BlendPixelTrait;
5583  oval_image=CloneImage(canvas,canvas->columns,canvas->rows,MagickTrue,
5584  exception);
5585  if (oval_image == (Image *) NULL)
5586  {
5587  canvas=DestroyImage(canvas);
5588  return((Image *) NULL);
5589  }
5590  (void) QueryColorCompliance("#000000",AllCompliance,
5591  &oval_image->background_color,exception);
5592  (void) SetImageBackgroundColor(oval_image,exception);
5593  draw_info=CloneDrawInfo((const ImageInfo *) NULL,(const DrawInfo *) NULL);
5594  (void) QueryColorCompliance("#ffffff",AllCompliance,&draw_info->fill,
5595  exception);
5596  (void) QueryColorCompliance("#ffffff",AllCompliance,&draw_info->stroke,
5597  exception);
5598  (void) FormatLocaleString(ellipse,MagickPathExtent,"ellipse %g,%g,%g,%g,"
5599  "0.0,360.0",image->columns/2.0,image->rows/2.0,image->columns/2.0-x,
5600  image->rows/2.0-y);
5601  draw_info->primitive=AcquireString(ellipse);
5602  (void) DrawImage(oval_image,draw_info,exception);
5603  draw_info=DestroyDrawInfo(draw_info);
5604  blur_image=BlurImage(oval_image,radius,sigma,exception);
5605  oval_image=DestroyImage(oval_image);
5606  if (blur_image == (Image *) NULL)
5607  {
5608  canvas=DestroyImage(canvas);
5609  return((Image *) NULL);
5610  }
5611  blur_image->alpha_trait=UndefinedPixelTrait;
5612  (void) CompositeImage(canvas,blur_image,IntensityCompositeOp,MagickTrue,
5613  0,0,exception);
5614  blur_image=DestroyImage(blur_image);
5615  vignette_image=MergeImageLayers(canvas,FlattenLayer,exception);
5616  canvas=DestroyImage(canvas);
5617  if (vignette_image != (Image *) NULL)
5618  (void) TransformImageColorspace(vignette_image,image->colorspace,exception);
5619  return(vignette_image);
5620 }
5621 
5622 /*
5623 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5624 % %
5625 % %
5626 % %
5627 % W a v e I m a g e %
5628 % %
5629 % %
5630 % %
5631 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5632 %
5633 % WaveImage() creates a "ripple" effect in the image by shifting the pixels
5634 % vertically along a sine wave whose amplitude and wavelength is specified
5635 % by the given parameters.
5636 %
5637 % The format of the WaveImage method is:
5638 %
5639 % Image *WaveImage(const Image *image,const double amplitude,
5640 % const double wave_length,const PixelInterpolateMethod method,
5641 % ExceptionInfo *exception)
5642 %
5643 % A description of each parameter follows:
5644 %
5645 % o image: the image.
5646 %
5647 % o amplitude, wave_length: Define the amplitude and wave length of the
5648 % sine wave.
5649 %
5650 % o interpolate: the pixel interpolation method.
5651 %
5652 % o exception: return any errors or warnings in this structure.
5653 %
5654 */
5655 MagickExport Image *WaveImage(const Image *image,const double amplitude,
5656  const double wave_length,const PixelInterpolateMethod method,
5657  ExceptionInfo *exception)
5658 {
5659 #define WaveImageTag "Wave/Image"
5660 
5661  CacheView
5662  *canvas_image_view,
5663  *wave_view;
5664 
5665  float
5666  *sine_map;
5667 
5668  Image
5669  *canvas_image,
5670  *wave_image;
5671 
5673  status;
5674 
5676  progress;
5677 
5678  register ssize_t
5679  i;
5680 
5681  ssize_t
5682  y;
5683 
5684  /*
5685  Initialize wave image attributes.
5686  */
5687  assert(image != (Image *) NULL);
5688  assert(image->signature == MagickCoreSignature);
5689  if (image->debug != MagickFalse)
5690  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
5691  assert(exception != (ExceptionInfo *) NULL);
5692  assert(exception->signature == MagickCoreSignature);
5693  canvas_image=CloneImage(image,0,0,MagickTrue,exception);
5694  if (canvas_image == (Image *) NULL)
5695  return((Image *) NULL);
5696  if ((canvas_image->alpha_trait == UndefinedPixelTrait) &&
5697  (canvas_image->background_color.alpha != OpaqueAlpha))
5698  (void) SetImageAlpha(canvas_image,OpaqueAlpha,exception);
5699  wave_image=CloneImage(canvas_image,canvas_image->columns,(size_t)
5700  (canvas_image->rows+2.0*fabs(amplitude)),MagickTrue,exception);
5701  if (wave_image == (Image *) NULL)
5702  {
5703  canvas_image=DestroyImage(canvas_image);
5704  return((Image *) NULL);
5705  }
5706  if (SetImageStorageClass(wave_image,DirectClass,exception) == MagickFalse)
5707  {
5708  canvas_image=DestroyImage(canvas_image);
5709  wave_image=DestroyImage(wave_image);
5710  return((Image *) NULL);
5711  }
5712  /*
5713  Allocate sine map.
5714  */
5715  sine_map=(float *) AcquireQuantumMemory((size_t) wave_image->columns,
5716  sizeof(*sine_map));
5717  if (sine_map == (float *) NULL)
5718  {
5719  canvas_image=DestroyImage(canvas_image);
5720  wave_image=DestroyImage(wave_image);
5721  ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
5722  }
5723  for (i=0; i < (ssize_t) wave_image->columns; i++)
5724  sine_map[i]=(float) fabs(amplitude)+amplitude*sin((double)
5725  ((2.0*MagickPI*i)/wave_length));
5726  /*
5727  Wave image.
5728  */
5729  status=MagickTrue;
5730  progress=0;
5731  canvas_image_view=AcquireVirtualCacheView(canvas_image,exception);
5732  wave_view=AcquireAuthenticCacheView(wave_image,exception);
5733  (void) SetCacheViewVirtualPixelMethod(canvas_image_view,
5735 #if defined(MAGICKCORE_OPENMP_SUPPORT)
5736  #pragma omp parallel for schedule(static) shared(progress,status) \
5737  magick_number_threads(canvas_image,wave_image,wave_image->rows,1)
5738 #endif
5739  for (y=0; y < (ssize_t) wave_image->rows; y++)
5740  {
5741  register const Quantum
5742  *magick_restrict p;
5743 
5744  register Quantum
5745  *magick_restrict q;
5746 
5747  register ssize_t
5748  x;
5749 
5750  if (status == MagickFalse)
5751  continue;
5752  p=GetCacheViewVirtualPixels(canvas_image_view,0,y,canvas_image->columns,1,
5753  exception);
5754  q=QueueCacheViewAuthenticPixels(wave_view,0,y,wave_image->columns,1,
5755  exception);
5756  if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
5757  {
5758  status=MagickFalse;
5759  continue;
5760  }
5761  for (x=0; x < (ssize_t) wave_image->columns; x++)
5762  {
5763  status=InterpolatePixelChannels(canvas_image,canvas_image_view,
5764  wave_image,method,(double) x,(double) (y-sine_map[x]),q,exception);
5765  if (status == MagickFalse)
5766  break;
5767  p+=GetPixelChannels(canvas_image);
5768  q+=GetPixelChannels(wave_image);
5769  }
5770  if (SyncCacheViewAuthenticPixels(wave_view,exception) == MagickFalse)
5771  status=MagickFalse;
5772  if (image->progress_monitor != (