1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3%                                                                             %
4%                                                                             %
5%                                                                             %
6%              EEEEE  N   N  H   H   AAA   N   N   CCCC  EEEEE                %
7%              E      NN  N  H   H  A   A  NN  N  C      E                    %
8%              EEE    N N N  HHHHH  AAAAA  N N N  C      EEE                  %
9%              E      N  NN  H   H  A   A  N  NN  C      E                    %
10%              EEEEE  N   N  H   H  A   A  N   N   CCCC  EEEEE                %
11%                                                                             %
12%                                                                             %
13%                    MagickCore Image Enhancement Methods                     %
14%                                                                             %
15%                              Software Design                                %
16%                                   Cristy                                    %
17%                                 July 1992                                   %
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  Include declarations.
42*/
43#include "MagickCore/studio.h"
44#include "MagickCore/accelerate-private.h"
45#include "MagickCore/artifact.h"
46#include "MagickCore/attribute.h"
47#include "MagickCore/cache.h"
48#include "MagickCore/cache-view.h"
49#include "MagickCore/channel.h"
50#include "MagickCore/color.h"
51#include "MagickCore/color-private.h"
52#include "MagickCore/colorspace.h"
53#include "MagickCore/colorspace-private.h"
54#include "MagickCore/composite-private.h"
55#include "MagickCore/enhance.h"
56#include "MagickCore/exception.h"
57#include "MagickCore/exception-private.h"
58#include "MagickCore/fx.h"
59#include "MagickCore/gem.h"
60#include "MagickCore/gem-private.h"
61#include "MagickCore/geometry.h"
62#include "MagickCore/histogram.h"
63#include "MagickCore/image.h"
64#include "MagickCore/image-private.h"
65#include "MagickCore/memory_.h"
66#include "MagickCore/monitor.h"
67#include "MagickCore/monitor-private.h"
68#include "MagickCore/option.h"
69#include "MagickCore/pixel.h"
70#include "MagickCore/pixel-accessor.h"
71#include "MagickCore/quantum.h"
72#include "MagickCore/quantum-private.h"
73#include "MagickCore/resample.h"
74#include "MagickCore/resample-private.h"
75#include "MagickCore/resource_.h"
76#include "MagickCore/statistic.h"
77#include "MagickCore/string_.h"
78#include "MagickCore/string-private.h"
79#include "MagickCore/thread-private.h"
80#include "MagickCore/threshold.h"
81#include "MagickCore/token.h"
82#include "MagickCore/xml-tree.h"
83#include "MagickCore/xml-tree-private.h"
84
85/*
86%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
87%                                                                             %
88%                                                                             %
89%                                                                             %
90%     A u t o G a m m a I m a g e                                             %
91%                                                                             %
92%                                                                             %
93%                                                                             %
94%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
95%
96%  AutoGammaImage() extract the 'mean' from the image and adjust the image
97%  to try make set its gamma appropriatally.
98%
99%  The format of the AutoGammaImage method is:
100%
101%      MagickBooleanType AutoGammaImage(Image *image,ExceptionInfo *exception)
102%
103%  A description of each parameter follows:
104%
105%    o image: The image to auto-level
106%
107%    o exception: return any errors or warnings in this structure.
108%
109*/
110MagickExport MagickBooleanType AutoGammaImage(Image *image,
111  ExceptionInfo *exception)
112{
113  double
114    gamma,
115    log_mean,
116    mean,
117    sans;
118
119  MagickStatusType
120    status;
121
122  register ssize_t
123    i;
124
125  log_mean=log(0.5);
126  if (image->channel_mask == DefaultChannels)
127    {
128      /*
129        Apply gamma correction equally across all given channels.
130      */
131      (void) GetImageMean(image,&mean,&sans,exception);
132      gamma=log(mean*QuantumScale)/log_mean;
133      return(LevelImage(image,0.0,(double) QuantumRange,gamma,exception));
134    }
135  /*
136    Auto-gamma each channel separately.
137  */
138  status=MagickTrue;
139  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
140  {
141    ChannelType
142      channel_mask;
143
144    PixelChannel channel=GetPixelChannelChannel(image,i);
145    PixelTrait traits=GetPixelChannelTraits(image,channel);
146    if ((traits & UpdatePixelTrait) == 0)
147      continue;
148    channel_mask=SetImageChannelMask(image,(ChannelType) (1 << i));
149    status=GetImageMean(image,&mean,&sans,exception);
150    gamma=log(mean*QuantumScale)/log_mean;
151    status&=LevelImage(image,0.0,(double) QuantumRange,gamma,exception);
152    (void) SetImageChannelMask(image,channel_mask);
153    if (status == MagickFalse)
154      break;
155  }
156  return(status != 0 ? MagickTrue : MagickFalse);
157}
158
159/*
160%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
161%                                                                             %
162%                                                                             %
163%                                                                             %
164%     A u t o L e v e l I m a g e                                             %
165%                                                                             %
166%                                                                             %
167%                                                                             %
168%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
169%
170%  AutoLevelImage() adjusts the levels of a particular image channel by
171%  scaling the minimum and maximum values to the full quantum range.
172%
173%  The format of the LevelImage method is:
174%
175%      MagickBooleanType AutoLevelImage(Image *image,ExceptionInfo *exception)
176%
177%  A description of each parameter follows:
178%
179%    o image: The image to auto-level
180%
181%    o exception: return any errors or warnings in this structure.
182%
183*/
184MagickExport MagickBooleanType AutoLevelImage(Image *image,
185  ExceptionInfo *exception)
186{
187  return(MinMaxStretchImage(image,0.0,0.0,1.0,exception));
188}
189
190/*
191%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
192%                                                                             %
193%                                                                             %
194%                                                                             %
195%     B r i g h t n e s s C o n t r a s t I m a g e                           %
196%                                                                             %
197%                                                                             %
198%                                                                             %
199%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
200%
201%  BrightnessContrastImage() changes the brightness and/or contrast of an
202%  image.  It converts the brightness and contrast parameters into slope and
203%  intercept and calls a polynomical function to apply to the image.
204%
205%  The format of the BrightnessContrastImage method is:
206%
207%      MagickBooleanType BrightnessContrastImage(Image *image,
208%        const double brightness,const double contrast,ExceptionInfo *exception)
209%
210%  A description of each parameter follows:
211%
212%    o image: the image.
213%
214%    o brightness: the brightness percent (-100 .. 100).
215%
216%    o contrast: the contrast percent (-100 .. 100).
217%
218%    o exception: return any errors or warnings in this structure.
219%
220*/
221MagickExport MagickBooleanType BrightnessContrastImage(Image *image,
222  const double brightness,const double contrast,ExceptionInfo *exception)
223{
224#define BrightnessContastImageTag  "BrightnessContast/Image"
225
226  double
227    alpha,
228    coefficients[2],
229    intercept,
230    slope;
231
232  MagickBooleanType
233    status;
234
235  /*
236    Compute slope and intercept.
237  */
238  assert(image != (Image *) NULL);
239  assert(image->signature == MagickCoreSignature);
240  if (image->debug != MagickFalse)
241    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
242  alpha=contrast;
243  slope=tan((double) (MagickPI*(alpha/100.0+1.0)/4.0));
244  if (slope < 0.0)
245    slope=0.0;
246  intercept=brightness/100.0+((100-brightness)/200.0)*(1.0-slope);
247  coefficients[0]=slope;
248  coefficients[1]=intercept;
249  status=FunctionImage(image,PolynomialFunction,2,coefficients,exception);
250  return(status);
251}
252
253/*
254%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
255%                                                                             %
256%                                                                             %
257%                                                                             %
258%     C l u t I m a g e                                                       %
259%                                                                             %
260%                                                                             %
261%                                                                             %
262%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
263%
264%  ClutImage() replaces each color value in the given image, by using it as an
265%  index to lookup a replacement color value in a Color Look UP Table in the
266%  form of an image.  The values are extracted along a diagonal of the CLUT
267%  image so either a horizontal or vertial gradient image can be used.
268%
269%  Typically this is used to either re-color a gray-scale image according to a
270%  color gradient in the CLUT image, or to perform a freeform histogram
271%  (level) adjustment according to the (typically gray-scale) gradient in the
272%  CLUT image.
273%
274%  When the 'channel' mask includes the matte/alpha transparency channel but
275%  one image has no such channel it is assumed that that image is a simple
276%  gray-scale image that will effect the alpha channel values, either for
277%  gray-scale coloring (with transparent or semi-transparent colors), or
278%  a histogram adjustment of existing alpha channel values.   If both images
279%  have matte channels, direct and normal indexing is applied, which is rarely
280%  used.
281%
282%  The format of the ClutImage method is:
283%
284%      MagickBooleanType ClutImage(Image *image,Image *clut_image,
285%        const PixelInterpolateMethod method,ExceptionInfo *exception)
286%
287%  A description of each parameter follows:
288%
289%    o image: the image, which is replaced by indexed CLUT values
290%
291%    o clut_image: the color lookup table image for replacement color values.
292%
293%    o method: the pixel interpolation method.
294%
295%    o exception: return any errors or warnings in this structure.
296%
297*/
298MagickExport MagickBooleanType ClutImage(Image *image,const Image *clut_image,
299  const PixelInterpolateMethod method,ExceptionInfo *exception)
300{
301#define ClutImageTag  "Clut/Image"
302
303  CacheView
304    *clut_view,
305    *image_view;
306
307  MagickBooleanType
308    status;
309
310  MagickOffsetType
311    progress;
312
313  PixelInfo
314    *clut_map;
315
316  register ssize_t
317    i;
318
319  ssize_t adjust,
320    y;
321
322  assert(image != (Image *) NULL);
323  assert(image->signature == MagickCoreSignature);
324  if (image->debug != MagickFalse)
325    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
326  assert(clut_image != (Image *) NULL);
327  assert(clut_image->signature == MagickCoreSignature);
328  if( SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
329    return(MagickFalse);
330  if( (IsGrayColorspace(image->colorspace) != MagickFalse) &&
331      (IsGrayColorspace(clut_image->colorspace) == MagickFalse))
332    (void) SetImageColorspace(image,sRGBColorspace,exception);
333  clut_map=(PixelInfo *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*clut_map));
334  if (clut_map == (PixelInfo *) NULL)
335    ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
336      image->filename);
337  /*
338    Clut image.
339  */
340  status=MagickTrue;
341  progress=0;
342  adjust=(ssize_t) (clut_image->interpolate == IntegerInterpolatePixel ? 0 : 1);
343  clut_view=AcquireVirtualCacheView(clut_image,exception);
344  for (i=0; i <= (ssize_t) MaxMap; i++)
345  {
346    GetPixelInfo(clut_image,clut_map+i);
347    (void) InterpolatePixelInfo(clut_image,clut_view,method,
348      (double) i*(clut_image->columns-adjust)/MaxMap,(double) i*
349      (clut_image->rows-adjust)/MaxMap,clut_map+i,exception);
350  }
351  clut_view=DestroyCacheView(clut_view);
352  image_view=AcquireAuthenticCacheView(image,exception);
353#if defined(MAGICKCORE_OPENMP_SUPPORT)
354  #pragma omp parallel for schedule(static,4) shared(progress,status) \
355    magick_threads(image,image,image->rows,1)
356#endif
357  for (y=0; y < (ssize_t) image->rows; y++)
358  {
359    PixelInfo
360      pixel;
361
362    register Quantum
363      *magick_restrict q;
364
365    register ssize_t
366      x;
367
368    if (status == MagickFalse)
369      continue;
370    q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
371    if (q == (Quantum *) NULL)
372      {
373        status=MagickFalse;
374        continue;
375      }
376    GetPixelInfo(image,&pixel);
377    for (x=0; x < (ssize_t) image->columns; x++)
378    {
379      PixelTrait
380        traits;
381
382      if (GetPixelReadMask(image,q) == 0)
383        {
384          q+=GetPixelChannels(image);
385          continue;
386        }
387      GetPixelInfoPixel(image,q,&pixel);
388      traits=GetPixelChannelTraits(image,RedPixelChannel);
389      if ((traits & UpdatePixelTrait) != 0)
390        pixel.red=clut_map[ScaleQuantumToMap(ClampToQuantum(
391          pixel.red))].red;
392      traits=GetPixelChannelTraits(image,GreenPixelChannel);
393      if ((traits & UpdatePixelTrait) != 0)
394        pixel.green=clut_map[ScaleQuantumToMap(ClampToQuantum(
395          pixel.green))].green;
396      traits=GetPixelChannelTraits(image,BluePixelChannel);
397      if ((traits & UpdatePixelTrait) != 0)
398        pixel.blue=clut_map[ScaleQuantumToMap(ClampToQuantum(
399          pixel.blue))].blue;
400      traits=GetPixelChannelTraits(image,BlackPixelChannel);
401      if ((traits & UpdatePixelTrait) != 0)
402        pixel.black=clut_map[ScaleQuantumToMap(ClampToQuantum(
403          pixel.black))].black;
404      traits=GetPixelChannelTraits(image,AlphaPixelChannel);
405      if ((traits & UpdatePixelTrait) != 0)
406        pixel.alpha=clut_map[ScaleQuantumToMap(ClampToQuantum(
407          pixel.alpha))].alpha;
408      SetPixelViaPixelInfo(image,&pixel,q);
409      q+=GetPixelChannels(image);
410    }
411    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
412      status=MagickFalse;
413    if (image->progress_monitor != (MagickProgressMonitor) NULL)
414      {
415        MagickBooleanType
416          proceed;
417
418#if defined(MAGICKCORE_OPENMP_SUPPORT)
419        #pragma omp critical (MagickCore_ClutImage)
420#endif
421        proceed=SetImageProgress(image,ClutImageTag,progress++,image->rows);
422        if (proceed == MagickFalse)
423          status=MagickFalse;
424      }
425  }
426  image_view=DestroyCacheView(image_view);
427  clut_map=(PixelInfo *) RelinquishMagickMemory(clut_map);
428  if ((clut_image->alpha_trait != UndefinedPixelTrait) &&
429      ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0))
430    (void) SetImageAlphaChannel(image,ActivateAlphaChannel,exception);
431  return(status);
432}
433
434/*
435%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
436%                                                                             %
437%                                                                             %
438%                                                                             %
439%     C o l o r D e c i s i o n L i s t I m a g e                             %
440%                                                                             %
441%                                                                             %
442%                                                                             %
443%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
444%
445%  ColorDecisionListImage() accepts a lightweight Color Correction Collection
446%  (CCC) file which solely contains one or more color corrections and applies
447%  the correction to the image.  Here is a sample CCC file:
448%
449%    <ColorCorrectionCollection xmlns="urn:ASC:CDL:v1.2">
450%          <ColorCorrection id="cc03345">
451%                <SOPNode>
452%                     <Slope> 0.9 1.2 0.5 </Slope>
453%                     <Offset> 0.4 -0.5 0.6 </Offset>
454%                     <Power> 1.0 0.8 1.5 </Power>
455%                </SOPNode>
456%                <SATNode>
457%                     <Saturation> 0.85 </Saturation>
458%                </SATNode>
459%          </ColorCorrection>
460%    </ColorCorrectionCollection>
461%
462%  which includes the slop, offset, and power for each of the RGB channels
463%  as well as the saturation.
464%
465%  The format of the ColorDecisionListImage method is:
466%
467%      MagickBooleanType ColorDecisionListImage(Image *image,
468%        const char *color_correction_collection,ExceptionInfo *exception)
469%
470%  A description of each parameter follows:
471%
472%    o image: the image.
473%
474%    o color_correction_collection: the color correction collection in XML.
475%
476%    o exception: return any errors or warnings in this structure.
477%
478*/
479MagickExport MagickBooleanType ColorDecisionListImage(Image *image,
480  const char *color_correction_collection,ExceptionInfo *exception)
481{
482#define ColorDecisionListCorrectImageTag  "ColorDecisionList/Image"
483
484  typedef struct _Correction
485  {
486    double
487      slope,
488      offset,
489      power;
490  } Correction;
491
492  typedef struct _ColorCorrection
493  {
494    Correction
495      red,
496      green,
497      blue;
498
499    double
500      saturation;
501  } ColorCorrection;
502
503  CacheView
504    *image_view;
505
506  char
507    token[MagickPathExtent];
508
509  ColorCorrection
510    color_correction;
511
512  const char
513    *content,
514    *p;
515
516  MagickBooleanType
517    status;
518
519  MagickOffsetType
520    progress;
521
522  PixelInfo
523    *cdl_map;
524
525  register ssize_t
526    i;
527
528  ssize_t
529    y;
530
531  XMLTreeInfo
532    *cc,
533    *ccc,
534    *sat,
535    *sop;
536
537  /*
538    Allocate and initialize cdl maps.
539  */
540  assert(image != (Image *) NULL);
541  assert(image->signature == MagickCoreSignature);
542  if (image->debug != MagickFalse)
543    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
544  if (color_correction_collection == (const char *) NULL)
545    return(MagickFalse);
546  ccc=NewXMLTree((const char *) color_correction_collection,exception);
547  if (ccc == (XMLTreeInfo *) NULL)
548    return(MagickFalse);
549  cc=GetXMLTreeChild(ccc,"ColorCorrection");
550  if (cc == (XMLTreeInfo *) NULL)
551    {
552      ccc=DestroyXMLTree(ccc);
553      return(MagickFalse);
554    }
555  color_correction.red.slope=1.0;
556  color_correction.red.offset=0.0;
557  color_correction.red.power=1.0;
558  color_correction.green.slope=1.0;
559  color_correction.green.offset=0.0;
560  color_correction.green.power=1.0;
561  color_correction.blue.slope=1.0;
562  color_correction.blue.offset=0.0;
563  color_correction.blue.power=1.0;
564  color_correction.saturation=0.0;
565  sop=GetXMLTreeChild(cc,"SOPNode");
566  if (sop != (XMLTreeInfo *) NULL)
567    {
568      XMLTreeInfo
569        *offset,
570        *power,
571        *slope;
572
573      slope=GetXMLTreeChild(sop,"Slope");
574      if (slope != (XMLTreeInfo *) NULL)
575        {
576          content=GetXMLTreeContent(slope);
577          p=(const char *) content;
578          for (i=0; (*p != '\0') && (i < 3); i++)
579          {
580            GetNextToken(p,&p,MagickPathExtent,token);
581            if (*token == ',')
582              GetNextToken(p,&p,MagickPathExtent,token);
583            switch (i)
584            {
585              case 0:
586              {
587                color_correction.red.slope=StringToDouble(token,(char **) NULL);
588                break;
589              }
590              case 1:
591              {
592                color_correction.green.slope=StringToDouble(token,
593                  (char **) NULL);
594                break;
595              }
596              case 2:
597              {
598                color_correction.blue.slope=StringToDouble(token,
599                  (char **) NULL);
600                break;
601              }
602            }
603          }
604        }
605      offset=GetXMLTreeChild(sop,"Offset");
606      if (offset != (XMLTreeInfo *) NULL)
607        {
608          content=GetXMLTreeContent(offset);
609          p=(const char *) content;
610          for (i=0; (*p != '\0') && (i < 3); i++)
611          {
612            GetNextToken(p,&p,MagickPathExtent,token);
613            if (*token == ',')
614              GetNextToken(p,&p,MagickPathExtent,token);
615            switch (i)
616            {
617              case 0:
618              {
619                color_correction.red.offset=StringToDouble(token,
620                  (char **) NULL);
621                break;
622              }
623              case 1:
624              {
625                color_correction.green.offset=StringToDouble(token,
626                  (char **) NULL);
627                break;
628              }
629              case 2:
630              {
631                color_correction.blue.offset=StringToDouble(token,
632                  (char **) NULL);
633                break;
634              }
635            }
636          }
637        }
638      power=GetXMLTreeChild(sop,"Power");
639      if (power != (XMLTreeInfo *) NULL)
640        {
641          content=GetXMLTreeContent(power);
642          p=(const char *) content;
643          for (i=0; (*p != '\0') && (i < 3); i++)
644          {
645            GetNextToken(p,&p,MagickPathExtent,token);
646            if (*token == ',')
647              GetNextToken(p,&p,MagickPathExtent,token);
648            switch (i)
649            {
650              case 0:
651              {
652                color_correction.red.power=StringToDouble(token,(char **) NULL);
653                break;
654              }
655              case 1:
656              {
657                color_correction.green.power=StringToDouble(token,
658                  (char **) NULL);
659                break;
660              }
661              case 2:
662              {
663                color_correction.blue.power=StringToDouble(token,
664                  (char **) NULL);
665                break;
666              }
667            }
668          }
669        }
670    }
671  sat=GetXMLTreeChild(cc,"SATNode");
672  if (sat != (XMLTreeInfo *) NULL)
673    {
674      XMLTreeInfo
675        *saturation;
676
677      saturation=GetXMLTreeChild(sat,"Saturation");
678      if (saturation != (XMLTreeInfo *) NULL)
679        {
680          content=GetXMLTreeContent(saturation);
681          p=(const char *) content;
682          GetNextToken(p,&p,MagickPathExtent,token);
683          color_correction.saturation=StringToDouble(token,(char **) NULL);
684        }
685    }
686  ccc=DestroyXMLTree(ccc);
687  if (image->debug != MagickFalse)
688    {
689      (void) LogMagickEvent(TransformEvent,GetMagickModule(),
690        "  Color Correction Collection:");
691      (void) LogMagickEvent(TransformEvent,GetMagickModule(),
692        "  color_correction.red.slope: %g",color_correction.red.slope);
693      (void) LogMagickEvent(TransformEvent,GetMagickModule(),
694        "  color_correction.red.offset: %g",color_correction.red.offset);
695      (void) LogMagickEvent(TransformEvent,GetMagickModule(),
696        "  color_correction.red.power: %g",color_correction.red.power);
697      (void) LogMagickEvent(TransformEvent,GetMagickModule(),
698        "  color_correction.green.slope: %g",color_correction.green.slope);
699      (void) LogMagickEvent(TransformEvent,GetMagickModule(),
700        "  color_correction.green.offset: %g",color_correction.green.offset);
701      (void) LogMagickEvent(TransformEvent,GetMagickModule(),
702        "  color_correction.green.power: %g",color_correction.green.power);
703      (void) LogMagickEvent(TransformEvent,GetMagickModule(),
704        "  color_correction.blue.slope: %g",color_correction.blue.slope);
705      (void) LogMagickEvent(TransformEvent,GetMagickModule(),
706        "  color_correction.blue.offset: %g",color_correction.blue.offset);
707      (void) LogMagickEvent(TransformEvent,GetMagickModule(),
708        "  color_correction.blue.power: %g",color_correction.blue.power);
709      (void) LogMagickEvent(TransformEvent,GetMagickModule(),
710        "  color_correction.saturation: %g",color_correction.saturation);
711    }
712  cdl_map=(PixelInfo *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*cdl_map));
713  if (cdl_map == (PixelInfo *) NULL)
714    ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
715      image->filename);
716  for (i=0; i <= (ssize_t) MaxMap; i++)
717  {
718    cdl_map[i].red=(double) ScaleMapToQuantum((double)
719      (MaxMap*(pow(color_correction.red.slope*i/MaxMap+
720      color_correction.red.offset,color_correction.red.power))));
721    cdl_map[i].green=(double) ScaleMapToQuantum((double)
722      (MaxMap*(pow(color_correction.green.slope*i/MaxMap+
723      color_correction.green.offset,color_correction.green.power))));
724    cdl_map[i].blue=(double) ScaleMapToQuantum((double)
725      (MaxMap*(pow(color_correction.blue.slope*i/MaxMap+
726      color_correction.blue.offset,color_correction.blue.power))));
727  }
728  if (image->storage_class == PseudoClass)
729    for (i=0; i < (ssize_t) image->colors; i++)
730    {
731      /*
732        Apply transfer function to colormap.
733      */
734      double
735        luma;
736
737      luma=0.21267f*image->colormap[i].red+0.71526*image->colormap[i].green+
738        0.07217f*image->colormap[i].blue;
739      image->colormap[i].red=luma+color_correction.saturation*cdl_map[
740        ScaleQuantumToMap(ClampToQuantum(image->colormap[i].red))].red-luma;
741      image->colormap[i].green=luma+color_correction.saturation*cdl_map[
742        ScaleQuantumToMap(ClampToQuantum(image->colormap[i].green))].green-luma;
743      image->colormap[i].blue=luma+color_correction.saturation*cdl_map[
744        ScaleQuantumToMap(ClampToQuantum(image->colormap[i].blue))].blue-luma;
745    }
746  /*
747    Apply transfer function to image.
748  */
749  status=MagickTrue;
750  progress=0;
751  image_view=AcquireAuthenticCacheView(image,exception);
752#if defined(MAGICKCORE_OPENMP_SUPPORT)
753  #pragma omp parallel for schedule(static,4) shared(progress,status) \
754    magick_threads(image,image,image->rows,1)
755#endif
756  for (y=0; y < (ssize_t) image->rows; y++)
757  {
758    double
759      luma;
760
761    register Quantum
762      *magick_restrict q;
763
764    register ssize_t
765      x;
766
767    if (status == MagickFalse)
768      continue;
769    q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
770    if (q == (Quantum *) NULL)
771      {
772        status=MagickFalse;
773        continue;
774      }
775    for (x=0; x < (ssize_t) image->columns; x++)
776    {
777      luma=0.21267f*GetPixelRed(image,q)+0.71526*GetPixelGreen(image,q)+
778        0.07217f*GetPixelBlue(image,q);
779      SetPixelRed(image,ClampToQuantum(luma+color_correction.saturation*
780        (cdl_map[ScaleQuantumToMap(GetPixelRed(image,q))].red-luma)),q);
781      SetPixelGreen(image,ClampToQuantum(luma+color_correction.saturation*
782        (cdl_map[ScaleQuantumToMap(GetPixelGreen(image,q))].green-luma)),q);
783      SetPixelBlue(image,ClampToQuantum(luma+color_correction.saturation*
784        (cdl_map[ScaleQuantumToMap(GetPixelBlue(image,q))].blue-luma)),q);
785      q+=GetPixelChannels(image);
786    }
787    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
788      status=MagickFalse;
789    if (image->progress_monitor != (MagickProgressMonitor) NULL)
790      {
791        MagickBooleanType
792          proceed;
793
794#if defined(MAGICKCORE_OPENMP_SUPPORT)
795        #pragma omp critical (MagickCore_ColorDecisionListImageChannel)
796#endif
797        proceed=SetImageProgress(image,ColorDecisionListCorrectImageTag,
798          progress++,image->rows);
799        if (proceed == MagickFalse)
800          status=MagickFalse;
801      }
802  }
803  image_view=DestroyCacheView(image_view);
804  cdl_map=(PixelInfo *) RelinquishMagickMemory(cdl_map);
805  return(status);
806}
807
808/*
809%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
810%                                                                             %
811%                                                                             %
812%                                                                             %
813%     C o n t r a s t I m a g e                                               %
814%                                                                             %
815%                                                                             %
816%                                                                             %
817%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
818%
819%  ContrastImage() enhances the intensity differences between the lighter and
820%  darker elements of the image.  Set sharpen to a MagickTrue to increase the
821%  image contrast otherwise the contrast is reduced.
822%
823%  The format of the ContrastImage method is:
824%
825%      MagickBooleanType ContrastImage(Image *image,
826%        const MagickBooleanType sharpen,ExceptionInfo *exception)
827%
828%  A description of each parameter follows:
829%
830%    o image: the image.
831%
832%    o sharpen: Increase or decrease image contrast.
833%
834%    o exception: return any errors or warnings in this structure.
835%
836*/
837
838static void Contrast(const int sign,double *red,double *green,double *blue)
839{
840  double
841    brightness,
842    hue,
843    saturation;
844
845  /*
846    Enhance contrast: dark color become darker, light color become lighter.
847  */
848  assert(red != (double *) NULL);
849  assert(green != (double *) NULL);
850  assert(blue != (double *) NULL);
851  hue=0.0;
852  saturation=0.0;
853  brightness=0.0;
854  ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness);
855  brightness+=0.5*sign*(0.5*(sin((double) (MagickPI*(brightness-0.5)))+1.0)-
856    brightness);
857  if (brightness > 1.0)
858    brightness=1.0;
859  else
860    if (brightness < 0.0)
861      brightness=0.0;
862  ConvertHSBToRGB(hue,saturation,brightness,red,green,blue);
863}
864
865MagickExport MagickBooleanType ContrastImage(Image *image,
866  const MagickBooleanType sharpen,ExceptionInfo *exception)
867{
868#define ContrastImageTag  "Contrast/Image"
869
870  CacheView
871    *image_view;
872
873  int
874    sign;
875
876  MagickBooleanType
877    status;
878
879  MagickOffsetType
880    progress;
881
882  register ssize_t
883    i;
884
885  ssize_t
886    y;
887
888  assert(image != (Image *) NULL);
889  assert(image->signature == MagickCoreSignature);
890#if defined(MAGICKCORE_OPENCL_SUPPORT)
891  if (AccelerateContrastImage(image,sharpen,exception) != MagickFalse)
892    return(MagickTrue);
893#endif
894  if (image->debug != MagickFalse)
895    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
896  sign=sharpen != MagickFalse ? 1 : -1;
897  if (image->storage_class == PseudoClass)
898    {
899      /*
900        Contrast enhance colormap.
901      */
902      for (i=0; i < (ssize_t) image->colors; i++)
903      {
904        double
905          blue,
906          green,
907          red;
908
909        red=(double) image->colormap[i].red;
910        green=(double) image->colormap[i].green;
911        blue=(double) image->colormap[i].blue;
912        Contrast(sign,&red,&green,&blue);
913        image->colormap[i].red=(MagickRealType) red;
914        image->colormap[i].green=(MagickRealType) green;
915        image->colormap[i].blue=(MagickRealType) blue;
916      }
917    }
918  /*
919    Contrast enhance image.
920  */
921  status=MagickTrue;
922  progress=0;
923  image_view=AcquireAuthenticCacheView(image,exception);
924#if defined(MAGICKCORE_OPENMP_SUPPORT)
925  #pragma omp parallel for schedule(static,4) shared(progress,status) \
926    magick_threads(image,image,image->rows,1)
927#endif
928  for (y=0; y < (ssize_t) image->rows; y++)
929  {
930    double
931      blue,
932      green,
933      red;
934
935    register Quantum
936      *magick_restrict q;
937
938    register ssize_t
939      x;
940
941    if (status == MagickFalse)
942      continue;
943    q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
944    if (q == (Quantum *) NULL)
945      {
946        status=MagickFalse;
947        continue;
948      }
949    for (x=0; x < (ssize_t) image->columns; x++)
950    {
951      red=(double) GetPixelRed(image,q);
952      green=(double) GetPixelGreen(image,q);
953      blue=(double) GetPixelBlue(image,q);
954      Contrast(sign,&red,&green,&blue);
955      SetPixelRed(image,ClampToQuantum(red),q);
956      SetPixelGreen(image,ClampToQuantum(green),q);
957      SetPixelBlue(image,ClampToQuantum(blue),q);
958      q+=GetPixelChannels(image);
959    }
960    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
961      status=MagickFalse;
962    if (image->progress_monitor != (MagickProgressMonitor) NULL)
963      {
964        MagickBooleanType
965          proceed;
966
967#if defined(MAGICKCORE_OPENMP_SUPPORT)
968        #pragma omp critical (MagickCore_ContrastImage)
969#endif
970        proceed=SetImageProgress(image,ContrastImageTag,progress++,image->rows);
971        if (proceed == MagickFalse)
972          status=MagickFalse;
973      }
974  }
975  image_view=DestroyCacheView(image_view);
976  return(status);
977}
978
979/*
980%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
981%                                                                             %
982%                                                                             %
983%                                                                             %
984%     C o n t r a s t S t r e t c h I m a g e                                 %
985%                                                                             %
986%                                                                             %
987%                                                                             %
988%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
989%
990%  ContrastStretchImage() is a simple image enhancement technique that attempts
991%  to improve the contrast in an image by 'stretching' the range of intensity
992%  values it contains to span a desired range of values. It differs from the
993%  more sophisticated histogram equalization in that it can only apply a
994%  linear scaling function to the image pixel values.  As a result the
995%  'enhancement' is less harsh.
996%
997%  The format of the ContrastStretchImage method is:
998%
999%      MagickBooleanType ContrastStretchImage(Image *image,
1000%        const char *levels,ExceptionInfo *exception)
1001%
1002%  A description of each parameter follows:
1003%
1004%    o image: the image.
1005%
1006%    o black_point: the black point.
1007%
1008%    o white_point: the white point.
1009%
1010%    o levels: Specify the levels where the black and white points have the
1011%      range of 0 to number-of-pixels (e.g. 1%, 10x90%, etc.).
1012%
1013%    o exception: return any errors or warnings in this structure.
1014%
1015*/
1016MagickExport MagickBooleanType ContrastStretchImage(Image *image,
1017  const double black_point,const double white_point,ExceptionInfo *exception)
1018{
1019#define MaxRange(color)  ((double) ScaleQuantumToMap((Quantum) (color)))
1020#define ContrastStretchImageTag  "ContrastStretch/Image"
1021
1022  CacheView
1023    *image_view;
1024
1025  double
1026    *black,
1027    *histogram,
1028    *stretch_map,
1029    *white;
1030
1031  MagickBooleanType
1032    status;
1033
1034  MagickOffsetType
1035    progress;
1036
1037  register ssize_t
1038    i;
1039
1040  ssize_t
1041    y;
1042
1043  /*
1044    Allocate histogram and stretch map.
1045  */
1046  assert(image != (Image *) NULL);
1047  assert(image->signature == MagickCoreSignature);
1048  if (image->debug != MagickFalse)
1049    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1050  if (SetImageGray(image,exception) != MagickFalse)
1051    (void) SetImageColorspace(image,GRAYColorspace,exception);
1052  black=(double *) AcquireQuantumMemory(GetPixelChannels(image),sizeof(*black));
1053  white=(double *) AcquireQuantumMemory(GetPixelChannels(image),sizeof(*white));
1054  histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,GetPixelChannels(image)*
1055    sizeof(*histogram));
1056  stretch_map=(double *) AcquireQuantumMemory(MaxMap+1UL,
1057    GetPixelChannels(image)*sizeof(*stretch_map));
1058  if ((black == (double *) NULL) || (white == (double *) NULL) ||
1059      (histogram == (double *) NULL) || (stretch_map == (double *) NULL))
1060    {
1061      if (stretch_map != (double *) NULL)
1062        stretch_map=(double *) RelinquishMagickMemory(stretch_map);
1063      if (histogram != (double *) NULL)
1064        histogram=(double *) RelinquishMagickMemory(histogram);
1065      if (white != (double *) NULL)
1066        white=(double *) RelinquishMagickMemory(white);
1067      if (black != (double *) NULL)
1068        black=(double *) RelinquishMagickMemory(black);
1069      ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1070        image->filename);
1071    }
1072  /*
1073    Form histogram.
1074  */
1075  status=MagickTrue;
1076  (void) ResetMagickMemory(histogram,0,(MaxMap+1)*GetPixelChannels(image)*
1077    sizeof(*histogram));
1078  image_view=AcquireVirtualCacheView(image,exception);
1079  for (y=0; y < (ssize_t) image->rows; y++)
1080  {
1081    register const Quantum
1082      *magick_restrict p;
1083
1084    register ssize_t
1085      x;
1086
1087    if (status == MagickFalse)
1088      continue;
1089    p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1090    if (p == (const Quantum *) NULL)
1091      {
1092        status=MagickFalse;
1093        continue;
1094      }
1095    for (x=0; x < (ssize_t) image->columns; x++)
1096    {
1097      double
1098        pixel;
1099
1100      pixel=GetPixelIntensity(image,p);
1101      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1102      {
1103        if (image->channel_mask != DefaultChannels)
1104          pixel=(double) p[i];
1105        histogram[GetPixelChannels(image)*ScaleQuantumToMap(
1106          ClampToQuantum(pixel))+i]++;
1107      }
1108      p+=GetPixelChannels(image);
1109    }
1110  }
1111  image_view=DestroyCacheView(image_view);
1112  /*
1113    Find the histogram boundaries by locating the black/white levels.
1114  */
1115  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1116  {
1117    double
1118      intensity;
1119
1120    register ssize_t
1121      j;
1122
1123    black[i]=0.0;
1124    white[i]=MaxRange(QuantumRange);
1125    intensity=0.0;
1126    for (j=0; j <= (ssize_t) MaxMap; j++)
1127    {
1128      intensity+=histogram[GetPixelChannels(image)*j+i];
1129      if (intensity > black_point)
1130        break;
1131    }
1132    black[i]=(double) j;
1133    intensity=0.0;
1134    for (j=(ssize_t) MaxMap; j != 0; j--)
1135    {
1136      intensity+=histogram[GetPixelChannels(image)*j+i];
1137      if (intensity > ((double) image->columns*image->rows-white_point))
1138        break;
1139    }
1140    white[i]=(double) j;
1141  }
1142  histogram=(double *) RelinquishMagickMemory(histogram);
1143  /*
1144    Stretch the histogram to create the stretched image mapping.
1145  */
1146  (void) ResetMagickMemory(stretch_map,0,(MaxMap+1)*GetPixelChannels(image)*
1147    sizeof(*stretch_map));
1148  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1149  {
1150    register ssize_t
1151      j;
1152
1153    for (j=0; j <= (ssize_t) MaxMap; j++)
1154    {
1155      double
1156        gamma;
1157
1158      gamma=PerceptibleReciprocal(white[i]-black[i]);
1159      if (j < (ssize_t) black[i])
1160        stretch_map[GetPixelChannels(image)*j+i]=0.0;
1161      else
1162        if (j > (ssize_t) white[i])
1163          stretch_map[GetPixelChannels(image)*j+i]=(double) QuantumRange;
1164        else
1165          stretch_map[GetPixelChannels(image)*j+i]=(double) ScaleMapToQuantum(
1166            (double) (MaxMap*gamma*(j-black[i])));
1167    }
1168  }
1169  if (image->storage_class == PseudoClass)
1170    {
1171      register ssize_t
1172        j;
1173
1174      /*
1175        Stretch-contrast colormap.
1176      */
1177      for (j=0; j < (ssize_t) image->colors; j++)
1178      {
1179        if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1180          {
1181            i=GetPixelChannelOffset(image,RedPixelChannel);
1182            image->colormap[j].red=stretch_map[GetPixelChannels(image)*
1183              ScaleQuantumToMap(ClampToQuantum(image->colormap[j].red))+i];
1184          }
1185        if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1186          {
1187            i=GetPixelChannelOffset(image,GreenPixelChannel);
1188            image->colormap[j].green=stretch_map[GetPixelChannels(image)*
1189              ScaleQuantumToMap(ClampToQuantum(image->colormap[j].green))+i];
1190          }
1191        if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1192          {
1193            i=GetPixelChannelOffset(image,BluePixelChannel);
1194            image->colormap[j].blue=stretch_map[GetPixelChannels(image)*
1195              ScaleQuantumToMap(ClampToQuantum(image->colormap[j].blue))+i];
1196          }
1197        if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1198          {
1199            i=GetPixelChannelOffset(image,AlphaPixelChannel);
1200            image->colormap[j].alpha=stretch_map[GetPixelChannels(image)*
1201              ScaleQuantumToMap(ClampToQuantum(image->colormap[j].alpha))+i];
1202          }
1203      }
1204    }
1205  /*
1206    Stretch-contrast image.
1207  */
1208  status=MagickTrue;
1209  progress=0;
1210  image_view=AcquireAuthenticCacheView(image,exception);
1211#if defined(MAGICKCORE_OPENMP_SUPPORT)
1212  #pragma omp parallel for schedule(static,4) shared(progress,status) \
1213    magick_threads(image,image,image->rows,1)
1214#endif
1215  for (y=0; y < (ssize_t) image->rows; y++)
1216  {
1217    register Quantum
1218      *magick_restrict q;
1219
1220    register ssize_t
1221      x;
1222
1223    if (status == MagickFalse)
1224      continue;
1225    q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1226    if (q == (Quantum *) NULL)
1227      {
1228        status=MagickFalse;
1229        continue;
1230      }
1231    for (x=0; x < (ssize_t) image->columns; x++)
1232    {
1233      register ssize_t
1234        j;
1235
1236      if (GetPixelReadMask(image,q) == 0)
1237        {
1238          q+=GetPixelChannels(image);
1239          continue;
1240        }
1241      for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1242      {
1243        PixelChannel channel=GetPixelChannelChannel(image,j);
1244        PixelTrait traits=GetPixelChannelTraits(image,channel);
1245        if ((traits & UpdatePixelTrait) == 0)
1246          continue;
1247        q[j]=ClampToQuantum(stretch_map[GetPixelChannels(image)*
1248          ScaleQuantumToMap(q[j])+j]);
1249      }
1250      q+=GetPixelChannels(image);
1251    }
1252    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1253      status=MagickFalse;
1254    if (image->progress_monitor != (MagickProgressMonitor) NULL)
1255      {
1256        MagickBooleanType
1257          proceed;
1258
1259#if defined(MAGICKCORE_OPENMP_SUPPORT)
1260        #pragma omp critical (MagickCore_ContrastStretchImage)
1261#endif
1262        proceed=SetImageProgress(image,ContrastStretchImageTag,progress++,
1263          image->rows);
1264        if (proceed == MagickFalse)
1265          status=MagickFalse;
1266      }
1267  }
1268  image_view=DestroyCacheView(image_view);
1269  stretch_map=(double *) RelinquishMagickMemory(stretch_map);
1270  white=(double *) RelinquishMagickMemory(white);
1271  black=(double *) RelinquishMagickMemory(black);
1272  return(status);
1273}
1274
1275/*
1276%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1277%                                                                             %
1278%                                                                             %
1279%                                                                             %
1280%     E n h a n c e I m a g e                                                 %
1281%                                                                             %
1282%                                                                             %
1283%                                                                             %
1284%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1285%
1286%  EnhanceImage() applies a digital filter that improves the quality of a
1287%  noisy image.
1288%
1289%  The format of the EnhanceImage method is:
1290%
1291%      Image *EnhanceImage(const Image *image,ExceptionInfo *exception)
1292%
1293%  A description of each parameter follows:
1294%
1295%    o image: the image.
1296%
1297%    o exception: return any errors or warnings in this structure.
1298%
1299*/
1300MagickExport Image *EnhanceImage(const Image *image,ExceptionInfo *exception)
1301{
1302#define EnhanceImageTag  "Enhance/Image"
1303#define EnhancePixel(weight) \
1304  mean=QuantumScale*((double) GetPixelRed(image,r)+pixel.red)/2.0; \
1305  distance=QuantumScale*((double) GetPixelRed(image,r)-pixel.red); \
1306  distance_squared=(4.0+mean)*distance*distance; \
1307  mean=QuantumScale*((double) GetPixelGreen(image,r)+pixel.green)/2.0; \
1308  distance=QuantumScale*((double) GetPixelGreen(image,r)-pixel.green); \
1309  distance_squared+=(7.0-mean)*distance*distance; \
1310  mean=QuantumScale*((double) GetPixelBlue(image,r)+pixel.blue)/2.0; \
1311  distance=QuantumScale*((double) GetPixelBlue(image,r)-pixel.blue); \
1312  distance_squared+=(5.0-mean)*distance*distance; \
1313  mean=QuantumScale*((double) GetPixelBlack(image,r)+pixel.black)/2.0; \
1314  distance=QuantumScale*((double) GetPixelBlack(image,r)-pixel.black); \
1315  distance_squared+=(5.0-mean)*distance*distance; \
1316  mean=QuantumScale*((double) GetPixelAlpha(image,r)+pixel.alpha)/2.0; \
1317  distance=QuantumScale*((double) GetPixelAlpha(image,r)-pixel.alpha); \
1318  distance_squared+=(5.0-mean)*distance*distance; \
1319  if (distance_squared < 0.069) \
1320    { \
1321      aggregate.red+=(weight)*GetPixelRed(image,r); \
1322      aggregate.green+=(weight)*GetPixelGreen(image,r); \
1323      aggregate.blue+=(weight)*GetPixelBlue(image,r); \
1324      aggregate.black+=(weight)*GetPixelBlack(image,r); \
1325      aggregate.alpha+=(weight)*GetPixelAlpha(image,r); \
1326      total_weight+=(weight); \
1327    } \
1328  r+=GetPixelChannels(image);
1329
1330  CacheView
1331    *enhance_view,
1332    *image_view;
1333
1334  Image
1335    *enhance_image;
1336
1337  MagickBooleanType
1338    status;
1339
1340  MagickOffsetType
1341    progress;
1342
1343  ssize_t
1344    y;
1345
1346  /*
1347    Initialize enhanced image attributes.
1348  */
1349  assert(image != (const Image *) NULL);
1350  assert(image->signature == MagickCoreSignature);
1351  if (image->debug != MagickFalse)
1352    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1353  assert(exception != (ExceptionInfo *) NULL);
1354  assert(exception->signature == MagickCoreSignature);
1355  enhance_image=CloneImage(image,image->columns,image->rows,MagickTrue,
1356    exception);
1357  if (enhance_image == (Image *) NULL)
1358    return((Image *) NULL);
1359  if (SetImageStorageClass(enhance_image,DirectClass,exception) == MagickFalse)
1360    {
1361      enhance_image=DestroyImage(enhance_image);
1362      return((Image *) NULL);
1363    }
1364  /*
1365    Enhance image.
1366  */
1367  status=MagickTrue;
1368  progress=0;
1369  image_view=AcquireVirtualCacheView(image,exception);
1370  enhance_view=AcquireAuthenticCacheView(enhance_image,exception);
1371#if defined(MAGICKCORE_OPENMP_SUPPORT)
1372  #pragma omp parallel for schedule(static,4) shared(progress,status) \
1373    magick_threads(image,enhance_image,image->rows,1)
1374#endif
1375  for (y=0; y < (ssize_t) image->rows; y++)
1376  {
1377    PixelInfo
1378      pixel;
1379
1380    register const Quantum
1381      *magick_restrict p;
1382
1383    register Quantum
1384      *magick_restrict q;
1385
1386    register ssize_t
1387      x;
1388
1389    ssize_t
1390      center;
1391
1392    if (status == MagickFalse)
1393      continue;
1394    p=GetCacheViewVirtualPixels(image_view,-2,y-2,image->columns+4,5,exception);
1395    q=QueueCacheViewAuthenticPixels(enhance_view,0,y,enhance_image->columns,1,
1396      exception);
1397    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1398      {
1399        status=MagickFalse;
1400        continue;
1401      }
1402    center=(ssize_t) GetPixelChannels(image)*(2*(image->columns+4)+2);
1403    GetPixelInfo(image,&pixel);
1404    for (x=0; x < (ssize_t) image->columns; x++)
1405    {
1406      double
1407        distance,
1408        distance_squared,
1409        mean,
1410        total_weight;
1411
1412      PixelInfo
1413        aggregate;
1414
1415      register const Quantum
1416        *magick_restrict r;
1417
1418      if (GetPixelReadMask(image,p) == 0)
1419        {
1420          SetPixelBackgoundColor(enhance_image,q);
1421          p+=GetPixelChannels(image);
1422          q+=GetPixelChannels(enhance_image);
1423          continue;
1424        }
1425      GetPixelInfo(image,&aggregate);
1426      total_weight=0.0;
1427      GetPixelInfoPixel(image,p+center,&pixel);
1428      r=p;
1429      EnhancePixel(5.0); EnhancePixel(8.0); EnhancePixel(10.0);
1430        EnhancePixel(8.0); EnhancePixel(5.0);
1431      r=p+GetPixelChannels(image)*(image->columns+4);
1432      EnhancePixel(8.0); EnhancePixel(20.0); EnhancePixel(40.0);
1433        EnhancePixel(20.0); EnhancePixel(8.0);
1434      r=p+2*GetPixelChannels(image)*(image->columns+4);
1435      EnhancePixel(10.0); EnhancePixel(40.0); EnhancePixel(80.0);
1436        EnhancePixel(40.0); EnhancePixel(10.0);
1437      r=p+3*GetPixelChannels(image)*(image->columns+4);
1438      EnhancePixel(8.0); EnhancePixel(20.0); EnhancePixel(40.0);
1439        EnhancePixel(20.0); EnhancePixel(8.0);
1440      r=p+4*GetPixelChannels(image)*(image->columns+4);
1441      EnhancePixel(5.0); EnhancePixel(8.0); EnhancePixel(10.0);
1442        EnhancePixel(8.0); EnhancePixel(5.0);
1443      pixel.red=((aggregate.red+total_weight/2.0)/total_weight);
1444      pixel.green=((aggregate.green+total_weight/2.0)/total_weight);
1445      pixel.blue=((aggregate.blue+total_weight/2.0)/total_weight);
1446      pixel.black=((aggregate.black+total_weight/2.0)/total_weight);
1447      pixel.alpha=((aggregate.alpha+total_weight/2.0)/total_weight);
1448      SetPixelViaPixelInfo(image,&pixel,q);
1449      p+=GetPixelChannels(image);
1450      q+=GetPixelChannels(enhance_image);
1451    }
1452    if (SyncCacheViewAuthenticPixels(enhance_view,exception) == MagickFalse)
1453      status=MagickFalse;
1454    if (image->progress_monitor != (MagickProgressMonitor) NULL)
1455      {
1456        MagickBooleanType
1457          proceed;
1458
1459#if defined(MAGICKCORE_OPENMP_SUPPORT)
1460        #pragma omp critical (MagickCore_EnhanceImage)
1461#endif
1462        proceed=SetImageProgress(image,EnhanceImageTag,progress++,image->rows);
1463        if (proceed == MagickFalse)
1464          status=MagickFalse;
1465      }
1466  }
1467  enhance_view=DestroyCacheView(enhance_view);
1468  image_view=DestroyCacheView(image_view);
1469  if (status == MagickFalse)
1470    enhance_image=DestroyImage(enhance_image);
1471  return(enhance_image);
1472}
1473
1474/*
1475%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1476%                                                                             %
1477%                                                                             %
1478%                                                                             %
1479%     E q u a l i z e I m a g e                                               %
1480%                                                                             %
1481%                                                                             %
1482%                                                                             %
1483%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1484%
1485%  EqualizeImage() applies a histogram equalization to the image.
1486%
1487%  The format of the EqualizeImage method is:
1488%
1489%      MagickBooleanType EqualizeImage(Image *image,ExceptionInfo *exception)
1490%
1491%  A description of each parameter follows:
1492%
1493%    o image: the image.
1494%
1495%    o exception: return any errors or warnings in this structure.
1496%
1497*/
1498MagickExport MagickBooleanType EqualizeImage(Image *image,
1499  ExceptionInfo *exception)
1500{
1501#define EqualizeImageTag  "Equalize/Image"
1502
1503  CacheView
1504    *image_view;
1505
1506  double
1507    black[CompositePixelChannel+1],
1508    *equalize_map,
1509    *histogram,
1510    *map,
1511    white[CompositePixelChannel+1];
1512
1513  MagickBooleanType
1514    status;
1515
1516  MagickOffsetType
1517    progress;
1518
1519  register ssize_t
1520    i;
1521
1522  ssize_t
1523    y;
1524
1525  /*
1526    Allocate and initialize histogram arrays.
1527  */
1528  assert(image != (Image *) NULL);
1529  assert(image->signature == MagickCoreSignature);
1530#if defined(MAGICKCORE_OPENCL_SUPPORT)
1531  if (AccelerateEqualizeImage(image,exception) != MagickFalse)
1532    return(MagickTrue);
1533#endif
1534  if (image->debug != MagickFalse)
1535    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1536  equalize_map=(double *) AcquireQuantumMemory(MaxMap+1UL,
1537    GetPixelChannels(image)*sizeof(*equalize_map));
1538  histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,GetPixelChannels(image)*
1539    sizeof(*histogram));
1540  map=(double *) AcquireQuantumMemory(MaxMap+1UL,GetPixelChannels(image)*
1541    sizeof(*map));
1542  if ((equalize_map == (double *) NULL) || (histogram == (double *) NULL) ||
1543      (map == (double *) NULL))
1544    {
1545      if (map != (double *) NULL)
1546        map=(double *) RelinquishMagickMemory(map);
1547      if (histogram != (double *) NULL)
1548        histogram=(double *) RelinquishMagickMemory(histogram);
1549      if (equalize_map != (double *) NULL)
1550        equalize_map=(double *) RelinquishMagickMemory(equalize_map);
1551      ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1552        image->filename);
1553    }
1554  /*
1555    Form histogram.
1556  */
1557  status=MagickTrue;
1558  (void) ResetMagickMemory(histogram,0,(MaxMap+1)*GetPixelChannels(image)*
1559    sizeof(*histogram));
1560  image_view=AcquireVirtualCacheView(image,exception);
1561  for (y=0; y < (ssize_t) image->rows; y++)
1562  {
1563    register const Quantum
1564      *magick_restrict p;
1565
1566    register ssize_t
1567      x;
1568
1569    if (status == MagickFalse)
1570      continue;
1571    p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1572    if (p == (const Quantum *) NULL)
1573      {
1574        status=MagickFalse;
1575        continue;
1576      }
1577    for (x=0; x < (ssize_t) image->columns; x++)
1578    {
1579      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1580      {
1581        double
1582          intensity;
1583
1584        intensity=p[i];
1585        if ((image->channel_mask & SyncChannels) != 0)
1586          intensity=GetPixelIntensity(image,p);
1587        histogram[GetPixelChannels(image)*ScaleQuantumToMap(intensity)+i]++;
1588      }
1589      p+=GetPixelChannels(image);
1590    }
1591  }
1592  image_view=DestroyCacheView(image_view);
1593  /*
1594    Integrate the histogram to get the equalization map.
1595  */
1596  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1597  {
1598    double
1599      intensity;
1600
1601    register ssize_t
1602      j;
1603
1604    intensity=0.0;
1605    for (j=0; j <= (ssize_t) MaxMap; j++)
1606    {
1607      intensity+=histogram[GetPixelChannels(image)*j+i];
1608      map[GetPixelChannels(image)*j+i]=intensity;
1609    }
1610  }
1611  (void) ResetMagickMemory(equalize_map,0,(MaxMap+1)*GetPixelChannels(image)*
1612    sizeof(*equalize_map));
1613  (void) ResetMagickMemory(black,0,sizeof(*black));
1614  (void) ResetMagickMemory(white,0,sizeof(*white));
1615  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1616  {
1617    register ssize_t
1618      j;
1619
1620    black[i]=map[i];
1621    white[i]=map[GetPixelChannels(image)*MaxMap+i];
1622    if (black[i] != white[i])
1623      for (j=0; j <= (ssize_t) MaxMap; j++)
1624        equalize_map[GetPixelChannels(image)*j+i]=(double)
1625          ScaleMapToQuantum((double) ((MaxMap*(map[
1626          GetPixelChannels(image)*j+i]-black[i]))/(white[i]-black[i])));
1627  }
1628  histogram=(double *) RelinquishMagickMemory(histogram);
1629  map=(double *) RelinquishMagickMemory(map);
1630  if (image->storage_class == PseudoClass)
1631    {
1632      register ssize_t
1633        j;
1634
1635      /*
1636        Equalize colormap.
1637      */
1638      for (j=0; j < (ssize_t) image->colors; j++)
1639      {
1640        if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1641          {
1642            PixelChannel channel=GetPixelChannelChannel(image,RedPixelChannel);
1643            if (black[channel] != white[channel])
1644              image->colormap[j].red=equalize_map[GetPixelChannels(image)*
1645                ScaleQuantumToMap(ClampToQuantum(image->colormap[j].red))+
1646                channel];
1647          }
1648        if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1649          {
1650            PixelChannel channel=GetPixelChannelChannel(image,
1651              GreenPixelChannel);
1652            if (black[channel] != white[channel])
1653              image->colormap[j].green=equalize_map[GetPixelChannels(image)*
1654                ScaleQuantumToMap(ClampToQuantum(image->colormap[j].green))+
1655                channel];
1656          }
1657        if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1658          {
1659            PixelChannel channel=GetPixelChannelChannel(image,BluePixelChannel);
1660            if (black[channel] != white[channel])
1661              image->colormap[j].blue=equalize_map[GetPixelChannels(image)*
1662                ScaleQuantumToMap(ClampToQuantum(image->colormap[j].blue))+
1663                channel];
1664          }
1665        if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1666          {
1667            PixelChannel channel=GetPixelChannelChannel(image,
1668              AlphaPixelChannel);
1669            if (black[channel] != white[channel])
1670              image->colormap[j].alpha=equalize_map[GetPixelChannels(image)*
1671                ScaleQuantumToMap(ClampToQuantum(image->colormap[j].alpha))+
1672                channel];
1673          }
1674      }
1675    }
1676  /*
1677    Equalize image.
1678  */
1679  progress=0;
1680  image_view=AcquireAuthenticCacheView(image,exception);
1681#if defined(MAGICKCORE_OPENMP_SUPPORT)
1682  #pragma omp parallel for schedule(static,4) shared(progress,status) \
1683    magick_threads(image,image,image->rows,1)
1684#endif
1685  for (y=0; y < (ssize_t) image->rows; y++)
1686  {
1687    register Quantum
1688      *magick_restrict q;
1689
1690    register ssize_t
1691      x;
1692
1693    if (status == MagickFalse)
1694      continue;
1695    q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1696    if (q == (Quantum *) NULL)
1697      {
1698        status=MagickFalse;
1699        continue;
1700      }
1701    for (x=0; x < (ssize_t) image->columns; x++)
1702    {
1703      register ssize_t
1704        j;
1705
1706      if (GetPixelReadMask(image,q) == 0)
1707        {
1708          q+=GetPixelChannels(image);
1709          continue;
1710        }
1711      for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1712      {
1713        PixelChannel channel=GetPixelChannelChannel(image,j);
1714        PixelTrait traits=GetPixelChannelTraits(image,channel);
1715        if (((traits & UpdatePixelTrait) == 0) || (black[j] == white[j]))
1716          continue;
1717        q[j]=ClampToQuantum(equalize_map[GetPixelChannels(image)*
1718          ScaleQuantumToMap(q[j])+j]);
1719      }
1720      q+=GetPixelChannels(image);
1721    }
1722    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1723      status=MagickFalse;
1724    if (image->progress_monitor != (MagickProgressMonitor) NULL)
1725      {
1726        MagickBooleanType
1727          proceed;
1728
1729#if defined(MAGICKCORE_OPENMP_SUPPORT)
1730        #pragma omp critical (MagickCore_EqualizeImage)
1731#endif
1732        proceed=SetImageProgress(image,EqualizeImageTag,progress++,image->rows);
1733        if (proceed == MagickFalse)
1734          status=MagickFalse;
1735      }
1736  }
1737  image_view=DestroyCacheView(image_view);
1738  equalize_map=(double *) RelinquishMagickMemory(equalize_map);
1739  return(status);
1740}
1741
1742/*
1743%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1744%                                                                             %
1745%                                                                             %
1746%                                                                             %
1747%     G a m m a I m a g e                                                     %
1748%                                                                             %
1749%                                                                             %
1750%                                                                             %
1751%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1752%
1753%  GammaImage() gamma-corrects a particular image channel.  The same
1754%  image viewed on different devices will have perceptual differences in the
1755%  way the image's intensities are represented on the screen.  Specify
1756%  individual gamma levels for the red, green, and blue channels, or adjust
1757%  all three with the gamma parameter.  Values typically range from 0.8 to 2.3.
1758%
1759%  You can also reduce the influence of a particular channel with a gamma
1760%  value of 0.
1761%
1762%  The format of the GammaImage method is:
1763%
1764%      MagickBooleanType GammaImage(Image *image,const double gamma,
1765%        ExceptionInfo *exception)
1766%
1767%  A description of each parameter follows:
1768%
1769%    o image: the image.
1770%
1771%    o level: the image gamma as a string (e.g. 1.6,1.2,1.0).
1772%
1773%    o gamma: the image gamma.
1774%
1775*/
1776
1777static inline double gamma_pow(const double value,const double gamma)
1778{
1779  return(value < 0.0 ? value : pow(value,gamma));
1780}
1781
1782MagickExport MagickBooleanType GammaImage(Image *image,const double gamma,
1783  ExceptionInfo *exception)
1784{
1785#define GammaCorrectImageTag  "GammaCorrect/Image"
1786
1787  CacheView
1788    *image_view;
1789
1790  MagickBooleanType
1791    status;
1792
1793  MagickOffsetType
1794    progress;
1795
1796  Quantum
1797    *gamma_map;
1798
1799  register ssize_t
1800    i;
1801
1802  ssize_t
1803    y;
1804
1805  /*
1806    Allocate and initialize gamma maps.
1807  */
1808  assert(image != (Image *) NULL);
1809  assert(image->signature == MagickCoreSignature);
1810  if (image->debug != MagickFalse)
1811    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1812  if (gamma == 1.0)
1813    return(MagickTrue);
1814  gamma_map=(Quantum *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*gamma_map));
1815  if (gamma_map == (Quantum *) NULL)
1816    ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1817      image->filename);
1818  (void) ResetMagickMemory(gamma_map,0,(MaxMap+1)*sizeof(*gamma_map));
1819  if (gamma != 0.0)
1820    for (i=0; i <= (ssize_t) MaxMap; i++)
1821      gamma_map[i]=ScaleMapToQuantum((double) (MaxMap*pow((double) i/
1822        MaxMap,1.0/gamma)));
1823  if (image->storage_class == PseudoClass)
1824    for (i=0; i < (ssize_t) image->colors; i++)
1825    {
1826      /*
1827        Gamma-correct colormap.
1828      */
1829#if !defined(MAGICKCORE_HDRI_SUPPORT)
1830      if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1831        image->colormap[i].red=(double) gamma_map[ScaleQuantumToMap(
1832          ClampToQuantum(image->colormap[i].red))];
1833      if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1834        image->colormap[i].green=(double) gamma_map[ScaleQuantumToMap(
1835          ClampToQuantum(image->colormap[i].green))];
1836      if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1837        image->colormap[i].blue=(double) gamma_map[ScaleQuantumToMap(
1838          ClampToQuantum(image->colormap[i].blue))];
1839      if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1840        image->colormap[i].alpha=(double) gamma_map[ScaleQuantumToMap(
1841          ClampToQuantum(image->colormap[i].alpha))];
1842#else
1843      if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1844        image->colormap[i].red=QuantumRange*gamma_pow(QuantumScale*
1845          image->colormap[i].red,1.0/gamma);
1846      if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1847        image->colormap[i].green=QuantumRange*gamma_pow(QuantumScale*
1848          image->colormap[i].green,1.0/gamma);
1849      if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1850        image->colormap[i].blue=QuantumRange*gamma_pow(QuantumScale*
1851          image->colormap[i].blue,1.0/gamma);
1852      if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1853        image->colormap[i].alpha=QuantumRange*gamma_pow(QuantumScale*
1854          image->colormap[i].alpha,1.0/gamma);
1855#endif
1856    }
1857  /*
1858    Gamma-correct image.
1859  */
1860  status=MagickTrue;
1861  progress=0;
1862  image_view=AcquireAuthenticCacheView(image,exception);
1863#if defined(MAGICKCORE_OPENMP_SUPPORT)
1864  #pragma omp parallel for schedule(static,4) shared(progress,status) \
1865    magick_threads(image,image,image->rows,1)
1866#endif
1867  for (y=0; y < (ssize_t) image->rows; y++)
1868  {
1869    register Quantum
1870      *magick_restrict q;
1871
1872    register ssize_t
1873      x;
1874
1875    if (status == MagickFalse)
1876      continue;
1877    q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1878    if (q == (Quantum *) NULL)
1879      {
1880        status=MagickFalse;
1881        continue;
1882      }
1883    for (x=0; x < (ssize_t) image->columns; x++)
1884    {
1885      register ssize_t
1886        j;
1887
1888      if (GetPixelReadMask(image,q) == 0)
1889        {
1890          q+=GetPixelChannels(image);
1891          continue;
1892        }
1893      for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1894      {
1895        PixelChannel channel=GetPixelChannelChannel(image,j);
1896        PixelTrait traits=GetPixelChannelTraits(image,channel);
1897        if ((traits & UpdatePixelTrait) == 0)
1898          continue;
1899#if !defined(MAGICKCORE_HDRI_SUPPORT)
1900        q[j]=gamma_map[ScaleQuantumToMap(q[j])];
1901#else
1902        q[j]=QuantumRange*gamma_pow(QuantumScale*q[j],1.0/gamma);
1903#endif
1904      }
1905      q+=GetPixelChannels(image);
1906    }
1907    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1908      status=MagickFalse;
1909    if (image->progress_monitor != (MagickProgressMonitor) NULL)
1910      {
1911        MagickBooleanType
1912          proceed;
1913
1914#if defined(MAGICKCORE_OPENMP_SUPPORT)
1915        #pragma omp critical (MagickCore_GammaImage)
1916#endif
1917        proceed=SetImageProgress(image,GammaCorrectImageTag,progress++,
1918          image->rows);
1919        if (proceed == MagickFalse)
1920          status=MagickFalse;
1921      }
1922  }
1923  image_view=DestroyCacheView(image_view);
1924  gamma_map=(Quantum *) RelinquishMagickMemory(gamma_map);
1925  if (image->gamma != 0.0)
1926    image->gamma*=gamma;
1927  return(status);
1928}
1929
1930/*
1931%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1932%                                                                             %
1933%                                                                             %
1934%                                                                             %
1935%     G r a y s c a l e I m a g e                                             %
1936%                                                                             %
1937%                                                                             %
1938%                                                                             %
1939%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1940%
1941%  GrayscaleImage() converts the image to grayscale.
1942%
1943%  The format of the GrayscaleImage method is:
1944%
1945%      MagickBooleanType GrayscaleImage(Image *image,
1946%        const PixelIntensityMethod method ,ExceptionInfo *exception)
1947%
1948%  A description of each parameter follows:
1949%
1950%    o image: the image.
1951%
1952%    o method: the pixel intensity method.
1953%
1954%    o exception: return any errors or warnings in this structure.
1955%
1956*/
1957MagickExport MagickBooleanType GrayscaleImage(Image *image,
1958  const PixelIntensityMethod method,ExceptionInfo *exception)
1959{
1960#define GrayscaleImageTag  "Grayscale/Image"
1961
1962  CacheView
1963    *image_view;
1964
1965  MagickBooleanType
1966    status;
1967
1968  MagickOffsetType
1969    progress;
1970
1971  ssize_t
1972    y;
1973
1974  assert(image != (Image *) NULL);
1975  assert(image->signature == MagickCoreSignature);
1976  if (image->debug != MagickFalse)
1977    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1978  if (image->storage_class == PseudoClass)
1979    {
1980      if (SyncImage(image,exception) == MagickFalse)
1981        return(MagickFalse);
1982      if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1983        return(MagickFalse);
1984    }
1985#if defined(MAGICKCORE_OPENCL_SUPPORT)
1986  if (AccelerateGrayscaleImage(image,method,exception) != MagickFalse)
1987    {
1988      image->intensity=method;
1989      image->type=GrayscaleType;
1990      return(SetImageColorspace(image,GRAYColorspace,exception));
1991    }
1992#endif
1993  /*
1994    Grayscale image.
1995  */
1996  status=MagickTrue;
1997  progress=0;
1998  image_view=AcquireAuthenticCacheView(image,exception);
1999#if defined(MAGICKCORE_OPENMP_SUPPORT)
2000  #pragma omp parallel for schedule(static,4) shared(progress,status) \
2001    magick_threads(image,image,image->rows,1)
2002#endif
2003  for (y=0; y < (ssize_t) image->rows; y++)
2004  {
2005    register Quantum
2006      *magick_restrict q;
2007
2008    register ssize_t
2009      x;
2010
2011    if (status == MagickFalse)
2012      continue;
2013    q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2014    if (q == (Quantum *) NULL)
2015      {
2016        status=MagickFalse;
2017        continue;
2018      }
2019    for (x=0; x < (ssize_t) image->columns; x++)
2020    {
2021      MagickRealType
2022        blue,
2023        green,
2024        red,
2025        intensity;
2026
2027      if (GetPixelReadMask(image,q) == 0)
2028        {
2029          q+=GetPixelChannels(image);
2030          continue;
2031        }
2032      red=(MagickRealType) GetPixelRed(image,q);
2033      green=(MagickRealType) GetPixelGreen(image,q);
2034      blue=(MagickRealType) GetPixelBlue(image,q);
2035      intensity=0.0;
2036      switch (method)
2037      {
2038        case AveragePixelIntensityMethod:
2039        {
2040          intensity=(red+green+blue)/3.0;
2041          break;
2042        }
2043        case BrightnessPixelIntensityMethod:
2044        {
2045          intensity=MagickMax(MagickMax(red,green),blue);
2046          break;
2047        }
2048        case LightnessPixelIntensityMethod:
2049        {
2050          intensity=(MagickMin(MagickMin(red,green),blue)+
2051            MagickMax(MagickMax(red,green),blue))/2.0;
2052          break;
2053        }
2054        case MSPixelIntensityMethod:
2055        {
2056          intensity=(MagickRealType) (((double) red*red+green*green+
2057            blue*blue)/3.0);
2058          break;
2059        }
2060        case Rec601LumaPixelIntensityMethod:
2061        {
2062          if (image->colorspace == RGBColorspace)
2063            {
2064              red=EncodePixelGamma(red);
2065              green=EncodePixelGamma(green);
2066              blue=EncodePixelGamma(blue);
2067            }
2068          intensity=0.298839*red+0.586811*green+0.114350*blue;
2069          break;
2070        }
2071        case Rec601LuminancePixelIntensityMethod:
2072        {
2073          if (image->colorspace == sRGBColorspace)
2074            {
2075              red=DecodePixelGamma(red);
2076              green=DecodePixelGamma(green);
2077              blue=DecodePixelGamma(blue);
2078            }
2079          intensity=0.298839*red+0.586811*green+0.114350*blue;
2080          break;
2081        }
2082        case Rec709LumaPixelIntensityMethod:
2083        default:
2084        {
2085          if (image->colorspace == RGBColorspace)
2086            {
2087              red=EncodePixelGamma(red);
2088              green=EncodePixelGamma(green);
2089              blue=EncodePixelGamma(blue);
2090            }
2091          intensity=0.212656*red+0.715158*green+0.072186*blue;
2092          break;
2093        }
2094        case Rec709LuminancePixelIntensityMethod:
2095        {
2096          if (image->colorspace == sRGBColorspace)
2097            {
2098              red=DecodePixelGamma(red);
2099              green=DecodePixelGamma(green);
2100              blue=DecodePixelGamma(blue);
2101            }
2102          intensity=0.212656*red+0.715158*green+0.072186*blue;
2103          break;
2104        }
2105        case RMSPixelIntensityMethod:
2106        {
2107          intensity=(MagickRealType) (sqrt((double) red*red+green*green+
2108            blue*blue)/sqrt(3.0));
2109          break;
2110        }
2111      }
2112      SetPixelGray(image,ClampToQuantum(intensity),q);
2113      q+=GetPixelChannels(image);
2114    }
2115    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2116      status=MagickFalse;
2117    if (image->progress_monitor != (MagickProgressMonitor) NULL)
2118      {
2119        MagickBooleanType
2120          proceed;
2121
2122#if defined(MAGICKCORE_OPENMP_SUPPORT)
2123        #pragma omp critical (MagickCore_GrayscaleImage)
2124#endif
2125        proceed=SetImageProgress(image,GrayscaleImageTag,progress++,
2126           image->rows);
2127        if (proceed == MagickFalse)
2128          status=MagickFalse;
2129      }
2130  }
2131  image_view=DestroyCacheView(image_view);
2132  image->intensity=method;
2133  image->type=GrayscaleType;
2134  return(SetImageColorspace(image,GRAYColorspace,exception));
2135}
2136
2137/*
2138%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2139%                                                                             %
2140%                                                                             %
2141%                                                                             %
2142%     H a l d C l u t I m a g e                                               %
2143%                                                                             %
2144%                                                                             %
2145%                                                                             %
2146%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2147%
2148%  HaldClutImage() applies a Hald color lookup table to the image.  A Hald
2149%  color lookup table is a 3-dimensional color cube mapped to 2 dimensions.
2150%  Create it with the HALD coder.  You can apply any color transformation to
2151%  the Hald image and then use this method to apply the transform to the
2152%  image.
2153%
2154%  The format of the HaldClutImage method is:
2155%
2156%      MagickBooleanType HaldClutImage(Image *image,Image *hald_image,
2157%        ExceptionInfo *exception)
2158%
2159%  A description of each parameter follows:
2160%
2161%    o image: the image, which is replaced by indexed CLUT values
2162%
2163%    o hald_image: the color lookup table image for replacement color values.
2164%
2165%    o exception: return any errors or warnings in this structure.
2166%
2167*/
2168MagickExport MagickBooleanType HaldClutImage(Image *image,
2169  const Image *hald_image,ExceptionInfo *exception)
2170{
2171#define HaldClutImageTag  "Clut/Image"
2172
2173  typedef struct _HaldInfo
2174  {
2175    double
2176      x,
2177      y,
2178      z;
2179  } HaldInfo;
2180
2181  CacheView
2182    *hald_view,
2183    *image_view;
2184
2185  double
2186    width;
2187
2188  MagickBooleanType
2189    status;
2190
2191  MagickOffsetType
2192    progress;
2193
2194  PixelInfo
2195    zero;
2196
2197  size_t
2198    cube_size,
2199    length,
2200    level;
2201
2202  ssize_t
2203    y;
2204
2205  assert(image != (Image *) NULL);
2206  assert(image->signature == MagickCoreSignature);
2207  if (image->debug != MagickFalse)
2208    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2209  assert(hald_image != (Image *) NULL);
2210  assert(hald_image->signature == MagickCoreSignature);
2211  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2212    return(MagickFalse);
2213  if (image->alpha_trait == UndefinedPixelTrait)
2214    (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2215  /*
2216    Hald clut image.
2217  */
2218  status=MagickTrue;
2219  progress=0;
2220  length=(size_t) MagickMin((MagickRealType) hald_image->columns,
2221    (MagickRealType) hald_image->rows);
2222  for (level=2; (level*level*level) < length; level++) ;
2223  level*=level;
2224  cube_size=level*level;
2225  width=(double) hald_image->columns;
2226  GetPixelInfo(hald_image,&zero);
2227  hald_view=AcquireVirtualCacheView(hald_image,exception);
2228  image_view=AcquireAuthenticCacheView(image,exception);
2229#if defined(MAGICKCORE_OPENMP_SUPPORT)
2230  #pragma omp parallel for schedule(static,4) shared(progress,status) \
2231    magick_threads(image,image,image->rows,1)
2232#endif
2233  for (y=0; y < (ssize_t) image->rows; y++)
2234  {
2235    register Quantum
2236      *magick_restrict q;
2237
2238    register ssize_t
2239      x;
2240
2241    if (status == MagickFalse)
2242      continue;
2243    q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2244    if (q == (Quantum *) NULL)
2245      {
2246        status=MagickFalse;
2247        continue;
2248      }
2249    for (x=0; x < (ssize_t) image->columns; x++)
2250    {
2251      double
2252        offset;
2253
2254      HaldInfo
2255        point;
2256
2257      PixelInfo
2258        pixel,
2259        pixel1,
2260        pixel2,
2261        pixel3,
2262        pixel4;
2263
2264      point.x=QuantumScale*(level-1.0)*GetPixelRed(image,q);
2265      point.y=QuantumScale*(level-1.0)*GetPixelGreen(image,q);
2266      point.z=QuantumScale*(level-1.0)*GetPixelBlue(image,q);
2267      offset=point.x+level*floor(point.y)+cube_size*floor(point.z);
2268      point.x-=floor(point.x);
2269      point.y-=floor(point.y);
2270      point.z-=floor(point.z);
2271      pixel1=zero;
2272      (void) InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate,
2273        fmod(offset,width),floor(offset/width),&pixel1,exception);
2274      pixel2=zero;
2275      (void) InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate,
2276        fmod(offset+level,width),floor((offset+level)/width),&pixel2,exception);
2277      pixel3=zero;
2278      CompositePixelInfoAreaBlend(&pixel1,pixel1.alpha,&pixel2,pixel2.alpha,
2279        point.y,&pixel3);
2280      offset+=cube_size;
2281      (void) InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate,
2282        fmod(offset,width),floor(offset/width),&pixel1,exception);
2283      (void) InterpolatePixelInfo(hald_image,hald_view,hald_image->interpolate,
2284        fmod(offset+level,width),floor((offset+level)/width),&pixel2,exception);
2285      pixel4=zero;
2286      CompositePixelInfoAreaBlend(&pixel1,pixel1.alpha,&pixel2,pixel2.alpha,
2287        point.y,&pixel4);
2288      pixel=zero;
2289      CompositePixelInfoAreaBlend(&pixel3,pixel3.alpha,&pixel4,pixel4.alpha,
2290        point.z,&pixel);
2291      if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2292        SetPixelRed(image,ClampToQuantum(pixel.red),q);
2293      if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2294        SetPixelGreen(image,ClampToQuantum(pixel.green),q);
2295      if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2296        SetPixelBlue(image,ClampToQuantum(pixel.blue),q);
2297      if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2298          (image->colorspace == CMYKColorspace))
2299        SetPixelBlack(image,ClampToQuantum(pixel.black),q);
2300      if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2301          (image->alpha_trait != UndefinedPixelTrait))
2302        SetPixelAlpha(image,ClampToQuantum(pixel.alpha),q);
2303      q+=GetPixelChannels(image);
2304    }
2305    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2306      status=MagickFalse;
2307    if (image->progress_monitor != (MagickProgressMonitor) NULL)
2308      {
2309        MagickBooleanType
2310          proceed;
2311
2312#if defined(MAGICKCORE_OPENMP_SUPPORT)
2313        #pragma omp critical (MagickCore_HaldClutImage)
2314#endif
2315        proceed=SetImageProgress(image,HaldClutImageTag,progress++,image->rows);
2316        if (proceed == MagickFalse)
2317          status=MagickFalse;
2318      }
2319  }
2320  hald_view=DestroyCacheView(hald_view);
2321  image_view=DestroyCacheView(image_view);
2322  return(status);
2323}
2324
2325/*
2326%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2327%                                                                             %
2328%                                                                             %
2329%                                                                             %
2330%     L e v e l I m a g e                                                     %
2331%                                                                             %
2332%                                                                             %
2333%                                                                             %
2334%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2335%
2336%  LevelImage() adjusts the levels of a particular image channel by
2337%  scaling the colors falling between specified white and black points to
2338%  the full available quantum range.
2339%
2340%  The parameters provided represent the black, and white points.  The black
2341%  point specifies the darkest color in the image. Colors darker than the
2342%  black point are set to zero.  White point specifies the lightest color in
2343%  the image.  Colors brighter than the white point are set to the maximum
2344%  quantum value.
2345%
2346%  If a '!' flag is given, map black and white colors to the given levels
2347%  rather than mapping those levels to black and white.  See
2348%  LevelizeImage() below.
2349%
2350%  Gamma specifies a gamma correction to apply to the image.
2351%
2352%  The format of the LevelImage method is:
2353%
2354%      MagickBooleanType LevelImage(Image *image,const double black_point,
2355%        const double white_point,const double gamma,ExceptionInfo *exception)
2356%
2357%  A description of each parameter follows:
2358%
2359%    o image: the image.
2360%
2361%    o black_point: The level to map zero (black) to.
2362%
2363%    o white_point: The level to map QuantumRange (white) to.
2364%
2365%    o exception: return any errors or warnings in this structure.
2366%
2367*/
2368
2369static inline double LevelPixel(const double black_point,
2370  const double white_point,const double gamma,const double pixel)
2371{
2372  double
2373    level_pixel,
2374    scale;
2375
2376  if (fabs(white_point-black_point) < MagickEpsilon)
2377    return(pixel);
2378  scale=1.0/(white_point-black_point);
2379  level_pixel=QuantumRange*gamma_pow(scale*((double) pixel-black_point),
2380    1.0/gamma);
2381  return(level_pixel);
2382}
2383
2384MagickExport MagickBooleanType LevelImage(Image *image,const double black_point,
2385  const double white_point,const double gamma,ExceptionInfo *exception)
2386{
2387#define LevelImageTag  "Level/Image"
2388
2389  CacheView
2390    *image_view;
2391
2392  MagickBooleanType
2393    status;
2394
2395  MagickOffsetType
2396    progress;
2397
2398  register ssize_t
2399    i;
2400
2401  ssize_t
2402    y;
2403
2404  /*
2405    Allocate and initialize levels map.
2406  */
2407  assert(image != (Image *) NULL);
2408  assert(image->signature == MagickCoreSignature);
2409  if (image->debug != MagickFalse)
2410    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2411  if (image->storage_class == PseudoClass)
2412    for (i=0; i < (ssize_t) image->colors; i++)
2413    {
2414      /*
2415        Level colormap.
2416      */
2417      if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2418        image->colormap[i].red=(double) ClampToQuantum(LevelPixel(black_point,
2419          white_point,gamma,image->colormap[i].red));
2420      if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2421        image->colormap[i].green=(double) ClampToQuantum(LevelPixel(black_point,
2422          white_point,gamma,image->colormap[i].green));
2423      if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2424        image->colormap[i].blue=(double) ClampToQuantum(LevelPixel(black_point,
2425          white_point,gamma,image->colormap[i].blue));
2426      if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2427        image->colormap[i].alpha=(double) ClampToQuantum(LevelPixel(black_point,
2428          white_point,gamma,image->colormap[i].alpha));
2429    }
2430  /*
2431    Level image.
2432  */
2433  status=MagickTrue;
2434  progress=0;
2435  image_view=AcquireAuthenticCacheView(image,exception);
2436#if defined(MAGICKCORE_OPENMP_SUPPORT)
2437  #pragma omp parallel for schedule(static,4) shared(progress,status) \
2438    magick_threads(image,image,image->rows,1)
2439#endif
2440  for (y=0; y < (ssize_t) image->rows; y++)
2441  {
2442    register Quantum
2443      *magick_restrict q;
2444
2445    register ssize_t
2446      x;
2447
2448    if (status == MagickFalse)
2449      continue;
2450    q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2451    if (q == (Quantum *) NULL)
2452      {
2453        status=MagickFalse;
2454        continue;
2455      }
2456    for (x=0; x < (ssize_t) image->columns; x++)
2457    {
2458      register ssize_t
2459        j;
2460
2461      if (GetPixelReadMask(image,q) == 0)
2462        {
2463          q+=GetPixelChannels(image);
2464          continue;
2465        }
2466      for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
2467      {
2468        PixelChannel channel=GetPixelChannelChannel(image,j);
2469        PixelTrait traits=GetPixelChannelTraits(image,channel);
2470        if ((traits & UpdatePixelTrait) == 0)
2471          continue;
2472        q[j]=ClampToQuantum(LevelPixel(black_point,white_point,gamma,
2473          (double) q[j]));
2474      }
2475      q+=GetPixelChannels(image);
2476    }
2477    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2478      status=MagickFalse;
2479    if (image->progress_monitor != (MagickProgressMonitor) NULL)
2480      {
2481        MagickBooleanType
2482          proceed;
2483
2484#if defined(MAGICKCORE_OPENMP_SUPPORT)
2485        #pragma omp critical (MagickCore_LevelImage)
2486#endif
2487        proceed=SetImageProgress(image,LevelImageTag,progress++,image->rows);
2488        if (proceed == MagickFalse)
2489          status=MagickFalse;
2490      }
2491  }
2492  image_view=DestroyCacheView(image_view);
2493  (void) ClampImage(image,exception);
2494  return(status);
2495}
2496
2497/*
2498%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2499%                                                                             %
2500%                                                                             %
2501%                                                                             %
2502%     L e v e l i z e I m a g e                                               %
2503%                                                                             %
2504%                                                                             %
2505%                                                                             %
2506%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2507%
2508%  LevelizeImage() applies the reversed LevelImage() operation to just
2509%  the specific channels specified.  It compresses the full range of color
2510%  values, so that they lie between the given black and white points. Gamma is
2511%  applied before the values are mapped.
2512%
2513%  LevelizeImage() can be called with by using a +level command line
2514%  API option, or using a '!' on a -level or LevelImage() geometry string.
2515%
2516%  It can be used to de-contrast a greyscale image to the exact levels
2517%  specified.  Or by using specific levels for each channel of an image you
2518%  can convert a gray-scale image to any linear color gradient, according to
2519%  those levels.
2520%
2521%  The format of the LevelizeImage method is:
2522%
2523%      MagickBooleanType LevelizeImage(Image *image,const double black_point,
2524%        const double white_point,const double gamma,ExceptionInfo *exception)
2525%
2526%  A description of each parameter follows:
2527%
2528%    o image: the image.
2529%
2530%    o black_point: The level to map zero (black) to.
2531%
2532%    o white_point: The level to map QuantumRange (white) to.
2533%
2534%    o gamma: adjust gamma by this factor before mapping values.
2535%
2536%    o exception: return any errors or warnings in this structure.
2537%
2538*/
2539MagickExport MagickBooleanType LevelizeImage(Image *image,
2540  const double black_point,const double white_point,const double gamma,
2541  ExceptionInfo *exception)
2542{
2543#define LevelizeImageTag  "Levelize/Image"
2544#define LevelizeValue(x) ClampToQuantum(((MagickRealType) gamma_pow((double) \
2545  (QuantumScale*(x)),gamma))*(white_point-black_point)+black_point)
2546
2547  CacheView
2548    *image_view;
2549
2550  MagickBooleanType
2551    status;
2552
2553  MagickOffsetType
2554    progress;
2555
2556  register ssize_t
2557    i;
2558
2559  ssize_t
2560    y;
2561
2562  /*
2563    Allocate and initialize levels map.
2564  */
2565  assert(image != (Image *) NULL);
2566  assert(image->signature == MagickCoreSignature);
2567  if (image->debug != MagickFalse)
2568    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2569  if (image->storage_class == PseudoClass)
2570    for (i=0; i < (ssize_t) image->colors; i++)
2571    {
2572      /*
2573        Level colormap.
2574      */
2575      if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2576        image->colormap[i].red=(double) LevelizeValue(image->colormap[i].red);
2577      if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2578        image->colormap[i].green=(double) LevelizeValue(
2579          image->colormap[i].green);
2580      if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2581        image->colormap[i].blue=(double) LevelizeValue(image->colormap[i].blue);
2582      if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
2583        image->colormap[i].alpha=(double) LevelizeValue(
2584          image->colormap[i].alpha);
2585    }
2586  /*
2587    Level image.
2588  */
2589  status=MagickTrue;
2590  progress=0;
2591  image_view=AcquireAuthenticCacheView(image,exception);
2592#if defined(MAGICKCORE_OPENMP_SUPPORT)
2593  #pragma omp parallel for schedule(static,4) shared(progress,status) \
2594    magick_threads(image,image,image->rows,1)
2595#endif
2596  for (y=0; y < (ssize_t) image->rows; y++)
2597  {
2598    register Quantum
2599      *magick_restrict q;
2600
2601    register ssize_t
2602      x;
2603
2604    if (status == MagickFalse)
2605      continue;
2606    q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2607    if (q == (Quantum *) NULL)
2608      {
2609        status=MagickFalse;
2610        continue;
2611      }
2612    for (x=0; x < (ssize_t) image->columns; x++)
2613    {
2614      register ssize_t
2615        j;
2616
2617      if (GetPixelReadMask(image,q) == 0)
2618        {
2619          q+=GetPixelChannels(image);
2620          continue;
2621        }
2622      for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
2623      {
2624        PixelChannel channel=GetPixelChannelChannel(image,j);
2625        PixelTrait traits=GetPixelChannelTraits(image,channel);
2626        if ((traits & UpdatePixelTrait) == 0)
2627          continue;
2628        q[j]=LevelizeValue(q[j]);
2629      }
2630      q+=GetPixelChannels(image);
2631    }
2632    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2633      status=MagickFalse;
2634    if (image->progress_monitor != (MagickProgressMonitor) NULL)
2635      {
2636        MagickBooleanType
2637          proceed;
2638
2639#if defined(MAGICKCORE_OPENMP_SUPPORT)
2640        #pragma omp critical (MagickCore_LevelizeImage)
2641#endif
2642        proceed=SetImageProgress(image,LevelizeImageTag,progress++,image->rows);
2643        if (proceed == MagickFalse)
2644          status=MagickFalse;
2645      }
2646  }
2647  image_view=DestroyCacheView(image_view);
2648  return(status);
2649}
2650
2651/*
2652%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2653%                                                                             %
2654%                                                                             %
2655%                                                                             %
2656%     L e v e l I m a g e C o l o r s                                         %
2657%                                                                             %
2658%                                                                             %
2659%                                                                             %
2660%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2661%
2662%  LevelImageColors() maps the given color to "black" and "white" values,
2663%  linearly spreading out the colors, and level values on a channel by channel
2664%  bases, as per LevelImage().  The given colors allows you to specify
2665%  different level ranges for each of the color channels separately.
2666%
2667%  If the boolean 'invert' is set true the image values will modifyed in the
2668%  reverse direction. That is any existing "black" and "white" colors in the
2669%  image will become the color values given, with all other values compressed
2670%  appropriatally.  This effectivally maps a greyscale gradient into the given
2671%  color gradient.
2672%
2673%  The format of the LevelImageColors method is:
2674%
2675%    MagickBooleanType LevelImageColors(Image *image,
2676%      const PixelInfo *black_color,const PixelInfo *white_color,
2677%      const MagickBooleanType invert,ExceptionInfo *exception)
2678%
2679%  A description of each parameter follows:
2680%
2681%    o image: the image.
2682%
2683%    o black_color: The color to map black to/from
2684%
2685%    o white_point: The color to map white to/from
2686%
2687%    o invert: if true map the colors (levelize), rather than from (level)
2688%
2689%    o exception: return any errors or warnings in this structure.
2690%
2691*/
2692MagickExport MagickBooleanType LevelImageColors(Image *image,
2693  const PixelInfo *black_color,const PixelInfo *white_color,
2694  const MagickBooleanType invert,ExceptionInfo *exception)
2695{
2696  ChannelType
2697    channel_mask;
2698
2699  MagickStatusType
2700    status;
2701
2702  /*
2703    Allocate and initialize levels map.
2704  */
2705  assert(image != (Image *) NULL);
2706  assert(image->signature == MagickCoreSignature);
2707  if (image->debug != MagickFalse)
2708    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2709  if ((IsGrayColorspace(image->colorspace) != MagickFalse) &&
2710      ((IsGrayColorspace(black_color->colorspace) == MagickFalse) ||
2711       (IsGrayColorspace(white_color->colorspace) == MagickFalse)))
2712    (void) SetImageColorspace(image,sRGBColorspace,exception);
2713  status=MagickTrue;
2714  if (invert == MagickFalse)
2715    {
2716      if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2717        {
2718          channel_mask=SetImageChannelMask(image,RedChannel);
2719          status&=LevelImage(image,black_color->red,white_color->red,1.0,
2720            exception);
2721          (void) SetImageChannelMask(image,channel_mask);
2722        }
2723      if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2724        {
2725          channel_mask=SetImageChannelMask(image,GreenChannel);
2726          status&=LevelImage(image,black_color->green,white_color->green,1.0,
2727            exception);
2728          (void) SetImageChannelMask(image,channel_mask);
2729        }
2730      if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2731        {
2732          channel_mask=SetImageChannelMask(image,BlueChannel);
2733          status&=LevelImage(image,black_color->blue,white_color->blue,1.0,
2734            exception);
2735          (void) SetImageChannelMask(image,channel_mask);
2736        }
2737      if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2738          (image->colorspace == CMYKColorspace))
2739        {
2740          channel_mask=SetImageChannelMask(image,BlackChannel);
2741          status&=LevelImage(image,black_color->black,white_color->black,1.0,
2742            exception);
2743          (void) SetImageChannelMask(image,channel_mask);
2744        }
2745      if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2746          (image->alpha_trait != UndefinedPixelTrait))
2747        {
2748          channel_mask=SetImageChannelMask(image,AlphaChannel);
2749          status&=LevelImage(image,black_color->alpha,white_color->alpha,1.0,
2750            exception);
2751          (void) SetImageChannelMask(image,channel_mask);
2752        }
2753    }
2754  else
2755    {
2756      if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
2757        {
2758          channel_mask=SetImageChannelMask(image,RedChannel);
2759          status&=LevelizeImage(image,black_color->red,white_color->red,1.0,
2760            exception);
2761          (void) SetImageChannelMask(image,channel_mask);
2762        }
2763      if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
2764        {
2765          channel_mask=SetImageChannelMask(image,GreenChannel);
2766          status&=LevelizeImage(image,black_color->green,white_color->green,1.0,
2767            exception);
2768          (void) SetImageChannelMask(image,channel_mask);
2769        }
2770      if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
2771        {
2772          channel_mask=SetImageChannelMask(image,BlueChannel);
2773          status&=LevelizeImage(image,black_color->blue,white_color->blue,1.0,
2774            exception);
2775          (void) SetImageChannelMask(image,channel_mask);
2776        }
2777      if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
2778          (image->colorspace == CMYKColorspace))
2779        {
2780          channel_mask=SetImageChannelMask(image,BlackChannel);
2781          status&=LevelizeImage(image,black_color->black,white_color->black,1.0,
2782            exception);
2783          (void) SetImageChannelMask(image,channel_mask);
2784        }
2785      if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
2786          (image->alpha_trait != UndefinedPixelTrait))
2787        {
2788          channel_mask=SetImageChannelMask(image,AlphaChannel);
2789          status&=LevelizeImage(image,black_color->alpha,white_color->alpha,1.0,
2790            exception);
2791          (void) SetImageChannelMask(image,channel_mask);
2792        }
2793    }
2794  return(status != 0 ? MagickTrue : MagickFalse);
2795}
2796
2797/*
2798%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2799%                                                                             %
2800%                                                                             %
2801%                                                                             %
2802%     L i n e a r S t r e t c h I m a g e                                     %
2803%                                                                             %
2804%                                                                             %
2805%                                                                             %
2806%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2807%
2808%  LinearStretchImage() discards any pixels below the black point and above
2809%  the white point and levels the remaining pixels.
2810%
2811%  The format of the LinearStretchImage method is:
2812%
2813%      MagickBooleanType LinearStretchImage(Image *image,
2814%        const double black_point,const double white_point,
2815%        ExceptionInfo *exception)
2816%
2817%  A description of each parameter follows:
2818%
2819%    o image: the image.
2820%
2821%    o black_point: the black point.
2822%
2823%    o white_point: the white point.
2824%
2825%    o exception: return any errors or warnings in this structure.
2826%
2827*/
2828MagickExport MagickBooleanType LinearStretchImage(Image *image,
2829  const double black_point,const double white_point,ExceptionInfo *exception)
2830{
2831#define LinearStretchImageTag  "LinearStretch/Image"
2832
2833  CacheView
2834    *image_view;
2835
2836  double
2837    *histogram,
2838    intensity;
2839
2840  MagickBooleanType
2841    status;
2842
2843  ssize_t
2844    black,
2845    white,
2846    y;
2847
2848  /*
2849    Allocate histogram and linear map.
2850  */
2851  assert(image != (Image *) NULL);
2852  assert(image->signature == MagickCoreSignature);
2853  histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*histogram));
2854  if (histogram == (double *) NULL)
2855    ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2856      image->filename);
2857  /*
2858    Form histogram.
2859  */
2860  (void) ResetMagickMemory(histogram,0,(MaxMap+1)*sizeof(*histogram));
2861  image_view=AcquireVirtualCacheView(image,exception);
2862  for (y=0; y < (ssize_t) image->rows; y++)
2863  {
2864    register const Quantum
2865      *magick_restrict p;
2866
2867    register ssize_t
2868      x;
2869
2870    p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2871    if (p == (const Quantum *) NULL)
2872      break;
2873    for (x=0; x < (ssize_t) image->columns; x++)
2874    {
2875      intensity=GetPixelIntensity(image,p);
2876      histogram[ScaleQuantumToMap(ClampToQuantum(intensity))]++;
2877      p+=GetPixelChannels(image);
2878    }
2879  }
2880  image_view=DestroyCacheView(image_view);
2881  /*
2882    Find the histogram boundaries by locating the black and white point levels.
2883  */
2884  intensity=0.0;
2885  for (black=0; black < (ssize_t) MaxMap; black++)
2886  {
2887    intensity+=histogram[black];
2888    if (intensity >= black_point)
2889      break;
2890  }
2891  intensity=0.0;
2892  for (white=(ssize_t) MaxMap; white != 0; white--)
2893  {
2894    intensity+=histogram[white];
2895    if (intensity >= white_point)
2896      break;
2897  }
2898  histogram=(double *) RelinquishMagickMemory(histogram);
2899  status=LevelImage(image,(double) ScaleMapToQuantum((MagickRealType) black),
2900    (double) ScaleMapToQuantum((MagickRealType) white),1.0,exception);
2901  return(status);
2902}
2903
2904
2905/*
2906%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2907%                                                                             %
2908%                                                                             %
2909%                                                                             %
2910%     M o d u l a t e I m a g e                                               %
2911%                                                                             %
2912%                                                                             %
2913%                                                                             %
2914%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2915%
2916%  ModulateImage() lets you control the brightness, saturation, and hue
2917%  of an image.  Modulate represents the brightness, saturation, and hue
2918%  as one parameter (e.g. 90,150,100).  If the image colorspace is HSL, the
2919%  modulation is lightness, saturation, and hue.  For HWB, use blackness,
2920%  whiteness, and hue. And for HCL, use chrome, luma, and hue.
2921%
2922%  The format of the ModulateImage method is:
2923%
2924%      MagickBooleanType ModulateImage(Image *image,const char *modulate,
2925%        ExceptionInfo *exception)
2926%
2927%  A description of each parameter follows:
2928%
2929%    o image: the image.
2930%
2931%    o modulate: Define the percent change in brightness, saturation, and hue.
2932%
2933%    o exception: return any errors or warnings in this structure.
2934%
2935*/
2936
2937static inline void ModulateHCL(const double percent_hue,
2938  const double percent_chroma,const double percent_luma,double *red,
2939  double *green,double *blue)
2940{
2941  double
2942    hue,
2943    luma,
2944    chroma;
2945
2946  /*
2947    Increase or decrease color luma, chroma, or hue.
2948  */
2949  ConvertRGBToHCL(*red,*green,*blue,&hue,&chroma,&luma);
2950  hue+=0.5*(0.01*percent_hue-1.0);
2951  while (hue < 0.0)
2952    hue+=1.0;
2953  while (hue > 1.0)
2954    hue-=1.0;
2955  chroma*=0.01*percent_chroma;
2956  luma*=0.01*percent_luma;
2957  ConvertHCLToRGB(hue,chroma,luma,red,green,blue);
2958}
2959
2960static inline void ModulateHCLp(const double percent_hue,
2961  const double percent_chroma,const double percent_luma,double *red,
2962  double *green,double *blue)
2963{
2964  double
2965    hue,
2966    luma,
2967    chroma;
2968
2969  /*
2970    Increase or decrease color luma, chroma, or hue.
2971  */
2972  ConvertRGBToHCLp(*red,*green,*blue,&hue,&chroma,&luma);
2973  hue+=0.5*(0.01*percent_hue-1.0);
2974  while (hue < 0.0)
2975    hue+=1.0;
2976  while (hue > 1.0)
2977    hue-=1.0;
2978  chroma*=0.01*percent_chroma;
2979  luma*=0.01*percent_luma;
2980  ConvertHCLpToRGB(hue,chroma,luma,red,green,blue);
2981}
2982
2983static inline void ModulateHSB(const double percent_hue,
2984  const double percent_saturation,const double percent_brightness,double *red,
2985  double *green,double *blue)
2986{
2987  double
2988    brightness,
2989    hue,
2990    saturation;
2991
2992  /*
2993    Increase or decrease color brightness, saturation, or hue.
2994  */
2995  ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness);
2996  hue+=0.5*(0.01*percent_hue-1.0);
2997  while (hue < 0.0)
2998    hue+=1.0;
2999  while (hue > 1.0)
3000    hue-=1.0;
3001  saturation*=0.01*percent_saturation;
3002  brightness*=0.01*percent_brightness;
3003  ConvertHSBToRGB(hue,saturation,brightness,red,green,blue);
3004}
3005
3006static inline void ModulateHSI(const double percent_hue,
3007  const double percent_saturation,const double percent_intensity,double *red,
3008  double *green,double *blue)
3009{
3010  double
3011    intensity,
3012    hue,
3013    saturation;
3014
3015  /*
3016    Increase or decrease color intensity, saturation, or hue.
3017  */
3018  ConvertRGBToHSI(*red,*green,*blue,&hue,&saturation,&intensity);
3019  hue+=0.5*(0.01*percent_hue-1.0);
3020  while (hue < 0.0)
3021    hue+=1.0;
3022  while (hue > 1.0)
3023    hue-=1.0;
3024  saturation*=0.01*percent_saturation;
3025  intensity*=0.01*percent_intensity;
3026  ConvertHSIToRGB(hue,saturation,intensity,red,green,blue);
3027}
3028
3029static inline void ModulateHSL(const double percent_hue,
3030  const double percent_saturation,const double percent_lightness,double *red,
3031  double *green,double *blue)
3032{
3033  double
3034    hue,
3035    lightness,
3036    saturation;
3037
3038  /*
3039    Increase or decrease color lightness, saturation, or hue.
3040  */
3041  ConvertRGBToHSL(*red,*green,*blue,&hue,&saturation,&lightness);
3042  hue+=0.5*(0.01*percent_hue-1.0);
3043  while (hue < 0.0)
3044    hue+=1.0;
3045  while (hue >= 1.0)
3046    hue-=1.0;
3047  saturation*=0.01*percent_saturation;
3048  lightness*=0.01*percent_lightness;
3049  ConvertHSLToRGB(hue,saturation,lightness,red,green,blue);
3050}
3051
3052static inline void ModulateHSV(const double percent_hue,
3053  const double percent_saturation,const double percent_value,double *red,
3054  double *green,double *blue)
3055{
3056  double
3057    hue,
3058    saturation,
3059    value;
3060
3061  /*
3062    Increase or decrease color value, saturation, or hue.
3063  */
3064  ConvertRGBToHSV(*red,*green,*blue,&hue,&saturation,&value);
3065  hue+=0.5*(0.01*percent_hue-1.0);
3066  while (hue < 0.0)
3067    hue+=1.0;
3068  while (hue >= 1.0)
3069    hue-=1.0;
3070  saturation*=0.01*percent_saturation;
3071  value*=0.01*percent_value;
3072  ConvertHSVToRGB(hue,saturation,value,red,green,blue);
3073}
3074
3075static inline void ModulateHWB(const double percent_hue,
3076  const double percent_whiteness,const double percent_blackness,double *red,
3077  double *green,double *blue)
3078{
3079  double
3080    blackness,
3081    hue,
3082    whiteness;
3083
3084  /*
3085    Increase or decrease color blackness, whiteness, or hue.
3086  */
3087  ConvertRGBToHWB(*red,*green,*blue,&hue,&whiteness,&blackness);
3088  hue+=0.5*(0.01*percent_hue-1.0);
3089  while (hue < 0.0)
3090    hue+=1.0;
3091  while (hue >= 1.0)
3092    hue-=1.0;
3093  blackness*=0.01*percent_blackness;
3094  whiteness*=0.01*percent_whiteness;
3095  ConvertHWBToRGB(hue,whiteness,blackness,red,green,blue);
3096}
3097
3098static inline void ModulateLCHab(const double percent_luma,
3099  const double percent_chroma,const double percent_hue,double *red,
3100  double *green,double *blue)
3101{
3102  double
3103    hue,
3104    luma,
3105    chroma;
3106
3107  /*
3108    Increase or decrease color luma, chroma, or hue.
3109  */
3110  ConvertRGBToLCHab(*red,*green,*blue,&luma,&chroma,&hue);
3111  luma*=0.01*percent_luma;
3112  chroma*=0.01*percent_chroma;
3113  hue+=0.5*(0.01*percent_hue-1.0);
3114  while (hue < 0.0)
3115    hue+=1.0;
3116  while (hue >= 1.0)
3117    hue-=1.0;
3118  ConvertLCHabToRGB(luma,chroma,hue,red,green,blue);
3119}
3120
3121static inline void ModulateLCHuv(const double percent_luma,
3122  const double percent_chroma,const double percent_hue,double *red,
3123  double *green,double *blue)
3124{
3125  double
3126    hue,
3127    luma,
3128    chroma;
3129
3130  /*
3131    Increase or decrease color luma, chroma, or hue.
3132  */
3133  ConvertRGBToLCHuv(*red,*green,*blue,&luma,&chroma,&hue);
3134  luma*=0.01*percent_luma;
3135  chroma*=0.01*percent_chroma;
3136  hue+=0.5*(0.01*percent_hue-1.0);
3137  while (hue < 0.0)
3138    hue+=1.0;
3139  while (hue >= 1.0)
3140    hue-=1.0;
3141  ConvertLCHuvToRGB(luma,chroma,hue,red,green,blue);
3142}
3143
3144MagickExport MagickBooleanType ModulateImage(Image *image,const char *modulate,
3145  ExceptionInfo *exception)
3146{
3147#define ModulateImageTag  "Modulate/Image"
3148
3149  CacheView
3150    *image_view;
3151
3152  ColorspaceType
3153    colorspace;
3154
3155  const char
3156    *artifact;
3157
3158  double
3159    percent_brightness,
3160    percent_hue,
3161    percent_saturation;
3162
3163  GeometryInfo
3164    geometry_info;
3165
3166  MagickBooleanType
3167    status;
3168
3169  MagickOffsetType
3170    progress;
3171
3172  MagickStatusType
3173    flags;
3174
3175  register ssize_t
3176    i;
3177
3178  ssize_t
3179    y;
3180
3181  /*
3182    Initialize modulate table.
3183  */
3184  assert(image != (Image *) NULL);
3185  assert(image->signature == MagickCoreSignature);
3186  if (image->debug != MagickFalse)
3187    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3188  if (modulate == (char *) NULL)
3189    return(MagickFalse);
3190  if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
3191    (void) SetImageColorspace(image,sRGBColorspace,exception);
3192  flags=ParseGeometry(modulate,&geometry_info);
3193  percent_brightness=geometry_info.rho;
3194  percent_saturation=geometry_info.sigma;
3195  if ((flags & SigmaValue) == 0)
3196    percent_saturation=100.0;
3197  percent_hue=geometry_info.xi;
3198  if ((flags & XiValue) == 0)
3199    percent_hue=100.0;
3200  colorspace=UndefinedColorspace;
3201  artifact=GetImageArtifact(image,"modulate:colorspace");
3202  if (artifact != (const char *) NULL)
3203    colorspace=(ColorspaceType) ParseCommandOption(MagickColorspaceOptions,
3204      MagickFalse,artifact);
3205  if (image->storage_class == PseudoClass)
3206    for (i=0; i < (ssize_t) image->colors; i++)
3207    {
3208      double
3209        blue,
3210        green,
3211        red;
3212
3213      /*
3214        Modulate image colormap.
3215      */
3216      red=(double) image->colormap[i].red;
3217      green=(double) image->colormap[i].green;
3218      blue=(double) image->colormap[i].blue;
3219      switch (colorspace)
3220      {
3221        case HCLColorspace:
3222        {
3223          ModulateHCL(percent_hue,percent_saturation,percent_brightness,
3224            &red,&green,&blue);
3225          break;
3226        }
3227        case HCLpColorspace:
3228        {
3229          ModulateHCLp(percent_hue,percent_saturation,percent_brightness,
3230            &red,&green,&blue);
3231          break;
3232        }
3233        case HSBColorspace:
3234        {
3235          ModulateHSB(percent_hue,percent_saturation,percent_brightness,
3236            &red,&green,&blue);
3237          break;
3238        }
3239        case HSIColorspace:
3240        {
3241          ModulateHSI(percent_hue,percent_saturation,percent_brightness,
3242            &red,&green,&blue);
3243          break;
3244        }
3245        case HSLColorspace:
3246        default:
3247        {
3248          ModulateHSL(percent_hue,percent_saturation,percent_brightness,
3249            &red,&green,&blue);
3250          break;
3251        }
3252        case HSVColorspace:
3253        {
3254          ModulateHSV(percent_hue,percent_saturation,percent_brightness,
3255            &red,&green,&blue);
3256          break;
3257        }
3258        case HWBColorspace:
3259        {
3260          ModulateHWB(percent_hue,percent_saturation,percent_brightness,
3261            &red,&green,&blue);
3262          break;
3263        }
3264        case LCHColorspace:
3265        case LCHabColorspace:
3266        {
3267          ModulateLCHab(percent_brightness,percent_saturation,percent_hue,
3268            &red,&green,&blue);
3269          break;
3270        }
3271        case LCHuvColorspace:
3272        {
3273          ModulateLCHuv(percent_brightness,percent_saturation,percent_hue,
3274            &red,&green,&blue);
3275          break;
3276        }
3277      }
3278      image->colormap[i].red=red;
3279      image->colormap[i].green=green;
3280      image->colormap[i].blue=blue;
3281    }
3282  /*
3283    Modulate image.
3284  */
3285#if defined(MAGICKCORE_OPENCL_SUPPORT)
3286  if (AccelerateModulateImage(image,percent_brightness,percent_hue,
3287        percent_saturation,colorspace,exception) != MagickFalse)
3288    return(MagickTrue);
3289#endif
3290  status=MagickTrue;
3291  progress=0;
3292  image_view=AcquireAuthenticCacheView(image,exception);
3293#if defined(MAGICKCORE_OPENMP_SUPPORT)
3294  #pragma omp parallel for schedule(static,4) shared(progress,status) \
3295    magick_threads(image,image,image->rows,1)
3296#endif
3297  for (y=0; y < (ssize_t) image->rows; y++)
3298  {
3299    register Quantum
3300      *magick_restrict q;
3301
3302    register ssize_t
3303      x;
3304
3305    if (status == MagickFalse)
3306      continue;
3307    q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3308    if (q == (Quantum *) NULL)
3309      {
3310        status=MagickFalse;
3311        continue;
3312      }
3313    for (x=0; x < (ssize_t) image->columns; x++)
3314    {
3315      double
3316        blue,
3317        green,
3318        red;
3319
3320      red=(double) GetPixelRed(image,q);
3321      green=(double) GetPixelGreen(image,q);
3322      blue=(double) GetPixelBlue(image,q);
3323      switch (colorspace)
3324      {
3325        case HCLColorspace:
3326        {
3327          ModulateHCL(percent_hue,percent_saturation,percent_brightness,
3328            &red,&green,&blue);
3329          break;
3330        }
3331        case HCLpColorspace:
3332        {
3333          ModulateHCLp(percent_hue,percent_saturation,percent_brightness,
3334            &red,&green,&blue);
3335          break;
3336        }
3337        case HSBColorspace:
3338        {
3339          ModulateHSB(percent_hue,percent_saturation,percent_brightness,
3340            &red,&green,&blue);
3341          break;
3342        }
3343        case HSLColorspace:
3344        default:
3345        {
3346          ModulateHSL(percent_hue,percent_saturation,percent_brightness,
3347            &red,&green,&blue);
3348          break;
3349        }
3350        case HSVColorspace:
3351        {
3352          ModulateHSV(percent_hue,percent_saturation,percent_brightness,
3353            &red,&green,&blue);
3354          break;
3355        }
3356        case HWBColorspace:
3357        {
3358          ModulateHWB(percent_hue,percent_saturation,percent_brightness,
3359            &red,&green,&blue);
3360          break;
3361        }
3362        case LCHabColorspace:
3363        {
3364          ModulateLCHab(percent_brightness,percent_saturation,percent_hue,
3365            &red,&green,&blue);
3366          break;
3367        }
3368        case LCHColorspace:
3369        case LCHuvColorspace:
3370        {
3371          ModulateLCHuv(percent_brightness,percent_saturation,percent_hue,
3372            &red,&green,&blue);
3373          break;
3374        }
3375      }
3376      SetPixelRed(image,ClampToQuantum(red),q);
3377      SetPixelGreen(image,ClampToQuantum(green),q);
3378      SetPixelBlue(image,ClampToQuantum(blue),q);
3379      q+=GetPixelChannels(image);
3380    }
3381    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3382      status=MagickFalse;
3383    if (image->progress_monitor != (MagickProgressMonitor) NULL)
3384      {
3385        MagickBooleanType
3386          proceed;
3387
3388#if defined(MAGICKCORE_OPENMP_SUPPORT)
3389        #pragma omp critical (MagickCore_ModulateImage)
3390#endif
3391        proceed=SetImageProgress(image,ModulateImageTag,progress++,image->rows);
3392        if (proceed == MagickFalse)
3393          status=MagickFalse;
3394      }
3395  }
3396  image_view=DestroyCacheView(image_view);
3397  return(status);
3398}
3399
3400/*
3401%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3402%                                                                             %
3403%                                                                             %
3404%                                                                             %
3405%     N e g a t e I m a g e                                                   %
3406%                                                                             %
3407%                                                                             %
3408%                                                                             %
3409%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3410%
3411%  NegateImage() negates the colors in the reference image.  The grayscale
3412%  option means that only grayscale values within the image are negated.
3413%
3414%  The format of the NegateImage method is:
3415%
3416%      MagickBooleanType NegateImage(Image *image,
3417%        const MagickBooleanType grayscale,ExceptionInfo *exception)
3418%
3419%  A description of each parameter follows:
3420%
3421%    o image: the image.
3422%
3423%    o grayscale: If MagickTrue, only negate grayscale pixels within the image.
3424%
3425%    o exception: return any errors or warnings in this structure.
3426%
3427*/
3428MagickExport MagickBooleanType NegateImage(Image *image,
3429  const MagickBooleanType grayscale,ExceptionInfo *exception)
3430{
3431#define NegateImageTag  "Negate/Image"
3432
3433  CacheView
3434    *image_view;
3435
3436  MagickBooleanType
3437    status;
3438
3439  MagickOffsetType
3440    progress;
3441
3442  register ssize_t
3443    i;
3444
3445  ssize_t
3446    y;
3447
3448  assert(image != (Image *) NULL);
3449  assert(image->signature == MagickCoreSignature);
3450  if (image->debug != MagickFalse)
3451    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3452  if (image->storage_class == PseudoClass)
3453    for (i=0; i < (ssize_t) image->colors; i++)
3454    {
3455      /*
3456        Negate colormap.
3457      */
3458      if( grayscale != MagickFalse )
3459        if ((image->colormap[i].red != image->colormap[i].green) ||
3460            (image->colormap[i].green != image->colormap[i].blue))
3461          continue;
3462      if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3463        image->colormap[i].red=QuantumRange-image->colormap[i].red;
3464      if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3465        image->colormap[i].green=QuantumRange-image->colormap[i].green;
3466      if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3467        image->colormap[i].blue=QuantumRange-image->colormap[i].blue;
3468    }
3469  /*
3470    Negate image.
3471  */
3472  status=MagickTrue;
3473  progress=0;
3474  image_view=AcquireAuthenticCacheView(image,exception);
3475  if( grayscale != MagickFalse )
3476    {
3477      for (y=0; y < (ssize_t) image->rows; y++)
3478      {
3479        MagickBooleanType
3480          sync;
3481
3482        register Quantum
3483          *magick_restrict q;
3484
3485        register ssize_t
3486          x;
3487
3488        if (status == MagickFalse)
3489          continue;
3490        q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
3491          exception);
3492        if (q == (Quantum *) NULL)
3493          {
3494            status=MagickFalse;
3495            continue;
3496          }
3497        for (x=0; x < (ssize_t) image->columns; x++)
3498        {
3499          register ssize_t
3500            j;
3501
3502          if ((GetPixelReadMask(image,q) == 0) ||
3503              IsPixelGray(image,q) != MagickFalse)
3504            {
3505              q+=GetPixelChannels(image);
3506              continue;
3507            }
3508          for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
3509          {
3510            PixelChannel channel=GetPixelChannelChannel(image,j);
3511            PixelTrait traits=GetPixelChannelTraits(image,channel);
3512            if ((traits & UpdatePixelTrait) == 0)
3513              continue;
3514            q[j]=QuantumRange-q[j];
3515          }
3516          q+=GetPixelChannels(image);
3517        }
3518        sync=SyncCacheViewAuthenticPixels(image_view,exception);
3519        if (sync == MagickFalse)
3520          status=MagickFalse;
3521        if (image->progress_monitor != (MagickProgressMonitor) NULL)
3522          {
3523            MagickBooleanType
3524              proceed;
3525
3526#if defined(MAGICKCORE_OPENMP_SUPPORT)
3527            #pragma omp critical (MagickCore_NegateImage)
3528#endif
3529            proceed=SetImageProgress(image,NegateImageTag,progress++,
3530              image->rows);
3531            if (proceed == MagickFalse)
3532              status=MagickFalse;
3533          }
3534      }
3535      image_view=DestroyCacheView(image_view);
3536      return(MagickTrue);
3537    }
3538  /*
3539    Negate image.
3540  */
3541#if defined(MAGICKCORE_OPENMP_SUPPORT)
3542  #pragma omp parallel for schedule(static,4) shared(progress,status) \
3543    magick_threads(image,image,image->rows,1)
3544#endif
3545  for (y=0; y < (ssize_t) image->rows; y++)
3546  {
3547    register Quantum
3548      *magick_restrict q;
3549
3550    register ssize_t
3551      x;
3552
3553    if (status == MagickFalse)
3554      continue;
3555    q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3556    if (q == (Quantum *) NULL)
3557      {
3558        status=MagickFalse;
3559        continue;
3560      }
3561    for (x=0; x < (ssize_t) image->columns; x++)
3562    {
3563      register ssize_t
3564        j;
3565
3566      if (GetPixelReadMask(image,q) == 0)
3567        {
3568          q+=GetPixelChannels(image);
3569          continue;
3570        }
3571      for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
3572      {
3573        PixelChannel channel=GetPixelChannelChannel(image,j);
3574        PixelTrait traits=GetPixelChannelTraits(image,channel);
3575        if ((traits & UpdatePixelTrait) == 0)
3576          continue;
3577        q[j]=QuantumRange-q[j];
3578      }
3579      q+=GetPixelChannels(image);
3580    }
3581    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3582      status=MagickFalse;
3583    if (image->progress_monitor != (MagickProgressMonitor) NULL)
3584      {
3585        MagickBooleanType
3586          proceed;
3587
3588#if defined(MAGICKCORE_OPENMP_SUPPORT)
3589        #pragma omp critical (MagickCore_NegateImage)
3590#endif
3591        proceed=SetImageProgress(image,NegateImageTag,progress++,image->rows);
3592        if (proceed == MagickFalse)
3593          status=MagickFalse;
3594      }
3595  }
3596  image_view=DestroyCacheView(image_view);
3597  return(status);
3598}
3599
3600/*
3601%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3602%                                                                             %
3603%                                                                             %
3604%                                                                             %
3605%     N o r m a l i z e I m a g e                                             %
3606%                                                                             %
3607%                                                                             %
3608%                                                                             %
3609%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3610%
3611%  The NormalizeImage() method enhances the contrast of a color image by
3612%  mapping the darkest 2 percent of all pixel to black and the brightest
3613%  1 percent to white.
3614%
3615%  The format of the NormalizeImage method is:
3616%
3617%      MagickBooleanType NormalizeImage(Image *image,ExceptionInfo *exception)
3618%
3619%  A description of each parameter follows:
3620%
3621%    o image: the image.
3622%
3623%    o exception: return any errors or warnings in this structure.
3624%
3625*/
3626MagickExport MagickBooleanType NormalizeImage(Image *image,
3627  ExceptionInfo *exception)
3628{
3629  double
3630    black_point,
3631    white_point;
3632
3633  black_point=(double) image->columns*image->rows*0.0015;
3634  white_point=(double) image->columns*image->rows*0.9995;
3635  return(ContrastStretchImage(image,black_point,white_point,exception));
3636}
3637
3638/*
3639%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3640%                                                                             %
3641%                                                                             %
3642%                                                                             %
3643%     S i g m o i d a l C o n t r a s t I m a g e                             %
3644%                                                                             %
3645%                                                                             %
3646%                                                                             %
3647%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3648%
3649%  SigmoidalContrastImage() adjusts the contrast of an image with a non-linear
3650%  sigmoidal contrast algorithm.  Increase the contrast of the image using a
3651%  sigmoidal transfer function without saturating highlights or shadows.
3652%  Contrast indicates how much to increase the contrast (0 is none; 3 is
3653%  typical; 20 is pushing it); mid-point indicates where midtones fall in the
3654%  resultant image (0 is white; 50% is middle-gray; 100% is black).  Set
3655%  sharpen to MagickTrue to increase the image contrast otherwise the contrast
3656%  is reduced.
3657%
3658%  The format of the SigmoidalContrastImage method is:
3659%
3660%      MagickBooleanType SigmoidalContrastImage(Image *image,
3661%        const MagickBooleanType sharpen,const char *levels,
3662%        ExceptionInfo *exception)
3663%
3664%  A description of each parameter follows:
3665%
3666%    o image: the image.
3667%
3668%    o sharpen: Increase or decrease image contrast.
3669%
3670%    o contrast: strength of the contrast, the larger the number the more
3671%      'threshold-like' it becomes.
3672%
3673%    o midpoint: midpoint of the function as a color value 0 to QuantumRange.
3674%
3675%    o exception: return any errors or warnings in this structure.
3676%
3677*/
3678
3679/*
3680  ImageMagick 6 has a version of this function which uses LUTs.
3681*/
3682
3683/*
3684  Sigmoidal function Sigmoidal with inflexion point moved to b and "slope
3685  constant" set to a.
3686
3687  The first version, based on the hyperbolic tangent tanh, when combined with
3688  the scaling step, is an exact arithmetic clone of the the sigmoid function
3689  based on the logistic curve. The equivalence is based on the identity
3690
3691    1/(1+exp(-t)) = (1+tanh(t/2))/2
3692
3693  (http://de.wikipedia.org/wiki/Sigmoidfunktion) and the fact that the
3694  scaled sigmoidal derivation is invariant under affine transformations of
3695  the ordinate.
3696
3697  The tanh version is almost certainly more accurate and cheaper.  The 0.5
3698  factor in the argument is to clone the legacy ImageMagick behavior. The
3699  reason for making the define depend on atanh even though it only uses tanh
3700  has to do with the construction of the inverse of the scaled sigmoidal.
3701*/
3702#if defined(MAGICKCORE_HAVE_ATANH)
3703#define Sigmoidal(a,b,x) ( tanh((0.5*(a))*((x)-(b))) )
3704#else
3705#define Sigmoidal(a,b,x) ( 1.0/(1.0+exp((a)*((b)-(x)))) )
3706#endif
3707/*
3708  Scaled sigmoidal function:
3709
3710    ( Sigmoidal(a,b,x) - Sigmoidal(a,b,0) ) /
3711    ( Sigmoidal(a,b,1) - Sigmoidal(a,b,0) )
3712
3713  See http://osdir.com/ml/video.image-magick.devel/2005-04/msg00006.html and
3714  http://www.cs.dartmouth.edu/farid/downloads/tutorials/fip.pdf.  The limit
3715  of ScaledSigmoidal as a->0 is the identity, but a=0 gives a division by
3716  zero. This is fixed below by exiting immediately when contrast is small,
3717  leaving the image (or colormap) unmodified. This appears to be safe because
3718  the series expansion of the logistic sigmoidal function around x=b is
3719
3720  1/2-a*(b-x)/4+...
3721
3722  so that the key denominator s(1)-s(0) is about a/4 (a/2 with tanh).
3723*/
3724#define ScaledSigmoidal(a,b,x) (                    \
3725  (Sigmoidal((a),(b),(x))-Sigmoidal((a),(b),0.0)) / \
3726  (Sigmoidal((a),(b),1.0)-Sigmoidal((a),(b),0.0)) )
3727/*
3728  Inverse of ScaledSigmoidal, used for +sigmoidal-contrast.  Because b
3729  may be 0 or 1, the argument of the hyperbolic tangent (resp. logistic
3730  sigmoidal) may be outside of the interval (-1,1) (resp. (0,1)), even
3731  when creating a LUT from in gamut values, hence the branching.  In
3732  addition, HDRI may have out of gamut values.
3733  InverseScaledSigmoidal is not a two-sided inverse of ScaledSigmoidal:
3734  It is only a right inverse. This is unavoidable.
3735*/
3736static inline double InverseScaledSigmoidal(const double a,const double b,
3737  const double x)
3738{
3739  const double sig0=Sigmoidal(a,b,0.0);
3740  const double sig1=Sigmoidal(a,b,1.0);
3741  const double argument=(sig1-sig0)*x+sig0;
3742  const double clamped=
3743    (
3744#if defined(MAGICKCORE_HAVE_ATANH)
3745      argument < -1+MagickEpsilon
3746      ?
3747      -1+MagickEpsilon
3748      :
3749      ( argument > 1-MagickEpsilon ? 1-MagickEpsilon : argument )
3750    );
3751  return(b+(2.0/a)*atanh(clamped));
3752#else
3753      argument < MagickEpsilon
3754      ?
3755      MagickEpsilon
3756      :
3757      ( argument > 1-MagickEpsilon ? 1-MagickEpsilon : argument )
3758    );
3759  return(b-log(1.0/clamped-1.0)/a);
3760#endif
3761}
3762
3763MagickExport MagickBooleanType SigmoidalContrastImage(Image *image,
3764  const MagickBooleanType sharpen,const double contrast,const double midpoint,
3765  ExceptionInfo *exception)
3766{
3767#define SigmoidalContrastImageTag  "SigmoidalContrast/Image"
3768#define ScaledSig(x) ( ClampToQuantum(QuantumRange* \
3769  ScaledSigmoidal(contrast,QuantumScale*midpoint,QuantumScale*(x))) )
3770#define InverseScaledSig(x) ( ClampToQuantum(QuantumRange* \
3771  InverseScaledSigmoidal(contrast,QuantumScale*midpoint,QuantumScale*(x))) )
3772
3773  CacheView
3774    *image_view;
3775
3776  MagickBooleanType
3777    status;
3778
3779  MagickOffsetType
3780    progress;
3781
3782  ssize_t
3783    y;
3784
3785  /*
3786    Convenience macros.
3787  */
3788  assert(image != (Image *) NULL);
3789  assert(image->signature == MagickCoreSignature);
3790  if (image->debug != MagickFalse)
3791    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3792  /*
3793    Side effect: may clamp values unless contrast<MagickEpsilon, in which
3794    case nothing is done.
3795  */
3796  if (contrast < MagickEpsilon)
3797    return(MagickTrue);
3798  /*
3799    Sigmoidal-contrast enhance colormap.
3800  */
3801  if (image->storage_class == PseudoClass)
3802    {
3803      register ssize_t
3804        i;
3805
3806      if( sharpen != MagickFalse )
3807        for (i=0; i < (ssize_t) image->colors; i++)
3808        {
3809          if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3810            image->colormap[i].red=(MagickRealType) ScaledSig(
3811              image->colormap[i].red);
3812          if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3813            image->colormap[i].green=(MagickRealType) ScaledSig(
3814              image->colormap[i].green);
3815          if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3816            image->colormap[i].blue=(MagickRealType) ScaledSig(
3817              image->colormap[i].blue);
3818          if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
3819            image->colormap[i].alpha=(MagickRealType) ScaledSig(
3820              image->colormap[i].alpha);
3821        }
3822      else
3823        for (i=0; i < (ssize_t) image->colors; i++)
3824        {
3825          if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
3826            image->colormap[i].red=(MagickRealType) InverseScaledSig(
3827              image->colormap[i].red);
3828          if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
3829            image->colormap[i].green=(MagickRealType) InverseScaledSig(
3830              image->colormap[i].green);
3831          if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
3832            image->colormap[i].blue=(MagickRealType) InverseScaledSig(
3833              image->colormap[i].blue);
3834          if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
3835            image->colormap[i].alpha=(MagickRealType) InverseScaledSig(
3836              image->colormap[i].alpha);
3837        }
3838    }
3839  /*
3840    Sigmoidal-contrast enhance image.
3841  */
3842  status=MagickTrue;
3843  progress=0;
3844  image_view=AcquireAuthenticCacheView(image,exception);
3845#if defined(MAGICKCORE_OPENMP_SUPPORT)
3846  #pragma omp parallel for schedule(static,4) shared(progress,status) \
3847    magick_threads(image,image,image->rows,1)
3848#endif
3849  for (y=0; y < (ssize_t) image->rows; y++)
3850  {
3851    register Quantum
3852      *magick_restrict q;
3853
3854    register ssize_t
3855      x;
3856
3857    if (status == MagickFalse)
3858      continue;
3859    q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3860    if (q == (Quantum *) NULL)
3861      {
3862        status=MagickFalse;
3863        continue;
3864      }
3865    for (x=0; x < (ssize_t) image->columns; x++)
3866    {
3867      register ssize_t
3868        i;
3869
3870      if (GetPixelReadMask(image,q) == 0)
3871        {
3872          q+=GetPixelChannels(image);
3873          continue;
3874        }
3875      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3876      {
3877        PixelChannel channel=GetPixelChannelChannel(image,i);
3878        PixelTrait traits=GetPixelChannelTraits(image,channel);
3879        if ((traits & UpdatePixelTrait) == 0)
3880          continue;
3881        if( sharpen != MagickFalse )
3882          q[i]=ScaledSig(q[i]);
3883        else
3884          q[i]=InverseScaledSig(q[i]);
3885      }
3886      q+=GetPixelChannels(image);
3887    }
3888    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3889      status=MagickFalse;
3890    if (image->progress_monitor != (MagickProgressMonitor) NULL)
3891      {
3892        MagickBooleanType
3893          proceed;
3894
3895#if defined(MAGICKCORE_OPENMP_SUPPORT)
3896        #pragma omp critical (MagickCore_SigmoidalContrastImage)
3897#endif
3898        proceed=SetImageProgress(image,SigmoidalContrastImageTag,progress++,
3899          image->rows);
3900        if (proceed == MagickFalse)
3901          status=MagickFalse;
3902      }
3903  }
3904  image_view=DestroyCacheView(image_view);
3905  return(status);
3906}
3907