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