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/cache.h"
00045 #include "magick/cache-view.h"
00046 #include "magick/color.h"
00047 #include "magick/color-private.h"
00048 #include "magick/colorspace.h"
00049 #include "magick/enhance.h"
00050 #include "magick/exception.h"
00051 #include "magick/exception-private.h"
00052 #include "magick/gem.h"
00053 #include "magick/geometry.h"
00054 #include "magick/image.h"
00055 #include "magick/image-private.h"
00056 #include "magick/memory_.h"
00057 #include "magick/monitor.h"
00058 #include "magick/monitor-private.h"
00059 #include "magick/quantum.h"
00060 #include "magick/quantum-private.h"
00061 #include "magick/resample.h"
00062 #include "magick/resample-private.h"
00063 #include "magick/string_.h"
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094 MagickExport MagickBooleanType ClutImage(Image *image,const Image *clut_image)
00095 {
00096 return(ClutImageChannel(image,DefaultChannels,clut_image));
00097 }
00098
00099 MagickExport MagickBooleanType ClutImageChannel(Image *image,
00100 const ChannelType channel,const Image *clut_image)
00101 {
00102 #define ClutImageTag "Clut/Image"
00103
00104 ExceptionInfo
00105 *exception;
00106
00107 long
00108 adjust,
00109 progress,
00110 y;
00111
00112 MagickBooleanType
00113 status;
00114
00115 MagickPixelPacket
00116 zero;
00117
00118 ResampleFilter
00119 **resample_filter;
00120
00121 ViewInfo
00122 **image_view;
00123
00124 assert(image != (Image *) NULL);
00125 assert(image->signature == MagickSignature);
00126 if (image->debug != MagickFalse)
00127 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00128 assert(clut_image != (Image *) NULL);
00129 assert(clut_image->signature == MagickSignature);
00130 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
00131 return(MagickFalse);
00132
00133
00134
00135 status=MagickTrue;
00136 progress=0;
00137 GetMagickPixelPacket(clut_image,&zero);
00138 adjust=clut_image->interpolate == IntegerInterpolatePixel ? 0 : 1;
00139 exception=(&image->exception);
00140 resample_filter=AcquireResampleFilterThreadSet(clut_image,MagickTrue,
00141 exception);
00142 image_view=AcquireCacheViewThreadSet(image);
00143 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00144 #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
00145 #endif
00146 for (y=0; y < (long) image->rows; y++)
00147 {
00148 MagickPixelPacket
00149 pixel;
00150
00151 register IndexPacket
00152 *indexes;
00153
00154 register long
00155 id,
00156 x;
00157
00158 register PixelPacket
00159 *q;
00160
00161 if (status == MagickFalse)
00162 continue;
00163 id=GetCacheViewThreadId();
00164 q=GetCacheViewAuthenticPixels(image_view[id],0,y,image->columns,1,
00165 exception);
00166 if (q == (PixelPacket *) NULL)
00167 {
00168 status=MagickFalse;
00169 continue;
00170 }
00171 indexes=GetCacheViewAuthenticIndexes(image_view[id]);
00172 pixel=zero;
00173 for (x=0; x < (long) image->columns; x++)
00174 {
00175 if ((channel & RedChannel) != 0)
00176 {
00177 (void) ResamplePixelColor(resample_filter[id],QuantumScale*q->red*
00178 (clut_image->columns-adjust),QuantumScale*q->red*
00179 (clut_image->rows-adjust),&pixel);
00180 q->red=RoundToQuantum(pixel.red);
00181 }
00182 if ((channel & GreenChannel) != 0)
00183 {
00184 (void) ResamplePixelColor(resample_filter[id],QuantumScale*q->green*
00185 (clut_image->columns-adjust),QuantumScale*q->green*
00186 (clut_image->rows-adjust),&pixel);
00187 q->green=RoundToQuantum(pixel.green);
00188 }
00189 if ((channel & BlueChannel) != 0)
00190 {
00191 (void) ResamplePixelColor(resample_filter[id],QuantumScale*q->blue*
00192 (clut_image->columns-adjust),QuantumScale*q->blue*
00193 (clut_image->rows-adjust),&pixel);
00194 q->blue=RoundToQuantum(pixel.blue);
00195 }
00196 if ((channel & OpacityChannel) != 0)
00197 {
00198 if (clut_image->matte == MagickFalse)
00199 {
00200
00201
00202
00203 (void) ResamplePixelColor(resample_filter[id],QuantumScale*
00204 (QuantumRange-q->opacity)*(clut_image->columns+adjust),
00205 QuantumScale*(QuantumRange-q->opacity)*(clut_image->rows+
00206 adjust),&pixel);
00207 q->opacity=(Quantum) (QuantumRange-MagickPixelIntensityToQuantum(
00208 &pixel));
00209 }
00210 else
00211 if (image->matte == MagickFalse)
00212 {
00213
00214
00215
00216 (void) ResamplePixelColor(resample_filter[id],QuantumScale*
00217 PixelIntensity(q)*(clut_image->columns-adjust),QuantumScale*
00218 PixelIntensity(q)*(clut_image->rows-adjust),&pixel);
00219 q->opacity=RoundToQuantum(pixel.opacity);
00220 }
00221 else
00222 {
00223
00224
00225
00226 (void) ResamplePixelColor(resample_filter[id],QuantumScale*
00227 q->opacity*(clut_image->columns-adjust),QuantumScale*
00228 q->opacity* (clut_image->rows-adjust),&pixel);
00229 q->opacity=RoundToQuantum(pixel.opacity);
00230 }
00231 }
00232 if (((channel & IndexChannel) != 0) &&
00233 (image->colorspace == CMYKColorspace))
00234 {
00235 (void) ResamplePixelColor(resample_filter[id],QuantumScale*indexes[x]*
00236 (clut_image->columns-adjust),QuantumScale*indexes[x]*
00237 (clut_image->rows-adjust),&pixel);
00238 indexes[x]=RoundToQuantum(pixel.index);
00239 }
00240 q++;
00241 }
00242 if (SyncCacheViewAuthenticPixels(image_view[id],exception) == MagickFalse)
00243 status=MagickFalse;
00244 if (image->progress_monitor != (MagickProgressMonitor) NULL)
00245 {
00246 MagickBooleanType
00247 proceed;
00248
00249 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00250 #pragma omp critical
00251 #endif
00252 proceed=SetImageProgress(image,ClutImageTag,progress++,image->rows);
00253 if (proceed == MagickFalse)
00254 status=MagickFalse;
00255 }
00256 }
00257 image_view=DestroyCacheViewThreadSet(image_view);
00258 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
00259
00260
00261
00262 if ((clut_image->matte == MagickTrue) && ((channel & OpacityChannel) != 0))
00263 (void) SetImageAlphaChannel(image,ActivateAlphaChannel);
00264 return(status);
00265 }
00266
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295 static void Contrast(const int sign,Quantum *red,Quantum *green,Quantum *blue)
00296 {
00297 double
00298 brightness,
00299 hue,
00300 saturation;
00301
00302
00303
00304
00305 assert(red != (Quantum *) NULL);
00306 assert(green != (Quantum *) NULL);
00307 assert(blue != (Quantum *) NULL);
00308 hue=0.0;
00309 saturation=0.0;
00310 brightness=0.0;
00311 ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness);
00312 brightness+=0.5*sign*(0.5*(sin(MagickPI*(brightness-0.5))+1.0)-brightness);
00313 if (brightness > 1.0)
00314 brightness=1.0;
00315 else
00316 if (brightness < 0.0)
00317 brightness=0.0;
00318 ConvertHSBToRGB(hue,saturation,brightness,red,green,blue);
00319 }
00320
00321 MagickExport MagickBooleanType ContrastImage(Image *image,
00322 const MagickBooleanType sharpen)
00323 {
00324 #define ContrastImageTag "Contrast/Image"
00325
00326 ExceptionInfo
00327 *exception;
00328
00329 int
00330 sign;
00331
00332 long
00333 progress,
00334 y;
00335
00336 MagickBooleanType
00337 status;
00338
00339 register long
00340 i;
00341
00342 ViewInfo
00343 **image_view;
00344
00345 assert(image != (Image *) NULL);
00346 assert(image->signature == MagickSignature);
00347 if (image->debug != MagickFalse)
00348 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00349 sign=sharpen != MagickFalse ? 1 : -1;
00350 if (image->storage_class == PseudoClass)
00351 {
00352
00353
00354
00355 for (i=0; i < (long) image->colors; i++)
00356 Contrast(sign,&image->colormap[i].red,&image->colormap[i].green,
00357 &image->colormap[i].blue);
00358 }
00359
00360
00361
00362 status=MagickTrue;
00363 progress=0;
00364 exception=(&image->exception);
00365 image_view=AcquireCacheViewThreadSet(image);
00366 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00367 #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
00368 #endif
00369 for (y=0; y < (long) image->rows; y++)
00370 {
00371 register long
00372 id,
00373 x;
00374
00375 register PixelPacket
00376 *q;
00377
00378 if (status == MagickFalse)
00379 continue;
00380 id=GetCacheViewThreadId();
00381 q=GetCacheViewAuthenticPixels(image_view[id],0,y,image->columns,1,
00382 exception);
00383 if (q == (PixelPacket *) NULL)
00384 {
00385 status=MagickFalse;
00386 continue;
00387 }
00388 for (x=0; x < (long) image->columns; x++)
00389 {
00390 Contrast(sign,&q->red,&q->green,&q->blue);
00391 q++;
00392 }
00393 if (SyncCacheViewAuthenticPixels(image_view[id],exception) == MagickFalse)
00394 status=MagickFalse;
00395 if (image->progress_monitor != (MagickProgressMonitor) NULL)
00396 {
00397 MagickBooleanType
00398 proceed;
00399
00400 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00401 #pragma omp critical
00402 #endif
00403 proceed=SetImageProgress(image,ContrastImageTag,progress++,image->rows);
00404 if (proceed == MagickFalse)
00405 status=MagickFalse;
00406 }
00407 }
00408 image_view=DestroyCacheViewThreadSet(image_view);
00409 return(status);
00410 }
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438
00439
00440
00441
00442
00443
00444
00445
00446
00447
00448
00449
00450
00451
00452
00453 MagickExport MagickBooleanType ContrastStretchImage(Image *image,
00454 const char *levels)
00455 {
00456 double
00457 black_point,
00458 white_point;
00459
00460 GeometryInfo
00461 geometry_info;
00462
00463 MagickBooleanType
00464 status;
00465
00466 MagickStatusType
00467 flags;
00468
00469
00470
00471
00472 if (levels == (char *) NULL)
00473 return(MagickFalse);
00474 flags=ParseGeometry(levels,&geometry_info);
00475 black_point=geometry_info.rho;
00476 white_point=(double) image->columns*image->rows;
00477 if ((flags & SigmaValue) != 0)
00478 white_point=geometry_info.sigma;
00479 if ((flags & PercentValue) != 0)
00480 {
00481 black_point*=(double) QuantumRange/100.0;
00482 white_point*=(double) QuantumRange/100.0;
00483 }
00484 if ((flags & SigmaValue) == 0)
00485 white_point=(double) image->columns*image->rows-black_point;
00486 status=ContrastStretchImageChannel(image,DefaultChannels,black_point,
00487 white_point);
00488 return(status);
00489 }
00490
00491 MagickExport MagickBooleanType ContrastStretchImageChannel(Image *image,
00492 const ChannelType channel,const double black_point,const double white_point)
00493 {
00494 #define MaxRange(color) ((MagickRealType) ScaleQuantumToMap((Quantum) (color)))
00495 #define ContrastStretchImageTag "ContrastStretch/Image"
00496
00497 double
00498 intensity;
00499
00500 ExceptionInfo
00501 *exception;
00502
00503 long
00504 progress,
00505 y;
00506
00507 MagickBooleanType
00508 status;
00509
00510 MagickPixelPacket
00511 black,
00512 *histogram,
00513 *stretch_map,
00514 white;
00515
00516 register long
00517 i;
00518
00519 ViewInfo
00520 **image_view;
00521
00522
00523
00524
00525 assert(image != (Image *) NULL);
00526 assert(image->signature == MagickSignature);
00527 if (image->debug != MagickFalse)
00528 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00529 histogram=(MagickPixelPacket *) AcquireQuantumMemory(MaxMap+1UL,
00530 sizeof(*histogram));
00531 stretch_map=(MagickPixelPacket *) AcquireQuantumMemory(MaxMap+1UL,
00532 sizeof(*stretch_map));
00533 if ((histogram == (MagickPixelPacket *) NULL) ||
00534 (stretch_map == (MagickPixelPacket *) NULL))
00535 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
00536 image->filename);
00537
00538
00539
00540 status=MagickTrue;
00541 exception=(&image->exception);
00542 (void) ResetMagickMemory(histogram,0,(MaxMap+1)*sizeof(*histogram));
00543 image_view=AcquireCacheViewThreadSet(image);
00544 for (y=0; y < (long) image->rows; y++)
00545 {
00546 IndexPacket
00547 *indexes;
00548
00549 register const PixelPacket
00550 *p;
00551
00552 register long
00553 id,
00554 x;
00555
00556 if (status == MagickFalse)
00557 continue;
00558 id=GetCacheViewThreadId();
00559 p=GetCacheViewVirtualPixels(image_view[id],0,y,image->columns,1,exception);
00560 if (p == (const PixelPacket *) NULL)
00561 {
00562 status=MagickFalse;
00563 continue;
00564 }
00565 indexes=GetCacheViewAuthenticIndexes(image_view[id]);
00566 if (channel == DefaultChannels)
00567 for (x=0; x < (long) image->columns; x++)
00568 {
00569 Quantum
00570 intensity;
00571
00572 intensity=PixelIntensityToQuantum(p+x);
00573 histogram[ScaleQuantumToMap(intensity)].red++;
00574 histogram[ScaleQuantumToMap(intensity)].green++;
00575 histogram[ScaleQuantumToMap(intensity)].blue++;
00576 histogram[ScaleQuantumToMap(intensity)].index++;
00577 }
00578 else
00579 for (x=0; x < (long) image->columns; x++)
00580 {
00581 if ((channel & RedChannel) != 0)
00582 histogram[ScaleQuantumToMap((p+x)->red)].red++;
00583 if ((channel & GreenChannel) != 0)
00584 histogram[ScaleQuantumToMap((p+x)->green)].green++;
00585 if ((channel & BlueChannel) != 0)
00586 histogram[ScaleQuantumToMap((p+x)->blue)].blue++;
00587 if ((channel & OpacityChannel) != 0)
00588 histogram[ScaleQuantumToMap((p+x)->opacity)].opacity++;
00589 if (((channel & IndexChannel) != 0) &&
00590 (image->colorspace == CMYKColorspace))
00591 histogram[ScaleQuantumToMap(indexes[x])].index++;
00592 }
00593 }
00594
00595
00596
00597 black.red=0.0;
00598 white.red=MaxRange(QuantumRange);
00599 if ((channel & RedChannel) != 0)
00600 {
00601 intensity=0.0;
00602 for (i=0; i <= (long) MaxMap; i++)
00603 {
00604 intensity+=histogram[i].red;
00605 if (intensity > black_point)
00606 break;
00607 }
00608 black.red=(MagickRealType) i;
00609 intensity=0.0;
00610 for (i=(long) MaxMap; i != 0; i--)
00611 {
00612 intensity+=histogram[i].red;
00613 if (intensity > ((double) image->columns*image->rows-white_point))
00614 break;
00615 }
00616 white.red=(MagickRealType) i;
00617 }
00618 black.green=0.0;
00619 white.green=MaxRange(QuantumRange);
00620 if ((channel & GreenChannel) != 0)
00621 {
00622 intensity=0.0;
00623 for (i=0; i <= (long) MaxMap; i++)
00624 {
00625 intensity+=histogram[i].green;
00626 if (intensity > black_point)
00627 break;
00628 }
00629 black.green=(MagickRealType) i;
00630 intensity=0.0;
00631 for (i=(long) MaxMap; i != 0; i--)
00632 {
00633 intensity+=histogram[i].green;
00634 if (intensity > ((double) image->columns*image->rows-white_point))
00635 break;
00636 }
00637 white.green=(MagickRealType) i;
00638 }
00639 black.blue=0.0;
00640 white.blue=MaxRange(QuantumRange);
00641 if ((channel & BlueChannel) != 0)
00642 {
00643 intensity=0.0;
00644 for (i=0; i <= (long) MaxMap; i++)
00645 {
00646 intensity+=histogram[i].blue;
00647 if (intensity > black_point)
00648 break;
00649 }
00650 black.blue=(MagickRealType) i;
00651 intensity=0.0;
00652 for (i=(long) MaxMap; i != 0; i--)
00653 {
00654 intensity+=histogram[i].blue;
00655 if (intensity > ((double) image->columns*image->rows-white_point))
00656 break;
00657 }
00658 white.blue=(MagickRealType) i;
00659 }
00660 black.opacity=0.0;
00661 white.opacity=MaxRange(QuantumRange);
00662 if ((channel & OpacityChannel) != 0)
00663 {
00664 intensity=0.0;
00665 for (i=0; i <= (long) MaxMap; i++)
00666 {
00667 intensity+=histogram[i].opacity;
00668 if (intensity > black_point)
00669 break;
00670 }
00671 black.opacity=(MagickRealType) i;
00672 intensity=0.0;
00673 for (i=(long) MaxMap; i != 0; i--)
00674 {
00675 intensity+=histogram[i].opacity;
00676 if (intensity > ((double) image->columns*image->rows-white_point))
00677 break;
00678 }
00679 white.opacity=(MagickRealType) i;
00680 }
00681 black.index=0.0;
00682 white.index=MaxRange(QuantumRange);
00683 if (((channel & IndexChannel) != 0) && (image->colorspace == CMYKColorspace))
00684 {
00685 intensity=0.0;
00686 for (i=0; i <= (long) MaxMap; i++)
00687 {
00688 intensity+=histogram[i].index;
00689 if (intensity > black_point)
00690 break;
00691 }
00692 black.index=(MagickRealType) i;
00693 intensity=0.0;
00694 for (i=(long) MaxMap; i != 0; i--)
00695 {
00696 intensity+=histogram[i].index;
00697 if (intensity > ((double) image->columns*image->rows-white_point))
00698 break;
00699 }
00700 white.index=(MagickRealType) i;
00701 }
00702 histogram=(MagickPixelPacket *) RelinquishMagickMemory(histogram);
00703
00704
00705
00706 (void) ResetMagickMemory(stretch_map,0,(MaxMap+1)*sizeof(*stretch_map));
00707 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00708 #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
00709 #endif
00710 for (i=0; i <= (long) MaxMap; i++)
00711 {
00712 if ((channel & RedChannel) != 0)
00713 {
00714 if (i < (long) black.red)
00715 stretch_map[i].red=0.0;
00716 else
00717 if (i > (long) white.red)
00718 stretch_map[i].red=(MagickRealType) QuantumRange;
00719 else
00720 if (black.red != white.red)
00721 stretch_map[i].red=(MagickRealType) ScaleMapToQuantum(
00722 (MagickRealType) (MaxMap*(i-black.red)/(white.red-black.red)));
00723 }
00724 if ((channel & GreenChannel) != 0)
00725 {
00726 if (i < (long) black.green)
00727 stretch_map[i].green=0.0;
00728 else
00729 if (i > (long) white.green)
00730 stretch_map[i].green=(MagickRealType) QuantumRange;
00731 else
00732 if (black.green != white.green)
00733 stretch_map[i].green=(MagickRealType) ScaleMapToQuantum(
00734 (MagickRealType) (MaxMap*(i-black.green)/(white.green-
00735 black.green)));
00736 }
00737 if ((channel & BlueChannel) != 0)
00738 {
00739 if (i < (long) black.blue)
00740 stretch_map[i].blue=0.0;
00741 else
00742 if (i > (long) white.blue)
00743 stretch_map[i].blue=(MagickRealType) QuantumRange;
00744 else
00745 if (black.blue != white.blue)
00746 stretch_map[i].blue=(MagickRealType) ScaleMapToQuantum(
00747 (MagickRealType) (MaxMap*(i-black.blue)/(white.blue-
00748 black.blue)));
00749 }
00750 if ((channel & OpacityChannel) != 0)
00751 {
00752 if (i < (long) black.opacity)
00753 stretch_map[i].opacity=0.0;
00754 else
00755 if (i > (long) white.opacity)
00756 stretch_map[i].opacity=(MagickRealType) QuantumRange;
00757 else
00758 if (black.opacity != white.opacity)
00759 stretch_map[i].opacity=(MagickRealType) ScaleMapToQuantum(
00760 (MagickRealType) (MaxMap*(i-black.opacity)/(white.opacity-
00761 black.opacity)));
00762 }
00763 if (((channel & IndexChannel) != 0) &&
00764 (image->colorspace == CMYKColorspace))
00765 {
00766 if (i < (long) black.index)
00767 stretch_map[i].index=0.0;
00768 else
00769 if (i > (long) white.index)
00770 stretch_map[i].index=(MagickRealType) QuantumRange;
00771 else
00772 if (black.index != white.index)
00773 stretch_map[i].index=(MagickRealType) ScaleMapToQuantum(
00774 (MagickRealType) (MaxMap*(i-black.index)/(white.index-
00775 black.index)));
00776 }
00777 }
00778
00779
00780
00781 if (((channel & OpacityChannel) != 0) || (((channel & IndexChannel) != 0) &&
00782 (image->colorspace == CMYKColorspace)))
00783 image->storage_class=DirectClass;
00784 if (image->storage_class == PseudoClass)
00785 {
00786
00787
00788
00789 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00790 #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
00791 #endif
00792 for (i=0; i < (long) image->colors; i++)
00793 {
00794 if ((channel & RedChannel) != 0)
00795 {
00796 if (black.red != white.red)
00797 image->colormap[i].red=RoundToQuantum(stretch_map[
00798 ScaleQuantumToMap(image->colormap[i].red)].red);
00799 }
00800 if ((channel & GreenChannel) != 0)
00801 {
00802 if (black.green != white.green)
00803 image->colormap[i].green=RoundToQuantum(stretch_map[
00804 ScaleQuantumToMap(image->colormap[i].green)].green);
00805 }
00806 if ((channel & BlueChannel) != 0)
00807 {
00808 if (black.blue != white.blue)
00809 image->colormap[i].blue=RoundToQuantum(stretch_map[
00810 ScaleQuantumToMap(image->colormap[i].blue)].blue);
00811 }
00812 if ((channel & OpacityChannel) != 0)
00813 {
00814 if (black.opacity != white.opacity)
00815 image->colormap[i].opacity=RoundToQuantum(stretch_map[
00816 ScaleQuantumToMap(image->colormap[i].opacity)].opacity);
00817 }
00818 }
00819 }
00820
00821
00822
00823 status=MagickTrue;
00824 progress=0;
00825 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00826 #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
00827 #endif
00828 for (y=0; y < (long) image->rows; y++)
00829 {
00830 IndexPacket
00831 *indexes;
00832
00833 register long
00834 id,
00835 x;
00836
00837 register PixelPacket
00838 *q;
00839
00840 if (status == MagickFalse)
00841 continue;
00842 id=GetCacheViewThreadId();
00843 q=GetCacheViewAuthenticPixels(image_view[id],0,y,image->columns,1,
00844 exception);
00845 if (q == (PixelPacket *) NULL)
00846 {
00847 status=MagickFalse;
00848 continue;
00849 }
00850 indexes=GetCacheViewAuthenticIndexes(image_view[id]);
00851 for (x=0; x < (long) image->columns; x++)
00852 {
00853 if ((channel & RedChannel) != 0)
00854 {
00855 if (black.red != white.red)
00856 q->red=RoundToQuantum(stretch_map[ScaleQuantumToMap(
00857 q->red)].red);
00858 }
00859 if ((channel & GreenChannel) != 0)
00860 {
00861 if (black.green != white.green)
00862 q->green=RoundToQuantum(stretch_map[ScaleQuantumToMap(
00863 q->green)].green);
00864 }
00865 if ((channel & BlueChannel) != 0)
00866 {
00867 if (black.blue != white.blue)
00868 q->blue=RoundToQuantum(stretch_map[ScaleQuantumToMap(
00869 q->blue)].blue);
00870 }
00871 if ((channel & OpacityChannel) != 0)
00872 {
00873 if (black.opacity != white.opacity)
00874 q->opacity=RoundToQuantum(stretch_map[ScaleQuantumToMap(
00875 q->opacity)].opacity);
00876 }
00877 if (((channel & IndexChannel) != 0) &&
00878 (image->colorspace == CMYKColorspace))
00879 {
00880 if (black.index != white.index)
00881 indexes[x]=(IndexPacket) RoundToQuantum(stretch_map[
00882 ScaleQuantumToMap(indexes[x])].index);
00883 }
00884 q++;
00885 }
00886 if (SyncCacheViewAuthenticPixels(image_view[id],exception) == MagickFalse)
00887 status=MagickFalse;
00888 if (image->progress_monitor != (MagickProgressMonitor) NULL)
00889 {
00890 MagickBooleanType
00891 proceed;
00892
00893 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00894 #pragma omp critical
00895 #endif
00896 proceed=SetImageProgress(image,ContrastStretchImageTag,progress++,
00897 image->rows);
00898 if (proceed == MagickFalse)
00899 status=MagickFalse;
00900 }
00901 }
00902 image_view=DestroyCacheViewThreadSet(image_view);
00903 stretch_map=(MagickPixelPacket *) RelinquishMagickMemory(stretch_map);
00904 return(status);
00905 }
00906
00907
00908
00909
00910
00911
00912
00913
00914
00915
00916
00917
00918
00919
00920
00921
00922
00923
00924
00925
00926
00927
00928
00929
00930
00931
00932 MagickExport Image *EnhanceImage(const Image *image,ExceptionInfo *exception)
00933 {
00934 #define Enhance(weight) \
00935 mean=((MagickRealType) r->red+pixel.red)/2; \
00936 distance=(MagickRealType) r->red-(MagickRealType) pixel.red; \
00937 distance_squared=QuantumScale*(2.0*((MagickRealType) QuantumRange+1.0)+ \
00938 mean)*distance*distance; \
00939 mean=((MagickRealType) r->green+pixel.green)/2; \
00940 distance=(MagickRealType) r->green-(MagickRealType) pixel.green; \
00941 distance_squared+=4.0*distance*distance; \
00942 mean=((MagickRealType) r->blue+pixel.blue)/2; \
00943 distance=(MagickRealType) r->blue-(MagickRealType) pixel.blue; \
00944 distance_squared+=QuantumScale*(3.0*((MagickRealType) \
00945 QuantumRange+1.0)-1.0-mean)*distance*distance; \
00946 mean=((MagickRealType) r->opacity+pixel.opacity)/2; \
00947 distance=(MagickRealType) r->opacity-(MagickRealType) pixel.opacity; \
00948 distance_squared+=QuantumScale*(3.0*((MagickRealType) \
00949 QuantumRange+1.0)-1.0-mean)*distance*distance; \
00950 if (distance_squared < ((MagickRealType) QuantumRange*(MagickRealType) \
00951 QuantumRange/25.0f)) \
00952 { \
00953 aggregate.red+=(weight)*r->red; \
00954 aggregate.green+=(weight)*r->green; \
00955 aggregate.blue+=(weight)*r->blue; \
00956 aggregate.opacity+=(weight)*r->opacity; \
00957 total_weight+=(weight); \
00958 } \
00959 r++;
00960 #define EnhanceImageTag "Enhance/Image"
00961
00962 Image
00963 *enhance_image;
00964
00965 long
00966 progress,
00967 y;
00968
00969 MagickBooleanType
00970 status;
00971
00972 MagickPixelPacket
00973 zero;
00974
00975 ViewInfo
00976 **enhance_view,
00977 **image_view;
00978
00979
00980
00981
00982 assert(image != (const Image *) NULL);
00983 assert(image->signature == MagickSignature);
00984 if (image->debug != MagickFalse)
00985 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00986 assert(exception != (ExceptionInfo *) NULL);
00987 assert(exception->signature == MagickSignature);
00988 if ((image->columns < 5) || (image->rows < 5))
00989 return((Image *) NULL);
00990 enhance_image=CloneImage(image,image->columns,image->rows,MagickTrue,
00991 exception);
00992 if (enhance_image == (Image *) NULL)
00993 return((Image *) NULL);
00994 if (SetImageStorageClass(enhance_image,DirectClass) == MagickFalse)
00995 {
00996 InheritException(exception,&enhance_image->exception);
00997 enhance_image=DestroyImage(enhance_image);
00998 return((Image *) NULL);
00999 }
01000
01001
01002
01003 status=MagickTrue;
01004 progress=0;
01005 (void) ResetMagickMemory(&zero,0,sizeof(zero));
01006 image_view=AcquireCacheViewThreadSet(image);
01007 enhance_view=AcquireCacheViewThreadSet(enhance_image);
01008 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01009 #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
01010 #endif
01011 for (y=0; y < (lo