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