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