1 /*
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 % Cristy %
17 % October 1996 %
18 % %
19 % %
20 % Copyright 1999-2016 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 "MagickCore/studio.h"
44 #include "MagickCore/property.h"
45 #include "MagickCore/blob.h"
46 #include "MagickCore/cache-view.h"
47 #include "MagickCore/color.h"
48 #include "MagickCore/color-private.h"
49 #include "MagickCore/colormap.h"
50 #include "MagickCore/colorspace.h"
51 #include "MagickCore/colorspace-private.h"
52 #include "MagickCore/configure.h"
53 #include "MagickCore/constitute.h"
54 #include "MagickCore/decorate.h"
55 #include "MagickCore/draw.h"
56 #include "MagickCore/enhance.h"
57 #include "MagickCore/exception.h"
58 #include "MagickCore/exception-private.h"
59 #include "MagickCore/effect.h"
60 #include "MagickCore/fx.h"
61 #include "MagickCore/gem.h"
62 #include "MagickCore/geometry.h"
63 #include "MagickCore/image-private.h"
64 #include "MagickCore/list.h"
65 #include "MagickCore/log.h"
66 #include "MagickCore/memory_.h"
67 #include "MagickCore/monitor.h"
68 #include "MagickCore/monitor-private.h"
69 #include "MagickCore/montage.h"
70 #include "MagickCore/option.h"
71 #include "MagickCore/pixel-accessor.h"
72 #include "MagickCore/pixel-private.h"
73 #include "MagickCore/quantize.h"
74 #include "MagickCore/quantum.h"
75 #include "MagickCore/random_.h"
76 #include "MagickCore/random-private.h"
77 #include "MagickCore/resize.h"
78 #include "MagickCore/resource_.h"
79 #include "MagickCore/segment.h"
80 #include "MagickCore/shear.h"
81 #include "MagickCore/signature-private.h"
82 #include "MagickCore/string_.h"
83 #include "MagickCore/string-private.h"
84 #include "MagickCore/thread-private.h"
85 #include "MagickCore/threshold.h"
86 #include "MagickCore/token.h"
87 #include "MagickCore/transform.h"
88 #include "MagickCore/xml-tree.h"
89 #include "MagickCore/xml-tree-private.h"
90
91 /*
92 Define declarations.
93 */
94 #define ThresholdsFilename "thresholds.xml"
95
96 /*
97 Typedef declarations.
98 */
99 struct _ThresholdMap
100 {
101 char
102 *map_id,
103 *description;
104
105 size_t
106 width,
107 height;
108
109 ssize_t
110 divisor,
111 *levels;
112 };
113
114 /*
115 Static declarations.
116 */
117 static const char
118 *MinimalThresholdMap =
119 "<?xml version=\"1.0\"?>"
120 "<thresholds>"
121 " <threshold map=\"threshold\" alias=\"1x1\">"
122 " <description>Threshold 1x1 (non-dither)</description>"
123 " <levels width=\"1\" height=\"1\" divisor=\"2\">"
124 " 1"
125 " </levels>"
126 " </threshold>"
127 " <threshold map=\"checks\" alias=\"2x1\">"
128 " <description>Checkerboard 2x1 (dither)</description>"
129 " <levels width=\"2\" height=\"2\" divisor=\"3\">"
130 " 1 2"
131 " 2 1"
132 " </levels>"
133 " </threshold>"
134 "</thresholds>";
135
136 /*
137 Forward declarations.
138 */
139 static ThresholdMap
140 *GetThresholdMapFile(const char *,const char *,const char *,ExceptionInfo *);
141
142 /*
143 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
144 % %
145 % %
146 % %
147 % A d a p t i v e T h r e s h o l d I m a g e %
148 % %
149 % %
150 % %
151 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
152 %
153 % AdaptiveThresholdImage() selects an individual threshold for each pixel
154 % based on the range of intensity values in its local neighborhood. This
155 % allows for thresholding of an image whose global intensity histogram
156 % doesn't contain distinctive peaks.
157 %
158 % The format of the AdaptiveThresholdImage method is:
159 %
160 % Image *AdaptiveThresholdImage(const Image *image,const size_t width,
161 % const size_t height,const double bias,ExceptionInfo *exception)
162 %
163 % A description of each parameter follows:
164 %
165 % o image: the image.
166 %
167 % o width: the width of the local neighborhood.
168 %
169 % o height: the height of the local neighborhood.
170 %
171 % o bias: the mean bias.
172 %
173 % o exception: return any errors or warnings in this structure.
174 %
175 */
AdaptiveThresholdImage(const Image * image,const size_t width,const size_t height,const double bias,ExceptionInfo * exception)176 MagickExport Image *AdaptiveThresholdImage(const Image *image,
177 const size_t width,const size_t height,const double bias,
178 ExceptionInfo *exception)
179 {
180 #define AdaptiveThresholdImageTag "AdaptiveThreshold/Image"
181
182 CacheView
183 *image_view,
184 *threshold_view;
185
186 Image
187 *threshold_image;
188
189 MagickBooleanType
190 status;
191
192 MagickOffsetType
193 progress;
194
195 MagickSizeType
196 number_pixels;
197
198 ssize_t
199 y;
200
201 /*
202 Initialize threshold image attributes.
203 */
204 assert(image != (Image *) NULL);
205 assert(image->signature == MagickCoreSignature);
206 if (image->debug != MagickFalse)
207 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
208 assert(exception != (ExceptionInfo *) NULL);
209 assert(exception->signature == MagickCoreSignature);
210 threshold_image=CloneImage(image,image->columns,image->rows,MagickTrue,
211 exception);
212 if (threshold_image == (Image *) NULL)
213 return((Image *) NULL);
214 status=SetImageStorageClass(threshold_image,DirectClass,exception);
215 if (status == MagickFalse)
216 {
217 threshold_image=DestroyImage(threshold_image);
218 return((Image *) NULL);
219 }
220 /*
221 Threshold image.
222 */
223 status=MagickTrue;
224 progress=0;
225 number_pixels=(MagickSizeType) width*height;
226 image_view=AcquireVirtualCacheView(image,exception);
227 threshold_view=AcquireAuthenticCacheView(threshold_image,exception);
228 #if defined(MAGICKCORE_OPENMP_SUPPORT)
229 #pragma omp parallel for schedule(static,4) shared(progress,status) \
230 magick_threads(image,threshold_image,image->rows,1)
231 #endif
232 for (y=0; y < (ssize_t) image->rows; y++)
233 {
234 double
235 channel_bias[MaxPixelChannels],
236 channel_sum[MaxPixelChannels];
237
238 register const Quantum
239 *magick_restrict p,
240 *magick_restrict pixels;
241
242 register Quantum
243 *magick_restrict q;
244
245 register ssize_t
246 i,
247 x;
248
249 ssize_t
250 center,
251 u,
252 v;
253
254 if (status == MagickFalse)
255 continue;
256 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
257 (height/2L),image->columns+width,height,exception);
258 q=QueueCacheViewAuthenticPixels(threshold_view,0,y,threshold_image->columns,
259 1,exception);
260 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
261 {
262 status=MagickFalse;
263 continue;
264 }
265 center=(ssize_t) GetPixelChannels(image)*(image->columns+width)*(height/2L)+
266 GetPixelChannels(image)*(width/2);
267 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
268 {
269 PixelChannel channel=GetPixelChannelChannel(image,i);
270 PixelTrait traits=GetPixelChannelTraits(image,channel);
271 PixelTrait threshold_traits=GetPixelChannelTraits(threshold_image,
272 channel);
273 if ((traits == UndefinedPixelTrait) ||
274 (threshold_traits == UndefinedPixelTrait))
275 continue;
276 if (((threshold_traits & CopyPixelTrait) != 0) ||
277 (GetPixelReadMask(image,p) == 0))
278 {
279 SetPixelChannel(threshold_image,channel,p[center+i],q);
280 continue;
281 }
282 pixels=p;
283 channel_bias[channel]=0.0;
284 channel_sum[channel]=0.0;
285 for (v=0; v < (ssize_t) height; v++)
286 {
287 for (u=0; u < (ssize_t) width; u++)
288 {
289 if (u == (ssize_t) (width-1))
290 channel_bias[channel]+=pixels[i];
291 channel_sum[channel]+=pixels[i];
292 pixels+=GetPixelChannels(image);
293 }
294 pixels+=GetPixelChannels(image)*image->columns;
295 }
296 }
297 for (x=0; x < (ssize_t) image->columns; x++)
298 {
299 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
300 {
301 double
302 mean;
303
304 PixelChannel channel=GetPixelChannelChannel(image,i);
305 PixelTrait traits=GetPixelChannelTraits(image,channel);
306 PixelTrait threshold_traits=GetPixelChannelTraits(threshold_image,
307 channel);
308 if ((traits == UndefinedPixelTrait) ||
309 (threshold_traits == UndefinedPixelTrait))
310 continue;
311 if (((threshold_traits & CopyPixelTrait) != 0) ||
312 (GetPixelReadMask(image,p) == 0))
313 {
314 SetPixelChannel(threshold_image,channel,p[center+i],q);
315 continue;
316 }
317 channel_sum[channel]-=channel_bias[channel];
318 channel_bias[channel]=0.0;
319 pixels=p;
320 for (v=0; v < (ssize_t) height; v++)
321 {
322 channel_bias[channel]+=pixels[i];
323 pixels+=(width-1)*GetPixelChannels(image);
324 channel_sum[channel]+=pixels[i];
325 pixels+=GetPixelChannels(image)*(image->columns+1);
326 }
327 mean=(double) (channel_sum[channel]/number_pixels+bias);
328 SetPixelChannel(threshold_image,channel,(Quantum) ((double)
329 p[center+i] <= mean ? 0 : QuantumRange),q);
330 }
331 p+=GetPixelChannels(image);
332 q+=GetPixelChannels(threshold_image);
333 }
334 if (SyncCacheViewAuthenticPixels(threshold_view,exception) == MagickFalse)
335 status=MagickFalse;
336 if (image->progress_monitor != (MagickProgressMonitor) NULL)
337 {
338 MagickBooleanType
339 proceed;
340
341 #if defined(MAGICKCORE_OPENMP_SUPPORT)
342 #pragma omp critical (MagickCore_AdaptiveThresholdImage)
343 #endif
344 proceed=SetImageProgress(image,AdaptiveThresholdImageTag,progress++,
345 image->rows);
346 if (proceed == MagickFalse)
347 status=MagickFalse;
348 }
349 }
350 threshold_image->type=image->type;
351 threshold_view=DestroyCacheView(threshold_view);
352 image_view=DestroyCacheView(image_view);
353 if (status == MagickFalse)
354 threshold_image=DestroyImage(threshold_image);
355 return(threshold_image);
356 }
357
358 /*
359 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
360 % %
361 % %
362 % %
363 % B i l e v e l I m a g e %
364 % %
365 % %
366 % %
367 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
368 %
369 % BilevelImage() changes the value of individual pixels based on the
370 % intensity of each pixel channel. The result is a high-contrast image.
371 %
372 % More precisely each channel value of the image is 'thresholded' so that if
373 % it is equal to or less than the given value it is set to zero, while any
374 % value greater than that give is set to it maximum or QuantumRange.
375 %
376 % This function is what is used to implement the "-threshold" operator for
377 % the command line API.
378 %
379 % If the default channel setting is given the image is thresholded using just
380 % the gray 'intensity' of the image, rather than the individual channels.
381 %
382 % The format of the BilevelImage method is:
383 %
384 % MagickBooleanType BilevelImage(Image *image,const double threshold,
385 % ExceptionInfo *exception)
386 %
387 % A description of each parameter follows:
388 %
389 % o image: the image.
390 %
391 % o threshold: define the threshold values.
392 %
393 % o exception: return any errors or warnings in this structure.
394 %
395 % Aside: You can get the same results as operator using LevelImages()
396 % with the 'threshold' value for both the black_point and the white_point.
397 %
398 */
BilevelImage(Image * image,const double threshold,ExceptionInfo * exception)399 MagickExport MagickBooleanType BilevelImage(Image *image,const double threshold,
400 ExceptionInfo *exception)
401 {
402 #define ThresholdImageTag "Threshold/Image"
403
404 CacheView
405 *image_view;
406
407 MagickBooleanType
408 status;
409
410 MagickOffsetType
411 progress;
412
413 ssize_t
414 y;
415
416 assert(image != (Image *) NULL);
417 assert(image->signature == MagickCoreSignature);
418 if (image->debug != MagickFalse)
419 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
420 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
421 return(MagickFalse);
422 if (IsGrayColorspace(image->colorspace) != MagickFalse)
423 (void) SetImageColorspace(image,sRGBColorspace,exception);
424 /*
425 Bilevel threshold image.
426 */
427 status=MagickTrue;
428 progress=0;
429 image_view=AcquireAuthenticCacheView(image,exception);
430 #if defined(MAGICKCORE_OPENMP_SUPPORT)
431 #pragma omp parallel for schedule(static,4) shared(progress,status) \
432 magick_threads(image,image,image->rows,1)
433 #endif
434 for (y=0; y < (ssize_t) image->rows; y++)
435 {
436 register ssize_t
437 x;
438
439 register Quantum
440 *magick_restrict q;
441
442 if (status == MagickFalse)
443 continue;
444 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
445 if (q == (Quantum *) NULL)
446 {
447 status=MagickFalse;
448 continue;
449 }
450 for (x=0; x < (ssize_t) image->columns; x++)
451 {
452 double
453 pixel;
454
455 register ssize_t
456 i;
457
458 if (GetPixelReadMask(image,q) == 0)
459 {
460 q+=GetPixelChannels(image);
461 continue;
462 }
463 pixel=GetPixelIntensity(image,q);
464 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
465 {
466 PixelChannel channel=GetPixelChannelChannel(image,i);
467 PixelTrait traits=GetPixelChannelTraits(image,channel);
468 if ((traits & UpdatePixelTrait) == 0)
469 continue;
470 if (image->channel_mask != DefaultChannels)
471 pixel=(double) q[i];
472 q[i]=(Quantum) (pixel <= threshold ? 0 : QuantumRange);
473 }
474 q+=GetPixelChannels(image);
475 }
476 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
477 status=MagickFalse;
478 if (image->progress_monitor != (MagickProgressMonitor) NULL)
479 {
480 MagickBooleanType
481 proceed;
482
483 #if defined(MAGICKCORE_OPENMP_SUPPORT)
484 #pragma omp critical (MagickCore_BilevelImage)
485 #endif
486 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
487 image->rows);
488 if (proceed == MagickFalse)
489 status=MagickFalse;
490 }
491 }
492 image_view=DestroyCacheView(image_view);
493 return(status);
494 }
495
496 /*
497 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
498 % %
499 % %
500 % %
501 % B l a c k T h r e s h o l d I m a g e %
502 % %
503 % %
504 % %
505 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
506 %
507 % BlackThresholdImage() is like ThresholdImage() but forces all pixels below
508 % the threshold into black while leaving all pixels at or above the threshold
509 % unchanged.
510 %
511 % The format of the BlackThresholdImage method is:
512 %
513 % MagickBooleanType BlackThresholdImage(Image *image,
514 % const char *threshold,ExceptionInfo *exception)
515 %
516 % A description of each parameter follows:
517 %
518 % o image: the image.
519 %
520 % o threshold: define the threshold value.
521 %
522 % o exception: return any errors or warnings in this structure.
523 %
524 */
BlackThresholdImage(Image * image,const char * thresholds,ExceptionInfo * exception)525 MagickExport MagickBooleanType BlackThresholdImage(Image *image,
526 const char *thresholds,ExceptionInfo *exception)
527 {
528 #define ThresholdImageTag "Threshold/Image"
529
530 CacheView
531 *image_view;
532
533 GeometryInfo
534 geometry_info;
535
536 MagickBooleanType
537 status;
538
539 MagickOffsetType
540 progress;
541
542 PixelInfo
543 threshold;
544
545 MagickStatusType
546 flags;
547
548 ssize_t
549 y;
550
551 assert(image != (Image *) NULL);
552 assert(image->signature == MagickCoreSignature);
553 if (image->debug != MagickFalse)
554 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
555 if (thresholds == (const char *) NULL)
556 return(MagickTrue);
557 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
558 return(MagickFalse);
559 if (IsGrayColorspace(image->colorspace) != MagickFalse)
560 (void) SetImageColorspace(image,sRGBColorspace,exception);
561 GetPixelInfo(image,&threshold);
562 flags=ParseGeometry(thresholds,&geometry_info);
563 threshold.red=geometry_info.rho;
564 threshold.green=geometry_info.rho;
565 threshold.blue=geometry_info.rho;
566 threshold.black=geometry_info.rho;
567 threshold.alpha=100.0;
568 if ((flags & SigmaValue) != 0)
569 threshold.green=geometry_info.sigma;
570 if ((flags & XiValue) != 0)
571 threshold.blue=geometry_info.xi;
572 if ((flags & PsiValue) != 0)
573 threshold.alpha=geometry_info.psi;
574 if (threshold.colorspace == CMYKColorspace)
575 {
576 if ((flags & PsiValue) != 0)
577 threshold.black=geometry_info.psi;
578 if ((flags & ChiValue) != 0)
579 threshold.alpha=geometry_info.chi;
580 }
581 if ((flags & PercentValue) != 0)
582 {
583 threshold.red*=(MagickRealType) (QuantumRange/100.0);
584 threshold.green*=(MagickRealType) (QuantumRange/100.0);
585 threshold.blue*=(MagickRealType) (QuantumRange/100.0);
586 threshold.black*=(MagickRealType) (QuantumRange/100.0);
587 threshold.alpha*=(MagickRealType) (QuantumRange/100.0);
588 }
589 /*
590 White threshold image.
591 */
592 status=MagickTrue;
593 progress=0;
594 image_view=AcquireAuthenticCacheView(image,exception);
595 #if defined(MAGICKCORE_OPENMP_SUPPORT)
596 #pragma omp parallel for schedule(static,4) shared(progress,status) \
597 magick_threads(image,image,image->rows,1)
598 #endif
599 for (y=0; y < (ssize_t) image->rows; y++)
600 {
601 register ssize_t
602 x;
603
604 register Quantum
605 *magick_restrict q;
606
607 if (status == MagickFalse)
608 continue;
609 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
610 if (q == (Quantum *) NULL)
611 {
612 status=MagickFalse;
613 continue;
614 }
615 for (x=0; x < (ssize_t) image->columns; x++)
616 {
617 double
618 pixel;
619
620 register ssize_t
621 i;
622
623 if (GetPixelReadMask(image,q) == 0)
624 {
625 q+=GetPixelChannels(image);
626 continue;
627 }
628 pixel=GetPixelIntensity(image,q);
629 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
630 {
631 PixelChannel channel=GetPixelChannelChannel(image,i);
632 PixelTrait traits=GetPixelChannelTraits(image,channel);
633 if ((traits & UpdatePixelTrait) == 0)
634 continue;
635 if (image->channel_mask != DefaultChannels)
636 pixel=(double) q[i];
637 if (pixel < GetPixelInfoChannel(&threshold,channel))
638 q[i]=(Quantum) 0;
639 }
640 q+=GetPixelChannels(image);
641 }
642 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
643 status=MagickFalse;
644 if (image->progress_monitor != (MagickProgressMonitor) NULL)
645 {
646 MagickBooleanType
647 proceed;
648
649 #if defined(MAGICKCORE_OPENMP_SUPPORT)
650 #pragma omp critical (MagickCore_BlackThresholdImage)
651 #endif
652 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
653 image->rows);
654 if (proceed == MagickFalse)
655 status=MagickFalse;
656 }
657 }
658 image_view=DestroyCacheView(image_view);
659 return(status);
660 }
661
662 /*
663 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
664 % %
665 % %
666 % %
667 % C l a m p I m a g e %
668 % %
669 % %
670 % %
671 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
672 %
673 % ClampImage() set each pixel whose value is below zero to zero and any the
674 % pixel whose value is above the quantum range to the quantum range (e.g.
675 % 65535) otherwise the pixel value remains unchanged.
676 %
677 % The format of the ClampImage method is:
678 %
679 % MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
680 %
681 % A description of each parameter follows:
682 %
683 % o image: the image.
684 %
685 % o exception: return any errors or warnings in this structure.
686 %
687 */
688
ClampImage(Image * image,ExceptionInfo * exception)689 MagickExport MagickBooleanType ClampImage(Image *image,ExceptionInfo *exception)
690 {
691 #define ClampImageTag "Clamp/Image"
692
693 CacheView
694 *image_view;
695
696 MagickBooleanType
697 status;
698
699 MagickOffsetType
700 progress;
701
702 ssize_t
703 y;
704
705 assert(image != (Image *) NULL);
706 assert(image->signature == MagickCoreSignature);
707 if (image->debug != MagickFalse)
708 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
709 if (image->storage_class == PseudoClass)
710 {
711 register ssize_t
712 i;
713
714 register PixelInfo
715 *magick_restrict q;
716
717 q=image->colormap;
718 for (i=0; i < (ssize_t) image->colors; i++)
719 {
720 q->red=(double) ClampPixel(q->red);
721 q->green=(double) ClampPixel(q->green);
722 q->blue=(double) ClampPixel(q->blue);
723 q->alpha=(double) ClampPixel(q->alpha);
724 q++;
725 }
726 return(SyncImage(image,exception));
727 }
728 /*
729 Clamp image.
730 */
731 status=MagickTrue;
732 progress=0;
733 image_view=AcquireAuthenticCacheView(image,exception);
734 #if defined(MAGICKCORE_OPENMP_SUPPORT)
735 #pragma omp parallel for schedule(static,4) shared(progress,status) \
736 magick_threads(image,image,image->rows,1)
737 #endif
738 for (y=0; y < (ssize_t) image->rows; y++)
739 {
740 register ssize_t
741 x;
742
743 register Quantum
744 *magick_restrict q;
745
746 if (status == MagickFalse)
747 continue;
748 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
749 if (q == (Quantum *) NULL)
750 {
751 status=MagickFalse;
752 continue;
753 }
754 for (x=0; x < (ssize_t) image->columns; x++)
755 {
756 register ssize_t
757 i;
758
759 if (GetPixelReadMask(image,q) == 0)
760 {
761 q+=GetPixelChannels(image);
762 continue;
763 }
764 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
765 {
766 PixelChannel channel=GetPixelChannelChannel(image,i);
767 PixelTrait traits=GetPixelChannelTraits(image,channel);
768 if ((traits & UpdatePixelTrait) == 0)
769 continue;
770 q[i]=ClampPixel(q[i]);
771 }
772 q+=GetPixelChannels(image);
773 }
774 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
775 status=MagickFalse;
776 if (image->progress_monitor != (MagickProgressMonitor) NULL)
777 {
778 MagickBooleanType
779 proceed;
780
781 #if defined(MAGICKCORE_OPENMP_SUPPORT)
782 #pragma omp critical (MagickCore_ClampImage)
783 #endif
784 proceed=SetImageProgress(image,ClampImageTag,progress++,image->rows);
785 if (proceed == MagickFalse)
786 status=MagickFalse;
787 }
788 }
789 image_view=DestroyCacheView(image_view);
790 return(status);
791 }
792
793 /*
794 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
795 % %
796 % %
797 % %
798 % D e s t r o y T h r e s h o l d M a p %
799 % %
800 % %
801 % %
802 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
803 %
804 % DestroyThresholdMap() de-allocate the given ThresholdMap
805 %
806 % The format of the ListThresholdMaps method is:
807 %
808 % ThresholdMap *DestroyThresholdMap(Threshold *map)
809 %
810 % A description of each parameter follows.
811 %
812 % o map: Pointer to the Threshold map to destroy
813 %
814 */
DestroyThresholdMap(ThresholdMap * map)815 MagickExport ThresholdMap *DestroyThresholdMap(ThresholdMap *map)
816 {
817 assert(map != (ThresholdMap *) NULL);
818 if (map->map_id != (char *) NULL)
819 map->map_id=DestroyString(map->map_id);
820 if (map->description != (char *) NULL)
821 map->description=DestroyString(map->description);
822 if (map->levels != (ssize_t *) NULL)
823 map->levels=(ssize_t *) RelinquishMagickMemory(map->levels);
824 map=(ThresholdMap *) RelinquishMagickMemory(map);
825 return(map);
826 }
827
828 /*
829 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
830 % %
831 % %
832 % %
833 % G e t T h r e s h o l d M a p %
834 % %
835 % %
836 % %
837 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
838 %
839 % GetThresholdMap() loads and searches one or more threshold map files for the
840 % map matching the given name or alias.
841 %
842 % The format of the GetThresholdMap method is:
843 %
844 % ThresholdMap *GetThresholdMap(const char *map_id,
845 % ExceptionInfo *exception)
846 %
847 % A description of each parameter follows.
848 %
849 % o map_id: ID of the map to look for.
850 %
851 % o exception: return any errors or warnings in this structure.
852 %
853 */
GetThresholdMap(const char * map_id,ExceptionInfo * exception)854 MagickExport ThresholdMap *GetThresholdMap(const char *map_id,
855 ExceptionInfo *exception)
856 {
857 ThresholdMap
858 *map;
859
860 map=GetThresholdMapFile(MinimalThresholdMap,"built-in",map_id,exception);
861 if (map != (ThresholdMap *) NULL)
862 return(map);
863 #if !defined(MAGICKCORE_ZERO_CONFIGURATION_SUPPORT)
864 {
865 const StringInfo
866 *option;
867
868 LinkedListInfo
869 *options;
870
871 options=GetConfigureOptions(ThresholdsFilename,exception);
872 option=(const StringInfo *) GetNextValueInLinkedList(options);
873 while (option != (const StringInfo *) NULL)
874 {
875 map=GetThresholdMapFile((const char *) GetStringInfoDatum(option),
876 GetStringInfoPath(option),map_id,exception);
877 if (map != (ThresholdMap *) NULL)
878 break;
879 option=(const StringInfo *) GetNextValueInLinkedList(options);
880 }
881 options=DestroyConfigureOptions(options);
882 }
883 #endif
884 return(map);
885 }
886
887 /*
888 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
889 % %
890 % %
891 % %
892 + G e t T h r e s h o l d M a p F i l e %
893 % %
894 % %
895 % %
896 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
897 %
898 % GetThresholdMapFile() look for a given threshold map name or alias in the
899 % given XML file data, and return the allocated the map when found.
900 %
901 % The format of the ListThresholdMaps method is:
902 %
903 % ThresholdMap *GetThresholdMap(const char *xml,const char *filename,
904 % const char *map_id,ExceptionInfo *exception)
905 %
906 % A description of each parameter follows.
907 %
908 % o xml: The threshold map list in XML format.
909 %
910 % o filename: The threshold map XML filename.
911 %
912 % o map_id: ID of the map to look for in XML list.
913 %
914 % o exception: return any errors or warnings in this structure.
915 %
916 */
GetThresholdMapFile(const char * xml,const char * filename,const char * map_id,ExceptionInfo * exception)917 static ThresholdMap *GetThresholdMapFile(const char *xml,const char *filename,
918 const char *map_id,ExceptionInfo *exception)
919 {
920 char
921 *p;
922
923 const char
924 *attribute,
925 *content;
926
927 double
928 value;
929
930 register ssize_t
931 i;
932
933 ThresholdMap
934 *map;
935
936 XMLTreeInfo
937 *description,
938 *levels,
939 *threshold,
940 *thresholds;
941
942 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
943 "Loading threshold map file \"%s\" ...",filename);
944 map=(ThresholdMap *) NULL;
945 thresholds=NewXMLTree(xml,exception);
946 if (thresholds == (XMLTreeInfo *) NULL)
947 return(map);
948 for (threshold=GetXMLTreeChild(thresholds,"threshold");
949 threshold != (XMLTreeInfo *) NULL;
950 threshold=GetNextXMLTreeTag(threshold))
951 {
952 attribute=GetXMLTreeAttribute(threshold,"map");
953 if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
954 break;
955 attribute=GetXMLTreeAttribute(threshold,"alias");
956 if ((attribute != (char *) NULL) && (LocaleCompare(map_id,attribute) == 0))
957 break;
958 }
959 if (threshold == (XMLTreeInfo *) NULL)
960 {
961 thresholds=DestroyXMLTree(thresholds);
962 return(map);
963 }
964 description=GetXMLTreeChild(threshold,"description");
965 if (description == (XMLTreeInfo *) NULL)
966 {
967 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
968 "XmlMissingElement", "<description>, map \"%s\"",map_id);
969 thresholds=DestroyXMLTree(thresholds);
970 return(map);
971 }
972 levels=GetXMLTreeChild(threshold,"levels");
973 if (levels == (XMLTreeInfo *) NULL)
974 {
975 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
976 "XmlMissingElement", "<levels>, map \"%s\"", map_id);
977 thresholds=DestroyXMLTree(thresholds);
978 return(map);
979 }
980 map=(ThresholdMap *) AcquireMagickMemory(sizeof(ThresholdMap));
981 if (map == (ThresholdMap *) NULL)
982 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
983 map->map_id=(char *) NULL;
984 map->description=(char *) NULL;
985 map->levels=(ssize_t *) NULL;
986 attribute=GetXMLTreeAttribute(threshold,"map");
987 if (attribute != (char *) NULL)
988 map->map_id=ConstantString(attribute);
989 content=GetXMLTreeContent(description);
990 if (content != (char *) NULL)
991 map->description=ConstantString(content);
992 attribute=GetXMLTreeAttribute(levels,"width");
993 if (attribute == (char *) NULL)
994 {
995 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
996 "XmlMissingAttribute", "<levels width>, map \"%s\"",map_id);
997 thresholds=DestroyXMLTree(thresholds);
998 map=DestroyThresholdMap(map);
999 return(map);
1000 }
1001 map->width=StringToUnsignedLong(attribute);
1002 if (map->width == 0)
1003 {
1004 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1005 "XmlInvalidAttribute", "<levels width>, map \"%s\"",map_id);
1006 thresholds=DestroyXMLTree(thresholds);
1007 map=DestroyThresholdMap(map);
1008 return(map);
1009 }
1010 attribute=GetXMLTreeAttribute(levels,"height");
1011 if (attribute == (char *) NULL)
1012 {
1013 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1014 "XmlMissingAttribute", "<levels height>, map \"%s\"",map_id);
1015 thresholds=DestroyXMLTree(thresholds);
1016 map=DestroyThresholdMap(map);
1017 return(map);
1018 }
1019 map->height=StringToUnsignedLong(attribute);
1020 if (map->height == 0)
1021 {
1022 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1023 "XmlInvalidAttribute", "<levels height>, map \"%s\"",map_id);
1024 thresholds=DestroyXMLTree(thresholds);
1025 map=DestroyThresholdMap(map);
1026 return(map);
1027 }
1028 attribute=GetXMLTreeAttribute(levels,"divisor");
1029 if (attribute == (char *) NULL)
1030 {
1031 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1032 "XmlMissingAttribute", "<levels divisor>, map \"%s\"",map_id);
1033 thresholds=DestroyXMLTree(thresholds);
1034 map=DestroyThresholdMap(map);
1035 return(map);
1036 }
1037 map->divisor=(ssize_t) StringToLong(attribute);
1038 if (map->divisor < 2)
1039 {
1040 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1041 "XmlInvalidAttribute", "<levels divisor>, map \"%s\"",map_id);
1042 thresholds=DestroyXMLTree(thresholds);
1043 map=DestroyThresholdMap(map);
1044 return(map);
1045 }
1046 content=GetXMLTreeContent(levels);
1047 if (content == (char *) NULL)
1048 {
1049 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1050 "XmlMissingContent", "<levels>, map \"%s\"",map_id);
1051 thresholds=DestroyXMLTree(thresholds);
1052 map=DestroyThresholdMap(map);
1053 return(map);
1054 }
1055 map->levels=(ssize_t *) AcquireQuantumMemory((size_t) map->width,map->height*
1056 sizeof(*map->levels));
1057 if (map->levels == (ssize_t *) NULL)
1058 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
1059 for (i=0; i < (ssize_t) (map->width*map->height); i++)
1060 {
1061 map->levels[i]=(ssize_t) strtol(content,&p,10);
1062 if (p == content)
1063 {
1064 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1065 "XmlInvalidContent", "<level> too few values, map \"%s\"",map_id);
1066 thresholds=DestroyXMLTree(thresholds);
1067 map=DestroyThresholdMap(map);
1068 return(map);
1069 }
1070 if ((map->levels[i] < 0) || (map->levels[i] > map->divisor))
1071 {
1072 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1073 "XmlInvalidContent", "<level> %.20g out of range, map \"%s\"",
1074 (double) map->levels[i],map_id);
1075 thresholds=DestroyXMLTree(thresholds);
1076 map=DestroyThresholdMap(map);
1077 return(map);
1078 }
1079 content=p;
1080 }
1081 value=(double) strtol(content,&p,10);
1082 (void) value;
1083 if (p != content)
1084 {
1085 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1086 "XmlInvalidContent", "<level> too many values, map \"%s\"",map_id);
1087 thresholds=DestroyXMLTree(thresholds);
1088 map=DestroyThresholdMap(map);
1089 return(map);
1090 }
1091 thresholds=DestroyXMLTree(thresholds);
1092 return(map);
1093 }
1094
1095 /*
1096 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1097 % %
1098 % %
1099 % %
1100 + L i s t T h r e s h o l d M a p F i l e %
1101 % %
1102 % %
1103 % %
1104 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1105 %
1106 % ListThresholdMapFile() lists the threshold maps and their descriptions
1107 % in the given XML file data.
1108 %
1109 % The format of the ListThresholdMaps method is:
1110 %
1111 % MagickBooleanType ListThresholdMaps(FILE *file,const char*xml,
1112 % const char *filename,ExceptionInfo *exception)
1113 %
1114 % A description of each parameter follows.
1115 %
1116 % o file: An pointer to the output FILE.
1117 %
1118 % o xml: The threshold map list in XML format.
1119 %
1120 % o filename: The threshold map XML filename.
1121 %
1122 % o exception: return any errors or warnings in this structure.
1123 %
1124 */
ListThresholdMapFile(FILE * file,const char * xml,const char * filename,ExceptionInfo * exception)1125 MagickBooleanType ListThresholdMapFile(FILE *file,const char *xml,
1126 const char *filename,ExceptionInfo *exception)
1127 {
1128 const char
1129 *alias,
1130 *content,
1131 *map;
1132
1133 XMLTreeInfo
1134 *description,
1135 *threshold,
1136 *thresholds;
1137
1138 assert( xml != (char *) NULL );
1139 assert( file != (FILE *) NULL );
1140 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1141 "Loading threshold map file \"%s\" ...",filename);
1142 thresholds=NewXMLTree(xml,exception);
1143 if ( thresholds == (XMLTreeInfo *) NULL )
1144 return(MagickFalse);
1145 (void) FormatLocaleFile(file,"%-16s %-12s %s\n","Map","Alias","Description");
1146 (void) FormatLocaleFile(file,
1147 "----------------------------------------------------\n");
1148 threshold=GetXMLTreeChild(thresholds,"threshold");
1149 for ( ; threshold != (XMLTreeInfo *) NULL;
1150 threshold=GetNextXMLTreeTag(threshold))
1151 {
1152 map=GetXMLTreeAttribute(threshold,"map");
1153 if (map == (char *) NULL)
1154 {
1155 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1156 "XmlMissingAttribute", "<map>");
1157 thresholds=DestroyXMLTree(thresholds);
1158 return(MagickFalse);
1159 }
1160 alias=GetXMLTreeAttribute(threshold,"alias");
1161 description=GetXMLTreeChild(threshold,"description");
1162 if (description == (XMLTreeInfo *) NULL)
1163 {
1164 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1165 "XmlMissingElement", "<description>, map \"%s\"",map);
1166 thresholds=DestroyXMLTree(thresholds);
1167 return(MagickFalse);
1168 }
1169 content=GetXMLTreeContent(description);
1170 if (content == (char *) NULL)
1171 {
1172 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1173 "XmlMissingContent", "<description>, map \"%s\"", map);
1174 thresholds=DestroyXMLTree(thresholds);
1175 return(MagickFalse);
1176 }
1177 (void) FormatLocaleFile(file,"%-16s %-12s %s\n",map,alias ? alias : "",
1178 content);
1179 }
1180 thresholds=DestroyXMLTree(thresholds);
1181 return(MagickTrue);
1182 }
1183
1184 /*
1185 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1186 % %
1187 % %
1188 % %
1189 % L i s t T h r e s h o l d M a p s %
1190 % %
1191 % %
1192 % %
1193 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1194 %
1195 % ListThresholdMaps() lists the threshold maps and their descriptions
1196 % as defined by "threshold.xml" to a file.
1197 %
1198 % The format of the ListThresholdMaps method is:
1199 %
1200 % MagickBooleanType ListThresholdMaps(FILE *file,ExceptionInfo *exception)
1201 %
1202 % A description of each parameter follows.
1203 %
1204 % o file: An pointer to the output FILE.
1205 %
1206 % o exception: return any errors or warnings in this structure.
1207 %
1208 */
ListThresholdMaps(FILE * file,ExceptionInfo * exception)1209 MagickExport MagickBooleanType ListThresholdMaps(FILE *file,
1210 ExceptionInfo *exception)
1211 {
1212 const StringInfo
1213 *option;
1214
1215 LinkedListInfo
1216 *options;
1217
1218 MagickStatusType
1219 status;
1220
1221 status=MagickTrue;
1222 if (file == (FILE *) NULL)
1223 file=stdout;
1224 options=GetConfigureOptions(ThresholdsFilename,exception);
1225 (void) FormatLocaleFile(file,
1226 "\n Threshold Maps for Ordered Dither Operations\n");
1227 option=(const StringInfo *) GetNextValueInLinkedList(options);
1228 while (option != (const StringInfo *) NULL)
1229 {
1230 (void) FormatLocaleFile(file,"\nPath: %s\n\n",GetStringInfoPath(option));
1231 status&=ListThresholdMapFile(file,(const char *) GetStringInfoDatum(option),
1232 GetStringInfoPath(option),exception);
1233 option=(const StringInfo *) GetNextValueInLinkedList(options);
1234 }
1235 options=DestroyConfigureOptions(options);
1236 return(status != 0 ? MagickTrue : MagickFalse);
1237 }
1238
1239 /*
1240 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1241 % %
1242 % %
1243 % %
1244 % O r d e r e d D i t h e r I m a g e %
1245 % %
1246 % %
1247 % %
1248 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1249 %
1250 % OrderedDitherImage() will perform a ordered dither based on a number
1251 % of pre-defined dithering threshold maps, but over multiple intensity
1252 % levels, which can be different for different channels, according to the
1253 % input argument.
1254 %
1255 % The format of the OrderedDitherImage method is:
1256 %
1257 % MagickBooleanType OrderedDitherImage(Image *image,
1258 % const char *threshold_map,ExceptionInfo *exception)
1259 %
1260 % A description of each parameter follows:
1261 %
1262 % o image: the image.
1263 %
1264 % o threshold_map: A string containing the name of the threshold dither
1265 % map to use, followed by zero or more numbers representing the number
1266 % of color levels tho dither between.
1267 %
1268 % Any level number less than 2 will be equivalent to 2, and means only
1269 % binary dithering will be applied to each color channel.
1270 %
1271 % No numbers also means a 2 level (bitmap) dither will be applied to all
1272 % channels, while a single number is the number of levels applied to each
1273 % channel in sequence. More numbers will be applied in turn to each of
1274 % the color channels.
1275 %
1276 % For example: "o3x3,6" will generate a 6 level posterization of the
1277 % image with a ordered 3x3 diffused pixel dither being applied between
1278 % each level. While checker,8,8,4 will produce a 332 colormaped image
1279 % with only a single checkerboard hash pattern (50% grey) between each
1280 % color level, to basically double the number of color levels with
1281 % a bare minimim of dithering.
1282 %
1283 % o exception: return any errors or warnings in this structure.
1284 %
1285 */
OrderedDitherImage(Image * image,const char * threshold_map,ExceptionInfo * exception)1286 MagickExport MagickBooleanType OrderedDitherImage(Image *image,
1287 const char *threshold_map,ExceptionInfo *exception)
1288 {
1289 #define DitherImageTag "Dither/Image"
1290
1291 CacheView
1292 *image_view;
1293
1294 char
1295 token[MagickPathExtent];
1296
1297 const char
1298 *p;
1299
1300 double
1301 levels[CompositePixelChannel];
1302
1303 MagickBooleanType
1304 status;
1305
1306 MagickOffsetType
1307 progress;
1308
1309 register ssize_t
1310 i;
1311
1312 ssize_t
1313 y;
1314
1315 ThresholdMap
1316 *map;
1317
1318 assert(image != (Image *) NULL);
1319 assert(image->signature == MagickCoreSignature);
1320 if (image->debug != MagickFalse)
1321 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1322 assert(exception != (ExceptionInfo *) NULL);
1323 assert(exception->signature == MagickCoreSignature);
1324 if (threshold_map == (const char *) NULL)
1325 return(MagickTrue);
1326 p=(char *) threshold_map;
1327 while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
1328 (*p != '\0'))
1329 p++;
1330 threshold_map=p;
1331 while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
1332 (*p != '\0'))
1333 {
1334 if ((p-threshold_map) >= (MagickPathExtent-1))
1335 break;
1336 token[p-threshold_map]=(*p);
1337 p++;
1338 }
1339 token[p-threshold_map]='\0';
1340 map=GetThresholdMap(token,exception);
1341 if (map == (ThresholdMap *) NULL)
1342 {
1343 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1344 "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
1345 return(MagickFalse);
1346 }
1347 for (i=0; i < MaxPixelChannels; i++)
1348 levels[i]=2.0;
1349 p=strchr((char *) threshold_map,',');
1350 if ((p != (char *) NULL) && (isdigit((int) ((unsigned char) *(++p))) != 0))
1351 for (i=0; (*p != '\0') && (i < MaxPixelChannels); i++)
1352 {
1353 GetNextToken(p,&p,MagickPathExtent,token);
1354 if (*token == ',')
1355 GetNextToken(p,&p,MagickPathExtent,token);
1356 levels[i]=StringToDouble(token,(char **) NULL);
1357 }
1358 for (i=0; i < MaxPixelChannels; i++)
1359 if (fabs(levels[i]) >= 1)
1360 levels[i]-=1.0;
1361 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1362 return(MagickFalse);
1363 status=MagickTrue;
1364 progress=0;
1365 image_view=AcquireAuthenticCacheView(image,exception);
1366 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1367 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1368 magick_threads(image,image,image->rows,1)
1369 #endif
1370 for (y=0; y < (ssize_t) image->rows; y++)
1371 {
1372 register ssize_t
1373 x;
1374
1375 register Quantum
1376 *magick_restrict q;
1377
1378 if (status == MagickFalse)
1379 continue;
1380 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1381 if (q == (Quantum *) NULL)
1382 {
1383 status=MagickFalse;
1384 continue;
1385 }
1386 for (x=0; x < (ssize_t) image->columns; x++)
1387 {
1388 register ssize_t
1389 i;
1390
1391 ssize_t
1392 n;
1393
1394 n=0;
1395 if (GetPixelReadMask(image,q) == 0)
1396 {
1397 q+=GetPixelChannels(image);
1398 continue;
1399 }
1400 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1401 {
1402 ssize_t
1403 level,
1404 threshold;
1405
1406 PixelChannel channel=GetPixelChannelChannel(image,i);
1407 PixelTrait traits=GetPixelChannelTraits(image,channel);
1408 if ((traits & UpdatePixelTrait) == 0)
1409 continue;
1410 if (fabs(levels[n++]) < MagickEpsilon)
1411 continue;
1412 threshold=(ssize_t) (QuantumScale*q[i]*(levels[n]*(map->divisor-1)+1));
1413 level=threshold/(map->divisor-1);
1414 threshold-=level*(map->divisor-1);
1415 q[i]=ClampToQuantum((double) (level+(threshold >=
1416 map->levels[(x % map->width)+map->width*(y % map->height)]))*
1417 QuantumRange/levels[n]);
1418 n++;
1419 }
1420 q+=GetPixelChannels(image);
1421 }
1422 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1423 status=MagickFalse;
1424 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1425 {
1426 MagickBooleanType
1427 proceed;
1428
1429 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1430 #pragma omp critical (MagickCore_OrderedDitherImage)
1431 #endif
1432 proceed=SetImageProgress(image,DitherImageTag,progress++,image->rows);
1433 if (proceed == MagickFalse)
1434 status=MagickFalse;
1435 }
1436 }
1437 image_view=DestroyCacheView(image_view);
1438 map=DestroyThresholdMap(map);
1439 return(MagickTrue);
1440 }
1441
1442 /*
1443 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1444 % %
1445 % %
1446 % %
1447 % P e r c e p t i b l e I m a g e %
1448 % %
1449 % %
1450 % %
1451 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1452 %
1453 % PerceptibleImage() set each pixel whose value is less than |epsilon| to
1454 % epsilon or -epsilon (whichever is closer) otherwise the pixel value remains
1455 % unchanged.
1456 %
1457 % The format of the PerceptibleImage method is:
1458 %
1459 % MagickBooleanType PerceptibleImage(Image *image,const double epsilon,
1460 % ExceptionInfo *exception)
1461 %
1462 % A description of each parameter follows:
1463 %
1464 % o image: the image.
1465 %
1466 % o epsilon: the epsilon threshold (e.g. 1.0e-9).
1467 %
1468 % o exception: return any errors or warnings in this structure.
1469 %
1470 */
1471
PerceptibleThreshold(const Quantum quantum,const double epsilon)1472 static inline Quantum PerceptibleThreshold(const Quantum quantum,
1473 const double epsilon)
1474 {
1475 double
1476 sign;
1477
1478 sign=(double) quantum < 0.0 ? -1.0 : 1.0;
1479 if ((sign*quantum) >= epsilon)
1480 return(quantum);
1481 return((Quantum) (sign*epsilon));
1482 }
1483
PerceptibleImage(Image * image,const double epsilon,ExceptionInfo * exception)1484 MagickExport MagickBooleanType PerceptibleImage(Image *image,
1485 const double epsilon,ExceptionInfo *exception)
1486 {
1487 #define PerceptibleImageTag "Perceptible/Image"
1488
1489 CacheView
1490 *image_view;
1491
1492 MagickBooleanType
1493 status;
1494
1495 MagickOffsetType
1496 progress;
1497
1498 ssize_t
1499 y;
1500
1501 assert(image != (Image *) NULL);
1502 assert(image->signature == MagickCoreSignature);
1503 if (image->debug != MagickFalse)
1504 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1505 if (image->storage_class == PseudoClass)
1506 {
1507 register ssize_t
1508 i;
1509
1510 register PixelInfo
1511 *magick_restrict q;
1512
1513 q=image->colormap;
1514 for (i=0; i < (ssize_t) image->colors; i++)
1515 {
1516 q->red=(double) PerceptibleThreshold(ClampToQuantum(q->red),
1517 epsilon);
1518 q->green=(double) PerceptibleThreshold(ClampToQuantum(q->green),
1519 epsilon);
1520 q->blue=(double) PerceptibleThreshold(ClampToQuantum(q->blue),
1521 epsilon);
1522 q->alpha=(double) PerceptibleThreshold(ClampToQuantum(q->alpha),
1523 epsilon);
1524 q++;
1525 }
1526 return(SyncImage(image,exception));
1527 }
1528 /*
1529 Perceptible image.
1530 */
1531 status=MagickTrue;
1532 progress=0;
1533 image_view=AcquireAuthenticCacheView(image,exception);
1534 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1535 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1536 magick_threads(image,image,image->rows,1)
1537 #endif
1538 for (y=0; y < (ssize_t) image->rows; y++)
1539 {
1540 register ssize_t
1541 x;
1542
1543 register Quantum
1544 *magick_restrict q;
1545
1546 if (status == MagickFalse)
1547 continue;
1548 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1549 if (q == (Quantum *) NULL)
1550 {
1551 status=MagickFalse;
1552 continue;
1553 }
1554 for (x=0; x < (ssize_t) image->columns; x++)
1555 {
1556 register ssize_t
1557 i;
1558
1559 if (GetPixelReadMask(image,q) == 0)
1560 {
1561 q+=GetPixelChannels(image);
1562 continue;
1563 }
1564 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1565 {
1566 PixelChannel channel=GetPixelChannelChannel(image,i);
1567 PixelTrait traits=GetPixelChannelTraits(image,channel);
1568 if (traits == UndefinedPixelTrait)
1569 continue;
1570 q[i]=PerceptibleThreshold(q[i],epsilon);
1571 }
1572 q+=GetPixelChannels(image);
1573 }
1574 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1575 status=MagickFalse;
1576 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1577 {
1578 MagickBooleanType
1579 proceed;
1580
1581 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1582 #pragma omp critical (MagickCore_PerceptibleImage)
1583 #endif
1584 proceed=SetImageProgress(image,PerceptibleImageTag,progress++,image->rows);
1585 if (proceed == MagickFalse)
1586 status=MagickFalse;
1587 }
1588 }
1589 image_view=DestroyCacheView(image_view);
1590 return(status);
1591 }
1592
1593 /*
1594 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1595 % %
1596 % %
1597 % %
1598 % R a n d o m T h r e s h o l d I m a g e %
1599 % %
1600 % %
1601 % %
1602 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1603 %
1604 % RandomThresholdImage() changes the value of individual pixels based on the
1605 % intensity of each pixel compared to a random threshold. The result is a
1606 % low-contrast, two color image.
1607 %
1608 % The format of the RandomThresholdImage method is:
1609 %
1610 % MagickBooleanType RandomThresholdImage(Image *image,
1611 % const char *thresholds,ExceptionInfo *exception)
1612 %
1613 % A description of each parameter follows:
1614 %
1615 % o image: the image.
1616 %
1617 % o low,high: Specify the high and low thresholds. These values range from
1618 % 0 to QuantumRange.
1619 %
1620 % o exception: return any errors or warnings in this structure.
1621 %
1622 */
RandomThresholdImage(Image * image,const double min_threshold,const double max_threshold,ExceptionInfo * exception)1623 MagickExport MagickBooleanType RandomThresholdImage(Image *image,
1624 const double min_threshold, const double max_threshold,ExceptionInfo *exception)
1625 {
1626 #define ThresholdImageTag "Threshold/Image"
1627
1628 CacheView
1629 *image_view;
1630
1631 MagickBooleanType
1632 status;
1633
1634 MagickOffsetType
1635 progress;
1636
1637 PixelInfo
1638 threshold;
1639
1640 RandomInfo
1641 **magick_restrict random_info;
1642
1643 ssize_t
1644 y;
1645
1646 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1647 unsigned long
1648 key;
1649 #endif
1650
1651 assert(image != (Image *) NULL);
1652 assert(image->signature == MagickCoreSignature);
1653 if (image->debug != MagickFalse)
1654 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1655 assert(exception != (ExceptionInfo *) NULL);
1656 assert(exception->signature == MagickCoreSignature);
1657 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1658 return(MagickFalse);
1659 GetPixelInfo(image,&threshold);
1660 /*
1661 Random threshold image.
1662 */
1663 status=MagickTrue;
1664 progress=0;
1665 random_info=AcquireRandomInfoThreadSet();
1666 image_view=AcquireAuthenticCacheView(image,exception);
1667 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1668 key=GetRandomSecretKey(random_info[0]);
1669 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1670 magick_threads(image,image,image->rows,key == ~0UL)
1671 #endif
1672 for (y=0; y < (ssize_t) image->rows; y++)
1673 {
1674 const int
1675 id = GetOpenMPThreadId();
1676
1677 register Quantum
1678 *magick_restrict q;
1679
1680 register ssize_t
1681 x;
1682
1683 if (status == MagickFalse)
1684 continue;
1685 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1686 if (q == (Quantum *) NULL)
1687 {
1688 status=MagickFalse;
1689 continue;
1690 }
1691 for (x=0; x < (ssize_t) image->columns; x++)
1692 {
1693 register ssize_t
1694 i;
1695
1696 if (GetPixelReadMask(image,q) == 0)
1697 {
1698 q+=GetPixelChannels(image);
1699 continue;
1700 }
1701 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1702 {
1703 double
1704 threshold;
1705
1706 PixelChannel channel=GetPixelChannelChannel(image,i);
1707 PixelTrait traits=GetPixelChannelTraits(image,channel);
1708 if ((traits & UpdatePixelTrait) == 0)
1709 continue;
1710 if ((double) q[i] < min_threshold)
1711 threshold=min_threshold;
1712 else
1713 if ((double) q[i] > max_threshold)
1714 threshold=max_threshold;
1715 else
1716 threshold=(double) (QuantumRange*
1717 GetPseudoRandomValue(random_info[id]));
1718 q[i]=(double) q[i] <= threshold ? 0 : QuantumRange;
1719 }
1720 q+=GetPixelChannels(image);
1721 }
1722 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1723 status=MagickFalse;
1724 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1725 {
1726 MagickBooleanType
1727 proceed;
1728
1729 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1730 #pragma omp critical (MagickCore_RandomThresholdImage)
1731 #endif
1732 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1733 image->rows);
1734 if (proceed == MagickFalse)
1735 status=MagickFalse;
1736 }
1737 }
1738 image_view=DestroyCacheView(image_view);
1739 random_info=DestroyRandomInfoThreadSet(random_info);
1740 return(status);
1741 }
1742
1743 /*
1744 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1745 % %
1746 % %
1747 % %
1748 % W h i t e T h r e s h o l d I m a g e %
1749 % %
1750 % %
1751 % %
1752 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1753 %
1754 % WhiteThresholdImage() is like ThresholdImage() but forces all pixels above
1755 % the threshold into white while leaving all pixels at or below the threshold
1756 % unchanged.
1757 %
1758 % The format of the WhiteThresholdImage method is:
1759 %
1760 % MagickBooleanType WhiteThresholdImage(Image *image,
1761 % const char *threshold,ExceptionInfo *exception)
1762 %
1763 % A description of each parameter follows:
1764 %
1765 % o image: the image.
1766 %
1767 % o threshold: Define the threshold value.
1768 %
1769 % o exception: return any errors or warnings in this structure.
1770 %
1771 */
WhiteThresholdImage(Image * image,const char * thresholds,ExceptionInfo * exception)1772 MagickExport MagickBooleanType WhiteThresholdImage(Image *image,
1773 const char *thresholds,ExceptionInfo *exception)
1774 {
1775 #define ThresholdImageTag "Threshold/Image"
1776
1777 CacheView
1778 *image_view;
1779
1780 GeometryInfo
1781 geometry_info;
1782
1783 MagickBooleanType
1784 status;
1785
1786 MagickOffsetType
1787 progress;
1788
1789 PixelInfo
1790 threshold;
1791
1792 MagickStatusType
1793 flags;
1794
1795 ssize_t
1796 y;
1797
1798 assert(image != (Image *) NULL);
1799 assert(image->signature == MagickCoreSignature);
1800 if (image->debug != MagickFalse)
1801 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1802 if (thresholds == (const char *) NULL)
1803 return(MagickTrue);
1804 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1805 return(MagickFalse);
1806 if (IsGrayColorspace(image->colorspace) != MagickFalse)
1807 (void) TransformImageColorspace(image,sRGBColorspace,exception);
1808 GetPixelInfo(image,&threshold);
1809 flags=ParseGeometry(thresholds,&geometry_info);
1810 threshold.red=geometry_info.rho;
1811 threshold.green=geometry_info.rho;
1812 threshold.blue=geometry_info.rho;
1813 threshold.black=geometry_info.rho;
1814 threshold.alpha=100.0;
1815 if ((flags & SigmaValue) != 0)
1816 threshold.green=geometry_info.sigma;
1817 if ((flags & XiValue) != 0)
1818 threshold.blue=geometry_info.xi;
1819 if ((flags & PsiValue) != 0)
1820 threshold.alpha=geometry_info.psi;
1821 if (threshold.colorspace == CMYKColorspace)
1822 {
1823 if ((flags & PsiValue) != 0)
1824 threshold.black=geometry_info.psi;
1825 if ((flags & ChiValue) != 0)
1826 threshold.alpha=geometry_info.chi;
1827 }
1828 if ((flags & PercentValue) != 0)
1829 {
1830 threshold.red*=(MagickRealType) (QuantumRange/100.0);
1831 threshold.green*=(MagickRealType) (QuantumRange/100.0);
1832 threshold.blue*=(MagickRealType) (QuantumRange/100.0);
1833 threshold.black*=(MagickRealType) (QuantumRange/100.0);
1834 threshold.alpha*=(MagickRealType) (QuantumRange/100.0);
1835 }
1836 /*
1837 White threshold image.
1838 */
1839 status=MagickTrue;
1840 progress=0;
1841 image_view=AcquireAuthenticCacheView(image,exception);
1842 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1843 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1844 magick_threads(image,image,image->rows,1)
1845 #endif
1846 for (y=0; y < (ssize_t) image->rows; y++)
1847 {
1848 register ssize_t
1849 x;
1850
1851 register Quantum
1852 *magick_restrict q;
1853
1854 if (status == MagickFalse)
1855 continue;
1856 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1857 if (q == (Quantum *) NULL)
1858 {
1859 status=MagickFalse;
1860 continue;
1861 }
1862 for (x=0; x < (ssize_t) image->columns; x++)
1863 {
1864 double
1865 pixel;
1866
1867 register ssize_t
1868 i;
1869
1870 if (GetPixelReadMask(image,q) == 0)
1871 {
1872 q+=GetPixelChannels(image);
1873 continue;
1874 }
1875 pixel=GetPixelIntensity(image,q);
1876 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1877 {
1878 PixelChannel channel=GetPixelChannelChannel(image,i);
1879 PixelTrait traits=GetPixelChannelTraits(image,channel);
1880 if ((traits & UpdatePixelTrait) == 0)
1881 continue;
1882 if (image->channel_mask != DefaultChannels)
1883 pixel=(double) q[i];
1884 if (pixel > GetPixelInfoChannel(&threshold,channel))
1885 q[i]=QuantumRange;
1886 }
1887 q+=GetPixelChannels(image);
1888 }
1889 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1890 status=MagickFalse;
1891 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1892 {
1893 MagickBooleanType
1894 proceed;
1895
1896 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1897 #pragma omp critical (MagickCore_WhiteThresholdImage)
1898 #endif
1899 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
1900 image->rows);
1901 if (proceed == MagickFalse)
1902 status=MagickFalse;
1903 }
1904 }
1905 image_view=DestroyCacheView(image_view);
1906 return(status);
1907 }
1908