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