diff options
author | 2014-02-14 21:48:29 +0000 | |
---|---|---|
committer | 2014-02-14 21:48:29 +0000 | |
commit | 6c89c34614573797ce63e429229b6f7848df0bb7 (patch) | |
tree | 2df08f36c986bc11916b79faceb9350d1cd496f2 | |
parent | 9836bc3fdddd51ed050102d03916cc5c7928dcc6 (diff) |
Improved distance field sampling
There are two different fixes here. First, it computes the distance value properly within the shader. Second, it handles anti-aliasing properly by doing a correction based on the gradient of the texture coordinates.
R=bsalomon@google.com, reed@google.com
Author: jvanverth@google.com
Review URL: https://codereview.chromium.org/149853002
git-svn-id: http://skia.googlecode.com/svn/trunk@13461 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r-- | src/gpu/GrAtlas.cpp | 4 | ||||
-rw-r--r-- | src/gpu/GrAtlas.h | 2 | ||||
-rwxr-xr-x | src/gpu/GrDistanceFieldTextContext.cpp | 12 | ||||
-rw-r--r-- | src/gpu/GrTextStrike.h | 2 | ||||
-rwxr-xr-x | src/gpu/effects/GrDistanceFieldTextureEffect.cpp | 72 | ||||
-rwxr-xr-x | src/gpu/effects/GrDistanceFieldTextureEffect.h | 11 |
6 files changed, 78 insertions, 25 deletions
diff --git a/src/gpu/GrAtlas.cpp b/src/gpu/GrAtlas.cpp index 119def1024..646c213b86 100644 --- a/src/gpu/GrAtlas.cpp +++ b/src/gpu/GrAtlas.cpp @@ -236,3 +236,7 @@ void GrAtlasMgr::freePlot(GrPlot* plot) { // GrPrintf("~GrPlot %p [%d %d] %d\n", this, plot->fOffset.fX, plot->fOffset.fY, gCounter); #endif } + +SkISize GrAtlas::getSize() const { + return SkISize::Make(GR_ATLAS_TEXTURE_WIDTH, GR_ATLAS_TEXTURE_HEIGHT); +} diff --git a/src/gpu/GrAtlas.h b/src/gpu/GrAtlas.h index 3d6869817d..e290822fe1 100644 --- a/src/gpu/GrAtlas.h +++ b/src/gpu/GrAtlas.h @@ -99,6 +99,8 @@ public: bool isEmpty() { return NULL == fPlots; } + SkISize getSize() const; + private: GrPlot* fPlots; GrAtlasMgr* fAtlasMgr; diff --git a/src/gpu/GrDistanceFieldTextContext.cpp b/src/gpu/GrDistanceFieldTextContext.cpp index bd3927c648..27323c4e60 100755 --- a/src/gpu/GrDistanceFieldTextContext.cpp +++ b/src/gpu/GrDistanceFieldTextContext.cpp @@ -8,6 +8,7 @@ #include "GrDistanceFieldTextContext.h" #include "GrAtlas.h" #include "GrDrawTarget.h" +#include "GrDrawTargetCaps.h" #include "GrFontScaler.h" #include "SkGlyphCache.h" #include "GrIndexBuffer.h" @@ -46,6 +47,7 @@ GrDistanceFieldTextContext::~GrDistanceFieldTextContext() { bool GrDistanceFieldTextContext::canDraw(const SkPaint& paint) { return !paint.getRasterizer() && !paint.getMaskFilter() && paint.getStyle() == SkPaint::kFill_Style && + fContext->getTextTarget()->caps()->shaderDerivativeSupport() && !SkDraw::ShouldDrawTextAsPaths(paint, fContext->getMatrix()); } @@ -72,8 +74,9 @@ void GrDistanceFieldTextContext::flushGlyphs() { GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode); // This effect could be stored with one of the cache objects (atlas?) + SkISize size = fStrike->getAtlasSize(); drawState->addCoverageEffect( - GrDistanceFieldTextureEffect::Create(fCurrTexture, params), + GrDistanceFieldTextureEffect::Create(fCurrTexture, params, size), kGlyphCoordsAttributeIndex)->unref(); if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) { @@ -263,22 +266,23 @@ HAS_ATLAS: sy += dy; width *= scale; height *= scale; - + GrFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX); GrFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY); GrFixed tw = SkIntToFixed(glyph->fBounds.width()); GrFixed th = SkIntToFixed(glyph->fBounds.height()); + static const size_t kVertexSize = 2 * sizeof(SkPoint); fVertices[2*fCurrVertex].setRectFan(sx, sy, sx + width, sy + height, - 2 * sizeof(SkPoint)); + kVertexSize); fVertices[2*fCurrVertex+1].setRectFan(SkFixedToFloat(texture->normalizeFixedX(tx)), SkFixedToFloat(texture->normalizeFixedY(ty)), SkFixedToFloat(texture->normalizeFixedX(tx + tw)), SkFixedToFloat(texture->normalizeFixedY(ty + th)), - 2 * sizeof(SkPoint)); + kVertexSize); fCurrVertex += 4; } diff --git a/src/gpu/GrTextStrike.h b/src/gpu/GrTextStrike.h index c5a3f65625..17dcec618c 100644 --- a/src/gpu/GrTextStrike.h +++ b/src/gpu/GrTextStrike.h @@ -39,6 +39,8 @@ public: inline GrGlyph* getGlyph(GrGlyph::PackedID, GrFontScaler*); bool getGlyphAtlas(GrGlyph*, GrFontScaler*); + SkISize getAtlasSize() const { return fAtlas.getSize(); } + // testing int countGlyphs() const { return fCache.getArray().count(); } const GrGlyph* glyphAt(int index) const { diff --git a/src/gpu/effects/GrDistanceFieldTextureEffect.cpp b/src/gpu/effects/GrDistanceFieldTextureEffect.cpp index 8c38f9bebf..88a36fdc8d 100755 --- a/src/gpu/effects/GrDistanceFieldTextureEffect.cpp +++ b/src/gpu/effects/GrDistanceFieldTextureEffect.cpp @@ -13,14 +13,17 @@ #include "GrTBackendEffectFactory.h" #include "GrTexture.h" -// The distance field is constructed as unsigned char values, so that the zero value is at 128. -// Hence our zero threshold is 128/255. +// The distance field is constructed as unsigned char values, so that the zero value is at 128, +// and the range is [-4, 4 - 1/255). Hence our multiplier is 8 - 1/32 and zero threshold is 128/255. +#define MULTIPLIER "7.96875" #define THRESHOLD "0.50196078431" class GrGLDistanceFieldTextureEffect : public GrGLVertexEffect { public: - GrGLDistanceFieldTextureEffect(const GrBackendEffectFactory& factory, const GrDrawEffect& drawEffect) - : INHERITED (factory) {} + GrGLDistanceFieldTextureEffect(const GrBackendEffectFactory& factory, + const GrDrawEffect& drawEffect) + : INHERITED (factory) + , fTextureSize(SkSize::Make(-1.f,-1.f)) {} virtual void emitCode(GrGLFullShaderBuilder* builder, const GrDrawEffect& drawEffect, @@ -31,43 +34,77 @@ public: const TextureSamplerArray& samplers) SK_OVERRIDE { SkASSERT(1 == drawEffect.castEffect<GrDistanceFieldTextureEffect>().numVertexAttribs()); + SkAssertResult(builder->enableFeature(GrGLShaderBuilder::kStandardDerivatives_GLSLFeature)); + SkString fsCoordName; - const char* vsVaryingName; - const char* fsVaryingNamePtr; - builder->addVarying(kVec2f_GrSLType, "textureCoords", &vsVaryingName, &fsVaryingNamePtr); - fsCoordName = fsVaryingNamePtr; + const char* vsCoordName; + const char* fsCoordNamePtr; + builder->addVarying(kVec2f_GrSLType, "textureCoords", &vsCoordName, &fsCoordNamePtr); + fsCoordName = fsCoordNamePtr; - const char* attrName = + const char* attrName0 = builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0])->c_str(); - builder->vsCodeAppendf("\t%s = %s;\n", vsVaryingName, attrName); + builder->vsCodeAppendf("\t%s = %s;\n", vsCoordName, attrName0); + + const char* textureSizeUniName = NULL; + fTextureSizeUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, + kVec2f_GrSLType, "TextureSize", + &textureSizeUniName); builder->fsCodeAppend("\tvec4 texColor = "); builder->fsAppendTextureLookup(samplers[0], fsCoordName.c_str(), kVec2f_GrSLType); builder->fsCodeAppend(";\n"); - builder->fsCodeAppend("\tfloat distance = texColor.r;\n"); + builder->fsCodeAppend("\tfloat distance = "MULTIPLIER"*(texColor.r - "THRESHOLD");\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. + builder->fsCodeAppendf("\tvec2 st = %s*%s;\n", fsCoordName.c_str(), textureSizeUniName); + builder->fsCodeAppend("\tvec2 Jdx = dFdx(st);\n"); + builder->fsCodeAppend("\tvec2 Jdy = dFdy(st);\n"); + builder->fsCodeAppend("\tvec2 st_grad = normalize(st);\n"); + builder->fsCodeAppend("\tvec2 grad = vec2(st_grad.x*Jdx.x + st_grad.y*Jdy.x,\n"); + builder->fsCodeAppend("\t st_grad.x*Jdx.y + st_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("\tfloat afwidth = 0.7071*length(vec2(dFdx(distance), dFdy(distance)));\n"); - builder->fsCodeAppend("\tfloat val = smoothstep("THRESHOLD"-afwidth, "THRESHOLD"+afwidth, distance);\n"); + builder->fsCodeAppend("\tfloat afwidth = 0.7071*length(grad);\n"); + builder->fsCodeAppend("\tfloat val = smoothstep(-afwidth, afwidth, distance);\n"); builder->fsCodeAppendf("\t%s = %s;\n", outputColor, (GrGLSLExpr4(inputColor) * GrGLSLExpr1("val")).c_str()); } virtual void setData(const GrGLUniformManager& uman, - const GrDrawEffect& drawEffect) SK_OVERRIDE {} + const GrDrawEffect& drawEffect) SK_OVERRIDE { + SkASSERT(fTextureSizeUni.isValid()); + const GrDistanceFieldTextureEffect& distanceFieldEffect = + drawEffect.castEffect<GrDistanceFieldTextureEffect>(); + if (distanceFieldEffect.getSize().width() != fTextureSize.width() || + distanceFieldEffect.getSize().height() != fTextureSize.height()) { + fTextureSize = distanceFieldEffect.getSize(); + uman.set2f(fTextureSizeUni, + distanceFieldEffect.getSize().width(), + distanceFieldEffect.getSize().height()); + } + } private: + GrGLUniformManager::UniformHandle fTextureSizeUni; + SkSize fTextureSize; + typedef GrGLVertexEffect INHERITED; }; /////////////////////////////////////////////////////////////////////////////// GrDistanceFieldTextureEffect::GrDistanceFieldTextureEffect(GrTexture* texture, - const GrTextureParams& params) - : fTextureAccess(texture, params) { + const GrTextureParams& params, + const SkISize& size) + : fTextureAccess(texture, params) + , fSize(SkSize::Make(SkIntToScalar(size.width()), SkIntToScalar(size.height()))) { this->addTextureAccess(&fTextureAccess); this->addVertexAttrib(kVec2f_GrSLType); } @@ -112,6 +149,7 @@ GrEffectRef* GrDistanceFieldTextureEffect::TestCreate(SkRandom* random, }; GrTextureParams params(tileModes, random->nextBool() ? GrTextureParams::kBilerp_FilterMode : GrTextureParams::kNone_FilterMode); + SkISize size = SkISize::Make(1024, 2048); - return GrDistanceFieldTextureEffect::Create(textures[texIdx], params); + return GrDistanceFieldTextureEffect::Create(textures[texIdx], params, size); } diff --git a/src/gpu/effects/GrDistanceFieldTextureEffect.h b/src/gpu/effects/GrDistanceFieldTextureEffect.h index fc37ddb99a..e754bb200b 100755 --- a/src/gpu/effects/GrDistanceFieldTextureEffect.h +++ b/src/gpu/effects/GrDistanceFieldTextureEffect.h @@ -21,27 +21,30 @@ class GrGLDistanceFieldTextureEffect; */ class GrDistanceFieldTextureEffect : public GrVertexEffect { public: - static GrEffectRef* Create(GrTexture* tex, const GrTextureParams& p) { - AutoEffectUnref effect(SkNEW_ARGS(GrDistanceFieldTextureEffect, (tex, p))); + static GrEffectRef* Create(GrTexture* tex, const GrTextureParams& p, const SkISize& s) { + AutoEffectUnref effect(SkNEW_ARGS(GrDistanceFieldTextureEffect, (tex, p, s))); return CreateEffectRef(effect); } virtual ~GrDistanceFieldTextureEffect() {} - static const char* Name() { return "Texture"; } + static const char* Name() { return "DistanceFieldTexture"; } virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE; + const SkSize& getSize() const { return fSize; } typedef GrGLDistanceFieldTextureEffect GLEffect; virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE; private: - GrDistanceFieldTextureEffect(GrTexture* texture, const GrTextureParams& params); + GrDistanceFieldTextureEffect(GrTexture* texture, const GrTextureParams& params, + const SkISize& textureSize); virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE; GrTextureAccess fTextureAccess; + SkSize fSize; GR_DECLARE_EFFECT_TEST; |