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