| 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; | 
| } | 
|  |