]> granicus.if.org Git - imagemagick/blob - MagickCore/threshold.c
c13e476ca0ffa2f42ec676d8ad49d8c41effaad2
[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-2015 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 & UpdatePixelTrait) == 0)
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       break;
889     option=(const StringInfo *) GetNextValueInLinkedList(options);
890   }
891   options=DestroyConfigureOptions(options);
892   return(map);
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     {
969       thresholds=DestroyXMLTree(thresholds);
970       return(map);
971     }
972   description=GetXMLTreeChild(threshold,"description");
973   if (description == (XMLTreeInfo *) NULL)
974     {
975       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
976         "XmlMissingElement", "<description>, map \"%s\"",map_id);
977       thresholds=DestroyXMLTree(thresholds);
978       return(map);
979     }
980   levels=GetXMLTreeChild(threshold,"levels");
981   if (levels == (XMLTreeInfo *) NULL)
982     {
983       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
984         "XmlMissingElement", "<levels>, map \"%s\"", map_id);
985       thresholds=DestroyXMLTree(thresholds);
986       return(map);
987     }
988   map=(ThresholdMap *) AcquireMagickMemory(sizeof(ThresholdMap));
989   if (map == (ThresholdMap *) NULL)
990     ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
991   map->map_id=(char *) NULL;
992   map->description=(char *) NULL;
993   map->levels=(ssize_t *) NULL;
994   attribute=GetXMLTreeAttribute(threshold,"map");
995   if (attribute != (char *) NULL)
996     map->map_id=ConstantString(attribute);
997   content=GetXMLTreeContent(description);
998   if (content != (char *) NULL)
999     map->description=ConstantString(content);
1000   attribute=GetXMLTreeAttribute(levels,"width");
1001   if (attribute == (char *) NULL)
1002     {
1003       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1004         "XmlMissingAttribute", "<levels width>, map \"%s\"",map_id);
1005       thresholds=DestroyXMLTree(thresholds);
1006       map=DestroyThresholdMap(map);
1007       return(map);
1008     }
1009   map->width=StringToUnsignedLong(attribute);
1010   if (map->width == 0)
1011     {
1012       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1013        "XmlInvalidAttribute", "<levels width>, map \"%s\"",map_id);
1014       thresholds=DestroyXMLTree(thresholds);
1015       map=DestroyThresholdMap(map);
1016       return(map);
1017     }
1018   attribute=GetXMLTreeAttribute(levels,"height");
1019   if (attribute == (char *) NULL)
1020     {
1021       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1022         "XmlMissingAttribute", "<levels height>, map \"%s\"",map_id);
1023       thresholds=DestroyXMLTree(thresholds);
1024       map=DestroyThresholdMap(map);
1025       return(map);
1026     }
1027   map->height=StringToUnsignedLong(attribute);
1028   if (map->height == 0)
1029     {
1030       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1031         "XmlInvalidAttribute", "<levels height>, map \"%s\"",map_id);
1032       thresholds=DestroyXMLTree(thresholds);
1033       map=DestroyThresholdMap(map);
1034       return(map);
1035     }
1036   attribute=GetXMLTreeAttribute(levels,"divisor");
1037   if (attribute == (char *) NULL)
1038     {
1039       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1040         "XmlMissingAttribute", "<levels divisor>, map \"%s\"",map_id);
1041       thresholds=DestroyXMLTree(thresholds);
1042       map=DestroyThresholdMap(map);
1043       return(map);
1044     }
1045   map->divisor=(ssize_t) StringToLong(attribute);
1046   if (map->divisor < 2)
1047     {
1048       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1049         "XmlInvalidAttribute", "<levels divisor>, map \"%s\"",map_id);
1050       thresholds=DestroyXMLTree(thresholds);
1051       map=DestroyThresholdMap(map);
1052       return(map);
1053     }
1054   content=GetXMLTreeContent(levels);
1055   if (content == (char *) NULL)
1056     {
1057       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1058         "XmlMissingContent", "<levels>, map \"%s\"",map_id);
1059       thresholds=DestroyXMLTree(thresholds);
1060       map=DestroyThresholdMap(map);
1061       return(map);
1062     }
1063   map->levels=(ssize_t *) AcquireQuantumMemory((size_t) map->width,map->height*
1064     sizeof(*map->levels));
1065   if (map->levels == (ssize_t *) NULL)
1066     ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
1067   for (i=0; i < (ssize_t) (map->width*map->height); i++)
1068   {
1069     map->levels[i]=(ssize_t) strtol(content,&p,10);
1070     if (p == content)
1071       {
1072         (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1073           "XmlInvalidContent", "<level> too few values, map \"%s\"",map_id);
1074         thresholds=DestroyXMLTree(thresholds);
1075         map=DestroyThresholdMap(map);
1076         return(map);
1077       }
1078     if ((map->levels[i] < 0) || (map->levels[i] > map->divisor))
1079       {
1080         (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1081           "XmlInvalidContent", "<level> %.20g out of range, map \"%s\"",
1082           (double) map->levels[i],map_id);
1083         thresholds=DestroyXMLTree(thresholds);
1084         map=DestroyThresholdMap(map);
1085         return(map);
1086       }
1087     content=p;
1088   }
1089   value=(double) strtol(content,&p,10);
1090   (void) value;
1091   if (p != content)
1092     {
1093       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1094         "XmlInvalidContent", "<level> too many values, map \"%s\"",map_id);
1095      thresholds=DestroyXMLTree(thresholds);
1096      map=DestroyThresholdMap(map);
1097      return(map);
1098    }
1099   thresholds=DestroyXMLTree(thresholds);
1100   return(map);
1101 }
1102 \f
1103 /*
1104 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1105 %                                                                             %
1106 %                                                                             %
1107 %                                                                             %
1108 +  L i s t T h r e s h o l d M a p F i l e                                    %
1109 %                                                                             %
1110 %                                                                             %
1111 %                                                                             %
1112 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1113 %
1114 %  ListThresholdMapFile() lists the threshold maps and their descriptions
1115 %  in the given XML file data.
1116 %
1117 %  The format of the ListThresholdMaps method is:
1118 %
1119 %      MagickBooleanType ListThresholdMaps(FILE *file,const char*xml,
1120 %         const char *filename,ExceptionInfo *exception)
1121 %
1122 %  A description of each parameter follows.
1123 %
1124 %    o file:  An pointer to the output FILE.
1125 %
1126 %    o xml:  The threshold map list in XML format.
1127 %
1128 %    o filename:  The threshold map XML filename.
1129 %
1130 %    o exception: return any errors or warnings in this structure.
1131 %
1132 */
1133 MagickBooleanType ListThresholdMapFile(FILE *file,const char *xml,
1134   const char *filename,ExceptionInfo *exception)
1135 {
1136   const char
1137     *alias,
1138     *content,
1139     *map;
1140
1141   XMLTreeInfo
1142     *description,
1143     *threshold,
1144     *thresholds;
1145
1146   assert( xml != (char *)NULL );
1147   assert( file != (FILE *)NULL );
1148   (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1149     "Loading threshold map file \"%s\" ...",filename);
1150   thresholds=NewXMLTree(xml,exception);
1151   if ( thresholds == (XMLTreeInfo *)NULL )
1152     return(MagickFalse);
1153   (void) FormatLocaleFile(file,"%-16s %-12s %s\n","Map","Alias","Description");
1154   (void) FormatLocaleFile(file,
1155     "----------------------------------------------------\n");
1156   threshold=GetXMLTreeChild(thresholds,"threshold");
1157   for ( ; threshold != (XMLTreeInfo *) NULL;
1158           threshold=GetNextXMLTreeTag(threshold))
1159   {
1160     map=GetXMLTreeAttribute(threshold,"map");
1161     if (map == (char *) NULL)
1162       {
1163         (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1164           "XmlMissingAttribute", "<map>");
1165         thresholds=DestroyXMLTree(thresholds);
1166         return(MagickFalse);
1167       }
1168     alias=GetXMLTreeAttribute(threshold,"alias");
1169     description=GetXMLTreeChild(threshold,"description");
1170     if (description == (XMLTreeInfo *) NULL)
1171       {
1172         (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1173           "XmlMissingElement", "<description>, map \"%s\"",map);
1174         thresholds=DestroyXMLTree(thresholds);
1175         return(MagickFalse);
1176       }
1177     content=GetXMLTreeContent(description);
1178     if (content == (char *) NULL)
1179       {
1180         (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1181           "XmlMissingContent", "<description>, map \"%s\"", map);
1182         thresholds=DestroyXMLTree(thresholds);
1183         return(MagickFalse);
1184       }
1185     (void) FormatLocaleFile(file,"%-16s %-12s %s\n",map,alias ? alias : "",
1186       content);
1187   }
1188   thresholds=DestroyXMLTree(thresholds);
1189   return(MagickTrue);
1190 }
1191 \f
1192 /*
1193 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1194 %                                                                             %
1195 %                                                                             %
1196 %                                                                             %
1197 %  L i s t T h r e s h o l d M a p s                                          %
1198 %                                                                             %
1199 %                                                                             %
1200 %                                                                             %
1201 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1202 %
1203 %  ListThresholdMaps() lists the threshold maps and their descriptions
1204 %  as defined by "threshold.xml" to a file.
1205 %
1206 %  The format of the ListThresholdMaps method is:
1207 %
1208 %      MagickBooleanType ListThresholdMaps(FILE *file,ExceptionInfo *exception)
1209 %
1210 %  A description of each parameter follows.
1211 %
1212 %    o file:  An pointer to the output FILE.
1213 %
1214 %    o exception: return any errors or warnings in this structure.
1215 %
1216 */
1217 MagickExport MagickBooleanType ListThresholdMaps(FILE *file,
1218   ExceptionInfo *exception)
1219 {
1220   const StringInfo
1221     *option;
1222
1223   LinkedListInfo
1224     *options;
1225
1226   MagickStatusType
1227     status;
1228
1229   status=MagickTrue;
1230   if (file == (FILE *) NULL)
1231     file=stdout;
1232   options=GetConfigureOptions(ThresholdsFilename,exception);
1233   (void) FormatLocaleFile(file,
1234     "\n   Threshold Maps for Ordered Dither Operations\n");
1235   option=(const StringInfo *) GetNextValueInLinkedList(options);
1236   while (option != (const StringInfo *) NULL)
1237   {
1238     (void) FormatLocaleFile(file,"\nPath: %s\n\n",GetStringInfoPath(option));
1239     status&=ListThresholdMapFile(file,(const char *) GetStringInfoDatum(option),
1240       GetStringInfoPath(option),exception);
1241     option=(const StringInfo *) GetNextValueInLinkedList(options);
1242   }
1243   options=DestroyConfigureOptions(options);
1244   return(status != 0 ? MagickTrue : MagickFalse);
1245 }
1246 \f
1247 /*
1248 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1249 %                                                                             %
1250 %                                                                             %
1251 %                                                                             %
1252 %     O r d e r e d P o s t e r i z e I m a g e                               %
1253 %                                                                             %
1254 %                                                                             %
1255 %                                                                             %
1256 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1257 %
1258 %  OrderedPosterizeImage() will perform a ordered dither based on a number
1259 %  of pre-defined dithering threshold maps, but over multiple intensity
1260 %  levels, which can be different for different channels, according to the
1261 %  input argument.
1262 %
1263 %  The format of the OrderedPosterizeImage method is:
1264 %
1265 %      MagickBooleanType OrderedPosterizeImage(Image *image,
1266 %        const char *threshold_map,ExceptionInfo *exception)
1267 %
1268 %  A description of each parameter follows:
1269 %
1270 %    o image: the image.
1271 %
1272 %    o threshold_map: A string containing the name of the threshold dither
1273 %      map to use, followed by zero or more numbers representing the number
1274 %      of color levels tho dither between.
1275 %
1276 %      Any level number less than 2 will be equivalent to 2, and means only
1277 %      binary dithering will be applied to each color channel.
1278 %
1279 %      No numbers also means a 2 level (bitmap) dither will be applied to all
1280 %      channels, while a single number is the number of levels applied to each
1281 %      channel in sequence.  More numbers will be applied in turn to each of
1282 %      the color channels.
1283 %
1284 %      For example: "o3x3,6" will generate a 6 level posterization of the
1285 %      image with a ordered 3x3 diffused pixel dither being applied between
1286 %      each level. While checker,8,8,4 will produce a 332 colormaped image
1287 %      with only a single checkerboard hash pattern (50% grey) between each
1288 %      color level, to basically double the number of color levels with
1289 %      a bare minimim of dithering.
1290 %
1291 %    o exception: return any errors or warnings in this structure.
1292 %
1293 */
1294 MagickExport MagickBooleanType OrderedPosterizeImage(Image *image,
1295   const char *threshold_map,ExceptionInfo *exception)
1296 {
1297 #define DitherImageTag  "Dither/Image"
1298
1299   CacheView
1300     *image_view;
1301
1302   char
1303     token[MaxTextExtent];
1304
1305   const char
1306     *p;
1307
1308   double
1309     levels[CompositePixelChannel];
1310
1311   MagickBooleanType
1312     status;
1313
1314   MagickOffsetType
1315     progress;
1316
1317   register ssize_t
1318     i;
1319
1320   ssize_t
1321     y;
1322
1323   ThresholdMap
1324     *map;
1325
1326   assert(image != (Image *) NULL);
1327   assert(image->signature == MagickSignature);
1328   if (image->debug != MagickFalse)
1329     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1330   assert(exception != (ExceptionInfo *) NULL);
1331   assert(exception->signature == MagickSignature);
1332   if (threshold_map == (const char *) NULL)
1333     return(MagickTrue);
1334   p=(char *) threshold_map;
1335   while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
1336          (*p != '\0'))
1337     p++;
1338   threshold_map=p;
1339   while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
1340          (*p != '\0'))
1341   {
1342     if ((p-threshold_map) >= (MaxTextExtent-1))
1343       break;
1344     token[p-threshold_map]=(*p);
1345     p++;
1346   }
1347   token[p-threshold_map]='\0';
1348   map=GetThresholdMap(token,exception);
1349   if (map == (ThresholdMap *) NULL)
1350     {
1351       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1352         "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
1353       return(MagickFalse);
1354     }
1355   for (i=0; i < MaxPixelChannels; i++)
1356     levels[i]=2.0;
1357   p=strchr((char *) threshold_map,',');
1358   if ((p != (char *) NULL) && (isdigit((int) ((unsigned char) *(++p))) != 0))
1359     for (i=0; (*p != '\0') && (i < MaxPixelChannels); i++)
1360     {
1361       GetMagickToken(p,&p,token);
1362       if (*token == ',')
1363         GetMagickToken(p,&p,token);
1364       levels[i]=StringToDouble(token,(char **) NULL);
1365     }
1366   for (i=0; i < MaxPixelChannels; i++)
1367     if (fabs(levels[i]) >= 1)
1368       levels[i]-=1.0;
1369   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1370     return(MagickFalse);
1371   status=MagickTrue;
1372   progress=0;
1373   image_view=AcquireAuthenticCacheView(image,exception);
1374 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1375   #pragma omp parallel for schedule(static,4) shared(progress,status) \
1376     magick_threads(image,image,image->rows,1)
1377 #endif
1378   for (y=0; y < (ssize_t) image->rows; y++)
1379   {
1380     register ssize_t
1381       x;
1382
1383     register Quantum
1384       *restrict q;
1385
1386     if (status == MagickFalse)
1387       continue;
1388     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1389     if (q == (Quantum *) NULL)
1390       {
1391         status=MagickFalse;
1392         continue;
1393       }
1394     for (x=0; x < (ssize_t) image->columns; x++)
1395     {
1396       register ssize_t
1397         i;
1398
1399       ssize_t
1400         n;
1401
1402       n=0;
1403       if (GetPixelReadMask(image,q) == 0)
1404         {
1405           q+=GetPixelChannels(image);
1406           continue;
1407         }
1408       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1409       {
1410         ssize_t
1411           level,
1412           threshold;
1413
1414         PixelChannel channel=GetPixelChannelChannel(image,i);
1415         PixelTrait traits=GetPixelChannelTraits(image,channel);
1416         if ((traits & UpdatePixelTrait) == 0)
1417           continue;
1418         if (fabs(levels[n++]) < MagickEpsilon)
1419           continue;
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_OrderedPosterizeImage)
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 == MagickSignature);
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         *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       *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 (GetPixelReadMask(image,q) == 0)
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 thresholds: a geometry string containing low,high thresholds.  If the
1626 %      string contains 2x2, 3x3, or 4x4, an ordered dither of order 2, 3, or 4
1627 %      is performed instead.
1628 %
1629 %    o exception: return any errors or warnings in this structure.
1630 %
1631 */
1632 MagickExport MagickBooleanType RandomThresholdImage(Image *image,
1633   const char *thresholds,ExceptionInfo *exception)
1634 {
1635 #define ThresholdImageTag  "Threshold/Image"
1636
1637   CacheView
1638     *image_view;
1639
1640   double
1641     min_threshold,
1642     max_threshold;
1643
1644   GeometryInfo
1645     geometry_info;
1646
1647   MagickStatusType
1648     flags;
1649
1650   MagickBooleanType
1651     status;
1652
1653   MagickOffsetType
1654     progress;
1655
1656   PixelInfo
1657     threshold;
1658
1659   RandomInfo
1660     **restrict random_info;
1661
1662   ssize_t
1663     y;
1664
1665 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1666   unsigned long
1667     key;
1668 #endif
1669
1670   assert(image != (Image *) NULL);
1671   assert(image->signature == MagickSignature);
1672   if (image->debug != MagickFalse)
1673     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1674   assert(exception != (ExceptionInfo *) NULL);
1675   assert(exception->signature == MagickSignature);
1676   if (thresholds == (const char *) NULL)
1677     return(MagickTrue);
1678   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1679     return(MagickFalse);
1680   GetPixelInfo(image,&threshold);
1681   min_threshold=0.0;
1682   max_threshold=(double) QuantumRange;
1683   flags=ParseGeometry(thresholds,&geometry_info);
1684   min_threshold=geometry_info.rho;
1685   max_threshold=geometry_info.sigma;
1686   if ((flags & SigmaValue) == 0)
1687     max_threshold=min_threshold;
1688   if (strchr(thresholds,'%') != (char *) NULL)
1689     {
1690       max_threshold*=(double) (0.01*QuantumRange);
1691       min_threshold*=(double) (0.01*QuantumRange);
1692     }
1693   /*
1694     Random threshold image.
1695   */
1696   status=MagickTrue;
1697   progress=0;
1698   random_info=AcquireRandomInfoThreadSet();
1699 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1700   key=GetRandomSecretKey(random_info[0]);
1701 #endif
1702   image_view=AcquireAuthenticCacheView(image,exception);
1703 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1704   #pragma omp parallel for schedule(static,4) shared(progress,status) \
1705     magick_threads(image,image,image->rows,key == ~0UL)
1706 #endif
1707   for (y=0; y < (ssize_t) image->rows; y++)
1708   {
1709     const int
1710       id = GetOpenMPThreadId();
1711
1712     register Quantum
1713       *restrict q;
1714
1715     register ssize_t
1716       x;
1717
1718     if (status == MagickFalse)
1719       continue;
1720     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1721     if (q == (Quantum *) NULL)
1722       {
1723         status=MagickFalse;
1724         continue;
1725       }
1726     for (x=0; x < (ssize_t) image->columns; x++)
1727     {
1728       register ssize_t
1729         i;
1730
1731       if (GetPixelReadMask(image,q) == 0)
1732         {
1733           q+=GetPixelChannels(image);
1734           continue;
1735         }
1736       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1737       {
1738         double
1739           threshold;
1740
1741         PixelChannel channel=GetPixelChannelChannel(image,i);
1742         PixelTrait traits=GetPixelChannelTraits(image,channel);
1743         if ((traits & UpdatePixelTrait) == 0)
1744           continue;
1745         if ((double) q[i] < min_threshold)
1746           threshold=min_threshold;
1747         else
1748           if ((double) q[i] > max_threshold)
1749             threshold=max_threshold;
1750           else
1751             threshold=(double) (QuantumRange*
1752               GetPseudoRandomValue(random_info[id]));
1753         q[i]=(double) q[i] <= threshold ? 0 : QuantumRange;
1754       }
1755       q+=GetPixelChannels(image);
1756     }
1757     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1758       status=MagickFalse;
1759     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1760       {
1761         MagickBooleanType
1762           proceed;
1763
1764 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1765         #pragma omp critical (MagickCore_RandomThresholdImage)
1766 #endif
1767         proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1768           image->rows);
1769         if (proceed == MagickFalse)
1770           status=MagickFalse;
1771       }
1772   }
1773   image_view=DestroyCacheView(image_view);
1774   random_info=DestroyRandomInfoThreadSet(random_info);
1775   return(status);
1776 }
1777 \f
1778 /*
1779 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1780 %                                                                             %
1781 %                                                                             %
1782 %                                                                             %
1783 %     W h i t e T h r e s h o l d I m a g e                                   %
1784 %                                                                             %
1785 %                                                                             %
1786 %                                                                             %
1787 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1788 %
1789 %  WhiteThresholdImage() is like ThresholdImage() but forces all pixels above
1790 %  the threshold into white while leaving all pixels at or below the threshold
1791 %  unchanged.
1792 %
1793 %  The format of the WhiteThresholdImage method is:
1794 %
1795 %      MagickBooleanType WhiteThresholdImage(Image *image,
1796 %        const char *threshold,ExceptionInfo *exception)
1797 %
1798 %  A description of each parameter follows:
1799 %
1800 %    o image: the image.
1801 %
1802 %    o threshold: Define the threshold value.
1803 %
1804 %    o exception: return any errors or warnings in this structure.
1805 %
1806 */
1807 MagickExport MagickBooleanType WhiteThresholdImage(Image *image,
1808   const char *thresholds,ExceptionInfo *exception)
1809 {
1810 #define ThresholdImageTag  "Threshold/Image"
1811
1812   CacheView
1813     *image_view;
1814
1815   GeometryInfo
1816     geometry_info;
1817
1818   MagickBooleanType
1819     status;
1820
1821   MagickOffsetType
1822     progress;
1823
1824   PixelInfo
1825     threshold;
1826
1827   MagickStatusType
1828     flags;
1829
1830   ssize_t
1831     y;
1832
1833   assert(image != (Image *) NULL);
1834   assert(image->signature == MagickSignature);
1835   if (image->debug != MagickFalse)
1836     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1837   if (thresholds == (const char *) NULL)
1838     return(MagickTrue);
1839   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1840     return(MagickFalse);
1841   if (IsGrayColorspace(image->colorspace) != MagickFalse)
1842     (void) TransformImageColorspace(image,sRGBColorspace,exception);
1843   GetPixelInfo(image,&threshold);
1844   flags=ParseGeometry(thresholds,&geometry_info);
1845   threshold.red=geometry_info.rho;
1846   threshold.green=geometry_info.rho;
1847   threshold.blue=geometry_info.rho;
1848   threshold.black=geometry_info.rho;
1849   threshold.alpha=100.0;
1850   if ((flags & SigmaValue) != 0)
1851     threshold.green=geometry_info.sigma;
1852   if ((flags & XiValue) != 0)
1853     threshold.blue=geometry_info.xi;
1854   if ((flags & PsiValue) != 0)
1855     threshold.alpha=geometry_info.psi;
1856   if (threshold.colorspace == CMYKColorspace)
1857     {
1858       if ((flags & PsiValue) != 0)
1859         threshold.black=geometry_info.psi;
1860       if ((flags & ChiValue) != 0)
1861         threshold.alpha=geometry_info.chi;
1862     }
1863   if ((flags & PercentValue) != 0)
1864     {
1865       threshold.red*=(MagickRealType) (QuantumRange/100.0);
1866       threshold.green*=(MagickRealType) (QuantumRange/100.0);
1867       threshold.blue*=(MagickRealType) (QuantumRange/100.0);
1868       threshold.black*=(MagickRealType) (QuantumRange/100.0);
1869       threshold.alpha*=(MagickRealType) (QuantumRange/100.0);
1870     }
1871   /*
1872     White threshold image.
1873   */
1874   status=MagickTrue;
1875   progress=0;
1876   image_view=AcquireAuthenticCacheView(image,exception);
1877 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1878   #pragma omp parallel for schedule(static,4) shared(progress,status) \
1879     magick_threads(image,image,image->rows,1)
1880 #endif
1881   for (y=0; y < (ssize_t) image->rows; y++)
1882   {
1883     register ssize_t
1884       x;
1885
1886     register Quantum
1887       *restrict q;
1888
1889     if (status == MagickFalse)
1890       continue;
1891     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1892     if (q == (Quantum *) NULL)
1893       {
1894         status=MagickFalse;
1895         continue;
1896       }
1897     for (x=0; x < (ssize_t) image->columns; x++)
1898     {
1899       double
1900         pixel;
1901
1902       register ssize_t
1903         i;
1904
1905       if (GetPixelReadMask(image,q) == 0)
1906         {
1907           q+=GetPixelChannels(image);
1908           continue;
1909         }
1910       pixel=GetPixelIntensity(image,q);
1911       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1912       {
1913         PixelChannel channel=GetPixelChannelChannel(image,i);
1914         PixelTrait traits=GetPixelChannelTraits(image,channel);
1915         if ((traits & UpdatePixelTrait) == 0)
1916           continue;
1917         if (image->channel_mask != DefaultChannels)
1918           pixel=(double) q[i];
1919         if (pixel > GetPixelInfoChannel(&threshold,channel))
1920           q[i]=QuantumRange;
1921       }
1922       q+=GetPixelChannels(image);
1923     }
1924     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1925       status=MagickFalse;
1926     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1927       {
1928         MagickBooleanType
1929           proceed;
1930
1931 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1932         #pragma omp critical (MagickCore_WhiteThresholdImage)
1933 #endif
1934         proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1935           image->rows);
1936         if (proceed == MagickFalse)
1937           status=MagickFalse;
1938       }
1939   }
1940   image_view=DestroyCacheView(image_view);
1941   return(status);
1942 }