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
This commit is contained in:
commit-bot@chromium.org 2014-03-27 11:26:10 +00:00
parent db35dab49e
commit dcb8ef9e86
7 changed files with 378 additions and 10 deletions

View File

@ -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',

View File

@ -1144,6 +1144,7 @@ private:
friend class SkPDFDevice;
friend class GrBitmapTextContext;
friend class GrDistanceFieldTextContext;
friend class GrStencilAndCoverTextContext;
friend class SkTextToPathIter;
friend class SkCanonicalizePaint;

View File

@ -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.

View File

@ -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,

View File

@ -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<const GrPath*> paths;
SkTDArray<SkMatrix> 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<size_t>(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();
}

View File

@ -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<const GrPath*> fPaths;
SkTDArray<SkMatrix> fTransforms;
SkPath fTmpPath;
};
#endif

View File

@ -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