Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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) {
198 path.moveTo(SkIntToScalar(x), SkIntToScalar(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
340 SkRRect rrect;
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);
425 rc.op(path, SkMatrix::I(), SkClipOp::kIntersect, true);
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
static void imoveTo(SkPath &path, int x, int y)
static SkIRect rand_rect(SkRandom &rand, int n)
static void test_really_a_rect(skiatest::Reporter *reporter)
static void test_huge(skiatest::Reporter *reporter)
static void test_path_bounds(skiatest::Reporter *reporter)
static void make_rand_rgn(SkRegion *rgn, SkRandom &rand)
static bool equalsAAClip(const SkRegion &rgn)
static void did_dx_affect(skiatest::Reporter *reporter, const SkScalar dx[], size_t count, bool changed)
static void rand_irect(SkIRect *r, int N, SkRandom &rand)
static void test_path_with_hole(skiatest::Reporter *reporter)
static void test_regressions()
static void test_rgn(skiatest::Reporter *reporter)
static void icubicTo(SkPath &path, int x0, int y0, int x1, int y1, int x2, int y2)
static bool operator==(const SkMask &a, const SkMask &b)
static void setRgnToPath(SkRegion *rgn, const SkPath &path)
static void test_nearly_integral(skiatest::Reporter *reporter)
static void test_irect(skiatest::Reporter *reporter)
static void test_crbug_422693(skiatest::Reporter *reporter)
static void copyToMask(const SkRegion &rgn, SkMaskBuilder *mask)
static void test_empty(skiatest::Reporter *reporter)
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition DM.cpp:213
reporter
int count
@ 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
static void sk_bzero(void *buffer, size_t size)
Definition SkMalloc.h:105
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:3824
#define SK_Scalar1
Definition SkScalar.h:18
#define SkIntToScalar(x)
Definition SkScalar.h:57
#define DEF_TEST(name, reporter)
Definition Test.h:312
#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 &)
const SkIRect & getBounds() const
Definition SkAAClip.h:33
bool op(const SkIRect &, SkClipOp)
void copyToMask(SkMaskBuilder *) const
Definition SkAAClip.cpp:851
bool setRegion(const SkRegion &)
void drawColor(SkColor color, SkBlendMode mode=SkBlendMode::kSrcOver)
Definition SkCanvas.h:1182
void clipRegion(const SkRegion &deviceRgn, SkClipOp op=SkClipOp::kIntersect)
static const SkMatrix & I()
bool isEmpty() const
Definition SkPath.cpp:406
static SkPath Rect(const SkRect &, SkPathDirection=SkPathDirection::kCW, unsigned startIndex=0)
Definition SkPath.cpp:3518
const SkRect & getBounds() const
Definition SkPath.cpp:420
bool isRect(SkRect *rect, bool *isClosed=nullptr, SkPathDirection *direction=nullptr) const
Definition SkPath.cpp:506
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
SkScalar w
SkScalar h
int32_t height
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
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