blob: 708151dcb8f56649bd9d2f3800253c70f5bf8649 [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);
cristy629a6c72009-09-13 23:28:22 +0000194#if defined(_OPENMP) && (_OPENMP >= 200203)
cristy07490992009-09-11 19:51:45 +0000195 #pragma omp parallel for schedule(static,1) 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
203 *__restrict indexes;
204
205 register const PixelPacket
206 *__restrict p;
207
208 register IndexPacket
209 *__restrict threshold_indexes;
210
211 register long
212 x;
213
214 register PixelPacket
215 *__restrict q;
216
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
cristy629a6c72009-09-13 23:28:22 +0000289#if defined(_OPENMP) && (_OPENMP >= 200203)
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);
cristy629a6c72009-09-13 23:28:22 +0000388#if defined(_OPENMP) && (_OPENMP >= 200203)
cristy07490992009-09-11 19:51:45 +0000389 #pragma omp parallel for schedule(static,1) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000390#endif
391 for (y=0; y < (long) image->rows; y++)
392 {
393 register IndexPacket
394 *__restrict indexes;
395
396 register long
397 x;
398
399 register PixelPacket
400 *__restrict q;
401
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
cristy629a6c72009-09-13 23:28:22 +0000456#if defined(_OPENMP) && (_OPENMP >= 200203)
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);
cristy629a6c72009-09-13 23:28:22 +0000574#if defined(_OPENMP) && (_OPENMP >= 200203)
cristy07490992009-09-11 19:51:45 +0000575 #pragma omp parallel for schedule(static,1) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000576#endif
577 for (y=0; y < (long) image->rows; y++)
578 {
579 register IndexPacket
580 *__restrict indexes;
581
582 register long
583 x;
584
585 register PixelPacket
586 *__restrict q;
587
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);
597 if (channel == DefaultChannels)
598 for (x=0; x < (long) image->columns; x++)
599 {
600 if ((MagickRealType) q->red < threshold.red)
601 q->red=(Quantum) 0;
602 if ((MagickRealType) q->green < threshold.green)
603 q->green=(Quantum) 0;
604 if ((MagickRealType) q->blue < threshold.blue)
605 q->blue=(Quantum) 0;
606 if ((image->colorspace == CMYKColorspace) &&
607 ((MagickRealType) indexes[x] < threshold.index))
608 indexes[x]=(Quantum) 0;
609 q++;
610 }
611 else
612 for (x=0; x < (long) image->columns; x++)
613 {
614 if (((channel & RedChannel) != 0) &&
615 ((MagickRealType) q->red < threshold.red))
616 q->red=(Quantum) 0;
617 if (((channel & GreenChannel) != 0) &&
618 ((MagickRealType) q->green < threshold.green))
619 q->green=(Quantum) 0;
620 if (((channel & BlueChannel) != 0) &&
621 ((MagickRealType) q->blue < threshold.blue))
622 q->blue=(Quantum) 0;
623 if (((channel & OpacityChannel) != 0) &&
624 ((MagickRealType) q->opacity < threshold.opacity))
625 q->opacity=(Quantum) 0;
626 if (((channel & IndexChannel) != 0) &&
627 (image->colorspace == CMYKColorspace) &&
628 ((MagickRealType) indexes[x] < threshold.index))
629 indexes[x]=(Quantum) 0;
630 q++;
631 }
632 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
633 status=MagickFalse;
634 if (image->progress_monitor != (MagickProgressMonitor) NULL)
635 {
636 MagickBooleanType
637 proceed;
638
cristy629a6c72009-09-13 23:28:22 +0000639#if defined(_OPENMP) && (_OPENMP >= 200203)
cristy3ed852e2009-09-05 21:47:34 +0000640 #pragma omp critical (MagickCore_BlackThresholdImageChannel)
641#endif
642 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
643 image->rows);
644 if (proceed == MagickFalse)
645 status=MagickFalse;
646 }
647 }
648 image_view=DestroyCacheView(image_view);
649 return(status);
650}
651
652/*
653%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
654% %
655% %
656% %
cristy1eb45dd2009-09-25 16:38:06 +0000657% C l a m p I m a g e %
658% %
659% %
660% %
661%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
662%
cristy611721d2009-09-25 16:42:17 +0000663% ClampImage() restricts the color range from 0 to pixel depth.
cristy1eb45dd2009-09-25 16:38:06 +0000664%
665% The format of the ClampImageChannel method is:
666%
667% MagickBooleanType ClampImage(Image *image)
668% MagickBooleanType ClampImageChannel(Image *image,
669% const ChannelType channel)
670%
671% A description of each parameter follows:
672%
673% o image: the image.
674%
675% o channel: the channel type.
676%
677*/
678
679static inline Quantum ClampToQuantum(const Quantum quantum)
680{
681#if defined(MAGICKCORE_HDRI_SUPPORT)
682 if (quantum <= 0)
683 return(0);
684 if (quantum >= QuantumRange)
685 return(QuantumRange);
686 return(quantum);
687#else
688 return(quantum);
689#endif
690}
691
692MagickExport MagickBooleanType ClampImage(Image *image)
693{
694 MagickBooleanType
695 status;
696
697 status=ClampImageChannel(image,DefaultChannels);
698 return(status);
699}
700
701MagickExport MagickBooleanType ClampImageChannel(Image *image,
702 const ChannelType channel)
703{
704#define ClampImageTag "Clamp/Image"
705
706 ExceptionInfo
707 *exception;
708
709 long
710 progress,
711 y;
712
713 MagickBooleanType
714 status;
715
716 CacheView
717 *image_view;
718
719 assert(image != (Image *) NULL);
720 assert(image->signature == MagickSignature);
721 if (image->debug != MagickFalse)
722 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
723 if (image->storage_class == PseudoClass)
724 {
725 register long
726 i;
727
728 register PixelPacket
729 *__restrict q;
730
731 q=image->colormap;
732 for (i=0; i < (long) image->colors; i++)
733 {
734 q->red=ClampToQuantum(q->red);
735 q->green=ClampToQuantum(q->green);
736 q->blue=ClampToQuantum(q->blue);
737 q->opacity=ClampToQuantum(q->opacity);
738 q++;
739 }
740 return(SyncImage(image));
741 }
742 /*
cristy611721d2009-09-25 16:42:17 +0000743 Clamp image.
cristy1eb45dd2009-09-25 16:38:06 +0000744 */
745 status=MagickTrue;
746 progress=0;
747 exception=(&image->exception);
748 image_view=AcquireCacheView(image);
749#if defined(_OPENMP) && (_OPENMP >= 200203)
750 #pragma omp parallel for schedule(static,1) shared(progress,status)
751#endif
752 for (y=0; y < (long) image->rows; y++)
753 {
754 register IndexPacket
755 *__restrict indexes;
756
757 register long
758 x;
759
760 register PixelPacket
761 *__restrict q;
762
763 if (status == MagickFalse)
764 continue;
765 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
766 if (q == (PixelPacket *) NULL)
767 {
768 status=MagickFalse;
769 continue;
770 }
771 indexes=GetCacheViewAuthenticIndexQueue(image_view);
772 for (x=0; x < (long) image->columns; x++)
773 {
774 if ((channel & RedChannel) != 0)
775 q->red=ClampToQuantum(q->red);
776 if ((channel & GreenChannel) != 0)
777 q->green=ClampToQuantum(q->green);
778 if ((channel & BlueChannel) != 0)
779 q->blue=ClampToQuantum(q->blue);
780 if ((channel & OpacityChannel) != 0)
781 q->opacity=ClampToQuantum(q->opacity);
782 if (((channel & IndexChannel) != 0) &&
783 (image->colorspace == CMYKColorspace))
784 indexes[x]=(IndexPacket) ClampToQuantum(indexes[x]);
785 q++;
786 }
787 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
788 status=MagickFalse;
789 if (image->progress_monitor != (MagickProgressMonitor) NULL)
790 {
791 MagickBooleanType
792 proceed;
793
794#if defined(_OPENMP) && (_OPENMP >= 200203)
795 #pragma omp critical (MagickCore_ClampImageChannel)
796#endif
797 proceed=SetImageProgress(image,ClampImageTag,progress++,
798 image->rows);
799 if (proceed == MagickFalse)
800 status=MagickFalse;
801 }
802 }
803 image_view=DestroyCacheView(image_view);
804 return(status);
805}
806
807/*
808%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
809% %
810% %
811% %
cristy3ed852e2009-09-05 21:47:34 +0000812% D e s t r o y T h r e s h o l d M a p %
813% %
814% %
815% %
816%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
817%
818% DestroyThresholdMap() de-allocate the given ThresholdMap
819%
820% The format of the ListThresholdMaps method is:
821%
822% ThresholdMap *DestroyThresholdMap(Threshold *map)
823%
824% A description of each parameter follows.
825%
826% o map: Pointer to the Threshold map to destroy
827%
828*/
829MagickExport ThresholdMap *DestroyThresholdMap(ThresholdMap *map)
830{
831 assert(map != (ThresholdMap *) NULL);
832 if (map->map_id != (char *) NULL)
833 map->map_id=DestroyString(map->map_id);
834 if (map->description != (char *) NULL)
835 map->description=DestroyString(map->description);
836 if (map->levels != (long *) NULL)
837 map->levels=(long *) RelinquishMagickMemory(map->levels);
838 map=(ThresholdMap *) RelinquishMagickMemory(map);
839 return(map);
840}
841
842/*
843%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
844% %
845% %
846% %
847+ G e t T h r e s h o l d M a p F i l e %
848% %
849% %
850% %
851%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
852%
853% GetThresholdMapFile() look for a given threshold map name or alias in the
854% given XML file data, and return the allocated the map when found.
855%
856% The format of the ListThresholdMaps method is:
857%
858% ThresholdMap *GetThresholdMap(const char *xml,const char *filename,
859% const char *map_id,ExceptionInfo *exception)
860%
861% A description of each parameter follows.
862%
863% o xml: The threshold map list in XML format.
864%
865% o filename: The threshold map XML filename.
866%
867% o map_id: ID of the map to look for in XML list.
868%
869% o exception: return any errors or warnings in this structure.
870%
871*/
872MagickExport ThresholdMap *GetThresholdMapFile(const char *xml,
873 const char *filename,const char *map_id,ExceptionInfo *exception)
874{
875 const char
876 *attr,
877 *content;
878
879 double
880 value;
881
882 ThresholdMap
883 *map;
884
885 XMLTreeInfo
886 *description,
887 *levels,
888 *threshold,
889 *thresholds;
890
891 map = (ThresholdMap *)NULL;
892 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
893 "Loading threshold map file \"%s\" ...",filename);
894 thresholds=NewXMLTree(xml,exception);
895 if ( thresholds == (XMLTreeInfo *)NULL )
896 return(map);
897
898 for( threshold = GetXMLTreeChild(thresholds,"threshold");
899 threshold != (XMLTreeInfo *)NULL;
900 threshold = GetNextXMLTreeTag(threshold) ) {
901 attr = GetXMLTreeAttribute(threshold, "map");
902 if ( (attr != (char *)NULL) && (LocaleCompare(map_id,attr) == 0) )
903 break;
904 attr = GetXMLTreeAttribute(threshold, "alias");
905 if ( (attr != (char *)NULL) && (LocaleCompare(map_id,attr) == 0) )
906 break;
907 }
908 if ( threshold == (XMLTreeInfo *)NULL ) {
909 return(map);
910 }
911 description = GetXMLTreeChild(threshold,"description");
912 if ( description == (XMLTreeInfo *)NULL ) {
913 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
914 "XmlMissingElement", "<description>, map \"%s\"", map_id);
915 thresholds = DestroyXMLTree(thresholds);
916 return(map);
917 }
918 levels = GetXMLTreeChild(threshold,"levels");
919 if ( levels == (XMLTreeInfo *)NULL ) {
920 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
921 "XmlMissingElement", "<levels>, map \"%s\"", map_id);
922 thresholds = DestroyXMLTree(thresholds);
923 return(map);
924 }
925
926 /* The map has been found -- Allocate a Threshold Map to return */
927 map = (ThresholdMap *)AcquireMagickMemory(sizeof(ThresholdMap));
928 if ( map == (ThresholdMap *)NULL )
929 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
930 map->map_id = (char *)NULL;
931 map->description = (char *)NULL;
932 map->levels = (long *) NULL;
933
934 /* Assign Basic Attributes */
935 attr = GetXMLTreeAttribute(threshold, "map");
936 if ( attr != (char *)NULL )
937 map->map_id = ConstantString(attr);
938
939 content = GetXMLTreeContent(description);
940 if ( content != (char *)NULL )
941 map->description = ConstantString(content);
942
943 attr = GetXMLTreeAttribute(levels, "width");
944 if ( attr == (char *)NULL ) {
945 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
946 "XmlMissingAttribute", "<levels width>, map \"%s\"", map_id);
947 thresholds = DestroyXMLTree(thresholds);
948 map = DestroyThresholdMap(map);
949 return(map);
950 }
951 map->width = (unsigned long) atoi(attr);
952 if ( map->width == 0 ) {
953 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
954 "XmlInvalidAttribute", "<levels width>, map \"%s\"", map_id);
955 thresholds = DestroyXMLTree(thresholds);
956 map = DestroyThresholdMap(map);
957 return(map);
958 }
959
960 attr = GetXMLTreeAttribute(levels, "height");
961 if ( attr == (char *)NULL ) {
962 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
963 "XmlMissingAttribute", "<levels height>, map \"%s\"", map_id);
964 thresholds = DestroyXMLTree(thresholds);
965 map = DestroyThresholdMap(map);
966 return(map);
967 }
968 map->height = (unsigned long) atoi(attr);
969 if ( map->height == 0 ) {
970 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
971 "XmlInvalidAttribute", "<levels height>, map \"%s\"", map_id);
972 thresholds = DestroyXMLTree(thresholds);
973 map = DestroyThresholdMap(map);
974 return(map);
975 }
976
977 attr = GetXMLTreeAttribute(levels, "divisor");
978 if ( attr == (char *)NULL ) {
979 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
980 "XmlMissingAttribute", "<levels divisor>, map \"%s\"", map_id);
981 thresholds = DestroyXMLTree(thresholds);
982 map = DestroyThresholdMap(map);
983 return(map);
984 }
985 map->divisor = atoi(attr);
986 if ( map->divisor < 2 ) {
987 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
988 "XmlInvalidAttribute", "<levels divisor>, map \"%s\"", map_id);
989 thresholds = DestroyXMLTree(thresholds);
990 map = DestroyThresholdMap(map);
991 return(map);
992 }
993
994 /* Allocate theshold levels array */
995 content = GetXMLTreeContent(levels);
996 if ( content == (char *)NULL ) {
997 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
998 "XmlMissingContent", "<levels>, map \"%s\"", map_id);
999 thresholds = DestroyXMLTree(thresholds);
1000 map = DestroyThresholdMap(map);
1001 return(map);
1002 }
1003 map->levels=(long *) AcquireQuantumMemory((size_t) map->width,map->height*
1004 sizeof(*map->levels));
1005 if ( map->levels == (long *)NULL )
1006 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
1007 { /* parse levels into integer array */
1008 int i;
1009 char *p;
1010 for( i=0; i< (long) (map->width*map->height); i++) {
1011 map->levels[i] = (int)strtol(content, &p, 10);
1012 if ( p == content ) {
1013 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1014 "XmlInvalidContent", "<level> too few values, map \"%s\"", map_id);
1015 thresholds = DestroyXMLTree(thresholds);
1016 map = DestroyThresholdMap(map);
1017 return(map);
1018 }
1019 if ( map->levels[i] < 0 || map->levels[i] > map->divisor ) {
1020 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1021 "XmlInvalidContent", "<level> %ld out of range, map \"%s\"",
1022 map->levels[i], map_id);
1023 thresholds = DestroyXMLTree(thresholds);
1024 map = DestroyThresholdMap(map);
1025 return(map);
1026 }
1027 content = p;
1028 }
1029 value=(double) strtol(content,&p,10);
1030 if (p != content)
1031 {
1032 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1033 "XmlInvalidContent", "<level> too many values, map \"%s\"", map_id);
1034 thresholds=DestroyXMLTree(thresholds);
1035 map=DestroyThresholdMap(map);
1036 return(map);
1037 }
1038 }
1039
1040 thresholds = DestroyXMLTree(thresholds);
1041 return(map);
1042}
1043
1044/*
1045%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1046% %
1047% %
1048% %
1049% G e t T h r e s h o l d M a p %
1050% %
1051% %
1052% %
1053%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1054%
1055% GetThresholdMap() load and search one or more threshold map files for the
1056% a map matching the given name or aliase.
1057%
1058% The format of the GetThresholdMap method is:
1059%
1060% ThresholdMap *GetThresholdMap(const char *map_id,
1061% ExceptionInfo *exception)
1062%
1063% A description of each parameter follows.
1064%
1065% o map_id: ID of the map to look for.
1066%
1067% o exception: return any errors or warnings in this structure.
1068%
1069*/
1070MagickExport ThresholdMap *GetThresholdMap(const char *map_id,
1071 ExceptionInfo *exception)
1072{
1073 const StringInfo
1074 *option;
1075
1076 LinkedListInfo
1077 *options;
1078
1079 ThresholdMap
1080 *map;
1081
1082 map=(ThresholdMap *)NULL;
1083 options=GetConfigureOptions(ThresholdsFilename,exception);
1084 while (( option=(const StringInfo *) GetNextValueInLinkedList(options) )
1085 != (const StringInfo *) NULL && map == (ThresholdMap *)NULL )
1086 map=GetThresholdMapFile((const char *) GetStringInfoDatum(option),
1087 GetStringInfoPath(option),map_id,exception);
1088 options=DestroyConfigureOptions(options);
1089 return(map);
1090}
1091
1092/*
1093%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1094% %
1095% %
1096% %
1097+ L i s t T h r e s h o l d M a p F i l e %
1098% %
1099% %
1100% %
1101%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1102%
1103% ListThresholdMapFile() lists the threshold maps and their descriptions
1104% in the given XML file data.
1105%
1106% The format of the ListThresholdMaps method is:
1107%
1108% MagickBooleanType ListThresholdMaps(FILE *file,const char*xml,
1109% const char *filename,ExceptionInfo *exception)
1110%
1111% A description of each parameter follows.
1112%
1113% o file: An pointer to the output FILE.
1114%
1115% o xml: The threshold map list in XML format.
1116%
1117% o filename: The threshold map XML filename.
1118%
1119% o exception: return any errors or warnings in this structure.
1120%
1121*/
1122MagickBooleanType ListThresholdMapFile(FILE *file,const char *xml,
1123 const char *filename,ExceptionInfo *exception)
1124{
1125 XMLTreeInfo *thresholds,*threshold,*description;
1126 const char *map,*alias,*content;
1127
1128 assert( xml != (char *)NULL );
1129 assert( file != (FILE *)NULL );
1130
1131 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1132 "Loading threshold map file \"%s\" ...",filename);
1133 thresholds=NewXMLTree(xml,exception);
1134 if ( thresholds == (XMLTreeInfo *)NULL )
1135 return(MagickFalse);
1136
1137 (void) fprintf(file,"%-16s %-12s %s\n", "Map", "Alias", "Description");
1138 (void) fprintf(file,"----------------------------------------------------\n");
1139
1140 for( threshold = GetXMLTreeChild(thresholds,"threshold");
1141 threshold != (XMLTreeInfo *)NULL;
1142 threshold = GetNextXMLTreeTag(threshold) )
1143 {
1144 map = GetXMLTreeAttribute(threshold, "map");
1145 if (map == (char *) NULL) {
1146 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1147 "XmlMissingAttribute", "<map>");
1148 thresholds=DestroyXMLTree(thresholds);
1149 return(MagickFalse);
1150 }
1151 alias = GetXMLTreeAttribute(threshold, "alias");
1152 /* alias is optional, no if test needed */
1153 description=GetXMLTreeChild(threshold,"description");
1154 if ( description == (XMLTreeInfo *)NULL ) {
1155 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1156 "XmlMissingElement", "<description>, map \"%s\"", map);
1157 thresholds=DestroyXMLTree(thresholds);
1158 return(MagickFalse);
1159 }
1160 content=GetXMLTreeContent(description);
1161 if ( content == (char *)NULL ) {
1162 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1163 "XmlMissingContent", "<description>, map \"%s\"", map);
1164 thresholds=DestroyXMLTree(thresholds);
1165 return(MagickFalse);
1166 }
1167 (void) fprintf(file,"%-16s %-12s %s\n",map,alias ? alias : "", content);
1168 }
1169 thresholds=DestroyXMLTree(thresholds);
1170 return(MagickTrue);
1171}
1172
1173/*
1174%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1175% %
1176% %
1177% %
1178% L i s t T h r e s h o l d M a p s %
1179% %
1180% %
1181% %
1182%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1183%
1184% ListThresholdMaps() lists the threshold maps and their descriptions
1185% as defined by "threshold.xml" to a file.
1186%
1187% The format of the ListThresholdMaps method is:
1188%
1189% MagickBooleanType ListThresholdMaps(FILE *file,ExceptionInfo *exception)
1190%
1191% A description of each parameter follows.
1192%
1193% o file: An pointer to the output FILE.
1194%
1195% o exception: return any errors or warnings in this structure.
1196%
1197*/
1198MagickExport MagickBooleanType ListThresholdMaps(FILE *file,
1199 ExceptionInfo *exception)
1200{
1201 const StringInfo
1202 *option;
1203
1204 LinkedListInfo
1205 *options;
1206
1207 MagickStatusType
1208 status;
1209
1210 status=MagickFalse;
1211 if ( file == (FILE *)NULL )
1212 file = stdout;
1213 options=GetConfigureOptions(ThresholdsFilename,exception);
1214
1215 (void) fprintf(file, "\n Threshold Maps for Ordered Dither Operations\n");
1216
1217 while ( ( option=(const StringInfo *) GetNextValueInLinkedList(options) )
1218 != (const StringInfo *) NULL)
1219 {
1220 (void) fprintf(file,"\nPATH: %s\n\n",GetStringInfoPath(option));
1221 status|=ListThresholdMapFile(file,(const char *) GetStringInfoDatum(option),
1222 GetStringInfoPath(option),exception);
1223 }
1224 options=DestroyConfigureOptions(options);
1225 return(status != 0 ? MagickTrue : MagickFalse);
1226}
1227
1228/*
1229%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1230% %
1231% %
1232% %
1233% O r d e r e d D i t h e r I m a g e %
1234% %
1235% %
1236% %
1237%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1238%
1239% OrderedDitherImage() uses the ordered dithering technique of reducing color
1240% images to monochrome using positional information to retain as much
1241% information as possible.
1242%
1243% WARNING: This function is deprecated, and is now just a call to
1244% the more more powerful OrderedPosterizeImage(); function.
1245%
1246% The format of the OrderedDitherImage method is:
1247%
1248% MagickBooleanType OrderedDitherImage(Image *image)
1249% MagickBooleanType OrderedDitherImageChannel(Image *image,
1250% const ChannelType channel,ExceptionInfo *exception)
1251%
1252% A description of each parameter follows:
1253%
1254% o image: the image.
1255%
1256% o channel: the channel or channels to be thresholded.
1257%
1258% o exception: return any errors or warnings in this structure.
1259%
1260*/
1261
1262MagickExport MagickBooleanType OrderedDitherImage(Image *image)
1263{
1264 MagickBooleanType
1265 status;
1266
1267 status=OrderedDitherImageChannel(image,DefaultChannels,&image->exception);
1268 return(status);
1269}
1270
1271MagickExport MagickBooleanType OrderedDitherImageChannel(Image *image,
1272 const ChannelType channel,ExceptionInfo *exception)
1273{
1274 MagickBooleanType
1275 status;
1276
1277 /*
1278 Call the augumented function OrderedPosterizeImage()
1279 */
1280 status=OrderedPosterizeImageChannel(image,channel,"o8x8",exception);
1281 return(status);
1282}
1283
1284/*
1285%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1286% %
1287% %
1288% %
1289% O r d e r e d P o s t e r i z e I m a g e %
1290% %
1291% %
1292% %
1293%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1294%
1295% OrderedPosterizeImage() will perform a ordered dither based on a number
1296% of pre-defined dithering threshold maps, but over multiple intensity
1297% levels, which can be different for different channels, according to the
1298% input argument.
1299%
1300% The format of the OrderedPosterizeImage method is:
1301%
1302% MagickBooleanType OrderedPosterizeImage(Image *image,
1303% const char *threshold_map,ExceptionInfo *exception)
1304% MagickBooleanType OrderedPosterizeImageChannel(Image *image,
1305% const ChannelType channel,const char *threshold_map,
1306% ExceptionInfo *exception)
1307%
1308% A description of each parameter follows:
1309%
1310% o image: the image.
1311%
1312% o channel: the channel or channels to be thresholded.
1313%
1314% o threshold_map: A string containing the name of the threshold dither
1315% map to use, followed by zero or more numbers representing the number
1316% of color levels tho dither between.
1317%
1318% Any level number less than 2 will be equivelent to 2, and means only
1319% binary dithering will be applied to each color channel.
1320%
1321% No numbers also means a 2 level (bitmap) dither will be applied to all
1322% channels, while a single number is the number of levels applied to each
1323% channel in sequence. More numbers will be applied in turn to each of
1324% the color channels.
1325%
1326% For example: "o3x3,6" will generate a 6 level posterization of the
1327% image with a ordered 3x3 diffused pixel dither being applied between
1328% each level. While checker,8,8,4 will produce a 332 colormaped image
1329% with only a single checkerboard hash pattern (50% grey) between each
1330% color level, to basically double the number of color levels with
1331% a bare minimim of dithering.
1332%
1333% o exception: return any errors or warnings in this structure.
1334%
1335*/
1336MagickExport MagickBooleanType OrderedPosterizeImage(Image *image,
1337 const char *threshold_map,ExceptionInfo *exception)
1338{
1339 MagickBooleanType
1340 status;
1341
1342 status=OrderedPosterizeImageChannel(image,DefaultChannels,threshold_map,
1343 exception);
1344 return(status);
1345}
1346
1347MagickExport MagickBooleanType OrderedPosterizeImageChannel(Image *image,
1348 const ChannelType channel,const char *threshold_map,ExceptionInfo *exception)
1349{
1350#define DitherImageTag "Dither/Image"
1351
1352 long
1353 progress,
1354 y;
1355
1356 LongPixelPacket
1357 levels;
1358
1359 MagickBooleanType
1360 status;
1361
1362 ThresholdMap
1363 *map;
1364
1365 CacheView
1366 *image_view;
1367
1368 assert(image != (Image *) NULL);
1369 assert(image->signature == MagickSignature);
1370 if (image->debug != MagickFalse)
1371 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1372 assert(exception != (ExceptionInfo *) NULL);
1373 assert(exception->signature == MagickSignature);
1374 if (threshold_map == (const char *) NULL)
1375 return(MagickTrue);
1376 {
1377 char
1378 token[MaxTextExtent];
1379
1380 register const char
1381 *p;
1382
1383 p=(char *)threshold_map;
1384 while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
1385 (*p != '\0'))
1386 p++;
1387 threshold_map=p;
1388 while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
1389 (*p != '\0')) {
1390 if ((p-threshold_map) >= MaxTextExtent)
1391 break;
1392 token[p-threshold_map] = *p;
1393 p++;
1394 }
1395 token[p-threshold_map] = '\0';
1396 map = GetThresholdMap(token, exception);
1397 if ( map == (ThresholdMap *)NULL ) {
1398 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1399 "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
1400 return(MagickFalse);
1401 }
1402 }
1403 /* Set channel levels from extra comma seperated arguments
1404 Default to 2, the single value given, or individual channel values
1405 */
1406#if 1
1407 { /* parse directly as a comma seperated list of integers */
1408 char *p;
1409
1410 p = strchr((char *) threshold_map,',');
1411 if ( p != (char *)NULL && isdigit((int) ((unsigned char) *(++p))) )
1412 levels.index = (unsigned long) strtol(p, &p, 10);
1413 else
1414 levels.index = 2;
1415
1416 levels.red = ((channel & RedChannel ) != 0) ? levels.index : 0;
1417 levels.green = ((channel & GreenChannel) != 0) ? levels.index : 0;
1418 levels.blue = ((channel & BlueChannel) != 0) ? levels.index : 0;
1419 levels.opacity = ((channel & OpacityChannel) != 0) ? levels.index : 0;
1420 levels.index = ((channel & IndexChannel) != 0
1421 && (image->colorspace == CMYKColorspace)) ? levels.index : 0;
1422
1423 /* if more than a single number, each channel has a separate value */
1424 if ( p != (char *) NULL && *p == ',' ) {
1425 p=strchr((char *) threshold_map,',');
1426 p++;
1427 if ((channel & RedChannel) != 0)
1428 levels.red = (unsigned long) strtol(p, &p, 10), (void)(*p == ',' && p++);
1429 if ((channel & GreenChannel) != 0)
1430 levels.green = (unsigned long) strtol(p, &p, 10), (void)(*p == ',' && p++);
1431 if ((channel & BlueChannel) != 0)
1432 levels.blue = (unsigned long) strtol(p, &p, 10), (void)(*p == ',' && p++);
1433 if ((channel & IndexChannel) != 0 && image->colorspace == CMYKColorspace)
1434 levels.index=(unsigned long) strtol(p, &p, 10), (void)(*p == ',' && p++);
1435 if ((channel & OpacityChannel) != 0)
1436 levels.opacity = (unsigned long) strtol(p, &p, 10), (void)(*p == ',' && p++);
1437 }
1438 }
1439#else
1440 /* Parse level values as a geometry */
1441 /* This difficult!
1442 * How to map GeometryInfo structure elements into
1443 * LongPixelPacket structure elements, but according to channel?
1444 * Note the channels list may skip elements!!!!
1445 * EG -channel BA -ordered-dither map,2,3
1446 * will need to map g.rho -> l.blue, and g.sigma -> l.opacity
1447 * A simpler way is needed, probably converting geometry to a temporary
1448 * array, then using channel to advance the index into long pixel packet.
1449 */
1450#endif
1451
1452#if 0
1453printf("DEBUG levels r=%ld g=%ld b=%ld a=%ld i=%ld\n",
1454 levels.red, levels.green, levels.blue, levels.opacity, levels.index);
1455#endif
1456
1457 { /* Do the posterized ordered dithering of the image */
1458 int
1459 d;
1460
1461 /* d = number of psuedo-level divisions added between color levels */
1462 d = map->divisor-1;
1463
1464 /* reduce levels to levels - 1 */
1465 levels.red = levels.red ? levels.red-1 : 0;
1466 levels.green = levels.green ? levels.green-1 : 0;
1467 levels.blue = levels.blue ? levels.blue-1 : 0;
1468 levels.opacity = levels.opacity ? levels.opacity-1 : 0;
1469 levels.index = levels.index ? levels.index-1 : 0;
1470
1471 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1472 {
1473 InheritException(exception,&image->exception);
1474 return(MagickFalse);
1475 }
1476 status=MagickTrue;
1477 progress=0;
1478 image_view=AcquireCacheView(image);
cristy629a6c72009-09-13 23:28:22 +00001479#if defined(_OPENMP) && (_OPENMP >= 200203)
cristy07490992009-09-11 19:51:45 +00001480 #pragma omp parallel for schedule(static,1) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001481#endif
1482 for (y=0; y < (long) image->rows; y++)
1483 {
1484 register IndexPacket
1485 *__restrict indexes;
1486
1487 register long
1488 x;
1489
1490 register PixelPacket
1491 *__restrict q;
1492
1493 if (status == MagickFalse)
1494 continue;
1495 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1496 if (q == (PixelPacket *) NULL)
1497 {
1498 status=MagickFalse;
1499 continue;
1500 }
1501 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1502 for (x=0; x < (long) image->columns; x++)
1503 {
1504 register int
1505 threshold,
1506 t,
1507 l;
1508
1509 /*
1510 Figure out the dither threshold for this pixel
1511 This must be a integer from 1 to map->divisor-1
1512 */
1513 threshold = map->levels[(x%map->width) +map->width*(y%map->height)];
1514
1515 /* Dither each channel in the image as appropriate
1516 Notes on the integer Math...
1517 total number of divisions = (levels-1)*(divisor-1)+1)
1518 t1 = this colors psuedo_level =
1519 q->red * total_divisions / (QuantumRange+1)
1520 l = posterization level 0..levels
1521 t = dither threshold level 0..divisor-1 NB: 0 only on last
1522 Each color_level is of size QuantumRange / (levels-1)
1523 NB: All input levels and divisor are already had 1 subtracted
1524 Opacity is inverted so 'off' represents transparent.
1525 */
1526 if (levels.red) {
1527 t = (int) (QuantumScale*q->red*(levels.red*d+1));
1528 l = t/d; t = t-l*d;
1529 q->red=(Quantum) ((l+(t >= threshold))*QuantumRange/levels.red);
1530 }
1531 if (levels.green) {
1532 t = (int) (QuantumScale*q->green*(levels.green*d+1));
1533 l = t/d; t = t-l*d;
1534 q->green=(Quantum) ((l+(t >= threshold))*QuantumRange/levels.green);
1535 }
1536 if (levels.blue) {
1537 t = (int) (QuantumScale*q->blue*(levels.blue*d+1));
1538 l = t/d; t = t-l*d;
1539 q->blue=(Quantum) ((l+(t >= threshold))*QuantumRange/levels.blue);
1540 }
1541 if (levels.opacity) {
1542 t = (int) ((1.0-QuantumScale*q->opacity)*(levels.opacity*d+1));
1543 l = t/d; t = t-l*d;
1544 q->opacity=(Quantum) ((1.0-l-(t >= threshold))*QuantumRange/
1545 levels.opacity);
1546 }
1547 if (levels.index) {
1548 t = (int) (QuantumScale*indexes[x]*(levels.index*d+1));
1549 l = t/d; t = t-l*d;
1550 indexes[x]=(IndexPacket) ((l+(t>=threshold))*QuantumRange/
1551 levels.index);
1552 }
1553 q++;
1554 }
1555 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1556 status=MagickFalse;
1557 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1558 {
1559 MagickBooleanType
1560 proceed;
1561
cristy629a6c72009-09-13 23:28:22 +00001562#if defined(_OPENMP) && (_OPENMP >= 200203)
cristy3ed852e2009-09-05 21:47:34 +00001563 #pragma omp critical (MagickCore_OrderedPosterizeImageChannel)
1564#endif
1565 proceed=SetImageProgress(image,DitherImageTag,progress++,image->rows);
1566 if (proceed == MagickFalse)
1567 status=MagickFalse;
1568 }
1569 }
1570 image_view=DestroyCacheView(image_view);
1571 }
1572 map=DestroyThresholdMap(map);
1573 return(MagickTrue);
1574}
1575
1576/*
1577%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1578% %
1579% %
1580% %
1581% R a n d o m T h r e s h o l d I m a g e %
1582% %
1583% %
1584% %
1585%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1586%
1587% RandomThresholdImage() changes the value of individual pixels based on the
1588% intensity of each pixel compared to a random threshold. The result is a
1589% low-contrast, two color image.
1590%
1591% The format of the RandomThresholdImage method is:
1592%
1593% MagickBooleanType RandomThresholdImageChannel(Image *image,
1594% const char *thresholds,ExceptionInfo *exception)
1595% MagickBooleanType RandomThresholdImageChannel(Image *image,
1596% const ChannelType channel,const char *thresholds,
1597% ExceptionInfo *exception)
1598%
1599% A description of each parameter follows:
1600%
1601% o image: the image.
1602%
1603% o channel: the channel or channels to be thresholded.
1604%
1605% o thresholds: a geometry string containing low,high thresholds. If the
1606% string contains 2x2, 3x3, or 4x4, an ordered dither of order 2, 3, or 4
1607% is performed instead.
1608%
1609% o exception: return any errors or warnings in this structure.
1610%
1611*/
1612
1613MagickExport MagickBooleanType RandomThresholdImage(Image *image,
1614 const char *thresholds,ExceptionInfo *exception)
1615{
1616 MagickBooleanType
1617 status;
1618
1619 status=RandomThresholdImageChannel(image,DefaultChannels,thresholds,
1620 exception);
1621 return(status);
1622}
1623
1624MagickExport MagickBooleanType RandomThresholdImageChannel(Image *image,
1625 const ChannelType channel,const char *thresholds,ExceptionInfo *exception)
1626{
1627#define ThresholdImageTag "Threshold/Image"
1628
1629 GeometryInfo
1630 geometry_info;
1631
1632 MagickStatusType
1633 flags;
1634
1635 long
1636 progress,
1637 y;
1638
1639 MagickBooleanType
1640 status;
1641
1642 MagickPixelPacket
1643 threshold;
1644
1645 MagickRealType
1646 min_threshold,
1647 max_threshold;
1648
1649 RandomInfo
1650 **random_info;
1651
1652 CacheView
1653 *image_view;
1654
1655 assert(image != (Image *) NULL);
1656 assert(image->signature == MagickSignature);
1657 if (image->debug != MagickFalse)
1658 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1659 assert(exception != (ExceptionInfo *) NULL);
1660 assert(exception->signature == MagickSignature);
1661 if (thresholds == (const char *) NULL)
1662 return(MagickTrue);
1663 GetMagickPixelPacket(image,&threshold);
1664 min_threshold=0.0;
1665 max_threshold=(MagickRealType) QuantumRange;
1666 flags=ParseGeometry(thresholds,&geometry_info);
1667 min_threshold=geometry_info.rho;
1668 max_threshold=geometry_info.sigma;
1669 if ((flags & SigmaValue) == 0)
1670 max_threshold=min_threshold;
1671 if (strchr(thresholds,'%') != (char *) NULL)
1672 {
1673 max_threshold*=(MagickRealType) (0.01*QuantumRange);
1674 min_threshold*=(MagickRealType) (0.01*QuantumRange);
1675 }
1676 else
1677 if (((max_threshold == min_threshold) || (max_threshold == 1)) &&
1678 (min_threshold <= 8))
1679 {
1680 /*
1681 Backward Compatibility -- ordered-dither -- IM v 6.2.9-6.
1682 */
1683 status=OrderedPosterizeImageChannel(image,channel,thresholds,exception);
1684 return(status);
1685 }
1686 /*
1687 Random threshold image.
1688 */
1689 status=MagickTrue;
1690 progress=0;
1691 if (channel == AllChannels)
1692 {
1693 if (AcquireImageColormap(image,2) == MagickFalse)
1694 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1695 image->filename);
1696 random_info=AcquireRandomInfoThreadSet();
1697 image_view=AcquireCacheView(image);
cristy629a6c72009-09-13 23:28:22 +00001698#if defined(_OPENMP) && (_OPENMP >= 200203)
cristy07490992009-09-11 19:51:45 +00001699 #pragma omp parallel for schedule(static,1) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001700#endif
1701 for (y=0; y < (long) image->rows; y++)
1702 {
1703 MagickBooleanType
1704 sync;
1705
1706 register IndexPacket
1707 *__restrict indexes;
1708
1709 register long
1710 id,
1711 x;
1712
1713 register PixelPacket
1714 *__restrict q;
1715
1716 if (status == MagickFalse)
1717 continue;
1718 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1719 exception);
1720 if (q == (PixelPacket *) NULL)
1721 {
1722 status=MagickFalse;
1723 continue;
1724 }
1725 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1726 id=GetOpenMPThreadId();
1727 for (x=0; x < (long) image->columns; x++)
1728 {
1729 IndexPacket
1730 index;
1731
1732 MagickRealType
1733 intensity;
1734
1735 intensity=(MagickRealType) PixelIntensityToQuantum(q);
1736 if (intensity < min_threshold)
1737 threshold.index=min_threshold;
1738 else if (intensity > max_threshold)
1739 threshold.index=max_threshold;
1740 else
1741 threshold.index=(MagickRealType)(QuantumRange*
1742 GetPseudoRandomValue(random_info[id]));
1743 index=(IndexPacket) (intensity <= threshold.index ? 0 : 1);
1744 indexes[x]=index;
1745 *q++=image->colormap[(long) index];
1746 }
1747 sync=SyncCacheViewAuthenticPixels(image_view,exception);
1748 if (sync == MagickFalse)
1749 status=MagickFalse;
1750 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1751 {
1752 MagickBooleanType
1753 proceed;
1754
cristy629a6c72009-09-13 23:28:22 +00001755#if defined(_OPENMP) && (_OPENMP >= 200203)
cristy3ed852e2009-09-05 21:47:34 +00001756 #pragma omp critical (MagickCore_RandomThresholdImageChannel)
1757#endif
1758 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1759 image->rows);
1760 if (proceed == MagickFalse)
1761 status=MagickFalse;
1762 }
1763 }
1764 image_view=DestroyCacheView(image_view);
1765 random_info=DestroyRandomInfoThreadSet(random_info);
1766 return(status);
1767 }
1768 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1769 {
1770 InheritException(exception,&image->exception);
1771 return(MagickFalse);
1772 }
1773 random_info=AcquireRandomInfoThreadSet();
1774 image_view=AcquireCacheView(image);
cristy629a6c72009-09-13 23:28:22 +00001775#if defined(_OPENMP) && (_OPENMP >= 200203)
cristy07490992009-09-11 19:51:45 +00001776 #pragma omp parallel for schedule(static,1) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001777#endif
1778 for (y=0; y < (long) image->rows; y++)
1779 {
1780 register IndexPacket
1781 *__restrict indexes;
1782
1783 register long
1784 id,
1785 x;
1786
1787 register PixelPacket
1788 *__restrict q;
1789
1790 if (status == MagickFalse)
1791 continue;
1792 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1793 if (q == (PixelPacket *) NULL)
1794 {
1795 status=MagickFalse;
1796 continue;
1797 }
1798 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1799 id=GetOpenMPThreadId();
1800 for (x=0; x < (long) image->columns; x++)
1801 {
1802 if ((channel & RedChannel) != 0)
1803 {
1804 if ((MagickRealType) q->red < min_threshold)
1805 threshold.red=min_threshold;
1806 else
1807 if ((MagickRealType) q->red > max_threshold)
1808 threshold.red=max_threshold;
1809 else
1810 threshold.red=(MagickRealType) (QuantumRange*
1811 GetPseudoRandomValue(random_info[id]));
1812 }
1813 if ((channel & GreenChannel) != 0)
1814 {
1815 if ((MagickRealType) q->green < min_threshold)
1816 threshold.green=min_threshold;
1817 else
1818 if ((MagickRealType) q->green > max_threshold)
1819 threshold.green=max_threshold;
1820 else
1821 threshold.green=(MagickRealType) (QuantumRange*
1822 GetPseudoRandomValue(random_info[id]));
1823 }
1824 if ((channel & BlueChannel) != 0)
1825 {
1826 if ((MagickRealType) q->blue < min_threshold)
1827 threshold.blue=min_threshold;
1828 else
1829 if ((MagickRealType) q->blue > max_threshold)
1830 threshold.blue=max_threshold;
1831 else
1832 threshold.blue=(MagickRealType) (QuantumRange*
1833 GetPseudoRandomValue(random_info[id]));
1834 }
1835 if ((channel & OpacityChannel) != 0)
1836 {
1837 if ((MagickRealType) q->opacity < min_threshold)
1838 threshold.opacity=min_threshold;
1839 else
1840 if ((MagickRealType) q->opacity > max_threshold)
1841 threshold.opacity=max_threshold;
1842 else
1843 threshold.opacity=(MagickRealType) (QuantumRange*
1844 GetPseudoRandomValue(random_info[id]));
1845 }
1846 if (((channel & IndexChannel) != 0) &&
1847 (image->colorspace == CMYKColorspace))
1848 {
1849 if ((MagickRealType) indexes[x] < min_threshold)
1850 threshold.index=min_threshold;
1851 else
1852 if ((MagickRealType) indexes[x] > max_threshold)
1853 threshold.index=max_threshold;
1854 else
1855 threshold.index=(MagickRealType) (QuantumRange*
1856 GetPseudoRandomValue(random_info[id]));
1857 }
1858 if ((channel & RedChannel) != 0)
1859 q->red=(Quantum) ((MagickRealType) q->red <= threshold.red ? 0 :
1860 QuantumRange);
1861 if ((channel & GreenChannel) != 0)
1862 q->green=(Quantum) ((MagickRealType) q->green <= threshold.green ? 0 :
1863 QuantumRange);
1864 if ((channel & BlueChannel) != 0)
1865 q->blue=(Quantum) ((MagickRealType) q->blue <= threshold.blue ? 0 :
1866 QuantumRange);
1867 if ((channel & OpacityChannel) != 0)
1868 q->opacity=(Quantum) ((MagickRealType) q->opacity <= threshold.opacity ?
1869 0 : QuantumRange);
1870 if (((channel & IndexChannel) != 0) &&
1871 (image->colorspace == CMYKColorspace))
1872 indexes[x]=(IndexPacket) ((MagickRealType) indexes[x] <=
1873 threshold.index ? 0 : QuantumRange);
1874 q++;
1875 }
1876 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1877 status=MagickFalse;
1878 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1879 {
1880 MagickBooleanType
1881 proceed;
1882
cristy629a6c72009-09-13 23:28:22 +00001883#if defined(_OPENMP) && (_OPENMP >= 200203)
cristy3ed852e2009-09-05 21:47:34 +00001884 #pragma omp critical (MagickCore_RandomThresholdImageChannel)
1885#endif
1886 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1887 image->rows);
1888 if (proceed == MagickFalse)
1889 status=MagickFalse;
1890 }
1891 }
1892 image_view=DestroyCacheView(image_view);
1893 random_info=DestroyRandomInfoThreadSet(random_info);
1894 return(status);
1895}
1896
1897/*
1898%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1899% %
1900% %
1901% %
1902% W h i t e T h r e s h o l d I m a g e %
1903% %
1904% %
1905% %
1906%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1907%
1908% WhiteThresholdImage() is like ThresholdImage() but forces all pixels above
cristy4e101302009-09-17 12:49:12 +00001909% the threshold into white while leaving all pixels at or below the threshold
cristy3ed852e2009-09-05 21:47:34 +00001910% unchanged.
1911%
1912% The format of the WhiteThresholdImage method is:
1913%
1914% MagickBooleanType WhiteThresholdImage(Image *image,const char *threshold)
1915% MagickBooleanType WhiteThresholdImageChannel(Image *image,
1916% const ChannelType channel,const char *threshold,
1917% ExceptionInfo *exception)
1918%
1919% A description of each parameter follows:
1920%
1921% o image: the image.
1922%
1923% o channel: the channel or channels to be thresholded.
1924%
1925% o threshold: Define the threshold value.
1926%
1927% o exception: return any errors or warnings in this structure.
1928%
1929*/
1930MagickExport MagickBooleanType WhiteThresholdImage(Image *image,
1931 const char *threshold)
1932{
1933 MagickBooleanType
1934 status;
1935
1936 status=WhiteThresholdImageChannel(image,DefaultChannels,threshold,
1937 &image->exception);
1938 return(status);
1939}
1940
1941MagickExport MagickBooleanType WhiteThresholdImageChannel(Image *image,
1942 const ChannelType channel,const char *thresholds,ExceptionInfo *exception)
1943{
1944#define ThresholdImageTag "Threshold/Image"
1945
1946 GeometryInfo
1947 geometry_info;
1948
1949 long
1950 progress,
1951 y;
1952
1953 MagickBooleanType
1954 status;
1955
1956 MagickPixelPacket
1957 threshold;
1958
1959 MagickStatusType
1960 flags;
1961
1962 CacheView
1963 *image_view;
1964
1965 assert(image != (Image *) NULL);
1966 assert(image->signature == MagickSignature);
1967 if (image->debug != MagickFalse)
1968 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1969 if (thresholds == (const char *) NULL)
1970 return(MagickTrue);
1971 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1972 return(MagickFalse);
1973 flags=ParseGeometry(thresholds,&geometry_info);
1974 GetMagickPixelPacket(image,&threshold);
1975 threshold.red=geometry_info.rho;
1976 threshold.green=geometry_info.sigma;
1977 if ((flags & SigmaValue) == 0)
1978 threshold.green=threshold.red;
1979 threshold.blue=geometry_info.xi;
1980 if ((flags & XiValue) == 0)
1981 threshold.blue=threshold.red;
1982 threshold.opacity=geometry_info.psi;
1983 if ((flags & PsiValue) == 0)
1984 threshold.opacity=threshold.red;
1985 threshold.index=geometry_info.chi;
1986 if ((flags & ChiValue) == 0)
1987 threshold.index=threshold.red;
1988 if ((flags & PercentValue) != 0)
1989 {
1990 threshold.red*=(QuantumRange/100.0);
1991 threshold.green*=(QuantumRange/100.0);
1992 threshold.blue*=(QuantumRange/100.0);
1993 threshold.opacity*=(QuantumRange/100.0);
1994 threshold.index*=(QuantumRange/100.0);
1995 }
1996 /*
1997 White threshold image.
1998 */
1999 status=MagickTrue;
2000 progress=0;
2001 image_view=AcquireCacheView(image);
cristy629a6c72009-09-13 23:28:22 +00002002#if defined(_OPENMP) && (_OPENMP >= 200203)
cristy07490992009-09-11 19:51:45 +00002003 #pragma omp parallel for schedule(static,1) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00002004#endif
2005 for (y=0; y < (long) image->rows; y++)
2006 {
2007 register IndexPacket
2008 *__restrict indexes;
2009
2010 register long
2011 x;
2012
2013 register PixelPacket
2014 *__restrict q;
2015
2016 if (status == MagickFalse)
2017 continue;
2018 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2019 if (q == (PixelPacket *) NULL)
2020 {
2021 status=MagickFalse;
2022 continue;
2023 }
2024 indexes=GetCacheViewAuthenticIndexQueue(image_view);
2025 for (x=0; x < (long) image->columns; x++)
2026 {
2027 if (((channel & RedChannel) != 0) &&
2028 ((MagickRealType) q->red > threshold.red))
2029 q->red=(Quantum) QuantumRange;
2030 if (((channel & GreenChannel) != 0) &&
2031 ((MagickRealType) q->green > threshold.green))
2032 q->green=(Quantum) QuantumRange;
2033 if (((channel & BlueChannel) != 0) &&
2034 ((MagickRealType) q->blue > threshold.blue))
2035 q->blue=(Quantum) QuantumRange;
2036 if (((channel & OpacityChannel) != 0) &&
2037 ((MagickRealType) q->opacity > threshold.opacity))
2038 q->opacity=(Quantum) QuantumRange;
2039 if (((channel & IndexChannel) != 0) &&
2040 (image->colorspace == CMYKColorspace) &&
2041 ((MagickRealType) indexes[x] > threshold.index))
2042 indexes[x]=(Quantum) QuantumRange;
2043 q++;
2044 }
2045 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2046 status=MagickFalse;
2047 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2048 {
2049 MagickBooleanType
2050 proceed;
2051
cristy629a6c72009-09-13 23:28:22 +00002052#if defined(_OPENMP) && (_OPENMP >= 200203)
cristy3ed852e2009-09-05 21:47:34 +00002053 #pragma omp critical (MagickCore_WhiteThresholdImageChannel)
2054#endif
2055 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
2056 image->rows);
2057 if (proceed == MagickFalse)
2058 status=MagickFalse;
2059 }
2060 }
2061 image_view=DestroyCacheView(image_view);
2062 return(status);
2063}