blob: 1f03f89fc6d5ab25bee4e9362ccc1a9b8f05f8c3 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/display_list/geometry/dl_region.h"
#include "gtest/gtest.h"
#include "third_party/skia/include/core/SkRegion.h"
#include <random>
namespace flutter {
namespace testing {
TEST(DisplayListRegion, EmptyRegion) {
DlRegion region;
EXPECT_TRUE(region.isEmpty());
EXPECT_TRUE(region.getRects().empty());
}
TEST(DisplayListRegion, SingleRectangle) {
DlRegion region({SkIRect::MakeLTRB(10, 10, 50, 50)});
auto rects = region.getRects();
ASSERT_EQ(rects.size(), 1u);
EXPECT_EQ(rects.front(), SkIRect::MakeLTRB(10, 10, 50, 50));
}
TEST(DisplayListRegion, NonOverlappingRectangles1) {
std::vector<SkIRect> rects_in;
for (int i = 0; i < 10; ++i) {
SkIRect rect = SkIRect::MakeXYWH(50 * i, 50 * i, 50, 50);
rects_in.push_back(rect);
}
DlRegion region(rects_in);
auto rects = region.getRects();
std::vector<SkIRect> expected{
{0, 0, 50, 50}, {50, 50, 100, 100}, {100, 100, 150, 150},
{150, 150, 200, 200}, {200, 200, 250, 250}, {250, 250, 300, 300},
{300, 300, 350, 350}, {350, 350, 400, 400}, {400, 400, 450, 450},
{450, 450, 500, 500},
};
EXPECT_EQ(rects, expected);
}
TEST(DisplayListRegion, NonOverlappingRectangles2) {
DlRegion region({
SkIRect::MakeXYWH(5, 5, 10, 10),
SkIRect::MakeXYWH(25, 5, 10, 10),
SkIRect::MakeXYWH(5, 25, 10, 10),
SkIRect::MakeXYWH(25, 25, 10, 10),
});
auto rects = region.getRects();
std::vector<SkIRect> expected{
SkIRect::MakeXYWH(5, 5, 10, 10),
SkIRect::MakeXYWH(25, 5, 10, 10),
SkIRect::MakeXYWH(5, 25, 10, 10),
SkIRect::MakeXYWH(25, 25, 10, 10),
};
EXPECT_EQ(rects, expected);
}
TEST(DisplayListRegion, NonOverlappingRectangles3) {
DlRegion region({
SkIRect::MakeXYWH(0, 0, 10, 10),
SkIRect::MakeXYWH(-11, -11, 10, 10),
SkIRect::MakeXYWH(11, 11, 10, 10),
SkIRect::MakeXYWH(-11, 0, 10, 10),
SkIRect::MakeXYWH(0, 11, 10, 10),
SkIRect::MakeXYWH(0, -11, 10, 10),
SkIRect::MakeXYWH(11, 0, 10, 10),
SkIRect::MakeXYWH(11, -11, 10, 10),
SkIRect::MakeXYWH(-11, 11, 10, 10),
});
auto rects = region.getRects();
std::vector<SkIRect> expected{
SkIRect::MakeXYWH(-11, -11, 10, 10), //
SkIRect::MakeXYWH(0, -11, 10, 10), //
SkIRect::MakeXYWH(11, -11, 10, 10), //
SkIRect::MakeXYWH(-11, 0, 10, 10), //
SkIRect::MakeXYWH(0, 0, 10, 10), //
SkIRect::MakeXYWH(11, 0, 10, 10), //
SkIRect::MakeXYWH(-11, 11, 10, 10), //
SkIRect::MakeXYWH(0, 11, 10, 10), //
SkIRect::MakeXYWH(11, 11, 10, 10),
};
EXPECT_EQ(rects, expected);
}
TEST(DisplayListRegion, MergeTouchingRectangles) {
DlRegion region({
SkIRect::MakeXYWH(0, 0, 10, 10),
SkIRect::MakeXYWH(-10, -10, 10, 10),
SkIRect::MakeXYWH(10, 10, 10, 10),
SkIRect::MakeXYWH(-10, 0, 10, 10),
SkIRect::MakeXYWH(0, 10, 10, 10),
SkIRect::MakeXYWH(0, -10, 10, 10),
SkIRect::MakeXYWH(10, 0, 10, 10),
SkIRect::MakeXYWH(10, -10, 10, 10),
SkIRect::MakeXYWH(-10, 10, 10, 10),
});
auto rects = region.getRects();
std::vector<SkIRect> expected{
SkIRect::MakeXYWH(-10, -10, 30, 30),
};
EXPECT_EQ(rects, expected);
}
TEST(DisplayListRegion, OverlappingRectangles) {
std::vector<SkIRect> rects_in;
for (int i = 0; i < 10; ++i) {
SkIRect rect = SkIRect::MakeXYWH(10 * i, 10 * i, 50, 50);
rects_in.push_back(rect);
}
DlRegion region(rects_in);
auto rects = region.getRects();
std::vector<SkIRect> expected{
{0, 0, 50, 10}, {0, 10, 60, 20}, {0, 20, 70, 30},
{0, 30, 80, 40}, {0, 40, 90, 50}, {10, 50, 100, 60},
{20, 60, 110, 70}, {30, 70, 120, 80}, {40, 80, 130, 90},
{50, 90, 140, 100}, {60, 100, 140, 110}, {70, 110, 140, 120},
{80, 120, 140, 130}, {90, 130, 140, 140},
};
EXPECT_EQ(rects, expected);
}
TEST(DisplayListRegion, Deband) {
DlRegion region({
SkIRect::MakeXYWH(0, 0, 50, 50),
SkIRect::MakeXYWH(60, 0, 20, 20),
SkIRect::MakeXYWH(90, 0, 50, 50),
});
auto rects_with_deband = region.getRects(true);
std::vector<SkIRect> expected{
SkIRect::MakeXYWH(60, 0, 20, 20),
SkIRect::MakeXYWH(0, 0, 50, 50),
SkIRect::MakeXYWH(90, 0, 50, 50),
};
EXPECT_EQ(rects_with_deband, expected);
auto rects_without_deband = region.getRects(false);
std::vector<SkIRect> expected_without_deband{
SkIRect::MakeXYWH(0, 0, 50, 20), //
SkIRect::MakeXYWH(60, 0, 20, 20), //
SkIRect::MakeXYWH(90, 0, 50, 20), //
SkIRect::MakeXYWH(0, 20, 50, 30), //
SkIRect::MakeXYWH(90, 20, 50, 30),
};
EXPECT_EQ(rects_without_deband, expected_without_deband);
}
TEST(DisplayListRegion, Intersects1) {
DlRegion region1({
SkIRect::MakeXYWH(0, 0, 20, 20),
SkIRect::MakeXYWH(20, 20, 20, 20),
});
DlRegion region2({
SkIRect::MakeXYWH(20, 0, 20, 20),
SkIRect::MakeXYWH(0, 20, 20, 20),
});
EXPECT_FALSE(region1.intersects(region2));
EXPECT_FALSE(region2.intersects(region1));
EXPECT_TRUE(region1.intersects(region2.bounds()));
EXPECT_TRUE(region2.intersects(region1.bounds()));
EXPECT_TRUE(region1.intersects(SkIRect::MakeXYWH(0, 0, 20, 20)));
EXPECT_FALSE(region1.intersects(SkIRect::MakeXYWH(20, 0, 20, 20)));
EXPECT_TRUE(region1.intersects(
DlRegion(std::vector<SkIRect>{SkIRect::MakeXYWH(0, 0, 20, 20)})));
EXPECT_FALSE(region1.intersects(
DlRegion(std::vector<SkIRect>{SkIRect::MakeXYWH(20, 0, 20, 20)})));
EXPECT_FALSE(region1.intersects(SkIRect::MakeXYWH(-1, -1, 1, 1)));
EXPECT_TRUE(region1.intersects(SkIRect::MakeXYWH(0, 0, 1, 1)));
EXPECT_FALSE(region1.intersects(SkIRect::MakeXYWH(40, 40, 1, 1)));
EXPECT_TRUE(region1.intersects(SkIRect::MakeXYWH(39, 39, 1, 1)));
}
TEST(DisplayListRegion, Intersects2) {
DlRegion region1({
SkIRect::MakeXYWH(-10, -10, 20, 20),
SkIRect::MakeXYWH(-30, -30, 20, 20),
});
DlRegion region2({
SkIRect::MakeXYWH(20, 20, 5, 5),
SkIRect::MakeXYWH(0, 0, 20, 20),
});
EXPECT_TRUE(region1.intersects(region2));
EXPECT_TRUE(region2.intersects(region1));
}
TEST(DisplayListRegion, Intersection1) {
DlRegion region1({
SkIRect::MakeXYWH(0, 0, 20, 20),
SkIRect::MakeXYWH(20, 20, 20, 20),
});
DlRegion region2({
SkIRect::MakeXYWH(20, 0, 20, 20),
SkIRect::MakeXYWH(0, 20, 20, 20),
});
DlRegion i = DlRegion::MakeIntersection(region1, region2);
EXPECT_EQ(i.bounds(), SkIRect::MakeEmpty());
EXPECT_TRUE(i.isEmpty());
auto rects = i.getRects();
EXPECT_TRUE(rects.empty());
}
TEST(DisplayListRegion, Intersection2) {
DlRegion region1({
SkIRect::MakeXYWH(0, 0, 20, 20),
SkIRect::MakeXYWH(20, 20, 20, 20),
});
DlRegion region2({
SkIRect::MakeXYWH(0, 0, 20, 20),
SkIRect::MakeXYWH(20, 20, 20, 20),
});
DlRegion i = DlRegion::MakeIntersection(region1, region2);
EXPECT_EQ(i.bounds(), SkIRect::MakeXYWH(0, 0, 40, 40));
auto rects = i.getRects();
std::vector<SkIRect> expected{
SkIRect::MakeXYWH(0, 0, 20, 20),
SkIRect::MakeXYWH(20, 20, 20, 20),
};
EXPECT_EQ(rects, expected);
}
TEST(DisplayListRegion, Intersection3) {
DlRegion region1({
SkIRect::MakeXYWH(0, 0, 20, 20),
});
DlRegion region2({
SkIRect::MakeXYWH(-10, -10, 20, 20),
SkIRect::MakeXYWH(10, 10, 20, 20),
});
DlRegion i = DlRegion::MakeIntersection(region1, region2);
EXPECT_EQ(i.bounds(), SkIRect::MakeXYWH(0, 0, 20, 20));
auto rects = i.getRects();
std::vector<SkIRect> expected{
SkIRect::MakeXYWH(0, 0, 10, 10),
SkIRect::MakeXYWH(10, 10, 10, 10),
};
EXPECT_EQ(rects, expected);
}
TEST(DisplayListRegion, Union1) {
DlRegion region1({
SkIRect::MakeXYWH(0, 0, 20, 20),
SkIRect::MakeXYWH(20, 20, 20, 20),
});
DlRegion region2({
SkIRect::MakeXYWH(20, 0, 20, 20),
SkIRect::MakeXYWH(0, 20, 20, 20),
});
DlRegion u = DlRegion::MakeUnion(region1, region2);
EXPECT_EQ(u.bounds(), SkIRect::MakeXYWH(0, 0, 40, 40));
EXPECT_TRUE(u.isSimple());
auto rects = u.getRects();
std::vector<SkIRect> expected{
SkIRect::MakeXYWH(0, 0, 40, 40), //
};
EXPECT_EQ(rects, expected);
}
TEST(DisplayListRegion, Union2) {
DlRegion region1({
SkIRect::MakeXYWH(0, 0, 20, 20),
SkIRect::MakeXYWH(21, 21, 20, 20),
});
DlRegion region2({
SkIRect::MakeXYWH(21, 0, 20, 20),
SkIRect::MakeXYWH(0, 21, 20, 20),
});
DlRegion u = DlRegion::MakeUnion(region1, region2);
EXPECT_EQ(u.bounds(), SkIRect::MakeXYWH(0, 0, 41, 41));
auto rects = u.getRects();
std::vector<SkIRect> expected{
SkIRect::MakeXYWH(0, 0, 20, 20),
SkIRect::MakeXYWH(21, 0, 20, 20),
SkIRect::MakeXYWH(0, 21, 20, 20),
SkIRect::MakeXYWH(21, 21, 20, 20),
};
EXPECT_EQ(rects, expected);
}
TEST(DisplayListRegion, Union3) {
DlRegion region1({
SkIRect::MakeXYWH(-10, -10, 20, 20),
});
DlRegion region2({
SkIRect::MakeXYWH(0, 0, 20, 20),
});
DlRegion u = DlRegion::MakeUnion(region1, region2);
EXPECT_EQ(u.bounds(), SkIRect::MakeXYWH(-10, -10, 30, 30));
auto rects = u.getRects();
std::vector<SkIRect> expected{
SkIRect::MakeXYWH(-10, -10, 20, 10),
SkIRect::MakeXYWH(-10, 0, 30, 10),
SkIRect::MakeXYWH(0, 10, 20, 10),
};
EXPECT_EQ(rects, expected);
}
TEST(DisplayListRegion, UnionEmpty) {
{
DlRegion region1(std::vector<SkIRect>{});
DlRegion region2(std::vector<SkIRect>{});
DlRegion u = DlRegion::MakeUnion(region1, region2);
EXPECT_EQ(u.bounds(), SkIRect::MakeEmpty());
EXPECT_TRUE(u.isEmpty());
auto rects = u.getRects();
EXPECT_TRUE(rects.empty());
}
{
DlRegion region1(std::vector<SkIRect>{});
DlRegion region2({
SkIRect::MakeXYWH(0, 0, 20, 20),
});
DlRegion u = DlRegion::MakeUnion(region1, region2);
EXPECT_EQ(u.bounds(), SkIRect::MakeXYWH(0, 0, 20, 20));
auto rects = u.getRects();
std::vector<SkIRect> expected{
SkIRect::MakeXYWH(0, 0, 20, 20),
};
}
{
DlRegion region1({
SkIRect::MakeXYWH(0, 0, 20, 20),
});
DlRegion region2(std::vector<SkIRect>{});
DlRegion u = DlRegion::MakeUnion(region1, region2);
EXPECT_EQ(u.bounds(), SkIRect::MakeXYWH(0, 0, 20, 20));
auto rects = u.getRects();
std::vector<SkIRect> expected{
SkIRect::MakeXYWH(0, 0, 20, 20),
};
}
}
void CheckEquality(const DlRegion& dl_region, const SkRegion& sk_region) {
EXPECT_EQ(dl_region.bounds(), sk_region.getBounds());
// Do not deband the rectangles - identical to SkRegion::Iterator
auto rects = dl_region.getRects(false);
std::vector<SkIRect> skia_rects;
auto iterator = SkRegion::Iterator(sk_region);
while (!iterator.done()) {
skia_rects.push_back(iterator.rect());
iterator.next();
}
EXPECT_EQ(rects, skia_rects);
}
TEST(DisplayListRegion, TestAgainstSkRegion) {
struct Settings {
int max_size;
};
std::vector<Settings> all_settings{{100}, {400}, {800}};
std::vector<size_t> iterations{1, 10, 100, 1000};
for (const auto& settings : all_settings) {
for (const auto iterations_1 : iterations) {
for (const auto iterations_2 : iterations) {
std::random_device d;
std::seed_seq seed{::testing::UnitTest::GetInstance()->random_seed()};
std::mt19937 rng(seed);
SkRegion sk_region1;
SkRegion sk_region2;
std::uniform_int_distribution pos(0, 4000);
std::uniform_int_distribution size(1, settings.max_size);
std::vector<SkIRect> rects_in1;
std::vector<SkIRect> rects_in2;
for (size_t i = 0; i < iterations_1; ++i) {
SkIRect rect =
SkIRect::MakeXYWH(pos(rng), pos(rng), size(rng), size(rng));
rects_in1.push_back(rect);
}
for (size_t i = 0; i < iterations_2; ++i) {
SkIRect rect =
SkIRect::MakeXYWH(pos(rng), pos(rng), size(rng), size(rng));
rects_in2.push_back(rect);
}
DlRegion region1(rects_in1);
sk_region1.setRects(rects_in1.data(), rects_in1.size());
CheckEquality(region1, sk_region1);
DlRegion region2(rects_in2);
sk_region2.setRects(rects_in2.data(), rects_in2.size());
CheckEquality(region2, sk_region2);
auto intersects_1 = region1.intersects(region2);
auto intersects_2 = region2.intersects(region1);
auto sk_intesects = sk_region1.intersects(sk_region2);
EXPECT_EQ(intersects_1, intersects_2);
EXPECT_EQ(intersects_1, sk_intesects);
{
auto rects = region2.getRects(true);
for (const auto& r : rects) {
EXPECT_EQ(region1.intersects(r), sk_region1.intersects(r));
}
}
DlRegion dl_union = DlRegion::MakeUnion(region1, region2);
SkRegion sk_union(sk_region1);
sk_union.op(sk_region2, SkRegion::kUnion_Op);
CheckEquality(dl_union, sk_union);
DlRegion dl_intersection = DlRegion::MakeIntersection(region1, region2);
SkRegion sk_intersection(sk_region1);
sk_intersection.op(sk_region2, SkRegion::kIntersect_Op);
CheckEquality(dl_intersection, sk_intersection);
}
}
}
}
} // namespace testing
} // namespace flutter