blob: 85cbd80ee8792d1f12b5fdd12a5df77cc4d471d1 [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% %
20% Copyright 1999-2009 ImageMagick Studio LLC, a non-profit organization %
21% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37%
38*/
39
40/*
41 Include declarations.
42*/
43#include "magick/studio.h"
44#include "magick/property.h"
45#include "magick/blob.h"
46#include "magick/cache-view.h"
47#include "magick/color.h"
48#include "magick/color-private.h"
49#include "magick/colorspace.h"
50#include "magick/configure.h"
51#include "magick/constitute.h"
52#include "magick/decorate.h"
53#include "magick/draw.h"
54#include "magick/enhance.h"
55#include "magick/exception.h"
56#include "magick/exception-private.h"
57#include "magick/effect.h"
58#include "magick/fx.h"
59#include "magick/gem.h"
60#include "magick/geometry.h"
61#include "magick/image-private.h"
62#include "magick/list.h"
63#include "magick/log.h"
64#include "magick/memory_.h"
65#include "magick/monitor.h"
66#include "magick/monitor-private.h"
67#include "magick/montage.h"
68#include "magick/pixel-private.h"
69#include "magick/quantize.h"
70#include "magick/quantum.h"
71#include "magick/random_.h"
72#include "magick/random-private.h"
73#include "magick/resize.h"
74#include "magick/resource_.h"
75#include "magick/segment.h"
76#include "magick/shear.h"
77#include "magick/signature-private.h"
78#include "magick/string_.h"
79#include "magick/transform.h"
80#include "magick/threshold.h"
81#include "magick/option.h"
82#include "magick/xml-tree.h"
83
84/*
85 Define declarations.
86*/
87#define ThresholdsFilename "thresholds.xml"
88
89/*
90 Typedef declarations.
91*/
92struct _ThresholdMap
93{
94 char
95 *map_id,
96 *description;
97
98 unsigned long
99 width,
100 height;
101
102 long
103 divisor,
104 *levels;
105};
106
107/*
108%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
109% %
110% %
111% %
112% A d a p t i v e T h r e s h o l d I m a g e %
113% %
114% %
115% %
116%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
117%
118% AdaptiveThresholdImage() selects an individual threshold for each pixel
119% based on the range of intensity values in its local neighborhood. This
120% allows for thresholding of an image whose global intensity histogram
121% doesn't contain distinctive peaks.
122%
123% The format of the AdaptiveThresholdImage method is:
124%
125% Image *AdaptiveThresholdImage(const Image *image,
126% const unsigned long width,const unsigned long height,
127% const long offset,ExceptionInfo *exception)
128%
129% A description of each parameter follows:
130%
131% o image: the image.
132%
133% o width: the width of the local neighborhood.
134%
135% o height: the height of the local neighborhood.
136%
137% o offset: the mean offset.
138%
139% o exception: return any errors or warnings in this structure.
140%
141*/
142MagickExport Image *AdaptiveThresholdImage(const Image *image,
143 const unsigned long width,const unsigned long height,const long offset,
144 ExceptionInfo *exception)
145{
146#define ThresholdImageTag "Threshold/Image"
147
148 Image
149 *threshold_image;
150
151 long
152 progress,
153 y;
154
155 MagickBooleanType
156 status;
157
158 MagickPixelPacket
159 zero;
160
161 MagickRealType
162 number_pixels;
163
164 CacheView
165 *image_view,
166 *threshold_view;
167
168 assert(image != (const Image *) NULL);
169 assert(image->signature == MagickSignature);
170 if (image->debug != MagickFalse)
171 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
172 assert(exception != (ExceptionInfo *) NULL);
173 assert(exception->signature == MagickSignature);
174 if ((image->columns < width) || (image->rows < height))
175 ThrowImageException(OptionError,"ImageSmallerThanRadius");
176 threshold_image=CloneImage(image,0,0,MagickTrue,exception);
177 if (threshold_image == (Image *) NULL)
178 return((Image *) NULL);
179 if (SetImageStorageClass(threshold_image,DirectClass) == MagickFalse)
180 {
181 InheritException(exception,&threshold_image->exception);
182 threshold_image=DestroyImage(threshold_image);
183 return((Image *) NULL);
184 }
185 /*
186 Local adaptive threshold.
187 */
188 status=MagickTrue;
189 progress=0;
190 GetMagickPixelPacket(image,&zero);
191 number_pixels=(MagickRealType) width*height;
192 image_view=AcquireCacheView(image);
193 threshold_view=AcquireCacheView(threshold_image);
cristyb5d5f722009-11-04 03:03:49 +0000194#if defined(MAGICKCORE_OPENMP_SUPPORT)
195 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000196#endif
197 for (y=0; y < (long) image->rows; y++)
198 {
199 MagickBooleanType
200 sync;
201
202 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000203 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000204
205 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000206 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000207
208 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000209 *restrict threshold_indexes;
cristy3ed852e2009-09-05 21:47:34 +0000210
211 register long
212 x;
213
214 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000215 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000216
217 if (status == MagickFalse)
218 continue;
219 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-height/2L,
220 image->columns+width,height,exception);
221 q=GetCacheViewAuthenticPixels(threshold_view,0,y,threshold_image->columns,1,
222 exception);
223 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
224 {
225 status=MagickFalse;
226 continue;
227 }
228 indexes=GetCacheViewVirtualIndexQueue(image_view);
229 threshold_indexes=GetCacheViewAuthenticIndexQueue(threshold_view);
230 for (x=0; x < (long) image->columns; x++)
231 {
232 long
233 v;
234
235 MagickPixelPacket
236 mean,
237 pixel;
238
239 register const PixelPacket
240 *r;
241
242 register long
243 u;
244
245 pixel=zero;
246 mean=zero;
247 r=p;
248 for (v=0; v < (long) height; v++)
249 {
250 for (u=0; u < (long) width; u++)
251 {
252 pixel.red+=r[u].red;
253 pixel.green+=r[u].green;
254 pixel.blue+=r[u].blue;
255 pixel.opacity+=r[u].opacity;
256 if (image->colorspace == CMYKColorspace)
257 pixel.index=(MagickRealType) indexes[x+(r-p)+u];
258 }
259 r+=image->columns+width;
260 }
261 mean.red=(MagickRealType) (pixel.red/number_pixels+offset);
262 mean.green=(MagickRealType) (pixel.green/number_pixels+offset);
263 mean.blue=(MagickRealType) (pixel.blue/number_pixels+offset);
264 mean.opacity=(MagickRealType) (pixel.opacity/number_pixels+offset);
265 if (image->colorspace == CMYKColorspace)
266 mean.index=(MagickRealType) (pixel.index/number_pixels+offset);
267 q->red=(Quantum) (((MagickRealType) q->red <= mean.red) ?
268 0 : QuantumRange);
269 q->green=(Quantum) (((MagickRealType) q->green <= mean.green) ?
270 0 : QuantumRange);
271 q->blue=(Quantum) (((MagickRealType) q->blue <= mean.blue) ?
272 0 : QuantumRange);
273 q->opacity=(Quantum) (((MagickRealType) q->opacity <= mean.opacity) ?
274 0 : QuantumRange);
275 if (image->colorspace == CMYKColorspace)
276 threshold_indexes[x]=(IndexPacket) (((MagickRealType)
277 threshold_indexes[x] <= mean.index) ? 0 : QuantumRange);
278 p++;
279 q++;
280 }
281 sync=SyncCacheViewAuthenticPixels(threshold_view,exception);
282 if (sync == MagickFalse)
283 status=MagickFalse;
284 if (image->progress_monitor != (MagickProgressMonitor) NULL)
285 {
286 MagickBooleanType
287 proceed;
288
cristyb5d5f722009-11-04 03:03:49 +0000289#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000290 #pragma omp critical (MagickCore_AdaptiveThresholdImage)
291#endif
292 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
293 image->rows);
294 if (proceed == MagickFalse)
295 status=MagickFalse;
296 }
297 }
298 threshold_view=DestroyCacheView(threshold_view);
299 image_view=DestroyCacheView(image_view);
300 if (status == MagickFalse)
301 threshold_image=DestroyImage(threshold_image);
302 return(threshold_image);
303}
304
305/*
306%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
307% %
308% %
309% %
310% B i l e v e l I m a g e %
311% %
312% %
313% %
314%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
315%
316% BilevelImage() changes the value of individual pixels based on the
317% intensity of each pixel channel. The result is a high-contrast image.
318%
319% More precisely each channel value of the image is 'thresholded' so that if
320% it is equal to or less than the given value it is set to zero, while any
321% value greater than that give is set to it maximum or QuantumRange.
322%
323% This function is what is used to implement the "-threshold" operator for
324% the command line API.
325%
326% If the default channel setting is given the image is thresholded using just
327% the gray 'intensity' of the image, rather than the individual channels.
328%
329% The format of the BilevelImageChannel method is:
330%
331% MagickBooleanType BilevelImage(Image *image,const double threshold)
332% MagickBooleanType BilevelImageChannel(Image *image,
333% const ChannelType channel,const double threshold)
334%
335% A description of each parameter follows:
336%
337% o image: the image.
338%
339% o channel: the channel type.
340%
341% o threshold: define the threshold values.
342%
343% Aside: You can get the same results as operator using LevelImageChannels()
344% with the 'threshold' value for both the black_point and the white_point.
345%
346*/
347
348MagickExport MagickBooleanType BilevelImage(Image *image,const double threshold)
349{
350 MagickBooleanType
351 status;
352
353 status=BilevelImageChannel(image,DefaultChannels,threshold);
354 return(status);
355}
356
357MagickExport MagickBooleanType BilevelImageChannel(Image *image,
358 const ChannelType channel,const double threshold)
359{
360#define ThresholdImageTag "Threshold/Image"
361
362 ExceptionInfo
363 *exception;
364
365 long
366 progress,
367 y;
368
369 MagickBooleanType
370 status;
371
372 CacheView
373 *image_view;
374
375 assert(image != (Image *) NULL);
376 assert(image->signature == MagickSignature);
377 if (image->debug != MagickFalse)
378 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
379 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
380 return(MagickFalse);
381 /*
382 Bilevel threshold image.
383 */
384 status=MagickTrue;
385 progress=0;
386 exception=(&image->exception);
387 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +0000388#if defined(MAGICKCORE_OPENMP_SUPPORT)
389 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000390#endif
391 for (y=0; y < (long) image->rows; y++)
392 {
393 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000394 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000395
396 register long
397 x;
398
399 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000400 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000401
402 if (status == MagickFalse)
403 continue;
404 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
405 if (q == (PixelPacket *) NULL)
406 {
407 status=MagickFalse;
408 continue;
409 }
410 indexes=GetCacheViewAuthenticIndexQueue(image_view);
411 if (channel == DefaultChannels)
412 {
413 for (x=0; x < (long) image->columns; x++)
414 {
415 q->red=(Quantum) ((MagickRealType) PixelIntensityToQuantum(q) <=
416 threshold ? 0 : QuantumRange);
417 q->green=q->red;
418 q->blue=q->red;
419 q++;
420 }
421 }
422 else
423 for (x=0; x < (long) image->columns; x++)
424 {
425 if ((channel & RedChannel) != 0)
426 q->red=(Quantum) ((MagickRealType) q->red <= threshold ? 0 :
427 QuantumRange);
428 if ((channel & GreenChannel) != 0)
429 q->green=(Quantum) ((MagickRealType) q->green <= threshold ? 0 :
430 QuantumRange);
431 if ((channel & BlueChannel) != 0)
432 q->blue=(Quantum) ((MagickRealType) q->blue <= threshold ? 0 :
433 QuantumRange);
434 if ((channel & OpacityChannel) != 0)
435 {
436 if (image->matte == MagickFalse)
437 q->opacity=(Quantum) ((MagickRealType) q->opacity <= threshold ?
438 0 : QuantumRange);
439 else
440 q->opacity=(Quantum) ((MagickRealType) q->opacity <= threshold ?
441 OpaqueOpacity : TransparentOpacity);
442 }
443 if (((channel & IndexChannel) != 0) &&
444 (image->colorspace == CMYKColorspace))
445 indexes[x]=(IndexPacket) ((MagickRealType) indexes[x] <= threshold ?
446 0 : QuantumRange);
447 q++;
448 }
449 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
450 status=MagickFalse;
451 if (image->progress_monitor != (MagickProgressMonitor) NULL)
452 {
453 MagickBooleanType
454 proceed;
455
cristyb5d5f722009-11-04 03:03:49 +0000456#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000457 #pragma omp critical (MagickCore_BilevelImageChannel)
458#endif
459 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
460 image->rows);
461 if (proceed == MagickFalse)
462 status=MagickFalse;
463 }
464 }
465 image_view=DestroyCacheView(image_view);
466 return(status);
467}
468
469/*
470%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
471% %
472% %
473% %
474% B l a c k T h r e s h o l d I m a g e %
475% %
476% %
477% %
478%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
479%
480% BlackThresholdImage() is like ThresholdImage() but forces all pixels below
cristy4e101302009-09-17 12:49:12 +0000481% the threshold into black while leaving all pixels at or above the threshold
cristy3ed852e2009-09-05 21:47:34 +0000482% unchanged.
483%
484% The format of the BlackThresholdImage method is:
485%
486% MagickBooleanType BlackThresholdImage(Image *image,const char *threshold)
487% MagickBooleanType BlackThresholdImageChannel(Image *image,
488% const ChannelType channel,const char *threshold,
489% ExceptionInfo *exception)
490%
491% A description of each parameter follows:
492%
493% o image: the image.
494%
495% o channel: the channel or channels to be thresholded.
496%
497% o threshold: Define the threshold value.
498%
499% o exception: return any errors or warnings in this structure.
500%
501*/
502MagickExport MagickBooleanType BlackThresholdImage(Image *image,
503 const char *threshold)
504{
505 MagickBooleanType
506 status;
507
508 status=BlackThresholdImageChannel(image,DefaultChannels,threshold,
509 &image->exception);
510 return(status);
511}
512
513MagickExport MagickBooleanType BlackThresholdImageChannel(Image *image,
514 const ChannelType channel,const char *thresholds,ExceptionInfo *exception)
515{
516#define ThresholdImageTag "Threshold/Image"
517
518 GeometryInfo
519 geometry_info;
520
521 long
522 progress,
523 y;
524
525 MagickBooleanType
526 status;
527
528 MagickPixelPacket
529 threshold;
530
531 MagickStatusType
532 flags;
533
534 CacheView
535 *image_view;
536
537 assert(image != (Image *) NULL);
538 assert(image->signature == MagickSignature);
539 if (image->debug != MagickFalse)
540 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
541 if (thresholds == (const char *) NULL)
542 return(MagickTrue);
543 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
544 return(MagickFalse);
545 GetMagickPixelPacket(image,&threshold);
546 flags=ParseGeometry(thresholds,&geometry_info);
547 threshold.red=geometry_info.rho;
548 threshold.green=geometry_info.sigma;
549 if ((flags & SigmaValue) == 0)
550 threshold.green=threshold.red;
551 threshold.blue=geometry_info.xi;
552 if ((flags & XiValue) == 0)
553 threshold.blue=threshold.red;
554 threshold.opacity=geometry_info.psi;
555 if ((flags & PsiValue) == 0)
556 threshold.opacity=threshold.red;
557 threshold.index=geometry_info.chi;
558 if ((flags & ChiValue) == 0)
559 threshold.index=threshold.red;
560 if ((flags & PercentValue) != 0)
561 {
562 threshold.red*=(QuantumRange/100.0);
563 threshold.green*=(QuantumRange/100.0);
564 threshold.blue*=(QuantumRange/100.0);
565 threshold.opacity*=(QuantumRange/100.0);
566 threshold.index*=(QuantumRange/100.0);
567 }
568 /*
569 Black threshold image.
570 */
571 status=MagickTrue;
572 progress=0;
573 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +0000574#if defined(MAGICKCORE_OPENMP_SUPPORT)
575 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000576#endif
577 for (y=0; y < (long) image->rows; y++)
578 {
579 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000580 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000581
582 register long
583 x;
584
585 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000586 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000587
588 if (status == MagickFalse)
589 continue;
590 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
591 if (q == (PixelPacket *) NULL)
592 {
593 status=MagickFalse;
594 continue;
595 }
596 indexes=GetCacheViewAuthenticIndexQueue(image_view);
cristyb0ea1af2009-11-28 20:44:46 +0000597 for (x=0; x < (long) image->columns; x++)
598 {
599 if (channel != DefaultChannels)
600 {
601 if (PixelIntensity(q) < MagickPixelIntensity(&threshold))
602 {
603 q->red=(Quantum) 0;
604 q->green=(Quantum) 0;
605 q->blue=(Quantum) 0;
606 if (image->colorspace == CMYKColorspace)
607 indexes[x]=(Quantum) 0;
608 }
609 }
610 else
611 {
612 if (((channel & RedChannel) != 0) &&
613 ((MagickRealType) q->red < threshold.red))
614 q->red=(Quantum) 0;
615 if (((channel & GreenChannel) != 0) &&
616 ((MagickRealType) q->green < threshold.green))
617 q->green=(Quantum) 0;
618 if (((channel & BlueChannel) != 0) &&
619 ((MagickRealType) q->blue < threshold.blue))
620 q->blue=(Quantum) 0;
621 if (((channel & OpacityChannel) != 0) &&
622 ((MagickRealType) q->opacity < threshold.opacity))
623 q->opacity=(Quantum) 0;
624 if (((channel & IndexChannel) != 0) &&
625 (image->colorspace == CMYKColorspace) &&
626 ((MagickRealType) indexes[x] < threshold.index))
627 indexes[x]=(Quantum) 0;
628 }
629 q++;
630 }
cristy3ed852e2009-09-05 21:47:34 +0000631 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
632 status=MagickFalse;
633 if (image->progress_monitor != (MagickProgressMonitor) NULL)
634 {
635 MagickBooleanType
636 proceed;
637
cristyb5d5f722009-11-04 03:03:49 +0000638#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000639 #pragma omp critical (MagickCore_BlackThresholdImageChannel)
640#endif
641 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
642 image->rows);
643 if (proceed == MagickFalse)
644 status=MagickFalse;
645 }
646 }
647 image_view=DestroyCacheView(image_view);
648 return(status);
649}
650
651/*
652%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
653% %
654% %
655% %
cristy1eb45dd2009-09-25 16:38:06 +0000656% C l a m p I m a g e %
657% %
658% %
659% %
660%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
661%
cristyecb0c6d2009-09-25 16:50:09 +0000662% ClampImage() restricts the color range from 0 to the quantum depth.
cristy1eb45dd2009-09-25 16:38:06 +0000663%
664% The format of the ClampImageChannel method is:
665%
666% MagickBooleanType ClampImage(Image *image)
667% MagickBooleanType ClampImageChannel(Image *image,
668% const ChannelType channel)
669%
670% A description of each parameter follows:
671%
672% o image: the image.
673%
674% o channel: the channel type.
675%
676*/
677
678static inline Quantum ClampToQuantum(const Quantum quantum)
679{
680#if defined(MAGICKCORE_HDRI_SUPPORT)
681 if (quantum <= 0)
682 return(0);
683 if (quantum >= QuantumRange)
684 return(QuantumRange);
685 return(quantum);
686#else
687 return(quantum);
688#endif
689}
690
691MagickExport MagickBooleanType ClampImage(Image *image)
692{
693 MagickBooleanType
694 status;
695
696 status=ClampImageChannel(image,DefaultChannels);
697 return(status);
698}
699
700MagickExport MagickBooleanType ClampImageChannel(Image *image,
701 const ChannelType channel)
702{
703#define ClampImageTag "Clamp/Image"
704
705 ExceptionInfo
706 *exception;
707
708 long
709 progress,
710 y;
711
712 MagickBooleanType
713 status;
714
715 CacheView
716 *image_view;
717
718 assert(image != (Image *) NULL);
719 assert(image->signature == MagickSignature);
720 if (image->debug != MagickFalse)
721 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
722 if (image->storage_class == PseudoClass)
723 {
724 register long
725 i;
726
727 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000728 *restrict q;
cristy1eb45dd2009-09-25 16:38:06 +0000729
730 q=image->colormap;
731 for (i=0; i < (long) image->colors; i++)
732 {
733 q->red=ClampToQuantum(q->red);
734 q->green=ClampToQuantum(q->green);
735 q->blue=ClampToQuantum(q->blue);
736 q->opacity=ClampToQuantum(q->opacity);
737 q++;
738 }
739 return(SyncImage(image));
740 }
741 /*
cristy611721d2009-09-25 16:42:17 +0000742 Clamp image.
cristy1eb45dd2009-09-25 16:38:06 +0000743 */
744 status=MagickTrue;
745 progress=0;
746 exception=(&image->exception);
747 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +0000748#if defined(MAGICKCORE_OPENMP_SUPPORT)
749 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy1eb45dd2009-09-25 16:38:06 +0000750#endif
751 for (y=0; y < (long) image->rows; y++)
752 {
753 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000754 *restrict indexes;
cristy1eb45dd2009-09-25 16:38:06 +0000755
756 register long
757 x;
758
759 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000760 *restrict q;
cristy1eb45dd2009-09-25 16:38:06 +0000761
762 if (status == MagickFalse)
763 continue;
764 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
765 if (q == (PixelPacket *) NULL)
766 {
767 status=MagickFalse;
768 continue;
769 }
770 indexes=GetCacheViewAuthenticIndexQueue(image_view);
771 for (x=0; x < (long) image->columns; x++)
772 {
773 if ((channel & RedChannel) != 0)
774 q->red=ClampToQuantum(q->red);
775 if ((channel & GreenChannel) != 0)
776 q->green=ClampToQuantum(q->green);
777 if ((channel & BlueChannel) != 0)
778 q->blue=ClampToQuantum(q->blue);
779 if ((channel & OpacityChannel) != 0)
780 q->opacity=ClampToQuantum(q->opacity);
781 if (((channel & IndexChannel) != 0) &&
782 (image->colorspace == CMYKColorspace))
783 indexes[x]=(IndexPacket) ClampToQuantum(indexes[x]);
784 q++;
785 }
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)
cristy1eb45dd2009-09-25 16:38:06 +0000794 #pragma omp critical (MagickCore_ClampImageChannel)
795#endif
796 proceed=SetImageProgress(image,ClampImageTag,progress++,
797 image->rows);
798 if (proceed == MagickFalse)
799 status=MagickFalse;
800 }
801 }
802 image_view=DestroyCacheView(image_view);
803 return(status);
804}
805
806/*
807%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
808% %
809% %
810% %
cristy3ed852e2009-09-05 21:47:34 +0000811% D e s t r o y T h r e s h o l d M a p %
812% %
813% %
814% %
815%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
816%
817% DestroyThresholdMap() de-allocate the given ThresholdMap
818%
819% The format of the ListThresholdMaps method is:
820%
821% ThresholdMap *DestroyThresholdMap(Threshold *map)
822%
823% A description of each parameter follows.
824%
825% o map: Pointer to the Threshold map to destroy
826%
827*/
828MagickExport ThresholdMap *DestroyThresholdMap(ThresholdMap *map)
829{
830 assert(map != (ThresholdMap *) NULL);
831 if (map->map_id != (char *) NULL)
832 map->map_id=DestroyString(map->map_id);
833 if (map->description != (char *) NULL)
834 map->description=DestroyString(map->description);
835 if (map->levels != (long *) NULL)
836 map->levels=(long *) RelinquishMagickMemory(map->levels);
837 map=(ThresholdMap *) RelinquishMagickMemory(map);
838 return(map);
839}
840
841/*
842%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
843% %
844% %
845% %
846+ G e t T h r e s h o l d M a p F i l e %
847% %
848% %
849% %
850%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
851%
852% GetThresholdMapFile() look for a given threshold map name or alias in the
853% given XML file data, and return the allocated the map when found.
854%
855% The format of the ListThresholdMaps method is:
856%
857% ThresholdMap *GetThresholdMap(const char *xml,const char *filename,
858% const char *map_id,ExceptionInfo *exception)
859%
860% A description of each parameter follows.
861%
862% o xml: The threshold map list in XML format.
863%
864% o filename: The threshold map XML filename.
865%
866% o map_id: ID of the map to look for in XML list.
867%
868% o exception: return any errors or warnings in this structure.
869%
870*/
871MagickExport ThresholdMap *GetThresholdMapFile(const char *xml,
872 const char *filename,const char *map_id,ExceptionInfo *exception)
873{
874 const char
875 *attr,
876 *content;
877
878 double
879 value;
880
881 ThresholdMap
882 *map;
883
884 XMLTreeInfo
885 *description,
886 *levels,
887 *threshold,
888 *thresholds;
889
890 map = (ThresholdMap *)NULL;
891 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
892 "Loading threshold map file \"%s\" ...",filename);
893 thresholds=NewXMLTree(xml,exception);
894 if ( thresholds == (XMLTreeInfo *)NULL )
895 return(map);
896
897 for( threshold = GetXMLTreeChild(thresholds,"threshold");
898 threshold != (XMLTreeInfo *)NULL;
899 threshold = GetNextXMLTreeTag(threshold) ) {
900 attr = GetXMLTreeAttribute(threshold, "map");
901 if ( (attr != (char *)NULL) && (LocaleCompare(map_id,attr) == 0) )
902 break;
903 attr = GetXMLTreeAttribute(threshold, "alias");
904 if ( (attr != (char *)NULL) && (LocaleCompare(map_id,attr) == 0) )
905 break;
906 }
907 if ( threshold == (XMLTreeInfo *)NULL ) {
908 return(map);
909 }
910 description = GetXMLTreeChild(threshold,"description");
911 if ( description == (XMLTreeInfo *)NULL ) {
912 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
913 "XmlMissingElement", "<description>, map \"%s\"", map_id);
914 thresholds = DestroyXMLTree(thresholds);
915 return(map);
916 }
917 levels = GetXMLTreeChild(threshold,"levels");
918 if ( levels == (XMLTreeInfo *)NULL ) {
919 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
920 "XmlMissingElement", "<levels>, map \"%s\"", map_id);
921 thresholds = DestroyXMLTree(thresholds);
922 return(map);
923 }
924
925 /* The map has been found -- Allocate a Threshold Map to return */
926 map = (ThresholdMap *)AcquireMagickMemory(sizeof(ThresholdMap));
927 if ( map == (ThresholdMap *)NULL )
928 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
929 map->map_id = (char *)NULL;
930 map->description = (char *)NULL;
931 map->levels = (long *) NULL;
932
933 /* Assign Basic Attributes */
934 attr = GetXMLTreeAttribute(threshold, "map");
935 if ( attr != (char *)NULL )
936 map->map_id = ConstantString(attr);
937
938 content = GetXMLTreeContent(description);
939 if ( content != (char *)NULL )
940 map->description = ConstantString(content);
941
942 attr = GetXMLTreeAttribute(levels, "width");
943 if ( attr == (char *)NULL ) {
944 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
945 "XmlMissingAttribute", "<levels width>, map \"%s\"", map_id);
946 thresholds = DestroyXMLTree(thresholds);
947 map = DestroyThresholdMap(map);
948 return(map);
949 }
950 map->width = (unsigned long) atoi(attr);
951 if ( map->width == 0 ) {
952 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
953 "XmlInvalidAttribute", "<levels width>, map \"%s\"", map_id);
954 thresholds = DestroyXMLTree(thresholds);
955 map = DestroyThresholdMap(map);
956 return(map);
957 }
958
959 attr = GetXMLTreeAttribute(levels, "height");
960 if ( attr == (char *)NULL ) {
961 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
962 "XmlMissingAttribute", "<levels height>, map \"%s\"", map_id);
963 thresholds = DestroyXMLTree(thresholds);
964 map = DestroyThresholdMap(map);
965 return(map);
966 }
967 map->height = (unsigned long) atoi(attr);
968 if ( map->height == 0 ) {
969 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
970 "XmlInvalidAttribute", "<levels height>, map \"%s\"", map_id);
971 thresholds = DestroyXMLTree(thresholds);
972 map = DestroyThresholdMap(map);
973 return(map);
974 }
975
976 attr = GetXMLTreeAttribute(levels, "divisor");
977 if ( attr == (char *)NULL ) {
978 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
979 "XmlMissingAttribute", "<levels divisor>, map \"%s\"", map_id);
980 thresholds = DestroyXMLTree(thresholds);
981 map = DestroyThresholdMap(map);
982 return(map);
983 }
984 map->divisor = atoi(attr);
985 if ( map->divisor < 2 ) {
986 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
987 "XmlInvalidAttribute", "<levels divisor>, map \"%s\"", map_id);
988 thresholds = DestroyXMLTree(thresholds);
989 map = DestroyThresholdMap(map);
990 return(map);
991 }
992
993 /* Allocate theshold levels array */
994 content = GetXMLTreeContent(levels);
995 if ( content == (char *)NULL ) {
996 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
997 "XmlMissingContent", "<levels>, map \"%s\"", map_id);
998 thresholds = DestroyXMLTree(thresholds);
999 map = DestroyThresholdMap(map);
1000 return(map);
1001 }
1002 map->levels=(long *) AcquireQuantumMemory((size_t) map->width,map->height*
1003 sizeof(*map->levels));
1004 if ( map->levels == (long *)NULL )
1005 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
1006 { /* parse levels into integer array */
1007 int i;
1008 char *p;
1009 for( i=0; i< (long) (map->width*map->height); i++) {
1010 map->levels[i] = (int)strtol(content, &p, 10);
1011 if ( p == content ) {
1012 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1013 "XmlInvalidContent", "<level> too few values, map \"%s\"", map_id);
1014 thresholds = DestroyXMLTree(thresholds);
1015 map = DestroyThresholdMap(map);
1016 return(map);
1017 }
1018 if ( map->levels[i] < 0 || map->levels[i] > map->divisor ) {
1019 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1020 "XmlInvalidContent", "<level> %ld out of range, map \"%s\"",
1021 map->levels[i], map_id);
1022 thresholds = DestroyXMLTree(thresholds);
1023 map = DestroyThresholdMap(map);
1024 return(map);
1025 }
1026 content = p;
1027 }
1028 value=(double) strtol(content,&p,10);
1029 if (p != content)
1030 {
1031 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1032 "XmlInvalidContent", "<level> too many values, map \"%s\"", map_id);
1033 thresholds=DestroyXMLTree(thresholds);
1034 map=DestroyThresholdMap(map);
1035 return(map);
1036 }
1037 }
1038
1039 thresholds = DestroyXMLTree(thresholds);
1040 return(map);
1041}
1042
1043/*
1044%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1045% %
1046% %
1047% %
1048% G e t T h r e s h o l d M a p %
1049% %
1050% %
1051% %
1052%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1053%
1054% GetThresholdMap() load and search one or more threshold map files for the
1055% a map matching the given name or aliase.
1056%
1057% The format of the GetThresholdMap method is:
1058%
1059% ThresholdMap *GetThresholdMap(const char *map_id,
1060% ExceptionInfo *exception)
1061%
1062% A description of each parameter follows.
1063%
1064% o map_id: ID of the map to look for.
1065%
1066% o exception: return any errors or warnings in this structure.
1067%
1068*/
1069MagickExport ThresholdMap *GetThresholdMap(const char *map_id,
1070 ExceptionInfo *exception)
1071{
1072 const StringInfo
1073 *option;
1074
1075 LinkedListInfo
1076 *options;
1077
1078 ThresholdMap
1079 *map;
1080
1081 map=(ThresholdMap *)NULL;
1082 options=GetConfigureOptions(ThresholdsFilename,exception);
1083 while (( option=(const StringInfo *) GetNextValueInLinkedList(options) )
1084 != (const StringInfo *) NULL && map == (ThresholdMap *)NULL )
1085 map=GetThresholdMapFile((const char *) GetStringInfoDatum(option),
1086 GetStringInfoPath(option),map_id,exception);
1087 options=DestroyConfigureOptions(options);
1088 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{
1124 XMLTreeInfo *thresholds,*threshold,*description;
1125 const char *map,*alias,*content;
1126
1127 assert( xml != (char *)NULL );
1128 assert( file != (FILE *)NULL );
1129
1130 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1131 "Loading threshold map file \"%s\" ...",filename);
1132 thresholds=NewXMLTree(xml,exception);
1133 if ( thresholds == (XMLTreeInfo *)NULL )
1134 return(MagickFalse);
1135
1136 (void) fprintf(file,"%-16s %-12s %s\n", "Map", "Alias", "Description");
1137 (void) fprintf(file,"----------------------------------------------------\n");
1138
1139 for( threshold = GetXMLTreeChild(thresholds,"threshold");
1140 threshold != (XMLTreeInfo *)NULL;
1141 threshold = GetNextXMLTreeTag(threshold) )
1142 {
1143 map = GetXMLTreeAttribute(threshold, "map");
1144 if (map == (char *) NULL) {
1145 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1146 "XmlMissingAttribute", "<map>");
1147 thresholds=DestroyXMLTree(thresholds);
1148 return(MagickFalse);
1149 }
1150 alias = GetXMLTreeAttribute(threshold, "alias");
1151 /* alias is optional, no if test needed */
1152 description=GetXMLTreeChild(threshold,"description");
1153 if ( description == (XMLTreeInfo *)NULL ) {
1154 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1155 "XmlMissingElement", "<description>, map \"%s\"", map);
1156 thresholds=DestroyXMLTree(thresholds);
1157 return(MagickFalse);
1158 }
1159 content=GetXMLTreeContent(description);
1160 if ( content == (char *)NULL ) {
1161 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1162 "XmlMissingContent", "<description>, map \"%s\"", map);
1163 thresholds=DestroyXMLTree(thresholds);
1164 return(MagickFalse);
1165 }
1166 (void) fprintf(file,"%-16s %-12s %s\n",map,alias ? alias : "", content);
1167 }
1168 thresholds=DestroyXMLTree(thresholds);
1169 return(MagickTrue);
1170}
1171
1172/*
1173%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1174% %
1175% %
1176% %
1177% L i s t T h r e s h o l d M a p s %
1178% %
1179% %
1180% %
1181%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1182%
1183% ListThresholdMaps() lists the threshold maps and their descriptions
1184% as defined by "threshold.xml" to a file.
1185%
1186% The format of the ListThresholdMaps method is:
1187%
1188% MagickBooleanType ListThresholdMaps(FILE *file,ExceptionInfo *exception)
1189%
1190% A description of each parameter follows.
1191%
1192% o file: An pointer to the output FILE.
1193%
1194% o exception: return any errors or warnings in this structure.
1195%
1196*/
1197MagickExport MagickBooleanType ListThresholdMaps(FILE *file,
1198 ExceptionInfo *exception)
1199{
1200 const StringInfo
1201 *option;
1202
1203 LinkedListInfo
1204 *options;
1205
1206 MagickStatusType
1207 status;
1208
1209 status=MagickFalse;
1210 if ( file == (FILE *)NULL )
1211 file = stdout;
1212 options=GetConfigureOptions(ThresholdsFilename,exception);
1213
1214 (void) fprintf(file, "\n Threshold Maps for Ordered Dither Operations\n");
1215
1216 while ( ( option=(const StringInfo *) GetNextValueInLinkedList(options) )
1217 != (const StringInfo *) NULL)
1218 {
1219 (void) fprintf(file,"\nPATH: %s\n\n",GetStringInfoPath(option));
1220 status|=ListThresholdMapFile(file,(const char *) GetStringInfoDatum(option),
1221 GetStringInfoPath(option),exception);
1222 }
1223 options=DestroyConfigureOptions(options);
1224 return(status != 0 ? MagickTrue : MagickFalse);
1225}
1226
1227/*
1228%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1229% %
1230% %
1231% %
1232% O r d e r e d D i t h e r I m a g e %
1233% %
1234% %
1235% %
1236%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1237%
1238% OrderedDitherImage() uses the ordered dithering technique of reducing color
1239% images to monochrome using positional information to retain as much
1240% information as possible.
1241%
1242% WARNING: This function is deprecated, and is now just a call to
1243% the more more powerful OrderedPosterizeImage(); function.
1244%
1245% The format of the OrderedDitherImage method is:
1246%
1247% MagickBooleanType OrderedDitherImage(Image *image)
1248% MagickBooleanType OrderedDitherImageChannel(Image *image,
1249% const ChannelType channel,ExceptionInfo *exception)
1250%
1251% A description of each parameter follows:
1252%
1253% o image: the image.
1254%
1255% o channel: the channel or channels to be thresholded.
1256%
1257% o exception: return any errors or warnings in this structure.
1258%
1259*/
1260
1261MagickExport MagickBooleanType OrderedDitherImage(Image *image)
1262{
1263 MagickBooleanType
1264 status;
1265
1266 status=OrderedDitherImageChannel(image,DefaultChannels,&image->exception);
1267 return(status);
1268}
1269
1270MagickExport MagickBooleanType OrderedDitherImageChannel(Image *image,
1271 const ChannelType channel,ExceptionInfo *exception)
1272{
1273 MagickBooleanType
1274 status;
1275
1276 /*
1277 Call the augumented function OrderedPosterizeImage()
1278 */
1279 status=OrderedPosterizeImageChannel(image,channel,"o8x8",exception);
1280 return(status);
1281}
1282
1283/*
1284%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1285% %
1286% %
1287% %
1288% O r d e r e d P o s t e r i z e I m a g e %
1289% %
1290% %
1291% %
1292%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1293%
1294% OrderedPosterizeImage() will perform a ordered dither based on a number
1295% of pre-defined dithering threshold maps, but over multiple intensity
1296% levels, which can be different for different channels, according to the
1297% input argument.
1298%
1299% The format of the OrderedPosterizeImage method is:
1300%
1301% MagickBooleanType OrderedPosterizeImage(Image *image,
1302% const char *threshold_map,ExceptionInfo *exception)
1303% MagickBooleanType OrderedPosterizeImageChannel(Image *image,
1304% const ChannelType channel,const char *threshold_map,
1305% ExceptionInfo *exception)
1306%
1307% A description of each parameter follows:
1308%
1309% o image: the image.
1310%
1311% o channel: the channel or channels to be thresholded.
1312%
1313% o threshold_map: A string containing the name of the threshold dither
1314% map to use, followed by zero or more numbers representing the number
1315% of color levels tho dither between.
1316%
1317% Any level number less than 2 will be equivelent to 2, and means only
1318% binary dithering will be applied to each color channel.
1319%
1320% No numbers also means a 2 level (bitmap) dither will be applied to all
1321% channels, while a single number is the number of levels applied to each
1322% channel in sequence. More numbers will be applied in turn to each of
1323% the color channels.
1324%
1325% For example: "o3x3,6" will generate a 6 level posterization of the
1326% image with a ordered 3x3 diffused pixel dither being applied between
1327% each level. While checker,8,8,4 will produce a 332 colormaped image
1328% with only a single checkerboard hash pattern (50% grey) between each
1329% color level, to basically double the number of color levels with
1330% a bare minimim of dithering.
1331%
1332% o exception: return any errors or warnings in this structure.
1333%
1334*/
1335MagickExport MagickBooleanType OrderedPosterizeImage(Image *image,
1336 const char *threshold_map,ExceptionInfo *exception)
1337{
1338 MagickBooleanType
1339 status;
1340
1341 status=OrderedPosterizeImageChannel(image,DefaultChannels,threshold_map,
1342 exception);
1343 return(status);
1344}
1345
1346MagickExport MagickBooleanType OrderedPosterizeImageChannel(Image *image,
1347 const ChannelType channel,const char *threshold_map,ExceptionInfo *exception)
1348{
1349#define DitherImageTag "Dither/Image"
1350
1351 long
1352 progress,
1353 y;
1354
1355 LongPixelPacket
1356 levels;
1357
1358 MagickBooleanType
1359 status;
1360
1361 ThresholdMap
1362 *map;
1363
1364 CacheView
1365 *image_view;
1366
1367 assert(image != (Image *) NULL);
1368 assert(image->signature == MagickSignature);
1369 if (image->debug != MagickFalse)
1370 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1371 assert(exception != (ExceptionInfo *) NULL);
1372 assert(exception->signature == MagickSignature);
1373 if (threshold_map == (const char *) NULL)
1374 return(MagickTrue);
1375 {
1376 char
1377 token[MaxTextExtent];
1378
1379 register const char
1380 *p;
1381
1382 p=(char *)threshold_map;
1383 while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
1384 (*p != '\0'))
1385 p++;
1386 threshold_map=p;
1387 while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
1388 (*p != '\0')) {
1389 if ((p-threshold_map) >= MaxTextExtent)
1390 break;
1391 token[p-threshold_map] = *p;
1392 p++;
1393 }
1394 token[p-threshold_map] = '\0';
1395 map = GetThresholdMap(token, exception);
1396 if ( map == (ThresholdMap *)NULL ) {
1397 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1398 "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
1399 return(MagickFalse);
1400 }
1401 }
1402 /* Set channel levels from extra comma seperated arguments
1403 Default to 2, the single value given, or individual channel values
1404 */
1405#if 1
1406 { /* parse directly as a comma seperated list of integers */
1407 char *p;
1408
1409 p = strchr((char *) threshold_map,',');
1410 if ( p != (char *)NULL && isdigit((int) ((unsigned char) *(++p))) )
1411 levels.index = (unsigned long) strtol(p, &p, 10);
1412 else
1413 levels.index = 2;
1414
1415 levels.red = ((channel & RedChannel ) != 0) ? levels.index : 0;
1416 levels.green = ((channel & GreenChannel) != 0) ? levels.index : 0;
1417 levels.blue = ((channel & BlueChannel) != 0) ? levels.index : 0;
1418 levels.opacity = ((channel & OpacityChannel) != 0) ? levels.index : 0;
1419 levels.index = ((channel & IndexChannel) != 0
1420 && (image->colorspace == CMYKColorspace)) ? levels.index : 0;
1421
1422 /* if more than a single number, each channel has a separate value */
1423 if ( p != (char *) NULL && *p == ',' ) {
1424 p=strchr((char *) threshold_map,',');
1425 p++;
1426 if ((channel & RedChannel) != 0)
1427 levels.red = (unsigned long) strtol(p, &p, 10), (void)(*p == ',' && p++);
1428 if ((channel & GreenChannel) != 0)
1429 levels.green = (unsigned long) strtol(p, &p, 10), (void)(*p == ',' && p++);
1430 if ((channel & BlueChannel) != 0)
1431 levels.blue = (unsigned long) strtol(p, &p, 10), (void)(*p == ',' && p++);
1432 if ((channel & IndexChannel) != 0 && image->colorspace == CMYKColorspace)
1433 levels.index=(unsigned long) strtol(p, &p, 10), (void)(*p == ',' && p++);
1434 if ((channel & OpacityChannel) != 0)
1435 levels.opacity = (unsigned long) strtol(p, &p, 10), (void)(*p == ',' && p++);
1436 }
1437 }
1438#else
1439 /* Parse level values as a geometry */
1440 /* This difficult!
1441 * How to map GeometryInfo structure elements into
1442 * LongPixelPacket structure elements, but according to channel?
1443 * Note the channels list may skip elements!!!!
1444 * EG -channel BA -ordered-dither map,2,3
1445 * will need to map g.rho -> l.blue, and g.sigma -> l.opacity
1446 * A simpler way is needed, probably converting geometry to a temporary
1447 * array, then using channel to advance the index into long pixel packet.
1448 */
1449#endif
1450
1451#if 0
1452printf("DEBUG levels r=%ld g=%ld b=%ld a=%ld i=%ld\n",
1453 levels.red, levels.green, levels.blue, levels.opacity, levels.index);
1454#endif
1455
1456 { /* Do the posterized ordered dithering of the image */
1457 int
1458 d;
1459
1460 /* d = number of psuedo-level divisions added between color levels */
1461 d = map->divisor-1;
1462
1463 /* reduce levels to levels - 1 */
1464 levels.red = levels.red ? levels.red-1 : 0;
1465 levels.green = levels.green ? levels.green-1 : 0;
1466 levels.blue = levels.blue ? levels.blue-1 : 0;
1467 levels.opacity = levels.opacity ? levels.opacity-1 : 0;
1468 levels.index = levels.index ? levels.index-1 : 0;
1469
1470 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1471 {
1472 InheritException(exception,&image->exception);
1473 return(MagickFalse);
1474 }
1475 status=MagickTrue;
1476 progress=0;
1477 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00001478#if defined(MAGICKCORE_OPENMP_SUPPORT)
1479 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001480#endif
1481 for (y=0; y < (long) image->rows; y++)
1482 {
1483 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001484 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001485
1486 register long
1487 x;
1488
1489 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001490 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001491
1492 if (status == MagickFalse)
1493 continue;
1494 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1495 if (q == (PixelPacket *) NULL)
1496 {
1497 status=MagickFalse;
1498 continue;
1499 }
1500 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1501 for (x=0; x < (long) image->columns; x++)
1502 {
1503 register int
1504 threshold,
1505 t,
1506 l;
1507
1508 /*
1509 Figure out the dither threshold for this pixel
1510 This must be a integer from 1 to map->divisor-1
1511 */
1512 threshold = map->levels[(x%map->width) +map->width*(y%map->height)];
1513
1514 /* Dither each channel in the image as appropriate
1515 Notes on the integer Math...
1516 total number of divisions = (levels-1)*(divisor-1)+1)
1517 t1 = this colors psuedo_level =
1518 q->red * total_divisions / (QuantumRange+1)
1519 l = posterization level 0..levels
1520 t = dither threshold level 0..divisor-1 NB: 0 only on last
1521 Each color_level is of size QuantumRange / (levels-1)
1522 NB: All input levels and divisor are already had 1 subtracted
1523 Opacity is inverted so 'off' represents transparent.
1524 */
1525 if (levels.red) {
1526 t = (int) (QuantumScale*q->red*(levels.red*d+1));
1527 l = t/d; t = t-l*d;
1528 q->red=(Quantum) ((l+(t >= threshold))*QuantumRange/levels.red);
1529 }
1530 if (levels.green) {
1531 t = (int) (QuantumScale*q->green*(levels.green*d+1));
1532 l = t/d; t = t-l*d;
1533 q->green=(Quantum) ((l+(t >= threshold))*QuantumRange/levels.green);
1534 }
1535 if (levels.blue) {
1536 t = (int) (QuantumScale*q->blue*(levels.blue*d+1));
1537 l = t/d; t = t-l*d;
1538 q->blue=(Quantum) ((l+(t >= threshold))*QuantumRange/levels.blue);
1539 }
1540 if (levels.opacity) {
1541 t = (int) ((1.0-QuantumScale*q->opacity)*(levels.opacity*d+1));
1542 l = t/d; t = t-l*d;
1543 q->opacity=(Quantum) ((1.0-l-(t >= threshold))*QuantumRange/
1544 levels.opacity);
1545 }
1546 if (levels.index) {
1547 t = (int) (QuantumScale*indexes[x]*(levels.index*d+1));
1548 l = t/d; t = t-l*d;
1549 indexes[x]=(IndexPacket) ((l+(t>=threshold))*QuantumRange/
1550 levels.index);
1551 }
1552 q++;
1553 }
1554 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1555 status=MagickFalse;
1556 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1557 {
1558 MagickBooleanType
1559 proceed;
1560
cristyb5d5f722009-11-04 03:03:49 +00001561#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001562 #pragma omp critical (MagickCore_OrderedPosterizeImageChannel)
1563#endif
1564 proceed=SetImageProgress(image,DitherImageTag,progress++,image->rows);
1565 if (proceed == MagickFalse)
1566 status=MagickFalse;
1567 }
1568 }
1569 image_view=DestroyCacheView(image_view);
1570 }
1571 map=DestroyThresholdMap(map);
1572 return(MagickTrue);
1573}
1574
1575/*
1576%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1577% %
1578% %
1579% %
1580% R a n d o m T h r e s h o l d I m a g e %
1581% %
1582% %
1583% %
1584%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1585%
1586% RandomThresholdImage() changes the value of individual pixels based on the
1587% intensity of each pixel compared to a random threshold. The result is a
1588% low-contrast, two color image.
1589%
1590% The format of the RandomThresholdImage method is:
1591%
1592% MagickBooleanType RandomThresholdImageChannel(Image *image,
1593% const char *thresholds,ExceptionInfo *exception)
1594% MagickBooleanType RandomThresholdImageChannel(Image *image,
1595% const ChannelType channel,const char *thresholds,
1596% ExceptionInfo *exception)
1597%
1598% A description of each parameter follows:
1599%
1600% o image: the image.
1601%
1602% o channel: the channel or channels to be thresholded.
1603%
1604% o thresholds: a geometry string containing low,high thresholds. If the
1605% string contains 2x2, 3x3, or 4x4, an ordered dither of order 2, 3, or 4
1606% is performed instead.
1607%
1608% o exception: return any errors or warnings in this structure.
1609%
1610*/
1611
1612MagickExport MagickBooleanType RandomThresholdImage(Image *image,
1613 const char *thresholds,ExceptionInfo *exception)
1614{
1615 MagickBooleanType
1616 status;
1617
1618 status=RandomThresholdImageChannel(image,DefaultChannels,thresholds,
1619 exception);
1620 return(status);
1621}
1622
1623MagickExport MagickBooleanType RandomThresholdImageChannel(Image *image,
1624 const ChannelType channel,const char *thresholds,ExceptionInfo *exception)
1625{
1626#define ThresholdImageTag "Threshold/Image"
1627
1628 GeometryInfo
1629 geometry_info;
1630
1631 MagickStatusType
1632 flags;
1633
1634 long
1635 progress,
1636 y;
1637
1638 MagickBooleanType
1639 status;
1640
1641 MagickPixelPacket
1642 threshold;
1643
1644 MagickRealType
1645 min_threshold,
1646 max_threshold;
1647
1648 RandomInfo
1649 **random_info;
1650
1651 CacheView
1652 *image_view;
1653
1654 assert(image != (Image *) NULL);
1655 assert(image->signature == MagickSignature);
1656 if (image->debug != MagickFalse)
1657 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1658 assert(exception != (ExceptionInfo *) NULL);
1659 assert(exception->signature == MagickSignature);
1660 if (thresholds == (const char *) NULL)
1661 return(MagickTrue);
1662 GetMagickPixelPacket(image,&threshold);
1663 min_threshold=0.0;
1664 max_threshold=(MagickRealType) QuantumRange;
1665 flags=ParseGeometry(thresholds,&geometry_info);
1666 min_threshold=geometry_info.rho;
1667 max_threshold=geometry_info.sigma;
1668 if ((flags & SigmaValue) == 0)
1669 max_threshold=min_threshold;
1670 if (strchr(thresholds,'%') != (char *) NULL)
1671 {
1672 max_threshold*=(MagickRealType) (0.01*QuantumRange);
1673 min_threshold*=(MagickRealType) (0.01*QuantumRange);
1674 }
1675 else
1676 if (((max_threshold == min_threshold) || (max_threshold == 1)) &&
1677 (min_threshold <= 8))
1678 {
1679 /*
1680 Backward Compatibility -- ordered-dither -- IM v 6.2.9-6.
1681 */
1682 status=OrderedPosterizeImageChannel(image,channel,thresholds,exception);
1683 return(status);
1684 }
1685 /*
1686 Random threshold image.
1687 */
1688 status=MagickTrue;
1689 progress=0;
1690 if (channel == AllChannels)
1691 {
1692 if (AcquireImageColormap(image,2) == MagickFalse)
1693 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1694 image->filename);
1695 random_info=AcquireRandomInfoThreadSet();
1696 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00001697#if defined(MAGICKCORE_OPENMP_SUPPORT)
1698 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001699#endif
1700 for (y=0; y < (long) image->rows; y++)
1701 {
1702 MagickBooleanType
1703 sync;
1704
1705 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001706 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001707
1708 register long
1709 id,
1710 x;
1711
1712 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001713 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001714
1715 if (status == MagickFalse)
1716 continue;
1717 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1718 exception);
1719 if (q == (PixelPacket *) NULL)
1720 {
1721 status=MagickFalse;
1722 continue;
1723 }
1724 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1725 id=GetOpenMPThreadId();
1726 for (x=0; x < (long) image->columns; x++)
1727 {
1728 IndexPacket
1729 index;
1730
1731 MagickRealType
1732 intensity;
1733
1734 intensity=(MagickRealType) PixelIntensityToQuantum(q);
1735 if (intensity < min_threshold)
1736 threshold.index=min_threshold;
1737 else if (intensity > max_threshold)
1738 threshold.index=max_threshold;
1739 else
1740 threshold.index=(MagickRealType)(QuantumRange*
1741 GetPseudoRandomValue(random_info[id]));
1742 index=(IndexPacket) (intensity <= threshold.index ? 0 : 1);
1743 indexes[x]=index;
1744 *q++=image->colormap[(long) index];
1745 }
1746 sync=SyncCacheViewAuthenticPixels(image_view,exception);
1747 if (sync == MagickFalse)
1748 status=MagickFalse;
1749 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1750 {
1751 MagickBooleanType
1752 proceed;
1753
cristyb5d5f722009-11-04 03:03:49 +00001754#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001755 #pragma omp critical (MagickCore_RandomThresholdImageChannel)
1756#endif
1757 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1758 image->rows);
1759 if (proceed == MagickFalse)
1760 status=MagickFalse;
1761 }
1762 }
1763 image_view=DestroyCacheView(image_view);
1764 random_info=DestroyRandomInfoThreadSet(random_info);
1765 return(status);
1766 }
1767 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1768 {
1769 InheritException(exception,&image->exception);
1770 return(MagickFalse);
1771 }
1772 random_info=AcquireRandomInfoThreadSet();
1773 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00001774#if defined(MAGICKCORE_OPENMP_SUPPORT)
1775 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001776#endif
1777 for (y=0; y < (long) image->rows; y++)
1778 {
1779 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001780 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001781
1782 register long
1783 id,
1784 x;
1785
1786 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001787 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001788
1789 if (status == MagickFalse)
1790 continue;
1791 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1792 if (q == (PixelPacket *) NULL)
1793 {
1794 status=MagickFalse;
1795 continue;
1796 }
1797 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1798 id=GetOpenMPThreadId();
1799 for (x=0; x < (long) image->columns; x++)
1800 {
1801 if ((channel & RedChannel) != 0)
1802 {
1803 if ((MagickRealType) q->red < min_threshold)
1804 threshold.red=min_threshold;
1805 else
1806 if ((MagickRealType) q->red > max_threshold)
1807 threshold.red=max_threshold;
1808 else
1809 threshold.red=(MagickRealType) (QuantumRange*
1810 GetPseudoRandomValue(random_info[id]));
1811 }
1812 if ((channel & GreenChannel) != 0)
1813 {
1814 if ((MagickRealType) q->green < min_threshold)
1815 threshold.green=min_threshold;
1816 else
1817 if ((MagickRealType) q->green > max_threshold)
1818 threshold.green=max_threshold;
1819 else
1820 threshold.green=(MagickRealType) (QuantumRange*
1821 GetPseudoRandomValue(random_info[id]));
1822 }
1823 if ((channel & BlueChannel) != 0)
1824 {
1825 if ((MagickRealType) q->blue < min_threshold)
1826 threshold.blue=min_threshold;
1827 else
1828 if ((MagickRealType) q->blue > max_threshold)
1829 threshold.blue=max_threshold;
1830 else
1831 threshold.blue=(MagickRealType) (QuantumRange*
1832 GetPseudoRandomValue(random_info[id]));
1833 }
1834 if ((channel & OpacityChannel) != 0)
1835 {
1836 if ((MagickRealType) q->opacity < min_threshold)
1837 threshold.opacity=min_threshold;
1838 else
1839 if ((MagickRealType) q->opacity > max_threshold)
1840 threshold.opacity=max_threshold;
1841 else
1842 threshold.opacity=(MagickRealType) (QuantumRange*
1843 GetPseudoRandomValue(random_info[id]));
1844 }
1845 if (((channel & IndexChannel) != 0) &&
1846 (image->colorspace == CMYKColorspace))
1847 {
1848 if ((MagickRealType) indexes[x] < min_threshold)
1849 threshold.index=min_threshold;
1850 else
1851 if ((MagickRealType) indexes[x] > max_threshold)
1852 threshold.index=max_threshold;
1853 else
1854 threshold.index=(MagickRealType) (QuantumRange*
1855 GetPseudoRandomValue(random_info[id]));
1856 }
1857 if ((channel & RedChannel) != 0)
1858 q->red=(Quantum) ((MagickRealType) q->red <= threshold.red ? 0 :
1859 QuantumRange);
1860 if ((channel & GreenChannel) != 0)
1861 q->green=(Quantum) ((MagickRealType) q->green <= threshold.green ? 0 :
1862 QuantumRange);
1863 if ((channel & BlueChannel) != 0)
1864 q->blue=(Quantum) ((MagickRealType) q->blue <= threshold.blue ? 0 :
1865 QuantumRange);
1866 if ((channel & OpacityChannel) != 0)
1867 q->opacity=(Quantum) ((MagickRealType) q->opacity <= threshold.opacity ?
1868 0 : QuantumRange);
1869 if (((channel & IndexChannel) != 0) &&
1870 (image->colorspace == CMYKColorspace))
1871 indexes[x]=(IndexPacket) ((MagickRealType) indexes[x] <=
1872 threshold.index ? 0 : QuantumRange);
1873 q++;
1874 }
1875 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1876 status=MagickFalse;
1877 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1878 {
1879 MagickBooleanType
1880 proceed;
1881
cristyb5d5f722009-11-04 03:03:49 +00001882#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001883 #pragma omp critical (MagickCore_RandomThresholdImageChannel)
1884#endif
1885 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1886 image->rows);
1887 if (proceed == MagickFalse)
1888 status=MagickFalse;
1889 }
1890 }
1891 image_view=DestroyCacheView(image_view);
1892 random_info=DestroyRandomInfoThreadSet(random_info);
1893 return(status);
1894}
1895
1896/*
1897%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1898% %
1899% %
1900% %
1901% W h i t e T h r e s h o l d I m a g e %
1902% %
1903% %
1904% %
1905%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1906%
1907% WhiteThresholdImage() is like ThresholdImage() but forces all pixels above
cristy4e101302009-09-17 12:49:12 +00001908% the threshold into white while leaving all pixels at or below the threshold
cristy3ed852e2009-09-05 21:47:34 +00001909% unchanged.
1910%
1911% The format of the WhiteThresholdImage method is:
1912%
1913% MagickBooleanType WhiteThresholdImage(Image *image,const char *threshold)
1914% MagickBooleanType WhiteThresholdImageChannel(Image *image,
1915% const ChannelType channel,const char *threshold,
1916% ExceptionInfo *exception)
1917%
1918% A description of each parameter follows:
1919%
1920% o image: the image.
1921%
1922% o channel: the channel or channels to be thresholded.
1923%
1924% o threshold: Define the threshold value.
1925%
1926% o exception: return any errors or warnings in this structure.
1927%
1928*/
1929MagickExport MagickBooleanType WhiteThresholdImage(Image *image,
1930 const char *threshold)
1931{
1932 MagickBooleanType
1933 status;
1934
1935 status=WhiteThresholdImageChannel(image,DefaultChannels,threshold,
1936 &image->exception);
1937 return(status);
1938}
1939
1940MagickExport MagickBooleanType WhiteThresholdImageChannel(Image *image,
1941 const ChannelType channel,const char *thresholds,ExceptionInfo *exception)
1942{
1943#define ThresholdImageTag "Threshold/Image"
1944
1945 GeometryInfo
1946 geometry_info;
1947
1948 long
1949 progress,
1950 y;
1951
1952 MagickBooleanType
1953 status;
1954
1955 MagickPixelPacket
1956 threshold;
1957
1958 MagickStatusType
1959 flags;
1960
1961 CacheView
1962 *image_view;
1963
1964 assert(image != (Image *) NULL);
1965 assert(image->signature == MagickSignature);
1966 if (image->debug != MagickFalse)
1967 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1968 if (thresholds == (const char *) NULL)
1969 return(MagickTrue);
1970 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1971 return(MagickFalse);
1972 flags=ParseGeometry(thresholds,&geometry_info);
1973 GetMagickPixelPacket(image,&threshold);
1974 threshold.red=geometry_info.rho;
1975 threshold.green=geometry_info.sigma;
1976 if ((flags & SigmaValue) == 0)
1977 threshold.green=threshold.red;
1978 threshold.blue=geometry_info.xi;
1979 if ((flags & XiValue) == 0)
1980 threshold.blue=threshold.red;
1981 threshold.opacity=geometry_info.psi;
1982 if ((flags & PsiValue) == 0)
1983 threshold.opacity=threshold.red;
1984 threshold.index=geometry_info.chi;
1985 if ((flags & ChiValue) == 0)
1986 threshold.index=threshold.red;
1987 if ((flags & PercentValue) != 0)
1988 {
1989 threshold.red*=(QuantumRange/100.0);
1990 threshold.green*=(QuantumRange/100.0);
1991 threshold.blue*=(QuantumRange/100.0);
1992 threshold.opacity*=(QuantumRange/100.0);
1993 threshold.index*=(QuantumRange/100.0);
1994 }
1995 /*
1996 White threshold image.
1997 */
1998 status=MagickTrue;
1999 progress=0;
2000 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00002001#if defined(MAGICKCORE_OPENMP_SUPPORT)
2002 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002003#endif
2004 for (y=0; y < (long) image->rows; y++)
2005 {
2006 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00002007 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00002008
2009 register long
2010 x;
2011
2012 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00002013 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00002014
2015 if (status == MagickFalse)
2016 continue;
2017 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2018 if (q == (PixelPacket *) NULL)
2019 {
2020 status=MagickFalse;
2021 continue;
2022 }
2023 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2024 for (x=0; x < (long) image->columns; x++)
2025 {
cristyb0ea1af2009-11-28 20:44:46 +00002026 if (channel != DefaultChannels)
2027 {
2028 if (PixelIntensity(q) > MagickPixelIntensity(&threshold))
2029 {
2030 q->red=(Quantum) QuantumRange;
2031 q->green=(Quantum) QuantumRange;
2032 q->blue=(Quantum) QuantumRange;
2033 if (image->colorspace == CMYKColorspace)
2034 indexes[x]=(Quantum) QuantumRange;
2035 }
2036 }
2037 else
2038 {
2039 if (((channel & RedChannel) != 0) &&
2040 ((MagickRealType) q->red > threshold.red))
2041 q->red=(Quantum) QuantumRange;
2042 if (((channel & GreenChannel) != 0) &&
2043 ((MagickRealType) q->green > threshold.green))
2044 q->green=(Quantum) QuantumRange;
2045 if (((channel & BlueChannel) != 0) &&
2046 ((MagickRealType) q->blue > threshold.blue))
2047 q->blue=(Quantum) QuantumRange;
2048 if (((channel & OpacityChannel) != 0) &&
2049 ((MagickRealType) q->opacity > threshold.opacity))
2050 q->opacity=(Quantum) QuantumRange;
2051 if (((channel & IndexChannel) != 0) &&
2052 (image->colorspace == CMYKColorspace) &&
2053 ((MagickRealType) indexes[x] > threshold.index))
2054 indexes[x]=(Quantum) QuantumRange;
2055 }
cristy3ed852e2009-09-05 21:47:34 +00002056 q++;
2057 }
2058 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2059 status=MagickFalse;
2060 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2061 {
2062 MagickBooleanType
2063 proceed;
2064
cristyb5d5f722009-11-04 03:03:49 +00002065#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00002066 #pragma omp critical (MagickCore_WhiteThresholdImageChannel)
2067#endif
2068 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
2069 image->rows);
2070 if (proceed == MagickFalse)
2071 status=MagickFalse;
2072 }
2073 }
2074 image_view=DestroyCacheView(image_view);
2075 return(status);
2076}