blob: 95875c67d9d26dce47aacb674403f77433ede8e5 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% TTTTT H H RRRR EEEEE SSSSS H H OOO L DDDD %
7% T H H R R E SS H H O O L D D %
8% T HHHHH RRRR EEE SSS HHHHH O O L D D %
9% T H H R R E SS H H O O L D D %
10% T H H R R EEEEE SSSSS H H OOO LLLLL DDDD %
11% %
12% %
13% MagickCore Image Threshold Methods %
14% %
15% Software Design %
16% John Cristy %
17% October 1996 %
18% %
19% %
cristy7e41fe82010-12-04 23:12:08 +000020% Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% 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*/
cristy4c08aed2011-07-01 19:47:50 +000043#include "MagickCore/studio.h"
44#include "MagickCore/property.h"
45#include "MagickCore/blob.h"
46#include "MagickCore/cache-view.h"
47#include "MagickCore/color.h"
48#include "MagickCore/color-private.h"
49#include "MagickCore/colormap.h"
50#include "MagickCore/colorspace.h"
51#include "MagickCore/configure.h"
52#include "MagickCore/constitute.h"
53#include "MagickCore/decorate.h"
54#include "MagickCore/draw.h"
55#include "MagickCore/enhance.h"
56#include "MagickCore/exception.h"
57#include "MagickCore/exception-private.h"
58#include "MagickCore/effect.h"
59#include "MagickCore/fx.h"
60#include "MagickCore/gem.h"
61#include "MagickCore/geometry.h"
62#include "MagickCore/image-private.h"
63#include "MagickCore/list.h"
64#include "MagickCore/log.h"
65#include "MagickCore/memory_.h"
66#include "MagickCore/monitor.h"
67#include "MagickCore/monitor-private.h"
68#include "MagickCore/montage.h"
69#include "MagickCore/option.h"
70#include "MagickCore/pixel-accessor.h"
71#include "MagickCore/quantize.h"
72#include "MagickCore/quantum.h"
73#include "MagickCore/random_.h"
74#include "MagickCore/random-private.h"
75#include "MagickCore/resize.h"
76#include "MagickCore/resource_.h"
77#include "MagickCore/segment.h"
78#include "MagickCore/shear.h"
79#include "MagickCore/signature-private.h"
80#include "MagickCore/string_.h"
81#include "MagickCore/string-private.h"
82#include "MagickCore/thread-private.h"
83#include "MagickCore/threshold.h"
84#include "MagickCore/transform.h"
85#include "MagickCore/xml-tree.h"
cristy3ed852e2009-09-05 21:47:34 +000086
87/*
88 Define declarations.
89*/
90#define ThresholdsFilename "thresholds.xml"
91
92/*
93 Typedef declarations.
94*/
95struct _ThresholdMap
96{
97 char
98 *map_id,
99 *description;
100
cristybb503372010-05-27 20:51:26 +0000101 size_t
cristy3ed852e2009-09-05 21:47:34 +0000102 width,
103 height;
104
cristybb503372010-05-27 20:51:26 +0000105 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000106 divisor,
107 *levels;
108};
109
110/*
111%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
112% %
113% %
114% %
115% A d a p t i v e T h r e s h o l d I m a g e %
116% %
117% %
118% %
119%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
120%
121% AdaptiveThresholdImage() selects an individual threshold for each pixel
122% based on the range of intensity values in its local neighborhood. This
123% allows for thresholding of an image whose global intensity histogram
124% doesn't contain distinctive peaks.
125%
126% The format of the AdaptiveThresholdImage method is:
127%
cristyde5cc632011-07-18 14:47:00 +0000128% Image *AdaptiveThresholdImage(const Image *image,const size_t width,
129% const size_t height,const double bias,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000130%
131% A description of each parameter follows:
132%
133% o image: the image.
134%
135% o width: the width of the local neighborhood.
136%
137% o height: the height of the local neighborhood.
138%
cristyde5cc632011-07-18 14:47:00 +0000139% o bias: the mean bias.
cristy3ed852e2009-09-05 21:47:34 +0000140%
141% o exception: return any errors or warnings in this structure.
142%
143*/
144MagickExport Image *AdaptiveThresholdImage(const Image *image,
cristyde5cc632011-07-18 14:47:00 +0000145 const size_t width,const size_t height,const double bias,
cristy3ed852e2009-09-05 21:47:34 +0000146 ExceptionInfo *exception)
147{
cristyde5cc632011-07-18 14:47:00 +0000148#define AdaptiveThresholdImageTag "AdaptiveThreshold/Image"
cristy3ed852e2009-09-05 21:47:34 +0000149
cristyc4c8d132010-01-07 01:58:38 +0000150 CacheView
151 *image_view,
152 *threshold_view;
153
cristy3ed852e2009-09-05 21:47:34 +0000154 Image
155 *threshold_image;
156
cristy3ed852e2009-09-05 21:47:34 +0000157 MagickBooleanType
158 status;
159
cristy5f959472010-05-27 22:19:46 +0000160 MagickOffsetType
161 progress;
162
cristyde5cc632011-07-18 14:47:00 +0000163 MagickSizeType
cristy3ed852e2009-09-05 21:47:34 +0000164 number_pixels;
165
cristy5f959472010-05-27 22:19:46 +0000166 ssize_t
167 y;
168
cristyde5cc632011-07-18 14:47:00 +0000169 /*
170 Initialize threshold image attributes.
171 */
172 assert(image != (Image *) NULL);
cristy3ed852e2009-09-05 21:47:34 +0000173 assert(image->signature == MagickSignature);
174 if (image->debug != MagickFalse)
175 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
176 assert(exception != (ExceptionInfo *) NULL);
177 assert(exception->signature == MagickSignature);
cristyde5cc632011-07-18 14:47:00 +0000178 if ((width % 2) == 0)
179 ThrowImageException(OptionError,"KernelWidthMustBeAnOddNumber");
180 threshold_image=CloneImage(image,image->columns,image->rows,MagickTrue,
181 exception);
cristy3ed852e2009-09-05 21:47:34 +0000182 if (threshold_image == (Image *) NULL)
183 return((Image *) NULL);
184 if (SetImageStorageClass(threshold_image,DirectClass) == MagickFalse)
185 {
186 InheritException(exception,&threshold_image->exception);
187 threshold_image=DestroyImage(threshold_image);
188 return((Image *) NULL);
189 }
190 /*
cristyde5cc632011-07-18 14:47:00 +0000191 Threshold image.
cristy3ed852e2009-09-05 21:47:34 +0000192 */
193 status=MagickTrue;
194 progress=0;
cristyde5cc632011-07-18 14:47:00 +0000195 number_pixels=(MagickSizeType) width*height;
cristy3ed852e2009-09-05 21:47:34 +0000196 image_view=AcquireCacheView(image);
197 threshold_view=AcquireCacheView(threshold_image);
cristyb5d5f722009-11-04 03:03:49 +0000198#if defined(MAGICKCORE_OPENMP_SUPPORT)
199 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000200#endif
cristybb503372010-05-27 20:51:26 +0000201 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000202 {
cristy4c08aed2011-07-01 19:47:50 +0000203 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000204 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000205
cristy4c08aed2011-07-01 19:47:50 +0000206 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000207 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000208
cristyde5cc632011-07-18 14:47:00 +0000209 register ssize_t
210 x;
211
212 size_t
213 channels,
214 threshold_channels;
215
216 ssize_t
217 center;
218
cristy3ed852e2009-09-05 21:47:34 +0000219 if (status == MagickFalse)
220 continue;
cristyd99b0962010-05-29 23:14:26 +0000221 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
cristyde5cc632011-07-18 14:47:00 +0000222 (height/2L),image->columns+width,height,exception);
223 q=QueueCacheViewAuthenticPixels(threshold_view,0,y,threshold_image->columns,
224 1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000225 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000226 {
227 status=MagickFalse;
228 continue;
229 }
cristyde5cc632011-07-18 14:47:00 +0000230 channels=GetPixelChannels(image);
231 threshold_channels=GetPixelChannels(threshold_image);
232 center=channels*(image->columns+width)*(height/2L)+channels*(width/2);
cristybb503372010-05-27 20:51:26 +0000233 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000234 {
cristybb503372010-05-27 20:51:26 +0000235 register ssize_t
cristyde5cc632011-07-18 14:47:00 +0000236 i;
cristy3ed852e2009-09-05 21:47:34 +0000237
cristyde5cc632011-07-18 14:47:00 +0000238 for (i=0; i < (ssize_t) channels; i++)
cristy3ed852e2009-09-05 21:47:34 +0000239 {
cristyde5cc632011-07-18 14:47:00 +0000240 MagickRealType
241 mean,
242 pixel;
243
244 PixelChannel
245 channel;
246
247 PixelTrait
248 threshold_traits,
249 traits;
250
251 register const Quantum
252 *restrict pixels;
253
254 register ssize_t
255 u;
256
257 ssize_t
258 v;
259
cristy30301712011-07-18 15:06:51 +0000260 traits=GetPixelChannelMapTraits(image,(PixelChannel) i);
cristyde5cc632011-07-18 14:47:00 +0000261 if (traits == UndefinedPixelTrait)
262 continue;
cristy30301712011-07-18 15:06:51 +0000263 channel=GetPixelChannelMapChannel(image,(PixelChannel) i);
cristyde5cc632011-07-18 14:47:00 +0000264 threshold_traits=GetPixelChannelMapTraits(threshold_image,channel);
265 if (threshold_traits == UndefinedPixelTrait)
266 continue;
267 if ((threshold_traits & CopyPixelTrait) != 0)
268 {
cristy3472b3e2011-07-20 21:48:52 +0000269 q[channel]=p[center+i];
cristyde5cc632011-07-18 14:47:00 +0000270 continue;
271 }
272 pixels=p;
273 pixel=0.0;
274 for (v=0; v < (ssize_t) height; v++)
cristy3ed852e2009-09-05 21:47:34 +0000275 {
cristyde5cc632011-07-18 14:47:00 +0000276 for (u=0; u < (ssize_t) width; u++)
277 {
278 pixel+=pixels[i];
279 pixels+=channels;
280 }
281 pixels+=image->columns*channels;
cristy3ed852e2009-09-05 21:47:34 +0000282 }
cristyde5cc632011-07-18 14:47:00 +0000283 mean=pixel/number_pixels+bias;
cristyf7dc44c2011-07-20 14:41:15 +0000284 q[channel]=(Quantum) (((MagickRealType) p[center+i] <= mean) ? 0 :
285 QuantumRange);
cristy3ed852e2009-09-05 21:47:34 +0000286 }
cristyde5cc632011-07-18 14:47:00 +0000287 p+=channels;
288 q+=threshold_channels;
cristy3ed852e2009-09-05 21:47:34 +0000289 }
cristyde5cc632011-07-18 14:47:00 +0000290 if (SyncCacheViewAuthenticPixels(threshold_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000291 status=MagickFalse;
292 if (image->progress_monitor != (MagickProgressMonitor) NULL)
293 {
294 MagickBooleanType
295 proceed;
296
cristyb5d5f722009-11-04 03:03:49 +0000297#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000298 #pragma omp critical (MagickCore_AdaptiveThresholdImage)
299#endif
cristyde5cc632011-07-18 14:47:00 +0000300 proceed=SetImageProgress(image,AdaptiveThresholdImageTag,progress++,
cristy3ed852e2009-09-05 21:47:34 +0000301 image->rows);
302 if (proceed == MagickFalse)
303 status=MagickFalse;
304 }
305 }
cristyde5cc632011-07-18 14:47:00 +0000306 threshold_image->type=image->type;
cristy3ed852e2009-09-05 21:47:34 +0000307 threshold_view=DestroyCacheView(threshold_view);
308 image_view=DestroyCacheView(image_view);
309 if (status == MagickFalse)
310 threshold_image=DestroyImage(threshold_image);
311 return(threshold_image);
312}
313
314/*
315%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
316% %
317% %
318% %
319% B i l e v e l I m a g e %
320% %
321% %
322% %
323%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
324%
325% BilevelImage() changes the value of individual pixels based on the
326% intensity of each pixel channel. The result is a high-contrast image.
327%
328% More precisely each channel value of the image is 'thresholded' so that if
329% it is equal to or less than the given value it is set to zero, while any
330% value greater than that give is set to it maximum or QuantumRange.
331%
332% This function is what is used to implement the "-threshold" operator for
333% the command line API.
334%
335% If the default channel setting is given the image is thresholded using just
336% the gray 'intensity' of the image, rather than the individual channels.
337%
cristyf4ad9df2011-07-08 16:49:03 +0000338% The format of the BilevelImage method is:
cristy3ed852e2009-09-05 21:47:34 +0000339%
340% MagickBooleanType BilevelImage(Image *image,const double threshold)
cristy3ed852e2009-09-05 21:47:34 +0000341%
342% A description of each parameter follows:
343%
344% o image: the image.
345%
cristy3ed852e2009-09-05 21:47:34 +0000346% o threshold: define the threshold values.
347%
cristyf89cb1d2011-07-07 01:24:37 +0000348% Aside: You can get the same results as operator using LevelImages()
cristy3ed852e2009-09-05 21:47:34 +0000349% with the 'threshold' value for both the black_point and the white_point.
350%
351*/
cristyf4ad9df2011-07-08 16:49:03 +0000352MagickExport MagickBooleanType BilevelImage(Image *image,
353 const double threshold)
cristy3ed852e2009-09-05 21:47:34 +0000354{
355#define ThresholdImageTag "Threshold/Image"
356
cristyc4c8d132010-01-07 01:58:38 +0000357 CacheView
358 *image_view;
359
cristy3ed852e2009-09-05 21:47:34 +0000360 ExceptionInfo
361 *exception;
362
cristy3ed852e2009-09-05 21:47:34 +0000363 MagickBooleanType
364 status;
365
cristy5f959472010-05-27 22:19:46 +0000366 MagickOffsetType
367 progress;
368
369 ssize_t
370 y;
371
cristy3ed852e2009-09-05 21:47:34 +0000372 assert(image != (Image *) NULL);
373 assert(image->signature == MagickSignature);
374 if (image->debug != MagickFalse)
375 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
376 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
377 return(MagickFalse);
378 /*
379 Bilevel threshold image.
380 */
381 status=MagickTrue;
382 progress=0;
383 exception=(&image->exception);
384 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +0000385#if defined(MAGICKCORE_OPENMP_SUPPORT)
386 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000387#endif
cristybb503372010-05-27 20:51:26 +0000388 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000389 {
cristybb503372010-05-27 20:51:26 +0000390 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000391 x;
392
cristy4c08aed2011-07-01 19:47:50 +0000393 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000394 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000395
396 if (status == MagickFalse)
397 continue;
398 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000399 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000400 {
401 status=MagickFalse;
402 continue;
403 }
cristyf4ad9df2011-07-08 16:49:03 +0000404 if (image->sync != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000405 {
cristybb503372010-05-27 20:51:26 +0000406 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000407 {
cristy4c08aed2011-07-01 19:47:50 +0000408 SetPixelRed(image,(Quantum) ((MagickRealType)
409 GetPixelIntensity(image,q) <= threshold ? 0 : QuantumRange),q);
410 SetPixelGreen(image,GetPixelRed(image,q),q);
411 SetPixelBlue(image,GetPixelRed(image,q),q);
cristyed231572011-07-14 02:18:59 +0000412 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000413 }
414 }
415 else
cristybb503372010-05-27 20:51:26 +0000416 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000417 {
cristyed231572011-07-14 02:18:59 +0000418 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000419 SetPixelRed(image,(Quantum) ((MagickRealType)
420 GetPixelRed(image,q) <= threshold ? 0 : QuantumRange),q);
cristyed231572011-07-14 02:18:59 +0000421 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000422 SetPixelGreen(image,(Quantum) ((MagickRealType)
423 GetPixelGreen(image,q) <= threshold ? 0 : QuantumRange),q);
cristyed231572011-07-14 02:18:59 +0000424 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000425 SetPixelBlue(image,(Quantum) ((MagickRealType)
426 GetPixelBlue(image,q) <= threshold ? 0 : QuantumRange),q);
cristyed231572011-07-14 02:18:59 +0000427 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +0000428 (image->colorspace == CMYKColorspace))
429 SetPixelBlack(image,(Quantum) ((MagickRealType)
430 GetPixelBlack(image,q) <= threshold ? 0 : QuantumRange),q);
cristyed231572011-07-14 02:18:59 +0000431 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +0000432 {
433 if (image->matte == MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +0000434 SetPixelAlpha(image,(Quantum) ((MagickRealType)
435 GetPixelAlpha(image,q) <= threshold ? 0 : QuantumRange),q);
cristy3ed852e2009-09-05 21:47:34 +0000436 else
cristy4c08aed2011-07-01 19:47:50 +0000437 SetPixelAlpha(image,(Quantum) ((MagickRealType)
438 GetPixelAlpha(image,q) >= threshold ? OpaqueAlpha :
439 TransparentAlpha),q);
cristy3ed852e2009-09-05 21:47:34 +0000440 }
cristyed231572011-07-14 02:18:59 +0000441 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000442 }
443 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
444 status=MagickFalse;
445 if (image->progress_monitor != (MagickProgressMonitor) NULL)
446 {
447 MagickBooleanType
448 proceed;
449
cristyb5d5f722009-11-04 03:03:49 +0000450#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +0000451 #pragma omp critical (MagickCore_BilevelImage)
cristy3ed852e2009-09-05 21:47:34 +0000452#endif
453 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
454 image->rows);
455 if (proceed == MagickFalse)
456 status=MagickFalse;
457 }
458 }
459 image_view=DestroyCacheView(image_view);
460 return(status);
461}
462
463/*
464%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
465% %
466% %
467% %
468% B l a c k T h r e s h o l d I m a g e %
469% %
470% %
471% %
472%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
473%
474% BlackThresholdImage() is like ThresholdImage() but forces all pixels below
cristy4e101302009-09-17 12:49:12 +0000475% the threshold into black while leaving all pixels at or above the threshold
cristy3ed852e2009-09-05 21:47:34 +0000476% unchanged.
477%
478% The format of the BlackThresholdImage method is:
479%
cristyf4ad9df2011-07-08 16:49:03 +0000480% MagickBooleanType BlackThresholdImage(Image *image,
481% const char *threshold,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000482%
483% A description of each parameter follows:
484%
485% o image: the image.
486%
cristy3ed852e2009-09-05 21:47:34 +0000487% o threshold: Define the threshold value.
488%
489% o exception: return any errors or warnings in this structure.
490%
491*/
492MagickExport MagickBooleanType BlackThresholdImage(Image *image,
cristyf4ad9df2011-07-08 16:49:03 +0000493 const char *thresholds,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000494{
495#define ThresholdImageTag "Threshold/Image"
496
cristyc4c8d132010-01-07 01:58:38 +0000497 CacheView
498 *image_view;
499
cristy3ed852e2009-09-05 21:47:34 +0000500 GeometryInfo
501 geometry_info;
502
cristy3ed852e2009-09-05 21:47:34 +0000503 MagickBooleanType
504 status;
505
cristy5f959472010-05-27 22:19:46 +0000506 MagickOffsetType
507 progress;
508
cristy4c08aed2011-07-01 19:47:50 +0000509 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000510 threshold;
511
512 MagickStatusType
513 flags;
514
cristy5f959472010-05-27 22:19:46 +0000515 ssize_t
516 y;
517
cristy3ed852e2009-09-05 21:47:34 +0000518 assert(image != (Image *) NULL);
519 assert(image->signature == MagickSignature);
520 if (image->debug != MagickFalse)
521 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
522 if (thresholds == (const char *) NULL)
523 return(MagickTrue);
524 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
525 return(MagickFalse);
cristy4c08aed2011-07-01 19:47:50 +0000526 GetPixelInfo(image,&threshold);
cristy3ed852e2009-09-05 21:47:34 +0000527 flags=ParseGeometry(thresholds,&geometry_info);
528 threshold.red=geometry_info.rho;
529 threshold.green=geometry_info.sigma;
530 if ((flags & SigmaValue) == 0)
531 threshold.green=threshold.red;
532 threshold.blue=geometry_info.xi;
533 if ((flags & XiValue) == 0)
534 threshold.blue=threshold.red;
cristy4c08aed2011-07-01 19:47:50 +0000535 threshold.alpha=geometry_info.psi;
cristy3ed852e2009-09-05 21:47:34 +0000536 if ((flags & PsiValue) == 0)
cristy4c08aed2011-07-01 19:47:50 +0000537 threshold.alpha=threshold.red;
538 threshold.black=geometry_info.chi;
cristy3ed852e2009-09-05 21:47:34 +0000539 if ((flags & ChiValue) == 0)
cristy4c08aed2011-07-01 19:47:50 +0000540 threshold.black=threshold.red;
cristy3ed852e2009-09-05 21:47:34 +0000541 if ((flags & PercentValue) != 0)
542 {
543 threshold.red*=(QuantumRange/100.0);
544 threshold.green*=(QuantumRange/100.0);
545 threshold.blue*=(QuantumRange/100.0);
cristy4c08aed2011-07-01 19:47:50 +0000546 threshold.alpha*=(QuantumRange/100.0);
547 threshold.black*=(QuantumRange/100.0);
cristy3ed852e2009-09-05 21:47:34 +0000548 }
549 /*
550 Black threshold image.
551 */
552 status=MagickTrue;
553 progress=0;
554 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +0000555#if defined(MAGICKCORE_OPENMP_SUPPORT)
556 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000557#endif
cristybb503372010-05-27 20:51:26 +0000558 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000559 {
cristybb503372010-05-27 20:51:26 +0000560 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000561 x;
562
cristy4c08aed2011-07-01 19:47:50 +0000563 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000564 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000565
566 if (status == MagickFalse)
567 continue;
568 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000569 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000570 {
571 status=MagickFalse;
572 continue;
573 }
cristybb503372010-05-27 20:51:26 +0000574 for (x=0; x < (ssize_t) image->columns; x++)
cristyb0ea1af2009-11-28 20:44:46 +0000575 {
cristyf4ad9df2011-07-08 16:49:03 +0000576 if (image->sync != MagickFalse)
cristyb0ea1af2009-11-28 20:44:46 +0000577 {
cristy4c08aed2011-07-01 19:47:50 +0000578 if (GetPixelIntensity(image,q) < GetPixelInfoIntensity(&threshold))
cristyb0ea1af2009-11-28 20:44:46 +0000579 {
cristy4c08aed2011-07-01 19:47:50 +0000580 SetPixelRed(image,0,q);
581 SetPixelGreen(image,0,q);
582 SetPixelBlue(image,0,q);
cristyb0ea1af2009-11-28 20:44:46 +0000583 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +0000584 SetPixelBlack(image,0,q);
cristyb0ea1af2009-11-28 20:44:46 +0000585 }
586 }
587 else
588 {
cristyed231572011-07-14 02:18:59 +0000589 if (((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +0000590 ((MagickRealType) GetPixelRed(image,q) < threshold.red))
591 SetPixelRed(image,0,q);
cristyed231572011-07-14 02:18:59 +0000592 if (((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +0000593 ((MagickRealType) GetPixelGreen(image,q) < threshold.green))
594 SetPixelGreen(image,0,q);
cristyed231572011-07-14 02:18:59 +0000595 if (((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +0000596 ((MagickRealType) GetPixelBlue(image,q) < threshold.blue))
597 SetPixelBlue(image,0,q);
cristyed231572011-07-14 02:18:59 +0000598 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristyb0ea1af2009-11-28 20:44:46 +0000599 (image->colorspace == CMYKColorspace) &&
cristy4c08aed2011-07-01 19:47:50 +0000600 ((MagickRealType) GetPixelBlack(image,q) < threshold.black))
601 SetPixelBlack(image,0,q);
cristyed231572011-07-14 02:18:59 +0000602 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +0000603 ((MagickRealType) GetPixelAlpha(image,q) < threshold.alpha))
604 SetPixelAlpha(image,0,q);
cristyb0ea1af2009-11-28 20:44:46 +0000605 }
cristyed231572011-07-14 02:18:59 +0000606 q+=GetPixelChannels(image);
cristyb0ea1af2009-11-28 20:44:46 +0000607 }
cristy3ed852e2009-09-05 21:47:34 +0000608 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
609 status=MagickFalse;
610 if (image->progress_monitor != (MagickProgressMonitor) NULL)
611 {
612 MagickBooleanType
613 proceed;
614
cristyb5d5f722009-11-04 03:03:49 +0000615#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +0000616 #pragma omp critical (MagickCore_BlackThresholdImage)
cristy3ed852e2009-09-05 21:47:34 +0000617#endif
618 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
619 image->rows);
620 if (proceed == MagickFalse)
621 status=MagickFalse;
622 }
623 }
624 image_view=DestroyCacheView(image_view);
625 return(status);
626}
627
628/*
629%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
630% %
631% %
632% %
cristy1eb45dd2009-09-25 16:38:06 +0000633% C l a m p I m a g e %
634% %
635% %
636% %
637%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
638%
cristyecb0c6d2009-09-25 16:50:09 +0000639% ClampImage() restricts the color range from 0 to the quantum depth.
cristy1eb45dd2009-09-25 16:38:06 +0000640%
cristyf4ad9df2011-07-08 16:49:03 +0000641% The format of the ClampImage method is:
cristy1eb45dd2009-09-25 16:38:06 +0000642%
643% MagickBooleanType ClampImage(Image *image)
cristy1eb45dd2009-09-25 16:38:06 +0000644%
645% A description of each parameter follows:
646%
647% o image: the image.
648%
cristy1eb45dd2009-09-25 16:38:06 +0000649*/
650
cristy75ffdb72010-01-07 17:40:12 +0000651static inline Quantum ClampToUnsignedQuantum(const Quantum quantum)
cristy1eb45dd2009-09-25 16:38:06 +0000652{
653#if defined(MAGICKCORE_HDRI_SUPPORT)
654 if (quantum <= 0)
655 return(0);
656 if (quantum >= QuantumRange)
657 return(QuantumRange);
658 return(quantum);
659#else
660 return(quantum);
661#endif
662}
663
664MagickExport MagickBooleanType ClampImage(Image *image)
665{
cristy1eb45dd2009-09-25 16:38:06 +0000666#define ClampImageTag "Clamp/Image"
667
cristyc4c8d132010-01-07 01:58:38 +0000668 CacheView
669 *image_view;
670
cristy1eb45dd2009-09-25 16:38:06 +0000671 ExceptionInfo
672 *exception;
673
cristy1eb45dd2009-09-25 16:38:06 +0000674 MagickBooleanType
675 status;
676
cristy5f959472010-05-27 22:19:46 +0000677 MagickOffsetType
678 progress;
679
680 ssize_t
681 y;
682
cristy1eb45dd2009-09-25 16:38:06 +0000683 assert(image != (Image *) NULL);
684 assert(image->signature == MagickSignature);
685 if (image->debug != MagickFalse)
686 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
687 if (image->storage_class == PseudoClass)
688 {
cristybb503372010-05-27 20:51:26 +0000689 register ssize_t
cristy1eb45dd2009-09-25 16:38:06 +0000690 i;
691
692 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000693 *restrict q;
cristy1eb45dd2009-09-25 16:38:06 +0000694
695 q=image->colormap;
cristybb503372010-05-27 20:51:26 +0000696 for (i=0; i < (ssize_t) image->colors; i++)
cristy1eb45dd2009-09-25 16:38:06 +0000697 {
cristy4c08aed2011-07-01 19:47:50 +0000698 q->red=ClampToUnsignedQuantum(q->red);
699 q->green=ClampToUnsignedQuantum(q->green);
700 q->blue=ClampToUnsignedQuantum(q->blue);
701 q->alpha=ClampToUnsignedQuantum(q->alpha);
cristy1eb45dd2009-09-25 16:38:06 +0000702 q++;
703 }
704 return(SyncImage(image));
705 }
706 /*
cristy611721d2009-09-25 16:42:17 +0000707 Clamp image.
cristy1eb45dd2009-09-25 16:38:06 +0000708 */
709 status=MagickTrue;
710 progress=0;
711 exception=(&image->exception);
712 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +0000713#if defined(MAGICKCORE_OPENMP_SUPPORT)
714 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy1eb45dd2009-09-25 16:38:06 +0000715#endif
cristybb503372010-05-27 20:51:26 +0000716 for (y=0; y < (ssize_t) image->rows; y++)
cristy1eb45dd2009-09-25 16:38:06 +0000717 {
cristybb503372010-05-27 20:51:26 +0000718 register ssize_t
cristy1eb45dd2009-09-25 16:38:06 +0000719 x;
720
cristy4c08aed2011-07-01 19:47:50 +0000721 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000722 *restrict q;
cristy1eb45dd2009-09-25 16:38:06 +0000723
724 if (status == MagickFalse)
725 continue;
726 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000727 if (q == (const Quantum *) NULL)
cristy1eb45dd2009-09-25 16:38:06 +0000728 {
729 status=MagickFalse;
730 continue;
731 }
cristybb503372010-05-27 20:51:26 +0000732 for (x=0; x < (ssize_t) image->columns; x++)
cristy1eb45dd2009-09-25 16:38:06 +0000733 {
cristyed231572011-07-14 02:18:59 +0000734 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000735 SetPixelRed(image,ClampToUnsignedQuantum(GetPixelRed(image,q)),q);
cristyed231572011-07-14 02:18:59 +0000736 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000737 SetPixelGreen(image,ClampToUnsignedQuantum(GetPixelGreen(image,q)),q);
cristyed231572011-07-14 02:18:59 +0000738 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000739 SetPixelBlue(image,ClampToUnsignedQuantum(GetPixelBlue(image,q)),q);
cristyed231572011-07-14 02:18:59 +0000740 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy1eb45dd2009-09-25 16:38:06 +0000741 (image->colorspace == CMYKColorspace))
cristy4c08aed2011-07-01 19:47:50 +0000742 SetPixelBlack(image,ClampToUnsignedQuantum(GetPixelBlack(image,q)),q);
cristyed231572011-07-14 02:18:59 +0000743 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000744 SetPixelAlpha(image,ClampToUnsignedQuantum(GetPixelAlpha(image,q)),q);
cristyed231572011-07-14 02:18:59 +0000745 q+=GetPixelChannels(image);
cristy1eb45dd2009-09-25 16:38:06 +0000746 }
747 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
748 status=MagickFalse;
749 if (image->progress_monitor != (MagickProgressMonitor) NULL)
750 {
751 MagickBooleanType
752 proceed;
753
cristyb5d5f722009-11-04 03:03:49 +0000754#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +0000755 #pragma omp critical (MagickCore_ClampImage)
cristy1eb45dd2009-09-25 16:38:06 +0000756#endif
757 proceed=SetImageProgress(image,ClampImageTag,progress++,
758 image->rows);
759 if (proceed == MagickFalse)
760 status=MagickFalse;
761 }
762 }
763 image_view=DestroyCacheView(image_view);
764 return(status);
765}
766
767/*
768%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
769% %
770% %
771% %
cristy3ed852e2009-09-05 21:47:34 +0000772% D e s t r o y T h r e s h o l d M a p %
773% %
774% %
775% %
776%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
777%
778% DestroyThresholdMap() de-allocate the given ThresholdMap
779%
780% The format of the ListThresholdMaps method is:
781%
782% ThresholdMap *DestroyThresholdMap(Threshold *map)
783%
784% A description of each parameter follows.
785%
786% o map: Pointer to the Threshold map to destroy
787%
788*/
789MagickExport ThresholdMap *DestroyThresholdMap(ThresholdMap *map)
790{
791 assert(map != (ThresholdMap *) NULL);
792 if (map->map_id != (char *) NULL)
793 map->map_id=DestroyString(map->map_id);
794 if (map->description != (char *) NULL)
795 map->description=DestroyString(map->description);
cristybb503372010-05-27 20:51:26 +0000796 if (map->levels != (ssize_t *) NULL)
797 map->levels=(ssize_t *) RelinquishMagickMemory(map->levels);
cristy3ed852e2009-09-05 21:47:34 +0000798 map=(ThresholdMap *) RelinquishMagickMemory(map);
799 return(map);
800}
801
802/*
803%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
804% %
805% %
806% %
807+ G e t T h r e s h o l d M a p F i l e %
808% %
809% %
810% %
811%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
812%
813% GetThresholdMapFile() look for a given threshold map name or alias in the
814% given XML file data, and return the allocated the map when found.
815%
816% The format of the ListThresholdMaps method is:
817%
818% ThresholdMap *GetThresholdMap(const char *xml,const char *filename,
819% const char *map_id,ExceptionInfo *exception)
820%
821% A description of each parameter follows.
822%
823% o xml: The threshold map list in XML format.
824%
825% o filename: The threshold map XML filename.
826%
827% o map_id: ID of the map to look for in XML list.
828%
829% o exception: return any errors or warnings in this structure.
830%
831*/
832MagickExport ThresholdMap *GetThresholdMapFile(const char *xml,
833 const char *filename,const char *map_id,ExceptionInfo *exception)
834{
835 const char
836 *attr,
837 *content;
838
839 double
840 value;
841
cristy092b7f72010-11-05 15:54:37 +0000842 ThresholdMap
cristy3ed852e2009-09-05 21:47:34 +0000843 *map;
844
845 XMLTreeInfo
846 *description,
847 *levels,
848 *threshold,
849 *thresholds;
850
851 map = (ThresholdMap *)NULL;
852 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
853 "Loading threshold map file \"%s\" ...",filename);
854 thresholds=NewXMLTree(xml,exception);
855 if ( thresholds == (XMLTreeInfo *)NULL )
856 return(map);
857
858 for( threshold = GetXMLTreeChild(thresholds,"threshold");
859 threshold != (XMLTreeInfo *)NULL;
860 threshold = GetNextXMLTreeTag(threshold) ) {
861 attr = GetXMLTreeAttribute(threshold, "map");
862 if ( (attr != (char *)NULL) && (LocaleCompare(map_id,attr) == 0) )
863 break;
864 attr = GetXMLTreeAttribute(threshold, "alias");
865 if ( (attr != (char *)NULL) && (LocaleCompare(map_id,attr) == 0) )
866 break;
867 }
868 if ( threshold == (XMLTreeInfo *)NULL ) {
869 return(map);
870 }
871 description = GetXMLTreeChild(threshold,"description");
872 if ( description == (XMLTreeInfo *)NULL ) {
873 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
874 "XmlMissingElement", "<description>, map \"%s\"", map_id);
875 thresholds = DestroyXMLTree(thresholds);
876 return(map);
877 }
878 levels = GetXMLTreeChild(threshold,"levels");
879 if ( levels == (XMLTreeInfo *)NULL ) {
880 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
881 "XmlMissingElement", "<levels>, map \"%s\"", map_id);
882 thresholds = DestroyXMLTree(thresholds);
883 return(map);
884 }
885
886 /* The map has been found -- Allocate a Threshold Map to return */
cristy73bd4a52010-10-05 11:24:23 +0000887 map = (ThresholdMap *)AcquireMagickMemory(sizeof(ThresholdMap));
cristy3ed852e2009-09-05 21:47:34 +0000888 if ( map == (ThresholdMap *)NULL )
889 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
890 map->map_id = (char *)NULL;
891 map->description = (char *)NULL;
cristybb503372010-05-27 20:51:26 +0000892 map->levels = (ssize_t *) NULL;
cristy3ed852e2009-09-05 21:47:34 +0000893
894 /* Assign Basic Attributes */
895 attr = GetXMLTreeAttribute(threshold, "map");
896 if ( attr != (char *)NULL )
897 map->map_id = ConstantString(attr);
898
899 content = GetXMLTreeContent(description);
900 if ( content != (char *)NULL )
901 map->description = ConstantString(content);
902
903 attr = GetXMLTreeAttribute(levels, "width");
904 if ( attr == (char *)NULL ) {
905 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
906 "XmlMissingAttribute", "<levels width>, map \"%s\"", map_id);
907 thresholds = DestroyXMLTree(thresholds);
908 map = DestroyThresholdMap(map);
909 return(map);
910 }
cristye27293e2009-12-18 02:53:20 +0000911 map->width = StringToUnsignedLong(attr);
cristy3ed852e2009-09-05 21:47:34 +0000912 if ( map->width == 0 ) {
913 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
914 "XmlInvalidAttribute", "<levels width>, map \"%s\"", map_id);
915 thresholds = DestroyXMLTree(thresholds);
916 map = DestroyThresholdMap(map);
917 return(map);
918 }
919
920 attr = GetXMLTreeAttribute(levels, "height");
921 if ( attr == (char *)NULL ) {
922 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
923 "XmlMissingAttribute", "<levels height>, map \"%s\"", map_id);
924 thresholds = DestroyXMLTree(thresholds);
925 map = DestroyThresholdMap(map);
926 return(map);
927 }
cristye27293e2009-12-18 02:53:20 +0000928 map->height = StringToUnsignedLong(attr);
cristy3ed852e2009-09-05 21:47:34 +0000929 if ( map->height == 0 ) {
930 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
931 "XmlInvalidAttribute", "<levels height>, map \"%s\"", map_id);
932 thresholds = DestroyXMLTree(thresholds);
933 map = DestroyThresholdMap(map);
934 return(map);
935 }
936
937 attr = GetXMLTreeAttribute(levels, "divisor");
938 if ( attr == (char *)NULL ) {
939 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
940 "XmlMissingAttribute", "<levels divisor>, map \"%s\"", map_id);
941 thresholds = DestroyXMLTree(thresholds);
942 map = DestroyThresholdMap(map);
943 return(map);
944 }
cristy55a91cd2010-12-01 00:57:40 +0000945 map->divisor = (ssize_t) StringToLong(attr);
cristy3ed852e2009-09-05 21:47:34 +0000946 if ( map->divisor < 2 ) {
947 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
948 "XmlInvalidAttribute", "<levels divisor>, map \"%s\"", map_id);
949 thresholds = DestroyXMLTree(thresholds);
950 map = DestroyThresholdMap(map);
951 return(map);
952 }
953
954 /* Allocate theshold levels array */
955 content = GetXMLTreeContent(levels);
956 if ( content == (char *)NULL ) {
957 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
958 "XmlMissingContent", "<levels>, map \"%s\"", map_id);
959 thresholds = DestroyXMLTree(thresholds);
960 map = DestroyThresholdMap(map);
961 return(map);
962 }
cristybb503372010-05-27 20:51:26 +0000963 map->levels=(ssize_t *) AcquireQuantumMemory((size_t) map->width,map->height*
cristy3ed852e2009-09-05 21:47:34 +0000964 sizeof(*map->levels));
cristybb503372010-05-27 20:51:26 +0000965 if ( map->levels == (ssize_t *)NULL )
cristy3ed852e2009-09-05 21:47:34 +0000966 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
967 { /* parse levels into integer array */
cristycee97112010-05-28 00:44:52 +0000968 ssize_t i;
cristy3ed852e2009-09-05 21:47:34 +0000969 char *p;
cristybb503372010-05-27 20:51:26 +0000970 for( i=0; i< (ssize_t) (map->width*map->height); i++) {
cristycee97112010-05-28 00:44:52 +0000971 map->levels[i] = (ssize_t)strtol(content, &p, 10);
cristy3ed852e2009-09-05 21:47:34 +0000972 if ( p == content ) {
973 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
974 "XmlInvalidContent", "<level> too few values, map \"%s\"", map_id);
975 thresholds = DestroyXMLTree(thresholds);
976 map = DestroyThresholdMap(map);
977 return(map);
978 }
979 if ( map->levels[i] < 0 || map->levels[i] > map->divisor ) {
980 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
cristye8c25f92010-06-03 00:53:06 +0000981 "XmlInvalidContent", "<level> %.20g out of range, map \"%s\"",
982 (double) map->levels[i],map_id);
cristy3ed852e2009-09-05 21:47:34 +0000983 thresholds = DestroyXMLTree(thresholds);
984 map = DestroyThresholdMap(map);
985 return(map);
986 }
987 content = p;
988 }
989 value=(double) strtol(content,&p,10);
cristyda16f162011-02-19 23:52:17 +0000990 (void) value;
cristy3ed852e2009-09-05 21:47:34 +0000991 if (p != content)
992 {
993 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
994 "XmlInvalidContent", "<level> too many values, map \"%s\"", map_id);
995 thresholds=DestroyXMLTree(thresholds);
996 map=DestroyThresholdMap(map);
997 return(map);
998 }
999 }
1000
1001 thresholds = DestroyXMLTree(thresholds);
1002 return(map);
1003}
1004
1005/*
1006%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1007% %
1008% %
1009% %
1010% G e t T h r e s h o l d M a p %
1011% %
1012% %
1013% %
1014%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1015%
1016% GetThresholdMap() load and search one or more threshold map files for the
1017% a map matching the given name or aliase.
1018%
1019% The format of the GetThresholdMap method is:
1020%
1021% ThresholdMap *GetThresholdMap(const char *map_id,
1022% ExceptionInfo *exception)
1023%
1024% A description of each parameter follows.
1025%
1026% o map_id: ID of the map to look for.
1027%
1028% o exception: return any errors or warnings in this structure.
1029%
1030*/
1031MagickExport ThresholdMap *GetThresholdMap(const char *map_id,
1032 ExceptionInfo *exception)
1033{
1034 const StringInfo
1035 *option;
1036
1037 LinkedListInfo
1038 *options;
1039
1040 ThresholdMap
1041 *map;
1042
1043 map=(ThresholdMap *)NULL;
1044 options=GetConfigureOptions(ThresholdsFilename,exception);
1045 while (( option=(const StringInfo *) GetNextValueInLinkedList(options) )
1046 != (const StringInfo *) NULL && map == (ThresholdMap *)NULL )
1047 map=GetThresholdMapFile((const char *) GetStringInfoDatum(option),
1048 GetStringInfoPath(option),map_id,exception);
1049 options=DestroyConfigureOptions(options);
1050 return(map);
1051}
1052
1053/*
1054%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1055% %
1056% %
1057% %
1058+ L i s t T h r e s h o l d M a p F i l e %
1059% %
1060% %
1061% %
1062%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1063%
1064% ListThresholdMapFile() lists the threshold maps and their descriptions
1065% in the given XML file data.
1066%
1067% The format of the ListThresholdMaps method is:
1068%
1069% MagickBooleanType ListThresholdMaps(FILE *file,const char*xml,
1070% const char *filename,ExceptionInfo *exception)
1071%
1072% A description of each parameter follows.
1073%
1074% o file: An pointer to the output FILE.
1075%
1076% o xml: The threshold map list in XML format.
1077%
1078% o filename: The threshold map XML filename.
1079%
1080% o exception: return any errors or warnings in this structure.
1081%
1082*/
1083MagickBooleanType ListThresholdMapFile(FILE *file,const char *xml,
1084 const char *filename,ExceptionInfo *exception)
1085{
1086 XMLTreeInfo *thresholds,*threshold,*description;
1087 const char *map,*alias,*content;
1088
1089 assert( xml != (char *)NULL );
1090 assert( file != (FILE *)NULL );
1091
1092 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1093 "Loading threshold map file \"%s\" ...",filename);
1094 thresholds=NewXMLTree(xml,exception);
1095 if ( thresholds == (XMLTreeInfo *)NULL )
1096 return(MagickFalse);
1097
cristy1e604812011-05-19 18:07:50 +00001098 (void) FormatLocaleFile(file,"%-16s %-12s %s\n","Map","Alias","Description");
1099 (void) FormatLocaleFile(file,
1100 "----------------------------------------------------\n");
cristy3ed852e2009-09-05 21:47:34 +00001101
1102 for( threshold = GetXMLTreeChild(thresholds,"threshold");
1103 threshold != (XMLTreeInfo *)NULL;
1104 threshold = GetNextXMLTreeTag(threshold) )
1105 {
1106 map = GetXMLTreeAttribute(threshold, "map");
1107 if (map == (char *) NULL) {
1108 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1109 "XmlMissingAttribute", "<map>");
1110 thresholds=DestroyXMLTree(thresholds);
1111 return(MagickFalse);
1112 }
1113 alias = GetXMLTreeAttribute(threshold, "alias");
1114 /* alias is optional, no if test needed */
1115 description=GetXMLTreeChild(threshold,"description");
1116 if ( description == (XMLTreeInfo *)NULL ) {
1117 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1118 "XmlMissingElement", "<description>, map \"%s\"", map);
1119 thresholds=DestroyXMLTree(thresholds);
1120 return(MagickFalse);
1121 }
1122 content=GetXMLTreeContent(description);
1123 if ( content == (char *)NULL ) {
1124 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1125 "XmlMissingContent", "<description>, map \"%s\"", map);
1126 thresholds=DestroyXMLTree(thresholds);
1127 return(MagickFalse);
1128 }
cristy1e604812011-05-19 18:07:50 +00001129 (void) FormatLocaleFile(file,"%-16s %-12s %s\n",map,alias ? alias : "",
1130 content);
cristy3ed852e2009-09-05 21:47:34 +00001131 }
1132 thresholds=DestroyXMLTree(thresholds);
1133 return(MagickTrue);
1134}
1135
1136/*
1137%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1138% %
1139% %
1140% %
1141% L i s t T h r e s h o l d M a p s %
1142% %
1143% %
1144% %
1145%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1146%
1147% ListThresholdMaps() lists the threshold maps and their descriptions
1148% as defined by "threshold.xml" to a file.
1149%
1150% The format of the ListThresholdMaps method is:
1151%
1152% MagickBooleanType ListThresholdMaps(FILE *file,ExceptionInfo *exception)
1153%
1154% A description of each parameter follows.
1155%
1156% o file: An pointer to the output FILE.
1157%
1158% o exception: return any errors or warnings in this structure.
1159%
1160*/
1161MagickExport MagickBooleanType ListThresholdMaps(FILE *file,
1162 ExceptionInfo *exception)
1163{
1164 const StringInfo
1165 *option;
1166
1167 LinkedListInfo
1168 *options;
1169
1170 MagickStatusType
1171 status;
1172
1173 status=MagickFalse;
1174 if ( file == (FILE *)NULL )
1175 file = stdout;
1176 options=GetConfigureOptions(ThresholdsFilename,exception);
1177
cristy1e604812011-05-19 18:07:50 +00001178 (void) FormatLocaleFile(file,
1179 "\n Threshold Maps for Ordered Dither Operations\n");
cristy3ed852e2009-09-05 21:47:34 +00001180 while ( ( option=(const StringInfo *) GetNextValueInLinkedList(options) )
1181 != (const StringInfo *) NULL)
1182 {
cristyb51dff52011-05-19 16:55:47 +00001183 (void) FormatLocaleFile(file,"\nPATH: %s\n\n",GetStringInfoPath(option));
cristy3ed852e2009-09-05 21:47:34 +00001184 status|=ListThresholdMapFile(file,(const char *) GetStringInfoDatum(option),
1185 GetStringInfoPath(option),exception);
1186 }
1187 options=DestroyConfigureOptions(options);
1188 return(status != 0 ? MagickTrue : MagickFalse);
1189}
1190
1191/*
1192%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1193% %
1194% %
1195% %
cristy3ed852e2009-09-05 21:47:34 +00001196% O r d e r e d P o s t e r i z e I m a g e %
1197% %
1198% %
1199% %
1200%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1201%
1202% OrderedPosterizeImage() will perform a ordered dither based on a number
1203% of pre-defined dithering threshold maps, but over multiple intensity
1204% levels, which can be different for different channels, according to the
1205% input argument.
1206%
1207% The format of the OrderedPosterizeImage method is:
1208%
1209% MagickBooleanType OrderedPosterizeImage(Image *image,
1210% const char *threshold_map,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001211%
1212% A description of each parameter follows:
1213%
1214% o image: the image.
1215%
cristy3ed852e2009-09-05 21:47:34 +00001216% o threshold_map: A string containing the name of the threshold dither
1217% map to use, followed by zero or more numbers representing the number
1218% of color levels tho dither between.
1219%
cristyf998fb32011-04-27 23:00:47 +00001220% Any level number less than 2 will be equivalent to 2, and means only
cristy3ed852e2009-09-05 21:47:34 +00001221% binary dithering will be applied to each color channel.
1222%
1223% No numbers also means a 2 level (bitmap) dither will be applied to all
1224% channels, while a single number is the number of levels applied to each
1225% channel in sequence. More numbers will be applied in turn to each of
1226% the color channels.
1227%
1228% For example: "o3x3,6" will generate a 6 level posterization of the
1229% image with a ordered 3x3 diffused pixel dither being applied between
1230% each level. While checker,8,8,4 will produce a 332 colormaped image
1231% with only a single checkerboard hash pattern (50% grey) between each
1232% color level, to basically double the number of color levels with
1233% a bare minimim of dithering.
1234%
1235% o exception: return any errors or warnings in this structure.
1236%
1237*/
1238MagickExport MagickBooleanType OrderedPosterizeImage(Image *image,
1239 const char *threshold_map,ExceptionInfo *exception)
1240{
cristy3ed852e2009-09-05 21:47:34 +00001241#define DitherImageTag "Dither/Image"
1242
cristyc4c8d132010-01-07 01:58:38 +00001243 CacheView
1244 *image_view;
1245
cristy3ed852e2009-09-05 21:47:34 +00001246 LongPixelPacket
1247 levels;
1248
1249 MagickBooleanType
1250 status;
1251
cristy5f959472010-05-27 22:19:46 +00001252 MagickOffsetType
1253 progress;
1254
1255 ssize_t
1256 y;
1257
cristy3ed852e2009-09-05 21:47:34 +00001258 ThresholdMap
1259 *map;
1260
cristy3ed852e2009-09-05 21:47:34 +00001261 assert(image != (Image *) NULL);
1262 assert(image->signature == MagickSignature);
1263 if (image->debug != MagickFalse)
1264 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1265 assert(exception != (ExceptionInfo *) NULL);
1266 assert(exception->signature == MagickSignature);
1267 if (threshold_map == (const char *) NULL)
1268 return(MagickTrue);
1269 {
1270 char
1271 token[MaxTextExtent];
1272
1273 register const char
1274 *p;
1275
1276 p=(char *)threshold_map;
1277 while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
1278 (*p != '\0'))
1279 p++;
1280 threshold_map=p;
1281 while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
1282 (*p != '\0')) {
cristy37e0b382011-06-07 13:31:21 +00001283 if ((p-threshold_map) >= (MaxTextExtent-1))
cristy3ed852e2009-09-05 21:47:34 +00001284 break;
1285 token[p-threshold_map] = *p;
1286 p++;
1287 }
1288 token[p-threshold_map] = '\0';
1289 map = GetThresholdMap(token, exception);
1290 if ( map == (ThresholdMap *)NULL ) {
1291 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1292 "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
1293 return(MagickFalse);
1294 }
1295 }
glennrp1e7f7bc2011-03-02 19:25:28 +00001296 /* Set channel levels from extra comma separated arguments
cristy3ed852e2009-09-05 21:47:34 +00001297 Default to 2, the single value given, or individual channel values
1298 */
1299#if 1
glennrp1e7f7bc2011-03-02 19:25:28 +00001300 { /* parse directly as a comma separated list of integers */
cristy3ed852e2009-09-05 21:47:34 +00001301 char *p;
1302
1303 p = strchr((char *) threshold_map,',');
cristy13020672011-07-08 02:33:26 +00001304 levels.red=0;
1305 levels.green=0;
1306 levels.blue=0;
1307 levels.black=0;
1308 levels.alpha=0;
cristy3ed852e2009-09-05 21:47:34 +00001309 if ( p != (char *)NULL && isdigit((int) ((unsigned char) *(++p))) )
cristy4c08aed2011-07-01 19:47:50 +00001310 levels.black = (unsigned int) strtoul(p, &p, 10);
cristy3ed852e2009-09-05 21:47:34 +00001311 else
cristy4c08aed2011-07-01 19:47:50 +00001312 levels.black = 2;
cristy3ed852e2009-09-05 21:47:34 +00001313
cristyed231572011-07-14 02:18:59 +00001314 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy13020672011-07-08 02:33:26 +00001315 levels.red=levels.black;
cristyed231572011-07-14 02:18:59 +00001316 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy13020672011-07-08 02:33:26 +00001317 levels.green=levels.black;
cristyed231572011-07-14 02:18:59 +00001318 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy13020672011-07-08 02:33:26 +00001319 levels.blue=levels.black;
cristyed231572011-07-14 02:18:59 +00001320 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy13020672011-07-08 02:33:26 +00001321 (image->colorspace == CMYKColorspace))
1322 levels.black=levels.black;
cristyed231572011-07-14 02:18:59 +00001323 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
cristy13020672011-07-08 02:33:26 +00001324 (image->matte != MagickFalse))
1325 levels.alpha=levels.black;
cristy3ed852e2009-09-05 21:47:34 +00001326
1327 /* if more than a single number, each channel has a separate value */
1328 if ( p != (char *) NULL && *p == ',' ) {
1329 p=strchr((char *) threshold_map,',');
1330 p++;
cristyed231572011-07-14 02:18:59 +00001331 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristyecd0ab52010-05-30 14:59:20 +00001332 levels.red = (unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
cristyed231572011-07-14 02:18:59 +00001333 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristyecd0ab52010-05-30 14:59:20 +00001334 levels.green = (unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
cristyed231572011-07-14 02:18:59 +00001335 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristyecd0ab52010-05-30 14:59:20 +00001336 levels.blue = (unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
cristyed231572011-07-14 02:18:59 +00001337 if ((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0 &&
cristy4c08aed2011-07-01 19:47:50 +00001338 (image->colorspace == CMYKColorspace))
1339 levels.black=(unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
cristyed231572011-07-14 02:18:59 +00001340 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001341 levels.alpha = (unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
cristy3ed852e2009-09-05 21:47:34 +00001342 }
1343 }
1344#else
1345 /* Parse level values as a geometry */
1346 /* This difficult!
1347 * How to map GeometryInfo structure elements into
1348 * LongPixelPacket structure elements, but according to channel?
1349 * Note the channels list may skip elements!!!!
1350 * EG -channel BA -ordered-dither map,2,3
cristy4c08aed2011-07-01 19:47:50 +00001351 * will need to map g.rho -> l.blue, and g.sigma -> l.alpha
cristy3ed852e2009-09-05 21:47:34 +00001352 * A simpler way is needed, probably converting geometry to a temporary
cristybb503372010-05-27 20:51:26 +00001353 * array, then using channel to advance the index into ssize_t pixel packet.
cristy3ed852e2009-09-05 21:47:34 +00001354 */
1355#endif
1356
1357#if 0
cristye8c25f92010-06-03 00:53:06 +00001358printf("DEBUG levels r=%u g=%u b=%u a=%u i=%u\n",
cristy4c08aed2011-07-01 19:47:50 +00001359 levels.red, levels.green, levels.blue, levels.alpha, levels.index);
cristy3ed852e2009-09-05 21:47:34 +00001360#endif
1361
1362 { /* Do the posterized ordered dithering of the image */
cristycee97112010-05-28 00:44:52 +00001363 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001364 d;
1365
1366 /* d = number of psuedo-level divisions added between color levels */
1367 d = map->divisor-1;
1368
1369 /* reduce levels to levels - 1 */
1370 levels.red = levels.red ? levels.red-1 : 0;
1371 levels.green = levels.green ? levels.green-1 : 0;
1372 levels.blue = levels.blue ? levels.blue-1 : 0;
cristy4c08aed2011-07-01 19:47:50 +00001373 levels.black = levels.black ? levels.black-1 : 0;
1374 levels.alpha = levels.alpha ? levels.alpha-1 : 0;
cristy3ed852e2009-09-05 21:47:34 +00001375
1376 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1377 {
1378 InheritException(exception,&image->exception);
1379 return(MagickFalse);
1380 }
1381 status=MagickTrue;
1382 progress=0;
1383 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00001384#if defined(MAGICKCORE_OPENMP_SUPPORT)
1385 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001386#endif
cristybb503372010-05-27 20:51:26 +00001387 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001388 {
cristybb503372010-05-27 20:51:26 +00001389 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001390 x;
1391
cristy4c08aed2011-07-01 19:47:50 +00001392 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001393 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001394
1395 if (status == MagickFalse)
1396 continue;
1397 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001398 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001399 {
1400 status=MagickFalse;
1401 continue;
1402 }
cristybb503372010-05-27 20:51:26 +00001403 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001404 {
cristycee97112010-05-28 00:44:52 +00001405 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001406 threshold,
1407 t,
1408 l;
1409
1410 /*
1411 Figure out the dither threshold for this pixel
1412 This must be a integer from 1 to map->divisor-1
1413 */
1414 threshold = map->levels[(x%map->width) +map->width*(y%map->height)];
1415
1416 /* Dither each channel in the image as appropriate
1417 Notes on the integer Math...
1418 total number of divisions = (levels-1)*(divisor-1)+1)
1419 t1 = this colors psuedo_level =
1420 q->red * total_divisions / (QuantumRange+1)
1421 l = posterization level 0..levels
1422 t = dither threshold level 0..divisor-1 NB: 0 only on last
1423 Each color_level is of size QuantumRange / (levels-1)
1424 NB: All input levels and divisor are already had 1 subtracted
1425 Opacity is inverted so 'off' represents transparent.
1426 */
cristy4c08aed2011-07-01 19:47:50 +00001427 if (levels.red != 0) {
1428 t = (ssize_t) (QuantumScale*GetPixelRed(image,q)*(levels.red*d+1));
cristy3ed852e2009-09-05 21:47:34 +00001429 l = t/d; t = t-l*d;
cristy4c08aed2011-07-01 19:47:50 +00001430 SetPixelRed(image,RoundToQuantum((MagickRealType)
1431 ((l+(t >= threshold))*(MagickRealType) QuantumRange/levels.red)),q);
cristy3ed852e2009-09-05 21:47:34 +00001432 }
cristy4c08aed2011-07-01 19:47:50 +00001433 if (levels.green != 0) {
1434 t = (ssize_t) (QuantumScale*GetPixelGreen(image,q)*
cristyfba5a8b2011-05-03 17:12:12 +00001435 (levels.green*d+1));
cristy3ed852e2009-09-05 21:47:34 +00001436 l = t/d; t = t-l*d;
cristy4c08aed2011-07-01 19:47:50 +00001437 SetPixelGreen(image,RoundToQuantum((MagickRealType)
1438 ((l+(t >= threshold))*(MagickRealType) QuantumRange/levels.green)),q);
cristy3ed852e2009-09-05 21:47:34 +00001439 }
cristy4c08aed2011-07-01 19:47:50 +00001440 if (levels.blue != 0) {
1441 t = (ssize_t) (QuantumScale*GetPixelBlue(image,q)*
cristyfba5a8b2011-05-03 17:12:12 +00001442 (levels.blue*d+1));
cristy3ed852e2009-09-05 21:47:34 +00001443 l = t/d; t = t-l*d;
cristy4c08aed2011-07-01 19:47:50 +00001444 SetPixelBlue(image,RoundToQuantum((MagickRealType)
1445 ((l+(t >= threshold))*(MagickRealType) QuantumRange/levels.blue)),q);
cristy3ed852e2009-09-05 21:47:34 +00001446 }
cristy4c08aed2011-07-01 19:47:50 +00001447 if (levels.alpha != 0) {
1448 t = (ssize_t) ((1.0-QuantumScale*GetPixelAlpha(image,q))*
1449 (levels.alpha*d+1));
cristy3ed852e2009-09-05 21:47:34 +00001450 l = t/d; t = t-l*d;
cristy4c08aed2011-07-01 19:47:50 +00001451 SetPixelAlpha(image,RoundToQuantum((MagickRealType)
cristyfba5a8b2011-05-03 17:12:12 +00001452 ((1.0-l-(t >= threshold))*(MagickRealType) QuantumRange/
cristy4c08aed2011-07-01 19:47:50 +00001453 levels.alpha)),q);
cristy3ed852e2009-09-05 21:47:34 +00001454 }
cristy4c08aed2011-07-01 19:47:50 +00001455 if (levels.black != 0) {
1456 t = (ssize_t) (QuantumScale*GetPixelBlack(image,q)*
1457 (levels.black*d+1));
cristy3ed852e2009-09-05 21:47:34 +00001458 l = t/d; t = t-l*d;
cristy4c08aed2011-07-01 19:47:50 +00001459 SetPixelBlack(image,RoundToQuantum((MagickRealType)
1460 ((l+(t>=threshold))*(MagickRealType) QuantumRange/levels.black)),q);
cristy3ed852e2009-09-05 21:47:34 +00001461 }
cristyed231572011-07-14 02:18:59 +00001462 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001463 }
1464 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1465 status=MagickFalse;
1466 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1467 {
1468 MagickBooleanType
1469 proceed;
1470
cristyb5d5f722009-11-04 03:03:49 +00001471#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy13020672011-07-08 02:33:26 +00001472 #pragma omp critical (MagickCore_OrderedPosterizeImage)
cristy3ed852e2009-09-05 21:47:34 +00001473#endif
1474 proceed=SetImageProgress(image,DitherImageTag,progress++,image->rows);
1475 if (proceed == MagickFalse)
1476 status=MagickFalse;
1477 }
1478 }
1479 image_view=DestroyCacheView(image_view);
1480 }
1481 map=DestroyThresholdMap(map);
1482 return(MagickTrue);
1483}
1484
1485/*
1486%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1487% %
1488% %
1489% %
1490% R a n d o m T h r e s h o l d I m a g e %
1491% %
1492% %
1493% %
1494%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1495%
1496% RandomThresholdImage() changes the value of individual pixels based on the
1497% intensity of each pixel compared to a random threshold. The result is a
1498% low-contrast, two color image.
1499%
1500% The format of the RandomThresholdImage method is:
1501%
cristyf4ad9df2011-07-08 16:49:03 +00001502% MagickBooleanType RandomThresholdImage(Image *image,
cristy3ed852e2009-09-05 21:47:34 +00001503% const char *thresholds,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001504%
1505% A description of each parameter follows:
1506%
1507% o image: the image.
1508%
cristy3ed852e2009-09-05 21:47:34 +00001509% o thresholds: a geometry string containing low,high thresholds. If the
1510% string contains 2x2, 3x3, or 4x4, an ordered dither of order 2, 3, or 4
1511% is performed instead.
1512%
1513% o exception: return any errors or warnings in this structure.
1514%
1515*/
cristy3ed852e2009-09-05 21:47:34 +00001516MagickExport MagickBooleanType RandomThresholdImage(Image *image,
1517 const char *thresholds,ExceptionInfo *exception)
1518{
cristy3ed852e2009-09-05 21:47:34 +00001519#define ThresholdImageTag "Threshold/Image"
1520
cristyfa112112010-01-04 17:48:07 +00001521 CacheView
1522 *image_view;
1523
cristy3ed852e2009-09-05 21:47:34 +00001524 GeometryInfo
1525 geometry_info;
1526
1527 MagickStatusType
1528 flags;
1529
cristy3ed852e2009-09-05 21:47:34 +00001530 MagickBooleanType
1531 status;
1532
cristy5f959472010-05-27 22:19:46 +00001533 MagickOffsetType
1534 progress;
1535
cristy4c08aed2011-07-01 19:47:50 +00001536 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001537 threshold;
1538
1539 MagickRealType
1540 min_threshold,
1541 max_threshold;
1542
1543 RandomInfo
cristyfa112112010-01-04 17:48:07 +00001544 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +00001545
cristy5f959472010-05-27 22:19:46 +00001546 ssize_t
1547 y;
1548
cristy3ed852e2009-09-05 21:47:34 +00001549 assert(image != (Image *) NULL);
1550 assert(image->signature == MagickSignature);
1551 if (image->debug != MagickFalse)
1552 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1553 assert(exception != (ExceptionInfo *) NULL);
1554 assert(exception->signature == MagickSignature);
1555 if (thresholds == (const char *) NULL)
1556 return(MagickTrue);
cristy4c08aed2011-07-01 19:47:50 +00001557 GetPixelInfo(image,&threshold);
cristy3ed852e2009-09-05 21:47:34 +00001558 min_threshold=0.0;
1559 max_threshold=(MagickRealType) QuantumRange;
1560 flags=ParseGeometry(thresholds,&geometry_info);
1561 min_threshold=geometry_info.rho;
1562 max_threshold=geometry_info.sigma;
1563 if ((flags & SigmaValue) == 0)
1564 max_threshold=min_threshold;
1565 if (strchr(thresholds,'%') != (char *) NULL)
1566 {
1567 max_threshold*=(MagickRealType) (0.01*QuantumRange);
1568 min_threshold*=(MagickRealType) (0.01*QuantumRange);
1569 }
cristy3ed852e2009-09-05 21:47:34 +00001570 /*
1571 Random threshold image.
1572 */
1573 status=MagickTrue;
1574 progress=0;
cristyf4ad9df2011-07-08 16:49:03 +00001575 if (image->sync != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001576 {
1577 if (AcquireImageColormap(image,2) == MagickFalse)
1578 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1579 image->filename);
1580 random_info=AcquireRandomInfoThreadSet();
1581 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00001582#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy5c9e6f22010-09-17 17:31:01 +00001583 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001584#endif
cristybb503372010-05-27 20:51:26 +00001585 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001586 {
cristy5c9e6f22010-09-17 17:31:01 +00001587 const int
1588 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00001589
cristy3ed852e2009-09-05 21:47:34 +00001590 MagickBooleanType
1591 sync;
1592
cristybb503372010-05-27 20:51:26 +00001593 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001594 x;
1595
cristy4c08aed2011-07-01 19:47:50 +00001596 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001597 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001598
1599 if (status == MagickFalse)
1600 continue;
1601 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1602 exception);
cristy4c08aed2011-07-01 19:47:50 +00001603 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001604 {
1605 status=MagickFalse;
1606 continue;
1607 }
cristybb503372010-05-27 20:51:26 +00001608 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001609 {
cristy3ed852e2009-09-05 21:47:34 +00001610 MagickRealType
1611 intensity;
1612
cristy4c08aed2011-07-01 19:47:50 +00001613 Quantum
1614 index;
1615
1616 intensity=(MagickRealType) GetPixelIntensity(image,q);
cristy3ed852e2009-09-05 21:47:34 +00001617 if (intensity < min_threshold)
cristy4c08aed2011-07-01 19:47:50 +00001618 threshold.black=min_threshold;
cristy3ed852e2009-09-05 21:47:34 +00001619 else
cristy4c08aed2011-07-01 19:47:50 +00001620 if (intensity > max_threshold)
1621 threshold.black=max_threshold;
1622 else
1623 threshold.black=(MagickRealType)(QuantumRange*
1624 GetPseudoRandomValue(random_info[id]));
1625 index=(Quantum) (intensity <= threshold.black ? 0 : 1);
1626 SetPixelIndex(image,index,q);
1627 SetPixelPacket(image,image->colormap+(ssize_t) index,q);
cristyed231572011-07-14 02:18:59 +00001628 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001629 }
1630 sync=SyncCacheViewAuthenticPixels(image_view,exception);
1631 if (sync == MagickFalse)
1632 status=MagickFalse;
1633 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1634 {
1635 MagickBooleanType
1636 proceed;
1637
cristyb5d5f722009-11-04 03:03:49 +00001638#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00001639 #pragma omp critical (MagickCore_RandomThresholdImage)
cristy3ed852e2009-09-05 21:47:34 +00001640#endif
1641 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1642 image->rows);
1643 if (proceed == MagickFalse)
1644 status=MagickFalse;
1645 }
1646 }
1647 image_view=DestroyCacheView(image_view);
1648 random_info=DestroyRandomInfoThreadSet(random_info);
1649 return(status);
1650 }
1651 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1652 {
1653 InheritException(exception,&image->exception);
1654 return(MagickFalse);
1655 }
1656 random_info=AcquireRandomInfoThreadSet();
1657 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00001658#if defined(MAGICKCORE_OPENMP_SUPPORT)
1659 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001660#endif
cristybb503372010-05-27 20:51:26 +00001661 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001662 {
cristy5c9e6f22010-09-17 17:31:01 +00001663 const int
1664 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00001665
cristy4c08aed2011-07-01 19:47:50 +00001666 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001667 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001668
cristy5c9e6f22010-09-17 17:31:01 +00001669 register ssize_t
1670 x;
1671
cristy3ed852e2009-09-05 21:47:34 +00001672 if (status == MagickFalse)
1673 continue;
1674 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001675 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001676 {
1677 status=MagickFalse;
1678 continue;
1679 }
cristybb503372010-05-27 20:51:26 +00001680 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001681 {
cristyed231572011-07-14 02:18:59 +00001682 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +00001683 {
cristy4c08aed2011-07-01 19:47:50 +00001684 if ((MagickRealType) GetPixelRed(image,q) < min_threshold)
cristy3ed852e2009-09-05 21:47:34 +00001685 threshold.red=min_threshold;
1686 else
cristy4c08aed2011-07-01 19:47:50 +00001687 if ((MagickRealType) GetPixelRed(image,q) > max_threshold)
cristy3ed852e2009-09-05 21:47:34 +00001688 threshold.red=max_threshold;
1689 else
1690 threshold.red=(MagickRealType) (QuantumRange*
1691 GetPseudoRandomValue(random_info[id]));
1692 }
cristyed231572011-07-14 02:18:59 +00001693 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +00001694 {
cristy4c08aed2011-07-01 19:47:50 +00001695 if ((MagickRealType) GetPixelGreen(image,q) < min_threshold)
cristy3ed852e2009-09-05 21:47:34 +00001696 threshold.green=min_threshold;
1697 else
cristy4c08aed2011-07-01 19:47:50 +00001698 if ((MagickRealType) GetPixelGreen(image,q) > max_threshold)
cristy3ed852e2009-09-05 21:47:34 +00001699 threshold.green=max_threshold;
1700 else
1701 threshold.green=(MagickRealType) (QuantumRange*
1702 GetPseudoRandomValue(random_info[id]));
1703 }
cristyed231572011-07-14 02:18:59 +00001704 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +00001705 {
cristy4c08aed2011-07-01 19:47:50 +00001706 if ((MagickRealType) GetPixelBlue(image,q) < min_threshold)
cristy3ed852e2009-09-05 21:47:34 +00001707 threshold.blue=min_threshold;
1708 else
cristy4c08aed2011-07-01 19:47:50 +00001709 if ((MagickRealType) GetPixelBlue(image,q) > max_threshold)
cristy3ed852e2009-09-05 21:47:34 +00001710 threshold.blue=max_threshold;
1711 else
1712 threshold.blue=(MagickRealType) (QuantumRange*
1713 GetPseudoRandomValue(random_info[id]));
1714 }
cristyed231572011-07-14 02:18:59 +00001715 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00001716 (image->colorspace == CMYKColorspace))
1717 {
cristy4c08aed2011-07-01 19:47:50 +00001718 if ((MagickRealType) GetPixelBlack(image,q) < min_threshold)
1719 threshold.black=min_threshold;
cristy3ed852e2009-09-05 21:47:34 +00001720 else
cristy4c08aed2011-07-01 19:47:50 +00001721 if ((MagickRealType) GetPixelBlack(image,q) > max_threshold)
1722 threshold.black=max_threshold;
cristy3ed852e2009-09-05 21:47:34 +00001723 else
cristy4c08aed2011-07-01 19:47:50 +00001724 threshold.black=(MagickRealType) (QuantumRange*
1725 GetPseudoRandomValue(random_info[id]));
1726 }
cristyed231572011-07-14 02:18:59 +00001727 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001728 {
1729 if ((MagickRealType) GetPixelAlpha(image,q) < min_threshold)
1730 threshold.alpha=min_threshold;
1731 else
1732 if ((MagickRealType) GetPixelAlpha(image,q) > max_threshold)
1733 threshold.alpha=max_threshold;
1734 else
1735 threshold.alpha=(MagickRealType) (QuantumRange*
cristy3ed852e2009-09-05 21:47:34 +00001736 GetPseudoRandomValue(random_info[id]));
1737 }
cristyed231572011-07-14 02:18:59 +00001738 if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001739 SetPixelRed(image,(Quantum) ((MagickRealType)
1740 GetPixelRed(image,q) <= threshold.red ? 0 : QuantumRange),q);
cristyed231572011-07-14 02:18:59 +00001741 if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001742 SetPixelGreen(image,(Quantum) ((MagickRealType)
1743 GetPixelGreen(image,q) <= threshold.green ? 0 : QuantumRange),q);
cristyed231572011-07-14 02:18:59 +00001744 if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001745 SetPixelBlue(image,(Quantum) ((MagickRealType)
1746 GetPixelBlue(image,q) <= threshold.blue ? 0 : QuantumRange),q);
cristyed231572011-07-14 02:18:59 +00001747 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00001748 (image->colorspace == CMYKColorspace))
cristy4c08aed2011-07-01 19:47:50 +00001749 SetPixelBlack(image,(Quantum) ((MagickRealType)
1750 GetPixelBlack(image,q) <= threshold.black ? 0 : QuantumRange),q);
cristyed231572011-07-14 02:18:59 +00001751 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001752 SetPixelAlpha(image,(Quantum) ((MagickRealType)
1753 GetPixelAlpha(image,q) <= threshold.alpha ? 0 : QuantumRange),q);
cristyed231572011-07-14 02:18:59 +00001754 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001755 }
1756 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1757 status=MagickFalse;
1758 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1759 {
1760 MagickBooleanType
1761 proceed;
1762
cristyb5d5f722009-11-04 03:03:49 +00001763#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00001764 #pragma omp critical (MagickCore_RandomThresholdImage)
cristy3ed852e2009-09-05 21:47:34 +00001765#endif
1766 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1767 image->rows);
1768 if (proceed == MagickFalse)
1769 status=MagickFalse;
1770 }
1771 }
1772 image_view=DestroyCacheView(image_view);
1773 random_info=DestroyRandomInfoThreadSet(random_info);
1774 return(status);
1775}
1776
1777/*
1778%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1779% %
1780% %
1781% %
1782% W h i t e T h r e s h o l d I m a g e %
1783% %
1784% %
1785% %
1786%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1787%
1788% WhiteThresholdImage() is like ThresholdImage() but forces all pixels above
cristy4e101302009-09-17 12:49:12 +00001789% the threshold into white while leaving all pixels at or below the threshold
cristy3ed852e2009-09-05 21:47:34 +00001790% unchanged.
1791%
1792% The format of the WhiteThresholdImage method is:
1793%
cristyf4ad9df2011-07-08 16:49:03 +00001794% MagickBooleanType WhiteThresholdImage(Image *image,
1795% const char *threshold,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001796%
1797% A description of each parameter follows:
1798%
1799% o image: the image.
1800%
cristy3ed852e2009-09-05 21:47:34 +00001801% o threshold: Define the threshold value.
1802%
1803% o exception: return any errors or warnings in this structure.
1804%
1805*/
1806MagickExport MagickBooleanType WhiteThresholdImage(Image *image,
cristyf4ad9df2011-07-08 16:49:03 +00001807 const char *thresholds,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001808{
1809#define ThresholdImageTag "Threshold/Image"
1810
cristy5f959472010-05-27 22:19:46 +00001811 CacheView
1812 *image_view;
1813
cristy3ed852e2009-09-05 21:47:34 +00001814 GeometryInfo
1815 geometry_info;
1816
cristy3ed852e2009-09-05 21:47:34 +00001817 MagickBooleanType
1818 status;
1819
cristy4c08aed2011-07-01 19:47:50 +00001820 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001821 threshold;
1822
cristy5f959472010-05-27 22:19:46 +00001823 MagickOffsetType
1824 progress;
1825
cristy3ed852e2009-09-05 21:47:34 +00001826 MagickStatusType
1827 flags;
1828
cristy5f959472010-05-27 22:19:46 +00001829 ssize_t
1830 y;
cristy3ed852e2009-09-05 21:47:34 +00001831
1832 assert(image != (Image *) NULL);
1833 assert(image->signature == MagickSignature);
1834 if (image->debug != MagickFalse)
1835 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1836 if (thresholds == (const char *) NULL)
1837 return(MagickTrue);
1838 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1839 return(MagickFalse);
1840 flags=ParseGeometry(thresholds,&geometry_info);
cristy4c08aed2011-07-01 19:47:50 +00001841 GetPixelInfo(image,&threshold);
cristy3ed852e2009-09-05 21:47:34 +00001842 threshold.red=geometry_info.rho;
1843 threshold.green=geometry_info.sigma;
1844 if ((flags & SigmaValue) == 0)
1845 threshold.green=threshold.red;
1846 threshold.blue=geometry_info.xi;
1847 if ((flags & XiValue) == 0)
1848 threshold.blue=threshold.red;
cristy4c08aed2011-07-01 19:47:50 +00001849 threshold.alpha=geometry_info.psi;
cristy3ed852e2009-09-05 21:47:34 +00001850 if ((flags & PsiValue) == 0)
cristy4c08aed2011-07-01 19:47:50 +00001851 threshold.alpha=threshold.red;
1852 threshold.black=geometry_info.chi;
cristy3ed852e2009-09-05 21:47:34 +00001853 if ((flags & ChiValue) == 0)
cristy4c08aed2011-07-01 19:47:50 +00001854 threshold.black=threshold.red;
cristy3ed852e2009-09-05 21:47:34 +00001855 if ((flags & PercentValue) != 0)
1856 {
1857 threshold.red*=(QuantumRange/100.0);
1858 threshold.green*=(QuantumRange/100.0);
1859 threshold.blue*=(QuantumRange/100.0);
cristy4c08aed2011-07-01 19:47:50 +00001860 threshold.alpha*=(QuantumRange/100.0);
1861 threshold.black*=(QuantumRange/100.0);
cristy3ed852e2009-09-05 21:47:34 +00001862 }
1863 /*
1864 White threshold image.
1865 */
1866 status=MagickTrue;
1867 progress=0;
1868 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00001869#if defined(MAGICKCORE_OPENMP_SUPPORT)
1870 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001871#endif
cristybb503372010-05-27 20:51:26 +00001872 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001873 {
cristybb503372010-05-27 20:51:26 +00001874 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001875 x;
1876
cristy4c08aed2011-07-01 19:47:50 +00001877 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001878 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001879
1880 if (status == MagickFalse)
1881 continue;
1882 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001883 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001884 {
1885 status=MagickFalse;
1886 continue;
1887 }
cristybb503372010-05-27 20:51:26 +00001888 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001889 {
cristyf4ad9df2011-07-08 16:49:03 +00001890 if (image->sync != MagickFalse)
cristyb0ea1af2009-11-28 20:44:46 +00001891 {
cristy4c08aed2011-07-01 19:47:50 +00001892 if (GetPixelIntensity(image,q) > GetPixelInfoIntensity(&threshold))
cristyb0ea1af2009-11-28 20:44:46 +00001893 {
cristy4c08aed2011-07-01 19:47:50 +00001894 SetPixelRed(image,QuantumRange,q);
1895 SetPixelGreen(image,QuantumRange,q);
1896 SetPixelBlue(image,QuantumRange,q);
cristyb0ea1af2009-11-28 20:44:46 +00001897 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00001898 SetPixelBlack(image,QuantumRange,q);
cristyb0ea1af2009-11-28 20:44:46 +00001899 }
1900 }
1901 else
1902 {
cristyed231572011-07-14 02:18:59 +00001903 if (((GetPixelRedTraits(image) & UpdatePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +00001904 ((MagickRealType) GetPixelRed(image,q) > threshold.red))
1905 SetPixelRed(image,QuantumRange,q);
cristyed231572011-07-14 02:18:59 +00001906 if (((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +00001907 ((MagickRealType) GetPixelGreen(image,q) > threshold.green))
1908 SetPixelGreen(image,QuantumRange,q);
cristyed231572011-07-14 02:18:59 +00001909 if (((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +00001910 ((MagickRealType) GetPixelBlue(image,q) > threshold.blue))
1911 SetPixelBlue(image,QuantumRange,q);
cristyed231572011-07-14 02:18:59 +00001912 if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
cristyb0ea1af2009-11-28 20:44:46 +00001913 (image->colorspace == CMYKColorspace) &&
cristy4c08aed2011-07-01 19:47:50 +00001914 ((MagickRealType) GetPixelBlack(image,q)) > threshold.black)
1915 SetPixelBlack(image,QuantumRange,q);
cristyed231572011-07-14 02:18:59 +00001916 if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +00001917 ((MagickRealType) GetPixelAlpha(image,q) > threshold.alpha))
1918 SetPixelAlpha(image,QuantumRange,q);
cristyb0ea1af2009-11-28 20:44:46 +00001919 }
cristyed231572011-07-14 02:18:59 +00001920 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001921 }
1922 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1923 status=MagickFalse;
1924 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1925 {
1926 MagickBooleanType
1927 proceed;
1928
cristyb5d5f722009-11-04 03:03:49 +00001929#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00001930 #pragma omp critical (MagickCore_WhiteThresholdImage)
cristy3ed852e2009-09-05 21:47:34 +00001931#endif
1932 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1933 image->rows);
1934 if (proceed == MagickFalse)
1935 status=MagickFalse;
1936 }
1937 }
1938 image_view=DestroyCacheView(image_view);
1939 return(status);
1940}