00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043 #include "magick/studio.h"
00044 #include "magick/property.h"
00045 #include "magick/blob.h"
00046 #include "magick/cache-view.h"
00047 #include "magick/color.h"
00048 #include "magick/color-private.h"
00049 #include "magick/colorspace.h"
00050 #include "magick/configure.h"
00051 #include "magick/constitute.h"
00052 #include "magick/decorate.h"
00053 #include "magick/draw.h"
00054 #include "magick/enhance.h"
00055 #include "magick/exception.h"
00056 #include "magick/exception-private.h"
00057 #include "magick/effect.h"
00058 #include "magick/fx.h"
00059 #include "magick/gem.h"
00060 #include "magick/geometry.h"
00061 #include "magick/image-private.h"
00062 #include "magick/list.h"
00063 #include "magick/log.h"
00064 #include "magick/memory_.h"
00065 #include "magick/monitor.h"
00066 #include "magick/monitor-private.h"
00067 #include "magick/montage.h"
00068 #include "magick/pixel-private.h"
00069 #include "magick/quantize.h"
00070 #include "magick/quantum.h"
00071 #include "magick/random_.h"
00072 #include "magick/random-private.h"
00073 #include "magick/resize.h"
00074 #include "magick/resource_.h"
00075 #include "magick/segment.h"
00076 #include "magick/shear.h"
00077 #include "magick/signature-private.h"
00078 #include "magick/string_.h"
00079 #include "magick/transform.h"
00080 #include "magick/threshold.h"
00081 #include "magick/option.h"
00082 #include "magick/xml-tree.h"
00083
00084
00085
00086
00087 #define ThresholdsFilename "thresholds.xml"
00088
00089
00090
00091
00092 struct _ThresholdMap
00093 {
00094 char
00095 *map_id,
00096 *description;
00097
00098 unsigned long
00099 width,
00100 height;
00101
00102 long
00103 divisor,
00104 *levels;
00105 };
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142 MagickExport Image *AdaptiveThresholdImage(const Image *image,
00143 const unsigned long width,const unsigned long height,const long offset,
00144 ExceptionInfo *exception)
00145 {
00146 #define ThresholdImageTag "Threshold/Image"
00147
00148 Image
00149 *threshold_image;
00150
00151 long
00152 progress,
00153 y;
00154
00155 MagickBooleanType
00156 status;
00157
00158 MagickPixelPacket
00159 zero;
00160
00161 MagickRealType
00162 number_pixels;
00163
00164 CacheView
00165 *image_view,
00166 *threshold_view;
00167
00168 assert(image != (const Image *) NULL);
00169 assert(image->signature == MagickSignature);
00170 if (image->debug != MagickFalse)
00171 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00172 assert(exception != (ExceptionInfo *) NULL);
00173 assert(exception->signature == MagickSignature);
00174 if ((image->columns < width) || (image->rows < height))
00175 ThrowImageException(OptionError,"ImageSmallerThanRadius");
00176 threshold_image=CloneImage(image,0,0,MagickTrue,exception);
00177 if (threshold_image == (Image *) NULL)
00178 return((Image *) NULL);
00179 if (SetImageStorageClass(threshold_image,DirectClass) == MagickFalse)
00180 {
00181 InheritException(exception,&threshold_image->exception);
00182 threshold_image=DestroyImage(threshold_image);
00183 return((Image *) NULL);
00184 }
00185
00186
00187
00188 status=MagickTrue;
00189 progress=0;
00190 GetMagickPixelPacket(image,&zero);
00191 number_pixels=(MagickRealType) width*height;
00192 image_view=AcquireCacheView(image);
00193 threshold_view=AcquireCacheView(threshold_image);
00194 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00195 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
00196 #endif
00197 for (y=0; y < (long) image->rows; y++)
00198 {
00199 MagickBooleanType
00200 sync;
00201
00202 register const IndexPacket
00203 *__restrict indexes;
00204
00205 register const PixelPacket
00206 *__restrict p;
00207
00208 register IndexPacket
00209 *__restrict threshold_indexes;
00210
00211 register long
00212 x;
00213
00214 register PixelPacket
00215 *__restrict q;
00216
00217 if (status == MagickFalse)
00218 continue;
00219 p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-height/2L,
00220 image->columns+width,height,exception);
00221 q=GetCacheViewAuthenticPixels(threshold_view,0,y,threshold_image->columns,1,
00222 exception);
00223 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
00224 {
00225 status=MagickFalse;
00226 continue;
00227 }
00228 indexes=GetCacheViewVirtualIndexQueue(image_view);
00229 threshold_indexes=GetCacheViewAuthenticIndexQueue(threshold_view);
00230 for (x=0; x < (long) image->columns; x++)
00231 {
00232 long
00233 v;
00234
00235 MagickPixelPacket
00236 mean,
00237 pixel;
00238
00239 register const PixelPacket
00240 *r;
00241
00242 register long
00243 u;
00244
00245 pixel=zero;
00246 mean=zero;
00247 r=p;
00248 for (v=0; v < (long) height; v++)
00249 {
00250 for (u=0; u < (long) width; u++)
00251 {
00252 pixel.red+=r[u].red;
00253 pixel.green+=r[u].green;
00254 pixel.blue+=r[u].blue;
00255 pixel.opacity+=r[u].opacity;
00256 if (image->colorspace == CMYKColorspace)
00257 pixel.index=(MagickRealType) indexes[x+(r-p)+u];
00258 }
00259 r+=image->columns+width;
00260 }
00261 mean.red=(MagickRealType) (pixel.red/number_pixels+offset);
00262 mean.green=(MagickRealType) (pixel.green/number_pixels+offset);
00263 mean.blue=(MagickRealType) (pixel.blue/number_pixels+offset);
00264 mean.opacity=(MagickRealType) (pixel.opacity/number_pixels+offset);
00265 if (image->colorspace == CMYKColorspace)
00266 mean.index=(MagickRealType) (pixel.index/number_pixels+offset);
00267 q->red=(Quantum) (((MagickRealType) q->red <= mean.red) ?
00268 0 : QuantumRange);
00269 q->green=(Quantum) (((MagickRealType) q->green <= mean.green) ?
00270 0 : QuantumRange);
00271 q->blue=(Quantum) (((MagickRealType) q->blue <= mean.blue) ?
00272 0 : QuantumRange);
00273 q->opacity=(Quantum) (((MagickRealType) q->opacity <= mean.opacity) ?
00274 0 : QuantumRange);
00275 if (image->colorspace == CMYKColorspace)
00276 threshold_indexes[x]=(IndexPacket) (((MagickRealType)
00277 threshold_indexes[x] <= mean.index) ? 0 : QuantumRange);
00278 p++;
00279 q++;
00280 }
00281 sync=SyncCacheViewAuthenticPixels(threshold_view,exception);
00282 if (sync == MagickFalse)
00283 status=MagickFalse;
00284 if (image->progress_monitor != (MagickProgressMonitor) NULL)
00285 {
00286 MagickBooleanType
00287 proceed;
00288
00289 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00290 #pragma omp critical (MagickCore_AdaptiveThresholdImage)
00291 #endif
00292 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
00293 image->rows);
00294 if (proceed == MagickFalse)
00295 status=MagickFalse;
00296 }
00297 }
00298 threshold_view=DestroyCacheView(threshold_view);
00299 image_view=DestroyCacheView(image_view);
00300 if (status == MagickFalse)
00301 threshold_image=DestroyImage(threshold_image);
00302 return(threshold_image);
00303 }
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335
00336
00337
00338
00339
00340
00341
00342
00343
00344
00345
00346
00347
00348 MagickExport MagickBooleanType BilevelImage(Image *image,const double threshold)
00349 {
00350 MagickBooleanType
00351 status;
00352
00353 status=BilevelImageChannel(image,DefaultChannels,threshold);
00354 return(status);
00355 }
00356
00357 MagickExport MagickBooleanType BilevelImageChannel(Image *image,
00358 const ChannelType channel,const double threshold)
00359 {
00360 #define ThresholdImageTag "Threshold/Image"
00361
00362 ExceptionInfo
00363 *exception;
00364
00365 long
00366 progress,
00367 y;
00368
00369 MagickBooleanType
00370 status;
00371
00372 CacheView
00373 *image_view;
00374
00375 assert(image != (Image *) NULL);
00376 assert(image->signature == MagickSignature);
00377 if (image->debug != MagickFalse)
00378 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00379 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
00380 return(MagickFalse);
00381
00382
00383
00384 status=MagickTrue;
00385 progress=0;
00386 exception=(&image->exception);
00387 image_view=AcquireCacheView(image);
00388 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00389 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
00390 #endif
00391 for (y=0; y < (long) image->rows; y++)
00392 {
00393 register IndexPacket
00394 *__restrict indexes;
00395
00396 register long
00397 x;
00398
00399 register PixelPacket
00400 *__restrict q;
00401
00402 if (status == MagickFalse)
00403 continue;
00404 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
00405 if (q == (PixelPacket *) NULL)
00406 {
00407 status=MagickFalse;
00408 continue;
00409 }
00410 indexes=GetCacheViewAuthenticIndexQueue(image_view);
00411 if (channel == DefaultChannels)
00412 {
00413 for (x=0; x < (long) image->columns; x++)
00414 {
00415 q->red=(Quantum) ((MagickRealType) PixelIntensityToQuantum(q) <=
00416 threshold ? 0 : QuantumRange);
00417 q->green=q->red;
00418 q->blue=q->red;
00419 q++;
00420 }
00421 }
00422 else
00423 for (x=0; x < (long) image->columns; x++)
00424 {
00425 if ((channel & RedChannel) != 0)
00426 q->red=(Quantum) ((MagickRealType) q->red <= threshold ? 0 :
00427 QuantumRange);
00428 if ((channel & GreenChannel) != 0)
00429 q->green=(Quantum) ((MagickRealType) q->green <= threshold ? 0 :
00430 QuantumRange);
00431 if ((channel & BlueChannel) != 0)
00432 q->blue=(Quantum) ((MagickRealType) q->blue <= threshold ? 0 :
00433 QuantumRange);
00434 if ((channel & OpacityChannel) != 0)
00435 {
00436 if (image->matte == MagickFalse)
00437 q->opacity=(Quantum) ((MagickRealType) q->opacity <= threshold ?
00438 0 : QuantumRange);
00439 else
00440 q->opacity=(Quantum) ((MagickRealType) q->opacity <= threshold ?
00441 OpaqueOpacity : TransparentOpacity);
00442 }
00443 if (((channel & IndexChannel) != 0) &&
00444 (image->colorspace == CMYKColorspace))
00445 indexes[x]=(IndexPacket) ((MagickRealType) indexes[x] <= threshold ?
00446 0 : QuantumRange);
00447 q++;
00448 }
00449 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
00450 status=MagickFalse;
00451 if (image->progress_monitor != (MagickProgressMonitor) NULL)
00452 {
00453 MagickBooleanType
00454 proceed;
00455
00456 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00457 #pragma omp critical (MagickCore_BilevelImageChannel)
00458 #endif
00459 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
00460 image->rows);
00461 if (proceed == MagickFalse)
00462 status=MagickFalse;
00463 }
00464 }
00465 image_view=DestroyCacheView(image_view);
00466 return(status);
00467 }
00468
00469
00470
00471
00472
00473
00474
00475
00476
00477
00478
00479
00480
00481
00482
00483
00484
00485
00486
00487
00488
00489
00490
00491
00492
00493
00494
00495
00496
00497
00498
00499
00500
00501
00502 MagickExport MagickBooleanType BlackThresholdImage(Image *image,
00503 const char *threshold)
00504 {
00505 MagickBooleanType
00506 status;
00507
00508 status=BlackThresholdImageChannel(image,DefaultChannels,threshold,
00509 &image->exception);
00510 return(status);
00511 }
00512
00513 MagickExport MagickBooleanType BlackThresholdImageChannel(Image *image,
00514 const ChannelType channel,const char *thresholds,ExceptionInfo *exception)
00515 {
00516 #define ThresholdImageTag "Threshold/Image"
00517
00518 GeometryInfo
00519 geometry_info;
00520
00521 long
00522 progress,
00523 y;
00524
00525 MagickBooleanType
00526 status;
00527
00528 MagickPixelPacket
00529 threshold;
00530
00531 MagickStatusType
00532 flags;
00533
00534 CacheView
00535 *image_view;
00536
00537 assert(image != (Image *) NULL);
00538 assert(image->signature == MagickSignature);
00539 if (image->debug != MagickFalse)
00540 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00541 if (thresholds == (const char *) NULL)
00542 return(MagickTrue);
00543 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
00544 return(MagickFalse);
00545 GetMagickPixelPacket(image,&threshold);
00546 flags=ParseGeometry(thresholds,&geometry_info);
00547 threshold.red=geometry_info.rho;
00548 threshold.green=geometry_info.sigma;
00549 if ((flags & SigmaValue) == 0)
00550 threshold.green=threshold.red;
00551 threshold.blue=geometry_info.xi;
00552 if ((flags & XiValue) == 0)
00553 threshold.blue=threshold.red;
00554 threshold.opacity=geometry_info.psi;
00555 if ((flags & PsiValue) == 0)
00556 threshold.opacity=threshold.red;
00557 threshold.index=geometry_info.chi;
00558 if ((flags & ChiValue) == 0)
00559 threshold.index=threshold.red;
00560 if ((flags & PercentValue) != 0)
00561 {
00562 threshold.red*=(QuantumRange/100.0);
00563 threshold.green*=(QuantumRange/100.0);
00564 threshold.blue*=(QuantumRange/100.0);
00565 threshold.opacity*=(QuantumRange/100.0);
00566 threshold.index*=(QuantumRange/100.0);
00567 }
00568
00569
00570
00571 status=MagickTrue;
00572 progress=0;
00573 image_view=AcquireCacheView(image);
00574 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00575 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
00576 #endif
00577 for (y=0; y < (long) image->rows; y++)
00578 {
00579 register IndexPacket
00580 *__restrict indexes;
00581
00582 register long
00583 x;
00584
00585 register PixelPacket
00586 *__restrict q;
00587
00588 if (status == MagickFalse)
00589 continue;
00590 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
00591 if (q == (PixelPacket *) NULL)
00592 {
00593 status=MagickFalse;
00594 continue;
00595 }
00596 indexes=GetCacheViewAuthenticIndexQueue(image_view);
00597 if (channel == DefaultChannels)
00598 for (x=0; x < (long) image->columns; x++)
00599 {
00600 if ((MagickRealType) q->red < threshold.red)
00601 q->red=(Quantum) 0;
00602 if ((MagickRealType) q->green < threshold.green)
00603 q->green=(Quantum) 0;
00604 if ((MagickRealType) q->blue < threshold.blue)
00605 q->blue=(Quantum) 0;
00606 if ((image->colorspace == CMYKColorspace) &&
00607 ((MagickRealType) indexes[x] < threshold.index))
00608 indexes[x]=(Quantum) 0;
00609 q++;
00610 }
00611 else
00612 for (x=0; x < (long) image->columns; x++)
00613 {
00614 if (((channel & RedChannel) != 0) &&
00615 ((MagickRealType) q->red < threshold.red))
00616 q->red=(Quantum) 0;
00617 if (((channel & GreenChannel) != 0) &&
00618 ((MagickRealType) q->green < threshold.green))
00619 q->green=(Quantum) 0;
00620 if (((channel & BlueChannel) != 0) &&
00621 ((MagickRealType) q->blue < threshold.blue))
00622 q->blue=(Quantum) 0;
00623 if (((channel & OpacityChannel) != 0) &&
00624 ((MagickRealType) q->opacity < threshold.opacity))
00625 q->opacity=(Quantum) 0;
00626 if (((channel & IndexChannel) != 0) &&
00627 (image->colorspace == CMYKColorspace) &&
00628 ((MagickRealType) indexes[x] < threshold.index))
00629 indexes[x]=(Quantum) 0;
00630 q++;
00631 }
00632 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
00633 status=MagickFalse;
00634 if (image->progress_monitor != (MagickProgressMonitor) NULL)
00635 {
00636 MagickBooleanType
00637 proceed;
00638
00639 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00640 #pragma omp critical (MagickCore_BlackThresholdImageChannel)
00641 #endif
00642 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
00643 image->rows);
00644 if (proceed == MagickFalse)
00645 status=MagickFalse;
00646 }
00647 }
00648 image_view=DestroyCacheView(image_view);
00649 return(status);
00650 }
00651
00652
00653
00654
00655
00656
00657
00658
00659
00660
00661
00662
00663
00664
00665
00666
00667
00668
00669
00670
00671
00672
00673
00674
00675
00676
00677
00678
00679 static inline Quantum ClampToQuantum(const Quantum quantum)
00680 {
00681 #if defined(MAGICKCORE_HDRI_SUPPORT)
00682 if (quantum <= 0)
00683 return(0);
00684 if (quantum >= QuantumRange)
00685 return(QuantumRange);
00686 return(quantum);
00687 #else
00688 return(quantum);
00689 #endif
00690 }
00691
00692 MagickExport MagickBooleanType ClampImage(Image *image)
00693 {
00694 MagickBooleanType
00695 status;
00696
00697 status=ClampImageChannel(image,DefaultChannels);
00698 return(status);
00699 }
00700
00701 MagickExport MagickBooleanType ClampImageChannel(Image *image,
00702 const ChannelType channel)
00703 {
00704 #define ClampImageTag "Clamp/Image"
00705
00706 ExceptionInfo
00707 *exception;
00708
00709 long
00710 progress,
00711 y;
00712
00713 MagickBooleanType
00714 status;
00715
00716 CacheView
00717 *image_view;
00718
00719 assert(image != (Image *) NULL);
00720 assert(image->signature == MagickSignature);
00721 if (image->debug != MagickFalse)
00722 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00723 if (image->storage_class == PseudoClass)
00724 {
00725 register long
00726 i;
00727
00728 register PixelPacket
00729 *__restrict q;
00730
00731 q=image->colormap;
00732 for (i=0; i < (long) image->colors; i++)
00733 {
00734 q->red=ClampToQuantum(q->red);
00735 q->green=ClampToQuantum(q->green);
00736 q->blue=ClampToQuantum(q->blue);
00737 q->opacity=ClampToQuantum(q->opacity);
00738 q++;
00739 }
00740 return(SyncImage(image));
00741 }
00742
00743
00744
00745 status=MagickTrue;
00746 progress=0;
00747 exception=(&image->exception);
00748 image_view=AcquireCacheView(image);
00749 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00750 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
00751 #endif
00752 for (y=0; y < (long) image->rows; y++)
00753 {
00754 register IndexPacket
00755 *__restrict indexes;
00756
00757 register long
00758 x;
00759
00760 register PixelPacket
00761 *__restrict q;
00762
00763 if (status == MagickFalse)
00764 continue;
00765 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
00766 if (q == (PixelPacket *) NULL)
00767 {
00768 status=MagickFalse;
00769 continue;
00770 }
00771 indexes=GetCacheViewAuthenticIndexQueue(image_view);
00772 for (x=0; x < (long) image->columns; x++)
00773 {
00774 if ((channel & RedChannel) != 0)
00775 q->red=ClampToQuantum(q->red);
00776 if ((channel & GreenChannel) != 0)
00777 q->green=ClampToQuantum(q->green);
00778 if ((channel & BlueChannel) != 0)
00779 q->blue=ClampToQuantum(q->blue);
00780 if ((channel & OpacityChannel) != 0)
00781 q->opacity=ClampToQuantum(q->opacity);
00782 if (((channel & IndexChannel) != 0) &&
00783 (image->colorspace == CMYKColorspace))
00784 indexes[x]=(IndexPacket) ClampToQuantum(indexes[x]);
00785 q++;
00786 }
00787 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
00788 status=MagickFalse;
00789 if (image->progress_monitor != (MagickProgressMonitor) NULL)
00790 {
00791 MagickBooleanType
00792 proceed;
00793
00794 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00795 #pragma omp critical (MagickCore_ClampImageChannel)
00796 #endif
00797 proceed=SetImageProgress(image,ClampImageTag,progress++,
00798 image->rows);
00799 if (proceed == MagickFalse)
00800 status=MagickFalse;
00801 }
00802 }
00803 image_view=DestroyCacheView(image_view);
00804 return(status);
00805 }
00806
00807
00808
00809
00810
00811
00812
00813
00814
00815
00816
00817
00818
00819
00820
00821
00822
00823
00824
00825
00826
00827
00828
00829 MagickExport ThresholdMap *DestroyThresholdMap(ThresholdMap *map)
00830 {
00831 assert(map != (ThresholdMap *) NULL);
00832 if (map->map_id != (char *) NULL)
00833 map->map_id=DestroyString(map->map_id);
00834 if (map->description != (char *) NULL)
00835 map->description=DestroyString(map->description);
00836 if (map->levels != (long *) NULL)
00837 map->levels=(long *) RelinquishMagickMemory(map->levels);
00838 map=(ThresholdMap *) RelinquishMagickMemory(map);
00839 return(map);
00840 }
00841
00842
00843
00844
00845
00846
00847
00848
00849
00850
00851
00852
00853
00854
00855
00856
00857
00858
00859
00860
00861
00862
00863
00864
00865
00866
00867
00868
00869
00870
00871
00872 MagickExport ThresholdMap *GetThresholdMapFile(const char *xml,
00873 const char *filename,const char *map_id,ExceptionInfo *exception)
00874 {
00875 const char
00876 *attr,
00877 *content;
00878
00879 double
00880 value;
00881
00882 ThresholdMap
00883 *map;
00884
00885 XMLTreeInfo
00886 *description,
00887 *levels,
00888 *threshold,
00889 *thresholds;
00890
00891 map = (ThresholdMap *)NULL;
00892 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
00893 "Loading threshold map file \"%s\" ...",filename);
00894 thresholds=NewXMLTree(xml,exception);
00895 if ( thresholds == (XMLTreeInfo *)NULL )
00896 return(map);
00897
00898 for( threshold = GetXMLTreeChild(thresholds,"threshold");
00899 threshold != (XMLTreeInfo *)NULL;
00900 threshold = GetNextXMLTreeTag(threshold) ) {
00901 attr = GetXMLTreeAttribute(threshold, "map");
00902 if ( (attr != (char *)NULL) && (LocaleCompare(map_id,attr) == 0) )
00903 break;
00904 attr = GetXMLTreeAttribute(threshold, "alias");
00905 if ( (attr != (char *)NULL) && (LocaleCompare(map_id,attr) == 0) )
00906 break;
00907 }
00908 if ( threshold == (XMLTreeInfo *)NULL ) {
00909 return(map);
00910 }
00911 description = GetXMLTreeChild(threshold,"description");
00912 if ( description == (XMLTreeInfo *)NULL ) {
00913 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00914 "XmlMissingElement", "<description>, map \"%s\"", map_id);
00915 thresholds = DestroyXMLTree(thresholds);
00916 return(map);
00917 }
00918 levels = GetXMLTreeChild(threshold,"levels");
00919 if ( levels == (XMLTreeInfo *)NULL ) {
00920 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00921 "XmlMissingElement", "<levels>, map \"%s\"", map_id);
00922 thresholds = DestroyXMLTree(thresholds);
00923 return(map);
00924 }
00925
00926
00927 map = (ThresholdMap *)AcquireMagickMemory(sizeof(ThresholdMap));
00928 if ( map == (ThresholdMap *)NULL )
00929 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
00930 map->map_id = (char *)NULL;
00931 map->description = (char *)NULL;
00932 map->levels = (long *) NULL;
00933
00934
00935 attr = GetXMLTreeAttribute(threshold, "map");
00936 if ( attr != (char *)NULL )
00937 map->map_id = ConstantString(attr);
00938
00939 content = GetXMLTreeContent(description);
00940 if ( content != (char *)NULL )
00941 map->description = ConstantString(content);
00942
00943 attr = GetXMLTreeAttribute(levels, "width");
00944 if ( attr == (char *)NULL ) {
00945 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00946 "XmlMissingAttribute", "<levels width>, map \"%s\"", map_id);
00947 thresholds = DestroyXMLTree(thresholds);
00948 map = DestroyThresholdMap(map);
00949 return(map);
00950 }
00951 map->width = (unsigned long) atoi(attr);
00952 if ( map->width == 0 ) {
00953 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00954 "XmlInvalidAttribute", "<levels width>, map \"%s\"", map_id);
00955 thresholds = DestroyXMLTree(thresholds);
00956 map = DestroyThresholdMap(map);
00957 return(map);
00958 }
00959
00960 attr = GetXMLTreeAttribute(levels, "height");
00961 if ( attr == (char *)NULL ) {
00962 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00963 "XmlMissingAttribute", "<levels height>, map \"%s\"", map_id);
00964 thresholds = DestroyXMLTree(thresholds);
00965 map = DestroyThresholdMap(map);
00966 return(map);
00967 }
00968 map->height = (unsigned long) atoi(attr);
00969 if ( map->height == 0 ) {
00970 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00971 "XmlInvalidAttribute", "<levels height>, map \"%s\"", map_id);
00972 thresholds = DestroyXMLTree(thresholds);
00973 map = DestroyThresholdMap(map);
00974 return(map);
00975 }
00976
00977 attr = GetXMLTreeAttribute(levels, "divisor");
00978 if ( attr == (char *)NULL ) {
00979 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00980 "XmlMissingAttribute", "<levels divisor>, map \"%s\"", map_id);
00981 thresholds = DestroyXMLTree(thresholds);
00982 map = DestroyThresholdMap(map);
00983 return(map);
00984 }
00985 map->divisor = atoi(attr);
00986 if ( map->divisor < 2 ) {
00987 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00988 "XmlInvalidAttribute", "<levels divisor>, map \"%s\"", map_id);
00989 thresholds = DestroyXMLTree(thresholds);
00990 map = DestroyThresholdMap(map);
00991 return(map);
00992 }
00993
00994
00995 content = GetXMLTreeContent(levels);
00996 if ( content == (char *)NULL ) {
00997 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
00998 "XmlMissingContent", "<levels>, map \"%s\"", map_id);
00999 thresholds = DestroyXMLTree(thresholds);
01000 map = DestroyThresholdMap(map);
01001 return(map);
01002 }
01003 map->levels=(long *) AcquireQuantumMemory((size_t) map->width,map->height*
01004 sizeof(*map->levels));
01005 if ( map->levels == (long *)NULL )
01006 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
01007 {
01008 int i;
01009 char *p;
01010 for( i=0; i< (long) (map->width*map->height); i++) {
01011 map->levels[i] = (int)strtol(content, &p, 10);
01012 if ( p == content ) {
01013 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
01014 "XmlInvalidContent", "<level> too few values, map \"%s\"", map_id);
01015 thresholds = DestroyXMLTree(thresholds);
01016 map = DestroyThresholdMap(map);
01017 return(map);
01018 }
01019 if ( map->levels[i] < 0 || map->levels[i] > map->divisor ) {
01020 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
01021 "XmlInvalidContent", "<level> %ld out of range, map \"%s\"",
01022 map->levels[i], map_id);
01023 thresholds = DestroyXMLTree(thresholds);
01024 map = DestroyThresholdMap(map);
01025 return(map);
01026 }
01027 content = p;
01028 }
01029 value=(double) strtol(content,&p,10);
01030 if (p != content)
01031 {
01032 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
01033 "XmlInvalidContent", "<level> too many values, map \"%s\"", map_id);
01034 thresholds=DestroyXMLTree(thresholds);
01035 map=DestroyThresholdMap(map);
01036 return(map);
01037 }
01038 }
01039
01040 thresholds = DestroyXMLTree(thresholds);
01041 return(map);
01042 }
01043
01044
01045
01046
01047
01048
01049
01050
01051
01052
01053
01054
01055
01056
01057
01058
01059
01060
01061
01062
01063
01064
01065
01066
01067
01068
01069
01070 MagickExport ThresholdMap *GetThresholdMap(const char *map_id,
01071 ExceptionInfo *exception)
01072 {
01073 const StringInfo
01074 *option;
01075
01076 LinkedListInfo
01077 *options;
01078
01079 ThresholdMap
01080 *map;
01081
01082 map=(ThresholdMap *)NULL;
01083 options=GetConfigureOptions(ThresholdsFilename,exception);
01084 while (( option=(const StringInfo *) GetNextValueInLinkedList(options) )
01085 != (const StringInfo *) NULL && map == (ThresholdMap *)NULL )
01086 map=GetThresholdMapFile((const char *) GetStringInfoDatum(option),
01087 GetStringInfoPath(option),map_id,exception);
01088 options=DestroyConfigureOptions(options);
01089 return(map);
01090 }
01091
01092
01093
01094
01095
01096
01097
01098
01099
01100
01101
01102
01103
01104
01105
01106
01107
01108
01109
01110
01111
01112
01113
01114
01115
01116
01117
01118
01119
01120
01121
01122 MagickBooleanType ListThresholdMapFile(FILE *file,const char *xml,
01123 const char *filename,ExceptionInfo *exception)
01124 {
01125 XMLTreeInfo *thresholds,*threshold,*description;
01126 const char *map,*alias,*content;
01127
01128 assert( xml != (char *)NULL );
01129 assert( file != (FILE *)NULL );
01130
01131 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
01132 "Loading threshold map file \"%s\" ...",filename);
01133 thresholds=NewXMLTree(xml,exception);
01134 if ( thresholds == (XMLTreeInfo *)NULL )
01135 return(MagickFalse);
01136
01137 (void) fprintf(file,"%-16s %-12s %s\n", "Map", "Alias", "Description");
01138 (void) fprintf(file,"----------------------------------------------------\n");
01139
01140 for( threshold = GetXMLTreeChild(thresholds,"threshold");
01141 threshold != (XMLTreeInfo *)NULL;
01142 threshold = GetNextXMLTreeTag(threshold) )
01143 {
01144 map = GetXMLTreeAttribute(threshold, "map");
01145 if (map == (char *) NULL) {
01146 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
01147 "XmlMissingAttribute", "<map>");
01148 thresholds=DestroyXMLTree(thresholds);
01149 return(MagickFalse);
01150 }
01151 alias = GetXMLTreeAttribute(threshold, "alias");
01152
01153 description=GetXMLTreeChild(threshold,"description");
01154 if ( description == (XMLTreeInfo *)NULL ) {
01155 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
01156 "XmlMissingElement", "<description>, map \"%s\"", map);
01157 thresholds=DestroyXMLTree(thresholds);
01158 return(MagickFalse);
01159 }
01160 content=GetXMLTreeContent(description);
01161 if ( content == (char *)NULL ) {
01162 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
01163 "XmlMissingContent", "<description>, map \"%s\"", map);
01164 thresholds=DestroyXMLTree(thresholds);
01165 return(MagickFalse);
01166 }
01167 (void) fprintf(file,"%-16s %-12s %s\n",map,alias ? alias : "", content);
01168 }
01169 thresholds=DestroyXMLTree(thresholds);
01170 return(MagickTrue);
01171 }
01172
01173
01174
01175
01176
01177
01178
01179
01180
01181
01182
01183
01184
01185
01186
01187
01188
01189
01190
01191
01192
01193
01194
01195
01196
01197
01198 MagickExport MagickBooleanType ListThresholdMaps(FILE *file,
01199 ExceptionInfo *exception)
01200 {
01201 const StringInfo
01202 *option;
01203
01204 LinkedListInfo
01205 *options;
01206
01207 MagickStatusType
01208 status;
01209
01210 status=MagickFalse;
01211 if ( file == (FILE *)NULL )
01212 file = stdout;
01213 options=GetConfigureOptions(ThresholdsFilename,exception);
01214
01215 (void) fprintf(file, "\n Threshold Maps for Ordered Dither Operations\n");
01216
01217 while ( ( option=(const StringInfo *) GetNextValueInLinkedList(options) )
01218 != (const StringInfo *) NULL)
01219 {
01220 (void) fprintf(file,"\nPATH: %s\n\n",GetStringInfoPath(option));
01221 status|=ListThresholdMapFile(file,(const char *) GetStringInfoDatum(option),
01222 GetStringInfoPath(option),exception);
01223 }
01224 options=DestroyConfigureOptions(options);
01225 return(status != 0 ? MagickTrue : MagickFalse);
01226 }
01227
01228
01229
01230
01231
01232
01233
01234
01235
01236
01237
01238
01239
01240
01241
01242
01243
01244
01245
01246
01247
01248
01249
01250
01251
01252
01253
01254
01255
01256
01257
01258
01259
01260
01261
01262 MagickExport MagickBooleanType OrderedDitherImage(Image *image)
01263 {
01264 MagickBooleanType
01265 status;
01266
01267 status=OrderedDitherImageChannel(image,DefaultChannels,&image->exception);
01268 return(status);
01269 }
01270
01271 MagickExport MagickBooleanType OrderedDitherImageChannel(Image *image,
01272 const ChannelType channel,ExceptionInfo *exception)
01273 {
01274 MagickBooleanType
01275 status;
01276
01277
01278
01279
01280 status=OrderedPosterizeImageChannel(image,channel,"o8x8",exception);
01281 return(status);
01282 }
01283
01284
01285
01286
01287
01288
01289
01290
01291
01292
01293
01294
01295
01296
01297
01298
01299
01300
01301
01302
01303
01304
01305
01306
01307
01308
01309
01310
01311
01312
01313
01314
01315
01316
01317
01318
01319
01320
01321
01322
01323
01324
01325
01326
01327
01328
01329
01330
01331
01332
01333
01334
01335
01336 MagickExport MagickBooleanType OrderedPosterizeImage(Image *image,
01337 const char *threshold_map,ExceptionInfo *exception)
01338 {
01339 MagickBooleanType
01340 status;
01341
01342 status=OrderedPosterizeImageChannel(image,DefaultChannels,threshold_map,
01343 exception);
01344 return(status);
01345 }
01346
01347 MagickExport MagickBooleanType OrderedPosterizeImageChannel(Image *image,
01348 const ChannelType channel,const char *threshold_map,ExceptionInfo *exception)
01349 {
01350 #define DitherImageTag "Dither/Image"
01351
01352 long
01353 progress,
01354 y;
01355
01356 LongPixelPacket
01357 levels;
01358
01359 MagickBooleanType
01360 status;
01361
01362 ThresholdMap
01363 *map;
01364
01365 CacheView
01366 *image_view;
01367
01368 assert(image != (Image *) NULL);
01369 assert(image->signature == MagickSignature);
01370 if (image->debug != MagickFalse)
01371 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01372 assert(exception != (ExceptionInfo *) NULL);
01373 assert(exception->signature == MagickSignature);
01374 if (threshold_map == (const char *) NULL)
01375 return(MagickTrue);
01376 {
01377 char
01378 token[MaxTextExtent];
01379
01380 register const char
01381 *p;
01382
01383 p=(char *)threshold_map;
01384 while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
01385 (*p != '\0'))
01386 p++;
01387 threshold_map=p;
01388 while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
01389 (*p != '\0')) {
01390 if ((p-threshold_map) >= MaxTextExtent)
01391 break;
01392 token[p-threshold_map] = *p;
01393 p++;
01394 }
01395 token[p-threshold_map] = '\0';
01396 map = GetThresholdMap(token, exception);
01397 if ( map == (ThresholdMap *)NULL ) {
01398 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
01399 "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
01400 return(MagickFalse);
01401 }
01402 }
01403
01404
01405
01406 #if 1
01407 {
01408 char *p;
01409
01410 p = strchr((char *) threshold_map,',');
01411 if ( p != (char *)NULL && isdigit((int) ((unsigned char) *(++p))) )
01412 levels.index = (unsigned long) strtol(p, &p, 10);
01413 else
01414 levels.index = 2;
01415
01416 levels.red = ((channel & RedChannel ) != 0) ? levels.index : 0;
01417 levels.green = ((channel & GreenChannel) != 0) ? levels.index : 0;
01418 levels.blue = ((channel & BlueChannel) != 0) ? levels.index : 0;
01419 levels.opacity = ((channel & OpacityChannel) != 0) ? levels.index : 0;
01420 levels.index = ((channel & IndexChannel) != 0
01421 && (image->colorspace == CMYKColorspace)) ? levels.index : 0;
01422
01423
01424 if ( p != (char *) NULL && *p == ',' ) {
01425 p=strchr((char *) threshold_map,',');
01426 p++;
01427 if ((channel & RedChannel) != 0)
01428 levels.red = (unsigned long) strtol(p, &p, 10), (void)(*p == ',' && p++);
01429 if ((channel & GreenChannel) != 0)
01430 levels.green = (unsigned long) strtol(p, &p, 10), (void)(*p == ',' && p++);
01431 if ((channel & BlueChannel) != 0)
01432 levels.blue = (unsigned long) strtol(p, &p, 10), (void)(*p == ',' && p++);
01433 if ((channel & IndexChannel) != 0 && image->colorspace == CMYKColorspace)
01434 levels.index=(unsigned long) strtol(p, &p, 10), (void)(*p == ',' && p++);
01435 if ((channel & OpacityChannel) != 0)
01436 levels.opacity = (unsigned long) strtol(p, &p, 10), (void)(*p == ',' && p++);
01437 }
01438 }
01439 #else
01440
01441
01442
01443
01444
01445
01446
01447
01448
01449
01450 #endif
01451
01452 #if 0
01453 printf("DEBUG levels r=%ld g=%ld b=%ld a=%ld i=%ld\n",
01454 levels.red, levels.green, levels.blue, levels.opacity, levels.index);
01455 #endif
01456
01457 {
01458 int
01459 d;
01460
01461
01462 d = map->divisor-1;
01463
01464
01465 levels.red = levels.red ? levels.red-1 : 0;
01466 levels.green = levels.green ? levels.green-1 : 0;
01467 levels.blue = levels.blue ? levels.blue-1 : 0;
01468 levels.opacity = levels.opacity ? levels.opacity-1 : 0;
01469 levels.index = levels.index ? levels.index-1 : 0;
01470
01471 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
01472 {
01473 InheritException(exception,&image->exception);
01474 return(MagickFalse);
01475 }
01476 status=MagickTrue;
01477 progress=0;
01478 image_view=AcquireCacheView(image);
01479 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01480 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
01481 #endif
01482 for (y=0; y < (long) image->rows; y++)
01483 {
01484 register IndexPacket
01485 *__restrict indexes;
01486
01487 register long
01488 x;
01489
01490 register PixelPacket
01491 *__restrict q;
01492
01493 if (status == MagickFalse)
01494 continue;
01495 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
01496 if (q == (PixelPacket *) NULL)
01497 {
01498 status=MagickFalse;
01499 continue;
01500 }
01501 indexes=GetCacheViewAuthenticIndexQueue(image_view);
01502 for (x=0; x < (long) image->columns; x++)
01503 {
01504 register int
01505 threshold,
01506 t,
01507 l;
01508
01509
01510
01511
01512
01513 threshold = map->levels[(x%map->width) +map->width*(y%map->height)];
01514
01515
01516
01517
01518
01519
01520
01521
01522
01523
01524
01525
01526 if (levels.red) {
01527 t = (int) (QuantumScale*q->red*(levels.red*d+1));
01528 l = t/d; t = t-l*d;
01529 q->red=(Quantum) ((l+(t >= threshold))*QuantumRange/levels.red);
01530 }
01531 if (levels.green) {
01532 t = (int) (QuantumScale*q->green*(levels.green*d+1));
01533 l = t/d; t = t-l*d;
01534 q->green=(Quantum) ((l+(t >= threshold))*QuantumRange/levels.green);
01535 }
01536 if (levels.blue) {
01537 t = (int) (QuantumScale*q->blue*(levels.blue*d+1));
01538 l = t/d; t = t-l*d;
01539 q->blue=(Quantum) ((l+(t >= threshold))*QuantumRange/levels.blue);
01540 }
01541 if (levels.opacity) {
01542 t = (int) ((1.0-QuantumScale*q->opacity)*(levels.opacity*d+1));
01543 l = t/d; t = t-l*d;
01544 q->opacity=(Quantum) ((1.0-l-(t >= threshold))*QuantumRange/
01545 levels.opacity);
01546 }
01547 if (levels.index) {
01548 t = (int) (QuantumScale*indexes[x]*(levels.index*d+1));
01549 l = t/d; t = t-l*d;
01550 indexes[x]=(IndexPacket) ((l+(t>=threshold))*QuantumRange/
01551 levels.index);
01552 }
01553 q++;
01554 }
01555 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
01556 status=MagickFalse;
01557 if (image->progress_monitor != (MagickProgressMonitor) NULL)
01558 {
01559 MagickBooleanType
01560 proceed;
01561
01562 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01563 #pragma omp critical (MagickCore_OrderedPosterizeImageChannel)
01564 #endif
01565 proceed=SetImageProgress(image,DitherImageTag,progress++,image->rows);
01566 if (proceed == MagickFalse)
01567 status=MagickFalse;
01568 }
01569 }
01570 image_view=DestroyCacheView(image_view);
01571 }
01572 map=DestroyThresholdMap(map);
01573 return(MagickTrue);
01574 }
01575
01576
01577
01578
01579
01580
01581
01582
01583
01584
01585
01586
01587
01588
01589
01590
01591
01592
01593
01594
01595
01596
01597
01598
01599
01600
01601
01602
01603
01604
01605
01606
01607
01608
01609
01610
01611
01612
01613 MagickExport MagickBooleanType RandomThresholdImage(Image *image,
01614 const char *thresholds,ExceptionInfo *exception)
01615 {
01616 MagickBooleanType
01617 status;
01618
01619 status=RandomThresholdImageChannel(image,DefaultChannels,thresholds,
01620 exception);
01621 return(status);
01622 }
01623
01624 MagickExport MagickBooleanType RandomThresholdImageChannel(Image *image,
01625 const ChannelType channel,const char *thresholds,ExceptionInfo *exception)
01626 {
01627 #define ThresholdImageTag "Threshold/Image"
01628
01629 GeometryInfo
01630 geometry_info;
01631
01632 MagickStatusType
01633 flags;
01634
01635 long
01636 progress,
01637 y;
01638
01639 MagickBooleanType
01640 status;
01641
01642 MagickPixelPacket
01643 threshold;
01644
01645 MagickRealType
01646 min_threshold,
01647 max_threshold;
01648
01649 RandomInfo
01650 **random_info;
01651
01652 CacheView
01653 *image_view;
01654
01655 assert(image != (Image *) NULL);
01656 assert(image->signature == MagickSignature);
01657 if (image->debug != MagickFalse)
01658 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01659 assert(exception != (ExceptionInfo *) NULL);
01660 assert(exception->signature == MagickSignature);
01661 if (thresholds == (const char *) NULL)
01662 return(MagickTrue);
01663 GetMagickPixelPacket(image,&threshold);
01664 min_threshold=0.0;
01665 max_threshold=(MagickRealType) QuantumRange;
01666 flags=ParseGeometry(thresholds,&geometry_info);
01667 min_threshold=geometry_info.rho;
01668 max_threshold=geometry_info.sigma;
01669 if ((flags & SigmaValue) == 0)
01670 max_threshold=min_threshold;
01671 if (strchr(thresholds,'%') != (char *) NULL)
01672 {
01673 max_threshold*=(MagickRealType) (0.01*QuantumRange);
01674 min_threshold*=(MagickRealType) (0.01*QuantumRange);
01675 }
01676 else
01677 if (((max_threshold == min_threshold) || (max_threshold == 1)) &&
01678 (min_threshold <= 8))
01679 {
01680
01681
01682
01683 status=OrderedPosterizeImageChannel(image,channel,thresholds,exception);
01684 return(status);
01685 }
01686
01687
01688
01689 status=MagickTrue;
01690 progress=0;
01691 if (channel == AllChannels)
01692 {
01693 if (AcquireImageColormap(image,2) == MagickFalse)
01694 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
01695 image->filename);
01696 random_info=AcquireRandomInfoThreadSet();
01697 image_view=AcquireCacheView(image);
01698 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01699 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
01700 #endif
01701 for (y=0; y < (long) image->rows; y++)
01702 {
01703 MagickBooleanType
01704 sync;
01705
01706 register IndexPacket
01707 *__restrict indexes;
01708
01709 register long
01710 id,
01711 x;
01712
01713 register PixelPacket
01714 *__restrict q;
01715
01716 if (status == MagickFalse)
01717 continue;
01718 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
01719 exception);
01720 if (q == (PixelPacket *) NULL)
01721 {
01722 status=MagickFalse;
01723 continue;
01724 }
01725 indexes=GetCacheViewAuthenticIndexQueue(image_view);
01726 id=GetOpenMPThreadId();
01727 for (x=0; x < (long) image->columns; x++)
01728 {
01729 IndexPacket
01730 index;
01731
01732 MagickRealType
01733 intensity;
01734
01735 intensity=(MagickRealType) PixelIntensityToQuantum(q);
01736 if (intensity < min_threshold)
01737 threshold.index=min_threshold;
01738 else if (intensity > max_threshold)
01739 threshold.index=max_threshold;
01740 else
01741 threshold.index=(MagickRealType)(QuantumRange*
01742 GetPseudoRandomValue(random_info[id]));
01743 index=(IndexPacket) (intensity <= threshold.index ? 0 : 1);
01744 indexes[x]=index;
01745 *q++=image->colormap[(long) index];
01746 }
01747 sync=SyncCacheViewAuthenticPixels(image_view,exception);
01748 if (sync == MagickFalse)
01749 status=MagickFalse;
01750 if (image->progress_monitor != (MagickProgressMonitor) NULL)
01751 {
01752 MagickBooleanType
01753 proceed;
01754
01755 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01756 #pragma omp critical (MagickCore_RandomThresholdImageChannel)
01757 #endif
01758 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
01759 image->rows);
01760 if (proceed == MagickFalse)
01761 status=MagickFalse;
01762 }
01763 }
01764 image_view=DestroyCacheView(image_view);
01765 random_info=DestroyRandomInfoThreadSet(random_info);
01766 return(status);
01767 }
01768 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
01769 {
01770 InheritException(exception,&image->exception);
01771 return(MagickFalse);
01772 }
01773 random_info=AcquireRandomInfoThreadSet();
01774 image_view=AcquireCacheView(image);
01775 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01776 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
01777 #endif
01778 for (y=0; y < (long) image->rows; y++)
01779 {
01780 register IndexPacket
01781 *__restrict indexes;
01782
01783 register long
01784 id,
01785 x;
01786
01787 register PixelPacket
01788 *__restrict q;
01789
01790 if (status == MagickFalse)
01791 continue;
01792 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
01793 if (q == (PixelPacket *) NULL)
01794 {
01795 status=MagickFalse;
01796 continue;
01797 }
01798 indexes=GetCacheViewAuthenticIndexQueue(image_view);
01799 id=GetOpenMPThreadId();
01800 for (x=0; x < (long) image->columns; x++)
01801 {
01802 if ((channel & RedChannel) != 0)
01803 {
01804 if ((MagickRealType) q->red < min_threshold)
01805 threshold.red=min_threshold;
01806 else
01807 if ((MagickRealType) q->red > max_threshold)
01808 threshold.red=max_threshold;
01809 else
01810 threshold.red=(MagickRealType) (QuantumRange*
01811 GetPseudoRandomValue(random_info[id]));
01812 }
01813 if ((channel & GreenChannel) != 0)
01814 {
01815 if ((MagickRealType) q->green < min_threshold)
01816 threshold.green=min_threshold;
01817 else
01818 if ((MagickRealType) q->green > max_threshold)
01819 threshold.green=max_threshold;
01820 else
01821 threshold.green=(MagickRealType) (QuantumRange*
01822 GetPseudoRandomValue(random_info[id]));
01823 }
01824 if ((channel & BlueChannel) != 0)
01825 {
01826 if ((MagickRealType) q->blue < min_threshold)
01827 threshold.blue=min_threshold;
01828 else
01829 if ((MagickRealType) q->blue > max_threshold)
01830 threshold.blue=max_threshold;
01831 else
01832 threshold.blue=(MagickRealType) (QuantumRange*
01833 GetPseudoRandomValue(random_info[id]));
01834 }
01835 if ((channel & OpacityChannel) != 0)
01836 {
01837 if ((MagickRealType) q->opacity < min_threshold)
01838 threshold.opacity=min_threshold;
01839 else
01840 if ((MagickRealType) q->opacity > max_threshold)
01841 threshold.opacity=max_threshold;
01842 else
01843 threshold.opacity=(MagickRealType) (QuantumRange*
01844 GetPseudoRandomValue(random_info[id]));
01845 }
01846 if (((channel & IndexChannel) != 0) &&
01847 (image->colorspace == CMYKColorspace))
01848 {
01849 if ((MagickRealType) indexes[x] < min_threshold)
01850 threshold.index=min_threshold;
01851 else
01852 if ((MagickRealType) indexes[x] > max_threshold)
01853 threshold.index=max_threshold;
01854 else
01855 threshold.index=(MagickRealType) (QuantumRange*
01856 GetPseudoRandomValue(random_info[id]));
01857 }
01858 if ((channel & RedChannel) != 0)
01859 q->red=(Quantum) ((MagickRealType) q->red <= threshold.red ? 0 :
01860 QuantumRange);
01861 if ((channel & GreenChannel) != 0)
01862 q->green=(Quantum) ((MagickRealType) q->green <= threshold.green ? 0 :
01863 QuantumRange);
01864 if ((channel & BlueChannel) != 0)
01865 q->blue=(Quantum) ((MagickRealType) q->blue <= threshold.blue ? 0 :
01866 QuantumRange);
01867 if ((channel & OpacityChannel) != 0)
01868 q->opacity=(Quantum) ((MagickRealType) q->opacity <= threshold.opacity ?
01869 0 : QuantumRange);
01870 if (((channel & IndexChannel) != 0) &&
01871 (image->colorspace == CMYKColorspace))
01872 indexes[x]=(IndexPacket) ((MagickRealType) indexes[x] <=
01873 threshold.index ? 0 : QuantumRange);
01874 q++;
01875 }
01876 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
01877 status=MagickFalse;
01878 if (image->progress_monitor != (MagickProgressMonitor) NULL)
01879 {
01880 MagickBooleanType
01881 proceed;
01882
01883 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01884 #pragma omp critical (MagickCore_RandomThresholdImageChannel)
01885 #endif
01886 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
01887 image->rows);
01888 if (proceed == MagickFalse)
01889 status=MagickFalse;
01890 }
01891 }
01892 image_view=DestroyCacheView(image_view);
01893 random_info=DestroyRandomInfoThreadSet(random_info);
01894 return(status);
01895 }
01896
01897
01898
01899
01900
01901
01902
01903
01904
01905
01906
01907
01908
01909
01910
01911
01912
01913
01914
01915
01916
01917
01918
01919
01920
01921
01922
01923
01924
01925
01926
01927
01928
01929
01930 MagickExport MagickBooleanType WhiteThresholdImage(Image *image,
01931 const char *threshold)
01932 {
01933 MagickBooleanType
01934 status;
01935
01936 status=WhiteThresholdImageChannel(image,DefaultChannels,threshold,
01937 &image->exception);
01938 return(status);
01939 }
01940
01941 MagickExport MagickBooleanType WhiteThresholdImageChannel(Image *image,
01942 const ChannelType channel,const char *thresholds,ExceptionInfo *exception)
01943 {
01944 #define ThresholdImageTag "Threshold/Image"
01945
01946 GeometryInfo
01947 geometry_info;
01948
01949 long
01950 progress,
01951 y;
01952
01953 MagickBooleanType
01954 status;
01955
01956 MagickPixelPacket
01957 threshold;
01958
01959 MagickStatusType
01960 flags;
01961
01962 CacheView
01963 *image_view;
01964
01965 assert(image != (Image *) NULL);
01966 assert(image->signature == MagickSignature);
01967 if (image->debug != MagickFalse)
01968 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01969 if (thresholds == (const char *) NULL)
01970 return(MagickTrue);
01971 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
01972 return(MagickFalse);
01973 flags=ParseGeometry(thresholds,&geometry_info);
01974 GetMagickPixelPacket(image,&threshold);
01975 threshold.red=geometry_info.rho;
01976 threshold.green=geometry_info.sigma;
01977 if ((flags & SigmaValue) == 0)
01978 threshold.green=threshold.red;
01979 threshold.blue=geometry_info.xi;
01980 if ((flags & XiValue) == 0)
01981 threshold.blue=threshold.red;
01982 threshold.opacity=geometry_info.psi;
01983 if ((flags & PsiValue) == 0)
01984 threshold.opacity=threshold.red;
01985 threshold.index=geometry_info.chi;
01986 if ((flags & ChiValue) == 0)
01987 threshold.index=threshold.red;
01988 if ((flags & PercentValue) != 0)
01989 {
01990 threshold.red*=(QuantumRange/100.0);
01991 threshold.green*=(QuantumRange/100.0);
01992 threshold.blue*=(QuantumRange/100.0);
01993 threshold.opacity*=(QuantumRange/100.0);
01994 threshold.index*=(QuantumRange/100.0);
01995 }
01996
01997
01998
01999 status=MagickTrue;
02000 progress=0;
02001 image_view=AcquireCacheView(image);
02002 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02003 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
02004 #endif
02005 for (y=0; y < (long) image->rows; y++)
02006 {
02007 register IndexPacket
02008 *__restrict indexes;
02009
02010 register long
02011 x;
02012
02013 register PixelPacket
02014 *__restrict q;
02015
02016 if (status == MagickFalse)
02017 continue;
02018 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
02019 if (q == (PixelPacket *) NULL)
02020 {
02021 status=MagickFalse;
02022 continue;
02023 }
02024 indexes=GetCacheViewAuthenticIndexQueue(image_view);
02025 for (x=0; x < (long) image->columns; x++)
02026 {
02027 if (((channel & RedChannel) != 0) &&
02028 ((MagickRealType) q->red > threshold.red))
02029 q->red=(Quantum) QuantumRange;
02030 if (((channel & GreenChannel) != 0) &&
02031 ((MagickRealType) q->green > threshold.green))
02032 q->green=(Quantum) QuantumRange;
02033 if (((channel & BlueChannel) != 0) &&
02034 ((MagickRealType) q->blue > threshold.blue))
02035 q->blue=(Quantum) QuantumRange;
02036 if (((channel & OpacityChannel) != 0) &&
02037 ((MagickRealType) q->opacity > threshold.opacity))
02038 q->opacity=(Quantum) QuantumRange;
02039 if (((channel & IndexChannel) != 0) &&
02040 (image->colorspace == CMYKColorspace) &&
02041 ((MagickRealType) indexes[x] > threshold.index))
02042 indexes[x]=(Quantum) QuantumRange;
02043 q++;
02044 }
02045 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
02046 status=MagickFalse;
02047 if (image->progress_monitor != (MagickProgressMonitor) NULL)
02048 {
02049 MagickBooleanType
02050 proceed;
02051
02052 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02053 #pragma omp critical (MagickCore_WhiteThresholdImageChannel)
02054 #endif
02055 proceed=SetImageProgress(image,ThresholdImageTag,progress++,
02056 image->rows);
02057 if (proceed == MagickFalse)
02058 status=MagickFalse;
02059 }
02060 }
02061 image_view=DestroyCacheView(image_view);
02062 return(status);
02063 }