]> 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-2012 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/colorspace-private.h"
52 #include "MagickCore/configure.h"
53 #include "MagickCore/constitute.h"
54 #include "MagickCore/decorate.h"
55 #include "MagickCore/draw.h"
56 #include "MagickCore/enhance.h"
57 #include "MagickCore/exception.h"
58 #include "MagickCore/exception-private.h"
59 #include "MagickCore/effect.h"
60 #include "MagickCore/fx.h"
61 #include "MagickCore/gem.h"
62 #include "MagickCore/geometry.h"
63 #include "MagickCore/image-private.h"
64 #include "MagickCore/list.h"
65 #include "MagickCore/log.h"
66 #include "MagickCore/memory_.h"
67 #include "MagickCore/monitor.h"
68 #include "MagickCore/monitor-private.h"
69 #include "MagickCore/montage.h"
70 #include "MagickCore/option.h"
71 #include "MagickCore/pixel-accessor.h"
72 #include "MagickCore/quantize.h"
73 #include "MagickCore/quantum.h"
74 #include "MagickCore/random_.h"
75 #include "MagickCore/random-private.h"
76 #include "MagickCore/resize.h"
77 #include "MagickCore/resource_.h"
78 #include "MagickCore/segment.h"
79 #include "MagickCore/shear.h"
80 #include "MagickCore/signature-private.h"
81 #include "MagickCore/string_.h"
82 #include "MagickCore/string-private.h"
83 #include "MagickCore/thread-private.h"
84 #include "MagickCore/threshold.h"
85 #include "MagickCore/token.h"
86 #include "MagickCore/transform.h"
87 #include "MagickCore/xml-tree.h"
88 #include "MagickCore/xml-tree-private.h"
89 \f
90 /*
91   Define declarations.
92 */
93 #define ThresholdsFilename  "thresholds.xml"
94 \f
95 /*
96   Typedef declarations.
97 */
98 struct _ThresholdMap
99 {
100   char
101     *map_id,
102     *description;
103
104   size_t
105     width,
106     height;
107
108   ssize_t
109     divisor,
110     *levels;
111 };
112 \f
113 /*
114   Forward declarations.
115 */
116 static ThresholdMap
117   *GetThresholdMapFile(const char *,const char *,const char *,ExceptionInfo *);
118 \f
119 /*
120 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
121 %                                                                             %
122 %                                                                             %
123 %                                                                             %
124 %     A d a p t i v e T h r e s h o l d I m a g e                             %
125 %                                                                             %
126 %                                                                             %
127 %                                                                             %
128 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
129 %
130 %  AdaptiveThresholdImage() selects an individual threshold for each pixel
131 %  based on the range of intensity values in its local neighborhood.  This
132 %  allows for thresholding of an image whose global intensity histogram
133 %  doesn't contain distinctive peaks.
134 %
135 %  The format of the AdaptiveThresholdImage method is:
136 %
137 %      Image *AdaptiveThresholdImage(const Image *image,const size_t width,
138 %        const size_t height,const double bias,ExceptionInfo *exception)
139 %
140 %  A description of each parameter follows:
141 %
142 %    o image: the image.
143 %
144 %    o width: the width of the local neighborhood.
145 %
146 %    o height: the height of the local neighborhood.
147 %
148 %    o bias: the mean bias.
149 %
150 %    o exception: return any errors or warnings in this structure.
151 %
152 */
153 MagickExport Image *AdaptiveThresholdImage(const Image *image,
154   const size_t width,const size_t height,const double bias,
155   ExceptionInfo *exception)
156 {
157 #define AdaptiveThresholdImageTag  "AdaptiveThreshold/Image"
158
159   CacheView
160     *image_view,
161     *threshold_view;
162
163   Image
164     *threshold_image;
165
166   MagickBooleanType
167     status;
168
169   MagickOffsetType
170     progress;
171
172   MagickSizeType
173     number_pixels;
174
175   ssize_t
176     y;
177
178   /*
179     Initialize threshold image attributes.
180   */
181   assert(image != (Image *) NULL);
182   assert(image->signature == MagickSignature);
183   if (image->debug != MagickFalse)
184     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
185   assert(exception != (ExceptionInfo *) NULL);
186   assert(exception->signature == MagickSignature);
187   if ((width % 2) == 0)
188     ThrowImageException(OptionError,"KernelWidthMustBeAnOddNumber");
189   threshold_image=CloneImage(image,image->columns,image->rows,MagickTrue,
190     exception);
191   if (threshold_image == (Image *) NULL)
192     return((Image *) NULL);
193   status=SetImageStorageClass(threshold_image,DirectClass,exception);
194   if (status == MagickFalse)
195     {
196       threshold_image=DestroyImage(threshold_image);
197       return((Image *) NULL);
198     }
199   /*
200     Threshold image.
201   */
202   status=MagickTrue;
203   progress=0;
204   number_pixels=(MagickSizeType) width*height;
205   image_view=AcquireVirtualCacheView(image,exception);
206   threshold_view=AcquireAuthenticCacheView(threshold_image,exception);
207 #if defined(MAGICKCORE_OPENMP_SUPPORT)
208   #pragma omp parallel for schedule(static,4) shared(progress,status) \
209     dynamic_number_threads(image,image->columns,image->rows,1)
210 #endif
211   for (y=0; y < (ssize_t) image->rows; y++)
212   {
213     register const Quantum
214       *restrict p;
215
216     register Quantum
217       *restrict q;
218
219     register ssize_t
220       x;
221
222     ssize_t
223       center;
224
225     if (status == MagickFalse)
226       continue;
227     p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
228       (height/2L),image->columns+width,height,exception);
229     q=QueueCacheViewAuthenticPixels(threshold_view,0,y,threshold_image->columns,
230       1,exception);
231     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
232       {
233         status=MagickFalse;
234         continue;
235       }
236     center=(ssize_t) GetPixelChannels(image)*(image->columns+width)*(height/2L)+
237       GetPixelChannels(image)*(width/2);
238     for (x=0; x < (ssize_t) image->columns; x++)
239     {
240       register ssize_t
241         i;
242
243       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
244       {
245         double
246           mean,
247           pixel;
248
249         PixelChannel
250           channel;
251
252         PixelTrait
253           threshold_traits,
254           traits;
255
256         register const Quantum
257           *restrict pixels;
258
259         register ssize_t
260           u;
261
262         ssize_t
263           v;
264
265         channel=GetPixelChannelMapChannel(image,i);
266         traits=GetPixelChannelMapTraits(image,channel);
267         threshold_traits=GetPixelChannelMapTraits(threshold_image,channel);
268         if ((traits == UndefinedPixelTrait) ||
269             (threshold_traits == UndefinedPixelTrait))
270           continue;
271         if (((threshold_traits & CopyPixelTrait) != 0) ||
272             (GetPixelMask(image,p) != 0))
273           {
274             SetPixelChannel(threshold_image,channel,p[center+i],q);
275             continue;
276           }
277         pixels=p;
278         pixel=0.0;
279         for (v=0; v < (ssize_t) height; v++)
280         {
281           for (u=0; u < (ssize_t) width; u++)
282           {
283             pixel+=pixels[i];
284             pixels+=GetPixelChannels(image);
285           }
286           pixels+=image->columns*GetPixelChannels(image);
287         }
288         mean=(double) (pixel/number_pixels+bias);
289         SetPixelChannel(threshold_image,channel,(Quantum) ((double)
290           p[center+i] <= mean ? 0 : QuantumRange),q);
291       }
292       p+=GetPixelChannels(image);
293       q+=GetPixelChannels(threshold_image);
294     }
295     if (SyncCacheViewAuthenticPixels(threshold_view,exception) == MagickFalse)
296       status=MagickFalse;
297     if (image->progress_monitor != (MagickProgressMonitor) NULL)
298       {
299         MagickBooleanType
300           proceed;
301
302 #if defined(MAGICKCORE_OPENMP_SUPPORT)
303         #pragma omp critical (MagickCore_AdaptiveThresholdImage)
304 #endif
305         proceed=SetImageProgress(image,AdaptiveThresholdImageTag,progress++,
306           image->rows);
307         if (proceed == MagickFalse)
308           status=MagickFalse;
309       }
310   }
311   threshold_image->type=image->type;
312   threshold_view=DestroyCacheView(threshold_view);
313   image_view=DestroyCacheView(image_view);
314   if (status == MagickFalse)
315     threshold_image=DestroyImage(threshold_image);
316   return(threshold_image);
317 }
318 \f
319 /*
320 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
321 %                                                                             %
322 %                                                                             %
323 %                                                                             %
324 %     B i l e v e l I m a g e                                                 %
325 %                                                                             %
326 %                                                                             %
327 %                                                                             %
328 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
329 %
330 %  BilevelImage() changes the value of individual pixels based on the
331 %  intensity of each pixel channel.  The result is a high-contrast image.
332 %
333 %  More precisely each channel value of the image is 'thresholded' so that if
334 %  it is equal to or less than the given value it is set to zero, while any
335 %  value greater than that give is set to it maximum or QuantumRange.
336 %
337 %  This function is what is used to implement the "-threshold" operator for
338 %  the command line API.
339 %
340 %  If the default channel setting is given the image is thresholded using just
341 %  the gray 'intensity' of the image, rather than the individual channels.
342 %
343 %  The format of the BilevelImage method is:
344 %
345 %      MagickBooleanType BilevelImage(Image *image,const double threshold,
346 %        ExceptionInfo *exception)
347 %
348 %  A description of each parameter follows:
349 %
350 %    o image: the image.
351 %
352 %    o threshold: define the threshold values.
353 %
354 %    o exception: return any errors or warnings in this structure.
355 %
356 %  Aside: You can get the same results as operator using LevelImages()
357 %  with the 'threshold' value for both the black_point and the white_point.
358 %
359 */
360 MagickExport MagickBooleanType BilevelImage(Image *image,const double threshold,
361   ExceptionInfo *exception)
362 {
363 #define ThresholdImageTag  "Threshold/Image"
364
365   CacheView
366     *image_view;
367
368   MagickBooleanType
369     status;
370
371   MagickOffsetType
372     progress;
373
374   ssize_t
375     y;
376
377   assert(image != (Image *) NULL);
378   assert(image->signature == MagickSignature);
379   if (image->debug != MagickFalse)
380     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
381   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
382     return(MagickFalse);
383   /*
384     Bilevel threshold image.
385   */
386   status=MagickTrue;
387   progress=0;
388   image_view=AcquireAuthenticCacheView(image,exception);
389 #if defined(MAGICKCORE_OPENMP_SUPPORT)
390   #pragma omp parallel for schedule(static,4) shared(progress,status) \
391     dynamic_number_threads(image,image->columns,image->rows,1)
392 #endif
393   for (y=0; y < (ssize_t) image->rows; y++)
394   {
395     register ssize_t
396       x;
397
398     register Quantum
399       *restrict q;
400
401     if (status == MagickFalse)
402       continue;
403     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
404     if (q == (Quantum *) NULL)
405       {
406         status=MagickFalse;
407         continue;
408       }
409     for (x=0; x < (ssize_t) image->columns; x++)
410     {
411       double
412         pixel;
413
414       register ssize_t
415         i;
416
417       if (GetPixelMask(image,q) != 0)
418         {
419           q+=GetPixelChannels(image);
420           continue;
421         }
422       pixel=GetPixelIntensity(image,q);
423       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
424       {
425         PixelChannel
426           channel;
427
428         PixelTrait
429           traits;
430
431         channel=GetPixelChannelMapChannel(image,i);
432         traits=GetPixelChannelMapTraits(image,channel);
433         if ((traits & UpdatePixelTrait) == 0)
434           continue;
435         if (image->channel_mask != DefaultChannels)
436           pixel=(double) q[i];
437         q[i]=(Quantum) (pixel <= threshold ? 0 : QuantumRange);
438       }
439       q+=GetPixelChannels(image);
440     }
441     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
442       status=MagickFalse;
443     if (image->progress_monitor != (MagickProgressMonitor) NULL)
444       {
445         MagickBooleanType
446           proceed;
447
448 #if defined(MAGICKCORE_OPENMP_SUPPORT)
449         #pragma omp critical (MagickCore_BilevelImage)
450 #endif
451         proceed=SetImageProgress(image,ThresholdImageTag,progress++,
452           image->rows);
453         if (proceed == MagickFalse)
454           status=MagickFalse;
455       }
456   }
457   image_view=DestroyCacheView(image_view);
458   return(status);
459 }
460 \f
461 /*
462 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
463 %                                                                             %
464 %                                                                             %
465 %                                                                             %
466 %     B l a c k T h r e s h o l d I m a g e                                   %
467 %                                                                             %
468 %                                                                             %
469 %                                                                             %
470 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
471 %
472 %  BlackThresholdImage() is like ThresholdImage() but forces all pixels below
473 %  the threshold into black while leaving all pixels at or above the threshold
474 %  unchanged.
475 %
476 %  The format of the BlackThresholdImage method is:
477 %
478 %      MagickBooleanType BlackThresholdImage(Image *image,
479 %        const char *threshold,ExceptionInfo *exception)
480 %
481 %  A description of each parameter follows:
482 %
483 %    o image: the image.
484 %
485 %    o threshold: define the threshold value.
486 %
487 %    o exception: return any errors or warnings in this structure.
488 %
489 */
490 MagickExport MagickBooleanType BlackThresholdImage(Image *image,
491   const char *thresholds,ExceptionInfo *exception)
492 {
493 #define ThresholdImageTag  "Threshold/Image"
494
495   CacheView
496     *image_view;
497
498   GeometryInfo
499     geometry_info;
500
501   MagickBooleanType
502     status;
503
504   MagickOffsetType
505     progress;
506
507   PixelInfo
508     threshold;
509
510   MagickStatusType
511     flags;
512
513   ssize_t
514     y;
515
516   assert(image != (Image *) NULL);
517   assert(image->signature == MagickSignature);
518   if (image->debug != MagickFalse)
519     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
520   if (thresholds == (const char *) NULL)
521     return(MagickTrue);
522   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
523     return(MagickFalse);
524   if (IsGrayColorspace(image->colorspace) != MagickFalse)
525     (void) TransformImageColorspace(image,RGBColorspace,exception);
526   GetPixelInfo(image,&threshold);
527   flags=ParseGeometry(thresholds,&geometry_info);
528   threshold.red=geometry_info.rho;
529   threshold.green=geometry_info.rho;
530   threshold.blue=geometry_info.rho;
531   threshold.black=geometry_info.rho;
532   threshold.alpha=100.0;
533   if ((flags & SigmaValue) != 0)
534     threshold.green=geometry_info.sigma;
535   if ((flags & XiValue) != 0)
536     threshold.blue=geometry_info.xi;
537   if ((flags & PsiValue) != 0)
538     threshold.alpha=geometry_info.psi;
539   if (threshold.colorspace == CMYKColorspace)
540     {
541       if ((flags & PsiValue) != 0)
542         threshold.black=geometry_info.psi;
543       if ((flags & ChiValue) != 0)
544         threshold.alpha=geometry_info.chi;
545     }
546   if ((flags & PercentValue) != 0)
547     {
548       threshold.red*=(QuantumRange/100.0);
549       threshold.green*=(QuantumRange/100.0);
550       threshold.blue*=(QuantumRange/100.0);
551       threshold.black*=(QuantumRange/100.0);
552       threshold.alpha*=(QuantumRange/100.0);
553     }
554   /*
555     White threshold image.
556   */
557   status=MagickTrue;
558   progress=0;
559   image_view=AcquireAuthenticCacheView(image,exception);
560 #if defined(MAGICKCORE_OPENMP_SUPPORT)
561   #pragma omp parallel for schedule(static,4) shared(progress,status) \
562     dynamic_number_threads(image,image->columns,image->rows,1)
563 #endif
564   for (y=0; y < (ssize_t) image->rows; y++)
565   {
566     register ssize_t
567       x;
568
569     register Quantum
570       *restrict q;
571
572     if (status == MagickFalse)
573       continue;
574     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
575     if (q == (Quantum *) NULL)
576       {
577         status=MagickFalse;
578         continue;
579       }
580     for (x=0; x < (ssize_t) image->columns; x++)
581     {
582       double
583         pixel;
584
585       register ssize_t
586         i;
587
588       if (GetPixelMask(image,q) != 0)
589         {
590           q+=GetPixelChannels(image);
591           continue;
592         }
593       pixel=GetPixelIntensity(image,q);
594       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
595       {
596         PixelChannel
597           channel;
598
599         PixelTrait
600           traits;
601
602         channel=GetPixelChannelMapChannel(image,i);
603         traits=GetPixelChannelMapTraits(image,channel);
604         if ((traits & UpdatePixelTrait) == 0)
605           continue;
606         if (image->channel_mask != DefaultChannels)
607           pixel=(double) q[i];
608         if (pixel <= GetPixelInfoChannel(&threshold,channel))
609           q[i]=0;
610       }
611       q+=GetPixelChannels(image);
612     }
613     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
614       status=MagickFalse;
615     if (image->progress_monitor != (MagickProgressMonitor) NULL)
616       {
617         MagickBooleanType
618           proceed;
619
620 #if defined(MAGICKCORE_OPENMP_SUPPORT)
621         #pragma omp critical (MagickCore_BlackThresholdImage)
622 #endif
623         proceed=SetImageProgress(image,ThresholdImageTag,progress++,
624           image->rows);
625         if (proceed == MagickFalse)
626           status=MagickFalse;
627       }
628   }
629   image_view=DestroyCacheView(image_view);
630   return(status);
631 }
632 \f
633 /*
634 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
635 %                                                                             %
636 %                                                                             %
637 %                                                                             %
638 %     C l a m p I m a g e                                                     %
639 %                                                                             %
640 %                                                                             %
641 %                                                                             %
642 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
643 %
644 %  ClampImage() restricts the color range from 0 to the quantum depth.
645 %
646 %  The format of the ClampImage method is:
647 %
648 %      MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
649 %
650 %  A description of each parameter follows:
651 %
652 %    o image: the image.
653 %
654 %    o exception: return any errors or warnings in this structure.
655 %
656 */
657
658 static inline Quantum ClampToUnsignedQuantum(const Quantum quantum)
659 {
660   if (quantum <= 0)
661     return(0);
662   if (quantum >= QuantumRange)
663     return(QuantumRange);
664   return(quantum);
665 }
666
667 MagickExport MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
668 {
669 #if !defined(MAGICKCORE_HDRI_SUPPORT)
670   return(MagickTrue);
671 #else
672 #define ClampImageTag  "Clamp/Image"
673
674   CacheView
675     *image_view;
676
677   MagickBooleanType
678     status;
679
680   MagickOffsetType
681     progress;
682
683   ssize_t
684     y;
685
686   assert(image != (Image *) NULL);
687   assert(image->signature == MagickSignature);
688   if (image->debug != MagickFalse)
689     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
690   if (image->storage_class == PseudoClass)
691     {
692       register ssize_t
693         i;
694
695       register PixelInfo
696         *restrict q;
697
698       q=image->colormap;
699       for (i=0; i < (ssize_t) image->colors; i++)
700       {
701         q->red=(double) ClampToUnsignedQuantum(ClampToQuantum(q->red));
702         q->green=(double) ClampToUnsignedQuantum(ClampToQuantum(q->green));
703         q->blue=(double) ClampToUnsignedQuantum(ClampToQuantum(q->blue));
704         q->alpha=(double) ClampToUnsignedQuantum(ClampToQuantum(q->alpha));
705         q++;
706       }
707       return(SyncImage(image,exception));
708     }
709   /*
710     Clamp image.
711   */
712   status=MagickTrue;
713   progress=0;
714   image_view=AcquireAuthenticCacheView(image,exception);
715 #if defined(MAGICKCORE_OPENMP_SUPPORT)
716   #pragma omp parallel for schedule(static,4) shared(progress,status) \
717     dynamic_number_threads(image,image->columns,image->rows,1)
718 #endif
719   for (y=0; y < (ssize_t) image->rows; y++)
720   {
721     register ssize_t
722       x;
723
724     register Quantum
725       *restrict q;
726
727     if (status == MagickFalse)
728       continue;
729     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
730     if (q == (Quantum *) NULL)
731       {
732         status=MagickFalse;
733         continue;
734       }
735     for (x=0; x < (ssize_t) image->columns; x++)
736     {
737       register ssize_t
738         i;
739
740       if (GetPixelMask(image,q) != 0)
741         {
742           q+=GetPixelChannels(image);
743           continue;
744         }
745       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
746       {
747         PixelChannel
748           channel;
749
750         PixelTrait
751           traits;
752
753         channel=GetPixelChannelMapChannel(image,i);
754         traits=GetPixelChannelMapTraits(image,channel);
755         if (traits == UndefinedPixelTrait)
756           continue;
757         q[i]=ClampToUnsignedQuantum(q[i]);
758       }
759       q+=GetPixelChannels(image);
760     }
761     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
762       status=MagickFalse;
763     if (image->progress_monitor != (MagickProgressMonitor) NULL)
764       {
765         MagickBooleanType
766           proceed;
767
768 #if defined(MAGICKCORE_OPENMP_SUPPORT)
769         #pragma omp critical (MagickCore_ClampImage)
770 #endif
771         proceed=SetImageProgress(image,ClampImageTag,progress++,image->rows);
772         if (proceed == MagickFalse)
773           status=MagickFalse;
774       }
775   }
776   image_view=DestroyCacheView(image_view);
777   return(status);
778 #endif
779 }
780 \f
781 /*
782 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
783 %                                                                             %
784 %                                                                             %
785 %                                                                             %
786 %  D e s t r o y T h r e s h o l d M a p                                      %
787 %                                                                             %
788 %                                                                             %
789 %                                                                             %
790 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
791 %
792 %  DestroyThresholdMap() de-allocate the given ThresholdMap
793 %
794 %  The format of the ListThresholdMaps method is:
795 %
796 %      ThresholdMap *DestroyThresholdMap(Threshold *map)
797 %
798 %  A description of each parameter follows.
799 %
800 %    o map:    Pointer to the Threshold map to destroy
801 %
802 */
803 MagickExport ThresholdMap *DestroyThresholdMap(ThresholdMap *map)
804 {
805   assert(map != (ThresholdMap *) NULL);
806   if (map->map_id != (char *) NULL)
807     map->map_id=DestroyString(map->map_id);
808   if (map->description != (char *) NULL)
809     map->description=DestroyString(map->description);
810   if (map->levels != (ssize_t *) NULL)
811     map->levels=(ssize_t *) RelinquishMagickMemory(map->levels);
812   map=(ThresholdMap *) RelinquishMagickMemory(map);
813   return(map);
814 }
815 \f
816 /*
817 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
818 %                                                                             %
819 %                                                                             %
820 %                                                                             %
821 %  G e t T h r e s h o l d M a p                                              %
822 %                                                                             %
823 %                                                                             %
824 %                                                                             %
825 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
826 %
827 %  GetThresholdMap() loads and searches one or more threshold map files for the
828 %  map matching the given name or alias.
829 %
830 %  The format of the GetThresholdMap method is:
831 %
832 %      ThresholdMap *GetThresholdMap(const char *map_id,
833 %        ExceptionInfo *exception)
834 %
835 %  A description of each parameter follows.
836 %
837 %    o map_id:  ID of the map to look for.
838 %
839 %    o exception: return any errors or warnings in this structure.
840 %
841 */
842 MagickExport ThresholdMap *GetThresholdMap(const char *map_id,
843   ExceptionInfo *exception)
844 {
845   const StringInfo
846     *option;
847
848   LinkedListInfo
849     *options;
850
851   ThresholdMap
852     *map;
853
854   map=(ThresholdMap *)NULL;
855   options=GetConfigureOptions(ThresholdsFilename,exception);
856   while ((option=(const StringInfo *) GetNextValueInLinkedList(options)) !=
857          (const StringInfo *) NULL && (map == (ThresholdMap *) NULL))
858     map=GetThresholdMapFile((const char *) GetStringInfoDatum(option),
859       GetStringInfoPath(option),map_id,exception);
860   options=DestroyConfigureOptions(options);
861   return(map);
862 }
863 \f
864 /*
865 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
866 %                                                                             %
867 %                                                                             %
868 %                                                                             %
869 +  G e t T h r e s h o l d M a p F i l e                                      %
870 %                                                                             %
871 %                                                                             %
872 %                                                                             %
873 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
874 %
875 %  GetThresholdMapFile() look for a given threshold map name or alias in the
876 %  given XML file data, and return the allocated the map when found.
877 %
878 %  The format of the ListThresholdMaps method is:
879 %
880 %      ThresholdMap *GetThresholdMap(const char *xml,const char *filename,
881 %         const char *map_id,ExceptionInfo *exception)
882 %
883 %  A description of each parameter follows.
884 %
885 %    o xml:  The threshold map list in XML format.
886 %
887 %    o filename:  The threshold map XML filename.
888 %
889 %    o map_id:  ID of the map to look for in XML list.
890 %
891 %    o exception: return any errors or warnings in this structure.
892 %
893 */
894 static ThresholdMap *GetThresholdMapFile(const char *xml,
895   const char *filename,const char *map_id,ExceptionInfo *exception)
896 {
897   char
898     *p;
899
900   const char
901     *attribute,
902     *content;
903
904   double
905     value;
906
907   register ssize_t
908     i;
909
910   ThresholdMap
911     *map;
912
913   XMLTreeInfo
914     *description,
915     *levels,
916     *threshold,
917     *thresholds;
918
919   (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
920     "Loading threshold map file \"%s\" ...",filename);
921   map=(ThresholdMap *) NULL;
922   thresholds=NewXMLTree(xml,exception);
923   if (thresholds == (XMLTreeInfo *) NULL)
924     return(map);
925   for (threshold=GetXMLTreeChild(thresholds,"threshold");
926        threshold != (XMLTreeInfo *) NULL;
927        threshold=GetNextXMLTreeTag(threshold))
928   {
929     attribute=GetXMLTreeAttribute(threshold,"map");
930     if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
931       break;
932     attribute=GetXMLTreeAttribute(threshold,"alias");
933     if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
934       break;
935   }
936   if (threshold == (XMLTreeInfo *) NULL)
937     return(map);
938   description=GetXMLTreeChild(threshold,"description");
939   if (description == (XMLTreeInfo *) NULL)
940     {
941       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
942         "XmlMissingElement", "<description>, map \"%s\"",map_id);
943       thresholds=DestroyXMLTree(thresholds);
944       return(map);
945     }
946   levels=GetXMLTreeChild(threshold,"levels");
947   if (levels == (XMLTreeInfo *) NULL)
948     {
949       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
950         "XmlMissingElement", "<levels>, map \"%s\"", map_id);
951       thresholds=DestroyXMLTree(thresholds);
952       return(map);
953     }
954   map=(ThresholdMap *) AcquireMagickMemory(sizeof(ThresholdMap));
955   if (map == (ThresholdMap *) NULL)
956     ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
957   map->map_id=(char *) NULL;
958   map->description=(char *) NULL;
959   map->levels=(ssize_t *) NULL;
960   attribute=GetXMLTreeAttribute(threshold,"map");
961   if (attribute != (char *) NULL)
962     map->map_id=ConstantString(attribute);
963   content=GetXMLTreeContent(description);
964   if (content != (char *) NULL)
965     map->description=ConstantString(content);
966   attribute=GetXMLTreeAttribute(levels,"width");
967   if (attribute == (char *) NULL)
968     {
969       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
970         "XmlMissingAttribute", "<levels width>, map \"%s\"",map_id);
971       thresholds=DestroyXMLTree(thresholds);
972       map=DestroyThresholdMap(map);
973       return(map);
974     }
975   map->width=StringToUnsignedLong(attribute);
976   if (map->width == 0)
977     {
978       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
979        "XmlInvalidAttribute", "<levels width>, map \"%s\"",map_id);
980       thresholds=DestroyXMLTree(thresholds);
981       map=DestroyThresholdMap(map);
982       return(map);
983     }
984   attribute=GetXMLTreeAttribute(levels,"height");
985   if (attribute == (char *) NULL)
986     {
987       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
988         "XmlMissingAttribute", "<levels height>, map \"%s\"",map_id);
989       thresholds=DestroyXMLTree(thresholds);
990       map=DestroyThresholdMap(map);
991       return(map);
992     }
993   map->height=StringToUnsignedLong(attribute);
994   if (map->height == 0)
995     {
996       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
997         "XmlInvalidAttribute", "<levels height>, map \"%s\"",map_id);
998       thresholds=DestroyXMLTree(thresholds);
999       map=DestroyThresholdMap(map);
1000       return(map);
1001     }
1002   attribute=GetXMLTreeAttribute(levels,"divisor");
1003   if (attribute == (char *) NULL)
1004     {
1005       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1006         "XmlMissingAttribute", "<levels divisor>, map \"%s\"",map_id);
1007       thresholds=DestroyXMLTree(thresholds);
1008       map=DestroyThresholdMap(map);
1009       return(map);
1010     }
1011   map->divisor=(ssize_t) StringToLong(attribute);
1012   if (map->divisor < 2)
1013     {
1014       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1015         "XmlInvalidAttribute", "<levels divisor>, map \"%s\"",map_id);
1016       thresholds=DestroyXMLTree(thresholds);
1017       map=DestroyThresholdMap(map);
1018       return(map);
1019     }
1020   content=GetXMLTreeContent(levels);
1021   if (content == (char *) NULL)
1022     {
1023       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1024         "XmlMissingContent", "<levels>, map \"%s\"",map_id);
1025       thresholds=DestroyXMLTree(thresholds);
1026       map=DestroyThresholdMap(map);
1027       return(map);
1028     }
1029   map->levels=(ssize_t *) AcquireQuantumMemory((size_t) map->width,map->height*
1030     sizeof(*map->levels));
1031   if (map->levels == (ssize_t *) NULL)
1032     ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
1033   for (i=0; i < (ssize_t) (map->width*map->height); i++)
1034   {
1035     map->levels[i]=(ssize_t) strtol(content,&p,10);
1036     if (p == content)
1037       {
1038         (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1039           "XmlInvalidContent", "<level> too few values, map \"%s\"",map_id);
1040         thresholds=DestroyXMLTree(thresholds);
1041         map=DestroyThresholdMap(map);
1042         return(map);
1043       }
1044     if ((map->levels[i] < 0) || (map->levels[i] > map->divisor))
1045       {
1046         (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1047           "XmlInvalidContent", "<level> %.20g out of range, map \"%s\"",
1048           (double) map->levels[i],map_id);
1049         thresholds=DestroyXMLTree(thresholds);
1050         map=DestroyThresholdMap(map);
1051         return(map);
1052       }
1053     content=p;
1054   }
1055   value=(double) strtol(content,&p,10);
1056   (void) value;
1057   if (p != content)
1058     {
1059       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1060         "XmlInvalidContent", "<level> too many values, map \"%s\"",map_id);
1061      thresholds=DestroyXMLTree(thresholds);
1062      map=DestroyThresholdMap(map);
1063      return(map);
1064    }
1065   thresholds=DestroyXMLTree(thresholds);
1066   return(map);
1067 }
1068 \f
1069 /*
1070 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1071 %                                                                             %
1072 %                                                                             %
1073 %                                                                             %
1074 +  L i s t T h r e s h o l d M a p F i l e                                    %
1075 %                                                                             %
1076 %                                                                             %
1077 %                                                                             %
1078 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1079 %
1080 %  ListThresholdMapFile() lists the threshold maps and their descriptions
1081 %  in the given XML file data.
1082 %
1083 %  The format of the ListThresholdMaps method is:
1084 %
1085 %      MagickBooleanType ListThresholdMaps(FILE *file,const char*xml,
1086 %         const char *filename,ExceptionInfo *exception)
1087 %
1088 %  A description of each parameter follows.
1089 %
1090 %    o file:  An pointer to the output FILE.
1091 %
1092 %    o xml:  The threshold map list in XML format.
1093 %
1094 %    o filename:  The threshold map XML filename.
1095 %
1096 %    o exception: return any errors or warnings in this structure.
1097 %
1098 */
1099 MagickBooleanType ListThresholdMapFile(FILE *file,const char *xml,
1100   const char *filename,ExceptionInfo *exception)
1101 {
1102   const char
1103     *alias,
1104     *content,
1105     *map;
1106
1107   XMLTreeInfo
1108     *description,
1109     *threshold,
1110     *thresholds;
1111
1112   assert( xml != (char *)NULL );
1113   assert( file != (FILE *)NULL );
1114   (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1115     "Loading threshold map file \"%s\" ...",filename);
1116   thresholds=NewXMLTree(xml,exception);
1117   if ( thresholds == (XMLTreeInfo *)NULL )
1118     return(MagickFalse);
1119   (void) FormatLocaleFile(file,"%-16s %-12s %s\n","Map","Alias","Description");
1120   (void) FormatLocaleFile(file,
1121     "----------------------------------------------------\n");
1122   threshold=GetXMLTreeChild(thresholds,"threshold");
1123   for ( ; threshold != (XMLTreeInfo *) NULL;
1124           threshold=GetNextXMLTreeTag(threshold))
1125   {
1126     map=GetXMLTreeAttribute(threshold,"map");
1127     if (map == (char *) NULL)
1128       {
1129         (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1130           "XmlMissingAttribute", "<map>");
1131         thresholds=DestroyXMLTree(thresholds);
1132         return(MagickFalse);
1133       }
1134     alias=GetXMLTreeAttribute(threshold,"alias");
1135     description=GetXMLTreeChild(threshold,"description");
1136     if (description == (XMLTreeInfo *) NULL)
1137       {
1138         (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1139           "XmlMissingElement", "<description>, map \"%s\"",map);
1140         thresholds=DestroyXMLTree(thresholds);
1141         return(MagickFalse);
1142       }
1143     content=GetXMLTreeContent(description);
1144     if (content == (char *) NULL)
1145       {
1146         (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1147           "XmlMissingContent", "<description>, map \"%s\"", map);
1148         thresholds=DestroyXMLTree(thresholds);
1149         return(MagickFalse);
1150       }
1151     (void) FormatLocaleFile(file,"%-16s %-12s %s\n",map,alias ? alias : "",
1152       content);
1153   }
1154   thresholds=DestroyXMLTree(thresholds);
1155   return(MagickTrue);
1156 }
1157 \f
1158 /*
1159 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1160 %                                                                             %
1161 %                                                                             %
1162 %                                                                             %
1163 %  L i s t T h r e s h o l d M a p s                                          %
1164 %                                                                             %
1165 %                                                                             %
1166 %                                                                             %
1167 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1168 %
1169 %  ListThresholdMaps() lists the threshold maps and their descriptions
1170 %  as defined by "threshold.xml" to a file.
1171 %
1172 %  The format of the ListThresholdMaps method is:
1173 %
1174 %      MagickBooleanType ListThresholdMaps(FILE *file,ExceptionInfo *exception)
1175 %
1176 %  A description of each parameter follows.
1177 %
1178 %    o file:  An pointer to the output FILE.
1179 %
1180 %    o exception: return any errors or warnings in this structure.
1181 %
1182 */
1183 MagickExport MagickBooleanType ListThresholdMaps(FILE *file,
1184   ExceptionInfo *exception)
1185 {
1186   const StringInfo
1187     *option;
1188
1189   LinkedListInfo
1190     *options;
1191
1192   MagickStatusType
1193     status;
1194
1195   status=MagickFalse;
1196   if (file == (FILE *) NULL)
1197     file=stdout;
1198   options=GetConfigureOptions(ThresholdsFilename,exception);
1199   (void) FormatLocaleFile(file,
1200     "\n   Threshold Maps for Ordered Dither Operations\n");
1201   while ((option=(const StringInfo *) GetNextValueInLinkedList(options)) !=
1202          (const StringInfo *) NULL)
1203   {
1204     (void) FormatLocaleFile(file,"\nPATH: %s\n\n",GetStringInfoPath(option));
1205     status|=ListThresholdMapFile(file,(const char *) GetStringInfoDatum(option),
1206       GetStringInfoPath(option),exception);
1207   }
1208   options=DestroyConfigureOptions(options);
1209   return(status != 0 ? MagickTrue : MagickFalse);
1210 }
1211 \f
1212 /*
1213 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1214 %                                                                             %
1215 %                                                                             %
1216 %                                                                             %
1217 %     O r d e r e d P o s t e r i z e I m a g e                               %
1218 %                                                                             %
1219 %                                                                             %
1220 %                                                                             %
1221 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1222 %
1223 %  OrderedPosterizeImage() will perform a ordered dither based on a number
1224 %  of pre-defined dithering threshold maps, but over multiple intensity
1225 %  levels, which can be different for different channels, according to the
1226 %  input argument.
1227 %
1228 %  The format of the OrderedPosterizeImage method is:
1229 %
1230 %      MagickBooleanType OrderedPosterizeImage(Image *image,
1231 %        const char *threshold_map,ExceptionInfo *exception)
1232 %
1233 %  A description of each parameter follows:
1234 %
1235 %    o image: the image.
1236 %
1237 %    o threshold_map: A string containing the name of the threshold dither
1238 %      map to use, followed by zero or more numbers representing the number
1239 %      of color levels tho dither between.
1240 %
1241 %      Any level number less than 2 will be equivalent to 2, and means only
1242 %      binary dithering will be applied to each color channel.
1243 %
1244 %      No numbers also means a 2 level (bitmap) dither will be applied to all
1245 %      channels, while a single number is the number of levels applied to each
1246 %      channel in sequence.  More numbers will be applied in turn to each of
1247 %      the color channels.
1248 %
1249 %      For example: "o3x3,6" will generate a 6 level posterization of the
1250 %      image with a ordered 3x3 diffused pixel dither being applied between
1251 %      each level. While checker,8,8,4 will produce a 332 colormaped image
1252 %      with only a single checkerboard hash pattern (50% grey) between each
1253 %      color level, to basically double the number of color levels with
1254 %      a bare minimim of dithering.
1255 %
1256 %    o exception: return any errors or warnings in this structure.
1257 %
1258 */
1259 MagickExport MagickBooleanType OrderedPosterizeImage(Image *image,
1260   const char *threshold_map,ExceptionInfo *exception)
1261 {
1262 #define DitherImageTag  "Dither/Image"
1263
1264   CacheView
1265     *image_view;
1266
1267   char
1268     token[MaxTextExtent];
1269
1270   const char
1271     *p;
1272
1273   MagickBooleanType
1274     status;
1275
1276   MagickOffsetType
1277     progress;
1278
1279   double
1280     levels[CompositePixelChannel];
1281
1282   register ssize_t
1283     i;
1284
1285   ssize_t
1286     y;
1287
1288   ThresholdMap
1289     *map;
1290
1291   assert(image != (Image *) NULL);
1292   assert(image->signature == MagickSignature);
1293   if (image->debug != MagickFalse)
1294     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1295   assert(exception != (ExceptionInfo *) NULL);
1296   assert(exception->signature == MagickSignature);
1297   if (threshold_map == (const char *) NULL)
1298     return(MagickTrue);
1299   p=(char *) threshold_map;
1300   while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
1301          (*p != '\0'))
1302     p++;
1303   threshold_map=p;
1304   while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
1305          (*p != '\0'))
1306   {
1307     if ((p-threshold_map) >= (MaxTextExtent-1))
1308       break;
1309     token[p-threshold_map]=(*p);
1310     p++;
1311   }
1312   token[p-threshold_map]='\0';
1313   map=GetThresholdMap(token,exception);
1314   if (map == (ThresholdMap *) NULL)
1315     {
1316       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1317         "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
1318       return(MagickFalse);
1319     }
1320   for (i=0; i < MaxPixelChannels; i++)
1321     levels[i]=2.0;
1322   p=strchr((char *) threshold_map,',');
1323   if ((p != (char *) NULL) && (isdigit((int) ((unsigned char) *(++p))) != 0))
1324     for (i=0; (*p != '\0') && (i < MaxPixelChannels); i++)
1325     {
1326       GetMagickToken(p,&p,token);
1327       if (*token == ',')
1328         GetMagickToken(p,&p,token);
1329       levels[i]=StringToDouble(token,(char **) NULL);
1330     }
1331   for (i=0; i < MaxPixelChannels; i++)
1332     if (fabs(levels[i]) >= 1)
1333       levels[i]-=1.0;
1334   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1335     return(MagickFalse);
1336   status=MagickTrue;
1337   progress=0;
1338   image_view=AcquireAuthenticCacheView(image,exception);
1339 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1340   #pragma omp parallel for schedule(static,4) shared(progress,status) \
1341     dynamic_number_threads(image,image->columns,image->rows,1)
1342 #endif
1343   for (y=0; y < (ssize_t) image->rows; y++)
1344   {
1345     register ssize_t
1346       x;
1347
1348     register Quantum
1349       *restrict q;
1350
1351     if (status == MagickFalse)
1352       continue;
1353     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1354     if (q == (Quantum *) NULL)
1355       {
1356         status=MagickFalse;
1357         continue;
1358       }
1359     for (x=0; x < (ssize_t) image->columns; x++)
1360     {
1361       register ssize_t
1362         i;
1363
1364       ssize_t
1365         n;
1366
1367       n=0;
1368       if (GetPixelMask(image,q) != 0)
1369         {
1370           q+=GetPixelChannels(image);
1371           continue;
1372         }
1373       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1374       {
1375         PixelChannel
1376           channel;
1377
1378         PixelTrait
1379           traits;
1380
1381         ssize_t
1382           level,
1383           threshold;
1384
1385         channel=GetPixelChannelMapChannel(image,i);
1386         traits=GetPixelChannelMapTraits(image,channel);
1387         if ((traits & UpdatePixelTrait) == 0)
1388           continue;
1389         if (fabs(levels[n++]) < MagickEpsilon)
1390           continue;
1391         threshold=(ssize_t) (QuantumScale*q[i]*(levels[n]*(map->divisor-1)+1));
1392         level=threshold/(map->divisor-1);
1393         threshold-=level*(map->divisor-1);
1394         q[i]=ClampToQuantum((double) (level+(threshold >=
1395           map->levels[(x % map->width)+map->width*(y % map->height)]))*
1396           QuantumRange/levels[n]);
1397         n++;
1398       }
1399       q+=GetPixelChannels(image);
1400     }
1401     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1402       status=MagickFalse;
1403     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1404       {
1405         MagickBooleanType
1406           proceed;
1407
1408 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1409         #pragma omp critical (MagickCore_OrderedPosterizeImage)
1410 #endif
1411         proceed=SetImageProgress(image,DitherImageTag,progress++,image->rows);
1412         if (proceed == MagickFalse)
1413           status=MagickFalse;
1414       }
1415   }
1416   image_view=DestroyCacheView(image_view);
1417   map=DestroyThresholdMap(map);
1418   return(MagickTrue);
1419 }
1420 \f
1421 /*
1422 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1423 %                                                                             %
1424 %                                                                             %
1425 %                                                                             %
1426 %     R a n d o m T h r e s h o l d I m a g e                                 %
1427 %                                                                             %
1428 %                                                                             %
1429 %                                                                             %
1430 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1431 %
1432 %  RandomThresholdImage() changes the value of individual pixels based on the
1433 %  intensity of each pixel compared to a random threshold.  The result is a
1434 %  low-contrast, two color image.
1435 %
1436 %  The format of the RandomThresholdImage method is:
1437 %
1438 %      MagickBooleanType RandomThresholdImage(Image *image,
1439 %        const char *thresholds,ExceptionInfo *exception)
1440 %
1441 %  A description of each parameter follows:
1442 %
1443 %    o image: the image.
1444 %
1445 %    o thresholds: a geometry string containing low,high thresholds.  If the
1446 %      string contains 2x2, 3x3, or 4x4, an ordered dither of order 2, 3, or 4
1447 %      is performed instead.
1448 %
1449 %    o exception: return any errors or warnings in this structure.
1450 %
1451 */
1452 MagickExport MagickBooleanType RandomThresholdImage(Image *image,
1453   const char *thresholds,ExceptionInfo *exception)
1454 {
1455 #define ThresholdImageTag  "Threshold/Image"
1456
1457   CacheView
1458     *image_view;
1459
1460   GeometryInfo
1461     geometry_info;
1462
1463   MagickStatusType
1464     flags;
1465
1466   MagickBooleanType
1467     status;
1468
1469   MagickOffsetType
1470     progress;
1471
1472   PixelInfo
1473     threshold;
1474
1475   double
1476     min_threshold,
1477     max_threshold;
1478
1479   RandomInfo
1480     **restrict random_info;
1481
1482   ssize_t
1483     y;
1484
1485   unsigned long
1486     key;
1487
1488   assert(image != (Image *) NULL);
1489   assert(image->signature == MagickSignature);
1490   if (image->debug != MagickFalse)
1491     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1492   assert(exception != (ExceptionInfo *) NULL);
1493   assert(exception->signature == MagickSignature);
1494   if (thresholds == (const char *) NULL)
1495     return(MagickTrue);
1496   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1497     return(MagickFalse);
1498   GetPixelInfo(image,&threshold);
1499   min_threshold=0.0;
1500   max_threshold=(double) QuantumRange;
1501   flags=ParseGeometry(thresholds,&geometry_info);
1502   min_threshold=geometry_info.rho;
1503   max_threshold=geometry_info.sigma;
1504   if ((flags & SigmaValue) == 0)
1505     max_threshold=min_threshold;
1506   if (strchr(thresholds,'%') != (char *) NULL)
1507     {
1508       max_threshold*=(double) (0.01*QuantumRange);
1509       min_threshold*=(double) (0.01*QuantumRange);
1510     }
1511   /*
1512     Random threshold image.
1513   */
1514   status=MagickTrue;
1515   progress=0;
1516   random_info=AcquireRandomInfoThreadSet();
1517   key=GetRandomSecretKey(random_info[0]);
1518   image_view=AcquireAuthenticCacheView(image,exception);
1519 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1520   #pragma omp parallel for schedule(static,4) shared(progress,status) \
1521     dynamic_number_threads(image,image->columns,image->rows,key == ~0UL)
1522 #endif
1523   for (y=0; y < (ssize_t) image->rows; y++)
1524   {
1525     const int
1526       id = GetOpenMPThreadId();
1527
1528     register Quantum
1529       *restrict q;
1530
1531     register ssize_t
1532       x;
1533
1534     if (status == MagickFalse)
1535       continue;
1536     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1537     if (q == (Quantum *) NULL)
1538       {
1539         status=MagickFalse;
1540         continue;
1541       }
1542     for (x=0; x < (ssize_t) image->columns; x++)
1543     {
1544       register ssize_t
1545         i;
1546
1547       if (GetPixelMask(image,q) != 0)
1548         {
1549           q+=GetPixelChannels(image);
1550           continue;
1551         }
1552       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1553       {
1554         double
1555           threshold;
1556
1557         PixelChannel
1558           channel;
1559
1560         PixelTrait
1561           traits;
1562
1563         channel=GetPixelChannelMapChannel(image,i);
1564         traits=GetPixelChannelMapTraits(image,channel);
1565         if ((traits & UpdatePixelTrait) == 0)
1566           continue;
1567         if ((double) q[i] < min_threshold)
1568           threshold=min_threshold;
1569         else
1570           if ((double) q[i] > max_threshold)
1571             threshold=max_threshold;
1572           else
1573             threshold=(double) (QuantumRange*
1574               GetPseudoRandomValue(random_info[id]));
1575         q[i]=(double) q[i] <= threshold ? 0 : QuantumRange;
1576       }
1577       q+=GetPixelChannels(image);
1578     }
1579     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1580       status=MagickFalse;
1581     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1582       {
1583         MagickBooleanType
1584           proceed;
1585
1586 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1587         #pragma omp critical (MagickCore_RandomThresholdImage)
1588 #endif
1589         proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1590           image->rows);
1591         if (proceed == MagickFalse)
1592           status=MagickFalse;
1593       }
1594   }
1595   image_view=DestroyCacheView(image_view);
1596   random_info=DestroyRandomInfoThreadSet(random_info);
1597   return(status);
1598 }
1599 \f
1600 /*
1601 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1602 %                                                                             %
1603 %                                                                             %
1604 %                                                                             %
1605 %     W h i t e T h r e s h o l d I m a g e                                   %
1606 %                                                                             %
1607 %                                                                             %
1608 %                                                                             %
1609 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1610 %
1611 %  WhiteThresholdImage() is like ThresholdImage() but forces all pixels above
1612 %  the threshold into white while leaving all pixels at or below the threshold
1613 %  unchanged.
1614 %
1615 %  The format of the WhiteThresholdImage method is:
1616 %
1617 %      MagickBooleanType WhiteThresholdImage(Image *image,
1618 %        const char *threshold,ExceptionInfo *exception)
1619 %
1620 %  A description of each parameter follows:
1621 %
1622 %    o image: the image.
1623 %
1624 %    o threshold: Define the threshold value.
1625 %
1626 %    o exception: return any errors or warnings in this structure.
1627 %
1628 */
1629 MagickExport MagickBooleanType WhiteThresholdImage(Image *image,
1630   const char *thresholds,ExceptionInfo *exception)
1631 {
1632 #define ThresholdImageTag  "Threshold/Image"
1633
1634   CacheView
1635     *image_view;
1636
1637   GeometryInfo
1638     geometry_info;
1639
1640   MagickBooleanType
1641     status;
1642
1643   MagickOffsetType
1644     progress;
1645
1646   PixelInfo
1647     threshold;
1648
1649   MagickStatusType
1650     flags;
1651
1652   ssize_t
1653     y;
1654
1655   assert(image != (Image *) NULL);
1656   assert(image->signature == MagickSignature);
1657   if (image->debug != MagickFalse)
1658     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1659   if (thresholds == (const char *) NULL)
1660     return(MagickTrue);
1661   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1662     return(MagickFalse);
1663   if (IsGrayColorspace(image->colorspace) != MagickFalse)
1664     (void) TransformImageColorspace(image,RGBColorspace,exception);
1665   GetPixelInfo(image,&threshold);
1666   flags=ParseGeometry(thresholds,&geometry_info);
1667   threshold.red=geometry_info.rho;
1668   threshold.green=geometry_info.rho;
1669   threshold.blue=geometry_info.rho;
1670   threshold.black=geometry_info.rho;
1671   threshold.alpha=100.0;
1672   if ((flags & SigmaValue) != 0)
1673     threshold.green=geometry_info.sigma;
1674   if ((flags & XiValue) != 0)
1675     threshold.blue=geometry_info.xi;
1676   if ((flags & PsiValue) != 0)
1677     threshold.alpha=geometry_info.psi;
1678   if (threshold.colorspace == CMYKColorspace)
1679     {
1680       if ((flags & PsiValue) != 0)
1681         threshold.black=geometry_info.psi;
1682       if ((flags & ChiValue) != 0)
1683         threshold.alpha=geometry_info.chi;
1684     }
1685   if ((flags & PercentValue) != 0)
1686     {
1687       threshold.red*=(QuantumRange/100.0);
1688       threshold.green*=(QuantumRange/100.0);
1689       threshold.blue*=(QuantumRange/100.0);
1690       threshold.black*=(QuantumRange/100.0);
1691       threshold.alpha*=(QuantumRange/100.0);
1692     }
1693   /*
1694     White threshold image.
1695   */
1696   status=MagickTrue;
1697   progress=0;
1698   image_view=AcquireAuthenticCacheView(image,exception);
1699 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1700   #pragma omp parallel for schedule(static,4) shared(progress,status) \
1701     dynamic_number_threads(image,image->columns,image->rows,1)
1702 #endif
1703   for (y=0; y < (ssize_t) image->rows; y++)
1704   {
1705     register ssize_t
1706       x;
1707
1708     register Quantum
1709       *restrict q;
1710
1711     if (status == MagickFalse)
1712       continue;
1713     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1714     if (q == (Quantum *) NULL)
1715       {
1716         status=MagickFalse;
1717         continue;
1718       }
1719     for (x=0; x < (ssize_t) image->columns; x++)
1720     {
1721       double
1722         pixel;
1723
1724       register ssize_t
1725         i;
1726
1727       if (GetPixelMask(image,q) != 0)
1728         {
1729           q+=GetPixelChannels(image);
1730           continue;
1731         }
1732       pixel=GetPixelIntensity(image,q);
1733       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1734       {
1735         PixelChannel
1736           channel;
1737
1738         PixelTrait
1739           traits;
1740
1741         channel=GetPixelChannelMapChannel(image,i);
1742         traits=GetPixelChannelMapTraits(image,channel);
1743         if ((traits & UpdatePixelTrait) == 0)
1744           continue;
1745         if (image->channel_mask != DefaultChannels)
1746           pixel=(double) q[i];
1747         if (pixel > GetPixelInfoChannel(&threshold,channel))
1748           q[i]=QuantumRange;
1749       }
1750       q+=GetPixelChannels(image);
1751     }
1752     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1753       status=MagickFalse;
1754     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1755       {
1756         MagickBooleanType
1757           proceed;
1758
1759 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1760         #pragma omp critical (MagickCore_WhiteThresholdImage)
1761 #endif
1762         proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1763           image->rows);
1764         if (proceed == MagickFalse)
1765           status=MagickFalse;
1766       }
1767   }
1768   image_view=DestroyCacheView(image_view);
1769   return(status);
1770 }