diff options
author | danakj@chromium.org <danakj@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-08-22 01:16:43 +0000 |
---|---|---|
committer | danakj@chromium.org <danakj@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-08-22 01:21:15 +0000 |
commit | d5467eb7835eccd7b1e1a65096c9b1f37c70fcb2 (patch) | |
tree | db295fcc90bc936cf52818bffd73d7560c80f0f3 /cc/base | |
parent | 903cf847d2d66f33e1f2cdab5f45a1b07558fa7a (diff) | |
download | chromium_src-d5467eb7835eccd7b1e1a65096c9b1f37c70fcb2.zip chromium_src-d5467eb7835eccd7b1e1a65096c9b1f37c70fcb2.tar.gz chromium_src-d5467eb7835eccd7b1e1a65096c9b1f37c70fcb2.tar.bz2 |
cc: Replace Region with SimpleEnclosedRegion for occlusion tracking
Instead of using an arbitrary Region, which is costly (slow), use
a new SimpleEnclosedRegion. This class tracks only a single Rect
at a given time so it is very fast and small. It tries to get
something like the largest rect enclosed in the actual Region
(were we to track such a Region) in an online fashion, ie it
doesn't remember anything except its current largest possible
rect.
BUG=405663
Review URL: https://codereview.chromium.org/202523002
Cr-Commit-Position: refs/heads/master@{#291292}
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@291292 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'cc/base')
-rw-r--r-- | cc/base/region.cc | 9 | ||||
-rw-r--r-- | cc/base/region.h | 3 | ||||
-rw-r--r-- | cc/base/simple_enclosed_region.cc | 136 | ||||
-rw-r--r-- | cc/base/simple_enclosed_region.h | 122 | ||||
-rw-r--r-- | cc/base/simple_enclosed_region_unittest.cc | 637 |
5 files changed, 906 insertions, 1 deletions
diff --git a/cc/base/region.cc b/cc/base/region.cc index 79c30ec..2484cc2 100644 --- a/cc/base/region.cc +++ b/cc/base/region.cc @@ -3,8 +3,10 @@ // found in the LICENSE file. #include "cc/base/region.h" + #include "base/debug/trace_event_argument.h" #include "base/values.h" +#include "cc/base/simple_enclosed_region.h" namespace cc { @@ -80,6 +82,13 @@ void Region::Subtract(const Region& region) { skregion_.op(region.skregion_, SkRegion::kDifference_Op); } +void Region::Subtract(const SimpleEnclosedRegion& region) { + for (size_t i = 0; i < region.GetRegionComplexity(); ++i) { + skregion_.op(gfx::RectToSkIRect(region.GetRect(i)), + SkRegion::kDifference_Op); + } +} + void Region::Union(const gfx::Rect& rect) { skregion_.op(gfx::RectToSkIRect(rect), SkRegion::kUnion_Op); } diff --git a/cc/base/region.h b/cc/base/region.h index 3ef32db..d78d4cb 100644 --- a/cc/base/region.h +++ b/cc/base/region.h @@ -7,7 +7,6 @@ #include <string> -#include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "cc/base/cc_export.h" #include "third_party/skia/include/core/SkRegion.h" @@ -22,6 +21,7 @@ class TracedValue; } namespace cc { +class SimpleEnclosedRegion; class CC_EXPORT Region { public: @@ -47,6 +47,7 @@ class CC_EXPORT Region { void Subtract(const gfx::Rect& rect); void Subtract(const Region& region); + void Subtract(const SimpleEnclosedRegion& region); void Union(const gfx::Rect& rect); void Union(const Region& region); void Intersect(const gfx::Rect& rect); diff --git a/cc/base/simple_enclosed_region.cc b/cc/base/simple_enclosed_region.cc new file mode 100644 index 0000000..e2683f3 --- /dev/null +++ b/cc/base/simple_enclosed_region.cc @@ -0,0 +1,136 @@ +// Copyright 2014 The Chromium 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 "cc/base/simple_enclosed_region.h" + +#include "base/logging.h" +#include "cc/base/region.h" + +namespace cc { + +static bool RectIsLargerArea(const gfx::Rect& a, const gfx::Rect b) { + int64 a_area = static_cast<int64>(a.width()) * a.height(); + int64 b_area = static_cast<int64>(b.width()) * b.height(); + return a_area > b_area; +} + +SimpleEnclosedRegion::SimpleEnclosedRegion(const Region& region) { + for (Region::Iterator it(region); it.has_rect(); it.next()) + Union(it.rect()); +} + +SimpleEnclosedRegion::~SimpleEnclosedRegion() { +} + +void SimpleEnclosedRegion::Subtract(const gfx::Rect& sub_rect) { + // We want to keep as much of the current rect as we can, so find the one + // largest rectangle inside |rect_| that does not intersect with |sub_rect|. + if (!rect_.Intersects(sub_rect)) + return; + if (sub_rect.Contains(rect_)) { + rect_ = gfx::Rect(); + return; + } + + int left = rect_.x(); + int right = rect_.right(); + int top = rect_.y(); + int bottom = rect_.bottom(); + + int delta_left = sub_rect.x() - left; + int delta_right = right - sub_rect.right(); + int delta_top = sub_rect.y() - top; + int delta_bottom = bottom - sub_rect.bottom(); + + // The horizontal rect is the larger of the two rectangles above or below + // |sub_rect| and inside rect_. + int horizontal_top = top; + int horizontal_bottom = bottom; + if (delta_top > delta_bottom) + horizontal_bottom = sub_rect.y(); + else + horizontal_top = sub_rect.bottom(); + // The vertical rect is the larger of the two rectangles to the left or the + // right of |sub_rect| and inside rect_. + int vertical_left = left; + int vertical_right = right; + if (delta_left > delta_right) + vertical_right = sub_rect.x(); + else + vertical_left = sub_rect.right(); + + rect_.SetRect( + left, horizontal_top, right - left, horizontal_bottom - horizontal_top); + + gfx::Rect vertical_rect( + vertical_left, top, vertical_right - vertical_left, bottom - top); + if (RectIsLargerArea(vertical_rect, rect_)) + rect_ = vertical_rect; +} + +void SimpleEnclosedRegion::Union(const gfx::Rect& new_rect) { + // We want to keep track of a region but bound its complexity at a constant + // size. We keep track of the largest rectangle seen by area. If we can add + // the |new_rect| to this rectangle then we do that, as that is the cheapest + // way to increase the area returned without increasing the complexity. + if (new_rect.IsEmpty()) + return; + if (rect_.Contains(new_rect)) + return; + if (new_rect.Contains(rect_)) { + rect_ = new_rect; + return; + } + + int left = rect_.x(); + int top = rect_.y(); + int right = rect_.right(); + int bottom = rect_.bottom(); + + int new_left = new_rect.x(); + int new_top = new_rect.y(); + int new_right = new_rect.right(); + int new_bottom = new_rect.bottom(); + + // This attempts to expand each edge of |rect_| if the |new_rect| entirely + // covers or is adjacent to an entire edge of |rect_|. If this is true for + // an edge of |rect_| then it can be expanded out to share that edge with the + // same edge of |new_rect|. After, the same thing is done to try expand + // |new_rect| relative to |rect_|. + if (new_top <= top && new_bottom >= bottom) { + if (new_left < left && new_right >= left) + left = new_left; + if (new_right > right && new_left <= right) + right = new_right; + } else if (new_left <= left && new_right >= right) { + if (new_top < top && new_bottom >= top) + top = new_top; + if (new_bottom > bottom && new_top <= bottom) + bottom = new_bottom; + } else if (top <= new_top && bottom >= new_bottom) { + if (left < new_left && right >= new_left) + new_left = left; + if (right > new_right && left <= new_right) + new_right = right; + } else if (left <= new_left && right >= new_right) { + if (top < new_top && bottom >= new_top) + new_top = top; + if (bottom > new_bottom && top <= new_bottom) + new_bottom = bottom; + } + + rect_.SetRect(left, top, right - left, bottom - top); + + gfx::Rect adjusted_new_rect( + new_left, new_top, new_right - new_left, new_bottom - new_top); + if (RectIsLargerArea(adjusted_new_rect, rect_)) + rect_ = adjusted_new_rect; +} + +gfx::Rect SimpleEnclosedRegion::GetRect(size_t i) const { + DCHECK_LT(i, GetRegionComplexity()); + return rect_; +} + +} // namespace cc diff --git a/cc/base/simple_enclosed_region.h b/cc/base/simple_enclosed_region.h new file mode 100644 index 0000000..c9625ba --- /dev/null +++ b/cc/base/simple_enclosed_region.h @@ -0,0 +1,122 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_BASE_SIMPLE_ENCLOSED_REGION_H_ +#define CC_BASE_SIMPLE_ENCLOSED_REGION_H_ + +#include <string> + +#include "cc/base/cc_export.h" +#include "ui/gfx/rect.h" + +namespace cc { + +class Region; + +// A constant-sized approximation of a Region. The SimpleEnclosedRegion may +// exclude points in its approximation (may have false negatives) but will never +// include a point that would not be in the actual Region (no false positives). +class CC_EXPORT SimpleEnclosedRegion { + public: + SimpleEnclosedRegion() : rect_() {} + SimpleEnclosedRegion(const SimpleEnclosedRegion& region) + : rect_(region.rect_) {} + explicit SimpleEnclosedRegion(const gfx::Rect& rect) : rect_(rect) {} + SimpleEnclosedRegion(int x, int y, int w, int h) : rect_(x, y, w, h) {} + SimpleEnclosedRegion(int w, int h) : rect_(w, h) {} + explicit SimpleEnclosedRegion(const Region& region); + ~SimpleEnclosedRegion(); + + const SimpleEnclosedRegion& operator=(const gfx::Rect& rect) { + rect_ = rect; + return *this; + } + const SimpleEnclosedRegion& operator=(const SimpleEnclosedRegion& region) { + rect_ = region.rect_; + return *this; + } + + bool IsEmpty() const { return rect_.IsEmpty(); } + void Clear() { rect_ = gfx::Rect(); } + size_t GetRegionComplexity() const { return rect_.IsEmpty() ? 0 : 1; } + + bool Contains(const gfx::Point& point) const { return rect_.Contains(point); } + bool Contains(const gfx::Rect& rect) const { return rect_.Contains(rect); } + bool Contains(const SimpleEnclosedRegion& region) const { + return rect_.Contains(region.rect_); + } + + bool Intersects(const gfx::Rect& rect) const { + return rect_.Intersects(rect); + } + bool Intersects(const SimpleEnclosedRegion& region) const { + return rect_.Intersects(region.rect_); + } + + void Subtract(const gfx::Rect& sub_rect); + void Subtract(const SimpleEnclosedRegion& sub_region) { + Subtract(sub_region.rect_); + } + void Union(const gfx::Rect& new_rect); + void Union(const SimpleEnclosedRegion& new_region) { + Union(new_region.rect_); + } + void Intersect(const gfx::Rect& in_rect) { return rect_.Intersect(in_rect); } + void Intersect(const SimpleEnclosedRegion& in_region) { + Intersect(in_region.rect_); + } + + bool Equals(const SimpleEnclosedRegion& other) const { + bool both_empty = rect_.IsEmpty() && other.rect_.IsEmpty(); + return both_empty || rect_ == other.rect_; + } + + gfx::Rect bounds() const { return rect_; } + + // The value of |i| must be less than GetRegionComplexity(). + gfx::Rect GetRect(size_t i) const; + + std::string ToString() const { return rect_.ToString(); } + + private: + gfx::Rect rect_; +}; + +inline bool operator==(const SimpleEnclosedRegion& a, + const SimpleEnclosedRegion& b) { + return a.Equals(b); +} + +inline bool operator!=(const SimpleEnclosedRegion& a, + const SimpleEnclosedRegion& b) { + return !(a == b); +} + +inline SimpleEnclosedRegion SubtractSimpleEnclosedRegions( + const SimpleEnclosedRegion& a, + const SimpleEnclosedRegion& b) { + SimpleEnclosedRegion result = a; + result.Subtract(b); + return result; +} + +inline SimpleEnclosedRegion IntersectSimpleEnclosedRegions( + const SimpleEnclosedRegion& a, + const SimpleEnclosedRegion& b) { + SimpleEnclosedRegion result = a; + result.Intersect(b); + return result; +} + +inline SimpleEnclosedRegion UnionSimpleEnclosedRegions( + const SimpleEnclosedRegion& a, + const SimpleEnclosedRegion& b) { + SimpleEnclosedRegion result = a; + result.Union(b); + return result; +} + +} // namespace cc + +#endif // CC_BASE_SIMPLE_ENCLOSED_REGION_H_ diff --git a/cc/base/simple_enclosed_region_unittest.cc b/cc/base/simple_enclosed_region_unittest.cc new file mode 100644 index 0000000..869da4e --- /dev/null +++ b/cc/base/simple_enclosed_region_unittest.cc @@ -0,0 +1,637 @@ +// Copyright 2014 The Chromium 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 "cc/base/simple_enclosed_region.h" + +#include <algorithm> +#include <vector> + +#include "base/logging.h" +#include "cc/base/region.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace cc { +namespace { + +bool ExpectRegionEq(const gfx::Rect& rect, const SimpleEnclosedRegion& region) { + std::vector<gfx::Rect> actual_rects; + std::vector<gfx::Rect> expected_rects; + + if (!rect.IsEmpty()) + expected_rects.push_back(rect); + + for (size_t i = 0; i < region.GetRegionComplexity(); ++i) + actual_rects.push_back(region.GetRect(i)); + + if (rect.IsEmpty() != region.IsEmpty()) { + LOG(ERROR) << "Expected: " << rect.IsEmpty() + << " Actual: " << region.IsEmpty(); + return false; + } + + if (expected_rects.size() != actual_rects.size()) { + LOG(ERROR) << "Expected: " << expected_rects.size() + << " Actual: " << actual_rects.size(); + return false; + } + + std::sort(actual_rects.begin(), actual_rects.end()); + std::sort(expected_rects.begin(), expected_rects.end()); + + for (size_t i = 0; i < expected_rects.size(); ++i) { + if (expected_rects[i] != actual_rects[i]) { + LOG(ERROR) << "Expected: " << expected_rects[i].ToString() + << " Actual: " << actual_rects[i].ToString(); + return false; + } + } + + return true; +} + +TEST(SimpleEnclosedRegionTest, Create) { + SimpleEnclosedRegion r1; + EXPECT_TRUE(r1.IsEmpty()); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(), r1)); + + SimpleEnclosedRegion r2(gfx::Rect(2, 3, 4, 5)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(2, 3, 4, 5), r2)); + + SimpleEnclosedRegion r3(2, 3, 4, 5); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(2, 3, 4, 5), r3)); + + SimpleEnclosedRegion r4(4, 5); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(4, 5), r4)); + + SimpleEnclosedRegion r5(Region(gfx::Rect(2, 3, 4, 5))); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(2, 3, 4, 5), r5)); + + SimpleEnclosedRegion r6(r5); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(2, 3, 4, 5), r6)); +} + +TEST(SimpleEnclosedRegionTest, Assign) { + SimpleEnclosedRegion r; + EXPECT_TRUE(r.IsEmpty()); + + r = gfx::Rect(2, 3, 4, 5); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(2, 3, 4, 5), r)); + + r = SimpleEnclosedRegion(3, 4, 5, 6); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(3, 4, 5, 6), r)); +} + +TEST(SimpleEnclosedRegionTest, Clear) { + SimpleEnclosedRegion r(1, 2, 3, 4); + EXPECT_FALSE(r.IsEmpty()); + r.Clear(); + EXPECT_TRUE(r.IsEmpty()); +} + +TEST(SimpleEnclosedRegionTest, GetRegionComplexity) { + SimpleEnclosedRegion empty; + EXPECT_EQ(0u, empty.GetRegionComplexity()); + + SimpleEnclosedRegion stuff; + stuff.Union(gfx::Rect(1, 2, 3, 4)); + EXPECT_EQ(1u, stuff.GetRegionComplexity()); + + // The SimpleEnclosedRegion only holds up to 1 rect. + stuff.Union(gfx::Rect(5, 6, 7, 8)); + EXPECT_EQ(1u, stuff.GetRegionComplexity()); +} + +TEST(SimpleEnclosedRegionTest, Contains) { + SimpleEnclosedRegion r(1, 2, 5, 6); + + EXPECT_FALSE(r.Contains(gfx::Point(0, 2))); + EXPECT_FALSE(r.Contains(gfx::Point(1, 1))); + EXPECT_TRUE(r.Contains(gfx::Point(1, 2))); + + EXPECT_FALSE(r.Contains(gfx::Point(6, 2))); + EXPECT_FALSE(r.Contains(gfx::Point(5, 1))); + EXPECT_TRUE(r.Contains(gfx::Point(5, 2))); + + EXPECT_FALSE(r.Contains(gfx::Point(0, 7))); + EXPECT_FALSE(r.Contains(gfx::Point(1, 8))); + EXPECT_TRUE(r.Contains(gfx::Point(1, 7))); + + EXPECT_FALSE(r.Contains(gfx::Point(6, 7))); + EXPECT_FALSE(r.Contains(gfx::Point(5, 8))); + EXPECT_TRUE(r.Contains(gfx::Point(5, 7))); + + EXPECT_FALSE(r.Contains(gfx::Rect(0, 2, 1, 1))); + EXPECT_FALSE(r.Contains(gfx::Rect(1, 1, 1, 1))); + EXPECT_TRUE(r.Contains(gfx::Rect(1, 2, 1, 1))); + EXPECT_FALSE(r.Contains(gfx::Rect(0, 1, 2, 2))); + + EXPECT_FALSE(r.Contains(gfx::Rect(6, 2, 1, 1))); + EXPECT_FALSE(r.Contains(gfx::Rect(5, 1, 1, 1))); + EXPECT_TRUE(r.Contains(gfx::Rect(5, 2, 1, 1))); + EXPECT_FALSE(r.Contains(gfx::Rect(5, 1, 2, 2))); + + EXPECT_FALSE(r.Contains(gfx::Rect(0, 7, 1, 1))); + EXPECT_FALSE(r.Contains(gfx::Rect(1, 8, 1, 1))); + EXPECT_TRUE(r.Contains(gfx::Rect(1, 7, 1, 1))); + EXPECT_FALSE(r.Contains(gfx::Rect(0, 7, 2, 2))); + + EXPECT_FALSE(r.Contains(gfx::Rect(6, 7, 1, 1))); + EXPECT_FALSE(r.Contains(gfx::Rect(5, 8, 1, 1))); + EXPECT_TRUE(r.Contains(gfx::Rect(5, 7, 1, 1))); + EXPECT_FALSE(r.Contains(gfx::Rect(5, 7, 2, 2))); + + gfx::Rect q(1, 2, 5, 6); + EXPECT_TRUE(r.Contains(q)) << q.ToString(); + q.Inset(-1, 0, 0, 0); + EXPECT_FALSE(r.Contains(q)) << q.ToString(); + q.Inset(1, -1, 0, 0); + EXPECT_FALSE(r.Contains(q)) << q.ToString(); + q.Inset(0, 1, -1, 0); + EXPECT_FALSE(r.Contains(q)) << q.ToString(); + q.Inset(0, 0, 1, -1); + EXPECT_FALSE(r.Contains(q)) << q.ToString(); + + q.Inset(1, 0, 0, 1); + EXPECT_TRUE(r.Contains(q)) << q.ToString(); + q.Inset(-1, 1, 0, 0); + EXPECT_TRUE(r.Contains(q)) << q.ToString(); + q.Inset(0, -1, 1, 0); + EXPECT_TRUE(r.Contains(q)) << q.ToString(); + q.Inset(0, 0, -1, 1); + EXPECT_TRUE(r.Contains(q)) << q.ToString(); + + EXPECT_FALSE(r.Contains(SimpleEnclosedRegion(0, 2, 1, 1))); + EXPECT_FALSE(r.Contains(SimpleEnclosedRegion(1, 1, 1, 1))); + EXPECT_TRUE(r.Contains(SimpleEnclosedRegion(1, 2, 1, 1))); + EXPECT_FALSE(r.Contains(SimpleEnclosedRegion(0, 1, 2, 2))); + + EXPECT_FALSE(r.Contains(SimpleEnclosedRegion(6, 2, 1, 1))); + EXPECT_FALSE(r.Contains(SimpleEnclosedRegion(5, 1, 1, 1))); + EXPECT_TRUE(r.Contains(SimpleEnclosedRegion(5, 2, 1, 1))); + EXPECT_FALSE(r.Contains(SimpleEnclosedRegion(5, 1, 2, 2))); + + EXPECT_FALSE(r.Contains(SimpleEnclosedRegion(0, 7, 1, 1))); + EXPECT_FALSE(r.Contains(SimpleEnclosedRegion(1, 8, 1, 1))); + EXPECT_TRUE(r.Contains(SimpleEnclosedRegion(1, 7, 1, 1))); + EXPECT_FALSE(r.Contains(SimpleEnclosedRegion(0, 7, 2, 2))); + + EXPECT_FALSE(r.Contains(SimpleEnclosedRegion(6, 7, 1, 1))); + EXPECT_FALSE(r.Contains(SimpleEnclosedRegion(5, 8, 1, 1))); + EXPECT_TRUE(r.Contains(SimpleEnclosedRegion(5, 7, 1, 1))); + EXPECT_FALSE(r.Contains(SimpleEnclosedRegion(5, 7, 2, 2))); + + q = gfx::Rect(1, 2, 5, 6); + EXPECT_TRUE(r.Contains(SimpleEnclosedRegion(q))) << q.ToString(); + q.Inset(-1, 0, 0, 0); + EXPECT_FALSE(r.Contains(SimpleEnclosedRegion(q))) << q.ToString(); + q.Inset(1, -1, 0, 0); + EXPECT_FALSE(r.Contains(SimpleEnclosedRegion(q))) << q.ToString(); + q.Inset(0, 1, -1, 0); + EXPECT_FALSE(r.Contains(SimpleEnclosedRegion(q))) << q.ToString(); + q.Inset(0, 0, 1, -1); + EXPECT_FALSE(r.Contains(SimpleEnclosedRegion(q))) << q.ToString(); + + q.Inset(1, 0, 0, 1); + EXPECT_TRUE(r.Contains(SimpleEnclosedRegion(q))) << q.ToString(); + q.Inset(-1, 1, 0, 0); + EXPECT_TRUE(r.Contains(SimpleEnclosedRegion(q))) << q.ToString(); + q.Inset(0, -1, 1, 0); + EXPECT_TRUE(r.Contains(SimpleEnclosedRegion(q))) << q.ToString(); + q.Inset(0, 0, -1, 1); + EXPECT_TRUE(r.Contains(SimpleEnclosedRegion(q))) << q.ToString(); +} + +TEST(SimpleEnclosedRegionTest, Intersects) { + SimpleEnclosedRegion r(1, 2, 5, 6); + + EXPECT_FALSE(r.Intersects(gfx::Rect(0, 2, 1, 1))); + EXPECT_FALSE(r.Intersects(gfx::Rect(1, 1, 1, 1))); + EXPECT_TRUE(r.Intersects(gfx::Rect(1, 2, 1, 1))); + EXPECT_TRUE(r.Intersects(gfx::Rect(0, 1, 2, 2))); + + EXPECT_FALSE(r.Intersects(gfx::Rect(6, 2, 1, 1))); + EXPECT_FALSE(r.Intersects(gfx::Rect(5, 1, 1, 1))); + EXPECT_TRUE(r.Intersects(gfx::Rect(5, 2, 1, 1))); + EXPECT_TRUE(r.Intersects(gfx::Rect(5, 1, 2, 2))); + + EXPECT_FALSE(r.Intersects(gfx::Rect(0, 7, 1, 1))); + EXPECT_FALSE(r.Intersects(gfx::Rect(1, 8, 1, 1))); + EXPECT_TRUE(r.Intersects(gfx::Rect(1, 7, 1, 1))); + EXPECT_TRUE(r.Intersects(gfx::Rect(0, 7, 2, 2))); + + EXPECT_FALSE(r.Intersects(gfx::Rect(6, 7, 1, 1))); + EXPECT_FALSE(r.Intersects(gfx::Rect(5, 8, 1, 1))); + EXPECT_TRUE(r.Intersects(gfx::Rect(5, 7, 1, 1))); + EXPECT_TRUE(r.Intersects(gfx::Rect(5, 7, 2, 2))); + + gfx::Rect q(1, 2, 5, 6); + EXPECT_TRUE(r.Intersects(q)) << q.ToString(); + q.Inset(-1, 0, 0, 0); + EXPECT_TRUE(r.Intersects(q)) << q.ToString(); + q.Inset(1, -1, 0, 0); + EXPECT_TRUE(r.Intersects(q)) << q.ToString(); + q.Inset(0, 1, -1, 0); + EXPECT_TRUE(r.Intersects(q)) << q.ToString(); + q.Inset(0, 0, 1, -1); + EXPECT_TRUE(r.Intersects(q)) << q.ToString(); + + q.Inset(1, 0, 0, 1); + EXPECT_TRUE(r.Intersects(q)) << q.ToString(); + q.Inset(-1, 1, 0, 0); + EXPECT_TRUE(r.Intersects(q)) << q.ToString(); + q.Inset(0, -1, 1, 0); + EXPECT_TRUE(r.Intersects(q)) << q.ToString(); + q.Inset(0, 0, -1, 1); + EXPECT_TRUE(r.Intersects(q)) << q.ToString(); + + EXPECT_FALSE(r.Intersects(SimpleEnclosedRegion(0, 2, 1, 1))); + EXPECT_FALSE(r.Intersects(SimpleEnclosedRegion(1, 1, 1, 1))); + EXPECT_TRUE(r.Intersects(SimpleEnclosedRegion(1, 2, 1, 1))); + EXPECT_TRUE(r.Intersects(SimpleEnclosedRegion(0, 1, 2, 2))); + + EXPECT_FALSE(r.Intersects(SimpleEnclosedRegion(6, 2, 1, 1))); + EXPECT_FALSE(r.Intersects(SimpleEnclosedRegion(5, 1, 1, 1))); + EXPECT_TRUE(r.Intersects(SimpleEnclosedRegion(5, 2, 1, 1))); + EXPECT_TRUE(r.Intersects(SimpleEnclosedRegion(5, 1, 2, 2))); + + EXPECT_FALSE(r.Intersects(SimpleEnclosedRegion(0, 7, 1, 1))); + EXPECT_FALSE(r.Intersects(SimpleEnclosedRegion(1, 8, 1, 1))); + EXPECT_TRUE(r.Intersects(SimpleEnclosedRegion(1, 7, 1, 1))); + EXPECT_TRUE(r.Intersects(SimpleEnclosedRegion(0, 7, 2, 2))); + + EXPECT_FALSE(r.Intersects(SimpleEnclosedRegion(6, 7, 1, 1))); + EXPECT_FALSE(r.Intersects(SimpleEnclosedRegion(5, 8, 1, 1))); + EXPECT_TRUE(r.Intersects(SimpleEnclosedRegion(5, 7, 1, 1))); + EXPECT_TRUE(r.Intersects(SimpleEnclosedRegion(5, 7, 2, 2))); + + q = gfx::Rect(1, 2, 5, 6); + EXPECT_TRUE(r.Intersects(SimpleEnclosedRegion(q))) << q.ToString(); + q.Inset(-1, 0, 0, 0); + EXPECT_TRUE(r.Intersects(SimpleEnclosedRegion(q))) << q.ToString(); + q.Inset(1, -1, 0, 0); + EXPECT_TRUE(r.Intersects(SimpleEnclosedRegion(q))) << q.ToString(); + q.Inset(0, 1, -1, 0); + EXPECT_TRUE(r.Intersects(SimpleEnclosedRegion(q))) << q.ToString(); + q.Inset(0, 0, 1, -1); + EXPECT_TRUE(r.Intersects(SimpleEnclosedRegion(q))) << q.ToString(); + + q.Inset(1, 0, 0, 1); + EXPECT_TRUE(r.Intersects(SimpleEnclosedRegion(q))) << q.ToString(); + q.Inset(-1, 1, 0, 0); + EXPECT_TRUE(r.Intersects(SimpleEnclosedRegion(q))) << q.ToString(); + q.Inset(0, -1, 1, 0); + EXPECT_TRUE(r.Intersects(SimpleEnclosedRegion(q))) << q.ToString(); + q.Inset(0, 0, -1, 1); + EXPECT_TRUE(r.Intersects(SimpleEnclosedRegion(q))) << q.ToString(); +} + +TEST(SimpleEnclosedRegionTest, Equals) { + SimpleEnclosedRegion r(1, 2, 3, 4); + EXPECT_TRUE(r.Equals(SimpleEnclosedRegion(1, 2, 3, 4))); + EXPECT_FALSE(r.Equals(SimpleEnclosedRegion(2, 2, 3, 4))); + EXPECT_FALSE(r.Equals(SimpleEnclosedRegion(1, 3, 3, 4))); + EXPECT_FALSE(r.Equals(SimpleEnclosedRegion(1, 2, 4, 4))); + EXPECT_FALSE(r.Equals(SimpleEnclosedRegion(1, 2, 3, 5))); + EXPECT_FALSE(r.Equals(SimpleEnclosedRegion(2, 2, 2, 4))); + EXPECT_FALSE(r.Equals(SimpleEnclosedRegion(1, 3, 3, 3))); +} + +TEST(SimpleEnclosedRegionTest, Bounds) { + SimpleEnclosedRegion r; + EXPECT_EQ(gfx::Rect(), r.bounds()); + r = gfx::Rect(3, 4, 5, 6); + EXPECT_EQ(gfx::Rect(3, 4, 5, 6), r.bounds()); + r.Union(gfx::Rect(1, 2, 12, 13)); + EXPECT_EQ(gfx::Rect(1, 2, 12, 13), r.bounds()); +} + +TEST(SimpleEnclosedRegionTest, GetRect) { + SimpleEnclosedRegion r(3, 4, 5, 6); + EXPECT_EQ(gfx::Rect(3, 4, 5, 6), r.GetRect(0)); + r.Union(gfx::Rect(1, 2, 12, 13)); + EXPECT_EQ(gfx::Rect(1, 2, 12, 13), r.GetRect(0)); +} + +TEST(SimpleEnclosedRegionTest, Union) { + SimpleEnclosedRegion r; + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(), r)); + + // Empty Union anything = anything. + r.Union(gfx::Rect(4, 5, 6, 7)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(4, 5, 6, 7), r)); + + // Anything Union empty = anything. + r.Union(gfx::Rect()); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(4, 5, 6, 7), r)); + + // Anything Union contained rect = Anything. + r.Union(gfx::Rect(5, 6, 4, 5)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(4, 5, 6, 7), r)); + + // Anything Union containing rect = containing rect. + r.Union(gfx::Rect(2, 3, 8, 9)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(2, 3, 8, 9), r)); + r.Union(gfx::Rect(2, 3, 9, 10)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(2, 3, 9, 10), r)); + + // Union with a smaller disjoint rect is ignored. + r.Union(gfx::Rect(20, 21, 9, 9)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(2, 3, 9, 10), r)); + + // Union with a smaller overlapping rect is ignored. + r.Union(gfx::Rect(3, 4, 9, 9)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(2, 3, 9, 10), r)); + + // Union with an equal sized rect can be either one. + r.Union(gfx::Rect(4, 4, 9, 10)); + EXPECT_EQ(1u, r.GetRegionComplexity()); + EXPECT_TRUE(r.bounds() == gfx::Rect(2, 3, 9, 10) || + r.bounds() == gfx::Rect(4, 4, 9, 10)); + + // Union with a larger disjoint rect is taken. + r.Union(gfx::Rect(20, 21, 12, 13)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(20, 21, 12, 13), r)); + + // Union with a larger overlapping rect is taken. + r.Union(gfx::Rect(19, 19, 12, 14)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(19, 19, 12, 14), r)); + + // True also when the rect covers one edge of the existing region. + r = gfx::Rect(10, 10, 10, 10); + r.Union(gfx::Rect(12, 7, 9, 16)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(12, 7, 9, 16), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Union(gfx::Rect(9, 7, 9, 16)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(9, 7, 9, 16), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Union(gfx::Rect(7, 12, 16, 9)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(7, 12, 16, 9), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Union(gfx::Rect(7, 9, 16, 9)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(7, 9, 16, 9), r)); + + // But if the existing region can be expanded to make a larger rect, then it + // will. Union area is 9*12 = 108. By merging, we make a rect with an area of + // 10*11 = 110. The resulting rect is expanded as far as possible while + // remaining enclosed in the Union. + r = gfx::Rect(10, 10, 10, 10); + r.Union(gfx::Rect(12, 9, 9, 12)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 11, 10), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Union(gfx::Rect(9, 9, 9, 12)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(9, 10, 11, 10), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Union(gfx::Rect(9, 12, 12, 9)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 10, 11), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Union(gfx::Rect(9, 9, 12, 9)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 9, 10, 11), r)); + + r = gfx::Rect(12, 9, 9, 12); + r.Union(gfx::Rect(10, 10, 10, 10)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 11, 10), r)); + + r = gfx::Rect(9, 9, 9, 12); + r.Union(gfx::Rect(10, 10, 10, 10)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(9, 10, 11, 10), r)); + + r = gfx::Rect(9, 12, 12, 9); + r.Union(gfx::Rect(10, 10, 10, 10)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 10, 11), r)); + + r = gfx::Rect(9, 9, 12, 9); + r.Union(gfx::Rect(10, 10, 10, 10)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 9, 10, 11), r)); +} + +TEST(SimpleEnclosedRegionTest, Subtract) { + SimpleEnclosedRegion r; + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(), r)); + + // Empty Subtract anything = empty. + r.Subtract(gfx::Rect(4, 5, 6, 7)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(), r)); + + // Subtracting an enclosing rect = empty. + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(10, 10, 10, 10)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(9, 9, 12, 12)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(), r)); + + // Subtracting a rect that covers one side of the region will shrink that + // side. + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(18, 10, 10, 10)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 8, 10), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(18, 8, 10, 14)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 8, 10), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(10, 18, 10, 10)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 10, 8), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(8, 18, 14, 10)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 10, 8), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(2, 10, 10, 10)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(12, 10, 8, 10), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(2, 8, 10, 14)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(12, 10, 8, 10), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(10, 2, 10, 10)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 12, 10, 8), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(8, 2, 14, 10)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 12, 10, 8), r)); + + // Subtracting a rect that does not cover a full side will still shrink that + // side. + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(18, 12, 10, 8)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 8, 10), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(18, 12, 10, 10)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 8, 10), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(12, 18, 8, 10)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 10, 8), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(12, 18, 10, 10)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 10, 8), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(2, 12, 10, 8)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(12, 10, 8, 10), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(2, 12, 10, 10)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(12, 10, 8, 10), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(12, 2, 8, 10)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 12, 10, 8), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(12, 2, 10, 10)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 12, 10, 8), r)); + + // Subtracting a rect inside the region will make it choose the larger result. + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(11, 11, 7, 8)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(18, 10, 2, 10), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(11, 11, 8, 7)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 18, 10, 2), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(12, 11, 7, 8)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 2, 10), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(11, 12, 8, 7)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 10, 2), r)); + + // Subtracting a rect that cuts the region in two will choose the larger side. + // Here it's the top side. + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(10, 14, 10, 3)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 10, 4), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(0, 14, 30, 3)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 10, 4), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(10, 14, 8, 3)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 10, 4), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(0, 14, 18, 3)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 10, 4), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(12, 14, 18, 3)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 10, 4), r)); + + // Here it's the bottom side. + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(10, 13, 10, 3)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 16, 10, 4), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(0, 13, 30, 3)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 16, 10, 4), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(10, 13, 8, 3)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 16, 10, 4), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(0, 13, 18, 3)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 16, 10, 4), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(12, 13, 18, 3)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 16, 10, 4), r)); + + // Here it's the left side. + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(14, 10, 3, 10)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 4, 10), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(14, 10, 3, 10)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 4, 10), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(14, 10, 3, 10)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 4, 10), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(14, 10, 3, 10)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 4, 10), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(14, 10, 3, 10)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 4, 10), r)); + + // Here it's the right side. + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(13, 10, 3, 10)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(16, 10, 4, 10), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(13, 10, 3, 10)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(16, 10, 4, 10), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(13, 10, 3, 10)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(16, 10, 4, 10), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(13, 10, 3, 10)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(16, 10, 4, 10), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(13, 10, 3, 10)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(16, 10, 4, 10), r)); + + // Subtracting a rect that leaves three possible choices will choose the + // larger. + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(10, 14, 7, 3)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 10, 4), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(10, 14, 5, 3)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(15, 10, 5, 10), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(13, 14, 7, 3)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 10, 4), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(15, 14, 5, 3)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 5, 10), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(14, 10, 3, 7)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 4, 10), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(14, 10, 3, 5)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 15, 10, 5), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(14, 13, 3, 7)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 4, 10), r)); + + r = gfx::Rect(10, 10, 10, 10); + r.Subtract(gfx::Rect(14, 15, 3, 5)); + EXPECT_TRUE(ExpectRegionEq(gfx::Rect(10, 10, 10, 5), r)); +} + +} // namespace +} // namespace cc |