diff options
Diffstat (limited to 'chromium/third_party/skia/src/gpu/GrDistanceFieldTextContext.cpp')
-rwxr-xr-x | chromium/third_party/skia/src/gpu/GrDistanceFieldTextContext.cpp | 380 |
1 files changed, 339 insertions, 41 deletions
diff --git a/chromium/third_party/skia/src/gpu/GrDistanceFieldTextContext.cpp b/chromium/third_party/skia/src/gpu/GrDistanceFieldTextContext.cpp index 2f67c4915a5..fe6e50fbde0 100755 --- a/chromium/third_party/skia/src/gpu/GrDistanceFieldTextContext.cpp +++ b/chromium/third_party/skia/src/gpu/GrDistanceFieldTextContext.cpp @@ -7,11 +7,19 @@ #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 "GrStrokeInfo.h" #include "GrTextStrike.h" #include "GrTextStrike_impl.h" +#include "SkDistanceFieldGen.h" +#include "SkDraw.h" +#include "SkGpuDevice.h" #include "SkPath.h" #include "SkRTConf.h" #include "SkStrokeRec.h" @@ -19,19 +27,26 @@ static const int kGlyphCoordsAttributeIndex = 1; +static const int kSmallDFFontSize = 32; +static const int kSmallDFFontLimit = 32; +static const int kMediumDFFontSize = 64; +static const int kMediumDFFontLimit = 64; +static const int kLargeDFFontSize = 128; + SK_CONF_DECLARE(bool, c_DumpFontCache, "gpu.dumpFontCache", false, "Dump the contents of the font cache before every purge."); - GrDistanceFieldTextContext::GrDistanceFieldTextContext(GrContext* context, - const GrPaint& paint, - SkColor color, - SkScalar textRatio) - : GrTextContext(context, paint) - , fTextRatio(textRatio) { - fSkPaintColor = color; - + const SkDeviceProperties& properties, + bool enable) + : GrTextContext(context, properties) { +#if SK_FORCE_DISTANCEFIELD_FONTS + fEnableDFRendering = true; +#else + fEnableDFRendering = enable; +#endif fStrike = NULL; + fGammaTexture = NULL; fCurrTexture = NULL; fCurrVertex = 0; @@ -42,6 +57,36 @@ GrDistanceFieldTextContext::GrDistanceFieldTextContext(GrContext* context, GrDistanceFieldTextContext::~GrDistanceFieldTextContext() { this->flushGlyphs(); + SkSafeSetNull(fGammaTexture); +} + +bool GrDistanceFieldTextContext::canDraw(const SkPaint& paint) { + if (!fEnableDFRendering && !paint.isDistanceFieldTextTEMP()) { + return false; + } + + // rasterizers and mask filters modify alpha, which doesn't + // translate well to distance + if (paint.getRasterizer() || paint.getMaskFilter() || + !fContext->getTextTarget()->caps()->shaderDerivativeSupport()) { + return false; + } + + // TODO: add some stroking support + if (paint.getStyle() != SkPaint::kFill_Style) { + return false; + } + + // TODO: choose an appropriate maximum scale for distance fields and + // enable perspective + if (SkDraw::ShouldDrawTextAsPaths(paint, fContext->getMatrix())) { + return false; + } + + // distance fields cannot represent color fonts + SkScalerContext::Rec rec; + SkScalerContext::MakeRec(paint, &fDeviceProperties, NULL, &rec); + return rec.getFormat() != SkMask::kARGB32_Format; } static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) { @@ -62,16 +107,34 @@ void GrDistanceFieldTextContext::flushGlyphs() { if (fCurrVertex > 0) { // setup our sampler state for our text texture/atlas - SkASSERT(GrIsALIGN4(fCurrVertex)); + SkASSERT(SkIsAlign4(fCurrVertex)); SkASSERT(fCurrTexture); GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode); + GrTextureParams gammaParams(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode); - // This effect could be stored with one of the cache objects (atlas?) - drawState->addCoverageEffect( - GrDistanceFieldTextureEffect::Create(fCurrTexture, params), - kGlyphCoordsAttributeIndex)->unref(); + // 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), + kGlyphCoordsAttributeIndex)->unref(); - if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) { if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() || kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() || fPaint.numColorStages()) { @@ -81,13 +144,28 @@ void GrDistanceFieldTextContext::flushGlyphs() { // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by // the mask texture color. The end result is that we get // mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor - int a = SkColorGetA(fSkPaintColor); + int a = SkColorGetA(fSkPaint.getColor()); // paintAlpha drawState->setColor(SkColorSetARGB(a, a, a, a)); // paintColor - drawState->setBlendConstant(skcolor_to_grcolor_nopremultiply(fSkPaintColor)); + drawState->setBlendConstant(colorNoPreMul); drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff); } else { +#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()); @@ -111,13 +189,13 @@ namespace { // position + texture coord extern const GrVertexAttrib gTextVertexAttribs[] = { {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding}, - {kVec2f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding} + {kVec2f_GrVertexAttribType, sizeof(SkPoint), kEffect_GrVertexAttribBinding} }; }; void GrDistanceFieldTextContext::drawPackedGlyph(GrGlyph::PackedID packed, - GrFixed vx, GrFixed vy, + SkFixed vx, SkFixed vy, GrFontScaler* scaler) { if (NULL == fDrawTarget) { return; @@ -153,13 +231,13 @@ void GrDistanceFieldTextContext::drawPackedGlyph(GrGlyph::PackedID packed, } */ if (NULL == glyph->fPlot) { - if (fStrike->getGlyphAtlas(glyph, scaler)) { + if (fStrike->addGlyphToAtlas(glyph, scaler)) { goto HAS_ATLAS; } // try to clear out an unused plot before we flush - fContext->getFontCache()->freePlotExceptFor(fStrike); - if (fStrike->getGlyphAtlas(glyph, scaler)) { + if (fContext->getFontCache()->freeUnusedPlot(fStrike) && + fStrike->addGlyphToAtlas(glyph, scaler)) { goto HAS_ATLAS; } @@ -173,10 +251,9 @@ void GrDistanceFieldTextContext::drawPackedGlyph(GrGlyph::PackedID packed, this->flushGlyphs(); fContext->flush(); - // try to purge - fContext->getFontCache()->purgeExceptFor(fStrike); - // need to use new flush count here - if (fStrike->getGlyphAtlas(glyph, scaler)) { + // we should have an unused plot now + if (fContext->getFontCache()->freeUnusedPlot(fStrike) && + fStrike->addGlyphToAtlas(glyph, scaler)) { goto HAS_ATLAS; } @@ -191,12 +268,13 @@ void GrDistanceFieldTextContext::drawPackedGlyph(GrGlyph::PackedID packed, } GrContext::AutoMatrix am; - SkMatrix translate; - translate.setTranslate(sx, sy); + SkMatrix ctm; + ctm.setScale(fTextRatio, fTextRatio); + ctm.postTranslate(sx, sy); GrPaint tmpPaint(fPaint); - am.setPreConcat(fContext, translate, &tmpPaint); - SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle); - fContext->drawPath(tmpPaint, *glyph->fPath, stroke); + am.setPreConcat(fContext, ctm, &tmpPaint); + GrStrokeInfo strokeInfo(SkStrokeRec::kFill_InitStyle); + fContext->drawPath(tmpPaint, *glyph->fPath, strokeInfo); return; } @@ -243,13 +321,13 @@ HAS_ATLAS: GrTCast<void**>(&fVertices), NULL); GrAlwaysAssert(success); - SkASSERT(2*sizeof(GrPoint) == fDrawTarget->getDrawState().getVertexSize()); + SkASSERT(2*sizeof(SkPoint) == fDrawTarget->getDrawState().getVertexSize()); } - SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft); - SkScalar dy = SkIntToScalar(glyph->fBounds.fTop); - SkScalar width = SkIntToScalar(glyph->fBounds.width()); - SkScalar height = SkIntToScalar(glyph->fBounds.height()); + SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset); + SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset); + SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2*SK_DistanceFieldInset); + SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2*SK_DistanceFieldInset); SkScalar scale = fTextRatio; dx *= scale; @@ -259,20 +337,240 @@ HAS_ATLAS: 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()); + SkFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX + SK_DistanceFieldInset); + SkFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY + SK_DistanceFieldInset); + SkFixed tw = SkIntToFixed(glyph->fBounds.width() - 2*SK_DistanceFieldInset); + SkFixed th = SkIntToFixed(glyph->fBounds.height() - 2*SK_DistanceFieldInset); + 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; } + +inline void GrDistanceFieldTextContext::init(const GrPaint& paint, const SkPaint& skPaint) { + GrTextContext::init(paint, skPaint); + + fStrike = NULL; + + fCurrTexture = NULL; + fCurrVertex = 0; + + fVertices = NULL; + fMaxVertices = 0; + + if (fSkPaint.getTextSize() <= kSmallDFFontLimit) { + fTextRatio = fSkPaint.getTextSize()/kSmallDFFontSize; + fSkPaint.setTextSize(SkIntToScalar(kSmallDFFontSize)); + } else if (fSkPaint.getTextSize() <= kMediumDFFontLimit) { + fTextRatio = fSkPaint.getTextSize()/kMediumDFFontSize; + fSkPaint.setTextSize(SkIntToScalar(kMediumDFFontSize)); + } else { + fTextRatio = fSkPaint.getTextSize()/kLargeDFFontSize; + fSkPaint.setTextSize(SkIntToScalar(kLargeDFFontSize)); + } + + fUseLCDText = fSkPaint.isLCDRenderText(); + + fSkPaint.setLCDRenderText(false); + fSkPaint.setAutohinted(false); + fSkPaint.setHinting(SkPaint::kNormal_Hinting); + fSkPaint.setSubpixelText(true); + +} + +inline void GrDistanceFieldTextContext::finish() { + flushGlyphs(); + + 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<uint8_t> 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) { + SkASSERT(byteLength == 0 || text != NULL); + + // nothing to draw or can't draw + if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/ + || fSkPaint.getRasterizer()) { + return; + } + + this->init(paint, skPaint); + + SkScalar sizeRatio = fTextRatio; + + SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc(); + + 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? + const char* stop = text + byteLength; + if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) { + SkFixed stopX = 0; + SkFixed stopY = 0; + + const char* textPtr = text; + while (textPtr < stop) { + // don't need x, y here, since all subpixel variants will have the + // same advance + const SkGlyph& glyph = glyphCacheProc(cache, &textPtr, 0, 0); + + stopX += glyph.fAdvanceX; + stopY += glyph.fAdvanceY; + } + SkASSERT(textPtr == stop); + + SkScalar alignX = SkFixedToScalar(stopX)*sizeRatio; + SkScalar alignY = SkFixedToScalar(stopY)*sizeRatio; + + if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) { + alignX = SkScalarHalf(alignX); + alignY = SkScalarHalf(alignY); + } + + x -= alignX; + y -= alignY; + } + + SkFixed fx = SkScalarToFixed(x); + SkFixed fy = SkScalarToFixed(y); + SkFixed fixedScale = SkScalarToFixed(sizeRatio); + while (text < stop) { + const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); + + if (glyph.fWidth) { + this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(), + glyph.getSubXFixed(), + glyph.getSubYFixed()), + fx, + fy, + fontScaler); + } + + fx += SkFixedMul_portable(glyph.fAdvanceX, fixedScale); + fy += SkFixedMul_portable(glyph.fAdvanceY, fixedScale); + } + + this->finish(); +} + +void GrDistanceFieldTextContext::drawPosText(const GrPaint& paint, const SkPaint& skPaint, + const char text[], size_t byteLength, + const SkScalar pos[], SkScalar constY, + int scalarsPerPosition) { + + SkASSERT(byteLength == 0 || text != NULL); + SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition); + + // nothing to draw + if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/) { + return; + } + + this->init(paint, skPaint); + + SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc(); + + 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; + + if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) { + while (text < stop) { + // the last 2 parameters are ignored + const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); + + if (glyph.fWidth) { + SkScalar x = pos[0]; + SkScalar y = scalarsPerPosition == 1 ? constY : pos[1]; + + this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(), + glyph.getSubXFixed(), + glyph.getSubYFixed()), + SkScalarToFixed(x), + SkScalarToFixed(y), + fontScaler); + } + pos += scalarsPerPosition; + } + } else { + int alignShift = SkPaint::kCenter_Align == fSkPaint.getTextAlign() ? 1 : 0; + while (text < stop) { + // the last 2 parameters are ignored + const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); + + if (glyph.fWidth) { + SkScalar x = pos[0]; + SkScalar y = scalarsPerPosition == 1 ? constY : pos[1]; + + this->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(), + glyph.getSubXFixed(), + glyph.getSubYFixed()), + SkScalarToFixed(x) - (glyph.fAdvanceX >> alignShift), + SkScalarToFixed(y) - (glyph.fAdvanceY >> alignShift), + fontScaler); + } + pos += scalarsPerPosition; + } + } + + this->finish(); +} |