| Index: gm/path_stroke_with_zero_length.cpp | 
| diff --git a/gm/path_stroke_with_zero_length.cpp b/gm/path_stroke_with_zero_length.cpp | 
| index dc52947da3b2f79bc07ab6c133b4a5461d4015ed..6f542bac3f2ea0dde3dd77c29069fe009f053d17 100644 | 
| --- a/gm/path_stroke_with_zero_length.cpp | 
| +++ b/gm/path_stroke_with_zero_length.cpp | 
| @@ -9,46 +9,187 @@ | 
| #include "SkStream.h" | 
| #include "gm.h" | 
|  | 
| + | 
| // Test how short paths are stroked with various caps | 
| -DEF_SIMPLE_GM(path_stroke_with_zero_length, canvas, 240, 120) { | 
| -    SkPath paths[5]; | 
| -    paths[0].moveTo(30.0f, 0);  // single line segment | 
| -    paths[0].rLineTo(30.0f, 0); | 
| +class StrokeZeroGM : public skiagm::GM { | 
| +    SkPath fPaths[8]; | 
| +    SkPath fClipL, fClipR, fClipS; | 
| + | 
| +protected: | 
| +    void onOnceBeforeDraw() override { | 
| +        fClipL.moveTo(0, 0); | 
| +        fClipL.lineTo(3, 0); | 
| +        fClipL.lineTo(2.5f, 1); | 
| +        fClipL.lineTo(3.5f, 2.5f); | 
| +        fClipL.lineTo(2.5f, 4); | 
| +        fClipL.lineTo(3, 5); | 
| +        fClipL.lineTo(0, 5); | 
| +        fClipL.close(); | 
| + | 
| +        fClipR.moveTo(34, 0); | 
| +        fClipR.lineTo(34, 5); | 
| +        fClipR.lineTo(31, 5); | 
| +        fClipR.lineTo(30.5, 4); | 
| +        fClipR.lineTo(31.5, 2.5); | 
| +        fClipR.lineTo(30.5, 1); | 
| +        fClipR.lineTo(31, 0); | 
| +        fClipR.close(); | 
| + | 
| +        fClipS.addRect(SkRect::MakeIWH(4, 5)); | 
| + | 
| +        fPaths[0].moveTo(30, 0);  // single line segment | 
| +        fPaths[0].rLineTo(30, 0); | 
| + | 
| +        fPaths[1].moveTo(90, 0);  // single line segment with close (does not draw caps) | 
| +        fPaths[1].rLineTo(30, 0); | 
| +        fPaths[1].close(); | 
| + | 
| +        fPaths[2].moveTo(150, 0);  // zero-length line | 
| +        fPaths[2].rLineTo(0, 0); | 
| + | 
| +        fPaths[3].moveTo(180, 0);  // zero-length line with close (expected not to draw) | 
| +        fPaths[3].rLineTo(0, 0); | 
| +        fPaths[3].close(); | 
| + | 
| +        fPaths[4].moveTo(210, 0);  // close only, no line | 
| +        fPaths[4].close(); | 
| + | 
| +        fPaths[5].moveTo(30, 90);  // all combos below should draw two caps | 
| +        fPaths[5].rLineTo(0, 0); | 
| +        fPaths[5].moveTo(60, 90); | 
| +        fPaths[5].rLineTo(0, 0); | 
| + | 
| +        fPaths[6].moveTo(90, 90); | 
| +        fPaths[6].close(); | 
| +        fPaths[6].moveTo(120, 90); | 
| +        fPaths[6].close(); | 
| + | 
| +        fPaths[7].moveTo(150, 90); | 
| +        fPaths[7].rLineTo(0, 0); | 
| +        fPaths[7].moveTo(180, 90); | 
| +        fPaths[7].close(); | 
| +    } | 
| + | 
| + | 
| +    SkString onShortName() override { | 
| +        return SkString("path_stroke_with_zero_length"); | 
| +    } | 
| + | 
| +    SkISize onISize() override { | 
| +        return SkISize::Make(1120, 840); | 
| +    } | 
|  | 
| -    paths[1].moveTo(90.0f, 0);  // single line segment with close | 
| -    paths[1].rLineTo(30.0f, 0); | 
| -    paths[1].close(); | 
| +    void onDraw(SkCanvas* canvas) override { | 
| +        SkPaint bkgrnd; | 
| +        bkgrnd.setColor(SK_ColorWHITE); | 
| +        canvas->drawRect(SkRect::MakeIWH(onISize().fWidth, onISize().fHeight), bkgrnd); | 
|  | 
| -    paths[2].moveTo(150.0f, 0);  // zero-length line | 
| -    paths[2].rLineTo(0, 0); | 
| +         auto drawPaths = [&](SkPaint& paint, int indexMask) { | 
| +            canvas->translate(0, 30.0f); | 
| +            int index = 0; | 
| +            for (const SkPath& path : fPaths) { | 
| +                if (indexMask & (1 << index)) { | 
| +                    canvas->drawPath(path, paint); | 
| +                } | 
| +                if (paint.getStrokeWidth() < 2) { | 
| +                    drawFat(canvas, path, paint, index); | 
| +                } | 
| +                ++index; | 
| +            } | 
| +        }; | 
|  | 
| -    paths[3].moveTo(180.0f, 0);  // zero-length line with close | 
| -    paths[3].rLineTo(0, 0); | 
| -    paths[3].close(); | 
| +        if (false) { // debugging variant that draws a single element | 
| +            SkScalar width = 0; | 
| +            bool antialias = true; | 
|  | 
| -    paths[4].moveTo(210.0f, 0);  // close only, no line | 
| -    paths[4].close(); | 
| +            SkPaint butt; | 
| +            butt.setAntiAlias(antialias); | 
| +            butt.setStyle(SkPaint::kStroke_Style); | 
| +            butt.setStrokeWidth(width); | 
|  | 
| -    auto drawPaths = [&](const SkPaint& paint) { | 
| -        canvas->translate(0, 30.0f); | 
| -        for (const SkPath& path : paths) { | 
| -            canvas->drawPath(path, paint); | 
| +            SkPaint round(butt); | 
| +            round.setStrokeCap(SkPaint::kRound_Cap); | 
| +            drawPaths(round, 1 << 7); | 
| +            return; | 
| } | 
| -    }; | 
| - | 
| -    SkAutoCanvasRestore autoCanvasRestore(canvas, true); | 
| - | 
| -    SkPaint butt; | 
| -    butt.setStyle(SkPaint::kStroke_Style); | 
| -    butt.setStrokeWidth(20.0f); | 
| -    butt.setStrokeCap(SkPaint::kButt_Cap); | 
| -    drawPaths(butt); | 
| - | 
| -    SkPaint round(butt); | 
| -    round.setStrokeCap(SkPaint::kRound_Cap); | 
| -    drawPaths(round); | 
| - | 
| -    SkPaint square(butt); | 
| -    square.setStrokeCap(SkPaint::kSquare_Cap); | 
| -    drawPaths(square); | 
| -} | 
| + | 
| +        SkScalar widths[] = { 0, .999f, 1, 1.001f, 20 }; | 
| +        bool aliases[] = { false, true }; | 
| +        for (bool antialias : aliases) { | 
| +            canvas->save(); | 
| +            for (SkScalar width : widths) { | 
| +                canvas->save(); | 
| +                SkPaint butt; | 
| +                butt.setAntiAlias(antialias); | 
| +                butt.setStyle(SkPaint::kStroke_Style); | 
| +                butt.setStrokeWidth(width); | 
| +                drawPaths(butt, -1); | 
| + | 
| +                SkPaint round(butt); | 
| +                round.setStrokeCap(SkPaint::kRound_Cap); | 
| +                drawPaths(round, -1); | 
| + | 
| +                SkPaint square(butt); | 
| +                square.setStrokeCap(SkPaint::kSquare_Cap); | 
| +                drawPaths(square, -1); | 
| +                canvas->restore(); | 
| +                canvas->translate(220, 0); | 
| +            } | 
| +            canvas->restore(); | 
| +            canvas->translate(0, 210); | 
| +        } | 
| +    } | 
| + | 
| +private: | 
| +    void drawFat(SkCanvas* canvas, const SkPath& path, const SkPaint& paint, int index) { | 
| +        const SkScalar scale = 10; | 
| +        SkRect bounds = path.getBounds(); | 
| +        SkBitmap offscreen; | 
| +        offscreen.allocN32Pixels(SkScalarRoundToInt(bounds.width() + 4), | 
| +                SkScalarRoundToInt(bounds.height() + 4)); | 
| +        SkScalar pathX = bounds.fLeft - 2; | 
| +        SkScalar pathY = bounds.fTop - 2; | 
| +        SkMatrix cMatrix = canvas->getTotalMatrix(); | 
| +        if (!canvas->readPixels(&offscreen, SkScalarRoundToInt(pathX + cMatrix.getTranslateX()), | 
| +                SkScalarRoundToInt(pathY + cMatrix.getTranslateY()))) { | 
| +            return; | 
| +        } | 
| + | 
| +        canvas->save(); | 
| +        SkMatrix clipM; | 
| +        clipM.reset(); | 
| +        clipM.preScale(scale, scale); | 
| +        clipM.postTranslate(bounds.fLeft - 17, bounds.fTop - 24.5f + 420); | 
| +        SkPath clip; | 
| +        if (index < 2) { | 
| +            fClipL.transform(clipM, &clip); | 
| +        } else { | 
| +            fClipS.transform(clipM, &clip); | 
| +        } | 
| +        canvas->clipPath(clip, SkRegion::kIntersect_Op, true); | 
| +        canvas->scale(scale, scale); | 
| +        canvas->drawBitmap(offscreen, (bounds.fLeft - 17) / scale, | 
| +                    (bounds.fTop - 20 + 420) / scale); | 
| +        canvas->restore(); | 
| + | 
| +        if (bounds.width() > 20) { | 
| +            canvas->save(); | 
| +            clipM.reset(); | 
| +            clipM.preScale(scale, scale); | 
| +            clipM.postTranslate(bounds.fLeft - 17 - 275, bounds.fTop - 24.5f + 420); | 
| +            SkPath clip; | 
| +            fClipR.transform(clipM, &clip); | 
| +            canvas->clipPath(clip, SkRegion::kIntersect_Op, true); | 
| +            canvas->scale(10.f, 10.f); | 
| +            canvas->drawBitmap(offscreen, (bounds.fLeft - 17 - 275 | 
| +                    + (index >= 5 ? 5 : 0)) / scale, (bounds.fTop - 20 + 420) / scale); | 
| +            canvas->restore(); | 
| +        } | 
| +    } | 
| + | 
| +}; | 
| + | 
| +/////////////////////////////////////////////////////////////////////////////// | 
| + | 
| +DEF_GM( return new StrokeZeroGM(); ) | 
| + | 
|  |