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