Add TextRun object to nvpr text

Adds a TextRun object that defines a resolution-independent, paint-
indepent draw for a string of nvpr text.

BUG=skia:

Review URL: https://codereview.chromium.org/1375353004
This commit is contained in:
cdalton 2015-10-05 14:57:20 -07:00 committed by Commit bot
parent cc7f305c69
commit 3bd909a44c
2 changed files with 177 additions and 187 deletions

View File

@ -25,9 +25,7 @@
GrStencilAndCoverTextContext::GrStencilAndCoverTextContext(GrContext* context,
const SkSurfaceProps& surfaceProps)
: INHERITED(context, surfaceProps)
, fDraw(nullptr)
, fStroke(SkStrokeRec::kFill_InitStyle) {
: INHERITED(context, surfaceProps) {
}
GrStencilAndCoverTextContext*
@ -71,20 +69,103 @@ void GrStencilAndCoverTextContext::onDrawText(GrDrawContext* dc, GrRenderTarget*
size_t byteLength,
SkScalar x, SkScalar y,
const SkIRect& regionClipBounds) {
SkASSERT(byteLength == 0 || text != nullptr);
TextRun run(skPaint);
run.setText(text, byteLength, x, y, fContext, &fSurfaceProps);
run.draw(dc, rt, clip, paint, viewMatrix, regionClipBounds, fFallbackTextContext, skPaint);
}
if (text == nullptr || byteLength == 0 /*|| fRC->isEmpty()*/) {
return;
void GrStencilAndCoverTextContext::onDrawPosText(GrDrawContext* dc, GrRenderTarget* rt,
const GrClip& clip,
const GrPaint& paint,
const SkPaint& skPaint,
const SkMatrix& viewMatrix,
const char text[],
size_t byteLength,
const SkScalar pos[],
int scalarsPerPosition,
const SkPoint& offset,
const SkIRect& regionClipBounds) {
TextRun run(skPaint);
run.setPosText(text, byteLength, pos, scalarsPerPosition, offset, fContext, &fSurfaceProps);
run.draw(dc, rt, clip, paint, viewMatrix, regionClipBounds, fFallbackTextContext, skPaint);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
GrStencilAndCoverTextContext::TextRun::TextRun(const SkPaint& fontAndStroke)
: fStroke(fontAndStroke),
fFont(fontAndStroke) {
SkASSERT(!fStroke.isHairlineStyle()); // Hairlines are not supported.
// Setting to "fill" ensures that no strokes get baked into font outlines. (We use the GPU path
// rendering API for stroking).
fFont.setStyle(SkPaint::kFill_Style);
if (fFont.isFakeBoldText() && SkStrokeRec::kStroke_Style != fStroke.getStyle()) {
// Instead of letting fake bold get baked into the glyph outlines, do it with GPU stroke.
SkScalar fakeBoldScale = SkScalarInterpFunc(fFont.getTextSize(),
kStdFakeBoldInterpKeys,
kStdFakeBoldInterpValues,
kStdFakeBoldInterpLength);
SkScalar extra = SkScalarMul(fFont.getTextSize(), fakeBoldScale);
fStroke.setStrokeStyle(fStroke.needToApply() ? fStroke.getWidth() + extra : extra,
true /*strokeAndFill*/);
fFont.setFakeBoldText(false);
}
this->init(rt, clip, paint, skPaint, byteLength, viewMatrix, regionClipBounds);
if (!fFont.getPathEffect() && !fStroke.isDashed()) {
// We can draw the glyphs from canonically sized paths.
fTextRatio = fFont.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fFont.getTextSize();
SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
// Compensate for the glyphs being scaled by fTextRatio.
if (!fStroke.isFillStyle()) {
fStroke.setStrokeStyle(fStroke.getWidth() / fTextRatio,
SkStrokeRec::kStrokeAndFill_Style == fStroke.getStyle());
}
fFont.setLinearText(true);
fFont.setLCDRenderText(false);
fFont.setAutohinted(false);
fFont.setHinting(SkPaint::kNo_Hinting);
fFont.setSubpixelText(true);
fFont.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
fUsingRawGlyphPaths = SK_Scalar1 == fFont.getTextScaleX() &&
0 == fFont.getTextSkewX() &&
!fFont.isFakeBoldText() &&
!fFont.isVerticalText();
} else {
fTextRatio = fTextInverseRatio = 1.0f;
fUsingRawGlyphPaths = false;
}
// When drawing from canonically sized paths, the actual local coords are fTextRatio * coords.
fLocalMatrix.setScale(fTextRatio, fTextRatio);
}
GrStencilAndCoverTextContext::TextRun::~TextRun() {
}
void GrStencilAndCoverTextContext::TextRun::setText(const char text[], size_t byteLength,
SkScalar x, SkScalar y, GrContext* ctx,
const SkSurfaceProps* surfaceProps) {
SkASSERT(byteLength == 0 || text != nullptr);
SkAutoGlyphCacheNoGamma autoGlyphCache(fFont, surfaceProps, nullptr);
SkGlyphCache* glyphCache = autoGlyphCache.getCache();
fDraw.reset(GrPathRangeDraw::Create(this->createGlyphs(ctx, glyphCache),
GrPathRendering::kTranslate_PathTransformType,
fFont.countText(text, byteLength)));
SkDrawCacheProc glyphCacheProc = fFont.getDrawCacheProc();
const char* stop = text + byteLength;
// Measure first if needed.
if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) {
if (fFont.getTextAlign() != SkPaint::kLeft_Align) {
SkFixed stopX = 0;
SkFixed stopY = 0;
@ -92,7 +173,7 @@ void GrStencilAndCoverTextContext::onDrawText(GrDrawContext* dc, GrRenderTarget*
while (textPtr < stop) {
// We don't need x, y here, since all subpixel variants will have the
// same advance.
const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &textPtr, 0, 0);
const SkGlyph& glyph = glyphCacheProc(glyphCache, &textPtr, 0, 0);
stopX += glyph.fAdvanceX;
stopY += glyph.fAdvanceY;
@ -102,7 +183,7 @@ void GrStencilAndCoverTextContext::onDrawText(GrDrawContext* dc, GrRenderTarget*
SkScalar alignX = SkFixedToScalar(stopX) * fTextRatio;
SkScalar alignY = SkFixedToScalar(stopY) * fTextRatio;
if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) {
if (fFont.getTextAlign() == SkPaint::kCenter_Align) {
alignX = SkScalarHalf(alignX);
alignY = SkScalarHalf(alignY);
}
@ -118,7 +199,7 @@ void GrStencilAndCoverTextContext::onDrawText(GrDrawContext* dc, GrRenderTarget*
SkFixed fx = SkScalarToFixed(x);
SkFixed fy = SkScalarToFixed(y);
while (text < stop) {
const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
const SkGlyph& glyph = glyphCacheProc(glyphCache, &text, 0, 0);
fx += SkFixedMul(autokern.adjust(glyph), fixedSizeRatio);
if (glyph.fWidth) {
this->appendGlyph(glyph, SkPoint::Make(SkFixedToScalar(fx), SkFixedToScalar(fy)));
@ -127,39 +208,30 @@ void GrStencilAndCoverTextContext::onDrawText(GrDrawContext* dc, GrRenderTarget*
fx += SkFixedMul(glyph.fAdvanceX, fixedSizeRatio);
fy += SkFixedMul(glyph.fAdvanceY, fixedSizeRatio);
}
this->finish(dc);
}
void GrStencilAndCoverTextContext::onDrawPosText(GrDrawContext* dc, GrRenderTarget* rt,
const GrClip& clip,
const GrPaint& paint,
const SkPaint& skPaint,
const SkMatrix& viewMatrix,
const char text[],
size_t byteLength,
const SkScalar pos[],
int scalarsPerPosition,
const SkPoint& offset,
const SkIRect& regionClipBounds) {
void GrStencilAndCoverTextContext::TextRun::setPosText(const char text[], size_t byteLength,
const SkScalar pos[], int scalarsPerPosition,
const SkPoint& offset, GrContext* ctx,
const SkSurfaceProps* surfaceProps) {
SkASSERT(byteLength == 0 || text != nullptr);
SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
// nothing to draw
if (text == nullptr || byteLength == 0/* || fRC->isEmpty()*/) {
return;
}
SkAutoGlyphCacheNoGamma autoGlyphCache(fFont, surfaceProps, nullptr);
SkGlyphCache* glyphCache = autoGlyphCache.getCache();
this->init(rt, clip, paint, skPaint, byteLength, viewMatrix, regionClipBounds);
fDraw.reset(GrPathRangeDraw::Create(this->createGlyphs(ctx, glyphCache),
GrPathRendering::kTranslate_PathTransformType,
fFont.countText(text, byteLength)));
SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
SkDrawCacheProc glyphCacheProc = fFont.getDrawCacheProc();
const char* stop = text + byteLength;
SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
SkTextAlignProc alignProc(fSkPaint.getTextAlign());
SkTextAlignProc alignProc(fFont.getTextAlign());
while (text < stop) {
const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
const SkGlyph& glyph = glyphCacheProc(glyphCache, &text, 0, 0);
if (glyph.fWidth) {
SkPoint tmsLoc;
tmsProc(pos, &tmsLoc);
@ -170,23 +242,22 @@ void GrStencilAndCoverTextContext::onDrawPosText(GrDrawContext* dc, GrRenderTarg
}
pos += scalarsPerPosition;
}
this->finish(dc);
}
static GrPathRange* get_gr_glyphs(GrContext* ctx,
const SkTypeface* typeface,
const SkDescriptor* desc,
const GrStrokeInfo& stroke) {
GrPathRange* GrStencilAndCoverTextContext::TextRun::createGlyphs(GrContext* ctx,
SkGlyphCache* glyphCache) {
SkTypeface* typeface = fUsingRawGlyphPaths ? fFont.getTypeface()
: glyphCache->getScalerContext()->getTypeface();
const SkDescriptor* desc = fUsingRawGlyphPaths ? nullptr : &glyphCache->getDescriptor();
static const GrUniqueKey::Domain kPathGlyphDomain = GrUniqueKey::GenerateDomain();
int strokeDataCount = stroke.computeUniqueKeyFragmentData32Cnt();
int strokeDataCount = fStroke.computeUniqueKeyFragmentData32Cnt();
GrUniqueKey glyphKey;
GrUniqueKey::Builder builder(&glyphKey, kPathGlyphDomain, 2 + strokeDataCount);
reinterpret_cast<uint32_t&>(builder[0]) = desc ? desc->getChecksum() : 0;
reinterpret_cast<uint32_t&>(builder[1]) = typeface ? typeface->uniqueID() : 0;
if (strokeDataCount > 0) {
stroke.asUniqueKeyFragment(&builder[2]);
fStroke.asUniqueKeyFragment(&builder[2]);
}
builder.finish();
@ -194,7 +265,7 @@ static GrPathRange* get_gr_glyphs(GrContext* ctx,
static_cast<GrPathRange*>(
ctx->resourceProvider()->findAndRefResourceByUniqueKey(glyphKey)));
if (nullptr == glyphs) {
glyphs.reset(ctx->resourceProvider()->createGlyphs(typeface, desc, stroke));
glyphs.reset(ctx->resourceProvider()->createGlyphs(typeface, desc, fStroke));
ctx->resourceProvider()->assignUniqueKeyToResource(glyphKey, glyphs);
} else {
SkASSERT(nullptr == desc || glyphs->isEqualTo(*desc));
@ -203,107 +274,32 @@ static GrPathRange* get_gr_glyphs(GrContext* ctx,
return glyphs.detach();
}
void GrStencilAndCoverTextContext::init(GrRenderTarget* rt,
const GrClip& clip,
const GrPaint& paint,
const SkPaint& skPaint,
size_t textByteLength,
const SkMatrix& viewMatrix,
const SkIRect& regionClipBounds) {
fClip = clip;
fRenderTarget.reset(SkRef(rt));
fRegionClipBounds = regionClipBounds;
fClip.getConservativeBounds(fRenderTarget->width(), fRenderTarget->height(), &fClipRect);
fPaint = paint;
fSkPaint = skPaint;
// Don't bake strokes into the glyph outlines. We will stroke the glyphs using the GPU instead.
fStroke = GrStrokeInfo(fSkPaint);
fSkPaint.setStyle(SkPaint::kFill_Style);
SkASSERT(!fStroke.isHairlineStyle()); // Hairlines are not supported.
if (fSkPaint.isFakeBoldText() && SkStrokeRec::kStroke_Style != fStroke.getStyle()) {
// Instead of baking fake bold into the glyph outlines, do it with the GPU stroke.
SkScalar fakeBoldScale = SkScalarInterpFunc(fSkPaint.getTextSize(),
kStdFakeBoldInterpKeys,
kStdFakeBoldInterpValues,
kStdFakeBoldInterpLength);
SkScalar extra = SkScalarMul(fSkPaint.getTextSize(), fakeBoldScale);
fStroke.setStrokeStyle(fStroke.needToApply() ? fStroke.getWidth() + extra : extra,
true /*strokeAndFill*/);
fSkPaint.setFakeBoldText(false);
}
bool canUseRawPaths;
if (!fStroke.isDashed()) {
// We can draw the glyphs from canonically sized paths.
fTextRatio = fSkPaint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fSkPaint.getTextSize();
// Compensate for the glyphs being scaled by fTextRatio.
if (!fStroke.isFillStyle()) {
fStroke.setStrokeStyle(fStroke.getWidth() / fTextRatio,
SkStrokeRec::kStrokeAndFill_Style == fStroke.getStyle());
}
fSkPaint.setLinearText(true);
fSkPaint.setLCDRenderText(false);
fSkPaint.setAutohinted(false);
fSkPaint.setHinting(SkPaint::kNo_Hinting);
fSkPaint.setSubpixelText(true);
fSkPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
canUseRawPaths = SK_Scalar1 == fSkPaint.getTextScaleX() &&
0 == fSkPaint.getTextSkewX() &&
!fSkPaint.isFakeBoldText() &&
!fSkPaint.isVerticalText();
} else {
fTextRatio = fTextInverseRatio = 1.0f;
canUseRawPaths = false;
}
fViewMatrix = viewMatrix;
fViewMatrix.preScale(fTextRatio, fTextRatio);
fLocalMatrix.setScale(fTextRatio, fTextRatio);
fGlyphCache = fSkPaint.detachCache(&fSurfaceProps, nullptr, true /*ignoreGamma*/);
fGlyphs = canUseRawPaths ?
get_gr_glyphs(fContext, fSkPaint.getTypeface(), nullptr, fStroke) :
get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->getTypeface(),
&fGlyphCache->getDescriptor(), fStroke);
}
inline void GrStencilAndCoverTextContext::appendGlyph(const SkGlyph& glyph, const SkPoint& pos) {
inline void GrStencilAndCoverTextContext::TextRun::appendGlyph(const SkGlyph& glyph,
const SkPoint& pos) {
// Stick the glyphs we can't draw into the fallback arrays.
if (SkMask::kARGB32_Format == glyph.fMaskFormat) {
fFallbackIndices.push_back(glyph.getGlyphID());
fFallbackPositions.push_back(pos);
} else {
// TODO: infer the reserve count from the text length.
if (!fDraw) {
fDraw = GrPathRangeDraw::Create(fGlyphs,
GrPathRendering::kTranslate_PathTransformType,
64);
}
float translate[] = { fTextInverseRatio * pos.x(), fTextInverseRatio * pos.y() };
fDraw->append(glyph.getGlyphID(), translate);
}
}
void GrStencilAndCoverTextContext::flush(GrDrawContext* dc) {
if (fDraw) {
SkASSERT(fDraw->count());
void GrStencilAndCoverTextContext::TextRun::draw(GrDrawContext* dc,
GrRenderTarget* rt,
const GrClip& clip,
const GrPaint& paint,
const SkMatrix& viewMatrix,
const SkIRect& regionClipBounds,
GrTextContext* fallbackTextContext,
const SkPaint& originalSkPaint) const {
SkASSERT(fDraw);
// We should only be flushing about once every run. However, if this impacts performance
// we could move the creation of the GrPipelineBuilder earlier.
GrPipelineBuilder pipelineBuilder(fPaint, fRenderTarget, fClip);
SkASSERT(fRenderTarget->isStencilBufferMultisampled() || !fPaint.isAntiAlias());
pipelineBuilder.setState(GrPipelineBuilder::kHWAntialias_Flag, fPaint.isAntiAlias());
if (fDraw->count()) {
GrPipelineBuilder pipelineBuilder(paint, rt, clip);
SkASSERT(rt->isStencilBufferMultisampled() || !paint.isAntiAlias());
pipelineBuilder.setState(GrPipelineBuilder::kHWAntialias_Flag, paint.isAntiAlias());
GR_STATIC_CONST_SAME_STENCIL(kStencilPass,
kZero_StencilOp,
@ -315,50 +311,38 @@ void GrStencilAndCoverTextContext::flush(GrDrawContext* dc) {
*pipelineBuilder.stencil() = kStencilPass;
dc->drawPathsFromRange(&pipelineBuilder, fViewMatrix, fLocalMatrix, fPaint.getColor(),
fDraw, GrPathRendering::kWinding_FillType);
fDraw->unref();
fDraw = nullptr;
SkMatrix drawMatrix(viewMatrix);
drawMatrix.preScale(fTextRatio, fTextRatio);
dc->drawPathsFromRange(&pipelineBuilder, drawMatrix, fLocalMatrix, paint.getColor(), fDraw,
GrPathRendering::kWinding_FillType);
}
if (fFallbackIndices.count()) {
SkASSERT(fFallbackPositions.count() == fFallbackIndices.count());
SkPaint fallbackSkPaint(fSkPaint);
enum { kPreservedFlags = SkPaint::kFakeBoldText_Flag | SkPaint::kLinearText_Flag |
SkPaint::kLCDRenderText_Flag | SkPaint::kAutoHinting_Flag };
SkPaint fallbackSkPaint(originalSkPaint);
fStroke.applyToPaint(&fallbackSkPaint);
if (!fStroke.isFillStyle()) {
fallbackSkPaint.setStrokeWidth(fStroke.getWidth() * fTextRatio);
}
fallbackSkPaint.setTextAlign(SkPaint::kLeft_Align); // Align has already been accounted for.
fallbackSkPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
fallbackSkPaint.setHinting(fFont.getHinting());
fallbackSkPaint.setFlags((fFont.getFlags() & kPreservedFlags) |
(originalSkPaint.getFlags() & ~kPreservedFlags));
// No need for subpixel positioning with bitmap glyphs. TODO: revisit if non-bitmap color
// glyphs show up and https://code.google.com/p/skia/issues/detail?id=4408 gets resolved.
fallbackSkPaint.setSubpixelText(false);
fallbackSkPaint.setTextSize(fSkPaint.getTextSize() * fTextRatio);
fallbackSkPaint.setTextSize(fFont.getTextSize() * fTextRatio);
SkMatrix fallbackMatrix(fViewMatrix);
fallbackMatrix.preScale(fTextInverseRatio, fTextInverseRatio);
fFallbackTextContext->drawPosText(dc, fRenderTarget, fClip, fPaint, fallbackSkPaint,
fallbackMatrix, (char*)fFallbackIndices.begin(),
fallbackTextContext->drawPosText(dc, rt, clip, paint, fallbackSkPaint, viewMatrix,
(char*)fFallbackIndices.begin(),
sizeof(uint16_t) * fFallbackIndices.count(),
fFallbackPositions[0].asScalars(), 2, SkPoint::Make(0, 0),
fRegionClipBounds);
fFallbackIndices.reset();
fFallbackPositions.reset();
regionClipBounds);
}
}
void GrStencilAndCoverTextContext::finish(GrDrawContext* dc) {
this->flush(dc);
SkASSERT(!fDraw);
SkASSERT(!fFallbackIndices.count());
SkASSERT(!fFallbackPositions.count());
fGlyphs->unref();
fGlyphs = nullptr;
SkGlyphCache::AttachCache(fGlyphCache);
fGlyphCache = nullptr;
}

View File

@ -15,7 +15,6 @@
class GrTextStrike;
class GrPath;
class GrPathRange;
class GrPathRangeDraw;
class SkSurfaceProps;
/*
@ -30,25 +29,6 @@ public:
virtual ~GrStencilAndCoverTextContext();
private:
SkScalar fTextRatio;
float fTextInverseRatio;
SkGlyphCache* fGlyphCache;
GrPathRange* fGlyphs;
GrPathRangeDraw* fDraw;
GrStrokeInfo fStroke;
SkSTArray<32, uint16_t, true> fFallbackIndices;
SkSTArray<32, SkPoint, true> fFallbackPositions;
SkMatrix fViewMatrix;
SkMatrix fLocalMatrix;
SkAutoTUnref<GrRenderTarget> fRenderTarget;
GrClip fClip;
SkIRect fClipRect;
SkIRect fRegionClipBounds;
GrPaint fPaint;
SkPaint fSkPaint;
GrStencilAndCoverTextContext(GrContext*, const SkSurfaceProps&);
bool canDraw(const GrRenderTarget*, const GrClip&, const GrPaint&,
@ -65,11 +45,37 @@ private:
const SkScalar pos[], int scalarsPerPosition,
const SkPoint& offset, const SkIRect& regionClipBounds) override;
void init(GrRenderTarget*, const GrClip&, const GrPaint&, const SkPaint&, size_t textByteLength,
const SkMatrix& viewMatrix, const SkIRect& regionClipBounds);
class TextRun {
public:
TextRun(const SkPaint& fontAndStroke);
~TextRun();
void setText(const char text[], size_t byteLength, SkScalar x, SkScalar y,
GrContext*, const SkSurfaceProps*);
void setPosText(const char text[], size_t byteLength,
const SkScalar pos[], int scalarsPerPosition, const SkPoint& offset,
GrContext*, const SkSurfaceProps*);
void draw(GrDrawContext*, GrRenderTarget*, const GrClip&, const GrPaint&, const SkMatrix&,
const SkIRect& regionClipBounds, GrTextContext* fallbackTextContext,
const SkPaint& originalSkPaint) const;
private:
GrPathRange* createGlyphs(GrContext*, SkGlyphCache*);
void appendGlyph(const SkGlyph&, const SkPoint&);
void flush(GrDrawContext* dc);
void finish(GrDrawContext* dc);
GrStrokeInfo fStroke;
SkPaint fFont;
SkScalar fTextRatio;
float fTextInverseRatio;
SkMatrix fLocalMatrix;
bool fUsingRawGlyphPaths;
SkAutoTUnref<GrPathRangeDraw> fDraw;
SkSTArray<32, uint16_t, true> fFallbackIndices;
SkSTArray<32, SkPoint, true> fFallbackPositions;
};
typedef GrTextContext INHERITED;
};