]> granicus.if.org Git - imagemagick/blobdiff - MagickCore/enhance.c
(no commit message)
[imagemagick] / MagickCore / enhance.c
index f9bda9d760ee71ccfa9b0bfc80190e552327abac..bf1817f97f5cd0e2188746db1bbb017734438477 100644 (file)
 %
 %  The format of the AutoGammaImage method is:
 %
-%      MagickBooleanType AutoGammaImage(Image *image)
+%      MagickBooleanType AutoGammaImage(Image *image,ExceptionInfo *exception)
 %
 %  A description of each parameter follows:
 %
 %    o image: The image to auto-level
 %
+%    o exception: return any errors or warnings in this structure.
+%
 */
-MagickExport MagickBooleanType AutoGammaImage(Image *image)
+MagickExport MagickBooleanType AutoGammaImage(Image *image,
+  ExceptionInfo *exception)
 {
-  MagickStatusType
-    status;
-
   double
     gamma,
     log_mean,
     mean,
     sans;
 
+  MagickStatusType
+    status;
+
+  register ssize_t
+    i;
+
   log_mean=log(0.5);
   if (image->sync != MagickFalse)
     {
       /*
-        Apply gamma correction equally accross all given channels
+        Apply gamma correction equally across all given channels.
       */
-      (void) GetImageMean(image,&mean,&sans,&image->exception);
+      (void) GetImageMean(image,&mean,&sans,exception);
       gamma=log(mean*QuantumScale)/log_mean;
-      return(LevelImage(image,0.0,(double) QuantumRange,gamma));
+      return(LevelImage(image,0.0,(double) QuantumRange,gamma,exception));
     }
   /*
     Auto-gamma each channel separately.
   */
   status=MagickTrue;
-  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
-    {
-      PushPixelChannelMap(image,RedChannel);
-      (void) GetImageMean(image,&mean,&sans,&image->exception);
-      gamma=log(mean*QuantumScale)/log_mean;
-      status=status && LevelImage(image,0.0,(double) QuantumRange,gamma);
-      PopPixelChannelMap(image);
-    }
-  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
-    {
-      PushPixelChannelMap(image,GreenChannel);
-      (void) GetImageMean(image,&mean,&sans,&image->exception);
-      gamma=log(mean*QuantumScale)/log_mean;
-      status=status && LevelImage(image,0.0,(double) QuantumRange,gamma);
-      PopPixelChannelMap(image);
-    }
-  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
-    {
-      PushPixelChannelMap(image,BlueChannel);
-      (void) GetImageMean(image,&mean,&sans,&image->exception);
-      gamma=log(mean*QuantumScale)/log_mean;
-      status=status && LevelImage(image,0.0,(double) QuantumRange,gamma);
-      PopPixelChannelMap(image);
-    }
-  if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
-      (image->colorspace == CMYKColorspace))
-    {
-      PushPixelChannelMap(image,BlackChannel);
-      (void) GetImageMean(image,&mean,&sans,&image->exception);
-      gamma=log(mean*QuantumScale)/log_mean;
-      status=status && LevelImage(image,0.0,(double) QuantumRange,gamma);
-      PopPixelChannelMap(image);
-    }
-  if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
-      (image->matte == MagickTrue))
-    {
-      PushPixelChannelMap(image,AlphaChannel);
-      (void) GetImageMean(image,&mean,&sans,&image->exception);
-      gamma=log(mean*QuantumScale)/log_mean;
-      status=status && LevelImage(image,0.0,(double) QuantumRange,gamma);
-      PopPixelChannelMap(image);
-    }
+  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
+  {
+    ChannelType
+      channel_mask;
+
+    PixelTrait
+      traits;
+
+    traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
+    if ((traits & UpdatePixelTrait) == 0)
+      continue;
+    channel_mask=SetPixelChannelMask(image,(ChannelType) i);
+    status=GetImageMean(image,&mean,&sans,exception);
+    gamma=log(mean*QuantumScale)/log_mean;
+    status&=LevelImage(image,0.0,(double) QuantumRange,gamma,exception);
+    (void) SetPixelChannelMask(image,channel_mask);
+    if (status == MagickFalse)
+      break;
+  }
   return(status != 0 ? MagickTrue : MagickFalse);
 }
 \f
@@ -182,16 +165,19 @@ MagickExport MagickBooleanType AutoGammaImage(Image *image)
 %
 %  The format of the LevelImage method is:
 %
-%      MagickBooleanType AutoLevelImage(Image *image)
+%      MagickBooleanType AutoLevelImage(Image *image,ExceptionInfo *exception)
 %
 %  A description of each parameter follows:
 %
 %    o image: The image to auto-level
 %
+%    o exception: return any errors or warnings in this structure.
+%
 */
-MagickExport MagickBooleanType AutoLevelImage(Image *image)
+MagickExport MagickBooleanType AutoLevelImage(Image *image,
+  ExceptionInfo *exception)
 {
-  return(MinMaxStretchImage(image,0.0,0.0));
+  return(MinMaxStretchImage(image,0.0,0.0,exception));
 }
 \f
 /*
@@ -205,14 +191,14 @@ MagickExport MagickBooleanType AutoLevelImage(Image *image)
 %                                                                             %
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %
-%  Use BrightnessContrastImage() to change the brightness and/or contrast of
-%  an image.  It converts the brightness and contrast parameters into slope
-%  and intercept and calls a polynomical function to apply to the image.
+%  BrightnessContrastImage() changes the brightness and/or contrast of an
+%  image.  It converts the brightness and contrast parameters into slope and
+%  intercept and calls a polynomical function to apply to the image.
 %
 %  The format of the BrightnessContrastImage method is:
 %
 %      MagickBooleanType BrightnessContrastImage(Image *image,
-%        const double brightness,const double contrast)
+%        const double brightness,const double contrast,ExceptionInfo *exception)
 %
 %  A description of each parameter follows:
 %
@@ -222,16 +208,18 @@ MagickExport MagickBooleanType AutoLevelImage(Image *image)
 %
 %    o contrast: the contrast percent (-100 .. 100).
 %
+%    o exception: return any errors or warnings in this structure.
+%
 */
 MagickExport MagickBooleanType BrightnessContrastImage(Image *image,
-  const double brightness,const double contrast)
+  const double brightness,const double contrast,ExceptionInfo *exception)
 {
 #define BrightnessContastImageTag  "BrightnessContast/Image"
 
   double
     alpha,
-    intercept,
     coefficients[2],
+    intercept,
     slope;
 
   MagickBooleanType
@@ -251,8 +239,183 @@ MagickExport MagickBooleanType BrightnessContrastImage(Image *image,
   intercept=brightness/100.0+((100-brightness)/200.0)*(1.0-slope);
   coefficients[0]=slope;
   coefficients[1]=intercept;
-  status=FunctionImage(image,PolynomialFunction,2,coefficients,
-    &image->exception);
+  status=FunctionImage(image,PolynomialFunction,2,coefficients,exception);
+  return(status);
+}
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%                                                                             %
+%                                                                             %
+%                                                                             %
+%     C l u t I m a g e                                                       %
+%                                                                             %
+%                                                                             %
+%                                                                             %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%  ClutImage() replaces each color value in the given image, by using it as an
+%  index to lookup a replacement color value in a Color Look UP Table in the
+%  form of an image.  The values are extracted along a diagonal of the CLUT
+%  image so either a horizontal or vertial gradient image can be used.
+%
+%  Typically this is used to either re-color a gray-scale image according to a
+%  color gradient in the CLUT image, or to perform a freeform histogram
+%  (level) adjustment according to the (typically gray-scale) gradient in the
+%  CLUT image.
+%
+%  When the 'channel' mask includes the matte/alpha transparency channel but
+%  one image has no such channel it is assumed that that image is a simple
+%  gray-scale image that will effect the alpha channel values, either for
+%  gray-scale coloring (with transparent or semi-transparent colors), or
+%  a histogram adjustment of existing alpha channel values.   If both images
+%  have matte channels, direct and normal indexing is applied, which is rarely
+%  used.
+%
+%  The format of the ClutImage method is:
+%
+%      MagickBooleanType ClutImage(Image *image,Image *clut_image,
+%        ExceptionInfo *exception)
+%
+%  A description of each parameter follows:
+%
+%    o image: the image, which is replaced by indexed CLUT values
+%
+%    o clut_image: the color lookup table image for replacement color values.
+%
+%    o channel: the channel.
+%
+%    o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType ClutImage(Image *image,const Image *clut_image,
+  ExceptionInfo *exception)
+{
+#define ClutImageTag  "Clut/Image"
+
+  CacheView
+    *clut_view,
+    *image_view;
+
+  double
+    *clut_map;
+
+  MagickBooleanType
+    status;
+
+  MagickOffsetType
+    progress;
+
+  register ssize_t
+    x;
+
+  ssize_t
+    adjust,
+    y;
+
+  assert(image != (Image *) NULL);
+  assert(image->signature == MagickSignature);
+  if (image->debug != MagickFalse)
+    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+  assert(clut_image != (Image *) NULL);
+  assert(clut_image->signature == MagickSignature);
+  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
+    return(MagickFalse);
+  clut_map=(double *) AcquireQuantumMemory(MaxMap+1UL,GetPixelChannels(image)*
+    sizeof(*clut_map));
+  if (clut_map == (double *) NULL)
+    ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+      image->filename);
+  /*
+    Clut image.
+  */
+  status=MagickTrue;
+  progress=0;
+  adjust=(ssize_t) (clut_image->interpolate == IntegerInterpolatePixel ? 0 : 1);
+  clut_view=AcquireCacheView(clut_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+  #pragma omp parallel for schedule(dynamic,4)
+#endif
+  for (x=0; x <= (ssize_t) MaxMap; x++)
+  {
+    register ssize_t
+      i;
+
+    for (i=0; i < (ssize_t) GetPixelChannels(clut_image); i++)
+      (void) InterpolatePixelChannel(clut_image,clut_view,(PixelChannel) i,
+        UndefinedInterpolatePixel,QuantumScale*x*(clut_image->columns-adjust),
+        QuantumScale*x*(clut_image->rows-adjust),clut_map+x*
+        GetPixelChannels(clut_image)+i,exception);
+  }
+  clut_view=DestroyCacheView(clut_view);
+  image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+  #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+  for (y=0; y < (ssize_t) image->rows; y++)
+  {
+    register Quantum
+      *restrict q;
+
+    register ssize_t
+      x;
+
+    if (status == MagickFalse)
+      continue;
+    q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
+    if (q == (Quantum *) NULL)
+      {
+        status=MagickFalse;
+        continue;
+      }
+    for (x=0; x < (ssize_t) image->columns; x++)
+    {
+      register ssize_t
+        i;
+
+      for (i=0; i < (ssize_t) GetPixelChannels(clut_image); i++)
+      {
+        PixelChannel
+          channel;
+
+        PixelTrait 
+          clut_traits,
+          traits;
+
+        clut_traits=GetPixelChannelMapTraits(clut_image,(PixelChannel) i);
+        if (clut_traits == UndefinedPixelTrait)
+          continue;
+        channel=GetPixelChannelMapChannel(clut_image,(PixelChannel) i);
+        traits=GetPixelChannelMapTraits(clut_image,channel);
+        if (traits == UndefinedPixelTrait)
+          continue;
+        if ((traits & UpdatePixelTrait) == 0)
+          continue;
+        q[channel]=ClampToQuantum(clut_map[ScaleQuantumToMap(q[channel])*
+          GetPixelChannels(clut_image)+channel]);
+      }
+      q+=GetPixelChannels(image);
+    }
+    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+      status=MagickFalse;
+    if (image->progress_monitor != (MagickProgressMonitor) NULL)
+      {
+        MagickBooleanType
+          proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+  #pragma omp critical (MagickCore_ClutImage)
+#endif
+        proceed=SetImageProgress(image,ClutImageTag,progress++,image->rows);
+        if (proceed == MagickFalse)
+          status=MagickFalse;
+      }
+  }
+  image_view=DestroyCacheView(image_view);
+  clut_map=(double *) RelinquishMagickMemory(clut_map);
+  if ((clut_image->matte != MagickFalse) &&
+      ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0))
+    (void) SetImageAlphaChannel(image,ActivateAlphaChannel,exception);
   return(status);
 }
 \f
@@ -290,7 +453,7 @@ MagickExport MagickBooleanType BrightnessContrastImage(Image *image,
 %  The format of the ColorDecisionListImage method is:
 %
 %      MagickBooleanType ColorDecisionListImage(Image *image,
-%        const char *color_correction_collection)
+%        const char *color_correction_collection,ExceptionInfo *exception)
 %
 %  A description of each parameter follows:
 %
@@ -298,9 +461,11 @@ MagickExport MagickBooleanType BrightnessContrastImage(Image *image,
 %
 %    o color_correction_collection: the color correction collection in XML.
 %
+%    o exception: return any errors or warnings in this structure.
+%
 */
 MagickExport MagickBooleanType ColorDecisionListImage(Image *image,
-  const char *color_correction_collection)
+  const char *color_correction_collection,ExceptionInfo *exception)
 {
 #define ColorDecisionListCorrectImageTag  "ColorDecisionList/Image"
 
@@ -336,9 +501,6 @@ MagickExport MagickBooleanType ColorDecisionListImage(Image *image,
     *content,
     *p;
 
-  ExceptionInfo
-    *exception;
-
   MagickBooleanType
     status;
 
@@ -369,7 +531,7 @@ MagickExport MagickBooleanType ColorDecisionListImage(Image *image,
     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   if (color_correction_collection == (const char *) NULL)
     return(MagickFalse);
-  ccc=NewXMLTree((const char *) color_correction_collection,&image->exception);
+  ccc=NewXMLTree((const char *) color_correction_collection,exception);
   if (ccc == (XMLTreeInfo *) NULL)
     return(MagickFalse);
   cc=GetXMLTreeChild(ccc,"ColorCorrection");
@@ -547,14 +709,14 @@ MagickExport MagickBooleanType ColorDecisionListImage(Image *image,
 #endif
   for (i=0; i <= (ssize_t) MaxMap; i++)
   {
-    cdl_map[i].red=ClampToQuantum((MagickRealType) ScaleMapToQuantum((
-      MagickRealType) (MaxMap*(pow(color_correction.red.slope*i/MaxMap+
+    cdl_map[i].red=ClampToQuantum((MagickRealType) ScaleMapToQuantum(
+      (MagickRealType) (MaxMap*(pow(color_correction.red.slope*i/MaxMap+
       color_correction.red.offset,color_correction.red.power)))));
-    cdl_map[i].green=ClampToQuantum((MagickRealType) ScaleMapToQuantum((
-      MagickRealType) (MaxMap*(pow(color_correction.green.slope*i/MaxMap+
+    cdl_map[i].green=ClampToQuantum((MagickRealType) ScaleMapToQuantum(
+      (MagickRealType) (MaxMap*(pow(color_correction.green.slope*i/MaxMap+
       color_correction.green.offset,color_correction.green.power)))));
-    cdl_map[i].blue=ClampToQuantum((MagickRealType) ScaleMapToQuantum((
-      MagickRealType) (MaxMap*(pow(color_correction.blue.slope*i/MaxMap+
+    cdl_map[i].blue=ClampToQuantum((MagickRealType) ScaleMapToQuantum(
+      (MagickRealType) (MaxMap*(pow(color_correction.blue.slope*i/MaxMap+
       color_correction.blue.offset,color_correction.blue.power)))));
   }
   if (image->storage_class == PseudoClass)
@@ -568,195 +730,34 @@ MagickExport MagickBooleanType ColorDecisionListImage(Image *image,
       for (i=0; i < (ssize_t) image->colors; i++)
       {
         double
-          luma;
-
-        luma=0.2126*image->colormap[i].red+0.7152*image->colormap[i].green+
-          0.0722*image->colormap[i].blue;
-        image->colormap[i].red=ClampToQuantum(luma+color_correction.saturation*
-          cdl_map[ScaleQuantumToMap(image->colormap[i].red)].red-luma);
-        image->colormap[i].green=ClampToQuantum(luma+
-          color_correction.saturation*cdl_map[ScaleQuantumToMap(
-          image->colormap[i].green)].green-luma);
-        image->colormap[i].blue=ClampToQuantum(luma+color_correction.saturation*
-          cdl_map[ScaleQuantumToMap(image->colormap[i].blue)].blue-luma);
-      }
-    }
-  /*
-    Apply transfer function to image.
-  */
-  status=MagickTrue;
-  progress=0;
-  exception=(&image->exception);
-  image_view=AcquireCacheView(image);
-#if defined(MAGICKCORE_OPENMP_SUPPORT)
-  #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
-#endif
-  for (y=0; y < (ssize_t) image->rows; y++)
-  {
-    double
-      luma;
-
-    register Quantum
-      *restrict q;
-
-    register ssize_t
-      x;
-
-    if (status == MagickFalse)
-      continue;
-    q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
-    if (q == (const Quantum *) NULL)
-      {
-        status=MagickFalse;
-        continue;
-      }
-    for (x=0; x < (ssize_t) image->columns; x++)
-    {
-      luma=0.2126*GetPixelRed(image,q)+0.7152*GetPixelGreen(image,q)+0.0722*
-        GetPixelBlue(image,q);
-      SetPixelRed(image,ClampToQuantum(luma+color_correction.saturation*
-        (cdl_map[ScaleQuantumToMap(GetPixelRed(image,q))].red-luma)),q);
-      SetPixelGreen(image,ClampToQuantum(luma+color_correction.saturation*
-        (cdl_map[ScaleQuantumToMap(GetPixelGreen(image,q))].green-luma)),q);
-      SetPixelBlue(image,ClampToQuantum(luma+color_correction.saturation*
-        (cdl_map[ScaleQuantumToMap(GetPixelBlue(image,q))].blue-luma)),q);
-      q+=GetPixelChannels(image);
-    }
-    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
-      status=MagickFalse;
-    if (image->progress_monitor != (MagickProgressMonitor) NULL)
-      {
-        MagickBooleanType
-          proceed;
-
-#if defined(MAGICKCORE_OPENMP_SUPPORT)
-  #pragma omp critical (MagickCore_ColorDecisionListImageChannel)
-#endif
-        proceed=SetImageProgress(image,ColorDecisionListCorrectImageTag,
-          progress++,image->rows);
-        if (proceed == MagickFalse)
-          status=MagickFalse;
-      }
-  }
-  image_view=DestroyCacheView(image_view);
-  cdl_map=(PixelPacket *) RelinquishMagickMemory(cdl_map);
-  return(status);
-}
-\f
-/*
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%                                                                             %
-%                                                                             %
-%                                                                             %
-%     C l u t I m a g e                                                       %
-%                                                                             %
-%                                                                             %
-%                                                                             %
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%
-%  ClutImage() replaces each color value in the given image, by using it as an
-%  index to lookup a replacement color value in a Color Look UP Table in the
-%  form of an image.  The values are extracted along a diagonal of the CLUT
-%  image so either a horizontal or vertial gradient image can be used.
-%
-%  Typically this is used to either re-color a gray-scale image according to a
-%  color gradient in the CLUT image, or to perform a freeform histogram
-%  (level) adjustment according to the (typically gray-scale) gradient in the
-%  CLUT image.
-%
-%  When the 'channel' mask includes the matte/alpha transparency channel but
-%  one image has no such channel it is assumed that that image is a simple
-%  gray-scale image that will effect the alpha channel values, either for
-%  gray-scale coloring (with transparent or semi-transparent colors), or
-%  a histogram adjustment of existing alpha channel values.   If both images
-%  have matte channels, direct and normal indexing is applied, which is rarely
-%  used.
-%
-%  The format of the ClutImage method is:
-%
-%      MagickBooleanType ClutImage(Image *image,Image *clut_image)
-%
-%  A description of each parameter follows:
-%
-%    o image: the image, which is replaced by indexed CLUT values
-%
-%    o clut_image: the color lookup table image for replacement color values.
-%
-%    o channel: the channel.
-%
-*/
-MagickExport MagickBooleanType ClutImage(Image *image,const Image *clut_image)
-{
-#define ClampAlphaPixelChannel(pixel) ClampToQuantum((pixel)->alpha)
-#define ClampBlackPixelChannel(pixel) ClampToQuantum((pixel)->black)
-#define ClampBluePixelChannel(pixel) ClampToQuantum((pixel)->blue)
-#define ClampGreenPixelChannel(pixel) ClampToQuantum((pixel)->green)
-#define ClampRedPixelChannel(pixel) ClampToQuantum((pixel)->red)
-#define ClutImageTag  "Clut/Image"
-
-  CacheView
-    *clut_view,
-    *image_view;
-
-  ExceptionInfo
-    *exception;
-
-  MagickBooleanType
-    status;
-
-  MagickOffsetType
-    progress;
-
-  PixelInfo
-    *clut_map;
-
-  register ssize_t
-    i;
-
-  ssize_t
-    adjust,
-    y;
-
-  assert(image != (Image *) NULL);
-  assert(image->signature == MagickSignature);
-  if (image->debug != MagickFalse)
-    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
-  assert(clut_image != (Image *) NULL);
-  assert(clut_image->signature == MagickSignature);
-  if (SetImageStorageClass(image,DirectClass) == MagickFalse)
-    return(MagickFalse);
-  clut_map=(PixelInfo *) AcquireQuantumMemory(MaxMap+1UL,
-    sizeof(*clut_map));
-  if (clut_map == (PixelInfo *) NULL)
-    ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
-      image->filename);
+          luma;
+
+        luma=0.2126*image->colormap[i].red+0.7152*image->colormap[i].green+
+          0.0722*image->colormap[i].blue;
+        image->colormap[i].red=ClampToQuantum(luma+
+          color_correction.saturation*cdl_map[ScaleQuantumToMap(
+          image->colormap[i].red)].red-luma);
+        image->colormap[i].green=ClampToQuantum(luma+
+          color_correction.saturation*cdl_map[ScaleQuantumToMap(
+          image->colormap[i].green)].green-luma);
+        image->colormap[i].blue=ClampToQuantum(luma+
+          color_correction.saturation*cdl_map[ScaleQuantumToMap(
+          image->colormap[i].blue)].blue-luma);
+      }
+    }
   /*
-    Clut image.
+    Apply transfer function to image.
   */
   status=MagickTrue;
   progress=0;
-  adjust=(ssize_t) (clut_image->interpolate == IntegerInterpolatePixel ? 0 : 1);
-  exception=(&image->exception);
-  clut_view=AcquireCacheView(clut_image);
-#if defined(MAGICKCORE_OPENMP_SUPPORT)
-  #pragma omp parallel for schedule(dynamic,4)
-#endif
-  for (i=0; i <= (ssize_t) MaxMap; i++)
-  {
-    GetPixelInfo(clut_image,clut_map+i);
-    (void) InterpolatePixelInfo(clut_image,clut_view,
-      UndefinedInterpolatePixel,QuantumScale*i*(clut_image->columns-adjust),
-      QuantumScale*i*(clut_image->rows-adjust),clut_map+i,exception);
-  }
-  clut_view=DestroyCacheView(clut_view);
   image_view=AcquireCacheView(image);
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
 #endif
   for (y=0; y < (ssize_t) image->rows; y++)
   {
-    PixelInfo
-      pixel;
+    double
+      luma;
 
     register Quantum
       *restrict q;
@@ -772,36 +773,16 @@ MagickExport MagickBooleanType ClutImage(Image *image,const Image *clut_image)
         status=MagickFalse;
         continue;
       }
-    GetPixelInfo(image,&pixel);
     for (x=0; x < (ssize_t) image->columns; x++)
     {
-      SetPixelInfo(image,q,&pixel);
-      if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
-        SetPixelRed(image,ClampRedPixelChannel(clut_map+
-          ScaleQuantumToMap(GetPixelRed(image,q))),q);
-      if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
-        SetPixelGreen(image,ClampGreenPixelChannel(clut_map+
-          ScaleQuantumToMap(GetPixelGreen(image,q))),q);
-      if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
-        SetPixelBlue(image,ClampBluePixelChannel(clut_map+
-          ScaleQuantumToMap(GetPixelBlue(image,q))),q);
-      if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
-          (image->colorspace == CMYKColorspace))
-        SetPixelBlack(image,ClampBlackPixelChannel(clut_map+
-          ScaleQuantumToMap(GetPixelBlack(image,q))),q);
-      if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
-        {
-          if (clut_image->matte == MagickFalse)
-            SetPixelAlpha(image,GetPixelInfoIntensity(clut_map+
-              ScaleQuantumToMap((Quantum) GetPixelAlpha(image,q))),q);
-          else
-            if (image->matte == MagickFalse)
-              SetPixelAlpha(image,ClampAlphaPixelChannel(clut_map+
-                ScaleQuantumToMap((Quantum) GetPixelInfoIntensity(&pixel))),q);
-            else
-              SetPixelAlpha(image,ClampAlphaPixelChannel(clut_map+
-                ScaleQuantumToMap(GetPixelAlpha(image,q))),q);
-        }
+      luma=0.2126*GetPixelRed(image,q)+0.7152*GetPixelGreen(image,q)+0.0722*
+        GetPixelBlue(image,q);
+      SetPixelRed(image,ClampToQuantum(luma+color_correction.saturation*
+        (cdl_map[ScaleQuantumToMap(GetPixelRed(image,q))].red-luma)),q);
+      SetPixelGreen(image,ClampToQuantum(luma+color_correction.saturation*
+        (cdl_map[ScaleQuantumToMap(GetPixelGreen(image,q))].green-luma)),q);
+      SetPixelBlue(image,ClampToQuantum(luma+color_correction.saturation*
+        (cdl_map[ScaleQuantumToMap(GetPixelBlue(image,q))].blue-luma)),q);
       q+=GetPixelChannels(image);
     }
     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
@@ -812,18 +793,16 @@ MagickExport MagickBooleanType ClutImage(Image *image,const Image *clut_image)
           proceed;
 
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
-  #pragma omp critical (MagickCore_ClutImage)
+  #pragma omp critical (MagickCore_ColorDecisionListImageChannel)
 #endif
-        proceed=SetImageProgress(image,ClutImageTag,progress++,image->rows);
+        proceed=SetImageProgress(image,ColorDecisionListCorrectImageTag,
+          progress++,image->rows);
         if (proceed == MagickFalse)
           status=MagickFalse;
       }
   }
   image_view=DestroyCacheView(image_view);
-  clut_map=(PixelInfo *) RelinquishMagickMemory(clut_map);
-  if ((clut_image->matte != MagickFalse) &&
-      ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0))
-    (void) SetImageAlphaChannel(image,ActivateAlphaChannel);
+  cdl_map=(PixelPacket *) RelinquishMagickMemory(cdl_map);
   return(status);
 }
 \f
@@ -845,7 +824,7 @@ MagickExport MagickBooleanType ClutImage(Image *image,const Image *clut_image)
 %  The format of the ContrastImage method is:
 %
 %      MagickBooleanType ContrastImage(Image *image,
-%        const MagickBooleanType sharpen)
+%        const MagickBooleanType sharpen,ExceptionInfo *exception)
 %
 %  A description of each parameter follows:
 %
@@ -853,6 +832,8 @@ MagickExport MagickBooleanType ClutImage(Image *image,const Image *clut_image)
 %
 %    o sharpen: Increase or decrease image contrast.
 %
+%    o exception: return any errors or warnings in this structure.
+%
 */
 
 static void Contrast(const int sign,Quantum *red,Quantum *green,Quantum *blue)
@@ -883,16 +864,13 @@ static void Contrast(const int sign,Quantum *red,Quantum *green,Quantum *blue)
 }
 
 MagickExport MagickBooleanType ContrastImage(Image *image,
-  const MagickBooleanType sharpen)
+  const MagickBooleanType sharpen,ExceptionInfo *exception)
 {
 #define ContrastImageTag  "Contrast/Image"
 
   CacheView
     *image_view;
 
-  ExceptionInfo
-    *exception;
-
   int
     sign;
 
@@ -927,7 +905,6 @@ MagickExport MagickBooleanType ContrastImage(Image *image,
   */
   status=MagickTrue;
   progress=0;
-  exception=(&image->exception);
   image_view=AcquireCacheView(image);
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
@@ -994,17 +971,17 @@ MagickExport MagickBooleanType ContrastImage(Image *image,
 %                                                                             %
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %
-%  The ContrastStretchImage() is a simple image enhancement technique that
-%  attempts to improve the contrast in an image by `stretching' the range of
-%  intensity values it contains to span a desired range of values. It differs
-%  from the more sophisticated histogram equalization in that it can only
-%  apply %  a linear scaling function to the image pixel values.  As a result
-%  the `enhancement' is less harsh.
+%  ContrastStretchImage() is a simple image enhancement technique that attempts
+%  to improve the contrast in an image by `stretching' the range of intensity
+%  values it contains to span a desired range of values. It differs from the
+%  more sophisticated histogram equalization in that it can only apply a
+%  linear scaling function to the image pixel values.  As a result the
+%  `enhancement' is less harsh.
 %
 %  The format of the ContrastStretchImage method is:
 %
 %      MagickBooleanType ContrastStretchImage(Image *image,
-%        const char *levels)
+%        const char *levels,ExceptionInfo *exception)
 %
 %  A description of each parameter follows:
 %
@@ -1017,9 +994,11 @@ MagickExport MagickBooleanType ContrastImage(Image *image,
 %    o levels: Specify the levels where the black and white points have the
 %      range of 0 to number-of-pixels (e.g. 1%, 10x90%, etc.).
 %
+%    o exception: return any errors or warnings in this structure.
+%
 */
 MagickExport MagickBooleanType ContrastStretchImage(Image *image,
-  const double black_point,const double white_point)
+  const double black_point,const double white_point,ExceptionInfo *exception)
 {
 #define MaxRange(color)  ((MagickRealType) ScaleQuantumToMap((Quantum) (color)))
 #define ContrastStretchImageTag  "ContrastStretch/Image"
@@ -1027,23 +1006,17 @@ MagickExport MagickBooleanType ContrastStretchImage(Image *image,
   CacheView
     *image_view;
 
-  double
-    intensity;
-
-  ExceptionInfo
-    *exception;
-
   MagickBooleanType
     status;
 
   MagickOffsetType
     progress;
 
-  PixelInfo
-    black,
+  double
+    *black,
     *histogram,
     *stretch_map,
-    white;
+    *white;
 
   register ssize_t
     i;
@@ -1058,20 +1031,32 @@ MagickExport MagickBooleanType ContrastStretchImage(Image *image,
   assert(image->signature == MagickSignature);
   if (image->debug != MagickFalse)
     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
-  histogram=(PixelInfo *) AcquireQuantumMemory(MaxMap+1UL,
-    sizeof(*histogram));
-  stretch_map=(PixelInfo *) AcquireQuantumMemory(MaxMap+1UL,
-    sizeof(*stretch_map));
-  if ((histogram == (PixelInfo *) NULL) ||
-      (stretch_map == (PixelInfo *) NULL))
-    ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
-      image->filename);
+  black=(double *) AcquireQuantumMemory(GetPixelChannels(image),sizeof(*black));
+  white=(double *) AcquireQuantumMemory(GetPixelChannels(image),sizeof(*white));
+  histogram=(double *) AcquireQuantumMemory(MaxMap+1UL,
+    GetPixelChannels(image)*sizeof(*histogram));
+  stretch_map=(double *) AcquireQuantumMemory(MaxMap+1UL,
+    GetPixelChannels(image)*sizeof(*stretch_map));
+  if ((black == (double *) NULL) || (white == (double *) NULL) ||
+      (histogram == (double *) NULL) || (stretch_map == (double *) NULL))
+    {
+      if (stretch_map != (double *) NULL)
+        stretch_map=(double *) RelinquishMagickMemory(stretch_map);
+      if (histogram != (double *) NULL)
+        histogram=(double *) RelinquishMagickMemory(histogram);
+      if (white != (double *) NULL)
+        white=(double *) RelinquishMagickMemory(white);
+      if (black != (double *) NULL)
+        black=(double *) RelinquishMagickMemory(black);
+      ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+        image->filename);
+    }
   /*
     Form histogram.
   */
   status=MagickTrue;
-  exception=(&image->exception);
-  (void) ResetMagickMemory(histogram,0,(MaxMap+1)*sizeof(*histogram));
+  (void) ResetMagickMemory(histogram,0,(MaxMap+1)*GetPixelChannels(image)*
+    sizeof(*histogram));
   image_view=AcquireCacheView(image);
   for (y=0; y < (ssize_t) image->rows; y++)
   {
@@ -1089,264 +1074,135 @@ MagickExport MagickBooleanType ContrastStretchImage(Image *image,
         status=MagickFalse;
         continue;
       }
-    if (image->sync != MagickFalse)
-      for (x=0; x < (ssize_t) image->columns; x++)
-      {
-        Quantum
-          intensity;
-
-        intensity=GetPixelIntensity(image,p);
-        histogram[ScaleQuantumToMap(intensity)].red++;
-        histogram[ScaleQuantumToMap(intensity)].green++;
-        histogram[ScaleQuantumToMap(intensity)].blue++;
-        histogram[ScaleQuantumToMap(intensity)].black++;
-        p+=GetPixelChannels(image);
-      }
-    else
-      for (x=0; x < (ssize_t) image->columns; x++)
+    for (x=0; x < (ssize_t) image->columns; x++)
+    {
+      register ssize_t
+        i;
+
+      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
       {
-        if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
-          histogram[ScaleQuantumToMap(GetPixelRed(image,p))].red++;
-        if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
-          histogram[ScaleQuantumToMap(GetPixelGreen(image,p))].green++;
-        if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
-          histogram[ScaleQuantumToMap(GetPixelBlue(image,p))].blue++;
-        if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
-            (image->colorspace == CMYKColorspace))
-          histogram[ScaleQuantumToMap(GetPixelBlack(image,p))].black++;
-        if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
-          histogram[ScaleQuantumToMap(GetPixelAlpha(image,p))].alpha++;
-        p+=GetPixelChannels(image);
+        PixelTrait
+          traits;
+
+        traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
+        if ((traits & UpdatePixelTrait) == 0)
+          continue;
+        histogram[GetPixelChannels(image)*ScaleQuantumToMap(p[i])+i]++;
       }
+      p+=GetPixelChannels(image);
+    }
   }
   /*
     Find the histogram boundaries by locating the black/white levels.
   */
-  black.red=0.0;
-  white.red=MaxRange(QuantumRange);
-  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
-    {
-      intensity=0.0;
-      for (i=0; i <= (ssize_t) MaxMap; i++)
-      {
-        intensity+=histogram[i].red;
-        if (intensity > black_point)
-          break;
-      }
-      black.red=(MagickRealType) i;
-      intensity=0.0;
-      for (i=(ssize_t) MaxMap; i != 0; i--)
-      {
-        intensity+=histogram[i].red;
-        if (intensity > ((double) image->columns*image->rows-white_point))
-          break;
-      }
-      white.red=(MagickRealType) i;
-    }
-  black.green=0.0;
-  white.green=MaxRange(QuantumRange);
-  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
-    {
-      intensity=0.0;
-      for (i=0; i <= (ssize_t) MaxMap; i++)
-      {
-        intensity+=histogram[i].green;
-        if (intensity > black_point)
-          break;
-      }
-      black.green=(MagickRealType) i;
-      intensity=0.0;
-      for (i=(ssize_t) MaxMap; i != 0; i--)
-      {
-        intensity+=histogram[i].green;
-        if (intensity > ((double) image->columns*image->rows-white_point))
-          break;
-      }
-      white.green=(MagickRealType) i;
-    }
-  black.blue=0.0;
-  white.blue=MaxRange(QuantumRange);
-  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
-    {
-      intensity=0.0;
-      for (i=0; i <= (ssize_t) MaxMap; i++)
-      {
-        intensity+=histogram[i].blue;
-        if (intensity > black_point)
-          break;
-      }
-      black.blue=(MagickRealType) i;
-      intensity=0.0;
-      for (i=(ssize_t) MaxMap; i != 0; i--)
-      {
-        intensity+=histogram[i].blue;
-        if (intensity > ((double) image->columns*image->rows-white_point))
-          break;
-      }
-      white.blue=(MagickRealType) i;
-    }
-  black.alpha=0.0;
-  white.alpha=MaxRange(QuantumRange);
-  if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+  #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
+  {
+    double
+      intensity;
+
+    register ssize_t
+      j;
+
+    black[i]=0.0;
+    white[i]=MaxRange(QuantumRange);
+    intensity=0.0;
+    for (j=0; j <= (ssize_t) MaxMap; j++)
     {
-      intensity=0.0;
-      for (i=0; i <= (ssize_t) MaxMap; i++)
-      {
-        intensity+=histogram[i].alpha;
-        if (intensity > black_point)
-          break;
-      }
-      black.alpha=(MagickRealType) i;
-      intensity=0.0;
-      for (i=(ssize_t) MaxMap; i != 0; i--)
-      {
-        intensity+=histogram[i].alpha;
-        if (intensity > ((double) image->columns*image->rows-white_point))
-          break;
-      }
-      white.alpha=(MagickRealType) i;
+      intensity+=histogram[GetPixelChannels(image)*j+i];
+      if (intensity > black_point)
+        break;
     }
-  black.black=0.0;
-  white.black=MaxRange(QuantumRange);
-  if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) && (image->colorspace == CMYKColorspace))
+    black[i]=(MagickRealType) j;
+    intensity=0.0;
+    for (j=(ssize_t) MaxMap; j != 0; j--)
     {
-      intensity=0.0;
-      for (i=0; i <= (ssize_t) MaxMap; i++)
-      {
-        intensity+=histogram[i].black;
-        if (intensity > black_point)
-          break;
-      }
-      black.black=(MagickRealType) i;
-      intensity=0.0;
-      for (i=(ssize_t) MaxMap; i != 0; i--)
-      {
-        intensity+=histogram[i].black;
-        if (intensity > ((double) image->columns*image->rows-white_point))
-          break;
-      }
-      white.black=(MagickRealType) i;
+      intensity+=histogram[GetPixelChannels(image)*j+i];
+      if (intensity > ((double) image->columns*image->rows-white_point))
+        break;
     }
-  histogram=(PixelInfo *) RelinquishMagickMemory(histogram);
+    white[i]=(MagickRealType) j;
+  }
+  histogram=(double *) RelinquishMagickMemory(histogram);
   /*
     Stretch the histogram to create the stretched image mapping.
   */
-  (void) ResetMagickMemory(stretch_map,0,(MaxMap+1)*sizeof(*stretch_map));
+  (void) ResetMagickMemory(stretch_map,0,(MaxMap+1)*GetPixelChannels(image)*
+    sizeof(*stretch_map));
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
 #endif
-  for (i=0; i <= (ssize_t) MaxMap; i++)
+  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   {
-    if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
-      {
-        if (i < (ssize_t) black.red)
-          stretch_map[i].red=0.0;
-        else
-          if (i > (ssize_t) white.red)
-            stretch_map[i].red=(MagickRealType) QuantumRange;
-          else
-            if (black.red != white.red)
-              stretch_map[i].red=(MagickRealType) ScaleMapToQuantum(
-                (MagickRealType) (MaxMap*(i-black.red)/(white.red-black.red)));
-      }
-    if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
-      {
-        if (i < (ssize_t) black.green)
-          stretch_map[i].green=0.0;
-        else
-          if (i > (ssize_t) white.green)
-            stretch_map[i].green=(MagickRealType) QuantumRange;
-          else
-            if (black.green != white.green)
-              stretch_map[i].green=(MagickRealType) ScaleMapToQuantum(
-                (MagickRealType) (MaxMap*(i-black.green)/(white.green-
-                black.green)));
-      }
-    if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
-      {
-        if (i < (ssize_t) black.blue)
-          stretch_map[i].blue=0.0;
-        else
-          if (i > (ssize_t) white.blue)
-            stretch_map[i].blue=(MagickRealType) QuantumRange;
-          else
-            if (black.blue != white.blue)
-              stretch_map[i].blue=(MagickRealType) ScaleMapToQuantum(
-                (MagickRealType) (MaxMap*(i-black.blue)/(white.blue-
-                black.blue)));
-      }
-    if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
-      {
-        if (i < (ssize_t) black.alpha)
-          stretch_map[i].alpha=0.0;
-        else
-          if (i > (ssize_t) white.alpha)
-            stretch_map[i].alpha=(MagickRealType) QuantumRange;
-          else
-            if (black.alpha != white.alpha)
-              stretch_map[i].alpha=(MagickRealType) ScaleMapToQuantum(
-                (MagickRealType) (MaxMap*(i-black.alpha)/(white.alpha-
-                black.alpha)));
-      }
-    if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
-        (image->colorspace == CMYKColorspace))
-      {
-        if (i < (ssize_t) black.black)
-          stretch_map[i].black=0.0;
+    register ssize_t
+      j;
+
+    for (j=0; j <= (ssize_t) MaxMap; j++)
+    {
+      if (j < (ssize_t) black[i])
+        stretch_map[GetPixelChannels(image)*j+i]=0.0;
+      else
+        if (j > (ssize_t) white[i])
+          stretch_map[GetPixelChannels(image)*j+i]=(MagickRealType)
+            QuantumRange;
         else
-          if (i > (ssize_t) white.black)
-            stretch_map[i].black=(MagickRealType) QuantumRange;
-          else
-            if (black.black != white.black)
-              stretch_map[i].black=(MagickRealType) ScaleMapToQuantum(
-                (MagickRealType) (MaxMap*(i-black.black)/(white.black-
-                black.black)));
-      }
+          if (black[i] != white[i])
+            stretch_map[GetPixelChannels(image)*j+i]=(MagickRealType)
+              ScaleMapToQuantum((MagickRealType) (MaxMap*(j-black[i])/
+              (white[i]-black[i])));
+    }
   }
-  /*
-    Stretch the image.
-  */
-  if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) || (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
-      (image->colorspace == CMYKColorspace)))
-    image->storage_class=DirectClass;
   if (image->storage_class == PseudoClass)
     {
+      register ssize_t
+        j;
+
       /*
-        Stretch colormap.
+        Stretch-contrast colormap.
       */
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
 #endif
-      for (i=0; i < (ssize_t) image->colors; i++)
+      for (j=0; j < (ssize_t) image->colors; j++)
       {
         if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
           {
-            if (black.red != white.red)
+            i=GetPixelChannelMapChannel(image,RedPixelChannel);
+            if (black[i] != white[i])
               image->colormap[i].red=ClampToQuantum(stretch_map[
-                ScaleQuantumToMap(image->colormap[i].red)].red);
+                GetPixelChannels(image)*ScaleQuantumToMap(
+                image->colormap[i].red)]+i);
           }
         if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
           {
-            if (black.green != white.green)
-              image->colormap[i].green=ClampToQuantum(stretch_map[
-                ScaleQuantumToMap(image->colormap[i].green)].green);
+            i=GetPixelChannelMapChannel(image,GreenPixelChannel);
+            if (black[i] != white[i])
+              image->colormap[i].red=ClampToQuantum(stretch_map[
+                GetPixelChannels(image)*ScaleQuantumToMap(
+                image->colormap[i].red)]+i);
           }
         if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
           {
-            if (black.blue != white.blue)
-              image->colormap[i].blue=ClampToQuantum(stretch_map[
-                ScaleQuantumToMap(image->colormap[i].blue)].blue);
+            i=GetPixelChannelMapChannel(image,BluePixelChannel);
+            if (black[i] != white[i])
+              image->colormap[i].red=ClampToQuantum(stretch_map[
+                GetPixelChannels(image)*ScaleQuantumToMap(
+                image->colormap[i].red)]+i);
           }
         if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
           {
-            if (black.alpha != white.alpha)
-              image->colormap[i].alpha=ClampToQuantum(stretch_map[
-                ScaleQuantumToMap(image->colormap[i].alpha)].alpha);
+            i=GetPixelChannelMapChannel(image,AlphaPixelChannel);
+            if (black[i] != white[i])
+              image->colormap[i].red=ClampToQuantum(stretch_map[
+                GetPixelChannels(image)*ScaleQuantumToMap(
+                image->colormap[i].red)]+i);
           }
       }
     }
   /*
-    Stretch image.
+    Stretch-contrast image.
   */
   status=MagickTrue;
   progress=0;
@@ -1371,37 +1227,21 @@ MagickExport MagickBooleanType ContrastStretchImage(Image *image,
       }
     for (x=0; x < (ssize_t) image->columns; x++)
     {
-      if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
-        {
-          if (black.red != white.red)
-            SetPixelRed(image,ClampToQuantum(stretch_map[ScaleQuantumToMap(
-              GetPixelRed(image,q))].red),q);
-        }
-      if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
-        {
-          if (black.green != white.green)
-            SetPixelGreen(image,ClampToQuantum(stretch_map[ScaleQuantumToMap(
-              GetPixelGreen(image,q))].green),q);
-        }
-      if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
-        {
-          if (black.blue != white.blue)
-            SetPixelBlue(image,ClampToQuantum(stretch_map[ScaleQuantumToMap(
-              GetPixelBlue(image,q))].blue),q);
-        }
-      if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
-          (image->colorspace == CMYKColorspace))
-        {
-          if (black.black != white.black)
-            SetPixelBlack(image,ClampToQuantum(stretch_map[ScaleQuantumToMap(
-              GetPixelBlack(image,q))].black),q);
-        }
-      if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
-        {
-          if (black.alpha != white.alpha)
-            SetPixelAlpha(image,ClampToQuantum(stretch_map[ScaleQuantumToMap(
-              GetPixelAlpha(image,q))].alpha),q);
-        }
+      register ssize_t
+        i;
+
+      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
+      {
+        PixelTrait
+          traits;
+
+        traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
+        if ((traits & UpdatePixelTrait) == 0)
+          continue;
+        if (black[i] != white[i])
+          q[i]=ClampToQuantum(stretch_map[GetPixelChannels(image)*
+            ScaleQuantumToMap(q[i])+i]);
+      }
       q+=GetPixelChannels(image);
     }
     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
@@ -1421,7 +1261,9 @@ MagickExport MagickBooleanType ContrastStretchImage(Image *image,
       }
   }
   image_view=DestroyCacheView(image_view);
-  stretch_map=(PixelInfo *) RelinquishMagickMemory(stretch_map);
+  stretch_map=(double *) RelinquishMagickMemory(stretch_map);
+  white=(double *) RelinquishMagickMemory(white);
+  black=(double *) RelinquishMagickMemory(black);
   return(status);
 }
 \f
@@ -1516,9 +1358,8 @@ MagickExport Image *EnhanceImage(const Image *image,ExceptionInfo *exception)
     exception);
   if (enhance_image == (Image *) NULL)
     return((Image *) NULL);
-  if (SetImageStorageClass(enhance_image,DirectClass) == MagickFalse)
+  if (SetImageStorageClass(enhance_image,DirectClass,exception) == MagickFalse)
     {
-      InheritException(exception,&enhance_image->exception);
       enhance_image=DestroyImage(enhance_image);
       return((Image *) NULL);
     }
@@ -1906,7 +1747,8 @@ MagickExport MagickBooleanType EqualizeImage(Image *image)
 %
 %  The format of the GammaImage method is:
 %
-%      MagickBooleanType GammaImage(Image *image,const double gamma)
+%      MagickBooleanType GammaImage(Image *image,const double gamma,
+%        ExceptionInfo *exception)
 %
 %  A description of each parameter follows:
 %
@@ -1917,16 +1759,14 @@ MagickExport MagickBooleanType EqualizeImage(Image *image)
 %    o gamma: the image gamma.
 %
 */
-MagickExport MagickBooleanType GammaImage(Image *image,const double gamma)
+MagickExport MagickBooleanType GammaImage(Image *image,const double gamma,
+  ExceptionInfo *exception)
 {
 #define GammaCorrectImageTag  "GammaCorrect/Image"
 
   CacheView
     *image_view;
 
-  ExceptionInfo
-    *exception;
-
   MagickBooleanType
     status;
 
@@ -1957,8 +1797,8 @@ MagickExport MagickBooleanType GammaImage(Image *image,const double gamma)
       image->filename);
   (void) ResetMagickMemory(gamma_map,0,(MaxMap+1)*sizeof(*gamma_map));
   if (gamma != 0.0)
-#if defined(MAGICKCORE_OPENMP_SUPPORT)
-  #pragma omp parallel for schedule(dynamic,4)
+#if defined(MAGICKCORE_OPENMP_SUPPORT) && (MaxMap > 256)
+  #pragma omp parallel for
 #endif
     for (i=0; i <= (ssize_t) MaxMap; i++)
       gamma_map[i]=ClampToQuantum((MagickRealType) ScaleMapToQuantum((
@@ -1992,7 +1832,6 @@ MagickExport MagickBooleanType GammaImage(Image *image,const double gamma)
   */
   status=MagickTrue;
   progress=0;
-  exception=(&image->exception);
   image_view=AcquireCacheView(image);
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
@@ -2015,43 +1854,20 @@ MagickExport MagickBooleanType GammaImage(Image *image,const double gamma)
       }
     for (x=0; x < (ssize_t) image->columns; x++)
     {
-      if (image->sync != MagickFalse)
-        {
-          SetPixelRed(image,gamma_map[ScaleQuantumToMap(
-            GetPixelRed(image,q))],q);
-          SetPixelGreen(image,gamma_map[ScaleQuantumToMap(
-            GetPixelGreen(image,q))],q);
-          SetPixelBlue(image,gamma_map[ScaleQuantumToMap(
-            GetPixelBlue(image,q))],q);
-        }
-      else
-        {
-          if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
-            SetPixelRed(image,gamma_map[ScaleQuantumToMap(
-              GetPixelRed(image,q))],q);
-          if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
-            SetPixelGreen(image,gamma_map[
-              ScaleQuantumToMap(GetPixelGreen(image,q))],q);
-          if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
-            SetPixelBlue(image,gamma_map[
-              ScaleQuantumToMap(GetPixelBlue(image,q))],q);
-          if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
-            {
-              if (image->matte == MagickFalse)
-                SetPixelAlpha(image,gamma_map[
-                  ScaleQuantumToMap(GetPixelAlpha(image,q))],q);
-              else
-                SetPixelAlpha(image,gamma_map[
-                  ScaleQuantumToMap(GetPixelAlpha(image,q))],q);
-            }
-        }
+      register ssize_t
+        i;
+
+      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
+      {
+        PixelTrait
+          traits;
+
+        traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
+        if ((traits & UpdatePixelTrait) != 0)
+          q[i]=gamma_map[ScaleQuantumToMap(q[i])];
+      }
       q+=GetPixelChannels(image);
     }
-    if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
-        (image->colorspace == CMYKColorspace))
-      for (x=0; x < (ssize_t) image->columns; x++)
-        SetPixelBlack(image,gamma_map[ScaleQuantumToMap(
-          GetPixelBlack(image,q))],q);
     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
       status=MagickFalse;
     if (image->progress_monitor != (MagickProgressMonitor) NULL)
@@ -2157,10 +1973,11 @@ MagickExport MagickBooleanType HaldClutImage(Image *image,
     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   assert(hald_image != (Image *) NULL);
   assert(hald_image->signature == MagickSignature);
-  if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+  exception=(&image->exception);
+  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
     return(MagickFalse);
   if (image->matte == MagickFalse)
-    (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
+    (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
   /*
     Hald clut image.
   */
@@ -2172,7 +1989,6 @@ MagickExport MagickBooleanType HaldClutImage(Image *image,
   cube_size=level*level;
   width=(double) hald_image->columns;
   GetPixelInfo(hald_image,&zero);
-  exception=(&image->exception);
   image_view=AcquireCacheView(image);
   hald_view=AcquireCacheView(hald_image);
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
@@ -2307,19 +2123,23 @@ MagickExport MagickBooleanType HaldClutImage(Image *image,
 %
 %  The format of the LevelImage method is:
 %
-%      MagickBooleanType LevelImage(Image *image,const char *levels)
+%      MagickBooleanType LevelImage(Image *image,const double black_point,
+%        const double white_point,const double gamma,ExceptionInfo *exception)
 %
 %  A description of each parameter follows:
 %
 %    o image: the image.
 %
-%    o levels: Specify the levels where the black and white points have the
-%      range of 0-QuantumRange, and gamma has the range 0-10 (e.g. 10x90%+2).
-%      A '!' flag inverts the re-mapping.
+%    o black_point: The level to map zero (black) to.
+%
+%    o white_point: The level to map QuantumRange (white) to.
+%
+%    o exception: return any errors or warnings in this structure.
 %
 */
 MagickExport MagickBooleanType LevelImage(Image *image,
-  const double black_point,const double white_point,const double gamma)
+  const double black_point,const double white_point,const double gamma,
+  ExceptionInfo *exception)
 {
 #define LevelImageTag  "Level/Image"
 #define LevelQuantum(x) (ClampToQuantum((MagickRealType) QuantumRange* \
@@ -2328,9 +2148,6 @@ MagickExport MagickBooleanType LevelImage(Image *image,
   CacheView
     *image_view;
 
-  ExceptionInfo
-    *exception;
-
   MagickBooleanType
     status;
 
@@ -2377,7 +2194,6 @@ MagickExport MagickBooleanType LevelImage(Image *image,
   */
   status=MagickTrue;
   progress=0;
-  exception=(&image->exception);
   image_view=AcquireCacheView(image);
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
@@ -2401,22 +2217,17 @@ MagickExport MagickBooleanType LevelImage(Image *image,
     for (x=0; x < (ssize_t) image->columns; x++)
     {
       if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
-        SetPixelRed(image,LevelQuantum(
-          GetPixelRed(image,q)),q);
+        SetPixelRed(image,LevelQuantum(GetPixelRed(image,q)),q);
       if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
-        SetPixelGreen(image,
-          LevelQuantum(GetPixelGreen(image,q)),q);
+        SetPixelGreen(image,LevelQuantum(GetPixelGreen(image,q)),q);
       if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
-        SetPixelBlue(image,
-          LevelQuantum(GetPixelBlue(image,q)),q);
+        SetPixelBlue(image,LevelQuantum(GetPixelBlue(image,q)),q);
       if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
           (image->matte == MagickTrue))
-        SetPixelAlpha(image,
-          LevelQuantum(GetPixelAlpha(image,q)),q);
+        SetPixelAlpha(image,LevelQuantum(GetPixelAlpha(image,q)),q);
       if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
           (image->colorspace == CMYKColorspace))
-        SetPixelBlack(image,
-          LevelQuantum(GetPixelBlack(image,q)),q);
+        SetPixelBlack(image,LevelQuantum(GetPixelBlack(image,q)),q);
       q+=GetPixelChannels(image);
     }
     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
@@ -2473,7 +2284,7 @@ MagickExport MagickBooleanType LevelImage(Image *image,
 %
 %    o black_point: The level to map zero (black) to.
 %
-%    o white_point: The level to map QuantiumRange (white) to.
+%    o white_point: The level to map QuantumRange (white) to.
 %
 %    o gamma: adjust gamma by this factor before mapping values.
 %
@@ -2633,6 +2444,9 @@ MagickExport MagickBooleanType LevelImageColors(Image *image,
   const PixelInfo *black_color,const PixelInfo *white_color,
   const MagickBooleanType invert)
 {
+  ChannelType
+    channel_mask;
+
   MagickStatusType
     status;
 
@@ -2648,73 +2462,78 @@ MagickExport MagickBooleanType LevelImageColors(Image *image,
     {
       if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
         {
-          PushPixelChannelMap(image,RedChannel);
-          status|=LevelImage(image,black_color->red,white_color->red,1.0);
-          PopPixelChannelMap(image);
+          channel_mask=SetPixelChannelMask(image,RedChannel);
+          status|=LevelImage(image,black_color->red,white_color->red,1.0,
+            &image->exception);
+          (void) SetPixelChannelMask(image,channel_mask);
         }
       if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
         {
-          PushPixelChannelMap(image,GreenChannel);
-          status|=LevelImage(image,black_color->green,white_color->green,1.0);
-          PopPixelChannelMap(image);
+          channel_mask=SetPixelChannelMask(image,GreenChannel);
+          status|=LevelImage(image,black_color->green,white_color->green,1.0,
+            &image->exception);
+          (void) SetPixelChannelMask(image,channel_mask);
         }
       if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
         {
-          PushPixelChannelMap(image,BlueChannel);
-          status|=LevelImage(image,black_color->blue,white_color->blue,1.0);
-          PopPixelChannelMap(image);
+          channel_mask=SetPixelChannelMask(image,BlueChannel);
+          status|=LevelImage(image,black_color->blue,white_color->blue,1.0,
+            &image->exception);
+          (void) SetPixelChannelMask(image,channel_mask);
         }
       if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
           (image->colorspace == CMYKColorspace))
         {
-          PushPixelChannelMap(image,BlackChannel);
-          status|=LevelImage(image,black_color->black,white_color->black,1.0);
-          PopPixelChannelMap(image);
+          channel_mask=SetPixelChannelMask(image,BlackChannel);
+          status|=LevelImage(image,black_color->black,white_color->black,1.0,
+            &image->exception);
+          (void) SetPixelChannelMask(image,channel_mask);
         }
       if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
           (image->matte == MagickTrue))
         {
-          PushPixelChannelMap(image,AlphaChannel);
-          status|=LevelImage(image,black_color->alpha,white_color->alpha,1.0);
-          PopPixelChannelMap(image);
+          channel_mask=SetPixelChannelMask(image,AlphaChannel);
+          status|=LevelImage(image,black_color->alpha,white_color->alpha,1.0,
+            &image->exception);
+          (void) SetPixelChannelMask(image,channel_mask);
         }
     }
   else
     {
       if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
         {
-          PushPixelChannelMap(image,RedChannel);
+          channel_mask=SetPixelChannelMask(image,RedChannel);
           status|=LevelizeImage(image,black_color->red,white_color->red,1.0);
-          PopPixelChannelMap(image);
+          (void) SetPixelChannelMask(image,channel_mask);
         }
       if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
         {
-          PushPixelChannelMap(image,GreenChannel);
+          channel_mask=SetPixelChannelMask(image,GreenChannel);
           status|=LevelizeImage(image,black_color->green,white_color->green,
             1.0);
-          PopPixelChannelMap(image);
+          (void) SetPixelChannelMask(image,channel_mask);
         }
       if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
         {
-          PushPixelChannelMap(image,BlueChannel);
+          channel_mask=SetPixelChannelMask(image,BlueChannel);
           status|=LevelizeImage(image,black_color->blue,white_color->blue,1.0);
-          PopPixelChannelMap(image);
+          (void) SetPixelChannelMask(image,channel_mask);
         }
       if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
           (image->colorspace == CMYKColorspace))
         {
-          PushPixelChannelMap(image,BlackChannel);
+          channel_mask=SetPixelChannelMask(image,BlackChannel);
           status|=LevelizeImage(image,black_color->black,white_color->black,
             1.0);
-          PopPixelChannelMap(image);
+          (void) SetPixelChannelMask(image,channel_mask);
         }
       if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
           (image->matte == MagickTrue))
         {
-          PushPixelChannelMap(image,AlphaChannel);
+          channel_mask=SetPixelChannelMask(image,AlphaChannel);
           status|=LevelizeImage(image,black_color->alpha,white_color->alpha,
             1.0);
-          PopPixelChannelMap(image);
+          (void) SetPixelChannelMask(image,channel_mask);
         }
     }
   return(status == 0 ? MagickFalse : MagickTrue);
@@ -2731,8 +2550,8 @@ MagickExport MagickBooleanType LevelImageColors(Image *image,
 %                                                                             %
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %
-%  The LinearStretchImage() discards any pixels below the black point and
-%  above the white point and levels the remaining pixels.
+%  LinearStretchImage() discards any pixels below the black point and above
+%  the white point and levels the remaining pixels.
 %
 %  The format of the LinearStretchImage method is:
 %
@@ -2818,7 +2637,7 @@ MagickExport MagickBooleanType LinearStretchImage(Image *image,
       break;
   }
   histogram=(MagickRealType *) RelinquishMagickMemory(histogram);
-  status=LevelImage(image,(double) black,(double) white,1.0);
+  status=LevelImage(image,(double) black,(double) white,1.0,&image->exception);
   return(status);
 }
 \f
@@ -3123,7 +2942,7 @@ MagickExport MagickBooleanType ModulateImage(Image *image,const char *modulate)
 %  The format of the NegateImage method is:
 %
 %      MagickBooleanType NegateImage(Image *image,
-%        const MagickBooleanType grayscale)
+%        const MagickBooleanType grayscale,ExceptionInfo *exception)
 %
 %  A description of each parameter follows:
 %
@@ -3131,18 +2950,17 @@ MagickExport MagickBooleanType ModulateImage(Image *image,const char *modulate)
 %
 %    o grayscale: If MagickTrue, only negate grayscale pixels within the image.
 %
+%    o exception: return any errors or warnings in this structure.
+%
 */
 MagickExport MagickBooleanType NegateImage(Image *image,
-  const MagickBooleanType grayscale)
+  const MagickBooleanType grayscale,ExceptionInfo *exception)
 {
 #define NegateImageTag  "Negate/Image"
 
   CacheView
     *image_view;
 
-  ExceptionInfo
-    *exception;
-
   MagickBooleanType
     status;
 
@@ -3155,9 +2973,6 @@ MagickExport MagickBooleanType NegateImage(Image *image,
   ssize_t
     y;
 
-  size_t
-    channels;
-
   assert(image != (Image *) NULL);
   assert(image->signature == MagickSignature);
   if (image->debug != MagickFalse)
@@ -3192,9 +3007,7 @@ MagickExport MagickBooleanType NegateImage(Image *image,
   */
   status=MagickTrue;
   progress=0;
-  exception=(&image->exception);
   image_view=AcquireCacheView(image);
-  channels=GetPixelChannels(image);
   if (grayscale != MagickFalse)
     {
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
@@ -3225,13 +3038,12 @@ MagickExport MagickBooleanType NegateImage(Image *image,
           register ssize_t
             i;
 
-          if ((GetPixelRed(image,q) != GetPixelGreen(image,q)) ||
-              (GetPixelGreen(image,q) != GetPixelBlue(image,q)))
+          if (IsPixelGray(image,q) != MagickFalse)
             {
-              q+=channels;
+              q+=GetPixelChannels(image);
               continue;
             }
-          for (i=0; i < (ssize_t) channels; i++)
+          for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
           {
             PixelTrait 
               traits;
@@ -3240,7 +3052,7 @@ MagickExport MagickBooleanType NegateImage(Image *image,
             if ((traits & UpdatePixelTrait) != 0)
               q[i]=QuantumRange-q[i];
           }
-          q+=channels;
+          q+=GetPixelChannels(image);
         }
         sync=SyncCacheViewAuthenticPixels(image_view,exception);
         if (sync == MagickFalse)
@@ -3289,7 +3101,7 @@ MagickExport MagickBooleanType NegateImage(Image *image,
       register ssize_t
         i;
 
-      for (i=0; i < (ssize_t) channels; i++)
+      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
       {
         PixelTrait 
           traits;
@@ -3298,7 +3110,7 @@ MagickExport MagickBooleanType NegateImage(Image *image,
         if ((traits & UpdatePixelTrait) != 0)
           q[i]=QuantumRange-q[i];
       }
-      q+=channels;
+      q+=GetPixelChannels(image);
     }
     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
       status=MagickFalse;
@@ -3335,14 +3147,17 @@ MagickExport MagickBooleanType NegateImage(Image *image,
 %
 %  The format of the NormalizeImage method is:
 %
-%      MagickBooleanType NormalizeImage(Image *image)
+%      MagickBooleanType NormalizeImage(Image *image,ExceptionInfo *exception)
 %
 %  A description of each parameter follows:
 %
 %    o image: the image.
 %
+%    o exception: return any errors or warnings in this structure.
+%
 */
-MagickExport MagickBooleanType NormalizeImage(Image *image)
+MagickExport MagickBooleanType NormalizeImage(Image *image,
+  ExceptionInfo *exception)
 {
   double
     black_point,
@@ -3350,7 +3165,7 @@ MagickExport MagickBooleanType NormalizeImage(Image *image)
 
   black_point=(double) image->columns*image->rows*0.0015;
   white_point=(double) image->columns*image->rows*0.9995;
-  return(ContrastStretchImage(image,black_point,white_point));
+  return(ContrastStretchImage(image,black_point,white_point,exception));
 }
 \f
 /*