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