Clone of chromium aad1ce808763f59c7a3753e08f1500a104ecc6fd refs/remotes/origin/HEAD
diff --git a/cc/base/cc_export.h b/cc/base/cc_export.h
new file mode 100644
index 0000000..fe0e0e3
--- /dev/null
+++ b/cc/base/cc_export.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2012 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_CC_EXPORT_H_
+#define CC_BASE_CC_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(CC_IMPLEMENTATION)
+#define CC_EXPORT __declspec(dllexport)
+#else
+#define CC_EXPORT __declspec(dllimport)
+#endif // defined(CC_IMPLEMENTATION)
+
+#else // defined(WIN32)
+#if defined(CC_IMPLEMENTATION)
+#define CC_EXPORT __attribute__((visibility("default")))
+#else
+#define CC_EXPORT
+#endif
+#endif
+
+#else // defined(COMPONENT_BUILD)
+#define CC_EXPORT
+#endif
+
+#endif // CC_BASE_CC_EXPORT_H_
diff --git a/cc/base/completion_event.h b/cc/base/completion_event.h
new file mode 100644
index 0000000..96ccf54
--- /dev/null
+++ b/cc/base/completion_event.h
@@ -0,0 +1,63 @@
+// Copyright 2011 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_COMPLETION_EVENT_H_
+#define CC_BASE_COMPLETION_EVENT_H_
+
+#include "base/logging.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread_restrictions.h"
+
+namespace cc {
+
+// Used for making blocking calls from one thread to another. Use only when
+// absolutely certain that doing-so will not lead to a deadlock.
+//
+// It is safe to destroy this object as soon as Wait() returns.
+class CompletionEvent {
+ public:
+ CompletionEvent()
+ : event_(false /* manual_reset */, false /* initially_signaled */) {
+#if DCHECK_IS_ON
+ waited_ = false;
+ signaled_ = false;
+#endif
+ }
+
+ ~CompletionEvent() {
+#if DCHECK_IS_ON
+ DCHECK(waited_);
+ DCHECK(signaled_);
+#endif
+ }
+
+ void Wait() {
+#if DCHECK_IS_ON
+ DCHECK(!waited_);
+ waited_ = true;
+#endif
+ base::ThreadRestrictions::ScopedAllowWait allow_wait;
+ event_.Wait();
+ }
+
+ void Signal() {
+#if DCHECK_IS_ON
+ DCHECK(!signaled_);
+ signaled_ = true;
+#endif
+ event_.Signal();
+ }
+
+ private:
+ base::WaitableEvent event_;
+#if DCHECK_IS_ON
+ // Used to assert that Wait() and Signal() are each called exactly once.
+ bool waited_;
+ bool signaled_;
+#endif
+};
+
+} // namespace cc
+
+#endif // CC_BASE_COMPLETION_EVENT_H_
diff --git a/cc/base/delayed_unique_notifier.cc b/cc/base/delayed_unique_notifier.cc
new file mode 100644
index 0000000..c297267
--- /dev/null
+++ b/cc/base/delayed_unique_notifier.cc
@@ -0,0 +1,78 @@
+// 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/delayed_unique_notifier.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/location.h"
+#include "base/sequenced_task_runner.h"
+
+namespace cc {
+
+DelayedUniqueNotifier::DelayedUniqueNotifier(
+ base::SequencedTaskRunner* task_runner,
+ const base::Closure& closure,
+ const base::TimeDelta& delay)
+ : task_runner_(task_runner),
+ closure_(closure),
+ delay_(delay),
+ notification_pending_(false),
+ weak_ptr_factory_(this) {
+}
+
+DelayedUniqueNotifier::~DelayedUniqueNotifier() {
+}
+
+void DelayedUniqueNotifier::Schedule() {
+ if (notification_pending_) {
+ next_notification_time_ = Now() + delay_;
+ return;
+ }
+
+ next_notification_time_ = Now() + delay_;
+ task_runner_->PostDelayedTask(FROM_HERE,
+ base::Bind(&DelayedUniqueNotifier::NotifyIfTime,
+ weak_ptr_factory_.GetWeakPtr()),
+ delay_);
+ notification_pending_ = true;
+}
+
+void DelayedUniqueNotifier::Cancel() {
+ next_notification_time_ = base::TimeTicks();
+}
+
+bool DelayedUniqueNotifier::HasPendingNotification() const {
+ return notification_pending_ && !next_notification_time_.is_null();
+}
+
+base::TimeTicks DelayedUniqueNotifier::Now() const {
+ return base::TimeTicks::Now();
+}
+
+void DelayedUniqueNotifier::NotifyIfTime() {
+ // If next notifiaction time is not valid, then this schedule was canceled.
+ if (next_notification_time_.is_null()) {
+ notification_pending_ = false;
+ return;
+ }
+
+ // If the notification was rescheduled or arrived too early for any other
+ // reason, then post another task instead of running the callback.
+ base::TimeTicks now = Now();
+ if (next_notification_time_ > now) {
+ task_runner_->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&DelayedUniqueNotifier::NotifyIfTime,
+ weak_ptr_factory_.GetWeakPtr()),
+ next_notification_time_ - now);
+ return;
+ }
+
+ // Note the order here is important since closure might schedule another run.
+ notification_pending_ = false;
+ closure_.Run();
+}
+
+} // namespace cc
diff --git a/cc/base/delayed_unique_notifier.h b/cc/base/delayed_unique_notifier.h
new file mode 100644
index 0000000..1f93416
--- /dev/null
+++ b/cc/base/delayed_unique_notifier.h
@@ -0,0 +1,61 @@
+// 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_DELAYED_UNIQUE_NOTIFIER_H_
+#define CC_BASE_DELAYED_UNIQUE_NOTIFIER_H_
+
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "cc/base/cc_export.h"
+
+namespace base {
+class SequencedTaskRunner;
+} // namespace base
+
+namespace cc {
+
+class CC_EXPORT DelayedUniqueNotifier {
+ public:
+ // Configure this notifier to issue the |closure| notification in |delay| time
+ // from Schedule() call.
+ DelayedUniqueNotifier(base::SequencedTaskRunner* task_runner,
+ const base::Closure& closure,
+ const base::TimeDelta& delay);
+
+ // Destroying the notifier will ensure that no further notifications will
+ // happen from this class.
+ virtual ~DelayedUniqueNotifier();
+
+ // Schedule a notification to be run. If another notification is already
+ // pending, then it will happen in (at least) given delay from now. That is,
+ // if delay is 16ms and a notification has been scheduled 10ms ago (ie, it
+ // should trigger in no less than 6ms), then calling schedule will ensure that
+ // the only notification that arrives will happen in (at least) 16ms from now.
+ void Schedule();
+
+ // Cancel any previously scheduled runs.
+ void Cancel();
+
+ // Returns true if a notification is currently scheduled to run.
+ bool HasPendingNotification() const;
+
+ protected:
+ // Virtual for testing.
+ virtual base::TimeTicks Now() const;
+
+ private:
+ void NotifyIfTime();
+
+ base::SequencedTaskRunner* task_runner_;
+ base::Closure closure_;
+ base::TimeDelta delay_;
+ base::TimeTicks next_notification_time_;
+ bool notification_pending_;
+
+ base::WeakPtrFactory<DelayedUniqueNotifier> weak_ptr_factory_;
+};
+
+} // namespace cc
+
+#endif // CC_BASE_DELAYED_UNIQUE_NOTIFIER_H_
diff --git a/cc/base/delayed_unique_notifier_unittest.cc b/cc/base/delayed_unique_notifier_unittest.cc
new file mode 100644
index 0000000..6c7ae6e
--- /dev/null
+++ b/cc/base/delayed_unique_notifier_unittest.cc
@@ -0,0 +1,266 @@
+// 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 <deque>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/test/test_pending_task.h"
+#include "base/test/test_simple_task_runner.h"
+#include "cc/base/delayed_unique_notifier.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+namespace {
+
+class TestNotifier : public DelayedUniqueNotifier {
+ public:
+ TestNotifier(base::SequencedTaskRunner* task_runner,
+ const base::Closure& closure,
+ const base::TimeDelta& delay)
+ : DelayedUniqueNotifier(task_runner, closure, delay) {}
+ virtual ~TestNotifier() {}
+
+ // Overridden from DelayedUniqueNotifier:
+ virtual base::TimeTicks Now() const OVERRIDE { return now_; }
+
+ void SetNow(base::TimeTicks now) { now_ = now; }
+
+ private:
+ base::TimeTicks now_;
+};
+
+class DelayedUniqueNotifierTest : public testing::Test {
+ public:
+ DelayedUniqueNotifierTest() : notification_count_(0) {}
+
+ virtual void SetUp() OVERRIDE {
+ notification_count_ = 0;
+ task_runner_ = make_scoped_refptr(new base::TestSimpleTaskRunner);
+ }
+
+ void Notify() { ++notification_count_; }
+
+ int NotificationCount() const { return notification_count_; }
+
+ std::deque<base::TestPendingTask> TakePendingTasks() {
+ std::deque<base::TestPendingTask> tasks = task_runner_->GetPendingTasks();
+ task_runner_->ClearPendingTasks();
+ return tasks;
+ }
+
+ protected:
+ int notification_count_;
+ scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+};
+
+TEST_F(DelayedUniqueNotifierTest, ZeroDelay) {
+ base::TimeDelta delay = base::TimeDelta::FromInternalValue(0);
+ TestNotifier notifier(
+ task_runner_.get(),
+ base::Bind(&DelayedUniqueNotifierTest::Notify, base::Unretained(this)),
+ delay);
+
+ EXPECT_EQ(0, NotificationCount());
+
+ // Basic schedule for |delay| from now.
+ base::TimeTicks schedule_time =
+ base::TimeTicks() + base::TimeDelta::FromInternalValue(10);
+
+ notifier.SetNow(schedule_time);
+ notifier.Schedule();
+
+ std::deque<base::TestPendingTask> tasks = TakePendingTasks();
+ ASSERT_EQ(1u, tasks.size());
+ EXPECT_EQ(base::TimeTicks() + delay, tasks[0].GetTimeToRun());
+
+ tasks[0].task.Run();
+ EXPECT_EQ(1, NotificationCount());
+
+ // 5 schedules should result in only one run.
+ for (int i = 0; i < 5; ++i)
+ notifier.Schedule();
+
+ tasks = TakePendingTasks();
+ ASSERT_EQ(1u, tasks.size());
+ EXPECT_EQ(base::TimeTicks() + delay, tasks[0].GetTimeToRun());
+
+ tasks[0].task.Run();
+ EXPECT_EQ(2, NotificationCount());
+}
+
+TEST_F(DelayedUniqueNotifierTest, SmallDelay) {
+ base::TimeDelta delay = base::TimeDelta::FromInternalValue(20);
+ TestNotifier notifier(
+ task_runner_.get(),
+ base::Bind(&DelayedUniqueNotifierTest::Notify, base::Unretained(this)),
+ delay);
+
+ EXPECT_EQ(0, NotificationCount());
+
+ // Basic schedule for |delay| from now (now: 30, run time: 50).
+ base::TimeTicks schedule_time =
+ base::TimeTicks() + base::TimeDelta::FromInternalValue(30);
+
+ notifier.SetNow(schedule_time);
+ notifier.Schedule();
+
+ std::deque<base::TestPendingTask> tasks = TakePendingTasks();
+
+ ASSERT_EQ(1u, tasks.size());
+ EXPECT_EQ(base::TimeTicks() + delay, tasks[0].GetTimeToRun());
+
+ // It's not yet time to run, so we expect no notifications.
+ tasks[0].task.Run();
+ EXPECT_EQ(0, NotificationCount());
+
+ tasks = TakePendingTasks();
+
+ ASSERT_EQ(1u, tasks.size());
+ // Now the time should be delay minus whatever the value of now happens to be
+ // (now: 30, run time: 50).
+ base::TimeTicks scheduled_run_time = notifier.Now() + delay;
+ base::TimeTicks scheduled_delay =
+ base::TimeTicks() + (scheduled_run_time - notifier.Now());
+ EXPECT_EQ(scheduled_delay, tasks[0].GetTimeToRun());
+
+ // Move closer to the run time (time: 49, run time: 50).
+ notifier.SetNow(notifier.Now() + base::TimeDelta::FromInternalValue(19));
+
+ // It's not yet time to run, so we expect no notifications.
+ tasks[0].task.Run();
+ EXPECT_EQ(0, NotificationCount());
+
+ tasks = TakePendingTasks();
+ ASSERT_EQ(1u, tasks.size());
+
+ // Now the time should be delay minus whatever the value of now happens to be.
+ scheduled_delay = base::TimeTicks() + (scheduled_run_time - notifier.Now());
+ EXPECT_EQ(scheduled_delay, tasks[0].GetTimeToRun());
+
+ // Move to exactly the run time (time: 50, run time: 50).
+ notifier.SetNow(notifier.Now() + base::TimeDelta::FromInternalValue(1));
+
+ // It's time to run!
+ tasks[0].task.Run();
+ EXPECT_EQ(1, NotificationCount());
+
+ tasks = TakePendingTasks();
+ EXPECT_EQ(0u, tasks.size());
+}
+
+TEST_F(DelayedUniqueNotifierTest, RescheduleDelay) {
+ base::TimeDelta delay = base::TimeDelta::FromInternalValue(20);
+ TestNotifier notifier(
+ task_runner_.get(),
+ base::Bind(&DelayedUniqueNotifierTest::Notify, base::Unretained(this)),
+ delay);
+
+ base::TimeTicks schedule_time;
+ // Move time 19 units forward and reschedule, expecting that we still need to
+ // run in |delay| time and we don't get a notification.
+ for (int i = 0; i < 10; ++i) {
+ EXPECT_EQ(0, NotificationCount());
+
+ // Move time forward 19 units.
+ schedule_time = notifier.Now() + base::TimeDelta::FromInternalValue(19);
+ notifier.SetNow(schedule_time);
+ notifier.Schedule();
+
+ std::deque<base::TestPendingTask> tasks = TakePendingTasks();
+
+ ASSERT_EQ(1u, tasks.size());
+ EXPECT_EQ(base::TimeTicks() + delay, tasks[0].GetTimeToRun());
+
+ // It's not yet time to run, so we expect no notifications.
+ tasks[0].task.Run();
+ EXPECT_EQ(0, NotificationCount());
+ }
+
+ // Move time forward 20 units, expecting a notification.
+ schedule_time = notifier.Now() + base::TimeDelta::FromInternalValue(20);
+ notifier.SetNow(schedule_time);
+
+ std::deque<base::TestPendingTask> tasks = TakePendingTasks();
+
+ ASSERT_EQ(1u, tasks.size());
+ EXPECT_EQ(base::TimeTicks() + delay, tasks[0].GetTimeToRun());
+
+ // Time to run!
+ tasks[0].task.Run();
+ EXPECT_EQ(1, NotificationCount());
+}
+
+TEST_F(DelayedUniqueNotifierTest, CancelAndHasPendingNotification) {
+ base::TimeDelta delay = base::TimeDelta::FromInternalValue(20);
+ TestNotifier notifier(
+ task_runner_.get(),
+ base::Bind(&DelayedUniqueNotifierTest::Notify, base::Unretained(this)),
+ delay);
+
+ EXPECT_EQ(0, NotificationCount());
+
+ // Schedule for |delay| seconds from now.
+ base::TimeTicks schedule_time =
+ notifier.Now() + base::TimeDelta::FromInternalValue(10);
+ notifier.SetNow(schedule_time);
+ notifier.Schedule();
+ EXPECT_TRUE(notifier.HasPendingNotification());
+
+ // Cancel the run.
+ notifier.Cancel();
+ EXPECT_FALSE(notifier.HasPendingNotification());
+
+ std::deque<base::TestPendingTask> tasks = TakePendingTasks();
+
+ ASSERT_EQ(1u, tasks.size());
+ EXPECT_EQ(base::TimeTicks() + delay, tasks[0].GetTimeToRun());
+
+ // Time to run, but a canceled task!
+ tasks[0].task.Run();
+ EXPECT_EQ(0, NotificationCount());
+ EXPECT_FALSE(notifier.HasPendingNotification());
+
+ tasks = TakePendingTasks();
+ EXPECT_EQ(0u, tasks.size());
+
+ notifier.Schedule();
+ EXPECT_TRUE(notifier.HasPendingNotification());
+ tasks = TakePendingTasks();
+
+ ASSERT_EQ(1u, tasks.size());
+ EXPECT_EQ(base::TimeTicks() + delay, tasks[0].GetTimeToRun());
+
+ // Advance the time.
+ notifier.SetNow(notifier.Now() + delay);
+
+ // This should run since it wasn't canceled.
+ tasks[0].task.Run();
+ EXPECT_EQ(1, NotificationCount());
+ EXPECT_FALSE(notifier.HasPendingNotification());
+
+ for (int i = 0; i < 10; ++i) {
+ notifier.Schedule();
+ EXPECT_TRUE(notifier.HasPendingNotification());
+ notifier.Cancel();
+ EXPECT_FALSE(notifier.HasPendingNotification());
+ }
+
+ tasks = TakePendingTasks();
+
+ ASSERT_EQ(1u, tasks.size());
+ EXPECT_EQ(base::TimeTicks() + delay, tasks[0].GetTimeToRun());
+
+ // Time to run, but a canceled task!
+ notifier.SetNow(notifier.Now() + delay);
+ tasks[0].task.Run();
+ EXPECT_EQ(1, NotificationCount());
+
+ tasks = TakePendingTasks();
+ EXPECT_EQ(0u, tasks.size());
+ EXPECT_FALSE(notifier.HasPendingNotification());
+}
+
+} // namespace
+} // namespace cc
diff --git a/cc/base/float_quad_unittest.cc b/cc/base/float_quad_unittest.cc
new file mode 100644
index 0000000..c2186fd
--- /dev/null
+++ b/cc/base/float_quad_unittest.cc
@@ -0,0 +1,62 @@
+// Copyright 2011 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/math_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/quad_f.h"
+#include "ui/gfx/rect_f.h"
+#include "ui/gfx/transform.h"
+
+namespace cc {
+namespace {
+
+// TODO(danakj) Move this test to ui/gfx/ when we don't need MathUtil::MapQuad.
+TEST(FloatQuadTest, IsRectilinearTest) {
+ const int kNumRectilinear = 8;
+ gfx::Transform rectilinear_trans[kNumRectilinear];
+ rectilinear_trans[1].Rotate(90.f);
+ rectilinear_trans[2].Rotate(180.f);
+ rectilinear_trans[3].Rotate(270.f);
+ rectilinear_trans[4].SkewX(0.00000000001f);
+ rectilinear_trans[5].SkewY(0.00000000001f);
+ rectilinear_trans[6].Scale(0.00001f, 0.00001f);
+ rectilinear_trans[6].Rotate(180.f);
+ rectilinear_trans[7].Scale(100000.f, 100000.f);
+ rectilinear_trans[7].Rotate(180.f);
+
+ gfx::QuadF original(
+ gfx::RectF(0.01010101f, 0.01010101f, 100.01010101f, 100.01010101f));
+
+ for (int i = 0; i < kNumRectilinear; ++i) {
+ bool clipped = false;
+ gfx::QuadF quad =
+ MathUtil::MapQuad(rectilinear_trans[i], original, &clipped);
+ ASSERT_TRUE(!clipped) << "case " << i;
+ EXPECT_TRUE(quad.IsRectilinear()) << "case " << i;
+ }
+
+ const int kNumNonRectilinear = 10;
+ gfx::Transform non_rectilinear_trans[kNumNonRectilinear];
+ non_rectilinear_trans[0].Rotate(359.9999f);
+ non_rectilinear_trans[1].Rotate(0.0000001f);
+ non_rectilinear_trans[2].Rotate(89.9999f);
+ non_rectilinear_trans[3].Rotate(90.00001f);
+ non_rectilinear_trans[4].Rotate(179.9999f);
+ non_rectilinear_trans[5].Rotate(180.00001f);
+ non_rectilinear_trans[6].Rotate(269.9999f);
+ non_rectilinear_trans[7].Rotate(270.0001f);
+ non_rectilinear_trans[8].SkewX(0.00001f);
+ non_rectilinear_trans[9].SkewY(0.00001f);
+
+ for (int i = 0; i < kNumNonRectilinear; ++i) {
+ bool clipped = false;
+ gfx::QuadF quad =
+ MathUtil::MapQuad(non_rectilinear_trans[i], original, &clipped);
+ ASSERT_TRUE(!clipped) << "case " << i;
+ EXPECT_FALSE(quad.IsRectilinear()) << "case " << i;
+ }
+}
+
+} // namespace
+} // namespace cc
diff --git a/cc/base/invalidation_region.cc b/cc/base/invalidation_region.cc
new file mode 100644
index 0000000..5fbca8d
--- /dev/null
+++ b/cc/base/invalidation_region.cc
@@ -0,0 +1,39 @@
+// Copyright 2013 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/invalidation_region.h"
+
+#include "base/metrics/histogram.h"
+
+namespace {
+
+const int kMaxInvalidationRectCount = 256;
+
+} // namespace
+
+namespace cc {
+
+InvalidationRegion::InvalidationRegion() {}
+
+InvalidationRegion::~InvalidationRegion() {}
+
+void InvalidationRegion::Swap(Region* region) {
+ region_.Swap(region);
+}
+
+void InvalidationRegion::Clear() {
+ region_.Clear();
+}
+
+void InvalidationRegion::Union(const gfx::Rect& rect) {
+ region_.Union(rect);
+ SimplifyIfNeeded();
+}
+
+void InvalidationRegion::SimplifyIfNeeded() {
+ if (region_.GetRegionComplexity() > kMaxInvalidationRectCount)
+ region_ = region_.bounds();
+}
+
+} // namespace cc
diff --git a/cc/base/invalidation_region.h b/cc/base/invalidation_region.h
new file mode 100644
index 0000000..9cb2fe3
--- /dev/null
+++ b/cc/base/invalidation_region.h
@@ -0,0 +1,35 @@
+// Copyright 2013 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_INVALIDATION_REGION_H_
+#define CC_BASE_INVALIDATION_REGION_H_
+
+#include "cc/base/cc_export.h"
+#include "cc/base/region.h"
+#include "ui/gfx/rect.h"
+
+namespace cc {
+
+// This class behaves similarly to Region, but it may have false positives. That
+// is, InvalidationRegion can be simplified to encompass a larger area than the
+// collection of rects unioned.
+class CC_EXPORT InvalidationRegion {
+ public:
+ InvalidationRegion();
+ ~InvalidationRegion();
+
+ void Swap(Region* region);
+ void Clear();
+ void Union(const gfx::Rect& rect);
+ bool IsEmpty() const { return region_.IsEmpty(); }
+
+ private:
+ void SimplifyIfNeeded();
+
+ Region region_;
+};
+
+} // namespace cc
+
+#endif // CC_BASE_INVALIDATION_REGION_H_
diff --git a/cc/base/latency_info_swap_promise.cc b/cc/base/latency_info_swap_promise.cc
new file mode 100644
index 0000000..8c6ee0e
--- /dev/null
+++ b/cc/base/latency_info_swap_promise.cc
@@ -0,0 +1,52 @@
+// Copyright 2013 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/latency_info_swap_promise.h"
+
+#include "base/logging.h"
+
+namespace {
+ ui::LatencyComponentType DidNotSwapReasonToLatencyComponentType(
+ cc::SwapPromise::DidNotSwapReason reason) {
+ switch (reason) {
+ case cc::SwapPromise::DID_NOT_SWAP_UNKNOWN:
+ case cc::SwapPromise::SWAP_FAILS:
+ return ui::INPUT_EVENT_LATENCY_TERMINATED_SWAP_FAILED_COMPONENT;
+ case cc::SwapPromise::COMMIT_FAILS:
+ return ui::INPUT_EVENT_LATENCY_TERMINATED_COMMIT_FAILED_COMPONENT;
+ case cc::SwapPromise::COMMIT_NO_UPDATE:
+ return ui::INPUT_EVENT_LATENCY_TERMINATED_COMMIT_NO_UPDATE_COMPONENT;
+ }
+ NOTREACHED() << "Unhandled DidNotSwapReason.";
+ return ui::INPUT_EVENT_LATENCY_TERMINATED_SWAP_FAILED_COMPONENT;
+ }
+} // namespace
+
+namespace cc {
+
+LatencyInfoSwapPromise::LatencyInfoSwapPromise(const ui::LatencyInfo& latency)
+ : latency_(latency) {
+}
+
+LatencyInfoSwapPromise::~LatencyInfoSwapPromise() {
+}
+
+void LatencyInfoSwapPromise::DidSwap(CompositorFrameMetadata* metadata) {
+ DCHECK(!latency_.terminated);
+ metadata->latency_info.push_back(latency_);
+}
+
+void LatencyInfoSwapPromise::DidNotSwap(DidNotSwapReason reason) {
+ latency_.AddLatencyNumber(DidNotSwapReasonToLatencyComponentType(reason),
+ 0, 0);
+ // TODO(miletus): Turn this back on once per-event LatencyInfo tracking
+ // is enabled in GPU side.
+ // DCHECK(latency_.terminated);
+}
+
+int64 LatencyInfoSwapPromise::TraceId() const {
+ return latency_.trace_id;
+}
+
+} // namespace cc
diff --git a/cc/base/latency_info_swap_promise.h b/cc/base/latency_info_swap_promise.h
new file mode 100644
index 0000000..a210a3f
--- /dev/null
+++ b/cc/base/latency_info_swap_promise.h
@@ -0,0 +1,30 @@
+// Copyright 2013 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_LATENCY_INFO_SWAP_PROMISE_H_
+#define CC_BASE_LATENCY_INFO_SWAP_PROMISE_H_
+
+#include "base/compiler_specific.h"
+#include "cc/base/swap_promise.h"
+#include "ui/events/latency_info.h"
+
+namespace cc {
+
+class CC_EXPORT LatencyInfoSwapPromise : public SwapPromise {
+ public:
+ explicit LatencyInfoSwapPromise(const ui::LatencyInfo& latency_info);
+ virtual ~LatencyInfoSwapPromise();
+
+ virtual void DidSwap(CompositorFrameMetadata* metadata) OVERRIDE;
+ virtual void DidNotSwap(DidNotSwapReason reason) OVERRIDE;
+
+ virtual int64 TraceId() const OVERRIDE;
+
+ private:
+ ui::LatencyInfo latency_;
+};
+
+} // namespace cc
+
+#endif // CC_BASE_LATENCY_INFO_SWAP_PROMISE_H_
diff --git a/cc/base/latency_info_swap_promise_monitor.cc b/cc/base/latency_info_swap_promise_monitor.cc
new file mode 100644
index 0000000..f724d6c
--- /dev/null
+++ b/cc/base/latency_info_swap_promise_monitor.cc
@@ -0,0 +1,97 @@
+// Copyright 2013 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/latency_info_swap_promise_monitor.h"
+
+#include "base/threading/platform_thread.h"
+#include "cc/base/latency_info_swap_promise.h"
+#include "cc/trees/layer_tree_host.h"
+#include "cc/trees/layer_tree_host_impl.h"
+#include "cc/trees/layer_tree_impl.h"
+
+namespace {
+
+bool AddRenderingScheduledComponent(ui::LatencyInfo* latency_info) {
+ if (latency_info->FindLatency(
+ ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_COMPONENT, 0, NULL))
+ return false;
+ latency_info->AddLatencyNumber(
+ ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_COMPONENT, 0, 0);
+ return true;
+}
+
+bool AddForwardingScrollUpdateToMainComponent(ui::LatencyInfo* latency_info) {
+ if (latency_info->FindLatency(
+ ui::INPUT_EVENT_LATENCY_FORWARD_SCROLL_UPDATE_TO_MAIN_COMPONENT,
+ 0,
+ NULL))
+ return false;
+ latency_info->AddLatencyNumber(
+ ui::INPUT_EVENT_LATENCY_FORWARD_SCROLL_UPDATE_TO_MAIN_COMPONENT,
+ 0,
+ latency_info->trace_id);
+ return true;
+}
+
+} // namespace
+
+namespace cc {
+
+LatencyInfoSwapPromiseMonitor::LatencyInfoSwapPromiseMonitor(
+ ui::LatencyInfo* latency,
+ LayerTreeHost* layer_tree_host,
+ LayerTreeHostImpl* layer_tree_host_impl)
+ : SwapPromiseMonitor(layer_tree_host, layer_tree_host_impl),
+ latency_(latency) {}
+
+LatencyInfoSwapPromiseMonitor::~LatencyInfoSwapPromiseMonitor() {}
+
+void LatencyInfoSwapPromiseMonitor::OnSetNeedsCommitOnMain() {
+ if (AddRenderingScheduledComponent(latency_)) {
+ scoped_ptr<SwapPromise> swap_promise(new LatencyInfoSwapPromise(*latency_));
+ layer_tree_host_->QueueSwapPromise(swap_promise.Pass());
+ }
+}
+
+void LatencyInfoSwapPromiseMonitor::OnSetNeedsRedrawOnImpl() {
+ if (AddRenderingScheduledComponent(latency_)) {
+ scoped_ptr<SwapPromise> swap_promise(new LatencyInfoSwapPromise(*latency_));
+ layer_tree_host_impl_->active_tree()->QueueSwapPromise(swap_promise.Pass());
+ }
+}
+
+void LatencyInfoSwapPromiseMonitor::OnForwardScrollUpdateToMainThreadOnImpl() {
+ if (AddForwardingScrollUpdateToMainComponent(latency_)) {
+ int64 new_sequence_number = 0;
+ for (ui::LatencyInfo::LatencyMap::const_iterator it =
+ latency_->latency_components.begin();
+ it != latency_->latency_components.end();
+ ++it) {
+ if (it->first.first == ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT) {
+ new_sequence_number =
+ (static_cast<int64>(base::PlatformThread::CurrentId()) << 32) |
+ (it->second.sequence_number & 0xffffffff);
+ DCHECK(new_sequence_number != it->second.sequence_number);
+ break;
+ }
+ }
+ if (!new_sequence_number)
+ return;
+ ui::LatencyInfo new_latency;
+ new_latency.AddLatencyNumber(
+ ui::INPUT_EVENT_LATENCY_BEGIN_SCROLL_UPDATE_MAIN_COMPONENT,
+ 0,
+ new_sequence_number);
+ new_latency.TraceEventType("ScrollUpdate");
+ new_latency.CopyLatencyFrom(
+ *latency_,
+ ui::INPUT_EVENT_LATENCY_FORWARD_SCROLL_UPDATE_TO_MAIN_COMPONENT);
+ scoped_ptr<SwapPromise> swap_promise(
+ new LatencyInfoSwapPromise(new_latency));
+ layer_tree_host_impl_->QueueSwapPromiseForMainThreadScrollUpdate(
+ swap_promise.Pass());
+ }
+}
+
+} // namespace cc
diff --git a/cc/base/latency_info_swap_promise_monitor.h b/cc/base/latency_info_swap_promise_monitor.h
new file mode 100644
index 0000000..1a114fb
--- /dev/null
+++ b/cc/base/latency_info_swap_promise_monitor.h
@@ -0,0 +1,37 @@
+// Copyright 2013 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 "base/compiler_specific.h"
+#include "cc/base/swap_promise_monitor.h"
+
+#ifndef CC_BASE_LATENCY_INFO_SWAP_PROMISE_MONITOR_H_
+#define CC_BASE_LATENCY_INFO_SWAP_PROMISE_MONITOR_H_
+
+namespace ui {
+struct LatencyInfo;
+} // namespace ui
+
+namespace cc {
+
+// A LatencyInfoSwapPromiseMonitor queues a LatencyInfoSwapPromise into
+// LayerTreeHost or LayerTreeHostImpl if there is compositor state change
+// while it is being mointored.
+class CC_EXPORT LatencyInfoSwapPromiseMonitor : public SwapPromiseMonitor {
+ public:
+ LatencyInfoSwapPromiseMonitor(ui::LatencyInfo* latency,
+ LayerTreeHost* layer_tree_host,
+ LayerTreeHostImpl* layer_tree_host_impl);
+ virtual ~LatencyInfoSwapPromiseMonitor();
+
+ virtual void OnSetNeedsCommitOnMain() OVERRIDE;
+ virtual void OnSetNeedsRedrawOnImpl() OVERRIDE;
+ virtual void OnForwardScrollUpdateToMainThreadOnImpl() OVERRIDE;
+
+ private:
+ ui::LatencyInfo* latency_;
+};
+
+} // namespace cc
+
+#endif // CC_BASE_LATENCY_INFO_SWAP_PROMISE_MONITOR_H_
diff --git a/cc/base/math_util.cc b/cc/base/math_util.cc
new file mode 100644
index 0000000..5df1e56
--- /dev/null
+++ b/cc/base/math_util.cc
@@ -0,0 +1,834 @@
+// Copyright 2012 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/math_util.h"
+
+#include <algorithm>
+#include <cmath>
+#include <limits>
+
+#include "base/debug/trace_event_argument.h"
+#include "base/values.h"
+#include "ui/gfx/quad_f.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/rect_conversions.h"
+#include "ui/gfx/rect_f.h"
+#include "ui/gfx/transform.h"
+#include "ui/gfx/vector2d_f.h"
+
+namespace cc {
+
+const double MathUtil::kPiDouble = 3.14159265358979323846;
+const float MathUtil::kPiFloat = 3.14159265358979323846f;
+
+static HomogeneousCoordinate ProjectHomogeneousPoint(
+ const gfx::Transform& transform,
+ const gfx::PointF& p) {
+ // In this case, the layer we are trying to project onto is perpendicular to
+ // ray (point p and z-axis direction) that we are trying to project. This
+ // happens when the layer is rotated so that it is infinitesimally thin, or
+ // when it is co-planar with the camera origin -- i.e. when the layer is
+ // invisible anyway.
+ if (!transform.matrix().get(2, 2))
+ return HomogeneousCoordinate(0.0, 0.0, 0.0, 1.0);
+
+ SkMScalar z = -(transform.matrix().get(2, 0) * p.x() +
+ transform.matrix().get(2, 1) * p.y() +
+ transform.matrix().get(2, 3)) /
+ transform.matrix().get(2, 2);
+ HomogeneousCoordinate result(p.x(), p.y(), z, 1.0);
+ transform.matrix().mapMScalars(result.vec, result.vec);
+ return result;
+}
+
+static HomogeneousCoordinate ProjectHomogeneousPoint(
+ const gfx::Transform& transform,
+ const gfx::PointF& p,
+ bool* clipped) {
+ HomogeneousCoordinate h = ProjectHomogeneousPoint(transform, p);
+ *clipped = h.w() <= 0;
+ return h;
+}
+
+static HomogeneousCoordinate MapHomogeneousPoint(
+ const gfx::Transform& transform,
+ const gfx::Point3F& p) {
+ HomogeneousCoordinate result(p.x(), p.y(), p.z(), 1.0);
+ transform.matrix().mapMScalars(result.vec, result.vec);
+ return result;
+}
+
+static HomogeneousCoordinate ComputeClippedPointForEdge(
+ const HomogeneousCoordinate& h1,
+ const HomogeneousCoordinate& h2) {
+ // Points h1 and h2 form a line in 4d, and any point on that line can be
+ // represented as an interpolation between h1 and h2:
+ // p = (1-t) h1 + (t) h2
+ //
+ // We want to compute point p such that p.w == epsilon, where epsilon is a
+ // small non-zero number. (but the smaller the number is, the higher the risk
+ // of overflow)
+ // To do this, we solve for t in the following equation:
+ // p.w = epsilon = (1-t) * h1.w + (t) * h2.w
+ //
+ // Once paramter t is known, the rest of p can be computed via
+ // p = (1-t) h1 + (t) h2.
+
+ // Technically this is a special case of the following assertion, but its a
+ // good idea to keep it an explicit sanity check here.
+ DCHECK_NE(h2.w(), h1.w());
+ // Exactly one of h1 or h2 (but not both) must be on the negative side of the
+ // w plane when this is called.
+ DCHECK(h1.ShouldBeClipped() ^ h2.ShouldBeClipped());
+
+ // ...or any positive non-zero small epsilon
+ SkMScalar w = 0.00001f;
+ SkMScalar t = (w - h1.w()) / (h2.w() - h1.w());
+
+ SkMScalar x = (SK_MScalar1 - t) * h1.x() + t * h2.x();
+ SkMScalar y = (SK_MScalar1 - t) * h1.y() + t * h2.y();
+ SkMScalar z = (SK_MScalar1 - t) * h1.z() + t * h2.z();
+
+ return HomogeneousCoordinate(x, y, z, w);
+}
+
+static inline void ExpandBoundsToIncludePoint(float* xmin,
+ float* xmax,
+ float* ymin,
+ float* ymax,
+ const gfx::PointF& p) {
+ *xmin = std::min(p.x(), *xmin);
+ *xmax = std::max(p.x(), *xmax);
+ *ymin = std::min(p.y(), *ymin);
+ *ymax = std::max(p.y(), *ymax);
+}
+
+static inline void AddVertexToClippedQuad(const gfx::PointF& new_vertex,
+ gfx::PointF clipped_quad[8],
+ int* num_vertices_in_clipped_quad) {
+ clipped_quad[*num_vertices_in_clipped_quad] = new_vertex;
+ (*num_vertices_in_clipped_quad)++;
+}
+
+static inline void AddVertexToClippedQuad3d(const gfx::Point3F& new_vertex,
+ gfx::Point3F clipped_quad[8],
+ int* num_vertices_in_clipped_quad) {
+ clipped_quad[*num_vertices_in_clipped_quad] = new_vertex;
+ (*num_vertices_in_clipped_quad)++;
+}
+
+gfx::Rect MathUtil::MapEnclosingClippedRect(const gfx::Transform& transform,
+ const gfx::Rect& src_rect) {
+ if (transform.IsIdentityOrIntegerTranslation()) {
+ gfx::Vector2d offset(static_cast<int>(transform.matrix().getFloat(0, 3)),
+ static_cast<int>(transform.matrix().getFloat(1, 3)));
+ return src_rect + offset;
+ }
+ return gfx::ToEnclosingRect(MapClippedRect(transform, gfx::RectF(src_rect)));
+}
+
+gfx::RectF MathUtil::MapClippedRect(const gfx::Transform& transform,
+ const gfx::RectF& src_rect) {
+ if (transform.IsIdentityOrTranslation()) {
+ gfx::Vector2dF offset(transform.matrix().getFloat(0, 3),
+ transform.matrix().getFloat(1, 3));
+ return src_rect + offset;
+ }
+
+ // Apply the transform, but retain the result in homogeneous coordinates.
+
+ SkMScalar quad[4 * 2]; // input: 4 x 2D points
+ quad[0] = src_rect.x();
+ quad[1] = src_rect.y();
+ quad[2] = src_rect.right();
+ quad[3] = src_rect.y();
+ quad[4] = src_rect.right();
+ quad[5] = src_rect.bottom();
+ quad[6] = src_rect.x();
+ quad[7] = src_rect.bottom();
+
+ SkMScalar result[4 * 4]; // output: 4 x 4D homogeneous points
+ transform.matrix().map2(quad, 4, result);
+
+ HomogeneousCoordinate hc0(result[0], result[1], result[2], result[3]);
+ HomogeneousCoordinate hc1(result[4], result[5], result[6], result[7]);
+ HomogeneousCoordinate hc2(result[8], result[9], result[10], result[11]);
+ HomogeneousCoordinate hc3(result[12], result[13], result[14], result[15]);
+ return ComputeEnclosingClippedRect(hc0, hc1, hc2, hc3);
+}
+
+gfx::Rect MathUtil::ProjectEnclosingClippedRect(const gfx::Transform& transform,
+ const gfx::Rect& src_rect) {
+ if (transform.IsIdentityOrIntegerTranslation()) {
+ gfx::Vector2d offset(static_cast<int>(transform.matrix().getFloat(0, 3)),
+ static_cast<int>(transform.matrix().getFloat(1, 3)));
+ return src_rect + offset;
+ }
+ return gfx::ToEnclosingRect(
+ ProjectClippedRect(transform, gfx::RectF(src_rect)));
+}
+
+gfx::RectF MathUtil::ProjectClippedRect(const gfx::Transform& transform,
+ const gfx::RectF& src_rect) {
+ if (transform.IsIdentityOrTranslation()) {
+ gfx::Vector2dF offset(transform.matrix().getFloat(0, 3),
+ transform.matrix().getFloat(1, 3));
+ return src_rect + offset;
+ }
+
+ // Perform the projection, but retain the result in homogeneous coordinates.
+ gfx::QuadF q = gfx::QuadF(src_rect);
+ HomogeneousCoordinate h1 = ProjectHomogeneousPoint(transform, q.p1());
+ HomogeneousCoordinate h2 = ProjectHomogeneousPoint(transform, q.p2());
+ HomogeneousCoordinate h3 = ProjectHomogeneousPoint(transform, q.p3());
+ HomogeneousCoordinate h4 = ProjectHomogeneousPoint(transform, q.p4());
+
+ return ComputeEnclosingClippedRect(h1, h2, h3, h4);
+}
+
+gfx::Rect MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(
+ const gfx::Transform& transform,
+ const gfx::Rect& rect) {
+ DCHECK(transform.Preserves2dAxisAlignment());
+
+ if (transform.IsIdentityOrIntegerTranslation()) {
+ gfx::Vector2d offset(static_cast<int>(transform.matrix().getFloat(0, 3)),
+ static_cast<int>(transform.matrix().getFloat(1, 3)));
+ return rect + offset;
+ }
+ if (transform.IsIdentityOrTranslation()) {
+ gfx::Vector2dF offset(transform.matrix().getFloat(0, 3),
+ transform.matrix().getFloat(1, 3));
+ return gfx::ToEnclosedRect(rect + offset);
+ }
+
+ SkMScalar quad[2 * 2]; // input: 2 x 2D points
+ quad[0] = rect.x();
+ quad[1] = rect.y();
+ quad[2] = rect.right();
+ quad[3] = rect.bottom();
+
+ SkMScalar result[4 * 2]; // output: 2 x 4D homogeneous points
+ transform.matrix().map2(quad, 2, result);
+
+ HomogeneousCoordinate hc0(result[0], result[1], result[2], result[3]);
+ HomogeneousCoordinate hc1(result[4], result[5], result[6], result[7]);
+ DCHECK(!hc0.ShouldBeClipped());
+ DCHECK(!hc1.ShouldBeClipped());
+
+ gfx::PointF top_left(hc0.CartesianPoint2d());
+ gfx::PointF bottom_right(hc1.CartesianPoint2d());
+ return gfx::ToEnclosedRect(gfx::BoundingRect(top_left, bottom_right));
+}
+
+void MathUtil::MapClippedQuad(const gfx::Transform& transform,
+ const gfx::QuadF& src_quad,
+ gfx::PointF clipped_quad[8],
+ int* num_vertices_in_clipped_quad) {
+ HomogeneousCoordinate h1 =
+ MapHomogeneousPoint(transform, gfx::Point3F(src_quad.p1()));
+ HomogeneousCoordinate h2 =
+ MapHomogeneousPoint(transform, gfx::Point3F(src_quad.p2()));
+ HomogeneousCoordinate h3 =
+ MapHomogeneousPoint(transform, gfx::Point3F(src_quad.p3()));
+ HomogeneousCoordinate h4 =
+ MapHomogeneousPoint(transform, gfx::Point3F(src_quad.p4()));
+
+ // The order of adding the vertices to the array is chosen so that
+ // clockwise / counter-clockwise orientation is retained.
+
+ *num_vertices_in_clipped_quad = 0;
+
+ if (!h1.ShouldBeClipped()) {
+ AddVertexToClippedQuad(
+ h1.CartesianPoint2d(), clipped_quad, num_vertices_in_clipped_quad);
+ }
+
+ if (h1.ShouldBeClipped() ^ h2.ShouldBeClipped()) {
+ AddVertexToClippedQuad(
+ ComputeClippedPointForEdge(h1, h2).CartesianPoint2d(),
+ clipped_quad,
+ num_vertices_in_clipped_quad);
+ }
+
+ if (!h2.ShouldBeClipped()) {
+ AddVertexToClippedQuad(
+ h2.CartesianPoint2d(), clipped_quad, num_vertices_in_clipped_quad);
+ }
+
+ if (h2.ShouldBeClipped() ^ h3.ShouldBeClipped()) {
+ AddVertexToClippedQuad(
+ ComputeClippedPointForEdge(h2, h3).CartesianPoint2d(),
+ clipped_quad,
+ num_vertices_in_clipped_quad);
+ }
+
+ if (!h3.ShouldBeClipped()) {
+ AddVertexToClippedQuad(
+ h3.CartesianPoint2d(), clipped_quad, num_vertices_in_clipped_quad);
+ }
+
+ if (h3.ShouldBeClipped() ^ h4.ShouldBeClipped()) {
+ AddVertexToClippedQuad(
+ ComputeClippedPointForEdge(h3, h4).CartesianPoint2d(),
+ clipped_quad,
+ num_vertices_in_clipped_quad);
+ }
+
+ if (!h4.ShouldBeClipped()) {
+ AddVertexToClippedQuad(
+ h4.CartesianPoint2d(), clipped_quad, num_vertices_in_clipped_quad);
+ }
+
+ if (h4.ShouldBeClipped() ^ h1.ShouldBeClipped()) {
+ AddVertexToClippedQuad(
+ ComputeClippedPointForEdge(h4, h1).CartesianPoint2d(),
+ clipped_quad,
+ num_vertices_in_clipped_quad);
+ }
+
+ DCHECK_LE(*num_vertices_in_clipped_quad, 8);
+}
+
+bool MathUtil::MapClippedQuad3d(const gfx::Transform& transform,
+ const gfx::QuadF& src_quad,
+ gfx::Point3F clipped_quad[8],
+ int* num_vertices_in_clipped_quad) {
+ HomogeneousCoordinate h1 =
+ MapHomogeneousPoint(transform, gfx::Point3F(src_quad.p1()));
+ HomogeneousCoordinate h2 =
+ MapHomogeneousPoint(transform, gfx::Point3F(src_quad.p2()));
+ HomogeneousCoordinate h3 =
+ MapHomogeneousPoint(transform, gfx::Point3F(src_quad.p3()));
+ HomogeneousCoordinate h4 =
+ MapHomogeneousPoint(transform, gfx::Point3F(src_quad.p4()));
+
+ // The order of adding the vertices to the array is chosen so that
+ // clockwise / counter-clockwise orientation is retained.
+
+ *num_vertices_in_clipped_quad = 0;
+
+ if (!h1.ShouldBeClipped()) {
+ AddVertexToClippedQuad3d(
+ h1.CartesianPoint3d(), clipped_quad, num_vertices_in_clipped_quad);
+ }
+
+ if (h1.ShouldBeClipped() ^ h2.ShouldBeClipped()) {
+ AddVertexToClippedQuad3d(
+ ComputeClippedPointForEdge(h1, h2).CartesianPoint3d(),
+ clipped_quad,
+ num_vertices_in_clipped_quad);
+ }
+
+ if (!h2.ShouldBeClipped()) {
+ AddVertexToClippedQuad3d(
+ h2.CartesianPoint3d(), clipped_quad, num_vertices_in_clipped_quad);
+ }
+
+ if (h2.ShouldBeClipped() ^ h3.ShouldBeClipped()) {
+ AddVertexToClippedQuad3d(
+ ComputeClippedPointForEdge(h2, h3).CartesianPoint3d(),
+ clipped_quad,
+ num_vertices_in_clipped_quad);
+ }
+
+ if (!h3.ShouldBeClipped()) {
+ AddVertexToClippedQuad3d(
+ h3.CartesianPoint3d(), clipped_quad, num_vertices_in_clipped_quad);
+ }
+
+ if (h3.ShouldBeClipped() ^ h4.ShouldBeClipped()) {
+ AddVertexToClippedQuad3d(
+ ComputeClippedPointForEdge(h3, h4).CartesianPoint3d(),
+ clipped_quad,
+ num_vertices_in_clipped_quad);
+ }
+
+ if (!h4.ShouldBeClipped()) {
+ AddVertexToClippedQuad3d(
+ h4.CartesianPoint3d(), clipped_quad, num_vertices_in_clipped_quad);
+ }
+
+ if (h4.ShouldBeClipped() ^ h1.ShouldBeClipped()) {
+ AddVertexToClippedQuad3d(
+ ComputeClippedPointForEdge(h4, h1).CartesianPoint3d(),
+ clipped_quad,
+ num_vertices_in_clipped_quad);
+ }
+
+ DCHECK_LE(*num_vertices_in_clipped_quad, 8);
+ return (*num_vertices_in_clipped_quad >= 4);
+}
+
+gfx::RectF MathUtil::ComputeEnclosingRectOfVertices(
+ const gfx::PointF vertices[],
+ int num_vertices) {
+ if (num_vertices < 2)
+ return gfx::RectF();
+
+ float xmin = std::numeric_limits<float>::max();
+ float xmax = -std::numeric_limits<float>::max();
+ float ymin = std::numeric_limits<float>::max();
+ float ymax = -std::numeric_limits<float>::max();
+
+ for (int i = 0; i < num_vertices; ++i)
+ ExpandBoundsToIncludePoint(&xmin, &xmax, &ymin, &ymax, vertices[i]);
+
+ return gfx::RectF(gfx::PointF(xmin, ymin),
+ gfx::SizeF(xmax - xmin, ymax - ymin));
+}
+
+gfx::RectF MathUtil::ComputeEnclosingClippedRect(
+ const HomogeneousCoordinate& h1,
+ const HomogeneousCoordinate& h2,
+ const HomogeneousCoordinate& h3,
+ const HomogeneousCoordinate& h4) {
+ // This function performs clipping as necessary and computes the enclosing 2d
+ // gfx::RectF of the vertices. Doing these two steps simultaneously allows us
+ // to avoid the overhead of storing an unknown number of clipped vertices.
+
+ // If no vertices on the quad are clipped, then we can simply return the
+ // enclosing rect directly.
+ bool something_clipped = h1.ShouldBeClipped() || h2.ShouldBeClipped() ||
+ h3.ShouldBeClipped() || h4.ShouldBeClipped();
+ if (!something_clipped) {
+ gfx::QuadF mapped_quad = gfx::QuadF(h1.CartesianPoint2d(),
+ h2.CartesianPoint2d(),
+ h3.CartesianPoint2d(),
+ h4.CartesianPoint2d());
+ return mapped_quad.BoundingBox();
+ }
+
+ bool everything_clipped = h1.ShouldBeClipped() && h2.ShouldBeClipped() &&
+ h3.ShouldBeClipped() && h4.ShouldBeClipped();
+ if (everything_clipped)
+ return gfx::RectF();
+
+ float xmin = std::numeric_limits<float>::max();
+ float xmax = -std::numeric_limits<float>::max();
+ float ymin = std::numeric_limits<float>::max();
+ float ymax = -std::numeric_limits<float>::max();
+
+ if (!h1.ShouldBeClipped())
+ ExpandBoundsToIncludePoint(&xmin, &xmax, &ymin, &ymax,
+ h1.CartesianPoint2d());
+
+ if (h1.ShouldBeClipped() ^ h2.ShouldBeClipped())
+ ExpandBoundsToIncludePoint(&xmin,
+ &xmax,
+ &ymin,
+ &ymax,
+ ComputeClippedPointForEdge(h1, h2)
+ .CartesianPoint2d());
+
+ if (!h2.ShouldBeClipped())
+ ExpandBoundsToIncludePoint(&xmin, &xmax, &ymin, &ymax,
+ h2.CartesianPoint2d());
+
+ if (h2.ShouldBeClipped() ^ h3.ShouldBeClipped())
+ ExpandBoundsToIncludePoint(&xmin,
+ &xmax,
+ &ymin,
+ &ymax,
+ ComputeClippedPointForEdge(h2, h3)
+ .CartesianPoint2d());
+
+ if (!h3.ShouldBeClipped())
+ ExpandBoundsToIncludePoint(&xmin, &xmax, &ymin, &ymax,
+ h3.CartesianPoint2d());
+
+ if (h3.ShouldBeClipped() ^ h4.ShouldBeClipped())
+ ExpandBoundsToIncludePoint(&xmin,
+ &xmax,
+ &ymin,
+ &ymax,
+ ComputeClippedPointForEdge(h3, h4)
+ .CartesianPoint2d());
+
+ if (!h4.ShouldBeClipped())
+ ExpandBoundsToIncludePoint(&xmin, &xmax, &ymin, &ymax,
+ h4.CartesianPoint2d());
+
+ if (h4.ShouldBeClipped() ^ h1.ShouldBeClipped())
+ ExpandBoundsToIncludePoint(&xmin,
+ &xmax,
+ &ymin,
+ &ymax,
+ ComputeClippedPointForEdge(h4, h1)
+ .CartesianPoint2d());
+
+ return gfx::RectF(gfx::PointF(xmin, ymin),
+ gfx::SizeF(xmax - xmin, ymax - ymin));
+}
+
+gfx::QuadF MathUtil::MapQuad(const gfx::Transform& transform,
+ const gfx::QuadF& q,
+ bool* clipped) {
+ if (transform.IsIdentityOrTranslation()) {
+ gfx::QuadF mapped_quad(q);
+ mapped_quad += gfx::Vector2dF(transform.matrix().getFloat(0, 3),
+ transform.matrix().getFloat(1, 3));
+ *clipped = false;
+ return mapped_quad;
+ }
+
+ HomogeneousCoordinate h1 =
+ MapHomogeneousPoint(transform, gfx::Point3F(q.p1()));
+ HomogeneousCoordinate h2 =
+ MapHomogeneousPoint(transform, gfx::Point3F(q.p2()));
+ HomogeneousCoordinate h3 =
+ MapHomogeneousPoint(transform, gfx::Point3F(q.p3()));
+ HomogeneousCoordinate h4 =
+ MapHomogeneousPoint(transform, gfx::Point3F(q.p4()));
+
+ *clipped = h1.ShouldBeClipped() || h2.ShouldBeClipped() ||
+ h3.ShouldBeClipped() || h4.ShouldBeClipped();
+
+ // Result will be invalid if clipped == true. But, compute it anyway just in
+ // case, to emulate existing behavior.
+ return gfx::QuadF(h1.CartesianPoint2d(),
+ h2.CartesianPoint2d(),
+ h3.CartesianPoint2d(),
+ h4.CartesianPoint2d());
+}
+
+gfx::QuadF MathUtil::MapQuad3d(const gfx::Transform& transform,
+ const gfx::QuadF& q,
+ gfx::Point3F* p,
+ bool* clipped) {
+ if (transform.IsIdentityOrTranslation()) {
+ gfx::QuadF mapped_quad(q);
+ mapped_quad += gfx::Vector2dF(transform.matrix().getFloat(0, 3),
+ transform.matrix().getFloat(1, 3));
+ *clipped = false;
+ p[0] = gfx::Point3F(mapped_quad.p1().x(), mapped_quad.p1().y(), 0.0f);
+ p[1] = gfx::Point3F(mapped_quad.p2().x(), mapped_quad.p2().y(), 0.0f);
+ p[2] = gfx::Point3F(mapped_quad.p3().x(), mapped_quad.p3().y(), 0.0f);
+ p[3] = gfx::Point3F(mapped_quad.p4().x(), mapped_quad.p4().y(), 0.0f);
+ return mapped_quad;
+ }
+
+ HomogeneousCoordinate h1 =
+ MapHomogeneousPoint(transform, gfx::Point3F(q.p1()));
+ HomogeneousCoordinate h2 =
+ MapHomogeneousPoint(transform, gfx::Point3F(q.p2()));
+ HomogeneousCoordinate h3 =
+ MapHomogeneousPoint(transform, gfx::Point3F(q.p3()));
+ HomogeneousCoordinate h4 =
+ MapHomogeneousPoint(transform, gfx::Point3F(q.p4()));
+
+ *clipped = h1.ShouldBeClipped() || h2.ShouldBeClipped() ||
+ h3.ShouldBeClipped() || h4.ShouldBeClipped();
+
+ // Result will be invalid if clipped == true. But, compute it anyway just in
+ // case, to emulate existing behavior.
+ p[0] = h1.CartesianPoint3d();
+ p[1] = h2.CartesianPoint3d();
+ p[2] = h3.CartesianPoint3d();
+ p[3] = h4.CartesianPoint3d();
+
+ return gfx::QuadF(h1.CartesianPoint2d(),
+ h2.CartesianPoint2d(),
+ h3.CartesianPoint2d(),
+ h4.CartesianPoint2d());
+}
+
+gfx::PointF MathUtil::MapPoint(const gfx::Transform& transform,
+ const gfx::PointF& p,
+ bool* clipped) {
+ HomogeneousCoordinate h = MapHomogeneousPoint(transform, gfx::Point3F(p));
+
+ if (h.w() > 0) {
+ *clipped = false;
+ return h.CartesianPoint2d();
+ }
+
+ // The cartesian coordinates will be invalid after dividing by w.
+ *clipped = true;
+
+ // Avoid dividing by w if w == 0.
+ if (!h.w())
+ return gfx::PointF();
+
+ // This return value will be invalid because clipped == true, but (1) users of
+ // this code should be ignoring the return value when clipped == true anyway,
+ // and (2) this behavior is more consistent with existing behavior of WebKit
+ // transforms if the user really does not ignore the return value.
+ return h.CartesianPoint2d();
+}
+
+gfx::Point3F MathUtil::MapPoint(const gfx::Transform& transform,
+ const gfx::Point3F& p,
+ bool* clipped) {
+ HomogeneousCoordinate h = MapHomogeneousPoint(transform, p);
+
+ if (h.w() > 0) {
+ *clipped = false;
+ return h.CartesianPoint3d();
+ }
+
+ // The cartesian coordinates will be invalid after dividing by w.
+ *clipped = true;
+
+ // Avoid dividing by w if w == 0.
+ if (!h.w())
+ return gfx::Point3F();
+
+ // This return value will be invalid because clipped == true, but (1) users of
+ // this code should be ignoring the return value when clipped == true anyway,
+ // and (2) this behavior is more consistent with existing behavior of WebKit
+ // transforms if the user really does not ignore the return value.
+ return h.CartesianPoint3d();
+}
+
+gfx::QuadF MathUtil::ProjectQuad(const gfx::Transform& transform,
+ const gfx::QuadF& q,
+ bool* clipped) {
+ gfx::QuadF projected_quad;
+ bool clipped_point;
+ projected_quad.set_p1(ProjectPoint(transform, q.p1(), &clipped_point));
+ *clipped = clipped_point;
+ projected_quad.set_p2(ProjectPoint(transform, q.p2(), &clipped_point));
+ *clipped |= clipped_point;
+ projected_quad.set_p3(ProjectPoint(transform, q.p3(), &clipped_point));
+ *clipped |= clipped_point;
+ projected_quad.set_p4(ProjectPoint(transform, q.p4(), &clipped_point));
+ *clipped |= clipped_point;
+
+ return projected_quad;
+}
+
+gfx::PointF MathUtil::ProjectPoint(const gfx::Transform& transform,
+ const gfx::PointF& p,
+ bool* clipped) {
+ HomogeneousCoordinate h = ProjectHomogeneousPoint(transform, p, clipped);
+ // Avoid dividing by w if w == 0.
+ if (!h.w())
+ return gfx::PointF();
+
+ // This return value will be invalid if clipped == true, but (1) users of
+ // this code should be ignoring the return value when clipped == true anyway,
+ // and (2) this behavior is more consistent with existing behavior of WebKit
+ // transforms if the user really does not ignore the return value.
+ return h.CartesianPoint2d();
+}
+
+gfx::Point3F MathUtil::ProjectPoint3D(const gfx::Transform& transform,
+ const gfx::PointF& p,
+ bool* clipped) {
+ HomogeneousCoordinate h = ProjectHomogeneousPoint(transform, p, clipped);
+ if (!h.w())
+ return gfx::Point3F();
+ return h.CartesianPoint3d();
+}
+
+gfx::RectF MathUtil::ScaleRectProportional(const gfx::RectF& input_outer_rect,
+ const gfx::RectF& scale_outer_rect,
+ const gfx::RectF& scale_inner_rect) {
+ gfx::RectF output_inner_rect = input_outer_rect;
+ float scale_rect_to_input_scale_x =
+ scale_outer_rect.width() / input_outer_rect.width();
+ float scale_rect_to_input_scale_y =
+ scale_outer_rect.height() / input_outer_rect.height();
+
+ gfx::Vector2dF top_left_diff =
+ scale_inner_rect.origin() - scale_outer_rect.origin();
+ gfx::Vector2dF bottom_right_diff =
+ scale_inner_rect.bottom_right() - scale_outer_rect.bottom_right();
+ output_inner_rect.Inset(top_left_diff.x() / scale_rect_to_input_scale_x,
+ top_left_diff.y() / scale_rect_to_input_scale_y,
+ -bottom_right_diff.x() / scale_rect_to_input_scale_x,
+ -bottom_right_diff.y() / scale_rect_to_input_scale_y);
+ return output_inner_rect;
+}
+
+static inline bool NearlyZero(double value) {
+ return std::abs(value) < std::numeric_limits<double>::epsilon();
+}
+
+static inline float ScaleOnAxis(double a, double b, double c) {
+ if (NearlyZero(b) && NearlyZero(c))
+ return std::abs(a);
+ if (NearlyZero(a) && NearlyZero(c))
+ return std::abs(b);
+ if (NearlyZero(a) && NearlyZero(b))
+ return std::abs(c);
+
+ // Do the sqrt as a double to not lose precision.
+ return static_cast<float>(std::sqrt(a * a + b * b + c * c));
+}
+
+gfx::Vector2dF MathUtil::ComputeTransform2dScaleComponents(
+ const gfx::Transform& transform,
+ float fallback_value) {
+ if (transform.HasPerspective())
+ return gfx::Vector2dF(fallback_value, fallback_value);
+ float x_scale = ScaleOnAxis(transform.matrix().getDouble(0, 0),
+ transform.matrix().getDouble(1, 0),
+ transform.matrix().getDouble(2, 0));
+ float y_scale = ScaleOnAxis(transform.matrix().getDouble(0, 1),
+ transform.matrix().getDouble(1, 1),
+ transform.matrix().getDouble(2, 1));
+ return gfx::Vector2dF(x_scale, y_scale);
+}
+
+float MathUtil::SmallestAngleBetweenVectors(const gfx::Vector2dF& v1,
+ const gfx::Vector2dF& v2) {
+ double dot_product = gfx::DotProduct(v1, v2) / v1.Length() / v2.Length();
+ // Clamp to compensate for rounding errors.
+ dot_product = std::max(-1.0, std::min(1.0, dot_product));
+ return static_cast<float>(Rad2Deg(std::acos(dot_product)));
+}
+
+gfx::Vector2dF MathUtil::ProjectVector(const gfx::Vector2dF& source,
+ const gfx::Vector2dF& destination) {
+ float projected_length =
+ gfx::DotProduct(source, destination) / destination.LengthSquared();
+ return gfx::Vector2dF(projected_length * destination.x(),
+ projected_length * destination.y());
+}
+
+scoped_ptr<base::Value> MathUtil::AsValue(const gfx::Size& s) {
+ scoped_ptr<base::DictionaryValue> res(new base::DictionaryValue());
+ res->SetDouble("width", s.width());
+ res->SetDouble("height", s.height());
+ return res.Pass();
+}
+
+scoped_ptr<base::Value> MathUtil::AsValue(const gfx::Rect& r) {
+ scoped_ptr<base::ListValue> res(new base::ListValue());
+ res->AppendInteger(r.x());
+ res->AppendInteger(r.y());
+ res->AppendInteger(r.width());
+ res->AppendInteger(r.height());
+ return res.Pass();
+}
+
+bool MathUtil::FromValue(const base::Value* raw_value, gfx::Rect* out_rect) {
+ const base::ListValue* value = NULL;
+ if (!raw_value->GetAsList(&value))
+ return false;
+
+ if (value->GetSize() != 4)
+ return false;
+
+ int x, y, w, h;
+ bool ok = true;
+ ok &= value->GetInteger(0, &x);
+ ok &= value->GetInteger(1, &y);
+ ok &= value->GetInteger(2, &w);
+ ok &= value->GetInteger(3, &h);
+ if (!ok)
+ return false;
+
+ *out_rect = gfx::Rect(x, y, w, h);
+ return true;
+}
+
+scoped_ptr<base::Value> MathUtil::AsValue(const gfx::PointF& pt) {
+ scoped_ptr<base::ListValue> res(new base::ListValue());
+ res->AppendDouble(pt.x());
+ res->AppendDouble(pt.y());
+ return res.Pass();
+}
+
+void MathUtil::AddToTracedValue(const gfx::Size& s,
+ base::debug::TracedValue* res) {
+ res->SetDouble("width", s.width());
+ res->SetDouble("height", s.height());
+}
+
+void MathUtil::AddToTracedValue(const gfx::SizeF& s,
+ base::debug::TracedValue* res) {
+ res->SetDouble("width", s.width());
+ res->SetDouble("height", s.height());
+}
+
+void MathUtil::AddToTracedValue(const gfx::Rect& r,
+ base::debug::TracedValue* res) {
+ res->AppendInteger(r.x());
+ res->AppendInteger(r.y());
+ res->AppendInteger(r.width());
+ res->AppendInteger(r.height());
+}
+
+void MathUtil::AddToTracedValue(const gfx::PointF& pt,
+ base::debug::TracedValue* res) {
+ res->AppendDouble(pt.x());
+ res->AppendDouble(pt.y());
+}
+
+void MathUtil::AddToTracedValue(const gfx::Point3F& pt,
+ base::debug::TracedValue* res) {
+ res->AppendDouble(pt.x());
+ res->AppendDouble(pt.y());
+ res->AppendDouble(pt.z());
+}
+
+void MathUtil::AddToTracedValue(const gfx::Vector2d& v,
+ base::debug::TracedValue* res) {
+ res->AppendInteger(v.x());
+ res->AppendInteger(v.y());
+}
+
+void MathUtil::AddToTracedValue(const gfx::Vector2dF& v,
+ base::debug::TracedValue* res) {
+ res->AppendDouble(v.x());
+ res->AppendDouble(v.y());
+}
+
+void MathUtil::AddToTracedValue(const gfx::ScrollOffset& v,
+ base::debug::TracedValue* res) {
+ res->AppendDouble(v.x());
+ res->AppendDouble(v.y());
+}
+
+void MathUtil::AddToTracedValue(const gfx::QuadF& q,
+ base::debug::TracedValue* res) {
+ res->AppendDouble(q.p1().x());
+ res->AppendDouble(q.p1().y());
+ res->AppendDouble(q.p2().x());
+ res->AppendDouble(q.p2().y());
+ res->AppendDouble(q.p3().x());
+ res->AppendDouble(q.p3().y());
+ res->AppendDouble(q.p4().x());
+ res->AppendDouble(q.p4().y());
+}
+
+void MathUtil::AddToTracedValue(const gfx::RectF& rect,
+ base::debug::TracedValue* res) {
+ res->AppendDouble(rect.x());
+ res->AppendDouble(rect.y());
+ res->AppendDouble(rect.width());
+ res->AppendDouble(rect.height());
+}
+
+void MathUtil::AddToTracedValue(const gfx::Transform& transform,
+ base::debug::TracedValue* res) {
+ const SkMatrix44& m = transform.matrix();
+ for (int row = 0; row < 4; ++row) {
+ for (int col = 0; col < 4; ++col)
+ res->AppendDouble(m.getDouble(row, col));
+ }
+}
+
+void MathUtil::AddToTracedValue(const gfx::BoxF& box,
+ base::debug::TracedValue* res) {
+ res->AppendInteger(box.x());
+ res->AppendInteger(box.y());
+ res->AppendInteger(box.z());
+ res->AppendInteger(box.width());
+ res->AppendInteger(box.height());
+ res->AppendInteger(box.depth());
+}
+
+double MathUtil::AsDoubleSafely(double value) {
+ return std::min(value, std::numeric_limits<double>::max());
+}
+
+float MathUtil::AsFloatSafely(float value) {
+ return std::min(value, std::numeric_limits<float>::max());
+}
+
+} // namespace cc
diff --git a/cc/base/math_util.h b/cc/base/math_util.h
new file mode 100644
index 0000000..622ea4d
--- /dev/null
+++ b/cc/base/math_util.h
@@ -0,0 +1,235 @@
+// Copyright 2012 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_MATH_UTIL_H_
+#define CC_BASE_MATH_UTIL_H_
+
+#include <algorithm>
+#include <cmath>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "cc/base/cc_export.h"
+#include "ui/gfx/box_f.h"
+#include "ui/gfx/geometry/scroll_offset.h"
+#include "ui/gfx/point3_f.h"
+#include "ui/gfx/point_f.h"
+#include "ui/gfx/size.h"
+#include "ui/gfx/transform.h"
+
+namespace base {
+class Value;
+namespace debug {
+class TracedValue;
+}
+}
+
+namespace gfx {
+class QuadF;
+class Rect;
+class RectF;
+class Transform;
+class Vector2dF;
+class Vector2d;
+}
+
+namespace cc {
+
+struct HomogeneousCoordinate {
+ HomogeneousCoordinate(SkMScalar x, SkMScalar y, SkMScalar z, SkMScalar w) {
+ vec[0] = x;
+ vec[1] = y;
+ vec[2] = z;
+ vec[3] = w;
+ }
+
+ bool ShouldBeClipped() const { return w() <= 0.0; }
+
+ gfx::PointF CartesianPoint2d() const {
+ if (w() == SK_MScalar1)
+ return gfx::PointF(x(), y());
+
+ // For now, because this code is used privately only by MathUtil, it should
+ // never be called when w == 0, and we do not yet need to handle that case.
+ DCHECK(w());
+ SkMScalar inv_w = SK_MScalar1 / w();
+ return gfx::PointF(x() * inv_w, y() * inv_w);
+ }
+
+ gfx::Point3F CartesianPoint3d() const {
+ if (w() == SK_MScalar1)
+ return gfx::Point3F(x(), y(), z());
+
+ // For now, because this code is used privately only by MathUtil, it should
+ // never be called when w == 0, and we do not yet need to handle that case.
+ DCHECK(w());
+ SkMScalar inv_w = SK_MScalar1 / w();
+ return gfx::Point3F(x() * inv_w, y() * inv_w, z() * inv_w);
+ }
+
+ SkMScalar x() const { return vec[0]; }
+ SkMScalar y() const { return vec[1]; }
+ SkMScalar z() const { return vec[2]; }
+ SkMScalar w() const { return vec[3]; }
+
+ SkMScalar vec[4];
+};
+
+class CC_EXPORT MathUtil {
+ public:
+ static const double kPiDouble;
+ static const float kPiFloat;
+
+ static double Deg2Rad(double deg) { return deg * kPiDouble / 180.0; }
+ static double Rad2Deg(double rad) { return rad * 180.0 / kPiDouble; }
+
+ static float Deg2Rad(float deg) { return deg * kPiFloat / 180.0f; }
+ static float Rad2Deg(float rad) { return rad * 180.0f / kPiFloat; }
+
+ static float Round(float f) {
+ return (f > 0.f) ? std::floor(f + 0.5f) : std::ceil(f - 0.5f);
+ }
+ static double Round(double d) {
+ return (d > 0.0) ? std::floor(d + 0.5) : std::ceil(d - 0.5);
+ }
+
+ template <typename T> static T ClampToRange(T value, T min, T max) {
+ return std::min(std::max(value, min), max);
+ }
+
+ // Background: Existing transform code does not do the right thing in
+ // MapRect / MapQuad / ProjectQuad when there is a perspective projection that
+ // causes one of the transformed vertices to go to w < 0. In those cases, it
+ // is necessary to perform clipping in homogeneous coordinates, after applying
+ // the transform, before dividing-by-w to convert to cartesian coordinates.
+ //
+ // These functions return the axis-aligned rect that encloses the correctly
+ // clipped, transformed polygon.
+ static gfx::Rect MapEnclosingClippedRect(const gfx::Transform& transform,
+ const gfx::Rect& rect);
+ static gfx::RectF MapClippedRect(const gfx::Transform& transform,
+ const gfx::RectF& rect);
+ static gfx::Rect ProjectEnclosingClippedRect(const gfx::Transform& transform,
+ const gfx::Rect& rect);
+ static gfx::RectF ProjectClippedRect(const gfx::Transform& transform,
+ const gfx::RectF& rect);
+
+ // This function is only valid when the transform preserves 2d axis
+ // alignment and the resulting rect will not be clipped.
+ static gfx::Rect MapEnclosedRectWith2dAxisAlignedTransform(
+ const gfx::Transform& transform,
+ const gfx::Rect& rect);
+
+ // Returns an array of vertices that represent the clipped polygon. After
+ // returning, indexes from 0 to num_vertices_in_clipped_quad are valid in the
+ // clipped_quad array. Note that num_vertices_in_clipped_quad may be zero,
+ // which means the entire quad was clipped, and none of the vertices in the
+ // array are valid.
+ static void MapClippedQuad(const gfx::Transform& transform,
+ const gfx::QuadF& src_quad,
+ gfx::PointF clipped_quad[8],
+ int* num_vertices_in_clipped_quad);
+ static bool MapClippedQuad3d(const gfx::Transform& transform,
+ const gfx::QuadF& src_quad,
+ gfx::Point3F clipped_quad[8],
+ int* num_vertices_in_clipped_quad);
+
+ static gfx::RectF ComputeEnclosingRectOfVertices(const gfx::PointF vertices[],
+ int num_vertices);
+ static gfx::RectF ComputeEnclosingClippedRect(
+ const HomogeneousCoordinate& h1,
+ const HomogeneousCoordinate& h2,
+ const HomogeneousCoordinate& h3,
+ const HomogeneousCoordinate& h4);
+
+ // NOTE: These functions do not do correct clipping against w = 0 plane, but
+ // they correctly detect the clipped condition via the boolean clipped.
+ static gfx::QuadF MapQuad(const gfx::Transform& transform,
+ const gfx::QuadF& quad,
+ bool* clipped);
+ static gfx::QuadF MapQuad3d(const gfx::Transform& transform,
+ const gfx::QuadF& q,
+ gfx::Point3F* p,
+ bool* clipped);
+ static gfx::PointF MapPoint(const gfx::Transform& transform,
+ const gfx::PointF& point,
+ bool* clipped);
+ static gfx::Point3F MapPoint(const gfx::Transform&,
+ const gfx::Point3F&,
+ bool* clipped);
+ static gfx::QuadF ProjectQuad(const gfx::Transform& transform,
+ const gfx::QuadF& quad,
+ bool* clipped);
+ static gfx::PointF ProjectPoint(const gfx::Transform& transform,
+ const gfx::PointF& point,
+ bool* clipped);
+ // Identical to the above function, but coerces the homogeneous coordinate to
+ // a 3d rather than a 2d point.
+ static gfx::Point3F ProjectPoint3D(const gfx::Transform& transform,
+ const gfx::PointF& point,
+ bool* clipped);
+
+ static gfx::Vector2dF ComputeTransform2dScaleComponents(const gfx::Transform&,
+ float fallbackValue);
+
+ // Makes a rect that has the same relationship to input_outer_rect as
+ // scale_inner_rect has to scale_outer_rect. scale_inner_rect should be
+ // contained within scale_outer_rect, and likewise the rectangle that is
+ // returned will be within input_outer_rect at a similar relative, scaled
+ // position.
+ static gfx::RectF ScaleRectProportional(const gfx::RectF& input_outer_rect,
+ const gfx::RectF& scale_outer_rect,
+ const gfx::RectF& scale_inner_rect);
+
+ // Returns the smallest angle between the given two vectors in degrees.
+ // Neither vector is assumed to be normalized.
+ static float SmallestAngleBetweenVectors(const gfx::Vector2dF& v1,
+ const gfx::Vector2dF& v2);
+
+ // Projects the |source| vector onto |destination|. Neither vector is assumed
+ // to be normalized.
+ static gfx::Vector2dF ProjectVector(const gfx::Vector2dF& source,
+ const gfx::Vector2dF& destination);
+
+ // Conversion to value.
+ static scoped_ptr<base::Value> AsValue(const gfx::Size& s);
+ static scoped_ptr<base::Value> AsValue(const gfx::Rect& r);
+ static bool FromValue(const base::Value*, gfx::Rect* out_rect);
+ static scoped_ptr<base::Value> AsValue(const gfx::PointF& q);
+
+ static void AddToTracedValue(const gfx::Size& s,
+ base::debug::TracedValue* res);
+ static void AddToTracedValue(const gfx::SizeF& s,
+ base::debug::TracedValue* res);
+ static void AddToTracedValue(const gfx::Rect& r,
+ base::debug::TracedValue* res);
+ static void AddToTracedValue(const gfx::PointF& q,
+ base::debug::TracedValue* res);
+ static void AddToTracedValue(const gfx::Point3F&,
+ base::debug::TracedValue* res);
+ static void AddToTracedValue(const gfx::Vector2d& v,
+ base::debug::TracedValue* res);
+ static void AddToTracedValue(const gfx::Vector2dF& v,
+ base::debug::TracedValue* res);
+ static void AddToTracedValue(const gfx::ScrollOffset& v,
+ base::debug::TracedValue* res);
+ static void AddToTracedValue(const gfx::QuadF& q,
+ base::debug::TracedValue* res);
+ static void AddToTracedValue(const gfx::RectF& rect,
+ base::debug::TracedValue* res);
+ static void AddToTracedValue(const gfx::Transform& transform,
+ base::debug::TracedValue* res);
+ static void AddToTracedValue(const gfx::BoxF& box,
+ base::debug::TracedValue* res);
+
+ // Returns a base::Value representation of the floating point value.
+ // If the value is inf, returns max double/float representation.
+ static double AsDoubleSafely(double value);
+ static float AsFloatSafely(float value);
+};
+
+} // namespace cc
+
+#endif // CC_BASE_MATH_UTIL_H_
diff --git a/cc/base/math_util_unittest.cc b/cc/base/math_util_unittest.cc
new file mode 100644
index 0000000..fb8c27f
--- /dev/null
+++ b/cc/base/math_util_unittest.cc
@@ -0,0 +1,197 @@
+// Copyright 2012 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/math_util.h"
+
+#include <cmath>
+
+#include "cc/test/geometry_test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/rect_f.h"
+#include "ui/gfx/transform.h"
+
+namespace cc {
+namespace {
+
+TEST(MathUtilTest, ProjectionOfPerpendicularPlane) {
+ // In this case, the m33() element of the transform becomes zero, which could
+ // cause a divide-by-zero when projecting points/quads.
+
+ gfx::Transform transform;
+ transform.MakeIdentity();
+ transform.matrix().set(2, 2, 0);
+
+ gfx::RectF rect = gfx::RectF(0, 0, 1, 1);
+ gfx::RectF projected_rect = MathUtil::ProjectClippedRect(transform, rect);
+
+ EXPECT_EQ(0, projected_rect.x());
+ EXPECT_EQ(0, projected_rect.y());
+ EXPECT_TRUE(projected_rect.IsEmpty());
+}
+
+TEST(MathUtilTest, EnclosingClippedRectUsesCorrectInitialBounds) {
+ HomogeneousCoordinate h1(-100, -100, 0, 1);
+ HomogeneousCoordinate h2(-10, -10, 0, 1);
+ HomogeneousCoordinate h3(10, 10, 0, -1);
+ HomogeneousCoordinate h4(100, 100, 0, -1);
+
+ // The bounds of the enclosing clipped rect should be -100 to -10 for both x
+ // and y. However, if there is a bug where the initial xmin/xmax/ymin/ymax are
+ // initialized to numeric_limits<float>::min() (which is zero, not -flt_max)
+ // then the enclosing clipped rect will be computed incorrectly.
+ gfx::RectF result = MathUtil::ComputeEnclosingClippedRect(h1, h2, h3, h4);
+
+ // Due to floating point math in ComputeClippedPointForEdge this result
+ // is fairly imprecise. 0.15f was empirically determined.
+ EXPECT_RECT_NEAR(
+ gfx::RectF(gfx::PointF(-100, -100), gfx::SizeF(90, 90)), result, 0.15f);
+}
+
+TEST(MathUtilTest, EnclosingRectOfVerticesUsesCorrectInitialBounds) {
+ gfx::PointF vertices[3];
+ int num_vertices = 3;
+
+ vertices[0] = gfx::PointF(-10, -100);
+ vertices[1] = gfx::PointF(-100, -10);
+ vertices[2] = gfx::PointF(-30, -30);
+
+ // The bounds of the enclosing rect should be -100 to -10 for both x and y.
+ // However, if there is a bug where the initial xmin/xmax/ymin/ymax are
+ // initialized to numeric_limits<float>::min() (which is zero, not -flt_max)
+ // then the enclosing clipped rect will be computed incorrectly.
+ gfx::RectF result =
+ MathUtil::ComputeEnclosingRectOfVertices(vertices, num_vertices);
+
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(gfx::PointF(-100, -100), gfx::SizeF(90, 90)),
+ result);
+}
+
+TEST(MathUtilTest, SmallestAngleBetweenVectors) {
+ gfx::Vector2dF x(1, 0);
+ gfx::Vector2dF y(0, 1);
+ gfx::Vector2dF test_vector(0.5, 0.5);
+
+ // Orthogonal vectors are at an angle of 90 degress.
+ EXPECT_EQ(90, MathUtil::SmallestAngleBetweenVectors(x, y));
+
+ // A vector makes a zero angle with itself.
+ EXPECT_EQ(0, MathUtil::SmallestAngleBetweenVectors(x, x));
+ EXPECT_EQ(0, MathUtil::SmallestAngleBetweenVectors(y, y));
+ EXPECT_EQ(0, MathUtil::SmallestAngleBetweenVectors(test_vector, test_vector));
+
+ // Parallel but reversed vectors are at 180 degrees.
+ EXPECT_FLOAT_EQ(180, MathUtil::SmallestAngleBetweenVectors(x, -x));
+ EXPECT_FLOAT_EQ(180, MathUtil::SmallestAngleBetweenVectors(y, -y));
+ EXPECT_FLOAT_EQ(
+ 180, MathUtil::SmallestAngleBetweenVectors(test_vector, -test_vector));
+
+ // The test vector is at a known angle.
+ EXPECT_FLOAT_EQ(
+ 45, std::floor(MathUtil::SmallestAngleBetweenVectors(test_vector, x)));
+ EXPECT_FLOAT_EQ(
+ 45, std::floor(MathUtil::SmallestAngleBetweenVectors(test_vector, y)));
+}
+
+TEST(MathUtilTest, VectorProjection) {
+ gfx::Vector2dF x(1, 0);
+ gfx::Vector2dF y(0, 1);
+ gfx::Vector2dF test_vector(0.3f, 0.7f);
+
+ // Orthogonal vectors project to a zero vector.
+ EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), MathUtil::ProjectVector(x, y));
+ EXPECT_VECTOR_EQ(gfx::Vector2dF(0, 0), MathUtil::ProjectVector(y, x));
+
+ // Projecting a vector onto the orthonormal basis gives the corresponding
+ // component of the vector.
+ EXPECT_VECTOR_EQ(gfx::Vector2dF(test_vector.x(), 0),
+ MathUtil::ProjectVector(test_vector, x));
+ EXPECT_VECTOR_EQ(gfx::Vector2dF(0, test_vector.y()),
+ MathUtil::ProjectVector(test_vector, y));
+
+ // Finally check than an arbitrary vector projected to another one gives a
+ // vector parallel to the second vector.
+ gfx::Vector2dF target_vector(0.5, 0.2f);
+ gfx::Vector2dF projected_vector =
+ MathUtil::ProjectVector(test_vector, target_vector);
+ EXPECT_EQ(projected_vector.x() / target_vector.x(),
+ projected_vector.y() / target_vector.y());
+}
+
+TEST(MathUtilTest, MapEnclosedRectWith2dAxisAlignedTransform) {
+ gfx::Rect input(1, 2, 3, 4);
+ gfx::Rect output;
+ gfx::Transform transform;
+
+ // Identity.
+ output =
+ MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform, input);
+ EXPECT_EQ(input, output);
+
+ // Integer translate.
+ transform.Translate(2.0, 3.0);
+ output =
+ MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform, input);
+ EXPECT_EQ(gfx::Rect(3, 5, 3, 4), output);
+
+ // Non-integer translate.
+ transform.Translate(0.5, 0.5);
+ output =
+ MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform, input);
+ EXPECT_EQ(gfx::Rect(4, 6, 2, 3), output);
+
+ // Scale.
+ transform = gfx::Transform();
+ transform.Scale(2.0, 3.0);
+ output =
+ MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform, input);
+ EXPECT_EQ(gfx::Rect(2, 6, 6, 12), output);
+
+ // Rotate Z.
+ transform = gfx::Transform();
+ transform.Translate(1.0, 2.0);
+ transform.RotateAboutZAxis(90.0);
+ transform.Translate(-1.0, -2.0);
+ output =
+ MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform, input);
+ EXPECT_EQ(gfx::Rect(-3, 2, 4, 3), output);
+
+ // Rotate X.
+ transform = gfx::Transform();
+ transform.RotateAboutXAxis(90.0);
+ output =
+ MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform, input);
+ EXPECT_TRUE(output.IsEmpty());
+
+ transform = gfx::Transform();
+ transform.RotateAboutXAxis(180.0);
+ output =
+ MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform, input);
+ EXPECT_EQ(gfx::Rect(1, -6, 3, 4), output);
+
+ // Rotate Y.
+ transform = gfx::Transform();
+ transform.RotateAboutYAxis(90.0);
+ output =
+ MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform, input);
+ EXPECT_TRUE(output.IsEmpty());
+
+ transform = gfx::Transform();
+ transform.RotateAboutYAxis(180.0);
+ output =
+ MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform, input);
+ EXPECT_EQ(gfx::Rect(-4, 2, 3, 4), output);
+
+ // Translate Z.
+ transform = gfx::Transform();
+ transform.ApplyPerspectiveDepth(10.0);
+ transform.Translate3d(0.0, 0.0, 5.0);
+ output =
+ MathUtil::MapEnclosedRectWith2dAxisAlignedTransform(transform, input);
+ EXPECT_EQ(gfx::Rect(2, 4, 6, 8), output);
+}
+
+} // namespace
+} // namespace cc
diff --git a/cc/base/ref_counted_managed.h b/cc/base/ref_counted_managed.h
new file mode 100644
index 0000000..8bb836f
--- /dev/null
+++ b/cc/base/ref_counted_managed.h
@@ -0,0 +1,65 @@
+// Copyright 2013 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_REF_COUNTED_MANAGED_H_
+#define CC_BASE_REF_COUNTED_MANAGED_H_
+
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "cc/base/cc_export.h"
+
+namespace cc {
+
+template <typename T> class RefCountedManaged;
+
+template <typename T>
+class CC_EXPORT RefCountedManager {
+ protected:
+ RefCountedManager() : live_object_count_(0) {}
+ ~RefCountedManager() {
+ CHECK_EQ(0, live_object_count_);
+ }
+
+ virtual void Release(T* object) = 0;
+
+ private:
+ friend class RefCountedManaged<T>;
+ int live_object_count_;
+};
+
+template <typename T>
+class CC_EXPORT RefCountedManaged : public base::subtle::RefCountedBase {
+ public:
+ explicit RefCountedManaged(RefCountedManager<T>* manager)
+ : manager_(manager) {
+ manager_->live_object_count_++;
+ }
+
+ void AddRef() const {
+ base::subtle::RefCountedBase::AddRef();
+ }
+
+ void Release() {
+ if (base::subtle::RefCountedBase::Release()) {
+ DCHECK_GT(manager_->live_object_count_, 0);
+ manager_->live_object_count_--;
+
+ // This must be the last statement in case manager deletes
+ // the object immediately.
+ manager_->Release(static_cast<T*>(this));
+ }
+ }
+
+ protected:
+ ~RefCountedManaged() {}
+
+ private:
+ RefCountedManager<T>* manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(RefCountedManaged<T>);
+};
+
+} // namespace cc
+
+#endif // CC_BASE_REF_COUNTED_MANAGED_H_
diff --git a/cc/base/region.cc b/cc/base/region.cc
new file mode 100644
index 0000000..d752b20
--- /dev/null
+++ b/cc/base/region.cc
@@ -0,0 +1,153 @@
+// Copyright (c) 2012 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/region.h"
+
+#include "base/debug/trace_event_argument.h"
+#include "base/values.h"
+#include "cc/base/simple_enclosed_region.h"
+
+namespace cc {
+
+Region::Region() {
+}
+
+Region::Region(const Region& region)
+ : skregion_(region.skregion_) {
+}
+
+Region::Region(const gfx::Rect& rect)
+ : skregion_(gfx::RectToSkIRect(rect)) {
+}
+
+Region::~Region() {
+}
+
+const Region& Region::operator=(const gfx::Rect& rect) {
+ skregion_ = SkRegion(gfx::RectToSkIRect(rect));
+ return *this;
+}
+
+const Region& Region::operator=(const Region& region) {
+ skregion_ = region.skregion_;
+ return *this;
+}
+
+void Region::Swap(Region* region) {
+ region->skregion_.swap(skregion_);
+}
+
+void Region::Clear() {
+ skregion_.setEmpty();
+}
+
+bool Region::IsEmpty() const {
+ return skregion_.isEmpty();
+}
+
+int Region::GetRegionComplexity() const {
+ return skregion_.computeRegionComplexity();
+}
+
+bool Region::Contains(const gfx::Point& point) const {
+ return skregion_.contains(point.x(), point.y());
+}
+
+bool Region::Contains(const gfx::Rect& rect) const {
+ if (rect.IsEmpty())
+ return true;
+ return skregion_.contains(gfx::RectToSkIRect(rect));
+}
+
+bool Region::Contains(const Region& region) const {
+ if (region.IsEmpty())
+ return true;
+ return skregion_.contains(region.skregion_);
+}
+
+bool Region::Intersects(const gfx::Rect& rect) const {
+ return skregion_.intersects(gfx::RectToSkIRect(rect));
+}
+
+bool Region::Intersects(const Region& region) const {
+ return skregion_.intersects(region.skregion_);
+}
+
+void Region::Subtract(const gfx::Rect& rect) {
+ skregion_.op(gfx::RectToSkIRect(rect), SkRegion::kDifference_Op);
+}
+
+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);
+}
+
+void Region::Union(const Region& region) {
+ skregion_.op(region.skregion_, SkRegion::kUnion_Op);
+}
+
+void Region::Intersect(const gfx::Rect& rect) {
+ skregion_.op(gfx::RectToSkIRect(rect), SkRegion::kIntersect_Op);
+}
+
+void Region::Intersect(const Region& region) {
+ skregion_.op(region.skregion_, SkRegion::kIntersect_Op);
+}
+
+std::string Region::ToString() const {
+ if (IsEmpty())
+ return gfx::Rect().ToString();
+
+ std::string result;
+ for (Iterator it(*this); it.has_rect(); it.next()) {
+ if (!result.empty())
+ result += " | ";
+ result += it.rect().ToString();
+ }
+ return result;
+}
+
+scoped_ptr<base::Value> Region::AsValue() const {
+ scoped_ptr<base::ListValue> result(new base::ListValue());
+ for (Iterator it(*this); it.has_rect(); it.next()) {
+ gfx::Rect rect(it.rect());
+ result->AppendInteger(rect.x());
+ result->AppendInteger(rect.y());
+ result->AppendInteger(rect.width());
+ result->AppendInteger(rect.height());
+ }
+ return result.Pass();
+}
+
+void Region::AsValueInto(base::debug::TracedValue* result) const {
+ for (Iterator it(*this); it.has_rect(); it.next()) {
+ gfx::Rect rect(it.rect());
+ result->AppendInteger(rect.x());
+ result->AppendInteger(rect.y());
+ result->AppendInteger(rect.width());
+ result->AppendInteger(rect.height());
+ }
+}
+
+Region::Iterator::Iterator() {
+}
+
+Region::Iterator::Iterator(const Region& region)
+ : it_(region.skregion_) {
+}
+
+Region::Iterator::~Iterator() {
+}
+
+} // namespace cc
diff --git a/cc/base/region.h b/cc/base/region.h
new file mode 100644
index 0000000..d78d4cb
--- /dev/null
+++ b/cc/base/region.h
@@ -0,0 +1,140 @@
+// Copyright (c) 2012 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_REGION_H_
+#define CC_BASE_REGION_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "cc/base/cc_export.h"
+#include "third_party/skia/include/core/SkRegion.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/skia_util.h"
+
+namespace base {
+class Value;
+namespace debug {
+class TracedValue;
+}
+}
+
+namespace cc {
+class SimpleEnclosedRegion;
+
+class CC_EXPORT Region {
+ public:
+ Region();
+ Region(const Region& region);
+ Region(const gfx::Rect& rect); // NOLINT(runtime/explicit)
+ ~Region();
+
+ const Region& operator=(const gfx::Rect& rect);
+ const Region& operator=(const Region& region);
+
+ void Swap(Region* region);
+ void Clear();
+ bool IsEmpty() const;
+ int GetRegionComplexity() const;
+
+ bool Contains(const gfx::Point& point) const;
+ bool Contains(const gfx::Rect& rect) const;
+ bool Contains(const Region& region) const;
+
+ bool Intersects(const gfx::Rect& rect) const;
+ bool Intersects(const Region& region) const;
+
+ 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);
+ void Intersect(const Region& region);
+
+ bool Equals(const Region& other) const {
+ return skregion_ == other.skregion_;
+ }
+
+ gfx::Rect bounds() const {
+ return gfx::SkIRectToRect(skregion_.getBounds());
+ }
+
+ std::string ToString() const;
+ scoped_ptr<base::Value> AsValue() const;
+ void AsValueInto(base::debug::TracedValue* array) const;
+
+ class CC_EXPORT Iterator {
+ public:
+ Iterator();
+ explicit Iterator(const Region& region);
+ ~Iterator();
+
+ gfx::Rect rect() const {
+ return gfx::SkIRectToRect(it_.rect());
+ }
+
+ void next() {
+ it_.next();
+ }
+
+ bool has_rect() const {
+ return !it_.done();
+ }
+
+ private:
+ SkRegion::Iterator it_;
+ };
+
+ private:
+ SkRegion skregion_;
+};
+
+inline bool operator==(const Region& a, const Region& b) {
+ return a.Equals(b);
+}
+
+inline bool operator!=(const Region& a, const Region& b) {
+ return !(a == b);
+}
+
+inline Region SubtractRegions(const Region& a, const Region& b) {
+ Region result = a;
+ result.Subtract(b);
+ return result;
+}
+
+inline Region SubtractRegions(const Region& a, const gfx::Rect& b) {
+ Region result = a;
+ result.Subtract(b);
+ return result;
+}
+
+inline Region IntersectRegions(const Region& a, const Region& b) {
+ Region result = a;
+ result.Intersect(b);
+ return result;
+}
+
+inline Region IntersectRegions(const Region& a, const gfx::Rect& b) {
+ Region result = a;
+ result.Intersect(b);
+ return result;
+}
+
+inline Region UnionRegions(const Region& a, const Region& b) {
+ Region result = a;
+ result.Union(b);
+ return result;
+}
+
+inline Region UnionRegions(const Region& a, const gfx::Rect& b) {
+ Region result = a;
+ result.Union(b);
+ return result;
+}
+
+} // namespace cc
+
+#endif // CC_BASE_REGION_H_
diff --git a/cc/base/region_unittest.cc b/cc/base/region_unittest.cc
new file mode 100644
index 0000000..c9a218d
--- /dev/null
+++ b/cc/base/region_unittest.cc
@@ -0,0 +1,454 @@
+// Copyright 2012 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/region.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+namespace {
+
+#define TEST_INSIDE_RECT(r, x, y, w, h) \
+ EXPECT_TRUE(r.Contains(gfx::Point(x, y))); \
+ EXPECT_TRUE(r.Contains(gfx::Point(x + w - 1, y))); \
+ EXPECT_TRUE(r.Contains(gfx::Point(x, y + h - 1))); \
+ EXPECT_TRUE(r.Contains(gfx::Point(x + w - 1, y + h - 1))); \
+ EXPECT_TRUE(r.Contains(gfx::Point(x, y + h / 2))); \
+ EXPECT_TRUE(r.Contains(gfx::Point(x + w - 1, y + h / 2))); \
+ EXPECT_TRUE(r.Contains(gfx::Point(x + w / 2, y))); \
+ EXPECT_TRUE(r.Contains(gfx::Point(x + w / 2, y + h - 1))); \
+ EXPECT_TRUE(r.Contains(gfx::Point(x + w / 2, y + h / 2))); \
+
+#define TEST_LEFT_OF_RECT(r, x, y, w, h) \
+ EXPECT_FALSE(r.Contains(gfx::Point(x - 1, y))); \
+ EXPECT_FALSE(r.Contains(gfx::Point(x - 1, y + h - 1))); \
+
+#define TEST_RIGHT_OF_RECT(r, x, y, w, h) \
+ EXPECT_FALSE(r.Contains(gfx::Point(x + w, y))); \
+ EXPECT_FALSE(r.Contains(gfx::Point(x + w, y + h - 1))); \
+
+#define TEST_TOP_OF_RECT(r, x, y, w, h) \
+ EXPECT_FALSE(r.Contains(gfx::Point(x, y - 1))); \
+ EXPECT_FALSE(r.Contains(gfx::Point(x + w - 1, y - 1))); \
+
+#define TEST_BOTTOM_OF_RECT(r, x, y, w, h) \
+ EXPECT_FALSE(r.Contains(gfx::Point(x, y + h))); \
+ EXPECT_FALSE(r.Contains(gfx::Point(x + w - 1, y + h))); \
+
+TEST(RegionTest, ContainsPoint) {
+ Region r;
+
+ EXPECT_FALSE(r.Contains(gfx::Point(0, 0)));
+
+ r.Union(gfx::Rect(35, 35, 1, 1));
+ TEST_INSIDE_RECT(r, 35, 35, 1, 1);
+ TEST_LEFT_OF_RECT(r, 35, 35, 1, 1);
+ TEST_RIGHT_OF_RECT(r, 35, 35, 1, 1);
+ TEST_TOP_OF_RECT(r, 35, 35, 1, 1);
+ TEST_BOTTOM_OF_RECT(r, 35, 35, 1, 1);
+
+ r.Union(gfx::Rect(30, 30, 10, 10));
+ TEST_INSIDE_RECT(r, 30, 30, 10, 10);
+ TEST_LEFT_OF_RECT(r, 30, 30, 10, 10);
+ TEST_RIGHT_OF_RECT(r, 30, 30, 10, 10);
+ TEST_TOP_OF_RECT(r, 30, 30, 10, 10);
+ TEST_BOTTOM_OF_RECT(r, 30, 30, 10, 10);
+
+ r.Union(gfx::Rect(31, 40, 10, 10));
+ EXPECT_FALSE(r.Contains(gfx::Point(30, 40)));
+ EXPECT_TRUE(r.Contains(gfx::Point(31, 40)));
+ EXPECT_FALSE(r.Contains(gfx::Point(40, 39)));
+ EXPECT_TRUE(r.Contains(gfx::Point(40, 40)));
+
+ TEST_INSIDE_RECT(r, 30, 30, 10, 10);
+ TEST_LEFT_OF_RECT(r, 30, 30, 10, 10);
+ TEST_RIGHT_OF_RECT(r, 30, 30, 10, 10);
+ TEST_TOP_OF_RECT(r, 30, 30, 10, 10);
+ TEST_INSIDE_RECT(r, 31, 40, 10, 10);
+ TEST_LEFT_OF_RECT(r, 31, 40, 10, 10);
+ TEST_RIGHT_OF_RECT(r, 31, 40, 10, 10);
+ TEST_BOTTOM_OF_RECT(r, 31, 40, 10, 10);
+
+ r.Union(gfx::Rect(42, 40, 10, 10));
+
+ TEST_INSIDE_RECT(r, 42, 40, 10, 10);
+ TEST_LEFT_OF_RECT(r, 42, 40, 10, 10);
+ TEST_RIGHT_OF_RECT(r, 42, 40, 10, 10);
+ TEST_TOP_OF_RECT(r, 42, 40, 10, 10);
+ TEST_BOTTOM_OF_RECT(r, 42, 40, 10, 10);
+
+ TEST_INSIDE_RECT(r, 30, 30, 10, 10);
+ TEST_LEFT_OF_RECT(r, 30, 30, 10, 10);
+ TEST_RIGHT_OF_RECT(r, 30, 30, 10, 10);
+ TEST_TOP_OF_RECT(r, 30, 30, 10, 10);
+ TEST_INSIDE_RECT(r, 31, 40, 10, 10);
+ TEST_LEFT_OF_RECT(r, 31, 40, 10, 10);
+ TEST_RIGHT_OF_RECT(r, 31, 40, 10, 10);
+ TEST_BOTTOM_OF_RECT(r, 31, 40, 10, 10);
+}
+
+TEST(RegionTest, EmptySpan) {
+ Region r;
+ r.Union(gfx::Rect(5, 0, 10, 10));
+ r.Union(gfx::Rect(0, 5, 10, 10));
+ r.Subtract(gfx::Rect(7, 7, 10, 0));
+
+ for (Region::Iterator it(r); it.has_rect(); it.next())
+ EXPECT_FALSE(it.rect().IsEmpty());
+}
+
+#define TEST_NO_INTERSECT(a, b) { \
+ Region ar = a; \
+ Region br = b; \
+ EXPECT_FALSE(ar.Intersects(br)); \
+ EXPECT_FALSE(br.Intersects(ar)); \
+ EXPECT_FALSE(ar.Intersects(b)); \
+ EXPECT_FALSE(br.Intersects(a)); \
+}
+
+#define TEST_INTERSECT(a, b) { \
+ Region ar = a; \
+ Region br = b; \
+ EXPECT_TRUE(ar.Intersects(br)); \
+ EXPECT_TRUE(br.Intersects(ar)); \
+ EXPECT_TRUE(ar.Intersects(b)); \
+ EXPECT_TRUE(br.Intersects(a)); \
+}
+
+TEST(RegionTest, IntersectsRegion) {
+ Region r;
+
+ TEST_NO_INTERSECT(gfx::Rect(), gfx::Rect());
+ TEST_NO_INTERSECT(gfx::Rect(), gfx::Rect(0, 0, 1, 1));
+ TEST_NO_INTERSECT(gfx::Rect(), gfx::Rect(1, 1, 1, 1));
+
+ TEST_NO_INTERSECT(gfx::Rect(-1, -1, 2, 2), gfx::Rect());
+
+ r.Union(gfx::Rect(0, 0, 1, 1));
+ TEST_NO_INTERSECT(r, gfx::Rect());
+ TEST_INTERSECT(r, gfx::Rect(0, 0, 1, 1));
+ TEST_INTERSECT(r, gfx::Rect(0, 0, 2, 2));
+ TEST_INTERSECT(r, gfx::Rect(-1, 0, 2, 2));
+ TEST_INTERSECT(r, gfx::Rect(-1, -1, 2, 2));
+ TEST_INTERSECT(r, gfx::Rect(0, -1, 2, 2));
+ TEST_INTERSECT(r, gfx::Rect(-1, -1, 3, 3));
+
+ r.Union(gfx::Rect(0, 0, 3, 3));
+ r.Union(gfx::Rect(10, 0, 3, 3));
+ r.Union(gfx::Rect(0, 10, 13, 3));
+ TEST_NO_INTERSECT(r, gfx::Rect());
+ TEST_INTERSECT(r, gfx::Rect(1, 1, 1, 1));
+ TEST_INTERSECT(r, gfx::Rect(0, 0, 2, 2));
+ TEST_INTERSECT(r, gfx::Rect(1, 0, 2, 2));
+ TEST_INTERSECT(r, gfx::Rect(1, 1, 2, 2));
+ TEST_INTERSECT(r, gfx::Rect(0, 1, 2, 2));
+ TEST_INTERSECT(r, gfx::Rect(0, 0, 3, 3));
+ TEST_INTERSECT(r, gfx::Rect(-1, -1, 2, 2));
+ TEST_INTERSECT(r, gfx::Rect(2, -1, 2, 2));
+ TEST_INTERSECT(r, gfx::Rect(2, 2, 2, 2));
+ TEST_INTERSECT(r, gfx::Rect(-1, 2, 2, 2));
+
+ TEST_INTERSECT(r, gfx::Rect(11, 1, 1, 1));
+ TEST_INTERSECT(r, gfx::Rect(10, 0, 2, 2));
+ TEST_INTERSECT(r, gfx::Rect(11, 0, 2, 2));
+ TEST_INTERSECT(r, gfx::Rect(11, 1, 2, 2));
+ TEST_INTERSECT(r, gfx::Rect(10, 1, 2, 2));
+ TEST_INTERSECT(r, gfx::Rect(10, 0, 3, 3));
+ TEST_INTERSECT(r, gfx::Rect(9, -1, 2, 2));
+ TEST_INTERSECT(r, gfx::Rect(12, -1, 2, 2));
+ TEST_INTERSECT(r, gfx::Rect(12, 2, 2, 2));
+ TEST_INTERSECT(r, gfx::Rect(9, 2, 2, 2));
+
+ TEST_INTERSECT(r, gfx::Rect(0, -1, 13, 5));
+ TEST_INTERSECT(r, gfx::Rect(1, -1, 11, 5));
+ TEST_INTERSECT(r, gfx::Rect(2, -1, 9, 5));
+ TEST_INTERSECT(r, gfx::Rect(2, -1, 8, 5));
+ TEST_INTERSECT(r, gfx::Rect(3, -1, 8, 5));
+ TEST_NO_INTERSECT(r, gfx::Rect(3, -1, 7, 5));
+
+ TEST_INTERSECT(r, gfx::Rect(0, 1, 13, 1));
+ TEST_INTERSECT(r, gfx::Rect(1, 1, 11, 1));
+ TEST_INTERSECT(r, gfx::Rect(2, 1, 9, 1));
+ TEST_INTERSECT(r, gfx::Rect(2, 1, 8, 1));
+ TEST_INTERSECT(r, gfx::Rect(3, 1, 8, 1));
+ TEST_NO_INTERSECT(r, gfx::Rect(3, 1, 7, 1));
+
+ TEST_INTERSECT(r, gfx::Rect(0, 0, 13, 13));
+ TEST_INTERSECT(r, gfx::Rect(0, 1, 13, 11));
+ TEST_INTERSECT(r, gfx::Rect(0, 2, 13, 9));
+ TEST_INTERSECT(r, gfx::Rect(0, 2, 13, 8));
+ TEST_INTERSECT(r, gfx::Rect(0, 3, 13, 8));
+ TEST_NO_INTERSECT(r, gfx::Rect(0, 3, 13, 7));
+}
+
+TEST(RegionTest, ReadPastFullSpanVectorInIntersectsTest) {
+ Region r;
+
+ // This region has enough spans to fill its allocated Vector exactly.
+ r.Union(gfx::Rect(400, 300, 1, 800));
+ r.Union(gfx::Rect(785, 585, 1, 1));
+ r.Union(gfx::Rect(787, 585, 1, 1));
+ r.Union(gfx::Rect(0, 587, 16, 162));
+ r.Union(gfx::Rect(26, 590, 300, 150));
+ r.Union(gfx::Rect(196, 750, 1, 1));
+ r.Union(gfx::Rect(0, 766, 1, 1));
+ r.Union(gfx::Rect(0, 782, 1, 1));
+ r.Union(gfx::Rect(745, 798, 1, 1));
+ r.Union(gfx::Rect(795, 882, 10, 585));
+ r.Union(gfx::Rect(100, 1499, 586, 1));
+ r.Union(gfx::Rect(100, 1500, 585, 784));
+ // This query rect goes past the bottom of the Region, causing the
+ // test to reach the last span and try go past it. It should not read
+ // memory off the end of the span Vector.
+ TEST_NO_INTERSECT(r, gfx::Rect(0, 2184, 1, 150));
+}
+
+#define TEST_NO_CONTAINS(a, b) \
+ { \
+ Region ar = a; \
+ Region br = b; \
+ EXPECT_FALSE(ar.Contains(br)); \
+ EXPECT_FALSE(ar.Contains(b)); \
+ }
+
+#define TEST_CONTAINS(a, b) \
+ { \
+ Region ar = a; \
+ Region br = b; \
+ EXPECT_TRUE(ar.Contains(br)); \
+ EXPECT_TRUE(ar.Contains(b)); \
+ }
+
+TEST(RegionTest, ContainsRegion) {
+ TEST_CONTAINS(gfx::Rect(), gfx::Rect());
+ TEST_CONTAINS(gfx::Rect(0, 0, 1, 1), gfx::Rect());
+ TEST_CONTAINS(gfx::Rect(10, 10, 1, 1), gfx::Rect());
+
+ TEST_NO_CONTAINS(gfx::Rect(), gfx::Rect(0, 0, 1, 1));
+ TEST_NO_CONTAINS(gfx::Rect(), gfx::Rect(1, 1, 1, 1));
+
+ TEST_NO_CONTAINS(gfx::Rect(10, 10, 1, 1), gfx::Rect(11, 10, 1, 1));
+ TEST_NO_CONTAINS(gfx::Rect(10, 10, 1, 1), gfx::Rect(10, 11, 1, 1));
+ TEST_NO_CONTAINS(gfx::Rect(10, 10, 1, 1), gfx::Rect(9, 10, 1, 1));
+ TEST_NO_CONTAINS(gfx::Rect(10, 10, 1, 1), gfx::Rect(10, 9, 1, 1));
+ TEST_NO_CONTAINS(gfx::Rect(10, 10, 1, 1), gfx::Rect(9, 9, 2, 2));
+ TEST_NO_CONTAINS(gfx::Rect(10, 10, 1, 1), gfx::Rect(10, 9, 2, 2));
+ TEST_NO_CONTAINS(gfx::Rect(10, 10, 1, 1), gfx::Rect(9, 10, 2, 2));
+ TEST_NO_CONTAINS(gfx::Rect(10, 10, 1, 1), gfx::Rect(10, 10, 2, 2));
+ TEST_NO_CONTAINS(gfx::Rect(10, 10, 1, 1), gfx::Rect(9, 9, 3, 3));
+
+ Region h_lines;
+ for (int i = 10; i < 20; i += 2)
+ h_lines.Union(gfx::Rect(i, 10, 1, 10));
+
+ TEST_CONTAINS(gfx::Rect(10, 10, 9, 10), h_lines);
+ TEST_NO_CONTAINS(gfx::Rect(10, 10, 9, 9), h_lines);
+ TEST_NO_CONTAINS(gfx::Rect(10, 11, 9, 9), h_lines);
+ TEST_NO_CONTAINS(gfx::Rect(10, 10, 8, 10), h_lines);
+ TEST_NO_CONTAINS(gfx::Rect(11, 10, 8, 10), h_lines);
+
+ Region v_lines;
+ for (int i = 10; i < 20; i += 2)
+ v_lines.Union(gfx::Rect(10, i, 10, 1));
+
+ TEST_CONTAINS(gfx::Rect(10, 10, 10, 9), v_lines);
+ TEST_NO_CONTAINS(gfx::Rect(10, 10, 9, 9), v_lines);
+ TEST_NO_CONTAINS(gfx::Rect(11, 10, 9, 9), v_lines);
+ TEST_NO_CONTAINS(gfx::Rect(10, 10, 10, 8), v_lines);
+ TEST_NO_CONTAINS(gfx::Rect(10, 11, 10, 8), v_lines);
+
+ Region grid;
+ for (int i = 10; i < 20; i += 2)
+ for (int j = 10; j < 20; j += 2)
+ grid.Union(gfx::Rect(i, j, 1, 1));
+
+ TEST_CONTAINS(gfx::Rect(10, 10, 9, 9), grid);
+ TEST_NO_CONTAINS(gfx::Rect(10, 10, 9, 8), grid);
+ TEST_NO_CONTAINS(gfx::Rect(10, 11, 9, 8), grid);
+ TEST_NO_CONTAINS(gfx::Rect(10, 10, 8, 9), grid);
+ TEST_NO_CONTAINS(gfx::Rect(11, 10, 8, 9), grid);
+
+ TEST_CONTAINS(h_lines, h_lines);
+ TEST_CONTAINS(v_lines, v_lines);
+ TEST_NO_CONTAINS(v_lines, h_lines);
+ TEST_NO_CONTAINS(h_lines, v_lines);
+ TEST_CONTAINS(grid, grid);
+ TEST_CONTAINS(h_lines, grid);
+ TEST_CONTAINS(v_lines, grid);
+ TEST_NO_CONTAINS(grid, h_lines);
+ TEST_NO_CONTAINS(grid, v_lines);
+
+ for (int i = 10; i < 20; i += 2)
+ TEST_CONTAINS(h_lines, gfx::Rect(i, 10, 1, 10));
+
+ for (int i = 10; i < 20; i += 2)
+ TEST_CONTAINS(v_lines, gfx::Rect(10, i, 10, 1));
+
+ for (int i = 10; i < 20; i += 2)
+ for (int j = 10; j < 20; j += 2)
+ TEST_CONTAINS(grid, gfx::Rect(i, j, 1, 1));
+
+ Region container;
+ container.Union(gfx::Rect(0, 0, 40, 20));
+ container.Union(gfx::Rect(0, 20, 41, 20));
+ TEST_CONTAINS(container, gfx::Rect(5, 5, 30, 30));
+
+ container.Clear();
+ container.Union(gfx::Rect(0, 0, 10, 10));
+ container.Union(gfx::Rect(0, 30, 10, 10));
+ container.Union(gfx::Rect(30, 30, 10, 10));
+ container.Union(gfx::Rect(30, 0, 10, 10));
+ TEST_NO_CONTAINS(container, gfx::Rect(5, 5, 30, 30));
+
+ container.Clear();
+ container.Union(gfx::Rect(0, 0, 10, 10));
+ container.Union(gfx::Rect(0, 30, 10, 10));
+ container.Union(gfx::Rect(30, 0, 10, 40));
+ TEST_NO_CONTAINS(container, gfx::Rect(5, 5, 30, 30));
+
+ container.Clear();
+ container.Union(gfx::Rect(30, 0, 10, 10));
+ container.Union(gfx::Rect(30, 30, 10, 10));
+ container.Union(gfx::Rect(0, 0, 10, 40));
+ TEST_NO_CONTAINS(container, gfx::Rect(5, 5, 30, 30));
+
+ container.Clear();
+ container.Union(gfx::Rect(0, 0, 10, 40));
+ container.Union(gfx::Rect(30, 0, 10, 40));
+ TEST_NO_CONTAINS(container, gfx::Rect(5, 5, 30, 30));
+
+ container.Clear();
+ container.Union(gfx::Rect(0, 0, 40, 40));
+ TEST_NO_CONTAINS(container, gfx::Rect(10, -1, 20, 10));
+
+ container.Clear();
+ container.Union(gfx::Rect(0, 0, 40, 40));
+ TEST_NO_CONTAINS(container, gfx::Rect(10, 31, 20, 10));
+
+ container.Clear();
+ container.Union(gfx::Rect(0, 0, 40, 20));
+ container.Union(gfx::Rect(0, 20, 41, 20));
+ TEST_NO_CONTAINS(container, gfx::Rect(-1, 10, 10, 20));
+
+ container.Clear();
+ container.Union(gfx::Rect(0, 0, 40, 20));
+ container.Union(gfx::Rect(0, 20, 41, 20));
+ TEST_NO_CONTAINS(container, gfx::Rect(31, 10, 10, 20));
+
+ container.Clear();
+ container.Union(gfx::Rect(0, 0, 40, 40));
+ container.Subtract(gfx::Rect(0, 20, 60, 0));
+ TEST_NO_CONTAINS(container, gfx::Rect(31, 10, 10, 20));
+
+ container.Clear();
+ container.Union(gfx::Rect(0, 0, 60, 20));
+ container.Union(gfx::Rect(30, 20, 10, 20));
+ TEST_NO_CONTAINS(container, gfx::Rect(0, 0, 10, 39));
+ TEST_NO_CONTAINS(container, gfx::Rect(0, 0, 10, 40));
+ TEST_NO_CONTAINS(container, gfx::Rect(0, 0, 10, 41));
+ TEST_NO_CONTAINS(container, gfx::Rect(29, 0, 10, 39));
+ TEST_CONTAINS(container, gfx::Rect(30, 0, 10, 40));
+ TEST_NO_CONTAINS(container, gfx::Rect(31, 0, 10, 41));
+ TEST_NO_CONTAINS(container, gfx::Rect(49, 0, 10, 39));
+ TEST_NO_CONTAINS(container, gfx::Rect(50, 0, 10, 40));
+ TEST_NO_CONTAINS(container, gfx::Rect(51, 0, 10, 41));
+
+ container.Clear();
+ container.Union(gfx::Rect(30, 0, 10, 20));
+ container.Union(gfx::Rect(0, 20, 60, 20));
+ TEST_NO_CONTAINS(container, gfx::Rect(0, 0, 10, 39));
+ TEST_NO_CONTAINS(container, gfx::Rect(0, 0, 10, 40));
+ TEST_NO_CONTAINS(container, gfx::Rect(0, 0, 10, 41));
+ TEST_NO_CONTAINS(container, gfx::Rect(29, 0, 10, 39));
+ TEST_CONTAINS(container, gfx::Rect(30, 0, 10, 40));
+ TEST_NO_CONTAINS(container, gfx::Rect(31, 0, 10, 41));
+ TEST_NO_CONTAINS(container, gfx::Rect(49, 0, 10, 39));
+ TEST_NO_CONTAINS(container, gfx::Rect(50, 0, 10, 40));
+ TEST_NO_CONTAINS(container, gfx::Rect(51, 0, 10, 41));
+}
+
+TEST(RegionTest, Union) {
+ Region r;
+ Region r2;
+
+ // A rect uniting a contained rect does not change the region.
+ r2 = r = gfx::Rect(0, 0, 50, 50);
+ r2.Union(gfx::Rect(20, 20, 10, 10));
+ EXPECT_EQ(r, r2);
+
+ // A rect uniting a containing rect gives back the containing rect.
+ r = gfx::Rect(0, 0, 50, 50);
+ r.Union(gfx::Rect(0, 0, 100, 100));
+ EXPECT_EQ(Region(gfx::Rect(0, 0, 100, 100)), r);
+
+ // A complex region uniting a contained rect does not change the region.
+ r = gfx::Rect(0, 0, 50, 50);
+ r.Union(gfx::Rect(100, 0, 50, 50));
+ r2 = r;
+ r2.Union(gfx::Rect(20, 20, 10, 10));
+ EXPECT_EQ(r, r2);
+
+ // A complex region uniting a containing rect gives back the containing rect.
+ r = gfx::Rect(0, 0, 50, 50);
+ r.Union(gfx::Rect(100, 0, 50, 50));
+ r.Union(gfx::Rect(0, 0, 500, 500));
+ EXPECT_EQ(Region(gfx::Rect(0, 0, 500, 500)), r);
+}
+
+TEST(RegionTest, IsEmpty) {
+ EXPECT_TRUE(Region().IsEmpty());
+ EXPECT_TRUE(Region(gfx::Rect()).IsEmpty());
+ EXPECT_TRUE(Region(Region()).IsEmpty());
+ EXPECT_TRUE(Region(gfx::Rect(10, 10, 10, 0)).IsEmpty());
+ EXPECT_TRUE(Region(gfx::Rect(10, 10, 0, 10)).IsEmpty());
+ EXPECT_TRUE(Region(gfx::Rect(-10, 10, 10, 0)).IsEmpty());
+ EXPECT_TRUE(Region(gfx::Rect(-10, 10, 0, 10)).IsEmpty());
+ EXPECT_FALSE(Region(gfx::Rect(-1, -1, 1, 1)).IsEmpty());
+ EXPECT_FALSE(Region(gfx::Rect(0, 0, 1, 1)).IsEmpty());
+ EXPECT_FALSE(Region(gfx::Rect(0, 0, 2, 2)).IsEmpty());
+
+ EXPECT_TRUE(SkIRect::MakeXYWH(10, 10, 10, 0).isEmpty());
+ EXPECT_TRUE(SkIRect::MakeXYWH(10, 10, 0, 10).isEmpty());
+ EXPECT_TRUE(SkIRect::MakeXYWH(-10, 10, 10, 0).isEmpty());
+ EXPECT_TRUE(SkIRect::MakeXYWH(-10, 10, 0, 10).isEmpty());
+ EXPECT_FALSE(SkIRect::MakeXYWH(-1, -1, 1, 1).isEmpty());
+ EXPECT_FALSE(SkIRect::MakeXYWH(0, 0, 1, 1).isEmpty());
+ EXPECT_FALSE(SkIRect::MakeXYWH(0, 0, 2, 2).isEmpty());
+}
+
+TEST(RegionTest, Clear) {
+ Region r;
+
+ r = gfx::Rect(0, 0, 50, 50);
+ EXPECT_FALSE(r.IsEmpty());
+ r.Clear();
+ EXPECT_TRUE(r.IsEmpty());
+
+ r = gfx::Rect(0, 0, 50, 50);
+ r.Union(gfx::Rect(100, 0, 50, 50));
+ r.Union(gfx::Rect(0, 0, 500, 500));
+ EXPECT_FALSE(r.IsEmpty());
+ r.Clear();
+ EXPECT_TRUE(r.IsEmpty());
+}
+
+TEST(RegionSwap, Swap) {
+ Region r1, r2, r3;
+
+ r1 = gfx::Rect(0, 0, 50, 50);
+ r1.Swap(&r2);
+ EXPECT_TRUE(r1.IsEmpty());
+ EXPECT_EQ(r2.ToString(), Region(gfx::Rect(0, 0, 50, 50)).ToString());
+
+ r1 = gfx::Rect(0, 0, 50, 50);
+ r1.Union(gfx::Rect(100, 0, 50, 50));
+ r1.Union(gfx::Rect(0, 0, 500, 500));
+ r3 = r1;
+ r1.Swap(&r2);
+ EXPECT_EQ(r1.ToString(), Region(gfx::Rect(0, 0, 50, 50)).ToString());
+ EXPECT_EQ(r2.ToString(), r3.ToString());
+}
+
+} // namespace
+} // namespace cc
diff --git a/cc/base/rolling_time_delta_history.cc b/cc/base/rolling_time_delta_history.cc
new file mode 100644
index 0000000..0f95cc5
--- /dev/null
+++ b/cc/base/rolling_time_delta_history.cc
@@ -0,0 +1,67 @@
+// 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 <cmath>
+
+#include "cc/base/rolling_time_delta_history.h"
+
+namespace cc {
+
+RollingTimeDeltaHistory::RollingTimeDeltaHistory(size_t max_size)
+ : max_size_(max_size) {}
+
+RollingTimeDeltaHistory::~RollingTimeDeltaHistory() {}
+
+void RollingTimeDeltaHistory::InsertSample(base::TimeDelta time) {
+ if (max_size_ == 0)
+ return;
+
+ if (sample_set_.size() == max_size_) {
+ sample_set_.erase(chronological_sample_deque_.front());
+ chronological_sample_deque_.pop_front();
+ }
+
+ TimeDeltaMultiset::iterator it = sample_set_.insert(time);
+ chronological_sample_deque_.push_back(it);
+}
+
+size_t RollingTimeDeltaHistory::SampleCount() {
+ return sample_set_.size();
+}
+
+void RollingTimeDeltaHistory::Clear() {
+ chronological_sample_deque_.clear();
+ sample_set_.clear();
+}
+
+base::TimeDelta RollingTimeDeltaHistory::Percentile(double percent) const {
+ if (sample_set_.size() == 0)
+ return base::TimeDelta();
+
+ double fraction = percent / 100.0;
+
+ if (fraction <= 0.0)
+ return *(sample_set_.begin());
+
+ if (fraction >= 1.0)
+ return *(sample_set_.rbegin());
+
+ size_t num_smaller_samples =
+ static_cast<size_t>(std::ceil(fraction * sample_set_.size())) - 1;
+
+ if (num_smaller_samples > sample_set_.size() / 2) {
+ size_t num_larger_samples = sample_set_.size() - num_smaller_samples - 1;
+ TimeDeltaMultiset::const_reverse_iterator it = sample_set_.rbegin();
+ for (size_t i = 0; i < num_larger_samples; i++)
+ it++;
+ return *it;
+ }
+
+ TimeDeltaMultiset::const_iterator it = sample_set_.begin();
+ for (size_t i = 0; i < num_smaller_samples; i++)
+ it++;
+ return *it;
+}
+
+} // namespace cc
diff --git a/cc/base/rolling_time_delta_history.h b/cc/base/rolling_time_delta_history.h
new file mode 100644
index 0000000..603c813
--- /dev/null
+++ b/cc/base/rolling_time_delta_history.h
@@ -0,0 +1,46 @@
+// 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_ROLLING_TIME_DELTA_HISTORY_H_
+#define CC_BASE_ROLLING_TIME_DELTA_HISTORY_H_
+
+#include <deque>
+#include <set>
+
+#include "base/time/time.h"
+#include "cc/base/cc_export.h"
+
+namespace cc {
+
+// Stores a limited number of samples. When the maximum size is reached, each
+// insertion results in the deletion of the oldest remaining sample.
+class CC_EXPORT RollingTimeDeltaHistory {
+ public:
+ explicit RollingTimeDeltaHistory(size_t max_size);
+
+ ~RollingTimeDeltaHistory();
+
+ void InsertSample(base::TimeDelta time);
+
+ size_t SampleCount();
+
+ void Clear();
+
+ // Returns the smallest sample that is greater than or equal to the specified
+ // percent of samples. If there aren't any samples, returns base::TimeDelta().
+ base::TimeDelta Percentile(double percent) const;
+
+ private:
+ typedef std::multiset<base::TimeDelta> TimeDeltaMultiset;
+
+ TimeDeltaMultiset sample_set_;
+ std::deque<TimeDeltaMultiset::iterator> chronological_sample_deque_;
+ size_t max_size_;
+
+ DISALLOW_COPY_AND_ASSIGN(RollingTimeDeltaHistory);
+};
+
+} // namespace cc
+
+#endif // CC_BASE_ROLLING_TIME_DELTA_HISTORY_H_
diff --git a/cc/base/rolling_time_delta_history_unittest.cc b/cc/base/rolling_time_delta_history_unittest.cc
new file mode 100644
index 0000000..5458240
--- /dev/null
+++ b/cc/base/rolling_time_delta_history_unittest.cc
@@ -0,0 +1,109 @@
+// 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/rolling_time_delta_history.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+namespace {
+
+TEST(RollingTimeDeltaHistoryTest, EmptyHistory) {
+ RollingTimeDeltaHistory empty_history(0);
+
+ EXPECT_EQ(base::TimeDelta(), empty_history.Percentile(0.0));
+ EXPECT_EQ(base::TimeDelta(), empty_history.Percentile(50.0));
+ EXPECT_EQ(base::TimeDelta(), empty_history.Percentile(100.0));
+
+ empty_history.InsertSample(base::TimeDelta::FromMilliseconds(10));
+ empty_history.InsertSample(base::TimeDelta::FromMilliseconds(15));
+ empty_history.InsertSample(base::TimeDelta::FromMilliseconds(20));
+
+ EXPECT_EQ(base::TimeDelta(), empty_history.Percentile(0.0));
+ EXPECT_EQ(base::TimeDelta(), empty_history.Percentile(50.0));
+ EXPECT_EQ(base::TimeDelta(), empty_history.Percentile(100.0));
+
+ empty_history.Clear();
+ EXPECT_EQ(base::TimeDelta(), empty_history.Percentile(0.0));
+ EXPECT_EQ(base::TimeDelta(), empty_history.Percentile(50.0));
+ EXPECT_EQ(base::TimeDelta(), empty_history.Percentile(100.0));
+}
+
+TEST(RollingTimeDeltaHistoryTest, SizeOneHistory) {
+ RollingTimeDeltaHistory size_one_history(1);
+ base::TimeDelta sample1 = base::TimeDelta::FromMilliseconds(10);
+ base::TimeDelta sample2 = base::TimeDelta::FromMilliseconds(20);
+
+ EXPECT_EQ(base::TimeDelta(), size_one_history.Percentile(0.0));
+ EXPECT_EQ(base::TimeDelta(), size_one_history.Percentile(50.0));
+ EXPECT_EQ(base::TimeDelta(), size_one_history.Percentile(100.0));
+
+ size_one_history.InsertSample(sample1);
+ EXPECT_EQ(sample1, size_one_history.Percentile(0.0));
+ EXPECT_EQ(sample1, size_one_history.Percentile(50.0));
+ EXPECT_EQ(sample1, size_one_history.Percentile(100.0));
+
+ size_one_history.InsertSample(sample2);
+ EXPECT_EQ(sample2, size_one_history.Percentile(0.0));
+ EXPECT_EQ(sample2, size_one_history.Percentile(50.0));
+ EXPECT_EQ(sample2, size_one_history.Percentile(100.0));
+
+ size_one_history.Clear();
+ EXPECT_EQ(base::TimeDelta(), size_one_history.Percentile(0.0));
+ EXPECT_EQ(base::TimeDelta(), size_one_history.Percentile(50.0));
+ EXPECT_EQ(base::TimeDelta(), size_one_history.Percentile(100.0));
+}
+
+TEST(RollingTimeDeltaHistoryTest, LargeHistory) {
+ RollingTimeDeltaHistory large_history(100);
+ base::TimeDelta sample1 = base::TimeDelta::FromMilliseconds(150);
+ base::TimeDelta sample2 = base::TimeDelta::FromMilliseconds(250);
+ base::TimeDelta sample3 = base::TimeDelta::FromMilliseconds(200);
+
+ large_history.InsertSample(sample1);
+ large_history.InsertSample(sample2);
+
+ EXPECT_EQ(sample1, large_history.Percentile(0.0));
+ EXPECT_EQ(sample1, large_history.Percentile(25.0));
+ EXPECT_EQ(sample2, large_history.Percentile(75.0));
+ EXPECT_EQ(sample2, large_history.Percentile(100.0));
+
+ large_history.InsertSample(sample3);
+ EXPECT_EQ(sample1, large_history.Percentile(0.0));
+ EXPECT_EQ(sample1, large_history.Percentile(25.0));
+ EXPECT_EQ(sample3, large_history.Percentile(50.0));
+ EXPECT_EQ(sample2, large_history.Percentile(100.0));
+
+ // Fill the history.
+ for (int i = 1; i <= 97; i++)
+ large_history.InsertSample(base::TimeDelta::FromMilliseconds(i));
+
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(1),
+ large_history.Percentile(0.0));
+ for (int i = 1; i <= 97; i++) {
+ EXPECT_EQ(base::TimeDelta::FromMilliseconds(i),
+ large_history.Percentile(i - 0.5));
+ }
+ EXPECT_EQ(sample1, large_history.Percentile(97.5));
+ EXPECT_EQ(sample3, large_history.Percentile(98.5));
+ EXPECT_EQ(sample2, large_history.Percentile(99.5));
+
+ // Continue inserting samples, causing the oldest samples to be discarded.
+ base::TimeDelta sample4 = base::TimeDelta::FromMilliseconds(100);
+ base::TimeDelta sample5 = base::TimeDelta::FromMilliseconds(102);
+ base::TimeDelta sample6 = base::TimeDelta::FromMilliseconds(104);
+ large_history.InsertSample(sample4);
+ large_history.InsertSample(sample5);
+ large_history.InsertSample(sample6);
+ EXPECT_EQ(sample4, large_history.Percentile(97.5));
+ EXPECT_EQ(sample5, large_history.Percentile(98.5));
+ EXPECT_EQ(sample6, large_history.Percentile(99.5));
+
+ large_history.Clear();
+ EXPECT_EQ(base::TimeDelta(), large_history.Percentile(0.0));
+ EXPECT_EQ(base::TimeDelta(), large_history.Percentile(50.0));
+ EXPECT_EQ(base::TimeDelta(), large_history.Percentile(100.0));
+}
+
+} // namespace
+} // namespace cc
diff --git a/cc/base/scoped_ptr_algorithm.h b/cc/base/scoped_ptr_algorithm.h
new file mode 100644
index 0000000..79f4eee
--- /dev/null
+++ b/cc/base/scoped_ptr_algorithm.h
@@ -0,0 +1,30 @@
+// Copyright 2012 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_SCOPED_PTR_ALGORITHM_H_
+#define CC_BASE_SCOPED_PTR_ALGORITHM_H_
+
+namespace cc {
+
+// ScopedContainers need to implement a swap() method since they do not allow
+// assignment to their iterators.
+template <class ForwardIterator, class Predicate, class ScopedContainer>
+ForwardIterator remove_if(
+ ScopedContainer* container,
+ ForwardIterator first,
+ ForwardIterator last,
+ Predicate predicate) {
+ ForwardIterator result = first;
+ for (; first != last; ++first) {
+ if (!predicate(*first)) {
+ container->swap(first, result);
+ ++result;
+ }
+ }
+ return result;
+}
+
+} // namespace cc
+
+#endif // CC_BASE_SCOPED_PTR_ALGORITHM_H_
diff --git a/cc/base/scoped_ptr_deque.h b/cc/base/scoped_ptr_deque.h
new file mode 100644
index 0000000..cb4adfc
--- /dev/null
+++ b/cc/base/scoped_ptr_deque.h
@@ -0,0 +1,137 @@
+// Copyright 2012 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_SCOPED_PTR_DEQUE_H_
+#define CC_BASE_SCOPED_PTR_DEQUE_H_
+
+#include <algorithm>
+#include <deque>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/stl_util.h"
+
+namespace cc {
+
+// This type acts like a deque<scoped_ptr> based on top of std::deque. The
+// ScopedPtrDeque has ownership of all elements in the deque.
+template <typename T>
+class ScopedPtrDeque {
+ public:
+ typedef typename std::deque<T*>::const_iterator const_iterator;
+ typedef typename std::deque<T*>::reverse_iterator reverse_iterator;
+ typedef typename std::deque<T*>::const_reverse_iterator
+ const_reverse_iterator;
+
+#if defined(OS_ANDROID)
+ // On Android the iterator is not a class, so we can't block assignment.
+ typedef typename std::deque<T*>::iterator iterator;
+#else
+ // Ban setting values on the iterator directly. New pointers must be passed
+ // to methods on the ScopedPtrDeque class to appear in the deque.
+ class iterator : public std::deque<T*>::iterator {
+ public:
+ explicit iterator(const typename std::deque<T*>::iterator& other)
+ : std::deque<T*>::iterator(other) {}
+ T* const& operator*() { return std::deque<T*>::iterator::operator*(); }
+ };
+#endif
+
+ ScopedPtrDeque() {}
+
+ ~ScopedPtrDeque() { clear(); }
+
+ size_t size() const {
+ return data_.size();
+ }
+
+ T* at(size_t index) const {
+ DCHECK(index < size());
+ return data_[index];
+ }
+
+ T* operator[](size_t index) const {
+ return at(index);
+ }
+
+ T* front() const {
+ DCHECK(!empty());
+ return at(0);
+ }
+
+ T* back() const {
+ DCHECK(!empty());
+ return at(size() - 1);
+ }
+
+ bool empty() const {
+ return data_.empty();
+ }
+
+ scoped_ptr<T> take_front() {
+ scoped_ptr<T> ret(front());
+ data_.pop_front();
+ return ret.Pass();
+ }
+
+ scoped_ptr<T> take_back() {
+ scoped_ptr<T> ret(back());
+ data_.pop_back();
+ return ret.Pass();
+ }
+
+ void clear() {
+ STLDeleteElements(&data_);
+ }
+
+ void push_front(scoped_ptr<T> item) {
+ data_.push_front(item.release());
+ }
+
+ void push_back(scoped_ptr<T> item) {
+ data_.push_back(item.release());
+ }
+
+ void insert(iterator position, scoped_ptr<T> item) {
+ DCHECK(position <= end());
+ data_.insert(position, item.release());
+ }
+
+ scoped_ptr<T> take(iterator position) {
+ DCHECK(position < end());
+ scoped_ptr<T> ret(*position);
+ data_.erase(position);
+ return ret.Pass();
+ }
+
+ void swap(iterator a, iterator b) {
+ DCHECK(a < end());
+ DCHECK(b < end());
+ if (a == end() || b == end() || a == b)
+ return;
+ typename std::deque<T*>::iterator writable_a = a;
+ typename std::deque<T*>::iterator writable_b = b;
+ std::swap(*writable_a, *writable_b);
+ }
+
+ iterator begin() { return static_cast<iterator>(data_.begin()); }
+ const_iterator begin() const { return data_.begin(); }
+ iterator end() { return static_cast<iterator>(data_.end()); }
+ const_iterator end() const { return data_.end(); }
+
+ reverse_iterator rbegin() { return data_.rbegin(); }
+ const_reverse_iterator rbegin() const { return data_.rbegin(); }
+ reverse_iterator rend() { return data_.rend(); }
+ const_reverse_iterator rend() const { return data_.rend(); }
+
+ private:
+ std::deque<T*> data_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedPtrDeque);
+};
+
+} // namespace cc
+
+#endif // CC_BASE_SCOPED_PTR_DEQUE_H_
diff --git a/cc/base/scoped_ptr_vector.h b/cc/base/scoped_ptr_vector.h
new file mode 100644
index 0000000..0b83e35
--- /dev/null
+++ b/cc/base/scoped_ptr_vector.h
@@ -0,0 +1,200 @@
+// Copyright 2012 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_SCOPED_PTR_VECTOR_H_
+#define CC_BASE_SCOPED_PTR_VECTOR_H_
+
+#include <algorithm>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/stl_util.h"
+
+namespace cc {
+
+// This type acts like a vector<scoped_ptr> based on top of std::vector. The
+// ScopedPtrVector has ownership of all elements in the vector.
+template <typename T>
+class ScopedPtrVector {
+ public:
+ typedef typename std::vector<T*>::const_iterator const_iterator;
+ typedef typename std::vector<T*>::reverse_iterator reverse_iterator;
+ typedef typename std::vector<T*>::const_reverse_iterator
+ const_reverse_iterator;
+
+#if defined(OS_ANDROID)
+ // On Android the iterator is not a class, so we can't block assignment.
+ typedef typename std::vector<T*>::iterator iterator;
+#else
+ // Ban setting values on the iterator directly. New pointers must be passed
+ // to methods on the ScopedPtrVector class to appear in the vector.
+ class iterator : public std::vector<T*>::iterator {
+ public:
+ iterator(const typename std::vector<T*>::iterator& other) // NOLINT
+ : std::vector<T*>::iterator(other) {}
+ T* const& operator*() { return std::vector<T*>::iterator::operator*(); }
+ };
+#endif
+
+ ScopedPtrVector() {}
+
+ ~ScopedPtrVector() { clear(); }
+
+ size_t size() const {
+ return data_.size();
+ }
+
+ T* at(size_t index) const {
+ DCHECK(index < size());
+ return data_[index];
+ }
+
+ T* operator[](size_t index) const {
+ return at(index);
+ }
+
+ T* front() const {
+ DCHECK(!empty());
+ return at(0);
+ }
+
+ T* back() const {
+ DCHECK(!empty());
+ return at(size() - 1);
+ }
+
+ bool empty() const {
+ return data_.empty();
+ }
+
+ scoped_ptr<T> take(iterator position) {
+ if (position == end())
+ return nullptr;
+ DCHECK(position < end());
+
+ typename std::vector<T*>::iterator writable_position = position;
+ scoped_ptr<T> ret(*writable_position);
+ *writable_position = NULL;
+ return ret.Pass();
+ }
+
+ scoped_ptr<T> take_back() {
+ DCHECK(!empty());
+ if (empty())
+ return nullptr;
+ return take(end() - 1);
+ }
+
+ void erase(iterator position) {
+ if (position == end())
+ return;
+ typename std::vector<T*>::iterator writable_position = position;
+ delete *writable_position;
+ data_.erase(position);
+ }
+
+ void erase(iterator first, iterator last) {
+ DCHECK(first <= last);
+ for (iterator it = first; it != last; ++it) {
+ DCHECK(it < end());
+
+ typename std::vector<T*>::iterator writable_it = it;
+ delete *writable_it;
+ }
+ data_.erase(first, last);
+ }
+
+ void reserve(size_t size) {
+ data_.reserve(size);
+ }
+
+ void clear() {
+ STLDeleteElements(&data_);
+ }
+
+ void push_back(scoped_ptr<T> item) {
+ data_.push_back(item.release());
+ }
+
+ void pop_back() {
+ delete data_.back();
+ data_.pop_back();
+ }
+
+ void insert(iterator position, scoped_ptr<T> item) {
+ DCHECK(position <= end());
+ data_.insert(position, item.release());
+ }
+
+ void insert_and_take(iterator position, ScopedPtrVector<T>* other) {
+ std::vector<T*> tmp_data;
+ for (ScopedPtrVector<T>::iterator it = other->begin(); it != other->end();
+ ++it) {
+ tmp_data.push_back(other->take(it).release());
+ }
+ data_.insert(position, tmp_data.begin(), tmp_data.end());
+ }
+
+ template <typename Predicate>
+ iterator partition(Predicate predicate) {
+ typename std::vector<T*>::iterator first = begin();
+ typename std::vector<T*>::iterator last = end();
+ return static_cast<iterator>(std::partition(first, last, predicate));
+ }
+
+ void swap(ScopedPtrVector<T>& other) {
+ data_.swap(other.data_);
+ }
+
+ void swap(iterator a, iterator b) {
+ DCHECK(a < end());
+ DCHECK(b < end());
+ if (a == end() || b == end() || a == b)
+ return;
+ typename std::vector<T*>::iterator writable_a = a;
+ typename std::vector<T*>::iterator writable_b = b;
+ std::swap(*writable_a, *writable_b);
+ }
+
+ template<class Compare>
+ inline void sort(Compare comp) {
+ std::sort(data_.begin(), data_.end(), comp);
+ }
+
+ template <class Compare>
+ inline void make_heap(Compare comp) {
+ std::make_heap(data_.begin(), data_.end(), comp);
+ }
+
+ template <class Compare>
+ inline void push_heap(Compare comp) {
+ std::push_heap(data_.begin(), data_.end(), comp);
+ }
+
+ template <class Compare>
+ inline void pop_heap(Compare comp) {
+ std::pop_heap(data_.begin(), data_.end(), comp);
+ }
+
+ iterator begin() { return static_cast<iterator>(data_.begin()); }
+ const_iterator begin() const { return data_.begin(); }
+ iterator end() { return static_cast<iterator>(data_.end()); }
+ const_iterator end() const { return data_.end(); }
+
+ reverse_iterator rbegin() { return data_.rbegin(); }
+ const_reverse_iterator rbegin() const { return data_.rbegin(); }
+ reverse_iterator rend() { return data_.rend(); }
+ const_reverse_iterator rend() const { return data_.rend(); }
+
+ private:
+ std::vector<T*> data_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedPtrVector);
+};
+
+} // namespace cc
+
+#endif // CC_BASE_SCOPED_PTR_VECTOR_H_
diff --git a/cc/base/scoped_ptr_vector_unittest.cc b/cc/base/scoped_ptr_vector_unittest.cc
new file mode 100644
index 0000000..73a7f81
--- /dev/null
+++ b/cc/base/scoped_ptr_vector_unittest.cc
@@ -0,0 +1,105 @@
+// Copyright 2012 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 <set>
+
+#include "cc/base/scoped_ptr_vector.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace cc {
+namespace {
+
+class Data {
+ public:
+ static scoped_ptr<Data> Create(int i) { return make_scoped_ptr(new Data(i)); }
+ int data() const { return data_; }
+ private:
+ explicit Data(int i) : data_(i) {}
+ int data_;
+};
+
+class IsOddPredicate {
+ public:
+ bool operator()(const Data* data) { return (data->data() % 2) == 1; }
+};
+
+TEST(ScopedPtrVectorTest, PushBack) {
+ ScopedPtrVector<Data> v;
+
+ // Insert 5 things into the vector.
+ v.push_back(Data::Create(1));
+ v.push_back(Data::Create(2));
+ v.push_back(Data::Create(3));
+ v.push_back(Data::Create(4));
+ v.push_back(Data::Create(5));
+
+ EXPECT_EQ(5u, v.size());
+ EXPECT_EQ(1, v[0]->data());
+ EXPECT_EQ(2, v[1]->data());
+ EXPECT_EQ(3, v[2]->data());
+ EXPECT_EQ(4, v[3]->data());
+ EXPECT_EQ(5, v[4]->data());
+}
+
+TEST(ScopedPtrVectorTest, InsertAndTake) {
+ // Insert 3 things into each vector.
+ ScopedPtrVector<Data> v;
+ v.push_back(Data::Create(1));
+ v.push_back(Data::Create(2));
+ v.push_back(Data::Create(6));
+
+ ScopedPtrVector<Data> v2;
+ v2.push_back(Data::Create(3));
+ v2.push_back(Data::Create(4));
+ v2.push_back(Data::Create(5));
+
+ ScopedPtrVector<Data>::iterator it = v.begin();
+ ++it;
+ ++it;
+ EXPECT_EQ(6, (*it)->data());
+
+ v.insert_and_take(it, &v2);
+
+ EXPECT_EQ(6u, v.size());
+ EXPECT_EQ(1, v[0]->data());
+ EXPECT_EQ(2, v[1]->data());
+ EXPECT_EQ(3, v[2]->data());
+ EXPECT_EQ(4, v[3]->data());
+ EXPECT_EQ(5, v[4]->data());
+ EXPECT_EQ(6, v[5]->data());
+
+ EXPECT_EQ(3u, v2.size());
+ EXPECT_EQ(NULL, v2[0]);
+ EXPECT_EQ(NULL, v2[1]);
+ EXPECT_EQ(NULL, v2[2]);
+}
+
+TEST(ScopedPtrVectorTest, Partition) {
+ ScopedPtrVector<Data> v;
+ v.push_back(Data::Create(1));
+ v.push_back(Data::Create(2));
+ v.push_back(Data::Create(3));
+ v.push_back(Data::Create(4));
+ v.push_back(Data::Create(5));
+
+ ScopedPtrVector<Data>::iterator it = v.partition(IsOddPredicate());
+ std::set<int> odd_numbers;
+ for (ScopedPtrVector<Data>::iterator second_it = v.begin();
+ second_it != it;
+ ++second_it) {
+ EXPECT_EQ(1, (*second_it)->data() % 2);
+ odd_numbers.insert((*second_it)->data());
+ }
+ EXPECT_EQ(3u, odd_numbers.size());
+
+ std::set<int> even_numbers;
+ for (; it != v.end(); ++it) {
+ EXPECT_EQ(0, (*it)->data() % 2);
+ even_numbers.insert((*it)->data());
+ }
+ EXPECT_EQ(2u, even_numbers.size());
+}
+
+} // namespace
+} // namespace cc
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
diff --git a/cc/base/swap_promise.h b/cc/base/swap_promise.h
new file mode 100644
index 0000000..a406fda
--- /dev/null
+++ b/cc/base/swap_promise.h
@@ -0,0 +1,53 @@
+// Copyright 2013 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_SWAP_PROMISE_H_
+#define CC_BASE_SWAP_PROMISE_H_
+
+#include "cc/output/compositor_frame_metadata.h"
+
+namespace cc {
+
+// When a change to the compositor's state/invalidation/whatever happens, a
+// Swap Promise can be inserted into LayerTreeHost/LayerTreeImpl, to track
+// whether the compositor's reply to the new state/invaliadtion/whatever is
+// completed in the compositor, i.e. the compositor knows it has been sent
+// to its output or not.
+//
+// If the new compositor state is sent to the output, SwapPromise::DidSwap()
+// will be called, and if the compositor fails to send its new state to the
+// output, SwapPromise::DidNotSwap() will be called.
+//
+// Client wishes to use SwapPromise should have a subclass that defines
+// the behavior of DidSwap() and DidNotSwap(). Notice that the promise can
+// be broken at either main or impl thread, e.g. commit fails on main thread,
+// new frame data has no actual damage so LayerTreeHostImpl::SwapBuffers()
+// bails out early on impl thread, so don't assume that DidSwap() and
+// DidNotSwap() are called at a particular thread. It is better to let the
+// subclass carry thread-safe member data and operate on that member data in
+// DidSwap() and DidNotSwap().
+class CC_EXPORT SwapPromise {
+ public:
+ enum DidNotSwapReason {
+ DID_NOT_SWAP_UNKNOWN,
+ SWAP_FAILS,
+ COMMIT_FAILS,
+ COMMIT_NO_UPDATE,
+ };
+
+ SwapPromise() {}
+ virtual ~SwapPromise() {}
+
+ virtual void DidSwap(CompositorFrameMetadata* metadata) = 0;
+ virtual void DidNotSwap(DidNotSwapReason reason) = 0;
+
+ // A non-zero trace id identifies a trace flow object that is embedded in the
+ // swap promise. This can be used for registering additional flow steps to
+ // visualize the object's path through the system.
+ virtual int64 TraceId() const = 0;
+};
+
+} // namespace cc
+
+#endif // CC_BASE_SWAP_PROMISE_H_
diff --git a/cc/base/swap_promise_monitor.cc b/cc/base/swap_promise_monitor.cc
new file mode 100644
index 0000000..0c04f35
--- /dev/null
+++ b/cc/base/swap_promise_monitor.cc
@@ -0,0 +1,31 @@
+// Copyright 2013 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 "base/logging.h"
+#include "cc/base/swap_promise_monitor.h"
+#include "cc/trees/layer_tree_host.h"
+#include "cc/trees/layer_tree_host_impl.h"
+
+namespace cc {
+
+SwapPromiseMonitor::SwapPromiseMonitor(LayerTreeHost* layer_tree_host,
+ LayerTreeHostImpl* layer_tree_host_impl)
+ : layer_tree_host_(layer_tree_host),
+ layer_tree_host_impl_(layer_tree_host_impl) {
+ DCHECK((layer_tree_host && !layer_tree_host_impl) ||
+ (!layer_tree_host && layer_tree_host_impl));
+ if (layer_tree_host_)
+ layer_tree_host_->InsertSwapPromiseMonitor(this);
+ if (layer_tree_host_impl_)
+ layer_tree_host_impl_->InsertSwapPromiseMonitor(this);
+}
+
+SwapPromiseMonitor::~SwapPromiseMonitor() {
+ if (layer_tree_host_)
+ layer_tree_host_->RemoveSwapPromiseMonitor(this);
+ if (layer_tree_host_impl_)
+ layer_tree_host_impl_->RemoveSwapPromiseMonitor(this);
+}
+
+} // namespace cc
diff --git a/cc/base/swap_promise_monitor.h b/cc/base/swap_promise_monitor.h
new file mode 100644
index 0000000..b8c8cd0
--- /dev/null
+++ b/cc/base/swap_promise_monitor.h
@@ -0,0 +1,45 @@
+// Copyright 2013 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_SWAP_PROMISE_MONITOR_H_
+#define CC_BASE_SWAP_PROMISE_MONITOR_H_
+
+#include "cc/base/cc_export.h"
+
+namespace cc {
+
+class LayerTreeHost;
+class LayerTreeHostImpl;
+
+// A SwapPromiseMonitor is used to monitor compositor state change that
+// should be associated with a SwapPromise, e.g. SetNeedsCommit() is
+// called on main thread or SetNeedsRedraw() is called on impl thread.
+// Creating a SwapPromiseMonitor will insert itself into a LayerTreeHost
+// or LayerTreeHostImpl. You must provide a pointer to the appropriate
+// structure to the monitor (and only one of the two). Notification of
+// compositor state change will be sent through OnSetNeedsCommitOnMain()
+// or OnSetNeedsRedrawOnImpl(). When SwapPromiseMonitor is destroyed, it
+// will unregister itself from LayerTreeHost or LayerTreeHostImpl.
+class CC_EXPORT SwapPromiseMonitor {
+ public:
+ // If the monitor lives on the main thread, pass in layer_tree_host
+ // and set layer_tree_host_impl to NULL.
+ // If the monitor lives on the impl thread, pass in layer_tree_host_impl
+ // and set layer_tree_host to NULL.
+ SwapPromiseMonitor(LayerTreeHost* layer_tree_host,
+ LayerTreeHostImpl* layer_tree_host_impl);
+ virtual ~SwapPromiseMonitor();
+
+ virtual void OnSetNeedsCommitOnMain() = 0;
+ virtual void OnSetNeedsRedrawOnImpl() = 0;
+ virtual void OnForwardScrollUpdateToMainThreadOnImpl() = 0;
+
+ protected:
+ LayerTreeHost* layer_tree_host_;
+ LayerTreeHostImpl* layer_tree_host_impl_;
+};
+
+} // namespace cc
+
+#endif // CC_BASE_SWAP_PROMISE_MONITOR_H_
diff --git a/cc/base/switches.cc b/cc/base/switches.cc
new file mode 100644
index 0000000..5bfa6ae
--- /dev/null
+++ b/cc/base/switches.cc
@@ -0,0 +1,120 @@
+// Copyright (c) 2012 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/switches.h"
+
+#include "base/command_line.h"
+
+namespace cc {
+namespace switches {
+
+const char kDisableThreadedAnimation[] = "disable-threaded-animation";
+
+// Disables layer-edge anti-aliasing in the compositor.
+const char kDisableCompositedAntialiasing[] =
+ "disable-composited-antialiasing";
+
+// Disables sending the next BeginMainFrame before the previous commit
+// activates. Overrides the kEnableMainFrameBeforeActivation flag.
+const char kDisableMainFrameBeforeActivation[] =
+ "disable-main-frame-before-activation";
+
+// Enables sending the next BeginMainFrame before the previous commit activates.
+const char kEnableMainFrameBeforeActivation[] =
+ "enable-main-frame-before-activation";
+
+const char kEnableTopControlsPositionCalculation[] =
+ "enable-top-controls-position-calculation";
+
+// The height of the movable top controls.
+const char kTopControlsHeight[] = "top-controls-height";
+
+// Percentage of the top controls need to be hidden before they will auto hide.
+const char kTopControlsHideThreshold[] = "top-controls-hide-threshold";
+
+// Percentage of the top controls need to be shown before they will auto show.
+const char kTopControlsShowThreshold[] = "top-controls-show-threshold";
+
+// Re-rasters everything multiple times to simulate a much slower machine.
+// Give a scale factor to cause raster to take that many times longer to
+// complete, such as --slow-down-raster-scale-factor=25.
+const char kSlowDownRasterScaleFactor[] = "slow-down-raster-scale-factor";
+
+// Max tiles allowed for each tilings interest area.
+const char kMaxTilesForInterestArea[] = "max-tiles-for-interest-area";
+
+// The amount of unused resource memory compositor is allowed to keep around.
+const char kMaxUnusedResourceMemoryUsagePercentage[] =
+ "max-unused-resource-memory-usage-percentage";
+
+// Causes the compositor to render to textures which are then sent to the parent
+// through the texture mailbox mechanism.
+// Requires --enable-compositor-frame-message.
+const char kCompositeToMailbox[] = "composite-to-mailbox";
+
+// Check that property changes during paint do not occur.
+const char kStrictLayerPropertyChangeChecking[] =
+ "strict-layer-property-change-checking";
+
+// Virtual viewport for fixed-position elements, scrollbars during pinch.
+const char kEnablePinchVirtualViewport[] = "enable-pinch-virtual-viewport";
+const char kDisablePinchVirtualViewport[] = "disable-pinch-virtual-viewport";
+
+// Disable partial swap which is needed for some OpenGL drivers / emulators.
+const char kUIDisablePartialSwap[] = "ui-disable-partial-swap";
+
+// Enables the GPU benchmarking extension
+const char kEnableGpuBenchmarking[] = "enable-gpu-benchmarking";
+
+// Renders a border around compositor layers to help debug and study
+// layer compositing.
+const char kShowCompositedLayerBorders[] = "show-composited-layer-borders";
+const char kUIShowCompositedLayerBorders[] = "ui-show-layer-borders";
+
+// Draws a FPS indicator
+const char kShowFPSCounter[] = "show-fps-counter";
+const char kUIShowFPSCounter[] = "ui-show-fps-counter";
+
+// Renders a border that represents the bounding box for the layer's animation.
+const char kShowLayerAnimationBounds[] = "show-layer-animation-bounds";
+const char kUIShowLayerAnimationBounds[] = "ui-show-layer-animation-bounds";
+
+// Show rects in the HUD around layers whose properties have changed.
+const char kShowPropertyChangedRects[] = "show-property-changed-rects";
+const char kUIShowPropertyChangedRects[] = "ui-show-property-changed-rects";
+
+// Show rects in the HUD around damage as it is recorded into each render
+// surface.
+const char kShowSurfaceDamageRects[] = "show-surface-damage-rects";
+const char kUIShowSurfaceDamageRects[] = "ui-show-surface-damage-rects";
+
+// Show rects in the HUD around the screen-space transformed bounds of every
+// layer.
+const char kShowScreenSpaceRects[] = "show-screenspace-rects";
+const char kUIShowScreenSpaceRects[] = "ui-show-screenspace-rects";
+
+// Show rects in the HUD around the screen-space transformed bounds of every
+// layer's replica, when they have one.
+const char kShowReplicaScreenSpaceRects[] = "show-replica-screenspace-rects";
+const char kUIShowReplicaScreenSpaceRects[] =
+ "ui-show-replica-screenspace-rects";
+
+// Show rects in the HUD wherever something is known to be drawn opaque and is
+// considered occluding the pixels behind it.
+const char kShowOccludingRects[] = "show-occluding-rects";
+const char kUIShowOccludingRects[] = "ui-show-occluding-rects";
+
+// Show rects in the HUD wherever something is not known to be drawn opaque and
+// is not considered to be occluding the pixels behind it.
+const char kShowNonOccludingRects[] = "show-nonoccluding-rects";
+const char kUIShowNonOccludingRects[] = "ui-show-nonoccluding-rects";
+
+// Prevents the layer tree unit tests from timing out.
+const char kCCLayerTreeTestNoTimeout[] = "cc-layer-tree-test-no-timeout";
+
+// Makes pixel tests write their output instead of read it.
+const char kCCRebaselinePixeltests[] = "cc-rebaseline-pixeltests";
+
+} // namespace switches
+} // namespace cc
diff --git a/cc/base/switches.h b/cc/base/switches.h
new file mode 100644
index 0000000..bbf680e
--- /dev/null
+++ b/cc/base/switches.h
@@ -0,0 +1,67 @@
+// Copyright (c) 2012 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.
+
+// Defines all the "cc" command-line switches.
+
+#ifndef CC_BASE_SWITCHES_H_
+#define CC_BASE_SWITCHES_H_
+
+#include "cc/base/cc_export.h"
+
+// Since cc is used from the render process, anything that goes here also needs
+// to be added to render_process_host_impl.cc.
+
+namespace cc {
+namespace switches {
+
+// Switches for the renderer compositor only.
+CC_EXPORT extern const char kDisableThreadedAnimation[];
+CC_EXPORT extern const char kDisableCompositedAntialiasing[];
+CC_EXPORT extern const char kDisableMainFrameBeforeActivation[];
+CC_EXPORT extern const char kEnableMainFrameBeforeActivation[];
+CC_EXPORT extern const char kEnableTopControlsPositionCalculation[];
+CC_EXPORT extern const char kJankInsteadOfCheckerboard[];
+CC_EXPORT extern const char kTopControlsHeight[];
+CC_EXPORT extern const char kTopControlsHideThreshold[];
+CC_EXPORT extern const char kTopControlsShowThreshold[];
+CC_EXPORT extern const char kSlowDownRasterScaleFactor[];
+CC_EXPORT extern const char kCompositeToMailbox[];
+CC_EXPORT extern const char kMaxTilesForInterestArea[];
+CC_EXPORT extern const char kMaxUnusedResourceMemoryUsagePercentage[];
+CC_EXPORT extern const char kEnablePinchVirtualViewport[];
+CC_EXPORT extern const char kDisablePinchVirtualViewport[];
+CC_EXPORT extern const char kStrictLayerPropertyChangeChecking[];
+
+// Switches for both the renderer and ui compositors.
+CC_EXPORT extern const char kUIDisablePartialSwap[];
+CC_EXPORT extern const char kEnableGpuBenchmarking[];
+
+// Debug visualizations.
+CC_EXPORT extern const char kShowCompositedLayerBorders[];
+CC_EXPORT extern const char kUIShowCompositedLayerBorders[];
+CC_EXPORT extern const char kShowFPSCounter[];
+CC_EXPORT extern const char kUIShowFPSCounter[];
+CC_EXPORT extern const char kShowLayerAnimationBounds[];
+CC_EXPORT extern const char kUIShowLayerAnimationBounds[];
+CC_EXPORT extern const char kShowPropertyChangedRects[];
+CC_EXPORT extern const char kUIShowPropertyChangedRects[];
+CC_EXPORT extern const char kShowSurfaceDamageRects[];
+CC_EXPORT extern const char kUIShowSurfaceDamageRects[];
+CC_EXPORT extern const char kShowScreenSpaceRects[];
+CC_EXPORT extern const char kUIShowScreenSpaceRects[];
+CC_EXPORT extern const char kShowReplicaScreenSpaceRects[];
+CC_EXPORT extern const char kUIShowReplicaScreenSpaceRects[];
+CC_EXPORT extern const char kShowOccludingRects[];
+CC_EXPORT extern const char kUIShowOccludingRects[];
+CC_EXPORT extern const char kShowNonOccludingRects[];
+CC_EXPORT extern const char kUIShowNonOccludingRects[];
+
+// Unit test related.
+CC_EXPORT extern const char kCCLayerTreeTestNoTimeout[];
+CC_EXPORT extern const char kCCRebaselinePixeltests[];
+
+} // namespace switches
+} // namespace cc
+
+#endif // CC_BASE_SWITCHES_H_
diff --git a/cc/base/tiling_data.cc b/cc/base/tiling_data.cc
new file mode 100644
index 0000000..879d421
--- /dev/null
+++ b/cc/base/tiling_data.cc
@@ -0,0 +1,668 @@
+// Copyright 2010 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/tiling_data.h"
+
+#include <algorithm>
+
+#include "ui/gfx/rect.h"
+#include "ui/gfx/vector2d.h"
+
+namespace cc {
+
+static int ComputeNumTiles(int max_texture_size,
+ int total_size,
+ int border_texels) {
+ if (max_texture_size - 2 * border_texels <= 0)
+ return total_size > 0 && max_texture_size >= total_size ? 1 : 0;
+
+ int num_tiles = std::max(1,
+ 1 + (total_size - 1 - 2 * border_texels) /
+ (max_texture_size - 2 * border_texels));
+ return total_size > 0 ? num_tiles : 0;
+}
+
+TilingData::TilingData()
+ : border_texels_(0) {
+ RecomputeNumTiles();
+}
+
+TilingData::TilingData(const gfx::Size& max_texture_size,
+ const gfx::Size& tiling_size,
+ bool has_border_texels)
+ : max_texture_size_(max_texture_size),
+ tiling_size_(tiling_size),
+ border_texels_(has_border_texels ? 1 : 0) {
+ RecomputeNumTiles();
+}
+
+TilingData::TilingData(const gfx::Size& max_texture_size,
+ const gfx::Size& tiling_size,
+ int border_texels)
+ : max_texture_size_(max_texture_size),
+ tiling_size_(tiling_size),
+ border_texels_(border_texels) {
+ RecomputeNumTiles();
+}
+
+void TilingData::SetTilingSize(const gfx::Size& tiling_size) {
+ tiling_size_ = tiling_size;
+ RecomputeNumTiles();
+}
+
+void TilingData::SetMaxTextureSize(const gfx::Size& max_texture_size) {
+ max_texture_size_ = max_texture_size;
+ RecomputeNumTiles();
+}
+
+void TilingData::SetHasBorderTexels(bool has_border_texels) {
+ border_texels_ = has_border_texels ? 1 : 0;
+ RecomputeNumTiles();
+}
+
+void TilingData::SetBorderTexels(int border_texels) {
+ border_texels_ = border_texels;
+ RecomputeNumTiles();
+}
+
+int TilingData::TileXIndexFromSrcCoord(int src_position) const {
+ if (num_tiles_x_ <= 1)
+ return 0;
+
+ DCHECK_GT(max_texture_size_.width() - 2 * border_texels_, 0);
+ int x = (src_position - border_texels_) /
+ (max_texture_size_.width() - 2 * border_texels_);
+ return std::min(std::max(x, 0), num_tiles_x_ - 1);
+}
+
+int TilingData::TileYIndexFromSrcCoord(int src_position) const {
+ if (num_tiles_y_ <= 1)
+ return 0;
+
+ DCHECK_GT(max_texture_size_.height() - 2 * border_texels_, 0);
+ int y = (src_position - border_texels_) /
+ (max_texture_size_.height() - 2 * border_texels_);
+ return std::min(std::max(y, 0), num_tiles_y_ - 1);
+}
+
+int TilingData::FirstBorderTileXIndexFromSrcCoord(int src_position) const {
+ if (num_tiles_x_ <= 1)
+ return 0;
+
+ DCHECK_GT(max_texture_size_.width() - 2 * border_texels_, 0);
+ int inner_tile_size = max_texture_size_.width() - 2 * border_texels_;
+ int x = (src_position - 2 * border_texels_) / inner_tile_size;
+ return std::min(std::max(x, 0), num_tiles_x_ - 1);
+}
+
+int TilingData::FirstBorderTileYIndexFromSrcCoord(int src_position) const {
+ if (num_tiles_y_ <= 1)
+ return 0;
+
+ DCHECK_GT(max_texture_size_.height() - 2 * border_texels_, 0);
+ int inner_tile_size = max_texture_size_.height() - 2 * border_texels_;
+ int y = (src_position - 2 * border_texels_) / inner_tile_size;
+ return std::min(std::max(y, 0), num_tiles_y_ - 1);
+}
+
+int TilingData::LastBorderTileXIndexFromSrcCoord(int src_position) const {
+ if (num_tiles_x_ <= 1)
+ return 0;
+
+ DCHECK_GT(max_texture_size_.width() - 2 * border_texels_, 0);
+ int inner_tile_size = max_texture_size_.width() - 2 * border_texels_;
+ int x = src_position / inner_tile_size;
+ return std::min(std::max(x, 0), num_tiles_x_ - 1);
+}
+
+int TilingData::LastBorderTileYIndexFromSrcCoord(int src_position) const {
+ if (num_tiles_y_ <= 1)
+ return 0;
+
+ DCHECK_GT(max_texture_size_.height() - 2 * border_texels_, 0);
+ int inner_tile_size = max_texture_size_.height() - 2 * border_texels_;
+ int y = src_position / inner_tile_size;
+ return std::min(std::max(y, 0), num_tiles_y_ - 1);
+}
+
+gfx::Rect TilingData::ExpandRectIgnoringBordersToTileBounds(
+ const gfx::Rect& rect) const {
+ if (rect.IsEmpty() || has_empty_bounds())
+ return gfx::Rect();
+ if (rect.x() > tiling_size_.width() || rect.y() > tiling_size_.height())
+ return gfx::Rect();
+ int index_x = TileXIndexFromSrcCoord(rect.x());
+ int index_y = TileYIndexFromSrcCoord(rect.y());
+ int index_right = TileXIndexFromSrcCoord(rect.right() - 1);
+ int index_bottom = TileYIndexFromSrcCoord(rect.bottom() - 1);
+
+ gfx::Rect rect_top_left(TileBounds(index_x, index_y));
+ gfx::Rect rect_bottom_right(TileBounds(index_right, index_bottom));
+
+ return gfx::UnionRects(rect_top_left, rect_bottom_right);
+}
+
+gfx::Rect TilingData::ExpandRectToTileBounds(const gfx::Rect& rect) const {
+ if (rect.IsEmpty() || has_empty_bounds())
+ return gfx::Rect();
+ if (rect.x() > tiling_size_.width() || rect.y() > tiling_size_.height())
+ return gfx::Rect();
+ int index_x = FirstBorderTileXIndexFromSrcCoord(rect.x());
+ int index_y = FirstBorderTileYIndexFromSrcCoord(rect.y());
+ int index_right = LastBorderTileXIndexFromSrcCoord(rect.right() - 1);
+ int index_bottom = LastBorderTileYIndexFromSrcCoord(rect.bottom() - 1);
+
+ gfx::Rect rect_top_left(TileBounds(index_x, index_y));
+ gfx::Rect rect_bottom_right(TileBounds(index_right, index_bottom));
+
+ return gfx::UnionRects(rect_top_left, rect_bottom_right);
+}
+
+gfx::Rect TilingData::TileBounds(int i, int j) const {
+ AssertTile(i, j);
+ int max_texture_size_x = max_texture_size_.width() - 2 * border_texels_;
+ int max_texture_size_y = max_texture_size_.height() - 2 * border_texels_;
+
+ int lo_x = max_texture_size_x * i;
+ if (i != 0)
+ lo_x += border_texels_;
+
+ int lo_y = max_texture_size_y * j;
+ if (j != 0)
+ lo_y += border_texels_;
+
+ int hi_x = max_texture_size_x * (i + 1) + border_texels_;
+ if (i + 1 == num_tiles_x_)
+ hi_x += border_texels_;
+
+ int hi_y = max_texture_size_y * (j + 1) + border_texels_;
+ if (j + 1 == num_tiles_y_)
+ hi_y += border_texels_;
+
+ hi_x = std::min(hi_x, tiling_size_.width());
+ hi_y = std::min(hi_y, tiling_size_.height());
+
+ int x = lo_x;
+ int y = lo_y;
+ int width = hi_x - lo_x;
+ int height = hi_y - lo_y;
+ DCHECK_GE(x, 0);
+ DCHECK_GE(y, 0);
+ DCHECK_GE(width, 0);
+ DCHECK_GE(height, 0);
+ DCHECK_LE(x, tiling_size_.width());
+ DCHECK_LE(y, tiling_size_.height());
+ return gfx::Rect(x, y, width, height);
+}
+
+gfx::Rect TilingData::TileBoundsWithBorder(int i, int j) const {
+ AssertTile(i, j);
+ int max_texture_size_x = max_texture_size_.width() - 2 * border_texels_;
+ int max_texture_size_y = max_texture_size_.height() - 2 * border_texels_;
+
+ int lo_x = max_texture_size_x * i;
+ int lo_y = max_texture_size_y * j;
+
+ int hi_x = lo_x + max_texture_size_x + 2 * border_texels_;
+ int hi_y = lo_y + max_texture_size_y + 2 * border_texels_;
+
+ hi_x = std::min(hi_x, tiling_size_.width());
+ hi_y = std::min(hi_y, tiling_size_.height());
+
+ int x = lo_x;
+ int y = lo_y;
+ int width = hi_x - lo_x;
+ int height = hi_y - lo_y;
+ DCHECK_GE(x, 0);
+ DCHECK_GE(y, 0);
+ DCHECK_GE(width, 0);
+ DCHECK_GE(height, 0);
+ DCHECK_LE(x, tiling_size_.width());
+ DCHECK_LE(y, tiling_size_.height());
+ return gfx::Rect(x, y, width, height);
+}
+
+int TilingData::TilePositionX(int x_index) const {
+ DCHECK_GE(x_index, 0);
+ DCHECK_LT(x_index, num_tiles_x_);
+
+ int pos = (max_texture_size_.width() - 2 * border_texels_) * x_index;
+ if (x_index != 0)
+ pos += border_texels_;
+
+ return pos;
+}
+
+int TilingData::TilePositionY(int y_index) const {
+ DCHECK_GE(y_index, 0);
+ DCHECK_LT(y_index, num_tiles_y_);
+
+ int pos = (max_texture_size_.height() - 2 * border_texels_) * y_index;
+ if (y_index != 0)
+ pos += border_texels_;
+
+ return pos;
+}
+
+int TilingData::TileSizeX(int x_index) const {
+ DCHECK_GE(x_index, 0);
+ DCHECK_LT(x_index, num_tiles_x_);
+
+ if (!x_index && num_tiles_x_ == 1)
+ return tiling_size_.width();
+ if (!x_index && num_tiles_x_ > 1)
+ return max_texture_size_.width() - border_texels_;
+ if (x_index < num_tiles_x_ - 1)
+ return max_texture_size_.width() - 2 * border_texels_;
+ if (x_index == num_tiles_x_ - 1)
+ return tiling_size_.width() - TilePositionX(x_index);
+
+ NOTREACHED();
+ return 0;
+}
+
+int TilingData::TileSizeY(int y_index) const {
+ DCHECK_GE(y_index, 0);
+ DCHECK_LT(y_index, num_tiles_y_);
+
+ if (!y_index && num_tiles_y_ == 1)
+ return tiling_size_.height();
+ if (!y_index && num_tiles_y_ > 1)
+ return max_texture_size_.height() - border_texels_;
+ if (y_index < num_tiles_y_ - 1)
+ return max_texture_size_.height() - 2 * border_texels_;
+ if (y_index == num_tiles_y_ - 1)
+ return tiling_size_.height() - TilePositionY(y_index);
+
+ NOTREACHED();
+ return 0;
+}
+
+gfx::Vector2d TilingData::TextureOffset(int x_index, int y_index) const {
+ int left = (!x_index || num_tiles_x_ == 1) ? 0 : border_texels_;
+ int top = (!y_index || num_tiles_y_ == 1) ? 0 : border_texels_;
+
+ return gfx::Vector2d(left, top);
+}
+
+void TilingData::RecomputeNumTiles() {
+ num_tiles_x_ = ComputeNumTiles(
+ max_texture_size_.width(), tiling_size_.width(), border_texels_);
+ num_tiles_y_ = ComputeNumTiles(
+ max_texture_size_.height(), tiling_size_.height(), border_texels_);
+}
+
+TilingData::BaseIterator::BaseIterator(const TilingData* tiling_data)
+ : tiling_data_(tiling_data),
+ index_x_(-1),
+ index_y_(-1) {
+}
+
+TilingData::Iterator::Iterator() : BaseIterator(NULL) { done(); }
+
+TilingData::Iterator::Iterator(const TilingData* tiling_data,
+ const gfx::Rect& consider_rect,
+ bool include_borders)
+ : BaseIterator(tiling_data), left_(-1), right_(-1), bottom_(-1) {
+ if (tiling_data_->num_tiles_x() <= 0 || tiling_data_->num_tiles_y() <= 0) {
+ done();
+ return;
+ }
+
+ gfx::Rect tiling_bounds_rect(tiling_data_->tiling_size());
+ gfx::Rect rect(consider_rect);
+ rect.Intersect(tiling_bounds_rect);
+
+ gfx::Rect top_left_tile;
+ if (include_borders) {
+ index_x_ = tiling_data_->FirstBorderTileXIndexFromSrcCoord(rect.x());
+ index_y_ = tiling_data_->FirstBorderTileYIndexFromSrcCoord(rect.y());
+ right_ = tiling_data_->LastBorderTileXIndexFromSrcCoord(rect.right() - 1);
+ bottom_ = tiling_data_->LastBorderTileYIndexFromSrcCoord(rect.bottom() - 1);
+ top_left_tile = tiling_data_->TileBoundsWithBorder(index_x_, index_y_);
+ } else {
+ index_x_ = tiling_data_->TileXIndexFromSrcCoord(rect.x());
+ index_y_ = tiling_data_->TileYIndexFromSrcCoord(rect.y());
+ right_ = tiling_data_->TileXIndexFromSrcCoord(rect.right() - 1);
+ bottom_ = tiling_data_->TileYIndexFromSrcCoord(rect.bottom() - 1);
+ top_left_tile = tiling_data_->TileBounds(index_x_, index_y_);
+ }
+ left_ = index_x_;
+
+ // Index functions always return valid indices, so explicitly check
+ // for non-intersecting rects.
+ if (!top_left_tile.Intersects(rect))
+ done();
+}
+
+TilingData::Iterator& TilingData::Iterator::operator++() {
+ if (!*this)
+ return *this;
+
+ index_x_++;
+ if (index_x_ > right_) {
+ index_x_ = left_;
+ index_y_++;
+ if (index_y_ > bottom_)
+ done();
+ }
+
+ return *this;
+}
+
+TilingData::DifferenceIterator::DifferenceIterator(
+ const TilingData* tiling_data,
+ const gfx::Rect& consider_rect,
+ const gfx::Rect& ignore_rect)
+ : BaseIterator(tiling_data),
+ consider_left_(-1),
+ consider_top_(-1),
+ consider_right_(-1),
+ consider_bottom_(-1),
+ ignore_left_(-1),
+ ignore_top_(-1),
+ ignore_right_(-1),
+ ignore_bottom_(-1) {
+ if (tiling_data_->num_tiles_x() <= 0 || tiling_data_->num_tiles_y() <= 0) {
+ done();
+ return;
+ }
+
+ gfx::Rect tiling_bounds_rect(tiling_data_->tiling_size());
+ gfx::Rect consider(consider_rect);
+ gfx::Rect ignore(ignore_rect);
+ consider.Intersect(tiling_bounds_rect);
+ ignore.Intersect(tiling_bounds_rect);
+ if (consider.IsEmpty()) {
+ done();
+ return;
+ }
+
+ consider_left_ = tiling_data_->TileXIndexFromSrcCoord(consider.x());
+ consider_top_ = tiling_data_->TileYIndexFromSrcCoord(consider.y());
+ consider_right_ = tiling_data_->TileXIndexFromSrcCoord(consider.right() - 1);
+ consider_bottom_ =
+ tiling_data_->TileYIndexFromSrcCoord(consider.bottom() - 1);
+
+ if (!ignore.IsEmpty()) {
+ ignore_left_ = tiling_data_->TileXIndexFromSrcCoord(ignore.x());
+ ignore_top_ = tiling_data_->TileYIndexFromSrcCoord(ignore.y());
+ ignore_right_ = tiling_data_->TileXIndexFromSrcCoord(ignore.right() - 1);
+ ignore_bottom_ = tiling_data_->TileYIndexFromSrcCoord(ignore.bottom() - 1);
+
+ // Clamp ignore indices to consider indices.
+ ignore_left_ = std::max(ignore_left_, consider_left_);
+ ignore_top_ = std::max(ignore_top_, consider_top_);
+ ignore_right_ = std::min(ignore_right_, consider_right_);
+ ignore_bottom_ = std::min(ignore_bottom_, consider_bottom_);
+ }
+
+ if (ignore_left_ == consider_left_ && ignore_right_ == consider_right_ &&
+ ignore_top_ == consider_top_ && ignore_bottom_ == consider_bottom_) {
+ done();
+ return;
+ }
+
+ index_x_ = consider_left_;
+ index_y_ = consider_top_;
+
+ if (in_ignore_rect())
+ ++(*this);
+}
+
+TilingData::DifferenceIterator& TilingData::DifferenceIterator::operator++() {
+ if (!*this)
+ return *this;
+
+ index_x_++;
+ if (in_ignore_rect())
+ index_x_ = ignore_right_ + 1;
+
+ if (index_x_ > consider_right_) {
+ index_x_ = consider_left_;
+ index_y_++;
+
+ if (in_ignore_rect()) {
+ index_x_ = ignore_right_ + 1;
+ // If the ignore rect spans the whole consider rect horizontally, then
+ // ignore_right + 1 will be out of bounds.
+ if (in_ignore_rect() || index_x_ > consider_right_) {
+ index_y_ = ignore_bottom_ + 1;
+ index_x_ = consider_left_;
+ }
+ }
+
+ if (index_y_ > consider_bottom_)
+ done();
+ }
+
+ return *this;
+}
+
+TilingData::SpiralDifferenceIterator::SpiralDifferenceIterator()
+ : BaseIterator(NULL) {
+ done();
+}
+
+TilingData::SpiralDifferenceIterator::SpiralDifferenceIterator(
+ const TilingData* tiling_data,
+ const gfx::Rect& consider_rect,
+ const gfx::Rect& ignore_rect,
+ const gfx::Rect& center_rect)
+ : BaseIterator(tiling_data),
+ consider_left_(-1),
+ consider_top_(-1),
+ consider_right_(-1),
+ consider_bottom_(-1),
+ ignore_left_(-1),
+ ignore_top_(-1),
+ ignore_right_(-1),
+ ignore_bottom_(-1),
+ direction_(RIGHT),
+ delta_x_(1),
+ delta_y_(0),
+ current_step_(0),
+ horizontal_step_count_(0),
+ vertical_step_count_(0) {
+ if (tiling_data_->num_tiles_x() <= 0 || tiling_data_->num_tiles_y() <= 0) {
+ done();
+ return;
+ }
+
+ gfx::Rect tiling_bounds_rect(tiling_data_->tiling_size());
+ gfx::Rect consider(consider_rect);
+ gfx::Rect ignore(ignore_rect);
+ gfx::Rect center(center_rect);
+ consider.Intersect(tiling_bounds_rect);
+ ignore.Intersect(tiling_bounds_rect);
+ if (consider.IsEmpty()) {
+ done();
+ return;
+ }
+
+ consider_left_ = tiling_data_->TileXIndexFromSrcCoord(consider.x());
+ consider_top_ = tiling_data_->TileYIndexFromSrcCoord(consider.y());
+ consider_right_ = tiling_data_->TileXIndexFromSrcCoord(consider.right() - 1);
+ consider_bottom_ =
+ tiling_data_->TileYIndexFromSrcCoord(consider.bottom() - 1);
+
+ if (!ignore.IsEmpty()) {
+ ignore_left_ = tiling_data_->TileXIndexFromSrcCoord(ignore.x());
+ ignore_top_ = tiling_data_->TileYIndexFromSrcCoord(ignore.y());
+ ignore_right_ = tiling_data_->TileXIndexFromSrcCoord(ignore.right() - 1);
+ ignore_bottom_ = tiling_data_->TileYIndexFromSrcCoord(ignore.bottom() - 1);
+
+ // Clamp ignore indices to consider indices.
+ ignore_left_ = std::max(ignore_left_, consider_left_);
+ ignore_top_ = std::max(ignore_top_, consider_top_);
+ ignore_right_ = std::min(ignore_right_, consider_right_);
+ ignore_bottom_ = std::min(ignore_bottom_, consider_bottom_);
+ }
+
+ if (ignore_left_ == consider_left_ && ignore_right_ == consider_right_ &&
+ ignore_top_ == consider_top_ && ignore_bottom_ == consider_bottom_) {
+ done();
+ return;
+ }
+
+ // Determine around left, such that it is between -1 and num_tiles_x.
+ int around_left = 0;
+ if (center.x() < 0 || center.IsEmpty())
+ around_left = -1;
+ else if (center.x() > tiling_data->tiling_size().width())
+ around_left = tiling_data->num_tiles_x();
+ else
+ around_left = tiling_data->TileXIndexFromSrcCoord(center.x());
+
+ // Determine around top, such that it is between -1 and num_tiles_y.
+ int around_top = 0;
+ if (center.y() < 0 || center.IsEmpty())
+ around_top = -1;
+ else if (center.y() > tiling_data->tiling_size().height())
+ around_top = tiling_data->num_tiles_y();
+ else
+ around_top = tiling_data->TileYIndexFromSrcCoord(center.y());
+
+ // Determine around right, such that it is between -1 and num_tiles_x.
+ int right_src_coord = center.right() - 1;
+ int around_right = 0;
+ if (right_src_coord < 0 || center.IsEmpty()) {
+ around_right = -1;
+ } else if (right_src_coord > tiling_data->tiling_size().width()) {
+ around_right = tiling_data->num_tiles_x();
+ } else {
+ around_right = tiling_data->TileXIndexFromSrcCoord(right_src_coord);
+ }
+
+ // Determine around bottom, such that it is between -1 and num_tiles_y.
+ int bottom_src_coord = center.bottom() - 1;
+ int around_bottom = 0;
+ if (bottom_src_coord < 0 || center.IsEmpty()) {
+ around_bottom = -1;
+ } else if (bottom_src_coord > tiling_data->tiling_size().height()) {
+ around_bottom = tiling_data->num_tiles_y();
+ } else {
+ around_bottom = tiling_data->TileYIndexFromSrcCoord(bottom_src_coord);
+ }
+
+ vertical_step_count_ = around_bottom - around_top + 1;
+ horizontal_step_count_ = around_right - around_left + 1;
+ current_step_ = horizontal_step_count_ - 1;
+
+ index_x_ = around_right;
+ index_y_ = around_bottom;
+
+ // The current index is the bottom right of the around rect, which is also
+ // ignored. So we have to advance.
+ ++(*this);
+}
+
+TilingData::SpiralDifferenceIterator& TilingData::SpiralDifferenceIterator::
+operator++() {
+ int cannot_hit_consider_count = 0;
+ while (cannot_hit_consider_count < 4) {
+ if (needs_direction_switch())
+ switch_direction();
+
+ index_x_ += delta_x_;
+ index_y_ += delta_y_;
+ ++current_step_;
+
+ if (in_consider_rect()) {
+ cannot_hit_consider_count = 0;
+
+ if (!in_ignore_rect())
+ break;
+
+ // Steps needed to reach the very edge of the ignore rect, while remaining
+ // inside (so that the continue would take us outside).
+ int steps_to_edge = 0;
+ switch (direction_) {
+ case UP:
+ steps_to_edge = index_y_ - ignore_top_;
+ break;
+ case LEFT:
+ steps_to_edge = index_x_ - ignore_left_;
+ break;
+ case DOWN:
+ steps_to_edge = ignore_bottom_ - index_y_;
+ break;
+ case RIGHT:
+ steps_to_edge = ignore_right_ - index_x_;
+ break;
+ }
+
+ // We need to switch directions in |max_steps|.
+ int max_steps = current_step_count() - current_step_;
+
+ int steps_to_take = std::min(steps_to_edge, max_steps);
+ DCHECK_GE(steps_to_take, 0);
+
+ index_x_ += steps_to_take * delta_x_;
+ index_y_ += steps_to_take * delta_y_;
+ current_step_ += steps_to_take;
+ } else {
+ int max_steps = current_step_count() - current_step_;
+ int steps_to_take = max_steps;
+ bool can_hit_consider_rect = false;
+ switch (direction_) {
+ case UP:
+ if (valid_column() && consider_bottom_ < index_y_)
+ steps_to_take = index_y_ - consider_bottom_ - 1;
+ can_hit_consider_rect |= consider_right_ >= index_x_;
+ break;
+ case LEFT:
+ if (valid_row() && consider_right_ < index_x_)
+ steps_to_take = index_x_ - consider_right_ - 1;
+ can_hit_consider_rect |= consider_top_ <= index_y_;
+ break;
+ case DOWN:
+ if (valid_column() && consider_top_ > index_y_)
+ steps_to_take = consider_top_ - index_y_ - 1;
+ can_hit_consider_rect |= consider_left_ <= index_x_;
+ break;
+ case RIGHT:
+ if (valid_row() && consider_left_ > index_x_)
+ steps_to_take = consider_left_ - index_x_ - 1;
+ can_hit_consider_rect |= consider_bottom_ >= index_y_;
+ break;
+ }
+ steps_to_take = std::min(steps_to_take, max_steps);
+ DCHECK_GE(steps_to_take, 0);
+
+ index_x_ += steps_to_take * delta_x_;
+ index_y_ += steps_to_take * delta_y_;
+ current_step_ += steps_to_take;
+
+ if (can_hit_consider_rect)
+ cannot_hit_consider_count = 0;
+ else
+ ++cannot_hit_consider_count;
+ }
+ }
+
+ if (cannot_hit_consider_count >= 4)
+ done();
+ return *this;
+}
+
+bool TilingData::SpiralDifferenceIterator::needs_direction_switch() const {
+ return current_step_ >= current_step_count();
+}
+
+void TilingData::SpiralDifferenceIterator::switch_direction() {
+ int new_delta_x_ = delta_y_;
+ delta_y_ = -delta_x_;
+ delta_x_ = new_delta_x_;
+
+ current_step_ = 0;
+ direction_ = static_cast<Direction>((direction_ + 1) % 4);
+
+ if (direction_ == RIGHT || direction_ == LEFT) {
+ ++vertical_step_count_;
+ ++horizontal_step_count_;
+ }
+}
+
+} // namespace cc
diff --git a/cc/base/tiling_data.h b/cc/base/tiling_data.h
new file mode 100644
index 0000000..a2e0a13
--- /dev/null
+++ b/cc/base/tiling_data.h
@@ -0,0 +1,206 @@
+// Copyright 2010 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_TILING_DATA_H_
+#define CC_BASE_TILING_DATA_H_
+
+#include <utility>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "cc/base/cc_export.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/size.h"
+
+namespace gfx {
+class Vector2d;
+}
+
+namespace cc {
+
+class CC_EXPORT TilingData {
+ public:
+ TilingData();
+ TilingData(const gfx::Size& max_texture_size,
+ const gfx::Size& tiling_size,
+ bool has_border_texels);
+ TilingData(const gfx::Size& max_texture_size,
+ const gfx::Size& tiling_size,
+ int border_texels);
+
+ gfx::Size tiling_size() const { return tiling_size_; }
+ void SetTilingSize(const gfx::Size& tiling_size);
+
+ gfx::Size max_texture_size() const { return max_texture_size_; }
+ void SetMaxTextureSize(const gfx::Size& max_texture_size);
+
+ int border_texels() const { return border_texels_; }
+ void SetHasBorderTexels(bool has_border_texels);
+ void SetBorderTexels(int border_texels);
+
+ bool has_empty_bounds() const { return !num_tiles_x_ || !num_tiles_y_; }
+ int num_tiles_x() const { return num_tiles_x_; }
+ int num_tiles_y() const { return num_tiles_y_; }
+ // Return the tile index whose non-border texels include src_position.
+ int TileXIndexFromSrcCoord(int src_position) const;
+ int TileYIndexFromSrcCoord(int src_position) const;
+ // Return the lowest tile index whose border texels include src_position.
+ int FirstBorderTileXIndexFromSrcCoord(int src_position) const;
+ int FirstBorderTileYIndexFromSrcCoord(int src_position) const;
+ // Return the highest tile index whose border texels include src_position.
+ int LastBorderTileXIndexFromSrcCoord(int src_position) const;
+ int LastBorderTileYIndexFromSrcCoord(int src_position) const;
+
+ gfx::Rect ExpandRectIgnoringBordersToTileBounds(const gfx::Rect& rect) const;
+ gfx::Rect ExpandRectToTileBounds(const gfx::Rect& rect) const;
+
+ gfx::Rect TileBounds(int i, int j) const;
+ gfx::Rect TileBoundsWithBorder(int i, int j) const;
+ int TilePositionX(int x_index) const;
+ int TilePositionY(int y_index) const;
+ int TileSizeX(int x_index) const;
+ int TileSizeY(int y_index) const;
+
+ // Difference between TileBound's and TileBoundWithBorder's origin().
+ gfx::Vector2d TextureOffset(int x_index, int y_index) const;
+
+ class CC_EXPORT BaseIterator {
+ public:
+ operator bool() const { return index_x_ != -1 && index_y_ != -1; }
+
+ int index_x() const { return index_x_; }
+ int index_y() const { return index_y_; }
+ std::pair<int, int> index() const {
+ return std::make_pair(index_x_, index_y_);
+ }
+
+ protected:
+ explicit BaseIterator(const TilingData* tiling_data);
+ void done() {
+ index_x_ = -1;
+ index_y_ = -1;
+ }
+
+ const TilingData* tiling_data_;
+ int index_x_;
+ int index_y_;
+ };
+
+ // Iterate through tiles whose bounds + optional border intersect with |rect|.
+ class CC_EXPORT Iterator : public BaseIterator {
+ public:
+ Iterator();
+ Iterator(const TilingData* tiling_data,
+ const gfx::Rect& consider_rect,
+ bool include_borders);
+ Iterator& operator++();
+
+ private:
+ int left_;
+ int right_;
+ int bottom_;
+ };
+
+ // Iterate through all indices whose bounds (not including borders) intersect
+ // with |consider| but which also do not intersect with |ignore|.
+ class CC_EXPORT DifferenceIterator : public BaseIterator {
+ public:
+ DifferenceIterator(const TilingData* tiling_data,
+ const gfx::Rect& consider_rect,
+ const gfx::Rect& ignore_rect);
+ DifferenceIterator& operator++();
+
+ private:
+ bool in_ignore_rect() const {
+ return index_x_ >= ignore_left_ && index_x_ <= ignore_right_ &&
+ index_y_ >= ignore_top_ && index_y_ <= ignore_bottom_;
+ }
+
+ int consider_left_;
+ int consider_top_;
+ int consider_right_;
+ int consider_bottom_;
+ int ignore_left_;
+ int ignore_top_;
+ int ignore_right_;
+ int ignore_bottom_;
+ };
+
+ // Iterate through all indices whose bounds + border intersect with
+ // |consider| but which also do not intersect with |ignore|. The iterator
+ // order is a counterclockwise spiral around the given center.
+ class CC_EXPORT SpiralDifferenceIterator : public BaseIterator {
+ public:
+ SpiralDifferenceIterator();
+ SpiralDifferenceIterator(const TilingData* tiling_data,
+ const gfx::Rect& consider_rect,
+ const gfx::Rect& ignore_rect,
+ const gfx::Rect& center_rect);
+ SpiralDifferenceIterator& operator++();
+
+ private:
+ bool in_consider_rect() const {
+ return index_x_ >= consider_left_ && index_x_ <= consider_right_ &&
+ index_y_ >= consider_top_ && index_y_ <= consider_bottom_;
+ }
+ bool in_ignore_rect() const {
+ return index_x_ >= ignore_left_ && index_x_ <= ignore_right_ &&
+ index_y_ >= ignore_top_ && index_y_ <= ignore_bottom_;
+ }
+ bool valid_column() const {
+ return index_x_ >= consider_left_ && index_x_ <= consider_right_;
+ }
+ bool valid_row() const {
+ return index_y_ >= consider_top_ && index_y_ <= consider_bottom_;
+ }
+
+ int current_step_count() const {
+ return (direction_ == UP || direction_ == DOWN) ? vertical_step_count_
+ : horizontal_step_count_;
+ }
+
+ bool needs_direction_switch() const;
+ void switch_direction();
+
+ int consider_left_;
+ int consider_top_;
+ int consider_right_;
+ int consider_bottom_;
+ int ignore_left_;
+ int ignore_top_;
+ int ignore_right_;
+ int ignore_bottom_;
+
+ enum Direction { UP, LEFT, DOWN, RIGHT };
+
+ Direction direction_;
+ int delta_x_;
+ int delta_y_;
+ int current_step_;
+ int horizontal_step_count_;
+ int vertical_step_count_;
+ };
+
+ private:
+ void AssertTile(int i, int j) const {
+ DCHECK_GE(i, 0);
+ DCHECK_LT(i, num_tiles_x_);
+ DCHECK_GE(j, 0);
+ DCHECK_LT(j, num_tiles_y_);
+ }
+
+ void RecomputeNumTiles();
+
+ gfx::Size max_texture_size_;
+ gfx::Size tiling_size_;
+ int border_texels_;
+
+ // These are computed values.
+ int num_tiles_x_;
+ int num_tiles_y_;
+};
+
+} // namespace cc
+
+#endif // CC_BASE_TILING_DATA_H_
diff --git a/cc/base/tiling_data_unittest.cc b/cc/base/tiling_data_unittest.cc
new file mode 100644
index 0000000..9c2f944
--- /dev/null
+++ b/cc/base/tiling_data_unittest.cc
@@ -0,0 +1,1940 @@
+// Copyright 2013 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/tiling_data.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "cc/test/geometry_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+namespace {
+
+int NumTiles(const gfx::Size& max_texture_size,
+ const gfx::Size& tiling_size,
+ bool has_border_texels) {
+ TilingData tiling(max_texture_size, tiling_size, has_border_texels);
+ int num_tiles = tiling.num_tiles_x() * tiling.num_tiles_y();
+
+ // Assert no overflow.
+ EXPECT_GE(num_tiles, 0);
+ if (num_tiles > 0)
+ EXPECT_EQ(num_tiles / tiling.num_tiles_x(), tiling.num_tiles_y());
+
+ return num_tiles;
+}
+
+int XIndex(const gfx::Size& max_texture_size,
+ const gfx::Size& tiling_size,
+ bool has_border_texels,
+ int x_coord) {
+ TilingData tiling(max_texture_size, tiling_size, has_border_texels);
+ return tiling.TileXIndexFromSrcCoord(x_coord);
+}
+
+int YIndex(const gfx::Size& max_texture_size,
+ const gfx::Size& tiling_size,
+ bool has_border_texels,
+ int y_coord) {
+ TilingData tiling(max_texture_size, tiling_size, has_border_texels);
+ return tiling.TileYIndexFromSrcCoord(y_coord);
+}
+
+int MinBorderXIndex(const gfx::Size& max_texture_size,
+ const gfx::Size& tiling_size,
+ bool has_border_texels,
+ int x_coord) {
+ TilingData tiling(max_texture_size, tiling_size, has_border_texels);
+ return tiling.FirstBorderTileXIndexFromSrcCoord(x_coord);
+}
+
+int MinBorderYIndex(const gfx::Size& max_texture_size,
+ const gfx::Size& tiling_size,
+ bool has_border_texels,
+ int y_coord) {
+ TilingData tiling(max_texture_size, tiling_size, has_border_texels);
+ return tiling.FirstBorderTileYIndexFromSrcCoord(y_coord);
+}
+
+int MaxBorderXIndex(const gfx::Size& max_texture_size,
+ const gfx::Size& tiling_size,
+ bool has_border_texels,
+ int x_coord) {
+ TilingData tiling(max_texture_size, tiling_size, has_border_texels);
+ return tiling.LastBorderTileXIndexFromSrcCoord(x_coord);
+}
+
+int MaxBorderYIndex(const gfx::Size& max_texture_size,
+ const gfx::Size& tiling_size,
+ bool has_border_texels,
+ int y_coord) {
+ TilingData tiling(max_texture_size, tiling_size, has_border_texels);
+ return tiling.LastBorderTileYIndexFromSrcCoord(y_coord);
+}
+
+int PosX(const gfx::Size& max_texture_size,
+ const gfx::Size& tiling_size,
+ bool has_border_texels,
+ int x_index) {
+ TilingData tiling(max_texture_size, tiling_size, has_border_texels);
+ return tiling.TilePositionX(x_index);
+}
+
+int PosY(const gfx::Size& max_texture_size,
+ const gfx::Size& tiling_size,
+ bool has_border_texels,
+ int y_index) {
+ TilingData tiling(max_texture_size, tiling_size, has_border_texels);
+ return tiling.TilePositionY(y_index);
+}
+
+int SizeX(const gfx::Size& max_texture_size,
+ const gfx::Size& tiling_size,
+ bool has_border_texels,
+ int x_index) {
+ TilingData tiling(max_texture_size, tiling_size, has_border_texels);
+ return tiling.TileSizeX(x_index);
+}
+
+int SizeY(const gfx::Size& max_texture_size,
+ const gfx::Size& tiling_size,
+ bool has_border_texels,
+ int y_index) {
+ TilingData tiling(max_texture_size, tiling_size, has_border_texels);
+ return tiling.TileSizeY(y_index);
+}
+
+class TilingDataTest : public ::testing::TestWithParam<gfx::Point> {};
+
+TEST(TilingDataTest, NumTiles_NoTiling) {
+ EXPECT_EQ(1, NumTiles(gfx::Size(16, 16), gfx::Size(16, 16), false));
+ EXPECT_EQ(1, NumTiles(gfx::Size(16, 16), gfx::Size(15, 15), true));
+ EXPECT_EQ(1, NumTiles(gfx::Size(16, 16), gfx::Size(16, 16), true));
+ EXPECT_EQ(1, NumTiles(gfx::Size(16, 16), gfx::Size(1, 16), false));
+ EXPECT_EQ(1, NumTiles(gfx::Size(15, 15), gfx::Size(15, 15), true));
+ EXPECT_EQ(1, NumTiles(gfx::Size(32, 16), gfx::Size(32, 16), false));
+ EXPECT_EQ(1, NumTiles(gfx::Size(32, 16), gfx::Size(32, 16), true));
+}
+
+TEST(TilingDataTest, NumTiles_TilingNoBorders) {
+ EXPECT_EQ(0, NumTiles(gfx::Size(0, 0), gfx::Size(0, 0), false));
+ EXPECT_EQ(0, NumTiles(gfx::Size(0, 0), gfx::Size(4, 0), false));
+ EXPECT_EQ(0, NumTiles(gfx::Size(0, 0), gfx::Size(0, 4), false));
+ EXPECT_EQ(0, NumTiles(gfx::Size(4, 4), gfx::Size(4, 0), false));
+ EXPECT_EQ(0, NumTiles(gfx::Size(4, 4), gfx::Size(0, 4), false));
+ EXPECT_EQ(0, NumTiles(gfx::Size(0, 0), gfx::Size(1, 1), false));
+
+ EXPECT_EQ(1, NumTiles(gfx::Size(1, 1), gfx::Size(1, 1), false));
+ EXPECT_EQ(2, NumTiles(gfx::Size(1, 1), gfx::Size(1, 2), false));
+ EXPECT_EQ(2, NumTiles(gfx::Size(1, 1), gfx::Size(2, 1), false));
+ EXPECT_EQ(1, NumTiles(gfx::Size(2, 2), gfx::Size(1, 1), false));
+ EXPECT_EQ(1, NumTiles(gfx::Size(2, 2), gfx::Size(1, 2), false));
+ EXPECT_EQ(1, NumTiles(gfx::Size(2, 2), gfx::Size(2, 1), false));
+ EXPECT_EQ(1, NumTiles(gfx::Size(2, 2), gfx::Size(2, 2), false));
+ EXPECT_EQ(1, NumTiles(gfx::Size(3, 3), gfx::Size(3, 3), false));
+
+ EXPECT_EQ(1, NumTiles(gfx::Size(4, 4), gfx::Size(1, 4), false));
+ EXPECT_EQ(1, NumTiles(gfx::Size(4, 4), gfx::Size(2, 4), false));
+ EXPECT_EQ(1, NumTiles(gfx::Size(4, 4), gfx::Size(3, 4), false));
+ EXPECT_EQ(1, NumTiles(gfx::Size(4, 4), gfx::Size(4, 4), false));
+ EXPECT_EQ(2, NumTiles(gfx::Size(4, 4), gfx::Size(5, 4), false));
+ EXPECT_EQ(2, NumTiles(gfx::Size(4, 4), gfx::Size(6, 4), false));
+ EXPECT_EQ(2, NumTiles(gfx::Size(4, 4), gfx::Size(7, 4), false));
+ EXPECT_EQ(2, NumTiles(gfx::Size(4, 4), gfx::Size(8, 4), false));
+ EXPECT_EQ(3, NumTiles(gfx::Size(4, 4), gfx::Size(9, 4), false));
+ EXPECT_EQ(3, NumTiles(gfx::Size(4, 4), gfx::Size(10, 4), false));
+ EXPECT_EQ(3, NumTiles(gfx::Size(4, 4), gfx::Size(11, 4), false));
+
+ EXPECT_EQ(1, NumTiles(gfx::Size(5, 5), gfx::Size(1, 5), false));
+ EXPECT_EQ(1, NumTiles(gfx::Size(5, 5), gfx::Size(2, 5), false));
+ EXPECT_EQ(1, NumTiles(gfx::Size(5, 5), gfx::Size(3, 5), false));
+ EXPECT_EQ(1, NumTiles(gfx::Size(5, 5), gfx::Size(4, 5), false));
+ EXPECT_EQ(1, NumTiles(gfx::Size(5, 5), gfx::Size(5, 5), false));
+ EXPECT_EQ(2, NumTiles(gfx::Size(5, 5), gfx::Size(6, 5), false));
+ EXPECT_EQ(2, NumTiles(gfx::Size(5, 5), gfx::Size(7, 5), false));
+ EXPECT_EQ(2, NumTiles(gfx::Size(5, 5), gfx::Size(8, 5), false));
+ EXPECT_EQ(2, NumTiles(gfx::Size(5, 5), gfx::Size(9, 5), false));
+ EXPECT_EQ(2, NumTiles(gfx::Size(5, 5), gfx::Size(10, 5), false));
+ EXPECT_EQ(3, NumTiles(gfx::Size(5, 5), gfx::Size(11, 5), false));
+
+ EXPECT_EQ(1, NumTiles(gfx::Size(16, 16), gfx::Size(16, 16), false));
+ EXPECT_EQ(1, NumTiles(gfx::Size(17, 17), gfx::Size(16, 16), false));
+ EXPECT_EQ(4, NumTiles(gfx::Size(15, 15), gfx::Size(16, 16), false));
+ EXPECT_EQ(4, NumTiles(gfx::Size(8, 8), gfx::Size(16, 16), false));
+ EXPECT_EQ(6, NumTiles(gfx::Size(8, 8), gfx::Size(17, 16), false));
+
+ EXPECT_EQ(8, NumTiles(gfx::Size(5, 8), gfx::Size(17, 16), false));
+}
+
+TEST(TilingDataTest, NumTiles_TilingWithBorders) {
+ EXPECT_EQ(0, NumTiles(gfx::Size(0, 0), gfx::Size(0, 0), true));
+ EXPECT_EQ(0, NumTiles(gfx::Size(0, 0), gfx::Size(4, 0), true));
+ EXPECT_EQ(0, NumTiles(gfx::Size(0, 0), gfx::Size(0, 4), true));
+ EXPECT_EQ(0, NumTiles(gfx::Size(4, 4), gfx::Size(4, 0), true));
+ EXPECT_EQ(0, NumTiles(gfx::Size(4, 4), gfx::Size(0, 4), true));
+ EXPECT_EQ(0, NumTiles(gfx::Size(0, 0), gfx::Size(1, 1), true));
+
+ EXPECT_EQ(1, NumTiles(gfx::Size(1, 1), gfx::Size(1, 1), true));
+ EXPECT_EQ(0, NumTiles(gfx::Size(1, 1), gfx::Size(1, 2), true));
+ EXPECT_EQ(0, NumTiles(gfx::Size(1, 1), gfx::Size(2, 1), true));
+ EXPECT_EQ(1, NumTiles(gfx::Size(2, 2), gfx::Size(1, 1), true));
+ EXPECT_EQ(1, NumTiles(gfx::Size(2, 2), gfx::Size(1, 2), true));
+ EXPECT_EQ(1, NumTiles(gfx::Size(2, 2), gfx::Size(2, 1), true));
+ EXPECT_EQ(1, NumTiles(gfx::Size(2, 2), gfx::Size(2, 2), true));
+
+ EXPECT_EQ(1, NumTiles(gfx::Size(3, 3), gfx::Size(1, 3), true));
+ EXPECT_EQ(1, NumTiles(gfx::Size(3, 3), gfx::Size(2, 3), true));
+ EXPECT_EQ(1, NumTiles(gfx::Size(3, 3), gfx::Size(3, 3), true));
+ EXPECT_EQ(2, NumTiles(gfx::Size(3, 3), gfx::Size(4, 3), true));
+ EXPECT_EQ(3, NumTiles(gfx::Size(3, 3), gfx::Size(5, 3), true));
+ EXPECT_EQ(4, NumTiles(gfx::Size(3, 3), gfx::Size(6, 3), true));
+ EXPECT_EQ(5, NumTiles(gfx::Size(3, 3), gfx::Size(7, 3), true));
+
+ EXPECT_EQ(1, NumTiles(gfx::Size(4, 4), gfx::Size(1, 4), true));
+ EXPECT_EQ(1, NumTiles(gfx::Size(4, 4), gfx::Size(2, 4), true));
+ EXPECT_EQ(1, NumTiles(gfx::Size(4, 4), gfx::Size(3, 4), true));
+ EXPECT_EQ(1, NumTiles(gfx::Size(4, 4), gfx::Size(4, 4), true));
+ EXPECT_EQ(2, NumTiles(gfx::Size(4, 4), gfx::Size(5, 4), true));
+ EXPECT_EQ(2, NumTiles(gfx::Size(4, 4), gfx::Size(6, 4), true));
+ EXPECT_EQ(3, NumTiles(gfx::Size(4, 4), gfx::Size(7, 4), true));
+ EXPECT_EQ(3, NumTiles(gfx::Size(4, 4), gfx::Size(8, 4), true));
+ EXPECT_EQ(4, NumTiles(gfx::Size(4, 4), gfx::Size(9, 4), true));
+ EXPECT_EQ(4, NumTiles(gfx::Size(4, 4), gfx::Size(10, 4), true));
+ EXPECT_EQ(5, NumTiles(gfx::Size(4, 4), gfx::Size(11, 4), true));
+
+ EXPECT_EQ(1, NumTiles(gfx::Size(5, 5), gfx::Size(1, 5), true));
+ EXPECT_EQ(1, NumTiles(gfx::Size(5, 5), gfx::Size(2, 5), true));
+ EXPECT_EQ(1, NumTiles(gfx::Size(5, 5), gfx::Size(3, 5), true));
+ EXPECT_EQ(1, NumTiles(gfx::Size(5, 5), gfx::Size(4, 5), true));
+ EXPECT_EQ(1, NumTiles(gfx::Size(5, 5), gfx::Size(5, 5), true));
+ EXPECT_EQ(2, NumTiles(gfx::Size(5, 5), gfx::Size(6, 5), true));
+ EXPECT_EQ(2, NumTiles(gfx::Size(5, 5), gfx::Size(7, 5), true));
+ EXPECT_EQ(2, NumTiles(gfx::Size(5, 5), gfx::Size(8, 5), true));
+ EXPECT_EQ(3, NumTiles(gfx::Size(5, 5), gfx::Size(9, 5), true));
+ EXPECT_EQ(3, NumTiles(gfx::Size(5, 5), gfx::Size(10, 5), true));
+ EXPECT_EQ(3, NumTiles(gfx::Size(5, 5), gfx::Size(11, 5), true));
+
+ EXPECT_EQ(30, NumTiles(gfx::Size(8, 5), gfx::Size(16, 32), true));
+}
+
+TEST(TilingDataTest, TileXIndexFromSrcCoord) {
+ EXPECT_EQ(0, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 0));
+ EXPECT_EQ(0, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 1));
+ EXPECT_EQ(0, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 2));
+ EXPECT_EQ(1, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 3));
+ EXPECT_EQ(1, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 4));
+ EXPECT_EQ(1, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 5));
+ EXPECT_EQ(2, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 6));
+ EXPECT_EQ(2, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 7));
+ EXPECT_EQ(2, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 8));
+ EXPECT_EQ(3, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 9));
+ EXPECT_EQ(3, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 10));
+ EXPECT_EQ(3, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 11));
+
+ EXPECT_EQ(0, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 0));
+ EXPECT_EQ(0, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 1));
+ EXPECT_EQ(1, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 2));
+ EXPECT_EQ(2, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 3));
+ EXPECT_EQ(3, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 4));
+ EXPECT_EQ(4, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 5));
+ EXPECT_EQ(5, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 6));
+ EXPECT_EQ(6, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 7));
+ EXPECT_EQ(7, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 8));
+ EXPECT_EQ(7, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 9));
+ EXPECT_EQ(7, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 10));
+ EXPECT_EQ(7, XIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 11));
+
+ EXPECT_EQ(0, XIndex(gfx::Size(1, 1), gfx::Size(1, 1), false, 0));
+ EXPECT_EQ(0, XIndex(gfx::Size(2, 2), gfx::Size(2, 2), false, 0));
+ EXPECT_EQ(0, XIndex(gfx::Size(2, 2), gfx::Size(2, 2), false, 1));
+ EXPECT_EQ(0, XIndex(gfx::Size(3, 3), gfx::Size(3, 3), false, 0));
+ EXPECT_EQ(0, XIndex(gfx::Size(3, 3), gfx::Size(3, 3), false, 1));
+ EXPECT_EQ(0, XIndex(gfx::Size(3, 3), gfx::Size(3, 3), false, 2));
+
+ EXPECT_EQ(0, XIndex(gfx::Size(3, 3), gfx::Size(4, 3), false, 0));
+ EXPECT_EQ(0, XIndex(gfx::Size(3, 3), gfx::Size(4, 3), false, 1));
+ EXPECT_EQ(0, XIndex(gfx::Size(3, 3), gfx::Size(4, 3), false, 2));
+ EXPECT_EQ(1, XIndex(gfx::Size(3, 3), gfx::Size(4, 3), false, 3));
+
+ EXPECT_EQ(0, XIndex(gfx::Size(1, 1), gfx::Size(1, 1), true, 0));
+ EXPECT_EQ(0, XIndex(gfx::Size(2, 2), gfx::Size(2, 2), true, 0));
+ EXPECT_EQ(0, XIndex(gfx::Size(2, 2), gfx::Size(2, 2), true, 1));
+ EXPECT_EQ(0, XIndex(gfx::Size(3, 3), gfx::Size(3, 3), true, 0));
+ EXPECT_EQ(0, XIndex(gfx::Size(3, 3), gfx::Size(3, 3), true, 1));
+ EXPECT_EQ(0, XIndex(gfx::Size(3, 3), gfx::Size(3, 3), true, 2));
+
+ EXPECT_EQ(0, XIndex(gfx::Size(3, 3), gfx::Size(4, 3), true, 0));
+ EXPECT_EQ(0, XIndex(gfx::Size(3, 3), gfx::Size(4, 3), true, 1));
+ EXPECT_EQ(1, XIndex(gfx::Size(3, 3), gfx::Size(4, 3), true, 2));
+ EXPECT_EQ(1, XIndex(gfx::Size(3, 3), gfx::Size(4, 3), true, 3));
+}
+
+TEST(TilingDataTest, FirstBorderTileXIndexFromSrcCoord) {
+ EXPECT_EQ(0, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 0));
+ EXPECT_EQ(0, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 1));
+ EXPECT_EQ(0, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 2));
+ EXPECT_EQ(1, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 3));
+ EXPECT_EQ(1, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 4));
+ EXPECT_EQ(1, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 5));
+ EXPECT_EQ(2, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 6));
+ EXPECT_EQ(2, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 7));
+ EXPECT_EQ(2, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 8));
+ EXPECT_EQ(3, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 9));
+ EXPECT_EQ(3, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 10));
+ EXPECT_EQ(3, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 11));
+
+ EXPECT_EQ(0, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 0));
+ EXPECT_EQ(0, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 1));
+ EXPECT_EQ(0, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 2));
+ EXPECT_EQ(1, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 3));
+ EXPECT_EQ(2, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 4));
+ EXPECT_EQ(3, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 5));
+ EXPECT_EQ(4, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 6));
+ EXPECT_EQ(5, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 7));
+ EXPECT_EQ(6, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 8));
+ EXPECT_EQ(7, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 9));
+ EXPECT_EQ(7, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 10));
+ EXPECT_EQ(7, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 11));
+
+ EXPECT_EQ(0, MinBorderXIndex(gfx::Size(1, 1), gfx::Size(1, 1), false, 0));
+ EXPECT_EQ(0, MinBorderXIndex(gfx::Size(2, 2), gfx::Size(2, 2), false, 0));
+ EXPECT_EQ(0, MinBorderXIndex(gfx::Size(2, 2), gfx::Size(2, 2), false, 1));
+ EXPECT_EQ(0, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(3, 3), false, 0));
+ EXPECT_EQ(0, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(3, 3), false, 1));
+ EXPECT_EQ(0, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(3, 3), false, 2));
+
+ EXPECT_EQ(0, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(4, 3), false, 0));
+ EXPECT_EQ(0, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(4, 3), false, 1));
+ EXPECT_EQ(0, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(4, 3), false, 2));
+ EXPECT_EQ(1, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(4, 3), false, 3));
+
+ EXPECT_EQ(0, MinBorderXIndex(gfx::Size(1, 1), gfx::Size(1, 1), true, 0));
+ EXPECT_EQ(0, MinBorderXIndex(gfx::Size(2, 2), gfx::Size(2, 2), true, 0));
+ EXPECT_EQ(0, MinBorderXIndex(gfx::Size(2, 2), gfx::Size(2, 2), true, 1));
+ EXPECT_EQ(0, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(3, 3), true, 0));
+ EXPECT_EQ(0, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(3, 3), true, 1));
+ EXPECT_EQ(0, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(3, 3), true, 2));
+
+ EXPECT_EQ(0, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(4, 3), true, 0));
+ EXPECT_EQ(0, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(4, 3), true, 1));
+ EXPECT_EQ(0, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(4, 3), true, 2));
+ EXPECT_EQ(1, MinBorderXIndex(gfx::Size(3, 3), gfx::Size(4, 3), true, 3));
+}
+
+TEST(TilingDataTest, LastBorderTileXIndexFromSrcCoord) {
+ EXPECT_EQ(0, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 0));
+ EXPECT_EQ(0, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 1));
+ EXPECT_EQ(0, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 2));
+ EXPECT_EQ(1, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 3));
+ EXPECT_EQ(1, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 4));
+ EXPECT_EQ(1, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 5));
+ EXPECT_EQ(2, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 6));
+ EXPECT_EQ(2, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 7));
+ EXPECT_EQ(2, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 8));
+ EXPECT_EQ(3, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 9));
+ EXPECT_EQ(3, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 10));
+ EXPECT_EQ(3, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 11));
+
+ EXPECT_EQ(0, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 0));
+ EXPECT_EQ(1, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 1));
+ EXPECT_EQ(2, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 2));
+ EXPECT_EQ(3, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 3));
+ EXPECT_EQ(4, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 4));
+ EXPECT_EQ(5, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 5));
+ EXPECT_EQ(6, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 6));
+ EXPECT_EQ(7, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 7));
+ EXPECT_EQ(7, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 8));
+ EXPECT_EQ(7, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 9));
+ EXPECT_EQ(7, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 10));
+ EXPECT_EQ(7, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 11));
+
+ EXPECT_EQ(0, MaxBorderXIndex(gfx::Size(1, 1), gfx::Size(1, 1), false, 0));
+ EXPECT_EQ(0, MaxBorderXIndex(gfx::Size(2, 2), gfx::Size(2, 2), false, 0));
+ EXPECT_EQ(0, MaxBorderXIndex(gfx::Size(2, 2), gfx::Size(2, 2), false, 1));
+ EXPECT_EQ(0, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(3, 3), false, 0));
+ EXPECT_EQ(0, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(3, 3), false, 1));
+ EXPECT_EQ(0, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(3, 3), false, 2));
+
+ EXPECT_EQ(0, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(4, 3), false, 0));
+ EXPECT_EQ(0, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(4, 3), false, 1));
+ EXPECT_EQ(0, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(4, 3), false, 2));
+ EXPECT_EQ(1, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(4, 3), false, 3));
+
+ EXPECT_EQ(0, MaxBorderXIndex(gfx::Size(1, 1), gfx::Size(1, 1), true, 0));
+ EXPECT_EQ(0, MaxBorderXIndex(gfx::Size(2, 2), gfx::Size(2, 2), true, 0));
+ EXPECT_EQ(0, MaxBorderXIndex(gfx::Size(2, 2), gfx::Size(2, 2), true, 1));
+ EXPECT_EQ(0, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(3, 3), true, 0));
+ EXPECT_EQ(0, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(3, 3), true, 1));
+ EXPECT_EQ(0, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(3, 3), true, 2));
+
+ EXPECT_EQ(0, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(4, 3), true, 0));
+ EXPECT_EQ(1, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(4, 3), true, 1));
+ EXPECT_EQ(1, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(4, 3), true, 2));
+ EXPECT_EQ(1, MaxBorderXIndex(gfx::Size(3, 3), gfx::Size(4, 3), true, 3));
+}
+
+TEST(TilingDataTest, TileYIndexFromSrcCoord) {
+ EXPECT_EQ(0, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 0));
+ EXPECT_EQ(0, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 1));
+ EXPECT_EQ(0, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 2));
+ EXPECT_EQ(1, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 3));
+ EXPECT_EQ(1, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 4));
+ EXPECT_EQ(1, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 5));
+ EXPECT_EQ(2, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 6));
+ EXPECT_EQ(2, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 7));
+ EXPECT_EQ(2, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 8));
+ EXPECT_EQ(3, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 9));
+ EXPECT_EQ(3, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 10));
+ EXPECT_EQ(3, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 11));
+
+ EXPECT_EQ(0, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 0));
+ EXPECT_EQ(0, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 1));
+ EXPECT_EQ(1, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 2));
+ EXPECT_EQ(2, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 3));
+ EXPECT_EQ(3, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 4));
+ EXPECT_EQ(4, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 5));
+ EXPECT_EQ(5, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 6));
+ EXPECT_EQ(6, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 7));
+ EXPECT_EQ(7, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 8));
+ EXPECT_EQ(7, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 9));
+ EXPECT_EQ(7, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 10));
+ EXPECT_EQ(7, YIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 11));
+
+ EXPECT_EQ(0, YIndex(gfx::Size(1, 1), gfx::Size(1, 1), false, 0));
+ EXPECT_EQ(0, YIndex(gfx::Size(2, 2), gfx::Size(2, 2), false, 0));
+ EXPECT_EQ(0, YIndex(gfx::Size(2, 2), gfx::Size(2, 2), false, 1));
+ EXPECT_EQ(0, YIndex(gfx::Size(3, 3), gfx::Size(3, 3), false, 0));
+ EXPECT_EQ(0, YIndex(gfx::Size(3, 3), gfx::Size(3, 3), false, 1));
+ EXPECT_EQ(0, YIndex(gfx::Size(3, 3), gfx::Size(3, 3), false, 2));
+
+ EXPECT_EQ(0, YIndex(gfx::Size(3, 3), gfx::Size(3, 4), false, 0));
+ EXPECT_EQ(0, YIndex(gfx::Size(3, 3), gfx::Size(3, 4), false, 1));
+ EXPECT_EQ(0, YIndex(gfx::Size(3, 3), gfx::Size(3, 4), false, 2));
+ EXPECT_EQ(1, YIndex(gfx::Size(3, 3), gfx::Size(3, 4), false, 3));
+
+ EXPECT_EQ(0, YIndex(gfx::Size(1, 1), gfx::Size(1, 1), true, 0));
+ EXPECT_EQ(0, YIndex(gfx::Size(2, 2), gfx::Size(2, 2), true, 0));
+ EXPECT_EQ(0, YIndex(gfx::Size(2, 2), gfx::Size(2, 2), true, 1));
+ EXPECT_EQ(0, YIndex(gfx::Size(3, 3), gfx::Size(3, 3), true, 0));
+ EXPECT_EQ(0, YIndex(gfx::Size(3, 3), gfx::Size(3, 3), true, 1));
+ EXPECT_EQ(0, YIndex(gfx::Size(3, 3), gfx::Size(3, 3), true, 2));
+
+ EXPECT_EQ(0, YIndex(gfx::Size(3, 3), gfx::Size(3, 4), true, 0));
+ EXPECT_EQ(0, YIndex(gfx::Size(3, 3), gfx::Size(3, 4), true, 1));
+ EXPECT_EQ(1, YIndex(gfx::Size(3, 3), gfx::Size(3, 4), true, 2));
+ EXPECT_EQ(1, YIndex(gfx::Size(3, 3), gfx::Size(3, 4), true, 3));
+}
+
+TEST(TilingDataTest, FirstBorderTileYIndexFromSrcCoord) {
+ EXPECT_EQ(0, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 0));
+ EXPECT_EQ(0, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 1));
+ EXPECT_EQ(0, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 2));
+ EXPECT_EQ(1, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 3));
+ EXPECT_EQ(1, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 4));
+ EXPECT_EQ(1, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 5));
+ EXPECT_EQ(2, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 6));
+ EXPECT_EQ(2, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 7));
+ EXPECT_EQ(2, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 8));
+ EXPECT_EQ(3, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 9));
+ EXPECT_EQ(3, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 10));
+ EXPECT_EQ(3, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 11));
+
+ EXPECT_EQ(0, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 0));
+ EXPECT_EQ(0, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 1));
+ EXPECT_EQ(0, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 2));
+ EXPECT_EQ(1, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 3));
+ EXPECT_EQ(2, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 4));
+ EXPECT_EQ(3, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 5));
+ EXPECT_EQ(4, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 6));
+ EXPECT_EQ(5, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 7));
+ EXPECT_EQ(6, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 8));
+ EXPECT_EQ(7, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 9));
+ EXPECT_EQ(7, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 10));
+ EXPECT_EQ(7, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 11));
+
+ EXPECT_EQ(0, MinBorderYIndex(gfx::Size(1, 1), gfx::Size(1, 1), false, 0));
+ EXPECT_EQ(0, MinBorderYIndex(gfx::Size(2, 2), gfx::Size(2, 2), false, 0));
+ EXPECT_EQ(0, MinBorderYIndex(gfx::Size(2, 2), gfx::Size(2, 2), false, 1));
+ EXPECT_EQ(0, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 3), false, 0));
+ EXPECT_EQ(0, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 3), false, 1));
+ EXPECT_EQ(0, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 3), false, 2));
+
+ EXPECT_EQ(0, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 4), false, 0));
+ EXPECT_EQ(0, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 4), false, 1));
+ EXPECT_EQ(0, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 4), false, 2));
+ EXPECT_EQ(1, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 4), false, 3));
+
+ EXPECT_EQ(0, MinBorderYIndex(gfx::Size(1, 1), gfx::Size(1, 1), true, 0));
+ EXPECT_EQ(0, MinBorderYIndex(gfx::Size(2, 2), gfx::Size(2, 2), true, 0));
+ EXPECT_EQ(0, MinBorderYIndex(gfx::Size(2, 2), gfx::Size(2, 2), true, 1));
+ EXPECT_EQ(0, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 3), true, 0));
+ EXPECT_EQ(0, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 3), true, 1));
+ EXPECT_EQ(0, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 3), true, 2));
+
+ EXPECT_EQ(0, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 4), true, 0));
+ EXPECT_EQ(0, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 4), true, 1));
+ EXPECT_EQ(0, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 4), true, 2));
+ EXPECT_EQ(1, MinBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 4), true, 3));
+}
+
+TEST(TilingDataTest, LastBorderTileYIndexFromSrcCoord) {
+ EXPECT_EQ(0, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 0));
+ EXPECT_EQ(0, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 1));
+ EXPECT_EQ(0, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 2));
+ EXPECT_EQ(1, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 3));
+ EXPECT_EQ(1, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 4));
+ EXPECT_EQ(1, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 5));
+ EXPECT_EQ(2, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 6));
+ EXPECT_EQ(2, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 7));
+ EXPECT_EQ(2, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 8));
+ EXPECT_EQ(3, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 9));
+ EXPECT_EQ(3, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 10));
+ EXPECT_EQ(3, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), false, 11));
+
+ EXPECT_EQ(0, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 0));
+ EXPECT_EQ(1, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 1));
+ EXPECT_EQ(2, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 2));
+ EXPECT_EQ(3, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 3));
+ EXPECT_EQ(4, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 4));
+ EXPECT_EQ(5, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 5));
+ EXPECT_EQ(6, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 6));
+ EXPECT_EQ(7, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 7));
+ EXPECT_EQ(7, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 8));
+ EXPECT_EQ(7, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 9));
+ EXPECT_EQ(7, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 10));
+ EXPECT_EQ(7, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(10, 10), true, 11));
+
+ EXPECT_EQ(0, MaxBorderYIndex(gfx::Size(1, 1), gfx::Size(1, 1), false, 0));
+ EXPECT_EQ(0, MaxBorderYIndex(gfx::Size(2, 2), gfx::Size(2, 2), false, 0));
+ EXPECT_EQ(0, MaxBorderYIndex(gfx::Size(2, 2), gfx::Size(2, 2), false, 1));
+ EXPECT_EQ(0, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 3), false, 0));
+ EXPECT_EQ(0, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 3), false, 1));
+ EXPECT_EQ(0, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 3), false, 2));
+
+ EXPECT_EQ(0, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 4), false, 0));
+ EXPECT_EQ(0, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 4), false, 1));
+ EXPECT_EQ(0, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 4), false, 2));
+ EXPECT_EQ(1, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 4), false, 3));
+
+ EXPECT_EQ(0, MaxBorderYIndex(gfx::Size(1, 1), gfx::Size(1, 1), true, 0));
+ EXPECT_EQ(0, MaxBorderYIndex(gfx::Size(2, 2), gfx::Size(2, 2), true, 0));
+ EXPECT_EQ(0, MaxBorderYIndex(gfx::Size(2, 2), gfx::Size(2, 2), true, 1));
+ EXPECT_EQ(0, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 3), true, 0));
+ EXPECT_EQ(0, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 3), true, 1));
+ EXPECT_EQ(0, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 3), true, 2));
+
+ EXPECT_EQ(0, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 4), true, 0));
+ EXPECT_EQ(1, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 4), true, 1));
+ EXPECT_EQ(1, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 4), true, 2));
+ EXPECT_EQ(1, MaxBorderYIndex(gfx::Size(3, 3), gfx::Size(3, 4), true, 3));
+}
+
+TEST(TilingDataTest, TileSizeX) {
+ EXPECT_EQ(5, SizeX(gfx::Size(5, 5), gfx::Size(5, 5), false, 0));
+ EXPECT_EQ(5, SizeX(gfx::Size(5, 5), gfx::Size(5, 5), true, 0));
+
+ EXPECT_EQ(5, SizeX(gfx::Size(5, 5), gfx::Size(6, 6), false, 0));
+ EXPECT_EQ(1, SizeX(gfx::Size(5, 5), gfx::Size(6, 6), false, 1));
+ EXPECT_EQ(4, SizeX(gfx::Size(5, 5), gfx::Size(6, 6), true, 0));
+ EXPECT_EQ(2, SizeX(gfx::Size(5, 5), gfx::Size(6, 6), true, 1));
+
+ EXPECT_EQ(5, SizeX(gfx::Size(5, 5), gfx::Size(8, 8), false, 0));
+ EXPECT_EQ(3, SizeX(gfx::Size(5, 5), gfx::Size(8, 8), false, 1));
+ EXPECT_EQ(4, SizeX(gfx::Size(5, 5), gfx::Size(8, 8), true, 0));
+ EXPECT_EQ(4, SizeX(gfx::Size(5, 5), gfx::Size(8, 8), true, 1));
+
+ EXPECT_EQ(5, SizeX(gfx::Size(5, 5), gfx::Size(10, 10), false, 0));
+ EXPECT_EQ(5, SizeX(gfx::Size(5, 5), gfx::Size(10, 10), false, 1));
+ EXPECT_EQ(4, SizeX(gfx::Size(5, 5), gfx::Size(10, 10), true, 0));
+ EXPECT_EQ(3, SizeX(gfx::Size(5, 5), gfx::Size(10, 10), true, 1));
+ EXPECT_EQ(3, SizeX(gfx::Size(5, 5), gfx::Size(10, 10), true, 2));
+
+ EXPECT_EQ(4, SizeX(gfx::Size(5, 5), gfx::Size(11, 11), true, 2));
+ EXPECT_EQ(3, SizeX(gfx::Size(5, 5), gfx::Size(12, 12), true, 2));
+
+ EXPECT_EQ(3, SizeX(gfx::Size(5, 9), gfx::Size(12, 17), true, 2));
+}
+
+TEST(TilingDataTest, TileSizeY) {
+ EXPECT_EQ(5, SizeY(gfx::Size(5, 5), gfx::Size(5, 5), false, 0));
+ EXPECT_EQ(5, SizeY(gfx::Size(5, 5), gfx::Size(5, 5), true, 0));
+
+ EXPECT_EQ(5, SizeY(gfx::Size(5, 5), gfx::Size(6, 6), false, 0));
+ EXPECT_EQ(1, SizeY(gfx::Size(5, 5), gfx::Size(6, 6), false, 1));
+ EXPECT_EQ(4, SizeY(gfx::Size(5, 5), gfx::Size(6, 6), true, 0));
+ EXPECT_EQ(2, SizeY(gfx::Size(5, 5), gfx::Size(6, 6), true, 1));
+
+ EXPECT_EQ(5, SizeY(gfx::Size(5, 5), gfx::Size(8, 8), false, 0));
+ EXPECT_EQ(3, SizeY(gfx::Size(5, 5), gfx::Size(8, 8), false, 1));
+ EXPECT_EQ(4, SizeY(gfx::Size(5, 5), gfx::Size(8, 8), true, 0));
+ EXPECT_EQ(4, SizeY(gfx::Size(5, 5), gfx::Size(8, 8), true, 1));
+
+ EXPECT_EQ(5, SizeY(gfx::Size(5, 5), gfx::Size(10, 10), false, 0));
+ EXPECT_EQ(5, SizeY(gfx::Size(5, 5), gfx::Size(10, 10), false, 1));
+ EXPECT_EQ(4, SizeY(gfx::Size(5, 5), gfx::Size(10, 10), true, 0));
+ EXPECT_EQ(3, SizeY(gfx::Size(5, 5), gfx::Size(10, 10), true, 1));
+ EXPECT_EQ(3, SizeY(gfx::Size(5, 5), gfx::Size(10, 10), true, 2));
+
+ EXPECT_EQ(4, SizeY(gfx::Size(5, 5), gfx::Size(11, 11), true, 2));
+ EXPECT_EQ(3, SizeY(gfx::Size(5, 5), gfx::Size(12, 12), true, 2));
+
+ EXPECT_EQ(3, SizeY(gfx::Size(9, 5), gfx::Size(17, 12), true, 2));
+}
+
+TEST(TilingDataTest, TileSizeX_and_TilePositionX) {
+ // Single tile cases:
+ EXPECT_EQ(1, SizeX(gfx::Size(3, 3), gfx::Size(1, 1), false, 0));
+ EXPECT_EQ(0, PosX(gfx::Size(3, 3), gfx::Size(1, 1), false, 0));
+ EXPECT_EQ(1, SizeX(gfx::Size(3, 3), gfx::Size(1, 100), false, 0));
+ EXPECT_EQ(0, PosX(gfx::Size(3, 3), gfx::Size(1, 100), false, 0));
+ EXPECT_EQ(3, SizeX(gfx::Size(3, 3), gfx::Size(3, 1), false, 0));
+ EXPECT_EQ(0, PosX(gfx::Size(3, 3), gfx::Size(3, 1), false, 0));
+ EXPECT_EQ(3, SizeX(gfx::Size(3, 3), gfx::Size(3, 100), false, 0));
+ EXPECT_EQ(0, PosX(gfx::Size(3, 3), gfx::Size(3, 100), false, 0));
+ EXPECT_EQ(1, SizeX(gfx::Size(3, 3), gfx::Size(1, 1), true, 0));
+ EXPECT_EQ(0, PosX(gfx::Size(3, 3), gfx::Size(1, 1), true, 0));
+ EXPECT_EQ(1, SizeX(gfx::Size(3, 3), gfx::Size(1, 100), true, 0));
+ EXPECT_EQ(0, PosX(gfx::Size(3, 3), gfx::Size(1, 100), true, 0));
+ EXPECT_EQ(3, SizeX(gfx::Size(3, 3), gfx::Size(3, 1), true, 0));
+ EXPECT_EQ(0, PosX(gfx::Size(3, 3), gfx::Size(3, 1), true, 0));
+ EXPECT_EQ(3, SizeX(gfx::Size(3, 3), gfx::Size(3, 100), true, 0));
+ EXPECT_EQ(0, PosX(gfx::Size(3, 3), gfx::Size(3, 100), true, 0));
+
+ // Multiple tiles:
+ // no border
+ // positions 0, 3
+ EXPECT_EQ(2, NumTiles(gfx::Size(3, 3), gfx::Size(6, 1), false));
+ EXPECT_EQ(3, SizeX(gfx::Size(3, 3), gfx::Size(6, 1), false, 0));
+ EXPECT_EQ(3, SizeX(gfx::Size(3, 3), gfx::Size(6, 1), false, 1));
+ EXPECT_EQ(0, PosX(gfx::Size(3, 3), gfx::Size(6, 1), false, 0));
+ EXPECT_EQ(3, PosX(gfx::Size(3, 3), gfx::Size(6, 1), false, 1));
+ EXPECT_EQ(3, SizeX(gfx::Size(3, 3), gfx::Size(6, 100), false, 0));
+ EXPECT_EQ(3, SizeX(gfx::Size(3, 3), gfx::Size(6, 100), false, 1));
+ EXPECT_EQ(0, PosX(gfx::Size(3, 3), gfx::Size(6, 100), false, 0));
+ EXPECT_EQ(3, PosX(gfx::Size(3, 3), gfx::Size(6, 100), false, 1));
+
+ // Multiple tiles:
+ // with border
+ // positions 0, 2, 3, 4
+ EXPECT_EQ(4, NumTiles(gfx::Size(3, 3), gfx::Size(6, 1), true));
+ EXPECT_EQ(2, SizeX(gfx::Size(3, 3), gfx::Size(6, 1), true, 0));
+ EXPECT_EQ(1, SizeX(gfx::Size(3, 3), gfx::Size(6, 1), true, 1));
+ EXPECT_EQ(1, SizeX(gfx::Size(3, 3), gfx::Size(6, 1), true, 2));
+ EXPECT_EQ(2, SizeX(gfx::Size(3, 3), gfx::Size(6, 1), true, 3));
+ EXPECT_EQ(0, PosX(gfx::Size(3, 3), gfx::Size(6, 1), true, 0));
+ EXPECT_EQ(2, PosX(gfx::Size(3, 3), gfx::Size(6, 1), true, 1));
+ EXPECT_EQ(3, PosX(gfx::Size(3, 3), gfx::Size(6, 1), true, 2));
+ EXPECT_EQ(4, PosX(gfx::Size(3, 3), gfx::Size(6, 1), true, 3));
+ EXPECT_EQ(2, SizeX(gfx::Size(3, 7), gfx::Size(6, 100), true, 0));
+ EXPECT_EQ(1, SizeX(gfx::Size(3, 7), gfx::Size(6, 100), true, 1));
+ EXPECT_EQ(1, SizeX(gfx::Size(3, 7), gfx::Size(6, 100), true, 2));
+ EXPECT_EQ(2, SizeX(gfx::Size(3, 7), gfx::Size(6, 100), true, 3));
+ EXPECT_EQ(0, PosX(gfx::Size(3, 7), gfx::Size(6, 100), true, 0));
+ EXPECT_EQ(2, PosX(gfx::Size(3, 7), gfx::Size(6, 100), true, 1));
+ EXPECT_EQ(3, PosX(gfx::Size(3, 7), gfx::Size(6, 100), true, 2));
+ EXPECT_EQ(4, PosX(gfx::Size(3, 7), gfx::Size(6, 100), true, 3));
+}
+
+TEST(TilingDataTest, TileSizeY_and_TilePositionY) {
+ // Single tile cases:
+ EXPECT_EQ(1, SizeY(gfx::Size(3, 3), gfx::Size(1, 1), false, 0));
+ EXPECT_EQ(0, PosY(gfx::Size(3, 3), gfx::Size(1, 1), false, 0));
+ EXPECT_EQ(1, SizeY(gfx::Size(3, 3), gfx::Size(100, 1), false, 0));
+ EXPECT_EQ(0, PosY(gfx::Size(3, 3), gfx::Size(100, 1), false, 0));
+ EXPECT_EQ(3, SizeY(gfx::Size(3, 3), gfx::Size(1, 3), false, 0));
+ EXPECT_EQ(0, PosY(gfx::Size(3, 3), gfx::Size(1, 3), false, 0));
+ EXPECT_EQ(3, SizeY(gfx::Size(3, 3), gfx::Size(100, 3), false, 0));
+ EXPECT_EQ(0, PosY(gfx::Size(3, 3), gfx::Size(100, 3), false, 0));
+ EXPECT_EQ(1, SizeY(gfx::Size(3, 3), gfx::Size(1, 1), true, 0));
+ EXPECT_EQ(0, PosY(gfx::Size(3, 3), gfx::Size(1, 1), true, 0));
+ EXPECT_EQ(1, SizeY(gfx::Size(3, 3), gfx::Size(100, 1), true, 0));
+ EXPECT_EQ(0, PosY(gfx::Size(3, 3), gfx::Size(100, 1), true, 0));
+ EXPECT_EQ(3, SizeY(gfx::Size(3, 3), gfx::Size(1, 3), true, 0));
+ EXPECT_EQ(0, PosY(gfx::Size(3, 3), gfx::Size(1, 3), true, 0));
+ EXPECT_EQ(3, SizeY(gfx::Size(3, 3), gfx::Size(100, 3), true, 0));
+ EXPECT_EQ(0, PosY(gfx::Size(3, 3), gfx::Size(100, 3), true, 0));
+
+ // Multiple tiles:
+ // no border
+ // positions 0, 3
+ EXPECT_EQ(2, NumTiles(gfx::Size(3, 3), gfx::Size(1, 6), false));
+ EXPECT_EQ(3, SizeY(gfx::Size(3, 3), gfx::Size(1, 6), false, 0));
+ EXPECT_EQ(3, SizeY(gfx::Size(3, 3), gfx::Size(1, 6), false, 1));
+ EXPECT_EQ(0, PosY(gfx::Size(3, 3), gfx::Size(1, 6), false, 0));
+ EXPECT_EQ(3, PosY(gfx::Size(3, 3), gfx::Size(1, 6), false, 1));
+ EXPECT_EQ(3, SizeY(gfx::Size(3, 3), gfx::Size(100, 6), false, 0));
+ EXPECT_EQ(3, SizeY(gfx::Size(3, 3), gfx::Size(100, 6), false, 1));
+ EXPECT_EQ(0, PosY(gfx::Size(3, 3), gfx::Size(100, 6), false, 0));
+ EXPECT_EQ(3, PosY(gfx::Size(3, 3), gfx::Size(100, 6), false, 1));
+
+ // Multiple tiles:
+ // with border
+ // positions 0, 2, 3, 4
+ EXPECT_EQ(4, NumTiles(gfx::Size(3, 3), gfx::Size(1, 6), true));
+ EXPECT_EQ(2, SizeY(gfx::Size(3, 3), gfx::Size(1, 6), true, 0));
+ EXPECT_EQ(1, SizeY(gfx::Size(3, 3), gfx::Size(1, 6), true, 1));
+ EXPECT_EQ(1, SizeY(gfx::Size(3, 3), gfx::Size(1, 6), true, 2));
+ EXPECT_EQ(2, SizeY(gfx::Size(3, 3), gfx::Size(1, 6), true, 3));
+ EXPECT_EQ(0, PosY(gfx::Size(3, 3), gfx::Size(1, 6), true, 0));
+ EXPECT_EQ(2, PosY(gfx::Size(3, 3), gfx::Size(1, 6), true, 1));
+ EXPECT_EQ(3, PosY(gfx::Size(3, 3), gfx::Size(1, 6), true, 2));
+ EXPECT_EQ(4, PosY(gfx::Size(3, 3), gfx::Size(1, 6), true, 3));
+ EXPECT_EQ(2, SizeY(gfx::Size(7, 3), gfx::Size(100, 6), true, 0));
+ EXPECT_EQ(1, SizeY(gfx::Size(7, 3), gfx::Size(100, 6), true, 1));
+ EXPECT_EQ(1, SizeY(gfx::Size(7, 3), gfx::Size(100, 6), true, 2));
+ EXPECT_EQ(2, SizeY(gfx::Size(7, 3), gfx::Size(100, 6), true, 3));
+ EXPECT_EQ(0, PosY(gfx::Size(7, 3), gfx::Size(100, 6), true, 0));
+ EXPECT_EQ(2, PosY(gfx::Size(7, 3), gfx::Size(100, 6), true, 1));
+ EXPECT_EQ(3, PosY(gfx::Size(7, 3), gfx::Size(100, 6), true, 2));
+ EXPECT_EQ(4, PosY(gfx::Size(7, 3), gfx::Size(100, 6), true, 3));
+}
+
+TEST(TilingDataTest, SetTotalSize) {
+ TilingData data(gfx::Size(5, 5), gfx::Size(5, 5), false);
+ EXPECT_EQ(5, data.tiling_size().width());
+ EXPECT_EQ(5, data.tiling_size().height());
+ EXPECT_EQ(1, data.num_tiles_x());
+ EXPECT_EQ(5, data.TileSizeX(0));
+ EXPECT_EQ(1, data.num_tiles_y());
+ EXPECT_EQ(5, data.TileSizeY(0));
+
+ data.SetTilingSize(gfx::Size(6, 5));
+ EXPECT_EQ(6, data.tiling_size().width());
+ EXPECT_EQ(5, data.tiling_size().height());
+ EXPECT_EQ(2, data.num_tiles_x());
+ EXPECT_EQ(5, data.TileSizeX(0));
+ EXPECT_EQ(1, data.TileSizeX(1));
+ EXPECT_EQ(1, data.num_tiles_y());
+ EXPECT_EQ(5, data.TileSizeY(0));
+
+ data.SetTilingSize(gfx::Size(5, 12));
+ EXPECT_EQ(5, data.tiling_size().width());
+ EXPECT_EQ(12, data.tiling_size().height());
+ EXPECT_EQ(1, data.num_tiles_x());
+ EXPECT_EQ(5, data.TileSizeX(0));
+ EXPECT_EQ(3, data.num_tiles_y());
+ EXPECT_EQ(5, data.TileSizeY(0));
+ EXPECT_EQ(5, data.TileSizeY(1));
+ EXPECT_EQ(2, data.TileSizeY(2));
+}
+
+TEST(TilingDataTest, SetMaxTextureSizeNoBorders) {
+ TilingData data(gfx::Size(8, 8), gfx::Size(16, 32), false);
+ EXPECT_EQ(2, data.num_tiles_x());
+ EXPECT_EQ(4, data.num_tiles_y());
+
+ data.SetMaxTextureSize(gfx::Size(32, 32));
+ EXPECT_EQ(gfx::Size(32, 32), data.max_texture_size());
+ EXPECT_EQ(1, data.num_tiles_x());
+ EXPECT_EQ(1, data.num_tiles_y());
+
+ data.SetMaxTextureSize(gfx::Size(2, 2));
+ EXPECT_EQ(gfx::Size(2, 2), data.max_texture_size());
+ EXPECT_EQ(8, data.num_tiles_x());
+ EXPECT_EQ(16, data.num_tiles_y());
+
+ data.SetMaxTextureSize(gfx::Size(5, 5));
+ EXPECT_EQ(gfx::Size(5, 5), data.max_texture_size());
+ EXPECT_EQ(4, data.num_tiles_x());
+ EXPECT_EQ(7, data.num_tiles_y());
+
+ data.SetMaxTextureSize(gfx::Size(8, 5));
+ EXPECT_EQ(gfx::Size(8, 5), data.max_texture_size());
+ EXPECT_EQ(2, data.num_tiles_x());
+ EXPECT_EQ(7, data.num_tiles_y());
+}
+
+TEST(TilingDataTest, SetMaxTextureSizeBorders) {
+ TilingData data(gfx::Size(8, 8), gfx::Size(16, 32), true);
+ EXPECT_EQ(3, data.num_tiles_x());
+ EXPECT_EQ(5, data.num_tiles_y());
+
+ data.SetMaxTextureSize(gfx::Size(32, 32));
+ EXPECT_EQ(gfx::Size(32, 32), data.max_texture_size());
+ EXPECT_EQ(1, data.num_tiles_x());
+ EXPECT_EQ(1, data.num_tiles_y());
+
+ data.SetMaxTextureSize(gfx::Size(2, 2));
+ EXPECT_EQ(gfx::Size(2, 2), data.max_texture_size());
+ EXPECT_EQ(0, data.num_tiles_x());
+ EXPECT_EQ(0, data.num_tiles_y());
+
+ data.SetMaxTextureSize(gfx::Size(5, 5));
+ EXPECT_EQ(gfx::Size(5, 5), data.max_texture_size());
+ EXPECT_EQ(5, data.num_tiles_x());
+ EXPECT_EQ(10, data.num_tiles_y());
+
+ data.SetMaxTextureSize(gfx::Size(8, 5));
+ EXPECT_EQ(gfx::Size(8, 5), data.max_texture_size());
+ EXPECT_EQ(3, data.num_tiles_x());
+ EXPECT_EQ(10, data.num_tiles_y());
+}
+
+TEST(TilingDataTest, ExpandRectIgnoringBordersToTileBoundsEmpty) {
+ TilingData empty_total_size(gfx::Size(0, 0), gfx::Size(8, 8), true);
+ EXPECT_RECT_EQ(
+ gfx::Rect(),
+ empty_total_size.ExpandRectIgnoringBordersToTileBounds(gfx::Rect()));
+ EXPECT_RECT_EQ(gfx::Rect(),
+ empty_total_size.ExpandRectIgnoringBordersToTileBounds(
+ gfx::Rect(100, 100, 100, 100)));
+ EXPECT_RECT_EQ(gfx::Rect(),
+ empty_total_size.ExpandRectIgnoringBordersToTileBounds(
+ gfx::Rect(100, 100)));
+
+ TilingData empty_max_texture_size(gfx::Size(8, 8), gfx::Size(0, 0), true);
+ EXPECT_RECT_EQ(gfx::Rect(),
+ empty_max_texture_size.ExpandRectIgnoringBordersToTileBounds(
+ gfx::Rect()));
+ EXPECT_RECT_EQ(gfx::Rect(),
+ empty_max_texture_size.ExpandRectIgnoringBordersToTileBounds(
+ gfx::Rect(100, 100, 100, 100)));
+ EXPECT_RECT_EQ(gfx::Rect(),
+ empty_max_texture_size.ExpandRectIgnoringBordersToTileBounds(
+ gfx::Rect(100, 100)));
+}
+
+TEST(TilingDataTest, ExpandRectIgnoringBordersToTileBounds) {
+ TilingData data(gfx::Size(4, 4), gfx::Size(16, 32), true);
+
+ // Small rect at origin rounds up to tile 0, 0.
+ gfx::Rect at_origin_src(1, 1);
+ gfx::Rect at_origin_result(data.TileBounds(0, 0));
+ EXPECT_NE(at_origin_src, at_origin_result);
+ EXPECT_RECT_EQ(at_origin_result,
+ data.ExpandRectIgnoringBordersToTileBounds(at_origin_src));
+
+ // Arbitrary internal rect.
+ gfx::Rect rect_src(6, 6, 1, 3);
+ // Tile 2, 2 => gfx::Rect(4, 4, 4, 4)
+ // Tile 2, 3 => gfx::Rect(4, 6, 4, 4)
+ gfx::Rect rect_result(
+ gfx::UnionRects(data.TileBounds(2, 2), data.TileBounds(2, 3)));
+ EXPECT_NE(rect_src, rect_result);
+ EXPECT_RECT_EQ(rect_result,
+ data.ExpandRectIgnoringBordersToTileBounds(rect_src));
+
+ // On tile bounds does not round up to next tile (ignores the border).
+ gfx::Rect border_rect_src(
+ gfx::UnionRects(data.TileBounds(1, 2), data.TileBounds(3, 4)));
+ gfx::Rect border_rect_result(
+ gfx::UnionRects(data.TileBounds(1, 2), data.TileBounds(3, 4)));
+ EXPECT_RECT_EQ(border_rect_result,
+ data.ExpandRectIgnoringBordersToTileBounds(border_rect_src));
+
+ // Equal to tiling rect.
+ EXPECT_RECT_EQ(gfx::Rect(data.tiling_size()),
+ data.ExpandRectIgnoringBordersToTileBounds(
+ gfx::Rect(data.tiling_size())));
+
+ // Containing, but larger than tiling rect.
+ EXPECT_RECT_EQ(
+ gfx::Rect(data.tiling_size()),
+ data.ExpandRectIgnoringBordersToTileBounds(gfx::Rect(100, 100)));
+
+ // Non-intersecting with tiling rect.
+ gfx::Rect non_intersect(200, 200, 100, 100);
+ EXPECT_FALSE(non_intersect.Intersects(gfx::Rect(data.tiling_size())));
+ EXPECT_RECT_EQ(gfx::Rect(),
+ data.ExpandRectIgnoringBordersToTileBounds(non_intersect));
+
+ TilingData data2(gfx::Size(8, 8), gfx::Size(32, 64), true);
+
+ // Inside other tile border texels doesn't include other tiles.
+ gfx::Rect inner_rect_src(data2.TileBounds(1, 1));
+ inner_rect_src.Inset(data2.border_texels(), data.border_texels());
+ gfx::Rect inner_rect_result(data2.TileBounds(1, 1));
+ gfx::Rect expanded =
+ data2.ExpandRectIgnoringBordersToTileBounds(inner_rect_src);
+ EXPECT_EQ(inner_rect_result.ToString(), expanded.ToString());
+}
+
+TEST(TilingDataTest, ExpandRectToTileBounds) {
+ TilingData data(gfx::Size(4, 4), gfx::Size(16, 32), true);
+
+ // Small rect at origin rounds up to tile 0, 0.
+ gfx::Rect at_origin_src(1, 1);
+ gfx::Rect at_origin_result(data.TileBounds(0, 0));
+ EXPECT_NE(at_origin_src, at_origin_result);
+ EXPECT_RECT_EQ(at_origin_result, data.ExpandRectToTileBounds(at_origin_src));
+
+ // Arbitrary internal rect.
+ gfx::Rect rect_src(6, 6, 1, 3);
+ // Tile 2, 2 => gfx::Rect(4, 4, 4, 4)
+ // Tile 3, 4 => gfx::Rect(6, 8, 4, 4)
+ gfx::Rect rect_result(
+ gfx::UnionRects(data.TileBounds(2, 2), data.TileBounds(3, 4)));
+ EXPECT_NE(rect_src, rect_result);
+ EXPECT_RECT_EQ(rect_result, data.ExpandRectToTileBounds(rect_src));
+
+ // On tile bounds rounds up to next tile (since border overlaps).
+ gfx::Rect border_rect_src(
+ gfx::UnionRects(data.TileBounds(1, 2), data.TileBounds(3, 4)));
+ gfx::Rect border_rect_result(
+ gfx::UnionRects(data.TileBounds(0, 1), data.TileBounds(4, 5)));
+ EXPECT_RECT_EQ(border_rect_result,
+ data.ExpandRectToTileBounds(border_rect_src));
+
+ // Equal to tiling rect.
+ EXPECT_RECT_EQ(gfx::Rect(data.tiling_size()),
+ data.ExpandRectToTileBounds(gfx::Rect(data.tiling_size())));
+
+ // Containing, but larger than tiling rect.
+ EXPECT_RECT_EQ(gfx::Rect(data.tiling_size()),
+ data.ExpandRectToTileBounds(gfx::Rect(100, 100)));
+
+ // Non-intersecting with tiling rect.
+ gfx::Rect non_intersect(200, 200, 100, 100);
+ EXPECT_FALSE(non_intersect.Intersects(gfx::Rect(data.tiling_size())));
+ EXPECT_RECT_EQ(gfx::Rect(), data.ExpandRectToTileBounds(non_intersect));
+
+ TilingData data2(gfx::Size(8, 8), gfx::Size(32, 64), true);
+
+ // Inside other tile border texels doesn't include other tiles.
+ gfx::Rect inner_rect_src(data2.TileBounds(1, 1));
+ inner_rect_src.Inset(data2.border_texels(), data.border_texels());
+ gfx::Rect inner_rect_result(data2.TileBounds(1, 1));
+ gfx::Rect expanded = data2.ExpandRectToTileBounds(inner_rect_src);
+ EXPECT_EQ(inner_rect_result.ToString(), expanded.ToString());
+}
+
+TEST(TilingDataTest, Assignment) {
+ {
+ TilingData source(gfx::Size(8, 8), gfx::Size(16, 32), true);
+ TilingData dest = source;
+ EXPECT_EQ(source.border_texels(), dest.border_texels());
+ EXPECT_EQ(source.max_texture_size(), dest.max_texture_size());
+ EXPECT_EQ(source.num_tiles_x(), dest.num_tiles_x());
+ EXPECT_EQ(source.num_tiles_y(), dest.num_tiles_y());
+ EXPECT_EQ(source.tiling_size(), dest.tiling_size());
+ }
+ {
+ TilingData source(gfx::Size(7, 3), gfx::Size(6, 100), false);
+ TilingData dest(source);
+ EXPECT_EQ(source.border_texels(), dest.border_texels());
+ EXPECT_EQ(source.max_texture_size(), dest.max_texture_size());
+ EXPECT_EQ(source.num_tiles_x(), dest.num_tiles_x());
+ EXPECT_EQ(source.num_tiles_y(), dest.num_tiles_y());
+ EXPECT_EQ(source.tiling_size(), dest.tiling_size());
+ }
+}
+
+TEST(TilingDataTest, SetBorderTexels) {
+ TilingData data(gfx::Size(8, 8), gfx::Size(16, 32), false);
+ EXPECT_EQ(2, data.num_tiles_x());
+ EXPECT_EQ(4, data.num_tiles_y());
+
+ data.SetHasBorderTexels(true);
+ EXPECT_EQ(3, data.num_tiles_x());
+ EXPECT_EQ(5, data.num_tiles_y());
+
+ data.SetHasBorderTexels(false);
+ EXPECT_EQ(2, data.num_tiles_x());
+ EXPECT_EQ(4, data.num_tiles_y());
+}
+
+TEST(TilingDataTest, LargeBorders) {
+ TilingData data(gfx::Size(100, 80), gfx::Size(200, 145), 30);
+ EXPECT_EQ(30, data.border_texels());
+
+ EXPECT_EQ(70, data.TileSizeX(0));
+ EXPECT_EQ(40, data.TileSizeX(1));
+ EXPECT_EQ(40, data.TileSizeX(2));
+ EXPECT_EQ(50, data.TileSizeX(3));
+ EXPECT_EQ(4, data.num_tiles_x());
+
+ EXPECT_EQ(50, data.TileSizeY(0));
+ EXPECT_EQ(20, data.TileSizeY(1));
+ EXPECT_EQ(20, data.TileSizeY(2));
+ EXPECT_EQ(20, data.TileSizeY(3));
+ EXPECT_EQ(35, data.TileSizeY(4));
+ EXPECT_EQ(5, data.num_tiles_y());
+
+ EXPECT_RECT_EQ(gfx::Rect(70, 50), data.TileBounds(0, 0));
+ EXPECT_RECT_EQ(gfx::Rect(70, 50, 40, 20), data.TileBounds(1, 1));
+ EXPECT_RECT_EQ(gfx::Rect(110, 110, 40, 35), data.TileBounds(2, 4));
+ EXPECT_RECT_EQ(gfx::Rect(150, 70, 50, 20), data.TileBounds(3, 2));
+ EXPECT_RECT_EQ(gfx::Rect(150, 110, 50, 35), data.TileBounds(3, 4));
+
+ EXPECT_RECT_EQ(gfx::Rect(100, 80), data.TileBoundsWithBorder(0, 0));
+ EXPECT_RECT_EQ(gfx::Rect(40, 20, 100, 80), data.TileBoundsWithBorder(1, 1));
+ EXPECT_RECT_EQ(gfx::Rect(80, 80, 100, 65), data.TileBoundsWithBorder(2, 4));
+ EXPECT_RECT_EQ(gfx::Rect(120, 40, 80, 80), data.TileBoundsWithBorder(3, 2));
+ EXPECT_RECT_EQ(gfx::Rect(120, 80, 80, 65), data.TileBoundsWithBorder(3, 4));
+
+ EXPECT_EQ(0, data.TileXIndexFromSrcCoord(0));
+ EXPECT_EQ(0, data.TileXIndexFromSrcCoord(69));
+ EXPECT_EQ(1, data.TileXIndexFromSrcCoord(70));
+ EXPECT_EQ(1, data.TileXIndexFromSrcCoord(109));
+ EXPECT_EQ(2, data.TileXIndexFromSrcCoord(110));
+ EXPECT_EQ(2, data.TileXIndexFromSrcCoord(149));
+ EXPECT_EQ(3, data.TileXIndexFromSrcCoord(150));
+ EXPECT_EQ(3, data.TileXIndexFromSrcCoord(199));
+
+ EXPECT_EQ(0, data.TileYIndexFromSrcCoord(0));
+ EXPECT_EQ(0, data.TileYIndexFromSrcCoord(49));
+ EXPECT_EQ(1, data.TileYIndexFromSrcCoord(50));
+ EXPECT_EQ(1, data.TileYIndexFromSrcCoord(69));
+ EXPECT_EQ(2, data.TileYIndexFromSrcCoord(70));
+ EXPECT_EQ(2, data.TileYIndexFromSrcCoord(89));
+ EXPECT_EQ(3, data.TileYIndexFromSrcCoord(90));
+ EXPECT_EQ(3, data.TileYIndexFromSrcCoord(109));
+ EXPECT_EQ(4, data.TileYIndexFromSrcCoord(110));
+ EXPECT_EQ(4, data.TileYIndexFromSrcCoord(144));
+
+ EXPECT_EQ(0, data.FirstBorderTileXIndexFromSrcCoord(0));
+ EXPECT_EQ(0, data.FirstBorderTileXIndexFromSrcCoord(99));
+ EXPECT_EQ(1, data.FirstBorderTileXIndexFromSrcCoord(100));
+ EXPECT_EQ(1, data.FirstBorderTileXIndexFromSrcCoord(139));
+ EXPECT_EQ(2, data.FirstBorderTileXIndexFromSrcCoord(140));
+ EXPECT_EQ(2, data.FirstBorderTileXIndexFromSrcCoord(179));
+ EXPECT_EQ(3, data.FirstBorderTileXIndexFromSrcCoord(180));
+ EXPECT_EQ(3, data.FirstBorderTileXIndexFromSrcCoord(199));
+
+ EXPECT_EQ(0, data.FirstBorderTileYIndexFromSrcCoord(0));
+ EXPECT_EQ(0, data.FirstBorderTileYIndexFromSrcCoord(79));
+ EXPECT_EQ(1, data.FirstBorderTileYIndexFromSrcCoord(80));
+ EXPECT_EQ(1, data.FirstBorderTileYIndexFromSrcCoord(99));
+ EXPECT_EQ(2, data.FirstBorderTileYIndexFromSrcCoord(100));
+ EXPECT_EQ(2, data.FirstBorderTileYIndexFromSrcCoord(119));
+ EXPECT_EQ(3, data.FirstBorderTileYIndexFromSrcCoord(120));
+ EXPECT_EQ(3, data.FirstBorderTileYIndexFromSrcCoord(139));
+ EXPECT_EQ(4, data.FirstBorderTileYIndexFromSrcCoord(140));
+ EXPECT_EQ(4, data.FirstBorderTileYIndexFromSrcCoord(144));
+
+ EXPECT_EQ(0, data.LastBorderTileXIndexFromSrcCoord(0));
+ EXPECT_EQ(0, data.LastBorderTileXIndexFromSrcCoord(39));
+ EXPECT_EQ(1, data.LastBorderTileXIndexFromSrcCoord(40));
+ EXPECT_EQ(1, data.LastBorderTileXIndexFromSrcCoord(79));
+ EXPECT_EQ(2, data.LastBorderTileXIndexFromSrcCoord(80));
+ EXPECT_EQ(2, data.LastBorderTileXIndexFromSrcCoord(119));
+ EXPECT_EQ(3, data.LastBorderTileXIndexFromSrcCoord(120));
+ EXPECT_EQ(3, data.LastBorderTileXIndexFromSrcCoord(199));
+
+ EXPECT_EQ(0, data.LastBorderTileYIndexFromSrcCoord(0));
+ EXPECT_EQ(0, data.LastBorderTileYIndexFromSrcCoord(19));
+ EXPECT_EQ(1, data.LastBorderTileYIndexFromSrcCoord(20));
+ EXPECT_EQ(1, data.LastBorderTileYIndexFromSrcCoord(39));
+ EXPECT_EQ(2, data.LastBorderTileYIndexFromSrcCoord(40));
+ EXPECT_EQ(2, data.LastBorderTileYIndexFromSrcCoord(59));
+ EXPECT_EQ(3, data.LastBorderTileYIndexFromSrcCoord(60));
+ EXPECT_EQ(3, data.LastBorderTileYIndexFromSrcCoord(79));
+ EXPECT_EQ(4, data.LastBorderTileYIndexFromSrcCoord(80));
+ EXPECT_EQ(4, data.LastBorderTileYIndexFromSrcCoord(144));
+}
+
+void TestIterate(const TilingData& data,
+ gfx::Rect rect,
+ int expect_left,
+ int expect_top,
+ int expect_right,
+ int expect_bottom,
+ bool include_borders) {
+ EXPECT_GE(expect_left, 0);
+ EXPECT_GE(expect_top, 0);
+ EXPECT_LT(expect_right, data.num_tiles_x());
+ EXPECT_LT(expect_bottom, data.num_tiles_y());
+
+ std::vector<std::pair<int, int> > original_expected;
+ for (int x = 0; x < data.num_tiles_x(); ++x) {
+ for (int y = 0; y < data.num_tiles_y(); ++y) {
+ gfx::Rect bounds;
+ if (include_borders)
+ bounds = data.TileBoundsWithBorder(x, y);
+ else
+ bounds = data.TileBounds(x, y);
+ if (x >= expect_left && x <= expect_right &&
+ y >= expect_top && y <= expect_bottom) {
+ EXPECT_TRUE(bounds.Intersects(rect));
+ original_expected.push_back(std::make_pair(x, y));
+ } else {
+ EXPECT_FALSE(bounds.Intersects(rect));
+ }
+ }
+ }
+
+ // Verify with vanilla iterator.
+ {
+ std::vector<std::pair<int, int> > expected = original_expected;
+ for (TilingData::Iterator iter(&data, rect, include_borders); iter;
+ ++iter) {
+ bool found = false;
+ for (size_t i = 0; i < expected.size(); ++i) {
+ if (expected[i] == iter.index()) {
+ expected[i] = expected.back();
+ expected.pop_back();
+ found = true;
+ break;
+ }
+ }
+ EXPECT_TRUE(found);
+ }
+ EXPECT_EQ(0u, expected.size());
+ }
+
+ // Make sure this also works with a difference iterator and an empty ignore.
+ // The difference iterator never includes borders, so ignore it otherwise.
+ if (!include_borders) {
+ std::vector<std::pair<int, int> > expected = original_expected;
+ for (TilingData::DifferenceIterator iter(&data, rect, gfx::Rect()); iter;
+ ++iter) {
+ bool found = false;
+ for (size_t i = 0; i < expected.size(); ++i) {
+ if (expected[i] == iter.index()) {
+ expected[i] = expected.back();
+ expected.pop_back();
+ found = true;
+ break;
+ }
+ }
+ EXPECT_TRUE(found);
+ }
+ EXPECT_EQ(0u, expected.size());
+ }
+}
+
+void TestIterateBorders(const TilingData& data,
+ gfx::Rect rect,
+ int expect_left,
+ int expect_top,
+ int expect_right,
+ int expect_bottom) {
+ bool include_borders = true;
+ TestIterate(data,
+ rect,
+ expect_left,
+ expect_top,
+ expect_right,
+ expect_bottom,
+ include_borders);
+}
+
+void TestIterateNoBorders(const TilingData& data,
+ gfx::Rect rect,
+ int expect_left,
+ int expect_top,
+ int expect_right,
+ int expect_bottom) {
+ bool include_borders = false;
+ TestIterate(data,
+ rect,
+ expect_left,
+ expect_top,
+ expect_right,
+ expect_bottom,
+ include_borders);
+}
+
+void TestIterateAll(const TilingData& data,
+ gfx::Rect rect,
+ int expect_left,
+ int expect_top,
+ int expect_right,
+ int expect_bottom) {
+ TestIterateBorders(
+ data, rect, expect_left, expect_top, expect_right, expect_bottom);
+ TestIterateNoBorders(
+ data, rect, expect_left, expect_top, expect_right, expect_bottom);
+}
+
+TEST(TilingDataTest, IteratorNoBorderTexels) {
+ TilingData data(gfx::Size(10, 10), gfx::Size(40, 25), false);
+ // X border index by src coord: [0-10), [10-20), [20, 30), [30, 40)
+ // Y border index by src coord: [0-10), [10-20), [20, 25)
+ TestIterateAll(data, gfx::Rect(40, 25), 0, 0, 3, 2);
+ TestIterateAll(data, gfx::Rect(15, 15, 8, 8), 1, 1, 2, 2);
+
+ // Oversized.
+ TestIterateAll(data, gfx::Rect(-100, -100, 1000, 1000), 0, 0, 3, 2);
+ TestIterateAll(data, gfx::Rect(-100, 20, 1000, 1), 0, 2, 3, 2);
+ TestIterateAll(data, gfx::Rect(29, -100, 31, 1000), 2, 0, 3, 2);
+ // Nonintersecting.
+ TestIterateAll(data, gfx::Rect(60, 80, 100, 100), 0, 0, -1, -1);
+}
+
+TEST(TilingDataTest, BordersIteratorOneBorderTexel) {
+ TilingData data(gfx::Size(10, 20), gfx::Size(25, 45), true);
+ // X border index by src coord: [0-10), [8-18), [16-25)
+ // Y border index by src coord: [0-20), [18-38), [36-45)
+ TestIterateBorders(data, gfx::Rect(25, 45), 0, 0, 2, 2);
+ TestIterateBorders(data, gfx::Rect(18, 19, 3, 17), 2, 0, 2, 1);
+ TestIterateBorders(data, gfx::Rect(10, 20, 6, 16), 1, 1, 1, 1);
+ TestIterateBorders(data, gfx::Rect(9, 19, 8, 18), 0, 0, 2, 2);
+ // Oversized.
+ TestIterateBorders(data, gfx::Rect(-100, -100, 1000, 1000), 0, 0, 2, 2);
+ TestIterateBorders(data, gfx::Rect(-100, 20, 1000, 1), 0, 1, 2, 1);
+ TestIterateBorders(data, gfx::Rect(18, -100, 6, 1000), 2, 0, 2, 2);
+ // Nonintersecting.
+ TestIterateBorders(data, gfx::Rect(60, 80, 100, 100), 0, 0, -1, -1);
+}
+
+TEST(TilingDataTest, NoBordersIteratorOneBorderTexel) {
+ TilingData data(gfx::Size(10, 20), gfx::Size(25, 45), true);
+ // X index by src coord: [0-9), [9-17), [17-25)
+ // Y index by src coord: [0-19), [19-37), [37-45)
+ TestIterateNoBorders(data, gfx::Rect(25, 45), 0, 0, 2, 2);
+ TestIterateNoBorders(data, gfx::Rect(17, 19, 3, 18), 2, 1, 2, 1);
+ TestIterateNoBorders(data, gfx::Rect(17, 19, 3, 19), 2, 1, 2, 2);
+ TestIterateNoBorders(data, gfx::Rect(8, 18, 9, 19), 0, 0, 1, 1);
+ TestIterateNoBorders(data, gfx::Rect(9, 19, 9, 19), 1, 1, 2, 2);
+ // Oversized.
+ TestIterateNoBorders(data, gfx::Rect(-100, -100, 1000, 1000), 0, 0, 2, 2);
+ TestIterateNoBorders(data, gfx::Rect(-100, 20, 1000, 1), 0, 1, 2, 1);
+ TestIterateNoBorders(data, gfx::Rect(18, -100, 6, 1000), 2, 0, 2, 2);
+ // Nonintersecting.
+ TestIterateNoBorders(data, gfx::Rect(60, 80, 100, 100), 0, 0, -1, -1);
+}
+
+TEST(TilingDataTest, BordersIteratorManyBorderTexels) {
+ TilingData data(gfx::Size(50, 60), gfx::Size(65, 110), 20);
+ // X border index by src coord: [0-50), [10-60), [20-65)
+ // Y border index by src coord: [0-60), [20-80), [40-100), [60-110)
+ TestIterateBorders(data, gfx::Rect(65, 110), 0, 0, 2, 3);
+ TestIterateBorders(data, gfx::Rect(50, 60, 15, 65), 1, 1, 2, 3);
+ TestIterateBorders(data, gfx::Rect(60, 30, 2, 10), 2, 0, 2, 1);
+ // Oversized.
+ TestIterateBorders(data, gfx::Rect(-100, -100, 1000, 1000), 0, 0, 2, 3);
+ TestIterateBorders(data, gfx::Rect(-100, 10, 1000, 10), 0, 0, 2, 0);
+ TestIterateBorders(data, gfx::Rect(10, -100, 10, 1000), 0, 0, 1, 3);
+ // Nonintersecting.
+ TestIterateBorders(data, gfx::Rect(65, 110, 100, 100), 0, 0, -1, -1);
+}
+
+TEST(TilingDataTest, NoBordersIteratorManyBorderTexels) {
+ TilingData data(gfx::Size(50, 60), gfx::Size(65, 110), 20);
+ // X index by src coord: [0-30), [30-40), [40, 65)
+ // Y index by src coord: [0-40), [40-60), [60, 80), [80-110)
+ TestIterateNoBorders(data, gfx::Rect(65, 110), 0, 0, 2, 3);
+ TestIterateNoBorders(data, gfx::Rect(30, 40, 15, 65), 1, 1, 2, 3);
+ TestIterateNoBorders(data, gfx::Rect(60, 20, 2, 21), 2, 0, 2, 1);
+ // Oversized.
+ TestIterateNoBorders(data, gfx::Rect(-100, -100, 1000, 1000), 0, 0, 2, 3);
+ TestIterateNoBorders(data, gfx::Rect(-100, 10, 1000, 10), 0, 0, 2, 0);
+ TestIterateNoBorders(data, gfx::Rect(10, -100, 10, 1000), 0, 0, 0, 3);
+ // Nonintersecting.
+ TestIterateNoBorders(data, gfx::Rect(65, 110, 100, 100), 0, 0, -1, -1);
+}
+
+TEST(TilingDataTest, IteratorOneTile) {
+ TilingData no_border(gfx::Size(1000, 1000), gfx::Size(30, 40), false);
+ TestIterateAll(no_border, gfx::Rect(30, 40), 0, 0, 0, 0);
+ TestIterateAll(no_border, gfx::Rect(10, 10, 20, 20), 0, 0, 0, 0);
+ TestIterateAll(no_border, gfx::Rect(30, 40, 100, 100), 0, 0, -1, -1);
+
+ TilingData one_border(gfx::Size(1000, 1000), gfx::Size(30, 40), true);
+ TestIterateAll(one_border, gfx::Rect(30, 40), 0, 0, 0, 0);
+ TestIterateAll(one_border, gfx::Rect(10, 10, 20, 20), 0, 0, 0, 0);
+ TestIterateAll(one_border, gfx::Rect(30, 40, 100, 100), 0, 0, -1, -1);
+
+ TilingData big_border(gfx::Size(1000, 1000), gfx::Size(30, 40), 50);
+ TestIterateAll(big_border, gfx::Rect(30, 40), 0, 0, 0, 0);
+ TestIterateAll(big_border, gfx::Rect(10, 10, 20, 20), 0, 0, 0, 0);
+ TestIterateAll(big_border, gfx::Rect(30, 40, 100, 100), 0, 0, -1, -1);
+}
+
+TEST(TilingDataTest, IteratorNoTiles) {
+ TilingData data(gfx::Size(100, 100), gfx::Size(), false);
+ TestIterateAll(data, gfx::Rect(100, 100), 0, 0, -1, -1);
+}
+
+void TestDiff(const TilingData& data,
+ gfx::Rect consider,
+ gfx::Rect ignore,
+ size_t num_tiles) {
+ std::vector<std::pair<int, int> > expected;
+ for (int y = 0; y < data.num_tiles_y(); ++y) {
+ for (int x = 0; x < data.num_tiles_x(); ++x) {
+ gfx::Rect bounds = data.TileBounds(x, y);
+ if (bounds.Intersects(consider) && !bounds.Intersects(ignore))
+ expected.push_back(std::make_pair(x, y));
+ }
+ }
+
+ // Sanity check the test.
+ EXPECT_EQ(num_tiles, expected.size());
+
+ for (TilingData::DifferenceIterator iter(&data, consider, ignore); iter;
+ ++iter) {
+ bool found = false;
+ for (size_t i = 0; i < expected.size(); ++i) {
+ if (expected[i] == iter.index()) {
+ expected[i] = expected.back();
+ expected.pop_back();
+ found = true;
+ break;
+ }
+ }
+ EXPECT_TRUE(found);
+ }
+ EXPECT_EQ(0u, expected.size());
+}
+
+TEST(TilingDataTest, DifferenceIteratorIgnoreGeometry) {
+ // This test is checking that the iterator can handle different geometries of
+ // ignore rects relative to the consider rect. The consider rect indices
+ // themselves are mostly tested by the non-difference iterator tests, so the
+ // full rect is mostly used here for simplicity.
+
+ // X border index by src coord: [0-10), [10-20), [20, 30), [30, 40)
+ // Y border index by src coord: [0-10), [10-20), [20, 25)
+ TilingData data(gfx::Size(10, 10), gfx::Size(40, 25), false);
+
+ // Fully ignored
+ TestDiff(data, gfx::Rect(40, 25), gfx::Rect(40, 25), 0);
+ TestDiff(data, gfx::Rect(40, 25), gfx::Rect(-100, -100, 200, 200), 0);
+ TestDiff(data, gfx::Rect(40, 25), gfx::Rect(9, 9, 30, 15), 0);
+ TestDiff(data, gfx::Rect(15, 15, 8, 8), gfx::Rect(15, 15, 8, 8), 0);
+
+ // Fully un-ignored
+ TestDiff(data, gfx::Rect(40, 25), gfx::Rect(-30, -20, 8, 8), 12);
+ TestDiff(data, gfx::Rect(40, 25), gfx::Rect(), 12);
+
+ // Top left, remove 2x2 tiles
+ TestDiff(data, gfx::Rect(40, 25), gfx::Rect(20, 19), 8);
+ // Bottom right, remove 2x2 tiles
+ TestDiff(data, gfx::Rect(40, 25), gfx::Rect(20, 15, 20, 6), 8);
+ // Bottom left, remove 2x2 tiles
+ TestDiff(data, gfx::Rect(40, 25), gfx::Rect(0, 15, 20, 6), 8);
+ // Top right, remove 2x2 tiles
+ TestDiff(data, gfx::Rect(40, 25), gfx::Rect(20, 0, 20, 19), 8);
+ // Center, remove only one tile
+ TestDiff(data, gfx::Rect(40, 25), gfx::Rect(10, 10, 5, 5), 11);
+
+ // Left column, flush left, removing two columns
+ TestDiff(data, gfx::Rect(40, 25), gfx::Rect(11, 25), 6);
+ // Middle column, removing two columns
+ TestDiff(data, gfx::Rect(40, 25), gfx::Rect(11, 0, 11, 25), 6);
+ // Right column, flush right, removing one column
+ TestDiff(data, gfx::Rect(40, 25), gfx::Rect(30, 0, 2, 25), 9);
+
+ // Top row, flush top, removing one row
+ TestDiff(data, gfx::Rect(40, 25), gfx::Rect(0, 5, 40, 5), 8);
+ // Middle row, removing one row
+ TestDiff(data, gfx::Rect(40, 25), gfx::Rect(0, 13, 40, 5), 8);
+ // Bottom row, flush bottom, removing two rows
+ TestDiff(data, gfx::Rect(40, 25), gfx::Rect(0, 13, 40, 12), 4);
+
+ // Non-intersecting, but still touching two of the same tiles.
+ TestDiff(data, gfx::Rect(8, 0, 32, 25), gfx::Rect(0, 12, 5, 12), 10);
+
+ // Intersecting, but neither contains the other. 2x3 with one overlap.
+ TestDiff(data, gfx::Rect(5, 2, 20, 10), gfx::Rect(25, 15, 5, 10), 5);
+}
+
+TEST(TilingDataTest, DifferenceIteratorManyBorderTexels) {
+ // X border index by src coord: [0-50), [10-60), [20-65)
+ // Y border index by src coord: [0-60), [20-80), [40-100), [60-110)
+ // X tile bounds by src coord: [0-30), [30-40), [40-65)
+ // Y tile bounds by src coord: [0-40), [40-60), [60-80), [80-110)
+ TilingData data(gfx::Size(50, 60), gfx::Size(65, 110), 20);
+
+ // Knock out two rows, but not the left column.
+ TestDiff(data, gfx::Rect(10, 30, 55, 80), gfx::Rect(30, 59, 20, 2), 8);
+
+ // Knock out one row.
+ TestDiff(data, gfx::Rect(10, 30, 55, 80), gfx::Rect(29, 59, 20, 1), 9);
+
+ // Overlap all tiles with ignore rect.
+ TestDiff(data, gfx::Rect(65, 110), gfx::Rect(29, 39, 12, 42), 0);
+
+ gfx::Rect tile = data.TileBounds(1, 1);
+
+ // Ignore one tile.
+ TestDiff(data, gfx::Rect(20, 30, 45, 80), tile, 11);
+
+ // Include one tile.
+ TestDiff(data, tile, gfx::Rect(), 1);
+}
+
+TEST(TilingDataTest, DifferenceIteratorOneTile) {
+ TilingData no_border(gfx::Size(1000, 1000), gfx::Size(30, 40), false);
+ TestDiff(no_border, gfx::Rect(30, 40), gfx::Rect(), 1);
+ TestDiff(no_border, gfx::Rect(5, 5, 100, 100), gfx::Rect(5, 5, 1, 1), 0);
+
+ TilingData one_border(gfx::Size(1000, 1000), gfx::Size(30, 40), true);
+ TestDiff(one_border, gfx::Rect(30, 40), gfx::Rect(), 1);
+ TestDiff(one_border, gfx::Rect(5, 5, 100, 100), gfx::Rect(5, 5, 1, 1), 0);
+
+ TilingData big_border(gfx::Size(1000, 1000), gfx::Size(30, 40), 50);
+ TestDiff(big_border, gfx::Rect(30, 40), gfx::Rect(), 1);
+ TestDiff(big_border, gfx::Rect(5, 5, 100, 100), gfx::Rect(5, 5, 1, 1), 0);
+}
+
+TEST(TilingDataTest, DifferenceIteratorNoTiles) {
+ TilingData data(gfx::Size(100, 100), gfx::Size(), false);
+ TestDiff(data, gfx::Rect(100, 100), gfx::Rect(5, 5), 0);
+}
+
+void TestSpiralIterate(int source_line_number,
+ const TilingData& tiling_data,
+ const gfx::Rect& consider,
+ const gfx::Rect& ignore,
+ const gfx::Rect& center,
+ const std::vector<std::pair<int, int> >& expected) {
+ std::vector<std::pair<int, int> > actual;
+ for (TilingData::SpiralDifferenceIterator it(
+ &tiling_data, consider, ignore, center);
+ it;
+ ++it) {
+ actual.push_back(it.index());
+ }
+
+ EXPECT_EQ(expected.size(), actual.size()) << "error from line "
+ << source_line_number;
+ for (size_t i = 0; i < std::min(expected.size(), actual.size()); ++i) {
+ EXPECT_EQ(expected[i].first, actual[i].first)
+ << "i: " << i << " error from line: " << source_line_number;
+ EXPECT_EQ(expected[i].second, actual[i].second)
+ << "i: " << i << " error from line: " << source_line_number;
+ }
+}
+
+TEST(TilingDataTest, SpiralDifferenceIteratorNoIgnoreFullConsider) {
+ TilingData tiling_data(gfx::Size(10, 10), gfx::Size(30, 30), false);
+ gfx::Rect consider(30, 30);
+ gfx::Rect ignore;
+ std::vector<std::pair<int, int> > expected;
+
+ // Center is in the center of the tiling.
+ gfx::Rect center(15, 15, 1, 1);
+
+ // Layout of the tiling data, and expected return order:
+ // x 0 1 2
+ // y.------
+ // 0| 4 3 2
+ // 1| 5 * 1
+ // 2| 6 7 8
+ expected.push_back(std::make_pair(2, 1));
+ expected.push_back(std::make_pair(2, 0));
+ expected.push_back(std::make_pair(1, 0));
+ expected.push_back(std::make_pair(0, 0));
+ expected.push_back(std::make_pair(0, 1));
+ expected.push_back(std::make_pair(0, 2));
+ expected.push_back(std::make_pair(1, 2));
+ expected.push_back(std::make_pair(2, 2));
+
+ TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+
+ // Center is off to the right side of the tiling (and far away).
+ center = gfx::Rect(100, 15, 1, 1);
+
+ // Layout of the tiling data, and expected return order:
+ // x 0 1 2
+ // y.------
+ // 0| 7 4 1
+ // 1| 8 5 2 *
+ // 2| 9 6 3
+ expected.clear();
+ expected.push_back(std::make_pair(2, 0));
+ expected.push_back(std::make_pair(2, 1));
+ expected.push_back(std::make_pair(2, 2));
+ expected.push_back(std::make_pair(1, 0));
+ expected.push_back(std::make_pair(1, 1));
+ expected.push_back(std::make_pair(1, 2));
+ expected.push_back(std::make_pair(0, 0));
+ expected.push_back(std::make_pair(0, 1));
+ expected.push_back(std::make_pair(0, 2));
+
+ TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+
+ // Center is the bottom right corner of the tiling.
+ center = gfx::Rect(25, 25, 1, 1);
+
+ // Layout of the tiling data, and expected return order:
+ // x 0 1 2
+ // y.------
+ // 0| 6 5 4
+ // 1| 7 2 1
+ // 2| 8 3 *
+ expected.clear();
+ expected.push_back(std::make_pair(2, 1));
+ expected.push_back(std::make_pair(1, 1));
+ expected.push_back(std::make_pair(1, 2));
+ expected.push_back(std::make_pair(2, 0));
+ expected.push_back(std::make_pair(1, 0));
+ expected.push_back(std::make_pair(0, 0));
+ expected.push_back(std::make_pair(0, 1));
+ expected.push_back(std::make_pair(0, 2));
+
+ TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+
+ // Center is off the top left side of the tiling.
+ center = gfx::Rect(-60, -50, 1, 1);
+
+ // Layout of the tiling data, and expected return order:
+ // * x 0 1 2
+ // y.------
+ // 0| 1 2 6
+ // 1| 3 4 5
+ // 2| 7 8 9
+ expected.clear();
+ expected.push_back(std::make_pair(0, 0));
+ expected.push_back(std::make_pair(1, 0));
+ expected.push_back(std::make_pair(0, 1));
+ expected.push_back(std::make_pair(1, 1));
+ expected.push_back(std::make_pair(2, 1));
+ expected.push_back(std::make_pair(2, 0));
+ expected.push_back(std::make_pair(0, 2));
+ expected.push_back(std::make_pair(1, 2));
+ expected.push_back(std::make_pair(2, 2));
+
+ TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+
+ // Two tile center.
+ center = gfx::Rect(15, 15, 1, 10);
+
+ // Layout of the tiling data, and expected return order:
+ // x 0 1 2
+ // y.------
+ // 0| 5 4 3
+ // 1| 6 * 2
+ // 2| 7 * 1
+ expected.clear();
+ expected.push_back(std::make_pair(2, 2));
+ expected.push_back(std::make_pair(2, 1));
+ expected.push_back(std::make_pair(2, 0));
+ expected.push_back(std::make_pair(1, 0));
+ expected.push_back(std::make_pair(0, 0));
+ expected.push_back(std::make_pair(0, 1));
+ expected.push_back(std::make_pair(0, 2));
+
+ TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+}
+
+TEST(TilingDataTest, SpiralDifferenceIteratorSmallConsider) {
+ TilingData tiling_data(gfx::Size(10, 10), gfx::Size(50, 50), false);
+ gfx::Rect ignore;
+ std::vector<std::pair<int, int> > expected;
+ gfx::Rect center(15, 15, 1, 1);
+
+ // Consider is one cell.
+ gfx::Rect consider(1, 1);
+
+ // Layout of the tiling data, and expected return order:
+ // x 0 1 2 3 4
+ // y.----------
+ // 0| 1
+ // 1| *
+ // 2|
+ // 3|
+ // 4|
+ expected.push_back(std::make_pair(0, 0));
+
+ TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+
+ // Consider is bottom right corner.
+ consider = gfx::Rect(25, 25, 10, 10);
+
+ // Layout of the tiling data, and expected return order:
+ // x 0 1 2 3 4
+ // y.----------
+ // 0|
+ // 1| *
+ // 2| 1 2
+ // 3| 3 4
+ // 4|
+ expected.clear();
+ expected.push_back(std::make_pair(2, 2));
+ expected.push_back(std::make_pair(3, 2));
+ expected.push_back(std::make_pair(2, 3));
+ expected.push_back(std::make_pair(3, 3));
+
+ TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+
+ // Consider is one column.
+ consider = gfx::Rect(11, 0, 1, 100);
+
+ // Layout of the tiling data, and expected return order:
+ // x 0 1 2 3 4
+ // y.----------
+ // 0| 2
+ // 1| *
+ // 2| 3
+ // 3| 4
+ // 4| 5
+ expected.clear();
+ expected.push_back(std::make_pair(1, 0));
+ expected.push_back(std::make_pair(1, 2));
+ expected.push_back(std::make_pair(1, 3));
+ expected.push_back(std::make_pair(1, 4));
+
+ TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+}
+
+TEST(TilingDataTest, SpiralDifferenceIteratorHasIgnore) {
+ TilingData tiling_data(gfx::Size(10, 10), gfx::Size(50, 50), false);
+ gfx::Rect consider(50, 50);
+ std::vector<std::pair<int, int> > expected;
+ gfx::Rect center(15, 15, 1, 1);
+
+ // Full ignore.
+ gfx::Rect ignore(50, 50);
+
+ // Layout of the tiling data, and expected return order:
+ // x 0 1 2 3 4
+ // y.----------
+ // 0| . . . . .
+ // 1| . * . . .
+ // 2| . . . . .
+ // 3| . . . . .
+ // 4| . . . . .
+ expected.clear();
+
+ TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+
+ // 3 column ignore.
+ ignore = gfx::Rect(15, 0, 20, 100);
+
+ // Layout of the tiling data, and expected return order:
+ // x 0 1 2 3 4
+ // y.----------
+ // 0| 1 . . . 8
+ // 1| 2 * . . 7
+ // 2| 3 . . . 6
+ // 3| 4 . . . 5
+ // 4| 9 . . . 10
+ expected.clear();
+
+ expected.push_back(std::make_pair(0, 0));
+ expected.push_back(std::make_pair(0, 1));
+ expected.push_back(std::make_pair(0, 2));
+ expected.push_back(std::make_pair(0, 3));
+ expected.push_back(std::make_pair(4, 3));
+ expected.push_back(std::make_pair(4, 2));
+ expected.push_back(std::make_pair(4, 1));
+ expected.push_back(std::make_pair(4, 0));
+ expected.push_back(std::make_pair(0, 4));
+ expected.push_back(std::make_pair(4, 4));
+
+ TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+
+ // Ignore covers the top half.
+ ignore = gfx::Rect(50, 25);
+
+ // Layout of the tiling data, and expected return order:
+ // x 0 1 2 3 4
+ // y.----------
+ // 0| . . . . .
+ // 1| . * . . .
+ // 2| . . . . .
+ // 3| 1 2 3 4 5
+ // 4| 6 7 8 9 10
+ expected.clear();
+
+ expected.push_back(std::make_pair(0, 3));
+ expected.push_back(std::make_pair(1, 3));
+ expected.push_back(std::make_pair(2, 3));
+ expected.push_back(std::make_pair(3, 3));
+ expected.push_back(std::make_pair(4, 3));
+ expected.push_back(std::make_pair(0, 4));
+ expected.push_back(std::make_pair(1, 4));
+ expected.push_back(std::make_pair(2, 4));
+ expected.push_back(std::make_pair(3, 4));
+ expected.push_back(std::make_pair(4, 4));
+
+ TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+}
+
+TEST(TilingDataTest, SpiralDifferenceIteratorRectangleCenter) {
+ TilingData tiling_data(gfx::Size(10, 10), gfx::Size(50, 50), false);
+ gfx::Rect consider(50, 50);
+ std::vector<std::pair<int, int> > expected;
+ gfx::Rect ignore;
+
+ // Two cell center
+ gfx::Rect center(25, 25, 1, 10);
+
+ // Layout of the tiling data, and expected return order:
+ // x 0 1 2 3 4
+ // y.----------
+ // 0| J I H G F
+ // 1| K 5 4 3 E
+ // 2| L 6 * 2 D
+ // 3| M 7 * 1 C
+ // 4| N 8 9 A B
+ expected.clear();
+
+ expected.push_back(std::make_pair(3, 3));
+ expected.push_back(std::make_pair(3, 2));
+ expected.push_back(std::make_pair(3, 1));
+ expected.push_back(std::make_pair(2, 1));
+ expected.push_back(std::make_pair(1, 1));
+ expected.push_back(std::make_pair(1, 2));
+ expected.push_back(std::make_pair(1, 3));
+ expected.push_back(std::make_pair(1, 4));
+ expected.push_back(std::make_pair(2, 4));
+ expected.push_back(std::make_pair(3, 4));
+ expected.push_back(std::make_pair(4, 4));
+ expected.push_back(std::make_pair(4, 3));
+ expected.push_back(std::make_pair(4, 2));
+ expected.push_back(std::make_pair(4, 1));
+ expected.push_back(std::make_pair(4, 0));
+ expected.push_back(std::make_pair(3, 0));
+ expected.push_back(std::make_pair(2, 0));
+ expected.push_back(std::make_pair(1, 0));
+ expected.push_back(std::make_pair(0, 0));
+ expected.push_back(std::make_pair(0, 1));
+ expected.push_back(std::make_pair(0, 2));
+ expected.push_back(std::make_pair(0, 3));
+ expected.push_back(std::make_pair(0, 4));
+
+ TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+
+ // Three by two center.
+ center = gfx::Rect(15, 25, 20, 10);
+
+ // Layout of the tiling data, and expected return order:
+ // x 0 1 2 3 4
+ // y.----------
+ // 0| J I H G F
+ // 1| 7 6 5 4 3
+ // 2| 8 * * * 2
+ // 3| 9 * * * 1
+ // 4| A B C D E
+ expected.clear();
+
+ expected.push_back(std::make_pair(4, 3));
+ expected.push_back(std::make_pair(4, 2));
+ expected.push_back(std::make_pair(4, 1));
+ expected.push_back(std::make_pair(3, 1));
+ expected.push_back(std::make_pair(2, 1));
+ expected.push_back(std::make_pair(1, 1));
+ expected.push_back(std::make_pair(0, 1));
+ expected.push_back(std::make_pair(0, 2));
+ expected.push_back(std::make_pair(0, 3));
+ expected.push_back(std::make_pair(0, 4));
+ expected.push_back(std::make_pair(1, 4));
+ expected.push_back(std::make_pair(2, 4));
+ expected.push_back(std::make_pair(3, 4));
+ expected.push_back(std::make_pair(4, 4));
+ expected.push_back(std::make_pair(4, 0));
+ expected.push_back(std::make_pair(3, 0));
+ expected.push_back(std::make_pair(2, 0));
+ expected.push_back(std::make_pair(1, 0));
+ expected.push_back(std::make_pair(0, 0));
+
+ TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+
+ // Column center off the left side.
+ center = gfx::Rect(-50, 0, 30, 50);
+
+ // Layout of the tiling data, and expected return order:
+ // x 0 1 2 3 4
+ // y.----------
+ // * 0| 5 A F K P
+ // * 1| 4 9 E J O
+ // * 2| 3 8 D I N
+ // * 3| 2 7 C H M
+ // * 4| 1 6 B G L
+ expected.clear();
+
+ expected.push_back(std::make_pair(0, 4));
+ expected.push_back(std::make_pair(0, 3));
+ expected.push_back(std::make_pair(0, 2));
+ expected.push_back(std::make_pair(0, 1));
+ expected.push_back(std::make_pair(0, 0));
+ expected.push_back(std::make_pair(1, 4));
+ expected.push_back(std::make_pair(1, 3));
+ expected.push_back(std::make_pair(1, 2));
+ expected.push_back(std::make_pair(1, 1));
+ expected.push_back(std::make_pair(1, 0));
+ expected.push_back(std::make_pair(2, 4));
+ expected.push_back(std::make_pair(2, 3));
+ expected.push_back(std::make_pair(2, 2));
+ expected.push_back(std::make_pair(2, 1));
+ expected.push_back(std::make_pair(2, 0));
+ expected.push_back(std::make_pair(3, 4));
+ expected.push_back(std::make_pair(3, 3));
+ expected.push_back(std::make_pair(3, 2));
+ expected.push_back(std::make_pair(3, 1));
+ expected.push_back(std::make_pair(3, 0));
+ expected.push_back(std::make_pair(4, 4));
+ expected.push_back(std::make_pair(4, 3));
+ expected.push_back(std::make_pair(4, 2));
+ expected.push_back(std::make_pair(4, 1));
+ expected.push_back(std::make_pair(4, 0));
+
+ TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+}
+
+TEST(TilingDataTest, SpiralDifferenceIteratorEdgeCases) {
+ TilingData tiling_data(gfx::Size(10, 10), gfx::Size(30, 30), false);
+ std::vector<std::pair<int, int> > expected;
+ gfx::Rect center;
+ gfx::Rect consider;
+ gfx::Rect ignore;
+
+ // Ignore contains, but is not equal to, consider and center.
+ ignore = gfx::Rect(15, 0, 20, 30);
+ consider = gfx::Rect(20, 10, 10, 20);
+ center = gfx::Rect(25, 0, 5, 5);
+
+ // Layout of the tiling data, and expected return order:
+ // x 0 1 2
+ // y.------
+ // 0| . *
+ // 1| . .
+ // 2| . .
+ expected.clear();
+
+ TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+
+ // Center intersects with consider.
+ ignore = gfx::Rect();
+ center = gfx::Rect(0, 15, 30, 15);
+ consider = gfx::Rect(15, 30);
+
+ // Layout of the tiling data, and expected return order:
+ // x 0 1 2
+ // y.------
+ // 0| 2 1
+ // 1| * * *
+ // 2| * * *
+ expected.clear();
+
+ expected.push_back(std::make_pair(1, 0));
+ expected.push_back(std::make_pair(0, 0));
+
+ TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+
+ // Consider and ignore are non-intersecting.
+ ignore = gfx::Rect(5, 30);
+ consider = gfx::Rect(25, 0, 5, 30);
+ center = gfx::Rect(15, 0, 1, 1);
+
+ // Layout of the tiling data, and expected return order:
+ // x 0 1 2
+ // y.------
+ // 0| . * 1
+ // 1| . 2
+ // 2| . 3
+ expected.clear();
+
+ expected.push_back(std::make_pair(2, 0));
+ expected.push_back(std::make_pair(2, 1));
+ expected.push_back(std::make_pair(2, 2));
+
+ TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+
+ // Center intersects with ignore.
+ consider = gfx::Rect(30, 30);
+ center = gfx::Rect(15, 0, 1, 30);
+ ignore = gfx::Rect(0, 15, 30, 1);
+
+ // Layout of the tiling data, and expected return order:
+ // x 0 1 2
+ // y.------
+ // 0| 3 * 2
+ // 1| . * .
+ // 2| 4 * 1
+ expected.clear();
+
+ expected.push_back(std::make_pair(2, 2));
+ expected.push_back(std::make_pair(2, 0));
+ expected.push_back(std::make_pair(0, 0));
+ expected.push_back(std::make_pair(0, 2));
+
+ TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+
+ // Center and ignore are the same.
+ consider = gfx::Rect(30, 30);
+ center = gfx::Rect(15, 0, 1, 30);
+ ignore = center;
+
+ // Layout of the tiling data, and expected return order:
+ // x 0 1 2
+ // y.------
+ // 0| 4 * 3
+ // 1| 5 * 2
+ // 2| 6 * 1
+ expected.clear();
+
+ expected.push_back(std::make_pair(2, 2));
+ expected.push_back(std::make_pair(2, 1));
+ expected.push_back(std::make_pair(2, 0));
+ expected.push_back(std::make_pair(0, 0));
+ expected.push_back(std::make_pair(0, 1));
+ expected.push_back(std::make_pair(0, 2));
+
+ TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+
+ // Empty tiling data.
+ TilingData empty_data(gfx::Size(0, 0), gfx::Size(0, 0), false);
+
+ expected.clear();
+ TestSpiralIterate(__LINE__, empty_data, consider, ignore, center, expected);
+
+ // Empty consider.
+ ignore = gfx::Rect();
+ center = gfx::Rect(1, 1, 1, 1);
+ consider = gfx::Rect();
+
+ expected.clear();
+ TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+
+ // Empty center. Note: This arbitrarily puts the center to be off the top-left
+ // corner.
+ consider = gfx::Rect(30, 30);
+ ignore = gfx::Rect();
+ center = gfx::Rect();
+
+ // Layout of the tiling data, and expected return order:
+ // * x 0 1 2
+ // y.------
+ // 0| 1 2 6
+ // 1| 3 4 5
+ // 2| 7 8 9
+ expected.clear();
+
+ expected.push_back(std::make_pair(0, 0));
+ expected.push_back(std::make_pair(1, 0));
+ expected.push_back(std::make_pair(0, 1));
+ expected.push_back(std::make_pair(1, 1));
+ expected.push_back(std::make_pair(2, 1));
+ expected.push_back(std::make_pair(2, 0));
+ expected.push_back(std::make_pair(0, 2));
+ expected.push_back(std::make_pair(1, 2));
+ expected.push_back(std::make_pair(2, 2));
+
+ TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+
+ // Every rect is empty.
+ ignore = gfx::Rect();
+ center = gfx::Rect();
+ consider = gfx::Rect();
+
+ expected.clear();
+ TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+
+ // Center is just to the left of cover, and off of the tiling's left side.
+ consider = gfx::Rect(30, 30);
+ ignore = gfx::Rect();
+ center = gfx::Rect(-20, 0, 19, 30);
+
+ // Layout of the tiling data, and expected return order:
+ // x 0 1 2
+ // y.------
+ // *0| 3 6 9
+ // *1| 2 5 8
+ // *2| 1 4 7
+ expected.clear();
+
+ expected.push_back(std::make_pair(0, 2));
+ expected.push_back(std::make_pair(0, 1));
+ expected.push_back(std::make_pair(0, 0));
+ expected.push_back(std::make_pair(1, 2));
+ expected.push_back(std::make_pair(1, 1));
+ expected.push_back(std::make_pair(1, 0));
+ expected.push_back(std::make_pair(2, 2));
+ expected.push_back(std::make_pair(2, 1));
+ expected.push_back(std::make_pair(2, 0));
+
+ TestSpiralIterate(__LINE__, tiling_data, consider, ignore, center, expected);
+}
+
+} // namespace
+
+} // namespace cc
diff --git a/cc/base/unique_notifier.cc b/cc/base/unique_notifier.cc
new file mode 100644
index 0000000..0656c6d
--- /dev/null
+++ b/cc/base/unique_notifier.cc
@@ -0,0 +1,42 @@
+// 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/unique_notifier.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/location.h"
+#include "base/sequenced_task_runner.h"
+
+namespace cc {
+
+UniqueNotifier::UniqueNotifier(base::SequencedTaskRunner* task_runner,
+ const base::Closure& closure)
+ : task_runner_(task_runner),
+ closure_(closure),
+ notification_pending_(false),
+ weak_ptr_factory_(this) {
+}
+
+UniqueNotifier::~UniqueNotifier() {
+}
+
+void UniqueNotifier::Schedule() {
+ if (notification_pending_)
+ return;
+
+ task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&UniqueNotifier::Notify, weak_ptr_factory_.GetWeakPtr()));
+ notification_pending_ = true;
+}
+
+void UniqueNotifier::Notify() {
+ // Note that the order here is important in case closure schedules another
+ // run.
+ notification_pending_ = false;
+ closure_.Run();
+}
+
+} // namespace cc
diff --git a/cc/base/unique_notifier.h b/cc/base/unique_notifier.h
new file mode 100644
index 0000000..89bfecc
--- /dev/null
+++ b/cc/base/unique_notifier.h
@@ -0,0 +1,45 @@
+// 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_UNIQUE_NOTIFIER_H_
+#define CC_BASE_UNIQUE_NOTIFIER_H_
+
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "cc/base/cc_export.h"
+
+namespace base {
+class SequencedTaskRunner;
+} // namespace base
+
+namespace cc {
+
+class CC_EXPORT UniqueNotifier {
+ public:
+ // Configure this notifier to issue the |closure| notification when scheduled.
+ UniqueNotifier(base::SequencedTaskRunner* task_runner,
+ const base::Closure& closure);
+
+ // Destroying the notifier will ensure that no further notifications will
+ // happen from this class.
+ ~UniqueNotifier();
+
+ // Schedule a notification to be run. If another notification is already
+ // pending, then only one notification will take place.
+ void Schedule();
+
+ private:
+ void Notify();
+
+ // TODO(dcheng): How come this doesn't need to hold a ref to the task runner?
+ base::SequencedTaskRunner* task_runner_;
+ base::Closure closure_;
+ bool notification_pending_;
+
+ base::WeakPtrFactory<UniqueNotifier> weak_ptr_factory_;
+};
+
+} // namespace cc
+
+#endif // CC_BASE_UNIQUE_NOTIFIER_H_
diff --git a/cc/base/unique_notifier_unittest.cc b/cc/base/unique_notifier_unittest.cc
new file mode 100644
index 0000000..ec1b83c
--- /dev/null
+++ b/cc/base/unique_notifier_unittest.cc
@@ -0,0 +1,62 @@
+// 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 "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "cc/base/unique_notifier.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+namespace {
+
+class UniqueNotifierTest : public testing::Test {
+ public:
+ UniqueNotifierTest() : notification_count_(0) {}
+
+ virtual void SetUp() OVERRIDE { ResetNotificationCount(); }
+
+ void Notify() { ++notification_count_; }
+
+ int NotificationCount() const { return notification_count_; }
+
+ void ResetNotificationCount() { notification_count_ = 0; }
+
+ protected:
+ int notification_count_;
+};
+
+TEST_F(UniqueNotifierTest, Schedule) {
+ {
+ UniqueNotifier notifier(
+ base::MessageLoopProxy::current().get(),
+ base::Bind(&UniqueNotifierTest::Notify, base::Unretained(this)));
+
+ EXPECT_EQ(0, NotificationCount());
+
+ // Basic schedule should result in a run.
+ notifier.Schedule();
+
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(1, NotificationCount());
+
+ // Multiple schedules should only result in one run.
+ for (int i = 0; i < 5; ++i)
+ notifier.Schedule();
+
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(2, NotificationCount());
+
+ notifier.Schedule();
+ }
+
+ // Notifier went out of scope, so we don't expect to get a notification.
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(2, NotificationCount());
+}
+
+} // namespace
+} // namespace cc
diff --git a/cc/base/util.h b/cc/base/util.h
new file mode 100644
index 0000000..1d716ae
--- /dev/null
+++ b/cc/base/util.h
@@ -0,0 +1,29 @@
+// Copyright 2012 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_UTIL_H_
+#define CC_BASE_UTIL_H_
+
+#include <limits>
+
+#include "base/basictypes.h"
+
+namespace cc {
+
+template <typename T> T RoundUp(T n, T mul) {
+ COMPILE_ASSERT(std::numeric_limits<T>::is_integer, type_must_be_integral);
+ return (n > 0) ? ((n + mul - 1) / mul) * mul
+ : (n / mul) * mul;
+}
+
+template <typename T> T RoundDown(T n, T mul) {
+ COMPILE_ASSERT(std::numeric_limits<T>::is_integer, type_must_be_integral);
+ return (n > 0) ? (n / mul) * mul
+ : (n == 0) ? 0
+ : ((n - mul + 1) / mul) * mul;
+}
+
+} // namespace cc
+
+#endif // CC_BASE_UTIL_H_
diff --git a/cc/base/util_unittest.cc b/cc/base/util_unittest.cc
new file mode 100644
index 0000000..6665a6a
--- /dev/null
+++ b/cc/base/util_unittest.cc
@@ -0,0 +1,67 @@
+// Copyright 2013 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/util.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+namespace {
+
+TEST(UtilTest, RoundUp) {
+ for (int multiplier = 1; multiplier <= 10; ++multiplier) {
+ // Try attempts in descending order, so that we can
+ // determine the correct value before it's needed.
+ int correct;
+ for (int attempt = 5 * multiplier; attempt >= -5 * multiplier; --attempt) {
+ if ((attempt % multiplier) == 0)
+ correct = attempt;
+ EXPECT_EQ(correct, RoundUp(attempt, multiplier))
+ << "attempt=" << attempt << " multiplier=" << multiplier;
+ }
+ }
+
+ for (unsigned multiplier = 1; multiplier <= 10; ++multiplier) {
+ // Try attempts in descending order, so that we can
+ // determine the correct value before it's needed.
+ unsigned correct;
+ for (unsigned attempt = 5 * multiplier; attempt > 0; --attempt) {
+ if ((attempt % multiplier) == 0)
+ correct = attempt;
+ EXPECT_EQ(correct, RoundUp(attempt, multiplier))
+ << "attempt=" << attempt << " multiplier=" << multiplier;
+ }
+ EXPECT_EQ(0u, RoundUp(0u, multiplier))
+ << "attempt=0 multiplier=" << multiplier;
+ }
+}
+
+TEST(UtilTest, RoundDown) {
+ for (int multiplier = 1; multiplier <= 10; ++multiplier) {
+ // Try attempts in ascending order, so that we can
+ // determine the correct value before it's needed.
+ int correct;
+ for (int attempt = -5 * multiplier; attempt <= 5 * multiplier; ++attempt) {
+ if ((attempt % multiplier) == 0)
+ correct = attempt;
+ EXPECT_EQ(correct, RoundDown(attempt, multiplier))
+ << "attempt=" << attempt << " multiplier=" << multiplier;
+ }
+ }
+
+ for (unsigned multiplier = 1; multiplier <= 10; ++multiplier) {
+ // Try attempts in ascending order, so that we can
+ // determine the correct value before it's needed.
+ unsigned correct;
+ for (unsigned attempt = 0; attempt <= 5 * multiplier; ++attempt) {
+ if ((attempt % multiplier) == 0)
+ correct = attempt;
+ EXPECT_EQ(correct, RoundDown(attempt, multiplier))
+ << "attempt=" << attempt << " multiplier=" << multiplier;
+ }
+ }
+}
+
+} // namespace
+} // namespace cc