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/artifact.h"
00045 #include "magick/cache.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/composite-private.h"
00051 #include "magick/enhance.h"
00052 #include "magick/exception.h"
00053 #include "magick/exception-private.h"
00054 #include "magick/gem.h"
00055 #include "magick/geometry.h"
00056 #include "magick/histogram.h"
00057 #include "magick/image.h"
00058 #include "magick/image-private.h"
00059 #include "magick/memory_.h"
00060 #include "magick/monitor.h"
00061 #include "magick/monitor-private.h"
00062 #include "magick/option.h"
00063 #include "magick/quantum.h"
00064 #include "magick/quantum-private.h"
00065 #include "magick/resample.h"
00066 #include "magick/resample-private.h"
00067 #include "magick/statistic.h"
00068 #include "magick/string_.h"
00069 #include "magick/thread-private.h"
00070 #include "magick/token.h"
00071 #include "magick/xml-tree.h"
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103 MagickExport MagickBooleanType AutoGammaImage(Image *image)
00104 {
00105 return(AutoGammaImageChannel(image,DefaultChannels));
00106 }
00107
00108 MagickExport MagickBooleanType AutoGammaImageChannel(Image *image,
00109 const ChannelType channel)
00110 {
00111 MagickStatusType
00112 status;
00113
00114 double
00115 mean,junk,gamma,logmean;
00116
00117 logmean=log(0.5);
00118
00119 if ((channel & SyncChannels) != 0 )
00120 {
00121
00122
00123
00124 GetImageChannelMean(image, channel, &mean, &junk, &image->exception);
00125 gamma = log(mean*QuantumScale)/logmean;
00126
00127 return LevelImageChannel(image, channel,
00128 0.0, (double)QuantumRange, gamma);
00129 }
00130
00131
00132
00133
00134 status = MagickTrue;
00135 if ((channel & RedChannel) != 0)
00136 {
00137 GetImageChannelMean(image, RedChannel, &mean, &junk, &image->exception);
00138 gamma = log(mean*QuantumScale)/logmean;
00139
00140 status = status && LevelImageChannel(image, RedChannel,
00141 0.0, (double)QuantumRange, gamma);
00142 }
00143 if ((channel & GreenChannel) != 0)
00144 {
00145 GetImageChannelMean(image, GreenChannel, &mean, &junk, &image->exception);
00146 gamma = log(mean*QuantumScale)/logmean;
00147
00148 status = status && LevelImageChannel(image, GreenChannel,
00149 0.0, (double)QuantumRange, gamma);
00150 }
00151 if ((channel & BlueChannel) != 0)
00152 {
00153 GetImageChannelMean(image, BlueChannel, &mean, &junk, &image->exception);
00154 gamma = log(mean*QuantumScale)/logmean;
00155
00156 status = status && LevelImageChannel(image, BlueChannel,
00157 0.0, (double)QuantumRange, gamma);
00158 }
00159 if (((channel & OpacityChannel) != 0) &&
00160 (image->matte == MagickTrue))
00161 {
00162 GetImageChannelMean(image, OpacityChannel, &mean, &junk, &image->exception);
00163 gamma = log(mean*QuantumScale)/logmean;
00164
00165 status = status && LevelImageChannel(image, OpacityChannel,
00166 0.0, (double)QuantumRange, gamma);
00167 }
00168 if (((channel & IndexChannel) != 0) &&
00169 (image->colorspace == CMYKColorspace))
00170 {
00171 GetImageChannelMean(image, IndexChannel, &mean, &junk, &image->exception);
00172 gamma = log(mean*QuantumScale)/logmean;
00173
00174 status = status && LevelImageChannel(image, IndexChannel,
00175 0.0, (double)QuantumRange, gamma);
00176 }
00177 return(status != 0 ? MagickTrue : MagickFalse);
00178 }
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210 MagickExport MagickBooleanType AutoLevelImage(Image *image)
00211 {
00212 return(AutoLevelImageChannel(image,DefaultChannels));
00213 }
00214
00215 MagickExport MagickBooleanType AutoLevelImageChannel(Image *image,
00216 const ChannelType channel)
00217 {
00218
00219
00220
00221 return MinMaxStretchImage(image, channel, 0.0, 0.0);
00222 }
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267 MagickExport MagickBooleanType ColorDecisionListImage(Image *image,
00268 const char *color_correction_collection)
00269 {
00270 #define ColorDecisionListCorrectImageTag "ColorDecisionList/Image"
00271
00272 typedef struct _Correction
00273 {
00274 double
00275 slope,
00276 offset,
00277 power;
00278 } Correction;
00279
00280 typedef struct _ColorCorrection
00281 {
00282 Correction
00283 red,
00284 green,
00285 blue;
00286
00287 double
00288 saturation;
00289 } ColorCorrection;
00290
00291 char
00292 token[MaxTextExtent];
00293
00294 ColorCorrection
00295 color_correction;
00296
00297 const char
00298 *content,
00299 *p;
00300
00301 ExceptionInfo
00302 *exception;
00303
00304 long
00305 progress,
00306 y;
00307
00308 MagickBooleanType
00309 status;
00310
00311 PixelPacket
00312 *cdl_map;
00313
00314 register long
00315 i;
00316
00317 XMLTreeInfo
00318 *cc,
00319 *ccc,
00320 *sat,
00321 *sop;
00322
00323 CacheView
00324 *image_view;
00325
00326
00327
00328
00329 assert(image != (Image *) NULL);
00330 assert(image->signature == MagickSignature);
00331 if (image->debug != MagickFalse)
00332 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00333 if (color_correction_collection == (const char *) NULL)
00334 return(MagickFalse);
00335 ccc=NewXMLTree((const char *) color_correction_collection,&image->exception);
00336 if (ccc == (XMLTreeInfo *) NULL)
00337 return(MagickFalse);
00338 cc=GetXMLTreeChild(ccc,"ColorCorrection");
00339 if (cc == (XMLTreeInfo *) NULL)
00340 {
00341 ccc=DestroyXMLTree(ccc);
00342 return(MagickFalse);
00343 }
00344 color_correction.red.slope=1.0;
00345 color_correction.red.offset=0.0;
00346 color_correction.red.power=1.0;
00347 color_correction.green.slope=1.0;
00348 color_correction.green.offset=0.0;
00349 color_correction.green.power=1.0;
00350 color_correction.blue.slope=1.0;
00351 color_correction.blue.offset=0.0;
00352 color_correction.blue.power=1.0;
00353 color_correction.saturation=0.0;
00354 sop=GetXMLTreeChild(cc,"SOPNode");
00355 if (sop != (XMLTreeInfo *) NULL)
00356 {
00357 XMLTreeInfo
00358 *offset,
00359 *power,
00360 *slope;
00361
00362 slope=GetXMLTreeChild(sop,"Slope");
00363 if (slope != (XMLTreeInfo *) NULL)
00364 {
00365 content=GetXMLTreeContent(slope);
00366 p=(const char *) content;
00367 for (i=0; (*p != '\0') && (i < 3); i++)
00368 {
00369 GetMagickToken(p,&p,token);
00370 if (*token == ',')
00371 GetMagickToken(p,&p,token);
00372 switch (i)
00373 {
00374 case 0: color_correction.red.slope=atof(token); break;
00375 case 1: color_correction.green.slope=atof(token); break;
00376 case 2: color_correction.blue.slope=atof(token); break;
00377 }
00378 }
00379 }
00380 offset=GetXMLTreeChild(sop,"Offset");
00381 if (offset != (XMLTreeInfo *) NULL)
00382 {
00383 content=GetXMLTreeContent(offset);
00384 p=(const char *) content;
00385 for (i=0; (*p != '\0') && (i < 3); i++)
00386 {
00387 GetMagickToken(p,&p,token);
00388 if (*token == ',')
00389 GetMagickToken(p,&p,token);
00390 switch (i)
00391 {
00392 case 0: color_correction.red.offset=atof(token); break;
00393 case 1: color_correction.green.offset=atof(token); break;
00394 case 2: color_correction.blue.offset=atof(token); break;
00395 }
00396 }
00397 }
00398 power=GetXMLTreeChild(sop,"Power");
00399 if (power != (XMLTreeInfo *) NULL)
00400 {
00401 content=GetXMLTreeContent(power);
00402 p=(const char *) content;
00403 for (i=0; (*p != '\0') && (i < 3); i++)
00404 {
00405 GetMagickToken(p,&p,token);
00406 if (*token == ',')
00407 GetMagickToken(p,&p,token);
00408 switch (i)
00409 {
00410 case 0: color_correction.red.power=atof(token); break;
00411 case 1: color_correction.green.power=atof(token); break;
00412 case 2: color_correction.blue.power=atof(token); break;
00413 }
00414 }
00415 }
00416 }
00417 sat=GetXMLTreeChild(cc,"SATNode");
00418 if (sat != (XMLTreeInfo *) NULL)
00419 {
00420 XMLTreeInfo
00421 *saturation;
00422
00423 saturation=GetXMLTreeChild(sat,"Saturation");
00424 if (saturation != (XMLTreeInfo *) NULL)
00425 {
00426 content=GetXMLTreeContent(saturation);
00427 p=(const char *) content;
00428 GetMagickToken(p,&p,token);
00429 color_correction.saturation=atof(token);
00430 }
00431 }
00432 ccc=DestroyXMLTree(ccc);
00433 if (image->debug != MagickFalse)
00434 {
00435 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
00436 " Color Correction Collection:");
00437 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
00438 " color_correction.red.slope: %g",color_correction.red.slope);
00439 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
00440 " color_correction.red.offset: %g",color_correction.red.offset);
00441 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
00442 " color_correction.red.power: %g",color_correction.red.power);
00443 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
00444 " color_correction.green.slope: %g",color_correction.green.slope);
00445 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
00446 " color_correction.green.offset: %g",color_correction.green.offset);
00447 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
00448 " color_correction.green.power: %g",color_correction.green.power);
00449 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
00450 " color_correction.blue.slope: %g",color_correction.blue.slope);
00451 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
00452 " color_correction.blue.offset: %g",color_correction.blue.offset);
00453 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
00454 " color_correction.blue.power: %g",color_correction.blue.power);
00455 (void) LogMagickEvent(TransformEvent,GetMagickModule(),
00456 " color_correction.saturation: %g",color_correction.saturation);
00457 }
00458 cdl_map=(PixelPacket *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*cdl_map));
00459 if (cdl_map == (PixelPacket *) NULL)
00460 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
00461 image->filename);
00462 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00463 #pragma omp parallel for schedule(dynamic,4)
00464 #endif
00465 for (i=0; i <= (long) MaxMap; i++)
00466 {
00467 cdl_map[i].red=RoundToQuantum((MagickRealType) ScaleMapToQuantum((
00468 MagickRealType) (MaxMap*(pow(color_correction.red.slope*i/MaxMap+
00469 color_correction.red.offset,color_correction.red.power)))));
00470 cdl_map[i].green=RoundToQuantum((MagickRealType) ScaleMapToQuantum((
00471 MagickRealType) (MaxMap*(pow(color_correction.green.slope*i/MaxMap+
00472 color_correction.green.offset,color_correction.green.power)))));
00473 cdl_map[i].blue=RoundToQuantum((MagickRealType) ScaleMapToQuantum((
00474 MagickRealType) (MaxMap*(pow(color_correction.blue.slope*i/MaxMap+
00475 color_correction.blue.offset,color_correction.blue.power)))));
00476 }
00477 if (image->storage_class == PseudoClass)
00478 {
00479
00480
00481
00482 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00483 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
00484 #endif
00485 for (i=0; i < (long) image->colors; i++)
00486 {
00487 double
00488 luma;
00489
00490 luma=0.2126*image->colormap[i].red+0.7152*image->colormap[i].green+
00491 0.0722*image->colormap[i].blue;
00492 image->colormap[i].red=RoundToQuantum(luma+color_correction.saturation*
00493 cdl_map[ScaleQuantumToMap(image->colormap[i].red)].red-luma);
00494 image->colormap[i].green=RoundToQuantum(luma+
00495 color_correction.saturation*cdl_map[ScaleQuantumToMap(
00496 image->colormap[i].green)].green-luma);
00497 image->colormap[i].blue=RoundToQuantum(luma+color_correction.saturation*
00498 cdl_map[ScaleQuantumToMap(image->colormap[i].blue)].blue-luma);
00499 }
00500 }
00501
00502
00503
00504 status=MagickTrue;
00505 progress=0;
00506 exception=(&image->exception);
00507 image_view=AcquireCacheView(image);
00508 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00509 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
00510 #endif
00511 for (y=0; y < (long) image->rows; y++)
00512 {
00513 double
00514 luma;
00515
00516 register long
00517 x;
00518
00519 register PixelPacket
00520 *__restrict q;
00521
00522 if (status == MagickFalse)
00523 continue;
00524 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
00525 if (q == (PixelPacket *) NULL)
00526 {
00527 status=MagickFalse;
00528 continue;
00529 }
00530 for (x=0; x < (long) image->columns; x++)
00531 {
00532 luma=0.2126*q->red+0.7152*q->green+0.0722*q->blue;
00533 q->red=RoundToQuantum(luma+color_correction.saturation*
00534 (cdl_map[ScaleQuantumToMap(q->red)].red-luma));
00535 q->green=RoundToQuantum(luma+color_correction.saturation*
00536 (cdl_map[ScaleQuantumToMap(q->green)].green-luma));
00537 q->blue=RoundToQuantum(luma+color_correction.saturation*
00538 (cdl_map[ScaleQuantumToMap(q->blue)].blue-luma));
00539 q++;
00540 }
00541 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
00542 status=MagickFalse;
00543 if (image->progress_monitor != (MagickProgressMonitor) NULL)
00544 {
00545 MagickBooleanType
00546 proceed;
00547
00548 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00549 #pragma omp critical (MagickCore_ColorDecisionListImageChannel)
00550 #endif
00551 proceed=SetImageProgress(image,ColorDecisionListCorrectImageTag,
00552 progress++,image->rows);
00553 if (proceed == MagickFalse)
00554 status=MagickFalse;
00555 }
00556 }
00557 image_view=DestroyCacheView(image_view);
00558 cdl_map=(PixelPacket *) RelinquishMagickMemory(cdl_map);
00559 return(status);
00560 }
00561
00562
00563
00564
00565
00566
00567
00568
00569
00570
00571
00572
00573
00574
00575
00576
00577
00578
00579
00580
00581
00582
00583
00584
00585
00586
00587
00588
00589
00590
00591
00592
00593
00594
00595
00596
00597
00598
00599
00600
00601
00602
00603
00604
00605
00606
00607 MagickExport MagickBooleanType ClutImage(Image *image,const Image *clut_image)
00608 {
00609 return(ClutImageChannel(image,DefaultChannels,clut_image));
00610 }
00611
00612 MagickExport MagickBooleanType ClutImageChannel(Image *image,
00613 const ChannelType channel,const Image *clut_image)
00614 {
00615 #define ClutImageTag "Clut/Image"
00616
00617 ExceptionInfo
00618 *exception;
00619
00620 long
00621 adjust,
00622 progress,
00623 y;
00624
00625 MagickBooleanType
00626 status;
00627
00628 MagickPixelPacket
00629 zero;
00630
00631 ResampleFilter
00632 **resample_filter;
00633
00634 CacheView
00635 *image_view;
00636
00637 assert(image != (Image *) NULL);
00638 assert(image->signature == MagickSignature);
00639 if (image->debug != MagickFalse)
00640 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00641 assert(clut_image != (Image *) NULL);
00642 assert(clut_image->signature == MagickSignature);
00643 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
00644 return(MagickFalse);
00645
00646
00647
00648 status=MagickTrue;
00649 progress=0;
00650 GetMagickPixelPacket(clut_image,&zero);
00651 adjust=clut_image->interpolate == IntegerInterpolatePixel ? 0 : 1;
00652 exception=(&image->exception);
00653 resample_filter=AcquireResampleFilterThreadSet(clut_image,MagickTrue,
00654 exception);
00655 image_view=AcquireCacheView(image);
00656 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00657 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
00658 #endif
00659 for (y=0; y < (long) image->rows; y++)
00660 {
00661 MagickPixelPacket
00662 pixel;
00663
00664 register IndexPacket
00665 *__restrict indexes;
00666
00667 register long
00668 id,
00669 x;
00670
00671 register PixelPacket
00672 *__restrict q;
00673
00674 if (status == MagickFalse)
00675 continue;
00676 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
00677 if (q == (PixelPacket *) NULL)
00678 {
00679 status=MagickFalse;
00680 continue;
00681 }
00682 indexes=GetCacheViewAuthenticIndexQueue(image_view);
00683 pixel=zero;
00684 id=GetOpenMPThreadId();
00685 for (x=0; x < (long) image->columns; x++)
00686 {
00687
00688
00689
00690
00691
00692
00693
00694
00695
00696
00697
00698 if ((channel & OpacityChannel) != 0)
00699 {
00700 if (clut_image->matte == MagickFalse)
00701 {
00702
00703
00704
00705 (void) ResamplePixelColor(resample_filter[id],QuantumScale*
00706 (QuantumRange-q->opacity)*(clut_image->columns+adjust),
00707 QuantumScale*(QuantumRange-q->opacity)*(clut_image->rows+
00708 adjust),&pixel);
00709 q->opacity=(Quantum) (QuantumRange-MagickPixelIntensityToQuantum(
00710 &pixel));
00711 }
00712 else
00713 if (image->matte == MagickFalse)
00714 {
00715
00716
00717
00718 (void) ResamplePixelColor(resample_filter[id],QuantumScale*
00719 PixelIntensity(q)*(clut_image->columns-adjust),QuantumScale*
00720 PixelIntensity(q)*(clut_image->rows-adjust),&pixel);
00721 q->opacity=RoundToQuantum(pixel.opacity);
00722 }
00723 else
00724 {
00725
00726
00727
00728 (void) ResamplePixelColor(resample_filter[id],QuantumScale*
00729 q->opacity*(clut_image->columns-adjust),QuantumScale*
00730 q->opacity* (clut_image->rows-adjust),&pixel);
00731 q->opacity=RoundToQuantum(pixel.opacity);
00732 }
00733 }
00734 if ((channel & RedChannel) != 0)
00735 {
00736 (void) ResamplePixelColor(resample_filter[id],QuantumScale*q->red*
00737 (clut_image->columns-adjust),QuantumScale*q->red*
00738 (clut_image->rows-adjust),&pixel);
00739 q->red=RoundToQuantum(pixel.red);
00740 }
00741 if ((channel & GreenChannel) != 0)
00742 {
00743 (void) ResamplePixelColor(resample_filter[id],QuantumScale*q->green*
00744 (clut_image->columns-adjust),QuantumScale*q->green*
00745 (clut_image->rows-adjust),&pixel);
00746 q->green=RoundToQuantum(pixel.green);
00747 }
00748 if ((channel & BlueChannel) != 0)
00749 {
00750 (void) ResamplePixelColor(resample_filter[id],QuantumScale*q->blue*
00751 (clut_image->columns-adjust),QuantumScale*q->blue*
00752 (clut_image->rows-adjust),&pixel);
00753 q->blue=RoundToQuantum(pixel.blue);
00754 }
00755 if (((channel & IndexChannel) != 0) &&
00756 (image->colorspace == CMYKColorspace))
00757 {
00758 (void) ResamplePixelColor(resample_filter[id],QuantumScale*indexes[x]*
00759 (clut_image->columns-adjust),QuantumScale*indexes[x]*
00760 (clut_image->rows-adjust),&pixel);
00761 indexes[x]=RoundToQuantum(pixel.index);
00762 }
00763 q++;
00764 }
00765 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
00766 status=MagickFalse;
00767 if (image->progress_monitor != (MagickProgressMonitor) NULL)
00768 {
00769 MagickBooleanType
00770 proceed;
00771
00772 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00773 #pragma omp critical (MagickCore_ClutImageChannel)
00774 #endif
00775 proceed=SetImageProgress(image,ClutImageTag,progress++,image->rows);
00776 if (proceed == MagickFalse)
00777 status=MagickFalse;
00778 }
00779 }
00780 image_view=DestroyCacheView(image_view);
00781 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
00782
00783
00784
00785 if ((clut_image->matte != MagickFalse) && ((channel & OpacityChannel) != 0))
00786 (void) SetImageAlphaChannel(image,ActivateAlphaChannel);
00787 return(status);
00788 }
00789
00790
00791
00792
00793
00794
00795
00796
00797
00798
00799
00800
00801
00802
00803
00804
00805
00806
00807
00808
00809
00810
00811
00812
00813
00814
00815
00816
00817
00818 static void Contrast(const int sign,Quantum *red,Quantum *green,Quantum *blue)
00819 {
00820 double
00821 brightness,
00822 hue,
00823 saturation;
00824
00825
00826
00827
00828 assert(red != (Quantum *) NULL);
00829 assert(green != (Quantum *) NULL);
00830 assert(blue != (Quantum *) NULL);
00831 hue=0.0;
00832 saturation=0.0;
00833 brightness=0.0;
00834 ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness);
00835 brightness+=0.5*sign*(0.5*(sin(MagickPI*(brightness-0.5))+1.0)-brightness);
00836 if (brightness > 1.0)
00837 brightness=1.0;
00838 else
00839 if (brightness < 0.0)
00840 brightness=0.0;
00841 ConvertHSBToRGB(hue,saturation,brightness,red,green,blue);
00842 }
00843
00844 MagickExport MagickBooleanType ContrastImage(Image *image,
00845 const MagickBooleanType sharpen)
00846 {
00847 #define ContrastImageTag "Contrast/Image"
00848
00849 ExceptionInfo
00850 *exception;
00851
00852 int
00853 sign;
00854
00855 long
00856 progress,
00857 y;
00858
00859 MagickBooleanType
00860 status;
00861
00862 register long
00863 i;
00864
00865 CacheView
00866 *image_view;
00867
00868 assert(image != (Image *) NULL);
00869 assert(image->signature == MagickSignature);
00870 if (image->debug != MagickFalse)
00871 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00872 sign=sharpen != MagickFalse ? 1 : -1;
00873 if (image->storage_class == PseudoClass)
00874 {
00875
00876
00877
00878 for (i=0; i < (long) image->colors; i++)
00879 Contrast(sign,&image->colormap[i].red,&image->colormap[i].green,
00880 &image->colormap[i].blue);
00881 }
00882
00883
00884
00885 status=MagickTrue;
00886 progress=0;
00887 exception=(&image->exception);
00888 image_view=AcquireCacheView(image);
00889 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00890 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
00891 #endif
00892 for (y=0; y < (long) image->rows; y++)
00893 {
00894 register long
00895 x;
00896
00897 register PixelPacket
00898 *__restrict q;
00899
00900 if (status == MagickFalse)
00901 continue;
00902 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
00903 if (q == (PixelPacket *) NULL)
00904 {
00905 status=MagickFalse;
00906 continue;
00907 }
00908 for (x=0; x < (long) image->columns; x++)
00909 {
00910 Contrast(sign,&q->red,&q->green,&q->blue);
00911 q++;
00912 }
00913 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
00914 status=MagickFalse;
00915 if (image->progress_monitor != (MagickProgressMonitor) NULL)
00916 {
00917 MagickBooleanType
00918 proceed;
00919
00920 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00921 #pragma omp critical (MagickCore_ContrastImage)
00922 #endif
00923 proceed=SetImageProgress(image,ContrastImageTag,progress++,image->rows);
00924 if (proceed == MagickFalse)
00925 status=MagickFalse;
00926 }
00927 }
00928 image_view=DestroyCacheView(image_view);
00929 return(status);
00930 }
00931
00932
00933
00934
00935
00936
00937
00938
00939
00940
00941
00942
00943
00944
00945
00946
00947
00948
00949
00950
00951
00952
00953
00954
00955
00956
00957
00958
00959
00960
00961
00962
00963
00964
00965
00966
00967
00968
00969
00970
00971
00972
00973 MagickExport MagickBooleanType ContrastStretchImage(Image *image,
00974 const char *levels)
00975 {
00976 double
00977 black_point,
00978 white_point;
00979
00980 GeometryInfo
00981 geometry_info;
00982
00983 MagickBooleanType
00984 status;
00985
00986 MagickStatusType
00987 flags;
00988
00989
00990
00991
00992 if (levels == (char *) NULL)
00993 return(MagickFalse);
00994 flags=ParseGeometry(levels,&geometry_info);
00995 black_point=geometry_info.rho;
00996 white_point=(double) image->columns*image->rows;
00997 if ((flags & SigmaValue) != 0)
00998 white_point=geometry_info.sigma;
00999 if ((flags & PercentValue) != 0)
01000 {
01001 black_point*=(double) QuantumRange/100.0;
01002 white_point*=(double) QuantumRange/100.0;
01003 }
01004 if ((flags & SigmaValue) == 0)
01005 white_point=(double) image->columns*image->rows-black_point;
01006 status=ContrastStretchImageChannel(image,DefaultChannels,black_point,
01007 white_point);
01008 return(status);
01009 }
01010
01011 MagickExport MagickBooleanType ContrastStretchImageChannel(Image *image,
01012 const ChannelType channel,const double black_point,const double white_point)
01013 {
01014 #define MaxRange(color) ((MagickRealType) ScaleQuantumToMap((Quantum) (color)))
01015 #define ContrastStretchImageTag "ContrastStretch/Image"
01016
01017 double
01018 intensity;
01019
01020 ExceptionInfo
01021 *exception;
01022
01023 long
01024 progress,
01025 y;
01026
01027 MagickBooleanType
01028 status;
01029
01030 MagickPixelPacket
01031 black,
01032 *histogram,
01033 *stretch_map,
01034 white;
01035
01036 register long
01037 i;
01038
01039 CacheView
01040 *image_view;
01041
01042
01043
01044
01045 assert(image != (Image *) NULL);
01046 assert(image->signature == MagickSignature);
01047 if (image->debug != MagickFalse)
01048 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01049 histogram=(MagickPixelPacket *) AcquireQuantumMemory(MaxMap+1UL,
01050 sizeof(*histogram));
01051 stretch_map=(MagickPixelPacket *) AcquireQuantumMemory(MaxMap+1UL,
01052 sizeof(*stretch_map));
01053 if ((histogram == (MagickPixelPacket *) NULL) ||
01054 (stretch_map == (MagickPixelPacket *) NULL))
01055 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
01056 image->filename);
01057
01058
01059
01060 status=MagickTrue;
01061 exception=(&image->exception);
01062 (void) ResetMagickMemory(histogram,0,(MaxMap+1)*sizeof(*histogram));
01063 image_view=AcquireCacheView(image);
01064 for (y=0; y < (long) image->rows; y++)
01065 {
01066 register const PixelPacket
01067 *__restrict p;
01068
01069 register IndexPacket
01070 *__restrict indexes;
01071
01072 register long
01073 x;
01074
01075 if (status == MagickFalse)
01076 continue;
01077 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
01078 if (p == (const PixelPacket *) NULL)
01079 {
01080 status=MagickFalse;
01081 continue;
01082 }
01083 indexes=GetCacheViewAuthenticIndexQueue(image_view);
01084 if (channel == DefaultChannels)
01085 for (x=0; x < (long) image->columns; x++)
01086 {
01087 Quantum
01088 intensity;
01089
01090 intensity=PixelIntensityToQuantum(p);
01091 histogram[ScaleQuantumToMap(intensity)].red++;
01092 histogram[ScaleQuantumToMap(intensity)].green++;
01093 histogram[ScaleQuantumToMap(intensity)].blue++;
01094 histogram[ScaleQuantumToMap(intensity)].index++;
01095 p++;
01096 }
01097 else
01098 for (x=0; x < (long) image->columns; x++)
01099 {
01100 if ((channel & RedChannel) != 0)
01101 histogram[ScaleQuantumToMap(p->red)].red++;
01102 if ((channel & GreenChannel) != 0)
01103 histogram[ScaleQuantumToMap(p->green)].green++;
01104 if ((channel & BlueChannel) != 0)
01105 histogram[ScaleQuantumToMap(p->blue)].blue++;
01106 if ((channel & OpacityChannel) != 0)
01107 histogram[ScaleQuantumToMap(p->opacity)].opacity++;
01108 if (((channel & IndexChannel) != 0) &&
01109 (image->colorspace == CMYKColorspace))
01110 histogram[ScaleQuantumToMap(indexes[x])].index++;
01111 p++;
01112 }
01113 }
01114
01115
01116
01117 black.red=0.0;
01118 white.red=MaxRange(QuantumRange);
01119 if ((channel & RedChannel) != 0)
01120 {
01121 intensity=0.0;
01122 for (i=0; i <= (long) MaxMap; i++)
01123 {
01124 intensity+=histogram[i].red;
01125 if (intensity > black_point)
01126 break;
01127 }
01128 black.red=(MagickRealType) i;
01129 intensity=0.0;
01130 for (i=(long) MaxMap; i != 0; i--)
01131 {
01132 intensity+=histogram[i].red;
01133 if (intensity > ((double) image->columns*image->rows-white_point))
01134 break;
01135 }
01136 white.red=(MagickRealType) i;
01137 }
01138 black.green=0.0;
01139 white.green=MaxRange(QuantumRange);
01140 if ((channel & GreenChannel) != 0)
01141 {
01142 intensity=0.0;
01143 for (i=0; i <= (long) MaxMap; i++)
01144 {
01145 intensity+=histogram[i].green;
01146 if (intensity > black_point)
01147 break;
01148 }
01149 black.green=(MagickRealType) i;
01150 intensity=0.0;
01151 for (i=(long) MaxMap; i != 0; i--)
01152 {
01153 intensity+=histogram[i].green;
01154 if (intensity > ((double) image->columns*image->rows-white_point))
01155 break;
01156 }
01157 white.green=(MagickRealType) i;
01158 }
01159 black.blue=0.0;
01160 white.blue=MaxRange(QuantumRange);
01161 if ((channel & BlueChannel) != 0)
01162 {
01163 intensity=0.0;
01164 for (i=0; i <= (long) MaxMap; i++)
01165 {
01166 intensity+=histogram[i].blue;
01167 if (intensity > black_point)
01168 break;
01169 }
01170 black.blue=(MagickRealType) i;
01171 intensity=0.0;
01172 for (i=(long) MaxMap; i != 0; i--)
01173 {
01174 intensity+=histogram[i].blue;
01175 if (intensity > ((double) image->columns*image->rows-white_point))
01176 break;
01177 }
01178 white.blue=(MagickRealType) i;
01179 }
01180 black.opacity=0.0;
01181 white.opacity=MaxRange(QuantumRange);
01182 if ((channel & OpacityChannel) != 0)
01183 {
01184 intensity=0.0;
01185 for (i=0; i <= (long) MaxMap; i++)
01186 {
01187 intensity+=histogram[i].opacity;
01188 if (intensity > black_point)
01189 break;
01190 }
01191 black.opacity=(MagickRealType) i;
01192 intensity=0.0;
01193 for (i=(long) MaxMap; i != 0; i--)
01194 {
01195 intensity+=histogram[i].opacity;
01196 if (intensity > ((double) image->columns*image->rows-white_point))
01197 break;
01198 }
01199 white.opacity=(MagickRealType) i;
01200 }
01201 black.index=0.0;
01202 white.index=MaxRange(QuantumRange);
01203 if (((channel & IndexChannel) != 0) && (image->colorspace == CMYKColorspace))
01204 {
01205 intensity=0.0;
01206 for (i=0; i <= (long) MaxMap; i++)
01207 {
01208 intensity+=histogram[i].index;
01209 if (intensity > black_point)
01210 break;
01211 }
01212 black.index=(MagickRealType) i;
01213 intensity=0.0;
01214 for (i=(long) MaxMap; i != 0; i--)
01215 {
01216 intensity+=histogram[i].index;
01217 if (intensity > ((double) image->columns*image->rows-white_point))
01218 break;
01219 }
01220 white.index=(MagickRealType) i;
01221 }
01222 histogram=(MagickPixelPacket *) RelinquishMagickMemory(histogram);
01223
01224
01225
01226 (void) ResetMagickMemory(stretch_map,0,(MaxMap+1)*sizeof(*stretch_map));
01227 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01228 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
01229 #endif
01230 for (i=0; i <= (long) MaxMap; i++)
01231 {
01232 if ((channel & RedChannel) != 0)
01233 {
01234 if (i < (long) black.red)
01235 stretch_map[i].red=0.0;
01236 else
01237 if (i > (long) white.red)
01238 stretch_map[i].red=(MagickRealType) QuantumRange;
01239 else
01240 if (black.red != white.red)
01241 stretch_map[i].red=(MagickRealType) ScaleMapToQuantum(
01242 (MagickRealType) (MaxMap*(i-black.red)/(white.red-black.red)));
01243 }
01244 if ((channel & GreenChannel) != 0)
01245 {
01246 if (i < (long) black.green)
01247 stretch_map[i].green=0.0;
01248 else
01249 if (i > (long) white.green)
01250 stretch_map[i].green=(MagickRealType) QuantumRange;
01251 else
01252 if (black.green != white.green)
01253 stretch_map[i].green=(MagickRealType) ScaleMapToQuantum(
01254 (MagickRealType) (MaxMap*(i-black.green)/(white.green-
01255 black.green)));
01256 }
01257 if ((channel & BlueChannel) != 0)
01258 {
01259 if (i < (long) black.blue)
01260 stretch_map[i].blue=0.0;
01261 else
01262 if (i > (long) white.blue)
01263 stretch_map[i].blue=(MagickRealType) QuantumRange;
01264 else
01265 if (black.blue != white.blue)
01266 stretch_map[i].blue=(MagickRealType) ScaleMapToQuantum(
01267 (MagickRealType) (MaxMap*(i-black.blue)/(white.blue-
01268 black.blue)));
01269 }
01270 if ((channel & OpacityChannel) != 0)
01271 {
01272 if (i < (long) black.opacity)
01273 stretch_map[i].opacity=0.0;
01274 else
01275 if (i > (long) white.opacity)
01276 stretch_map[i].opacity=(MagickRealType) QuantumRange;
01277 else
01278 if (black.opacity != white.opacity)
01279 stretch_map[i].opacity=(MagickRealType) ScaleMapToQuantum(
01280 (MagickRealType) (MaxMap*(i-black.opacity)/(white.opacity-
01281 black.opacity)));
01282 }
01283 if (((channel & IndexChannel) != 0) &&
01284 (image->colorspace == CMYKColorspace))
01285 {
01286 if (i < (long) black.index)
01287 stretch_map[i].index=0.0;
01288 else
01289 if (i > (long) white.index)
01290 stretch_map[i].index=(MagickRealType) QuantumRange;
01291 else
01292 if (black.index != white.index)
01293 stretch_map[i].index=(MagickRealType) ScaleMapToQuantum(
01294 (MagickRealType) (MaxMap*(i-black.index)/(white.index-
01295 black.index)));
01296 }
01297 }
01298
01299
01300
01301 if (((channel & OpacityChannel) != 0) || (((channel & IndexChannel) != 0) &&
01302 (image->colorspace == CMYKColorspace)))
01303 image->storage_class=DirectClass;
01304 if (image->storage_class == PseudoClass)
01305 {
01306
01307
01308
01309 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01310 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
01311 #endif
01312 for (i=0; i < (long) image->colors; i++)
01313 {
01314 if ((channel & RedChannel) != 0)
01315 {
01316 if (black.red != white.red)
01317 image->colormap[i].red=RoundToQuantum(stretch_map[
01318 ScaleQuantumToMap(image->colormap[i].red)].red);
01319 }
01320 if ((channel & GreenChannel) != 0)
01321 {
01322 if (black.green != white.green)
01323 image->colormap[i].green=RoundToQuantum(stretch_map[
01324 ScaleQuantumToMap(image->colormap[i].green)].green);
01325 }
01326 if ((channel & BlueChannel) != 0)
01327 {
01328 if (black.blue != white.blue)
01329 image->colormap[i].blue=RoundToQuantum(stretch_map[
01330 ScaleQuantumToMap(image->colormap[i].blue)].blue);
01331 }
01332 if ((channel & OpacityChannel) != 0)
01333 {
01334 if (black.opacity != white.opacity)
01335 image->colormap[i].opacity=RoundToQuantum(stretch_map[
01336 ScaleQuantumToMap(image->colormap[i].opacity)].opacity);
01337 }
01338 }
01339 }
01340
01341
01342
01343 status=MagickTrue;
01344 progress=0;
01345 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01346 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
01347 #endif
01348 for (y=0; y < (long) image->rows; y++)
01349 {
01350 register IndexPacket
01351 *__restrict indexes;
01352
01353 register long
01354 x;
01355
01356 register PixelPacket
01357 *__restrict q;
01358
01359 if (status == MagickFalse)
01360 continue;
01361 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
01362 if (q == (PixelPacket *) NULL)
01363 {
01364 status=MagickFalse;
01365 continue;
01366 }
01367 indexes=GetCacheViewAuthenticIndexQueue(image_view);
01368 for (x=0; x < (long) image->columns; x++)
01369 {
01370 if ((channel & RedChannel) != 0)
01371 {
01372 if (black.red != white.red)
01373 q->red=RoundToQuantum(stretch_map[ScaleQuantumToMap(q->red)].red);
01374 }
01375 if ((channel & GreenChannel) != 0)
01376 {
01377 if (black.green != white.green)
01378 q->green=RoundToQuantum(stretch_map[ScaleQuantumToMap(
01379 q->green)].green);
01380 }
01381 if ((channel & BlueChannel) != 0)
01382 {
01383 if (black.blue != white.blue)
01384 q->blue=RoundToQuantum(stretch_map[ScaleQuantumToMap(
01385 q->blue)].blue);
01386 }
01387 if ((channel & OpacityChannel) != 0)
01388 {
01389 if (black.opacity != white.opacity)
01390 q->opacity=RoundToQuantum(stretch_map[ScaleQuantumToMap(
01391 q->opacity)].opacity);
01392 }
01393 if (((channel & IndexChannel) != 0) &&
01394 (image->colorspace == CMYKColorspace))
01395 {
01396 if (black.index != white.index)
01397 indexes[x]=(IndexPacket) RoundToQuantum(stretch_map[
01398 ScaleQuantumToMap(indexes[x])].index);
01399 }
01400 q++;
01401 }
01402 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
01403 status=MagickFalse;
01404 if (image->progress_monitor != (MagickProgressMonitor) NULL)
01405 {
01406 MagickBooleanType
01407 proceed;
01408
01409 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01410 #pragma omp critical (MagickCore_ContrastStretchImageChannel)
01411 #endif
01412 proceed=SetImageProgress(image,ContrastStretchImageTag,progress++,
01413 image->rows);
01414 if (proceed == MagickFalse)
01415 status=MagickFalse;
01416 }
01417 }
01418 image_view=DestroyCacheView(image_view);
01419 stretch_map=(MagickPixelPacket *) RelinquishMagickMemory(stretch_map);
01420 return(status);
01421 }
01422
01423
01424
01425
01426
01427
01428
01429
01430
01431
01432
01433
01434
01435
01436
01437
01438
01439
01440
01441
01442
01443
01444
01445
01446
01447
01448 MagickExport Image *EnhanceImage(const Image *image,ExceptionInfo *exception)
01449 {
01450 #define Enhance(weight) \
01451 mean=((MagickRealType) r->red+pixel.red)/2; \
01452 distance=(MagickRealType) r->red-(MagickRealType) pixel.red; \
01453 distance_squared=QuantumScale*(2.0*((MagickRealType) QuantumRange+1.0)+ \
01454 mean)*distance*distance; \
01455 mean=((MagickRealType) r->green+pixel.green)/2; \
01456 distance=(MagickRealType) r->green-(MagickRealType) pixel.green; \
01457 distance_squared+=4.0*distance*distance; \
01458 mean=((MagickRealType) r->blue+pixel.blue)/2; \
01459 distance=(MagickRealType) r->blue-(MagickRealType) pixel.blue; \
01460 distance_squared+=QuantumScale*(3.0*((MagickRealType) \
01461 QuantumRange+1.0)-1.0-mean)*distance*distance; \
01462 mean=((MagickRealType) r->opacity+pixel.opacity)/2; \
01463 distance=(MagickRealType) r->opacity-(MagickRealType) pixel.opacity; \
01464 distance_squared+=QuantumScale*(3.0*((MagickRealType) \
01465 QuantumRange+1.0)-1.0-mean)*distance*distance; \
01466 if (distance_squared < ((MagickRealType) QuantumRange*(MagickRealType) \
01467 QuantumRange/25.0f)) \
01468 { \
01469 aggregate.red+=(weight)*r->red; \
01470 aggregate.green+=(weight)*r->green; \
01471 aggregate.blue+=(weight)*r->blue; \
01472 aggregate.opacity+=(weight)*r->opacity; \
01473 total_weight+=(weight); \
01474 } \
01475 r++;
01476 #define EnhanceImageTag "Enhance/Image"
01477
01478 Image
01479 *enhance_image;
01480
01481 long
01482 progress,
01483 y;
01484
01485 MagickBooleanType
01486 status;
01487
01488 MagickPixelPacket
01489 zero;
01490
01491 CacheView
01492 *enhance_view,
01493 *image_view;
01494
01495
01496
01497
01498 assert(image != (const Image *) NULL);
01499 assert(image->signature == MagickSignature);
01500 if (image->debug != MagickFalse)
01501 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01502 assert(exception != (ExceptionInfo *) NULL);
01503 assert(exception->signature == MagickSignature);
01504 if ((image->columns < 5) || (image->rows < 5))
01505 return((Image *) NULL);
01506 enhance_image=CloneImage(image,image->columns,image->rows,MagickTrue,
01507 exception);
01508 if (enhance_image == (Image *) NULL)
01509 return((Image *) NULL);
01510 if (SetImageStorageClass(enhance_image,DirectClass) == MagickFalse)
01511 {
01512 InheritException(exception,&enhance_image->exception);
01513 enhance_image=DestroyImage(enhance_image);
01514 return((Image *) NULL);
01515 }
01516
01517
01518
01519 status=MagickTrue;
01520 progress=0;
01521 (void) ResetMagickMemory(&zero,0,sizeof(zero));
01522 image_view=AcquireCacheView(image);
01523 enhance_view=AcquireCacheView(enhance_image);
01524 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01525 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
01526 #endif
01527 for (y=0; y < (long) image->rows; y++)
01528 {
01529 register const PixelPacket
01530 *__restrict p;
01531
01532 register long
01533 x;
01534
01535 register PixelPacket
01536 *__restrict q;
01537
01538
01539
01540
01541 if (status == MagickFalse)
01542 continue;
01543 p=GetCacheViewVirtualPixels(image_view,-2,y-2,image->columns+4,5,exception);
01544 q=QueueCacheViewAuthenticPixels(enhance_view,0,y,enhance_image->columns,1,
01545 exception);
01546 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
01547 {
01548 status=MagickFalse;
01549 continue;
01550 }
01551 for (x=0; x < (long) image->columns; x++)
01552 {
01553 MagickPixelPacket
01554 aggregate;
01555
01556 MagickRealType
01557 distance,
01558 distance_squared,
01559 mean,
01560 total_weight;
01561
01562 PixelPacket
01563 pixel;
01564
01565 register const PixelPacket
01566 *__restrict r;
01567
01568
01569
01570
01571 aggregate=zero;
01572 total_weight=0.0;
01573 r=p+2*(image->columns+4)+2;
01574 pixel=(*r);
01575 r=p;
01576 Enhance(5.0); Enhance(8.0); Enhance(10.0); Enhance(8.0); Enhance(5.0);
01577 r=p+(image->columns+4);
01578 Enhance(8.0); Enhance(20.0); Enhance(40.0); Enhance(20.0); Enhance(8.0);
01579 r=p+2*(image->columns+4);
01580 Enhance(10.0); Enhance(40.0); Enhance(80.0); Enhance(40.0); Enhance(10.0);
01581 r=p+3*(image->columns+4);
01582 Enhance(8.0); Enhance(20.0); Enhance(40.0); Enhance(20.0); Enhance(8.0);
01583 r=p+4*(image->columns+4);
01584 Enhance(5.0); Enhance(8.0); Enhance(10.0); Enhance(8.0); Enhance(5.0);
01585 q->red=(Quantum) ((aggregate.red+(total_weight/2)-1)/total_weight);
01586 q->green=(Quantum) ((aggregate.green+(total_weight/2)-1)/total_weight);
01587 q->blue=(Quantum) ((aggregate.blue+(total_weight/2)-1)/total_weight);
01588 q->opacity=(Quantum) ((aggregate.opacity+(total_weight/2)-1)/
01589 total_weight);
01590 p++;
01591 q++;
01592 }
01593 if (SyncCacheViewAuthenticPixels(enhance_view,exception) == MagickFalse)
01594 status=MagickFalse;
01595 if (image->progress_monitor != (MagickProgressMonitor) NULL)
01596 {
01597 MagickBooleanType
01598 proceed;
01599
01600 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01601 #pragma omp critical (MagickCore_EnhanceImage)
01602 #endif
01603 proceed=SetImageProgress(image,EnhanceImageTag,progress++,image->rows);
01604 if (proceed == MagickFalse)
01605 status=MagickFalse;
01606 }
01607 }
01608 enhance_view=DestroyCacheView(enhance_view);
01609 image_view=DestroyCacheView(image_view);
01610 return(enhance_image);
01611 }
01612
01613
01614
01615
01616
01617
01618
01619
01620
01621
01622
01623
01624
01625
01626
01627
01628
01629
01630
01631
01632
01633
01634
01635
01636
01637
01638
01639
01640 MagickExport MagickBooleanType EqualizeImage(Image *image)
01641 {
01642 return(EqualizeImageChannel(image,DefaultChannels));
01643 }
01644
01645 MagickExport MagickBooleanType EqualizeImageChannel(Image *image,
01646 const ChannelType channel)
01647 {
01648 #define EqualizeImageTag "Equalize/Image"
01649
01650 ExceptionInfo
01651 *exception;
01652
01653 long
01654 progress,
01655 y;
01656
01657 MagickBooleanType
01658 status;
01659
01660 MagickPixelPacket
01661 black,
01662 *equalize_map,
01663 *histogram,
01664 intensity,
01665 *map,
01666 white;
01667
01668 register long
01669 i;
01670
01671 CacheView
01672 *image_view;
01673
01674
01675
01676
01677 assert(image != (Image *) NULL);
01678 assert(image->signature == MagickSignature);
01679 if (image->debug != MagickFalse)
01680 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01681 equalize_map=(MagickPixelPacket *) AcquireQuantumMemory(MaxMap+1UL,
01682 sizeof(*equalize_map));
01683 histogram=(MagickPixelPacket *) AcquireQuantumMemory(MaxMap+1UL,
01684 sizeof(*histogram));
01685 map=(MagickPixelPacket *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*map));
01686 if ((equalize_map == (MagickPixelPacket *) NULL) ||
01687 (histogram == (MagickPixelPacket *) NULL) ||
01688 (map == (MagickPixelPacket *) NULL))
01689 {
01690 if (map != (MagickPixelPacket *) NULL)
01691 map=(MagickPixelPacket *) RelinquishMagickMemory(map);
01692 if (histogram != (MagickPixelPacket *) NULL)
01693 histogram=(MagickPixelPacket *) RelinquishMagickMemory(histogram);
01694 if (equalize_map != (MagickPixelPacket *) NULL)
01695 equalize_map=(MagickPixelPacket *) RelinquishMagickMemory(equalize_map);
01696 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
01697 image->filename);
01698 }
01699
01700
01701
01702 (void) ResetMagickMemory(histogram,0,(MaxMap+1)*sizeof(*histogram));
01703 exception=(&image->exception);
01704 for (y=0; y < (long) image->rows; y++)
01705 {
01706 register const IndexPacket
01707 *__restrict indexes;
01708
01709 register const PixelPacket
01710 *__restrict p;
01711
01712 register long
01713 x;
01714
01715 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
01716 if (p == (const PixelPacket *) NULL)
01717 break;
01718 indexes=GetVirtualIndexQueue(image);
01719 for (x=0; x < (long) image->columns; x++)
01720 {
01721 if ((channel & RedChannel) != 0)
01722 histogram[ScaleQuantumToMap(p->red)].red++;
01723 if ((channel & GreenChannel) != 0)
01724 histogram[ScaleQuantumToMap(p->green)].green++;
01725 if ((channel & BlueChannel) != 0)
01726 histogram[ScaleQuantumToMap(p->blue)].blue++;
01727 if ((channel & OpacityChannel) != 0)
01728 histogram[ScaleQuantumToMap(p->opacity)].opacity++;
01729 if (((channel & IndexChannel) != 0) &&
01730 (image->colorspace == CMYKColorspace))
01731 histogram[ScaleQuantumToMap(indexes[x])].index++;
01732 p++;
01733 }
01734 }
01735
01736
01737
01738 (void) ResetMagickMemory(&intensity,0,sizeof(intensity));
01739 for (i=0; i <= (long) MaxMap; i++)
01740 {
01741 if ((channel & RedChannel) != 0)
01742 intensity.red+=histogram[i].red;
01743 if ((channel & GreenChannel) != 0)
01744 intensity.green+=histogram[i].green;
01745 if ((channel & BlueChannel) != 0)
01746 intensity.blue+=histogram[i].blue;
01747 if ((channel & OpacityChannel) != 0)
01748 intensity.opacity+=histogram[i].opacity;
01749 if (((channel & IndexChannel) != 0) &&
01750 (image->colorspace == CMYKColorspace))
01751 intensity.index+=histogram[i].index;
01752 map[i]=intensity;
01753 }
01754 black=map[0];
01755 white=map[(int) MaxMap];
01756 (void) ResetMagickMemory(equalize_map,0,(MaxMap+1)*sizeof(*equalize_map));
01757 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01758 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
01759 #endif
01760 for (i=0; i <= (long) MaxMap; i++)
01761 {
01762 if (((channel & RedChannel) != 0) && (white.red != black.red))
01763 equalize_map[i].red=(MagickRealType) ScaleMapToQuantum((MagickRealType)
01764 ((MaxMap*(map[i].red-black.red))/(white.red-black.red)));
01765 if (((channel & GreenChannel) != 0) && (white.green != black.green))
01766 equalize_map[i].green=(MagickRealType) ScaleMapToQuantum((MagickRealType)
01767 ((MaxMap*(map[i].green-black.green))/(white.green-black.green)));
01768 if (((channel & BlueChannel) != 0) && (white.blue != black.blue))
01769 equalize_map[i].blue=(MagickRealType) ScaleMapToQuantum((MagickRealType)
01770 ((MaxMap*(map[i].blue-black.blue))/(white.blue-black.blue)));
01771 if (((channel & OpacityChannel) != 0) && (white.opacity != black.opacity))
01772 equalize_map[i].opacity=(MagickRealType) ScaleMapToQuantum(
01773 (MagickRealType) ((MaxMap*(map[i].opacity-black.opacity))/
01774 (white.opacity-black.opacity)));
01775 if ((((channel & IndexChannel) != 0) &&
01776 (image->colorspace == CMYKColorspace)) &&
01777 (white.index != black.index))
01778 equalize_map[i].index=(MagickRealType) ScaleMapToQuantum((MagickRealType)
01779 ((MaxMap*(map[i].index-black.index))/(white.index-black.index)));
01780 }
01781 histogram=(MagickPixelPacket *) RelinquishMagickMemory(histogram);
01782 map=(MagickPixelPacket *) RelinquishMagickMemory(map);
01783 if (image->storage_class == PseudoClass)
01784 {
01785
01786
01787
01788 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01789 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
01790 #endif
01791 for (i=0; i < (long) image->colors; i++)
01792 {
01793 if (((channel & RedChannel) != 0) && (white.red != black.red))
01794 image->colormap[i].red=RoundToQuantum(equalize_map[
01795 ScaleQuantumToMap(image->colormap[i].red)].red);
01796 if (((channel & GreenChannel) != 0) && (white.green != black.green))
01797 image->colormap[i].green=RoundToQuantum(equalize_map[
01798 ScaleQuantumToMap(image->colormap[i].green)].green);
01799 if (((channel & BlueChannel) != 0) && (white.blue != black.blue))
01800 image->colormap[i].blue=RoundToQuantum(equalize_map[
01801 ScaleQuantumToMap(image->colormap[i].blue)].blue);
01802 if (((channel & OpacityChannel) != 0) &&
01803 (white.opacity != black.opacity))
01804 image->colormap[i].opacity=RoundToQuantum(equalize_map[
01805 ScaleQuantumToMap(image->colormap[i].opacity)].opacity);
01806 }
01807 }
01808
01809
01810
01811 status=MagickTrue;
01812 progress=0;
01813 exception=(&image->exception);
01814 image_view=AcquireCacheView(image);
01815 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01816 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
01817 #endif
01818 for (y=0; y < (long) image->rows; y++)
01819 {
01820 register IndexPacket
01821 *__restrict indexes;
01822
01823 register long
01824 x;
01825
01826 register PixelPacket
01827 *__restrict q;
01828
01829 if (status == MagickFalse)
01830 continue;
01831 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
01832 if (q == (PixelPacket *) NULL)
01833 {
01834 status=MagickFalse;
01835 continue;
01836 }
01837 indexes=GetCacheViewAuthenticIndexQueue(image_view);
01838 for (x=0; x < (long) image->columns; x++)
01839 {
01840 if (((channel & RedChannel) != 0) && (white.red != black.red))
01841 q->red=RoundToQuantum(equalize_map[ScaleQuantumToMap(q->red)].red);
01842 if (((channel & GreenChannel) != 0) && (white.green != black.green))
01843 q->green=RoundToQuantum(equalize_map[ScaleQuantumToMap(
01844 q->green)].green);
01845 if (((channel & BlueChannel) != 0) && (white.blue != black.blue))
01846 q->blue=RoundToQuantum(equalize_map[ScaleQuantumToMap(q->blue)].blue);
01847 if (((channel & OpacityChannel) != 0) && (white.opacity != black.opacity))
01848 q->opacity=RoundToQuantum(equalize_map[ScaleQuantumToMap(
01849 q->opacity)].opacity);
01850 if ((((channel & IndexChannel) != 0) &&
01851 (image->colorspace == CMYKColorspace)) &&
01852 (white.index != black.index))
01853 indexes[x]=RoundToQuantum(equalize_map[ScaleQuantumToMap(
01854 indexes[x])].index);
01855 q++;
01856 }
01857 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
01858 status=MagickFalse;
01859 if (image->progress_monitor != (MagickProgressMonitor) NULL)
01860 {
01861 MagickBooleanType
01862 proceed;
01863
01864 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01865 #pragma omp critical (MagickCore_EqualizeImageChannel)
01866 #endif
01867 proceed=SetImageProgress(image,EqualizeImageTag,progress++,image->rows);
01868 if (proceed == MagickFalse)
01869 status=MagickFalse;
01870 }
01871 }
01872 image_view=DestroyCacheView(image_view);
01873 equalize_map=(MagickPixelPacket *) RelinquishMagickMemory(equalize_map);
01874 return(status);
01875 }
01876
01877
01878
01879
01880
01881
01882
01883
01884
01885
01886
01887
01888
01889
01890
01891
01892
01893
01894
01895
01896
01897
01898
01899
01900
01901
01902
01903
01904
01905
01906
01907
01908
01909
01910
01911
01912 MagickExport MagickBooleanType GammaImage(Image *image,const char *level)
01913 {
01914 GeometryInfo
01915 geometry_info;
01916
01917 MagickPixelPacket
01918 gamma;
01919
01920 MagickStatusType
01921 flags,
01922 status;
01923
01924 assert(image != (Image *) NULL);
01925 assert(image->signature == MagickSignature);
01926 if (image->debug != MagickFalse)
01927 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01928 if (level == (char *) NULL)
01929 return(MagickFalse);
01930 flags=ParseGeometry(level,&geometry_info);
01931 gamma.red=geometry_info.rho;
01932 gamma.green=geometry_info.sigma;
01933 if ((flags & SigmaValue) == 0)
01934 gamma.green=gamma.red;
01935 gamma.blue=geometry_info.xi;
01936 if ((flags & XiValue) == 0)
01937 gamma.blue=gamma.red;
01938 if ((gamma.red == 1.0) && (gamma.green == 1.0) && (gamma.blue == 1.0))
01939 return(MagickTrue);
01940 if ((gamma.red == gamma.green) && (gamma.green == gamma.blue))
01941 status=GammaImageChannel(image,(const ChannelType) (RedChannel |
01942 GreenChannel | BlueChannel),(double) gamma.red);
01943 else
01944 {
01945 status=GammaImageChannel(image,RedChannel,(double) gamma.red);
01946 status|=GammaImageChannel(image,GreenChannel,(double) gamma.green);
01947 status|=GammaImageChannel(image,BlueChannel,(double) gamma.blue);
01948 }
01949 return(status != 0 ? MagickTrue : MagickFalse);
01950 }
01951
01952 MagickExport MagickBooleanType GammaImageChannel(Image *image,
01953 const ChannelType channel,const double gamma)
01954 {
01955 #define GammaCorrectImageTag "GammaCorrect/Image"
01956
01957 ExceptionInfo
01958 *exception;
01959
01960 long
01961 progress,
01962 y;
01963
01964 MagickBooleanType
01965 status;
01966
01967 Quantum
01968 *gamma_map;
01969
01970 register long
01971 i;
01972
01973 CacheView
01974 *image_view;
01975
01976
01977
01978
01979 assert(image != (Image *) NULL);
01980 assert(image->signature == MagickSignature);
01981 if (image->debug != MagickFalse)
01982 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01983 if (gamma == 1.0)
01984 return(MagickTrue);
01985 gamma_map=(Quantum *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*gamma_map));
01986 if (gamma_map == (Quantum *) NULL)
01987 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
01988 image->filename);
01989 (void) ResetMagickMemory(gamma_map,0,(MaxMap+1)*sizeof(*gamma_map));
01990 if (gamma != 0.0)
01991 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01992 #pragma omp parallel for schedule(dynamic,4)
01993 #endif
01994 for (i=0; i <= (long) MaxMap; i++)
01995 gamma_map[i]=RoundToQuantum((MagickRealType) ScaleMapToQuantum((
01996 MagickRealType) (MaxMap*pow((double) i/MaxMap,1.0/gamma))));
01997 if (image->storage_class == PseudoClass)
01998 {
01999
02000
02001
02002 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02003 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
02004 #endif
02005 for (i=0; i < (long) image->colors; i++)
02006 {
02007 if ((channel & RedChannel) != 0)
02008 image->colormap[i].red=gamma_map[
02009 ScaleQuantumToMap(image->colormap[i].red)];
02010 if ((channel & GreenChannel) != 0)
02011 image->colormap[i].green=gamma_map[
02012 ScaleQuantumToMap(image->colormap[i].green)];
02013 if ((channel & BlueChannel) != 0)
02014 image->colormap[i].blue=gamma_map[
02015 ScaleQuantumToMap(image->colormap[i].blue)];
02016 if ((channel & OpacityChannel) != 0)
02017 {
02018 if (image->matte == MagickFalse)
02019 image->colormap[i].opacity=gamma_map[
02020 ScaleQuantumToMap(image->colormap[i].opacity)];
02021 else
02022 image->colormap[i].opacity=(Quantum) QuantumRange-
02023 gamma_map[ScaleQuantumToMap((Quantum) (QuantumRange-
02024 image->colormap[i].opacity))];
02025 }
02026 }
02027 }
02028
02029
02030
02031 status=MagickTrue;
02032 progress=0;
02033 exception=(&image->exception);
02034 image_view=AcquireCacheView(image);
02035 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02036 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
02037 #endif
02038 for (y=0; y < (long) image->rows; y++)
02039 {
02040 register IndexPacket
02041 *__restrict indexes;
02042
02043 register long
02044 x;
02045
02046 register PixelPacket
02047 *__restrict q;
02048
02049 if (status == MagickFalse)
02050 continue;
02051 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
02052 if (q == (PixelPacket *) NULL)
02053 {
02054 status=MagickFalse;
02055 continue;
02056 }
02057 indexes=GetCacheViewAuthenticIndexQueue(image_view);
02058 for (x=0; x < (long) image->columns; x++)
02059 {
02060 if (channel == DefaultChannels)
02061 {
02062 q->red=gamma_map[ScaleQuantumToMap(q->red)];
02063 q->green=gamma_map[ScaleQuantumToMap(q->green)];
02064 q->blue=gamma_map[ScaleQuantumToMap(q->blue)];
02065 }
02066 else
02067 {
02068 if ((channel & RedChannel) != 0)
02069 q->red=gamma_map[ScaleQuantumToMap(q->red)];
02070 if ((channel & GreenChannel) != 0)
02071 q->green=gamma_map[ScaleQuantumToMap(q->green)];
02072 if ((channel & BlueChannel) != 0)
02073 q->blue=gamma_map[ScaleQuantumToMap(q->blue)];
02074 if ((channel & OpacityChannel) != 0)
02075 {
02076 if (image->matte == MagickFalse)
02077 q->opacity=gamma_map[ScaleQuantumToMap(q->opacity)];
02078 else
02079 q->opacity=(Quantum) QuantumRange-gamma_map[
02080 ScaleQuantumToMap((Quantum) (QuantumRange-q->opacity))];
02081 }
02082 }
02083 q++;
02084 }
02085 if (((channel & IndexChannel) != 0) &&
02086 (image->colorspace == CMYKColorspace))
02087 for (x=0; x < (long) image->columns; x++)
02088 indexes[x]=gamma_map[ScaleQuantumToMap(indexes[x])];
02089 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
02090 status=MagickFalse;
02091 if (image->progress_monitor != (MagickProgressMonitor) NULL)
02092 {
02093 MagickBooleanType
02094 proceed;
02095
02096 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02097 #pragma omp critical (MagickCore_GammaImageChannel)
02098 #endif
02099 proceed=SetImageProgress(image,GammaCorrectImageTag,progress++,
02100 image->rows);
02101 if (proceed == MagickFalse)
02102 status=MagickFalse;
02103 }
02104 }
02105 image_view=DestroyCacheView(image_view);
02106 gamma_map=(Quantum *) RelinquishMagickMemory(gamma_map);
02107 if (image->gamma != 0.0)
02108 image->gamma*=gamma;
02109 return(status);
02110 }
02111
02112
02113
02114
02115
02116
02117
02118
02119
02120
02121
02122
02123
02124
02125
02126
02127
02128
02129
02130
02131
02132
02133
02134
02135
02136
02137
02138
02139
02140
02141
02142
02143
02144
02145 static inline size_t MagickMin(const size_t x,const size_t y)
02146 {
02147 if (x < y)
02148 return(x);
02149 return(y);
02150 }
02151
02152 MagickExport MagickBooleanType HaldClutImage(Image *image,
02153 const Image *hald_image)
02154 {
02155 return(HaldClutImageChannel(image,DefaultChannels,hald_image));
02156 }
02157
02158 MagickExport MagickBooleanType HaldClutImageChannel(Image *image,
02159 const ChannelType channel,const Image *hald_image)
02160 {
02161 #define HaldClutImageTag "Clut/Image"
02162
02163 typedef struct _HaldInfo
02164 {
02165 MagickRealType
02166 x,
02167 y,
02168 z;
02169 } HaldInfo;
02170
02171 double
02172 width;
02173
02174 ExceptionInfo
02175 *exception;
02176
02177 long
02178 progress,
02179 y;
02180
02181 MagickBooleanType
02182 status;
02183
02184 MagickPixelPacket
02185 zero;
02186
02187 ResampleFilter
02188 **resample_filter;
02189
02190 size_t
02191 cube_size,
02192 length,
02193 level;
02194
02195 CacheView
02196 *image_view;
02197
02198 assert(image != (Image *) NULL);
02199 assert(image->signature == MagickSignature);
02200 if (image->debug != MagickFalse)
02201 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
02202 assert(hald_image != (Image *) NULL);
02203 assert(hald_image->signature == MagickSignature);
02204 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
02205 return(MagickFalse);
02206 if (image->matte == MagickFalse)
02207 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
02208
02209
02210
02211 status=MagickTrue;
02212 progress=0;
02213 length=MagickMin(hald_image->columns,hald_image->rows);
02214 for (level=2; (level*level*level) < length; level++) ;
02215 level*=level;
02216 cube_size=level*level;
02217 width=(double) hald_image->columns;
02218 GetMagickPixelPacket(hald_image,&zero);
02219 exception=(&image->exception);
02220 resample_filter=AcquireResampleFilterThreadSet(hald_image,MagickTrue,
02221 exception);
02222 image_view=AcquireCacheView(image);
02223 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02224 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
02225 #endif
02226 for (y=0; y < (long) image->rows; y++)
02227 {
02228 double
02229 offset;
02230
02231 HaldInfo
02232 point;
02233
02234 MagickPixelPacket
02235 pixel,
02236 pixel1,
02237 pixel2,
02238 pixel3,
02239 pixel4;
02240
02241 register IndexPacket
02242 *__restrict indexes;
02243
02244 register long
02245 id,
02246 x;
02247
02248 register PixelPacket
02249 *__restrict q;
02250
02251 if (status == MagickFalse)
02252 continue;
02253 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
02254 if (q == (PixelPacket *) NULL)
02255 {
02256 status=MagickFalse;
02257 continue;
02258 }
02259 indexes=GetCacheViewAuthenticIndexQueue(image_view);
02260 pixel=zero;
02261 pixel1=zero;
02262 pixel2=zero;
02263 pixel3=zero;
02264 pixel4=zero;
02265 id=GetOpenMPThreadId();
02266 for (x=0; x < (long) image->columns; x++)
02267 {
02268 point.x=QuantumScale*(level-1.0)*q->red;
02269 point.y=QuantumScale*(level-1.0)*q->green;
02270 point.z=QuantumScale*(level-1.0)*q->blue;
02271 offset=point.x+level*floor(point.y)+cube_size*floor(point.z);
02272 point.x-=floor(point.x);
02273 point.y-=floor(point.y);
02274 point.z-=floor(point.z);
02275 (void) ResamplePixelColor(resample_filter[id],fmod(offset,width),
02276 floor(offset/width),&pixel1);
02277 (void) ResamplePixelColor(resample_filter[id],fmod(offset+level,width),
02278 floor((offset+level)/width),&pixel2);
02279 MagickPixelCompositeAreaBlend(&pixel1,pixel1.opacity,&pixel2,
02280 pixel2.opacity,point.y,&pixel3);
02281 offset+=cube_size;
02282 (void) ResamplePixelColor(resample_filter[id],fmod(offset,width),
02283 floor(offset/width),&pixel1);
02284 (void) ResamplePixelColor(resample_filter[id],fmod(offset+level,width),
02285 floor((offset+level)/width),&pixel2);
02286 MagickPixelCompositeAreaBlend(&pixel1,pixel1.opacity,&pixel2,
02287 pixel2.opacity,point.y,&pixel4);
02288 MagickPixelCompositeAreaBlend(&pixel3,pixel3.opacity,&pixel4,
02289 pixel4.opacity,point.z,&pixel);
02290 if ((channel & RedChannel) != 0)
02291 q->red=RoundToQuantum(pixel.red);
02292 if ((channel & GreenChannel) != 0)
02293 q->green=RoundToQuantum(pixel.green);
02294 if ((channel & BlueChannel) != 0)
02295 q->blue=RoundToQuantum(pixel.blue);
02296 if (((channel & OpacityChannel) != 0) && (image->matte != MagickFalse))
02297 q->opacity=RoundToQuantum(pixel.opacity);
02298 if (((channel & IndexChannel) != 0) &&
02299 (image->colorspace == CMYKColorspace))
02300 indexes[x]=RoundToQuantum(pixel.index);
02301 q++;
02302 }
02303 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
02304 status=MagickFalse;
02305 if (image->progress_monitor != (MagickProgressMonitor) NULL)
02306 {
02307 MagickBooleanType
02308 proceed;
02309
02310 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02311 #pragma omp critical (MagickCore_HaldClutImageChannel)
02312 #endif
02313 proceed=SetImageProgress(image,HaldClutImageTag,progress++,image->rows);
02314 if (proceed == MagickFalse)
02315 status=MagickFalse;
02316 }
02317 }
02318 image_view=DestroyCacheView(image_view);
02319 resample_filter=DestroyResampleFilterThreadSet(resample_filter);
02320 return(status);
02321 }
02322
02323
02324
02325
02326
02327
02328
02329
02330
02331
02332
02333
02334
02335
02336
02337
02338
02339
02340
02341
02342
02343
02344
02345
02346
02347
02348
02349
02350
02351
02352
02353
02354
02355
02356
02357
02358
02359
02360
02361
02362
02363
02364 MagickExport MagickBooleanType LevelImage(Image *image,const char *levels)
02365 {
02366 double
02367 black_point,
02368 gamma,
02369 white_point;
02370
02371 GeometryInfo
02372 geometry_info;
02373
02374 MagickBooleanType
02375 status;
02376
02377 MagickStatusType
02378 flags;
02379
02380
02381
02382
02383 if (levels == (char *) NULL)
02384 return(MagickFalse);
02385 flags=ParseGeometry(levels,&geometry_info);
02386 black_point=geometry_info.rho;
02387 white_point=(double) QuantumRange;
02388 if ((flags & SigmaValue) != 0)
02389 white_point=geometry_info.sigma;
02390 gamma=1.0;
02391 if ((flags & XiValue) != 0)
02392 gamma=geometry_info.xi;
02393 if ((flags & PercentValue) != 0)
02394 {
02395 black_point*=(double) image->columns*image->rows/100.0;
02396 white_point*=(double) image->columns*image->rows/100.0;
02397 }
02398 if ((flags & SigmaValue) == 0)
02399 white_point=(double) QuantumRange-black_point;
02400 if ((flags & AspectValue ) == 0)
02401 status=LevelImageChannel(image,DefaultChannels,black_point,white_point,
02402 gamma);
02403 else
02404 status=LevelizeImage(image,black_point,white_point,gamma);
02405 return(status);
02406 }
02407
02408
02409
02410
02411
02412
02413
02414
02415
02416
02417
02418
02419
02420
02421
02422
02423
02424
02425
02426
02427
02428
02429
02430
02431
02432
02433
02434
02435
02436
02437
02438
02439
02440
02441
02442
02443
02444
02445
02446
02447
02448
02449
02450
02451 MagickExport MagickBooleanType LevelizeImage(Image *image,
02452 const double black_point,const double white_point,const double gamma)
02453 {
02454 MagickBooleanType
02455 status;
02456
02457 status=LevelizeImageChannel(image,DefaultChannels,black_point,white_point,
02458 gamma);
02459 return(status);
02460 }
02461
02462 MagickExport MagickBooleanType LevelImageChannel(Image *image,
02463 const ChannelType channel,const double black_point,const double white_point,
02464 const double gamma)
02465 {
02466 #define LevelImageTag "Level/Image"
02467 #define LevelValue(x) (RoundToQuantum((MagickRealType) QuantumRange* \
02468 pow(((double) (x)-black_point)/(white_point-black_point),1.0/gamma)))
02469
02470 ExceptionInfo
02471 *exception;
02472
02473 long
02474 progress,
02475 y;
02476
02477 MagickBooleanType
02478 status;
02479
02480 register long
02481 i;
02482
02483 CacheView
02484 *image_view;
02485
02486
02487
02488
02489 assert(image != (Image *) NULL);
02490 assert(image->signature == MagickSignature);
02491 if (image->debug != MagickFalse)
02492 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
02493 if (image->storage_class == PseudoClass)
02494 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02495 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
02496 #endif
02497 for (i=0; i < (long) image->colors; i++)
02498 {
02499
02500
02501
02502 if ((channel & RedChannel) != 0)
02503 image->colormap[i].red=LevelValue(image->colormap[i].red);
02504 if ((channel & GreenChannel) != 0)
02505 image->colormap[i].green=LevelValue(image->colormap[i].green);
02506 if ((channel & BlueChannel) != 0)
02507 image->colormap[i].blue=LevelValue(image->colormap[i].blue);
02508 if ((channel & OpacityChannel) != 0)
02509 image->colormap[i].opacity=LevelValue(image->colormap[i].opacity);
02510 }
02511
02512
02513
02514 status=MagickTrue;
02515 progress=0;
02516 exception=(&image->exception);
02517 image_view=AcquireCacheView(image);
02518 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02519 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
02520 #endif
02521 for (y=0; y < (long) image->rows; y++)
02522 {
02523 register IndexPacket
02524 *__restrict indexes;
02525
02526 register long
02527 x;
02528
02529 register PixelPacket
02530 *__restrict q;
02531
02532 if (status == MagickFalse)
02533 continue;
02534 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
02535 if (q == (PixelPacket *) NULL)
02536 {
02537 status=MagickFalse;
02538 continue;
02539 }
02540 indexes=GetCacheViewAuthenticIndexQueue(image_view);
02541 for (x=0; x < (long) image->columns; x++)
02542 {
02543 if ((channel & RedChannel) != 0)
02544 q->red=LevelValue(q->red);
02545 if ((channel & GreenChannel) != 0)
02546 q->green=LevelValue(q->green);
02547 if ((channel & BlueChannel) != 0)
02548 q->blue=LevelValue(q->blue);
02549 if (((channel & OpacityChannel) != 0) &&
02550 (image->matte == MagickTrue))
02551 q->opacity=LevelValue(q->opacity);
02552 if (((channel & IndexChannel) != 0) &&
02553 (image->colorspace == CMYKColorspace))
02554 indexes[x]=LevelValue(indexes[x]);
02555 q++;
02556 }
02557 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
02558 status=MagickFalse;
02559 if (image->progress_monitor != (MagickProgressMonitor) NULL)
02560 {
02561 MagickBooleanType
02562 proceed;
02563
02564 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02565 #pragma omp critical (MagickCore_LevelImageChannel)
02566 #endif
02567 proceed=SetImageProgress(image,LevelImageTag,progress++,image->rows);
02568 if (proceed == MagickFalse)
02569 status=MagickFalse;
02570 }
02571 }
02572 image_view=DestroyCacheView(image_view);
02573 return(status);
02574 }
02575
02576
02577
02578
02579
02580
02581
02582
02583
02584
02585
02586
02587
02588
02589
02590
02591
02592
02593
02594
02595
02596
02597
02598
02599
02600
02601
02602
02603
02604
02605
02606
02607
02608
02609
02610
02611
02612
02613
02614
02615
02616
02617
02618 MagickExport MagickBooleanType LevelizeImageChannel(Image *image,
02619 const ChannelType channel,const double black_point,const double white_point,
02620 const double gamma)
02621 {
02622 #define LevelizeImageTag "Levelize/Image"
02623 #define LevelizeValue(x) (RoundToQuantum(((MagickRealType) \
02624 pow((double)(QuantumScale*(x)),1.0/gamma))*(white_point-black_point)+ \
02625 black_point))
02626
02627 ExceptionInfo
02628 *exception;
02629
02630 long
02631 progress,
02632 y;
02633
02634 MagickBooleanType
02635 status;
02636
02637 register long
02638 i;
02639
02640 CacheView
02641 *image_view;
02642
02643
02644
02645
02646 assert(image != (Image *) NULL);
02647 assert(image->signature == MagickSignature);
02648 if (image->debug != MagickFalse)
02649 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
02650 if (image->storage_class == PseudoClass)
02651 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02652 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
02653 #endif
02654 for (i=0; i < (long) image->colors; i++)
02655 {
02656
02657
02658
02659 if ((channel & RedChannel) != 0)
02660 image->colormap[i].red=LevelizeValue(image->colormap[i].red);
02661 if ((channel & GreenChannel) != 0)
02662 image->colormap[i].green=LevelizeValue(image->colormap[i].green);
02663 if ((channel & BlueChannel) != 0)
02664 image->colormap[i].blue=LevelizeValue(image->colormap[i].blue);
02665 if ((channel & OpacityChannel) != 0)
02666 image->colormap[i].opacity=LevelizeValue(image->colormap[i].opacity);
02667 }
02668
02669
02670
02671 status=MagickTrue;
02672 progress=0;
02673 exception=(&image->exception);
02674 image_view=AcquireCacheView(image);
02675 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02676 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
02677 #endif
02678 for (y=0; y < (long) image->rows; y++)
02679 {
02680 register IndexPacket
02681 *__restrict indexes;
02682
02683 register long
02684 x;
02685
02686 register PixelPacket
02687 *__restrict q;
02688
02689 if (status == MagickFalse)
02690 continue;
02691 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
02692 if (q == (PixelPacket *) NULL)
02693 {
02694 status=MagickFalse;
02695 continue;
02696 }
02697 indexes=GetCacheViewAuthenticIndexQueue(image_view);
02698 for (x=0; x < (long) image->columns; x++)
02699 {
02700 if ((channel & RedChannel) != 0)
02701 q->red=LevelizeValue(q->red);
02702 if ((channel & GreenChannel) != 0)
02703 q->green=LevelizeValue(q->green);
02704 if ((channel & BlueChannel) != 0)
02705 q->blue=LevelizeValue(q->blue);
02706 if (((channel & OpacityChannel) != 0) &&
02707 (image->matte == MagickTrue))
02708 q->opacity=LevelizeValue(q->opacity);
02709 if (((channel & IndexChannel) != 0) &&
02710 (image->colorspace == CMYKColorspace))
02711 indexes[x]=LevelizeValue(indexes[x]);
02712 q++;
02713 }
02714 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
02715 status=MagickFalse;
02716 if (image->progress_monitor != (MagickProgressMonitor) NULL)
02717 {
02718 MagickBooleanType
02719 proceed;
02720
02721 #if defined(MAGICKCORE_OPENMP_SUPPORT)
02722 #pragma omp critical (MagickCore_LevelizeImageChannel)
02723 #endif
02724 proceed=SetImageProgress(image,LevelizeImageTag,progress++,image->rows);
02725 if (proceed == MagickFalse)
02726 status=MagickFalse;
02727 }
02728 }
02729 return(status);
02730 }
02731
02732
02733
02734
02735
02736
02737
02738
02739
02740
02741
02742
02743
02744
02745
02746
02747
02748
02749
02750
02751
02752
02753
02754
02755
02756
02757
02758
02759
02760
02761
02762
02763
02764
02765
02766
02767
02768
02769
02770
02771
02772
02773
02774
02775
02776
02777 MagickExport MagickBooleanType LevelColorsImage(Image *image,
02778 const MagickPixelPacket *black_color,const MagickPixelPacket *white_color,
02779 const MagickBooleanType invert)
02780 {
02781 MagickBooleanType
02782 status;
02783
02784 status=LevelColorsImageChannel(image,DefaultChannels,black_color,white_color,
02785 invert);
02786 return(status);
02787 }
02788
02789 MagickExport MagickBooleanType LevelColorsImageChannel(Image *image,
02790 const ChannelType channel,const MagickPixelPacket *black_color,
02791 const MagickPixelPacket *white_color,const MagickBooleanType invert)
02792 {
02793 MagickStatusType
02794 status;
02795
02796
02797
02798
02799 assert(image != (Image *) NULL);
02800 assert(image->signature == MagickSignature);
02801 if (image->debug != MagickFalse)
02802 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
02803 status=MagickFalse;
02804 if (invert == MagickFalse)
02805 {
02806 if ((channel & RedChannel) != 0)
02807 status|=LevelImageChannel(image,RedChannel,
02808 black_color->red,white_color->red,(double) 1.0);
02809 if ((channel & GreenChannel) != 0)
02810 status|=LevelImageChannel(image,GreenChannel,
02811 black_color->green,white_color->green,(double) 1.0);
02812 if ((channel & BlueChannel) != 0)
02813 status|=LevelImageChannel(image,BlueChannel,
02814 black_color->blue,white_color->blue,(double) 1.0);
02815 if (((channel & OpacityChannel) != 0) &&
02816 (image->matte == MagickTrue))
02817 status|=LevelImageChannel(image,OpacityChannel,
02818 black_color->opacity,white_color->opacity,(double) 1.0);
02819 if (((channel & IndexChannel) != 0) &&
02820 (image->colorspace == CMYKColorspace))
02821 status|=LevelImageChannel(image,IndexChannel,
02822 black_color->index,white_color->index,(double) 1.0);
02823 }
02824 else
02825 {
02826 if ((channel & RedChannel) != 0)
02827 status|=LevelizeImageChannel(image,RedChannel,
02828 black_color->red,white_color->red,(double) 1.0);
02829 if ((channel & GreenChannel) != 0)
02830 status|=LevelizeImageChannel(image,GreenChannel,
02831 black_color->green,white_color->green,(double) 1.0);
02832 if ((channel & BlueChannel) != 0)
02833 status|=LevelizeImageChannel(image,BlueChannel,
02834 black_color->blue,white_color->blue,(double) 1.0);
02835 if (((channel & OpacityChannel) != 0) &&
02836 (image->matte == MagickTrue))
02837 status|=LevelizeImageChannel(image,OpacityChannel,
02838 black_color->opacity,white_color->opacity,(double) 1.0);
02839 if (((channel & IndexChannel) != 0) &&
02840 (image->colorspace == CMYKColorspace))
02841 status|=LevelizeImageChannel(image,IndexChannel,
02842 black_color->index,white_color->index,(double) 1.0);
02843 }
02844 return(status == 0 ? MagickFalse : MagickTrue);
02845 }
02846
02847
02848
02849
02850
02851
02852
02853
02854
02855
02856
02857
02858
02859
02860
02861
02862
02863
02864
02865
02866
02867
02868
02869
02870
02871
02872
02873
02874
02875 MagickExport MagickBooleanType LinearStretchImage(Image *image,
02876 const double black_point,const double white_point)
02877 {
02878 #define LinearStretchImageTag "LinearStretch/Image"
02879
02880 ExceptionInfo
02881 *exception;
02882
02883 long
02884 black,
02885 white,
02886 y;
02887
02888 MagickBooleanType
02889 status;
02890
02891 MagickRealType
02892 *histogram,
02893 intensity;
02894
02895 MagickSizeType
02896 number_pixels;
02897
02898
02899
02900
02901 assert(image != (Image *) NULL);
02902 assert(image->signature == MagickSignature);
02903 histogram=(MagickRealType *) AcquireQuantumMemory(MaxMap+1UL,
02904 sizeof(*histogram));
02905 if (histogram == (MagickRealType *) NULL)
02906 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
02907 image->filename);
02908
02909
02910
02911 (void) ResetMagickMemory(histogram,0,(MaxMap+1)*sizeof(*histogram));
02912 exception=(&image->exception);
02913 for (y=0; y < (long) image->rows; y++)
02914 {
02915 register const PixelPacket
02916 *__restrict p;
02917
02918 register long
02919 x;
02920
02921 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
02922 if (p == (const PixelPacket *) NULL)
02923 break;
02924 for (x=(long) image->columns-1; x >= 0; x--)
02925 {
02926 histogram[ScaleQuantumToMap(PixelIntensityToQuantum(p))]++;
02927 p++;
02928 }
02929 }
02930
02931
02932
02933 number_pixels=(MagickSizeType) image->columns*image->rows;
02934 intensity=0.0;
02935 for (black=0; black < (long) MaxMap; black++)
02936 {
02937 intensity+=histogram[black];
02938 if (intensity >= black_point)
02939 break;
02940 }
02941 intensity=0.0;
02942 for (white=(long) MaxMap; white != 0; white--)
02943 {
02944 intensity+=histogram[white];
02945 if (intensity >= white_point)
02946 break;
02947 }
02948 histogram=(MagickRealType *) RelinquishMagickMemory(histogram);
02949 status=LevelImageChannel(image,DefaultChannels,(double) black,(double) white,
02950 1.0);
02951 return(status);
02952 }
02953
02954
02955
02956
02957
02958
02959
02960
02961
02962
02963
02964
02965
02966
02967
02968
02969
02970
02971
02972
02973
02974
02975
02976
02977
02978
02979
02980
02981
02982
02983
02984 static void ModulateHSB(const double percent_hue,
02985 const double percent_saturation,const double percent_brightness,
02986 Quantum *red,Quantum *green,Quantum *blue)
02987 {
02988 double
02989 brightness,
02990 hue,
02991 saturation;
02992
02993
02994
02995
02996 assert(red != (Quantum *) NULL);
02997 assert(green != (Quantum *) NULL);
02998 assert(blue != (Quantum *) NULL);
02999 ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness);
03000 hue+=0.5*(0.01*percent_hue-1.0);
03001 while (hue < 0.0)
03002 hue+=1.0;
03003 while (hue > 1.0)
03004 hue-=1.0;
03005 saturation*=0.01*percent_saturation;
03006 brightness*=0.01*percent_brightness;
03007 ConvertHSBToRGB(hue,saturation,brightness,red,green,blue);
03008 }
03009
03010 static void ModulateHSL(const double percent_hue,
03011 const double percent_saturation,const double percent_lightness,
03012 Quantum *red,Quantum *green,Quantum *blue)
03013 {
03014 double
03015 hue,
03016 lightness,
03017 saturation;
03018
03019
03020
03021
03022 assert(red != (Quantum *) NULL);
03023 assert(green != (Quantum *) NULL);
03024 assert(blue != (Quantum *) NULL);
03025 ConvertRGBToHSL(*red,*green,*blue,&hue,&saturation,&lightness);
03026 hue+=0.5*(0.01*percent_hue-1.0);
03027 while (hue < 0.0)
03028 hue+=1.0;
03029 while (hue > 1.0)
03030 hue-=1.0;
03031 saturation*=0.01*percent_saturation;
03032 lightness*=0.01*percent_lightness;
03033 ConvertHSLToRGB(hue,saturation,lightness,red,green,blue);
03034 }
03035
03036 static void ModulateHWB(const double percent_hue,const double percent_whiteness, const double percent_blackness,Quantum *red,Quantum *green,Quantum *blue)
03037 {
03038 double
03039 blackness,
03040 hue,
03041 whiteness;
03042
03043
03044
03045
03046 assert(red != (Quantum *) NULL);
03047 assert(green != (Quantum *) NULL);
03048 assert(blue != (Quantum *) NULL);
03049 ConvertRGBToHWB(*red,*green,*blue,&hue,&whiteness,&blackness);
03050 hue+=0.5*(0.01*percent_hue-1.0);
03051 while (hue < 0.0)
03052 hue+=1.0;
03053 while (hue > 1.0)
03054 hue-=1.0;
03055 blackness*=0.01*percent_blackness;
03056 whiteness*=0.01*percent_whiteness;
03057 ConvertHWBToRGB(hue,whiteness,blackness,red,green,blue);
03058 }
03059
03060 MagickExport MagickBooleanType ModulateImage(Image *image,const char *modulate)
03061 {
03062 #define ModulateImageTag "Modulate/Image"
03063
03064 ColorspaceType
03065 colorspace;
03066
03067 const char
03068 *artifact;
03069
03070 double
03071 percent_brightness,
03072 percent_hue,
03073 percent_saturation;
03074
03075 ExceptionInfo
03076 *exception;
03077
03078 GeometryInfo
03079 geometry_info;
03080
03081 long
03082 progress,
03083 y;
03084
03085 MagickBooleanType
03086 status;
03087
03088 MagickStatusType
03089 flags;
03090
03091 register long
03092 i;
03093
03094 CacheView
03095 *image_view;
03096
03097
03098
03099
03100 assert(image != (Image *) NULL);
03101 assert(image->signature == MagickSignature);
03102 if (image->debug != MagickFalse)
03103 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
03104 if (modulate == (char *) NULL)
03105 return(MagickFalse);
03106 flags=ParseGeometry(modulate,&geometry_info);
03107 percent_brightness=geometry_info.rho;
03108 percent_saturation=geometry_info.sigma;
03109 if ((flags & SigmaValue) == 0)
03110 percent_saturation=100.0;
03111 percent_hue=geometry_info.xi;
03112 if ((flags & XiValue) == 0)
03113 percent_hue=100.0;
03114 colorspace=UndefinedColorspace;
03115 artifact=GetImageArtifact(image,"modulate:colorspace");
03116 if (artifact != (const char *) NULL)
03117 colorspace=(ColorspaceType) ParseMagickOption(MagickColorspaceOptions,
03118 MagickFalse,artifact);
03119 if (image->storage_class == PseudoClass)
03120 {
03121
03122
03123
03124 #if defined(MAGICKCORE_OPENMP_SUPPORT)
03125 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
03126 #endif
03127 for (i=0; i < (long) image->colors; i++)
03128 switch (colorspace)
03129 {
03130 case HSBColorspace:
03131 {
03132 ModulateHSB(percent_hue,percent_saturation,percent_brightness,
03133 &image->colormap[i].red,&image->colormap[i].green,
03134 &image->colormap[i].blue);
03135 break;
03136 }
03137 case HSLColorspace:
03138 default:
03139 {
03140 ModulateHSL(percent_hue,percent_saturation,percent_brightness,
03141 &image->colormap[i].red,&image->colormap[i].green,
03142 &image->colormap[i].blue);
03143 break;
03144 }
03145 case HWBColorspace:
03146 {
03147 ModulateHWB(percent_hue,percent_saturation,percent_brightness,
03148 &image->colormap[i].red,&image->colormap[i].green,
03149 &image->colormap[i].blue);
03150 break;
03151 }
03152 }
03153 }
03154
03155
03156
03157 status=MagickTrue;
03158 progress=0;
03159 exception=(&image->exception);
03160 image_view=AcquireCacheView(image);
03161 #if defined(MAGICKCORE_OPENMP_SUPPORT)
03162 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
03163 #endif
03164 for (y=0; y < (long) image->rows; y++)
03165 {
03166 register long
03167 x;
03168
03169 register PixelPacket
03170 *__restrict q;
03171
03172 if (status == MagickFalse)
03173 continue;
03174 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
03175 if (q == (PixelPacket *) NULL)
03176 {
03177 status=MagickFalse;
03178 continue;
03179 }
03180 for (x=0; x < (long) image->columns; x++)
03181 {
03182 switch (colorspace)
03183 {
03184 case HSBColorspace:
03185 {
03186 ModulateHSB(percent_hue,percent_saturation,percent_brightness,
03187 &q->red,&q->green,&q->blue);
03188 break;
03189 }
03190 case HSLColorspace:
03191 default:
03192 {
03193 ModulateHSL(percent_hue,percent_saturation,percent_brightness,
03194 &q->red,&q->green,&q->blue);
03195 break;
03196 }
03197 case HWBColorspace:
03198 {
03199 ModulateHWB(percent_hue,percent_saturation,percent_brightness,
03200 &q->red,&q->green,&q->blue);
03201 break;
03202 }
03203 }
03204 q++;
03205 }
03206 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
03207 status=MagickFalse;
03208 if (image->progress_monitor != (MagickProgressMonitor) NULL)
03209 {
03210 MagickBooleanType
03211 proceed;
03212
03213 #if defined(MAGICKCORE_OPENMP_SUPPORT)
03214 #pragma omp critical (MagickCore_ModulateImage)
03215 #endif
03216 proceed=SetImageProgress(image,ModulateImageTag,progress++,image->rows);
03217 if (proceed == MagickFalse)
03218 status=MagickFalse;
03219 }
03220 }
03221 image_view=DestroyCacheView(image_view);
03222 return(status);
03223 }
03224
03225
03226
03227
03228
03229
03230
03231
03232
03233
03234
03235
03236
03237
03238
03239
03240
03241
03242
03243
03244
03245
03246
03247
03248
03249
03250
03251
03252
03253
03254
03255
03256 MagickExport MagickBooleanType NegateImage(Image *image,
03257 const MagickBooleanType grayscale)
03258 {
03259 MagickBooleanType
03260 status;
03261
03262 status=NegateImageChannel(image,DefaultChannels,grayscale);
03263 return(status);
03264 }
03265
03266 MagickExport MagickBooleanType NegateImageChannel(Image *image,
03267 const ChannelType channel,const MagickBooleanType grayscale)
03268 {
03269 #define NegateImageTag "Negate/Image"
03270
03271 ExceptionInfo
03272 *exception;
03273
03274 long
03275 progress,
03276 y;
03277
03278 MagickBooleanType
03279 status;
03280
03281 register long
03282 i;
03283
03284 CacheView
03285 *image_view;
03286
03287 assert(image != (Image *) NULL);
03288 assert(image->signature == MagickSignature);
03289 if (image->debug != MagickFalse)
03290 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
03291 if (image->storage_class == PseudoClass)
03292 {
03293
03294
03295
03296 #if defined(MAGICKCORE_OPENMP_SUPPORT)
03297 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
03298 #endif
03299 for (i=0; i < (long) image->colors; i++)
03300 {
03301 if (grayscale != MagickFalse)
03302 if ((image->colormap[i].red != image->colormap[i].green) ||
03303 (image->colormap[i].green != image->colormap[i].blue))
03304 continue;
03305 if ((channel & RedChannel) != 0)
03306 image->colormap[i].red=(Quantum) QuantumRange-
03307 image->colormap[i].red;
03308 if ((channel & GreenChannel) != 0)
03309 image->colormap[i].green=(Quantum) QuantumRange-
03310 image->colormap[i].green;
03311 if ((channel & BlueChannel) != 0)
03312 image->colormap[i].blue=(Quantum) QuantumRange-
03313 image->colormap[i].blue;
03314 }
03315 }
03316
03317
03318
03319 status=MagickTrue;
03320 progress=0;
03321 exception=(&image->exception);
03322 image_view=AcquireCacheView(image);
03323 if (grayscale != MagickFalse)
03324 {
03325 #if defined(MAGICKCORE_OPENMP_SUPPORT)
03326 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
03327 #endif
03328 for (y=0; y < (long) image->rows; y++)
03329 {
03330 MagickBooleanType
03331 sync;
03332
03333 register IndexPacket
03334 *__restrict indexes;
03335
03336 register long
03337 x;
03338
03339 register PixelPacket
03340 *__restrict q;
03341
03342 if (status == MagickFalse)
03343 continue;
03344 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
03345 exception);
03346 if (q == (PixelPacket *) NULL)
03347 {
03348 status=MagickFalse;
03349 continue;
03350 }
03351 indexes=GetCacheViewAuthenticIndexQueue(image_view);
03352 for (x=0; x < (long) image->columns; x++)
03353 {
03354 if ((q->red != q->green) || (q->green != q->blue))
03355 {
03356 q++;
03357 continue;
03358 }
03359 if ((channel & RedChannel) != 0)
03360 q->red=(Quantum) QuantumRange-q->red;
03361 if ((channel & GreenChannel) != 0)
03362 q->green=(Quantum) QuantumRange-q->green;
03363 if ((channel & BlueChannel) != 0)
03364 q->blue=(Quantum) QuantumRange-q->blue;
03365 if ((channel & OpacityChannel) != 0)
03366 q->opacity=(Quantum) QuantumRange-q->opacity;
03367 if (((channel & IndexChannel) != 0) &&
03368 (image->colorspace == CMYKColorspace))
03369 indexes[x]=(IndexPacket) QuantumRange-indexes[x];
03370 q++;
03371 }
03372 sync=SyncCacheViewAuthenticPixels(image_view,exception);
03373 if (sync == MagickFalse)
03374 status=MagickFalse;
03375 if (image->progress_monitor != (MagickProgressMonitor) NULL)
03376 {
03377 MagickBooleanType
03378 proceed;
03379
03380 #if defined(MAGICKCORE_OPENMP_SUPPORT)
03381 #pragma omp critical (MagickCore_NegateImageChannel)
03382 #endif
03383 proceed=SetImageProgress(image,NegateImageTag,progress++,
03384 image->rows);
03385 if (proceed == MagickFalse)
03386 status=MagickFalse;
03387 }
03388 }
03389 image_view=DestroyCacheView(image_view);
03390 return(MagickTrue);
03391 }
03392
03393
03394
03395 #if defined(MAGICKCORE_OPENMP_SUPPORT)
03396 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
03397 #endif
03398 for (y=0; y < (long) image->rows; y++)
03399 {
03400 register IndexPacket
03401 *__restrict indexes;
03402
03403 register long
03404 x;
03405
03406 register PixelPacket
03407 *__restrict q;
03408
03409 if (status == MagickFalse)
03410 continue;
03411 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
03412 if (q == (PixelPacket *) NULL)
03413 {
03414 status=MagickFalse;
03415 continue;
03416 }
03417 indexes=GetCacheViewAuthenticIndexQueue(image_view);
03418 for (x=0; x < (long) image->columns; x++)
03419 {
03420 if ((channel & RedChannel) != 0)
03421 q->red=(Quantum) QuantumRange-q->red;
03422 if ((channel & GreenChannel) != 0)
03423 q->green=(Quantum) QuantumRange-q->green;
03424 if ((channel & BlueChannel) != 0)
03425 q->blue=(Quantum) QuantumRange-q->blue;
03426 if ((channel & OpacityChannel) != 0)
03427 q->opacity=(Quantum) QuantumRange-q->opacity;
03428 if (((channel & IndexChannel) != 0) &&
03429 (image->colorspace == CMYKColorspace))
03430 indexes[x]=(IndexPacket) QuantumRange-indexes[x];
03431 q++;
03432 }
03433 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
03434 status=MagickFalse;
03435 if (image->progress_monitor != (MagickProgressMonitor) NULL)
03436 {
03437 MagickBooleanType
03438 proceed;
03439
03440 #if defined(MAGICKCORE_OPENMP_SUPPORT)
03441 #pragma omp critical (MagickCore_NegateImageChannel)
03442 #endif
03443 proceed=SetImageProgress(image,NegateImageTag,progress++,image->rows);
03444 if (proceed == MagickFalse)
03445 status=MagickFalse;
03446 }
03447 }
03448 image_view=DestroyCacheView(image_view);
03449 return(status);
03450 }
03451
03452
03453
03454
03455
03456
03457
03458
03459
03460
03461
03462
03463
03464
03465
03466
03467
03468
03469
03470
03471
03472
03473
03474
03475
03476
03477
03478
03479
03480
03481 MagickExport MagickBooleanType NormalizeImage(Image *image)
03482 {
03483 MagickBooleanType
03484 status;
03485
03486 status=NormalizeImageChannel(image,DefaultChannels);
03487 return(status);
03488 }
03489
03490 MagickExport MagickBooleanType NormalizeImageChannel(Image *image,
03491 const ChannelType channel)
03492 {
03493 double
03494 black_point,
03495 white_point;
03496
03497 black_point=(double) image->columns*image->rows*0.02;
03498 white_point=(double) image->columns*image->rows*0.99;
03499 return(ContrastStretchImageChannel(image,channel,black_point,white_point));
03500 }
03501
03502
03503
03504
03505
03506
03507
03508
03509
03510
03511
03512
03513
03514
03515
03516
03517
03518
03519
03520
03521
03522
03523
03524
03525
03526
03527
03528
03529
03530
03531
03532
03533
03534
03535
03536
03537
03538
03539
03540
03541
03542
03543
03544 MagickExport MagickBooleanType SigmoidalContrastImage(Image *image,
03545 const MagickBooleanType sharpen,const char *levels)
03546 {
03547 GeometryInfo
03548 geometry_info;
03549
03550 MagickBooleanType
03551 status;
03552
03553 MagickStatusType
03554 flags;
03555
03556 flags=ParseGeometry(levels,&geometry_info);
03557 if ((flags & SigmaValue) == 0)
03558 geometry_info.sigma=1.0*QuantumRange/2.0;
03559 if ((flags & PercentValue) != 0)
03560 geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
03561 status=SigmoidalContrastImageChannel(image,DefaultChannels,sharpen,
03562 geometry_info.rho,geometry_info.sigma);
03563 return(status);
03564 }
03565
03566 MagickExport MagickBooleanType SigmoidalContrastImageChannel(Image *image,
03567 const ChannelType channel,const MagickBooleanType sharpen,
03568 const double contrast,const double midpoint)
03569 {
03570 #define SigmoidalContrastImageTag "SigmoidalContrast/Image"
03571
03572 ExceptionInfo
03573 *exception;
03574
03575 long
03576 progress,
03577 y;
03578
03579 MagickBooleanType
03580 status;
03581
03582 MagickRealType
03583 *sigmoidal_map;
03584
03585 register long
03586 i;
03587
03588 CacheView
03589 *image_view;
03590
03591
03592
03593
03594 assert(image != (Image *) NULL);
03595 assert(image->signature == MagickSignature);
03596 if (image->debug != MagickFalse)
03597 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
03598 sigmoidal_map=(MagickRealType *) AcquireQuantumMemory(MaxMap+1UL,
03599 sizeof(*sigmoidal_map));
03600 if (sigmoidal_map == (MagickRealType *) NULL)
03601 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
03602 image->filename);
03603 (void) ResetMagickMemory(sigmoidal_map,0,(MaxMap+1)*sizeof(*sigmoidal_map));
03604 #if defined(MAGICKCORE_OPENMP_SUPPORT)
03605 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
03606 #endif
03607 for (i=0; i <= (long) MaxMap; i++)
03608 {
03609 if (sharpen != MagickFalse)
03610 {
03611 sigmoidal_map[i]=(MagickRealType) ScaleMapToQuantum((MagickRealType)
03612 (MaxMap*((1.0/(1.0+exp(contrast*(midpoint/(double) QuantumRange-
03613 (double) i/MaxMap))))-(1.0/(1.0+exp(contrast*(midpoint/
03614 (double) QuantumRange)))))/((1.0/(1.0+exp(contrast*(midpoint/
03615 (double) QuantumRange-1.0))))-(1.0/(1.0+exp(contrast*(midpoint/
03616 (double) QuantumRange)))))+0.5));
03617 continue;
03618 }
03619 sigmoidal_map[i]=(MagickRealType) ScaleMapToQuantum((MagickRealType)
03620 (MaxMap*(QuantumScale*midpoint-log((1.0-(1.0/(1.0+exp(midpoint/
03621 (double) QuantumRange*contrast))+((double) i/MaxMap)*((1.0/
03622 (1.0+exp(contrast*(midpoint/(double) QuantumRange-1.0))))-(1.0/
03623 (1.0+exp(midpoint/(double) QuantumRange*contrast))))))/
03624 (1.0/(1.0+exp(midpoint/(double) QuantumRange*contrast))+
03625 ((double) i/MaxMap)*((1.0/(1.0+exp(contrast*(midpoint/
03626 (double) QuantumRange-1.0))))-(1.0/(1.0+exp(midpoint/
03627 (double) QuantumRange*contrast))))))/contrast)));
03628 }
03629 if (image->storage_class == PseudoClass)
03630 {
03631
03632
03633
03634 #if defined(MAGICKCORE_OPENMP_SUPPORT)
03635 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
03636 #endif
03637 for (i=0; i < (long) image->colors; i++)
03638 {
03639 if ((channel & RedChannel) != 0)
03640 image->colormap[i].red=RoundToQuantum(sigmoidal_map[
03641 ScaleQuantumToMap(image->colormap[i].red)]);
03642 if ((channel & GreenChannel) != 0)
03643 image->colormap[i].green=RoundToQuantum(sigmoidal_map[
03644 ScaleQuantumToMap(image->colormap[i].green)]);
03645 if ((channel & BlueChannel) != 0)
03646 image->colormap[i].blue=RoundToQuantum(sigmoidal_map[
03647 ScaleQuantumToMap(image->colormap[i].blue)]);
03648 if ((channel & OpacityChannel) != 0)
03649 image->colormap[i].opacity=RoundToQuantum(sigmoidal_map[
03650 ScaleQuantumToMap(image->colormap[i].opacity)]);
03651 }
03652 }
03653
03654
03655
03656 status=MagickTrue;
03657 progress=0;
03658 exception=(&image->exception);
03659 image_view=AcquireCacheView(image);
03660 #if defined(MAGICKCORE_OPENMP_SUPPORT)
03661 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
03662 #endif
03663 for (y=0; y < (long) image->rows; y++)
03664 {
03665 register IndexPacket
03666 *__restrict indexes;
03667
03668 register long
03669 x;
03670
03671 register PixelPacket
03672 *__restrict q;
03673
03674 if (status == MagickFalse)
03675 continue;
03676 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
03677 if (q == (PixelPacket *) NULL)
03678 {
03679 status=MagickFalse;
03680 continue;
03681 }
03682 indexes=GetCacheViewAuthenticIndexQueue(image_view);
03683 for (x=0; x < (long) image->columns; x++)
03684 {
03685 if ((channel & RedChannel) != 0)
03686 q->red=RoundToQuantum(sigmoidal_map[ScaleQuantumToMap(q->red)]);
03687 if ((channel & GreenChannel) != 0)
03688 q->green=RoundToQuantum(sigmoidal_map[ScaleQuantumToMap(q->green)]);
03689 if ((channel & BlueChannel) != 0)
03690 q->blue=RoundToQuantum(sigmoidal_map[ScaleQuantumToMap(q->blue)]);
03691 if ((channel & OpacityChannel) != 0)
03692 q->opacity=RoundToQuantum(sigmoidal_map[ScaleQuantumToMap(q->opacity)]);
03693 if (((channel & IndexChannel) != 0) &&
03694 (image->colorspace == CMYKColorspace))
03695 indexes[x]=(IndexPacket) RoundToQuantum(sigmoidal_map[
03696 ScaleQuantumToMap(indexes[x])]);
03697 q++;
03698 }
03699 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
03700 status=MagickFalse;
03701 if (image->progress_monitor != (MagickProgressMonitor) NULL)
03702 {
03703 MagickBooleanType
03704 proceed;
03705
03706 #if defined(MAGICKCORE_OPENMP_SUPPORT)
03707 #pragma omp critical (MagickCore_SigmoidalContrastImageChannel)
03708 #endif
03709 proceed=SetImageProgress(image,SigmoidalContrastImageTag,progress++,
03710 image->rows);
03711 if (proceed == MagickFalse)
03712 status=MagickFalse;
03713 }
03714 }
03715 image_view=DestroyCacheView(image_view);
03716 sigmoidal_map=(MagickRealType *) RelinquishMagickMemory(sigmoidal_map);
03717 return(status);
03718 }