]> granicus.if.org Git - imagemagick/blobdiff - MagickCore/threshold.c
(no commit message)
[imagemagick] / MagickCore / threshold.c
index eae061966a5061c0207dc5d723cad3fda4955418..e7f1103bd77590e4750febc61c3021f2f973555c 100644 (file)
 %                      MagickCore Image Threshold Methods                     %
 %                                                                             %
 %                               Software Design                               %
-%                                 John Cristy                                 %
+%                                    Cristy                                   %
 %                                 October 1996                                %
 %                                                                             %
 %                                                                             %
-%  Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization      %
+%  Copyright 1999-2014 ImageMagick Studio LLC, a non-profit organization      %
 %  dedicated to making software imaging solutions freely available.           %
 %                                                                             %
 %  You may not use this file except in compliance with the License.  You may  %
@@ -48,6 +48,7 @@
 #include "MagickCore/color-private.h"
 #include "MagickCore/colormap.h"
 #include "MagickCore/colorspace.h"
+#include "MagickCore/colorspace-private.h"
 #include "MagickCore/configure.h"
 #include "MagickCore/constitute.h"
 #include "MagickCore/decorate.h"
@@ -81,6 +82,7 @@
 #include "MagickCore/string-private.h"
 #include "MagickCore/thread-private.h"
 #include "MagickCore/threshold.h"
+#include "MagickCore/token.h"
 #include "MagickCore/transform.h"
 #include "MagickCore/xml-tree.h"
 #include "MagickCore/xml-tree-private.h"
@@ -108,6 +110,34 @@ struct _ThresholdMap
     *levels;
 };
 \f
+/*
+  Static declarations.
+*/
+static const char
+  *MinimalThresholdMap =
+    "<?xml version=\"1.0\"?>"
+    "<thresholds>"
+    "  <threshold map=\"threshold\" alias=\"1x1\">"
+    "    <description>Threshold 1x1 (non-dither)</description>"
+    "    <levels width=\"1\" height=\"1\" divisor=\"2\">"
+    "        1"
+    "    </levels>"
+    "  </threshold>"
+    "  <threshold map=\"checks\" alias=\"2x1\">"
+    "    <description>Checkerboard 2x1 (dither)</description>"
+    "    <levels width=\"2\" height=\"2\" divisor=\"3\">"
+    "       1 2"
+    "       2 1"
+    "    </levels>"
+    "  </threshold>"
+    "</thresholds>";
+\f
+/*
+  Forward declarations.
+*/
+static ThresholdMap
+  *GetThresholdMapFile(const char *,const char *,const char *,ExceptionInfo *);
+\f
 /*
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %                                                                             %
@@ -176,13 +206,12 @@ MagickExport Image *AdaptiveThresholdImage(const Image *image,
     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   assert(exception != (ExceptionInfo *) NULL);
   assert(exception->signature == MagickSignature);
-  if ((width % 2) == 0)
-    ThrowImageException(OptionError,"KernelWidthMustBeAnOddNumber");
   threshold_image=CloneImage(image,image->columns,image->rows,MagickTrue,
     exception);
   if (threshold_image == (Image *) NULL)
     return((Image *) NULL);
-  if (SetImageStorageClass(threshold_image,DirectClass,exception) == MagickFalse)
+  status=SetImageStorageClass(threshold_image,DirectClass,exception);
+  if (status == MagickFalse)
     {
       threshold_image=DestroyImage(threshold_image);
       return((Image *) NULL);
@@ -193,24 +222,33 @@ MagickExport Image *AdaptiveThresholdImage(const Image *image,
   status=MagickTrue;
   progress=0;
   number_pixels=(MagickSizeType) width*height;
-  image_view=AcquireCacheView(image);
-  threshold_view=AcquireCacheView(threshold_image);
+  image_view=AcquireVirtualCacheView(image,exception);
+  threshold_view=AcquireAuthenticCacheView(threshold_image,exception);
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
-  #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+  #pragma omp parallel for schedule(static,4) shared(progress,status) \
+    magick_threads(image,threshold_image,image->rows,1)
 #endif
   for (y=0; y < (ssize_t) image->rows; y++)
   {
+    double
+      channel_bias[MaxPixelChannels],
+      channel_sum[MaxPixelChannels];
+
     register const Quantum
-      *restrict p;
+      *restrict p,
+      *restrict pixels;
 
     register Quantum
       *restrict q;
 
     register ssize_t
+      i,
       x;
 
     ssize_t
-      center;
+      center,
+      u,
+      v;
 
     if (status == MagickFalse)
       continue;
@@ -225,57 +263,68 @@ MagickExport Image *AdaptiveThresholdImage(const Image *image,
       }
     center=(ssize_t) GetPixelChannels(image)*(image->columns+width)*(height/2L)+
       GetPixelChannels(image)*(width/2);
+    for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
+    {
+      PixelChannel channel=GetPixelChannelChannel(image,i);
+      PixelTrait traits=GetPixelChannelTraits(image,channel);
+      PixelTrait threshold_traits=GetPixelChannelTraits(threshold_image,
+        channel);
+      if ((traits == UndefinedPixelTrait) ||
+          (threshold_traits == UndefinedPixelTrait))
+        continue;
+      if (((threshold_traits & CopyPixelTrait) != 0) ||
+          (GetPixelReadMask(image,p) == 0))
+        {
+          SetPixelChannel(threshold_image,channel,p[center+i],q);
+          continue;
+        }
+      pixels=p;
+      channel_bias[channel]=0.0;
+      channel_sum[channel]=0.0;
+      for (v=0; v < (ssize_t) height; v++)
+      {
+        for (u=0; u < (ssize_t) width; u++)
+        {
+          if (u == (ssize_t) (width-1))
+            channel_bias[channel]+=pixels[i];
+          channel_sum[channel]+=pixels[i];
+          pixels+=GetPixelChannels(image);
+        }
+        pixels+=(image->columns-1)*GetPixelChannels(image);
+      }
+    }
     for (x=0; x < (ssize_t) image->columns; x++)
     {
-      register ssize_t
-        i;
-
       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
       {
-        MagickRealType
-          mean,
-          pixel;
-
-        PixelChannel
-          channel;
+        double
+          mean;
 
-        PixelTrait
-          threshold_traits,
-          traits;
-
-        register const Quantum
-          *restrict pixels;
-
-        register ssize_t
-          u;
-
-        ssize_t
-          v;
-
-        traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
-        channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
-        threshold_traits=GetPixelChannelMapTraits(threshold_image,channel);
+        PixelChannel channel=GetPixelChannelChannel(image,i);
+        PixelTrait traits=GetPixelChannelTraits(image,channel);
+        PixelTrait threshold_traits=GetPixelChannelTraits(threshold_image,
+          channel);
         if ((traits == UndefinedPixelTrait) ||
             (threshold_traits == UndefinedPixelTrait))
           continue;
-        if ((threshold_traits & CopyPixelTrait) != 0)
+        if (((threshold_traits & CopyPixelTrait) != 0) ||
+            (GetPixelReadMask(image,p) == 0))
           {
             SetPixelChannel(threshold_image,channel,p[center+i],q);
             continue;
           }
+        channel_sum[channel]-=channel_bias[channel];
+        channel_bias[channel]=0.0;
         pixels=p;
-        pixel=0.0;
         for (v=0; v < (ssize_t) height; v++)
         {
-          for (u=0; u < (ssize_t) width; u++)
-          {
-            pixel+=pixels[i];
-            pixels+=GetPixelChannels(image);
-          }
-          pixels+=image->columns*GetPixelChannels(image);
+          channel_bias[channel]+=pixels[i];
+          pixels+=(width-1)*GetPixelChannels(image);
+          channel_sum[channel]+=pixels[i];
+          pixels+=(image->columns-1)*GetPixelChannels(image);
         }
-        mean=(MagickRealType) (pixel/number_pixels+bias);
-        SetPixelChannel(threshold_image,channel,(Quantum) ((MagickRealType)
+        mean=(double) (channel_sum[channel]/number_pixels+bias);
+        SetPixelChannel(threshold_image,channel,(Quantum) ((double)
           p[center+i] <= mean ? 0 : QuantumRange),q);
       }
       p+=GetPixelChannels(image);
@@ -289,7 +338,7 @@ MagickExport Image *AdaptiveThresholdImage(const Image *image,
           proceed;
 
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
-  #pragma omp critical (MagickCore_AdaptiveThresholdImage)
+        #pragma omp critical (MagickCore_AdaptiveThresholdImage)
 #endif
         proceed=SetImageProgress(image,AdaptiveThresholdImageTag,progress++,
           image->rows);
@@ -331,7 +380,8 @@ MagickExport Image *AdaptiveThresholdImage(const Image *image,
 %
 %  The format of the BilevelImage method is:
 %
-%      MagickBooleanType BilevelImage(Image *image,const double threshold)
+%      MagickBooleanType BilevelImage(Image *image,const double threshold,
+%        ExceptionInfo *exception)
 %
 %  A description of each parameter follows:
 %
@@ -339,20 +389,20 @@ MagickExport Image *AdaptiveThresholdImage(const Image *image,
 %
 %    o threshold: define the threshold values.
 %
+%    o exception: return any errors or warnings in this structure.
+%
 %  Aside: You can get the same results as operator using LevelImages()
 %  with the 'threshold' value for both the black_point and the white_point.
 %
 */
-MagickExport MagickBooleanType BilevelImage(Image *image,const double threshold)
+MagickExport MagickBooleanType BilevelImage(Image *image,const double threshold,
+  ExceptionInfo *exception)
 {
 #define ThresholdImageTag  "Threshold/Image"
 
   CacheView
     *image_view;
 
-  ExceptionInfo
-    *exception;
-
   MagickBooleanType
     status;
 
@@ -366,17 +416,19 @@ MagickExport MagickBooleanType BilevelImage(Image *image,const double threshold)
   assert(image->signature == MagickSignature);
   if (image->debug != MagickFalse)
     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
-  exception=(&image->exception);
   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
     return(MagickFalse);
+  if (IsGrayColorspace(image->colorspace) != MagickFalse)
+    (void) SetImageColorspace(image,sRGBColorspace,exception);
   /*
     Bilevel threshold image.
   */
   status=MagickTrue;
   progress=0;
-  image_view=AcquireCacheView(image);
+  image_view=AcquireAuthenticCacheView(image,exception);
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
-  #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+  #pragma omp parallel for schedule(static,4) shared(progress,status) \
+    magick_threads(image,image,image->rows,1)
 #endif
   for (y=0; y < (ssize_t) image->rows; y++)
   {
@@ -396,18 +448,27 @@ MagickExport MagickBooleanType BilevelImage(Image *image,const double threshold)
       }
     for (x=0; x < (ssize_t) image->columns; x++)
     {
+      double
+        pixel;
+
       register ssize_t
         i;
 
+      if (GetPixelReadMask(image,q) == 0)
+        {
+          q+=GetPixelChannels(image);
+          continue;
+        }
+      pixel=GetPixelIntensity(image,q);
       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
       {
-        PixelTrait
-          traits;
-
-        traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
-        if ((traits & UpdatePixelTrait) != 0)
-          q[i]=(Quantum) ((MagickRealType) q[i] <= threshold ? 0 :
-            QuantumRange);
+        PixelChannel channel=GetPixelChannelChannel(image,i);
+        PixelTrait traits=GetPixelChannelTraits(image,channel);
+        if ((traits & UpdatePixelTrait) == 0)
+          continue;
+        if (image->channel_mask != DefaultChannels)
+          pixel=(double) q[i];
+        q[i]=(Quantum) (pixel <= threshold ? 0 : QuantumRange);
       }
       q+=GetPixelChannels(image);
     }
@@ -419,7 +480,7 @@ MagickExport MagickBooleanType BilevelImage(Image *image,const double threshold)
           proceed;
 
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
-  #pragma omp critical (MagickCore_BilevelImage)
+        #pragma omp critical (MagickCore_BilevelImage)
 #endif
         proceed=SetImageProgress(image,ThresholdImageTag,progress++,
           image->rows);
@@ -477,15 +538,12 @@ MagickExport MagickBooleanType BlackThresholdImage(Image *image,
   MagickOffsetType
     progress;
 
-  MagickRealType
-    threshold[5];
+  PixelInfo
+    threshold;
 
   MagickStatusType
     flags;
 
-  register ssize_t
-    i;
-
   ssize_t
     y;
 
@@ -497,28 +555,45 @@ MagickExport MagickBooleanType BlackThresholdImage(Image *image,
     return(MagickTrue);
   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
     return(MagickFalse);
+  if (IsGrayColorspace(image->colorspace) != MagickFalse)
+    (void) SetImageColorspace(image,sRGBColorspace,exception);
+  GetPixelInfo(image,&threshold);
   flags=ParseGeometry(thresholds,&geometry_info);
-  for (i=0; i < 5; i++)
-    threshold[i]=geometry_info.rho;
+  threshold.red=geometry_info.rho;
+  threshold.green=geometry_info.rho;
+  threshold.blue=geometry_info.rho;
+  threshold.black=geometry_info.rho;
+  threshold.alpha=100.0;
   if ((flags & SigmaValue) != 0)
-    threshold[1]=geometry_info.sigma;
+    threshold.green=geometry_info.sigma;
   if ((flags & XiValue) != 0)
-    threshold[2]=geometry_info.xi;
+    threshold.blue=geometry_info.xi;
   if ((flags & PsiValue) != 0)
-    threshold[3]=geometry_info.psi;
-  if ((flags & ChiValue) != 0)
-    threshold[4]=geometry_info.chi;
+    threshold.alpha=geometry_info.psi;
+  if (threshold.colorspace == CMYKColorspace)
+    {
+      if ((flags & PsiValue) != 0)
+        threshold.black=geometry_info.psi;
+      if ((flags & ChiValue) != 0)
+        threshold.alpha=geometry_info.chi;
+    }
   if ((flags & PercentValue) != 0)
-    for (i=0; i < 5; i++)
-      threshold[i]*=(QuantumRange/100.0);
+    {
+      threshold.red*=(MagickRealType) (QuantumRange/100.0);
+      threshold.green*=(MagickRealType) (QuantumRange/100.0);
+      threshold.blue*=(MagickRealType) (QuantumRange/100.0);
+      threshold.black*=(MagickRealType) (QuantumRange/100.0);
+      threshold.alpha*=(MagickRealType) (QuantumRange/100.0);
+    }
   /*
     White threshold image.
   */
   status=MagickTrue;
   progress=0;
-  image_view=AcquireCacheView(image);
+  image_view=AcquireAuthenticCacheView(image,exception);
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
-  #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+  #pragma omp parallel for schedule(static,4) shared(progress,status) \
+    magick_threads(image,image,image->rows,1)
 #endif
   for (y=0; y < (ssize_t) image->rows; y++)
   {
@@ -538,23 +613,28 @@ MagickExport MagickBooleanType BlackThresholdImage(Image *image,
       }
     for (x=0; x < (ssize_t) image->columns; x++)
     {
+      double
+        pixel;
+
       register ssize_t
         i;
 
-      ssize_t
-        n;
-
-      n=0;
+      if (GetPixelReadMask(image,q) == 0)
+        {
+          q+=GetPixelChannels(image);
+          continue;
+        }
+      pixel=GetPixelIntensity(image,q);
       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
       {
-        PixelTrait
-          traits;
-
-        traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
+        PixelChannel channel=GetPixelChannelChannel(image,i);
+        PixelTrait traits=GetPixelChannelTraits(image,channel);
         if ((traits & UpdatePixelTrait) == 0)
           continue;
-        if ((MagickRealType) q[i] < threshold[n++ % 5])
-          q[i]=QuantumRange;
+        if (image->channel_mask != DefaultChannels)
+          pixel=(double) q[i];
+        if (pixel <= GetPixelInfoChannel(&threshold,channel))
+          q[i]=(Quantum) 0;
       }
       q+=GetPixelChannels(image);
     }
@@ -566,7 +646,7 @@ MagickExport MagickBooleanType BlackThresholdImage(Image *image,
           proceed;
 
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
-  #pragma omp critical (MagickCore_WhiteThresholdImage)
+        #pragma omp critical (MagickCore_BlackThresholdImage)
 #endif
         proceed=SetImageProgress(image,ThresholdImageTag,progress++,
           image->rows);
@@ -589,41 +669,42 @@ MagickExport MagickBooleanType BlackThresholdImage(Image *image,
 %                                                                             %
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %
-%  ClampImage() restricts the color range from 0 to the quantum depth.
+%  ClampImage() set each pixel whose value is below zero to zero and any the
+%  pixel whose value is above the quantum range to the quantum range (e.g.
+%  65535) otherwise the pixel value remains unchanged.
 %
 %  The format of the ClampImage method is:
 %
-%      MagickBooleanType ClampImage(Image *image)
+%      MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
 %
 %  A description of each parameter follows:
 %
 %    o image: the image.
 %
+%    o exception: return any errors or warnings in this structure.
+%
 */
 
-static inline Quantum ClampToUnsignedQuantum(const Quantum quantum)
+static inline Quantum ClampPixel(const MagickRealType value)
 {
-#if defined(MAGICKCORE_HDRI_SUPPORT)
-  if (quantum <= 0)
-    return(0);
-  if (quantum >= QuantumRange)
-    return(QuantumRange);
-  return(quantum);
+#if !defined(MAGICKCORE_HDRI_SUPPORT)
+  return((Quantum) value);
 #else
-  return(quantum);
+  if (value < 0.0f)
+    return(0.0);
+  if (value >= (MagickRealType) QuantumRange)
+    return((Quantum) QuantumRange);
+  return(value);
 #endif
 }
 
-MagickExport MagickBooleanType ClampImage(Image *image)
+MagickExport MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
 {
 #define ClampImageTag  "Clamp/Image"
 
   CacheView
     *image_view;
 
-  ExceptionInfo
-    *exception;
-
   MagickBooleanType
     status;
 
@@ -642,29 +723,29 @@ MagickExport MagickBooleanType ClampImage(Image *image)
       register ssize_t
         i;
 
-      register PixelPacket
+      register PixelInfo
         *restrict q;
 
       q=image->colormap;
       for (i=0; i < (ssize_t) image->colors; i++)
       {
-        q->red=ClampToUnsignedQuantum(q->red);
-        q->green=ClampToUnsignedQuantum(q->green);
-        q->blue=ClampToUnsignedQuantum(q->blue);
-        q->alpha=ClampToUnsignedQuantum(q->alpha);
+        q->red=(double) ClampPixel(q->red);
+        q->green=(double) ClampPixel(q->green);
+        q->blue=(double) ClampPixel(q->blue);
+        q->alpha=(double) ClampPixel(q->alpha);
         q++;
       }
-      return(SyncImage(image));
+      return(SyncImage(image,exception));
     }
   /*
     Clamp image.
   */
   status=MagickTrue;
   progress=0;
-  exception=(&image->exception);
-  image_view=AcquireCacheView(image);
+  image_view=AcquireAuthenticCacheView(image,exception);
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
-  #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+  #pragma omp parallel for schedule(static,4) shared(progress,status) \
+    magick_threads(image,image,image->rows,1)
 #endif
   for (y=0; y < (ssize_t) image->rows; y++)
   {
@@ -687,15 +768,18 @@ MagickExport MagickBooleanType ClampImage(Image *image)
       register ssize_t
         i;
 
+      if (GetPixelReadMask(image,q) == 0)
+        {
+          q+=GetPixelChannels(image);
+          continue;
+        }
       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
       {
-        PixelTrait
-          traits;
-
-        traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
-        if (traits == UndefinedPixelTrait)
+        PixelChannel channel=GetPixelChannelChannel(image,i);
+        PixelTrait traits=GetPixelChannelTraits(image,channel);
+        if ((traits & UpdatePixelTrait) == 0)
           continue;
-        q[i]=ClampToUnsignedQuantum(q[i]);
+        q[i]=ClampPixel(q[i]);
       }
       q+=GetPixelChannels(image);
     }
@@ -707,10 +791,9 @@ MagickExport MagickBooleanType ClampImage(Image *image)
           proceed;
 
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
-  #pragma omp critical (MagickCore_ClampImage)
+        #pragma omp critical (MagickCore_ClampImage)
 #endif
-        proceed=SetImageProgress(image,ClampImageTag,progress++,
-          image->rows);
+        proceed=SetImageProgress(image,ClampImageTag,progress++,image->rows);
         if (proceed == MagickFalse)
           status=MagickFalse;
       }
@@ -759,6 +842,61 @@ MagickExport ThresholdMap *DestroyThresholdMap(ThresholdMap *map)
 %                                                                             %
 %                                                                             %
 %                                                                             %
+%  G e t T h r e s h o l d M a p                                              %
+%                                                                             %
+%                                                                             %
+%                                                                             %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%  GetThresholdMap() loads and searches one or more threshold map files for the
+%  map matching the given name or alias.
+%
+%  The format of the GetThresholdMap method is:
+%
+%      ThresholdMap *GetThresholdMap(const char *map_id,
+%        ExceptionInfo *exception)
+%
+%  A description of each parameter follows.
+%
+%    o map_id:  ID of the map to look for.
+%
+%    o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport ThresholdMap *GetThresholdMap(const char *map_id,
+  ExceptionInfo *exception)
+{
+  const StringInfo
+    *option;
+
+  LinkedListInfo
+    *options;
+
+  ThresholdMap
+    *map;
+
+  map=GetThresholdMapFile(MinimalThresholdMap,"built-in",map_id,exception);
+  if (map != (ThresholdMap *) NULL)
+    return(map);
+  options=GetConfigureOptions(ThresholdsFilename,exception);
+  option=(const StringInfo *) GetNextValueInLinkedList(options);
+  while (option != (const StringInfo *) NULL)
+  {
+    map=GetThresholdMapFile((const char *) GetStringInfoDatum(option),
+      GetStringInfoPath(option),map_id,exception);
+    if (map != (ThresholdMap *) NULL)
+      break;
+    option=(const StringInfo *) GetNextValueInLinkedList(options);
+  }
+  options=DestroyConfigureOptions(options);
+  return(map);
+}
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%                                                                             %
+%                                                                             %
+%                                                                             %
 +  G e t T h r e s h o l d M a p F i l e                                      %
 %                                                                             %
 %                                                                             %
@@ -784,174 +922,180 @@ MagickExport ThresholdMap *DestroyThresholdMap(ThresholdMap *map)
 %    o exception: return any errors or warnings in this structure.
 %
 */
-MagickExport ThresholdMap *GetThresholdMapFile(const char *xml,
-  const char *filename,const char *map_id,ExceptionInfo *exception)
+static ThresholdMap *GetThresholdMapFile(const char *xml,const char *filename,
+  const char *map_id,ExceptionInfo *exception)
 {
+  char
+    *p;
+
   const char
-    *attr,
+    *attribute,
     *content;
 
   double
     value;
 
+  register ssize_t
+    i;
+
   ThresholdMap
-     *map;
+    *map;
 
   XMLTreeInfo
-     *description,
-     *levels,
-     *threshold,
-     *thresholds;
+    *description,
+    *levels,
+    *threshold,
+    *thresholds;
 
-  map = (ThresholdMap *)NULL;
   (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
     "Loading threshold map file \"%s\" ...",filename);
+  map=(ThresholdMap *) NULL;
   thresholds=NewXMLTree(xml,exception);
-  if ( thresholds == (XMLTreeInfo *)NULL )
+  if (thresholds == (XMLTreeInfo *) NULL)
     return(map);
-
-  for( threshold = GetXMLTreeChild(thresholds,"threshold");
-       threshold != (XMLTreeInfo *)NULL;
-       threshold = GetNextXMLTreeTag(threshold) ) {
-    attr = GetXMLTreeAttribute(threshold, "map");
-    if ( (attr != (char *)NULL) && (LocaleCompare(map_id,attr) == 0) )
+  for (threshold=GetXMLTreeChild(thresholds,"threshold");
+       threshold != (XMLTreeInfo *) NULL;
+       threshold=GetNextXMLTreeTag(threshold))
+  {
+    attribute=GetXMLTreeAttribute(threshold,"map");
+    if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
       break;
-    attr = GetXMLTreeAttribute(threshold, "alias");
-    if ( (attr != (char *)NULL) && (LocaleCompare(map_id,attr) == 0) )
+    attribute=GetXMLTreeAttribute(threshold,"alias");
+    if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
       break;
   }
-  if ( threshold == (XMLTreeInfo *)NULL ) {
-    return(map);
-  }
-  description = GetXMLTreeChild(threshold,"description");
-  if ( description == (XMLTreeInfo *)NULL ) {
-    (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
-      "XmlMissingElement", "<description>, map \"%s\"", map_id);
-    thresholds = DestroyXMLTree(thresholds);
-    return(map);
-  }
-  levels = GetXMLTreeChild(threshold,"levels");
-  if ( levels == (XMLTreeInfo *)NULL ) {
-    (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
-      "XmlMissingElement", "<levels>, map \"%s\"", map_id);
-    thresholds = DestroyXMLTree(thresholds);
-    return(map);
-  }
-
-  /* The map has been found -- Allocate a Threshold Map to return */
-  map = (ThresholdMap *)AcquireMagickMemory(sizeof(ThresholdMap));
-  if ( map == (ThresholdMap *)NULL )
+  if (threshold == (XMLTreeInfo *) NULL)
+    {
+      thresholds=DestroyXMLTree(thresholds);
+      return(map);
+    }
+  description=GetXMLTreeChild(threshold,"description");
+  if (description == (XMLTreeInfo *) NULL)
+    {
+      (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+        "XmlMissingElement", "<description>, map \"%s\"",map_id);
+      thresholds=DestroyXMLTree(thresholds);
+      return(map);
+    }
+  levels=GetXMLTreeChild(threshold,"levels");
+  if (levels == (XMLTreeInfo *) NULL)
+    {
+      (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+        "XmlMissingElement", "<levels>, map \"%s\"", map_id);
+      thresholds=DestroyXMLTree(thresholds);
+      return(map);
+    }
+  map=(ThresholdMap *) AcquireMagickMemory(sizeof(ThresholdMap));
+  if (map == (ThresholdMap *) NULL)
     ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
-  map->map_id = (char *)NULL;
-  map->description = (char *)NULL;
-  map->levels = (ssize_t *) NULL;
-
-  /* Assign Basic Attributes */
-  attr = GetXMLTreeAttribute(threshold, "map");
-  if ( attr != (char *)NULL )
-    map->map_id = ConstantString(attr);
-
-  content = GetXMLTreeContent(description);
-  if ( content != (char *)NULL )
-    map->description = ConstantString(content);
-
-  attr = GetXMLTreeAttribute(levels, "width");
-  if ( attr == (char *)NULL ) {
-    (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
-      "XmlMissingAttribute", "<levels width>, map \"%s\"", map_id);
-    thresholds = DestroyXMLTree(thresholds);
-    map = DestroyThresholdMap(map);
-    return(map);
-  }
-  map->width = StringToUnsignedLong(attr);
-  if ( map->width == 0 ) {
-    (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
-     "XmlInvalidAttribute", "<levels width>, map \"%s\"", map_id);
-    thresholds = DestroyXMLTree(thresholds);
-    map = DestroyThresholdMap(map);
-    return(map);
-  }
-
-  attr = GetXMLTreeAttribute(levels, "height");
-  if ( attr == (char *)NULL ) {
-    (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
-      "XmlMissingAttribute", "<levels height>, map \"%s\"", map_id);
-    thresholds = DestroyXMLTree(thresholds);
-    map = DestroyThresholdMap(map);
-    return(map);
-  }
-  map->height = StringToUnsignedLong(attr);
-  if ( map->height == 0 ) {
-    (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
-      "XmlInvalidAttribute", "<levels height>, map \"%s\"", map_id);
-    thresholds = DestroyXMLTree(thresholds);
-    map = DestroyThresholdMap(map);
-    return(map);
-  }
-
-  attr = GetXMLTreeAttribute(levels, "divisor");
-  if ( attr == (char *)NULL ) {
-    (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
-      "XmlMissingAttribute", "<levels divisor>, map \"%s\"", map_id);
-    thresholds = DestroyXMLTree(thresholds);
-    map = DestroyThresholdMap(map);
-    return(map);
-  }
-  map->divisor = (ssize_t) StringToLong(attr);
-  if ( map->divisor < 2 ) {
-    (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
-      "XmlInvalidAttribute", "<levels divisor>, map \"%s\"", map_id);
-    thresholds = DestroyXMLTree(thresholds);
-    map = DestroyThresholdMap(map);
-    return(map);
-  }
-
-  /* Allocate theshold levels array */
-  content = GetXMLTreeContent(levels);
-  if ( content == (char *)NULL ) {
-    (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
-      "XmlMissingContent", "<levels>, map \"%s\"", map_id);
-    thresholds = DestroyXMLTree(thresholds);
-    map = DestroyThresholdMap(map);
-    return(map);
-  }
+  map->map_id=(char *) NULL;
+  map->description=(char *) NULL;
+  map->levels=(ssize_t *) NULL;
+  attribute=GetXMLTreeAttribute(threshold,"map");
+  if (attribute != (char *) NULL)
+    map->map_id=ConstantString(attribute);
+  content=GetXMLTreeContent(description);
+  if (content != (char *) NULL)
+    map->description=ConstantString(content);
+  attribute=GetXMLTreeAttribute(levels,"width");
+  if (attribute == (char *) NULL)
+    {
+      (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+        "XmlMissingAttribute", "<levels width>, map \"%s\"",map_id);
+      thresholds=DestroyXMLTree(thresholds);
+      map=DestroyThresholdMap(map);
+      return(map);
+    }
+  map->width=StringToUnsignedLong(attribute);
+  if (map->width == 0)
+    {
+      (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+       "XmlInvalidAttribute", "<levels width>, map \"%s\"",map_id);
+      thresholds=DestroyXMLTree(thresholds);
+      map=DestroyThresholdMap(map);
+      return(map);
+    }
+  attribute=GetXMLTreeAttribute(levels,"height");
+  if (attribute == (char *) NULL)
+    {
+      (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+        "XmlMissingAttribute", "<levels height>, map \"%s\"",map_id);
+      thresholds=DestroyXMLTree(thresholds);
+      map=DestroyThresholdMap(map);
+      return(map);
+    }
+  map->height=StringToUnsignedLong(attribute);
+  if (map->height == 0)
+    {
+      (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+        "XmlInvalidAttribute", "<levels height>, map \"%s\"",map_id);
+      thresholds=DestroyXMLTree(thresholds);
+      map=DestroyThresholdMap(map);
+      return(map);
+    }
+  attribute=GetXMLTreeAttribute(levels,"divisor");
+  if (attribute == (char *) NULL)
+    {
+      (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+        "XmlMissingAttribute", "<levels divisor>, map \"%s\"",map_id);
+      thresholds=DestroyXMLTree(thresholds);
+      map=DestroyThresholdMap(map);
+      return(map);
+    }
+  map->divisor=(ssize_t) StringToLong(attribute);
+  if (map->divisor < 2)
+    {
+      (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+        "XmlInvalidAttribute", "<levels divisor>, map \"%s\"",map_id);
+      thresholds=DestroyXMLTree(thresholds);
+      map=DestroyThresholdMap(map);
+      return(map);
+    }
+  content=GetXMLTreeContent(levels);
+  if (content == (char *) NULL)
+    {
+      (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+        "XmlMissingContent", "<levels>, map \"%s\"",map_id);
+      thresholds=DestroyXMLTree(thresholds);
+      map=DestroyThresholdMap(map);
+      return(map);
+    }
   map->levels=(ssize_t *) AcquireQuantumMemory((size_t) map->width,map->height*
     sizeof(*map->levels));
-  if ( map->levels == (ssize_t *)NULL )
+  if (map->levels == (ssize_t *) NULL)
     ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
-  { /* parse levels into integer array */
-    ssize_t i;
-    char *p;
-    for( i=0; i< (ssize_t) (map->width*map->height); i++) {
-      map->levels[i] = (ssize_t)strtol(content, &p, 10);
-      if ( p == content ) {
+  for (i=0; i < (ssize_t) (map->width*map->height); i++)
+  {
+    map->levels[i]=(ssize_t) strtol(content,&p,10);
+    if (p == content)
+      {
         (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
-          "XmlInvalidContent", "<level> too few values, map \"%s\"", map_id);
-        thresholds = DestroyXMLTree(thresholds);
-        map = DestroyThresholdMap(map);
+          "XmlInvalidContent", "<level> too few values, map \"%s\"",map_id);
+        thresholds=DestroyXMLTree(thresholds);
+        map=DestroyThresholdMap(map);
         return(map);
       }
-      if ( map->levels[i] < 0 || map->levels[i] > map->divisor ) {
+    if ((map->levels[i] < 0) || (map->levels[i] > map->divisor))
+      {
         (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
           "XmlInvalidContent", "<level> %.20g out of range, map \"%s\"",
           (double) map->levels[i],map_id);
-        thresholds = DestroyXMLTree(thresholds);
-        map = DestroyThresholdMap(map);
+        thresholds=DestroyXMLTree(thresholds);
+        map=DestroyThresholdMap(map);
         return(map);
       }
-      content = p;
-    }
-    value=(double) strtol(content,&p,10);
-    (void) value;
-    if (p != content)
-      {
-        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
-          "XmlInvalidContent", "<level> too many values, map \"%s\"", map_id);
-       thresholds=DestroyXMLTree(thresholds);
-       map=DestroyThresholdMap(map);
-       return(map);
-     }
+    content=p;
   }
+  value=(double) strtol(content,&p,10);
+  (void) value;
+  if (p != content)
+    {
+      (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+        "XmlInvalidContent", "<level> too many values, map \"%s\"",map_id);
+     thresholds=DestroyXMLTree(thresholds);
+     map=DestroyThresholdMap(map);
+     return(map);
+   }
   thresholds=DestroyXMLTree(thresholds);
   return(map);
 }
@@ -961,54 +1105,6 @@ MagickExport ThresholdMap *GetThresholdMapFile(const char *xml,
 %                                                                             %
 %                                                                             %
 %                                                                             %
-%  G e t T h r e s h o l d M a p                                              %
-%                                                                             %
-%                                                                             %
-%                                                                             %
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%
-%  GetThresholdMap() load and search one or more threshold map files for the
-%  a map matching the given name or aliase.
-%
-%  The format of the GetThresholdMap method is:
-%
-%      ThresholdMap *GetThresholdMap(const char *map_id,
-%         ExceptionInfo *exception)
-%
-%  A description of each parameter follows.
-%
-%    o map_id:  ID of the map to look for.
-%
-%    o exception: return any errors or warnings in this structure.
-%
-*/
-MagickExport ThresholdMap *GetThresholdMap(const char *map_id,
-  ExceptionInfo *exception)
-{
-  const StringInfo
-    *option;
-
-  LinkedListInfo
-    *options;
-
-  ThresholdMap
-    *map;
-
-  map=(ThresholdMap *)NULL;
-  options=GetConfigureOptions(ThresholdsFilename,exception);
-  while (( option=(const StringInfo *) GetNextValueInLinkedList(options) )
-          != (const StringInfo *) NULL && map == (ThresholdMap *)NULL )
-    map=GetThresholdMapFile((const char *) GetStringInfoDatum(option),
-      GetStringInfoPath(option),map_id,exception);
-  options=DestroyConfigureOptions(options);
-  return(map);
-}
-\f
-/*
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%                                                                             %
-%                                                                             %
-%                                                                             %
 +  L i s t T h r e s h o l d M a p F i l e                                    %
 %                                                                             %
 %                                                                             %
@@ -1058,7 +1154,8 @@ MagickBooleanType ListThresholdMapFile(FILE *file,const char *xml,
   (void) FormatLocaleFile(file,
     "----------------------------------------------------\n");
   threshold=GetXMLTreeChild(thresholds,"threshold");
-  for ( ; threshold != (XMLTreeInfo *) NULL; threshold=GetNextXMLTreeTag(threshold))
+  for ( ; threshold != (XMLTreeInfo *) NULL;
+          threshold=GetNextXMLTreeTag(threshold))
   {
     map=GetXMLTreeAttribute(threshold,"map");
     if (map == (char *) NULL)
@@ -1073,7 +1170,7 @@ MagickBooleanType ListThresholdMapFile(FILE *file,const char *xml,
     if (description == (XMLTreeInfo *) NULL)
       {
         (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
-          "XmlMissingElement", "<description>, map \"%s\"", map);
+          "XmlMissingElement", "<description>, map \"%s\"",map);
         thresholds=DestroyXMLTree(thresholds);
         return(MagickFalse);
       }
@@ -1135,12 +1232,13 @@ MagickExport MagickBooleanType ListThresholdMaps(FILE *file,
   options=GetConfigureOptions(ThresholdsFilename,exception);
   (void) FormatLocaleFile(file,
     "\n   Threshold Maps for Ordered Dither Operations\n");
-  while ((option=(const StringInfo *) GetNextValueInLinkedList(options)) !=
-         (const StringInfo *) NULL)
+  option=(const StringInfo *) GetNextValueInLinkedList(options);
+  while (option != (const StringInfo *) NULL)
   {
-    (void) FormatLocaleFile(file,"\nPATH: %s\n\n",GetStringInfoPath(option));
-    status|=ListThresholdMapFile(file,(const char *) GetStringInfoDatum(option),
+    (void) FormatLocaleFile(file,"\nPath: %s\n\n",GetStringInfoPath(option));
+    status&=ListThresholdMapFile(file,(const char *) GetStringInfoDatum(option),
       GetStringInfoPath(option),exception);
+    option=(const StringInfo *) GetNextValueInLinkedList(options);
   }
   options=DestroyConfigureOptions(options);
   return(status != 0 ? MagickTrue : MagickFalse);
@@ -1201,8 +1299,14 @@ MagickExport MagickBooleanType OrderedPosterizeImage(Image *image,
   CacheView
     *image_view;
 
-  PixelLongPacket
-    levels;
+  char
+    token[MaxTextExtent];
+
+  const char
+    *p;
+
+  double
+    levels[CompositePixelChannel];
 
   MagickBooleanType
     status;
@@ -1210,6 +1314,9 @@ MagickExport MagickBooleanType OrderedPosterizeImage(Image *image,
   MagickOffsetType
     progress;
 
+  register ssize_t
+    i;
+
   ssize_t
     y;
 
@@ -1224,220 +1331,271 @@ MagickExport MagickBooleanType OrderedPosterizeImage(Image *image,
   assert(exception->signature == MagickSignature);
   if (threshold_map == (const char *) NULL)
     return(MagickTrue);
+  p=(char *) threshold_map;
+  while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
+         (*p != '\0'))
+    p++;
+  threshold_map=p;
+  while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
+         (*p != '\0'))
   {
-    char
-      token[MaxTextExtent];
-
-    register const char
-      *p;
-
-    p=(char *)threshold_map;
-    while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
-           (*p != '\0'))
-      p++;
-    threshold_map=p;
-    while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
-           (*p != '\0')) {
-      if ((p-threshold_map) >= (MaxTextExtent-1))
-        break;
-      token[p-threshold_map] = *p;
-      p++;
+    if ((p-threshold_map) >= (MaxTextExtent-1))
+      break;
+    token[p-threshold_map]=(*p);
+    p++;
+  }
+  token[p-threshold_map]='\0';
+  map=GetThresholdMap(token,exception);
+  if (map == (ThresholdMap *) NULL)
+    {
+      (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+        "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
+      return(MagickFalse);
     }
-    token[p-threshold_map] = '\0';
-    map = GetThresholdMap(token, exception);
-    if (map == (ThresholdMap *) NULL)
+  for (i=0; i < MaxPixelChannels; i++)
+    levels[i]=2.0;
+  p=strchr((char *) threshold_map,',');
+  if ((p != (char *) NULL) && (isdigit((int) ((unsigned char) *(++p))) != 0))
+    for (i=0; (*p != '\0') && (i < MaxPixelChannels); i++)
+    {
+      GetMagickToken(p,&p,token);
+      if (*token == ',')
+        GetMagickToken(p,&p,token);
+      levels[i]=StringToDouble(token,(char **) NULL);
+    }
+  for (i=0; i < MaxPixelChannels; i++)
+    if (fabs(levels[i]) >= 1)
+      levels[i]-=1.0;
+  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
+    return(MagickFalse);
+  status=MagickTrue;
+  progress=0;
+  image_view=AcquireAuthenticCacheView(image,exception);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+  #pragma omp parallel for schedule(static,4) shared(progress,status) \
+    magick_threads(image,image,image->rows,1)
+#endif
+  for (y=0; y < (ssize_t) image->rows; y++)
+  {
+    register ssize_t
+      x;
+
+    register Quantum
+      *restrict q;
+
+    if (status == MagickFalse)
+      continue;
+    q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
+    if (q == (Quantum *) NULL)
       {
-        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
-          "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
-        return(MagickFalse);
+        status=MagickFalse;
+        continue;
       }
-  }
-  /* Set channel levels from extra comma separated arguments
-     Default to 2, the single value given, or individual channel values
-  */
-#if 1
-  { /* parse directly as a comma separated list of integers */
-    char *p;
-
-    p = strchr((char *) threshold_map,',');
-    levels.red=0;
-    levels.green=0;
-    levels.blue=0;
-    levels.black=0;
-    levels.alpha=0;
-    if ( p != (char *)NULL && isdigit((int) ((unsigned char) *(++p))) )
-      levels.black = (unsigned int) strtoul(p, &p, 10);
-    else
-      levels.black = 2;
-
-    if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
-      levels.red=levels.black;
-    if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
-      levels.green=levels.black;
-    if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
-      levels.blue=levels.black;
-    if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
-        (image->colorspace == CMYKColorspace))
-      levels.black=levels.black;
-    if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
-        (image->matte != MagickFalse))
-      levels.alpha=levels.black;
-    /*
-      If more than a single number, each channel has a separate value.
-    */
-    if (p != (char *) NULL && *p == ',')
+    for (x=0; x < (ssize_t) image->columns; x++)
+    {
+      register ssize_t
+        i;
+
+      ssize_t
+        n;
+
+      n=0;
+      if (GetPixelReadMask(image,q) == 0)
+        {
+          q+=GetPixelChannels(image);
+          continue;
+        }
+      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
       {
-      p=strchr((char *) threshold_map,',');
-      p++;
-      if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
-        levels.red = (unsigned int) strtoul(p, &p, 10),   (void)(*p == ',' && p++);
-      if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
-        levels.green = (unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
-      if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
-        levels.blue = (unsigned int) strtoul(p, &p, 10),  (void)(*p == ',' && p++);
-      if ((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0 &&
-          (image->colorspace == CMYKColorspace))
-        levels.black=(unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
-      if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
-        levels.alpha = (unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
+        ssize_t
+          level,
+          threshold;
+
+        PixelChannel channel=GetPixelChannelChannel(image,i);
+        PixelTrait traits=GetPixelChannelTraits(image,channel);
+        if ((traits & UpdatePixelTrait) == 0)
+          continue;
+        if (fabs(levels[n++]) < MagickEpsilon)
+          continue;
+        threshold=(ssize_t) (QuantumScale*q[i]*(levels[n]*(map->divisor-1)+1));
+        level=threshold/(map->divisor-1);
+        threshold-=level*(map->divisor-1);
+        q[i]=ClampToQuantum((double) (level+(threshold >=
+          map->levels[(x % map->width)+map->width*(y % map->height)]))*
+          QuantumRange/levels[n]);
+        n++;
+      }
+      q+=GetPixelChannels(image);
     }
-  }
-#else
-  /* Parse level values as a geometry */
-  /* This difficult!
-   * How to map   GeometryInfo structure elements into
-   * PixelLongPacket structure elements, but according to channel?
-   * Note the channels list may skip elements!!!!
-   * EG  -channel BA  -ordered-dither map,2,3
-   * will need to map  g.rho -> l.blue, and g.sigma -> l.alpha
-   * A simpler way is needed, probably converting geometry to a temporary
-   * array, then using channel to advance the index into ssize_t pixel packet.
-   */
-#endif
+    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+      status=MagickFalse;
+    if (image->progress_monitor != (MagickProgressMonitor) NULL)
+      {
+        MagickBooleanType
+          proceed;
 
-#if 0
-printf("DEBUG levels  r=%u g=%u b=%u a=%u i=%u\n",
-     levels.red, levels.green, levels.blue, levels.alpha, levels.index);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+        #pragma omp critical (MagickCore_OrderedPosterizeImage)
 #endif
+        proceed=SetImageProgress(image,DitherImageTag,progress++,image->rows);
+        if (proceed == MagickFalse)
+          status=MagickFalse;
+      }
+  }
+  image_view=DestroyCacheView(image_view);
+  map=DestroyThresholdMap(map);
+  return(MagickTrue);
+}
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%                                                                             %
+%                                                                             %
+%                                                                             %
+%     P e r c e p t i b l e I m a g e                                         %
+%                                                                             %
+%                                                                             %
+%                                                                             %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%  PerceptibleImage() set each pixel whose value is less than |epsilon| to
+%  epsilon or -epsilon (whichever is closer) otherwise the pixel value remains
+%  unchanged.
+%
+%  The format of the PerceptibleImage method is:
+%
+%      MagickBooleanType PerceptibleImage(Image *image,const double epsilon,
+%        ExceptionInfo *exception)
+%
+%  A description of each parameter follows:
+%
+%    o image: the image.
+%
+%    o epsilon: the epsilon threshold (e.g. 1.0e-9).
+%
+%    o exception: return any errors or warnings in this structure.
+%
+*/
 
-  { /* Do the posterized ordered dithering of the image */
-    ssize_t
-      d;
+static inline Quantum PerceptibleThreshold(const Quantum quantum,
+  const double epsilon)
+{
+  double
+    sign;
 
-    /* d = number of psuedo-level divisions added between color levels */
-    d = map->divisor-1;
+  sign=(double) quantum < 0.0 ? -1.0 : 1.0;
+  if ((sign*quantum) >= epsilon)
+    return(quantum);
+  return((Quantum) (sign*epsilon));
+}
 
-    /* reduce levels to levels - 1 */
-    levels.red     = levels.red     ? levels.red-1     : 0;
-    levels.green   = levels.green   ? levels.green-1   : 0;
-    levels.blue    = levels.blue    ? levels.blue-1    : 0;
-    levels.black   = levels.black   ? levels.black-1   : 0;
-    levels.alpha = levels.alpha ? levels.alpha-1 : 0;
+MagickExport MagickBooleanType PerceptibleImage(Image *image,
+  const double epsilon,ExceptionInfo *exception)
+{
+#define PerceptibleImageTag  "Perceptible/Image"
 
-    if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
-      return(MagickFalse);
-    status=MagickTrue;
-    progress=0;
-    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++)
+  CacheView
+    *image_view;
+
+  MagickBooleanType
+    status;
+
+  MagickOffsetType
+    progress;
+
+  ssize_t
+    y;
+
+  assert(image != (Image *) NULL);
+  assert(image->signature == MagickSignature);
+  if (image->debug != MagickFalse)
+    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+  if (image->storage_class == PseudoClass)
     {
       register ssize_t
-        x;
+        i;
 
-      register Quantum
+      register PixelInfo
         *restrict q;
 
-      if (status == MagickFalse)
+      q=image->colormap;
+      for (i=0; i < (ssize_t) image->colors; i++)
+      {
+        q->red=(double) PerceptibleThreshold(ClampToQuantum(q->red),
+          epsilon);
+        q->green=(double) PerceptibleThreshold(ClampToQuantum(q->green),
+          epsilon);
+        q->blue=(double) PerceptibleThreshold(ClampToQuantum(q->blue),
+          epsilon);
+        q->alpha=(double) PerceptibleThreshold(ClampToQuantum(q->alpha),
+          epsilon);
+        q++;
+      }
+      return(SyncImage(image,exception));
+    }
+  /*
+    Perceptible image.
+  */
+  status=MagickTrue;
+  progress=0;
+  image_view=AcquireAuthenticCacheView(image,exception);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+  #pragma omp parallel for schedule(static,4) shared(progress,status) \
+    magick_threads(image,image,image->rows,1)
+#endif
+  for (y=0; y < (ssize_t) image->rows; y++)
+  {
+    register ssize_t
+      x;
+
+    register Quantum
+      *restrict q;
+
+    if (status == MagickFalse)
+      continue;
+    q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
+    if (q == (Quantum *) NULL)
+      {
+        status=MagickFalse;
         continue;
-      q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
-      if (q == (Quantum *) NULL)
+      }
+    for (x=0; x < (ssize_t) image->columns; x++)
+    {
+      register ssize_t
+        i;
+
+      if (GetPixelReadMask(image,q) == 0)
         {
-          status=MagickFalse;
+          q+=GetPixelChannels(image);
           continue;
         }
-      for (x=0; x < (ssize_t) image->columns; x++)
+      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
       {
-        register ssize_t
-          threshold,
-          t,
-          l;
-
-        /*
-          Figure out the dither threshold for this pixel
-          This must be a integer from 1 to map->divisor-1
-        */
-        threshold = map->levels[(x%map->width) +map->width*(y%map->height)];
-
-        /* Dither each channel in the image as appropriate
-          Notes on the integer Math...
-              total number of divisions = (levels-1)*(divisor-1)+1)
-              t1 = this colors psuedo_level =
-                      q->red * total_divisions / (QuantumRange+1)
-              l = posterization level       0..levels
-              t = dither threshold level    0..divisor-1  NB: 0 only on last
-              Each color_level is of size   QuantumRange / (levels-1)
-              NB: All input levels and divisor are already had 1 subtracted
-              Opacity is inverted so 'off' represents transparent.
-        */
-        if (levels.red != 0) {
-          t = (ssize_t) (QuantumScale*GetPixelRed(image,q)*(levels.red*d+1));
-          l = t/d;  t = t-l*d;
-          SetPixelRed(image,RoundToQuantum((MagickRealType)
-            ((l+(t >= threshold))*(MagickRealType) QuantumRange/levels.red)),q);
-        }
-        if (levels.green != 0) {
-          t = (ssize_t) (QuantumScale*GetPixelGreen(image,q)*
-            (levels.green*d+1));
-          l = t/d;  t = t-l*d;
-          SetPixelGreen(image,RoundToQuantum((MagickRealType)
-            ((l+(t >= threshold))*(MagickRealType) QuantumRange/levels.green)),q);
-        }
-        if (levels.blue != 0) {
-          t = (ssize_t) (QuantumScale*GetPixelBlue(image,q)*
-            (levels.blue*d+1));
-          l = t/d;  t = t-l*d;
-          SetPixelBlue(image,RoundToQuantum((MagickRealType)
-            ((l+(t >= threshold))*(MagickRealType) QuantumRange/levels.blue)),q);
-        }
-        if (levels.alpha != 0) {
-          t = (ssize_t) ((1.0-QuantumScale*GetPixelAlpha(image,q))*
-            (levels.alpha*d+1));
-          l = t/d;  t = t-l*d;
-          SetPixelAlpha(image,RoundToQuantum((MagickRealType)
-            ((1.0-l-(t >= threshold))*(MagickRealType) QuantumRange/
-            levels.alpha)),q);
-        }
-        if (levels.black != 0) {
-          t = (ssize_t) (QuantumScale*GetPixelBlack(image,q)*
-            (levels.black*d+1));
-          l = t/d;  t = t-l*d;
-          SetPixelBlack(image,RoundToQuantum((MagickRealType)
-            ((l+(t>=threshold))*(MagickRealType) QuantumRange/levels.black)),q);
-        }
-        q+=GetPixelChannels(image);
+        PixelChannel channel=GetPixelChannelChannel(image,i);
+        PixelTrait traits=GetPixelChannelTraits(image,channel);
+        if (traits == UndefinedPixelTrait)
+          continue;
+        q[i]=PerceptibleThreshold(q[i],epsilon);
       }
-      if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
-        status=MagickFalse;
-      if (image->progress_monitor != (MagickProgressMonitor) NULL)
-        {
-          MagickBooleanType
-            proceed;
+      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_OrderedPosterizeImage)
+        #pragma omp critical (MagickCore_PerceptibleImage)
 #endif
-          proceed=SetImageProgress(image,DitherImageTag,progress++,image->rows);
-          if (proceed == MagickFalse)
-            status=MagickFalse;
-        }
-    }
-    image_view=DestroyCacheView(image_view);
+        proceed=SetImageProgress(image,PerceptibleImageTag,progress++,image->rows);
+        if (proceed == MagickFalse)
+          status=MagickFalse;
+      }
   }
-  map=DestroyThresholdMap(map);
-  return(MagickTrue);
+  image_view=DestroyCacheView(image_view);
+  return(status);
 }
 \f
 /*
@@ -1479,6 +1637,10 @@ MagickExport MagickBooleanType RandomThresholdImage(Image *image,
   CacheView
     *image_view;
 
+  double
+    min_threshold,
+    max_threshold;
+
   GeometryInfo
     geometry_info;
 
@@ -1494,16 +1656,17 @@ MagickExport MagickBooleanType RandomThresholdImage(Image *image,
   PixelInfo
     threshold;
 
-  MagickRealType
-    min_threshold,
-    max_threshold;
-
   RandomInfo
     **restrict random_info;
 
   ssize_t
     y;
 
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+  unsigned long
+    key;
+#endif
+
   assert(image != (Image *) NULL);
   assert(image->signature == MagickSignature);
   if (image->debug != MagickFalse)
@@ -1512,9 +1675,11 @@ MagickExport MagickBooleanType RandomThresholdImage(Image *image,
   assert(exception->signature == MagickSignature);
   if (thresholds == (const char *) NULL)
     return(MagickTrue);
+  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
+    return(MagickFalse);
   GetPixelInfo(image,&threshold);
   min_threshold=0.0;
-  max_threshold=(MagickRealType) QuantumRange;
+  max_threshold=(double) QuantumRange;
   flags=ParseGeometry(thresholds,&geometry_info);
   min_threshold=geometry_info.rho;
   max_threshold=geometry_info.sigma;
@@ -1522,20 +1687,22 @@ MagickExport MagickBooleanType RandomThresholdImage(Image *image,
     max_threshold=min_threshold;
   if (strchr(thresholds,'%') != (char *) NULL)
     {
-      max_threshold*=(MagickRealType) (0.01*QuantumRange);
-      min_threshold*=(MagickRealType) (0.01*QuantumRange);
+      max_threshold*=(double) (0.01*QuantumRange);
+      min_threshold*=(double) (0.01*QuantumRange);
     }
   /*
     Random threshold image.
   */
   status=MagickTrue;
   progress=0;
-  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
-    return(MagickFalse);
   random_info=AcquireRandomInfoThreadSet();
-  image_view=AcquireCacheView(image);
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
-  #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+  key=GetRandomSecretKey(random_info[0]);
+#endif
+  image_view=AcquireAuthenticCacheView(image,exception);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+  #pragma omp parallel for schedule(static,4) shared(progress,status) \
+    magick_threads(image,image,image->rows,key == ~0UL)
 #endif
   for (y=0; y < (ssize_t) image->rows; y++)
   {
@@ -1561,28 +1728,29 @@ MagickExport MagickBooleanType RandomThresholdImage(Image *image,
       register ssize_t
         i;
 
+      if (GetPixelReadMask(image,q) == 0)
+        {
+          q+=GetPixelChannels(image);
+          continue;
+        }
       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
       {
-        MagickRealType
+        double
           threshold;
 
-        PixelTrait
-          traits;
-
-
-        traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
+        PixelChannel channel=GetPixelChannelChannel(image,i);
+        PixelTrait traits=GetPixelChannelTraits(image,channel);
         if ((traits & UpdatePixelTrait) == 0)
           continue;
-        if ((MagickRealType) q[i] < min_threshold)
+        if ((double) q[i] < min_threshold)
           threshold=min_threshold;
         else
-          if ((MagickRealType) q[i] > max_threshold)
+          if ((double) q[i] > max_threshold)
             threshold=max_threshold;
           else
-            threshold=(MagickRealType) (QuantumRange*
+            threshold=(double) (QuantumRange*
               GetPseudoRandomValue(random_info[id]));
-          q[i]=(Quantum) ((MagickRealType) q[i] <= threshold ? 0 :
-            QuantumRange);
+        q[i]=(double) q[i] <= threshold ? 0 : QuantumRange;
       }
       q+=GetPixelChannels(image);
     }
@@ -1594,7 +1762,7 @@ MagickExport MagickBooleanType RandomThresholdImage(Image *image,
           proceed;
 
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
-  #pragma omp critical (MagickCore_RandomThresholdImage)
+        #pragma omp critical (MagickCore_RandomThresholdImage)
 #endif
         proceed=SetImageProgress(image,ThresholdImageTag,progress++,
           image->rows);
@@ -1653,15 +1821,12 @@ MagickExport MagickBooleanType WhiteThresholdImage(Image *image,
   MagickOffsetType
     progress;
 
-  MagickRealType
-    threshold[5];
+  PixelInfo
+    threshold;
 
   MagickStatusType
     flags;
 
-  register ssize_t
-    i;
-
   ssize_t
     y;
 
@@ -1673,28 +1838,45 @@ MagickExport MagickBooleanType WhiteThresholdImage(Image *image,
     return(MagickTrue);
   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
     return(MagickFalse);
+  if (IsGrayColorspace(image->colorspace) != MagickFalse)
+    (void) TransformImageColorspace(image,sRGBColorspace,exception);
+  GetPixelInfo(image,&threshold);
   flags=ParseGeometry(thresholds,&geometry_info);
-  for (i=0; i < 5; i++)
-    threshold[i]=geometry_info.rho;
+  threshold.red=geometry_info.rho;
+  threshold.green=geometry_info.rho;
+  threshold.blue=geometry_info.rho;
+  threshold.black=geometry_info.rho;
+  threshold.alpha=100.0;
   if ((flags & SigmaValue) != 0)
-    threshold[1]=geometry_info.sigma;
+    threshold.green=geometry_info.sigma;
   if ((flags & XiValue) != 0)
-    threshold[2]=geometry_info.xi;
+    threshold.blue=geometry_info.xi;
   if ((flags & PsiValue) != 0)
-    threshold[3]=geometry_info.psi;
-  if ((flags & ChiValue) != 0)
-    threshold[4]=geometry_info.chi;
+    threshold.alpha=geometry_info.psi;
+  if (threshold.colorspace == CMYKColorspace)
+    {
+      if ((flags & PsiValue) != 0)
+        threshold.black=geometry_info.psi;
+      if ((flags & ChiValue) != 0)
+        threshold.alpha=geometry_info.chi;
+    }
   if ((flags & PercentValue) != 0)
-    for (i=0; i < 5; i++)
-      threshold[i]*=(QuantumRange/100.0);
+    {
+      threshold.red*=(MagickRealType) (QuantumRange/100.0);
+      threshold.green*=(MagickRealType) (QuantumRange/100.0);
+      threshold.blue*=(MagickRealType) (QuantumRange/100.0);
+      threshold.black*=(MagickRealType) (QuantumRange/100.0);
+      threshold.alpha*=(MagickRealType) (QuantumRange/100.0);
+    }
   /*
     White threshold image.
   */
   status=MagickTrue;
   progress=0;
-  image_view=AcquireCacheView(image);
+  image_view=AcquireAuthenticCacheView(image,exception);
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
-  #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+  #pragma omp parallel for schedule(static,4) shared(progress,status) \
+    magick_threads(image,image,image->rows,1)
 #endif
   for (y=0; y < (ssize_t) image->rows; y++)
   {
@@ -1714,22 +1896,27 @@ MagickExport MagickBooleanType WhiteThresholdImage(Image *image,
       }
     for (x=0; x < (ssize_t) image->columns; x++)
     {
+      double
+        pixel;
+
       register ssize_t
         i;
 
-      ssize_t
-        n;
-
-      n=0;
+      if (GetPixelReadMask(image,q) == 0)
+        {
+          q+=GetPixelChannels(image);
+          continue;
+        }
+      pixel=GetPixelIntensity(image,q);
       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
       {
-        PixelTrait
-          traits;
-
-        traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
+        PixelChannel channel=GetPixelChannelChannel(image,i);
+        PixelTrait traits=GetPixelChannelTraits(image,channel);
         if ((traits & UpdatePixelTrait) == 0)
           continue;
-        if ((MagickRealType) q[i] > threshold[n++ % 5])
+        if (image->channel_mask != DefaultChannels)
+          pixel=(double) q[i];
+        if (pixel > GetPixelInfoChannel(&threshold,channel))
           q[i]=QuantumRange;
       }
       q+=GetPixelChannels(image);
@@ -1742,7 +1929,7 @@ MagickExport MagickBooleanType WhiteThresholdImage(Image *image,
           proceed;
 
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
-  #pragma omp critical (MagickCore_WhiteThresholdImage)
+        #pragma omp critical (MagickCore_WhiteThresholdImage)
 #endif
         proceed=SetImageProgress(image,ThresholdImageTag,progress++,
           image->rows);