Home | History | Annotate | Download | only in MagickCore
      1 /*
      2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      3 %                                                                             %
      4 %                                                                             %
      5 %                                                                             %
      6 %       TTTTT  H   H  RRRR   EEEEE  SSSSS  H   H   OOO   L      DDDD          %
      7 %         T    H   H  R   R  E      SS     H   H  O   O  L      D   D         %
      8 %         T    HHHHH  RRRR   EEE     SSS   HHHHH  O   O  L      D   D         %
      9 %         T    H   H  R R    E         SS  H   H  O   O  L      D   D         %
     10 %         T    H   H  R  R   EEEEE  SSSSS  H   H   OOO   LLLLL  DDDD          %
     11 %                                                                             %
     12 %                                                                             %
     13 %                      MagickCore Image Threshold Methods                     %
     14 %                                                                             %
     15 %                               Software Design                               %
     16 %                                    Cristy                                   %
     17 %                                 October 1996                                %
     18 %                                                                             %
     19 %                                                                             %
     20 %  Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization      %
     21 %  dedicated to making software imaging solutions freely available.           %
     22 %                                                                             %
     23 %  You may not use this file except in compliance with the License.  You may  %
     24 %  obtain a copy of the License at                                            %
     25 %                                                                             %
     26 %    http://www.imagemagick.org/script/license.php                            %
     27 %                                                                             %
     28 %  Unless required by applicable law or agreed to in writing, software        %
     29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
     30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
     31 %  See the License for the specific language governing permissions and        %
     32 %  limitations under the License.                                             %
     33 %                                                                             %
     34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     35 %
     36 %
     37 %
     38 */
     39 
     40 /*
     42   Include declarations.
     43 */
     44 #include "MagickCore/studio.h"
     45 #include "MagickCore/property.h"
     46 #include "MagickCore/blob.h"
     47 #include "MagickCore/cache-view.h"
     48 #include "MagickCore/color.h"
     49 #include "MagickCore/color-private.h"
     50 #include "MagickCore/colormap.h"
     51 #include "MagickCore/colorspace.h"
     52 #include "MagickCore/colorspace-private.h"
     53 #include "MagickCore/configure.h"
     54 #include "MagickCore/constitute.h"
     55 #include "MagickCore/decorate.h"
     56 #include "MagickCore/draw.h"
     57 #include "MagickCore/enhance.h"
     58 #include "MagickCore/exception.h"
     59 #include "MagickCore/exception-private.h"
     60 #include "MagickCore/effect.h"
     61 #include "MagickCore/fx.h"
     62 #include "MagickCore/gem.h"
     63 #include "MagickCore/geometry.h"
     64 #include "MagickCore/image-private.h"
     65 #include "MagickCore/list.h"
     66 #include "MagickCore/log.h"
     67 #include "MagickCore/memory_.h"
     68 #include "MagickCore/monitor.h"
     69 #include "MagickCore/monitor-private.h"
     70 #include "MagickCore/montage.h"
     71 #include "MagickCore/option.h"
     72 #include "MagickCore/pixel-accessor.h"
     73 #include "MagickCore/pixel-private.h"
     74 #include "MagickCore/quantize.h"
     75 #include "MagickCore/quantum.h"
     76 #include "MagickCore/random_.h"
     77 #include "MagickCore/random-private.h"
     78 #include "MagickCore/resize.h"
     79 #include "MagickCore/resource_.h"
     80 #include "MagickCore/segment.h"
     81 #include "MagickCore/shear.h"
     82 #include "MagickCore/signature-private.h"
     83 #include "MagickCore/string_.h"
     84 #include "MagickCore/string-private.h"
     85 #include "MagickCore/thread-private.h"
     86 #include "MagickCore/threshold.h"
     87 #include "MagickCore/token.h"
     88 #include "MagickCore/transform.h"
     89 #include "MagickCore/xml-tree.h"
     90 #include "MagickCore/xml-tree-private.h"
     91 
     92 /*
     94   Define declarations.
     95 */
     96 #define ThresholdsFilename  "thresholds.xml"
     97 
     98 /*
    100   Typedef declarations.
    101 */
    102 struct _ThresholdMap
    103 {
    104   char
    105     *map_id,
    106     *description;
    107 
    108   size_t
    109     width,
    110     height;
    111 
    112   ssize_t
    113     divisor,
    114     *levels;
    115 };
    116 
    117 /*
    119   Static declarations.
    120 */
    121 static const char
    122   *MinimalThresholdMap =
    123     "<?xml version=\"1.0\"?>"
    124     "<thresholds>"
    125     "  <threshold map=\"threshold\" alias=\"1x1\">"
    126     "    <description>Threshold 1x1 (non-dither)</description>"
    127     "    <levels width=\"1\" height=\"1\" divisor=\"2\">"
    128     "        1"
    129     "    </levels>"
    130     "  </threshold>"
    131     "  <threshold map=\"checks\" alias=\"2x1\">"
    132     "    <description>Checkerboard 2x1 (dither)</description>"
    133     "    <levels width=\"2\" height=\"2\" divisor=\"3\">"
    134     "       1 2"
    135     "       2 1"
    136     "    </levels>"
    137     "  </threshold>"
    138     "</thresholds>";
    139 
    140 /*
    142   Forward declarations.
    143 */
    144 static ThresholdMap
    145   *GetThresholdMapFile(const char *,const char *,const char *,ExceptionInfo *);
    146 
    147 /*
    149 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    150 %                                                                             %
    151 %                                                                             %
    152 %                                                                             %
    153 %     A d a p t i v e T h r e s h o l d I m a g e                             %
    154 %                                                                             %
    155 %                                                                             %
    156 %                                                                             %
    157 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    158 %
    159 %  AdaptiveThresholdImage() selects an individual threshold for each pixel
    160 %  based on the range of intensity values in its local neighborhood.  This
    161 %  allows for thresholding of an image whose global intensity histogram
    162 %  doesn't contain distinctive peaks.
    163 %
    164 %  The format of the AdaptiveThresholdImage method is:
    165 %
    166 %      Image *AdaptiveThresholdImage(const Image *image,const size_t width,
    167 %        const size_t height,const double bias,ExceptionInfo *exception)
    168 %
    169 %  A description of each parameter follows:
    170 %
    171 %    o image: the image.
    172 %
    173 %    o width: the width of the local neighborhood.
    174 %
    175 %    o height: the height of the local neighborhood.
    176 %
    177 %    o bias: the mean bias.
    178 %
    179 %    o exception: return any errors or warnings in this structure.
    180 %
    181 */
    182 MagickExport Image *AdaptiveThresholdImage(const Image *image,
    183   const size_t width,const size_t height,const double bias,
    184   ExceptionInfo *exception)
    185 {
    186 #define AdaptiveThresholdImageTag  "AdaptiveThreshold/Image"
    187 
    188   CacheView
    189     *image_view,
    190     *threshold_view;
    191 
    192   Image
    193     *threshold_image;
    194 
    195   MagickBooleanType
    196     status;
    197 
    198   MagickOffsetType
    199     progress;
    200 
    201   MagickSizeType
    202     number_pixels;
    203 
    204   ssize_t
    205     y;
    206 
    207   /*
    208     Initialize threshold image attributes.
    209   */
    210   assert(image != (Image *) NULL);
    211   assert(image->signature == MagickCoreSignature);
    212   if (image->debug != MagickFalse)
    213     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
    214   assert(exception != (ExceptionInfo *) NULL);
    215   assert(exception->signature == MagickCoreSignature);
    216   threshold_image=CloneImage(image,image->columns,image->rows,MagickTrue,
    217     exception);
    218   if (threshold_image == (Image *) NULL)
    219     return((Image *) NULL);
    220   status=SetImageStorageClass(threshold_image,DirectClass,exception);
    221   if (status == MagickFalse)
    222     {
    223       threshold_image=DestroyImage(threshold_image);
    224       return((Image *) NULL);
    225     }
    226   /*
    227     Threshold image.
    228   */
    229   status=MagickTrue;
    230   progress=0;
    231   number_pixels=(MagickSizeType) width*height;
    232   image_view=AcquireVirtualCacheView(image,exception);
    233   threshold_view=AcquireAuthenticCacheView(threshold_image,exception);
    234 #if defined(MAGICKCORE_OPENMP_SUPPORT)
    235   #pragma omp parallel for schedule(static,4) shared(progress,status) \
    236     magick_threads(image,threshold_image,image->rows,1)
    237 #endif
    238   for (y=0; y < (ssize_t) image->rows; y++)
    239   {
    240     double
    241       channel_bias[MaxPixelChannels],
    242       channel_sum[MaxPixelChannels];
    243 
    244     register const Quantum
    245       *magick_restrict p,
    246       *magick_restrict pixels;
    247 
    248     register Quantum
    249       *magick_restrict q;
    250 
    251     register ssize_t
    252       i,
    253       x;
    254 
    255     ssize_t
    256       center,
    257       u,
    258       v;
    259 
    260     if (status == MagickFalse)
    261       continue;
    262     p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
    263       (height/2L),image->columns+width,height,exception);
    264     q=QueueCacheViewAuthenticPixels(threshold_view,0,y,threshold_image->columns,
    265       1,exception);
    266     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
    267       {
    268         status=MagickFalse;
    269         continue;
    270       }
    271     center=(ssize_t) GetPixelChannels(image)*(image->columns+width)*(height/2L)+
    272       GetPixelChannels(image)*(width/2);
    273     for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
    274     {
    275       PixelChannel channel=GetPixelChannelChannel(image,i);
    276       PixelTrait traits=GetPixelChannelTraits(image,channel);
    277       PixelTrait threshold_traits=GetPixelChannelTraits(threshold_image,
    278         channel);
    279       if ((traits == UndefinedPixelTrait) ||
    280           (threshold_traits == UndefinedPixelTrait))
    281         continue;
    282       if (((threshold_traits & CopyPixelTrait) != 0) ||
    283           (GetPixelReadMask(image,p) == 0))
    284         {
    285           SetPixelChannel(threshold_image,channel,p[center+i],q);
    286           continue;
    287         }
    288       pixels=p;
    289       channel_bias[channel]=0.0;
    290       channel_sum[channel]=0.0;
    291       for (v=0; v < (ssize_t) height; v++)
    292       {
    293         for (u=0; u < (ssize_t) width; u++)
    294         {
    295           if (u == (ssize_t) (width-1))
    296             channel_bias[channel]+=pixels[i];
    297           channel_sum[channel]+=pixels[i];
    298           pixels+=GetPixelChannels(image);
    299         }
    300         pixels+=GetPixelChannels(image)*image->columns;
    301       }
    302     }
    303     for (x=0; x < (ssize_t) image->columns; x++)
    304     {
    305       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
    306       {
    307         double
    308           mean;
    309 
    310         PixelChannel channel=GetPixelChannelChannel(image,i);
    311         PixelTrait traits=GetPixelChannelTraits(image,channel);
    312         PixelTrait threshold_traits=GetPixelChannelTraits(threshold_image,
    313           channel);
    314         if ((traits == UndefinedPixelTrait) ||
    315             (threshold_traits == UndefinedPixelTrait))
    316           continue;
    317         if (((threshold_traits & CopyPixelTrait) != 0) ||
    318             (GetPixelReadMask(image,p) == 0))
    319           {
    320             SetPixelChannel(threshold_image,channel,p[center+i],q);
    321             continue;
    322           }
    323         channel_sum[channel]-=channel_bias[channel];
    324         channel_bias[channel]=0.0;
    325         pixels=p;
    326         for (v=0; v < (ssize_t) height; v++)
    327         {
    328           channel_bias[channel]+=pixels[i];
    329           pixels+=(width-1)*GetPixelChannels(image);
    330           channel_sum[channel]+=pixels[i];
    331           pixels+=GetPixelChannels(image)*(image->columns+1);
    332         }
    333         mean=(double) (channel_sum[channel]/number_pixels+bias);
    334         SetPixelChannel(threshold_image,channel,(Quantum) ((double)
    335           p[center+i] <= mean ? 0 : QuantumRange),q);
    336       }
    337       p+=GetPixelChannels(image);
    338       q+=GetPixelChannels(threshold_image);
    339     }
    340     if (SyncCacheViewAuthenticPixels(threshold_view,exception) == MagickFalse)
    341       status=MagickFalse;
    342     if (image->progress_monitor != (MagickProgressMonitor) NULL)
    343       {
    344         MagickBooleanType
    345           proceed;
    346 
    347 #if defined(MAGICKCORE_OPENMP_SUPPORT)
    348         #pragma omp critical (MagickCore_AdaptiveThresholdImage)
    349 #endif
    350         proceed=SetImageProgress(image,AdaptiveThresholdImageTag,progress++,
    351           image->rows);
    352         if (proceed == MagickFalse)
    353           status=MagickFalse;
    354       }
    355   }
    356   threshold_image->type=image->type;
    357   threshold_view=DestroyCacheView(threshold_view);
    358   image_view=DestroyCacheView(image_view);
    359   if (status == MagickFalse)
    360     threshold_image=DestroyImage(threshold_image);
    361   return(threshold_image);
    362 }
    363 
    364 /*
    366 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    367 %                                                                             %
    368 %                                                                             %
    369 %                                                                             %
    370 %     B i l e v e l I m a g e                                                 %
    371 %                                                                             %
    372 %                                                                             %
    373 %                                                                             %
    374 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    375 %
    376 %  BilevelImage() changes the value of individual pixels based on the
    377 %  intensity of each pixel channel.  The result is a high-contrast image.
    378 %
    379 %  More precisely each channel value of the image is 'thresholded' so that if
    380 %  it is equal to or less than the given value it is set to zero, while any
    381 %  value greater than that give is set to it maximum or QuantumRange.
    382 %
    383 %  This function is what is used to implement the "-threshold" operator for
    384 %  the command line API.
    385 %
    386 %  If the default channel setting is given the image is thresholded using just
    387 %  the gray 'intensity' of the image, rather than the individual channels.
    388 %
    389 %  The format of the BilevelImage method is:
    390 %
    391 %      MagickBooleanType BilevelImage(Image *image,const double threshold,
    392 %        ExceptionInfo *exception)
    393 %
    394 %  A description of each parameter follows:
    395 %
    396 %    o image: the image.
    397 %
    398 %    o threshold: define the threshold values.
    399 %
    400 %    o exception: return any errors or warnings in this structure.
    401 %
    402 %  Aside: You can get the same results as operator using LevelImages()
    403 %  with the 'threshold' value for both the black_point and the white_point.
    404 %
    405 */
    406 MagickExport MagickBooleanType BilevelImage(Image *image,const double threshold,
    407   ExceptionInfo *exception)
    408 {
    409 #define ThresholdImageTag  "Threshold/Image"
    410 
    411   CacheView
    412     *image_view;
    413 
    414   MagickBooleanType
    415     status;
    416 
    417   MagickOffsetType
    418     progress;
    419 
    420   ssize_t
    421     y;
    422 
    423   assert(image != (Image *) NULL);
    424   assert(image->signature == MagickCoreSignature);
    425   if (image->debug != MagickFalse)
    426     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
    427   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
    428     return(MagickFalse);
    429   if (IsGrayColorspace(image->colorspace) != MagickFalse)
    430     (void) SetImageColorspace(image,sRGBColorspace,exception);
    431   /*
    432     Bilevel threshold image.
    433   */
    434   status=MagickTrue;
    435   progress=0;
    436   image_view=AcquireAuthenticCacheView(image,exception);
    437 #if defined(MAGICKCORE_OPENMP_SUPPORT)
    438   #pragma omp parallel for schedule(static,4) shared(progress,status) \
    439     magick_threads(image,image,image->rows,1)
    440 #endif
    441   for (y=0; y < (ssize_t) image->rows; y++)
    442   {
    443     register ssize_t
    444       x;
    445 
    446     register Quantum
    447       *magick_restrict q;
    448 
    449     if (status == MagickFalse)
    450       continue;
    451     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
    452     if (q == (Quantum *) NULL)
    453       {
    454         status=MagickFalse;
    455         continue;
    456       }
    457     for (x=0; x < (ssize_t) image->columns; x++)
    458     {
    459       double
    460         pixel;
    461 
    462       register ssize_t
    463         i;
    464 
    465       if (GetPixelReadMask(image,q) == 0)
    466         {
    467           q+=GetPixelChannels(image);
    468           continue;
    469         }
    470       pixel=GetPixelIntensity(image,q);
    471       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
    472       {
    473         PixelChannel channel=GetPixelChannelChannel(image,i);
    474         PixelTrait traits=GetPixelChannelTraits(image,channel);
    475         if ((traits & UpdatePixelTrait) == 0)
    476           continue;
    477         if (image->channel_mask != DefaultChannels)
    478           pixel=(double) q[i];
    479         q[i]=(Quantum) (pixel <= threshold ? 0 : QuantumRange);
    480       }
    481       q+=GetPixelChannels(image);
    482     }
    483     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
    484       status=MagickFalse;
    485     if (image->progress_monitor != (MagickProgressMonitor) NULL)
    486       {
    487         MagickBooleanType
    488           proceed;
    489 
    490 #if defined(MAGICKCORE_OPENMP_SUPPORT)
    491         #pragma omp critical (MagickCore_BilevelImage)
    492 #endif
    493         proceed=SetImageProgress(image,ThresholdImageTag,progress++,
    494           image->rows);
    495         if (proceed == MagickFalse)
    496           status=MagickFalse;
    497       }
    498   }
    499   image_view=DestroyCacheView(image_view);
    500   return(status);
    501 }
    502 
    503 /*
    505 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    506 %                                                                             %
    507 %                                                                             %
    508 %                                                                             %
    509 %     B l a c k T h r e s h o l d I m a g e                                   %
    510 %                                                                             %
    511 %                                                                             %
    512 %                                                                             %
    513 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    514 %
    515 %  BlackThresholdImage() is like ThresholdImage() but forces all pixels below
    516 %  the threshold into black while leaving all pixels at or above the threshold
    517 %  unchanged.
    518 %
    519 %  The format of the BlackThresholdImage method is:
    520 %
    521 %      MagickBooleanType BlackThresholdImage(Image *image,
    522 %        const char *threshold,ExceptionInfo *exception)
    523 %
    524 %  A description of each parameter follows:
    525 %
    526 %    o image: the image.
    527 %
    528 %    o threshold: define the threshold value.
    529 %
    530 %    o exception: return any errors or warnings in this structure.
    531 %
    532 */
    533 MagickExport MagickBooleanType BlackThresholdImage(Image *image,
    534   const char *thresholds,ExceptionInfo *exception)
    535 {
    536 #define ThresholdImageTag  "Threshold/Image"
    537 
    538   CacheView
    539     *image_view;
    540 
    541   GeometryInfo
    542     geometry_info;
    543 
    544   MagickBooleanType
    545     status;
    546 
    547   MagickOffsetType
    548     progress;
    549 
    550   PixelInfo
    551     threshold;
    552 
    553   MagickStatusType
    554     flags;
    555 
    556   ssize_t
    557     y;
    558 
    559   assert(image != (Image *) NULL);
    560   assert(image->signature == MagickCoreSignature);
    561   if (image->debug != MagickFalse)
    562     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
    563   if (thresholds == (const char *) NULL)
    564     return(MagickTrue);
    565   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
    566     return(MagickFalse);
    567   if (IsGrayColorspace(image->colorspace) != MagickFalse)
    568     (void) SetImageColorspace(image,sRGBColorspace,exception);
    569   GetPixelInfo(image,&threshold);
    570   flags=ParseGeometry(thresholds,&geometry_info);
    571   threshold.red=geometry_info.rho;
    572   threshold.green=geometry_info.rho;
    573   threshold.blue=geometry_info.rho;
    574   threshold.black=geometry_info.rho;
    575   threshold.alpha=100.0;
    576   if ((flags & SigmaValue) != 0)
    577     threshold.green=geometry_info.sigma;
    578   if ((flags & XiValue) != 0)
    579     threshold.blue=geometry_info.xi;
    580   if ((flags & PsiValue) != 0)
    581     threshold.alpha=geometry_info.psi;
    582   if (threshold.colorspace == CMYKColorspace)
    583     {
    584       if ((flags & PsiValue) != 0)
    585         threshold.black=geometry_info.psi;
    586       if ((flags & ChiValue) != 0)
    587         threshold.alpha=geometry_info.chi;
    588     }
    589   if ((flags & PercentValue) != 0)
    590     {
    591       threshold.red*=(MagickRealType) (QuantumRange/100.0);
    592       threshold.green*=(MagickRealType) (QuantumRange/100.0);
    593       threshold.blue*=(MagickRealType) (QuantumRange/100.0);
    594       threshold.black*=(MagickRealType) (QuantumRange/100.0);
    595       threshold.alpha*=(MagickRealType) (QuantumRange/100.0);
    596     }
    597   /*
    598     White threshold image.
    599   */
    600   status=MagickTrue;
    601   progress=0;
    602   image_view=AcquireAuthenticCacheView(image,exception);
    603 #if defined(MAGICKCORE_OPENMP_SUPPORT)
    604   #pragma omp parallel for schedule(static,4) shared(progress,status) \
    605     magick_threads(image,image,image->rows,1)
    606 #endif
    607   for (y=0; y < (ssize_t) image->rows; y++)
    608   {
    609     register ssize_t
    610       x;
    611 
    612     register Quantum
    613       *magick_restrict q;
    614 
    615     if (status == MagickFalse)
    616       continue;
    617     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
    618     if (q == (Quantum *) NULL)
    619       {
    620         status=MagickFalse;
    621         continue;
    622       }
    623     for (x=0; x < (ssize_t) image->columns; x++)
    624     {
    625       double
    626         pixel;
    627 
    628       register ssize_t
    629         i;
    630 
    631       if (GetPixelReadMask(image,q) == 0)
    632         {
    633           q+=GetPixelChannels(image);
    634           continue;
    635         }
    636       pixel=GetPixelIntensity(image,q);
    637       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
    638       {
    639         PixelChannel channel=GetPixelChannelChannel(image,i);
    640         PixelTrait traits=GetPixelChannelTraits(image,channel);
    641         if ((traits & UpdatePixelTrait) == 0)
    642           continue;
    643         if (image->channel_mask != DefaultChannels)
    644           pixel=(double) q[i];
    645         if (pixel < GetPixelInfoChannel(&threshold,channel))
    646           q[i]=(Quantum) 0;
    647       }
    648       q+=GetPixelChannels(image);
    649     }
    650     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
    651       status=MagickFalse;
    652     if (image->progress_monitor != (MagickProgressMonitor) NULL)
    653       {
    654         MagickBooleanType
    655           proceed;
    656 
    657 #if defined(MAGICKCORE_OPENMP_SUPPORT)
    658         #pragma omp critical (MagickCore_BlackThresholdImage)
    659 #endif
    660         proceed=SetImageProgress(image,ThresholdImageTag,progress++,
    661           image->rows);
    662         if (proceed == MagickFalse)
    663           status=MagickFalse;
    664       }
    665   }
    666   image_view=DestroyCacheView(image_view);
    667   return(status);
    668 }
    669 
    670 /*
    672 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    673 %                                                                             %
    674 %                                                                             %
    675 %                                                                             %
    676 %     C l a m p I m a g e                                                     %
    677 %                                                                             %
    678 %                                                                             %
    679 %                                                                             %
    680 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    681 %
    682 %  ClampImage() set each pixel whose value is below zero to zero and any the
    683 %  pixel whose value is above the quantum range to the quantum range (e.g.
    684 %  65535) otherwise the pixel value remains unchanged.
    685 %
    686 %  The format of the ClampImage method is:
    687 %
    688 %      MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
    689 %
    690 %  A description of each parameter follows:
    691 %
    692 %    o image: the image.
    693 %
    694 %    o exception: return any errors or warnings in this structure.
    695 %
    696 */
    697 
    698 MagickExport MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
    699 {
    700 #define ClampImageTag  "Clamp/Image"
    701 
    702   CacheView
    703     *image_view;
    704 
    705   MagickBooleanType
    706     status;
    707 
    708   MagickOffsetType
    709     progress;
    710 
    711   ssize_t
    712     y;
    713 
    714   assert(image != (Image *) NULL);
    715   assert(image->signature == MagickCoreSignature);
    716   if (image->debug != MagickFalse)
    717     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
    718   if (image->storage_class == PseudoClass)
    719     {
    720       register ssize_t
    721         i;
    722 
    723       register PixelInfo
    724         *magick_restrict q;
    725 
    726       q=image->colormap;
    727       for (i=0; i < (ssize_t) image->colors; i++)
    728       {
    729         q->red=(double) ClampPixel(q->red);
    730         q->green=(double) ClampPixel(q->green);
    731         q->blue=(double) ClampPixel(q->blue);
    732         q->alpha=(double) ClampPixel(q->alpha);
    733         q++;
    734       }
    735       return(SyncImage(image,exception));
    736     }
    737   /*
    738     Clamp image.
    739   */
    740   status=MagickTrue;
    741   progress=0;
    742   image_view=AcquireAuthenticCacheView(image,exception);
    743 #if defined(MAGICKCORE_OPENMP_SUPPORT)
    744   #pragma omp parallel for schedule(static,4) shared(progress,status) \
    745     magick_threads(image,image,image->rows,1)
    746 #endif
    747   for (y=0; y < (ssize_t) image->rows; y++)
    748   {
    749     register ssize_t
    750       x;
    751 
    752     register Quantum
    753       *magick_restrict q;
    754 
    755     if (status == MagickFalse)
    756       continue;
    757     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
    758     if (q == (Quantum *) NULL)
    759       {
    760         status=MagickFalse;
    761         continue;
    762       }
    763     for (x=0; x < (ssize_t) image->columns; x++)
    764     {
    765       register ssize_t
    766         i;
    767 
    768       if (GetPixelReadMask(image,q) == 0)
    769         {
    770           q+=GetPixelChannels(image);
    771           continue;
    772         }
    773       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
    774       {
    775         PixelChannel channel=GetPixelChannelChannel(image,i);
    776         PixelTrait traits=GetPixelChannelTraits(image,channel);
    777         if ((traits & UpdatePixelTrait) == 0)
    778           continue;
    779         q[i]=ClampPixel(q[i]);
    780       }
    781       q+=GetPixelChannels(image);
    782     }
    783     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
    784       status=MagickFalse;
    785     if (image->progress_monitor != (MagickProgressMonitor) NULL)
    786       {
    787         MagickBooleanType
    788           proceed;
    789 
    790 #if defined(MAGICKCORE_OPENMP_SUPPORT)
    791         #pragma omp critical (MagickCore_ClampImage)
    792 #endif
    793         proceed=SetImageProgress(image,ClampImageTag,progress++,image->rows);
    794         if (proceed == MagickFalse)
    795           status=MagickFalse;
    796       }
    797   }
    798   image_view=DestroyCacheView(image_view);
    799   return(status);
    800 }
    801 
    802 /*
    804 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    805 %                                                                             %
    806 %                                                                             %
    807 %                                                                             %
    808 %  D e s t r o y T h r e s h o l d M a p                                      %
    809 %                                                                             %
    810 %                                                                             %
    811 %                                                                             %
    812 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    813 %
    814 %  DestroyThresholdMap() de-allocate the given ThresholdMap
    815 %
    816 %  The format of the ListThresholdMaps method is:
    817 %
    818 %      ThresholdMap *DestroyThresholdMap(Threshold *map)
    819 %
    820 %  A description of each parameter follows.
    821 %
    822 %    o map:    Pointer to the Threshold map to destroy
    823 %
    824 */
    825 MagickExport ThresholdMap *DestroyThresholdMap(ThresholdMap *map)
    826 {
    827   assert(map != (ThresholdMap *) NULL);
    828   if (map->map_id != (char *) NULL)
    829     map->map_id=DestroyString(map->map_id);
    830   if (map->description != (char *) NULL)
    831     map->description=DestroyString(map->description);
    832   if (map->levels != (ssize_t *) NULL)
    833     map->levels=(ssize_t *) RelinquishMagickMemory(map->levels);
    834   map=(ThresholdMap *) RelinquishMagickMemory(map);
    835   return(map);
    836 }
    837 
    838 /*
    840 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    841 %                                                                             %
    842 %                                                                             %
    843 %                                                                             %
    844 %  G e t T h r e s h o l d M a p                                              %
    845 %                                                                             %
    846 %                                                                             %
    847 %                                                                             %
    848 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    849 %
    850 %  GetThresholdMap() loads and searches one or more threshold map files for the
    851 %  map matching the given name or alias.
    852 %
    853 %  The format of the GetThresholdMap method is:
    854 %
    855 %      ThresholdMap *GetThresholdMap(const char *map_id,
    856 %        ExceptionInfo *exception)
    857 %
    858 %  A description of each parameter follows.
    859 %
    860 %    o map_id:  ID of the map to look for.
    861 %
    862 %    o exception: return any errors or warnings in this structure.
    863 %
    864 */
    865 MagickExport ThresholdMap *GetThresholdMap(const char *map_id,
    866   ExceptionInfo *exception)
    867 {
    868   ThresholdMap
    869     *map;
    870 
    871   map=GetThresholdMapFile(MinimalThresholdMap,"built-in",map_id,exception);
    872   if (map != (ThresholdMap *) NULL)
    873     return(map);
    874 #if !defined(MAGICKCORE_ZERO_CONFIGURATION_SUPPORT)
    875   {
    876     const StringInfo
    877       *option;
    878 
    879     LinkedListInfo
    880       *options;
    881 
    882     options=GetConfigureOptions(ThresholdsFilename,exception);
    883     option=(const StringInfo *) GetNextValueInLinkedList(options);
    884     while (option != (const StringInfo *) NULL)
    885     {
    886       map=GetThresholdMapFile((const char *) GetStringInfoDatum(option),
    887         GetStringInfoPath(option),map_id,exception);
    888       if (map != (ThresholdMap *) NULL)
    889         break;
    890       option=(const StringInfo *) GetNextValueInLinkedList(options);
    891     }
    892     options=DestroyConfigureOptions(options);
    893   }
    894 #endif
    895   return(map);
    896 }
    897 
    898 /*
    900 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    901 %                                                                             %
    902 %                                                                             %
    903 %                                                                             %
    904 +  G e t T h r e s h o l d M a p F i l e                                      %
    905 %                                                                             %
    906 %                                                                             %
    907 %                                                                             %
    908 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    909 %
    910 %  GetThresholdMapFile() look for a given threshold map name or alias in the
    911 %  given XML file data, and return the allocated the map when found.
    912 %
    913 %  The format of the ListThresholdMaps method is:
    914 %
    915 %      ThresholdMap *GetThresholdMap(const char *xml,const char *filename,
    916 %         const char *map_id,ExceptionInfo *exception)
    917 %
    918 %  A description of each parameter follows.
    919 %
    920 %    o xml:  The threshold map list in XML format.
    921 %
    922 %    o filename:  The threshold map XML filename.
    923 %
    924 %    o map_id:  ID of the map to look for in XML list.
    925 %
    926 %    o exception: return any errors or warnings in this structure.
    927 %
    928 */
    929 static ThresholdMap *GetThresholdMapFile(const char *xml,const char *filename,
    930   const char *map_id,ExceptionInfo *exception)
    931 {
    932   char
    933     *p;
    934 
    935   const char
    936     *attribute,
    937     *content;
    938 
    939   double
    940     value;
    941 
    942   register ssize_t
    943     i;
    944 
    945   ThresholdMap
    946     *map;
    947 
    948   XMLTreeInfo
    949     *description,
    950     *levels,
    951     *threshold,
    952     *thresholds;
    953 
    954   (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
    955     "Loading threshold map file \"%s\" ...",filename);
    956   map=(ThresholdMap *) NULL;
    957   thresholds=NewXMLTree(xml,exception);
    958   if (thresholds == (XMLTreeInfo *) NULL)
    959     return(map);
    960   for (threshold=GetXMLTreeChild(thresholds,"threshold");
    961        threshold != (XMLTreeInfo *) NULL;
    962        threshold=GetNextXMLTreeTag(threshold))
    963   {
    964     attribute=GetXMLTreeAttribute(threshold,"map");
    965     if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
    966       break;
    967     attribute=GetXMLTreeAttribute(threshold,"alias");
    968     if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
    969       break;
    970   }
    971   if (threshold == (XMLTreeInfo *) NULL)
    972     {
    973       thresholds=DestroyXMLTree(thresholds);
    974       return(map);
    975     }
    976   description=GetXMLTreeChild(threshold,"description");
    977   if (description == (XMLTreeInfo *) NULL)
    978     {
    979       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
    980         "XmlMissingElement", "<description>, map \"%s\"",map_id);
    981       thresholds=DestroyXMLTree(thresholds);
    982       return(map);
    983     }
    984   levels=GetXMLTreeChild(threshold,"levels");
    985   if (levels == (XMLTreeInfo *) NULL)
    986     {
    987       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
    988         "XmlMissingElement", "<levels>, map \"%s\"", map_id);
    989       thresholds=DestroyXMLTree(thresholds);
    990       return(map);
    991     }
    992   map=(ThresholdMap *) AcquireMagickMemory(sizeof(ThresholdMap));
    993   if (map == (ThresholdMap *) NULL)
    994     ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
    995   map->map_id=(char *) NULL;
    996   map->description=(char *) NULL;
    997   map->levels=(ssize_t *) NULL;
    998   attribute=GetXMLTreeAttribute(threshold,"map");
    999   if (attribute != (char *) NULL)
   1000     map->map_id=ConstantString(attribute);
   1001   content=GetXMLTreeContent(description);
   1002   if (content != (char *) NULL)
   1003     map->description=ConstantString(content);
   1004   attribute=GetXMLTreeAttribute(levels,"width");
   1005   if (attribute == (char *) NULL)
   1006     {
   1007       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
   1008         "XmlMissingAttribute", "<levels width>, map \"%s\"",map_id);
   1009       thresholds=DestroyXMLTree(thresholds);
   1010       map=DestroyThresholdMap(map);
   1011       return(map);
   1012     }
   1013   map->width=StringToUnsignedLong(attribute);
   1014   if (map->width == 0)
   1015     {
   1016       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
   1017        "XmlInvalidAttribute", "<levels width>, map \"%s\"",map_id);
   1018       thresholds=DestroyXMLTree(thresholds);
   1019       map=DestroyThresholdMap(map);
   1020       return(map);
   1021     }
   1022   attribute=GetXMLTreeAttribute(levels,"height");
   1023   if (attribute == (char *) NULL)
   1024     {
   1025       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
   1026         "XmlMissingAttribute", "<levels height>, map \"%s\"",map_id);
   1027       thresholds=DestroyXMLTree(thresholds);
   1028       map=DestroyThresholdMap(map);
   1029       return(map);
   1030     }
   1031   map->height=StringToUnsignedLong(attribute);
   1032   if (map->height == 0)
   1033     {
   1034       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
   1035         "XmlInvalidAttribute", "<levels height>, map \"%s\"",map_id);
   1036       thresholds=DestroyXMLTree(thresholds);
   1037       map=DestroyThresholdMap(map);
   1038       return(map);
   1039     }
   1040   attribute=GetXMLTreeAttribute(levels,"divisor");
   1041   if (attribute == (char *) NULL)
   1042     {
   1043       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
   1044         "XmlMissingAttribute", "<levels divisor>, map \"%s\"",map_id);
   1045       thresholds=DestroyXMLTree(thresholds);
   1046       map=DestroyThresholdMap(map);
   1047       return(map);
   1048     }
   1049   map->divisor=(ssize_t) StringToLong(attribute);
   1050   if (map->divisor < 2)
   1051     {
   1052       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
   1053         "XmlInvalidAttribute", "<levels divisor>, map \"%s\"",map_id);
   1054       thresholds=DestroyXMLTree(thresholds);
   1055       map=DestroyThresholdMap(map);
   1056       return(map);
   1057     }
   1058   content=GetXMLTreeContent(levels);
   1059   if (content == (char *) NULL)
   1060     {
   1061       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
   1062         "XmlMissingContent", "<levels>, map \"%s\"",map_id);
   1063       thresholds=DestroyXMLTree(thresholds);
   1064       map=DestroyThresholdMap(map);
   1065       return(map);
   1066     }
   1067   map->levels=(ssize_t *) AcquireQuantumMemory((size_t) map->width,map->height*
   1068     sizeof(*map->levels));
   1069   if (map->levels == (ssize_t *) NULL)
   1070     ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
   1071   for (i=0; i < (ssize_t) (map->width*map->height); i++)
   1072   {
   1073     map->levels[i]=(ssize_t) strtol(content,&p,10);
   1074     if (p == content)
   1075       {
   1076         (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
   1077           "XmlInvalidContent", "<level> too few values, map \"%s\"",map_id);
   1078         thresholds=DestroyXMLTree(thresholds);
   1079         map=DestroyThresholdMap(map);
   1080         return(map);
   1081       }
   1082     if ((map->levels[i] < 0) || (map->levels[i] > map->divisor))
   1083       {
   1084         (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
   1085           "XmlInvalidContent", "<level> %.20g out of range, map \"%s\"",
   1086           (double) map->levels[i],map_id);
   1087         thresholds=DestroyXMLTree(thresholds);
   1088         map=DestroyThresholdMap(map);
   1089         return(map);
   1090       }
   1091     content=p;
   1092   }
   1093   value=(double) strtol(content,&p,10);
   1094   (void) value;
   1095   if (p != content)
   1096     {
   1097       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
   1098         "XmlInvalidContent", "<level> too many values, map \"%s\"",map_id);
   1099      thresholds=DestroyXMLTree(thresholds);
   1100      map=DestroyThresholdMap(map);
   1101      return(map);
   1102    }
   1103   thresholds=DestroyXMLTree(thresholds);
   1104   return(map);
   1105 }
   1106 
   1107 /*
   1109 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1110 %                                                                             %
   1111 %                                                                             %
   1112 %                                                                             %
   1113 +  L i s t T h r e s h o l d M a p F i l e                                    %
   1114 %                                                                             %
   1115 %                                                                             %
   1116 %                                                                             %
   1117 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1118 %
   1119 %  ListThresholdMapFile() lists the threshold maps and their descriptions
   1120 %  in the given XML file data.
   1121 %
   1122 %  The format of the ListThresholdMaps method is:
   1123 %
   1124 %      MagickBooleanType ListThresholdMaps(FILE *file,const char*xml,
   1125 %         const char *filename,ExceptionInfo *exception)
   1126 %
   1127 %  A description of each parameter follows.
   1128 %
   1129 %    o file:  An pointer to the output FILE.
   1130 %
   1131 %    o xml:  The threshold map list in XML format.
   1132 %
   1133 %    o filename:  The threshold map XML filename.
   1134 %
   1135 %    o exception: return any errors or warnings in this structure.
   1136 %
   1137 */
   1138 MagickBooleanType ListThresholdMapFile(FILE *file,const char *xml,
   1139   const char *filename,ExceptionInfo *exception)
   1140 {
   1141   const char
   1142     *alias,
   1143     *content,
   1144     *map;
   1145 
   1146   XMLTreeInfo
   1147     *description,
   1148     *threshold,
   1149     *thresholds;
   1150 
   1151   assert( xml != (char *) NULL );
   1152   assert( file != (FILE *) NULL );
   1153   (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
   1154     "Loading threshold map file \"%s\" ...",filename);
   1155   thresholds=NewXMLTree(xml,exception);
   1156   if ( thresholds == (XMLTreeInfo *) NULL )
   1157     return(MagickFalse);
   1158   (void) FormatLocaleFile(file,"%-16s %-12s %s\n","Map","Alias","Description");
   1159   (void) FormatLocaleFile(file,
   1160     "----------------------------------------------------\n");
   1161   threshold=GetXMLTreeChild(thresholds,"threshold");
   1162   for ( ; threshold != (XMLTreeInfo *) NULL;
   1163           threshold=GetNextXMLTreeTag(threshold))
   1164   {
   1165     map=GetXMLTreeAttribute(threshold,"map");
   1166     if (map == (char *) NULL)
   1167       {
   1168         (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
   1169           "XmlMissingAttribute", "<map>");
   1170         thresholds=DestroyXMLTree(thresholds);
   1171         return(MagickFalse);
   1172       }
   1173     alias=GetXMLTreeAttribute(threshold,"alias");
   1174     description=GetXMLTreeChild(threshold,"description");
   1175     if (description == (XMLTreeInfo *) NULL)
   1176       {
   1177         (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
   1178           "XmlMissingElement", "<description>, map \"%s\"",map);
   1179         thresholds=DestroyXMLTree(thresholds);
   1180         return(MagickFalse);
   1181       }
   1182     content=GetXMLTreeContent(description);
   1183     if (content == (char *) NULL)
   1184       {
   1185         (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
   1186           "XmlMissingContent", "<description>, map \"%s\"", map);
   1187         thresholds=DestroyXMLTree(thresholds);
   1188         return(MagickFalse);
   1189       }
   1190     (void) FormatLocaleFile(file,"%-16s %-12s %s\n",map,alias ? alias : "",
   1191       content);
   1192   }
   1193   thresholds=DestroyXMLTree(thresholds);
   1194   return(MagickTrue);
   1195 }
   1196 
   1197 /*
   1199 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1200 %                                                                             %
   1201 %                                                                             %
   1202 %                                                                             %
   1203 %  L i s t T h r e s h o l d M a p s                                          %
   1204 %                                                                             %
   1205 %                                                                             %
   1206 %                                                                             %
   1207 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1208 %
   1209 %  ListThresholdMaps() lists the threshold maps and their descriptions
   1210 %  as defined by "threshold.xml" to a file.
   1211 %
   1212 %  The format of the ListThresholdMaps method is:
   1213 %
   1214 %      MagickBooleanType ListThresholdMaps(FILE *file,ExceptionInfo *exception)
   1215 %
   1216 %  A description of each parameter follows.
   1217 %
   1218 %    o file:  An pointer to the output FILE.
   1219 %
   1220 %    o exception: return any errors or warnings in this structure.
   1221 %
   1222 */
   1223 MagickExport MagickBooleanType ListThresholdMaps(FILE *file,
   1224   ExceptionInfo *exception)
   1225 {
   1226   const StringInfo
   1227     *option;
   1228 
   1229   LinkedListInfo
   1230     *options;
   1231 
   1232   MagickStatusType
   1233     status;
   1234 
   1235   status=MagickTrue;
   1236   if (file == (FILE *) NULL)
   1237     file=stdout;
   1238   options=GetConfigureOptions(ThresholdsFilename,exception);
   1239   (void) FormatLocaleFile(file,
   1240     "\n   Threshold Maps for Ordered Dither Operations\n");
   1241   option=(const StringInfo *) GetNextValueInLinkedList(options);
   1242   while (option != (const StringInfo *) NULL)
   1243   {
   1244     (void) FormatLocaleFile(file,"\nPath: %s\n\n",GetStringInfoPath(option));
   1245     status&=ListThresholdMapFile(file,(const char *) GetStringInfoDatum(option),
   1246       GetStringInfoPath(option),exception);
   1247     option=(const StringInfo *) GetNextValueInLinkedList(options);
   1248   }
   1249   options=DestroyConfigureOptions(options);
   1250   return(status != 0 ? MagickTrue : MagickFalse);
   1251 }
   1252 
   1253 /*
   1255 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1256 %                                                                             %
   1257 %                                                                             %
   1258 %                                                                             %
   1259 %     O r d e r e d D i t h e r I m a g e                                     %
   1260 %                                                                             %
   1261 %                                                                             %
   1262 %                                                                             %
   1263 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1264 %
   1265 %  OrderedDitherImage() will perform a ordered dither based on a number
   1266 %  of pre-defined dithering threshold maps, but over multiple intensity
   1267 %  levels, which can be different for different channels, according to the
   1268 %  input argument.
   1269 %
   1270 %  The format of the OrderedDitherImage method is:
   1271 %
   1272 %      MagickBooleanType OrderedDitherImage(Image *image,
   1273 %        const char *threshold_map,ExceptionInfo *exception)
   1274 %
   1275 %  A description of each parameter follows:
   1276 %
   1277 %    o image: the image.
   1278 %
   1279 %    o threshold_map: A string containing the name of the threshold dither
   1280 %      map to use, followed by zero or more numbers representing the number
   1281 %      of color levels tho dither between.
   1282 %
   1283 %      Any level number less than 2 will be equivalent to 2, and means only
   1284 %      binary dithering will be applied to each color channel.
   1285 %
   1286 %      No numbers also means a 2 level (bitmap) dither will be applied to all
   1287 %      channels, while a single number is the number of levels applied to each
   1288 %      channel in sequence.  More numbers will be applied in turn to each of
   1289 %      the color channels.
   1290 %
   1291 %      For example: "o3x3,6" will generate a 6 level posterization of the
   1292 %      image with a ordered 3x3 diffused pixel dither being applied between
   1293 %      each level. While checker,8,8,4 will produce a 332 colormaped image
   1294 %      with only a single checkerboard hash pattern (50% grey) between each
   1295 %      color level, to basically double the number of color levels with
   1296 %      a bare minimim of dithering.
   1297 %
   1298 %    o exception: return any errors or warnings in this structure.
   1299 %
   1300 */
   1301 MagickExport MagickBooleanType OrderedDitherImage(Image *image,
   1302   const char *threshold_map,ExceptionInfo *exception)
   1303 {
   1304 #define DitherImageTag  "Dither/Image"
   1305 
   1306   CacheView
   1307     *image_view;
   1308 
   1309   char
   1310     token[MagickPathExtent];
   1311 
   1312   const char
   1313     *p;
   1314 
   1315   double
   1316     levels[CompositePixelChannel];
   1317 
   1318   MagickBooleanType
   1319     status;
   1320 
   1321   MagickOffsetType
   1322     progress;
   1323 
   1324   register ssize_t
   1325     i;
   1326 
   1327   ssize_t
   1328     y;
   1329 
   1330   ThresholdMap
   1331     *map;
   1332 
   1333   assert(image != (Image *) NULL);
   1334   assert(image->signature == MagickCoreSignature);
   1335   if (image->debug != MagickFalse)
   1336     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   1337   assert(exception != (ExceptionInfo *) NULL);
   1338   assert(exception->signature == MagickCoreSignature);
   1339   if (threshold_map == (const char *) NULL)
   1340     return(MagickTrue);
   1341   p=(char *) threshold_map;
   1342   while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
   1343          (*p != '\0'))
   1344     p++;
   1345   threshold_map=p;
   1346   while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
   1347          (*p != '\0'))
   1348   {
   1349     if ((p-threshold_map) >= (MagickPathExtent-1))
   1350       break;
   1351     token[p-threshold_map]=(*p);
   1352     p++;
   1353   }
   1354   token[p-threshold_map]='\0';
   1355   map=GetThresholdMap(token,exception);
   1356   if (map == (ThresholdMap *) NULL)
   1357     {
   1358       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
   1359         "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
   1360       return(MagickFalse);
   1361     }
   1362   for (i=0; i < MaxPixelChannels; i++)
   1363     levels[i]=2.0;
   1364   p=strchr((char *) threshold_map,',');
   1365   if ((p != (char *) NULL) && (isdigit((int) ((unsigned char) *(++p))) != 0))
   1366     for (i=0; (*p != '\0') && (i < MaxPixelChannels); i++)
   1367     {
   1368       GetNextToken(p,&p,MagickPathExtent,token);
   1369       if (*token == ',')
   1370         GetNextToken(p,&p,MagickPathExtent,token);
   1371       levels[i]=StringToDouble(token,(char **) NULL);
   1372     }
   1373   for (i=0; i < MaxPixelChannels; i++)
   1374     if (fabs(levels[i]) >= 1)
   1375       levels[i]-=1.0;
   1376   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
   1377     return(MagickFalse);
   1378   status=MagickTrue;
   1379   progress=0;
   1380   image_view=AcquireAuthenticCacheView(image,exception);
   1381 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   1382   #pragma omp parallel for schedule(static,4) shared(progress,status) \
   1383     magick_threads(image,image,image->rows,1)
   1384 #endif
   1385   for (y=0; y < (ssize_t) image->rows; y++)
   1386   {
   1387     register ssize_t
   1388       x;
   1389 
   1390     register Quantum
   1391       *magick_restrict q;
   1392 
   1393     if (status == MagickFalse)
   1394       continue;
   1395     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
   1396     if (q == (Quantum *) NULL)
   1397       {
   1398         status=MagickFalse;
   1399         continue;
   1400       }
   1401     for (x=0; x < (ssize_t) image->columns; x++)
   1402     {
   1403       register ssize_t
   1404         i;
   1405 
   1406       ssize_t
   1407         n;
   1408 
   1409       n=0;
   1410       if (GetPixelReadMask(image,q) == 0)
   1411         {
   1412           q+=GetPixelChannels(image);
   1413           continue;
   1414         }
   1415       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   1416       {
   1417         ssize_t
   1418           level,
   1419           threshold;
   1420 
   1421         PixelChannel channel=GetPixelChannelChannel(image,i);
   1422         PixelTrait traits=GetPixelChannelTraits(image,channel);
   1423         if ((traits & UpdatePixelTrait) == 0)
   1424           continue;
   1425         if (fabs(levels[n++]) < MagickEpsilon)
   1426           continue;
   1427         threshold=(ssize_t) (QuantumScale*q[i]*(levels[n]*(map->divisor-1)+1));
   1428         level=threshold/(map->divisor-1);
   1429         threshold-=level*(map->divisor-1);
   1430         q[i]=ClampToQuantum((double) (level+(threshold >=
   1431           map->levels[(x % map->width)+map->width*(y % map->height)]))*
   1432           QuantumRange/levels[n]);
   1433         n++;
   1434       }
   1435       q+=GetPixelChannels(image);
   1436     }
   1437     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
   1438       status=MagickFalse;
   1439     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   1440       {
   1441         MagickBooleanType
   1442           proceed;
   1443 
   1444 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   1445         #pragma omp critical (MagickCore_OrderedDitherImage)
   1446 #endif
   1447         proceed=SetImageProgress(image,DitherImageTag,progress++,image->rows);
   1448         if (proceed == MagickFalse)
   1449           status=MagickFalse;
   1450       }
   1451   }
   1452   image_view=DestroyCacheView(image_view);
   1453   map=DestroyThresholdMap(map);
   1454   return(MagickTrue);
   1455 }
   1456 
   1457 /*
   1459 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1460 %                                                                             %
   1461 %                                                                             %
   1462 %                                                                             %
   1463 %     P e r c e p t i b l e I m a g e                                         %
   1464 %                                                                             %
   1465 %                                                                             %
   1466 %                                                                             %
   1467 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1468 %
   1469 %  PerceptibleImage() set each pixel whose value is less than |epsilon| to
   1470 %  epsilon or -epsilon (whichever is closer) otherwise the pixel value remains
   1471 %  unchanged.
   1472 %
   1473 %  The format of the PerceptibleImage method is:
   1474 %
   1475 %      MagickBooleanType PerceptibleImage(Image *image,const double epsilon,
   1476 %        ExceptionInfo *exception)
   1477 %
   1478 %  A description of each parameter follows:
   1479 %
   1480 %    o image: the image.
   1481 %
   1482 %    o epsilon: the epsilon threshold (e.g. 1.0e-9).
   1483 %
   1484 %    o exception: return any errors or warnings in this structure.
   1485 %
   1486 */
   1487 
   1488 static inline Quantum PerceptibleThreshold(const Quantum quantum,
   1489   const double epsilon)
   1490 {
   1491   double
   1492     sign;
   1493 
   1494   sign=(double) quantum < 0.0 ? -1.0 : 1.0;
   1495   if ((sign*quantum) >= epsilon)
   1496     return(quantum);
   1497   return((Quantum) (sign*epsilon));
   1498 }
   1499 
   1500 MagickExport MagickBooleanType PerceptibleImage(Image *image,
   1501   const double epsilon,ExceptionInfo *exception)
   1502 {
   1503 #define PerceptibleImageTag  "Perceptible/Image"
   1504 
   1505   CacheView
   1506     *image_view;
   1507 
   1508   MagickBooleanType
   1509     status;
   1510 
   1511   MagickOffsetType
   1512     progress;
   1513 
   1514   ssize_t
   1515     y;
   1516 
   1517   assert(image != (Image *) NULL);
   1518   assert(image->signature == MagickCoreSignature);
   1519   if (image->debug != MagickFalse)
   1520     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   1521   if (image->storage_class == PseudoClass)
   1522     {
   1523       register ssize_t
   1524         i;
   1525 
   1526       register PixelInfo
   1527         *magick_restrict q;
   1528 
   1529       q=image->colormap;
   1530       for (i=0; i < (ssize_t) image->colors; i++)
   1531       {
   1532         q->red=(double) PerceptibleThreshold(ClampToQuantum(q->red),
   1533           epsilon);
   1534         q->green=(double) PerceptibleThreshold(ClampToQuantum(q->green),
   1535           epsilon);
   1536         q->blue=(double) PerceptibleThreshold(ClampToQuantum(q->blue),
   1537           epsilon);
   1538         q->alpha=(double) PerceptibleThreshold(ClampToQuantum(q->alpha),
   1539           epsilon);
   1540         q++;
   1541       }
   1542       return(SyncImage(image,exception));
   1543     }
   1544   /*
   1545     Perceptible image.
   1546   */
   1547   status=MagickTrue;
   1548   progress=0;
   1549   image_view=AcquireAuthenticCacheView(image,exception);
   1550 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   1551   #pragma omp parallel for schedule(static,4) shared(progress,status) \
   1552     magick_threads(image,image,image->rows,1)
   1553 #endif
   1554   for (y=0; y < (ssize_t) image->rows; y++)
   1555   {
   1556     register ssize_t
   1557       x;
   1558 
   1559     register Quantum
   1560       *magick_restrict q;
   1561 
   1562     if (status == MagickFalse)
   1563       continue;
   1564     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
   1565     if (q == (Quantum *) NULL)
   1566       {
   1567         status=MagickFalse;
   1568         continue;
   1569       }
   1570     for (x=0; x < (ssize_t) image->columns; x++)
   1571     {
   1572       register ssize_t
   1573         i;
   1574 
   1575       if (GetPixelReadMask(image,q) == 0)
   1576         {
   1577           q+=GetPixelChannels(image);
   1578           continue;
   1579         }
   1580       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   1581       {
   1582         PixelChannel channel=GetPixelChannelChannel(image,i);
   1583         PixelTrait traits=GetPixelChannelTraits(image,channel);
   1584         if (traits == UndefinedPixelTrait)
   1585           continue;
   1586         q[i]=PerceptibleThreshold(q[i],epsilon);
   1587       }
   1588       q+=GetPixelChannels(image);
   1589     }
   1590     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
   1591       status=MagickFalse;
   1592     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   1593       {
   1594         MagickBooleanType
   1595           proceed;
   1596 
   1597 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   1598         #pragma omp critical (MagickCore_PerceptibleImage)
   1599 #endif
   1600         proceed=SetImageProgress(image,PerceptibleImageTag,progress++,image->rows);
   1601         if (proceed == MagickFalse)
   1602           status=MagickFalse;
   1603       }
   1604   }
   1605   image_view=DestroyCacheView(image_view);
   1606   return(status);
   1607 }
   1608 
   1609 /*
   1611 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1612 %                                                                             %
   1613 %                                                                             %
   1614 %                                                                             %
   1615 %     R a n d o m T h r e s h o l d I m a g e                                 %
   1616 %                                                                             %
   1617 %                                                                             %
   1618 %                                                                             %
   1619 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1620 %
   1621 %  RandomThresholdImage() changes the value of individual pixels based on the
   1622 %  intensity of each pixel compared to a random threshold.  The result is a
   1623 %  low-contrast, two color image.
   1624 %
   1625 %  The format of the RandomThresholdImage method is:
   1626 %
   1627 %      MagickBooleanType RandomThresholdImage(Image *image,
   1628 %        const char *thresholds,ExceptionInfo *exception)
   1629 %
   1630 %  A description of each parameter follows:
   1631 %
   1632 %    o image: the image.
   1633 %
   1634 %    o low,high: Specify the high and low thresholds. These values range from
   1635 %      0 to QuantumRange.
   1636 %
   1637 %    o exception: return any errors or warnings in this structure.
   1638 %
   1639 */
   1640 MagickExport MagickBooleanType RandomThresholdImage(Image *image,
   1641   const double min_threshold, const double max_threshold,ExceptionInfo *exception)
   1642 {
   1643 #define ThresholdImageTag  "Threshold/Image"
   1644 
   1645   CacheView
   1646     *image_view;
   1647 
   1648   MagickBooleanType
   1649     status;
   1650 
   1651   MagickOffsetType
   1652     progress;
   1653 
   1654   PixelInfo
   1655     threshold;
   1656 
   1657   RandomInfo
   1658     **magick_restrict random_info;
   1659 
   1660   ssize_t
   1661     y;
   1662 
   1663 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   1664   unsigned long
   1665     key;
   1666 #endif
   1667 
   1668   assert(image != (Image *) NULL);
   1669   assert(image->signature == MagickCoreSignature);
   1670   if (image->debug != MagickFalse)
   1671     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   1672   assert(exception != (ExceptionInfo *) NULL);
   1673   assert(exception->signature == MagickCoreSignature);
   1674   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
   1675     return(MagickFalse);
   1676   GetPixelInfo(image,&threshold);
   1677   /*
   1678     Random threshold image.
   1679   */
   1680   status=MagickTrue;
   1681   progress=0;
   1682   random_info=AcquireRandomInfoThreadSet();
   1683   image_view=AcquireAuthenticCacheView(image,exception);
   1684 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   1685   key=GetRandomSecretKey(random_info[0]);
   1686   #pragma omp parallel for schedule(static,4) shared(progress,status) \
   1687     magick_threads(image,image,image->rows,key == ~0UL)
   1688 #endif
   1689   for (y=0; y < (ssize_t) image->rows; y++)
   1690   {
   1691     const int
   1692       id = GetOpenMPThreadId();
   1693 
   1694     register Quantum
   1695       *magick_restrict q;
   1696 
   1697     register ssize_t
   1698       x;
   1699 
   1700     if (status == MagickFalse)
   1701       continue;
   1702     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
   1703     if (q == (Quantum *) NULL)
   1704       {
   1705         status=MagickFalse;
   1706         continue;
   1707       }
   1708     for (x=0; x < (ssize_t) image->columns; x++)
   1709     {
   1710       register ssize_t
   1711         i;
   1712 
   1713       if (GetPixelReadMask(image,q) == 0)
   1714         {
   1715           q+=GetPixelChannels(image);
   1716           continue;
   1717         }
   1718       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   1719       {
   1720         double
   1721           threshold;
   1722 
   1723         PixelChannel channel=GetPixelChannelChannel(image,i);
   1724         PixelTrait traits=GetPixelChannelTraits(image,channel);
   1725         if ((traits & UpdatePixelTrait) == 0)
   1726           continue;
   1727         if ((double) q[i] < min_threshold)
   1728           threshold=min_threshold;
   1729         else
   1730           if ((double) q[i] > max_threshold)
   1731             threshold=max_threshold;
   1732           else
   1733             threshold=(double) (QuantumRange*
   1734               GetPseudoRandomValue(random_info[id]));
   1735         q[i]=(double) q[i] <= threshold ? 0 : QuantumRange;
   1736       }
   1737       q+=GetPixelChannels(image);
   1738     }
   1739     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
   1740       status=MagickFalse;
   1741     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   1742       {
   1743         MagickBooleanType
   1744           proceed;
   1745 
   1746 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   1747         #pragma omp critical (MagickCore_RandomThresholdImage)
   1748 #endif
   1749         proceed=SetImageProgress(image,ThresholdImageTag,progress++,
   1750           image->rows);
   1751         if (proceed == MagickFalse)
   1752           status=MagickFalse;
   1753       }
   1754   }
   1755   image_view=DestroyCacheView(image_view);
   1756   random_info=DestroyRandomInfoThreadSet(random_info);
   1757   return(status);
   1758 }
   1759 
   1760 /*
   1762 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1763 %                                                                             %
   1764 %                                                                             %
   1765 %                                                                             %
   1766 %     W h i t e T h r e s h o l d I m a g e                                   %
   1767 %                                                                             %
   1768 %                                                                             %
   1769 %                                                                             %
   1770 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1771 %
   1772 %  WhiteThresholdImage() is like ThresholdImage() but forces all pixels above
   1773 %  the threshold into white while leaving all pixels at or below the threshold
   1774 %  unchanged.
   1775 %
   1776 %  The format of the WhiteThresholdImage method is:
   1777 %
   1778 %      MagickBooleanType WhiteThresholdImage(Image *image,
   1779 %        const char *threshold,ExceptionInfo *exception)
   1780 %
   1781 %  A description of each parameter follows:
   1782 %
   1783 %    o image: the image.
   1784 %
   1785 %    o threshold: Define the threshold value.
   1786 %
   1787 %    o exception: return any errors or warnings in this structure.
   1788 %
   1789 */
   1790 MagickExport MagickBooleanType WhiteThresholdImage(Image *image,
   1791   const char *thresholds,ExceptionInfo *exception)
   1792 {
   1793 #define ThresholdImageTag  "Threshold/Image"
   1794 
   1795   CacheView
   1796     *image_view;
   1797 
   1798   GeometryInfo
   1799     geometry_info;
   1800 
   1801   MagickBooleanType
   1802     status;
   1803 
   1804   MagickOffsetType
   1805     progress;
   1806 
   1807   PixelInfo
   1808     threshold;
   1809 
   1810   MagickStatusType
   1811     flags;
   1812 
   1813   ssize_t
   1814     y;
   1815 
   1816   assert(image != (Image *) NULL);
   1817   assert(image->signature == MagickCoreSignature);
   1818   if (image->debug != MagickFalse)
   1819     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   1820   if (thresholds == (const char *) NULL)
   1821     return(MagickTrue);
   1822   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
   1823     return(MagickFalse);
   1824   if (IsGrayColorspace(image->colorspace) != MagickFalse)
   1825     (void) TransformImageColorspace(image,sRGBColorspace,exception);
   1826   GetPixelInfo(image,&threshold);
   1827   flags=ParseGeometry(thresholds,&geometry_info);
   1828   threshold.red=geometry_info.rho;
   1829   threshold.green=geometry_info.rho;
   1830   threshold.blue=geometry_info.rho;
   1831   threshold.black=geometry_info.rho;
   1832   threshold.alpha=100.0;
   1833   if ((flags & SigmaValue) != 0)
   1834     threshold.green=geometry_info.sigma;
   1835   if ((flags & XiValue) != 0)
   1836     threshold.blue=geometry_info.xi;
   1837   if ((flags & PsiValue) != 0)
   1838     threshold.alpha=geometry_info.psi;
   1839   if (threshold.colorspace == CMYKColorspace)
   1840     {
   1841       if ((flags & PsiValue) != 0)
   1842         threshold.black=geometry_info.psi;
   1843       if ((flags & ChiValue) != 0)
   1844         threshold.alpha=geometry_info.chi;
   1845     }
   1846   if ((flags & PercentValue) != 0)
   1847     {
   1848       threshold.red*=(MagickRealType) (QuantumRange/100.0);
   1849       threshold.green*=(MagickRealType) (QuantumRange/100.0);
   1850       threshold.blue*=(MagickRealType) (QuantumRange/100.0);
   1851       threshold.black*=(MagickRealType) (QuantumRange/100.0);
   1852       threshold.alpha*=(MagickRealType) (QuantumRange/100.0);
   1853     }
   1854   /*
   1855     White threshold image.
   1856   */
   1857   status=MagickTrue;
   1858   progress=0;
   1859   image_view=AcquireAuthenticCacheView(image,exception);
   1860 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   1861   #pragma omp parallel for schedule(static,4) shared(progress,status) \
   1862     magick_threads(image,image,image->rows,1)
   1863 #endif
   1864   for (y=0; y < (ssize_t) image->rows; y++)
   1865   {
   1866     register ssize_t
   1867       x;
   1868 
   1869     register Quantum
   1870       *magick_restrict q;
   1871 
   1872     if (status == MagickFalse)
   1873       continue;
   1874     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
   1875     if (q == (Quantum *) NULL)
   1876       {
   1877         status=MagickFalse;
   1878         continue;
   1879       }
   1880     for (x=0; x < (ssize_t) image->columns; x++)
   1881     {
   1882       double
   1883         pixel;
   1884 
   1885       register ssize_t
   1886         i;
   1887 
   1888       if (GetPixelReadMask(image,q) == 0)
   1889         {
   1890           q+=GetPixelChannels(image);
   1891           continue;
   1892         }
   1893       pixel=GetPixelIntensity(image,q);
   1894       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   1895       {
   1896         PixelChannel channel=GetPixelChannelChannel(image,i);
   1897         PixelTrait traits=GetPixelChannelTraits(image,channel);
   1898         if ((traits & UpdatePixelTrait) == 0)
   1899           continue;
   1900         if (image->channel_mask != DefaultChannels)
   1901           pixel=(double) q[i];
   1902         if (pixel > GetPixelInfoChannel(&threshold,channel))
   1903           q[i]=QuantumRange;
   1904       }
   1905       q+=GetPixelChannels(image);
   1906     }
   1907     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
   1908       status=MagickFalse;
   1909     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   1910       {
   1911         MagickBooleanType
   1912           proceed;
   1913 
   1914 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   1915         #pragma omp critical (MagickCore_WhiteThresholdImage)
   1916 #endif
   1917         proceed=SetImageProgress(image,ThresholdImageTag,progress++,
   1918           image->rows);
   1919         if (proceed == MagickFalse)
   1920           status=MagickFalse;
   1921       }
   1922   }
   1923   image_view=DestroyCacheView(image_view);
   1924   return(status);
   1925 }
   1926