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