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