|  | /* | 
|  | * Copyright 2011 Google Inc. | 
|  | * | 
|  | * Use of this source code is governed by a BSD-style license that can be | 
|  | * found in the LICENSE file. | 
|  | */ | 
|  |  | 
|  | #include "include/core/SkMatrix.h" | 
|  | #include "include/core/SkPath.h" | 
|  | #include "include/core/SkPathTypes.h" | 
|  | #include "include/core/SkPoint.h" | 
|  | #include "include/core/SkRRect.h" | 
|  | #include "include/core/SkRect.h" | 
|  | #include "include/core/SkRegion.h" | 
|  | #include "include/core/SkScalar.h" | 
|  | #include "include/core/SkTypes.h" | 
|  | #include "include/private/base/SkDebug.h" | 
|  | #include "src/base/SkAutoMalloc.h" | 
|  | #include "src/base/SkRandom.h" | 
|  | #include "src/core/SkScan.h" | 
|  | #include "tests/Test.h" | 
|  |  | 
|  | #include <array> | 
|  | #include <cstddef> | 
|  | #include <cstdint> | 
|  |  | 
|  | static void Union(SkRegion* rgn, const SkIRect& rect) { | 
|  | rgn->op(rect, SkRegion::kUnion_Op); | 
|  | } | 
|  |  | 
|  | #define TEST_NO_INTERSECT(rgn, rect)    REPORTER_ASSERT(reporter, !rgn.intersects(rect)) | 
|  | #define TEST_INTERSECT(rgn, rect)       REPORTER_ASSERT(reporter, rgn.intersects(rect)) | 
|  | #define TEST_NO_CONTAINS(rgn, rect)     REPORTER_ASSERT(reporter, !rgn.contains(rect)) | 
|  |  | 
|  | // inspired by http://code.google.com/p/skia/issues/detail?id=958 | 
|  | // | 
|  | static void test_fromchrome(skiatest::Reporter* reporter) { | 
|  | SkRegion r; | 
|  | Union(&r, SkIRect::MakeXYWH(0, 0, 1, 1)); | 
|  | TEST_NO_INTERSECT(r, SkIRect::MakeXYWH(0, 0, 0, 0)); | 
|  | TEST_INTERSECT(r, SkIRect::MakeXYWH(0, 0, 2, 2)); | 
|  | TEST_INTERSECT(r, SkIRect::MakeXYWH(-1, 0, 2, 2)); | 
|  | TEST_INTERSECT(r, SkIRect::MakeXYWH(-1, -1, 2, 2)); | 
|  | TEST_INTERSECT(r, SkIRect::MakeXYWH(0, -1, 2, 2)); | 
|  | TEST_INTERSECT(r, SkIRect::MakeXYWH(-1, -1, 3, 3)); | 
|  |  | 
|  | Union(&r, SkIRect::MakeXYWH(0, 0, 3, 3)); | 
|  | Union(&r, SkIRect::MakeXYWH(10, 0, 3, 3)); | 
|  | Union(&r, SkIRect::MakeXYWH(0, 10, 13, 3)); | 
|  | TEST_INTERSECT(r, SkIRect::MakeXYWH(-1, -1, 2, 2)); | 
|  | TEST_INTERSECT(r, SkIRect::MakeXYWH(2, -1, 2, 2)); | 
|  | TEST_INTERSECT(r, SkIRect::MakeXYWH(2, 2, 2, 2)); | 
|  | TEST_INTERSECT(r, SkIRect::MakeXYWH(-1, 2, 2, 2)); | 
|  |  | 
|  | TEST_INTERSECT(r, SkIRect::MakeXYWH(9, -1, 2, 2)); | 
|  | TEST_INTERSECT(r, SkIRect::MakeXYWH(12, -1, 2, 2)); | 
|  | TEST_INTERSECT(r, SkIRect::MakeXYWH(12, 2, 2, 2)); | 
|  | TEST_INTERSECT(r, SkIRect::MakeXYWH(9, 2, 2, 2)); | 
|  |  | 
|  | TEST_INTERSECT(r, SkIRect::MakeXYWH(0, -1, 13, 5)); | 
|  | TEST_INTERSECT(r, SkIRect::MakeXYWH(1, -1, 11, 5)); | 
|  | TEST_INTERSECT(r, SkIRect::MakeXYWH(2, -1, 9, 5)); | 
|  | TEST_INTERSECT(r, SkIRect::MakeXYWH(2, -1, 8, 5)); | 
|  | TEST_INTERSECT(r, SkIRect::MakeXYWH(3, -1, 8, 5)); | 
|  |  | 
|  | TEST_INTERSECT(r, SkIRect::MakeXYWH(0, 1, 13, 1)); | 
|  | TEST_INTERSECT(r, SkIRect::MakeXYWH(1, 1, 11, 1)); | 
|  | TEST_INTERSECT(r, SkIRect::MakeXYWH(2, 1, 9, 1)); | 
|  | TEST_INTERSECT(r, SkIRect::MakeXYWH(2, 1, 8, 1)); | 
|  | TEST_INTERSECT(r, SkIRect::MakeXYWH(3, 1, 8, 1)); | 
|  |  | 
|  | TEST_INTERSECT(r, SkIRect::MakeXYWH(0, 0, 13, 13)); | 
|  | TEST_INTERSECT(r, SkIRect::MakeXYWH(0, 1, 13, 11)); | 
|  | TEST_INTERSECT(r, SkIRect::MakeXYWH(0, 2, 13, 9)); | 
|  | TEST_INTERSECT(r, SkIRect::MakeXYWH(0, 2, 13, 8)); | 
|  |  | 
|  |  | 
|  | // These test SkRegion::contains(Rect) and SkRegion::contains(Region) | 
|  |  | 
|  | SkRegion container; | 
|  | Union(&container, SkIRect::MakeXYWH(0, 0, 40, 20)); | 
|  | Union(&container, SkIRect::MakeXYWH(30, 20, 10, 20)); | 
|  | TEST_NO_CONTAINS(container, SkIRect::MakeXYWH(0, 0, 10, 39)); | 
|  | TEST_NO_CONTAINS(container, SkIRect::MakeXYWH(29, 0, 10, 39)); | 
|  |  | 
|  | { | 
|  | SkRegion rgn; | 
|  | Union(&rgn, SkIRect::MakeXYWH(0, 0, 10, 10)); | 
|  | Union(&rgn, SkIRect::MakeLTRB(5, 10, 20, 20)); | 
|  | TEST_INTERSECT(rgn, SkIRect::MakeXYWH(15, 0, 5, 11)); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void test_empties(skiatest::Reporter* reporter) { | 
|  | SkRegion valid(SkIRect::MakeWH(10, 10)); | 
|  | SkRegion empty, empty2; | 
|  |  | 
|  | REPORTER_ASSERT(reporter, empty.isEmpty()); | 
|  | REPORTER_ASSERT(reporter, !valid.isEmpty()); | 
|  |  | 
|  | // test intersects | 
|  | REPORTER_ASSERT(reporter, !empty.intersects(empty2)); | 
|  | REPORTER_ASSERT(reporter, !valid.intersects(empty)); | 
|  |  | 
|  | // test contains | 
|  | REPORTER_ASSERT(reporter, !empty.contains(empty2)); | 
|  | REPORTER_ASSERT(reporter, !valid.contains(empty)); | 
|  | REPORTER_ASSERT(reporter, !empty.contains(valid)); | 
|  |  | 
|  | SkPath emptyPath; | 
|  | emptyPath.moveTo(1, 5); | 
|  | emptyPath.close(); | 
|  | SkRegion openClip; | 
|  | openClip.setRect({-16000, -16000, 16000, 16000}); | 
|  | empty.setPath(emptyPath, openClip);  // should not assert | 
|  | } | 
|  |  | 
|  | enum { | 
|  | W = 256, | 
|  | H = 256 | 
|  | }; | 
|  |  | 
|  | static SkIRect randRect(SkRandom& rand) { | 
|  | int x = rand.nextU() % W; | 
|  | int y = rand.nextU() % H; | 
|  | int w = rand.nextU() % W; | 
|  | int h = rand.nextU() % H; | 
|  | return SkIRect::MakeXYWH(x, y, w >> 1, h >> 1); | 
|  | } | 
|  |  | 
|  | static void randRgn(SkRandom& rand, SkRegion* rgn, int n) { | 
|  | rgn->setEmpty(); | 
|  | for (int i = 0; i < n; ++i) { | 
|  | rgn->op(randRect(rand), SkRegion::kUnion_Op); | 
|  | } | 
|  | } | 
|  |  | 
|  | static bool slow_contains(const SkRegion& outer, const SkRegion& inner) { | 
|  | SkRegion tmp; | 
|  | tmp.op(outer, inner, SkRegion::kUnion_Op); | 
|  | return outer == tmp; | 
|  | } | 
|  |  | 
|  | static bool slow_contains(const SkRegion& outer, const SkIRect& r) { | 
|  | SkRegion tmp; | 
|  | tmp.op(outer, SkRegion(r), SkRegion::kUnion_Op); | 
|  | return outer == tmp; | 
|  | } | 
|  |  | 
|  | static bool slow_intersects(const SkRegion& outer, const SkRegion& inner) { | 
|  | SkRegion tmp; | 
|  | return tmp.op(outer, inner, SkRegion::kIntersect_Op); | 
|  | } | 
|  |  | 
|  | static void test_contains_iter(skiatest::Reporter* reporter, const SkRegion& rgn) { | 
|  | SkRegion::Iterator iter(rgn); | 
|  | while (!iter.done()) { | 
|  | SkIRect r = iter.rect(); | 
|  | REPORTER_ASSERT(reporter, rgn.contains(r)); | 
|  | r.inset(-1, -1); | 
|  | REPORTER_ASSERT(reporter, !rgn.contains(r)); | 
|  | iter.next(); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void contains_proc(skiatest::Reporter* reporter, | 
|  | const SkRegion& a, const SkRegion& b) { | 
|  | // test rgn | 
|  | bool c0 = a.contains(b); | 
|  | bool c1 = slow_contains(a, b); | 
|  | REPORTER_ASSERT(reporter, c0 == c1); | 
|  |  | 
|  | // test rect | 
|  | SkIRect r = a.getBounds(); | 
|  | r.inset(r.width()/4, r.height()/4); | 
|  | c0 = a.contains(r); | 
|  | c1 = slow_contains(a, r); | 
|  | REPORTER_ASSERT(reporter, c0 == c1); | 
|  |  | 
|  | test_contains_iter(reporter, a); | 
|  | test_contains_iter(reporter, b); | 
|  | } | 
|  |  | 
|  | static void test_intersects_iter(skiatest::Reporter* reporter, const SkRegion& rgn) { | 
|  | SkRegion::Iterator iter(rgn); | 
|  | while (!iter.done()) { | 
|  | SkIRect r = iter.rect(); | 
|  | REPORTER_ASSERT(reporter, rgn.intersects(r)); | 
|  | r.inset(-1, -1); | 
|  | REPORTER_ASSERT(reporter, rgn.intersects(r)); | 
|  | iter.next(); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void intersects_proc(skiatest::Reporter* reporter, | 
|  | const SkRegion& a, const SkRegion& b) { | 
|  | bool c0 = a.intersects(b); | 
|  | bool c1 = slow_intersects(a, b); | 
|  | REPORTER_ASSERT(reporter, c0 == c1); | 
|  |  | 
|  | test_intersects_iter(reporter, a); | 
|  | test_intersects_iter(reporter, b); | 
|  | } | 
|  |  | 
|  | static void test_proc(skiatest::Reporter* reporter, | 
|  | void (*proc)(skiatest::Reporter*, | 
|  | const SkRegion& a, const SkRegion&)) { | 
|  | SkRandom rand; | 
|  | for (int i = 0; i < 10000; ++i) { | 
|  | SkRegion outer; | 
|  | randRgn(rand, &outer, 8); | 
|  | SkRegion inner; | 
|  | randRgn(rand, &inner, 2); | 
|  | proc(reporter, outer, inner); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void rand_rect(SkIRect* rect, SkRandom& rand) { | 
|  | int bits = 6; | 
|  | int shift = 32 - bits; | 
|  | rect->setLTRB(rand.nextU() >> shift, rand.nextU() >> shift, | 
|  | rand.nextU() >> shift, rand.nextU() >> shift); | 
|  | rect->sort(); | 
|  | } | 
|  |  | 
|  | static bool test_rects(const SkIRect rect[], int count) { | 
|  | SkRegion rgn0, rgn1; | 
|  |  | 
|  | for (int i = 0; i < count; i++) { | 
|  | rgn0.op(rect[i], SkRegion::kUnion_Op); | 
|  | } | 
|  | rgn1.setRects(rect, count); | 
|  |  | 
|  | if (rgn0 != rgn1) { | 
|  | SkDebugf("\n"); | 
|  | for (int i = 0; i < count; i++) { | 
|  | SkDebugf(" { %d, %d, %d, %d },\n", | 
|  | rect[i].fLeft, rect[i].fTop, | 
|  | rect[i].fRight, rect[i].fBottom); | 
|  | } | 
|  | SkDebugf("\n"); | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | DEF_TEST(Region, reporter) { | 
|  | const SkIRect r2[] = { | 
|  | { 0, 0, 1, 1 }, | 
|  | { 2, 2, 3, 3 }, | 
|  | }; | 
|  | REPORTER_ASSERT(reporter, test_rects(r2, std::size(r2))); | 
|  |  | 
|  | const SkIRect rects[] = { | 
|  | { 0, 0, 1, 2 }, | 
|  | { 2, 1, 3, 3 }, | 
|  | { 4, 0, 5, 1 }, | 
|  | { 6, 0, 7, 4 }, | 
|  | }; | 
|  | REPORTER_ASSERT(reporter, test_rects(rects, std::size(rects))); | 
|  |  | 
|  | SkRandom rand; | 
|  | for (int i = 0; i < 1000; i++) { | 
|  | SkRegion rgn0, rgn1; | 
|  |  | 
|  | const int N = 8; | 
|  | SkIRect rect[N]; | 
|  | for (int j = 0; j < N; j++) { | 
|  | rand_rect(&rect[j], rand); | 
|  | } | 
|  | REPORTER_ASSERT(reporter, test_rects(rect, N)); | 
|  | } | 
|  |  | 
|  | test_proc(reporter, contains_proc); | 
|  | test_proc(reporter, intersects_proc); | 
|  | test_empties(reporter); | 
|  | test_fromchrome(reporter); | 
|  | } | 
|  |  | 
|  | // Test that writeToMemory reports the same number of bytes whether there was a | 
|  | // buffer to write to or not. | 
|  | static void test_write(const SkRegion& region, skiatest::Reporter* r) { | 
|  | const size_t bytesNeeded = region.writeToMemory(nullptr); | 
|  | SkAutoMalloc storage(bytesNeeded); | 
|  | const size_t bytesWritten = region.writeToMemory(storage.get()); | 
|  | REPORTER_ASSERT(r, bytesWritten == bytesNeeded); | 
|  |  | 
|  | // Also check that the bytes are meaningful. | 
|  | SkRegion copy; | 
|  | REPORTER_ASSERT(r, copy.readFromMemory(storage.get(), bytesNeeded)); | 
|  | REPORTER_ASSERT(r, region == copy); | 
|  | } | 
|  |  | 
|  | DEF_TEST(Region_writeToMemory, r) { | 
|  | // Test an empty region. | 
|  | SkRegion region; | 
|  | REPORTER_ASSERT(r, region.isEmpty()); | 
|  | test_write(region, r); | 
|  |  | 
|  | // Test a rectangular region | 
|  | bool nonEmpty = region.setRect({0, 0, 50, 50}); | 
|  | REPORTER_ASSERT(r, nonEmpty); | 
|  | REPORTER_ASSERT(r, region.isRect()); | 
|  | test_write(region, r); | 
|  |  | 
|  | // Test a complex region | 
|  | nonEmpty = region.op({50, 50, 100, 100}, SkRegion::kUnion_Op); | 
|  | REPORTER_ASSERT(r, nonEmpty); | 
|  | REPORTER_ASSERT(r, region.isComplex()); | 
|  | test_write(region, r); | 
|  |  | 
|  | SkRegion complexRegion; | 
|  | Union(&complexRegion, SkIRect::MakeXYWH(0, 0, 1, 1)); | 
|  | Union(&complexRegion, SkIRect::MakeXYWH(0, 0, 3, 3)); | 
|  | Union(&complexRegion, SkIRect::MakeXYWH(10, 0, 3, 3)); | 
|  | Union(&complexRegion, SkIRect::MakeXYWH(0, 10, 13, 3)); | 
|  | test_write(complexRegion, r); | 
|  |  | 
|  | Union(&complexRegion, SkIRect::MakeXYWH(10, 20, 3, 3)); | 
|  | Union(&complexRegion, SkIRect::MakeXYWH(0,  20, 3, 3)); | 
|  | test_write(complexRegion, r); | 
|  | } | 
|  |  | 
|  | DEF_TEST(Region_readFromMemory_bad, r) { | 
|  | // These assume what our binary format is: conceivably we could change it | 
|  | // and might need to remove or change some of these tests. | 
|  | SkRegion region; | 
|  |  | 
|  | { | 
|  | // invalid boundary rectangle | 
|  | int32_t data[5] = {0, 4, 4, 8, 2}; | 
|  | REPORTER_ASSERT(r, 0 == region.readFromMemory(data, sizeof(data))); | 
|  | } | 
|  | // Region Layout, Serialized Format: | 
|  | //    COUNT LEFT TOP RIGHT BOTTOM Y_SPAN_COUNT TOTAL_INTERVAL_COUNT | 
|  | //    Top ( Bottom Span_Interval_Count ( Left Right )* Sentinel )+ Sentinel | 
|  | { | 
|  | // Example of valid data | 
|  | int32_t data[] = {9, 0, 0, 10, 10, 1, 2, 0, 10, 2, 0, 4, 6, 10, | 
|  | 2147483647, 2147483647}; | 
|  | REPORTER_ASSERT(r, 0 != region.readFromMemory(data, sizeof(data))); | 
|  | } | 
|  | { | 
|  | // Example of valid data with 4 intervals | 
|  | int32_t data[] = {19, 0, 0, 30, 30, 3, 4, 0, 10, 2, 0, 10, 20, 30, | 
|  | 2147483647, 20, 0, 2147483647, 30, 2, 0, 10, 20, 30, | 
|  | 2147483647, 2147483647}; | 
|  | REPORTER_ASSERT(r, 0 != region.readFromMemory(data, sizeof(data))); | 
|  | } | 
|  | { | 
|  | // Short count | 
|  | int32_t data[] = {8, 0, 0, 10, 10, 1, 2, 0, 10, 2, 0, 4, 6, 10, | 
|  | 2147483647, 2147483647}; | 
|  | REPORTER_ASSERT(r, 0 == region.readFromMemory(data, sizeof(data))); | 
|  | } | 
|  | { | 
|  | // bounds don't match | 
|  | int32_t data[] = {9, 0, 0, 10, 11, 1, 2, 0, 10, 2, 0, 4, 6, 10, | 
|  | 2147483647, 2147483647}; | 
|  | REPORTER_ASSERT(r, 0 == region.readFromMemory(data, sizeof(data))); | 
|  | } | 
|  | { | 
|  | //  bad yspan count | 
|  | int32_t data[] = {9, 0, 0, 10, 10, 2, 2, 0, 10, 2, 0, 4, 6, 10, | 
|  | 2147483647, 2147483647}; | 
|  | REPORTER_ASSERT(r, 0 == region.readFromMemory(data, sizeof(data))); | 
|  | } | 
|  | { | 
|  | // bad int count | 
|  | int32_t data[] = {9, 0, 0, 10, 10, 1, 3, 0, 10, 2, 0, 4, 6, 10, | 
|  | 2147483647, 2147483647}; | 
|  | REPORTER_ASSERT(r, 0 == region.readFromMemory(data, sizeof(data))); | 
|  | } | 
|  | { | 
|  | // bad final sentinal | 
|  | int32_t data[] = {9, 0, 0, 10, 10, 1, 2, 0, 10, 2, 0, 4, 6, 10, | 
|  | 2147483647, -1}; | 
|  | REPORTER_ASSERT(r, 0 == region.readFromMemory(data, sizeof(data))); | 
|  | } | 
|  | { | 
|  | // bad row sentinal | 
|  | int32_t data[] = {9, 0, 0, 10, 10, 1, 2, 0, 10, 2, 0, 4, 6, 10, | 
|  | -1, 2147483647}; | 
|  | REPORTER_ASSERT(r, 0 == region.readFromMemory(data, sizeof(data))); | 
|  | } | 
|  | { | 
|  | // starts with empty yspan | 
|  | int32_t data[] = {12, 0, 0, 10, 10, 2, 2, -5, 0, 0, 2147483647, 10, | 
|  | 2, 0, 4, 6, 10, 2147483647, 2147483647}; | 
|  | REPORTER_ASSERT(r, 0 == region.readFromMemory(data, sizeof(data))); | 
|  | } | 
|  | { | 
|  | // ends with empty yspan | 
|  | int32_t data[] = {12, 0, 0, 10, 10, 2, 2, 0, 10, 2, 0, 4, 6, 10, | 
|  | 2147483647, 15, 0, 2147483647, 2147483647}; | 
|  | REPORTER_ASSERT(r, 0 == region.readFromMemory(data, sizeof(data))); | 
|  | } | 
|  | { | 
|  | // y intervals out of order | 
|  | int32_t data[] = {19, 0, -20, 30, 10, 3, 4, 0, 10, 2, 0, 10, 20, 30, | 
|  | 2147483647, -20, 0, 2147483647, -10, 2, 0, 10, 20, 30, | 
|  | 2147483647, 2147483647}; | 
|  | REPORTER_ASSERT(r, 0 == region.readFromMemory(data, sizeof(data))); | 
|  | } | 
|  | { | 
|  | // x intervals out of order | 
|  | int32_t data[] = {9, 0, 0, 10, 10, 1, 2, 0, 10, 2, 6, 10, 0, 4, | 
|  | 2147483647, 2147483647}; | 
|  | REPORTER_ASSERT(r, 0 == region.readFromMemory(data, sizeof(data))); | 
|  | } | 
|  | } | 
|  |  | 
|  | DEF_TEST(region_toobig, reporter) { | 
|  | const int big = 1 << 30; | 
|  | const SkIRect neg = SkIRect::MakeXYWH(-big, -big, 10, 10); | 
|  | const SkIRect pos = SkIRect::MakeXYWH( big,  big, 10, 10); | 
|  |  | 
|  | REPORTER_ASSERT(reporter, !neg.isEmpty()); | 
|  | REPORTER_ASSERT(reporter, !pos.isEmpty()); | 
|  |  | 
|  | SkRegion negR(neg); | 
|  | SkRegion posR(pos); | 
|  |  | 
|  | REPORTER_ASSERT(reporter, !negR.isEmpty()); | 
|  | REPORTER_ASSERT(reporter, !posR.isEmpty()); | 
|  |  | 
|  | SkRegion rgn; | 
|  | rgn.op(negR, posR, SkRegion::kUnion_Op); | 
|  |  | 
|  | // If we union those to rectangles, the resulting coordinates span more than int32_t, so | 
|  | // we must mark the region as empty. | 
|  | REPORTER_ASSERT(reporter, rgn.isEmpty()); | 
|  | } | 
|  |  | 
|  | DEF_TEST(region_inverse_union_skbug_7491, reporter) { | 
|  | SkPath path; | 
|  | path.setFillType(SkPathFillType::kInverseWinding); | 
|  | path.moveTo(10, 20); path.lineTo(10, 30); path.lineTo(10.1f, 10); path.close(); | 
|  |  | 
|  | SkRegion clip; | 
|  | clip.op(SkIRect::MakeLTRB(10, 10, 15, 20), SkRegion::kUnion_Op); | 
|  | clip.op(SkIRect::MakeLTRB(20, 10, 25, 20), SkRegion::kUnion_Op); | 
|  |  | 
|  | SkRegion rgn; | 
|  | rgn.setPath(path, clip); | 
|  |  | 
|  | REPORTER_ASSERT(reporter, clip == rgn); | 
|  | } | 
|  |  | 
|  | DEF_TEST(giant_path_region, reporter) { | 
|  | const SkScalar big = 32767; | 
|  | SkPath path; | 
|  | path.moveTo(-big, 0); | 
|  | path.quadTo(big, 0, big, big); | 
|  | SkIRect ir = path.getBounds().round(); | 
|  | SkRegion rgn; | 
|  | rgn.setPath(path, SkRegion(ir)); | 
|  | } | 
|  |  | 
|  | DEF_TEST(rrect_region_crbug_850350, reporter) { | 
|  | SkMatrix m; | 
|  | m.reset(); | 
|  | m[1] = 0.753662348f; | 
|  | m[3] = 1.40079998E+20f; | 
|  |  | 
|  | const SkPoint corners[] = { | 
|  | { 2.65876e-19f, 0.0194088f }, | 
|  | { 4896, 0.00114702f }, | 
|  | { 0, 0 }, | 
|  | { 0.00114702f, 0.00495333f }, | 
|  | }; | 
|  | SkRRect rrect; | 
|  | rrect.setRectRadii({-8.72387e-31f, 1.29996e-38f, 4896, 1.125f}, corners); | 
|  |  | 
|  | SkPath path; | 
|  | path.addRRect(rrect); | 
|  | path.transform(m); | 
|  |  | 
|  | SkRegion rgn; | 
|  | rgn.setPath(path, SkRegion{SkIRect{0, 0, 24, 24}}); | 
|  | } | 
|  |  | 
|  | DEF_TEST(region_bug_chromium_873051, reporter) { | 
|  | SkRegion region; | 
|  | REPORTER_ASSERT(reporter,  region.setRect({0, 0, 0x7FFFFFFE, 0x7FFFFFFE})); | 
|  | REPORTER_ASSERT(reporter, !region.setRect({0, 0, 0x7FFFFFFE, 0x7FFFFFFF})); | 
|  | REPORTER_ASSERT(reporter, !region.setRect({0, 0, 0x7FFFFFFF, 0x7FFFFFFE})); | 
|  | REPORTER_ASSERT(reporter, !region.setRect({0, 0, 0x7FFFFFFF, 0x7FFFFFFF})); | 
|  | } | 
|  |  | 
|  | DEF_TEST(region_empty_iter, reporter) { | 
|  | SkRegion::Iterator emptyIter; | 
|  | REPORTER_ASSERT(reporter, !emptyIter.rewind()); | 
|  | REPORTER_ASSERT(reporter, emptyIter.done()); | 
|  | auto eRect = emptyIter.rect(); | 
|  | REPORTER_ASSERT(reporter, eRect.isEmpty()); | 
|  | REPORTER_ASSERT(reporter, SkIRect::MakeEmpty() == eRect); | 
|  | REPORTER_ASSERT(reporter, !emptyIter.rgn()); | 
|  |  | 
|  | SkRegion region; | 
|  | SkRegion::Iterator resetIter; | 
|  | resetIter.reset(region); | 
|  | REPORTER_ASSERT(reporter, resetIter.rewind()); | 
|  | REPORTER_ASSERT(reporter, resetIter.done()); | 
|  | auto rRect = resetIter.rect(); | 
|  | REPORTER_ASSERT(reporter, rRect.isEmpty()); | 
|  | REPORTER_ASSERT(reporter, SkIRect::MakeEmpty() == rRect); | 
|  | REPORTER_ASSERT(reporter, resetIter.rgn()); | 
|  | REPORTER_ASSERT(reporter, resetIter.rgn()->isEmpty()); | 
|  |  | 
|  | SkRegion::Iterator iter(region); | 
|  | REPORTER_ASSERT(reporter, iter.done()); | 
|  | auto iRect = iter.rect(); | 
|  | REPORTER_ASSERT(reporter, iRect.isEmpty()); | 
|  | REPORTER_ASSERT(reporter, SkIRect::MakeEmpty() == iRect); | 
|  | REPORTER_ASSERT(reporter, iter.rgn()); | 
|  | REPORTER_ASSERT(reporter, iter.rgn()->isEmpty()); | 
|  |  | 
|  | SkRegion::Cliperator clipIter(region, {0, 0, 100, 100}); | 
|  | REPORTER_ASSERT(reporter, clipIter.done()); | 
|  | auto cRect = clipIter.rect(); | 
|  | REPORTER_ASSERT(reporter, cRect.isEmpty()); | 
|  | REPORTER_ASSERT(reporter, SkIRect::MakeEmpty() == cRect); | 
|  |  | 
|  | SkRegion::Spanerator spanIter(region, 0, 0, 100); | 
|  | int left = 0, right = 0; | 
|  | REPORTER_ASSERT(reporter, !spanIter.next(&left, &right)); | 
|  | REPORTER_ASSERT(reporter, !left); | 
|  | REPORTER_ASSERT(reporter, !right); | 
|  | } | 
|  |  | 
|  | DEF_TEST(region_very_large, reporter) { | 
|  | SkIRect clipBounds = {-45000, -45000, 45000, 45000}; | 
|  | REPORTER_ASSERT(reporter, SkScan::PathRequiresTiling(clipBounds)); | 
|  |  | 
|  | // Create a path that is larger than the scan conversion limits of SkScan, which is internally | 
|  | // used to convert a path to a region. | 
|  | SkPath largePath = SkPath::RRect(SkRRect::MakeRectXY(SkRect::Make(clipBounds), 200.f, 200.f)); | 
|  |  | 
|  | SkRegion largeRegion; | 
|  | REPORTER_ASSERT(reporter, largeRegion.setPath(largePath, SkRegion{clipBounds})); | 
|  |  | 
|  | // The path should have been converted successfully, so the corners of clipBounds should not be | 
|  | // contained due to the path's rounded corners. | 
|  | REPORTER_ASSERT(reporter, !largeRegion.contains(-44995, -44995)); | 
|  | REPORTER_ASSERT(reporter, !largeRegion.contains(-44995,  44995)); | 
|  | REPORTER_ASSERT(reporter, !largeRegion.contains( 44995, -44995)); | 
|  | REPORTER_ASSERT(reporter, !largeRegion.contains( 44995,  44995)); | 
|  |  | 
|  | // But these points should be within the rounded corners. | 
|  | REPORTER_ASSERT(reporter, largeRegion.contains(-44600, -44600)); | 
|  | REPORTER_ASSERT(reporter, largeRegion.contains(-44600,  44600)); | 
|  | REPORTER_ASSERT(reporter, largeRegion.contains( 44600, -44600)); | 
|  | REPORTER_ASSERT(reporter, largeRegion.contains( 44600,  44600)); | 
|  |  | 
|  | // Make another path shaped like a D, so two corners will have its large radii and the other two | 
|  | // will be rectangular and thus clipped by the original region. | 
|  | static const SkVector kLargeRadii[4] = { {0.f, 0.f},       // TL | 
|  | {2000.f, 2000.f}, // TR | 
|  | {2000.f, 2000.f}, // BR | 
|  | {0.f, 0.f}};      // BL | 
|  | SkRRect largeRRect; | 
|  | largeRRect.setRectRadii(SkRect::Make(clipBounds), kLargeRadii); | 
|  | REPORTER_ASSERT(reporter, largeRegion.setPath(SkPath::RRect(largeRRect), SkRegion{largeRegion})); | 
|  |  | 
|  | REPORTER_ASSERT(reporter, !largeRegion.contains(-44995, -44995)); | 
|  | REPORTER_ASSERT(reporter, !largeRegion.contains(-44995,  44995)); | 
|  | REPORTER_ASSERT(reporter, !largeRegion.contains( 44995, -44995)); | 
|  | REPORTER_ASSERT(reporter, !largeRegion.contains( 44995,  44995)); | 
|  |  | 
|  | REPORTER_ASSERT(reporter, largeRegion.contains(-44600, -44600)); | 
|  | REPORTER_ASSERT(reporter, largeRegion.contains(-44600,  44600)); | 
|  | // Right side has been clipped by an even larger corner radii | 
|  | REPORTER_ASSERT(reporter, !largeRegion.contains( 44600, -44600)); | 
|  | REPORTER_ASSERT(reporter, !largeRegion.contains( 44600,  44600)); | 
|  |  | 
|  | // Now test that the very large path with a small clip also works | 
|  | largePath = SkPath::RRect(SkRRect::MakeRectXY({0.f, 0.f, 45000.f, 45000.f}, 200.f, 200.f)); | 
|  | SkRegion smallRegion; | 
|  | REPORTER_ASSERT(reporter, smallRegion.setPath(largePath, SkRegion{{0, 0, 500, 500}})); | 
|  |  | 
|  | REPORTER_ASSERT(reporter, !smallRegion.contains(5, 5)); | 
|  | REPORTER_ASSERT(reporter, smallRegion.contains(0, 499)); | 
|  | REPORTER_ASSERT(reporter, smallRegion.contains(499, 0)); | 
|  | REPORTER_ASSERT(reporter, smallRegion.contains(499, 499)); | 
|  | } |