Flutter Engine
The Flutter Engine
AAClipTest.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2011 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
16#include "include/core/SkPath.h"
18#include "include/core/SkRect.h"
24#include "src/base/SkRandom.h"
25#include "src/core/SkAAClip.h"
26#include "src/core/SkMask.h"
28#include "tests/Test.h"
29
30#include <cstdint>
31#include <cstring>
32#include <initializer_list>
33#include <string>
34
35static bool operator==(const SkMask& a, const SkMask& b) {
36 if (a.fFormat != b.fFormat || a.fBounds != b.fBounds) {
37 return false;
38 }
39 if (!a.fImage && !b.fImage) {
40 return true;
41 }
42 if (!a.fImage || !b.fImage) {
43 return false;
44 }
45
46 size_t wbytes = a.fBounds.width();
47 switch (a.fFormat) {
49 wbytes = (wbytes + 7) >> 3;
50 break;
53 break;
55 wbytes <<= 1;
56 break;
58 wbytes <<= 2;
59 break;
60 default:
61 SkDEBUGFAIL("unknown mask format");
62 return false;
63 }
64
65 const int h = a.fBounds.height();
66 const char* aptr = (const char*)a.fImage;
67 const char* bptr = (const char*)b.fImage;
68 for (int y = 0; y < h; ++y) {
69 if (0 != memcmp(aptr, bptr, wbytes)) {
70 return false;
71 }
72 aptr += wbytes;
73 bptr += wbytes;
74 }
75 return true;
76}
77
78static void copyToMask(const SkRegion& rgn, SkMaskBuilder* mask) {
79 mask->format() = SkMask::kA8_Format;
80
81 if (rgn.isEmpty()) {
82 mask->bounds().setEmpty();
83 mask->rowBytes() = 0;
84 mask->image() = nullptr;
85 return;
86 }
87
88 mask->bounds() = rgn.getBounds();
89 mask->rowBytes() = mask->fBounds.width();
91 sk_bzero(mask->image(), mask->computeImageSize());
92
94 mask->fBounds.height(),
98 bitmap.installPixels(info, mask->image(), mask->fRowBytes);
99
100 // canvas expects its coordinate system to always be 0,0 in the top/left
101 // so we translate the rgn to match that before drawing into the mask.
102 //
103 SkRegion tmpRgn(rgn);
104 tmpRgn.translate(-rgn.getBounds().fLeft, -rgn.getBounds().fTop);
105
106 SkCanvas canvas(bitmap);
107 canvas.clipRegion(tmpRgn);
108 canvas.drawColor(SK_ColorBLACK);
109}
110
111static void copyToMask(const SkRasterClip& rc, SkMaskBuilder* mask) {
112 if (rc.isBW()) {
113 copyToMask(rc.bwRgn(), mask);
114 } else {
115 rc.aaRgn().copyToMask(mask);
116 }
117}
118
119static bool operator==(const SkRasterClip& a, const SkRasterClip& b) {
120 if (a.isEmpty() && b.isEmpty()) {
121 return true;
122 } else if (a.isEmpty() != b.isEmpty() || a.isBW() != b.isBW() || a.isRect() != b.isRect()) {
123 return false;
124 }
125
126 SkMaskBuilder mask0, mask1;
127 copyToMask(a, &mask0);
128 copyToMask(b, &mask1);
129 SkAutoMaskFreeImage free0(mask0.image());
130 SkAutoMaskFreeImage free1(mask1.image());
131 return mask0 == mask1;
132}
133
134static SkIRect rand_rect(SkRandom& rand, int n) {
135 int x = rand.nextS() % n;
136 int y = rand.nextS() % n;
137 int w = rand.nextU() % n;
138 int h = rand.nextU() % n;
139 return SkIRect::MakeXYWH(x, y, w, h);
140}
141
142static void make_rand_rgn(SkRegion* rgn, SkRandom& rand) {
143 int count = rand.nextU() % 20;
144 for (int i = 0; i < count; ++i) {
145 rgn->op(rand_rect(rand, 100), SkRegion::kXOR_Op);
146 }
147}
148
149static bool operator==(const SkRegion& rgn, const SkAAClip& aaclip) {
150 SkMaskBuilder mask0, mask1;
151
152 copyToMask(rgn, &mask0);
153 aaclip.copyToMask(&mask1);
154 SkAutoMaskFreeImage free0(mask0.image());
155 SkAutoMaskFreeImage free1(mask1.image());
156 return mask0 == mask1;
157}
158
159static bool equalsAAClip(const SkRegion& rgn) {
160 SkAAClip aaclip;
161 aaclip.setRegion(rgn);
162 return rgn == aaclip;
163}
164
165static void setRgnToPath(SkRegion* rgn, const SkPath& path) {
166 SkIRect ir;
167 path.getBounds().round(&ir);
168 rgn->setPath(path, SkRegion(ir));
169}
170
171// aaclip.setRegion should create idential masks to the region
173 SkRandom rand;
174 for (int i = 0; i < 1000; i++) {
175 SkRegion rgn;
176 make_rand_rgn(&rgn, rand);
178 }
179
180 {
181 SkRegion rgn;
182 SkPath path;
183 path.addCircle(0, 0, SkIntToScalar(30));
184 setRgnToPath(&rgn, path);
186
187 path.reset();
188 path.moveTo(0, 0);
189 path.lineTo(SkIntToScalar(100), 0);
190 path.lineTo(SkIntToScalar(100 - 20), SkIntToScalar(20));
191 path.lineTo(SkIntToScalar(20), SkIntToScalar(20));
192 setRgnToPath(&rgn, path);
194 }
195}
196
197static void imoveTo(SkPath& path, int x, int y) {
199}
200
201static void icubicTo(SkPath& path, int x0, int y0, int x1, int y1, int x2, int y2) {
202 path.cubicTo(SkIntToScalar(x0), SkIntToScalar(y0),
205}
206
208 SkPath path;
210 const int height = 40;
211 const SkScalar sheight = SkIntToScalar(height);
212
213 path.addOval(SkRect::MakeWH(sheight, sheight));
214 REPORTER_ASSERT(reporter, sheight == path.getBounds().height());
215 clip.setPath(path, path.getBounds().roundOut(), true);
217
218 // this is the trimmed height of this cubic (with aa). The critical thing
219 // for this test is that it is less than height, which represents just
220 // the bounds of the path's control-points.
221 //
222 // This used to fail until we tracked the MinY in the BuilderBlitter.
223 //
224 const int teardrop_height = 12;
225 path.reset();
226 imoveTo(path, 0, 20);
227 icubicTo(path, 40, 40, 40, 0, 0, 20);
228 REPORTER_ASSERT(reporter, sheight == path.getBounds().height());
229 clip.setPath(path, path.getBounds().roundOut(), true);
230 REPORTER_ASSERT(reporter, teardrop_height == clip.getBounds().height());
231}
232
235
238
239 clip.translate(10, 10, &clip); // should have no effect on empty
242
243 SkIRect r = { 10, 10, 40, 50 };
244 clip.setRect(r);
248
249 clip.setEmpty();
252
253 SkMaskBuilder mask;
254 clip.copyToMask(&mask);
255 REPORTER_ASSERT(reporter, nullptr == mask.fImage);
257}
258
259static void rand_irect(SkIRect* r, int N, SkRandom& rand) {
260 r->setXYWH(0, 0, rand.nextU() % N, rand.nextU() % N);
261 int dx = rand.nextU() % (2*N);
262 int dy = rand.nextU() % (2*N);
263 // use int dx,dy to make the subtract be signed
264 r->offset(N - dx, N - dy);
265}
266
268 SkRandom rand;
269
270 for (int i = 0; i < 10000; i++) {
271 SkAAClip clip0, clip1;
272 SkRegion rgn0, rgn1;
273 SkIRect r0, r1;
274
275 rand_irect(&r0, 10, rand);
276 rand_irect(&r1, 10, rand);
277 clip0.setRect(r0);
278 clip1.setRect(r1);
279 rgn0.setRect(r0);
280 rgn1.setRect(r1);
282 SkAAClip clip2 = clip0; // leave clip0 unchanged for future iterations
283 SkRegion rgn2;
284 bool nonEmptyAA = clip2.op(clip1, op);
285 bool nonEmptyBW = rgn2.op(rgn0, rgn1, (SkRegion::Op) op);
286 if (nonEmptyAA != nonEmptyBW || clip2.getBounds() != rgn2.getBounds()) {
287 ERRORF(reporter, "%s %s "
288 "[%d %d %d %d] %s [%d %d %d %d] = BW:[%d %d %d %d] AA:[%d %d %d %d]\n",
289 nonEmptyAA == nonEmptyBW ? "true" : "false",
290 clip2.getBounds() == rgn2.getBounds() ? "true" : "false",
291 r0.fLeft, r0.fTop, r0.right(), r0.bottom(),
292 op == SkClipOp::kDifference ? "DIFF" : "INTERSECT",
293 r1.fLeft, r1.fTop, r1.right(), r1.bottom(),
294 rgn2.getBounds().fLeft, rgn2.getBounds().fTop,
295 rgn2.getBounds().right(), rgn2.getBounds().bottom(),
296 clip2.getBounds().fLeft, clip2.getBounds().fTop,
297 clip2.getBounds().right(), clip2.getBounds().bottom());
298 }
299
300 SkMaskBuilder maskBW, maskAA;
301 copyToMask(rgn2, &maskBW);
302 clip2.copyToMask(&maskAA);
303 SkAutoMaskFreeImage freeBW(maskBW.image());
304 SkAutoMaskFreeImage freeAA(maskAA.image());
305 REPORTER_ASSERT(reporter, maskBW == maskAA);
306 }
307 }
308}
309
311 static const uint8_t gExpectedImage[] = {
312 0xFF, 0xFF, 0xFF, 0xFF,
313 0xFF, 0xFF, 0xFF, 0xFF,
314 0x00, 0x00, 0x00, 0x00,
315 0x00, 0x00, 0x00, 0x00,
316 0xFF, 0xFF, 0xFF, 0xFF,
317 0xFF, 0xFF, 0xFF, 0xFF,
318 };
319 SkMask expected(gExpectedImage, SkIRect::MakeWH(4, 6), 4, SkMask::kA8_Format);
320
321 SkPath path;
322 path.addRect(SkRect::MakeXYWH(0, 0,
324 path.addRect(SkRect::MakeXYWH(0, SkIntToScalar(4),
326
327 for (int i = 0; i < 2; ++i) {
329 clip.setPath(path, path.getBounds().roundOut(), 1 == i);
330
331 SkMaskBuilder mask;
332 clip.copyToMask(&mask);
333 SkAutoMaskFreeImage freeM(mask.image());
334
335 REPORTER_ASSERT(reporter, expected == mask);
336 }
337}
338
341 rrect.setRectXY(SkRect::MakeWH(100, 100), 5, 5);
342
343 SkPath path;
344 path.addRRect(rrect);
345
347 clip.setPath(path, path.getBounds().roundOut(), true);
348
351
352 // This rect should intersect the clip, but slice-out all of the "soft" parts,
353 // leaving just a rect.
354 const SkIRect ir = SkIRect::MakeLTRB(10, -10, 50, 90);
355
357
359 // the clip recognized that that it is just a rect!
361}
362
364 size_t count, bool changed) {
365 SkIRect ir = { 0, 0, 10, 10 };
366
367 for (size_t i = 0; i < count; ++i) {
368 SkRect r;
369 r.set(ir);
370
371 SkRasterClip rc0(ir);
372 SkRasterClip rc1(ir);
373 SkRasterClip rc2(ir);
374
375 rc0.op(r, SkMatrix::I(), SkClipOp::kIntersect, false);
376 r.offset(dx[i], 0);
377 rc1.op(r, SkMatrix::I(), SkClipOp::kIntersect, true);
378 r.offset(-2*dx[i], 0);
379 rc2.op(r, SkMatrix::I(), SkClipOp::kIntersect, true);
380
381 REPORTER_ASSERT(reporter, changed != (rc0 == rc1));
382 REPORTER_ASSERT(reporter, changed != (rc0 == rc2));
383 }
384}
385
387 // All of these should generate equivalent rasterclips
388
389 static const SkScalar gSafeX[] = {
390 0, SK_Scalar1/1000, SK_Scalar1/100, SK_Scalar1/10,
391 };
392 did_dx_affect(reporter, gSafeX, std::size(gSafeX), false);
393
394 static const SkScalar gUnsafeX[] = {
396 };
397 did_dx_affect(reporter, gUnsafeX, std::size(gUnsafeX), true);
398}
399
400static void test_regressions() {
401 // these should not assert in the debug build
402 // bug was introduced in rev. 3209
403 {
405 SkRect r;
406 r.fLeft = 129.892181f;
407 r.fTop = 10.3999996f;
408 r.fRight = 130.892181f;
409 r.fBottom = 20.3999996f;
410 clip.setPath(SkPath::Rect(r), r.roundOut(), true);
411 }
412}
413
414// Building aaclip meant aa-scan-convert a path into a huge clip.
415// the old algorithm sized the supersampler to the size of the clip, which overflowed
416// its internal 16bit coordinates. The fix was to intersect the clip+path_bounds before
417// sizing the supersampler.
418//
419// Before the fix, the following code would assert in debug builds.
420//
422 SkRasterClip rc(SkIRect::MakeLTRB(-25000, -25000, 25000, 25000));
423 SkPath path;
424 path.addCircle(50, 50, 50);
426}
427
430 int big = 0x70000000;
431 SkIRect r = { -big, -big, big, big };
432 SkASSERT(r.width() < 0 && r.height() < 0);
433
434 clip.setRect(r);
435}
436
448}
static void imoveTo(SkPath &path, int x, int y)
Definition: AAClipTest.cpp:197
static SkIRect rand_rect(SkRandom &rand, int n)
Definition: AAClipTest.cpp:134
DEF_TEST(AAClip, reporter)
Definition: AAClipTest.cpp:437
static void test_really_a_rect(skiatest::Reporter *reporter)
Definition: AAClipTest.cpp:339
static void test_huge(skiatest::Reporter *reporter)
Definition: AAClipTest.cpp:428
static void test_path_bounds(skiatest::Reporter *reporter)
Definition: AAClipTest.cpp:207
static void make_rand_rgn(SkRegion *rgn, SkRandom &rand)
Definition: AAClipTest.cpp:142
static bool equalsAAClip(const SkRegion &rgn)
Definition: AAClipTest.cpp:159
static void did_dx_affect(skiatest::Reporter *reporter, const SkScalar dx[], size_t count, bool changed)
Definition: AAClipTest.cpp:363
static void rand_irect(SkIRect *r, int N, SkRandom &rand)
Definition: AAClipTest.cpp:259
static void test_path_with_hole(skiatest::Reporter *reporter)
Definition: AAClipTest.cpp:310
static void test_regressions()
Definition: AAClipTest.cpp:400
static void test_rgn(skiatest::Reporter *reporter)
Definition: AAClipTest.cpp:172
static void icubicTo(SkPath &path, int x0, int y0, int x1, int y1, int x2, int y2)
Definition: AAClipTest.cpp:201
static bool operator==(const SkMask &a, const SkMask &b)
Definition: AAClipTest.cpp:35
static void setRgnToPath(SkRegion *rgn, const SkPath &path)
Definition: AAClipTest.cpp:165
static void test_nearly_integral(skiatest::Reporter *reporter)
Definition: AAClipTest.cpp:386
static void test_irect(skiatest::Reporter *reporter)
Definition: AAClipTest.cpp:267
static void test_crbug_422693(skiatest::Reporter *reporter)
Definition: AAClipTest.cpp:421
static void copyToMask(const SkRegion &rgn, SkMaskBuilder *mask)
Definition: AAClipTest.cpp:78
static void test_empty(skiatest::Reporter *reporter)
Definition: AAClipTest.cpp:233
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
sk_bzero(glyphs, sizeof(glyphs))
reporter
Definition: FontMgrTest.cpp:39
int count
Definition: FontMgrTest.cpp:50
@ kPremul_SkAlphaType
pixel components are premultiplied by alpha
Definition: SkAlphaType.h:29
#define SkDEBUGFAIL(message)
Definition: SkAssert.h:118
#define SkASSERT(cond)
Definition: SkAssert.h:116
SkClipOp
Definition: SkClipOp.h:13
@ kAlpha_8_SkColorType
pixel with alpha in 8-bit byte
Definition: SkColorType.h:21
constexpr SkColor SK_ColorBLACK
Definition: SkColor.h:103
std::unique_ptr< uint8_t, SkFunctionObject< SkMaskBuilder::FreeImage > > SkAutoMaskFreeImage
Definition: SkMask.h:316
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
Definition: SkPath.cpp:3892
#define SK_Scalar1
Definition: SkScalar.h:18
#define SkIntToScalar(x)
Definition: SkScalar.h:57
#define REPORTER_ASSERT(r, cond,...)
Definition: Test.h:286
#define ERRORF(r,...)
Definition: Test.h:293
#define N
Definition: beziers.cpp:19
bool setRect(const SkIRect &)
Definition: SkAAClip.cpp:1271
const SkIRect & getBounds() const
Definition: SkAAClip.h:34
bool op(const SkIRect &, SkClipOp)
Definition: SkAAClip.cpp:1459
void copyToMask(SkMaskBuilder *) const
Definition: SkAAClip.cpp:851
bool setRegion(const SkRegion &)
Definition: SkAAClip.cpp:1313
void drawColor(SkColor color, SkBlendMode mode=SkBlendMode::kSrcOver)
Definition: SkCanvas.h:1182
void clipRegion(const SkRegion &deviceRgn, SkClipOp op=SkClipOp::kIntersect)
Definition: SkCanvas.cpp:1510
static const SkMatrix & I()
Definition: SkMatrix.cpp:1544
Definition: SkPath.h:59
bool isEmpty() const
Definition: SkPath.cpp:416
static SkPath Rect(const SkRect &, SkPathDirection=SkPathDirection::kCW, unsigned startIndex=0)
Definition: SkPath.cpp:3586
const SkRect & getBounds() const
Definition: SkPath.cpp:430
bool isRect(SkRect *rect, bool *isClosed=nullptr, SkPathDirection *direction=nullptr) const
Definition: SkPath.cpp:516
void setRectXY(const SkRect &rect, SkScalar xRad, SkScalar yRad)
Definition: SkRRect.cpp:52
uint32_t nextU()
Definition: SkRandom.h:42
int32_t nextS()
Definition: SkRandom.h:50
bool op(const SkIRect &, SkClipOp)
void translate(int dx, int dy)
Definition: SkRegion.h:349
@ kXOR_Op
target exclusive or with operand
Definition: SkRegion.h:370
const SkIRect & getBounds() const
Definition: SkRegion.h:165
bool op(const SkIRect &rect, Op op)
Definition: SkRegion.h:384
bool setRect(const SkIRect &rect)
Definition: SkRegion.cpp:192
bool isEmpty() const
Definition: SkRegion.h:146
bool setPath(const SkPath &path, const SkRegion &clip)
float SkScalar
Definition: extension.cpp:12
static bool b
struct MyStruct a[10]
double y
double x
SkRRect rrect
Definition: SkRecords.h:232
skia_private::AutoTArray< sk_sp< SkImageFilter > > filters TypedMatrix matrix TypedMatrix matrix SkScalar dx
Definition: SkRecords.h:208
Definition: bitmap.py:1
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
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
SkScalar w
SkScalar h
int32_t height
Definition: SkRect.h:32
static constexpr SkIRect MakeLTRB(int32_t l, int32_t t, int32_t r, int32_t b)
Definition: SkRect.h:91
constexpr int32_t bottom() const
Definition: SkRect.h:134
constexpr int32_t height() const
Definition: SkRect.h:165
constexpr int32_t right() const
Definition: SkRect.h:127
int32_t fTop
smaller y-axis bounds
Definition: SkRect.h:34
constexpr int32_t width() const
Definition: SkRect.h:158
void offset(int32_t dx, int32_t dy)
Definition: SkRect.h:367
void setEmpty()
Definition: SkRect.h:242
static constexpr SkIRect MakeWH(int32_t w, int32_t h)
Definition: SkRect.h:56
bool isEmpty() const
Definition: SkRect.h:202
void setXYWH(int32_t x, int32_t y, int32_t width, int32_t height)
Definition: SkRect.h:268
static constexpr SkIRect MakeXYWH(int32_t x, int32_t y, int32_t w, int32_t h)
Definition: SkRect.h:104
int32_t fLeft
smaller x-axis bounds
Definition: SkRect.h:33
static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at)
Format & format()
Definition: SkMask.h:239
uint32_t & rowBytes()
Definition: SkMask.h:238
static uint8_t * AllocImage(size_t bytes, AllocType=kUninit_Alloc)
Definition: SkMask.cpp:45
SkIRect & bounds()
Definition: SkMask.h:237
uint8_t *& image()
Definition: SkMask.h:236
Definition: SkMask.h:25
const uint32_t fRowBytes
Definition: SkMask.h:43
@ k3D_Format
3 8bit per pixl planes: alpha, mul, add
Definition: SkMask.h:29
@ kA8_Format
8bits per pixel mask (e.g. antialiasing)
Definition: SkMask.h:28
@ kLCD16_Format
565 alpha for r/g/b
Definition: SkMask.h:31
@ kARGB32_Format
SkPMColor.
Definition: SkMask.h:30
@ kBW_Format
1bit per pixel mask (e.g. monochrome)
Definition: SkMask.h:27
uint8_t const *const fImage
Definition: SkMask.h:41
const SkIRect fBounds
Definition: SkMask.h:42
size_t computeImageSize() const
Definition: SkMask.cpp:30
SkScalar fBottom
larger y-axis bounds
Definition: extension.cpp:17
SkScalar fLeft
smaller x-axis bounds
Definition: extension.cpp:14
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition: SkRect.h:659
SkScalar fRight
larger x-axis bounds
Definition: extension.cpp:16
void roundOut(SkIRect *dst) const
Definition: SkRect.h:1241
void offset(float dx, float dy)
Definition: SkRect.h:1016
constexpr float height() const
Definition: SkRect.h:769
bool isEmpty() const
Definition: SkRect.h:693
static constexpr SkRect MakeWH(float w, float h)
Definition: SkRect.h:609
void set(const SkIRect &src)
Definition: SkRect.h:849
SkScalar fTop
smaller y-axis bounds
Definition: extension.cpp:15