18b2bc252faed7c751cf9248c3833c0631f498b7dhalcanary/*
28b2bc252faed7c751cf9248c3833c0631f498b7dhalcanary * Copyright 2015 Google Inc.
38b2bc252faed7c751cf9248c3833c0631f498b7dhalcanary *
48b2bc252faed7c751cf9248c3833c0631f498b7dhalcanary * Use of this source code is governed by a BSD-style license that can be
58b2bc252faed7c751cf9248c3833c0631f498b7dhalcanary * found in the LICENSE file.
68b2bc252faed7c751cf9248c3833c0631f498b7dhalcanary */
78b2bc252faed7c751cf9248c3833c0631f498b7dhalcanary
8ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman#include "SkAutoPixmapStorage.h"
9ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman#include "SkColorPriv.h"
10ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman#include "SkImage.h"
11ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman#include "SkParsePath.h"
128b2bc252faed7c751cf9248c3833c0631f498b7dhalcanary#include "SkPath.h"
13ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman#include "SkSurface.h"
148b2bc252faed7c751cf9248c3833c0631f498b7dhalcanary#include "gm.h"
158b2bc252faed7c751cf9248c3833c0631f498b7dhalcanary
16ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman// GM to test combinations of stroking zero length paths with different caps and other settings
17ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman// Variables:
18ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman// * Antialiasing: On, Off
19ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman// * Caps: Butt, Round, Square
20ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman// * Stroke width: 0, 0.9, 1, 1.1, 15, 25
21ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman// * Path form: M, ML, MLZ, MZ
22ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman// * Path contours: 1 or 2
23ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman// * Path verbs: Line, Quad, Cubic, Conic
24ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman//
25ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman// Each test is drawn to a 50x20 offscreen surface, and expected to produce some number (0 - 2) of
26ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman// visible pieces of cap geometry. These are counted by scanning horizontally for peaks (blobs).
27ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman
28ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osmanstatic bool draw_path_cell(SkCanvas* canvas, SkImage* img, int expectedCaps) {
29ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    // Draw the image
30ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    canvas->drawImage(img, 0, 0);
31ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman
32ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    int w = img->width(), h = img->height();
33ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman
34ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    // Read the pixels back
35ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    SkImageInfo info = SkImageInfo::MakeN32Premul(w, h);
36ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    SkAutoPixmapStorage pmap;
37ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    pmap.alloc(info);
38ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    SkAssertResult(img->readPixels(pmap, 0, 0));
39ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman
40ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    // To account for rasterization differences, we scan the middle two rows [y, y+1] of the image
41ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    SkASSERT(h % 2 == 0);
42ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    int y = (h - 1) / 2;
43ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman
44ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    bool inBlob = false;
45ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    int numBlobs = 0;
46ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    for (int x = 0; x < w; ++x) {
47ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman        // We drew white-on-black. We can look for any non-zero value. Just check red.
48ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman        // And we care if either row is non-zero, so just add them to simplify everything.
49ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman        uint32_t v = SkGetPackedR32(*pmap.addr32(x, y)) + SkGetPackedR32(*pmap.addr32(x, y + 1));
50ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman
51ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman        if (!inBlob && v) {
52ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman            ++numBlobs;
53ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman        }
54ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman        inBlob = SkToBool(v);
55ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    }
561f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark
57ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    SkPaint outline;
58ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    outline.setStyle(SkPaint::kStroke_Style);
59ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    if (numBlobs == expectedCaps) {
60ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman        outline.setColor(0xFF007F00); // Green
61ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    } else if (numBlobs > expectedCaps) {
62ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman        outline.setColor(0xFF7F7F00); // Yellow -- more geometry than expected
63ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    } else {
64ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman        outline.setColor(0xFF7F0000); // Red -- missing some geometry
651f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark    }
661f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark
67ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    canvas->drawRect(SkRect::MakeWH(w, h), outline);
68ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    return numBlobs == expectedCaps;
69ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman}
701f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark
71ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osmanstatic const SkPaint::Cap kCaps[] = {
72ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    SkPaint::kButt_Cap,
73ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    SkPaint::kRound_Cap,
74ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    SkPaint::kSquare_Cap
75ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman};
76ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman
77ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osmanstatic const SkScalar kWidths[] = { 0.0f, 0.9f, 1.0f, 1.1f, 15.0f, 25.0f };
78ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman
79ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman// Full set of path structures for single contour case (each primitive with and without a close)
80ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osmanstatic const char* kAllVerbs[] = {
81ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    nullptr,
82ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    "z ",
83ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    "l 0 0 ",
84ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    "l 0 0 z ",
85ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    "q 0 0 0 0 ",
86ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    "q 0 0 0 0 z ",
87ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    "c 0 0 0 0 0 0 ",
88ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    "c 0 0 0 0 0 0 z ",
89ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    "a 0 0 0 0 0 0 0 ",
90ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    "a 0 0 0 0 0 0 0 z "
91ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman};
92ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman
93ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman// Reduced set of path structures for double contour case, to keep total number of cases down
94ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osmanstatic const char* kSomeVerbs[] = {
95ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    nullptr,
96ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    "z ",
97ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    "l 0 0 ",
98ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    "l 0 0 z ",
99ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    "q 0 0 0 0 ",
100ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    "q 0 0 0 0 z ",
101ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman};
1021f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark
103ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osmanstatic const int kCellWidth = 50;
104ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osmanstatic const int kCellHeight = 20;
105ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osmanstatic const int kCellPad = 2;
106ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman
107ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osmanstatic const int kNumRows = SK_ARRAY_COUNT(kCaps) * SK_ARRAY_COUNT(kWidths);
108ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osmanstatic const int kNumColumns = SK_ARRAY_COUNT(kAllVerbs);
109ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osmanstatic const int kTotalWidth = kNumColumns * (kCellWidth + kCellPad) + kCellPad;
110ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osmanstatic const int kTotalHeight = kNumRows * (kCellHeight + kCellPad) + kCellPad;
111ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman
112ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osmanstatic const int kDblContourNumColums = SK_ARRAY_COUNT(kSomeVerbs) * SK_ARRAY_COUNT(kSomeVerbs);
113ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osmanstatic const int kDblContourTotalWidth = kDblContourNumColums * (kCellWidth + kCellPad) + kCellPad;
114ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman
115ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman// 50% transparent versions of the colors used for positive/negative triage icons on gold.skia.org
116ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osmanstatic const SkColor kFailureRed = 0x7FE7298A;
117ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osmanstatic const SkColor kSuccessGreen = 0x7F1B9E77;
118ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman
119ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osmanstatic void draw_zero_length_capped_paths(SkCanvas* canvas, bool aa) {
120ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    canvas->translate(kCellPad, kCellPad);
121ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman
122ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    SkImageInfo info = canvas->imageInfo().makeWH(kCellWidth, kCellHeight);
123ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    auto surface = canvas->makeSurface(info);
124ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    if (!surface) {
125ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman        surface = SkSurface::MakeRasterN32Premul(kCellWidth, kCellHeight);
1261f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark    }
1278b2bc252faed7c751cf9248c3833c0631f498b7dhalcanary
128ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    SkPaint paint;
129ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    paint.setColor(SK_ColorWHITE);
130ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    paint.setAntiAlias(aa);
131ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    paint.setStyle(SkPaint::kStroke_Style);
1328b2bc252faed7c751cf9248c3833c0631f498b7dhalcanary
133ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    int numFailedTests = 0;
134ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    for (auto cap : kCaps) {
135ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman        for (auto width : kWidths) {
136ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman            paint.setStrokeCap(cap);
137ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman            paint.setStrokeWidth(width);
138ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman            canvas->save();
139515ccafc98d5b26f39f913a6a9606a0dac5c514fBrian Osman
140ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman            for (auto verb : kAllVerbs) {
141ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                SkString pathStr;
142ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                pathStr.appendf("M %f %f ", (kCellWidth - 1) * 0.5f, (kCellHeight - 1) * 0.5f);
143ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                if (verb) {
144ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                    pathStr.append(verb);
1451f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark                }
1468b2bc252faed7c751cf9248c3833c0631f498b7dhalcanary
147ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                SkPath path;
148ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                SkParsePath::FromSVGString(pathStr.c_str(), &path);
1498b2bc252faed7c751cf9248c3833c0631f498b7dhalcanary
150ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                surface->getCanvas()->clear(SK_ColorTRANSPARENT);
151ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                surface->getCanvas()->drawPath(path, paint);
152ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                auto img = surface->makeImageSnapshot();
1538b2bc252faed7c751cf9248c3833c0631f498b7dhalcanary
154ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                // All cases should draw one cap, except for butt capped, and dangling moves
155ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                // (without a verb or close), which shouldn't draw anything.
156ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                int expectedCaps = ((SkPaint::kButt_Cap == cap) || !verb) ? 0 : 1;
1571f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark
158ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                if (!draw_path_cell(canvas, img.get(), expectedCaps)) {
159ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                    ++numFailedTests;
160ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                }
161ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                canvas->translate(kCellWidth + kCellPad, 0);
1621f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark            }
1631f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark            canvas->restore();
164ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman            canvas->translate(0, kCellHeight + kCellPad);
1651f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        }
1661f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark    }
1671f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark
168ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    canvas->drawColor(numFailedTests > 0 ? kFailureRed : kSuccessGreen);
169ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman}
1701f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark
171ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian OsmanDEF_SIMPLE_GM_BG(zero_length_paths_aa, canvas, kTotalWidth, kTotalHeight, SK_ColorBLACK) {
172ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    draw_zero_length_capped_paths(canvas, true);
173ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman}
174ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman
175ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian OsmanDEF_SIMPLE_GM_BG(zero_length_paths_bw, canvas, kTotalWidth, kTotalHeight, SK_ColorBLACK) {
176ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    draw_zero_length_capped_paths(canvas, false);
177ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman}
178ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman
179ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osmanstatic void draw_zero_length_capped_paths_dbl_contour(SkCanvas* canvas, bool aa) {
180ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    canvas->translate(kCellPad, kCellPad);
1811f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark
182ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    SkImageInfo info = canvas->imageInfo().makeWH(kCellWidth, kCellHeight);
183ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    auto surface = canvas->makeSurface(info);
184ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    if (!surface) {
185ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman        surface = SkSurface::MakeRasterN32Premul(kCellWidth, kCellHeight);
186ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    }
187ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman
188ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    SkPaint paint;
189ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    paint.setColor(SK_ColorWHITE);
190ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    paint.setAntiAlias(aa);
191ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    paint.setStyle(SkPaint::kStroke_Style);
192ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman
193ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    int numFailedTests = 0;
194ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    for (auto cap : kCaps) {
195ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman        for (auto width : kWidths) {
196ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman            paint.setStrokeCap(cap);
197ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman            paint.setStrokeWidth(width);
1981f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark            canvas->save();
199ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman
200ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman            for (auto firstVerb : kSomeVerbs) {
201ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                for (auto secondVerb : kSomeVerbs) {
202ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                    int expectedCaps = 0;
203ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman
204ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                    SkString pathStr;
205ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                    pathStr.append("M 9.5 9.5 ");
206ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                    if (firstVerb) {
207ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                        pathStr.append(firstVerb);
208ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                        ++expectedCaps;
209ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                    }
210ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                    pathStr.append("M 40.5 9.5 ");
211ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                    if (secondVerb) {
212ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                        pathStr.append(secondVerb);
213ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                        ++expectedCaps;
214ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                    }
215ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman
216ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                    SkPath path;
217ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                    SkParsePath::FromSVGString(pathStr.c_str(), &path);
218ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman
219ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                    surface->getCanvas()->clear(SK_ColorTRANSPARENT);
220ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                    surface->getCanvas()->drawPath(path, paint);
221ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                    auto img = surface->makeImageSnapshot();
222ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman
223ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                    if (SkPaint::kButt_Cap == cap) {
224ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                        expectedCaps = 0;
225ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                    }
226ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman
227ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                    if (!draw_path_cell(canvas, img.get(), expectedCaps)) {
228ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                        ++numFailedTests;
229ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                    }
230ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                    canvas->translate(kCellWidth + kCellPad, 0);
231ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                }
232ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman            }
2331f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark            canvas->restore();
234ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman            canvas->translate(0, kCellHeight + kCellPad);
2351f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        }
2361f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark    }
2371f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark
238ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    canvas->drawColor(numFailedTests > 0 ? kFailureRed : kSuccessGreen);
239ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman}
2401f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark
241ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian OsmanDEF_SIMPLE_GM_BG(zero_length_paths_dbl_aa, canvas, kDblContourTotalWidth, kTotalHeight,
242ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                 SK_ColorBLACK) {
243ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    draw_zero_length_capped_paths_dbl_contour(canvas, true);
244ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman}
2451f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark
246ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian OsmanDEF_SIMPLE_GM_BG(zero_length_paths_dbl_bw, canvas, kDblContourTotalWidth, kTotalHeight,
247ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman                 SK_ColorBLACK) {
248ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman    draw_zero_length_capped_paths_dbl_contour(canvas, false);
249ee6aa80e88a7b2953e9b6e4dfa6bd44355815f03Brian Osman}
250