diff options
author | Zeno Albisser <zeno.albisser@digia.com> | 2013-08-15 21:46:11 +0200 |
---|---|---|
committer | Zeno Albisser <zeno.albisser@digia.com> | 2013-08-15 21:46:11 +0200 |
commit | 679147eead574d186ebf3069647b4c23e8ccace6 (patch) | |
tree | fc247a0ac8ff119f7c8550879ebb6d3dd8d1ff69 /chromium/cc/base | |
download | qtwebengine-chromium-679147eead574d186ebf3069647b4c23e8ccace6.tar.gz |
Initial import.
Diffstat (limited to 'chromium/cc/base')
23 files changed, 4456 insertions, 0 deletions
diff --git a/chromium/cc/base/cc_export.h b/chromium/cc/base/cc_export.h new file mode 100644 index 00000000000..fe0e0e369c7 --- /dev/null +++ b/chromium/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/chromium/cc/base/completion_event.h b/chromium/cc/base/completion_event.h new file mode 100644 index 00000000000..759ce9c3a27 --- /dev/null +++ b/chromium/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 */) { +#ifndef NDEBUG + waited_ = false; + signaled_ = false; +#endif + } + + ~CompletionEvent() { +#ifndef NDEBUG + DCHECK(waited_); + DCHECK(signaled_); +#endif + } + + void Wait() { +#ifndef NDEBUG + DCHECK(!waited_); + waited_ = true; +#endif + base::ThreadRestrictions::ScopedAllowWait allow_wait; + event_.Wait(); + } + + void Signal() { +#ifndef NDEBUG + DCHECK(!signaled_); + signaled_ = true; +#endif + event_.Signal(); + } + + private: + base::WaitableEvent event_; +#ifndef NDEBUG + // 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/chromium/cc/base/float_quad_unittest.cc b/chromium/cc/base/float_quad_unittest.cc new file mode 100644 index 00000000000..bb3446c0d69 --- /dev/null +++ b/chromium/cc/base/float_quad_unittest.cc @@ -0,0 +1,65 @@ +// 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.0); + rectilinear_trans[2].Rotate(180.0); + rectilinear_trans[3].Rotate(270.0); + rectilinear_trans[4].SkewX(0.00000000001); + rectilinear_trans[5].SkewY(0.00000000001); + rectilinear_trans[6].Scale(0.00001, 0.00001); + rectilinear_trans[6].Rotate(180.0); + rectilinear_trans[7].Scale(100000, 100000); + rectilinear_trans[7].Rotate(180.0); + + for (int i = 0; i < kNumRectilinear; ++i) { + bool clipped = false; + gfx::QuadF quad = MathUtil::MapQuad( + rectilinear_trans[i], + gfx::QuadF( + gfx::RectF(0.01010101f, 0.01010101f, 100.01010101f, 100.01010101f)), + &clipped); + ASSERT_TRUE(!clipped); + EXPECT_TRUE(quad.IsRectilinear()); + } + + const int kNumNonRectilinear = 10; + gfx::Transform non_rectilinear_trans[kNumNonRectilinear]; + non_rectilinear_trans[0].Rotate(359.999); + non_rectilinear_trans[1].Rotate(0.0000001); + non_rectilinear_trans[2].Rotate(89.999999); + non_rectilinear_trans[3].Rotate(90.0000001); + non_rectilinear_trans[4].Rotate(179.999999); + non_rectilinear_trans[5].Rotate(180.0000001); + non_rectilinear_trans[6].Rotate(269.999999); + non_rectilinear_trans[7].Rotate(270.0000001); + non_rectilinear_trans[8].SkewX(0.00001); + non_rectilinear_trans[9].SkewY(0.00001); + + for (int i = 0; i < kNumNonRectilinear; ++i) { + bool clipped = false; + gfx::QuadF quad = MathUtil::MapQuad( + non_rectilinear_trans[i], + gfx::QuadF( + gfx::RectF(0.01010101f, 0.01010101f, 100.01010101f, 100.01010101f)), + &clipped); + ASSERT_TRUE(!clipped); + EXPECT_FALSE(quad.IsRectilinear()); + } +} + +} // namespace +} // namespace cc diff --git a/chromium/cc/base/invalidation_region.cc b/chromium/cc/base/invalidation_region.cc new file mode 100644 index 00000000000..2c39d25ba06 --- /dev/null +++ b/chromium/cc/base/invalidation_region.cc @@ -0,0 +1,48 @@ +// 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) { + UMA_HISTOGRAM_CUSTOM_COUNTS( + "Renderer4.InvalidationRegionApproximateRectCount", + region_.GetRegionComplexity(), + 1, + 5000, + 50); + + SimplifyIfNeeded(); + region_.Swap(region); +} + +void InvalidationRegion::Clear() { + region_.Clear(); +} + +void InvalidationRegion::Union(gfx::Rect rect) { + // TODO(vmpstr): We should simplify the region after Union() after we get a + // good idea of what kind of regions are typical (from the UMA histogram). + region_.Union(rect); +} + +void InvalidationRegion::SimplifyIfNeeded() { + if (region_.GetRegionComplexity() > kMaxInvalidationRectCount) + region_ = region_.bounds(); +} + +} // namespace cc diff --git a/chromium/cc/base/invalidation_region.h b/chromium/cc/base/invalidation_region.h new file mode 100644 index 00000000000..fd061e8cba3 --- /dev/null +++ b/chromium/cc/base/invalidation_region.h @@ -0,0 +1,34 @@ +// 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(gfx::Rect rect); + + private: + void SimplifyIfNeeded(); + + Region region_; +}; + +} // namespace cc + +#endif // CC_BASE_INVALIDATION_REGION_H_ diff --git a/chromium/cc/base/math_util.cc b/chromium/cc/base/math_util.cc new file mode 100644 index 00000000000..fa7b21a8c29 --- /dev/null +++ b/chromium/cc/base/math_util.cc @@ -0,0 +1,576 @@ +// 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/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, + 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 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()); + + SkMScalar w = 0.00001; // or any positive non-zero small epsilon + + SkMScalar t = (w - h1.w()) / (h2.w() - h1.w()); + + SkMScalar x = (1 - t) * h1.x() + t * h2.x(); + SkMScalar y = (1 - t) * h1.y() + t * h2.y(); + SkMScalar z = (1 - t) * h1.z() + t * h2.z(); + + return HomogeneousCoordinate(x, y, z, w); +} + +static inline void ExpandBoundsToIncludePoint(float* xmin, + float* xmax, + float* ymin, + float* ymax, + 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(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)++; +} + +gfx::Rect MathUtil::MapClippedRect(const gfx::Transform& transform, + gfx::Rect src_rect) { + 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()) + return src_rect + + gfx::Vector2dF( + static_cast<float>(transform.matrix().getDouble(0, 3)), + static_cast<float>(transform.matrix().getDouble(1, 3))); + + // Apply the transform, but retain the result in homogeneous coordinates. + + double 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(); + + double 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::RectF MathUtil::ProjectClippedRect(const gfx::Transform& transform, + const gfx::RectF& src_rect) { + if (transform.IsIdentityOrTranslation()) { + return src_rect + + gfx::Vector2dF( + static_cast<float>(transform.matrix().getDouble(0, 3)), + static_cast<float>(transform.matrix().getDouble(1, 3))); + } + + // 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); +} + +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); +} + +gfx::RectF MathUtil::ComputeEnclosingRectOfVertices(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(static_cast<float>(transform.matrix().getDouble(0, 3)), + static_cast<float>(transform.matrix().getDouble(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::PointF MathUtil::MapPoint(const gfx::Transform& transform, + 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, + gfx::PointF p, + bool* clipped) { + HomogeneousCoordinate h = ProjectHomogeneousPoint(transform, p); + + if (h.w() > 0) { + // The cartesian coordinates will be valid in this case. + *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(); +} + +static inline float ScaleOnAxis(double a, double b, double c) { + return 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(gfx::Vector2dF v1, + 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(gfx::Vector2dF source, + 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(gfx::Size s) { + scoped_ptr<base::DictionaryValue> res(new base::DictionaryValue()); + res->SetDouble("width", s.width()); + res->SetDouble("height", s.height()); + return res.PassAs<base::Value>(); +} + +scoped_ptr<base::Value> MathUtil::AsValue(gfx::SizeF s) { + scoped_ptr<base::DictionaryValue> res(new base::DictionaryValue()); + res->SetDouble("width", s.width()); + res->SetDouble("height", s.height()); + return res.PassAs<base::Value>(); +} + +scoped_ptr<base::Value> MathUtil::AsValue(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.PassAs<base::Value>(); +} + +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(gfx::PointF pt) { + scoped_ptr<base::ListValue> res(new base::ListValue()); + res->AppendDouble(pt.x()); + res->AppendDouble(pt.y()); + return res.PassAs<base::Value>(); +} + +scoped_ptr<base::Value> MathUtil::AsValue(const gfx::QuadF& q) { + scoped_ptr<base::ListValue> res(new base::ListValue()); + 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()); + return res.PassAs<base::Value>(); +} + +scoped_ptr<base::Value> MathUtil::AsValue(const gfx::RectF& rect) { + scoped_ptr<base::ListValue> res(new base::ListValue()); + res->AppendDouble(rect.x()); + res->AppendDouble(rect.y()); + res->AppendDouble(rect.width()); + res->AppendDouble(rect.height()); + return res.PassAs<base::Value>(); +} + +scoped_ptr<base::Value> MathUtil::AsValue(const gfx::Transform& transform) { + scoped_ptr<base::ListValue> res(new base::ListValue()); + 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)); + } + return res.PassAs<base::Value>(); +} + +scoped_ptr<base::Value> MathUtil::AsValueSafely(double value) { + return scoped_ptr<base::Value>(base::Value::CreateDoubleValue( + std::min(value, std::numeric_limits<double>::max()))); +} + +scoped_ptr<base::Value> MathUtil::AsValueSafely(float value) { + return scoped_ptr<base::Value>(base::Value::CreateDoubleValue( + std::min(value, std::numeric_limits<float>::max()))); +} + +} // namespace cc diff --git a/chromium/cc/base/math_util.h b/chromium/cc/base/math_util.h new file mode 100644 index 00000000000..37f0c07602f --- /dev/null +++ b/chromium/cc/base/math_util.h @@ -0,0 +1,175 @@ +// 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 "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "cc/base/cc_export.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 gfx { +class QuadF; +class Rect; +class RectF; +class Transform; +class Vector2dF; +} + +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() == 1.0) + 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()); + double inv_w = 1.0 / w(); + return gfx::PointF(x() * inv_w, y() * inv_w); + } + + gfx::Point3F CartesianPoint3d() const { + if (w() == 1) + 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()); + double inv_w = 1.0 / 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 MapClippedRect(const gfx::Transform& transform, + gfx::Rect rect); + static gfx::RectF MapClippedRect(const gfx::Transform& transform, + const gfx::RectF& rect); + static gfx::RectF ProjectClippedRect(const gfx::Transform& transform, + const gfx::RectF& 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 gfx::RectF ComputeEnclosingRectOfVertices(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::PointF MapPoint(const gfx::Transform& transform, + 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, + gfx::PointF point, + bool* clipped); + + static gfx::Vector2dF ComputeTransform2dScaleComponents(const gfx::Transform&, + float fallbackValue); + + // Returns the smallest angle between the given two vectors in degrees. + // Neither vector is assumed to be normalized. + static float SmallestAngleBetweenVectors(gfx::Vector2dF v1, + gfx::Vector2dF v2); + + // Projects the |source| vector onto |destination|. Neither vector is assumed + // to be normalized. + static gfx::Vector2dF ProjectVector(gfx::Vector2dF source, + gfx::Vector2dF destination); + + // Conversion to value. + static scoped_ptr<base::Value> AsValue(gfx::Size s); + static scoped_ptr<base::Value> AsValue(gfx::SizeF s); + static scoped_ptr<base::Value> AsValue(gfx::Rect r); + static bool FromValue(const base::Value*, gfx::Rect* out_rect); + static scoped_ptr<base::Value> AsValue(gfx::PointF q); + static scoped_ptr<base::Value> AsValue(const gfx::QuadF& q); + static scoped_ptr<base::Value> AsValue(const gfx::RectF& rect); + static scoped_ptr<base::Value> AsValue(const gfx::Transform& transform); + + // Returns a base::Value representation of the floating point value. + // If the value is inf, returns max double/float representation. + static scoped_ptr<base::Value> AsValueSafely(double value); + static scoped_ptr<base::Value> AsValueSafely(float value); +}; + +} // namespace cc + +#endif // CC_BASE_MATH_UTIL_H_ diff --git a/chromium/cc/base/math_util_unittest.cc b/chromium/cc/base/math_util_unittest.cc new file mode 100644 index 00000000000..d62280ddb5f --- /dev/null +++ b/chromium/cc/base/math_util_unittest.cc @@ -0,0 +1,122 @@ +// 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().setDouble(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); + + EXPECT_FLOAT_RECT_EQ(gfx::RectF(gfx::PointF(-100, -100), gfx::SizeF(90, 90)), + result); +} + +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()); +} + +} // namespace +} // namespace cc diff --git a/chromium/cc/base/region.cc b/chromium/cc/base/region.cc new file mode 100644 index 00000000000..1cda32d3ba2 --- /dev/null +++ b/chromium/cc/base/region.cc @@ -0,0 +1,133 @@ +// 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/values.h" + +namespace cc { + +Region::Region() { +} + +Region::Region(const Region& region) + : skregion_(region.skregion_) { +} + +Region::Region(gfx::Rect rect) + : skregion_(gfx::RectToSkIRect(rect)) { +} + +Region::~Region() { +} + +const Region& Region::operator=(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(gfx::Point point) const { + return skregion_.contains(point.x(), point.y()); +} + +bool Region::Contains(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(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(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::Union(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(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.PassAs<base::Value>(); +} + +Region::Iterator::Iterator() { +} + +Region::Iterator::Iterator(const Region& region) + : it_(region.skregion_) { +} + +Region::Iterator::~Iterator() { +} + +} // namespace cc diff --git a/chromium/cc/base/region.h b/chromium/cc/base/region.h new file mode 100644 index 00000000000..26c1d2b1990 --- /dev/null +++ b/chromium/cc/base/region.h @@ -0,0 +1,135 @@ +// 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/logging.h" +#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 cc { + +class CC_EXPORT Region { + public: + Region(); + Region(const Region& region); + Region(gfx::Rect rect); // NOLINT(runtime/explicit) + ~Region(); + + const Region& operator=(gfx::Rect rect); + const Region& operator=(const Region& region); + + void Swap(Region* region); + void Clear(); + bool IsEmpty() const; + int GetRegionComplexity() const; + + bool Contains(gfx::Point point) const; + bool Contains(gfx::Rect rect) const; + bool Contains(const Region& region) const; + + bool Intersects(gfx::Rect rect) const; + bool Intersects(const Region& region) const; + + void Subtract(gfx::Rect rect); + void Subtract(const Region& region); + void Union(gfx::Rect rect); + void Union(const Region& region); + void Intersect(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; + + 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, 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, 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, gfx::Rect b) { + Region result = a; + result.Union(b); + return result; +} + +} // namespace cc + +#endif // CC_BASE_REGION_H_ diff --git a/chromium/cc/base/region_unittest.cc b/chromium/cc/base/region_unittest.cc new file mode 100644 index 00000000000..c9a218d692a --- /dev/null +++ b/chromium/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/chromium/cc/base/scoped_ptr_algorithm.h b/chromium/cc/base/scoped_ptr_algorithm.h new file mode 100644 index 00000000000..79f4eee49e6 --- /dev/null +++ b/chromium/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/chromium/cc/base/scoped_ptr_deque.h b/chromium/cc/base/scoped_ptr_deque.h new file mode 100644 index 00000000000..cb4adfc15be --- /dev/null +++ b/chromium/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/chromium/cc/base/scoped_ptr_hash_map.h b/chromium/cc/base/scoped_ptr_hash_map.h new file mode 100644 index 00000000000..47135822d2d --- /dev/null +++ b/chromium/cc/base/scoped_ptr_hash_map.h @@ -0,0 +1,157 @@ +// 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_HASH_MAP_H_ +#define CC_BASE_SCOPED_PTR_HASH_MAP_H_ + +#include <algorithm> +#include <utility> + +#include "base/basictypes.h" +#include "base/containers/hash_tables.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/stl_util.h" + +namespace cc { + +// This type acts like a hash_map<K, scoped_ptr<V> >, based on top of +// base::hash_map. The ScopedPtrHashMap has ownership of all values in the data +// structure. +template <typename Key, typename Value> +class ScopedPtrHashMap { + typedef base::hash_map<Key, Value*> Container; + + public: + typedef typename Container::iterator iterator; + typedef typename Container::const_iterator const_iterator; + + ScopedPtrHashMap() {} + + ~ScopedPtrHashMap() { clear(); } + + void swap(ScopedPtrHashMap<Key, Value>& other) { + data_.swap(other.data_); + } + + std::pair<iterator, bool> insert( + std::pair<Key, const scoped_ptr<Value> > pair) { + return data_.insert( + std::pair<Key, Value*>(pair.first, pair.second.release())); + } + + // Replaces value but not key if key is already present. + std::pair<iterator, bool> set(Key key, scoped_ptr<Value> data) { + iterator it = find(key); + if (it != end()) + erase(it); + Value* raw_ptr = data.release(); + return data_.insert(std::pair<Key, Value*>(key, raw_ptr)); + } + + // Does nothing if key is already present + std::pair<iterator, bool> add(Key key, scoped_ptr<Value> data) { + Value* raw_ptr = data.release(); + return data_.insert(std::pair<Key, Value*>(key, raw_ptr)); + } + + void erase(iterator it) { + if (it->second) + delete it->second; + data_.erase(it); + } + + size_t erase(const Key& k) { + iterator it = data_.find(k); + if (it == data_.end()) + return 0; + erase(it); + return 1; + } + + scoped_ptr<Value> take(iterator it) { + DCHECK(it != data_.end()); + if (it == data_.end()) + return scoped_ptr<Value>(); + + Key key = it->first; + scoped_ptr<Value> ret(it->second); + data_.erase(it); + data_.insert(std::pair<Key, Value*>(key, static_cast<Value*>(NULL))); + return ret.Pass(); + } + + scoped_ptr<Value> take(const Key& k) { + iterator it = find(k); + if (it == data_.end()) + return scoped_ptr<Value>(); + + return take(it); + } + + scoped_ptr<Value> take_and_erase(iterator it) { + DCHECK(it != data_.end()); + if (it == data_.end()) + return scoped_ptr<Value>(); + + scoped_ptr<Value> ret(it->second); + data_.erase(it); + return ret.Pass(); + } + + scoped_ptr<Value> take_and_erase(const Key& k) { + iterator it = find(k); + if (it == data_.end()) + return scoped_ptr<Value>(); + + return take_and_erase(it); + } + + // Returns the first element in the hash_map that matches the given key. + // If no such element exists it returns NULL. + Value* get(const Key& k) const { + const_iterator it = find(k); + if (it == end()) + return 0; + return it->second; + } + + inline bool contains(const Key& k) const { return data_.count(k) > 0; } + + inline void clear() { STLDeleteValues(&data_); } + + inline const_iterator find(const Key& k) const { return data_.find(k); } + inline iterator find(const Key& k) { return data_.find(k); } + + inline size_t count(const Key& k) const { return data_.count(k); } + inline std::pair<const_iterator, const_iterator> equal_range( + const Key& k) const { + return data_.equal_range(k); + } + inline std::pair<iterator, iterator> equal_range(const Key& k) { + return data_.equal_range(k); + } + + inline size_t size() const { return data_.size(); } + inline size_t max_size() const { return data_.max_size(); } + + inline bool empty() const { return data_.empty(); } + + inline size_t bucket_count() const { return data_.bucket_count(); } + inline void resize(size_t size) const { return data_.resize(size); } + + inline iterator begin() { return data_.begin(); } + inline const_iterator begin() const { return data_.begin(); } + inline iterator end() { return data_.end(); } + inline const_iterator end() const { return data_.end(); } + + private: + Container data_; + + DISALLOW_COPY_AND_ASSIGN(ScopedPtrHashMap); +}; + +} // namespace cc + +#endif // CC_BASE_SCOPED_PTR_HASH_MAP_H_ diff --git a/chromium/cc/base/scoped_ptr_vector.h b/chromium/cc/base/scoped_ptr_vector.h new file mode 100644 index 00000000000..856e2f51cd1 --- /dev/null +++ b/chromium/cc/base/scoped_ptr_vector.h @@ -0,0 +1,180 @@ +// 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 scoped_ptr<T>(); + 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 scoped_ptr<T>(NULL); + 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()); + } + + 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); + } + + 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/chromium/cc/base/scoped_ptr_vector_unittest.cc b/chromium/cc/base/scoped_ptr_vector_unittest.cc new file mode 100644 index 00000000000..4e450b9c823 --- /dev/null +++ b/chromium/cc/base/scoped_ptr_vector_unittest.cc @@ -0,0 +1,72 @@ +// 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/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_; +}; + +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]); +} + +} // namespace +} // namespace cc diff --git a/chromium/cc/base/switches.cc b/chromium/cc/base/switches.cc new file mode 100644 index 00000000000..2dac58b6911 --- /dev/null +++ b/chromium/cc/base/switches.cc @@ -0,0 +1,150 @@ +// 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 { + +// On platforms where checkerboards are used, prefer background colors instead +// of checkerboards. +const char kBackgroundColorInsteadOfCheckerboard[] = + "background-color-instead-of-checkerboard"; + +const char kDisableThreadedAnimation[] = "disable-threaded-animation"; + +// Disables layer-edge anti-aliasing in the compositor. +const char kDisableCompositedAntialiasing[] = + "disable-composited-antialiasing"; + +// Paint content on the main thread instead of the compositor thread. +// Overrides the kEnableImplSidePainting flag. +const char kDisableImplSidePainting[] = "disable-impl-side-painting"; + +// Paint content on the compositor thread instead of the main thread. +const char kEnableImplSidePainting[] = "enable-impl-side-painting"; + +const char kEnableTopControlsPositionCalculation[] = + "enable-top-controls-position-calculation"; + +// For any layers that can get drawn directly to screen, draw them with the Skia +// GPU backend. Only valid with gl rendering + threaded compositing + impl-side +// painting. +const char kForceDirectLayerDrawing[] = "force-direct-layer-drawing"; + +// 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"; + +// Number of worker threads used to rasterize content. +const char kNumRasterThreads[] = "num-raster-threads"; + +// Show metrics about overdraw in about:tracing recordings, such as the number +// of pixels culled, and the number of pixels drawn, for each frame. +const char kTraceOverdraw[] = "trace-overdraw"; + +// 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"; + +// The scale factor for low resolution tile contents. +const char kLowResolutionContentsScaleFactor[] = + "low-resolution-contents-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 kEnablePartialSwap[] = "enable-partial-swap"; +// Disable partial swap which is needed for some OpenGL drivers / emulators. +const char kUIDisablePartialSwap[] = "ui-disable-partial-swap"; + +const char kEnablePerTilePainting[] = "enable-per-tile-painting"; +const char kUIEnablePerTilePainting[] = "ui-enable-per-tile-painting"; + +// 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"; + +// 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"; + +// Enable the codepath that uses images within TileManager. +const char kUseMapImage[] = "use-map-image"; + +// Prevents the layer tree unit tests from timing out. +const char kCCLayerTreeTestNoTimeout[] = "cc-layer-tree-test-no-timeout"; + +bool IsImplSidePaintingEnabled() { + const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + + if (command_line.HasSwitch(cc::switches::kDisableImplSidePainting)) + return false; + else if (command_line.HasSwitch(cc::switches::kEnableImplSidePainting)) + return true; + +#if defined(OS_ANDROID) + return true; +#else + return false; +#endif +} + +} // namespace switches +} // namespace cc diff --git a/chromium/cc/base/switches.h b/chromium/cc/base/switches.h new file mode 100644 index 00000000000..f87eb53ee16 --- /dev/null +++ b/chromium/cc/base/switches.h @@ -0,0 +1,73 @@ +// 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 kBackgroundColorInsteadOfCheckerboard[]; +CC_EXPORT extern const char kDisableImplSidePainting[]; +CC_EXPORT extern const char kDisableThreadedAnimation[]; +CC_EXPORT extern const char kDisableCompositedAntialiasing[]; +CC_EXPORT extern const char kEnableImplSidePainting[]; +CC_EXPORT extern const char kEnableTopControlsPositionCalculation[]; +CC_EXPORT extern const char kForceDirectLayerDrawing[]; +CC_EXPORT extern const char kJankInsteadOfCheckerboard[]; +CC_EXPORT extern const char kNumRasterThreads[]; +CC_EXPORT extern const char kTopControlsHeight[]; +CC_EXPORT extern const char kTopControlsHideThreshold[]; +CC_EXPORT extern const char kTraceOverdraw[]; +CC_EXPORT extern const char kTopControlsShowThreshold[]; +CC_EXPORT extern const char kSlowDownRasterScaleFactor[]; +CC_EXPORT extern const char kLowResolutionContentsScaleFactor[]; +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 kEnablePartialSwap[]; +CC_EXPORT extern const char kStrictLayerPropertyChangeChecking[]; +CC_EXPORT extern const char kUseMapImage[]; + +// Switches for both the renderer and ui compositors. +CC_EXPORT extern const char kUIDisablePartialSwap[]; +CC_EXPORT extern const char kEnablePerTilePainting[]; +CC_EXPORT extern const char kUIEnablePerTilePainting[]; + +// 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 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 bool IsImplSidePaintingEnabled(); + +} // namespace switches +} // namespace cc + +#endif // CC_BASE_SWITCHES_H_ diff --git a/chromium/cc/base/tiling_data.cc b/chromium/cc/base/tiling_data.cc new file mode 100644 index 00000000000..1045d89addd --- /dev/null +++ b/chromium/cc/base/tiling_data.cc @@ -0,0 +1,408 @@ +// 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( + gfx::Size max_texture_size, + gfx::Size total_size, + bool has_border_texels) + : max_texture_size_(max_texture_size), + total_size_(total_size), + border_texels_(has_border_texels ? 1 : 0) { + RecomputeNumTiles(); +} + +TilingData::TilingData( + gfx::Size max_texture_size, + gfx::Size total_size, + int border_texels) + : max_texture_size_(max_texture_size), + total_size_(total_size), + border_texels_(border_texels) { + RecomputeNumTiles(); +} + +void TilingData::SetTotalSize(gfx::Size total_size) { + total_size_ = total_size; + RecomputeNumTiles(); +} + +void TilingData::SetMaxTextureSize(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::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 total_size_x = total_size_.width(); + int total_size_y = total_size_.height(); + + 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, total_size_x); + hi_y = std::min(hi_y, total_size_y); + + 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, total_size_.width()); + DCHECK_LE(y, total_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 total_size_x = total_size_.width(); + int total_size_y = total_size_.height(); + + 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, total_size_x); + hi_y = std::min(hi_y, total_size_y); + + 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, total_size_.width()); + DCHECK_LE(y, total_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 total_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 total_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 total_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 total_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(), total_size_.width(), border_texels_); + num_tiles_y_ = ComputeNumTiles( + max_texture_size_.height(), total_size_.height(), border_texels_); +} + +TilingData::BaseIterator::BaseIterator(const TilingData* tiling_data) + : tiling_data_(tiling_data), + index_x_(-1), + index_y_(-1) { +} + +TilingData::Iterator::Iterator(const TilingData* tiling_data, gfx::Rect rect) + : BaseIterator(tiling_data), + left_(-1), + right_(-1), + bottom_(-1) { + if (tiling_data_->num_tiles_x() <= 0 || tiling_data_->num_tiles_y() <= 0) { + done(); + return; + } + + rect.Intersect(gfx::Rect(tiling_data_->total_size())); + index_x_ = tiling_data_->FirstBorderTileXIndexFromSrcCoord(rect.x()); + index_y_ = tiling_data_->FirstBorderTileYIndexFromSrcCoord(rect.y()); + left_ = index_x_; + right_ = tiling_data_->LastBorderTileXIndexFromSrcCoord(rect.right() - 1); + bottom_ = tiling_data_->LastBorderTileYIndexFromSrcCoord(rect.bottom() - 1); + + // Index functions always return valid indices, so explicitly check + // for non-intersecting rects. + gfx::Rect new_rect = tiling_data_->TileBoundsWithBorder(index_x_, index_y_); + if (!new_rect.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, + gfx::Rect consider, + gfx::Rect ignore) + : 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 bounds(tiling_data_->total_size()); + consider.Intersect(bounds); + ignore.Intersect(bounds); + if (consider.IsEmpty()) { + done(); + return; + } + + consider_left_ = + tiling_data_->FirstBorderTileXIndexFromSrcCoord(consider.x()); + consider_top_ = + tiling_data_->FirstBorderTileYIndexFromSrcCoord(consider.y()); + consider_right_ = + tiling_data_->LastBorderTileXIndexFromSrcCoord(consider.right() - 1); + consider_bottom_ = + tiling_data_->LastBorderTileYIndexFromSrcCoord(consider.bottom() - 1); + + if (!ignore.IsEmpty()) { + ignore_left_ = + tiling_data_->FirstBorderTileXIndexFromSrcCoord(ignore.x()); + ignore_top_ = + tiling_data_->FirstBorderTileYIndexFromSrcCoord(ignore.y()); + ignore_right_ = + tiling_data_->LastBorderTileXIndexFromSrcCoord(ignore.right() - 1); + ignore_bottom_ = + tiling_data_->LastBorderTileYIndexFromSrcCoord(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; +} + +} // namespace cc diff --git a/chromium/cc/base/tiling_data.h b/chromium/cc/base/tiling_data.h new file mode 100644 index 00000000000..3bf9809579f --- /dev/null +++ b/chromium/cc/base/tiling_data.h @@ -0,0 +1,148 @@ +// 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( + gfx::Size max_texture_size, + gfx::Size total_size, + bool has_border_texels); + TilingData( + gfx::Size max_texture_size, + gfx::Size total_size, + int border_texels); + + gfx::Size total_size() const { return total_size_; } + void SetTotalSize(const gfx::Size total_size); + + gfx::Size max_texture_size() const { return max_texture_size_; } + void SetMaxTextureSize(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 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 all indices whose bounds + border intersect with |rect|. + class CC_EXPORT Iterator : public BaseIterator { + public: + Iterator(const TilingData* tiling_data, gfx::Rect rect); + Iterator& operator++(); + + private: + int left_; + int right_; + int bottom_; + }; + + // Iterate through all indices whose bounds + border intersect with + // |consider| but which also do not intersect with |ignore|. + class CC_EXPORT DifferenceIterator : public BaseIterator { + public: + DifferenceIterator( + const TilingData* tiling_data, + gfx::Rect consider, + gfx::Rect ignore); + 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_; + }; + + 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 total_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/chromium/cc/base/tiling_data_unittest.cc b/chromium/cc/base/tiling_data_unittest.cc new file mode 100644 index 00000000000..db2d6f215a9 --- /dev/null +++ b/chromium/cc/base/tiling_data_unittest.cc @@ -0,0 +1,1171 @@ +// 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 <vector> + +#include "cc/test/geometry_test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace cc { +namespace { + +int NumTiles( + gfx::Size max_texture_size, + gfx::Size total_size, + bool has_border_texels) { + TilingData tiling(max_texture_size, total_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( + gfx::Size max_texture_size, + gfx::Size total_size, + bool has_border_texels, + int x_coord) { + TilingData tiling(max_texture_size, total_size, has_border_texels); + return tiling.TileXIndexFromSrcCoord(x_coord); +} + +int YIndex( + gfx::Size max_texture_size, + gfx::Size total_size, + bool has_border_texels, + int y_coord) { + TilingData tiling(max_texture_size, total_size, has_border_texels); + return tiling.TileYIndexFromSrcCoord(y_coord); +} + +int MinBorderXIndex( + gfx::Size max_texture_size, + gfx::Size total_size, + bool has_border_texels, + int x_coord) { + TilingData tiling(max_texture_size, total_size, has_border_texels); + return tiling.FirstBorderTileXIndexFromSrcCoord(x_coord); +} + +int MinBorderYIndex( + gfx::Size max_texture_size, + gfx::Size total_size, + bool has_border_texels, + int y_coord) { + TilingData tiling(max_texture_size, total_size, has_border_texels); + return tiling.FirstBorderTileYIndexFromSrcCoord(y_coord); +} + +int MaxBorderXIndex( + gfx::Size max_texture_size, + gfx::Size total_size, + bool has_border_texels, + int x_coord) { + TilingData tiling(max_texture_size, total_size, has_border_texels); + return tiling.LastBorderTileXIndexFromSrcCoord(x_coord); +} + +int MaxBorderYIndex( + gfx::Size max_texture_size, + gfx::Size total_size, + bool has_border_texels, + int y_coord) { + TilingData tiling(max_texture_size, total_size, has_border_texels); + return tiling.LastBorderTileYIndexFromSrcCoord(y_coord); +} + +int PosX( + gfx::Size max_texture_size, + gfx::Size total_size, + bool has_border_texels, + int x_index) { + TilingData tiling(max_texture_size, total_size, has_border_texels); + return tiling.TilePositionX(x_index); +} + +int PosY( + gfx::Size max_texture_size, + gfx::Size total_size, + bool has_border_texels, + int y_index) { + TilingData tiling(max_texture_size, total_size, has_border_texels); + return tiling.TilePositionY(y_index); +} + +int SizeX( + gfx::Size max_texture_size, + gfx::Size total_size, + bool has_border_texels, + int x_index) { + TilingData tiling(max_texture_size, total_size, has_border_texels); + return tiling.TileSizeX(x_index); +} + +int SizeY( + gfx::Size max_texture_size, + gfx::Size total_size, + bool has_border_texels, + int y_index) { + TilingData tiling(max_texture_size, total_size, has_border_texels); + return tiling.TileSizeY(y_index); +} + +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.total_size().width()); + EXPECT_EQ(5, data.total_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.SetTotalSize(gfx::Size(6, 5)); + EXPECT_EQ(6, data.total_size().width()); + EXPECT_EQ(5, data.total_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.SetTotalSize(gfx::Size(5, 12)); + EXPECT_EQ(5, data.total_size().width()); + EXPECT_EQ(12, data.total_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, 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.total_size().width(), dest.total_size().width()); + EXPECT_EQ(source.total_size().height(), dest.total_size().height()); + } + { + 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.total_size().width(), dest.total_size().width()); + EXPECT_EQ(source.total_size().height(), dest.total_size().height()); + } +} + +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(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(0, 0, 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(0, 0, 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) { + + 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 = data.TileBoundsWithBorder(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); 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. + { + 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()); + } +} + +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) + TestIterate(data, gfx::Rect(0, 0, 40, 25), 0, 0, 3, 2); + TestIterate(data, gfx::Rect(15, 15, 8, 8), 1, 1, 2, 2); + + // Oversized. + TestIterate(data, gfx::Rect(-100, -100, 1000, 1000), 0, 0, 3, 2); + TestIterate(data, gfx::Rect(-100, 20, 1000, 1), 0, 2, 3, 2); + TestIterate(data, gfx::Rect(29, -100, 31, 1000), 2, 0, 3, 2); + // Nonintersecting. + TestIterate(data, gfx::Rect(60, 80, 100, 100), 0, 0, -1, -1); +} + +TEST(TilingDataTest, IteratorOneBorderTexel) { + 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) + TestIterate(data, gfx::Rect(0, 0, 25, 45), 0, 0, 2, 2); + TestIterate(data, gfx::Rect(18, 19, 3, 17), 2, 0, 2, 1); + TestIterate(data, gfx::Rect(10, 20, 6, 16), 1, 1, 1, 1); + TestIterate(data, gfx::Rect(9, 19, 8, 18), 0, 0, 2, 2); + + // Oversized. + TestIterate(data, gfx::Rect(-100, -100, 1000, 1000), 0, 0, 2, 2); + TestIterate(data, gfx::Rect(-100, 20, 1000, 1), 0, 1, 2, 1); + TestIterate(data, gfx::Rect(18, -100, 6, 1000), 2, 0, 2, 2); + // Nonintersecting. + TestIterate(data, gfx::Rect(60, 80, 100, 100), 0, 0, -1, -1); +} + +TEST(TilingDataTest, IteratorManyBorderTexels) { + 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) + TestIterate(data, gfx::Rect(0, 0, 65, 110), 0, 0, 2, 3); + TestIterate(data, gfx::Rect(50, 60, 15, 65), 1, 1, 2, 3); + TestIterate(data, gfx::Rect(60, 30, 2, 10), 2, 0, 2, 1); + + // Oversized. + TestIterate(data, gfx::Rect(-100, -100, 1000, 1000), 0, 0, 2, 3); + TestIterate(data, gfx::Rect(-100, 10, 1000, 10), 0, 0, 2, 0); + TestIterate(data, gfx::Rect(10, -100, 10, 1000), 0, 0, 1, 3); + // Nonintersecting. + TestIterate(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); + TestIterate(no_border, gfx::Rect(0, 0, 30, 40), 0, 0, 0, 0); + TestIterate(no_border, gfx::Rect(10, 10, 20, 20), 0, 0, 0, 0); + TestIterate(no_border, gfx::Rect(30, 40, 100, 100), 0, 0, -1, -1); + + TilingData one_border(gfx::Size(1000, 1000), gfx::Size(30, 40), true); + TestIterate(one_border, gfx::Rect(0, 0, 30, 40), 0, 0, 0, 0); + TestIterate(one_border, gfx::Rect(10, 10, 20, 20), 0, 0, 0, 0); + TestIterate(one_border, gfx::Rect(30, 40, 100, 100), 0, 0, -1, -1); + + TilingData big_border(gfx::Size(1000, 1000), gfx::Size(30, 40), 50); + TestIterate(big_border, gfx::Rect(0, 0, 30, 40), 0, 0, 0, 0); + TestIterate(big_border, gfx::Rect(10, 10, 20, 20), 0, 0, 0, 0); + TestIterate(big_border, gfx::Rect(30, 40, 100, 100), 0, 0, -1, -1); +} + +TEST(TilingDataTest, IteratorNoTiles) { + TilingData data(gfx::Size(100, 100), gfx::Size(), false); + TestIterate(data, gfx::Rect(0, 0, 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.TileBoundsWithBorder(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(0, 0, 40, 25), gfx::Rect(0, 0, 40, 25), 0); + TestDiff(data, gfx::Rect(0, 0, 40, 25), gfx::Rect(-100, -100, 200, 200), 0); + TestDiff(data, gfx::Rect(0, 0, 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(0, 0, 40, 25), gfx::Rect(-30, -20, 8, 8), 12); + TestDiff(data, gfx::Rect(0, 0, 40, 25), gfx::Rect(), 12); + + // Top left, remove 2x2 tiles + TestDiff(data, gfx::Rect(0, 0, 40, 25), gfx::Rect(0, 0, 20, 19), 8); + // Bottom right, remove 2x2 tiles + TestDiff(data, gfx::Rect(0, 0, 40, 25), gfx::Rect(20, 15, 20, 6), 8); + // Bottom left, remove 2x2 tiles + TestDiff(data, gfx::Rect(0, 0, 40, 25), gfx::Rect(0, 15, 20, 6), 8); + // Top right, remove 2x2 tiles + TestDiff(data, gfx::Rect(0, 0, 40, 25), gfx::Rect(20, 0, 20, 19), 8); + // Center, remove only one tile + TestDiff(data, gfx::Rect(0, 0, 40, 25), gfx::Rect(10, 10, 5, 5), 11); + + // Left column, flush left, removing two columns + TestDiff(data, gfx::Rect(0, 0, 40, 25), gfx::Rect(0, 0, 11, 25), 6); + // Middle column, removing two columns + TestDiff(data, gfx::Rect(0, 0, 40, 25), gfx::Rect(11, 0, 11, 25), 6); + // Right column, flush right, removing one column + TestDiff(data, gfx::Rect(0, 0, 40, 25), gfx::Rect(30, 0, 2, 25), 9); + + // Top row, flush top, removing one row + TestDiff(data, gfx::Rect(0, 0, 40, 25), gfx::Rect(0, 5, 40, 5), 8); + // Middle row, removing one row + TestDiff(data, gfx::Rect(0, 0, 40, 25), gfx::Rect(0, 13, 40, 5), 8); + // Bottom row, flush bottom, removing two rows + TestDiff(data, gfx::Rect(0, 0, 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) + TilingData data(gfx::Size(50, 60), gfx::Size(65, 110), 20); + + // Ignore one column, three rows + TestDiff(data, gfx::Rect(0, 30, 55, 80), gfx::Rect(5, 30, 5, 15), 9); + + // Knock out three columns, leaving only one. + TestDiff(data, gfx::Rect(10, 30, 55, 80), gfx::Rect(30, 59, 20, 1), 3); + + // Overlap all tiles with ignore rect. + TestDiff(data, gfx::Rect(0, 0, 65, 110), gfx::Rect(30, 59, 1, 2), 0); +} + +TEST(TilingDataTest, DifferenceIteratorOneTile) { + TilingData no_border(gfx::Size(1000, 1000), gfx::Size(30, 40), false); + TestDiff(no_border, gfx::Rect(0, 0, 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(0, 0, 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(0, 0, 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(0, 0, 100, 100), gfx::Rect(0, 0, 5, 5), 0); +} + +} // namespace +} // namespace cc diff --git a/chromium/cc/base/util.h b/chromium/cc/base/util.h new file mode 100644 index 00000000000..1d716ae2a42 --- /dev/null +++ b/chromium/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/chromium/cc/base/util_unittest.cc b/chromium/cc/base/util_unittest.cc new file mode 100644 index 00000000000..6665a6a458d --- /dev/null +++ b/chromium/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 |