| Index: src/gpu/GrStencilAndCoverTextContext.cpp
|
| diff --git a/src/gpu/GrStencilAndCoverTextContext.cpp b/src/gpu/GrStencilAndCoverTextContext.cpp
|
| index fbd32fc8561bb11b500eec9ca9188e903ecd4848..9c0fc2a8ec2a2fe10a5bb1e2930224b1dfea736c 100644
|
| --- a/src/gpu/GrStencilAndCoverTextContext.cpp
|
| +++ b/src/gpu/GrStencilAndCoverTextContext.cpp
|
| @@ -58,18 +58,8 @@ bool GrStencilAndCoverTextContext::canDraw(const GrRenderTarget* rt,
|
| 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 @@ void GrStencilAndCoverTextContext::onDrawText(GrDrawContext* dc, GrRenderTarget*
|
| 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 @@ void GrStencilAndCoverTextContext::onDrawPosText(GrDrawContext* dc, GrRenderTarg
|
| 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 @@ void GrStencilAndCoverTextContext::init(GrRenderTarget* rt,
|
| const GrPaint& paint,
|
| const SkPaint& skPaint,
|
| size_t textByteLength,
|
| - RenderMode renderMode,
|
| const SkMatrix& viewMatrix,
|
| const SkIRect& regionClipBounds) {
|
| fClip = clip;
|
| @@ -264,147 +220,69 @@ void GrStencilAndCoverTextContext::init(GrRenderTarget* rt,
|
| fPaint = paint;
|
| fSkPaint = skPaint;
|
|
|
| - fContextInitialMatrix = viewMatrix;
|
| - fViewMatrix = viewMatrix;
|
| - fLocalMatrix = SkMatrix::I();
|
| -
|
| - const bool otherBackendsWillDrawAsPaths =
|
| - SkDraw::ShouldDrawTextAsPaths(skPaint, fContextInitialMatrix);
|
| + // 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);
|
|
|
| - fUsingDeviceSpaceGlyphs = !otherBackendsWillDrawAsPaths &&
|
| - kMaxAccuracy_RenderMode == renderMode &&
|
| - SkToBool(fContextInitialMatrix.getType() &
|
| - (SkMatrix::kScale_Mask | SkMatrix::kAffine_Mask));
|
| + SkASSERT(!fStroke.isHairlineStyle()); // Hairlines are not supported.
|
|
|
| - if (fUsingDeviceSpaceGlyphs) {
|
| - // SkDraw::ShouldDrawTextAsPaths takes care of perspective transforms.
|
| - SkASSERT(!fContextInitialMatrix.hasPerspective());
|
| + 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*/);
|
|
|
| - // The whole shape (including stroke) will be baked into the glyph outlines. Make
|
| - // NVPR just fill the baked shapes.
|
| - fStroke = GrStrokeInfo(SkStrokeRec::kFill_InitStyle);
|
| + fSkPaint.setFakeBoldText(false);
|
| + }
|
|
|
| - fTextRatio = fTextInverseRatio = 1.0f;
|
| + bool canUseRawPaths;
|
| + if (!fStroke.isDashed()) {
|
| + // We can draw the glyphs from canonically sized paths.
|
| + fTextRatio = fSkPaint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
|
| + fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fSkPaint.getTextSize();
|
|
|
| - // Glyphs loaded by GPU path rendering have an inverted y-direction.
|
| - SkMatrix m;
|
| - m.setScale(1, -1);
|
| - fViewMatrix = m;
|
| -
|
| - // 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;
|
| + // Compensate for the glyphs being scaled by fTextRatio.
|
| + if (!fStroke.isFillStyle()) {
|
| + fStroke.setStrokeStyle(fStroke.getWidth() / fTextRatio,
|
| + SkStrokeRec::kStrokeAndFill_Style == fStroke.getStyle());
|
| }
|
|
|
| - fGlyphCache = fSkPaint.detachCache(&fSurfaceProps, &fContextInitialMatrix,
|
| - true /*ignoreGamma*/);
|
| - fGlyphs = get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->getTypeface(),
|
| - &fGlyphCache->getDescriptor(), fStroke);
|
| + 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 {
|
| - // 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);
|
| + fTextRatio = fTextInverseRatio = 1.0f;
|
| + canUseRawPaths = false;
|
| }
|
|
|
| -}
|
| + fViewMatrix = viewMatrix;
|
| + fViewMatrix.preScale(fTextRatio, fTextRatio);
|
| + fLocalMatrix.setScale(fTextRatio, fTextRatio);
|
|
|
| -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;
|
| + 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) {
|
| // 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 @@ inline void GrStencilAndCoverTextContext::appendGlyph(const SkGlyph& glyph, cons
|
| 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 @@ void GrStencilAndCoverTextContext::flush(GrDrawContext* dc) {
|
|
|
| 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);
|
| -
|
| - SkMatrix inverse;
|
| - if (this->mapToFallbackContext(&inverse)) {
|
| - inverse.mapPoints(fFallbackPositions.begin(), fFallbackPositions.count());
|
| - }
|
| -
|
| - fFallbackTextContext->drawPosText(dc, fRenderTarget, fClip, paintFallback, skPaintFallback,
|
| - fViewMatrix, (char*)fFallbackIndices.begin(),
|
| + 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 fallbackMatrix(fViewMatrix);
|
| + fallbackMatrix.preScale(fTextInverseRatio, fTextInverseRatio);
|
| +
|
| + 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 @@ void GrStencilAndCoverTextContext::finish(GrDrawContext* dc) {
|
|
|
| SkGlyphCache::AttachCache(fGlyphCache);
|
| fGlyphCache = nullptr;
|
| -
|
| - fViewMatrix = fContextInitialMatrix;
|
| }
|
|
|