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