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