Chromium Code Reviews| Index: src/gpu/GrStencilAndCoverTextContext.cpp |
| diff --git a/src/gpu/GrStencilAndCoverTextContext.cpp b/src/gpu/GrStencilAndCoverTextContext.cpp |
| index d6efe9810ce77a97b35aa01984431ec7218e0126..d33bc7a8bfd3395f565af138687e620749a90de5 100644 |
| --- a/src/gpu/GrStencilAndCoverTextContext.cpp |
| +++ b/src/gpu/GrStencilAndCoverTextContext.cpp |
| @@ -23,7 +23,9 @@ |
| GrStencilAndCoverTextContext::GrStencilAndCoverTextContext( |
| GrContext* context, const SkDeviceProperties& properties) |
| : GrTextContext(context, properties) |
| - , fPendingGlyphCount(0) { |
| + , fStroke(SkStrokeRec::kFill_InitStyle) |
| + , fQueuedGlyphCount(0) |
| + , fFallbackGlyphsIdx(kGlyphBufferSize) { |
| } |
| GrStencilAndCoverTextContext* GrStencilAndCoverTextContext::Create(GrContext* context, |
| @@ -63,10 +65,10 @@ bool GrStencilAndCoverTextContext::canDraw(const SkPaint& paint) { |
| } |
| void GrStencilAndCoverTextContext::onDrawText(const GrPaint& paint, |
| - const SkPaint& skPaint, |
| - const char text[], |
| - size_t byteLength, |
| - SkScalar x, SkScalar y) { |
| + const SkPaint& skPaint, |
| + const char text[], |
| + size_t byteLength, |
| + SkScalar x, SkScalar y) { |
| SkASSERT(byteLength == 0 || text != NULL); |
| if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) { |
| @@ -91,7 +93,7 @@ void GrStencilAndCoverTextContext::onDrawText(const GrPaint& paint, |
| this->init(paint, skPaint, byteLength, kMaxAccuracy_RenderMode); |
| // Transform our starting point. |
| - if (fNeedsDeviceSpaceGlyphs) { |
| + if (fUsingDeviceSpaceGlyphs) { |
| SkPoint loc; |
| fContextInitialMatrix.mapXY(x, y, &loc); |
| x = loc.fX; |
| @@ -140,7 +142,7 @@ void GrStencilAndCoverTextContext::onDrawText(const GrPaint& paint, |
| const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0); |
| fx += SkFixedMul_portable(autokern.adjust(glyph), fixedSizeRatio); |
| if (glyph.fWidth) { |
| - this->appendGlyph(glyph.getGlyphID(), SkFixedToScalar(fx), SkFixedToScalar(fy)); |
| + this->appendGlyph(glyph, SkPoint::Make(SkFixedToScalar(fx), SkFixedToScalar(fy))); |
| } |
| fx += SkFixedMul_portable(glyph.fAdvanceX, fixedSizeRatio); |
| @@ -151,12 +153,12 @@ void GrStencilAndCoverTextContext::onDrawText(const GrPaint& paint, |
| } |
| void GrStencilAndCoverTextContext::onDrawPosText(const GrPaint& paint, |
| - const SkPaint& skPaint, |
| - const char text[], |
| - size_t byteLength, |
| - const SkScalar pos[], |
| - int scalarsPerPosition, |
| - const SkPoint& offset) { |
| + const SkPaint& skPaint, |
| + const char text[], |
| + size_t byteLength, |
| + const SkScalar pos[], |
| + int scalarsPerPosition, |
| + const SkPoint& offset) { |
| SkASSERT(byteLength == 0 || text != NULL); |
| SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition); |
| @@ -189,7 +191,7 @@ void GrStencilAndCoverTextContext::onDrawPosText(const GrPaint& paint, |
| SkPoint loc; |
| alignProc(tmsLoc, glyph, &loc); |
| - this->appendGlyph(glyph.getGlyphID(), loc.x(), loc.y()); |
| + this->appendGlyph(glyph, loc); |
| } |
| pos += scalarsPerPosition; |
| } |
| @@ -232,15 +234,19 @@ void GrStencilAndCoverTextContext::init(const GrPaint& paint, |
| const bool otherBackendsWillDrawAsPaths = |
| SkDraw::ShouldDrawTextAsPaths(skPaint, fContextInitialMatrix); |
| - fNeedsDeviceSpaceGlyphs = !otherBackendsWillDrawAsPaths && |
| + fUsingDeviceSpaceGlyphs = !otherBackendsWillDrawAsPaths && |
| kMaxAccuracy_RenderMode == renderMode && |
| SkToBool(fContextInitialMatrix.getType() & |
| (SkMatrix::kScale_Mask | SkMatrix::kAffine_Mask)); |
| - if (fNeedsDeviceSpaceGlyphs) { |
| + if (fUsingDeviceSpaceGlyphs) { |
| // SkDraw::ShouldDrawTextAsPaths takes care of perspective transforms. |
| SkASSERT(!fContextInitialMatrix.hasPerspective()); |
| + // The whole shape (including stroke) will be baked into the glyph outlines. Make |
| + // NVPR just fill the baked shapes. |
| + fStroke = SkStrokeRec(SkStrokeRec::kFill_InitStyle); |
| + |
| fTextRatio = fTextInverseRatio = 1.0f; |
| // Glyphs loaded by GPU path rendering have an inverted y-direction. |
| @@ -254,30 +260,27 @@ void GrStencilAndCoverTextContext::init(const GrPaint& paint, |
| m.postScale(1, -1); |
| fPaint.localCoordChangeInverse(m); |
| - // The whole shape (including stroke) will be baked into the glyph outlines. Make |
| - // NVPR just fill the baked shapes. |
| fGlyphCache = fSkPaint.detachCache(&fDeviceProperties, &fContextInitialMatrix, false); |
| fGlyphs = get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->getTypeface(), |
| - &fGlyphCache->getDescriptor(), |
| - SkStrokeRec(SkStrokeRec::kFill_InitStyle)); |
| + &fGlyphCache->getDescriptor(), fStroke); |
| } else { |
| // Don't bake strokes into the glyph outlines. We will stroke the glyphs |
| // using the GPU instead. This is the fast path. |
| - SkStrokeRec gpuStroke = SkStrokeRec(fSkPaint); |
| + fStroke = SkStrokeRec(fSkPaint); |
| fSkPaint.setStyle(SkPaint::kFill_Style); |
| - if (gpuStroke.isHairlineStyle()) { |
| + if (fStroke.isHairlineStyle()) { |
| // Approximate hairline stroke. |
| SkScalar strokeWidth = SK_Scalar1 / |
| (SkVector::Make(fContextInitialMatrix.getScaleX(), |
| fContextInitialMatrix.getSkewY()).length()); |
| - gpuStroke.setStrokeStyle(strokeWidth, false /*strokeAndFill*/); |
| + fStroke.setStrokeStyle(strokeWidth, false /*strokeAndFill*/); |
| } else if (fSkPaint.isFakeBoldText() && |
| #ifdef SK_USE_FREETYPE_EMBOLDEN |
| kMaxPerformance_RenderMode == renderMode && |
| #endif |
| - SkStrokeRec::kStroke_Style != gpuStroke.getStyle()) { |
| + 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(), |
| @@ -285,8 +288,8 @@ void GrStencilAndCoverTextContext::init(const GrPaint& paint, |
| kStdFakeBoldInterpValues, |
| kStdFakeBoldInterpLength); |
| SkScalar extra = SkScalarMul(fSkPaint.getTextSize(), fakeBoldScale); |
| - gpuStroke.setStrokeStyle(gpuStroke.needToApply() ? gpuStroke.getWidth() + extra : extra, |
| - true /*strokeAndFill*/); |
| + fStroke.setStrokeStyle(fStroke.needToApply() ? fStroke.getWidth() + extra : extra, |
| + true /*strokeAndFill*/); |
| fSkPaint.setFakeBoldText(false); |
| } |
| @@ -299,9 +302,9 @@ void GrStencilAndCoverTextContext::init(const GrPaint& paint, |
| fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fSkPaint.getTextSize(); |
| // Compensate for the glyphs being scaled by fTextRatio. |
| - if (!gpuStroke.isFillStyle()) { |
| - gpuStroke.setStrokeStyle(gpuStroke.getWidth() / fTextRatio, |
| - SkStrokeRec::kStrokeAndFill_Style == gpuStroke.getStyle()); |
| + if (!fStroke.isFillStyle()) { |
| + fStroke.setStrokeStyle(fStroke.getWidth() / fTextRatio, |
| + SkStrokeRec::kStrokeAndFill_Style == fStroke.getStyle()); |
| } |
| fSkPaint.setLinearText(true); |
| @@ -328,9 +331,9 @@ void GrStencilAndCoverTextContext::init(const GrPaint& paint, |
| fGlyphCache = fSkPaint.detachCache(&fDeviceProperties, NULL, false); |
| fGlyphs = canUseRawPaths ? |
| - get_gr_glyphs(fContext, fSkPaint.getTypeface(), NULL, gpuStroke) : |
| + get_gr_glyphs(fContext, fSkPaint.getTypeface(), NULL, fStroke) : |
| get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->getTypeface(), |
| - &fGlyphCache->getDescriptor(), gpuStroke); |
| + &fGlyphCache->getDescriptor(), fStroke); |
| } |
| fStateRestore.set(&fDrawState); |
| @@ -347,31 +350,90 @@ void GrStencilAndCoverTextContext::init(const GrPaint& paint, |
| *fDrawState.stencil() = kStencilPass; |
| - SkASSERT(0 == fPendingGlyphCount); |
| + SkASSERT(0 == fQueuedGlyphCount); |
| + SkASSERT(kGlyphBufferSize == fFallbackGlyphsIdx); |
| +} |
| + |
| +bool GrStencilAndCoverTextContext::mapToFallbackContext(GrContext::AutoMatrix& autoMatrix, |
| + SkMatrix* inverse) { |
| + // The current view matrix is flipped because GPU path rendering glyphs have an |
| + // inverted y-direction. Unflip the view matrix for the fallback context. If using |
| + // device-space glyphs, we'll also need to restore the original view matrix since |
| + // we moved that transfomation into our local glyph cache for this scenario. Also |
| + // track the inverse operation so the caller can unmap the paint and glyph positions. |
| + if (fUsingDeviceSpaceGlyphs) { |
| + autoMatrix.set(fContext, fContextInitialMatrix); |
| + if (!fContextInitialMatrix.invert(inverse)) { |
| + return false; |
| + } |
| + inverse->preScale(1, -1); |
| + } else { |
| + inverse->setScale(1, -1); |
| + const SkMatrix& unflip = *inverse; // unflip is equal to its own inverse. |
| + autoMatrix.setPreConcat(fContext, unflip); |
| + } |
| + return true; |
| } |
| -inline void GrStencilAndCoverTextContext::appendGlyph(uint16_t glyphID, float x, float y) { |
| - if (fPendingGlyphCount >= kGlyphBufferSize) { |
| +inline void GrStencilAndCoverTextContext::appendGlyph(const SkGlyph& glyph, const SkPoint& pos) { |
| + if (fQueuedGlyphCount >= fFallbackGlyphsIdx) { |
| + SkASSERT(fQueuedGlyphCount == fFallbackGlyphsIdx); |
| this->flush(); |
| } |
| - fIndexBuffer[fPendingGlyphCount] = glyphID; |
| - fTransformBuffer[2 * fPendingGlyphCount] = fTextInverseRatio * x; |
| - fTransformBuffer[2 * fPendingGlyphCount + 1] = -fTextInverseRatio * y; |
| + // Stick the glyphs we can't draw at the end of the buffer, growing backwards. |
|
Chris Dalton
2014/11/25 23:13:54
Looking for feedback here.
Growing backwards will
jvanverth1
2014/11/26 19:41:35
On one hand, drawing them in reverse order doesn't
|
| + int index = (SkMask::kARGB32_Format == glyph.fMaskFormat) ? |
| + --fFallbackGlyphsIdx : fQueuedGlyphCount++; |
| - ++fPendingGlyphCount; |
| + fGlyphIndices[index] = glyph.getGlyphID(); |
| + fGlyphPositions[index].set(fTextInverseRatio * pos.x(), -fTextInverseRatio * pos.y()); |
| +} |
| + |
| +static const SkScalar* get_xy_scalar_array(const SkPoint* pointArray) { |
| + GR_STATIC_ASSERT(2 * sizeof(SkScalar) == sizeof(SkPoint)); |
| + GR_STATIC_ASSERT(0 == offsetof(SkPoint, fX)); |
| + |
| + return &pointArray[0].fX; |
| } |
| void GrStencilAndCoverTextContext::flush() { |
| - if (0 == fPendingGlyphCount) { |
| - return; |
| + if (fQueuedGlyphCount > 0) { |
| + fDrawTarget->drawPaths(&fDrawState, fGlyphs, |
| + fGlyphIndices, GrPathRange::kU16_PathIndexType, |
| + get_xy_scalar_array(fGlyphPositions), |
| + GrPathRendering::kTranslate_PathTransformType, |
| + fQueuedGlyphCount, GrPathRendering::kWinding_FillType); |
| + |
| + fQueuedGlyphCount = 0; |
| } |
| - fDrawTarget->drawPaths(&fDrawState, fGlyphs, fIndexBuffer, GrPathRange::kU16_PathIndexType, |
| - fTransformBuffer, GrPathRendering::kTranslate_PathTransformType, |
| - fPendingGlyphCount, GrPathRendering::kWinding_FillType); |
| + if (fFallbackGlyphsIdx < kGlyphBufferSize) { |
| + int fallbackGlyphCount = kGlyphBufferSize - fFallbackGlyphsIdx; |
| + |
| + GrPaint paintFallback(fPaint); |
| - fPendingGlyphCount = 0; |
| + SkPaint skPaintFallback(fSkPaint); |
| + if (!fUsingDeviceSpaceGlyphs) { |
| + fStroke.applyToPaint(&skPaintFallback); |
| + } |
| + skPaintFallback.setTextAlign(SkPaint::kLeft_Align); // Align has already been accounted for. |
| + skPaintFallback.setTextEncoding(SkPaint::kGlyphID_TextEncoding); |
| + |
| + GrContext::AutoMatrix autoMatrix; |
| + SkMatrix inverse; |
| + if (this->mapToFallbackContext(autoMatrix, &inverse)) { |
| + paintFallback.localCoordChangeInverse(inverse); |
| + inverse.mapPoints(&fGlyphPositions[fFallbackGlyphsIdx], fallbackGlyphCount); |
| + } |
| + |
| + fFallbackTextContext->drawPosText(paintFallback, skPaintFallback, |
| + (char*)&fGlyphIndices[fFallbackGlyphsIdx], |
| + 2 * fallbackGlyphCount, |
| + get_xy_scalar_array(&fGlyphPositions[fFallbackGlyphsIdx]), |
| + 2, SkPoint::Make(0, 0)); |
| + |
| + fFallbackGlyphsIdx = kGlyphBufferSize; |
| + } |
| } |
| void GrStencilAndCoverTextContext::finish() { |