Flutter Engine
The Flutter Engine
DrawPathTest.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2012 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
14#include "include/core/SkPath.h"
20#include "include/core/SkRect.h"
27#include "tests/Test.h"
28
29#include <cstdint>
30
31// test that we can draw an aa-rect at coordinates > 32K (bigger than fixedpoint)
34 SkPMColor pixel[1];
35 output.installPixels(SkImageInfo::MakeN32Premul(1, 1), pixel, 4);
36
37 auto surf = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(300, 33300));
38 SkCanvas* canvas = surf->getCanvas();
39
40 SkRect r = { 0, 33000, 300, 33300 };
41 int x = SkScalarRoundToInt(r.left());
42 int y = SkScalarRoundToInt(r.top());
43
44 // check that the pixel in question starts as transparent (by the surface)
45 if (surf->readPixels(output, x, y)) {
46 REPORTER_ASSERT(reporter, 0 == pixel[0]);
47 } else {
48 REPORTER_ASSERT(reporter, false, "readPixels failed");
49 }
50
52 paint.setAntiAlias(true);
53 paint.setColor(SK_ColorWHITE);
54
55 canvas->drawRect(r, paint);
56
57 // Now check that it is BLACK
58 if (surf->readPixels(output, x, y)) {
59 // don't know what swizzling PMColor did, but white should always
60 // appear the same.
61 REPORTER_ASSERT(reporter, 0xFFFFFFFF == pixel[0]);
62 } else {
63 REPORTER_ASSERT(reporter, false, "readPixels failed");
64 }
65}
66
67///////////////////////////////////////////////////////////////////////////////
68
69static void moveToH(SkPath* path, const uint32_t raw[]) {
70 const float* fptr = (const float*)raw;
71 path->moveTo(fptr[0], fptr[1]);
72}
73
74static void cubicToH(SkPath* path, const uint32_t raw[]) {
75 const float* fptr = (const float*)raw;
76 path->cubicTo(fptr[0], fptr[1], fptr[2], fptr[3], fptr[4], fptr[5]);
77}
78
79// This used to assert, because we performed a cast (int)(pt[0].fX * scale) to
80// arrive at an int (SkFDot6) rather than calling sk_float_round2int. The assert
81// was that the initial line-segment produced by the cubic was not monotonically
82// going down (i.e. the initial DY was negative). By rounding the floats, we get
83// the more proper result.
84//
85// http://code.google.com/p/chromium/issues/detail?id=131181
86//
87
88// we're not calling this test anymore; is that for a reason?
89
90static void test_crbug131181() {
91 /*
92 fX = 18.8943768,
93 fY = 129.121277
94 }, {
95 fX = 18.8937435,
96 fY = 129.121689
97 }, {
98 fX = 18.8950119,
99 fY = 129.120422
100 }, {
101 fX = 18.5030727,
102 fY = 129.13121
103 */
104 uint32_t data[] = {
105 0x419727af, 0x43011f0c, 0x41972663, 0x43011f27,
106 0x419728fc, 0x43011ed4, 0x4194064b, 0x43012197
107 };
108
109 SkPath path;
110 moveToH(&path, &data[0]);
111 cubicToH(&path, &data[2]);
112
114
116 paint.setAntiAlias(true);
117 surface->getCanvas()->drawPath(path, paint);
118}
119
120// This used to assert in debug builds (and crash writing bad memory in release)
121// because we overflowed an intermediate value (B coefficient) setting up our
122// stepper for the quadratic. Now we bias that value by 1/2 so we don't overflow
123static void test_crbug_140803() {
124 SkBitmap bm;
125 bm.allocN32Pixels(2700, 30*1024);
126 SkCanvas canvas(bm);
127
129 paint.setAntiAlias(true);
130 canvas.drawPath(SkPath().moveTo(2762, 20).quadTo(11, 21702, 10, 21706), paint);
131}
132
134 SkBitmap bm;
135 bm.allocN32Pixels(256, 256);
136
137 SkCanvas canvas(bm);
138 canvas.clear(SK_ColorWHITE);
139
140 // This creates a single cubic where the control points form an extremely skinny, vertical
141 // triangle contained within the x=0 column of pixels. Since it is convex (ignoring the leading
142 // moveTo's) it uses the convex aaa optimized edge walking algorithm after clipping the path to
143 // the device bounds. However, due to fixed-point math while walking these edges, the edge
144 // walking evaluates to coords that are very slightly less than 0 (i.e. 0.0012). Both the left
145 // and right edges would be out of bounds, but the edge walking is optimized to only clamp the
146 // left edge to the left bounds, and the right edge to the right bounds. After this clamping,
147 // the left and right edges are no longer sorted. This then led to incorrect behavior in various
148 // forms (described below).
149 SkPath path;
150 path.setFillType(SkPathFillType::kWinding);
151 path.moveTo(7.00649e-45f, 2.f);
152 path.moveTo(0.0160219f, 7.45063e-09f);
153 path.moveTo(192.263f, 8.40779e-44f);
154 path.moveTo(7.34684e-40f, 194.25f);
155 path.moveTo(2.3449e-38f, 6.01858e-36f);
156 path.moveTo(7.34684e-40f, 194.25f);
157 path.cubicTo(5.07266e-39f, 56.0488f,
158 0.0119172f, 0.f,
159 7.34684e-40f, 194.25f);
160
162 paint.setColor(SK_ColorRED);
163 paint.setAntiAlias(true);
164 // On debug builds, the inverted left/right edges led to a negative coverage that triggered an
165 // assert while converting to a uint8 alpha value. On release builds with UBSAN, it would
166 // detect a negative left shift when computing the pixel address and crash. On regular release
167 // builds it would write a saturate coverage value to pixels that wrapped around to the far edge
168 canvas.drawPath(path, paint);
169
170 // UBSAN and debug builds would fail inside the drawPath() call above, but detect the incorrect
171 // memory access on release builds so that the test would fail. Given the path, it should only
172 // touch pixels with x=0 but the incorrect addressing would wrap to the right edge.
173 for (int y = 0; y < 256; ++y) {
174 if (bm.getColor(255, y) != SK_ColorWHITE) {
175 REPORTER_ASSERT(reporter, false, "drawPath modified incorrect pixels");
176 break;
177 }
178 }
179}
180
181// Need to exercise drawing an inverse-path whose bounds intersect the clip,
182// but whose edges do not (since its a quad which draws only in the bottom half
183// of its bounds).
184// In the debug build, we used to assert in this case, until it was fixed.
185//
187 SkPath path;
188
189 path.moveTo(0, 20);
190 path.quadTo(10, 10, 20, 20);
191 path.toggleInverseFillType();
192
194
196 SkCanvas* canvas = surface->getCanvas();
197 canvas->save();
198 canvas->clipRect(SkRect::MakeWH(19, 11));
199
200 paint.setAntiAlias(false);
201 canvas->drawPath(path, paint);
202 paint.setAntiAlias(true);
203 canvas->drawPath(path, paint);
204
205 canvas->restore();
206
207 // Now do the test again, with the path flipped, so we only draw in the
208 // top half of our bounds, and have the clip intersect our bounds at the
209 // bottom.
210 path.reset(); // preserves our filltype
211 path.moveTo(0, 10);
212 path.quadTo(10, 20, 20, 10);
213 canvas->clipRect(SkRect::MakeXYWH(0, 19, 19, 11));
214
215 paint.setAntiAlias(false);
216 canvas->drawPath(path, paint);
217 paint.setAntiAlias(true);
218 canvas->drawPath(path, paint);
219}
220
221static void test_bug533() {
222 /*
223 http://code.google.com/p/skia/issues/detail?id=533
224 This particular test/bug only applies to the float case, where the
225 coordinates are very large.
226 */
227 SkPath path;
228 path.moveTo(64, 3);
229 path.quadTo(-329936, -100000000, 1153, 330003);
230
232 paint.setAntiAlias(true);
233
235 surface->getCanvas()->drawPath(path, paint);
236}
237
238static void test_crbug_140642() {
239 /*
240 * We used to see this construct, and due to rounding as we accumulated
241 * our length, the loop where we apply the phase would run off the end of
242 * the array, since it relied on just -= each interval value, which did not
243 * behave as "expected". Now the code explicitly checks for walking off the
244 * end of that array.
245
246 * A different (better) fix might be to rewrite dashing to do all of its
247 * length/phase/measure math using double, but this may need to be
248 * coordinated with SkPathMeasure, to be consistent between the two.
249
250 <path stroke="mintcream" stroke-dasharray="27734 35660 2157846850 247"
251 stroke-dashoffset="-248.135982067">
252 */
253
254 const SkScalar vals[] = { 27734, 35660, 2157846850.0f, 247 };
255 auto dontAssert = SkDashPathEffect::Make(vals, 4, -248.135982067f);
256}
257
258static void test_crbug_124652() {
259 /*
260 http://code.google.com/p/chromium/issues/detail?id=124652
261 This particular test/bug only applies to the float case, where
262 large values can "swamp" small ones.
263 */
264 SkScalar intervals[2] = {837099584, 33450};
265 auto dontAssert = SkDashPathEffect::Make(intervals, 2, -10);
266}
267
268static void test_bigcubic() {
269 SkPath path;
270 path.moveTo(64, 3);
271 path.cubicTo(-329936, -100000000, -329936, 100000000, 1153, 330003);
272
274 paint.setAntiAlias(true);
275
277 surface->getCanvas()->drawPath(path, paint);
278}
279
280// asserts if halfway case is not handled
281static void test_halfway() {
283 SkPath path;
284 path.moveTo(16365.5f, 1394);
285 path.lineTo(16365.5f, 1387.5f);
286 path.quadTo(16365.5f, 1385.43f, 16367, 1383.96f);
287 path.quadTo(16368.4f, 1382.5f, 16370.5f, 1382.5f);
288 path.lineTo(16465.5f, 1382.5f);
289 path.quadTo(16467.6f, 1382.5f, 16469, 1383.96f);
290 path.quadTo(16470.5f, 1385.43f, 16470.5f, 1387.5f);
291 path.lineTo(16470.5f, 1394);
292 path.quadTo(16470.5f, 1396.07f, 16469, 1397.54f);
293 path.quadTo(16467.6f, 1399, 16465.5f, 1399);
294 path.lineTo(16370.5f, 1399);
295 path.quadTo(16368.4f, 1399, 16367, 1397.54f);
296 path.quadTo(16365.5f, 1396.07f, 16365.5f, 1394);
297 path.close();
298 SkPath p2;
299 SkMatrix m;
300 m.reset();
301 m.postTranslate(0.001f, 0.001f);
302 path.transform(m, &p2);
303
305 SkCanvas* canvas = surface->getCanvas();
306 canvas->translate(-16366, -1383);
307 canvas->drawPath(p2, paint);
308
309 m.reset();
310 m.postTranslate(-0.001f, -0.001f);
311 path.transform(m, &p2);
312 canvas->drawPath(p2, paint);
313
314 m.reset();
315 path.transform(m, &p2);
316 canvas->drawPath(p2, paint);
317}
318
319// we used to assert if the bounds of the device (clip) was larger than 32K
320// even when the path itself was smaller. We just draw and hope in the debug
321// version to not assert.
322static void test_giantaa() {
323 const int W = 400;
324 const int H = 400;
326
328 paint.setAntiAlias(true);
329 SkPath path;
330 path.addOval(SkRect::MakeXYWH(-10, -10, 20 + W, 20 + H));
331 surface->getCanvas()->drawPath(path, paint);
332}
333
334// Extremely large path_length/dash_length ratios may cause infinite looping
335// in SkDashPathEffect::filterPath() due to single precision rounding.
336// The test is quite expensive, but it should get much faster after the fix
337// for http://crbug.com/165432 goes in.
339 SkPath path;
340 path.moveTo(0, 0);
341 path.lineTo(5000000, 0);
342
343 SkScalar intervals[] = { 0.2f, 0.2f };
344 sk_sp<SkPathEffect> dash(SkDashPathEffect::Make(intervals, 2, 0));
345
346 SkPath filteredPath;
349 paint.setPathEffect(dash);
350
352 // If we reach this, we passed.
354}
355
356// http://crbug.com/165432
357// Limit extreme dash path effects to avoid exhausting the system memory.
359 SkPath path;
360 path.moveTo(0, 0);
361 path.lineTo(10000000, 0);
362
363 SkScalar intervals[] = { 0.5f, 0.5f };
364 sk_sp<SkPathEffect> dash(SkDashPathEffect::Make(intervals, 2, 0));
365
368 paint.setPathEffect(dash);
369
370 SkPath filteredPath;
371 SkStrokeRec rec(paint);
372 REPORTER_ASSERT(reporter, !dash->filterPath(&filteredPath, path, &rec, nullptr));
373 REPORTER_ASSERT(reporter, filteredPath.isEmpty());
374}
375
376// http://crbug.com/472147
377// This is a simplified version from the bug. RRect radii not properly scaled.
380 SkCanvas* canvas = surface->getCanvas();
381 SkPaint p;
382 SkRect r = SkRect::MakeLTRB(-246.0f, 33.0f, 848.0f, 33554464.0f);
383 SkVector radii[4] = {
384 { 13.0f, 8.0f }, { 170.0f, 2.0 }, { 256.0f, 33554430.0f }, { 120.0f, 5.0f }
385 };
386 SkRRect rr;
387 rr.setRectRadii(r, radii);
388 canvas->drawRRect(rr, p);
389}
390
391// http://crbug.com/472147
392// RRect radii not properly scaled.
395 SkCanvas* canvas = surface->getCanvas();
396 SkPaint p;
397 SkRect r = SkRect::MakeLTRB(-246.0f, 33.0f, 848.0f, 33554464.0f);
398 SkVector radii[4] = {
399 { 13.0f, 8.0f }, { 170.0f, 2.0 }, { 256.0f, 33554430.0f }, { 120.0f, 5.0f }
400 };
401 SkRRect rr;
402 rr.setRectRadii(r, radii);
403 canvas->clipRRect(rr);
404
405 SkRect r2 = SkRect::MakeLTRB(0, 33, 1102, 33554464);
406 canvas->drawRect(r2, p);
407}
408
409DEF_TEST(DrawPath, reporter) {
410 test_giantaa();
411 test_bug533();
417 // why?
418 if ((false)) test_crbug131181();
425 test_halfway();
426}
static void test_crbug_165432(skiatest::Reporter *reporter)
static void moveToH(SkPath *path, const uint32_t raw[])
static void cubicToH(SkPath *path, const uint32_t raw[])
static void test_halfway()
static void test_bigcubic()
static void test_crbug_1239558(skiatest::Reporter *reporter)
static void test_giantaa()
static void test_crbug_472147_actual(skiatest::Reporter *reporter)
static void test_crbug_140803()
static void test_crbug_472147_simple(skiatest::Reporter *reporter)
static void test_crbug_140642()
static void test_crbug131181()
DEF_TEST(DrawPath, reporter)
static void test_bug533()
static void test_crbug_124652()
static void test_big_aa_rect(skiatest::Reporter *reporter)
static void test_infinite_dash(skiatest::Reporter *reporter)
static void test_inversepathwithclip()
reporter
Definition: FontMgrTest.cpp:39
uint32_t SkPMColor
Definition: SkColor.h:205
constexpr SkColor SK_ColorRED
Definition: SkColor.h:126
constexpr SkColor SK_ColorWHITE
Definition: SkColor.h:122
#define SkScalarRoundToInt(x)
Definition: SkScalar.h:37
#define REPORTER_ASSERT(r, cond,...)
Definition: Test.h:286
#define W
Definition: aaa.cpp:17
SkColor getColor(int x, int y) const
Definition: SkBitmap.h:874
void allocN32Pixels(int width, int height, bool isOpaque=false)
Definition: SkBitmap.cpp:232
void drawRect(const SkRect &rect, const SkPaint &paint)
Definition: SkCanvas.cpp:1673
void clipRect(const SkRect &rect, SkClipOp op, bool doAntiAlias)
Definition: SkCanvas.cpp:1361
void restore()
Definition: SkCanvas.cpp:461
void translate(SkScalar dx, SkScalar dy)
Definition: SkCanvas.cpp:1278
void clear(SkColor color)
Definition: SkCanvas.h:1199
void drawRRect(const SkRRect &rrect, const SkPaint &paint)
Definition: SkCanvas.cpp:1705
int save()
Definition: SkCanvas.cpp:447
void drawPath(const SkPath &path, const SkPaint &paint)
Definition: SkCanvas.cpp:1747
void clipRRect(const SkRRect &rrect, SkClipOp op, bool doAntiAlias)
Definition: SkCanvas.cpp:1439
static sk_sp< SkPathEffect > Make(const SkScalar intervals[], int count, SkScalar phase)
@ kStroke_Style
set to stroke geometry
Definition: SkPaint.h:194
bool filterPath(SkPath *dst, const SkPath &src, SkStrokeRec *, const SkRect *cullR) const
Definition: SkPath.h:59
bool isEmpty() const
Definition: SkPath.cpp:416
void setRectRadii(const SkRect &rect, const SkVector radii[4])
Definition: SkRRect.cpp:189
const Paint & paint
Definition: color_source.cc:38
VkSurfaceKHR surface
Definition: main.cc:49
float SkScalar
Definition: extension.cpp:12
double y
double x
SK_API sk_sp< SkSurface > Raster(const SkImageInfo &imageInfo, size_t rowBytes, const SkSurfaceProps *surfaceProps)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
SK_API bool FillPathWithPaint(const SkPath &src, const SkPaint &paint, SkPath *dst, const SkRect *cullRect, SkScalar resScale=1)
Definition: SkPathUtils.cpp:23
Definition: SkMD5.cpp:130
static SkImageInfo MakeN32Premul(int width, int height)
constexpr float left() const
Definition: SkRect.h:734
constexpr float top() const
Definition: SkRect.h:741
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition: SkRect.h:659
static constexpr SkRect MakeWH(float w, float h)
Definition: SkRect.h:609
static constexpr SkRect MakeLTRB(float l, float t, float r, float b)
Definition: SkRect.h:646
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63