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