]> 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-2010 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/transform.h"
83 #include "magick/threshold.h"
84 #include "magick/xml-tree.h"
85 \f
86 /*
87   Define declarations.
88 */
89 #define ThresholdsFilename  "thresholds.xml"
90 \f
91 /*
92   Typedef declarations.
93 */
94 struct _ThresholdMap
95 {
96   char
97     *map_id,
98     *description;
99
100   size_t
101     width,
102     height;
103
104   ssize_t
105     divisor,
106     *levels;
107 };
108 \f
109 /*
110 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
111 %                                                                             %
112 %                                                                             %
113 %                                                                             %
114 %     A d a p t i v e T h r e s h o l d I m a g e                             %
115 %                                                                             %
116 %                                                                             %
117 %                                                                             %
118 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
119 %
120 %  AdaptiveThresholdImage() selects an individual threshold for each pixel
121 %  based on the range of intensity values in its local neighborhood.  This
122 %  allows for thresholding of an image whose global intensity histogram
123 %  doesn't contain distinctive peaks.
124 %
125 %  The format of the AdaptiveThresholdImage method is:
126 %
127 %      Image *AdaptiveThresholdImage(const Image *image,
128 %        const size_t width,const size_t height,
129 %        const ssize_t offset,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 offset: the mean offset.
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 ssize_t offset,
146   ExceptionInfo *exception)
147 {
148 #define ThresholdImageTag  "Threshold/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   MagickPixelPacket
164     zero;
165
166   MagickRealType
167     number_pixels;
168
169   ssize_t
170     y;
171
172   assert(image != (const 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 ((image->columns < width) || (image->rows < height))
179     ThrowImageException(OptionError,"ImageSmallerThanRadius");
180   threshold_image=CloneImage(image,0,0,MagickTrue,exception);
181   if (threshold_image == (Image *) NULL)
182     return((Image *) NULL);
183   if (SetImageStorageClass(threshold_image,DirectClass) == MagickFalse)
184     {
185       InheritException(exception,&threshold_image->exception);
186       threshold_image=DestroyImage(threshold_image);
187       return((Image *) NULL);
188     }
189   /*
190     Local adaptive threshold.
191   */
192   status=MagickTrue;
193   progress=0;
194   GetMagickPixelPacket(image,&zero);
195   number_pixels=(MagickRealType) 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     MagickBooleanType
204       sync;
205
206     register const IndexPacket
207       *restrict indexes;
208
209     register const PixelPacket
210       *restrict p;
211
212     register IndexPacket
213       *restrict threshold_indexes;
214
215     register ssize_t
216       x;
217
218     register PixelPacket
219       *restrict q;
220
221     if (status == MagickFalse)
222       continue;
223     p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
224       height/2L,image->columns+width,height,exception);
225     q=GetCacheViewAuthenticPixels(threshold_view,0,y,threshold_image->columns,1,
226       exception);
227     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
228       {
229         status=MagickFalse;
230         continue;
231       }
232     indexes=GetCacheViewVirtualIndexQueue(image_view);
233     threshold_indexes=GetCacheViewAuthenticIndexQueue(threshold_view);
234     for (x=0; x < (ssize_t) image->columns; x++)
235     {
236       ssize_t
237         v;
238
239       MagickPixelPacket
240         mean,
241         pixel;
242
243       register const PixelPacket
244         *r;
245
246       register ssize_t
247         u;
248
249       pixel=zero;
250       mean=zero;
251       r=p;
252       for (v=0; v < (ssize_t) height; v++)
253       {
254         for (u=0; u < (ssize_t) width; u++)
255         {
256           pixel.red+=r[u].red;
257           pixel.green+=r[u].green;
258           pixel.blue+=r[u].blue;
259           pixel.opacity+=r[u].opacity;
260           if (image->colorspace == CMYKColorspace)
261             pixel.index=(MagickRealType) indexes[x+(r-p)+u];
262         }
263         r+=image->columns+width;
264       }
265       mean.red=(MagickRealType) (pixel.red/number_pixels+offset);
266       mean.green=(MagickRealType) (pixel.green/number_pixels+offset);
267       mean.blue=(MagickRealType) (pixel.blue/number_pixels+offset);
268       mean.opacity=(MagickRealType) (pixel.opacity/number_pixels+offset);
269       if (image->colorspace == CMYKColorspace)
270         mean.index=(MagickRealType) (pixel.index/number_pixels+offset);
271       q->red=(Quantum) (((MagickRealType) q->red <= mean.red) ?
272         0 : QuantumRange);
273       q->green=(Quantum) (((MagickRealType) q->green <= mean.green) ?
274         0 : QuantumRange);
275       q->blue=(Quantum) (((MagickRealType) q->blue <= mean.blue) ?
276         0 : QuantumRange);
277       q->opacity=(Quantum) (((MagickRealType) q->opacity <= mean.opacity) ?
278         0 : QuantumRange);
279       if (image->colorspace == CMYKColorspace)
280         threshold_indexes[x]=(IndexPacket) (((MagickRealType)
281           threshold_indexes[x] <= mean.index) ? 0 : QuantumRange);
282       p++;
283       q++;
284     }
285     sync=SyncCacheViewAuthenticPixels(threshold_view,exception);
286     if (sync == MagickFalse)
287       status=MagickFalse;
288     if (image->progress_monitor != (MagickProgressMonitor) NULL)
289       {
290         MagickBooleanType
291           proceed;
292
293 #if defined(MAGICKCORE_OPENMP_SUPPORT)
294   #pragma omp critical (MagickCore_AdaptiveThresholdImage)
295 #endif
296         proceed=SetImageProgress(image,ThresholdImageTag,progress++,
297           image->rows);
298         if (proceed == MagickFalse)
299           status=MagickFalse;
300       }
301   }
302   threshold_view=DestroyCacheView(threshold_view);
303   image_view=DestroyCacheView(image_view);
304   if (status == MagickFalse)
305     threshold_image=DestroyImage(threshold_image);
306   return(threshold_image);
307 }
308 \f
309 /*
310 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
311 %                                                                             %
312 %                                                                             %
313 %                                                                             %
314 %     B i l e v e l I m a g e                                                 %
315 %                                                                             %
316 %                                                                             %
317 %                                                                             %
318 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
319 %
320 %  BilevelImage() changes the value of individual pixels based on the
321 %  intensity of each pixel channel.  The result is a high-contrast image.
322 %
323 %  More precisely each channel value of the image is 'thresholded' so that if
324 %  it is equal to or less than the given value it is set to zero, while any
325 %  value greater than that give is set to it maximum or QuantumRange.
326 %
327 %  This function is what is used to implement the "-threshold" operator for
328 %  the command line API.
329 %
330 %  If the default channel setting is given the image is thresholded using just
331 %  the gray 'intensity' of the image, rather than the individual channels.
332 %
333 %  The format of the BilevelImageChannel method is:
334 %
335 %      MagickBooleanType BilevelImage(Image *image,const double threshold)
336 %      MagickBooleanType BilevelImageChannel(Image *image,
337 %        const ChannelType channel,const double threshold)
338 %
339 %  A description of each parameter follows:
340 %
341 %    o image: the image.
342 %
343 %    o channel: the channel type.
344 %
345 %    o threshold: define the threshold values.
346 %
347 %  Aside: You can get the same results as operator using LevelImageChannels()
348 %  with the 'threshold' value for both the black_point and the white_point.
349 %
350 */
351
352 MagickExport MagickBooleanType BilevelImage(Image *image,const double threshold)
353 {
354   MagickBooleanType
355     status;
356
357   status=BilevelImageChannel(image,DefaultChannels,threshold);
358   return(status);
359 }
360
361 MagickExport MagickBooleanType BilevelImageChannel(Image *image,
362   const ChannelType channel,const double threshold)
363 {
364 #define ThresholdImageTag  "Threshold/Image"
365
366   CacheView
367     *image_view;
368
369   ExceptionInfo
370     *exception;
371
372   MagickBooleanType
373     status;
374
375   MagickOffsetType
376     progress;
377
378   ssize_t
379     y;
380
381   assert(image != (Image *) NULL);
382   assert(image->signature == MagickSignature);
383   if (image->debug != MagickFalse)
384     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
385   if (SetImageStorageClass(image,DirectClass) == MagickFalse)
386     return(MagickFalse);
387   /*
388     Bilevel threshold image.
389   */
390   status=MagickTrue;
391   progress=0;
392   exception=(&image->exception);
393   image_view=AcquireCacheView(image);
394 #if defined(MAGICKCORE_OPENMP_SUPPORT)
395   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
396 #endif
397   for (y=0; y < (ssize_t) image->rows; y++)
398   {
399     register IndexPacket
400       *restrict indexes;
401
402     register ssize_t
403       x;
404
405     register PixelPacket
406       *restrict q;
407
408     if (status == MagickFalse)
409       continue;
410     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
411     if (q == (PixelPacket *) NULL)
412       {
413         status=MagickFalse;
414         continue;
415       }
416     indexes=GetCacheViewAuthenticIndexQueue(image_view);
417     if (channel == DefaultChannels)
418       {
419         for (x=0; x < (ssize_t) image->columns; x++)
420         {
421           q->red=(Quantum) ((MagickRealType) PixelIntensityToQuantum(q) <=
422             threshold ? 0 : QuantumRange);
423           q->green=q->red;
424           q->blue=q->red;
425           q++;
426         }
427       }
428     else
429       for (x=0; x < (ssize_t) image->columns; x++)
430       {
431         if ((channel & RedChannel) != 0)
432           q->red=(Quantum) ((MagickRealType) q->red <= threshold ? 0 :
433             QuantumRange);
434         if ((channel & GreenChannel) != 0)
435           q->green=(Quantum) ((MagickRealType) q->green <= threshold ? 0 :
436             QuantumRange);
437         if ((channel & BlueChannel) != 0)
438           q->blue=(Quantum) ((MagickRealType) q->blue <= threshold ? 0 :
439             QuantumRange);
440         if ((channel & OpacityChannel) != 0)
441           {
442             if (image->matte == MagickFalse)
443               q->opacity=(Quantum) ((MagickRealType) q->opacity <= threshold ?
444                 0 : QuantumRange);
445             else
446               q->opacity=(Quantum) ((MagickRealType) q->opacity <= threshold ?
447                 OpaqueOpacity : TransparentOpacity);
448           }
449         if (((channel & IndexChannel) != 0) &&
450             (image->colorspace == CMYKColorspace))
451           indexes[x]=(IndexPacket) ((MagickRealType) indexes[x] <= threshold ?
452             0 : QuantumRange);
453         q++;
454       }
455     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
456       status=MagickFalse;
457     if (image->progress_monitor != (MagickProgressMonitor) NULL)
458       {
459         MagickBooleanType
460           proceed;
461
462 #if defined(MAGICKCORE_OPENMP_SUPPORT)
463   #pragma omp critical (MagickCore_BilevelImageChannel)
464 #endif
465         proceed=SetImageProgress(image,ThresholdImageTag,progress++,
466           image->rows);
467         if (proceed == MagickFalse)
468           status=MagickFalse;
469       }
470   }
471   image_view=DestroyCacheView(image_view);
472   return(status);
473 }
474 \f
475 /*
476 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
477 %                                                                             %
478 %                                                                             %
479 %                                                                             %
480 %     B l a c k T h r e s h o l d I m a g e                                   %
481 %                                                                             %
482 %                                                                             %
483 %                                                                             %
484 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
485 %
486 %  BlackThresholdImage() is like ThresholdImage() but forces all pixels below
487 %  the threshold into black while leaving all pixels at or above the threshold
488 %  unchanged.
489 %
490 %  The format of the BlackThresholdImage method is:
491 %
492 %      MagickBooleanType BlackThresholdImage(Image *image,const char *threshold)
493 %      MagickBooleanType BlackThresholdImageChannel(Image *image,
494 %        const ChannelType channel,const char *threshold,
495 %        ExceptionInfo *exception)
496 %
497 %  A description of each parameter follows:
498 %
499 %    o image: the image.
500 %
501 %    o channel: the channel or channels to be thresholded.
502 %
503 %    o threshold: Define the threshold value.
504 %
505 %    o exception: return any errors or warnings in this structure.
506 %
507 */
508 MagickExport MagickBooleanType BlackThresholdImage(Image *image,
509   const char *threshold)
510 {
511   MagickBooleanType
512     status;
513
514   status=BlackThresholdImageChannel(image,DefaultChannels,threshold,
515     &image->exception);
516   return(status);
517 }
518
519 MagickExport MagickBooleanType BlackThresholdImageChannel(Image *image,
520   const ChannelType channel,const char *thresholds,ExceptionInfo *exception)
521 {
522 #define ThresholdImageTag  "Threshold/Image"
523
524   CacheView
525     *image_view;
526
527   GeometryInfo
528     geometry_info;
529
530   MagickBooleanType
531     status;
532
533   MagickOffsetType
534     progress;
535
536   MagickPixelPacket
537     threshold;
538
539   MagickStatusType
540     flags;
541
542   ssize_t
543     y;
544
545   assert(image != (Image *) NULL);
546   assert(image->signature == MagickSignature);
547   if (image->debug != MagickFalse)
548     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
549   if (thresholds == (const char *) NULL)
550     return(MagickTrue);
551   if (SetImageStorageClass(image,DirectClass) == MagickFalse)
552     return(MagickFalse);
553   GetMagickPixelPacket(image,&threshold);
554   flags=ParseGeometry(thresholds,&geometry_info);
555   threshold.red=geometry_info.rho;
556   threshold.green=geometry_info.sigma;
557   if ((flags & SigmaValue) == 0)
558     threshold.green=threshold.red;
559   threshold.blue=geometry_info.xi;
560   if ((flags & XiValue) == 0)
561     threshold.blue=threshold.red;
562   threshold.opacity=geometry_info.psi;
563   if ((flags & PsiValue) == 0)
564     threshold.opacity=threshold.red;
565   threshold.index=geometry_info.chi;
566   if ((flags & ChiValue) == 0)
567     threshold.index=threshold.red;
568   if ((flags & PercentValue) != 0)
569     {
570       threshold.red*=(QuantumRange/100.0);
571       threshold.green*=(QuantumRange/100.0);
572       threshold.blue*=(QuantumRange/100.0);
573       threshold.opacity*=(QuantumRange/100.0);
574       threshold.index*=(QuantumRange/100.0);
575     }
576   /*
577     Black threshold image.
578   */
579   status=MagickTrue;
580   progress=0;
581   image_view=AcquireCacheView(image);
582 #if defined(MAGICKCORE_OPENMP_SUPPORT)
583   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
584 #endif
585   for (y=0; y < (ssize_t) image->rows; y++)
586   {
587     register IndexPacket
588       *restrict indexes;
589
590     register ssize_t
591       x;
592
593     register PixelPacket
594       *restrict q;
595
596     if (status == MagickFalse)
597       continue;
598     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
599     if (q == (PixelPacket *) NULL)
600       {
601         status=MagickFalse;
602         continue;
603       }
604     indexes=GetCacheViewAuthenticIndexQueue(image_view);
605     for (x=0; x < (ssize_t) image->columns; x++)
606     {
607       if (channel != DefaultChannels)
608         {
609           if (PixelIntensity(q) < MagickPixelIntensity(&threshold))
610             {
611               q->red=(Quantum) 0;
612               q->green=(Quantum) 0;
613               q->blue=(Quantum) 0;
614               if (image->colorspace == CMYKColorspace)
615                 indexes[x]=(Quantum) 0;
616             }
617         }
618       else
619         {
620           if (((channel & RedChannel) != 0) &&
621               ((MagickRealType) q->red < threshold.red))
622             q->red=(Quantum) 0;
623           if (((channel & GreenChannel) != 0) &&
624               ((MagickRealType) q->green < threshold.green))
625             q->green=(Quantum) 0;
626           if (((channel & BlueChannel) != 0) &&
627               ((MagickRealType) q->blue < threshold.blue))
628             q->blue=(Quantum) 0;
629           if (((channel & OpacityChannel) != 0) &&
630               ((MagickRealType) q->opacity < threshold.opacity))
631             q->opacity=(Quantum) 0;
632           if (((channel & IndexChannel) != 0) &&
633               (image->colorspace == CMYKColorspace) &&
634               ((MagickRealType) indexes[x] < threshold.index))
635             indexes[x]=(Quantum) 0;
636         }
637       q++;
638     }
639     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
640       status=MagickFalse;
641     if (image->progress_monitor != (MagickProgressMonitor) NULL)
642       {
643         MagickBooleanType
644           proceed;
645
646 #if defined(MAGICKCORE_OPENMP_SUPPORT)
647   #pragma omp critical (MagickCore_BlackThresholdImageChannel)
648 #endif
649         proceed=SetImageProgress(image,ThresholdImageTag,progress++,
650           image->rows);
651         if (proceed == MagickFalse)
652           status=MagickFalse;
653       }
654   }
655   image_view=DestroyCacheView(image_view);
656   return(status);
657 }
658 \f
659 /*
660 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
661 %                                                                             %
662 %                                                                             %
663 %                                                                             %
664 %     C l a m p I m a g e                                                     %
665 %                                                                             %
666 %                                                                             %
667 %                                                                             %
668 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
669 %
670 %  ClampImage() restricts the color range from 0 to the quantum depth.
671 %
672 %  The format of the ClampImageChannel method is:
673 %
674 %      MagickBooleanType ClampImage(Image *image)
675 %      MagickBooleanType ClampImageChannel(Image *image,
676 %        const ChannelType channel)
677 %
678 %  A description of each parameter follows:
679 %
680 %    o image: the image.
681 %
682 %    o channel: the channel type.
683 %
684 */
685
686 static inline Quantum ClampToUnsignedQuantum(const Quantum quantum)
687 {
688 #if defined(MAGICKCORE_HDRI_SUPPORT)
689   if (quantum <= 0)
690     return(0);
691   if (quantum >= QuantumRange)
692     return(QuantumRange);
693   return(quantum);
694 #else
695   return(quantum);
696 #endif
697 }
698
699 MagickExport MagickBooleanType ClampImage(Image *image)
700 {
701   MagickBooleanType
702     status;
703
704   status=ClampImageChannel(image,DefaultChannels);
705   return(status);
706 }
707
708 MagickExport MagickBooleanType ClampImageChannel(Image *image,
709   const ChannelType channel)
710 {
711 #define ClampImageTag  "Clamp/Image"
712
713   CacheView
714     *image_view;
715
716   ExceptionInfo
717     *exception;
718
719   MagickBooleanType
720     status;
721
722   MagickOffsetType
723     progress;
724
725   ssize_t
726     y;
727
728   assert(image != (Image *) NULL);
729   assert(image->signature == MagickSignature);
730   if (image->debug != MagickFalse)
731     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
732   if (image->storage_class == PseudoClass)
733     {
734       register ssize_t
735         i;
736
737       register PixelPacket
738         *restrict q;
739
740       q=image->colormap;
741       for (i=0; i < (ssize_t) image->colors; i++)
742       {
743         q->red=ClampToUnsignedQuantum(q->red);
744         q->green=ClampToUnsignedQuantum(q->green);
745         q->blue=ClampToUnsignedQuantum(q->blue);
746         q->opacity=ClampToUnsignedQuantum(q->opacity);
747         q++;
748       }
749       return(SyncImage(image));
750     }
751   /*
752     Clamp image.
753   */
754   status=MagickTrue;
755   progress=0;
756   exception=(&image->exception);
757   image_view=AcquireCacheView(image);
758 #if defined(MAGICKCORE_OPENMP_SUPPORT)
759   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
760 #endif
761   for (y=0; y < (ssize_t) image->rows; y++)
762   {
763     register IndexPacket
764       *restrict indexes;
765
766     register ssize_t
767       x;
768
769     register PixelPacket
770       *restrict q;
771
772     if (status == MagickFalse)
773       continue;
774     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
775     if (q == (PixelPacket *) NULL)
776       {
777         status=MagickFalse;
778         continue;
779       }
780     indexes=GetCacheViewAuthenticIndexQueue(image_view);
781     for (x=0; x < (ssize_t) image->columns; x++)
782     {
783       if ((channel & RedChannel) != 0)
784         q->red=ClampToUnsignedQuantum(q->red);
785       if ((channel & GreenChannel) != 0)
786         q->green=ClampToUnsignedQuantum(q->green);
787       if ((channel & BlueChannel) != 0)
788         q->blue=ClampToUnsignedQuantum(q->blue);
789       if ((channel & OpacityChannel) != 0)
790         q->opacity=ClampToUnsignedQuantum(q->opacity);
791       if (((channel & IndexChannel) != 0) &&
792           (image->colorspace == CMYKColorspace))
793         indexes[x]=(IndexPacket) ClampToUnsignedQuantum(indexes[x]);
794       q++;
795     }
796     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
797       status=MagickFalse;
798     if (image->progress_monitor != (MagickProgressMonitor) NULL)
799       {
800         MagickBooleanType
801           proceed;
802
803 #if defined(MAGICKCORE_OPENMP_SUPPORT)
804   #pragma omp critical (MagickCore_ClampImageChannel)
805 #endif
806         proceed=SetImageProgress(image,ClampImageTag,progress++,
807           image->rows);
808         if (proceed == MagickFalse)
809           status=MagickFalse;
810       }
811   }
812   image_view=DestroyCacheView(image_view);
813   return(status);
814 }
815 \f
816 /*
817 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
818 %                                                                             %
819 %                                                                             %
820 %                                                                             %
821 %  D e s t r o y T h r e s h o l d M a p                                      %
822 %                                                                             %
823 %                                                                             %
824 %                                                                             %
825 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
826 %
827 %  DestroyThresholdMap() de-allocate the given ThresholdMap
828 %
829 %  The format of the ListThresholdMaps method is:
830 %
831 %      ThresholdMap *DestroyThresholdMap(Threshold *map)
832 %
833 %  A description of each parameter follows.
834 %
835 %    o map:    Pointer to the Threshold map to destroy
836 %
837 */
838 MagickExport ThresholdMap *DestroyThresholdMap(ThresholdMap *map)
839 {
840   assert(map != (ThresholdMap *) NULL);
841   if (map->map_id != (char *) NULL)
842     map->map_id=DestroyString(map->map_id);
843   if (map->description != (char *) NULL)
844     map->description=DestroyString(map->description);
845   if (map->levels != (ssize_t *) NULL)
846     map->levels=(ssize_t *) RelinquishMagickMemory(map->levels);
847   map=(ThresholdMap *) RelinquishMagickMemory(map);
848   return(map);
849 }
850 \f
851 /*
852 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
853 %                                                                             %
854 %                                                                             %
855 %                                                                             %
856 +  G e t T h r e s h o l d M a p F i l e                                      %
857 %                                                                             %
858 %                                                                             %
859 %                                                                             %
860 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
861 %
862 %  GetThresholdMapFile() look for a given threshold map name or alias in the
863 %  given XML file data, and return the allocated the map when found.
864 %
865 %  The format of the ListThresholdMaps method is:
866 %
867 %      ThresholdMap *GetThresholdMap(const char *xml,const char *filename,
868 %         const char *map_id,ExceptionInfo *exception)
869 %
870 %  A description of each parameter follows.
871 %
872 %    o xml:  The threshold map list in XML format.
873 %
874 %    o filename:  The threshold map XML filename.
875 %
876 %    o map_id:  ID of the map to look for in XML list.
877 %
878 %    o exception: return any errors or warnings in this structure.
879 %
880 */
881 MagickExport ThresholdMap *GetThresholdMapFile(const char *xml,
882   const char *filename,const char *map_id,ExceptionInfo *exception)
883 {
884   const char
885     *attr,
886     *content;
887
888   double
889     value;
890
891  ThresholdMap
892      *map;
893
894   XMLTreeInfo
895      *description,
896      *levels,
897      *threshold,
898      *thresholds;
899
900   map = (ThresholdMap *)NULL;
901   (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
902     "Loading threshold map file \"%s\" ...",filename);
903   thresholds=NewXMLTree(xml,exception);
904   if ( thresholds == (XMLTreeInfo *)NULL )
905     return(map);
906
907   for( threshold = GetXMLTreeChild(thresholds,"threshold");
908        threshold != (XMLTreeInfo *)NULL;
909        threshold = GetNextXMLTreeTag(threshold) ) {
910     attr = GetXMLTreeAttribute(threshold, "map");
911     if ( (attr != (char *)NULL) && (LocaleCompare(map_id,attr) == 0) )
912       break;
913     attr = GetXMLTreeAttribute(threshold, "alias");
914     if ( (attr != (char *)NULL) && (LocaleCompare(map_id,attr) == 0) )
915       break;
916   }
917   if ( threshold == (XMLTreeInfo *)NULL ) {
918     return(map);
919   }
920   description = GetXMLTreeChild(threshold,"description");
921   if ( description == (XMLTreeInfo *)NULL ) {
922     (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
923       "XmlMissingElement", "<description>, map \"%s\"", map_id);
924     thresholds = DestroyXMLTree(thresholds);
925     return(map);
926   }
927   levels = GetXMLTreeChild(threshold,"levels");
928   if ( levels == (XMLTreeInfo *)NULL ) {
929     (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
930       "XmlMissingElement", "<levels>, map \"%s\"", map_id);
931     thresholds = DestroyXMLTree(thresholds);
932     return(map);
933   }
934
935   /* The map has been found -- Allocate a Threshold Map to return */
936   map = (ThresholdMap *)AcquireMagickMemory(sizeof(ThresholdMap));
937   if ( map == (ThresholdMap *)NULL )
938     ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
939   map->map_id = (char *)NULL;
940   map->description = (char *)NULL;
941   map->levels = (ssize_t *) NULL;
942
943   /* Assign Basic Attributes */
944   attr = GetXMLTreeAttribute(threshold, "map");
945   if ( attr != (char *)NULL )
946     map->map_id = ConstantString(attr);
947
948   content = GetXMLTreeContent(description);
949   if ( content != (char *)NULL )
950     map->description = ConstantString(content);
951
952   attr = GetXMLTreeAttribute(levels, "width");
953   if ( attr == (char *)NULL ) {
954     (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
955       "XmlMissingAttribute", "<levels width>, map \"%s\"", map_id);
956     thresholds = DestroyXMLTree(thresholds);
957     map = DestroyThresholdMap(map);
958     return(map);
959   }
960   map->width = StringToUnsignedLong(attr);
961   if ( map->width == 0 ) {
962     (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
963      "XmlInvalidAttribute", "<levels width>, map \"%s\"", map_id);
964     thresholds = DestroyXMLTree(thresholds);
965     map = DestroyThresholdMap(map);
966     return(map);
967   }
968
969   attr = GetXMLTreeAttribute(levels, "height");
970   if ( attr == (char *)NULL ) {
971     (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
972       "XmlMissingAttribute", "<levels height>, map \"%s\"", map_id);
973     thresholds = DestroyXMLTree(thresholds);
974     map = DestroyThresholdMap(map);
975     return(map);
976   }
977   map->height = StringToUnsignedLong(attr);
978   if ( map->height == 0 ) {
979     (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
980       "XmlInvalidAttribute", "<levels height>, map \"%s\"", map_id);
981     thresholds = DestroyXMLTree(thresholds);
982     map = DestroyThresholdMap(map);
983     return(map);
984   }
985
986   attr = GetXMLTreeAttribute(levels, "divisor");
987   if ( attr == (char *)NULL ) {
988     (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
989       "XmlMissingAttribute", "<levels divisor>, map \"%s\"", map_id);
990     thresholds = DestroyXMLTree(thresholds);
991     map = DestroyThresholdMap(map);
992     return(map);
993   }
994   map->divisor = StringToLong(attr);
995   if ( map->divisor < 2 ) {
996     (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
997       "XmlInvalidAttribute", "<levels divisor>, map \"%s\"", map_id);
998     thresholds = DestroyXMLTree(thresholds);
999     map = DestroyThresholdMap(map);
1000     return(map);
1001   }
1002
1003   /* Allocate theshold levels array */
1004   content = GetXMLTreeContent(levels);
1005   if ( content == (char *)NULL ) {
1006     (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1007       "XmlMissingContent", "<levels>, map \"%s\"", map_id);
1008     thresholds = DestroyXMLTree(thresholds);
1009     map = DestroyThresholdMap(map);
1010     return(map);
1011   }
1012   map->levels=(ssize_t *) AcquireQuantumMemory((size_t) map->width,map->height*
1013     sizeof(*map->levels));
1014   if ( map->levels == (ssize_t *)NULL )
1015     ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
1016   { /* parse levels into integer array */
1017     ssize_t i;
1018     char *p;
1019     for( i=0; i< (ssize_t) (map->width*map->height); i++) {
1020       map->levels[i] = (ssize_t)strtol(content, &p, 10);
1021       if ( p == content ) {
1022         (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1023           "XmlInvalidContent", "<level> too few values, map \"%s\"", map_id);
1024         thresholds = DestroyXMLTree(thresholds);
1025         map = DestroyThresholdMap(map);
1026         return(map);
1027       }
1028       if ( map->levels[i] < 0 || map->levels[i] > map->divisor ) {
1029         (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1030           "XmlInvalidContent", "<level> %.20g out of range, map \"%s\"",
1031           (double) map->levels[i],map_id);
1032         thresholds = DestroyXMLTree(thresholds);
1033         map = DestroyThresholdMap(map);
1034         return(map);
1035       }
1036       content = p;
1037     }
1038     value=(double) strtol(content,&p,10);
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 seperated arguments
1415      Default to 2, the single value given, or individual channel values
1416   */
1417 #if 1
1418   { /* parse directly as a comma seperated 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=(Quantum) ((l+(t >= threshold))*QuantumRange/levels.red);
1541         }
1542         if (levels.green) {
1543           t = (ssize_t) (QuantumScale*q->green*(levels.green*d+1));
1544           l = t/d;  t = t-l*d;
1545           q->green=(Quantum) ((l+(t >= threshold))*QuantumRange/levels.green);
1546         }
1547         if (levels.blue) {
1548           t = (ssize_t) (QuantumScale*q->blue*(levels.blue*d+1));
1549           l = t/d;  t = t-l*d;
1550           q->blue=(Quantum) ((l+(t >= threshold))*QuantumRange/levels.blue);
1551         }
1552         if (levels.opacity) {
1553           t = (ssize_t) ((1.0-QuantumScale*q->opacity)*(levels.opacity*d+1));
1554           l = t/d;  t = t-l*d;
1555           q->opacity=(Quantum) ((1.0-l-(t >= threshold))*QuantumRange/
1556             levels.opacity);
1557         }
1558         if (levels.index) {
1559           t = (ssize_t) (QuantumScale*indexes[x]*(levels.index*d+1));
1560           l = t/d;  t = t-l*d;
1561           indexes[x]=(IndexPacket) ((l+(t>=threshold))*QuantumRange/
1562             levels.index);
1563         }
1564         q++;
1565       }
1566       if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1567         status=MagickFalse;
1568       if (image->progress_monitor != (MagickProgressMonitor) NULL)
1569         {
1570           MagickBooleanType
1571             proceed;
1572
1573 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1574   #pragma omp critical (MagickCore_OrderedPosterizeImageChannel)
1575 #endif
1576           proceed=SetImageProgress(image,DitherImageTag,progress++,image->rows);
1577           if (proceed == MagickFalse)
1578             status=MagickFalse;
1579         }
1580     }
1581     image_view=DestroyCacheView(image_view);
1582   }
1583   map=DestroyThresholdMap(map);
1584   return(MagickTrue);
1585 }
1586 \f
1587 /*
1588 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1589 %                                                                             %
1590 %                                                                             %
1591 %                                                                             %
1592 %     R a n d o m T h r e s h o l d I m a g e                                 %
1593 %                                                                             %
1594 %                                                                             %
1595 %                                                                             %
1596 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1597 %
1598 %  RandomThresholdImage() changes the value of individual pixels based on the
1599 %  intensity of each pixel compared to a random threshold.  The result is a
1600 %  low-contrast, two color image.
1601 %
1602 %  The format of the RandomThresholdImage method is:
1603 %
1604 %      MagickBooleanType RandomThresholdImageChannel(Image *image,
1605 %        const char *thresholds,ExceptionInfo *exception)
1606 %      MagickBooleanType RandomThresholdImageChannel(Image *image,
1607 %        const ChannelType channel,const char *thresholds,
1608 %        ExceptionInfo *exception)
1609 %
1610 %  A description of each parameter follows:
1611 %
1612 %    o image: the image.
1613 %
1614 %    o channel: the channel or channels to be thresholded.
1615 %
1616 %    o thresholds: a geometry string containing low,high thresholds.  If the
1617 %      string contains 2x2, 3x3, or 4x4, an ordered dither of order 2, 3, or 4
1618 %      is performed instead.
1619 %
1620 %    o exception: return any errors or warnings in this structure.
1621 %
1622 */
1623
1624 MagickExport MagickBooleanType RandomThresholdImage(Image *image,
1625   const char *thresholds,ExceptionInfo *exception)
1626 {
1627   MagickBooleanType
1628     status;
1629
1630   status=RandomThresholdImageChannel(image,DefaultChannels,thresholds,
1631     exception);
1632   return(status);
1633 }
1634
1635 MagickExport MagickBooleanType RandomThresholdImageChannel(Image *image,
1636   const ChannelType channel,const char *thresholds,ExceptionInfo *exception)
1637 {
1638 #define ThresholdImageTag  "Threshold/Image"
1639
1640   CacheView
1641     *image_view;
1642
1643   GeometryInfo
1644     geometry_info;
1645
1646   MagickStatusType
1647     flags;
1648
1649   MagickBooleanType
1650     status;
1651
1652   MagickOffsetType
1653     progress;
1654
1655   MagickPixelPacket
1656     threshold;
1657
1658   MagickRealType
1659     min_threshold,
1660     max_threshold;
1661
1662   RandomInfo
1663     **restrict random_info;
1664
1665   ssize_t
1666     y;
1667
1668   assert(image != (Image *) NULL);
1669   assert(image->signature == MagickSignature);
1670   if (image->debug != MagickFalse)
1671     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1672   assert(exception != (ExceptionInfo *) NULL);
1673   assert(exception->signature == MagickSignature);
1674   if (thresholds == (const char *) NULL)
1675     return(MagickTrue);
1676   GetMagickPixelPacket(image,&threshold);
1677   min_threshold=0.0;
1678   max_threshold=(MagickRealType) QuantumRange;
1679   flags=ParseGeometry(thresholds,&geometry_info);
1680   min_threshold=geometry_info.rho;
1681   max_threshold=geometry_info.sigma;
1682   if ((flags & SigmaValue) == 0)
1683     max_threshold=min_threshold;
1684   if (strchr(thresholds,'%') != (char *) NULL)
1685     {
1686       max_threshold*=(MagickRealType) (0.01*QuantumRange);
1687       min_threshold*=(MagickRealType) (0.01*QuantumRange);
1688     }
1689   else
1690     if (((max_threshold == min_threshold) || (max_threshold == 1)) &&
1691         (min_threshold <= 8))
1692       {
1693         /*
1694           Backward Compatibility -- ordered-dither -- IM v 6.2.9-6.
1695         */
1696         status=OrderedPosterizeImageChannel(image,channel,thresholds,exception);
1697         return(status);
1698       }
1699   /*
1700     Random threshold image.
1701   */
1702   status=MagickTrue;
1703   progress=0;
1704   if (channel == AllChannels)
1705     {
1706       if (AcquireImageColormap(image,2) == MagickFalse)
1707         ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1708           image->filename);
1709       random_info=AcquireRandomInfoThreadSet();
1710       image_view=AcquireCacheView(image);
1711 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1712       #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1713 #endif
1714       for (y=0; y < (ssize_t) image->rows; y++)
1715       {
1716         const int
1717           id = GetOpenMPThreadId();
1718
1719         MagickBooleanType
1720           sync;
1721
1722         register IndexPacket
1723           *restrict indexes;
1724
1725         register ssize_t
1726           x;
1727
1728         register PixelPacket
1729           *restrict q;
1730
1731         if (status == MagickFalse)
1732           continue;
1733         q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1734           exception);
1735         if (q == (PixelPacket *) NULL)
1736           {
1737             status=MagickFalse;
1738             continue;
1739           }
1740         indexes=GetCacheViewAuthenticIndexQueue(image_view);
1741         for (x=0; x < (ssize_t) image->columns; x++)
1742         {
1743           IndexPacket
1744             index;
1745
1746           MagickRealType
1747             intensity;
1748
1749           intensity=(MagickRealType) PixelIntensityToQuantum(q);
1750           if (intensity < min_threshold)
1751             threshold.index=min_threshold;
1752           else if (intensity > max_threshold)
1753             threshold.index=max_threshold;
1754           else
1755             threshold.index=(MagickRealType)(QuantumRange*
1756               GetPseudoRandomValue(random_info[id]));
1757           index=(IndexPacket) (intensity <= threshold.index ? 0 : 1);
1758           indexes[x]=index;
1759           *q++=image->colormap[(ssize_t) index];
1760         }
1761         sync=SyncCacheViewAuthenticPixels(image_view,exception);
1762         if (sync == MagickFalse)
1763           status=MagickFalse;
1764         if (image->progress_monitor != (MagickProgressMonitor) NULL)
1765           {
1766             MagickBooleanType
1767               proceed;
1768
1769 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1770   #pragma omp critical (MagickCore_RandomThresholdImageChannel)
1771 #endif
1772             proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1773               image->rows);
1774             if (proceed == MagickFalse)
1775               status=MagickFalse;
1776           }
1777       }
1778       image_view=DestroyCacheView(image_view);
1779       random_info=DestroyRandomInfoThreadSet(random_info);
1780       return(status);
1781     }
1782   if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1783     {
1784       InheritException(exception,&image->exception);
1785       return(MagickFalse);
1786     }
1787   random_info=AcquireRandomInfoThreadSet();
1788   image_view=AcquireCacheView(image);
1789 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1790   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1791 #endif
1792   for (y=0; y < (ssize_t) image->rows; y++)
1793   {
1794     const int
1795       id = GetOpenMPThreadId();
1796
1797     register IndexPacket
1798       *restrict indexes;
1799
1800     register PixelPacket
1801       *restrict q;
1802
1803     register ssize_t
1804       x;
1805
1806     if (status == MagickFalse)
1807       continue;
1808     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1809     if (q == (PixelPacket *) NULL)
1810       {
1811         status=MagickFalse;
1812         continue;
1813       }
1814     indexes=GetCacheViewAuthenticIndexQueue(image_view);
1815     for (x=0; x < (ssize_t) image->columns; x++)
1816     {
1817       if ((channel & RedChannel) != 0)
1818         {
1819           if ((MagickRealType) q->red < min_threshold)
1820             threshold.red=min_threshold;
1821           else
1822             if ((MagickRealType) q->red > max_threshold)
1823               threshold.red=max_threshold;
1824             else
1825               threshold.red=(MagickRealType) (QuantumRange*
1826                 GetPseudoRandomValue(random_info[id]));
1827         }
1828       if ((channel & GreenChannel) != 0)
1829         {
1830           if ((MagickRealType) q->green < min_threshold)
1831             threshold.green=min_threshold;
1832           else
1833             if ((MagickRealType) q->green > max_threshold)
1834               threshold.green=max_threshold;
1835             else
1836               threshold.green=(MagickRealType) (QuantumRange*
1837                 GetPseudoRandomValue(random_info[id]));
1838         }
1839       if ((channel & BlueChannel) != 0)
1840         {
1841           if ((MagickRealType) q->blue < min_threshold)
1842             threshold.blue=min_threshold;
1843           else
1844             if ((MagickRealType) q->blue > max_threshold)
1845               threshold.blue=max_threshold;
1846             else
1847               threshold.blue=(MagickRealType) (QuantumRange*
1848                 GetPseudoRandomValue(random_info[id]));
1849         }
1850       if ((channel & OpacityChannel) != 0)
1851         {
1852           if ((MagickRealType) q->opacity < min_threshold)
1853             threshold.opacity=min_threshold;
1854           else
1855             if ((MagickRealType) q->opacity > max_threshold)
1856               threshold.opacity=max_threshold;
1857             else
1858               threshold.opacity=(MagickRealType) (QuantumRange*
1859                 GetPseudoRandomValue(random_info[id]));
1860         }
1861       if (((channel & IndexChannel) != 0) &&
1862           (image->colorspace == CMYKColorspace))
1863         {
1864           if ((MagickRealType) indexes[x] < min_threshold)
1865             threshold.index=min_threshold;
1866           else
1867             if ((MagickRealType) indexes[x] > max_threshold)
1868               threshold.index=max_threshold;
1869             else
1870               threshold.index=(MagickRealType) (QuantumRange*
1871                 GetPseudoRandomValue(random_info[id]));
1872         }
1873       if ((channel & RedChannel) != 0)
1874         q->red=(Quantum) ((MagickRealType) q->red <= threshold.red ? 0 :
1875           QuantumRange);
1876       if ((channel & GreenChannel) != 0)
1877         q->green=(Quantum) ((MagickRealType) q->green <= threshold.green ? 0 :
1878           QuantumRange);
1879       if ((channel & BlueChannel) != 0)
1880         q->blue=(Quantum) ((MagickRealType) q->blue <= threshold.blue ? 0 :
1881           QuantumRange);
1882       if ((channel & OpacityChannel) != 0)
1883         q->opacity=(Quantum) ((MagickRealType) q->opacity <= threshold.opacity ?
1884           0 : QuantumRange);
1885       if (((channel & IndexChannel) != 0) &&
1886           (image->colorspace == CMYKColorspace))
1887         indexes[x]=(IndexPacket) ((MagickRealType) indexes[x] <=
1888           threshold.index ? 0 : QuantumRange);
1889       q++;
1890     }
1891     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1892       status=MagickFalse;
1893     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1894       {
1895         MagickBooleanType
1896           proceed;
1897
1898 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1899   #pragma omp critical (MagickCore_RandomThresholdImageChannel)
1900 #endif
1901         proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1902           image->rows);
1903         if (proceed == MagickFalse)
1904           status=MagickFalse;
1905       }
1906   }
1907   image_view=DestroyCacheView(image_view);
1908   random_info=DestroyRandomInfoThreadSet(random_info);
1909   return(status);
1910 }
1911 \f
1912 /*
1913 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1914 %                                                                             %
1915 %                                                                             %
1916 %                                                                             %
1917 %     W h i t e T h r e s h o l d I m a g e                                   %
1918 %                                                                             %
1919 %                                                                             %
1920 %                                                                             %
1921 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1922 %
1923 %  WhiteThresholdImage() is like ThresholdImage() but forces all pixels above
1924 %  the threshold into white while leaving all pixels at or below the threshold
1925 %  unchanged.
1926 %
1927 %  The format of the WhiteThresholdImage method is:
1928 %
1929 %      MagickBooleanType WhiteThresholdImage(Image *image,const char *threshold)
1930 %      MagickBooleanType WhiteThresholdImageChannel(Image *image,
1931 %        const ChannelType channel,const char *threshold,
1932 %        ExceptionInfo *exception)
1933 %
1934 %  A description of each parameter follows:
1935 %
1936 %    o image: the image.
1937 %
1938 %    o channel: the channel or channels to be thresholded.
1939 %
1940 %    o threshold: Define the threshold value.
1941 %
1942 %    o exception: return any errors or warnings in this structure.
1943 %
1944 */
1945 MagickExport MagickBooleanType WhiteThresholdImage(Image *image,
1946   const char *threshold)
1947 {
1948   MagickBooleanType
1949     status;
1950
1951   status=WhiteThresholdImageChannel(image,DefaultChannels,threshold,
1952     &image->exception);
1953   return(status);
1954 }
1955
1956 MagickExport MagickBooleanType WhiteThresholdImageChannel(Image *image,
1957   const ChannelType channel,const char *thresholds,ExceptionInfo *exception)
1958 {
1959 #define ThresholdImageTag  "Threshold/Image"
1960
1961   CacheView
1962     *image_view;
1963
1964   GeometryInfo
1965     geometry_info;
1966
1967   MagickBooleanType
1968     status;
1969
1970   MagickPixelPacket
1971     threshold;
1972
1973   MagickOffsetType
1974     progress;
1975
1976   MagickStatusType
1977     flags;
1978
1979   ssize_t
1980     y;
1981
1982   assert(image != (Image *) NULL);
1983   assert(image->signature == MagickSignature);
1984   if (image->debug != MagickFalse)
1985     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1986   if (thresholds == (const char *) NULL)
1987     return(MagickTrue);
1988   if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1989     return(MagickFalse);
1990   flags=ParseGeometry(thresholds,&geometry_info);
1991   GetMagickPixelPacket(image,&threshold);
1992   threshold.red=geometry_info.rho;
1993   threshold.green=geometry_info.sigma;
1994   if ((flags & SigmaValue) == 0)
1995     threshold.green=threshold.red;
1996   threshold.blue=geometry_info.xi;
1997   if ((flags & XiValue) == 0)
1998     threshold.blue=threshold.red;
1999   threshold.opacity=geometry_info.psi;
2000   if ((flags & PsiValue) == 0)
2001     threshold.opacity=threshold.red;
2002   threshold.index=geometry_info.chi;
2003   if ((flags & ChiValue) == 0)
2004     threshold.index=threshold.red;
2005   if ((flags & PercentValue) != 0)
2006     {
2007       threshold.red*=(QuantumRange/100.0);
2008       threshold.green*=(QuantumRange/100.0);
2009       threshold.blue*=(QuantumRange/100.0);
2010       threshold.opacity*=(QuantumRange/100.0);
2011       threshold.index*=(QuantumRange/100.0);
2012     }
2013   /*
2014     White threshold image.
2015   */
2016   status=MagickTrue;
2017   progress=0;
2018   image_view=AcquireCacheView(image);
2019 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2020   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
2021 #endif
2022   for (y=0; y < (ssize_t) image->rows; y++)
2023   {
2024     register IndexPacket
2025       *restrict indexes;
2026
2027     register ssize_t
2028       x;
2029
2030     register PixelPacket
2031       *restrict q;
2032
2033     if (status == MagickFalse)
2034       continue;
2035     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2036     if (q == (PixelPacket *) NULL)
2037       {
2038         status=MagickFalse;
2039         continue;
2040       }
2041     indexes=GetCacheViewAuthenticIndexQueue(image_view);
2042     for (x=0; x < (ssize_t) image->columns; x++)
2043     {
2044       if (channel != DefaultChannels)
2045         {
2046           if (PixelIntensity(q) > MagickPixelIntensity(&threshold))
2047             {
2048               q->red=(Quantum) QuantumRange;
2049               q->green=(Quantum) QuantumRange;
2050               q->blue=(Quantum) QuantumRange;
2051               if (image->colorspace == CMYKColorspace)
2052                 indexes[x]=(Quantum) QuantumRange;
2053             }
2054         }
2055       else
2056         {
2057           if (((channel & RedChannel) != 0) &&
2058               ((MagickRealType) q->red > threshold.red))
2059             q->red=(Quantum) QuantumRange;
2060           if (((channel & GreenChannel) != 0) &&
2061               ((MagickRealType) q->green > threshold.green))
2062             q->green=(Quantum) QuantumRange;
2063           if (((channel & BlueChannel) != 0) &&
2064               ((MagickRealType) q->blue > threshold.blue))
2065             q->blue=(Quantum) QuantumRange;
2066           if (((channel & OpacityChannel) != 0) &&
2067               ((MagickRealType) q->opacity > threshold.opacity))
2068             q->opacity=(Quantum) QuantumRange;
2069           if (((channel & IndexChannel) != 0) &&
2070               (image->colorspace == CMYKColorspace) &&
2071               ((MagickRealType) indexes[x] > threshold.index))
2072             indexes[x]=(Quantum) QuantumRange;
2073         }
2074       q++;
2075     }
2076     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2077       status=MagickFalse;
2078     if (image->progress_monitor != (MagickProgressMonitor) NULL)
2079       {
2080         MagickBooleanType
2081           proceed;
2082
2083 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2084   #pragma omp critical (MagickCore_WhiteThresholdImageChannel)
2085 #endif
2086         proceed=SetImageProgress(image,ThresholdImageTag,progress++,
2087           image->rows);
2088         if (proceed == MagickFalse)
2089           status=MagickFalse;
2090       }
2091   }
2092   image_view=DestroyCacheView(image_view);
2093   return(status);
2094 }