From 97efada074e4806479f1350ab1508939c2fdcb53 Mon Sep 17 00:00:00 2001 From: "bungeman@google.com" Date: Mon, 30 Jul 2012 20:40:50 +0000 Subject: Gamma correcting masks. https://codereview.appspot.com/6244068/ git-svn-id: http://skia.googlecode.com/svn/trunk@4841 2bbb7eff-a529-9590-31e7-b0007b416f81 --- src/core/SkPaint.cpp | 197 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 121 insertions(+), 76 deletions(-) (limited to 'src/core/SkPaint.cpp') diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp index f240737f7e..04a8f5a756 100644 --- a/src/core/SkPaint.cpp +++ b/src/core/SkPaint.cpp @@ -12,6 +12,7 @@ #include "SkFontHost.h" #include "SkImageFilter.h" #include "SkMaskFilter.h" +#include "SkMaskGamma.h" #include "SkPathEffect.h" #include "SkRasterizer.h" #include "SkShader.h" @@ -1432,7 +1433,6 @@ static bool justAColor(const SkPaint& paint, SkColor* color) { return true; } -#ifdef SK_USE_COLOR_LUMINANCE static SkColor computeLuminanceColor(const SkPaint& paint) { SkColor c; if (!justAColor(paint, &c)) { @@ -1443,53 +1443,6 @@ static SkColor computeLuminanceColor(const SkPaint& paint) { #define assert_byte(x) SkASSERT(0 == ((x) >> 8)) -static U8CPU reduce_lumbits(U8CPU x) { - static const uint8_t gReduceBits[] = { - 0x0, 0x55, 0xAA, 0xFF - }; - assert_byte(x); - return gReduceBits[x >> 6]; -} - -static unsigned computeLuminance(SkColor c) { - int r = SkColorGetR(c); - int g = SkColorGetG(c); - int b = SkColorGetB(c); - // compute luminance - // R=0.2126 G=0.7152 B=0.0722 - // scaling by 127 yields 27, 92, 9 - int luminance = r * 27 + g * 92 + b * 9; - luminance >>= 7; - assert_byte(luminance); - return luminance; -} - -#else -// returns 0..kLuminance_Max -static unsigned computeLuminance(const SkPaint& paint) { - SkColor c; - if (justAColor(paint, &c)) { - int r = SkColorGetR(c); - int g = SkColorGetG(c); - int b = SkColorGetB(c); - // compute luminance - // R=0.2126 G=0.7152 B=0.0722 - // scaling by 127 yields 27, 92, 9 -#if 1 - int luminance = r * 27 + g * 92 + b * 9; - luminance >>= 15 - SkScalerContext::kLuminance_Bits; -#else - int luminance = r * 2 + g * 5 + b * 1; - luminance >>= 11 - SkScalerContext::kLuminance_Bits; -#endif - SkASSERT(luminance <= SkScalerContext::kLuminance_Max); - return luminance; - } - // if we're not a single color, return the middle of the luminance range - return SkScalerContext::kLuminance_Max >> 1; -} -#endif - // Beyond this size, LCD doesn't appreciably improve quality, but it always // cost more RAM and draws slower, so we set a cap. #ifndef SK_MAX_SIZE_FOR_LCDTEXT @@ -1518,6 +1471,20 @@ static SkScalar sk_relax(SkScalar x) { #endif } +//#define SK_GAMMA_SRGB +#ifndef SK_GAMMA_CONTRAST + /** + * A value of 0.5 for SK_GAMMA_CONTRAST appears to be a good compromise. + * With lower values small text appears washed out (though correctly so). + * With higher values lcd fringing is worse and the smoothing effect of + * partial coverage is diminished. + */ + #define SK_GAMMA_CONTRAST (0.5f) +#endif +#ifndef SK_GAMMA_EXPONENT + #define SK_GAMMA_EXPONENT (2.2f) +#endif + void SkScalerContext::MakeRec(const SkPaint& paint, const SkMatrix* deviceMatrix, Rec* rec) { SkASSERT(deviceMatrix == NULL || !deviceMatrix->hasPerspective()); @@ -1619,12 +1586,17 @@ void SkScalerContext::MakeRec(const SkPaint& paint, // these modify fFlags, so do them after assigning fFlags rec->setHinting(computeHinting(paint)); -#ifdef SK_USE_COLOR_LUMINANCE + rec->setLuminanceColor(computeLuminanceColor(paint)); +#ifdef SK_GAMMA_SRGB + rec->setDeviceGamma(0); + rec->setPaintGamma(0); #else - rec->setLuminanceBits(computeLuminance(paint)); + rec->setDeviceGamma(SkFloatToScalar(SK_GAMMA_EXPONENT)); + rec->setPaintGamma(SkFloatToScalar(SK_GAMMA_EXPONENT)); #endif - + rec->setContrast(SkFloatToScalar(SK_GAMMA_CONTRAST)); + /* Allow the fonthost to modify our rec before we use it as a key into the cache. This way if we're asking for something that they will ignore, they can modify our rec up front, so we don't create duplicate cache @@ -1636,46 +1608,111 @@ void SkScalerContext::MakeRec(const SkPaint& paint, } /** - * We ensure that the rec is self-consistent and efficient (where possible) + * In order to call cachedDeviceLuminance, cachedPaintLuminance, or + * cachedMaskGamma the caller must hold the gMaskGammaCacheMutex and continue + * to hold it until the returned pointer is refed or forgotten. */ -void SkScalerContext::PostMakeRec(SkScalerContext::Rec* rec) { +SK_DECLARE_STATIC_MUTEX(gMaskGammaCacheMutex); +/** + * The caller must hold the gMaskGammaCacheMutex and continue to hold it until + * the returned SkColorSpaceLuminance pointer is refed or forgotten. + */ +static SkColorSpaceLuminance* cachedDeviceLuminance(SkScalar gammaExponent) { + static SkColorSpaceLuminance* gDeviceLuminance = NULL; + static SkScalar gGammaExponent = SK_ScalarMin; + if (gGammaExponent != gammaExponent) { + if (0 == gammaExponent) { + gDeviceLuminance = SkNEW(SkSRGBLuminance); + } else { + gDeviceLuminance = SkNEW_ARGS(SkGammaLuminance, (gammaExponent)); + } + gGammaExponent = gammaExponent; + } + return gDeviceLuminance; +} + +/** + * The caller must hold the gMaskGammaCacheMutex and continue to hold it until + * the returned SkColorSpaceLuminance pointer is refed or forgotten. + */ +static SkColorSpaceLuminance* cachedPaintLuminance(SkScalar gammaExponent) { + static SkColorSpaceLuminance* gPaintLuminance = NULL; + static SkScalar gGammaExponent = SK_ScalarMin; + if (gGammaExponent != gammaExponent) { + if (0 == gammaExponent) { + gPaintLuminance = SkNEW(SkSRGBLuminance); + } else { + gPaintLuminance = SkNEW_ARGS(SkGammaLuminance, (gammaExponent)); + } + gGammaExponent = gammaExponent; + } + return gPaintLuminance; +} + +/** + * The caller must hold the gMaskGammaCacheMutex and continue to hold it until + * the returned SkMaskGamma pointer is refed or forgotten. + */ +static SkMaskGamma* cachedMaskGamma(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma) { + static SkMaskGamma* gMaskGamma = NULL; + static SkScalar gContrast = SK_ScalarMin; + static SkScalar gPaintGamma = SK_ScalarMin; + static SkScalar gDeviceGamma = SK_ScalarMin; + if (gContrast != contrast || gPaintGamma != paintGamma || gDeviceGamma != deviceGamma) { + SkSafeUnref(gMaskGamma); + SkColorSpaceLuminance* paintLuminance = cachedPaintLuminance(paintGamma); + SkColorSpaceLuminance* deviceLuminance = cachedDeviceLuminance(deviceGamma); + gMaskGamma = SkNEW_ARGS(SkMaskGamma, (contrast, *paintLuminance, *deviceLuminance)); + gContrast = contrast; + gPaintGamma = paintGamma; + gDeviceGamma = deviceGamma; + } + return gMaskGamma; +} + +/** + * We ensure that the rec is self-consistent and efficient (where possible) + */ +void SkScalerContext::PostMakeRec(const SkPaint& paint, SkScalerContext::Rec* rec) { /** * If we're asking for A8, we force the colorlum to be gray, since that - * that limits the number of unique entries, and the scaler will only - * look at the lum of one of them. + * limits the number of unique entries, and the scaler will only look at + * the lum of one of them. */ switch (rec->fMaskFormat) { case SkMask::kLCD16_Format: case SkMask::kLCD32_Format: { -#ifdef SK_USE_COLOR_LUMINANCE // filter down the luminance color to a finite number of bits - SkColor c = rec->getLuminanceColor(); - c = SkColorSetRGB(reduce_lumbits(SkColorGetR(c)), - reduce_lumbits(SkColorGetG(c)), - reduce_lumbits(SkColorGetB(c))); - rec->setLuminanceColor(c); -#endif + SkColor color = rec->getLuminanceColor(); + SkAutoMutexAcquire ama(gMaskGammaCacheMutex); + SkMaskGamma* maskGamma = cachedMaskGamma(rec->getContrast(), + rec->getPaintGamma(), + rec->getDeviceGamma()); + rec->setLuminanceColor(maskGamma->cannonicalColor(color)); break; } case SkMask::kA8_Format: { -#ifdef SK_USE_COLOR_LUMINANCE // filter down the luminance to a single component, since A8 can't // use per-component information - unsigned lum = computeLuminance(rec->getLuminanceColor()); + + SkColor color = rec->getLuminanceColor(); + SkAutoMutexAcquire ama(gMaskGammaCacheMutex); + U8CPU lum = cachedPaintLuminance(rec->getPaintGamma())->computeLuminance(color); + // HACK: Prevents green from being pre-blended as white. + lum -= ((255 - lum) * lum) / 255; + // reduce to our finite number of bits - lum = reduce_lumbits(lum); - rec->setLuminanceColor(SkColorSetRGB(lum, lum, lum)); -#endif + SkMaskGamma* maskGamma = cachedMaskGamma(rec->getContrast(), + rec->getPaintGamma(), + rec->getDeviceGamma()); + color = SkColorSetRGB(lum, lum, lum); + rec->setLuminanceColor(maskGamma->cannonicalColor(color)); break; } case SkMask::kBW_Format: // No need to differentiate gamma if we're BW -#ifdef SK_USE_COLOR_LUMINANCE rec->setLuminanceColor(0); -#else - rec->setLuminanceBits(0); -#endif break; } } @@ -1700,11 +1737,7 @@ void SkPaint::descriptorProc(const SkMatrix* deviceMatrix, SkScalerContext::MakeRec(*this, deviceMatrix, &rec); if (ignoreGamma) { -#ifdef SK_USE_COLOR_LUMINANCE rec.setLuminanceColor(0); -#else - rec.setLuminanceBits(0); -#endif } size_t descSize = sizeof(rec); @@ -1739,7 +1772,7 @@ void SkPaint::descriptorProc(const SkMatrix* deviceMatrix, /////////////////////////////////////////////////////////////////////////// // Now that we're done tweaking the rec, call the PostMakeRec cleanup - SkScalerContext::PostMakeRec(&rec); + SkScalerContext::PostMakeRec(*this, &rec); descSize += SkDescriptor::ComputeOverhead(entryCount); @@ -1814,6 +1847,18 @@ SkGlyphCache* SkPaint::detachCache(const SkMatrix* deviceMatrix) const { return cache; } +/** + * Expands fDeviceGamma, fPaintGamma, fContrast, and fLumBits into a mask pre-blend. + */ +//static +SkMaskGamma::PreBlend SkScalerContext::GetMaskPreBlend(const SkScalerContext::Rec& rec) { + SkAutoMutexAcquire ama(gMaskGammaCacheMutex); + SkMaskGamma* maskGamma = cachedMaskGamma(rec.getContrast(), + rec.getPaintGamma(), + rec.getDeviceGamma()); + return maskGamma->preBlend(rec.getLuminanceColor()); +} + /////////////////////////////////////////////////////////////////////////////// #include "SkStream.h" -- cgit v1.2.3