Index: src/gpu/GrStencilAndCoverTextContext.cpp |
diff --git a/src/gpu/GrStencilAndCoverTextContext.cpp b/src/gpu/GrStencilAndCoverTextContext.cpp |
index 9c0fc2a8ec2a2fe10a5bb1e2930224b1dfea736c..9f394f901875364ff3fc59570716a215410f86f1 100644 |
--- a/src/gpu/GrStencilAndCoverTextContext.cpp |
+++ b/src/gpu/GrStencilAndCoverTextContext.cpp |
@@ -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); |
+} |
+ |
+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); |
+} |
- if (text == nullptr || byteLength == 0 /*|| fRC->isEmpty()*/) { |
- return; |
+//////////////////////////////////////////////////////////////////////////////////////////////////// |
+ |
+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); |
- |
- SkMatrix fallbackMatrix(fViewMatrix); |
- fallbackMatrix.preScale(fTextInverseRatio, fTextInverseRatio); |
- |
- fFallbackTextContext->drawPosText(dc, fRenderTarget, fClip, fPaint, fallbackSkPaint, |
- fallbackMatrix, (char*)fFallbackIndices.begin(), |
- sizeof(uint16_t) * fFallbackIndices.count(), |
- fFallbackPositions[0].asScalars(), 2, SkPoint::Make(0, 0), |
- fRegionClipBounds); |
- fFallbackIndices.reset(); |
- fFallbackPositions.reset(); |
- } |
-} |
- |
-void GrStencilAndCoverTextContext::finish(GrDrawContext* dc) { |
- this->flush(dc); |
+ fallbackSkPaint.setTextSize(fFont.getTextSize() * fTextRatio); |
- SkASSERT(!fDraw); |
- SkASSERT(!fFallbackIndices.count()); |
- SkASSERT(!fFallbackPositions.count()); |
- |
- fGlyphs->unref(); |
- fGlyphs = nullptr; |
- |
- SkGlyphCache::AttachCache(fGlyphCache); |
- fGlyphCache = nullptr; |
+ fallbackTextContext->drawPosText(dc, rt, clip, paint, fallbackSkPaint, viewMatrix, |
+ (char*)fFallbackIndices.begin(), |
+ sizeof(uint16_t) * fFallbackIndices.count(), |
+ fFallbackPositions[0].asScalars(), 2, SkPoint::Make(0, 0), |
+ regionClipBounds); |
+ } |
} |