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() { |