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