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