From dcb8ef9e866b2674e8f9a70b761fcc4fec87dbbe Mon Sep 17 00:00:00 2001 From: "commit-bot@chromium.org" Date: Thu, 27 Mar 2014 11:26:10 +0000 Subject: [PATCH] Implement text rendering with NVPR Use path rendering to render the text from outlines if supported by the GPU. Implement this in GrStencilAndCoverTextContext by copying large chunks of code from GrBitmapTextContext (drawText) and GrDistanceFieldTextContext (drawPosText). The drawing is implemented with "instanced" path drawing functions. Moves the creation of the "main" text context from SkGpuDevice to the GrContext::createTextContext. This is done because the decision of which text renderer is optimal can be made only with the internal implementation-specific information of the context. R=jvanverth@google.com, bsalomon@google.com Author: kkinnunen@nvidia.com Review URL: https://codereview.chromium.org/196133014 git-svn-id: http://skia.googlecode.com/svn/trunk@13962 2bbb7eff-a529-9590-31e7-b0007b416f81 --- gyp/gpu.gypi | 2 + include/core/SkPaint.h | 1 + include/gpu/GrContext.h | 11 ++ src/gpu/GrContext.cpp | 21 ++- src/gpu/GrStencilAndCoverTextContext.cpp | 289 +++++++++++++++++++++++++++++++ src/gpu/GrStencilAndCoverTextContext.h | 52 ++++++ src/gpu/SkGpuDevice.cpp | 12 +- 7 files changed, 378 insertions(+), 10 deletions(-) create mode 100644 src/gpu/GrStencilAndCoverTextContext.cpp create mode 100644 src/gpu/GrStencilAndCoverTextContext.h diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi index b92ad5b..ebb3d0a 100644 --- a/gyp/gpu.gypi +++ b/gyp/gpu.gypi @@ -110,6 +110,8 @@ '<(skia_src_path)/gpu/GrStencil.h', '<(skia_src_path)/gpu/GrStencilAndCoverPathRenderer.cpp', '<(skia_src_path)/gpu/GrStencilAndCoverPathRenderer.h', + '<(skia_src_path)/gpu/GrStencilAndCoverTextContext.cpp', + '<(skia_src_path)/gpu/GrStencilAndCoverTextContext.h', '<(skia_src_path)/gpu/GrStencilBuffer.cpp', '<(skia_src_path)/gpu/GrStencilBuffer.h', '<(skia_src_path)/gpu/GrTBSearch.h', diff --git a/include/core/SkPaint.h b/include/core/SkPaint.h index 7a647f0..38b77b9 100644 --- a/include/core/SkPaint.h +++ b/include/core/SkPaint.h @@ -1144,6 +1144,7 @@ private: friend class SkPDFDevice; friend class GrBitmapTextContext; friend class GrDistanceFieldTextContext; + friend class GrStencilAndCoverTextContext; friend class SkTextToPathIter; friend class SkCanonicalizePaint; diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h index 5f96457..b44c985 100644 --- a/include/gpu/GrContext.h +++ b/include/gpu/GrContext.h @@ -35,6 +35,7 @@ class GrResourceEntry; class GrResourceCache; class GrStencilBuffer; class GrTestTarget; +class GrTextContext; class GrTextureParams; class GrVertexBuffer; class GrVertexBufferAllocPool; @@ -116,6 +117,15 @@ public: */ int getGpuTextureCacheResourceCount() const; + /** + * Creates a new text rendering context that is optimal for the + * render target and the context. Caller assumes the ownership + * of the returned object. The returned object must be deleted + * before the context is destroyed. + */ + GrTextContext* createTextContext(GrRenderTarget*, + const SkDeviceProperties&); + /////////////////////////////////////////////////////////////////////////// // Textures @@ -954,6 +964,7 @@ private: // addExistingTextureToCache friend class GrTexture; friend class GrStencilAndCoverPathRenderer; + friend class GrStencilAndCoverTextContext; // Add an existing texture to the texture cache. This is intended solely // for use with textures released from an GrAutoScratchTexture. diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp index 3a2bbe8..0876b4d 100644 --- a/src/gpu/GrContext.cpp +++ b/src/gpu/GrContext.cpp @@ -14,24 +14,26 @@ #include "GrAARectRenderer.h" #include "GrBufferAllocPool.h" -#include "GrGpu.h" +#include "GrDistanceFieldTextContext.h" #include "GrDrawTargetCaps.h" -#include "GrIndexBuffer.h" +#include "GrGpu.h" #include "GrInOrderDrawBuffer.h" +#include "GrIndexBuffer.h" #include "GrOvalRenderer.h" #include "GrPathRenderer.h" #include "GrPathUtils.h" #include "GrResourceCache.h" #include "GrSoftwarePathRenderer.h" +#include "GrStencilAndCoverTextContext.h" #include "GrStencilBuffer.h" #include "GrTextStrike.h" #include "GrTracing.h" #include "SkGr.h" -#include "SkRTConf.h" #include "SkRRect.h" +#include "SkRTConf.h" #include "SkStrokeRec.h" -#include "SkTLazy.h" #include "SkTLS.h" +#include "SkTLazy.h" #include "SkTraceEvent.h" // It can be useful to set this to false to test whether a bug is caused by using the @@ -227,6 +229,17 @@ int GrContext::getGpuTextureCacheResourceCount() const { return fTextureCache->getCachedResourceCount(); } +GrTextContext* GrContext::createTextContext(GrRenderTarget* renderTarget, + const SkDeviceProperties& + leakyProperties) { + if (fGpu->caps()->pathRenderingSupport()) { + if (renderTarget->getStencilBuffer() && renderTarget->isMultisampled()) { + return SkNEW_ARGS(GrStencilAndCoverTextContext, (this, leakyProperties)); + } + } + return SkNEW_ARGS(GrDistanceFieldTextContext, (this, leakyProperties)); +} + //////////////////////////////////////////////////////////////////////////////// GrTexture* GrContext::findAndRefTexture(const GrTextureDesc& desc, diff --git a/src/gpu/GrStencilAndCoverTextContext.cpp b/src/gpu/GrStencilAndCoverTextContext.cpp new file mode 100644 index 0000000..8b9abac --- /dev/null +++ b/src/gpu/GrStencilAndCoverTextContext.cpp @@ -0,0 +1,289 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrStencilAndCoverTextContext.h" +#include "GrDrawTarget.h" +#include "GrFontScaler.h" +#include "GrGpu.h" +#include "GrPath.h" +#include "GrTextStrike.h" +#include "GrTextStrike_impl.h" +#include "SkAutoKern.h" +#include "SkDraw.h" +#include "SkGlyphCache.h" +#include "SkGpuDevice.h" +#include "SkPath.h" + +static const int kBaseSCFontSize = 64; +static const int kMaxReservedGlyphs = 64; + +GrStencilAndCoverTextContext::GrStencilAndCoverTextContext( + GrContext* context, const SkDeviceProperties& properties) + : GrTextContext(context, properties) + , fStroke(SkStrokeRec::kFill_InitStyle) { +} + +GrStencilAndCoverTextContext::~GrStencilAndCoverTextContext() { +} + +void GrStencilAndCoverTextContext::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 + if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) { + return; + } + + this->init(paint, skPaint, byteLength); + + + SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc(); + SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, NULL); + SkGlyphCache* cache = autoCache.getCache(); + GrFontScaler* scaler = GetGrFontScaler(cache); + GrTextStrike* strike = + fContext->getFontCache()->getStrike(scaler, true); + + const char* stop = text + byteLength; + + // Measure first if needed. + 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) * fTextRatio; + SkScalar alignY = SkFixedToScalar(stopY) * fTextRatio; + + if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) { + alignX = SkScalarHalf(alignX); + alignY = SkScalarHalf(alignY); + } + + x -= alignX; + y -= alignY; + } + + SkAutoKern autokern; + + SkFixed fx = SkScalarToFixed(x) + SK_FixedHalf; + SkFixed fy = SkScalarToFixed(y) + SK_FixedHalf; + SkFixed fixedSizeRatio = SkScalarToFixed(fTextRatio); + + while (text < stop) { + const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); + + fx += SkFixedMul_portable(autokern.adjust(glyph), fixedSizeRatio); + + if (glyph.fWidth) { + this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(), + glyph.getSubXFixed(), + glyph.getSubYFixed()), + SkPoint::Make(SkFixedToScalar(fx), + SkFixedToScalar(fy)), + strike, + scaler); + } + + fx += SkFixedMul_portable(glyph.fAdvanceX, fixedSizeRatio); + fy += SkFixedMul_portable(glyph.fAdvanceY, fixedSizeRatio); + } + + this->finish(); +} + +void GrStencilAndCoverTextContext::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/* || fRC->isEmpty()*/) { + return; + } + + this->init(paint, skPaint, byteLength); + + SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc(); + + SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, NULL); + SkGlyphCache* cache = autoCache.getCache(); + GrFontScaler* scaler = GetGrFontScaler(cache); + GrTextStrike* strike = + fContext->getFontCache()->getStrike(scaler, true); + + const char* stop = text + byteLength; + + SkTDArray paths; + SkTDArray transforms; + + 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->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(), + glyph.getSubXFixed(), + glyph.getSubYFixed()), + SkPoint::Make(x, y), + strike, + scaler); + } + + 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]; + x -= SkFixedToScalar((glyph.fAdvanceX >> alignShift)); + y -= SkFixedToScalar((glyph.fAdvanceY >> alignShift)); + + this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(), + glyph.getSubXFixed(), + glyph.getSubYFixed()), + SkPoint::Make(x, y), + strike, + scaler); + } + pos += scalarsPerPosition; + } + } + + this->finish(); +} + +bool GrStencilAndCoverTextContext::canDraw(const SkPaint& paint) { + return !paint.getRasterizer() + && !paint.getMaskFilter() + && (paint.getStyle() == SkPaint::kFill_Style + || paint.getStrokeWidth() > 0); + +} + +static bool has_thick_frame(const SkPaint& paint) { + return paint.getStrokeWidth() > 0 && + paint.getStyle() != SkPaint::kFill_Style; +} + +void GrStencilAndCoverTextContext::init(const GrPaint& paint, + const SkPaint& skPaint, + size_t textByteLength) { + GrTextContext::init(paint, skPaint); + fTextRatio = fSkPaint.getTextSize() / kBaseSCFontSize; + fSkPaint.setTextSize(SkIntToScalar(kBaseSCFontSize)); + fSkPaint.setLCDRenderText(false); + fSkPaint.setAutohinted(false); + fSkPaint.setSubpixelText(true); + if (has_thick_frame(fSkPaint)) { + // Compensate the glyphs being scaled up by fTextRatio, by scaling the + // stroke down. + fSkPaint.setStrokeWidth(fSkPaint.getStrokeWidth() / fTextRatio); + } + fStroke = SkStrokeRec(fSkPaint); + + // Make glyph cache produce paths geometry for fill. We will stroke them + // by passing fStroke to drawPath. + fSkPaint.setStyle(SkPaint::kFill_Style); + + fStateRestore.set(fDrawTarget->drawState()); + + fDrawTarget->drawState()->setFromPaint(fPaint, fContext->getMatrix(), + fContext->getRenderTarget()); + + GR_STATIC_CONST_SAME_STENCIL(kStencilPass, + kZero_StencilOp, + kZero_StencilOp, + kNotEqual_StencilFunc, + 0xffff, + 0x0000, + 0xffff); + + *fDrawTarget->drawState()->stencil() = kStencilPass; + + size_t reserveAmount; + switch (skPaint.getTextEncoding()) { + default: + SkASSERT(false); + case SkPaint::kUTF8_TextEncoding: + reserveAmount = textByteLength; + break; + case SkPaint::kUTF16_TextEncoding: + reserveAmount = textByteLength / 2; + break; + case SkPaint::kUTF32_TextEncoding: + case SkPaint::kGlyphID_TextEncoding: + reserveAmount = textByteLength / 4; + break; + } + fPaths.setReserve(reserveAmount); + fTransforms.setReserve(reserveAmount); +} + +inline void GrStencilAndCoverTextContext::appendGlyph(GrGlyph::PackedID glyphID, + const SkPoint& pos, + GrTextStrike* strike, + GrFontScaler* scaler) { + GrGlyph* glyph = strike->getGlyph(glyphID, scaler); + + if (scaler->getGlyphPath(glyph->glyphID(), &fTmpPath)) { + *fPaths.append() = fContext->createPath(fTmpPath, fStroke); + SkMatrix* t = fTransforms.append(); + t->setTranslate(pos.fX, pos.fY); + t->preScale(fTextRatio, fTextRatio); + } +} + +void GrStencilAndCoverTextContext::finish() { + if (fPaths.count() > 0) { + fDrawTarget->drawPaths(static_cast(fPaths.count()), + fPaths.begin(), fTransforms.begin(), + SkPath::kWinding_FillType, fStroke.getStyle()); + for (int i = 0; i < fPaths.count(); ++i) { + fPaths[i]->unref(); + } + if (fPaths.count() > kMaxReservedGlyphs) { + fPaths.reset(); + fTransforms.reset(); + } else { + fPaths.rewind(); + fTransforms.rewind(); + } + } + fTmpPath.reset(); + + fDrawTarget->drawState()->stencil()->setDisabled(); + fStateRestore.set(NULL); + GrTextContext::finish(); +} + diff --git a/src/gpu/GrStencilAndCoverTextContext.h b/src/gpu/GrStencilAndCoverTextContext.h new file mode 100644 index 0000000..0a53a8d --- /dev/null +++ b/src/gpu/GrStencilAndCoverTextContext.h @@ -0,0 +1,52 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrStencilAndCoverTextContext_DEFINED +#define GrStencilAndCoverTextContext_DEFINED + +#include "GrTextContext.h" +#include "GrDrawState.h" +#include "SkStrokeRec.h" + +class GrTextStrike; +class GrPath; + +/* + * This class implements text rendering using stencil and cover path rendering + * (by the means of GrDrawTarget::drawPath). + * This class exposes the functionality through GrTextContext interface. + */ +class GrStencilAndCoverTextContext : public GrTextContext { +public: + GrStencilAndCoverTextContext(GrContext*, const SkDeviceProperties&); + virtual ~GrStencilAndCoverTextContext(); + + virtual void drawText(const GrPaint&, const SkPaint&, const char text[], + size_t byteLength, + SkScalar x, SkScalar y) SK_OVERRIDE; + virtual void drawPosText(const GrPaint&, const SkPaint&, + const char text[], size_t byteLength, + const SkScalar pos[], SkScalar constY, + int scalarsPerPosition) SK_OVERRIDE; + + virtual bool canDraw(const SkPaint& paint) SK_OVERRIDE; + +private: + void init(const GrPaint&, const SkPaint&, size_t textByteLength); + void appendGlyph(GrGlyph::PackedID, const SkPoint&, + GrTextStrike*, GrFontScaler*); + void finish(); + + GrDrawState::AutoRestoreEffects fStateRestore; + SkScalar fTextRatio; + SkStrokeRec fStroke; + SkTDArray fPaths; + SkTDArray fTransforms; + SkPath fTmpPath; +}; + +#endif diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index a849c84..f9abd78 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -188,9 +188,6 @@ void SkGpuDevice::initFromRenderTarget(GrContext* context, fContext = context; fContext->ref(); - fMainTextContext = SkNEW_ARGS(GrDistanceFieldTextContext, (fContext, fLeakyProperties)); - fFallbackTextContext = SkNEW_ARGS(GrBitmapTextContext, (fContext, fLeakyProperties)); - fRenderTarget = NULL; fNeedClear = flags & kNeedClear_Flag; @@ -212,6 +209,9 @@ void SkGpuDevice::initFromRenderTarget(GrContext* context, SkPixelRef* pr = SkNEW_ARGS(SkGrPixelRef, (info, surface, SkToBool(flags & kCached_Flag))); this->setPixelRef(pr)->unref(); + + fMainTextContext = fContext->createTextContext(fRenderTarget, fLeakyProperties); + fFallbackTextContext = SkNEW_ARGS(GrBitmapTextContext, (fContext, fLeakyProperties)); } SkGpuDevice* SkGpuDevice::Create(GrContext* context, const SkImageInfo& origInfo, @@ -268,9 +268,6 @@ SkGpuDevice::SkGpuDevice(GrContext* context, fContext = context; fContext->ref(); - fMainTextContext = SkNEW_ARGS(GrDistanceFieldTextContext, (fContext, fLeakyProperties)); - fFallbackTextContext = SkNEW_ARGS(GrBitmapTextContext, (fContext, fLeakyProperties)); - fRenderTarget = NULL; fNeedClear = false; @@ -309,6 +306,9 @@ SkGpuDevice::SkGpuDevice(GrContext* context, width, height); SkASSERT(false); } + + fMainTextContext = fContext->createTextContext(fRenderTarget, fLeakyProperties); + fFallbackTextContext = SkNEW_ARGS(GrBitmapTextContext, (fContext, fLeakyProperties)); } #endif -- 2.7.4