blob: da42061a637fbcc9b64a55fdc992eafd664eb0b5 [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%
128% Image *AdaptiveThresholdImage(const Image *image,
cristybb503372010-05-27 20:51:26 +0000129% const size_t width,const size_t height,
130% const ssize_t offset,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000131%
132% A description of each parameter follows:
133%
134% o image: the image.
135%
136% o width: the width of the local neighborhood.
137%
138% o height: the height of the local neighborhood.
139%
140% o offset: the mean offset.
141%
142% o exception: return any errors or warnings in this structure.
143%
144*/
145MagickExport Image *AdaptiveThresholdImage(const Image *image,
cristybb503372010-05-27 20:51:26 +0000146 const size_t width,const size_t height,const ssize_t offset,
cristy3ed852e2009-09-05 21:47:34 +0000147 ExceptionInfo *exception)
148{
149#define ThresholdImageTag "Threshold/Image"
150
cristyc4c8d132010-01-07 01:58:38 +0000151 CacheView
152 *image_view,
153 *threshold_view;
154
cristy3ed852e2009-09-05 21:47:34 +0000155 Image
156 *threshold_image;
157
cristy3ed852e2009-09-05 21:47:34 +0000158 MagickBooleanType
159 status;
160
cristy5f959472010-05-27 22:19:46 +0000161 MagickOffsetType
162 progress;
163
cristy4c08aed2011-07-01 19:47:50 +0000164 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000165 zero;
166
167 MagickRealType
168 number_pixels;
169
cristy5f959472010-05-27 22:19:46 +0000170 ssize_t
171 y;
172
cristy3ed852e2009-09-05 21:47:34 +0000173 assert(image != (const Image *) NULL);
174 assert(image->signature == MagickSignature);
175 if (image->debug != MagickFalse)
176 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
177 assert(exception != (ExceptionInfo *) NULL);
178 assert(exception->signature == MagickSignature);
cristy3ed852e2009-09-05 21:47:34 +0000179 threshold_image=CloneImage(image,0,0,MagickTrue,exception);
180 if (threshold_image == (Image *) NULL)
181 return((Image *) NULL);
182 if (SetImageStorageClass(threshold_image,DirectClass) == MagickFalse)
183 {
184 InheritException(exception,&threshold_image->exception);
185 threshold_image=DestroyImage(threshold_image);
186 return((Image *) NULL);
187 }
188 /*
189 Local adaptive threshold.
190 */
191 status=MagickTrue;
192 progress=0;
cristy4c08aed2011-07-01 19:47:50 +0000193 GetPixelInfo(image,&zero);
cristy3ed852e2009-09-05 21:47:34 +0000194 number_pixels=(MagickRealType) width*height;
195 image_view=AcquireCacheView(image);
196 threshold_view=AcquireCacheView(threshold_image);
cristyb5d5f722009-11-04 03:03:49 +0000197#if defined(MAGICKCORE_OPENMP_SUPPORT)
198 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000199#endif
cristybb503372010-05-27 20:51:26 +0000200 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000201 {
202 MagickBooleanType
203 sync;
204
cristy4c08aed2011-07-01 19:47:50 +0000205 register const Quantum
cristyc47d1f82009-11-26 01:44:43 +0000206 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000207
cristybb503372010-05-27 20:51:26 +0000208 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000209 x;
210
cristy4c08aed2011-07-01 19:47:50 +0000211 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000212 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000213
214 if (status == MagickFalse)
215 continue;
cristyd99b0962010-05-29 23:14:26 +0000216 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
217 height/2L,image->columns+width,height,exception);
cristy3ed852e2009-09-05 21:47:34 +0000218 q=GetCacheViewAuthenticPixels(threshold_view,0,y,threshold_image->columns,1,
219 exception);
cristy4c08aed2011-07-01 19:47:50 +0000220 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000221 {
222 status=MagickFalse;
223 continue;
224 }
cristybb503372010-05-27 20:51:26 +0000225 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000226 {
cristy4c08aed2011-07-01 19:47:50 +0000227 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000228 mean,
229 pixel;
230
cristy4c08aed2011-07-01 19:47:50 +0000231 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000232 *r;
233
cristybb503372010-05-27 20:51:26 +0000234 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000235 u;
236
cristy9d314ff2011-03-09 01:30:28 +0000237 ssize_t
238 v;
239
cristy3ed852e2009-09-05 21:47:34 +0000240 pixel=zero;
241 mean=zero;
242 r=p;
cristybb503372010-05-27 20:51:26 +0000243 for (v=0; v < (ssize_t) height; v++)
cristy3ed852e2009-09-05 21:47:34 +0000244 {
cristybb503372010-05-27 20:51:26 +0000245 for (u=0; u < (ssize_t) width; u++)
cristy3ed852e2009-09-05 21:47:34 +0000246 {
cristydcfc1ad2011-07-07 16:25:41 +0000247 pixel.red+=GetPixelAlpha(image,r+u*GetPixelComponents(image));
248 pixel.green+=GetPixelGreen(image,r+u*GetPixelComponents(image));
249 pixel.blue+=GetPixelBlue(image,r+u*GetPixelComponents(image));
cristy3ed852e2009-09-05 21:47:34 +0000250 if (image->colorspace == CMYKColorspace)
cristydcfc1ad2011-07-07 16:25:41 +0000251 pixel.black+=GetPixelBlack(image,r+u*GetPixelComponents(image));
252 pixel.alpha+=GetPixelAlpha(image,r+u*GetPixelComponents(image));
cristy3ed852e2009-09-05 21:47:34 +0000253 }
cristydcfc1ad2011-07-07 16:25:41 +0000254 r+=(image->columns+width)*GetPixelComponents(image);
cristy3ed852e2009-09-05 21:47:34 +0000255 }
256 mean.red=(MagickRealType) (pixel.red/number_pixels+offset);
257 mean.green=(MagickRealType) (pixel.green/number_pixels+offset);
258 mean.blue=(MagickRealType) (pixel.blue/number_pixels+offset);
cristy4c08aed2011-07-01 19:47:50 +0000259 mean.black=(MagickRealType) (pixel.black/number_pixels+offset);
260 mean.alpha=(MagickRealType) (pixel.alpha/number_pixels+offset);
261 SetPixelRed(threshold_image,(Quantum) (((MagickRealType)
262 GetPixelRed(threshold_image,q) <= mean.red) ? 0 : QuantumRange),q);
263 SetPixelGreen(threshold_image,(Quantum) (((MagickRealType)
264 GetPixelGreen(threshold_image,q) <= mean.green) ? 0 : QuantumRange),q);
265 SetPixelBlue(threshold_image,(Quantum) (((MagickRealType)
266 GetPixelBlue(threshold_image,q) <= mean.blue) ? 0 : QuantumRange),q);
cristy3ed852e2009-09-05 21:47:34 +0000267 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +0000268 SetPixelBlack(threshold_image,(Quantum) (((MagickRealType)
269 GetPixelBlack(threshold_image,q) <= mean.black) ? 0 : QuantumRange),
270 q);
271 SetPixelAlpha(threshold_image,(Quantum) (((MagickRealType)
272 GetPixelAlpha(threshold_image,q) <= mean.alpha) ? 0 : QuantumRange),q);
cristydcfc1ad2011-07-07 16:25:41 +0000273 p+=GetPixelComponents(image);
274 q+=GetPixelComponents(threshold_image);
cristy3ed852e2009-09-05 21:47:34 +0000275 }
276 sync=SyncCacheViewAuthenticPixels(threshold_view,exception);
277 if (sync == MagickFalse)
278 status=MagickFalse;
279 if (image->progress_monitor != (MagickProgressMonitor) NULL)
280 {
281 MagickBooleanType
282 proceed;
283
cristyb5d5f722009-11-04 03:03:49 +0000284#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000285 #pragma omp critical (MagickCore_AdaptiveThresholdImage)
286#endif
287 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
288 image->rows);
289 if (proceed == MagickFalse)
290 status=MagickFalse;
291 }
292 }
293 threshold_view=DestroyCacheView(threshold_view);
294 image_view=DestroyCacheView(image_view);
295 if (status == MagickFalse)
296 threshold_image=DestroyImage(threshold_image);
297 return(threshold_image);
298}
299
300/*
301%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
302% %
303% %
304% %
305% B i l e v e l I m a g e %
306% %
307% %
308% %
309%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
310%
311% BilevelImage() changes the value of individual pixels based on the
312% intensity of each pixel channel. The result is a high-contrast image.
313%
314% More precisely each channel value of the image is 'thresholded' so that if
315% it is equal to or less than the given value it is set to zero, while any
316% value greater than that give is set to it maximum or QuantumRange.
317%
318% This function is what is used to implement the "-threshold" operator for
319% the command line API.
320%
321% If the default channel setting is given the image is thresholded using just
322% the gray 'intensity' of the image, rather than the individual channels.
323%
324% The format of the BilevelImageChannel method is:
325%
326% MagickBooleanType BilevelImage(Image *image,const double threshold)
327% MagickBooleanType BilevelImageChannel(Image *image,
328% const ChannelType channel,const double threshold)
329%
330% A description of each parameter follows:
331%
332% o image: the image.
333%
334% o channel: the channel type.
335%
336% o threshold: define the threshold values.
337%
cristyf89cb1d2011-07-07 01:24:37 +0000338% Aside: You can get the same results as operator using LevelImages()
cristy3ed852e2009-09-05 21:47:34 +0000339% with the 'threshold' value for both the black_point and the white_point.
340%
341*/
342
343MagickExport MagickBooleanType BilevelImage(Image *image,const double threshold)
344{
345 MagickBooleanType
346 status;
347
348 status=BilevelImageChannel(image,DefaultChannels,threshold);
349 return(status);
350}
351
352MagickExport MagickBooleanType BilevelImageChannel(Image *image,
353 const ChannelType channel,const double threshold)
354{
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 }
cristy3ed852e2009-09-05 21:47:34 +0000404 if (channel == DefaultChannels)
405 {
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);
cristydcfc1ad2011-07-07 16:25:41 +0000412 q+=GetPixelComponents(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 {
cristy2b9582a2011-07-04 17:38:56 +0000418 if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000419 SetPixelRed(image,(Quantum) ((MagickRealType)
420 GetPixelRed(image,q) <= threshold ? 0 : QuantumRange),q);
cristy2b9582a2011-07-04 17:38:56 +0000421 if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000422 SetPixelGreen(image,(Quantum) ((MagickRealType)
423 GetPixelGreen(image,q) <= threshold ? 0 : QuantumRange),q);
cristy2b9582a2011-07-04 17:38:56 +0000424 if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000425 SetPixelBlue(image,(Quantum) ((MagickRealType)
426 GetPixelBlue(image,q) <= threshold ? 0 : QuantumRange),q);
cristy2b9582a2011-07-04 17:38:56 +0000427 if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +0000428 (image->colorspace == CMYKColorspace))
429 SetPixelBlack(image,(Quantum) ((MagickRealType)
430 GetPixelBlack(image,q) <= threshold ? 0 : QuantumRange),q);
cristy2b9582a2011-07-04 17:38:56 +0000431 if ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 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 }
cristydcfc1ad2011-07-07 16:25:41 +0000441 q+=GetPixelComponents(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)
cristy3ed852e2009-09-05 21:47:34 +0000451 #pragma omp critical (MagickCore_BilevelImageChannel)
452#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%
480% MagickBooleanType BlackThresholdImage(Image *image,const char *threshold)
481% MagickBooleanType BlackThresholdImageChannel(Image *image,
482% const ChannelType channel,const char *threshold,
483% ExceptionInfo *exception)
484%
485% A description of each parameter follows:
486%
487% o image: the image.
488%
489% o channel: the channel or channels to be thresholded.
490%
491% o threshold: Define the threshold value.
492%
493% o exception: return any errors or warnings in this structure.
494%
495*/
496MagickExport MagickBooleanType BlackThresholdImage(Image *image,
497 const char *threshold)
498{
499 MagickBooleanType
500 status;
501
502 status=BlackThresholdImageChannel(image,DefaultChannels,threshold,
503 &image->exception);
504 return(status);
505}
506
507MagickExport MagickBooleanType BlackThresholdImageChannel(Image *image,
508 const ChannelType channel,const char *thresholds,ExceptionInfo *exception)
509{
510#define ThresholdImageTag "Threshold/Image"
511
cristyc4c8d132010-01-07 01:58:38 +0000512 CacheView
513 *image_view;
514
cristy3ed852e2009-09-05 21:47:34 +0000515 GeometryInfo
516 geometry_info;
517
cristy3ed852e2009-09-05 21:47:34 +0000518 MagickBooleanType
519 status;
520
cristy5f959472010-05-27 22:19:46 +0000521 MagickOffsetType
522 progress;
523
cristy4c08aed2011-07-01 19:47:50 +0000524 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000525 threshold;
526
527 MagickStatusType
528 flags;
529
cristy5f959472010-05-27 22:19:46 +0000530 ssize_t
531 y;
532
cristy3ed852e2009-09-05 21:47:34 +0000533 assert(image != (Image *) NULL);
534 assert(image->signature == MagickSignature);
535 if (image->debug != MagickFalse)
536 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
537 if (thresholds == (const char *) NULL)
538 return(MagickTrue);
539 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
540 return(MagickFalse);
cristy4c08aed2011-07-01 19:47:50 +0000541 GetPixelInfo(image,&threshold);
cristy3ed852e2009-09-05 21:47:34 +0000542 flags=ParseGeometry(thresholds,&geometry_info);
543 threshold.red=geometry_info.rho;
544 threshold.green=geometry_info.sigma;
545 if ((flags & SigmaValue) == 0)
546 threshold.green=threshold.red;
547 threshold.blue=geometry_info.xi;
548 if ((flags & XiValue) == 0)
549 threshold.blue=threshold.red;
cristy4c08aed2011-07-01 19:47:50 +0000550 threshold.alpha=geometry_info.psi;
cristy3ed852e2009-09-05 21:47:34 +0000551 if ((flags & PsiValue) == 0)
cristy4c08aed2011-07-01 19:47:50 +0000552 threshold.alpha=threshold.red;
553 threshold.black=geometry_info.chi;
cristy3ed852e2009-09-05 21:47:34 +0000554 if ((flags & ChiValue) == 0)
cristy4c08aed2011-07-01 19:47:50 +0000555 threshold.black=threshold.red;
cristy3ed852e2009-09-05 21:47:34 +0000556 if ((flags & PercentValue) != 0)
557 {
558 threshold.red*=(QuantumRange/100.0);
559 threshold.green*=(QuantumRange/100.0);
560 threshold.blue*=(QuantumRange/100.0);
cristy4c08aed2011-07-01 19:47:50 +0000561 threshold.alpha*=(QuantumRange/100.0);
562 threshold.black*=(QuantumRange/100.0);
cristy3ed852e2009-09-05 21:47:34 +0000563 }
564 /*
565 Black threshold image.
566 */
567 status=MagickTrue;
568 progress=0;
569 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +0000570#if defined(MAGICKCORE_OPENMP_SUPPORT)
571 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000572#endif
cristybb503372010-05-27 20:51:26 +0000573 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000574 {
cristybb503372010-05-27 20:51:26 +0000575 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000576 x;
577
cristy4c08aed2011-07-01 19:47:50 +0000578 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000579 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000580
581 if (status == MagickFalse)
582 continue;
583 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000584 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000585 {
586 status=MagickFalse;
587 continue;
588 }
cristybb503372010-05-27 20:51:26 +0000589 for (x=0; x < (ssize_t) image->columns; x++)
cristyb0ea1af2009-11-28 20:44:46 +0000590 {
591 if (channel != DefaultChannels)
592 {
cristy4c08aed2011-07-01 19:47:50 +0000593 if (GetPixelIntensity(image,q) < GetPixelInfoIntensity(&threshold))
cristyb0ea1af2009-11-28 20:44:46 +0000594 {
cristy4c08aed2011-07-01 19:47:50 +0000595 SetPixelRed(image,0,q);
596 SetPixelGreen(image,0,q);
597 SetPixelBlue(image,0,q);
cristyb0ea1af2009-11-28 20:44:46 +0000598 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +0000599 SetPixelBlack(image,0,q);
cristyb0ea1af2009-11-28 20:44:46 +0000600 }
601 }
602 else
603 {
cristy2b9582a2011-07-04 17:38:56 +0000604 if (((GetPixelRedTraits(image) & ActivePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +0000605 ((MagickRealType) GetPixelRed(image,q) < threshold.red))
606 SetPixelRed(image,0,q);
cristy2b9582a2011-07-04 17:38:56 +0000607 if (((GetPixelGreenTraits(image) & ActivePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +0000608 ((MagickRealType) GetPixelGreen(image,q) < threshold.green))
609 SetPixelGreen(image,0,q);
cristy2b9582a2011-07-04 17:38:56 +0000610 if (((GetPixelBlueTraits(image) & ActivePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +0000611 ((MagickRealType) GetPixelBlue(image,q) < threshold.blue))
612 SetPixelBlue(image,0,q);
cristy2b9582a2011-07-04 17:38:56 +0000613 if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
cristyb0ea1af2009-11-28 20:44:46 +0000614 (image->colorspace == CMYKColorspace) &&
cristy4c08aed2011-07-01 19:47:50 +0000615 ((MagickRealType) GetPixelBlack(image,q) < threshold.black))
616 SetPixelBlack(image,0,q);
cristy2b9582a2011-07-04 17:38:56 +0000617 if (((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +0000618 ((MagickRealType) GetPixelAlpha(image,q) < threshold.alpha))
619 SetPixelAlpha(image,0,q);
cristyb0ea1af2009-11-28 20:44:46 +0000620 }
cristydcfc1ad2011-07-07 16:25:41 +0000621 q+=GetPixelComponents(image);
cristyb0ea1af2009-11-28 20:44:46 +0000622 }
cristy3ed852e2009-09-05 21:47:34 +0000623 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
624 status=MagickFalse;
625 if (image->progress_monitor != (MagickProgressMonitor) NULL)
626 {
627 MagickBooleanType
628 proceed;
629
cristyb5d5f722009-11-04 03:03:49 +0000630#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000631 #pragma omp critical (MagickCore_BlackThresholdImageChannel)
632#endif
633 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
634 image->rows);
635 if (proceed == MagickFalse)
636 status=MagickFalse;
637 }
638 }
639 image_view=DestroyCacheView(image_view);
640 return(status);
641}
642
643/*
644%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
645% %
646% %
647% %
cristy1eb45dd2009-09-25 16:38:06 +0000648% C l a m p I m a g e %
649% %
650% %
651% %
652%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
653%
cristyecb0c6d2009-09-25 16:50:09 +0000654% ClampImage() restricts the color range from 0 to the quantum depth.
cristy1eb45dd2009-09-25 16:38:06 +0000655%
656% The format of the ClampImageChannel method is:
657%
658% MagickBooleanType ClampImage(Image *image)
659% MagickBooleanType ClampImageChannel(Image *image,
660% const ChannelType channel)
661%
662% A description of each parameter follows:
663%
664% o image: the image.
665%
666% o channel: the channel type.
667%
668*/
669
cristy75ffdb72010-01-07 17:40:12 +0000670static inline Quantum ClampToUnsignedQuantum(const Quantum quantum)
cristy1eb45dd2009-09-25 16:38:06 +0000671{
672#if defined(MAGICKCORE_HDRI_SUPPORT)
673 if (quantum <= 0)
674 return(0);
675 if (quantum >= QuantumRange)
676 return(QuantumRange);
677 return(quantum);
678#else
679 return(quantum);
680#endif
681}
682
683MagickExport MagickBooleanType ClampImage(Image *image)
684{
685 MagickBooleanType
686 status;
687
688 status=ClampImageChannel(image,DefaultChannels);
689 return(status);
690}
691
692MagickExport MagickBooleanType ClampImageChannel(Image *image,
693 const ChannelType channel)
694{
695#define ClampImageTag "Clamp/Image"
696
cristyc4c8d132010-01-07 01:58:38 +0000697 CacheView
698 *image_view;
699
cristy1eb45dd2009-09-25 16:38:06 +0000700 ExceptionInfo
701 *exception;
702
cristy1eb45dd2009-09-25 16:38:06 +0000703 MagickBooleanType
704 status;
705
cristy5f959472010-05-27 22:19:46 +0000706 MagickOffsetType
707 progress;
708
709 ssize_t
710 y;
711
cristy1eb45dd2009-09-25 16:38:06 +0000712 assert(image != (Image *) NULL);
713 assert(image->signature == MagickSignature);
714 if (image->debug != MagickFalse)
715 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
716 if (image->storage_class == PseudoClass)
717 {
cristybb503372010-05-27 20:51:26 +0000718 register ssize_t
cristy1eb45dd2009-09-25 16:38:06 +0000719 i;
720
721 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000722 *restrict q;
cristy1eb45dd2009-09-25 16:38:06 +0000723
724 q=image->colormap;
cristybb503372010-05-27 20:51:26 +0000725 for (i=0; i < (ssize_t) image->colors; i++)
cristy1eb45dd2009-09-25 16:38:06 +0000726 {
cristy4c08aed2011-07-01 19:47:50 +0000727 q->red=ClampToUnsignedQuantum(q->red);
728 q->green=ClampToUnsignedQuantum(q->green);
729 q->blue=ClampToUnsignedQuantum(q->blue);
730 q->alpha=ClampToUnsignedQuantum(q->alpha);
cristy1eb45dd2009-09-25 16:38:06 +0000731 q++;
732 }
733 return(SyncImage(image));
734 }
735 /*
cristy611721d2009-09-25 16:42:17 +0000736 Clamp image.
cristy1eb45dd2009-09-25 16:38:06 +0000737 */
738 status=MagickTrue;
739 progress=0;
740 exception=(&image->exception);
741 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +0000742#if defined(MAGICKCORE_OPENMP_SUPPORT)
743 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy1eb45dd2009-09-25 16:38:06 +0000744#endif
cristybb503372010-05-27 20:51:26 +0000745 for (y=0; y < (ssize_t) image->rows; y++)
cristy1eb45dd2009-09-25 16:38:06 +0000746 {
cristybb503372010-05-27 20:51:26 +0000747 register ssize_t
cristy1eb45dd2009-09-25 16:38:06 +0000748 x;
749
cristy4c08aed2011-07-01 19:47:50 +0000750 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000751 *restrict q;
cristy1eb45dd2009-09-25 16:38:06 +0000752
753 if (status == MagickFalse)
754 continue;
755 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000756 if (q == (const Quantum *) NULL)
cristy1eb45dd2009-09-25 16:38:06 +0000757 {
758 status=MagickFalse;
759 continue;
760 }
cristybb503372010-05-27 20:51:26 +0000761 for (x=0; x < (ssize_t) image->columns; x++)
cristy1eb45dd2009-09-25 16:38:06 +0000762 {
cristy2b9582a2011-07-04 17:38:56 +0000763 if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000764 SetPixelRed(image,ClampToUnsignedQuantum(GetPixelRed(image,q)),q);
cristy2b9582a2011-07-04 17:38:56 +0000765 if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000766 SetPixelGreen(image,ClampToUnsignedQuantum(GetPixelGreen(image,q)),q);
cristy2b9582a2011-07-04 17:38:56 +0000767 if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000768 SetPixelBlue(image,ClampToUnsignedQuantum(GetPixelBlue(image,q)),q);
cristy2b9582a2011-07-04 17:38:56 +0000769 if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
cristy1eb45dd2009-09-25 16:38:06 +0000770 (image->colorspace == CMYKColorspace))
cristy4c08aed2011-07-01 19:47:50 +0000771 SetPixelBlack(image,ClampToUnsignedQuantum(GetPixelBlack(image,q)),q);
cristy2b9582a2011-07-04 17:38:56 +0000772 if ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +0000773 SetPixelAlpha(image,ClampToUnsignedQuantum(GetPixelAlpha(image,q)),q);
cristydcfc1ad2011-07-07 16:25:41 +0000774 q+=GetPixelComponents(image);
cristy1eb45dd2009-09-25 16:38:06 +0000775 }
776 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
777 status=MagickFalse;
778 if (image->progress_monitor != (MagickProgressMonitor) NULL)
779 {
780 MagickBooleanType
781 proceed;
782
cristyb5d5f722009-11-04 03:03:49 +0000783#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy1eb45dd2009-09-25 16:38:06 +0000784 #pragma omp critical (MagickCore_ClampImageChannel)
785#endif
786 proceed=SetImageProgress(image,ClampImageTag,progress++,
787 image->rows);
788 if (proceed == MagickFalse)
789 status=MagickFalse;
790 }
791 }
792 image_view=DestroyCacheView(image_view);
793 return(status);
794}
795
796/*
797%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
798% %
799% %
800% %
cristy3ed852e2009-09-05 21:47:34 +0000801% D e s t r o y T h r e s h o l d M a p %
802% %
803% %
804% %
805%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
806%
807% DestroyThresholdMap() de-allocate the given ThresholdMap
808%
809% The format of the ListThresholdMaps method is:
810%
811% ThresholdMap *DestroyThresholdMap(Threshold *map)
812%
813% A description of each parameter follows.
814%
815% o map: Pointer to the Threshold map to destroy
816%
817*/
818MagickExport ThresholdMap *DestroyThresholdMap(ThresholdMap *map)
819{
820 assert(map != (ThresholdMap *) NULL);
821 if (map->map_id != (char *) NULL)
822 map->map_id=DestroyString(map->map_id);
823 if (map->description != (char *) NULL)
824 map->description=DestroyString(map->description);
cristybb503372010-05-27 20:51:26 +0000825 if (map->levels != (ssize_t *) NULL)
826 map->levels=(ssize_t *) RelinquishMagickMemory(map->levels);
cristy3ed852e2009-09-05 21:47:34 +0000827 map=(ThresholdMap *) RelinquishMagickMemory(map);
828 return(map);
829}
830
831/*
832%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
833% %
834% %
835% %
836+ G e t T h r e s h o l d M a p F i l e %
837% %
838% %
839% %
840%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
841%
842% GetThresholdMapFile() look for a given threshold map name or alias in the
843% given XML file data, and return the allocated the map when found.
844%
845% The format of the ListThresholdMaps method is:
846%
847% ThresholdMap *GetThresholdMap(const char *xml,const char *filename,
848% const char *map_id,ExceptionInfo *exception)
849%
850% A description of each parameter follows.
851%
852% o xml: The threshold map list in XML format.
853%
854% o filename: The threshold map XML filename.
855%
856% o map_id: ID of the map to look for in XML list.
857%
858% o exception: return any errors or warnings in this structure.
859%
860*/
861MagickExport ThresholdMap *GetThresholdMapFile(const char *xml,
862 const char *filename,const char *map_id,ExceptionInfo *exception)
863{
864 const char
865 *attr,
866 *content;
867
868 double
869 value;
870
cristy092b7f72010-11-05 15:54:37 +0000871 ThresholdMap
cristy3ed852e2009-09-05 21:47:34 +0000872 *map;
873
874 XMLTreeInfo
875 *description,
876 *levels,
877 *threshold,
878 *thresholds;
879
880 map = (ThresholdMap *)NULL;
881 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
882 "Loading threshold map file \"%s\" ...",filename);
883 thresholds=NewXMLTree(xml,exception);
884 if ( thresholds == (XMLTreeInfo *)NULL )
885 return(map);
886
887 for( threshold = GetXMLTreeChild(thresholds,"threshold");
888 threshold != (XMLTreeInfo *)NULL;
889 threshold = GetNextXMLTreeTag(threshold) ) {
890 attr = GetXMLTreeAttribute(threshold, "map");
891 if ( (attr != (char *)NULL) && (LocaleCompare(map_id,attr) == 0) )
892 break;
893 attr = GetXMLTreeAttribute(threshold, "alias");
894 if ( (attr != (char *)NULL) && (LocaleCompare(map_id,attr) == 0) )
895 break;
896 }
897 if ( threshold == (XMLTreeInfo *)NULL ) {
898 return(map);
899 }
900 description = GetXMLTreeChild(threshold,"description");
901 if ( description == (XMLTreeInfo *)NULL ) {
902 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
903 "XmlMissingElement", "<description>, map \"%s\"", map_id);
904 thresholds = DestroyXMLTree(thresholds);
905 return(map);
906 }
907 levels = GetXMLTreeChild(threshold,"levels");
908 if ( levels == (XMLTreeInfo *)NULL ) {
909 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
910 "XmlMissingElement", "<levels>, map \"%s\"", map_id);
911 thresholds = DestroyXMLTree(thresholds);
912 return(map);
913 }
914
915 /* The map has been found -- Allocate a Threshold Map to return */
cristy73bd4a52010-10-05 11:24:23 +0000916 map = (ThresholdMap *)AcquireMagickMemory(sizeof(ThresholdMap));
cristy3ed852e2009-09-05 21:47:34 +0000917 if ( map == (ThresholdMap *)NULL )
918 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
919 map->map_id = (char *)NULL;
920 map->description = (char *)NULL;
cristybb503372010-05-27 20:51:26 +0000921 map->levels = (ssize_t *) NULL;
cristy3ed852e2009-09-05 21:47:34 +0000922
923 /* Assign Basic Attributes */
924 attr = GetXMLTreeAttribute(threshold, "map");
925 if ( attr != (char *)NULL )
926 map->map_id = ConstantString(attr);
927
928 content = GetXMLTreeContent(description);
929 if ( content != (char *)NULL )
930 map->description = ConstantString(content);
931
932 attr = GetXMLTreeAttribute(levels, "width");
933 if ( attr == (char *)NULL ) {
934 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
935 "XmlMissingAttribute", "<levels width>, map \"%s\"", map_id);
936 thresholds = DestroyXMLTree(thresholds);
937 map = DestroyThresholdMap(map);
938 return(map);
939 }
cristye27293e2009-12-18 02:53:20 +0000940 map->width = StringToUnsignedLong(attr);
cristy3ed852e2009-09-05 21:47:34 +0000941 if ( map->width == 0 ) {
942 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
943 "XmlInvalidAttribute", "<levels width>, map \"%s\"", map_id);
944 thresholds = DestroyXMLTree(thresholds);
945 map = DestroyThresholdMap(map);
946 return(map);
947 }
948
949 attr = GetXMLTreeAttribute(levels, "height");
950 if ( attr == (char *)NULL ) {
951 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
952 "XmlMissingAttribute", "<levels height>, map \"%s\"", map_id);
953 thresholds = DestroyXMLTree(thresholds);
954 map = DestroyThresholdMap(map);
955 return(map);
956 }
cristye27293e2009-12-18 02:53:20 +0000957 map->height = StringToUnsignedLong(attr);
cristy3ed852e2009-09-05 21:47:34 +0000958 if ( map->height == 0 ) {
959 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
960 "XmlInvalidAttribute", "<levels height>, map \"%s\"", map_id);
961 thresholds = DestroyXMLTree(thresholds);
962 map = DestroyThresholdMap(map);
963 return(map);
964 }
965
966 attr = GetXMLTreeAttribute(levels, "divisor");
967 if ( attr == (char *)NULL ) {
968 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
969 "XmlMissingAttribute", "<levels divisor>, map \"%s\"", map_id);
970 thresholds = DestroyXMLTree(thresholds);
971 map = DestroyThresholdMap(map);
972 return(map);
973 }
cristy55a91cd2010-12-01 00:57:40 +0000974 map->divisor = (ssize_t) StringToLong(attr);
cristy3ed852e2009-09-05 21:47:34 +0000975 if ( map->divisor < 2 ) {
976 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
977 "XmlInvalidAttribute", "<levels divisor>, map \"%s\"", map_id);
978 thresholds = DestroyXMLTree(thresholds);
979 map = DestroyThresholdMap(map);
980 return(map);
981 }
982
983 /* Allocate theshold levels array */
984 content = GetXMLTreeContent(levels);
985 if ( content == (char *)NULL ) {
986 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
987 "XmlMissingContent", "<levels>, map \"%s\"", map_id);
988 thresholds = DestroyXMLTree(thresholds);
989 map = DestroyThresholdMap(map);
990 return(map);
991 }
cristybb503372010-05-27 20:51:26 +0000992 map->levels=(ssize_t *) AcquireQuantumMemory((size_t) map->width,map->height*
cristy3ed852e2009-09-05 21:47:34 +0000993 sizeof(*map->levels));
cristybb503372010-05-27 20:51:26 +0000994 if ( map->levels == (ssize_t *)NULL )
cristy3ed852e2009-09-05 21:47:34 +0000995 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
996 { /* parse levels into integer array */
cristycee97112010-05-28 00:44:52 +0000997 ssize_t i;
cristy3ed852e2009-09-05 21:47:34 +0000998 char *p;
cristybb503372010-05-27 20:51:26 +0000999 for( i=0; i< (ssize_t) (map->width*map->height); i++) {
cristycee97112010-05-28 00:44:52 +00001000 map->levels[i] = (ssize_t)strtol(content, &p, 10);
cristy3ed852e2009-09-05 21:47:34 +00001001 if ( p == content ) {
1002 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1003 "XmlInvalidContent", "<level> too few values, map \"%s\"", map_id);
1004 thresholds = DestroyXMLTree(thresholds);
1005 map = DestroyThresholdMap(map);
1006 return(map);
1007 }
1008 if ( map->levels[i] < 0 || map->levels[i] > map->divisor ) {
1009 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
cristye8c25f92010-06-03 00:53:06 +00001010 "XmlInvalidContent", "<level> %.20g out of range, map \"%s\"",
1011 (double) map->levels[i],map_id);
cristy3ed852e2009-09-05 21:47:34 +00001012 thresholds = DestroyXMLTree(thresholds);
1013 map = DestroyThresholdMap(map);
1014 return(map);
1015 }
1016 content = p;
1017 }
1018 value=(double) strtol(content,&p,10);
cristyda16f162011-02-19 23:52:17 +00001019 (void) value;
cristy3ed852e2009-09-05 21:47:34 +00001020 if (p != content)
1021 {
1022 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1023 "XmlInvalidContent", "<level> too many values, map \"%s\"", map_id);
1024 thresholds=DestroyXMLTree(thresholds);
1025 map=DestroyThresholdMap(map);
1026 return(map);
1027 }
1028 }
1029
1030 thresholds = DestroyXMLTree(thresholds);
1031 return(map);
1032}
1033
1034/*
1035%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1036% %
1037% %
1038% %
1039% G e t T h r e s h o l d M a p %
1040% %
1041% %
1042% %
1043%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1044%
1045% GetThresholdMap() load and search one or more threshold map files for the
1046% a map matching the given name or aliase.
1047%
1048% The format of the GetThresholdMap method is:
1049%
1050% ThresholdMap *GetThresholdMap(const char *map_id,
1051% ExceptionInfo *exception)
1052%
1053% A description of each parameter follows.
1054%
1055% o map_id: ID of the map to look for.
1056%
1057% o exception: return any errors or warnings in this structure.
1058%
1059*/
1060MagickExport ThresholdMap *GetThresholdMap(const char *map_id,
1061 ExceptionInfo *exception)
1062{
1063 const StringInfo
1064 *option;
1065
1066 LinkedListInfo
1067 *options;
1068
1069 ThresholdMap
1070 *map;
1071
1072 map=(ThresholdMap *)NULL;
1073 options=GetConfigureOptions(ThresholdsFilename,exception);
1074 while (( option=(const StringInfo *) GetNextValueInLinkedList(options) )
1075 != (const StringInfo *) NULL && map == (ThresholdMap *)NULL )
1076 map=GetThresholdMapFile((const char *) GetStringInfoDatum(option),
1077 GetStringInfoPath(option),map_id,exception);
1078 options=DestroyConfigureOptions(options);
1079 return(map);
1080}
1081
1082/*
1083%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1084% %
1085% %
1086% %
1087+ L i s t T h r e s h o l d M a p F i l e %
1088% %
1089% %
1090% %
1091%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1092%
1093% ListThresholdMapFile() lists the threshold maps and their descriptions
1094% in the given XML file data.
1095%
1096% The format of the ListThresholdMaps method is:
1097%
1098% MagickBooleanType ListThresholdMaps(FILE *file,const char*xml,
1099% const char *filename,ExceptionInfo *exception)
1100%
1101% A description of each parameter follows.
1102%
1103% o file: An pointer to the output FILE.
1104%
1105% o xml: The threshold map list in XML format.
1106%
1107% o filename: The threshold map XML filename.
1108%
1109% o exception: return any errors or warnings in this structure.
1110%
1111*/
1112MagickBooleanType ListThresholdMapFile(FILE *file,const char *xml,
1113 const char *filename,ExceptionInfo *exception)
1114{
1115 XMLTreeInfo *thresholds,*threshold,*description;
1116 const char *map,*alias,*content;
1117
1118 assert( xml != (char *)NULL );
1119 assert( file != (FILE *)NULL );
1120
1121 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1122 "Loading threshold map file \"%s\" ...",filename);
1123 thresholds=NewXMLTree(xml,exception);
1124 if ( thresholds == (XMLTreeInfo *)NULL )
1125 return(MagickFalse);
1126
cristy1e604812011-05-19 18:07:50 +00001127 (void) FormatLocaleFile(file,"%-16s %-12s %s\n","Map","Alias","Description");
1128 (void) FormatLocaleFile(file,
1129 "----------------------------------------------------\n");
cristy3ed852e2009-09-05 21:47:34 +00001130
1131 for( threshold = GetXMLTreeChild(thresholds,"threshold");
1132 threshold != (XMLTreeInfo *)NULL;
1133 threshold = GetNextXMLTreeTag(threshold) )
1134 {
1135 map = GetXMLTreeAttribute(threshold, "map");
1136 if (map == (char *) NULL) {
1137 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1138 "XmlMissingAttribute", "<map>");
1139 thresholds=DestroyXMLTree(thresholds);
1140 return(MagickFalse);
1141 }
1142 alias = GetXMLTreeAttribute(threshold, "alias");
1143 /* alias is optional, no if test needed */
1144 description=GetXMLTreeChild(threshold,"description");
1145 if ( description == (XMLTreeInfo *)NULL ) {
1146 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1147 "XmlMissingElement", "<description>, map \"%s\"", map);
1148 thresholds=DestroyXMLTree(thresholds);
1149 return(MagickFalse);
1150 }
1151 content=GetXMLTreeContent(description);
1152 if ( content == (char *)NULL ) {
1153 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1154 "XmlMissingContent", "<description>, map \"%s\"", map);
1155 thresholds=DestroyXMLTree(thresholds);
1156 return(MagickFalse);
1157 }
cristy1e604812011-05-19 18:07:50 +00001158 (void) FormatLocaleFile(file,"%-16s %-12s %s\n",map,alias ? alias : "",
1159 content);
cristy3ed852e2009-09-05 21:47:34 +00001160 }
1161 thresholds=DestroyXMLTree(thresholds);
1162 return(MagickTrue);
1163}
1164
1165/*
1166%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1167% %
1168% %
1169% %
1170% L i s t T h r e s h o l d M a p s %
1171% %
1172% %
1173% %
1174%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1175%
1176% ListThresholdMaps() lists the threshold maps and their descriptions
1177% as defined by "threshold.xml" to a file.
1178%
1179% The format of the ListThresholdMaps method is:
1180%
1181% MagickBooleanType ListThresholdMaps(FILE *file,ExceptionInfo *exception)
1182%
1183% A description of each parameter follows.
1184%
1185% o file: An pointer to the output FILE.
1186%
1187% o exception: return any errors or warnings in this structure.
1188%
1189*/
1190MagickExport MagickBooleanType ListThresholdMaps(FILE *file,
1191 ExceptionInfo *exception)
1192{
1193 const StringInfo
1194 *option;
1195
1196 LinkedListInfo
1197 *options;
1198
1199 MagickStatusType
1200 status;
1201
1202 status=MagickFalse;
1203 if ( file == (FILE *)NULL )
1204 file = stdout;
1205 options=GetConfigureOptions(ThresholdsFilename,exception);
1206
cristy1e604812011-05-19 18:07:50 +00001207 (void) FormatLocaleFile(file,
1208 "\n Threshold Maps for Ordered Dither Operations\n");
cristy3ed852e2009-09-05 21:47:34 +00001209 while ( ( option=(const StringInfo *) GetNextValueInLinkedList(options) )
1210 != (const StringInfo *) NULL)
1211 {
cristyb51dff52011-05-19 16:55:47 +00001212 (void) FormatLocaleFile(file,"\nPATH: %s\n\n",GetStringInfoPath(option));
cristy3ed852e2009-09-05 21:47:34 +00001213 status|=ListThresholdMapFile(file,(const char *) GetStringInfoDatum(option),
1214 GetStringInfoPath(option),exception);
1215 }
1216 options=DestroyConfigureOptions(options);
1217 return(status != 0 ? MagickTrue : MagickFalse);
1218}
1219
1220/*
1221%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1222% %
1223% %
1224% %
cristy3ed852e2009-09-05 21:47:34 +00001225% O r d e r e d P o s t e r i z e I m a g e %
1226% %
1227% %
1228% %
1229%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1230%
1231% OrderedPosterizeImage() will perform a ordered dither based on a number
1232% of pre-defined dithering threshold maps, but over multiple intensity
1233% levels, which can be different for different channels, according to the
1234% input argument.
1235%
1236% The format of the OrderedPosterizeImage method is:
1237%
1238% MagickBooleanType OrderedPosterizeImage(Image *image,
1239% const char *threshold_map,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001240%
1241% A description of each parameter follows:
1242%
1243% o image: the image.
1244%
cristy3ed852e2009-09-05 21:47:34 +00001245% o threshold_map: A string containing the name of the threshold dither
1246% map to use, followed by zero or more numbers representing the number
1247% of color levels tho dither between.
1248%
cristyf998fb32011-04-27 23:00:47 +00001249% Any level number less than 2 will be equivalent to 2, and means only
cristy3ed852e2009-09-05 21:47:34 +00001250% binary dithering will be applied to each color channel.
1251%
1252% No numbers also means a 2 level (bitmap) dither will be applied to all
1253% channels, while a single number is the number of levels applied to each
1254% channel in sequence. More numbers will be applied in turn to each of
1255% the color channels.
1256%
1257% For example: "o3x3,6" will generate a 6 level posterization of the
1258% image with a ordered 3x3 diffused pixel dither being applied between
1259% each level. While checker,8,8,4 will produce a 332 colormaped image
1260% with only a single checkerboard hash pattern (50% grey) between each
1261% color level, to basically double the number of color levels with
1262% a bare minimim of dithering.
1263%
1264% o exception: return any errors or warnings in this structure.
1265%
1266*/
1267MagickExport MagickBooleanType OrderedPosterizeImage(Image *image,
1268 const char *threshold_map,ExceptionInfo *exception)
1269{
cristy3ed852e2009-09-05 21:47:34 +00001270#define DitherImageTag "Dither/Image"
1271
cristyc4c8d132010-01-07 01:58:38 +00001272 CacheView
1273 *image_view;
1274
cristy3ed852e2009-09-05 21:47:34 +00001275 LongPixelPacket
1276 levels;
1277
1278 MagickBooleanType
1279 status;
1280
cristy5f959472010-05-27 22:19:46 +00001281 MagickOffsetType
1282 progress;
1283
1284 ssize_t
1285 y;
1286
cristy3ed852e2009-09-05 21:47:34 +00001287 ThresholdMap
1288 *map;
1289
cristy3ed852e2009-09-05 21:47:34 +00001290 assert(image != (Image *) NULL);
1291 assert(image->signature == MagickSignature);
1292 if (image->debug != MagickFalse)
1293 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1294 assert(exception != (ExceptionInfo *) NULL);
1295 assert(exception->signature == MagickSignature);
1296 if (threshold_map == (const char *) NULL)
1297 return(MagickTrue);
1298 {
1299 char
1300 token[MaxTextExtent];
1301
1302 register const char
1303 *p;
1304
1305 p=(char *)threshold_map;
1306 while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
1307 (*p != '\0'))
1308 p++;
1309 threshold_map=p;
1310 while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
1311 (*p != '\0')) {
cristy37e0b382011-06-07 13:31:21 +00001312 if ((p-threshold_map) >= (MaxTextExtent-1))
cristy3ed852e2009-09-05 21:47:34 +00001313 break;
1314 token[p-threshold_map] = *p;
1315 p++;
1316 }
1317 token[p-threshold_map] = '\0';
1318 map = GetThresholdMap(token, exception);
1319 if ( map == (ThresholdMap *)NULL ) {
1320 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1321 "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
1322 return(MagickFalse);
1323 }
1324 }
glennrp1e7f7bc2011-03-02 19:25:28 +00001325 /* Set channel levels from extra comma separated arguments
cristy3ed852e2009-09-05 21:47:34 +00001326 Default to 2, the single value given, or individual channel values
1327 */
1328#if 1
glennrp1e7f7bc2011-03-02 19:25:28 +00001329 { /* parse directly as a comma separated list of integers */
cristy3ed852e2009-09-05 21:47:34 +00001330 char *p;
1331
1332 p = strchr((char *) threshold_map,',');
cristy13020672011-07-08 02:33:26 +00001333 levels.red=0;
1334 levels.green=0;
1335 levels.blue=0;
1336 levels.black=0;
1337 levels.alpha=0;
cristy3ed852e2009-09-05 21:47:34 +00001338 if ( p != (char *)NULL && isdigit((int) ((unsigned char) *(++p))) )
cristy4c08aed2011-07-01 19:47:50 +00001339 levels.black = (unsigned int) strtoul(p, &p, 10);
cristy3ed852e2009-09-05 21:47:34 +00001340 else
cristy4c08aed2011-07-01 19:47:50 +00001341 levels.black = 2;
cristy3ed852e2009-09-05 21:47:34 +00001342
cristy13020672011-07-08 02:33:26 +00001343 if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
1344 levels.red=levels.black;
1345 if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
1346 levels.green=levels.black;
1347 if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
1348 levels.blue=levels.black;
1349 if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
1350 (image->colorspace == CMYKColorspace))
1351 levels.black=levels.black;
1352 if (((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0) &&
1353 (image->matte != MagickFalse))
1354 levels.alpha=levels.black;
cristy3ed852e2009-09-05 21:47:34 +00001355
1356 /* if more than a single number, each channel has a separate value */
1357 if ( p != (char *) NULL && *p == ',' ) {
1358 p=strchr((char *) threshold_map,',');
1359 p++;
cristy2b9582a2011-07-04 17:38:56 +00001360 if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
cristyecd0ab52010-05-30 14:59:20 +00001361 levels.red = (unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
cristy2b9582a2011-07-04 17:38:56 +00001362 if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
cristyecd0ab52010-05-30 14:59:20 +00001363 levels.green = (unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
cristy2b9582a2011-07-04 17:38:56 +00001364 if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
cristyecd0ab52010-05-30 14:59:20 +00001365 levels.blue = (unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
cristy2b9582a2011-07-04 17:38:56 +00001366 if ((GetPixelBlackTraits(image) & ActivePixelTrait) != 0 &&
cristy4c08aed2011-07-01 19:47:50 +00001367 (image->colorspace == CMYKColorspace))
1368 levels.black=(unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
cristy2b9582a2011-07-04 17:38:56 +00001369 if ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001370 levels.alpha = (unsigned int) strtoul(p, &p, 10), (void)(*p == ',' && p++);
cristy3ed852e2009-09-05 21:47:34 +00001371 }
1372 }
1373#else
1374 /* Parse level values as a geometry */
1375 /* This difficult!
1376 * How to map GeometryInfo structure elements into
1377 * LongPixelPacket structure elements, but according to channel?
1378 * Note the channels list may skip elements!!!!
1379 * EG -channel BA -ordered-dither map,2,3
cristy4c08aed2011-07-01 19:47:50 +00001380 * will need to map g.rho -> l.blue, and g.sigma -> l.alpha
cristy3ed852e2009-09-05 21:47:34 +00001381 * A simpler way is needed, probably converting geometry to a temporary
cristybb503372010-05-27 20:51:26 +00001382 * array, then using channel to advance the index into ssize_t pixel packet.
cristy3ed852e2009-09-05 21:47:34 +00001383 */
1384#endif
1385
1386#if 0
cristye8c25f92010-06-03 00:53:06 +00001387printf("DEBUG levels r=%u g=%u b=%u a=%u i=%u\n",
cristy4c08aed2011-07-01 19:47:50 +00001388 levels.red, levels.green, levels.blue, levels.alpha, levels.index);
cristy3ed852e2009-09-05 21:47:34 +00001389#endif
1390
1391 { /* Do the posterized ordered dithering of the image */
cristycee97112010-05-28 00:44:52 +00001392 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001393 d;
1394
1395 /* d = number of psuedo-level divisions added between color levels */
1396 d = map->divisor-1;
1397
1398 /* reduce levels to levels - 1 */
1399 levels.red = levels.red ? levels.red-1 : 0;
1400 levels.green = levels.green ? levels.green-1 : 0;
1401 levels.blue = levels.blue ? levels.blue-1 : 0;
cristy4c08aed2011-07-01 19:47:50 +00001402 levels.black = levels.black ? levels.black-1 : 0;
1403 levels.alpha = levels.alpha ? levels.alpha-1 : 0;
cristy3ed852e2009-09-05 21:47:34 +00001404
1405 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1406 {
1407 InheritException(exception,&image->exception);
1408 return(MagickFalse);
1409 }
1410 status=MagickTrue;
1411 progress=0;
1412 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00001413#if defined(MAGICKCORE_OPENMP_SUPPORT)
1414 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001415#endif
cristybb503372010-05-27 20:51:26 +00001416 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001417 {
cristybb503372010-05-27 20:51:26 +00001418 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001419 x;
1420
cristy4c08aed2011-07-01 19:47:50 +00001421 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001422 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001423
1424 if (status == MagickFalse)
1425 continue;
1426 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001427 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001428 {
1429 status=MagickFalse;
1430 continue;
1431 }
cristybb503372010-05-27 20:51:26 +00001432 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001433 {
cristycee97112010-05-28 00:44:52 +00001434 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001435 threshold,
1436 t,
1437 l;
1438
1439 /*
1440 Figure out the dither threshold for this pixel
1441 This must be a integer from 1 to map->divisor-1
1442 */
1443 threshold = map->levels[(x%map->width) +map->width*(y%map->height)];
1444
1445 /* Dither each channel in the image as appropriate
1446 Notes on the integer Math...
1447 total number of divisions = (levels-1)*(divisor-1)+1)
1448 t1 = this colors psuedo_level =
1449 q->red * total_divisions / (QuantumRange+1)
1450 l = posterization level 0..levels
1451 t = dither threshold level 0..divisor-1 NB: 0 only on last
1452 Each color_level is of size QuantumRange / (levels-1)
1453 NB: All input levels and divisor are already had 1 subtracted
1454 Opacity is inverted so 'off' represents transparent.
1455 */
cristy4c08aed2011-07-01 19:47:50 +00001456 if (levels.red != 0) {
1457 t = (ssize_t) (QuantumScale*GetPixelRed(image,q)*(levels.red*d+1));
cristy3ed852e2009-09-05 21:47:34 +00001458 l = t/d; t = t-l*d;
cristy4c08aed2011-07-01 19:47:50 +00001459 SetPixelRed(image,RoundToQuantum((MagickRealType)
1460 ((l+(t >= threshold))*(MagickRealType) QuantumRange/levels.red)),q);
cristy3ed852e2009-09-05 21:47:34 +00001461 }
cristy4c08aed2011-07-01 19:47:50 +00001462 if (levels.green != 0) {
1463 t = (ssize_t) (QuantumScale*GetPixelGreen(image,q)*
cristyfba5a8b2011-05-03 17:12:12 +00001464 (levels.green*d+1));
cristy3ed852e2009-09-05 21:47:34 +00001465 l = t/d; t = t-l*d;
cristy4c08aed2011-07-01 19:47:50 +00001466 SetPixelGreen(image,RoundToQuantum((MagickRealType)
1467 ((l+(t >= threshold))*(MagickRealType) QuantumRange/levels.green)),q);
cristy3ed852e2009-09-05 21:47:34 +00001468 }
cristy4c08aed2011-07-01 19:47:50 +00001469 if (levels.blue != 0) {
1470 t = (ssize_t) (QuantumScale*GetPixelBlue(image,q)*
cristyfba5a8b2011-05-03 17:12:12 +00001471 (levels.blue*d+1));
cristy3ed852e2009-09-05 21:47:34 +00001472 l = t/d; t = t-l*d;
cristy4c08aed2011-07-01 19:47:50 +00001473 SetPixelBlue(image,RoundToQuantum((MagickRealType)
1474 ((l+(t >= threshold))*(MagickRealType) QuantumRange/levels.blue)),q);
cristy3ed852e2009-09-05 21:47:34 +00001475 }
cristy4c08aed2011-07-01 19:47:50 +00001476 if (levels.alpha != 0) {
1477 t = (ssize_t) ((1.0-QuantumScale*GetPixelAlpha(image,q))*
1478 (levels.alpha*d+1));
cristy3ed852e2009-09-05 21:47:34 +00001479 l = t/d; t = t-l*d;
cristy4c08aed2011-07-01 19:47:50 +00001480 SetPixelAlpha(image,RoundToQuantum((MagickRealType)
cristyfba5a8b2011-05-03 17:12:12 +00001481 ((1.0-l-(t >= threshold))*(MagickRealType) QuantumRange/
cristy4c08aed2011-07-01 19:47:50 +00001482 levels.alpha)),q);
cristy3ed852e2009-09-05 21:47:34 +00001483 }
cristy4c08aed2011-07-01 19:47:50 +00001484 if (levels.black != 0) {
1485 t = (ssize_t) (QuantumScale*GetPixelBlack(image,q)*
1486 (levels.black*d+1));
cristy3ed852e2009-09-05 21:47:34 +00001487 l = t/d; t = t-l*d;
cristy4c08aed2011-07-01 19:47:50 +00001488 SetPixelBlack(image,RoundToQuantum((MagickRealType)
1489 ((l+(t>=threshold))*(MagickRealType) QuantumRange/levels.black)),q);
cristy3ed852e2009-09-05 21:47:34 +00001490 }
cristydcfc1ad2011-07-07 16:25:41 +00001491 q+=GetPixelComponents(image);
cristy3ed852e2009-09-05 21:47:34 +00001492 }
1493 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1494 status=MagickFalse;
1495 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1496 {
1497 MagickBooleanType
1498 proceed;
1499
cristyb5d5f722009-11-04 03:03:49 +00001500#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy13020672011-07-08 02:33:26 +00001501 #pragma omp critical (MagickCore_OrderedPosterizeImage)
cristy3ed852e2009-09-05 21:47:34 +00001502#endif
1503 proceed=SetImageProgress(image,DitherImageTag,progress++,image->rows);
1504 if (proceed == MagickFalse)
1505 status=MagickFalse;
1506 }
1507 }
1508 image_view=DestroyCacheView(image_view);
1509 }
1510 map=DestroyThresholdMap(map);
1511 return(MagickTrue);
1512}
1513
1514/*
1515%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1516% %
1517% %
1518% %
1519% R a n d o m T h r e s h o l d I m a g e %
1520% %
1521% %
1522% %
1523%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1524%
1525% RandomThresholdImage() changes the value of individual pixels based on the
1526% intensity of each pixel compared to a random threshold. The result is a
1527% low-contrast, two color image.
1528%
1529% The format of the RandomThresholdImage method is:
1530%
1531% MagickBooleanType RandomThresholdImageChannel(Image *image,
1532% const char *thresholds,ExceptionInfo *exception)
1533% MagickBooleanType RandomThresholdImageChannel(Image *image,
1534% const ChannelType channel,const char *thresholds,
1535% ExceptionInfo *exception)
1536%
1537% A description of each parameter follows:
1538%
1539% o image: the image.
1540%
1541% o channel: the channel or channels to be thresholded.
1542%
1543% o thresholds: a geometry string containing low,high thresholds. If the
1544% string contains 2x2, 3x3, or 4x4, an ordered dither of order 2, 3, or 4
1545% is performed instead.
1546%
1547% o exception: return any errors or warnings in this structure.
1548%
1549*/
1550
1551MagickExport MagickBooleanType RandomThresholdImage(Image *image,
1552 const char *thresholds,ExceptionInfo *exception)
1553{
1554 MagickBooleanType
1555 status;
1556
1557 status=RandomThresholdImageChannel(image,DefaultChannels,thresholds,
1558 exception);
1559 return(status);
1560}
1561
1562MagickExport MagickBooleanType RandomThresholdImageChannel(Image *image,
1563 const ChannelType channel,const char *thresholds,ExceptionInfo *exception)
1564{
1565#define ThresholdImageTag "Threshold/Image"
1566
cristyfa112112010-01-04 17:48:07 +00001567 CacheView
1568 *image_view;
1569
cristy3ed852e2009-09-05 21:47:34 +00001570 GeometryInfo
1571 geometry_info;
1572
1573 MagickStatusType
1574 flags;
1575
cristy3ed852e2009-09-05 21:47:34 +00001576 MagickBooleanType
1577 status;
1578
cristy5f959472010-05-27 22:19:46 +00001579 MagickOffsetType
1580 progress;
1581
cristy4c08aed2011-07-01 19:47:50 +00001582 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001583 threshold;
1584
1585 MagickRealType
1586 min_threshold,
1587 max_threshold;
1588
1589 RandomInfo
cristyfa112112010-01-04 17:48:07 +00001590 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +00001591
cristy5f959472010-05-27 22:19:46 +00001592 ssize_t
1593 y;
1594
cristy3ed852e2009-09-05 21:47:34 +00001595 assert(image != (Image *) NULL);
1596 assert(image->signature == MagickSignature);
1597 if (image->debug != MagickFalse)
1598 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1599 assert(exception != (ExceptionInfo *) NULL);
1600 assert(exception->signature == MagickSignature);
1601 if (thresholds == (const char *) NULL)
1602 return(MagickTrue);
cristy4c08aed2011-07-01 19:47:50 +00001603 GetPixelInfo(image,&threshold);
cristy3ed852e2009-09-05 21:47:34 +00001604 min_threshold=0.0;
1605 max_threshold=(MagickRealType) QuantumRange;
1606 flags=ParseGeometry(thresholds,&geometry_info);
1607 min_threshold=geometry_info.rho;
1608 max_threshold=geometry_info.sigma;
1609 if ((flags & SigmaValue) == 0)
1610 max_threshold=min_threshold;
1611 if (strchr(thresholds,'%') != (char *) NULL)
1612 {
1613 max_threshold*=(MagickRealType) (0.01*QuantumRange);
1614 min_threshold*=(MagickRealType) (0.01*QuantumRange);
1615 }
1616 else
1617 if (((max_threshold == min_threshold) || (max_threshold == 1)) &&
1618 (min_threshold <= 8))
1619 {
1620 /*
1621 Backward Compatibility -- ordered-dither -- IM v 6.2.9-6.
1622 */
cristy13020672011-07-08 02:33:26 +00001623 status=OrderedPosterizeImage(image,thresholds,exception);
cristy3ed852e2009-09-05 21:47:34 +00001624 return(status);
1625 }
1626 /*
1627 Random threshold image.
1628 */
1629 status=MagickTrue;
1630 progress=0;
cristy9a9230e2011-04-26 14:56:14 +00001631 if (channel == CompositeChannels)
cristy3ed852e2009-09-05 21:47:34 +00001632 {
1633 if (AcquireImageColormap(image,2) == MagickFalse)
1634 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1635 image->filename);
1636 random_info=AcquireRandomInfoThreadSet();
1637 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00001638#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy5c9e6f22010-09-17 17:31:01 +00001639 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001640#endif
cristybb503372010-05-27 20:51:26 +00001641 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001642 {
cristy5c9e6f22010-09-17 17:31:01 +00001643 const int
1644 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00001645
cristy3ed852e2009-09-05 21:47:34 +00001646 MagickBooleanType
1647 sync;
1648
cristybb503372010-05-27 20:51:26 +00001649 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001650 x;
1651
cristy4c08aed2011-07-01 19:47:50 +00001652 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001653 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001654
1655 if (status == MagickFalse)
1656 continue;
1657 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1658 exception);
cristy4c08aed2011-07-01 19:47:50 +00001659 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001660 {
1661 status=MagickFalse;
1662 continue;
1663 }
cristybb503372010-05-27 20:51:26 +00001664 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001665 {
cristy3ed852e2009-09-05 21:47:34 +00001666 MagickRealType
1667 intensity;
1668
cristy4c08aed2011-07-01 19:47:50 +00001669 Quantum
1670 index;
1671
1672 intensity=(MagickRealType) GetPixelIntensity(image,q);
cristy3ed852e2009-09-05 21:47:34 +00001673 if (intensity < min_threshold)
cristy4c08aed2011-07-01 19:47:50 +00001674 threshold.black=min_threshold;
cristy3ed852e2009-09-05 21:47:34 +00001675 else
cristy4c08aed2011-07-01 19:47:50 +00001676 if (intensity > max_threshold)
1677 threshold.black=max_threshold;
1678 else
1679 threshold.black=(MagickRealType)(QuantumRange*
1680 GetPseudoRandomValue(random_info[id]));
1681 index=(Quantum) (intensity <= threshold.black ? 0 : 1);
1682 SetPixelIndex(image,index,q);
1683 SetPixelPacket(image,image->colormap+(ssize_t) index,q);
cristydcfc1ad2011-07-07 16:25:41 +00001684 q+=GetPixelComponents(image);
cristy3ed852e2009-09-05 21:47:34 +00001685 }
1686 sync=SyncCacheViewAuthenticPixels(image_view,exception);
1687 if (sync == MagickFalse)
1688 status=MagickFalse;
1689 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1690 {
1691 MagickBooleanType
1692 proceed;
1693
cristyb5d5f722009-11-04 03:03:49 +00001694#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001695 #pragma omp critical (MagickCore_RandomThresholdImageChannel)
1696#endif
1697 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1698 image->rows);
1699 if (proceed == MagickFalse)
1700 status=MagickFalse;
1701 }
1702 }
1703 image_view=DestroyCacheView(image_view);
1704 random_info=DestroyRandomInfoThreadSet(random_info);
1705 return(status);
1706 }
1707 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1708 {
1709 InheritException(exception,&image->exception);
1710 return(MagickFalse);
1711 }
1712 random_info=AcquireRandomInfoThreadSet();
1713 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00001714#if defined(MAGICKCORE_OPENMP_SUPPORT)
1715 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001716#endif
cristybb503372010-05-27 20:51:26 +00001717 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001718 {
cristy5c9e6f22010-09-17 17:31:01 +00001719 const int
1720 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00001721
cristy4c08aed2011-07-01 19:47:50 +00001722 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001723 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001724
cristy5c9e6f22010-09-17 17:31:01 +00001725 register ssize_t
1726 x;
1727
cristy3ed852e2009-09-05 21:47:34 +00001728 if (status == MagickFalse)
1729 continue;
1730 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001731 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001732 {
1733 status=MagickFalse;
1734 continue;
1735 }
cristybb503372010-05-27 20:51:26 +00001736 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001737 {
cristy2b9582a2011-07-04 17:38:56 +00001738 if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +00001739 {
cristy4c08aed2011-07-01 19:47:50 +00001740 if ((MagickRealType) GetPixelRed(image,q) < min_threshold)
cristy3ed852e2009-09-05 21:47:34 +00001741 threshold.red=min_threshold;
1742 else
cristy4c08aed2011-07-01 19:47:50 +00001743 if ((MagickRealType) GetPixelRed(image,q) > max_threshold)
cristy3ed852e2009-09-05 21:47:34 +00001744 threshold.red=max_threshold;
1745 else
1746 threshold.red=(MagickRealType) (QuantumRange*
1747 GetPseudoRandomValue(random_info[id]));
1748 }
cristy2b9582a2011-07-04 17:38:56 +00001749 if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +00001750 {
cristy4c08aed2011-07-01 19:47:50 +00001751 if ((MagickRealType) GetPixelGreen(image,q) < min_threshold)
cristy3ed852e2009-09-05 21:47:34 +00001752 threshold.green=min_threshold;
1753 else
cristy4c08aed2011-07-01 19:47:50 +00001754 if ((MagickRealType) GetPixelGreen(image,q) > max_threshold)
cristy3ed852e2009-09-05 21:47:34 +00001755 threshold.green=max_threshold;
1756 else
1757 threshold.green=(MagickRealType) (QuantumRange*
1758 GetPseudoRandomValue(random_info[id]));
1759 }
cristy2b9582a2011-07-04 17:38:56 +00001760 if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
cristy3ed852e2009-09-05 21:47:34 +00001761 {
cristy4c08aed2011-07-01 19:47:50 +00001762 if ((MagickRealType) GetPixelBlue(image,q) < min_threshold)
cristy3ed852e2009-09-05 21:47:34 +00001763 threshold.blue=min_threshold;
1764 else
cristy4c08aed2011-07-01 19:47:50 +00001765 if ((MagickRealType) GetPixelBlue(image,q) > max_threshold)
cristy3ed852e2009-09-05 21:47:34 +00001766 threshold.blue=max_threshold;
1767 else
1768 threshold.blue=(MagickRealType) (QuantumRange*
1769 GetPseudoRandomValue(random_info[id]));
1770 }
cristy2b9582a2011-07-04 17:38:56 +00001771 if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00001772 (image->colorspace == CMYKColorspace))
1773 {
cristy4c08aed2011-07-01 19:47:50 +00001774 if ((MagickRealType) GetPixelBlack(image,q) < min_threshold)
1775 threshold.black=min_threshold;
cristy3ed852e2009-09-05 21:47:34 +00001776 else
cristy4c08aed2011-07-01 19:47:50 +00001777 if ((MagickRealType) GetPixelBlack(image,q) > max_threshold)
1778 threshold.black=max_threshold;
cristy3ed852e2009-09-05 21:47:34 +00001779 else
cristy4c08aed2011-07-01 19:47:50 +00001780 threshold.black=(MagickRealType) (QuantumRange*
1781 GetPseudoRandomValue(random_info[id]));
1782 }
cristy2b9582a2011-07-04 17:38:56 +00001783 if ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001784 {
1785 if ((MagickRealType) GetPixelAlpha(image,q) < min_threshold)
1786 threshold.alpha=min_threshold;
1787 else
1788 if ((MagickRealType) GetPixelAlpha(image,q) > max_threshold)
1789 threshold.alpha=max_threshold;
1790 else
1791 threshold.alpha=(MagickRealType) (QuantumRange*
cristy3ed852e2009-09-05 21:47:34 +00001792 GetPseudoRandomValue(random_info[id]));
1793 }
cristy2b9582a2011-07-04 17:38:56 +00001794 if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001795 SetPixelRed(image,(Quantum) ((MagickRealType)
1796 GetPixelRed(image,q) <= threshold.red ? 0 : QuantumRange),q);
cristy2b9582a2011-07-04 17:38:56 +00001797 if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001798 SetPixelGreen(image,(Quantum) ((MagickRealType)
1799 GetPixelGreen(image,q) <= threshold.green ? 0 : QuantumRange),q);
cristy2b9582a2011-07-04 17:38:56 +00001800 if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001801 SetPixelBlue(image,(Quantum) ((MagickRealType)
1802 GetPixelBlue(image,q) <= threshold.blue ? 0 : QuantumRange),q);
cristy2b9582a2011-07-04 17:38:56 +00001803 if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00001804 (image->colorspace == CMYKColorspace))
cristy4c08aed2011-07-01 19:47:50 +00001805 SetPixelBlack(image,(Quantum) ((MagickRealType)
1806 GetPixelBlack(image,q) <= threshold.black ? 0 : QuantumRange),q);
cristy2b9582a2011-07-04 17:38:56 +00001807 if ((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0)
cristy4c08aed2011-07-01 19:47:50 +00001808 SetPixelAlpha(image,(Quantum) ((MagickRealType)
1809 GetPixelAlpha(image,q) <= threshold.alpha ? 0 : QuantumRange),q);
cristydcfc1ad2011-07-07 16:25:41 +00001810 q+=GetPixelComponents(image);
cristy3ed852e2009-09-05 21:47:34 +00001811 }
1812 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1813 status=MagickFalse;
1814 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1815 {
1816 MagickBooleanType
1817 proceed;
1818
cristyb5d5f722009-11-04 03:03:49 +00001819#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001820 #pragma omp critical (MagickCore_RandomThresholdImageChannel)
1821#endif
1822 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1823 image->rows);
1824 if (proceed == MagickFalse)
1825 status=MagickFalse;
1826 }
1827 }
1828 image_view=DestroyCacheView(image_view);
1829 random_info=DestroyRandomInfoThreadSet(random_info);
1830 return(status);
1831}
1832
1833/*
1834%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1835% %
1836% %
1837% %
1838% W h i t e T h r e s h o l d I m a g e %
1839% %
1840% %
1841% %
1842%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1843%
1844% WhiteThresholdImage() is like ThresholdImage() but forces all pixels above
cristy4e101302009-09-17 12:49:12 +00001845% the threshold into white while leaving all pixels at or below the threshold
cristy3ed852e2009-09-05 21:47:34 +00001846% unchanged.
1847%
1848% The format of the WhiteThresholdImage method is:
1849%
1850% MagickBooleanType WhiteThresholdImage(Image *image,const char *threshold)
1851% MagickBooleanType WhiteThresholdImageChannel(Image *image,
1852% const ChannelType channel,const char *threshold,
1853% ExceptionInfo *exception)
1854%
1855% A description of each parameter follows:
1856%
1857% o image: the image.
1858%
1859% o channel: the channel or channels to be thresholded.
1860%
1861% o threshold: Define the threshold value.
1862%
1863% o exception: return any errors or warnings in this structure.
1864%
1865*/
1866MagickExport MagickBooleanType WhiteThresholdImage(Image *image,
1867 const char *threshold)
1868{
1869 MagickBooleanType
1870 status;
1871
1872 status=WhiteThresholdImageChannel(image,DefaultChannels,threshold,
1873 &image->exception);
1874 return(status);
1875}
1876
1877MagickExport MagickBooleanType WhiteThresholdImageChannel(Image *image,
1878 const ChannelType channel,const char *thresholds,ExceptionInfo *exception)
1879{
1880#define ThresholdImageTag "Threshold/Image"
1881
cristy5f959472010-05-27 22:19:46 +00001882 CacheView
1883 *image_view;
1884
cristy3ed852e2009-09-05 21:47:34 +00001885 GeometryInfo
1886 geometry_info;
1887
cristy3ed852e2009-09-05 21:47:34 +00001888 MagickBooleanType
1889 status;
1890
cristy4c08aed2011-07-01 19:47:50 +00001891 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001892 threshold;
1893
cristy5f959472010-05-27 22:19:46 +00001894 MagickOffsetType
1895 progress;
1896
cristy3ed852e2009-09-05 21:47:34 +00001897 MagickStatusType
1898 flags;
1899
cristy5f959472010-05-27 22:19:46 +00001900 ssize_t
1901 y;
cristy3ed852e2009-09-05 21:47:34 +00001902
1903 assert(image != (Image *) NULL);
1904 assert(image->signature == MagickSignature);
1905 if (image->debug != MagickFalse)
1906 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1907 if (thresholds == (const char *) NULL)
1908 return(MagickTrue);
1909 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1910 return(MagickFalse);
1911 flags=ParseGeometry(thresholds,&geometry_info);
cristy4c08aed2011-07-01 19:47:50 +00001912 GetPixelInfo(image,&threshold);
cristy3ed852e2009-09-05 21:47:34 +00001913 threshold.red=geometry_info.rho;
1914 threshold.green=geometry_info.sigma;
1915 if ((flags & SigmaValue) == 0)
1916 threshold.green=threshold.red;
1917 threshold.blue=geometry_info.xi;
1918 if ((flags & XiValue) == 0)
1919 threshold.blue=threshold.red;
cristy4c08aed2011-07-01 19:47:50 +00001920 threshold.alpha=geometry_info.psi;
cristy3ed852e2009-09-05 21:47:34 +00001921 if ((flags & PsiValue) == 0)
cristy4c08aed2011-07-01 19:47:50 +00001922 threshold.alpha=threshold.red;
1923 threshold.black=geometry_info.chi;
cristy3ed852e2009-09-05 21:47:34 +00001924 if ((flags & ChiValue) == 0)
cristy4c08aed2011-07-01 19:47:50 +00001925 threshold.black=threshold.red;
cristy3ed852e2009-09-05 21:47:34 +00001926 if ((flags & PercentValue) != 0)
1927 {
1928 threshold.red*=(QuantumRange/100.0);
1929 threshold.green*=(QuantumRange/100.0);
1930 threshold.blue*=(QuantumRange/100.0);
cristy4c08aed2011-07-01 19:47:50 +00001931 threshold.alpha*=(QuantumRange/100.0);
1932 threshold.black*=(QuantumRange/100.0);
cristy3ed852e2009-09-05 21:47:34 +00001933 }
1934 /*
1935 White threshold image.
1936 */
1937 status=MagickTrue;
1938 progress=0;
1939 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00001940#if defined(MAGICKCORE_OPENMP_SUPPORT)
1941 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001942#endif
cristybb503372010-05-27 20:51:26 +00001943 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001944 {
cristybb503372010-05-27 20:51:26 +00001945 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001946 x;
1947
cristy4c08aed2011-07-01 19:47:50 +00001948 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001949 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001950
1951 if (status == MagickFalse)
1952 continue;
1953 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001954 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001955 {
1956 status=MagickFalse;
1957 continue;
1958 }
cristybb503372010-05-27 20:51:26 +00001959 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001960 {
cristyb0ea1af2009-11-28 20:44:46 +00001961 if (channel != DefaultChannels)
1962 {
cristy4c08aed2011-07-01 19:47:50 +00001963 if (GetPixelIntensity(image,q) > GetPixelInfoIntensity(&threshold))
cristyb0ea1af2009-11-28 20:44:46 +00001964 {
cristy4c08aed2011-07-01 19:47:50 +00001965 SetPixelRed(image,QuantumRange,q);
1966 SetPixelGreen(image,QuantumRange,q);
1967 SetPixelBlue(image,QuantumRange,q);
cristyb0ea1af2009-11-28 20:44:46 +00001968 if (image->colorspace == CMYKColorspace)
cristy4c08aed2011-07-01 19:47:50 +00001969 SetPixelBlack(image,QuantumRange,q);
cristyb0ea1af2009-11-28 20:44:46 +00001970 }
1971 }
1972 else
1973 {
cristy2b9582a2011-07-04 17:38:56 +00001974 if (((GetPixelRedTraits(image) & ActivePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +00001975 ((MagickRealType) GetPixelRed(image,q) > threshold.red))
1976 SetPixelRed(image,QuantumRange,q);
cristy2b9582a2011-07-04 17:38:56 +00001977 if (((GetPixelGreenTraits(image) & ActivePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +00001978 ((MagickRealType) GetPixelGreen(image,q) > threshold.green))
1979 SetPixelGreen(image,QuantumRange,q);
cristy2b9582a2011-07-04 17:38:56 +00001980 if (((GetPixelBlueTraits(image) & ActivePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +00001981 ((MagickRealType) GetPixelBlue(image,q) > threshold.blue))
1982 SetPixelBlue(image,QuantumRange,q);
cristy2b9582a2011-07-04 17:38:56 +00001983 if (((GetPixelBlackTraits(image) & ActivePixelTrait) != 0) &&
cristyb0ea1af2009-11-28 20:44:46 +00001984 (image->colorspace == CMYKColorspace) &&
cristy4c08aed2011-07-01 19:47:50 +00001985 ((MagickRealType) GetPixelBlack(image,q)) > threshold.black)
1986 SetPixelBlack(image,QuantumRange,q);
cristy2b9582a2011-07-04 17:38:56 +00001987 if (((GetPixelAlphaTraits(image) & ActivePixelTrait) != 0) &&
cristy4c08aed2011-07-01 19:47:50 +00001988 ((MagickRealType) GetPixelAlpha(image,q) > threshold.alpha))
1989 SetPixelAlpha(image,QuantumRange,q);
cristyb0ea1af2009-11-28 20:44:46 +00001990 }
cristydcfc1ad2011-07-07 16:25:41 +00001991 q+=GetPixelComponents(image);
cristy3ed852e2009-09-05 21:47:34 +00001992 }
1993 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1994 status=MagickFalse;
1995 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1996 {
1997 MagickBooleanType
1998 proceed;
1999
cristyb5d5f722009-11-04 03:03:49 +00002000#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002001 #pragma omp critical (MagickCore_WhiteThresholdImageChannel)
2002#endif
2003 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
2004 image->rows);
2005 if (proceed == MagickFalse)
2006 status=MagickFalse;
2007 }
2008 }
2009 image_view=DestroyCacheView(image_view);
2010 return(status);
2011}