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