blob: e17b8eafe11ce98aab48d42be770af338860b88b [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% %
cristy45ef08f2012-12-07 13:13:34 +000020% Copyright 1999-2013 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"
cristy23e55c02012-04-10 01:21:56 +000051#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000052#include "MagickCore/configure.h"
53#include "MagickCore/constitute.h"
54#include "MagickCore/decorate.h"
55#include "MagickCore/draw.h"
56#include "MagickCore/enhance.h"
57#include "MagickCore/exception.h"
58#include "MagickCore/exception-private.h"
59#include "MagickCore/effect.h"
60#include "MagickCore/fx.h"
61#include "MagickCore/gem.h"
62#include "MagickCore/geometry.h"
63#include "MagickCore/image-private.h"
64#include "MagickCore/list.h"
65#include "MagickCore/log.h"
66#include "MagickCore/memory_.h"
67#include "MagickCore/monitor.h"
68#include "MagickCore/monitor-private.h"
69#include "MagickCore/montage.h"
70#include "MagickCore/option.h"
71#include "MagickCore/pixel-accessor.h"
72#include "MagickCore/quantize.h"
73#include "MagickCore/quantum.h"
74#include "MagickCore/random_.h"
75#include "MagickCore/random-private.h"
76#include "MagickCore/resize.h"
77#include "MagickCore/resource_.h"
78#include "MagickCore/segment.h"
79#include "MagickCore/shear.h"
80#include "MagickCore/signature-private.h"
81#include "MagickCore/string_.h"
82#include "MagickCore/string-private.h"
83#include "MagickCore/thread-private.h"
84#include "MagickCore/threshold.h"
cristy4e0b82a2011-09-29 12:47:44 +000085#include "MagickCore/token.h"
cristy4c08aed2011-07-01 19:47:50 +000086#include "MagickCore/transform.h"
87#include "MagickCore/xml-tree.h"
cristy433d1182011-09-04 13:38:52 +000088#include "MagickCore/xml-tree-private.h"
cristy3ed852e2009-09-05 21:47:34 +000089
90/*
91 Define declarations.
92*/
93#define ThresholdsFilename "thresholds.xml"
94
95/*
96 Typedef declarations.
97*/
98struct _ThresholdMap
99{
100 char
101 *map_id,
102 *description;
103
cristybb503372010-05-27 20:51:26 +0000104 size_t
cristy3ed852e2009-09-05 21:47:34 +0000105 width,
106 height;
107
cristybb503372010-05-27 20:51:26 +0000108 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000109 divisor,
110 *levels;
111};
112
113/*
cristyda9d0e42013-02-26 00:50:38 +0000114 Static declarations.
115*/
116static const char
117 *MinimalThresholdMap =
118 "<?xml version=\"1.0\"?>"
119 "<thresholds>"
120 " <threshold map=\"threshold\" alias=\"1x1\">"
121 " <description>Threshold 1x1 (non-dither)</description>"
122 " <levels width=\"1\" height=\"1\" divisor=\"2\">"
123 " 1"
124 " </levels>"
125 " </threshold>"
126 " <threshold map=\"checks\" alias=\"2x1\">"
127 " <description>Checkerboard 2x1 (dither)</description>"
128 " <levels width=\"2\" height=\"2\" divisor=\"3\">"
129 " 1 2"
130 " 2 1"
131 " </levels>"
132 " </threshold>"
133 "</thresholds>";
134
135/*
cristybd0ebf02011-09-29 01:19:42 +0000136 Forward declarations.
137*/
138static ThresholdMap
139 *GetThresholdMapFile(const char *,const char *,const char *,ExceptionInfo *);
140
141/*
cristy3ed852e2009-09-05 21:47:34 +0000142%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
143% %
144% %
145% %
146% A d a p t i v e T h r e s h o l d I m a g e %
147% %
148% %
149% %
150%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
151%
152% AdaptiveThresholdImage() selects an individual threshold for each pixel
153% based on the range of intensity values in its local neighborhood. This
154% allows for thresholding of an image whose global intensity histogram
155% doesn't contain distinctive peaks.
156%
157% The format of the AdaptiveThresholdImage method is:
158%
cristyde5cc632011-07-18 14:47:00 +0000159% Image *AdaptiveThresholdImage(const Image *image,const size_t width,
160% const size_t height,const double bias,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000161%
162% A description of each parameter follows:
163%
164% o image: the image.
165%
166% o width: the width of the local neighborhood.
167%
168% o height: the height of the local neighborhood.
169%
cristyde5cc632011-07-18 14:47:00 +0000170% o bias: the mean bias.
cristy3ed852e2009-09-05 21:47:34 +0000171%
172% o exception: return any errors or warnings in this structure.
173%
174*/
175MagickExport Image *AdaptiveThresholdImage(const Image *image,
cristyde5cc632011-07-18 14:47:00 +0000176 const size_t width,const size_t height,const double bias,
cristy3ed852e2009-09-05 21:47:34 +0000177 ExceptionInfo *exception)
178{
cristyde5cc632011-07-18 14:47:00 +0000179#define AdaptiveThresholdImageTag "AdaptiveThreshold/Image"
cristy3ed852e2009-09-05 21:47:34 +0000180
cristyc4c8d132010-01-07 01:58:38 +0000181 CacheView
182 *image_view,
183 *threshold_view;
184
cristy3ed852e2009-09-05 21:47:34 +0000185 Image
186 *threshold_image;
187
cristy3ed852e2009-09-05 21:47:34 +0000188 MagickBooleanType
189 status;
190
cristy5f959472010-05-27 22:19:46 +0000191 MagickOffsetType
192 progress;
193
cristyde5cc632011-07-18 14:47:00 +0000194 MagickSizeType
cristy3ed852e2009-09-05 21:47:34 +0000195 number_pixels;
196
cristy5f959472010-05-27 22:19:46 +0000197 ssize_t
198 y;
199
cristyde5cc632011-07-18 14:47:00 +0000200 /*
201 Initialize threshold image attributes.
202 */
203 assert(image != (Image *) NULL);
cristy3ed852e2009-09-05 21:47:34 +0000204 assert(image->signature == MagickSignature);
205 if (image->debug != MagickFalse)
206 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
207 assert(exception != (ExceptionInfo *) NULL);
208 assert(exception->signature == MagickSignature);
cristyde5cc632011-07-18 14:47:00 +0000209 threshold_image=CloneImage(image,image->columns,image->rows,MagickTrue,
210 exception);
cristy3ed852e2009-09-05 21:47:34 +0000211 if (threshold_image == (Image *) NULL)
212 return((Image *) NULL);
cristyb9eb87b2011-09-29 01:15:19 +0000213 status=SetImageStorageClass(threshold_image,DirectClass,exception);
214 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000215 {
cristy3ed852e2009-09-05 21:47:34 +0000216 threshold_image=DestroyImage(threshold_image);
217 return((Image *) NULL);
218 }
219 /*
cristyde5cc632011-07-18 14:47:00 +0000220 Threshold image.
cristy3ed852e2009-09-05 21:47:34 +0000221 */
222 status=MagickTrue;
223 progress=0;
cristyde5cc632011-07-18 14:47:00 +0000224 number_pixels=(MagickSizeType) width*height;
cristy46ff2672012-12-14 15:32:26 +0000225 image_view=AcquireVirtualCacheView(image,exception);
226 threshold_view=AcquireAuthenticCacheView(threshold_image,exception);
cristyb5d5f722009-11-04 03:03:49 +0000227#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000228 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +0000229 magick_threads(image,threshold_image,image->rows,1)
cristy3ed852e2009-09-05 21:47:34 +0000230#endif
cristybb503372010-05-27 20:51:26 +0000231 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000232 {
cristy525cf962012-09-09 15:00:40 +0000233 double
234 channel_bias[MaxPixelChannels],
235 channel_sum[MaxPixelChannels];
236
cristy4c08aed2011-07-01 19:47:50 +0000237 register const Quantum
cristy525cf962012-09-09 15:00:40 +0000238 *restrict p,
239 *restrict pixels;
cristy3ed852e2009-09-05 21:47:34 +0000240
cristy4c08aed2011-07-01 19:47:50 +0000241 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000242 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000243
cristyde5cc632011-07-18 14:47:00 +0000244 register ssize_t
cristy525cf962012-09-09 15:00:40 +0000245 i,
cristyde5cc632011-07-18 14:47:00 +0000246 x;
247
cristyde5cc632011-07-18 14:47:00 +0000248 ssize_t
cristy525cf962012-09-09 15:00:40 +0000249 center,
250 u,
251 v;
cristyde5cc632011-07-18 14:47:00 +0000252
cristy3ed852e2009-09-05 21:47:34 +0000253 if (status == MagickFalse)
254 continue;
cristyd99b0962010-05-29 23:14:26 +0000255 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
cristyde5cc632011-07-18 14:47:00 +0000256 (height/2L),image->columns+width,height,exception);
257 q=QueueCacheViewAuthenticPixels(threshold_view,0,y,threshold_image->columns,
258 1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000259 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000260 {
261 status=MagickFalse;
262 continue;
263 }
cristy5f9f2462011-09-28 23:37:58 +0000264 center=(ssize_t) GetPixelChannels(image)*(image->columns+width)*(height/2L)+
cristya0312c92011-07-23 21:04:30 +0000265 GetPixelChannels(image)*(width/2);
cristy525cf962012-09-09 15:00:40 +0000266 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
267 {
cristy5a23c552013-02-13 14:34:28 +0000268 PixelChannel channel=GetPixelChannelChannel(image,i);
269 PixelTrait traits=GetPixelChannelTraits(image,channel);
270 PixelTrait threshold_traits=GetPixelChannelTraits(threshold_image,
271 channel);
cristy525cf962012-09-09 15:00:40 +0000272 if ((traits == UndefinedPixelTrait) ||
273 (threshold_traits == UndefinedPixelTrait))
274 continue;
275 if (((threshold_traits & CopyPixelTrait) != 0) ||
cristy883fde12013-04-08 00:50:13 +0000276 (GetPixelReadMask(image,p) == 0))
cristy525cf962012-09-09 15:00:40 +0000277 {
278 SetPixelChannel(threshold_image,channel,p[center+i],q);
279 continue;
280 }
281 pixels=p;
282 channel_bias[channel]=0.0;
283 channel_sum[channel]=0.0;
284 for (v=0; v < (ssize_t) height; v++)
285 {
286 for (u=0; u < (ssize_t) width; u++)
287 {
288 if (u == (ssize_t) (width-1))
289 channel_bias[channel]+=pixels[i];
290 channel_sum[channel]+=pixels[i];
291 pixels+=GetPixelChannels(image);
292 }
cristyb772eff2013-03-06 22:30:46 +0000293 pixels+=(image->columns-1)*GetPixelChannels(image);
cristy525cf962012-09-09 15:00:40 +0000294 }
295 }
cristybb503372010-05-27 20:51:26 +0000296 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000297 {
cristya0312c92011-07-23 21:04:30 +0000298 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristy3ed852e2009-09-05 21:47:34 +0000299 {
cristya19f1d72012-08-07 18:24:38 +0000300 double
cristy525cf962012-09-09 15:00:40 +0000301 mean;
cristyde5cc632011-07-18 14:47:00 +0000302
cristy5a23c552013-02-13 14:34:28 +0000303 PixelChannel channel=GetPixelChannelChannel(image,i);
304 PixelTrait traits=GetPixelChannelTraits(image,channel);
305 PixelTrait threshold_traits=GetPixelChannelTraits(threshold_image,
306 channel);
cristy010d7d12011-08-31 01:02:48 +0000307 if ((traits == UndefinedPixelTrait) ||
308 (threshold_traits == UndefinedPixelTrait))
cristyde5cc632011-07-18 14:47:00 +0000309 continue;
cristy1eced092012-08-10 23:10:56 +0000310 if (((threshold_traits & CopyPixelTrait) != 0) ||
cristy883fde12013-04-08 00:50:13 +0000311 (GetPixelReadMask(image,p) == 0))
cristyde5cc632011-07-18 14:47:00 +0000312 {
cristy0beccfa2011-09-25 20:47:53 +0000313 SetPixelChannel(threshold_image,channel,p[center+i],q);
cristyde5cc632011-07-18 14:47:00 +0000314 continue;
315 }
cristy525cf962012-09-09 15:00:40 +0000316 channel_sum[channel]-=channel_bias[channel];
317 channel_bias[channel]=0.0;
cristyde5cc632011-07-18 14:47:00 +0000318 pixels=p;
cristyde5cc632011-07-18 14:47:00 +0000319 for (v=0; v < (ssize_t) height; v++)
cristy3ed852e2009-09-05 21:47:34 +0000320 {
cristy525cf962012-09-09 15:00:40 +0000321 channel_bias[channel]+=pixels[i];
322 pixels+=(width-1)*GetPixelChannels(image);
323 channel_sum[channel]+=pixels[i];
cristy4b5f2f82013-03-06 22:38:03 +0000324 pixels+=(image->columns-1)*GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000325 }
cristy525cf962012-09-09 15:00:40 +0000326 mean=(double) (channel_sum[channel]/number_pixels+bias);
cristya19f1d72012-08-07 18:24:38 +0000327 SetPixelChannel(threshold_image,channel,(Quantum) ((double)
cristy5f9f2462011-09-28 23:37:58 +0000328 p[center+i] <= mean ? 0 : QuantumRange),q);
cristy3ed852e2009-09-05 21:47:34 +0000329 }
cristya0312c92011-07-23 21:04:30 +0000330 p+=GetPixelChannels(image);
331 q+=GetPixelChannels(threshold_image);
cristy3ed852e2009-09-05 21:47:34 +0000332 }
cristyde5cc632011-07-18 14:47:00 +0000333 if (SyncCacheViewAuthenticPixels(threshold_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000334 status=MagickFalse;
335 if (image->progress_monitor != (MagickProgressMonitor) NULL)
336 {
337 MagickBooleanType
338 proceed;
339
cristyb5d5f722009-11-04 03:03:49 +0000340#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000341 #pragma omp critical (MagickCore_AdaptiveThresholdImage)
cristy3ed852e2009-09-05 21:47:34 +0000342#endif
cristyde5cc632011-07-18 14:47:00 +0000343 proceed=SetImageProgress(image,AdaptiveThresholdImageTag,progress++,
cristy3ed852e2009-09-05 21:47:34 +0000344 image->rows);
345 if (proceed == MagickFalse)
346 status=MagickFalse;
347 }
348 }
cristyde5cc632011-07-18 14:47:00 +0000349 threshold_image->type=image->type;
cristy3ed852e2009-09-05 21:47:34 +0000350 threshold_view=DestroyCacheView(threshold_view);
351 image_view=DestroyCacheView(image_view);
352 if (status == MagickFalse)
353 threshold_image=DestroyImage(threshold_image);
354 return(threshold_image);
355}
356
357/*
358%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
359% %
360% %
361% %
362% B i l e v e l I m a g e %
363% %
364% %
365% %
366%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
367%
368% BilevelImage() changes the value of individual pixels based on the
369% intensity of each pixel channel. The result is a high-contrast image.
370%
371% More precisely each channel value of the image is 'thresholded' so that if
372% it is equal to or less than the given value it is set to zero, while any
373% value greater than that give is set to it maximum or QuantumRange.
374%
375% This function is what is used to implement the "-threshold" operator for
376% the command line API.
377%
378% If the default channel setting is given the image is thresholded using just
379% the gray 'intensity' of the image, rather than the individual channels.
380%
cristyf4ad9df2011-07-08 16:49:03 +0000381% The format of the BilevelImage method is:
cristy3ed852e2009-09-05 21:47:34 +0000382%
cristye941a752011-10-15 01:52:48 +0000383% MagickBooleanType BilevelImage(Image *image,const double threshold,
384% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000385%
386% A description of each parameter follows:
387%
388% o image: the image.
389%
cristy3ed852e2009-09-05 21:47:34 +0000390% o threshold: define the threshold values.
391%
cristye941a752011-10-15 01:52:48 +0000392% o exception: return any errors or warnings in this structure.
393%
cristyf89cb1d2011-07-07 01:24:37 +0000394% Aside: You can get the same results as operator using LevelImages()
cristy3ed852e2009-09-05 21:47:34 +0000395% with the 'threshold' value for both the black_point and the white_point.
396%
397*/
cristye941a752011-10-15 01:52:48 +0000398MagickExport MagickBooleanType BilevelImage(Image *image,const double threshold,
399 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000400{
401#define ThresholdImageTag "Threshold/Image"
402
cristyc4c8d132010-01-07 01:58:38 +0000403 CacheView
404 *image_view;
405
cristy3ed852e2009-09-05 21:47:34 +0000406 MagickBooleanType
407 status;
408
cristy5f959472010-05-27 22:19:46 +0000409 MagickOffsetType
410 progress;
411
412 ssize_t
413 y;
414
cristy3ed852e2009-09-05 21:47:34 +0000415 assert(image != (Image *) NULL);
416 assert(image->signature == MagickSignature);
417 if (image->debug != MagickFalse)
418 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy574cc262011-08-05 01:23:58 +0000419 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000420 return(MagickFalse);
cristy3a3b9962013-02-08 18:41:18 +0000421 if (IsGrayColorspace(image->colorspace) != MagickFalse)
cristy0c81d062013-04-21 15:22:02 +0000422 (void) SetImageColorspace(image,sRGBColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +0000423 /*
424 Bilevel threshold image.
425 */
426 status=MagickTrue;
427 progress=0;
cristy46ff2672012-12-14 15:32:26 +0000428 image_view=AcquireAuthenticCacheView(image,exception);
cristyb5d5f722009-11-04 03:03:49 +0000429#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd065992012-08-09 15:27:39 +0000430 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +0000431 magick_threads(image,image,image->rows,1)
cristy3ed852e2009-09-05 21:47:34 +0000432#endif
cristybb503372010-05-27 20:51:26 +0000433 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000434 {
cristybb503372010-05-27 20:51:26 +0000435 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000436 x;
437
cristy4c08aed2011-07-01 19:47:50 +0000438 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000439 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000440
441 if (status == MagickFalse)
442 continue;
443 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000444 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000445 {
446 status=MagickFalse;
447 continue;
448 }
cristy805b6a02011-08-09 00:59:35 +0000449 for (x=0; x < (ssize_t) image->columns; x++)
450 {
cristy171e2352012-07-10 17:43:12 +0000451 double
452 pixel;
453
cristy95111202011-08-09 19:41:42 +0000454 register ssize_t
455 i;
456
cristy883fde12013-04-08 00:50:13 +0000457 if (GetPixelReadMask(image,q) == 0)
cristy10a6c612012-01-29 21:41:05 +0000458 {
459 q+=GetPixelChannels(image);
460 continue;
461 }
cristyf13c5942012-08-08 23:50:11 +0000462 pixel=GetPixelIntensity(image,q);
cristy95111202011-08-09 19:41:42 +0000463 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
464 {
cristy5a23c552013-02-13 14:34:28 +0000465 PixelChannel channel=GetPixelChannelChannel(image,i);
466 PixelTrait traits=GetPixelChannelTraits(image,channel);
cristyd09f8802012-02-04 16:44:10 +0000467 if ((traits & UpdatePixelTrait) == 0)
468 continue;
cristya64f4b92012-07-11 23:59:00 +0000469 if (image->channel_mask != DefaultChannels)
cristyf13c5942012-08-08 23:50:11 +0000470 pixel=(double) q[i];
cristy171e2352012-07-10 17:43:12 +0000471 q[i]=(Quantum) (pixel <= threshold ? 0 : QuantumRange);
cristy95111202011-08-09 19:41:42 +0000472 }
cristy805b6a02011-08-09 00:59:35 +0000473 q+=GetPixelChannels(image);
474 }
cristy3ed852e2009-09-05 21:47:34 +0000475 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
476 status=MagickFalse;
477 if (image->progress_monitor != (MagickProgressMonitor) NULL)
478 {
479 MagickBooleanType
480 proceed;
481
cristyb5d5f722009-11-04 03:03:49 +0000482#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000483 #pragma omp critical (MagickCore_BilevelImage)
cristy3ed852e2009-09-05 21:47:34 +0000484#endif
485 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
486 image->rows);
487 if (proceed == MagickFalse)
488 status=MagickFalse;
489 }
490 }
491 image_view=DestroyCacheView(image_view);
492 return(status);
493}
494
495/*
496%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
497% %
498% %
499% %
500% B l a c k T h r e s h o l d I m a g e %
501% %
502% %
503% %
504%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
505%
506% BlackThresholdImage() is like ThresholdImage() but forces all pixels below
cristy4e101302009-09-17 12:49:12 +0000507% the threshold into black while leaving all pixels at or above the threshold
cristy3ed852e2009-09-05 21:47:34 +0000508% unchanged.
509%
510% The format of the BlackThresholdImage method is:
511%
cristyf4ad9df2011-07-08 16:49:03 +0000512% MagickBooleanType BlackThresholdImage(Image *image,
513% const char *threshold,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000514%
515% A description of each parameter follows:
516%
517% o image: the image.
518%
cristy5f9f2462011-09-28 23:37:58 +0000519% o threshold: define the threshold value.
cristy3ed852e2009-09-05 21:47:34 +0000520%
521% o exception: return any errors or warnings in this structure.
522%
523*/
524MagickExport MagickBooleanType BlackThresholdImage(Image *image,
cristyf4ad9df2011-07-08 16:49:03 +0000525 const char *thresholds,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000526{
527#define ThresholdImageTag "Threshold/Image"
528
cristyc4c8d132010-01-07 01:58:38 +0000529 CacheView
530 *image_view;
531
cristy3ed852e2009-09-05 21:47:34 +0000532 GeometryInfo
533 geometry_info;
534
cristy3ed852e2009-09-05 21:47:34 +0000535 MagickBooleanType
536 status;
537
cristy5f959472010-05-27 22:19:46 +0000538 MagickOffsetType
539 progress;
540
cristyd6803382012-04-10 01:41:25 +0000541 PixelInfo
cristya12d8ba2012-04-29 16:33:41 +0000542 threshold;
cristy3ed852e2009-09-05 21:47:34 +0000543
544 MagickStatusType
545 flags;
546
cristy5f959472010-05-27 22:19:46 +0000547 ssize_t
548 y;
549
cristy3ed852e2009-09-05 21:47:34 +0000550 assert(image != (Image *) NULL);
551 assert(image->signature == MagickSignature);
552 if (image->debug != MagickFalse)
553 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
554 if (thresholds == (const char *) NULL)
555 return(MagickTrue);
cristy574cc262011-08-05 01:23:58 +0000556 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000557 return(MagickFalse);
cristy23e55c02012-04-10 01:21:56 +0000558 if (IsGrayColorspace(image->colorspace) != MagickFalse)
cristy0c81d062013-04-21 15:22:02 +0000559 (void) SetImageColorspace(image,sRGBColorspace,exception);
cristya12d8ba2012-04-29 16:33:41 +0000560 GetPixelInfo(image,&threshold);
cristy3ed852e2009-09-05 21:47:34 +0000561 flags=ParseGeometry(thresholds,&geometry_info);
cristya12d8ba2012-04-29 16:33:41 +0000562 threshold.red=geometry_info.rho;
563 threshold.green=geometry_info.rho;
564 threshold.blue=geometry_info.rho;
565 threshold.black=geometry_info.rho;
566 threshold.alpha=100.0;
cristy5f9f2462011-09-28 23:37:58 +0000567 if ((flags & SigmaValue) != 0)
cristya12d8ba2012-04-29 16:33:41 +0000568 threshold.green=geometry_info.sigma;
cristy5f9f2462011-09-28 23:37:58 +0000569 if ((flags & XiValue) != 0)
cristya12d8ba2012-04-29 16:33:41 +0000570 threshold.blue=geometry_info.xi;
cristy5f9f2462011-09-28 23:37:58 +0000571 if ((flags & PsiValue) != 0)
cristya12d8ba2012-04-29 16:33:41 +0000572 threshold.alpha=geometry_info.psi;
573 if (threshold.colorspace == CMYKColorspace)
cristyd6803382012-04-10 01:41:25 +0000574 {
575 if ((flags & PsiValue) != 0)
cristya12d8ba2012-04-29 16:33:41 +0000576 threshold.black=geometry_info.psi;
cristyd6803382012-04-10 01:41:25 +0000577 if ((flags & ChiValue) != 0)
cristya12d8ba2012-04-29 16:33:41 +0000578 threshold.alpha=geometry_info.chi;
579 }
580 if ((flags & PercentValue) != 0)
581 {
cristy65d4e5e2012-10-17 12:22:24 +0000582 threshold.red*=(MagickRealType) (QuantumRange/100.0);
583 threshold.green*=(MagickRealType) (QuantumRange/100.0);
584 threshold.blue*=(MagickRealType) (QuantumRange/100.0);
585 threshold.black*=(MagickRealType) (QuantumRange/100.0);
586 threshold.alpha*=(MagickRealType) (QuantumRange/100.0);
cristyd6803382012-04-10 01:41:25 +0000587 }
cristy3ed852e2009-09-05 21:47:34 +0000588 /*
cristy5f9f2462011-09-28 23:37:58 +0000589 White threshold image.
cristy3ed852e2009-09-05 21:47:34 +0000590 */
591 status=MagickTrue;
592 progress=0;
cristy46ff2672012-12-14 15:32:26 +0000593 image_view=AcquireAuthenticCacheView(image,exception);
cristyb5d5f722009-11-04 03:03:49 +0000594#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd065992012-08-09 15:27:39 +0000595 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +0000596 magick_threads(image,image,image->rows,1)
cristy3ed852e2009-09-05 21:47:34 +0000597#endif
cristybb503372010-05-27 20:51:26 +0000598 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000599 {
cristybb503372010-05-27 20:51:26 +0000600 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000601 x;
602
cristy4c08aed2011-07-01 19:47:50 +0000603 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000604 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000605
606 if (status == MagickFalse)
607 continue;
608 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000609 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000610 {
611 status=MagickFalse;
612 continue;
613 }
cristybb503372010-05-27 20:51:26 +0000614 for (x=0; x < (ssize_t) image->columns; x++)
cristyb0ea1af2009-11-28 20:44:46 +0000615 {
cristy81629aa2012-07-12 20:08:52 +0000616 double
617 pixel;
618
cristyc4567182012-06-24 20:55:08 +0000619 register ssize_t
620 i;
621
cristy883fde12013-04-08 00:50:13 +0000622 if (GetPixelReadMask(image,q) == 0)
cristy10a6c612012-01-29 21:41:05 +0000623 {
624 q+=GetPixelChannels(image);
625 continue;
626 }
cristyf13c5942012-08-08 23:50:11 +0000627 pixel=GetPixelIntensity(image,q);
cristy188f29a2012-06-24 19:09:53 +0000628 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
629 {
cristy5a23c552013-02-13 14:34:28 +0000630 PixelChannel channel=GetPixelChannelChannel(image,i);
631 PixelTrait traits=GetPixelChannelTraits(image,channel);
cristy188f29a2012-06-24 19:09:53 +0000632 if ((traits & UpdatePixelTrait) == 0)
633 continue;
cristy81629aa2012-07-12 20:08:52 +0000634 if (image->channel_mask != DefaultChannels)
cristyf13c5942012-08-08 23:50:11 +0000635 pixel=(double) q[i];
cristy81629aa2012-07-12 20:08:52 +0000636 if (pixel <= GetPixelInfoChannel(&threshold,channel))
cristy525cf962012-09-09 15:00:40 +0000637 q[i]=(Quantum) 0;
cristy188f29a2012-06-24 19:09:53 +0000638 }
cristyed231572011-07-14 02:18:59 +0000639 q+=GetPixelChannels(image);
cristyb0ea1af2009-11-28 20:44:46 +0000640 }
cristy3ed852e2009-09-05 21:47:34 +0000641 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
642 status=MagickFalse;
643 if (image->progress_monitor != (MagickProgressMonitor) NULL)
644 {
645 MagickBooleanType
646 proceed;
647
cristyb5d5f722009-11-04 03:03:49 +0000648#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristya12d8ba2012-04-29 16:33:41 +0000649 #pragma omp critical (MagickCore_BlackThresholdImage)
cristy3ed852e2009-09-05 21:47:34 +0000650#endif
651 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
652 image->rows);
653 if (proceed == MagickFalse)
654 status=MagickFalse;
655 }
656 }
657 image_view=DestroyCacheView(image_view);
658 return(status);
659}
660
661/*
662%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
663% %
664% %
665% %
cristy1eb45dd2009-09-25 16:38:06 +0000666% C l a m p I m a g e %
667% %
668% %
669% %
670%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
671%
cristy7884a932012-11-04 14:33:51 +0000672% ClampImage() set each pixel whose value is below zero to zero and any the
673% pixel whose value is above the quantum range to the quantum range (e.g.
674% 65535) otherwise the pixel value remains unchanged.
cristy1eb45dd2009-09-25 16:38:06 +0000675%
cristyf4ad9df2011-07-08 16:49:03 +0000676% The format of the ClampImage method is:
cristy1eb45dd2009-09-25 16:38:06 +0000677%
cristy092d71c2011-10-14 18:01:29 +0000678% MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
cristy1eb45dd2009-09-25 16:38:06 +0000679%
680% A description of each parameter follows:
681%
682% o image: the image.
683%
cristy092d71c2011-10-14 18:01:29 +0000684% o exception: return any errors or warnings in this structure.
685%
cristy1eb45dd2009-09-25 16:38:06 +0000686*/
687
cristyddcbde62012-11-06 00:17:10 +0000688static inline Quantum ClampPixel(const MagickRealType value)
689{
690#if !defined(MAGICKCORE_HDRI_SUPPORT)
691 return((Quantum) value);
692#else
cristy6a59acc2012-11-07 23:48:54 +0000693 if (value < 0.0f)
694 return(0.0);
cristyddcbde62012-11-06 00:17:10 +0000695 if (value >= (MagickRealType) QuantumRange)
cristy6a59acc2012-11-07 23:48:54 +0000696 return((Quantum) QuantumRange);
697 return(value);
cristyddcbde62012-11-06 00:17:10 +0000698#endif
699}
700
cristy092d71c2011-10-14 18:01:29 +0000701MagickExport MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
cristy1eb45dd2009-09-25 16:38:06 +0000702{
cristy1eb45dd2009-09-25 16:38:06 +0000703#define ClampImageTag "Clamp/Image"
704
cristyc4c8d132010-01-07 01:58:38 +0000705 CacheView
706 *image_view;
707
cristy1eb45dd2009-09-25 16:38:06 +0000708 MagickBooleanType
709 status;
710
cristy5f959472010-05-27 22:19:46 +0000711 MagickOffsetType
712 progress;
713
714 ssize_t
715 y;
716
cristy1eb45dd2009-09-25 16:38:06 +0000717 assert(image != (Image *) NULL);
718 assert(image->signature == MagickSignature);
719 if (image->debug != MagickFalse)
720 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
721 if (image->storage_class == PseudoClass)
722 {
cristybb503372010-05-27 20:51:26 +0000723 register ssize_t
cristy1eb45dd2009-09-25 16:38:06 +0000724 i;
725
cristy101ab702011-10-13 13:06:32 +0000726 register PixelInfo
cristyc47d1f82009-11-26 01:44:43 +0000727 *restrict q;
cristy1eb45dd2009-09-25 16:38:06 +0000728
729 q=image->colormap;
cristybb503372010-05-27 20:51:26 +0000730 for (i=0; i < (ssize_t) image->colors; i++)
cristy1eb45dd2009-09-25 16:38:06 +0000731 {
cristyddcbde62012-11-06 00:17:10 +0000732 q->red=(double) ClampPixel(q->red);
733 q->green=(double) ClampPixel(q->green);
734 q->blue=(double) ClampPixel(q->blue);
735 q->alpha=(double) ClampPixel(q->alpha);
cristy1eb45dd2009-09-25 16:38:06 +0000736 q++;
737 }
cristyea1a8aa2011-10-20 13:24:06 +0000738 return(SyncImage(image,exception));
cristy1eb45dd2009-09-25 16:38:06 +0000739 }
740 /*
cristy611721d2009-09-25 16:42:17 +0000741 Clamp image.
cristy1eb45dd2009-09-25 16:38:06 +0000742 */
743 status=MagickTrue;
744 progress=0;
cristy46ff2672012-12-14 15:32:26 +0000745 image_view=AcquireAuthenticCacheView(image,exception);
cristyb5d5f722009-11-04 03:03:49 +0000746#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd065992012-08-09 15:27:39 +0000747 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +0000748 magick_threads(image,image,image->rows,1)
cristy1eb45dd2009-09-25 16:38:06 +0000749#endif
cristybb503372010-05-27 20:51:26 +0000750 for (y=0; y < (ssize_t) image->rows; y++)
cristy1eb45dd2009-09-25 16:38:06 +0000751 {
cristybb503372010-05-27 20:51:26 +0000752 register ssize_t
cristy1eb45dd2009-09-25 16:38:06 +0000753 x;
754
cristy4c08aed2011-07-01 19:47:50 +0000755 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000756 *restrict q;
cristy1eb45dd2009-09-25 16:38:06 +0000757
758 if (status == MagickFalse)
759 continue;
760 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000761 if (q == (Quantum *) NULL)
cristy1eb45dd2009-09-25 16:38:06 +0000762 {
763 status=MagickFalse;
764 continue;
765 }
cristybb503372010-05-27 20:51:26 +0000766 for (x=0; x < (ssize_t) image->columns; x++)
cristy1eb45dd2009-09-25 16:38:06 +0000767 {
cristy5f9f2462011-09-28 23:37:58 +0000768 register ssize_t
769 i;
770
cristy883fde12013-04-08 00:50:13 +0000771 if (GetPixelReadMask(image,q) == 0)
cristy10a6c612012-01-29 21:41:05 +0000772 {
773 q+=GetPixelChannels(image);
774 continue;
775 }
cristy5f9f2462011-09-28 23:37:58 +0000776 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
777 {
cristy5a23c552013-02-13 14:34:28 +0000778 PixelChannel channel=GetPixelChannelChannel(image,i);
779 PixelTrait traits=GetPixelChannelTraits(image,channel);
cristy454bb022013-04-23 13:03:38 +0000780 if ((traits & UpdatePixelTrait) == 0)
cristy5f9f2462011-09-28 23:37:58 +0000781 continue;
cristyddcbde62012-11-06 00:17:10 +0000782 q[i]=ClampPixel(q[i]);
cristy5f9f2462011-09-28 23:37:58 +0000783 }
cristyed231572011-07-14 02:18:59 +0000784 q+=GetPixelChannels(image);
cristy1eb45dd2009-09-25 16:38:06 +0000785 }
786 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
787 status=MagickFalse;
788 if (image->progress_monitor != (MagickProgressMonitor) NULL)
789 {
790 MagickBooleanType
791 proceed;
792
cristyb5d5f722009-11-04 03:03:49 +0000793#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000794 #pragma omp critical (MagickCore_ClampImage)
cristy1eb45dd2009-09-25 16:38:06 +0000795#endif
cristyaa17cde2012-06-04 23:43:42 +0000796 proceed=SetImageProgress(image,ClampImageTag,progress++,image->rows);
cristy1eb45dd2009-09-25 16:38:06 +0000797 if (proceed == MagickFalse)
798 status=MagickFalse;
799 }
800 }
801 image_view=DestroyCacheView(image_view);
802 return(status);
803}
804
805/*
806%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
807% %
808% %
809% %
cristy3ed852e2009-09-05 21:47:34 +0000810% D e s t r o y T h r e s h o l d M a p %
811% %
812% %
813% %
814%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
815%
816% DestroyThresholdMap() de-allocate the given ThresholdMap
817%
818% The format of the ListThresholdMaps method is:
819%
820% ThresholdMap *DestroyThresholdMap(Threshold *map)
821%
822% A description of each parameter follows.
823%
824% o map: Pointer to the Threshold map to destroy
825%
826*/
827MagickExport ThresholdMap *DestroyThresholdMap(ThresholdMap *map)
828{
829 assert(map != (ThresholdMap *) NULL);
830 if (map->map_id != (char *) NULL)
831 map->map_id=DestroyString(map->map_id);
832 if (map->description != (char *) NULL)
833 map->description=DestroyString(map->description);
cristybb503372010-05-27 20:51:26 +0000834 if (map->levels != (ssize_t *) NULL)
835 map->levels=(ssize_t *) RelinquishMagickMemory(map->levels);
cristy3ed852e2009-09-05 21:47:34 +0000836 map=(ThresholdMap *) RelinquishMagickMemory(map);
837 return(map);
838}
839
840/*
841%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
842% %
843% %
844% %
cristyb9eb87b2011-09-29 01:15:19 +0000845% G e t T h r e s h o l d M a p %
846% %
847% %
848% %
849%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
850%
851% GetThresholdMap() loads and searches one or more threshold map files for the
852% map matching the given name or alias.
853%
854% The format of the GetThresholdMap method is:
855%
856% ThresholdMap *GetThresholdMap(const char *map_id,
857% ExceptionInfo *exception)
858%
859% A description of each parameter follows.
860%
861% o map_id: ID of the map to look for.
862%
863% o exception: return any errors or warnings in this structure.
864%
865*/
866MagickExport ThresholdMap *GetThresholdMap(const char *map_id,
867 ExceptionInfo *exception)
868{
869 const StringInfo
870 *option;
871
872 LinkedListInfo
873 *options;
874
875 ThresholdMap
876 *map;
877
cristyda9d0e42013-02-26 00:50:38 +0000878 map=GetThresholdMapFile(MinimalThresholdMap,"built-in",map_id,exception);
879 if (map != (ThresholdMap *) NULL)
880 return(map);
cristyb9eb87b2011-09-29 01:15:19 +0000881 options=GetConfigureOptions(ThresholdsFilename,exception);
cristyda9d0e42013-02-26 00:50:38 +0000882 option=(const StringInfo *) GetNextValueInLinkedList(options);
883 while (option != (const StringInfo *) NULL)
884 {
cristyb9eb87b2011-09-29 01:15:19 +0000885 map=GetThresholdMapFile((const char *) GetStringInfoDatum(option),
886 GetStringInfoPath(option),map_id,exception);
cristyda9d0e42013-02-26 00:50:38 +0000887 if (map != (ThresholdMap *) NULL)
888 return(map);
889 option=(const StringInfo *) GetNextValueInLinkedList(options);
890 }
cristyb9eb87b2011-09-29 01:15:19 +0000891 options=DestroyConfigureOptions(options);
cristyda9d0e42013-02-26 00:50:38 +0000892 return((ThresholdMap *) NULL);
cristyb9eb87b2011-09-29 01:15:19 +0000893}
894
895/*
896%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
897% %
898% %
899% %
cristy3ed852e2009-09-05 21:47:34 +0000900+ G e t T h r e s h o l d M a p F i l e %
901% %
902% %
903% %
904%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
905%
906% GetThresholdMapFile() look for a given threshold map name or alias in the
907% given XML file data, and return the allocated the map when found.
908%
909% The format of the ListThresholdMaps method is:
910%
911% ThresholdMap *GetThresholdMap(const char *xml,const char *filename,
912% const char *map_id,ExceptionInfo *exception)
913%
914% A description of each parameter follows.
915%
916% o xml: The threshold map list in XML format.
917%
918% o filename: The threshold map XML filename.
919%
920% o map_id: ID of the map to look for in XML list.
921%
922% o exception: return any errors or warnings in this structure.
923%
924*/
cristy311eb742013-02-25 19:55:37 +0000925static ThresholdMap *GetThresholdMapFile(const char *xml,const char *filename,
926 const char *map_id,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000927{
cristyb9eb87b2011-09-29 01:15:19 +0000928 char
929 *p;
930
cristy3ed852e2009-09-05 21:47:34 +0000931 const char
cristyb9eb87b2011-09-29 01:15:19 +0000932 *attribute,
cristy3ed852e2009-09-05 21:47:34 +0000933 *content;
934
935 double
936 value;
937
cristyb9eb87b2011-09-29 01:15:19 +0000938 register ssize_t
939 i;
cristy3ed852e2009-09-05 21:47:34 +0000940
941 ThresholdMap
942 *map;
943
cristyb9eb87b2011-09-29 01:15:19 +0000944 XMLTreeInfo
945 *description,
946 *levels,
947 *threshold,
948 *thresholds;
949
950 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
951 "Loading threshold map file \"%s\" ...",filename);
952 map=(ThresholdMap *) NULL;
953 thresholds=NewXMLTree(xml,exception);
954 if (thresholds == (XMLTreeInfo *) NULL)
955 return(map);
956 for (threshold=GetXMLTreeChild(thresholds,"threshold");
957 threshold != (XMLTreeInfo *) NULL;
958 threshold=GetNextXMLTreeTag(threshold))
959 {
960 attribute=GetXMLTreeAttribute(threshold,"map");
961 if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
962 break;
963 attribute=GetXMLTreeAttribute(threshold,"alias");
964 if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
965 break;
966 }
967 if (threshold == (XMLTreeInfo *) NULL)
968 return(map);
969 description=GetXMLTreeChild(threshold,"description");
970 if (description == (XMLTreeInfo *) NULL)
971 {
972 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
973 "XmlMissingElement", "<description>, map \"%s\"",map_id);
974 thresholds=DestroyXMLTree(thresholds);
975 return(map);
976 }
977 levels=GetXMLTreeChild(threshold,"levels");
978 if (levels == (XMLTreeInfo *) NULL)
979 {
980 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
981 "XmlMissingElement", "<levels>, map \"%s\"", map_id);
982 thresholds=DestroyXMLTree(thresholds);
983 return(map);
984 }
985 map=(ThresholdMap *) AcquireMagickMemory(sizeof(ThresholdMap));
986 if (map == (ThresholdMap *) NULL)
987 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
988 map->map_id=(char *) NULL;
989 map->description=(char *) NULL;
990 map->levels=(ssize_t *) NULL;
991 attribute=GetXMLTreeAttribute(threshold,"map");
992 if (attribute != (char *) NULL)
993 map->map_id=ConstantString(attribute);
994 content=GetXMLTreeContent(description);
995 if (content != (char *) NULL)
996 map->description=ConstantString(content);
997 attribute=GetXMLTreeAttribute(levels,"width");
998 if (attribute == (char *) NULL)
999 {
1000 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1001 "XmlMissingAttribute", "<levels width>, map \"%s\"",map_id);
1002 thresholds=DestroyXMLTree(thresholds);
1003 map=DestroyThresholdMap(map);
1004 return(map);
1005 }
1006 map->width=StringToUnsignedLong(attribute);
1007 if (map->width == 0)
1008 {
1009 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1010 "XmlInvalidAttribute", "<levels width>, map \"%s\"",map_id);
1011 thresholds=DestroyXMLTree(thresholds);
1012 map=DestroyThresholdMap(map);
1013 return(map);
1014 }
1015 attribute=GetXMLTreeAttribute(levels,"height");
1016 if (attribute == (char *) NULL)
1017 {
1018 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1019 "XmlMissingAttribute", "<levels height>, map \"%s\"",map_id);
1020 thresholds=DestroyXMLTree(thresholds);
1021 map=DestroyThresholdMap(map);
1022 return(map);
1023 }
1024 map->height=StringToUnsignedLong(attribute);
1025 if (map->height == 0)
1026 {
1027 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1028 "XmlInvalidAttribute", "<levels height>, map \"%s\"",map_id);
1029 thresholds=DestroyXMLTree(thresholds);
1030 map=DestroyThresholdMap(map);
1031 return(map);
1032 }
1033 attribute=GetXMLTreeAttribute(levels,"divisor");
1034 if (attribute == (char *) NULL)
1035 {
1036 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1037 "XmlMissingAttribute", "<levels divisor>, map \"%s\"",map_id);
1038 thresholds=DestroyXMLTree(thresholds);
1039 map=DestroyThresholdMap(map);
1040 return(map);
1041 }
1042 map->divisor=(ssize_t) StringToLong(attribute);
1043 if (map->divisor < 2)
1044 {
1045 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1046 "XmlInvalidAttribute", "<levels divisor>, map \"%s\"",map_id);
1047 thresholds=DestroyXMLTree(thresholds);
1048 map=DestroyThresholdMap(map);
1049 return(map);
1050 }
1051 content=GetXMLTreeContent(levels);
1052 if (content == (char *) NULL)
1053 {
1054 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1055 "XmlMissingContent", "<levels>, map \"%s\"",map_id);
1056 thresholds=DestroyXMLTree(thresholds);
1057 map=DestroyThresholdMap(map);
1058 return(map);
1059 }
1060 map->levels=(ssize_t *) AcquireQuantumMemory((size_t) map->width,map->height*
1061 sizeof(*map->levels));
1062 if (map->levels == (ssize_t *) NULL)
1063 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
1064 for (i=0; i < (ssize_t) (map->width*map->height); i++)
1065 {
1066 map->levels[i]=(ssize_t) strtol(content,&p,10);
1067 if (p == content)
1068 {
1069 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1070 "XmlInvalidContent", "<level> too few values, map \"%s\"",map_id);
1071 thresholds=DestroyXMLTree(thresholds);
1072 map=DestroyThresholdMap(map);
1073 return(map);
1074 }
1075 if ((map->levels[i] < 0) || (map->levels[i] > map->divisor))
1076 {
1077 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1078 "XmlInvalidContent", "<level> %.20g out of range, map \"%s\"",
1079 (double) map->levels[i],map_id);
1080 thresholds=DestroyXMLTree(thresholds);
1081 map=DestroyThresholdMap(map);
1082 return(map);
1083 }
1084 content=p;
1085 }
1086 value=(double) strtol(content,&p,10);
1087 (void) value;
1088 if (p != content)
1089 {
1090 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1091 "XmlInvalidContent", "<level> too many values, map \"%s\"",map_id);
1092 thresholds=DestroyXMLTree(thresholds);
1093 map=DestroyThresholdMap(map);
1094 return(map);
1095 }
1096 thresholds=DestroyXMLTree(thresholds);
cristy3ed852e2009-09-05 21:47:34 +00001097 return(map);
1098}
1099
1100/*
1101%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1102% %
1103% %
1104% %
1105+ L i s t T h r e s h o l d M a p F i l e %
1106% %
1107% %
1108% %
1109%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1110%
1111% ListThresholdMapFile() lists the threshold maps and their descriptions
1112% in the given XML file data.
1113%
1114% The format of the ListThresholdMaps method is:
1115%
1116% MagickBooleanType ListThresholdMaps(FILE *file,const char*xml,
1117% const char *filename,ExceptionInfo *exception)
1118%
1119% A description of each parameter follows.
1120%
1121% o file: An pointer to the output FILE.
1122%
1123% o xml: The threshold map list in XML format.
1124%
1125% o filename: The threshold map XML filename.
1126%
1127% o exception: return any errors or warnings in this structure.
1128%
1129*/
1130MagickBooleanType ListThresholdMapFile(FILE *file,const char *xml,
1131 const char *filename,ExceptionInfo *exception)
1132{
cristy5f9f2462011-09-28 23:37:58 +00001133 const char
1134 *alias,
1135 *content,
1136 *map;
1137
1138 XMLTreeInfo
1139 *description,
1140 *threshold,
1141 *thresholds;
cristy3ed852e2009-09-05 21:47:34 +00001142
1143 assert( xml != (char *)NULL );
1144 assert( file != (FILE *)NULL );
cristy3ed852e2009-09-05 21:47:34 +00001145 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1146 "Loading threshold map file \"%s\" ...",filename);
1147 thresholds=NewXMLTree(xml,exception);
1148 if ( thresholds == (XMLTreeInfo *)NULL )
1149 return(MagickFalse);
cristy1e604812011-05-19 18:07:50 +00001150 (void) FormatLocaleFile(file,"%-16s %-12s %s\n","Map","Alias","Description");
1151 (void) FormatLocaleFile(file,
1152 "----------------------------------------------------\n");
cristy5f9f2462011-09-28 23:37:58 +00001153 threshold=GetXMLTreeChild(thresholds,"threshold");
cristyb9eb87b2011-09-29 01:15:19 +00001154 for ( ; threshold != (XMLTreeInfo *) NULL;
1155 threshold=GetNextXMLTreeTag(threshold))
cristy3ed852e2009-09-05 21:47:34 +00001156 {
cristy5f9f2462011-09-28 23:37:58 +00001157 map=GetXMLTreeAttribute(threshold,"map");
1158 if (map == (char *) NULL)
1159 {
1160 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1161 "XmlMissingAttribute", "<map>");
1162 thresholds=DestroyXMLTree(thresholds);
1163 return(MagickFalse);
1164 }
1165 alias=GetXMLTreeAttribute(threshold,"alias");
cristy3ed852e2009-09-05 21:47:34 +00001166 description=GetXMLTreeChild(threshold,"description");
cristy5f9f2462011-09-28 23:37:58 +00001167 if (description == (XMLTreeInfo *) NULL)
1168 {
1169 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
cristyb9eb87b2011-09-29 01:15:19 +00001170 "XmlMissingElement", "<description>, map \"%s\"",map);
cristy5f9f2462011-09-28 23:37:58 +00001171 thresholds=DestroyXMLTree(thresholds);
1172 return(MagickFalse);
1173 }
cristy3ed852e2009-09-05 21:47:34 +00001174 content=GetXMLTreeContent(description);
cristy5f9f2462011-09-28 23:37:58 +00001175 if (content == (char *) NULL)
1176 {
1177 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1178 "XmlMissingContent", "<description>, map \"%s\"", map);
1179 thresholds=DestroyXMLTree(thresholds);
1180 return(MagickFalse);
1181 }
cristy1e604812011-05-19 18:07:50 +00001182 (void) FormatLocaleFile(file,"%-16s %-12s %s\n",map,alias ? alias : "",
1183 content);
cristy3ed852e2009-09-05 21:47:34 +00001184 }
1185 thresholds=DestroyXMLTree(thresholds);
1186 return(MagickTrue);
1187}
1188
1189/*
1190%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1191% %
1192% %
1193% %
1194% L i s t T h r e s h o l d M a p s %
1195% %
1196% %
1197% %
1198%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1199%
1200% ListThresholdMaps() lists the threshold maps and their descriptions
1201% as defined by "threshold.xml" to a file.
1202%
1203% The format of the ListThresholdMaps method is:
1204%
1205% MagickBooleanType ListThresholdMaps(FILE *file,ExceptionInfo *exception)
1206%
1207% A description of each parameter follows.
1208%
1209% o file: An pointer to the output FILE.
1210%
1211% o exception: return any errors or warnings in this structure.
1212%
1213*/
1214MagickExport MagickBooleanType ListThresholdMaps(FILE *file,
1215 ExceptionInfo *exception)
1216{
1217 const StringInfo
1218 *option;
1219
1220 LinkedListInfo
1221 *options;
1222
1223 MagickStatusType
1224 status;
1225
1226 status=MagickFalse;
cristy5f9f2462011-09-28 23:37:58 +00001227 if (file == (FILE *) NULL)
1228 file=stdout;
cristy3ed852e2009-09-05 21:47:34 +00001229 options=GetConfigureOptions(ThresholdsFilename,exception);
cristy1e604812011-05-19 18:07:50 +00001230 (void) FormatLocaleFile(file,
1231 "\n Threshold Maps for Ordered Dither Operations\n");
cristy1d755c42013-02-26 12:38:44 +00001232 option=(const StringInfo *) GetNextValueInLinkedList(options);
1233 while (option != (const StringInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001234 {
cristy1d755c42013-02-26 12:38:44 +00001235 (void) FormatLocaleFile(file,"\nPath: %s\n\n",GetStringInfoPath(option));
cristy3ed852e2009-09-05 21:47:34 +00001236 status|=ListThresholdMapFile(file,(const char *) GetStringInfoDatum(option),
1237 GetStringInfoPath(option),exception);
cristy1d755c42013-02-26 12:38:44 +00001238 option=(const StringInfo *) GetNextValueInLinkedList(options);
cristy3ed852e2009-09-05 21:47:34 +00001239 }
1240 options=DestroyConfigureOptions(options);
1241 return(status != 0 ? MagickTrue : MagickFalse);
1242}
1243
1244/*
1245%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1246% %
1247% %
1248% %
cristy3ed852e2009-09-05 21:47:34 +00001249% O r d e r e d P o s t e r i z e I m a g e %
1250% %
1251% %
1252% %
1253%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1254%
1255% OrderedPosterizeImage() will perform a ordered dither based on a number
1256% of pre-defined dithering threshold maps, but over multiple intensity
1257% levels, which can be different for different channels, according to the
1258% input argument.
1259%
1260% The format of the OrderedPosterizeImage method is:
1261%
1262% MagickBooleanType OrderedPosterizeImage(Image *image,
1263% const char *threshold_map,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001264%
1265% A description of each parameter follows:
1266%
1267% o image: the image.
1268%
cristy3ed852e2009-09-05 21:47:34 +00001269% o threshold_map: A string containing the name of the threshold dither
1270% map to use, followed by zero or more numbers representing the number
1271% of color levels tho dither between.
1272%
cristyf998fb32011-04-27 23:00:47 +00001273% Any level number less than 2 will be equivalent to 2, and means only
cristy3ed852e2009-09-05 21:47:34 +00001274% binary dithering will be applied to each color channel.
1275%
1276% No numbers also means a 2 level (bitmap) dither will be applied to all
1277% channels, while a single number is the number of levels applied to each
1278% channel in sequence. More numbers will be applied in turn to each of
1279% the color channels.
1280%
1281% For example: "o3x3,6" will generate a 6 level posterization of the
1282% image with a ordered 3x3 diffused pixel dither being applied between
1283% each level. While checker,8,8,4 will produce a 332 colormaped image
1284% with only a single checkerboard hash pattern (50% grey) between each
1285% color level, to basically double the number of color levels with
1286% a bare minimim of dithering.
1287%
1288% o exception: return any errors or warnings in this structure.
1289%
1290*/
1291MagickExport MagickBooleanType OrderedPosterizeImage(Image *image,
1292 const char *threshold_map,ExceptionInfo *exception)
1293{
cristy3ed852e2009-09-05 21:47:34 +00001294#define DitherImageTag "Dither/Image"
1295
cristyc4c8d132010-01-07 01:58:38 +00001296 CacheView
1297 *image_view;
1298
cristy4e0b82a2011-09-29 12:47:44 +00001299 char
1300 token[MaxTextExtent];
1301
1302 const char
1303 *p;
cristy3ed852e2009-09-05 21:47:34 +00001304
cristy311eb742013-02-25 19:55:37 +00001305 double
1306 levels[CompositePixelChannel];
1307
cristy3ed852e2009-09-05 21:47:34 +00001308 MagickBooleanType
1309 status;
1310
cristy5f959472010-05-27 22:19:46 +00001311 MagickOffsetType
1312 progress;
1313
cristy4e0b82a2011-09-29 12:47:44 +00001314 register ssize_t
1315 i;
1316
cristy5f959472010-05-27 22:19:46 +00001317 ssize_t
1318 y;
1319
cristy3ed852e2009-09-05 21:47:34 +00001320 ThresholdMap
1321 *map;
1322
cristy3ed852e2009-09-05 21:47:34 +00001323 assert(image != (Image *) NULL);
1324 assert(image->signature == MagickSignature);
1325 if (image->debug != MagickFalse)
1326 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1327 assert(exception != (ExceptionInfo *) NULL);
1328 assert(exception->signature == MagickSignature);
1329 if (threshold_map == (const char *) NULL)
1330 return(MagickTrue);
cristy4e0b82a2011-09-29 12:47:44 +00001331 p=(char *) threshold_map;
1332 while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
1333 (*p != '\0'))
1334 p++;
1335 threshold_map=p;
1336 while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
1337 (*p != '\0'))
cristy3ed852e2009-09-05 21:47:34 +00001338 {
cristy4e0b82a2011-09-29 12:47:44 +00001339 if ((p-threshold_map) >= (MaxTextExtent-1))
1340 break;
1341 token[p-threshold_map]=(*p);
1342 p++;
cristy3ed852e2009-09-05 21:47:34 +00001343 }
cristy4e0b82a2011-09-29 12:47:44 +00001344 token[p-threshold_map]='\0';
1345 map=GetThresholdMap(token,exception);
1346 if (map == (ThresholdMap *) NULL)
1347 {
1348 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1349 "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
cristy574cc262011-08-05 01:23:58 +00001350 return(MagickFalse);
cristy4e0b82a2011-09-29 12:47:44 +00001351 }
1352 for (i=0; i < MaxPixelChannels; i++)
1353 levels[i]=2.0;
1354 p=strchr((char *) threshold_map,',');
1355 if ((p != (char *) NULL) && (isdigit((int) ((unsigned char) *(++p))) != 0))
1356 for (i=0; (*p != '\0') && (i < MaxPixelChannels); i++)
1357 {
1358 GetMagickToken(p,&p,token);
1359 if (*token == ',')
1360 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00001361 levels[i]=StringToDouble(token,(char **) NULL);
cristy4e0b82a2011-09-29 12:47:44 +00001362 }
1363 for (i=0; i < MaxPixelChannels; i++)
1364 if (fabs(levels[i]) >= 1)
1365 levels[i]-=1.0;
1366 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1367 return(MagickFalse);
1368 status=MagickTrue;
1369 progress=0;
cristy46ff2672012-12-14 15:32:26 +00001370 image_view=AcquireAuthenticCacheView(image,exception);
cristyb5d5f722009-11-04 03:03:49 +00001371#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd065992012-08-09 15:27:39 +00001372 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +00001373 magick_threads(image,image,image->rows,1)
cristy3ed852e2009-09-05 21:47:34 +00001374#endif
cristy4e0b82a2011-09-29 12:47:44 +00001375 for (y=0; y < (ssize_t) image->rows; y++)
1376 {
1377 register ssize_t
1378 x;
1379
1380 register Quantum
1381 *restrict q;
1382
1383 if (status == MagickFalse)
1384 continue;
1385 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1386 if (q == (Quantum *) NULL)
1387 {
1388 status=MagickFalse;
1389 continue;
1390 }
1391 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001392 {
cristybb503372010-05-27 20:51:26 +00001393 register ssize_t
cristy4e0b82a2011-09-29 12:47:44 +00001394 i;
cristy3ed852e2009-09-05 21:47:34 +00001395
cristy4e0b82a2011-09-29 12:47:44 +00001396 ssize_t
1397 n;
cristy3ed852e2009-09-05 21:47:34 +00001398
cristy4e0b82a2011-09-29 12:47:44 +00001399 n=0;
cristy883fde12013-04-08 00:50:13 +00001400 if (GetPixelReadMask(image,q) == 0)
cristy10a6c612012-01-29 21:41:05 +00001401 {
1402 q+=GetPixelChannels(image);
1403 continue;
1404 }
cristy4e0b82a2011-09-29 12:47:44 +00001405 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristy3ed852e2009-09-05 21:47:34 +00001406 {
cristy4e0b82a2011-09-29 12:47:44 +00001407 ssize_t
cristy3f5d8152011-09-29 13:00:19 +00001408 level,
1409 threshold;
cristy3ed852e2009-09-05 21:47:34 +00001410
cristy5a23c552013-02-13 14:34:28 +00001411 PixelChannel channel=GetPixelChannelChannel(image,i);
1412 PixelTrait traits=GetPixelChannelTraits(image,channel);
cristy4e0b82a2011-09-29 12:47:44 +00001413 if ((traits & UpdatePixelTrait) == 0)
1414 continue;
cristy3f5d8152011-09-29 13:00:19 +00001415 if (fabs(levels[n++]) < MagickEpsilon)
1416 continue;
1417 threshold=(ssize_t) (QuantumScale*q[i]*(levels[n]*(map->divisor-1)+1));
1418 level=threshold/(map->divisor-1);
1419 threshold-=level*(map->divisor-1);
cristyada285b2012-07-07 19:00:46 +00001420 q[i]=ClampToQuantum((double) (level+(threshold >=
cristye42f6582012-02-11 17:59:50 +00001421 map->levels[(x % map->width)+map->width*(y % map->height)]))*
1422 QuantumRange/levels[n]);
cristy4e0b82a2011-09-29 12:47:44 +00001423 n++;
cristy3ed852e2009-09-05 21:47:34 +00001424 }
cristy4e0b82a2011-09-29 12:47:44 +00001425 q+=GetPixelChannels(image);
1426 }
1427 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1428 status=MagickFalse;
1429 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1430 {
1431 MagickBooleanType
1432 proceed;
cristy3ed852e2009-09-05 21:47:34 +00001433
cristyb5d5f722009-11-04 03:03:49 +00001434#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00001435 #pragma omp critical (MagickCore_OrderedPosterizeImage)
cristy3ed852e2009-09-05 21:47:34 +00001436#endif
cristy4e0b82a2011-09-29 12:47:44 +00001437 proceed=SetImageProgress(image,DitherImageTag,progress++,image->rows);
1438 if (proceed == MagickFalse)
1439 status=MagickFalse;
1440 }
cristy3ed852e2009-09-05 21:47:34 +00001441 }
cristy4e0b82a2011-09-29 12:47:44 +00001442 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00001443 map=DestroyThresholdMap(map);
1444 return(MagickTrue);
1445}
1446
1447/*
1448%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1449% %
1450% %
1451% %
cristy7884a932012-11-04 14:33:51 +00001452% P e r c e p t i b l e I m a g e %
1453% %
1454% %
1455% %
1456%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1457%
cristy1d755c42013-02-26 12:38:44 +00001458% PerceptibleImage() set each pixel whose value is less than |epsilon| to
1459% epsilon or -epsilon (whichever is closer) otherwise the pixel value remains
cristy7884a932012-11-04 14:33:51 +00001460% unchanged.
1461%
1462% The format of the PerceptibleImage method is:
1463%
1464% MagickBooleanType PerceptibleImage(Image *image,const double epsilon,
1465% ExceptionInfo *exception)
1466%
1467% A description of each parameter follows:
1468%
1469% o image: the image.
1470%
1471% o epsilon: the epsilon threshold (e.g. 1.0e-9).
1472%
1473% o exception: return any errors or warnings in this structure.
1474%
1475*/
1476
1477static inline Quantum PerceptibleThreshold(const Quantum quantum,
1478 const double epsilon)
1479{
1480 double
1481 sign;
1482
1483 sign=(double) quantum < 0.0 ? -1.0 : 1.0;
1484 if ((sign*quantum) >= epsilon)
1485 return(quantum);
1486 return((Quantum) (sign*epsilon));
1487}
1488
1489MagickExport MagickBooleanType PerceptibleImage(Image *image,
1490 const double epsilon,ExceptionInfo *exception)
1491{
1492#define PerceptibleImageTag "Perceptible/Image"
1493
1494 CacheView
1495 *image_view;
1496
1497 MagickBooleanType
1498 status;
1499
1500 MagickOffsetType
1501 progress;
1502
1503 ssize_t
1504 y;
1505
1506 assert(image != (Image *) NULL);
1507 assert(image->signature == MagickSignature);
1508 if (image->debug != MagickFalse)
1509 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1510 if (image->storage_class == PseudoClass)
1511 {
1512 register ssize_t
1513 i;
1514
1515 register PixelInfo
1516 *restrict q;
1517
1518 q=image->colormap;
1519 for (i=0; i < (ssize_t) image->colors; i++)
1520 {
1521 q->red=(double) PerceptibleThreshold(ClampToQuantum(q->red),
1522 epsilon);
1523 q->green=(double) PerceptibleThreshold(ClampToQuantum(q->green),
1524 epsilon);
1525 q->blue=(double) PerceptibleThreshold(ClampToQuantum(q->blue),
1526 epsilon);
1527 q->alpha=(double) PerceptibleThreshold(ClampToQuantum(q->alpha),
1528 epsilon);
1529 q++;
1530 }
1531 return(SyncImage(image,exception));
1532 }
1533 /*
1534 Perceptible image.
1535 */
1536 status=MagickTrue;
1537 progress=0;
cristy46ff2672012-12-14 15:32:26 +00001538 image_view=AcquireAuthenticCacheView(image,exception);
cristy7884a932012-11-04 14:33:51 +00001539#if defined(MAGICKCORE_OPENMP_SUPPORT)
1540 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +00001541 magick_threads(image,image,image->rows,1)
cristy7884a932012-11-04 14:33:51 +00001542#endif
1543 for (y=0; y < (ssize_t) image->rows; y++)
1544 {
1545 register ssize_t
1546 x;
1547
1548 register Quantum
1549 *restrict q;
1550
1551 if (status == MagickFalse)
1552 continue;
1553 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1554 if (q == (Quantum *) NULL)
1555 {
1556 status=MagickFalse;
1557 continue;
1558 }
1559 for (x=0; x < (ssize_t) image->columns; x++)
1560 {
1561 register ssize_t
1562 i;
1563
cristy883fde12013-04-08 00:50:13 +00001564 if (GetPixelReadMask(image,q) == 0)
cristy7884a932012-11-04 14:33:51 +00001565 {
1566 q+=GetPixelChannels(image);
1567 continue;
1568 }
1569 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1570 {
cristy5a23c552013-02-13 14:34:28 +00001571 PixelChannel channel=GetPixelChannelChannel(image,i);
1572 PixelTrait traits=GetPixelChannelTraits(image,channel);
cristy7884a932012-11-04 14:33:51 +00001573 if (traits == UndefinedPixelTrait)
1574 continue;
1575 q[i]=PerceptibleThreshold(q[i],epsilon);
1576 }
1577 q+=GetPixelChannels(image);
1578 }
1579 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1580 status=MagickFalse;
1581 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1582 {
1583 MagickBooleanType
1584 proceed;
1585
1586#if defined(MAGICKCORE_OPENMP_SUPPORT)
1587 #pragma omp critical (MagickCore_PerceptibleImage)
1588#endif
1589 proceed=SetImageProgress(image,PerceptibleImageTag,progress++,image->rows);
1590 if (proceed == MagickFalse)
1591 status=MagickFalse;
1592 }
1593 }
1594 image_view=DestroyCacheView(image_view);
1595 return(status);
1596}
1597
1598/*
1599%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1600% %
1601% %
1602% %
cristy3ed852e2009-09-05 21:47:34 +00001603% R a n d o m T h r e s h o l d I m a g e %
1604% %
1605% %
1606% %
1607%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1608%
1609% RandomThresholdImage() changes the value of individual pixels based on the
1610% intensity of each pixel compared to a random threshold. The result is a
1611% low-contrast, two color image.
1612%
1613% The format of the RandomThresholdImage method is:
1614%
cristyf4ad9df2011-07-08 16:49:03 +00001615% MagickBooleanType RandomThresholdImage(Image *image,
cristy3ed852e2009-09-05 21:47:34 +00001616% const char *thresholds,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001617%
1618% A description of each parameter follows:
1619%
1620% o image: the image.
1621%
cristy3ed852e2009-09-05 21:47:34 +00001622% o thresholds: a geometry string containing low,high thresholds. If the
1623% string contains 2x2, 3x3, or 4x4, an ordered dither of order 2, 3, or 4
1624% is performed instead.
1625%
1626% o exception: return any errors or warnings in this structure.
1627%
1628*/
cristy3ed852e2009-09-05 21:47:34 +00001629MagickExport MagickBooleanType RandomThresholdImage(Image *image,
1630 const char *thresholds,ExceptionInfo *exception)
1631{
cristy3ed852e2009-09-05 21:47:34 +00001632#define ThresholdImageTag "Threshold/Image"
1633
cristyfa112112010-01-04 17:48:07 +00001634 CacheView
1635 *image_view;
1636
cristy311eb742013-02-25 19:55:37 +00001637 double
1638 min_threshold,
1639 max_threshold;
1640
cristy3ed852e2009-09-05 21:47:34 +00001641 GeometryInfo
1642 geometry_info;
1643
1644 MagickStatusType
1645 flags;
1646
cristy3ed852e2009-09-05 21:47:34 +00001647 MagickBooleanType
1648 status;
1649
cristy5f959472010-05-27 22:19:46 +00001650 MagickOffsetType
1651 progress;
1652
cristy4c08aed2011-07-01 19:47:50 +00001653 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001654 threshold;
1655
cristy3ed852e2009-09-05 21:47:34 +00001656 RandomInfo
cristyfa112112010-01-04 17:48:07 +00001657 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +00001658
cristy5f959472010-05-27 22:19:46 +00001659 ssize_t
1660 y;
1661
glennrpb36143f2012-09-24 18:26:55 +00001662#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy57340e02012-05-05 00:53:23 +00001663 unsigned long
1664 key;
glennrpb36143f2012-09-24 18:26:55 +00001665#endif
cristy57340e02012-05-05 00:53:23 +00001666
cristy3ed852e2009-09-05 21:47:34 +00001667 assert(image != (Image *) NULL);
1668 assert(image->signature == MagickSignature);
1669 if (image->debug != MagickFalse)
1670 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1671 assert(exception != (ExceptionInfo *) NULL);
1672 assert(exception->signature == MagickSignature);
1673 if (thresholds == (const char *) NULL)
1674 return(MagickTrue);
cristye7452652012-04-14 01:34:21 +00001675 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1676 return(MagickFalse);
cristy4c08aed2011-07-01 19:47:50 +00001677 GetPixelInfo(image,&threshold);
cristy3ed852e2009-09-05 21:47:34 +00001678 min_threshold=0.0;
cristya19f1d72012-08-07 18:24:38 +00001679 max_threshold=(double) QuantumRange;
cristy3ed852e2009-09-05 21:47:34 +00001680 flags=ParseGeometry(thresholds,&geometry_info);
1681 min_threshold=geometry_info.rho;
1682 max_threshold=geometry_info.sigma;
1683 if ((flags & SigmaValue) == 0)
1684 max_threshold=min_threshold;
1685 if (strchr(thresholds,'%') != (char *) NULL)
1686 {
cristya19f1d72012-08-07 18:24:38 +00001687 max_threshold*=(double) (0.01*QuantumRange);
1688 min_threshold*=(double) (0.01*QuantumRange);
cristy3ed852e2009-09-05 21:47:34 +00001689 }
cristy3ed852e2009-09-05 21:47:34 +00001690 /*
1691 Random threshold image.
1692 */
1693 status=MagickTrue;
1694 progress=0;
cristy3ed852e2009-09-05 21:47:34 +00001695 random_info=AcquireRandomInfoThreadSet();
glennrpb36143f2012-09-24 18:26:55 +00001696#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy57340e02012-05-05 00:53:23 +00001697 key=GetRandomSecretKey(random_info[0]);
glennrpb36143f2012-09-24 18:26:55 +00001698#endif
cristy46ff2672012-12-14 15:32:26 +00001699 image_view=AcquireAuthenticCacheView(image,exception);
cristyb5d5f722009-11-04 03:03:49 +00001700#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd065992012-08-09 15:27:39 +00001701 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +00001702 magick_threads(image,image,image->rows,key == ~0UL)
cristy3ed852e2009-09-05 21:47:34 +00001703#endif
cristybb503372010-05-27 20:51:26 +00001704 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001705 {
cristy5c9e6f22010-09-17 17:31:01 +00001706 const int
1707 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00001708
cristy4c08aed2011-07-01 19:47:50 +00001709 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001710 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001711
cristy5c9e6f22010-09-17 17:31:01 +00001712 register ssize_t
1713 x;
1714
cristy3ed852e2009-09-05 21:47:34 +00001715 if (status == MagickFalse)
1716 continue;
1717 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001718 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001719 {
1720 status=MagickFalse;
1721 continue;
1722 }
cristybb503372010-05-27 20:51:26 +00001723 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001724 {
cristy5f9f2462011-09-28 23:37:58 +00001725 register ssize_t
1726 i;
1727
cristy883fde12013-04-08 00:50:13 +00001728 if (GetPixelReadMask(image,q) == 0)
cristy10a6c612012-01-29 21:41:05 +00001729 {
1730 q+=GetPixelChannels(image);
1731 continue;
1732 }
cristy5f9f2462011-09-28 23:37:58 +00001733 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1734 {
cristya19f1d72012-08-07 18:24:38 +00001735 double
cristy5f9f2462011-09-28 23:37:58 +00001736 threshold;
1737
cristy5a23c552013-02-13 14:34:28 +00001738 PixelChannel channel=GetPixelChannelChannel(image,i);
1739 PixelTrait traits=GetPixelChannelTraits(image,channel);
cristy5f9f2462011-09-28 23:37:58 +00001740 if ((traits & UpdatePixelTrait) == 0)
1741 continue;
cristya19f1d72012-08-07 18:24:38 +00001742 if ((double) q[i] < min_threshold)
cristy5f9f2462011-09-28 23:37:58 +00001743 threshold=min_threshold;
1744 else
cristya19f1d72012-08-07 18:24:38 +00001745 if ((double) q[i] > max_threshold)
cristy5f9f2462011-09-28 23:37:58 +00001746 threshold=max_threshold;
cristy3ed852e2009-09-05 21:47:34 +00001747 else
cristya19f1d72012-08-07 18:24:38 +00001748 threshold=(double) (QuantumRange*
cristy5f9f2462011-09-28 23:37:58 +00001749 GetPseudoRandomValue(random_info[id]));
cristya19f1d72012-08-07 18:24:38 +00001750 q[i]=(double) q[i] <= threshold ? 0 : QuantumRange;
cristy5f9f2462011-09-28 23:37:58 +00001751 }
cristyed231572011-07-14 02:18:59 +00001752 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001753 }
1754 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1755 status=MagickFalse;
1756 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1757 {
1758 MagickBooleanType
1759 proceed;
1760
cristyb5d5f722009-11-04 03:03:49 +00001761#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00001762 #pragma omp critical (MagickCore_RandomThresholdImage)
cristy3ed852e2009-09-05 21:47:34 +00001763#endif
1764 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1765 image->rows);
1766 if (proceed == MagickFalse)
1767 status=MagickFalse;
1768 }
1769 }
1770 image_view=DestroyCacheView(image_view);
1771 random_info=DestroyRandomInfoThreadSet(random_info);
1772 return(status);
1773}
1774
1775/*
1776%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1777% %
1778% %
1779% %
1780% W h i t e T h r e s h o l d I m a g e %
1781% %
1782% %
1783% %
1784%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1785%
1786% WhiteThresholdImage() is like ThresholdImage() but forces all pixels above
cristy4e101302009-09-17 12:49:12 +00001787% the threshold into white while leaving all pixels at or below the threshold
cristy3ed852e2009-09-05 21:47:34 +00001788% unchanged.
1789%
1790% The format of the WhiteThresholdImage method is:
1791%
cristyf4ad9df2011-07-08 16:49:03 +00001792% MagickBooleanType WhiteThresholdImage(Image *image,
1793% const char *threshold,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001794%
1795% A description of each parameter follows:
1796%
1797% o image: the image.
1798%
cristy3ed852e2009-09-05 21:47:34 +00001799% o threshold: Define the threshold value.
1800%
1801% o exception: return any errors or warnings in this structure.
1802%
1803*/
1804MagickExport MagickBooleanType WhiteThresholdImage(Image *image,
cristyf4ad9df2011-07-08 16:49:03 +00001805 const char *thresholds,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001806{
1807#define ThresholdImageTag "Threshold/Image"
1808
cristy5f959472010-05-27 22:19:46 +00001809 CacheView
1810 *image_view;
1811
cristy3ed852e2009-09-05 21:47:34 +00001812 GeometryInfo
1813 geometry_info;
1814
cristy3ed852e2009-09-05 21:47:34 +00001815 MagickBooleanType
1816 status;
1817
cristy5f959472010-05-27 22:19:46 +00001818 MagickOffsetType
1819 progress;
1820
cristyd6803382012-04-10 01:41:25 +00001821 PixelInfo
cristya12d8ba2012-04-29 16:33:41 +00001822 threshold;
cristy5f9f2462011-09-28 23:37:58 +00001823
cristy3ed852e2009-09-05 21:47:34 +00001824 MagickStatusType
1825 flags;
1826
cristy5f959472010-05-27 22:19:46 +00001827 ssize_t
1828 y;
cristy3ed852e2009-09-05 21:47:34 +00001829
1830 assert(image != (Image *) NULL);
1831 assert(image->signature == MagickSignature);
1832 if (image->debug != MagickFalse)
1833 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1834 if (thresholds == (const char *) NULL)
1835 return(MagickTrue);
cristy574cc262011-08-05 01:23:58 +00001836 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001837 return(MagickFalse);
cristy23e55c02012-04-10 01:21:56 +00001838 if (IsGrayColorspace(image->colorspace) != MagickFalse)
cristy6c312f02013-05-19 22:12:34 +00001839 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristya12d8ba2012-04-29 16:33:41 +00001840 GetPixelInfo(image,&threshold);
cristy3ed852e2009-09-05 21:47:34 +00001841 flags=ParseGeometry(thresholds,&geometry_info);
cristya12d8ba2012-04-29 16:33:41 +00001842 threshold.red=geometry_info.rho;
1843 threshold.green=geometry_info.rho;
1844 threshold.blue=geometry_info.rho;
1845 threshold.black=geometry_info.rho;
1846 threshold.alpha=100.0;
cristy5f9f2462011-09-28 23:37:58 +00001847 if ((flags & SigmaValue) != 0)
cristya12d8ba2012-04-29 16:33:41 +00001848 threshold.green=geometry_info.sigma;
cristy5f9f2462011-09-28 23:37:58 +00001849 if ((flags & XiValue) != 0)
cristya12d8ba2012-04-29 16:33:41 +00001850 threshold.blue=geometry_info.xi;
cristy5f9f2462011-09-28 23:37:58 +00001851 if ((flags & PsiValue) != 0)
cristya12d8ba2012-04-29 16:33:41 +00001852 threshold.alpha=geometry_info.psi;
1853 if (threshold.colorspace == CMYKColorspace)
cristyd6803382012-04-10 01:41:25 +00001854 {
1855 if ((flags & PsiValue) != 0)
cristya12d8ba2012-04-29 16:33:41 +00001856 threshold.black=geometry_info.psi;
cristyd6803382012-04-10 01:41:25 +00001857 if ((flags & ChiValue) != 0)
cristya12d8ba2012-04-29 16:33:41 +00001858 threshold.alpha=geometry_info.chi;
1859 }
1860 if ((flags & PercentValue) != 0)
1861 {
cristy65d4e5e2012-10-17 12:22:24 +00001862 threshold.red*=(MagickRealType) (QuantumRange/100.0);
1863 threshold.green*=(MagickRealType) (QuantumRange/100.0);
1864 threshold.blue*=(MagickRealType) (QuantumRange/100.0);
1865 threshold.black*=(MagickRealType) (QuantumRange/100.0);
1866 threshold.alpha*=(MagickRealType) (QuantumRange/100.0);
cristyd6803382012-04-10 01:41:25 +00001867 }
cristy3ed852e2009-09-05 21:47:34 +00001868 /*
1869 White threshold image.
1870 */
1871 status=MagickTrue;
1872 progress=0;
cristy46ff2672012-12-14 15:32:26 +00001873 image_view=AcquireAuthenticCacheView(image,exception);
cristyb5d5f722009-11-04 03:03:49 +00001874#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd065992012-08-09 15:27:39 +00001875 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +00001876 magick_threads(image,image,image->rows,1)
cristy3ed852e2009-09-05 21:47:34 +00001877#endif
cristybb503372010-05-27 20:51:26 +00001878 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001879 {
cristybb503372010-05-27 20:51:26 +00001880 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001881 x;
1882
cristy4c08aed2011-07-01 19:47:50 +00001883 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001884 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001885
1886 if (status == MagickFalse)
1887 continue;
1888 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001889 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001890 {
1891 status=MagickFalse;
1892 continue;
1893 }
cristybb503372010-05-27 20:51:26 +00001894 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001895 {
cristy81629aa2012-07-12 20:08:52 +00001896 double
1897 pixel;
1898
cristy188f29a2012-06-24 19:09:53 +00001899 register ssize_t
1900 i;
1901
cristy883fde12013-04-08 00:50:13 +00001902 if (GetPixelReadMask(image,q) == 0)
cristy10a6c612012-01-29 21:41:05 +00001903 {
1904 q+=GetPixelChannels(image);
1905 continue;
1906 }
cristyf13c5942012-08-08 23:50:11 +00001907 pixel=GetPixelIntensity(image,q);
cristy188f29a2012-06-24 19:09:53 +00001908 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1909 {
cristy5a23c552013-02-13 14:34:28 +00001910 PixelChannel channel=GetPixelChannelChannel(image,i);
1911 PixelTrait traits=GetPixelChannelTraits(image,channel);
cristy188f29a2012-06-24 19:09:53 +00001912 if ((traits & UpdatePixelTrait) == 0)
1913 continue;
cristy81629aa2012-07-12 20:08:52 +00001914 if (image->channel_mask != DefaultChannels)
cristyf13c5942012-08-08 23:50:11 +00001915 pixel=(double) q[i];
cristy81629aa2012-07-12 20:08:52 +00001916 if (pixel > GetPixelInfoChannel(&threshold,channel))
cristy188f29a2012-06-24 19:09:53 +00001917 q[i]=QuantumRange;
1918 }
cristyed231572011-07-14 02:18:59 +00001919 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001920 }
1921 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1922 status=MagickFalse;
1923 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1924 {
1925 MagickBooleanType
1926 proceed;
1927
cristyb5d5f722009-11-04 03:03:49 +00001928#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristya12d8ba2012-04-29 16:33:41 +00001929 #pragma omp critical (MagickCore_WhiteThresholdImage)
cristy3ed852e2009-09-05 21:47:34 +00001930#endif
1931 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1932 image->rows);
1933 if (proceed == MagickFalse)
1934 status=MagickFalse;
1935 }
1936 }
1937 image_view=DestroyCacheView(image_view);
1938 return(status);
1939}