From 4d517fdbb145cb95e5e935470df331e1b6667cfc Mon Sep 17 00:00:00 2001 From: jvanverth Date: Mon, 9 Jun 2014 12:04:55 -0700 Subject: [PATCH] Gamma correction for distance field text. Handles both non-LCD and LCD text. Uses a texture to look up the gamma correction values for a given text color or luminance. BUG=skia: R=reed@google.com, bungeman@google.com, robertphillips@google.com, bsalomon@google.com Author: jvanverth@google.com Review URL: https://codereview.chromium.org/258883002 --- include/core/SkPaint.h | 4 +- src/core/SkGlyphCache.h | 65 +++++++-- src/core/SkMaskGamma.h | 16 +++ src/core/SkPaint.cpp | 42 ++++-- src/core/SkScalerContext.h | 11 ++ src/gpu/GrDistanceFieldTextContext.cpp | 91 ++++++++++-- src/gpu/GrDistanceFieldTextContext.h | 1 + src/gpu/effects/GrDistanceFieldTextureEffect.cpp | 171 +++++++++++++++++++---- src/gpu/effects/GrDistanceFieldTextureEffect.h | 40 +++++- 9 files changed, 372 insertions(+), 69 deletions(-) diff --git a/include/core/SkPaint.h b/include/core/SkPaint.h index 95fc0b8..f766ca1 100644 --- a/include/core/SkPaint.h +++ b/include/core/SkPaint.h @@ -1081,7 +1081,8 @@ private: SkScalar measure_text(SkGlyphCache*, const char* text, size_t length, int* count, SkRect* bounds) const; - SkGlyphCache* detachCache(const SkDeviceProperties* deviceProperties, const SkMatrix*) const; + SkGlyphCache* detachCache(const SkDeviceProperties* deviceProperties, const SkMatrix*, + bool ignoreGamma) const; void descriptorProc(const SkDeviceProperties* deviceProperties, const SkMatrix* deviceMatrix, void (*proc)(SkTypeface*, const SkDescriptor*, void*), @@ -1131,6 +1132,7 @@ private: } friend class SkAutoGlyphCache; + friend class SkAutoGlyphCacheNoGamma; friend class SkCanvas; friend class SkDraw; friend class SkGraphics; // So Term() can be called. diff --git a/src/core/SkGlyphCache.h b/src/core/SkGlyphCache.h index 8b404dc..d18b61a 100644 --- a/src/core/SkGlyphCache.h +++ b/src/core/SkGlyphCache.h @@ -244,37 +244,72 @@ private: friend class SkGlyphCache_Globals; }; -class SkAutoGlyphCache { +class SkAutoGlyphCacheBase { public: - SkAutoGlyphCache(SkGlyphCache* cache) : fCache(cache) {} - SkAutoGlyphCache(SkTypeface* typeface, const SkDescriptor* desc) { + SkGlyphCache* getCache() const { return fCache; } + + void release() { + if (fCache) { + SkGlyphCache::AttachCache(fCache); + fCache = NULL; + } + } + +protected: + // Hide the constructors so we can't create one of these directly. + // Create SkAutoGlyphCache or SkAutoGlyphCacheNoCache instead. + SkAutoGlyphCacheBase(SkGlyphCache* cache) : fCache(cache) {} + SkAutoGlyphCacheBase(SkTypeface* typeface, const SkDescriptor* desc) { fCache = SkGlyphCache::DetachCache(typeface, desc); } + SkAutoGlyphCacheBase(const SkPaint& paint, + const SkDeviceProperties* deviceProperties, + const SkMatrix* matrix) { + fCache = NULL; + } + SkAutoGlyphCacheBase() {} + + SkGlyphCache* fCache; + +private: + static bool DetachProc(const SkGlyphCache*, void*); +}; + +class SkAutoGlyphCache : public SkAutoGlyphCacheBase { +public: + SkAutoGlyphCache(SkGlyphCache* cache) : SkAutoGlyphCacheBase(cache) {} + SkAutoGlyphCache(SkTypeface* typeface, const SkDescriptor* desc) : + SkAutoGlyphCacheBase(typeface, desc) {} SkAutoGlyphCache(const SkPaint& paint, const SkDeviceProperties* deviceProperties, const SkMatrix* matrix) { - fCache = paint.detachCache(deviceProperties, matrix); + fCache = paint.detachCache(deviceProperties, matrix, false); } - ~SkAutoGlyphCache() { + SkAutoGlyphCache() : SkAutoGlyphCacheBase() { if (fCache) { SkGlyphCache::AttachCache(fCache); } } +}; +#define SkAutoGlyphCache(...) SK_REQUIRE_LOCAL_VAR(SkAutoGlyphCache) - SkGlyphCache* getCache() const { return fCache; } - - void release() { +class SkAutoGlyphCacheNoGamma : public SkAutoGlyphCacheBase { +public: + SkAutoGlyphCacheNoGamma(SkGlyphCache* cache) : SkAutoGlyphCacheBase(cache) {} + SkAutoGlyphCacheNoGamma(SkTypeface* typeface, const SkDescriptor* desc) : + SkAutoGlyphCacheBase(typeface, desc) {} + SkAutoGlyphCacheNoGamma(const SkPaint& paint, + const SkDeviceProperties* deviceProperties, + const SkMatrix* matrix) { + fCache = paint.detachCache(deviceProperties, matrix, true); + } + SkAutoGlyphCacheNoGamma() : SkAutoGlyphCacheBase() { if (fCache) { SkGlyphCache::AttachCache(fCache); - fCache = NULL; } } - -private: - SkGlyphCache* fCache; - - static bool DetachProc(const SkGlyphCache*, void*); }; -#define SkAutoGlyphCache(...) SK_REQUIRE_LOCAL_VAR(SkAutoGlyphCache) +#define SkAutoGlyphCacheNoGamma(...) SK_REQUIRE_LOCAL_VAR(SkAutoGlyphCacheNoGamma) + #endif diff --git a/src/core/SkMaskGamma.h b/src/core/SkMaskGamma.h index 1f2b73c..08ed97f 100644 --- a/src/core/SkMaskGamma.h +++ b/src/core/SkMaskGamma.h @@ -136,6 +136,22 @@ public: */ PreBlend preBlend(SkColor color) const; + /** + * Get dimensions for the full table set, so it can be allocated as a block. + */ + void getGammaTableDimensions(int* tableWidth, int* numTables) const { + *tableWidth = 256; + *numTables = (1 << MAX_LUM_BITS); + } + + /** + * Provides direct access to the full table set, so it can be uploaded + * into a texture. + */ + const uint8_t* getGammaTables() const { + return (const uint8_t*) fGammaTables; + } + private: static const int MAX_LUM_BITS = B_LUM_BITS > (R_LUM_BITS > G_LUM_BITS ? R_LUM_BITS : G_LUM_BITS) diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp index 78d9470..b08b2c6 100644 --- a/src/core/SkPaint.cpp +++ b/src/core/SkPaint.cpp @@ -1814,10 +1814,8 @@ void SkScalerContext::PostMakeRec(const SkPaint&, SkScalerContext::Rec* rec) { /* * ignoreGamma tells us that the caller just wants metrics that are unaffected - * by gamma correction, so we jam the luminance field to 0 (most common value - * for black text) in hopes that we get a cache hit easier. A better solution - * would be for the fontcache lookup to know to ignore the luminance field - * entirely, but not sure how to do that and keep it fast. + * by gamma correction, so we set the rec to ignore preblend: i.e. gamma = 1, + * contrast = 0, luminanceColor = transparent black. */ void SkPaint::descriptorProc(const SkDeviceProperties* deviceProperties, const SkMatrix* deviceMatrix, @@ -1827,7 +1825,7 @@ void SkPaint::descriptorProc(const SkDeviceProperties* deviceProperties, SkScalerContext::MakeRec(*this, deviceProperties, deviceMatrix, &rec); if (ignoreGamma) { - rec.setLuminanceColor(0); + rec.ignorePreBlend(); } size_t descSize = sizeof(rec); @@ -1951,9 +1949,10 @@ void SkPaint::descriptorProc(const SkDeviceProperties* deviceProperties, } SkGlyphCache* SkPaint::detachCache(const SkDeviceProperties* deviceProperties, - const SkMatrix* deviceMatrix) const { + const SkMatrix* deviceMatrix, + bool ignoreGamma) const { SkGlyphCache* cache; - this->descriptorProc(deviceProperties, deviceMatrix, DetachDescProc, &cache, false); + this->descriptorProc(deviceProperties, deviceMatrix, DetachDescProc, &cache, ignoreGamma); return cache; } @@ -1969,6 +1968,33 @@ SkMaskGamma::PreBlend SkScalerContext::GetMaskPreBlend(const SkScalerContext::Re return maskGamma.preBlend(rec.getLuminanceColor()); } +size_t SkScalerContext::GetGammaLUTSize(SkScalar contrast, SkScalar paintGamma, + SkScalar deviceGamma, int* width, int* height) { + SkAutoMutexAcquire ama(gMaskGammaCacheMutex); + const SkMaskGamma& maskGamma = cachedMaskGamma(contrast, + paintGamma, + deviceGamma); + + maskGamma.getGammaTableDimensions(width, height); + size_t size = (*width)*(*height)*sizeof(uint8_t); + + return size; +} + +void SkScalerContext::GetGammaLUTData(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma, + void* data) { + SkAutoMutexAcquire ama(gMaskGammaCacheMutex); + const SkMaskGamma& maskGamma = cachedMaskGamma(contrast, + paintGamma, + deviceGamma); + int width, height; + maskGamma.getGammaTableDimensions(&width, &height); + size_t size = width*height*sizeof(uint8_t); + const uint8_t* gammaTables = maskGamma.getGammaTables(); + memcpy(data, gammaTables, size); +} + + /////////////////////////////////////////////////////////////////////////////// #include "SkStream.h" @@ -2557,7 +2583,7 @@ SkTextToPathIter::SkTextToPathIter( const char text[], size_t length, fPaint.setPathEffect(NULL); } - fCache = fPaint.detachCache(NULL, NULL); + fCache = fPaint.detachCache(NULL, NULL, false); SkPaint::Style style = SkPaint::kFill_Style; SkPathEffect* pe = NULL; diff --git a/src/core/SkScalerContext.h b/src/core/SkScalerContext.h index f18d217..af83685 100644 --- a/src/core/SkScalerContext.h +++ b/src/core/SkScalerContext.h @@ -184,6 +184,17 @@ public: void getPath(const SkGlyph&, SkPath*); void getFontMetrics(SkPaint::FontMetrics*); + /** Return the size in bytes of the associated gamma lookup table + */ + static size_t GetGammaLUTSize(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma, + int* width, int* height); + + /** Get the associated gamma lookup table. The 'data' pointer must point to pre-allocated + memory, with size in bytes greater than or equal to the return value of getGammaLUTSize(). + */ + static void GetGammaLUTData(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma, + void* data); + #ifdef SK_BUILD_FOR_ANDROID unsigned getBaseGlyphCount(SkUnichar charCode); diff --git a/src/gpu/GrDistanceFieldTextContext.cpp b/src/gpu/GrDistanceFieldTextContext.cpp index 2d2f9a1..a729939 100755 --- a/src/gpu/GrDistanceFieldTextContext.cpp +++ b/src/gpu/GrDistanceFieldTextContext.cpp @@ -7,10 +7,12 @@ #include "GrDistanceFieldTextContext.h" #include "GrAtlas.h" +#include "SkColorFilter.h" #include "GrDrawTarget.h" #include "GrDrawTargetCaps.h" #include "GrFontScaler.h" #include "SkGlyphCache.h" +#include "GrGpu.h" #include "GrIndexBuffer.h" #include "GrTextStrike.h" #include "GrTextStrike_impl.h" @@ -43,6 +45,7 @@ GrDistanceFieldTextContext::GrDistanceFieldTextContext(GrContext* context, fEnableDFRendering = enable; #endif fStrike = NULL; + fGammaTexture = NULL; fCurrTexture = NULL; fCurrVertex = 0; @@ -53,6 +56,7 @@ GrDistanceFieldTextContext::GrDistanceFieldTextContext(GrContext* context, GrDistanceFieldTextContext::~GrDistanceFieldTextContext() { this->flushGlyphs(); + SkSafeSetNull(fGammaTexture); } bool GrDistanceFieldTextContext::canDraw(const SkPaint& paint) { @@ -107,14 +111,26 @@ void GrDistanceFieldTextContext::flushGlyphs() { SkASSERT(SkIsAlign4(fCurrVertex)); SkASSERT(fCurrTexture); GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode); + GrTextureParams gammaParams(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode); // Effects could be stored with one of the cache objects (atlas?) + SkColor filteredColor; + SkColorFilter* colorFilter = fSkPaint.getColorFilter(); + if (NULL != colorFilter) { + filteredColor = colorFilter->filterColor(fSkPaint.getColor()); + } else { + filteredColor = fSkPaint.getColor(); + } if (fUseLCDText) { + GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor); bool useBGR = SkDeviceProperties::Geometry::kBGR_Layout == fDeviceProperties.fGeometry.getLayout(); drawState->addCoverageEffect(GrDistanceFieldLCDTextureEffect::Create( fCurrTexture, params, + fGammaTexture, + gammaParams, + colorNoPreMul, fContext->getMatrix().rectStaysRect() && fContext->getMatrix().isSimilarity(), useBGR), @@ -133,13 +149,24 @@ void GrDistanceFieldTextContext::flushGlyphs() { // paintAlpha drawState->setColor(SkColorSetARGB(a, a, a, a)); // paintColor - drawState->setBlendConstant(skcolor_to_grcolor_nopremultiply(fSkPaint.getColor())); + drawState->setBlendConstant(colorNoPreMul); drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff); } else { - drawState->addCoverageEffect(GrDistanceFieldTextureEffect::Create(fCurrTexture, params, +#ifdef SK_GAMMA_APPLY_TO_A8 + U8CPU lum = SkColorSpaceLuminance::computeLuminance(fDeviceProperties.fGamma, + filteredColor); + drawState->addCoverageEffect(GrDistanceFieldTextureEffect::Create( + fCurrTexture, params, + fGammaTexture, gammaParams, + lum/255.f, fContext->getMatrix().isSimilarity()), kGlyphCoordsAttributeIndex)->unref(); - +#else + drawState->addCoverageEffect(GrDistanceFieldTextureEffect::Create( + fCurrTexture, params, + fContext->getMatrix().isSimilarity()), + kGlyphCoordsAttributeIndex)->unref(); +#endif // set back to normal in case we took LCD path previously. drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff()); drawState->setColor(fPaint.getColor()); @@ -356,7 +383,9 @@ inline void GrDistanceFieldTextContext::init(const GrPaint& paint, const SkPaint fSkPaint.setLCDRenderText(false); fSkPaint.setAutohinted(false); + fSkPaint.setHinting(SkPaint::kNormal_Hinting); fSkPaint.setSubpixelText(true); + } inline void GrDistanceFieldTextContext::finish() { @@ -365,6 +394,46 @@ inline void GrDistanceFieldTextContext::finish() { GrTextContext::finish(); } +static void setup_gamma_texture(GrContext* context, const SkGlyphCache* cache, + const SkDeviceProperties& deviceProperties, + GrTexture** gammaTexture) { + if (NULL == *gammaTexture) { + int width, height; + size_t size; + +#ifdef SK_GAMMA_CONTRAST + SkScalar contrast = SK_GAMMA_CONTRAST; +#else + SkScalar contrast = 0.5f; +#endif + SkScalar paintGamma = deviceProperties.fGamma; + SkScalar deviceGamma = deviceProperties.fGamma; + + size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma, + &width, &height); + + SkAutoTArray data((int)size); + SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get()); + + // TODO: Update this to use the cache rather than directly creating a texture. + GrTextureDesc desc; + desc.fFlags = kDynamicUpdate_GrTextureFlagBit; + desc.fWidth = width; + desc.fHeight = height; + desc.fConfig = kAlpha_8_GrPixelConfig; + + *gammaTexture = context->getGpu()->createTexture(desc, NULL, 0); + if (NULL == *gammaTexture) { + return; + } + + context->writeTexturePixels(*gammaTexture, + 0, 0, width, height, + (*gammaTexture)->config(), data.get(), 0, + GrContext::kDontFlush_PixelOpsFlag); + } +} + void GrDistanceFieldTextContext::drawText(const GrPaint& paint, const SkPaint& skPaint, const char text[], size_t byteLength, SkScalar x, SkScalar y) { @@ -382,9 +451,11 @@ void GrDistanceFieldTextContext::drawText(const GrPaint& paint, const SkPaint& s SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc(); - SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, NULL); - SkGlyphCache* cache = autoCache.getCache(); - GrFontScaler* fontScaler = GetGrFontScaler(cache); + SkAutoGlyphCacheNoGamma autoCache(fSkPaint, &fDeviceProperties, NULL); + SkGlyphCache* cache = autoCache.getCache(); + GrFontScaler* fontScaler = GetGrFontScaler(cache); + + setup_gamma_texture(fContext, cache, fDeviceProperties, &fGammaTexture); // need to measure first // TODO - generate positions and pre-load cache as well? @@ -455,9 +526,11 @@ void GrDistanceFieldTextContext::drawPosText(const GrPaint& paint, const SkPaint SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc(); - SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, NULL); - SkGlyphCache* cache = autoCache.getCache(); - GrFontScaler* fontScaler = GetGrFontScaler(cache); + SkAutoGlyphCacheNoGamma autoCache(fSkPaint, &fDeviceProperties, NULL); + SkGlyphCache* cache = autoCache.getCache(); + GrFontScaler* fontScaler = GetGrFontScaler(cache); + + setup_gamma_texture(fContext, cache, fDeviceProperties, &fGammaTexture); const char* stop = text + byteLength; diff --git a/src/gpu/GrDistanceFieldTextContext.h b/src/gpu/GrDistanceFieldTextContext.h index 3dfffd1..3a602f0 100644 --- a/src/gpu/GrDistanceFieldTextContext.h +++ b/src/gpu/GrDistanceFieldTextContext.h @@ -34,6 +34,7 @@ private: SkScalar fTextRatio; bool fUseLCDText; bool fEnableDFRendering; + GrTexture* fGammaTexture; void init(const GrPaint&, const SkPaint&); void drawPackedGlyph(GrGlyph::PackedID, SkFixed left, SkFixed top, GrFontScaler*); diff --git a/src/gpu/effects/GrDistanceFieldTextureEffect.cpp b/src/gpu/effects/GrDistanceFieldTextureEffect.cpp index 6196cce..29f0985 100755 --- a/src/gpu/effects/GrDistanceFieldTextureEffect.cpp +++ b/src/gpu/effects/GrDistanceFieldTextureEffect.cpp @@ -15,6 +15,19 @@ #include "SkDistanceFieldGen.h" +// To get optical sizes people don't complain about when we blit correctly, +// we need to slightly bold each glyph. On the Mac, we need a larger bold value. +#if defined(SK_BUILD_FOR_MAC) +#define SK_DistanceFieldLCDFactor "0.33" +#define SK_DistanceFieldNonLCDFactor "0.25" +#else +#define SK_DistanceFieldLCDFactor "0.05" +#define SK_DistanceFieldNonLCDFactor "0.05" +#endif + +// Assuming a radius of the diagonal of the fragment, hence a factor of sqrt(2)/2 +#define SK_DistanceFieldAAFactor "0.7071" + class GrGLDistanceFieldTextureEffect : public GrGLVertexEffect { public: GrGLDistanceFieldTextureEffect(const GrBackendEffectFactory& factory, @@ -56,7 +69,8 @@ public: kVec2f_GrSLType); builder->fsCodeAppend(";\n"); builder->fsCodeAppend("\tfloat distance = " - SK_DistanceFieldMultiplier "*(texColor.r - " SK_DistanceFieldThreshold ");\n"); + SK_DistanceFieldMultiplier "*(texColor.r - " SK_DistanceFieldThreshold ")" + "+ " SK_DistanceFieldNonLCDFactor ";\n"); // we adjust for the effect of the transformation on the distance by using // the length of the gradient of the texture coordinates. We use st coordinates @@ -66,8 +80,7 @@ public: builder->fsCodeAppend("\tfloat afwidth;\n"); if (dfTexEffect.isSimilarity()) { // this gives us a smooth step across approximately one fragment - // (assuming a radius of the diagonal of the fragment, hence a factor of sqrt(2)/2) - builder->fsCodeAppend("\tafwidth = 0.7071*dFdx(st.x);\n"); + builder->fsCodeAppend("\tafwidth = " SK_DistanceFieldAAFactor "*dFdx(st.x);\n"); } else { builder->fsCodeAppend("\tvec2 Jdx = dFdx(st);\n"); builder->fsCodeAppend("\tvec2 Jdy = dFdy(st);\n"); @@ -88,12 +101,25 @@ public: builder->fsCodeAppend("\t uv_grad.x*Jdx.y + uv_grad.y*Jdy.y);\n"); // this gives us a smooth step across approximately one fragment - // (assuming a radius of the diagonal of the fragment, hence a factor of sqrt(2)/2) - builder->fsCodeAppend("\tafwidth = 0.7071*length(grad);\n"); + builder->fsCodeAppend("\tafwidth = " SK_DistanceFieldAAFactor "*length(grad);\n"); } - builder->fsCodeAppend("\tfloat val = smoothstep(-afwidth, afwidth, distance);\n"); +#ifdef SK_GAMMA_APPLY_TO_A8 + // adjust based on gamma + const char* luminanceUniName = NULL; + // width, height, 1/(3*width) + fLuminanceUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, + kFloat_GrSLType, "Luminance", + &luminanceUniName); + + builder->fsCodeAppendf("\tuv = vec2(val, %s);\n", luminanceUniName); + builder->fsCodeAppend("\tvec4 gammaColor = "); + builder->fsAppendTextureLookup(samplers[1], "uv", kVec2f_GrSLType); + builder->fsCodeAppend(";\n"); + builder->fsCodeAppend("\tval = gammaColor.r;\n"); +#endif + builder->fsCodeAppendf("\t%s = %s;\n", outputColor, (GrGLSLExpr4(inputColor) * GrGLSLExpr1("val")).c_str()); } @@ -110,6 +136,15 @@ public: SkIntToScalar(fTextureSize.width()), SkIntToScalar(fTextureSize.height())); } +#ifdef SK_GAMMA_APPLY_TO_A8 + const GrDistanceFieldTextureEffect& dfTexEffect = + drawEffect.castEffect(); + float luminance = dfTexEffect.getLuminance(); + if (luminance != fLuminance) { + uman.set1f(fLuminanceUni, luminance); + fLuminance = luminance; + } +#endif } static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) { @@ -122,6 +157,8 @@ public: private: GrGLUniformManager::UniformHandle fTextureSizeUni; SkISize fTextureSize; + GrGLUniformManager::UniformHandle fLuminanceUni; + float fLuminance; typedef GrGLVertexEffect INHERITED; }; @@ -130,10 +167,22 @@ private: GrDistanceFieldTextureEffect::GrDistanceFieldTextureEffect(GrTexture* texture, const GrTextureParams& params, +#ifdef SK_GAMMA_APPLY_TO_A8 + GrTexture* gamma, + const GrTextureParams& gammaParams, + float luminance, +#endif bool similarity) : fTextureAccess(texture, params) +#ifdef SK_GAMMA_APPLY_TO_A8 + , fGammaTextureAccess(gamma, gammaParams) + , fLuminance(luminance) +#endif , fIsSimilarity(similarity) { this->addTextureAccess(&fTextureAccess); +#ifdef SK_GAMMA_APPLY_TO_A8 + this->addTextureAccess(&fGammaTextureAccess); +#endif this->addVertexAttrib(kVec2f_GrSLType); } @@ -166,6 +215,10 @@ GrEffectRef* GrDistanceFieldTextureEffect::TestCreate(SkRandom* random, GrTexture* textures[]) { int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx : GrEffectUnitTest::kAlphaTextureIdx; +#ifdef SK_GAMMA_APPLY_TO_A8 + int texIdx2 = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx : + GrEffectUnitTest::kAlphaTextureIdx; +#endif static const SkShader::TileMode kTileModes[] = { SkShader::kClamp_TileMode, SkShader::kRepeat_TileMode, @@ -177,8 +230,16 @@ GrEffectRef* GrDistanceFieldTextureEffect::TestCreate(SkRandom* random, }; GrTextureParams params(tileModes, random->nextBool() ? GrTextureParams::kBilerp_FilterMode : GrTextureParams::kNone_FilterMode); +#ifdef SK_GAMMA_APPLY_TO_A8 + GrTextureParams params2(tileModes, random->nextBool() ? GrTextureParams::kBilerp_FilterMode : + GrTextureParams::kNone_FilterMode); +#endif return GrDistanceFieldTextureEffect::Create(textures[texIdx], params, +#ifdef SK_GAMMA_APPLY_TO_A8 + textures[texIdx2], params2, + random->nextF(), +#endif random->nextBool()); } @@ -211,7 +272,7 @@ public: fsCoordName = fsCoordNamePtr; const char* attrName0 = - builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0])->c_str(); + builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0])->c_str(); builder->vsCodeAppendf("\t%s = %s;\n", vsCoordName, attrName0); const char* textureSizeUniName = NULL; @@ -237,22 +298,24 @@ public: builder->fsAppendTextureLookup(samplers[0], "uv", kVec2f_GrSLType); builder->fsCodeAppend(";\n"); builder->fsCodeAppend("\tvec3 distance;\n"); - builder->fsCodeAppend("\tdistance.y = " - SK_DistanceFieldMultiplier "*(texColor.r - " SK_DistanceFieldThreshold ");\n"); + builder->fsCodeAppend("\tdistance.y = texColor.r;\n"); // red is distance to left offset builder->fsCodeAppend("\tvec2 uv_adjusted = uv - offset;\n"); builder->fsCodeAppend("\ttexColor = "); builder->fsAppendTextureLookup(samplers[0], "uv_adjusted", kVec2f_GrSLType); builder->fsCodeAppend(";\n"); - builder->fsCodeAppend("\tdistance.x = " - SK_DistanceFieldMultiplier "*(texColor.r - " SK_DistanceFieldThreshold ");\n"); + builder->fsCodeAppend("\tdistance.x = texColor.r;\n"); // blue is distance to right offset builder->fsCodeAppend("\tuv_adjusted = uv + offset;\n"); builder->fsCodeAppend("\ttexColor = "); builder->fsAppendTextureLookup(samplers[0], "uv_adjusted", kVec2f_GrSLType); builder->fsCodeAppend(";\n"); - builder->fsCodeAppend("\tdistance.z = " - SK_DistanceFieldMultiplier "*(texColor.r - " SK_DistanceFieldThreshold ");\n"); + builder->fsCodeAppend("\tdistance.z = texColor.r;\n"); + + builder->fsCodeAppend("\tdistance = " + "vec3(" SK_DistanceFieldMultiplier ")*(distance - vec3(" SK_DistanceFieldThreshold"))" + "+ vec3(" SK_DistanceFieldLCDFactor ");\n"); + // we adjust for the effect of the transformation on the distance by using // the length of the gradient of the texture coordinates. We use st coordinates // to ensure we're mapping 1:1 from texel space to pixel space. @@ -264,8 +327,7 @@ public: builder->fsCodeAppend("\tfloat afwidth;\n"); if (dfTexEffect.isUniformScale()) { // this gives us a smooth step across approximately one fragment - // (assuming a radius of the diagonal of the fragment, hence a factor of sqrt(2)/2) - builder->fsCodeAppend("\tafwidth = 0.7071*dx;\n"); + builder->fsCodeAppend("\tafwidth = " SK_DistanceFieldAAFactor "*dx;\n"); } else { builder->fsCodeAppend("\tvec2 uv_grad;\n"); if (builder->ctxInfo().caps()->dropsTileOnZeroDivide()) { @@ -283,12 +345,36 @@ public: builder->fsCodeAppend("\t uv_grad.x*Jdx.y + uv_grad.y*Jdy.y);\n"); // this gives us a smooth step across approximately one fragment - // (assuming a radius of the diagonal of the fragment, hence a factor of sqrt(2)/2) - builder->fsCodeAppend("\tafwidth = 0.7071*length(grad);\n"); + builder->fsCodeAppend("\tafwidth = " SK_DistanceFieldAAFactor "*length(grad);\n"); } builder->fsCodeAppend("\tvec4 val = vec4(smoothstep(vec3(-afwidth), vec3(afwidth), distance), 1.0);\n"); + // adjust based on gamma + const char* textColorUniName = NULL; + // width, height, 1/(3*width) + fTextColorUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, + kVec3f_GrSLType, "TextColor", + &textColorUniName); + + builder->fsCodeAppendf("\tuv = vec2(val.x, %s.x);\n", textColorUniName); + builder->fsCodeAppend("\tvec4 gammaColor = "); + builder->fsAppendTextureLookup(samplers[1], "uv", kVec2f_GrSLType); + builder->fsCodeAppend(";\n"); + builder->fsCodeAppend("\tval.x = gammaColor.r;\n"); + + builder->fsCodeAppendf("\tuv = vec2(val.y, %s.y);\n", textColorUniName); + builder->fsCodeAppend("\tgammaColor = "); + builder->fsAppendTextureLookup(samplers[1], "uv", kVec2f_GrSLType); + builder->fsCodeAppend(";\n"); + builder->fsCodeAppend("\tval.y = gammaColor.r;\n"); + + builder->fsCodeAppendf("\tuv = vec2(val.z, %s.z);\n", textColorUniName); + builder->fsCodeAppend("\tgammaColor = "); + builder->fsAppendTextureLookup(samplers[1], "uv", kVec2f_GrSLType); + builder->fsCodeAppend(";\n"); + builder->fsCodeAppend("\tval.z = gammaColor.r;\n"); + builder->fsCodeAppendf("\t%s = %s;\n", outputColor, (GrGLSLExpr4(inputColor) * GrGLSLExpr4("val")).c_str()); } @@ -296,12 +382,13 @@ public: virtual void setData(const GrGLUniformManager& uman, const GrDrawEffect& drawEffect) SK_OVERRIDE { SkASSERT(fTextureSizeUni.isValid()); + SkASSERT(fTextColorUni.isValid()); + const GrDistanceFieldLCDTextureEffect& dfTexEffect = + drawEffect.castEffect(); GrTexture* texture = drawEffect.effect()->get()->texture(0); if (texture->width() != fTextureSize.width() || texture->height() != fTextureSize.height()) { - const GrDistanceFieldLCDTextureEffect& dfTexEffect = - drawEffect.castEffect(); fTextureSize = SkISize::Make(texture->width(), texture->height()); float delta = 1.0f/(3.0f*texture->width()); if (dfTexEffect.useBGR()) { @@ -312,40 +399,55 @@ public: SkIntToScalar(fTextureSize.height()), delta); } + + GrColor textColor = dfTexEffect.getTextColor(); + if (textColor != fTextColor) { + static const float ONE_OVER_255 = 1.f / 255.f; + uman.set3f(fTextColorUni, + GrColorUnpackR(textColor) * ONE_OVER_255, + GrColorUnpackG(textColor) * ONE_OVER_255, + GrColorUnpackB(textColor) * ONE_OVER_255); + fTextColor = textColor; + } } static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) { const GrDistanceFieldLCDTextureEffect& dfTexEffect = drawEffect.castEffect(); - int uniformScale = dfTexEffect.isUniformScale() ? 0x01 : 0x00; - int useBGR = dfTexEffect.useBGR() ? 0x10 : 0x00; - return uniformScale | useBGR; + return dfTexEffect.isUniformScale() ? 0x01 : 0x00;; } private: GrGLUniformManager::UniformHandle fTextureSizeUni; SkISize fTextureSize; + GrGLUniformManager::UniformHandle fTextColorUni; + SkColor fTextColor; typedef GrGLVertexEffect INHERITED; }; /////////////////////////////////////////////////////////////////////////////// -GrDistanceFieldLCDTextureEffect::GrDistanceFieldLCDTextureEffect(GrTexture* texture, - const GrTextureParams& params, - bool uniformScale, - bool useBGR) +GrDistanceFieldLCDTextureEffect::GrDistanceFieldLCDTextureEffect( + GrTexture* texture, const GrTextureParams& params, + GrTexture* gamma, const GrTextureParams& gParams, + SkColor textColor, + bool uniformScale, bool useBGR) : fTextureAccess(texture, params) + , fGammaTextureAccess(gamma, gParams) + , fTextColor(textColor) , fUniformScale(uniformScale) , fUseBGR(useBGR) { this->addTextureAccess(&fTextureAccess); + this->addTextureAccess(&fGammaTextureAccess); this->addVertexAttrib(kVec2f_GrSLType); } bool GrDistanceFieldLCDTextureEffect::onIsEqual(const GrEffect& other) const { - const GrDistanceFieldLCDTextureEffect& cte = CastEffect(other); - return fTextureAccess == cte.fTextureAccess; + const GrDistanceFieldLCDTextureEffect& cte = + CastEffect(other); + return (fTextureAccess == cte.fTextureAccess && fGammaTextureAccess == cte.fGammaTextureAccess); } void GrDistanceFieldLCDTextureEffect::getConstantColorComponents(GrColor* color, @@ -371,7 +473,9 @@ GrEffectRef* GrDistanceFieldLCDTextureEffect::TestCreate(SkRandom* random, const GrDrawTargetCaps&, GrTexture* textures[]) { int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx : - GrEffectUnitTest::kAlphaTextureIdx; + GrEffectUnitTest::kAlphaTextureIdx; + int texIdx2 = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx : + GrEffectUnitTest::kAlphaTextureIdx; static const SkShader::TileMode kTileModes[] = { SkShader::kClamp_TileMode, SkShader::kRepeat_TileMode, @@ -383,7 +487,14 @@ GrEffectRef* GrDistanceFieldLCDTextureEffect::TestCreate(SkRandom* random, }; GrTextureParams params(tileModes, random->nextBool() ? GrTextureParams::kBilerp_FilterMode : GrTextureParams::kNone_FilterMode); - + GrTextureParams params2(tileModes, random->nextBool() ? GrTextureParams::kBilerp_FilterMode : + GrTextureParams::kNone_FilterMode); + GrColor textColor = GrColorPackRGBA(random->nextULessThan(256), + random->nextULessThan(256), + random->nextULessThan(256), + random->nextULessThan(256)); return GrDistanceFieldLCDTextureEffect::Create(textures[texIdx], params, + textures[texIdx2], params2, + textColor, random->nextBool(), random->nextBool()); } diff --git a/src/gpu/effects/GrDistanceFieldTextureEffect.h b/src/gpu/effects/GrDistanceFieldTextureEffect.h index 937908f..692290c 100644 --- a/src/gpu/effects/GrDistanceFieldTextureEffect.h +++ b/src/gpu/effects/GrDistanceFieldTextureEffect.h @@ -18,12 +18,23 @@ class GrGLDistanceFieldLCDTextureEffect; * The output color of this effect is a modulation of the input color and a sample from a * distance field texture (using a smoothed step function near 0.5). * It allows explicit specification of the filtering and wrap modes (GrTextureParams). The input - * coords are a custom attribute. + * coords are a custom attribute. Gamma correction is handled via a texture LUT. */ class GrDistanceFieldTextureEffect : public GrVertexEffect { public: - static GrEffectRef* Create(GrTexture* tex, const GrTextureParams& params, bool similarity) { - AutoEffectUnref effect(SkNEW_ARGS(GrDistanceFieldTextureEffect, (tex, params, similarity))); +#ifdef SK_GAMMA_APPLY_TO_A8 + static GrEffectRef* Create(GrTexture* tex, const GrTextureParams& params, + GrTexture* gamma, const GrTextureParams& gammaParams, float lum, + bool similarity) { + AutoEffectUnref effect(SkNEW_ARGS(GrDistanceFieldTextureEffect, (tex, params, + gamma, gammaParams, lum, + similarity))); +#else + static GrEffectRef* Create(GrTexture* tex, const GrTextureParams& params, + bool similarity) { + AutoEffectUnref effect(SkNEW_ARGS(GrDistanceFieldTextureEffect, (tex, params, + similarity))); +#endif return CreateEffectRef(effect); } @@ -32,6 +43,9 @@ public: static const char* Name() { return "DistanceFieldTexture"; } virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE; +#ifdef SK_GAMMA_APPLY_TO_A8 + float getLuminance() const { return fLuminance; } +#endif bool isSimilarity() const { return fIsSimilarity; } typedef GrGLDistanceFieldTextureEffect GLEffect; @@ -40,11 +54,18 @@ public: private: GrDistanceFieldTextureEffect(GrTexture* texture, const GrTextureParams& params, +#ifdef SK_GAMMA_APPLY_TO_A8 + GrTexture* gamma, const GrTextureParams& gammaParams, float lum, +#endif bool uniformScale); virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE; GrTextureAccess fTextureAccess; +#ifdef SK_GAMMA_APPLY_TO_A8 + GrTextureAccess fGammaTextureAccess; + float fLuminance; +#endif bool fIsSimilarity; GR_DECLARE_EFFECT_TEST; @@ -56,14 +77,17 @@ private: * The output color of this effect is a modulation of the input color and samples from a * distance field texture (using a smoothed step function near 0.5), adjusted for LCD displays. * It allows explicit specification of the filtering and wrap modes (GrTextureParams). The input - * coords are a custom attribute. + * coords are a custom attribute. Gamma correction is handled via a texture LUT. */ class GrDistanceFieldLCDTextureEffect : public GrVertexEffect { public: static GrEffectRef* Create(GrTexture* tex, const GrTextureParams& params, + GrTexture* gamma, const GrTextureParams& gammaParams, + SkColor textColor, bool uniformScale, bool useBGR) { AutoEffectUnref effect(SkNEW_ARGS(GrDistanceFieldLCDTextureEffect, - (tex, params, uniformScale, useBGR))); + (tex, params, gamma, gammaParams, textColor, uniformScale, + useBGR))); return CreateEffectRef(effect); } @@ -72,6 +96,7 @@ public: static const char* Name() { return "DistanceFieldLCDTexture"; } virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE; + GrColor getTextColor() const { return fTextColor; } bool isUniformScale() const { return fUniformScale; } bool useBGR() const { return fUseBGR; } @@ -81,11 +106,15 @@ public: private: GrDistanceFieldLCDTextureEffect(GrTexture* texture, const GrTextureParams& params, + GrTexture* gamma, const GrTextureParams& gammaParams, + SkColor textColor, bool uniformScale, bool useBGR); virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE; GrTextureAccess fTextureAccess; + GrTextureAccess fGammaTextureAccess; + GrColor fTextColor; bool fUniformScale; bool fUseBGR; @@ -94,5 +123,4 @@ private: typedef GrVertexEffect INHERITED; }; - #endif -- 2.7.4