Simplify nvpr text
- Drops device-space glyphs in favor of perf/simplicity.
- Removes residual complexities that would flip glyphs vertically for
compatibility with nvpr glyph loading, which is no longer used.
- Drops hairline support since they required new paths for every draw
matrix, and could only be supported under certain circumstances.
- Quits checking for color bitmap fonts in canDrawText since the
normal color emoji fallback will handle them anyway.
BUG=skia:
Review URL: https://codereview.chromium.org/1380973002
diff --git a/src/gpu/GrPathRendering.cpp b/src/gpu/GrPathRendering.cpp
index d98d9d2..0287eb0 100644
--- a/src/gpu/GrPathRendering.cpp
+++ b/src/gpu/GrPathRendering.cpp
@@ -19,9 +19,7 @@
#ifdef SK_DEBUG
, fDesc(desc.copy())
#endif
- {
- fFlipMatrix.setScale(1, -1);
- }
+ {}
virtual ~GlyphGenerator() {
#ifdef SK_DEBUG
@@ -39,7 +37,6 @@
fScalerContext->getMetrics(&skGlyph);
fScalerContext->getPath(skGlyph, out);
- out->transform(fFlipMatrix); // Load glyphs with the inverted y-direction.
}
#ifdef SK_DEBUG
bool isEqualTo(const SkDescriptor& desc) const override {
@@ -48,7 +45,6 @@
#endif
private:
const SkAutoTDelete<SkScalerContext> fScalerContext;
- SkMatrix fFlipMatrix;
#ifdef SK_DEBUG
SkDescriptor* const fDesc;
#endif
diff --git a/src/gpu/GrStencilAndCoverTextContext.cpp b/src/gpu/GrStencilAndCoverTextContext.cpp
index fbd32fc..9c0fc2a 100644
--- a/src/gpu/GrStencilAndCoverTextContext.cpp
+++ b/src/gpu/GrStencilAndCoverTextContext.cpp
@@ -58,18 +58,8 @@
return false;
}
}
-
- // No hairlines unless we can map the 1 px width to the object space.
- if (skPaint.getStyle() == SkPaint::kStroke_Style
- && skPaint.getStrokeWidth() == 0
- && viewMatrix.hasPerspective()) {
- return false;
- }
-
- // No color bitmap fonts.
- SkScalerContext::Rec rec;
- SkScalerContext::MakeRec(skPaint, &fSurfaceProps, nullptr, &rec);
- return rec.getFormat() != SkMask::kARGB32_Format;
+ // No hairlines. They would require new paths with customized strokes for every new draw matrix.
+ return SkPaint::kStroke_Style != skPaint.getStyle() || 0 != skPaint.getStrokeWidth();
}
void GrStencilAndCoverTextContext::onDrawText(GrDrawContext* dc, GrRenderTarget* rt,
@@ -87,31 +77,7 @@
return;
}
- // This is the slow path, mainly used by Skia unit tests. The other
- // backends (8888, gpu, ...) use device-space dependent glyph caches. In
- // order to match the glyph positions that the other code paths produce, we
- // must also use device-space dependent glyph cache. This has the
- // side-effect that the glyph shape outline will be in device-space,
- // too. This in turn has the side-effect that NVPR can not stroke the paths,
- // as the stroke in NVPR is defined in object-space.
- // NOTE: here we have following coincidence that works at the moment:
- // - When using the device-space glyphs, the transforms we pass to NVPR
- // instanced drawing are the global transforms, and the view transform is
- // identity. NVPR can not use non-affine transforms in the instanced
- // drawing. This is taken care of by SkDraw::ShouldDrawTextAsPaths since it
- // will turn off the use of device-space glyphs when perspective transforms
- // are in use.
-
- this->init(rt, clip, paint, skPaint, byteLength, kMaxAccuracy_RenderMode, viewMatrix,
- regionClipBounds);
-
- // Transform our starting point.
- if (fUsingDeviceSpaceGlyphs) {
- SkPoint loc;
- fContextInitialMatrix.mapXY(x, y, &loc);
- x = loc.fX;
- y = loc.fY;
- }
+ this->init(rt, clip, paint, skPaint, byteLength, viewMatrix, regionClipBounds);
SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
@@ -184,16 +150,7 @@
return;
}
- // This is the fast path. Here we do not bake in the device-transform to
- // the glyph outline or the advances. This is because we do not need to
- // position the glyphs at all, since the caller has done the positioning.
- // The positioning is based on SkPaint::measureText of individual
- // glyphs. That already uses glyph cache without device transforms. Device
- // transform is not part of SkPaint::measureText API, and thus we use the
- // same glyphs as what were measured.
-
- this->init(rt, clip, paint, skPaint, byteLength, kMaxPerformance_RenderMode, viewMatrix,
- regionClipBounds);
+ this->init(rt, clip, paint, skPaint, byteLength, viewMatrix, regionClipBounds);
SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
@@ -251,7 +208,6 @@
const GrPaint& paint,
const SkPaint& skPaint,
size_t textByteLength,
- RenderMode renderMode,
const SkMatrix& viewMatrix,
const SkIRect& regionClipBounds) {
fClip = clip;
@@ -264,147 +220,69 @@
fPaint = paint;
fSkPaint = skPaint;
- fContextInitialMatrix = viewMatrix;
- fViewMatrix = viewMatrix;
- fLocalMatrix = SkMatrix::I();
+ // 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);
- const bool otherBackendsWillDrawAsPaths =
- SkDraw::ShouldDrawTextAsPaths(skPaint, fContextInitialMatrix);
+ SkASSERT(!fStroke.isHairlineStyle()); // Hairlines are not supported.
- fUsingDeviceSpaceGlyphs = !otherBackendsWillDrawAsPaths &&
- kMaxAccuracy_RenderMode == renderMode &&
- SkToBool(fContextInitialMatrix.getType() &
- (SkMatrix::kScale_Mask | SkMatrix::kAffine_Mask));
+ 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*/);
- if (fUsingDeviceSpaceGlyphs) {
- // SkDraw::ShouldDrawTextAsPaths takes care of perspective transforms.
- SkASSERT(!fContextInitialMatrix.hasPerspective());
+ fSkPaint.setFakeBoldText(false);
+ }
- // The whole shape (including stroke) will be baked into the glyph outlines. Make
- // NVPR just fill the baked shapes.
- fStroke = GrStrokeInfo(SkStrokeRec::kFill_InitStyle);
+ 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;
+ }
- // Glyphs loaded by GPU path rendering have an inverted y-direction.
- SkMatrix m;
- m.setScale(1, -1);
- fViewMatrix = m;
+ fViewMatrix = viewMatrix;
+ fViewMatrix.preScale(fTextRatio, fTextRatio);
+ fLocalMatrix.setScale(fTextRatio, fTextRatio);
- // Post-flip the initial matrix so we're left with just the flip after
- // the paint preConcats the inverse.
- m = fContextInitialMatrix;
- m.postScale(1, -1);
- if (!m.invert(&fLocalMatrix)) {
- SkDebugf("Not invertible!\n");
- return;
- }
-
- fGlyphCache = fSkPaint.detachCache(&fSurfaceProps, &fContextInitialMatrix,
- true /*ignoreGamma*/);
- fGlyphs = get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->getTypeface(),
+ 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);
- } else {
- // Don't bake strokes into the glyph outlines. We will stroke the glyphs
- // using the GPU instead. This is the fast path.
- fStroke = GrStrokeInfo(fSkPaint);
- fSkPaint.setStyle(SkPaint::kFill_Style);
-
- if (fStroke.isHairlineStyle()) {
- // Approximate hairline stroke.
- SkScalar strokeWidth = SK_Scalar1 /
- (SkVector::Make(fContextInitialMatrix.getScaleX(),
- fContextInitialMatrix.getSkewY()).length());
- fStroke.setStrokeStyle(strokeWidth, false /*strokeAndFill*/);
-
- } else if (fSkPaint.isFakeBoldText() &&
-#ifdef SK_USE_FREETYPE_EMBOLDEN
- kMaxPerformance_RenderMode == renderMode &&
-#endif
- 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() && (otherBackendsWillDrawAsPaths ||
- kMaxPerformance_RenderMode == renderMode)) {
- // 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;
- }
-
- SkMatrix textMatrix;
- // Glyphs loaded by GPU path rendering have an inverted y-direction.
- textMatrix.setScale(fTextRatio, -fTextRatio);
- fViewMatrix.preConcat(textMatrix);
- fLocalMatrix = textMatrix;
-
- 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);
- }
-
-}
-
-bool GrStencilAndCoverTextContext::mapToFallbackContext(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) {
- fViewMatrix = 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.
- fViewMatrix.preConcat(unflip);
- }
- return true;
}
inline void GrStencilAndCoverTextContext::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().set(fTextInverseRatio * pos.x(),
- -fTextInverseRatio * pos.y());
+ fFallbackPositions.push_back(pos);
} else {
// TODO: infer the reserve count from the text length.
if (!fDraw) {
@@ -412,18 +290,11 @@
GrPathRendering::kTranslate_PathTransformType,
64);
}
- float translate[] = { fTextInverseRatio * pos.x(), -fTextInverseRatio * pos.y() };
+ float translate[] = { fTextInverseRatio * pos.x(), fTextInverseRatio * pos.y() };
fDraw->append(glyph.getGlyphID(), translate);
}
}
-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(GrDrawContext* dc) {
if (fDraw) {
SkASSERT(fDraw->count());
@@ -452,25 +323,27 @@
if (fFallbackIndices.count()) {
SkASSERT(fFallbackPositions.count() == fFallbackIndices.count());
- GrPaint paintFallback(fPaint);
- SkPaint skPaintFallback(fSkPaint);
- if (!fUsingDeviceSpaceGlyphs) {
- fStroke.applyToPaint(&skPaintFallback);
+ SkPaint fallbackSkPaint(fSkPaint);
+ fStroke.applyToPaint(&fallbackSkPaint);
+ if (!fStroke.isFillStyle()) {
+ fallbackSkPaint.setStrokeWidth(fStroke.getWidth() * fTextRatio);
}
- skPaintFallback.setTextAlign(SkPaint::kLeft_Align); // Align has already been accounted for.
- skPaintFallback.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ fallbackSkPaint.setTextAlign(SkPaint::kLeft_Align); // Align has already been accounted for.
+ fallbackSkPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ // 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 inverse;
- if (this->mapToFallbackContext(&inverse)) {
- inverse.mapPoints(fFallbackPositions.begin(), fFallbackPositions.count());
- }
+ SkMatrix fallbackMatrix(fViewMatrix);
+ fallbackMatrix.preScale(fTextInverseRatio, fTextInverseRatio);
- fFallbackTextContext->drawPosText(dc, fRenderTarget, fClip, paintFallback, skPaintFallback,
- fViewMatrix, (char*)fFallbackIndices.begin(),
+ fFallbackTextContext->drawPosText(dc, fRenderTarget, fClip, fPaint, fallbackSkPaint,
+ fallbackMatrix, (char*)fFallbackIndices.begin(),
sizeof(uint16_t) * fFallbackIndices.count(),
- get_xy_scalar_array(fFallbackPositions.begin()),
- 2, SkPoint::Make(0, 0), fRegionClipBounds);
+ fFallbackPositions[0].asScalars(), 2, SkPoint::Make(0, 0),
+ fRegionClipBounds);
fFallbackIndices.reset();
fFallbackPositions.reset();
}
@@ -488,6 +361,4 @@
SkGlyphCache::AttachCache(fGlyphCache);
fGlyphCache = nullptr;
-
- fViewMatrix = fContextInitialMatrix;
}
diff --git a/src/gpu/GrStencilAndCoverTextContext.h b/src/gpu/GrStencilAndCoverTextContext.h
index 4c57d3c..44b33a5 100644
--- a/src/gpu/GrStencilAndCoverTextContext.h
+++ b/src/gpu/GrStencilAndCoverTextContext.h
@@ -30,27 +30,6 @@
virtual ~GrStencilAndCoverTextContext();
private:
- enum RenderMode {
- /**
- * This is the render mode used by drawText(), which is mainly used by
- * the Skia unit tests. It tries match the other text backends exactly,
- * with the exception of not implementing LCD text, and doing anti-
- * aliasing with the built-in MSAA.
- */
- kMaxAccuracy_RenderMode,
-
- /**
- * This is the render mode used by drawPosText(). It ignores hinting and
- * LCD text, even if the client provided positions for hinted glyphs,
- * and renders from a canonically-sized, generic set of paths for the
- * given typeface. In the future we should work out a system for the
- * client to know it should not provide hinted glyph positions. This
- * render mode also tries to use GPU stroking for fake bold, even when
- * SK_USE_FREETYPE_EMBOLDEN is set.
- */
- kMaxPerformance_RenderMode,
- };
-
SkScalar fTextRatio;
float fTextInverseRatio;
SkGlyphCache* fGlyphCache;
@@ -61,10 +40,8 @@
SkSTArray<32, uint16_t, true> fFallbackIndices;
SkSTArray<32, SkPoint, true> fFallbackPositions;
- SkMatrix fContextInitialMatrix;
SkMatrix fViewMatrix;
SkMatrix fLocalMatrix;
- bool fUsingDeviceSpaceGlyphs;
SkAutoTUnref<GrRenderTarget> fRenderTarget;
GrClip fClip;
SkIRect fClipRect;
@@ -88,10 +65,8 @@
const SkScalar pos[], int scalarsPerPosition,
const SkPoint& offset, const SkIRect& regionClipBounds) override;
- void init(GrRenderTarget*, const GrClip&, const GrPaint&, const SkPaint&,
- size_t textByteLength, RenderMode, const SkMatrix& viewMatrix,
- const SkIRect& regionClipBounds);
- bool mapToFallbackContext(SkMatrix* inverse);
+ void init(GrRenderTarget*, const GrClip&, const GrPaint&, const SkPaint&, size_t textByteLength,
+ const SkMatrix& viewMatrix, const SkIRect& regionClipBounds);
void appendGlyph(const SkGlyph&, const SkPoint&);
void flush(GrDrawContext* dc);
void finish(GrDrawContext* dc);