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