blob: 4c48c8da3a1c569643d6fa0280089c4ec52b6c45 [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 %
cristyde984cd2013-12-01 14:49:27 +000016% Cristy %
cristy3ed852e2009-09-05 21:47:34 +000017% October 1996 %
18% %
19% %
cristyb56bb242014-11-25 17:12:48 +000020% Copyright 1999-2015 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"
cristy741129f2015-01-09 20:08:46 +000072#include "MagickCore/pixel-private.h"
cristy4c08aed2011-07-01 19:47:50 +000073#include "MagickCore/quantize.h"
74#include "MagickCore/quantum.h"
75#include "MagickCore/random_.h"
76#include "MagickCore/random-private.h"
77#include "MagickCore/resize.h"
78#include "MagickCore/resource_.h"
79#include "MagickCore/segment.h"
80#include "MagickCore/shear.h"
81#include "MagickCore/signature-private.h"
82#include "MagickCore/string_.h"
83#include "MagickCore/string-private.h"
84#include "MagickCore/thread-private.h"
85#include "MagickCore/threshold.h"
cristy4e0b82a2011-09-29 12:47:44 +000086#include "MagickCore/token.h"
cristy4c08aed2011-07-01 19:47:50 +000087#include "MagickCore/transform.h"
88#include "MagickCore/xml-tree.h"
cristy433d1182011-09-04 13:38:52 +000089#include "MagickCore/xml-tree-private.h"
cristy3ed852e2009-09-05 21:47:34 +000090
91/*
92 Define declarations.
93*/
94#define ThresholdsFilename "thresholds.xml"
95
96/*
97 Typedef declarations.
98*/
99struct _ThresholdMap
100{
101 char
102 *map_id,
103 *description;
104
cristybb503372010-05-27 20:51:26 +0000105 size_t
cristy3ed852e2009-09-05 21:47:34 +0000106 width,
107 height;
108
cristybb503372010-05-27 20:51:26 +0000109 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000110 divisor,
111 *levels;
112};
113
114/*
cristyda9d0e42013-02-26 00:50:38 +0000115 Static declarations.
116*/
117static const char
118 *MinimalThresholdMap =
119 "<?xml version=\"1.0\"?>"
120 "<thresholds>"
121 " <threshold map=\"threshold\" alias=\"1x1\">"
122 " <description>Threshold 1x1 (non-dither)</description>"
123 " <levels width=\"1\" height=\"1\" divisor=\"2\">"
124 " 1"
125 " </levels>"
126 " </threshold>"
127 " <threshold map=\"checks\" alias=\"2x1\">"
128 " <description>Checkerboard 2x1 (dither)</description>"
129 " <levels width=\"2\" height=\"2\" divisor=\"3\">"
130 " 1 2"
131 " 2 1"
132 " </levels>"
133 " </threshold>"
134 "</thresholds>";
135
136/*
cristybd0ebf02011-09-29 01:19:42 +0000137 Forward declarations.
138*/
139static ThresholdMap
140 *GetThresholdMapFile(const char *,const char *,const char *,ExceptionInfo *);
141
142/*
cristy3ed852e2009-09-05 21:47:34 +0000143%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
144% %
145% %
146% %
147% A d a p t i v e T h r e s h o l d I m a g e %
148% %
149% %
150% %
151%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
152%
153% AdaptiveThresholdImage() selects an individual threshold for each pixel
154% based on the range of intensity values in its local neighborhood. This
155% allows for thresholding of an image whose global intensity histogram
156% doesn't contain distinctive peaks.
157%
158% The format of the AdaptiveThresholdImage method is:
159%
cristyde5cc632011-07-18 14:47:00 +0000160% Image *AdaptiveThresholdImage(const Image *image,const size_t width,
161% const size_t height,const double bias,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000162%
163% A description of each parameter follows:
164%
165% o image: the image.
166%
167% o width: the width of the local neighborhood.
168%
169% o height: the height of the local neighborhood.
170%
cristyde5cc632011-07-18 14:47:00 +0000171% o bias: the mean bias.
cristy3ed852e2009-09-05 21:47:34 +0000172%
173% o exception: return any errors or warnings in this structure.
174%
175*/
176MagickExport Image *AdaptiveThresholdImage(const Image *image,
cristyde5cc632011-07-18 14:47:00 +0000177 const size_t width,const size_t height,const double bias,
cristy3ed852e2009-09-05 21:47:34 +0000178 ExceptionInfo *exception)
179{
cristyde5cc632011-07-18 14:47:00 +0000180#define AdaptiveThresholdImageTag "AdaptiveThreshold/Image"
cristy3ed852e2009-09-05 21:47:34 +0000181
cristyc4c8d132010-01-07 01:58:38 +0000182 CacheView
183 *image_view,
184 *threshold_view;
185
cristy3ed852e2009-09-05 21:47:34 +0000186 Image
187 *threshold_image;
188
cristy3ed852e2009-09-05 21:47:34 +0000189 MagickBooleanType
190 status;
191
cristy5f959472010-05-27 22:19:46 +0000192 MagickOffsetType
193 progress;
194
cristyde5cc632011-07-18 14:47:00 +0000195 MagickSizeType
cristy3ed852e2009-09-05 21:47:34 +0000196 number_pixels;
197
cristy5f959472010-05-27 22:19:46 +0000198 ssize_t
199 y;
200
cristyde5cc632011-07-18 14:47:00 +0000201 /*
202 Initialize threshold image attributes.
203 */
204 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000205 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000206 if (image->debug != MagickFalse)
207 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
208 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000209 assert(exception->signature == MagickCoreSignature);
cristyde5cc632011-07-18 14:47:00 +0000210 threshold_image=CloneImage(image,image->columns,image->rows,MagickTrue,
211 exception);
cristy3ed852e2009-09-05 21:47:34 +0000212 if (threshold_image == (Image *) NULL)
213 return((Image *) NULL);
cristyb9eb87b2011-09-29 01:15:19 +0000214 status=SetImageStorageClass(threshold_image,DirectClass,exception);
215 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000216 {
cristy3ed852e2009-09-05 21:47:34 +0000217 threshold_image=DestroyImage(threshold_image);
218 return((Image *) NULL);
219 }
220 /*
cristyde5cc632011-07-18 14:47:00 +0000221 Threshold image.
cristy3ed852e2009-09-05 21:47:34 +0000222 */
223 status=MagickTrue;
224 progress=0;
cristyde5cc632011-07-18 14:47:00 +0000225 number_pixels=(MagickSizeType) width*height;
cristy46ff2672012-12-14 15:32:26 +0000226 image_view=AcquireVirtualCacheView(image,exception);
227 threshold_view=AcquireAuthenticCacheView(threshold_image,exception);
cristyb5d5f722009-11-04 03:03:49 +0000228#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000229 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +0000230 magick_threads(image,threshold_image,image->rows,1)
cristy3ed852e2009-09-05 21:47:34 +0000231#endif
cristybb503372010-05-27 20:51:26 +0000232 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000233 {
cristy525cf962012-09-09 15:00:40 +0000234 double
235 channel_bias[MaxPixelChannels],
236 channel_sum[MaxPixelChannels];
237
cristy4c08aed2011-07-01 19:47:50 +0000238 register const Quantum
cristy525cf962012-09-09 15:00:40 +0000239 *restrict p,
240 *restrict pixels;
cristy3ed852e2009-09-05 21:47:34 +0000241
cristy4c08aed2011-07-01 19:47:50 +0000242 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000243 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000244
cristyde5cc632011-07-18 14:47:00 +0000245 register ssize_t
cristy525cf962012-09-09 15:00:40 +0000246 i,
cristyde5cc632011-07-18 14:47:00 +0000247 x;
248
cristyde5cc632011-07-18 14:47:00 +0000249 ssize_t
cristy525cf962012-09-09 15:00:40 +0000250 center,
251 u,
252 v;
cristyde5cc632011-07-18 14:47:00 +0000253
cristy3ed852e2009-09-05 21:47:34 +0000254 if (status == MagickFalse)
255 continue;
cristyd99b0962010-05-29 23:14:26 +0000256 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
cristyde5cc632011-07-18 14:47:00 +0000257 (height/2L),image->columns+width,height,exception);
258 q=QueueCacheViewAuthenticPixels(threshold_view,0,y,threshold_image->columns,
259 1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000260 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +0000261 {
262 status=MagickFalse;
263 continue;
264 }
cristy5f9f2462011-09-28 23:37:58 +0000265 center=(ssize_t) GetPixelChannels(image)*(image->columns+width)*(height/2L)+
cristya0312c92011-07-23 21:04:30 +0000266 GetPixelChannels(image)*(width/2);
cristy525cf962012-09-09 15:00:40 +0000267 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
268 {
cristy5a23c552013-02-13 14:34:28 +0000269 PixelChannel channel=GetPixelChannelChannel(image,i);
270 PixelTrait traits=GetPixelChannelTraits(image,channel);
271 PixelTrait threshold_traits=GetPixelChannelTraits(threshold_image,
272 channel);
cristy525cf962012-09-09 15:00:40 +0000273 if ((traits == UndefinedPixelTrait) ||
274 (threshold_traits == UndefinedPixelTrait))
275 continue;
276 if (((threshold_traits & CopyPixelTrait) != 0) ||
cristy883fde12013-04-08 00:50:13 +0000277 (GetPixelReadMask(image,p) == 0))
cristy525cf962012-09-09 15:00:40 +0000278 {
279 SetPixelChannel(threshold_image,channel,p[center+i],q);
280 continue;
281 }
282 pixels=p;
283 channel_bias[channel]=0.0;
284 channel_sum[channel]=0.0;
285 for (v=0; v < (ssize_t) height; v++)
286 {
287 for (u=0; u < (ssize_t) width; u++)
288 {
289 if (u == (ssize_t) (width-1))
290 channel_bias[channel]+=pixels[i];
291 channel_sum[channel]+=pixels[i];
292 pixels+=GetPixelChannels(image);
293 }
cristy0c7bac12014-12-07 15:49:24 +0000294 pixels+=GetPixelChannels(image)*image->columns;
cristy525cf962012-09-09 15:00:40 +0000295 }
296 }
cristybb503372010-05-27 20:51:26 +0000297 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000298 {
cristya0312c92011-07-23 21:04:30 +0000299 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristy3ed852e2009-09-05 21:47:34 +0000300 {
cristya19f1d72012-08-07 18:24:38 +0000301 double
cristy525cf962012-09-09 15:00:40 +0000302 mean;
cristyde5cc632011-07-18 14:47:00 +0000303
cristy5a23c552013-02-13 14:34:28 +0000304 PixelChannel channel=GetPixelChannelChannel(image,i);
305 PixelTrait traits=GetPixelChannelTraits(image,channel);
306 PixelTrait threshold_traits=GetPixelChannelTraits(threshold_image,
307 channel);
cristy010d7d12011-08-31 01:02:48 +0000308 if ((traits == UndefinedPixelTrait) ||
309 (threshold_traits == UndefinedPixelTrait))
cristyde5cc632011-07-18 14:47:00 +0000310 continue;
cristy1eced092012-08-10 23:10:56 +0000311 if (((threshold_traits & CopyPixelTrait) != 0) ||
cristy883fde12013-04-08 00:50:13 +0000312 (GetPixelReadMask(image,p) == 0))
cristyde5cc632011-07-18 14:47:00 +0000313 {
cristy0beccfa2011-09-25 20:47:53 +0000314 SetPixelChannel(threshold_image,channel,p[center+i],q);
cristyde5cc632011-07-18 14:47:00 +0000315 continue;
316 }
cristy525cf962012-09-09 15:00:40 +0000317 channel_sum[channel]-=channel_bias[channel];
318 channel_bias[channel]=0.0;
cristyde5cc632011-07-18 14:47:00 +0000319 pixels=p;
cristyde5cc632011-07-18 14:47:00 +0000320 for (v=0; v < (ssize_t) height; v++)
cristy3ed852e2009-09-05 21:47:34 +0000321 {
cristy525cf962012-09-09 15:00:40 +0000322 channel_bias[channel]+=pixels[i];
323 pixels+=(width-1)*GetPixelChannels(image);
324 channel_sum[channel]+=pixels[i];
dirka608e0a2015-01-06 22:24:27 +0000325 pixels+=GetPixelChannels(image)*(image->columns+1);
cristy3ed852e2009-09-05 21:47:34 +0000326 }
cristy525cf962012-09-09 15:00:40 +0000327 mean=(double) (channel_sum[channel]/number_pixels+bias);
cristya19f1d72012-08-07 18:24:38 +0000328 SetPixelChannel(threshold_image,channel,(Quantum) ((double)
cristy5f9f2462011-09-28 23:37:58 +0000329 p[center+i] <= mean ? 0 : QuantumRange),q);
cristy3ed852e2009-09-05 21:47:34 +0000330 }
cristya0312c92011-07-23 21:04:30 +0000331 p+=GetPixelChannels(image);
332 q+=GetPixelChannels(threshold_image);
cristy3ed852e2009-09-05 21:47:34 +0000333 }
cristyde5cc632011-07-18 14:47:00 +0000334 if (SyncCacheViewAuthenticPixels(threshold_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000335 status=MagickFalse;
336 if (image->progress_monitor != (MagickProgressMonitor) NULL)
337 {
338 MagickBooleanType
339 proceed;
340
cristyb5d5f722009-11-04 03:03:49 +0000341#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000342 #pragma omp critical (MagickCore_AdaptiveThresholdImage)
cristy3ed852e2009-09-05 21:47:34 +0000343#endif
cristyde5cc632011-07-18 14:47:00 +0000344 proceed=SetImageProgress(image,AdaptiveThresholdImageTag,progress++,
cristy3ed852e2009-09-05 21:47:34 +0000345 image->rows);
346 if (proceed == MagickFalse)
347 status=MagickFalse;
348 }
349 }
cristyde5cc632011-07-18 14:47:00 +0000350 threshold_image->type=image->type;
cristy3ed852e2009-09-05 21:47:34 +0000351 threshold_view=DestroyCacheView(threshold_view);
352 image_view=DestroyCacheView(image_view);
353 if (status == MagickFalse)
354 threshold_image=DestroyImage(threshold_image);
355 return(threshold_image);
356}
357
358/*
359%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
360% %
361% %
362% %
363% B i l e v e l I m a g e %
364% %
365% %
366% %
367%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
368%
369% BilevelImage() changes the value of individual pixels based on the
370% intensity of each pixel channel. The result is a high-contrast image.
371%
372% More precisely each channel value of the image is 'thresholded' so that if
373% it is equal to or less than the given value it is set to zero, while any
374% value greater than that give is set to it maximum or QuantumRange.
375%
376% This function is what is used to implement the "-threshold" operator for
377% the command line API.
378%
379% If the default channel setting is given the image is thresholded using just
380% the gray 'intensity' of the image, rather than the individual channels.
381%
cristyf4ad9df2011-07-08 16:49:03 +0000382% The format of the BilevelImage method is:
cristy3ed852e2009-09-05 21:47:34 +0000383%
cristye941a752011-10-15 01:52:48 +0000384% MagickBooleanType BilevelImage(Image *image,const double threshold,
385% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000386%
387% A description of each parameter follows:
388%
389% o image: the image.
390%
cristy3ed852e2009-09-05 21:47:34 +0000391% o threshold: define the threshold values.
392%
cristye941a752011-10-15 01:52:48 +0000393% o exception: return any errors or warnings in this structure.
394%
cristyf89cb1d2011-07-07 01:24:37 +0000395% Aside: You can get the same results as operator using LevelImages()
cristy3ed852e2009-09-05 21:47:34 +0000396% with the 'threshold' value for both the black_point and the white_point.
397%
398*/
cristye941a752011-10-15 01:52:48 +0000399MagickExport MagickBooleanType BilevelImage(Image *image,const double threshold,
400 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000401{
402#define ThresholdImageTag "Threshold/Image"
403
cristyc4c8d132010-01-07 01:58:38 +0000404 CacheView
405 *image_view;
406
cristy3ed852e2009-09-05 21:47:34 +0000407 MagickBooleanType
408 status;
409
cristy5f959472010-05-27 22:19:46 +0000410 MagickOffsetType
411 progress;
412
413 ssize_t
414 y;
415
cristy3ed852e2009-09-05 21:47:34 +0000416 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000417 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000418 if (image->debug != MagickFalse)
419 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy574cc262011-08-05 01:23:58 +0000420 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000421 return(MagickFalse);
cristy3a3b9962013-02-08 18:41:18 +0000422 if (IsGrayColorspace(image->colorspace) != MagickFalse)
cristy0c81d062013-04-21 15:22:02 +0000423 (void) SetImageColorspace(image,sRGBColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +0000424 /*
425 Bilevel threshold image.
426 */
427 status=MagickTrue;
428 progress=0;
cristy46ff2672012-12-14 15:32:26 +0000429 image_view=AcquireAuthenticCacheView(image,exception);
cristyb5d5f722009-11-04 03:03:49 +0000430#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd065992012-08-09 15:27:39 +0000431 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +0000432 magick_threads(image,image,image->rows,1)
cristy3ed852e2009-09-05 21:47:34 +0000433#endif
cristybb503372010-05-27 20:51:26 +0000434 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000435 {
cristybb503372010-05-27 20:51:26 +0000436 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000437 x;
438
cristy4c08aed2011-07-01 19:47:50 +0000439 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000440 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000441
442 if (status == MagickFalse)
443 continue;
444 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000445 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000446 {
447 status=MagickFalse;
448 continue;
449 }
cristy805b6a02011-08-09 00:59:35 +0000450 for (x=0; x < (ssize_t) image->columns; x++)
451 {
cristy171e2352012-07-10 17:43:12 +0000452 double
453 pixel;
454
cristy95111202011-08-09 19:41:42 +0000455 register ssize_t
456 i;
457
cristy883fde12013-04-08 00:50:13 +0000458 if (GetPixelReadMask(image,q) == 0)
cristy10a6c612012-01-29 21:41:05 +0000459 {
460 q+=GetPixelChannels(image);
461 continue;
462 }
cristyf13c5942012-08-08 23:50:11 +0000463 pixel=GetPixelIntensity(image,q);
cristy95111202011-08-09 19:41:42 +0000464 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
465 {
cristy5a23c552013-02-13 14:34:28 +0000466 PixelChannel channel=GetPixelChannelChannel(image,i);
467 PixelTrait traits=GetPixelChannelTraits(image,channel);
cristyd09f8802012-02-04 16:44:10 +0000468 if ((traits & UpdatePixelTrait) == 0)
469 continue;
cristya64f4b92012-07-11 23:59:00 +0000470 if (image->channel_mask != DefaultChannels)
cristyf13c5942012-08-08 23:50:11 +0000471 pixel=(double) q[i];
cristy171e2352012-07-10 17:43:12 +0000472 q[i]=(Quantum) (pixel <= threshold ? 0 : QuantumRange);
cristy95111202011-08-09 19:41:42 +0000473 }
cristy805b6a02011-08-09 00:59:35 +0000474 q+=GetPixelChannels(image);
475 }
cristy3ed852e2009-09-05 21:47:34 +0000476 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
477 status=MagickFalse;
478 if (image->progress_monitor != (MagickProgressMonitor) NULL)
479 {
480 MagickBooleanType
481 proceed;
482
cristyb5d5f722009-11-04 03:03:49 +0000483#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000484 #pragma omp critical (MagickCore_BilevelImage)
cristy3ed852e2009-09-05 21:47:34 +0000485#endif
486 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
487 image->rows);
488 if (proceed == MagickFalse)
489 status=MagickFalse;
490 }
491 }
492 image_view=DestroyCacheView(image_view);
493 return(status);
494}
495
496/*
497%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
498% %
499% %
500% %
501% B l a c k T h r e s h o l d I m a g e %
502% %
503% %
504% %
505%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
506%
507% BlackThresholdImage() is like ThresholdImage() but forces all pixels below
cristy4e101302009-09-17 12:49:12 +0000508% the threshold into black while leaving all pixels at or above the threshold
cristy3ed852e2009-09-05 21:47:34 +0000509% unchanged.
510%
511% The format of the BlackThresholdImage method is:
512%
cristyf4ad9df2011-07-08 16:49:03 +0000513% MagickBooleanType BlackThresholdImage(Image *image,
514% const char *threshold,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000515%
516% A description of each parameter follows:
517%
518% o image: the image.
519%
cristy5f9f2462011-09-28 23:37:58 +0000520% o threshold: define the threshold value.
cristy3ed852e2009-09-05 21:47:34 +0000521%
522% o exception: return any errors or warnings in this structure.
523%
524*/
525MagickExport MagickBooleanType BlackThresholdImage(Image *image,
cristyf4ad9df2011-07-08 16:49:03 +0000526 const char *thresholds,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000527{
528#define ThresholdImageTag "Threshold/Image"
529
cristyc4c8d132010-01-07 01:58:38 +0000530 CacheView
531 *image_view;
532
cristy3ed852e2009-09-05 21:47:34 +0000533 GeometryInfo
534 geometry_info;
535
cristy3ed852e2009-09-05 21:47:34 +0000536 MagickBooleanType
537 status;
538
cristy5f959472010-05-27 22:19:46 +0000539 MagickOffsetType
540 progress;
541
cristyd6803382012-04-10 01:41:25 +0000542 PixelInfo
cristya12d8ba2012-04-29 16:33:41 +0000543 threshold;
cristy3ed852e2009-09-05 21:47:34 +0000544
545 MagickStatusType
546 flags;
547
cristy5f959472010-05-27 22:19:46 +0000548 ssize_t
549 y;
550
cristy3ed852e2009-09-05 21:47:34 +0000551 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000552 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000553 if (image->debug != MagickFalse)
554 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
555 if (thresholds == (const char *) NULL)
556 return(MagickTrue);
cristy574cc262011-08-05 01:23:58 +0000557 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000558 return(MagickFalse);
cristy23e55c02012-04-10 01:21:56 +0000559 if (IsGrayColorspace(image->colorspace) != MagickFalse)
cristy0c81d062013-04-21 15:22:02 +0000560 (void) SetImageColorspace(image,sRGBColorspace,exception);
cristya12d8ba2012-04-29 16:33:41 +0000561 GetPixelInfo(image,&threshold);
cristy3ed852e2009-09-05 21:47:34 +0000562 flags=ParseGeometry(thresholds,&geometry_info);
cristya12d8ba2012-04-29 16:33:41 +0000563 threshold.red=geometry_info.rho;
564 threshold.green=geometry_info.rho;
565 threshold.blue=geometry_info.rho;
566 threshold.black=geometry_info.rho;
567 threshold.alpha=100.0;
cristy5f9f2462011-09-28 23:37:58 +0000568 if ((flags & SigmaValue) != 0)
cristya12d8ba2012-04-29 16:33:41 +0000569 threshold.green=geometry_info.sigma;
cristy5f9f2462011-09-28 23:37:58 +0000570 if ((flags & XiValue) != 0)
cristya12d8ba2012-04-29 16:33:41 +0000571 threshold.blue=geometry_info.xi;
cristy5f9f2462011-09-28 23:37:58 +0000572 if ((flags & PsiValue) != 0)
cristya12d8ba2012-04-29 16:33:41 +0000573 threshold.alpha=geometry_info.psi;
574 if (threshold.colorspace == CMYKColorspace)
cristyd6803382012-04-10 01:41:25 +0000575 {
576 if ((flags & PsiValue) != 0)
cristya12d8ba2012-04-29 16:33:41 +0000577 threshold.black=geometry_info.psi;
cristyd6803382012-04-10 01:41:25 +0000578 if ((flags & ChiValue) != 0)
cristya12d8ba2012-04-29 16:33:41 +0000579 threshold.alpha=geometry_info.chi;
580 }
581 if ((flags & PercentValue) != 0)
582 {
cristy65d4e5e2012-10-17 12:22:24 +0000583 threshold.red*=(MagickRealType) (QuantumRange/100.0);
584 threshold.green*=(MagickRealType) (QuantumRange/100.0);
585 threshold.blue*=(MagickRealType) (QuantumRange/100.0);
586 threshold.black*=(MagickRealType) (QuantumRange/100.0);
587 threshold.alpha*=(MagickRealType) (QuantumRange/100.0);
cristyd6803382012-04-10 01:41:25 +0000588 }
cristy3ed852e2009-09-05 21:47:34 +0000589 /*
cristy5f9f2462011-09-28 23:37:58 +0000590 White threshold image.
cristy3ed852e2009-09-05 21:47:34 +0000591 */
592 status=MagickTrue;
593 progress=0;
cristy46ff2672012-12-14 15:32:26 +0000594 image_view=AcquireAuthenticCacheView(image,exception);
cristyb5d5f722009-11-04 03:03:49 +0000595#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd065992012-08-09 15:27:39 +0000596 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +0000597 magick_threads(image,image,image->rows,1)
cristy3ed852e2009-09-05 21:47:34 +0000598#endif
cristybb503372010-05-27 20:51:26 +0000599 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000600 {
cristybb503372010-05-27 20:51:26 +0000601 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000602 x;
603
cristy4c08aed2011-07-01 19:47:50 +0000604 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000605 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000606
607 if (status == MagickFalse)
608 continue;
609 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000610 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000611 {
612 status=MagickFalse;
613 continue;
614 }
cristybb503372010-05-27 20:51:26 +0000615 for (x=0; x < (ssize_t) image->columns; x++)
cristyb0ea1af2009-11-28 20:44:46 +0000616 {
cristy81629aa2012-07-12 20:08:52 +0000617 double
618 pixel;
619
cristyc4567182012-06-24 20:55:08 +0000620 register ssize_t
621 i;
622
cristy883fde12013-04-08 00:50:13 +0000623 if (GetPixelReadMask(image,q) == 0)
cristy10a6c612012-01-29 21:41:05 +0000624 {
625 q+=GetPixelChannels(image);
626 continue;
627 }
cristyf13c5942012-08-08 23:50:11 +0000628 pixel=GetPixelIntensity(image,q);
cristy188f29a2012-06-24 19:09:53 +0000629 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
630 {
cristy5a23c552013-02-13 14:34:28 +0000631 PixelChannel channel=GetPixelChannelChannel(image,i);
632 PixelTrait traits=GetPixelChannelTraits(image,channel);
cristy188f29a2012-06-24 19:09:53 +0000633 if ((traits & UpdatePixelTrait) == 0)
634 continue;
cristy81629aa2012-07-12 20:08:52 +0000635 if (image->channel_mask != DefaultChannels)
cristyf13c5942012-08-08 23:50:11 +0000636 pixel=(double) q[i];
cristy53495ce2015-05-16 12:00:05 +0000637 if (pixel < GetPixelInfoChannel(&threshold,channel))
cristy525cf962012-09-09 15:00:40 +0000638 q[i]=(Quantum) 0;
cristy188f29a2012-06-24 19:09:53 +0000639 }
cristyed231572011-07-14 02:18:59 +0000640 q+=GetPixelChannels(image);
cristyb0ea1af2009-11-28 20:44:46 +0000641 }
cristy3ed852e2009-09-05 21:47:34 +0000642 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
643 status=MagickFalse;
644 if (image->progress_monitor != (MagickProgressMonitor) NULL)
645 {
646 MagickBooleanType
647 proceed;
648
cristyb5d5f722009-11-04 03:03:49 +0000649#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristya12d8ba2012-04-29 16:33:41 +0000650 #pragma omp critical (MagickCore_BlackThresholdImage)
cristy3ed852e2009-09-05 21:47:34 +0000651#endif
652 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
653 image->rows);
654 if (proceed == MagickFalse)
655 status=MagickFalse;
656 }
657 }
658 image_view=DestroyCacheView(image_view);
659 return(status);
660}
661
662/*
663%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
664% %
665% %
666% %
cristy1eb45dd2009-09-25 16:38:06 +0000667% C l a m p I m a g e %
668% %
669% %
670% %
671%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
672%
cristy7884a932012-11-04 14:33:51 +0000673% ClampImage() set each pixel whose value is below zero to zero and any the
674% pixel whose value is above the quantum range to the quantum range (e.g.
675% 65535) otherwise the pixel value remains unchanged.
cristy1eb45dd2009-09-25 16:38:06 +0000676%
cristyf4ad9df2011-07-08 16:49:03 +0000677% The format of the ClampImage method is:
cristy1eb45dd2009-09-25 16:38:06 +0000678%
cristy092d71c2011-10-14 18:01:29 +0000679% MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
cristy1eb45dd2009-09-25 16:38:06 +0000680%
681% A description of each parameter follows:
682%
683% o image: the image.
684%
cristy092d71c2011-10-14 18:01:29 +0000685% o exception: return any errors or warnings in this structure.
686%
cristy1eb45dd2009-09-25 16:38:06 +0000687*/
688
cristy092d71c2011-10-14 18:01:29 +0000689MagickExport MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
cristy1eb45dd2009-09-25 16:38:06 +0000690{
cristy1eb45dd2009-09-25 16:38:06 +0000691#define ClampImageTag "Clamp/Image"
692
cristyc4c8d132010-01-07 01:58:38 +0000693 CacheView
694 *image_view;
695
cristy1eb45dd2009-09-25 16:38:06 +0000696 MagickBooleanType
697 status;
698
cristy5f959472010-05-27 22:19:46 +0000699 MagickOffsetType
700 progress;
701
702 ssize_t
703 y;
704
cristy1eb45dd2009-09-25 16:38:06 +0000705 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000706 assert(image->signature == MagickCoreSignature);
cristy1eb45dd2009-09-25 16:38:06 +0000707 if (image->debug != MagickFalse)
708 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
709 if (image->storage_class == PseudoClass)
710 {
cristybb503372010-05-27 20:51:26 +0000711 register ssize_t
cristy1eb45dd2009-09-25 16:38:06 +0000712 i;
713
cristy101ab702011-10-13 13:06:32 +0000714 register PixelInfo
cristyc47d1f82009-11-26 01:44:43 +0000715 *restrict q;
cristy1eb45dd2009-09-25 16:38:06 +0000716
717 q=image->colormap;
cristybb503372010-05-27 20:51:26 +0000718 for (i=0; i < (ssize_t) image->colors; i++)
cristy1eb45dd2009-09-25 16:38:06 +0000719 {
cristyddcbde62012-11-06 00:17:10 +0000720 q->red=(double) ClampPixel(q->red);
721 q->green=(double) ClampPixel(q->green);
722 q->blue=(double) ClampPixel(q->blue);
723 q->alpha=(double) ClampPixel(q->alpha);
cristy1eb45dd2009-09-25 16:38:06 +0000724 q++;
725 }
cristyea1a8aa2011-10-20 13:24:06 +0000726 return(SyncImage(image,exception));
cristy1eb45dd2009-09-25 16:38:06 +0000727 }
728 /*
cristy611721d2009-09-25 16:42:17 +0000729 Clamp image.
cristy1eb45dd2009-09-25 16:38:06 +0000730 */
731 status=MagickTrue;
732 progress=0;
cristy46ff2672012-12-14 15:32:26 +0000733 image_view=AcquireAuthenticCacheView(image,exception);
cristyb5d5f722009-11-04 03:03:49 +0000734#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd065992012-08-09 15:27:39 +0000735 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +0000736 magick_threads(image,image,image->rows,1)
cristy1eb45dd2009-09-25 16:38:06 +0000737#endif
cristybb503372010-05-27 20:51:26 +0000738 for (y=0; y < (ssize_t) image->rows; y++)
cristy1eb45dd2009-09-25 16:38:06 +0000739 {
cristybb503372010-05-27 20:51:26 +0000740 register ssize_t
cristy1eb45dd2009-09-25 16:38:06 +0000741 x;
742
cristy4c08aed2011-07-01 19:47:50 +0000743 register Quantum
cristyc47d1f82009-11-26 01:44:43 +0000744 *restrict q;
cristy1eb45dd2009-09-25 16:38:06 +0000745
746 if (status == MagickFalse)
747 continue;
748 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000749 if (q == (Quantum *) NULL)
cristy1eb45dd2009-09-25 16:38:06 +0000750 {
751 status=MagickFalse;
752 continue;
753 }
cristybb503372010-05-27 20:51:26 +0000754 for (x=0; x < (ssize_t) image->columns; x++)
cristy1eb45dd2009-09-25 16:38:06 +0000755 {
cristy5f9f2462011-09-28 23:37:58 +0000756 register ssize_t
757 i;
758
cristy883fde12013-04-08 00:50:13 +0000759 if (GetPixelReadMask(image,q) == 0)
cristy10a6c612012-01-29 21:41:05 +0000760 {
761 q+=GetPixelChannels(image);
762 continue;
763 }
cristy5f9f2462011-09-28 23:37:58 +0000764 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
765 {
cristy5a23c552013-02-13 14:34:28 +0000766 PixelChannel channel=GetPixelChannelChannel(image,i);
767 PixelTrait traits=GetPixelChannelTraits(image,channel);
cristy454bb022013-04-23 13:03:38 +0000768 if ((traits & UpdatePixelTrait) == 0)
cristy5f9f2462011-09-28 23:37:58 +0000769 continue;
cristyddcbde62012-11-06 00:17:10 +0000770 q[i]=ClampPixel(q[i]);
cristy5f9f2462011-09-28 23:37:58 +0000771 }
cristyed231572011-07-14 02:18:59 +0000772 q+=GetPixelChannels(image);
cristy1eb45dd2009-09-25 16:38:06 +0000773 }
774 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
775 status=MagickFalse;
776 if (image->progress_monitor != (MagickProgressMonitor) NULL)
777 {
778 MagickBooleanType
779 proceed;
780
cristyb5d5f722009-11-04 03:03:49 +0000781#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000782 #pragma omp critical (MagickCore_ClampImage)
cristy1eb45dd2009-09-25 16:38:06 +0000783#endif
cristyaa17cde2012-06-04 23:43:42 +0000784 proceed=SetImageProgress(image,ClampImageTag,progress++,image->rows);
cristy1eb45dd2009-09-25 16:38:06 +0000785 if (proceed == MagickFalse)
786 status=MagickFalse;
787 }
788 }
789 image_view=DestroyCacheView(image_view);
790 return(status);
791}
792
793/*
794%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
795% %
796% %
797% %
cristy3ed852e2009-09-05 21:47:34 +0000798% D e s t r o y T h r e s h o l d M a p %
799% %
800% %
801% %
802%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
803%
804% DestroyThresholdMap() de-allocate the given ThresholdMap
805%
806% The format of the ListThresholdMaps method is:
807%
808% ThresholdMap *DestroyThresholdMap(Threshold *map)
809%
810% A description of each parameter follows.
811%
812% o map: Pointer to the Threshold map to destroy
813%
814*/
815MagickExport ThresholdMap *DestroyThresholdMap(ThresholdMap *map)
816{
817 assert(map != (ThresholdMap *) NULL);
818 if (map->map_id != (char *) NULL)
819 map->map_id=DestroyString(map->map_id);
820 if (map->description != (char *) NULL)
821 map->description=DestroyString(map->description);
cristybb503372010-05-27 20:51:26 +0000822 if (map->levels != (ssize_t *) NULL)
823 map->levels=(ssize_t *) RelinquishMagickMemory(map->levels);
cristy3ed852e2009-09-05 21:47:34 +0000824 map=(ThresholdMap *) RelinquishMagickMemory(map);
825 return(map);
826}
827
828/*
829%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
830% %
831% %
832% %
cristyb9eb87b2011-09-29 01:15:19 +0000833% G e t T h r e s h o l d M a p %
834% %
835% %
836% %
837%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
838%
839% GetThresholdMap() loads and searches one or more threshold map files for the
840% map matching the given name or alias.
841%
842% The format of the GetThresholdMap method is:
843%
844% ThresholdMap *GetThresholdMap(const char *map_id,
845% ExceptionInfo *exception)
846%
847% A description of each parameter follows.
848%
849% o map_id: ID of the map to look for.
850%
851% o exception: return any errors or warnings in this structure.
852%
853*/
854MagickExport ThresholdMap *GetThresholdMap(const char *map_id,
855 ExceptionInfo *exception)
856{
857 const StringInfo
858 *option;
859
860 LinkedListInfo
861 *options;
862
863 ThresholdMap
864 *map;
865
cristyda9d0e42013-02-26 00:50:38 +0000866 map=GetThresholdMapFile(MinimalThresholdMap,"built-in",map_id,exception);
867 if (map != (ThresholdMap *) NULL)
868 return(map);
cristyb9eb87b2011-09-29 01:15:19 +0000869 options=GetConfigureOptions(ThresholdsFilename,exception);
cristyda9d0e42013-02-26 00:50:38 +0000870 option=(const StringInfo *) GetNextValueInLinkedList(options);
871 while (option != (const StringInfo *) NULL)
872 {
cristyb9eb87b2011-09-29 01:15:19 +0000873 map=GetThresholdMapFile((const char *) GetStringInfoDatum(option),
874 GetStringInfoPath(option),map_id,exception);
cristyda9d0e42013-02-26 00:50:38 +0000875 if (map != (ThresholdMap *) NULL)
cristy44efc982013-12-29 15:20:50 +0000876 break;
cristyda9d0e42013-02-26 00:50:38 +0000877 option=(const StringInfo *) GetNextValueInLinkedList(options);
878 }
cristyb9eb87b2011-09-29 01:15:19 +0000879 options=DestroyConfigureOptions(options);
cristy44efc982013-12-29 15:20:50 +0000880 return(map);
cristyb9eb87b2011-09-29 01:15:19 +0000881}
882
883/*
884%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
885% %
886% %
887% %
cristy3ed852e2009-09-05 21:47:34 +0000888+ G e t T h r e s h o l d M a p F i l e %
889% %
890% %
891% %
892%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
893%
894% GetThresholdMapFile() look for a given threshold map name or alias in the
895% given XML file data, and return the allocated the map when found.
896%
897% The format of the ListThresholdMaps method is:
898%
899% ThresholdMap *GetThresholdMap(const char *xml,const char *filename,
900% const char *map_id,ExceptionInfo *exception)
901%
902% A description of each parameter follows.
903%
904% o xml: The threshold map list in XML format.
905%
906% o filename: The threshold map XML filename.
907%
908% o map_id: ID of the map to look for in XML list.
909%
910% o exception: return any errors or warnings in this structure.
911%
912*/
cristy311eb742013-02-25 19:55:37 +0000913static ThresholdMap *GetThresholdMapFile(const char *xml,const char *filename,
914 const char *map_id,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000915{
cristyb9eb87b2011-09-29 01:15:19 +0000916 char
917 *p;
918
cristy3ed852e2009-09-05 21:47:34 +0000919 const char
cristyb9eb87b2011-09-29 01:15:19 +0000920 *attribute,
cristy3ed852e2009-09-05 21:47:34 +0000921 *content;
922
923 double
924 value;
925
cristyb9eb87b2011-09-29 01:15:19 +0000926 register ssize_t
927 i;
cristy3ed852e2009-09-05 21:47:34 +0000928
929 ThresholdMap
930 *map;
931
cristyb9eb87b2011-09-29 01:15:19 +0000932 XMLTreeInfo
933 *description,
934 *levels,
935 *threshold,
936 *thresholds;
937
938 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
939 "Loading threshold map file \"%s\" ...",filename);
940 map=(ThresholdMap *) NULL;
941 thresholds=NewXMLTree(xml,exception);
942 if (thresholds == (XMLTreeInfo *) NULL)
943 return(map);
944 for (threshold=GetXMLTreeChild(thresholds,"threshold");
945 threshold != (XMLTreeInfo *) NULL;
946 threshold=GetNextXMLTreeTag(threshold))
947 {
948 attribute=GetXMLTreeAttribute(threshold,"map");
949 if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
950 break;
951 attribute=GetXMLTreeAttribute(threshold,"alias");
952 if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
953 break;
954 }
955 if (threshold == (XMLTreeInfo *) NULL)
cristyed5aadc2013-12-29 13:30:36 +0000956 {
957 thresholds=DestroyXMLTree(thresholds);
958 return(map);
959 }
cristyb9eb87b2011-09-29 01:15:19 +0000960 description=GetXMLTreeChild(threshold,"description");
961 if (description == (XMLTreeInfo *) NULL)
962 {
963 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
964 "XmlMissingElement", "<description>, map \"%s\"",map_id);
965 thresholds=DestroyXMLTree(thresholds);
966 return(map);
967 }
968 levels=GetXMLTreeChild(threshold,"levels");
969 if (levels == (XMLTreeInfo *) NULL)
970 {
971 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
972 "XmlMissingElement", "<levels>, map \"%s\"", map_id);
973 thresholds=DestroyXMLTree(thresholds);
974 return(map);
975 }
976 map=(ThresholdMap *) AcquireMagickMemory(sizeof(ThresholdMap));
977 if (map == (ThresholdMap *) NULL)
978 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
979 map->map_id=(char *) NULL;
980 map->description=(char *) NULL;
981 map->levels=(ssize_t *) NULL;
982 attribute=GetXMLTreeAttribute(threshold,"map");
983 if (attribute != (char *) NULL)
984 map->map_id=ConstantString(attribute);
985 content=GetXMLTreeContent(description);
986 if (content != (char *) NULL)
987 map->description=ConstantString(content);
988 attribute=GetXMLTreeAttribute(levels,"width");
989 if (attribute == (char *) NULL)
990 {
991 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
992 "XmlMissingAttribute", "<levels width>, map \"%s\"",map_id);
993 thresholds=DestroyXMLTree(thresholds);
994 map=DestroyThresholdMap(map);
995 return(map);
996 }
997 map->width=StringToUnsignedLong(attribute);
998 if (map->width == 0)
999 {
1000 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1001 "XmlInvalidAttribute", "<levels width>, map \"%s\"",map_id);
1002 thresholds=DestroyXMLTree(thresholds);
1003 map=DestroyThresholdMap(map);
1004 return(map);
1005 }
1006 attribute=GetXMLTreeAttribute(levels,"height");
1007 if (attribute == (char *) NULL)
1008 {
1009 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1010 "XmlMissingAttribute", "<levels height>, map \"%s\"",map_id);
1011 thresholds=DestroyXMLTree(thresholds);
1012 map=DestroyThresholdMap(map);
1013 return(map);
1014 }
1015 map->height=StringToUnsignedLong(attribute);
1016 if (map->height == 0)
1017 {
1018 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1019 "XmlInvalidAttribute", "<levels height>, map \"%s\"",map_id);
1020 thresholds=DestroyXMLTree(thresholds);
1021 map=DestroyThresholdMap(map);
1022 return(map);
1023 }
1024 attribute=GetXMLTreeAttribute(levels,"divisor");
1025 if (attribute == (char *) NULL)
1026 {
1027 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1028 "XmlMissingAttribute", "<levels divisor>, map \"%s\"",map_id);
1029 thresholds=DestroyXMLTree(thresholds);
1030 map=DestroyThresholdMap(map);
1031 return(map);
1032 }
1033 map->divisor=(ssize_t) StringToLong(attribute);
1034 if (map->divisor < 2)
1035 {
1036 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1037 "XmlInvalidAttribute", "<levels divisor>, map \"%s\"",map_id);
1038 thresholds=DestroyXMLTree(thresholds);
1039 map=DestroyThresholdMap(map);
1040 return(map);
1041 }
1042 content=GetXMLTreeContent(levels);
1043 if (content == (char *) NULL)
1044 {
1045 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1046 "XmlMissingContent", "<levels>, map \"%s\"",map_id);
1047 thresholds=DestroyXMLTree(thresholds);
1048 map=DestroyThresholdMap(map);
1049 return(map);
1050 }
1051 map->levels=(ssize_t *) AcquireQuantumMemory((size_t) map->width,map->height*
1052 sizeof(*map->levels));
1053 if (map->levels == (ssize_t *) NULL)
1054 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
1055 for (i=0; i < (ssize_t) (map->width*map->height); i++)
1056 {
1057 map->levels[i]=(ssize_t) strtol(content,&p,10);
1058 if (p == content)
1059 {
1060 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1061 "XmlInvalidContent", "<level> too few values, map \"%s\"",map_id);
1062 thresholds=DestroyXMLTree(thresholds);
1063 map=DestroyThresholdMap(map);
1064 return(map);
1065 }
1066 if ((map->levels[i] < 0) || (map->levels[i] > map->divisor))
1067 {
1068 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1069 "XmlInvalidContent", "<level> %.20g out of range, map \"%s\"",
1070 (double) map->levels[i],map_id);
1071 thresholds=DestroyXMLTree(thresholds);
1072 map=DestroyThresholdMap(map);
1073 return(map);
1074 }
1075 content=p;
1076 }
1077 value=(double) strtol(content,&p,10);
1078 (void) value;
1079 if (p != content)
1080 {
1081 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1082 "XmlInvalidContent", "<level> too many values, map \"%s\"",map_id);
1083 thresholds=DestroyXMLTree(thresholds);
1084 map=DestroyThresholdMap(map);
1085 return(map);
1086 }
1087 thresholds=DestroyXMLTree(thresholds);
cristy3ed852e2009-09-05 21:47:34 +00001088 return(map);
1089}
1090
1091/*
1092%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1093% %
1094% %
1095% %
1096+ L i s t T h r e s h o l d M a p F i l e %
1097% %
1098% %
1099% %
1100%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1101%
1102% ListThresholdMapFile() lists the threshold maps and their descriptions
1103% in the given XML file data.
1104%
1105% The format of the ListThresholdMaps method is:
1106%
1107% MagickBooleanType ListThresholdMaps(FILE *file,const char*xml,
1108% const char *filename,ExceptionInfo *exception)
1109%
1110% A description of each parameter follows.
1111%
1112% o file: An pointer to the output FILE.
1113%
1114% o xml: The threshold map list in XML format.
1115%
1116% o filename: The threshold map XML filename.
1117%
1118% o exception: return any errors or warnings in this structure.
1119%
1120*/
1121MagickBooleanType ListThresholdMapFile(FILE *file,const char *xml,
1122 const char *filename,ExceptionInfo *exception)
1123{
cristy5f9f2462011-09-28 23:37:58 +00001124 const char
1125 *alias,
1126 *content,
1127 *map;
1128
1129 XMLTreeInfo
1130 *description,
1131 *threshold,
1132 *thresholds;
cristy3ed852e2009-09-05 21:47:34 +00001133
cristyf432c632014-12-07 15:11:28 +00001134 assert( xml != (char *) NULL );
1135 assert( file != (FILE *) NULL );
cristy3ed852e2009-09-05 21:47:34 +00001136 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1137 "Loading threshold map file \"%s\" ...",filename);
1138 thresholds=NewXMLTree(xml,exception);
cristyf432c632014-12-07 15:11:28 +00001139 if ( thresholds == (XMLTreeInfo *) NULL )
cristy3ed852e2009-09-05 21:47:34 +00001140 return(MagickFalse);
cristy1e604812011-05-19 18:07:50 +00001141 (void) FormatLocaleFile(file,"%-16s %-12s %s\n","Map","Alias","Description");
1142 (void) FormatLocaleFile(file,
1143 "----------------------------------------------------\n");
cristy5f9f2462011-09-28 23:37:58 +00001144 threshold=GetXMLTreeChild(thresholds,"threshold");
cristyb9eb87b2011-09-29 01:15:19 +00001145 for ( ; threshold != (XMLTreeInfo *) NULL;
1146 threshold=GetNextXMLTreeTag(threshold))
cristy3ed852e2009-09-05 21:47:34 +00001147 {
cristy5f9f2462011-09-28 23:37:58 +00001148 map=GetXMLTreeAttribute(threshold,"map");
1149 if (map == (char *) NULL)
1150 {
1151 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1152 "XmlMissingAttribute", "<map>");
1153 thresholds=DestroyXMLTree(thresholds);
1154 return(MagickFalse);
1155 }
1156 alias=GetXMLTreeAttribute(threshold,"alias");
cristy3ed852e2009-09-05 21:47:34 +00001157 description=GetXMLTreeChild(threshold,"description");
cristy5f9f2462011-09-28 23:37:58 +00001158 if (description == (XMLTreeInfo *) NULL)
1159 {
1160 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
cristyb9eb87b2011-09-29 01:15:19 +00001161 "XmlMissingElement", "<description>, map \"%s\"",map);
cristy5f9f2462011-09-28 23:37:58 +00001162 thresholds=DestroyXMLTree(thresholds);
1163 return(MagickFalse);
1164 }
cristy3ed852e2009-09-05 21:47:34 +00001165 content=GetXMLTreeContent(description);
cristy5f9f2462011-09-28 23:37:58 +00001166 if (content == (char *) NULL)
1167 {
1168 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1169 "XmlMissingContent", "<description>, map \"%s\"", map);
1170 thresholds=DestroyXMLTree(thresholds);
1171 return(MagickFalse);
1172 }
cristy1e604812011-05-19 18:07:50 +00001173 (void) FormatLocaleFile(file,"%-16s %-12s %s\n",map,alias ? alias : "",
1174 content);
cristy3ed852e2009-09-05 21:47:34 +00001175 }
1176 thresholds=DestroyXMLTree(thresholds);
1177 return(MagickTrue);
1178}
1179
1180/*
1181%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1182% %
1183% %
1184% %
1185% L i s t T h r e s h o l d M a p s %
1186% %
1187% %
1188% %
1189%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1190%
1191% ListThresholdMaps() lists the threshold maps and their descriptions
1192% as defined by "threshold.xml" to a file.
1193%
1194% The format of the ListThresholdMaps method is:
1195%
1196% MagickBooleanType ListThresholdMaps(FILE *file,ExceptionInfo *exception)
1197%
1198% A description of each parameter follows.
1199%
1200% o file: An pointer to the output FILE.
1201%
1202% o exception: return any errors or warnings in this structure.
1203%
1204*/
1205MagickExport MagickBooleanType ListThresholdMaps(FILE *file,
1206 ExceptionInfo *exception)
1207{
1208 const StringInfo
1209 *option;
1210
1211 LinkedListInfo
1212 *options;
1213
1214 MagickStatusType
1215 status;
1216
cristy35892192014-05-26 12:04:36 +00001217 status=MagickTrue;
cristy5f9f2462011-09-28 23:37:58 +00001218 if (file == (FILE *) NULL)
1219 file=stdout;
cristy3ed852e2009-09-05 21:47:34 +00001220 options=GetConfigureOptions(ThresholdsFilename,exception);
cristy1e604812011-05-19 18:07:50 +00001221 (void) FormatLocaleFile(file,
1222 "\n Threshold Maps for Ordered Dither Operations\n");
cristy1d755c42013-02-26 12:38:44 +00001223 option=(const StringInfo *) GetNextValueInLinkedList(options);
1224 while (option != (const StringInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001225 {
cristy1d755c42013-02-26 12:38:44 +00001226 (void) FormatLocaleFile(file,"\nPath: %s\n\n",GetStringInfoPath(option));
cristyaae13f62013-08-15 14:41:32 +00001227 status&=ListThresholdMapFile(file,(const char *) GetStringInfoDatum(option),
cristy3ed852e2009-09-05 21:47:34 +00001228 GetStringInfoPath(option),exception);
cristy1d755c42013-02-26 12:38:44 +00001229 option=(const StringInfo *) GetNextValueInLinkedList(options);
cristy3ed852e2009-09-05 21:47:34 +00001230 }
1231 options=DestroyConfigureOptions(options);
1232 return(status != 0 ? MagickTrue : MagickFalse);
1233}
1234
1235/*
1236%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1237% %
1238% %
1239% %
cristy3ed852e2009-09-05 21:47:34 +00001240% O r d e r e d P o s t e r i z e I m a g e %
1241% %
1242% %
1243% %
1244%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1245%
1246% OrderedPosterizeImage() will perform a ordered dither based on a number
1247% of pre-defined dithering threshold maps, but over multiple intensity
1248% levels, which can be different for different channels, according to the
1249% input argument.
1250%
1251% The format of the OrderedPosterizeImage method is:
1252%
1253% MagickBooleanType OrderedPosterizeImage(Image *image,
1254% const char *threshold_map,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001255%
1256% A description of each parameter follows:
1257%
1258% o image: the image.
1259%
cristy3ed852e2009-09-05 21:47:34 +00001260% o threshold_map: A string containing the name of the threshold dither
1261% map to use, followed by zero or more numbers representing the number
1262% of color levels tho dither between.
1263%
cristyf998fb32011-04-27 23:00:47 +00001264% Any level number less than 2 will be equivalent to 2, and means only
cristy3ed852e2009-09-05 21:47:34 +00001265% binary dithering will be applied to each color channel.
1266%
1267% No numbers also means a 2 level (bitmap) dither will be applied to all
1268% channels, while a single number is the number of levels applied to each
1269% channel in sequence. More numbers will be applied in turn to each of
1270% the color channels.
1271%
1272% For example: "o3x3,6" will generate a 6 level posterization of the
1273% image with a ordered 3x3 diffused pixel dither being applied between
1274% each level. While checker,8,8,4 will produce a 332 colormaped image
1275% with only a single checkerboard hash pattern (50% grey) between each
1276% color level, to basically double the number of color levels with
1277% a bare minimim of dithering.
1278%
1279% o exception: return any errors or warnings in this structure.
1280%
1281*/
1282MagickExport MagickBooleanType OrderedPosterizeImage(Image *image,
1283 const char *threshold_map,ExceptionInfo *exception)
1284{
cristy3ed852e2009-09-05 21:47:34 +00001285#define DitherImageTag "Dither/Image"
1286
cristyc4c8d132010-01-07 01:58:38 +00001287 CacheView
1288 *image_view;
1289
cristy4e0b82a2011-09-29 12:47:44 +00001290 char
cristy151b66d2015-04-15 10:50:31 +00001291 token[MagickPathExtent];
cristy4e0b82a2011-09-29 12:47:44 +00001292
1293 const char
1294 *p;
cristy3ed852e2009-09-05 21:47:34 +00001295
cristy311eb742013-02-25 19:55:37 +00001296 double
1297 levels[CompositePixelChannel];
1298
cristy3ed852e2009-09-05 21:47:34 +00001299 MagickBooleanType
1300 status;
1301
cristy5f959472010-05-27 22:19:46 +00001302 MagickOffsetType
1303 progress;
1304
cristy4e0b82a2011-09-29 12:47:44 +00001305 register ssize_t
1306 i;
1307
cristy5f959472010-05-27 22:19:46 +00001308 ssize_t
1309 y;
1310
cristy3ed852e2009-09-05 21:47:34 +00001311 ThresholdMap
1312 *map;
1313
cristy3ed852e2009-09-05 21:47:34 +00001314 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001315 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001316 if (image->debug != MagickFalse)
1317 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1318 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001319 assert(exception->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001320 if (threshold_map == (const char *) NULL)
1321 return(MagickTrue);
cristy4e0b82a2011-09-29 12:47:44 +00001322 p=(char *) threshold_map;
1323 while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
1324 (*p != '\0'))
1325 p++;
1326 threshold_map=p;
1327 while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
1328 (*p != '\0'))
cristy3ed852e2009-09-05 21:47:34 +00001329 {
cristy151b66d2015-04-15 10:50:31 +00001330 if ((p-threshold_map) >= (MagickPathExtent-1))
cristy4e0b82a2011-09-29 12:47:44 +00001331 break;
1332 token[p-threshold_map]=(*p);
1333 p++;
cristy3ed852e2009-09-05 21:47:34 +00001334 }
cristy4e0b82a2011-09-29 12:47:44 +00001335 token[p-threshold_map]='\0';
1336 map=GetThresholdMap(token,exception);
1337 if (map == (ThresholdMap *) NULL)
1338 {
1339 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1340 "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
cristy574cc262011-08-05 01:23:58 +00001341 return(MagickFalse);
cristy4e0b82a2011-09-29 12:47:44 +00001342 }
1343 for (i=0; i < MaxPixelChannels; i++)
1344 levels[i]=2.0;
1345 p=strchr((char *) threshold_map,',');
1346 if ((p != (char *) NULL) && (isdigit((int) ((unsigned char) *(++p))) != 0))
1347 for (i=0; (*p != '\0') && (i < MaxPixelChannels); i++)
1348 {
1349 GetMagickToken(p,&p,token);
1350 if (*token == ',')
1351 GetMagickToken(p,&p,token);
cristydbdd0e32011-11-04 23:29:40 +00001352 levels[i]=StringToDouble(token,(char **) NULL);
cristy4e0b82a2011-09-29 12:47:44 +00001353 }
1354 for (i=0; i < MaxPixelChannels; i++)
1355 if (fabs(levels[i]) >= 1)
1356 levels[i]-=1.0;
1357 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1358 return(MagickFalse);
1359 status=MagickTrue;
1360 progress=0;
cristy46ff2672012-12-14 15:32:26 +00001361 image_view=AcquireAuthenticCacheView(image,exception);
cristyb5d5f722009-11-04 03:03:49 +00001362#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd065992012-08-09 15:27:39 +00001363 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +00001364 magick_threads(image,image,image->rows,1)
cristy3ed852e2009-09-05 21:47:34 +00001365#endif
cristy4e0b82a2011-09-29 12:47:44 +00001366 for (y=0; y < (ssize_t) image->rows; y++)
1367 {
1368 register ssize_t
1369 x;
1370
1371 register Quantum
1372 *restrict q;
1373
1374 if (status == MagickFalse)
1375 continue;
1376 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1377 if (q == (Quantum *) NULL)
1378 {
1379 status=MagickFalse;
1380 continue;
1381 }
1382 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001383 {
cristybb503372010-05-27 20:51:26 +00001384 register ssize_t
cristy4e0b82a2011-09-29 12:47:44 +00001385 i;
cristy3ed852e2009-09-05 21:47:34 +00001386
cristy4e0b82a2011-09-29 12:47:44 +00001387 ssize_t
1388 n;
cristy3ed852e2009-09-05 21:47:34 +00001389
cristy4e0b82a2011-09-29 12:47:44 +00001390 n=0;
cristy883fde12013-04-08 00:50:13 +00001391 if (GetPixelReadMask(image,q) == 0)
cristy10a6c612012-01-29 21:41:05 +00001392 {
1393 q+=GetPixelChannels(image);
1394 continue;
1395 }
cristy4e0b82a2011-09-29 12:47:44 +00001396 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
cristy3ed852e2009-09-05 21:47:34 +00001397 {
cristy4e0b82a2011-09-29 12:47:44 +00001398 ssize_t
cristy3f5d8152011-09-29 13:00:19 +00001399 level,
1400 threshold;
cristy3ed852e2009-09-05 21:47:34 +00001401
cristy5a23c552013-02-13 14:34:28 +00001402 PixelChannel channel=GetPixelChannelChannel(image,i);
1403 PixelTrait traits=GetPixelChannelTraits(image,channel);
cristy4e0b82a2011-09-29 12:47:44 +00001404 if ((traits & UpdatePixelTrait) == 0)
1405 continue;
cristy3f5d8152011-09-29 13:00:19 +00001406 if (fabs(levels[n++]) < MagickEpsilon)
1407 continue;
1408 threshold=(ssize_t) (QuantumScale*q[i]*(levels[n]*(map->divisor-1)+1));
1409 level=threshold/(map->divisor-1);
1410 threshold-=level*(map->divisor-1);
cristyada285b2012-07-07 19:00:46 +00001411 q[i]=ClampToQuantum((double) (level+(threshold >=
cristye42f6582012-02-11 17:59:50 +00001412 map->levels[(x % map->width)+map->width*(y % map->height)]))*
1413 QuantumRange/levels[n]);
cristy4e0b82a2011-09-29 12:47:44 +00001414 n++;
cristy3ed852e2009-09-05 21:47:34 +00001415 }
cristy4e0b82a2011-09-29 12:47:44 +00001416 q+=GetPixelChannels(image);
1417 }
1418 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1419 status=MagickFalse;
1420 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1421 {
1422 MagickBooleanType
1423 proceed;
cristy3ed852e2009-09-05 21:47:34 +00001424
cristyb5d5f722009-11-04 03:03:49 +00001425#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00001426 #pragma omp critical (MagickCore_OrderedPosterizeImage)
cristy3ed852e2009-09-05 21:47:34 +00001427#endif
cristy4e0b82a2011-09-29 12:47:44 +00001428 proceed=SetImageProgress(image,DitherImageTag,progress++,image->rows);
1429 if (proceed == MagickFalse)
1430 status=MagickFalse;
1431 }
cristy3ed852e2009-09-05 21:47:34 +00001432 }
cristy4e0b82a2011-09-29 12:47:44 +00001433 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +00001434 map=DestroyThresholdMap(map);
1435 return(MagickTrue);
1436}
1437
1438/*
1439%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1440% %
1441% %
1442% %
cristy7884a932012-11-04 14:33:51 +00001443% P e r c e p t i b l e I m a g e %
1444% %
1445% %
1446% %
1447%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1448%
cristy1d755c42013-02-26 12:38:44 +00001449% PerceptibleImage() set each pixel whose value is less than |epsilon| to
1450% epsilon or -epsilon (whichever is closer) otherwise the pixel value remains
cristy7884a932012-11-04 14:33:51 +00001451% unchanged.
1452%
1453% The format of the PerceptibleImage method is:
1454%
1455% MagickBooleanType PerceptibleImage(Image *image,const double epsilon,
1456% ExceptionInfo *exception)
1457%
1458% A description of each parameter follows:
1459%
1460% o image: the image.
1461%
1462% o epsilon: the epsilon threshold (e.g. 1.0e-9).
1463%
1464% o exception: return any errors or warnings in this structure.
1465%
1466*/
1467
1468static inline Quantum PerceptibleThreshold(const Quantum quantum,
1469 const double epsilon)
1470{
1471 double
1472 sign;
1473
1474 sign=(double) quantum < 0.0 ? -1.0 : 1.0;
1475 if ((sign*quantum) >= epsilon)
1476 return(quantum);
1477 return((Quantum) (sign*epsilon));
1478}
1479
1480MagickExport MagickBooleanType PerceptibleImage(Image *image,
1481 const double epsilon,ExceptionInfo *exception)
1482{
1483#define PerceptibleImageTag "Perceptible/Image"
1484
1485 CacheView
1486 *image_view;
1487
1488 MagickBooleanType
1489 status;
1490
1491 MagickOffsetType
1492 progress;
1493
1494 ssize_t
1495 y;
1496
1497 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001498 assert(image->signature == MagickCoreSignature);
cristy7884a932012-11-04 14:33:51 +00001499 if (image->debug != MagickFalse)
1500 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1501 if (image->storage_class == PseudoClass)
1502 {
1503 register ssize_t
1504 i;
1505
1506 register PixelInfo
1507 *restrict q;
1508
1509 q=image->colormap;
1510 for (i=0; i < (ssize_t) image->colors; i++)
1511 {
1512 q->red=(double) PerceptibleThreshold(ClampToQuantum(q->red),
1513 epsilon);
1514 q->green=(double) PerceptibleThreshold(ClampToQuantum(q->green),
1515 epsilon);
1516 q->blue=(double) PerceptibleThreshold(ClampToQuantum(q->blue),
1517 epsilon);
1518 q->alpha=(double) PerceptibleThreshold(ClampToQuantum(q->alpha),
1519 epsilon);
1520 q++;
1521 }
1522 return(SyncImage(image,exception));
1523 }
1524 /*
1525 Perceptible image.
1526 */
1527 status=MagickTrue;
1528 progress=0;
cristy46ff2672012-12-14 15:32:26 +00001529 image_view=AcquireAuthenticCacheView(image,exception);
cristy7884a932012-11-04 14:33:51 +00001530#if defined(MAGICKCORE_OPENMP_SUPPORT)
1531 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +00001532 magick_threads(image,image,image->rows,1)
cristy7884a932012-11-04 14:33:51 +00001533#endif
1534 for (y=0; y < (ssize_t) image->rows; y++)
1535 {
1536 register ssize_t
1537 x;
1538
1539 register Quantum
1540 *restrict q;
1541
1542 if (status == MagickFalse)
1543 continue;
1544 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1545 if (q == (Quantum *) NULL)
1546 {
1547 status=MagickFalse;
1548 continue;
1549 }
1550 for (x=0; x < (ssize_t) image->columns; x++)
1551 {
1552 register ssize_t
1553 i;
1554
cristy883fde12013-04-08 00:50:13 +00001555 if (GetPixelReadMask(image,q) == 0)
cristy7884a932012-11-04 14:33:51 +00001556 {
1557 q+=GetPixelChannels(image);
1558 continue;
1559 }
1560 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1561 {
cristy5a23c552013-02-13 14:34:28 +00001562 PixelChannel channel=GetPixelChannelChannel(image,i);
1563 PixelTrait traits=GetPixelChannelTraits(image,channel);
cristy7884a932012-11-04 14:33:51 +00001564 if (traits == UndefinedPixelTrait)
1565 continue;
1566 q[i]=PerceptibleThreshold(q[i],epsilon);
1567 }
1568 q+=GetPixelChannels(image);
1569 }
1570 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1571 status=MagickFalse;
1572 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1573 {
1574 MagickBooleanType
1575 proceed;
1576
1577#if defined(MAGICKCORE_OPENMP_SUPPORT)
1578 #pragma omp critical (MagickCore_PerceptibleImage)
1579#endif
1580 proceed=SetImageProgress(image,PerceptibleImageTag,progress++,image->rows);
1581 if (proceed == MagickFalse)
1582 status=MagickFalse;
1583 }
1584 }
1585 image_view=DestroyCacheView(image_view);
1586 return(status);
1587}
1588
1589/*
1590%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1591% %
1592% %
1593% %
cristy3ed852e2009-09-05 21:47:34 +00001594% R a n d o m T h r e s h o l d I m a g e %
1595% %
1596% %
1597% %
1598%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1599%
1600% RandomThresholdImage() changes the value of individual pixels based on the
1601% intensity of each pixel compared to a random threshold. The result is a
1602% low-contrast, two color image.
1603%
1604% The format of the RandomThresholdImage method is:
1605%
cristyf4ad9df2011-07-08 16:49:03 +00001606% MagickBooleanType RandomThresholdImage(Image *image,
cristy3ed852e2009-09-05 21:47:34 +00001607% const char *thresholds,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001608%
1609% A description of each parameter follows:
1610%
1611% o image: the image.
1612%
cristy3ed852e2009-09-05 21:47:34 +00001613% o thresholds: a geometry string containing low,high thresholds. If the
1614% string contains 2x2, 3x3, or 4x4, an ordered dither of order 2, 3, or 4
1615% is performed instead.
1616%
1617% o exception: return any errors or warnings in this structure.
1618%
1619*/
cristy3ed852e2009-09-05 21:47:34 +00001620MagickExport MagickBooleanType RandomThresholdImage(Image *image,
1621 const char *thresholds,ExceptionInfo *exception)
1622{
cristy3ed852e2009-09-05 21:47:34 +00001623#define ThresholdImageTag "Threshold/Image"
1624
cristyfa112112010-01-04 17:48:07 +00001625 CacheView
1626 *image_view;
1627
cristy311eb742013-02-25 19:55:37 +00001628 double
1629 min_threshold,
1630 max_threshold;
1631
cristy3ed852e2009-09-05 21:47:34 +00001632 GeometryInfo
1633 geometry_info;
1634
1635 MagickStatusType
1636 flags;
1637
cristy3ed852e2009-09-05 21:47:34 +00001638 MagickBooleanType
1639 status;
1640
cristy5f959472010-05-27 22:19:46 +00001641 MagickOffsetType
1642 progress;
1643
cristy4c08aed2011-07-01 19:47:50 +00001644 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00001645 threshold;
1646
cristy3ed852e2009-09-05 21:47:34 +00001647 RandomInfo
cristyfa112112010-01-04 17:48:07 +00001648 **restrict random_info;
cristy3ed852e2009-09-05 21:47:34 +00001649
cristy5f959472010-05-27 22:19:46 +00001650 ssize_t
1651 y;
1652
glennrpb36143f2012-09-24 18:26:55 +00001653#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy57340e02012-05-05 00:53:23 +00001654 unsigned long
1655 key;
glennrpb36143f2012-09-24 18:26:55 +00001656#endif
cristy57340e02012-05-05 00:53:23 +00001657
cristy3ed852e2009-09-05 21:47:34 +00001658 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001659 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001660 if (image->debug != MagickFalse)
1661 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1662 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001663 assert(exception->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001664 if (thresholds == (const char *) NULL)
1665 return(MagickTrue);
cristye7452652012-04-14 01:34:21 +00001666 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1667 return(MagickFalse);
cristy4c08aed2011-07-01 19:47:50 +00001668 GetPixelInfo(image,&threshold);
cristy3ed852e2009-09-05 21:47:34 +00001669 min_threshold=0.0;
cristya19f1d72012-08-07 18:24:38 +00001670 max_threshold=(double) QuantumRange;
cristy3ed852e2009-09-05 21:47:34 +00001671 flags=ParseGeometry(thresholds,&geometry_info);
1672 min_threshold=geometry_info.rho;
1673 max_threshold=geometry_info.sigma;
1674 if ((flags & SigmaValue) == 0)
1675 max_threshold=min_threshold;
1676 if (strchr(thresholds,'%') != (char *) NULL)
1677 {
cristya19f1d72012-08-07 18:24:38 +00001678 max_threshold*=(double) (0.01*QuantumRange);
1679 min_threshold*=(double) (0.01*QuantumRange);
cristy3ed852e2009-09-05 21:47:34 +00001680 }
cristy3ed852e2009-09-05 21:47:34 +00001681 /*
1682 Random threshold image.
1683 */
1684 status=MagickTrue;
1685 progress=0;
cristy3ed852e2009-09-05 21:47:34 +00001686 random_info=AcquireRandomInfoThreadSet();
cristy46ff2672012-12-14 15:32:26 +00001687 image_view=AcquireAuthenticCacheView(image,exception);
cristyb5d5f722009-11-04 03:03:49 +00001688#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyecd6dd12015-02-12 23:08:58 +00001689 key=GetRandomSecretKey(random_info[0]);
cristybd065992012-08-09 15:27:39 +00001690 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +00001691 magick_threads(image,image,image->rows,key == ~0UL)
cristy3ed852e2009-09-05 21:47:34 +00001692#endif
cristybb503372010-05-27 20:51:26 +00001693 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001694 {
cristy5c9e6f22010-09-17 17:31:01 +00001695 const int
1696 id = GetOpenMPThreadId();
cristy6ebe97c2010-07-03 01:17:28 +00001697
cristy4c08aed2011-07-01 19:47:50 +00001698 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001699 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001700
cristy5c9e6f22010-09-17 17:31:01 +00001701 register ssize_t
1702 x;
1703
cristy3ed852e2009-09-05 21:47:34 +00001704 if (status == MagickFalse)
1705 continue;
1706 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001707 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001708 {
1709 status=MagickFalse;
1710 continue;
1711 }
cristybb503372010-05-27 20:51:26 +00001712 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001713 {
cristy5f9f2462011-09-28 23:37:58 +00001714 register ssize_t
1715 i;
1716
cristy883fde12013-04-08 00:50:13 +00001717 if (GetPixelReadMask(image,q) == 0)
cristy10a6c612012-01-29 21:41:05 +00001718 {
1719 q+=GetPixelChannels(image);
1720 continue;
1721 }
cristy5f9f2462011-09-28 23:37:58 +00001722 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1723 {
cristya19f1d72012-08-07 18:24:38 +00001724 double
cristy5f9f2462011-09-28 23:37:58 +00001725 threshold;
1726
cristy5a23c552013-02-13 14:34:28 +00001727 PixelChannel channel=GetPixelChannelChannel(image,i);
1728 PixelTrait traits=GetPixelChannelTraits(image,channel);
cristy5f9f2462011-09-28 23:37:58 +00001729 if ((traits & UpdatePixelTrait) == 0)
1730 continue;
cristya19f1d72012-08-07 18:24:38 +00001731 if ((double) q[i] < min_threshold)
cristy5f9f2462011-09-28 23:37:58 +00001732 threshold=min_threshold;
1733 else
cristya19f1d72012-08-07 18:24:38 +00001734 if ((double) q[i] > max_threshold)
cristy5f9f2462011-09-28 23:37:58 +00001735 threshold=max_threshold;
cristy3ed852e2009-09-05 21:47:34 +00001736 else
cristya19f1d72012-08-07 18:24:38 +00001737 threshold=(double) (QuantumRange*
cristy5f9f2462011-09-28 23:37:58 +00001738 GetPseudoRandomValue(random_info[id]));
cristya19f1d72012-08-07 18:24:38 +00001739 q[i]=(double) q[i] <= threshold ? 0 : QuantumRange;
cristy5f9f2462011-09-28 23:37:58 +00001740 }
cristyed231572011-07-14 02:18:59 +00001741 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001742 }
1743 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1744 status=MagickFalse;
1745 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1746 {
1747 MagickBooleanType
1748 proceed;
1749
cristyb5d5f722009-11-04 03:03:49 +00001750#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00001751 #pragma omp critical (MagickCore_RandomThresholdImage)
cristy3ed852e2009-09-05 21:47:34 +00001752#endif
1753 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1754 image->rows);
1755 if (proceed == MagickFalse)
1756 status=MagickFalse;
1757 }
1758 }
1759 image_view=DestroyCacheView(image_view);
1760 random_info=DestroyRandomInfoThreadSet(random_info);
1761 return(status);
1762}
1763
1764/*
1765%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1766% %
1767% %
1768% %
1769% W h i t e T h r e s h o l d I m a g e %
1770% %
1771% %
1772% %
1773%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1774%
1775% WhiteThresholdImage() is like ThresholdImage() but forces all pixels above
cristy4e101302009-09-17 12:49:12 +00001776% the threshold into white while leaving all pixels at or below the threshold
cristy3ed852e2009-09-05 21:47:34 +00001777% unchanged.
1778%
1779% The format of the WhiteThresholdImage method is:
1780%
cristyf4ad9df2011-07-08 16:49:03 +00001781% MagickBooleanType WhiteThresholdImage(Image *image,
1782% const char *threshold,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001783%
1784% A description of each parameter follows:
1785%
1786% o image: the image.
1787%
cristy3ed852e2009-09-05 21:47:34 +00001788% o threshold: Define the threshold value.
1789%
1790% o exception: return any errors or warnings in this structure.
1791%
1792*/
1793MagickExport MagickBooleanType WhiteThresholdImage(Image *image,
cristyf4ad9df2011-07-08 16:49:03 +00001794 const char *thresholds,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001795{
1796#define ThresholdImageTag "Threshold/Image"
1797
cristy5f959472010-05-27 22:19:46 +00001798 CacheView
1799 *image_view;
1800
cristy3ed852e2009-09-05 21:47:34 +00001801 GeometryInfo
1802 geometry_info;
1803
cristy3ed852e2009-09-05 21:47:34 +00001804 MagickBooleanType
1805 status;
1806
cristy5f959472010-05-27 22:19:46 +00001807 MagickOffsetType
1808 progress;
1809
cristyd6803382012-04-10 01:41:25 +00001810 PixelInfo
cristya12d8ba2012-04-29 16:33:41 +00001811 threshold;
cristy5f9f2462011-09-28 23:37:58 +00001812
cristy3ed852e2009-09-05 21:47:34 +00001813 MagickStatusType
1814 flags;
1815
cristy5f959472010-05-27 22:19:46 +00001816 ssize_t
1817 y;
cristy3ed852e2009-09-05 21:47:34 +00001818
1819 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001820 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001821 if (image->debug != MagickFalse)
1822 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1823 if (thresholds == (const char *) NULL)
1824 return(MagickTrue);
cristy574cc262011-08-05 01:23:58 +00001825 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001826 return(MagickFalse);
cristy23e55c02012-04-10 01:21:56 +00001827 if (IsGrayColorspace(image->colorspace) != MagickFalse)
cristy6c312f02013-05-19 22:12:34 +00001828 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristya12d8ba2012-04-29 16:33:41 +00001829 GetPixelInfo(image,&threshold);
cristy3ed852e2009-09-05 21:47:34 +00001830 flags=ParseGeometry(thresholds,&geometry_info);
cristya12d8ba2012-04-29 16:33:41 +00001831 threshold.red=geometry_info.rho;
1832 threshold.green=geometry_info.rho;
1833 threshold.blue=geometry_info.rho;
1834 threshold.black=geometry_info.rho;
1835 threshold.alpha=100.0;
cristy5f9f2462011-09-28 23:37:58 +00001836 if ((flags & SigmaValue) != 0)
cristya12d8ba2012-04-29 16:33:41 +00001837 threshold.green=geometry_info.sigma;
cristy5f9f2462011-09-28 23:37:58 +00001838 if ((flags & XiValue) != 0)
cristya12d8ba2012-04-29 16:33:41 +00001839 threshold.blue=geometry_info.xi;
cristy5f9f2462011-09-28 23:37:58 +00001840 if ((flags & PsiValue) != 0)
cristya12d8ba2012-04-29 16:33:41 +00001841 threshold.alpha=geometry_info.psi;
1842 if (threshold.colorspace == CMYKColorspace)
cristyd6803382012-04-10 01:41:25 +00001843 {
1844 if ((flags & PsiValue) != 0)
cristya12d8ba2012-04-29 16:33:41 +00001845 threshold.black=geometry_info.psi;
cristyd6803382012-04-10 01:41:25 +00001846 if ((flags & ChiValue) != 0)
cristya12d8ba2012-04-29 16:33:41 +00001847 threshold.alpha=geometry_info.chi;
1848 }
1849 if ((flags & PercentValue) != 0)
1850 {
cristy65d4e5e2012-10-17 12:22:24 +00001851 threshold.red*=(MagickRealType) (QuantumRange/100.0);
1852 threshold.green*=(MagickRealType) (QuantumRange/100.0);
1853 threshold.blue*=(MagickRealType) (QuantumRange/100.0);
1854 threshold.black*=(MagickRealType) (QuantumRange/100.0);
1855 threshold.alpha*=(MagickRealType) (QuantumRange/100.0);
cristyd6803382012-04-10 01:41:25 +00001856 }
cristy3ed852e2009-09-05 21:47:34 +00001857 /*
1858 White threshold image.
1859 */
1860 status=MagickTrue;
1861 progress=0;
cristy46ff2672012-12-14 15:32:26 +00001862 image_view=AcquireAuthenticCacheView(image,exception);
cristyb5d5f722009-11-04 03:03:49 +00001863#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd065992012-08-09 15:27:39 +00001864 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +00001865 magick_threads(image,image,image->rows,1)
cristy3ed852e2009-09-05 21:47:34 +00001866#endif
cristybb503372010-05-27 20:51:26 +00001867 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001868 {
cristybb503372010-05-27 20:51:26 +00001869 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001870 x;
1871
cristy4c08aed2011-07-01 19:47:50 +00001872 register Quantum
cristyc47d1f82009-11-26 01:44:43 +00001873 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001874
1875 if (status == MagickFalse)
1876 continue;
1877 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001878 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001879 {
1880 status=MagickFalse;
1881 continue;
1882 }
cristybb503372010-05-27 20:51:26 +00001883 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001884 {
cristy81629aa2012-07-12 20:08:52 +00001885 double
1886 pixel;
1887
cristy188f29a2012-06-24 19:09:53 +00001888 register ssize_t
1889 i;
1890
cristy883fde12013-04-08 00:50:13 +00001891 if (GetPixelReadMask(image,q) == 0)
cristy10a6c612012-01-29 21:41:05 +00001892 {
1893 q+=GetPixelChannels(image);
1894 continue;
1895 }
cristyf13c5942012-08-08 23:50:11 +00001896 pixel=GetPixelIntensity(image,q);
cristy188f29a2012-06-24 19:09:53 +00001897 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1898 {
cristy5a23c552013-02-13 14:34:28 +00001899 PixelChannel channel=GetPixelChannelChannel(image,i);
1900 PixelTrait traits=GetPixelChannelTraits(image,channel);
cristy188f29a2012-06-24 19:09:53 +00001901 if ((traits & UpdatePixelTrait) == 0)
1902 continue;
cristy81629aa2012-07-12 20:08:52 +00001903 if (image->channel_mask != DefaultChannels)
cristyf13c5942012-08-08 23:50:11 +00001904 pixel=(double) q[i];
cristy81629aa2012-07-12 20:08:52 +00001905 if (pixel > GetPixelInfoChannel(&threshold,channel))
cristy188f29a2012-06-24 19:09:53 +00001906 q[i]=QuantumRange;
1907 }
cristyed231572011-07-14 02:18:59 +00001908 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001909 }
1910 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1911 status=MagickFalse;
1912 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1913 {
1914 MagickBooleanType
1915 proceed;
1916
cristyb5d5f722009-11-04 03:03:49 +00001917#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristya12d8ba2012-04-29 16:33:41 +00001918 #pragma omp critical (MagickCore_WhiteThresholdImage)
cristy3ed852e2009-09-05 21:47:34 +00001919#endif
1920 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1921 image->rows);
1922 if (proceed == MagickFalse)
1923 status=MagickFalse;
1924 }
1925 }
1926 image_view=DestroyCacheView(image_view);
1927 return(status);
1928}