blob: 8dfd33ea819929ebdcdca430a14aa364d56545ff [file] [log] [blame]
// Copyright 2015 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 "ui/views/animation/square_ink_drop_ripple.h"
#include <memory>
#include "base/stl_util.h"
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/geometry/size_f.h"
#include "ui/views/animation/ink_drop_ripple_observer.h"
#include "ui/views/animation/ink_drop_state.h"
#include "ui/views/animation/test/square_ink_drop_ripple_test_api.h"
#include "ui/views/animation/test/test_ink_drop_ripple_observer.h"
#include "ui/views/test/widget_test.h"
namespace views {
namespace test {
namespace {
using PaintedShape = views::test::SquareInkDropRippleTestApi::PaintedShape;
// Transforms a copy of |point| with |transform| and returns it.
gfx::Point TransformPoint(const gfx::Transform& transform,
const gfx::Point& point) {
gfx::Point transformed_point = point;
transform.TransformPoint(&transformed_point);
return transformed_point;
}
class SquareInkDropRippleCalculateTransformsTest : public WidgetTest {
public:
SquareInkDropRippleCalculateTransformsTest();
~SquareInkDropRippleCalculateTransformsTest() override;
protected:
// Half the width/height of the drawn ink drop.
static const int kHalfDrawnSize;
// The full width/height of the drawn ink drop.
static const int kDrawnSize;
// The radius of the rounded rectangle corners.
static const int kTransformedRadius;
// Half the width/height of the transformed ink drop.
static const int kHalfTransformedSize;
// The full width/height of the transformed ink drop.
static const int kTransformedSize;
// Constant points in the drawn space that will be transformed.
static const gfx::Point kDrawnCenterPoint;
static const gfx::Point kDrawnMidLeftPoint;
static const gfx::Point kDrawnMidRightPoint;
static const gfx::Point kDrawnTopMidPoint;
static const gfx::Point kDrawnBottomMidPoint;
// The test target.
SquareInkDropRipple ink_drop_ripple_;
// Provides internal access to the test target.
SquareInkDropRippleTestApi test_api_;
// The gfx::Transforms collection that is populated via the
// Calculate*Transforms() calls.
SquareInkDropRippleTestApi::InkDropTransforms transforms_;
private:
DISALLOW_COPY_AND_ASSIGN(SquareInkDropRippleCalculateTransformsTest);
};
const int SquareInkDropRippleCalculateTransformsTest::kHalfDrawnSize = 5;
const int SquareInkDropRippleCalculateTransformsTest::kDrawnSize =
2 * kHalfDrawnSize;
const int SquareInkDropRippleCalculateTransformsTest::kTransformedRadius = 10;
const int SquareInkDropRippleCalculateTransformsTest::kHalfTransformedSize =
100;
const int SquareInkDropRippleCalculateTransformsTest::kTransformedSize =
2 * kHalfTransformedSize;
const gfx::Point SquareInkDropRippleCalculateTransformsTest::kDrawnCenterPoint =
gfx::Point(kHalfDrawnSize, kHalfDrawnSize);
const gfx::Point
SquareInkDropRippleCalculateTransformsTest::kDrawnMidLeftPoint =
gfx::Point(0, kHalfDrawnSize);
const gfx::Point
SquareInkDropRippleCalculateTransformsTest::kDrawnMidRightPoint =
gfx::Point(kDrawnSize, kHalfDrawnSize);
const gfx::Point SquareInkDropRippleCalculateTransformsTest::kDrawnTopMidPoint =
gfx::Point(kHalfDrawnSize, 0);
const gfx::Point
SquareInkDropRippleCalculateTransformsTest::kDrawnBottomMidPoint =
gfx::Point(kHalfDrawnSize, kDrawnSize);
SquareInkDropRippleCalculateTransformsTest::
SquareInkDropRippleCalculateTransformsTest()
: ink_drop_ripple_(gfx::Size(kDrawnSize, kDrawnSize),
2,
gfx::Size(kHalfDrawnSize, kHalfDrawnSize),
1,
gfx::Point(),
SK_ColorBLACK,
0.175f),
test_api_(&ink_drop_ripple_) {}
SquareInkDropRippleCalculateTransformsTest::
~SquareInkDropRippleCalculateTransformsTest() = default;
} // namespace
TEST_F(SquareInkDropRippleCalculateTransformsTest,
TransformedPointsUsingTransformsFromCalculateCircleTransforms) {
test_api_.CalculateCircleTransforms(
gfx::Size(kTransformedSize, kTransformedSize), &transforms_);
struct {
PaintedShape shape;
gfx::Point center_point;
gfx::Point mid_left_point;
gfx::Point mid_right_point;
gfx::Point top_mid_point;
gfx::Point bottom_mid_point;
} test_cases[] = {
{PaintedShape::TOP_LEFT_CIRCLE, gfx::Point(0, 0),
gfx::Point(-kHalfTransformedSize, 0),
gfx::Point(kHalfTransformedSize, 0),
gfx::Point(0, -kHalfTransformedSize),
gfx::Point(0, kHalfTransformedSize)},
{PaintedShape::TOP_RIGHT_CIRCLE, gfx::Point(0, 0),
gfx::Point(-kHalfTransformedSize, 0),
gfx::Point(kHalfTransformedSize, 0),
gfx::Point(0, -kHalfTransformedSize),
gfx::Point(0, kHalfTransformedSize)},
{PaintedShape::BOTTOM_RIGHT_CIRCLE, gfx::Point(0, 0),
gfx::Point(-kHalfTransformedSize, 0),
gfx::Point(kHalfTransformedSize, 0),
gfx::Point(0, -kHalfTransformedSize),
gfx::Point(0, kHalfTransformedSize)},
{PaintedShape::BOTTOM_LEFT_CIRCLE, gfx::Point(0, 0),
gfx::Point(-kHalfTransformedSize, 0),
gfx::Point(kHalfTransformedSize, 0),
gfx::Point(0, -kHalfTransformedSize),
gfx::Point(0, kHalfTransformedSize)},
{PaintedShape::HORIZONTAL_RECT, gfx::Point(0, 0),
gfx::Point(-kHalfTransformedSize, 0),
gfx::Point(kHalfTransformedSize, 0), gfx::Point(0, 0), gfx::Point(0, 0)},
{PaintedShape::VERTICAL_RECT, gfx::Point(0, 0), gfx::Point(0, 0),
gfx::Point(0, 0), gfx::Point(0, -kHalfTransformedSize),
gfx::Point(0, kHalfTransformedSize)}};
for (const auto& test_case : test_cases) {
PaintedShape shape = test_case.shape;
SCOPED_TRACE(testing::Message() << " shape=" << shape);
gfx::Transform transform = transforms_[shape];
EXPECT_EQ(test_case.center_point,
TransformPoint(transform, kDrawnCenterPoint));
EXPECT_EQ(test_case.mid_left_point,
TransformPoint(transform, kDrawnMidLeftPoint));
EXPECT_EQ(test_case.mid_right_point,
TransformPoint(transform, kDrawnMidRightPoint));
EXPECT_EQ(test_case.top_mid_point,
TransformPoint(transform, kDrawnTopMidPoint));
EXPECT_EQ(test_case.bottom_mid_point,
TransformPoint(transform, kDrawnBottomMidPoint));
}
}
TEST_F(SquareInkDropRippleCalculateTransformsTest,
TransformedPointsUsingTransformsFromCalculateRectTransforms) {
test_api_.CalculateRectTransforms(
gfx::Size(kTransformedSize, kTransformedSize), kTransformedRadius,
&transforms_);
const int x_offset = kHalfTransformedSize - kTransformedRadius;
const int y_offset = kHalfTransformedSize - kTransformedRadius;
struct {
PaintedShape shape;
gfx::Point center_point;
gfx::Point mid_left_point;
gfx::Point mid_right_point;
gfx::Point top_mid_point;
gfx::Point bottom_mid_point;
} test_cases[] = {
{PaintedShape::TOP_LEFT_CIRCLE, gfx::Point(-x_offset, -y_offset),
gfx::Point(-kHalfTransformedSize, -y_offset),
gfx::Point(-x_offset + kTransformedRadius, -y_offset),
gfx::Point(-x_offset, -kHalfTransformedSize),
gfx::Point(-x_offset, -y_offset + kTransformedRadius)},
{PaintedShape::TOP_RIGHT_CIRCLE, gfx::Point(x_offset, -y_offset),
gfx::Point(x_offset - kTransformedRadius, -y_offset),
gfx::Point(kHalfTransformedSize, -y_offset),
gfx::Point(x_offset, -kHalfTransformedSize),
gfx::Point(x_offset, -y_offset + kTransformedRadius)},
{PaintedShape::BOTTOM_RIGHT_CIRCLE, gfx::Point(x_offset, y_offset),
gfx::Point(x_offset - kTransformedRadius, y_offset),
gfx::Point(kHalfTransformedSize, y_offset),
gfx::Point(x_offset, y_offset - kTransformedRadius),
gfx::Point(x_offset, kHalfTransformedSize)},
{PaintedShape::BOTTOM_LEFT_CIRCLE, gfx::Point(-x_offset, y_offset),
gfx::Point(-kHalfTransformedSize, y_offset),
gfx::Point(-x_offset + kTransformedRadius, y_offset),
gfx::Point(-x_offset, y_offset - kTransformedRadius),
gfx::Point(-x_offset, kHalfTransformedSize)},
{PaintedShape::HORIZONTAL_RECT, gfx::Point(0, 0),
gfx::Point(-kHalfTransformedSize, 0),
gfx::Point(kHalfTransformedSize, 0), gfx::Point(0, -y_offset),
gfx::Point(0, y_offset)},
{PaintedShape::VERTICAL_RECT, gfx::Point(0, 0), gfx::Point(-x_offset, 0),
gfx::Point(x_offset, 0), gfx::Point(0, -kHalfTransformedSize),
gfx::Point(0, kHalfTransformedSize)}};
for (const auto& test_case : test_cases) {
PaintedShape shape = test_case.shape;
SCOPED_TRACE(testing::Message() << " shape=" << shape);
gfx::Transform transform = transforms_[shape];
EXPECT_EQ(test_case.center_point,
TransformPoint(transform, kDrawnCenterPoint));
EXPECT_EQ(test_case.mid_left_point,
TransformPoint(transform, kDrawnMidLeftPoint));
EXPECT_EQ(test_case.mid_right_point,
TransformPoint(transform, kDrawnMidRightPoint));
EXPECT_EQ(test_case.top_mid_point,
TransformPoint(transform, kDrawnTopMidPoint));
EXPECT_EQ(test_case.bottom_mid_point,
TransformPoint(transform, kDrawnBottomMidPoint));
}
}
TEST_F(SquareInkDropRippleCalculateTransformsTest, RippleIsPixelAligned) {
// Create a ripple that would not naturally be pixel aligned at a fractional
// scale factor.
const gfx::Point center(14, 14);
const gfx::Rect drawn_rect_bounds(0, 0, 10, 10);
SquareInkDropRipple ink_drop_ripple(drawn_rect_bounds.size(), 2,
gfx::Size(1, 1), // unimportant
1, center, SK_ColorBLACK, 0.175f);
SquareInkDropRippleTestApi test_api(&ink_drop_ripple);
// Add to a widget so we can control the DSF.
auto* widget = CreateTopLevelPlatformWidget();
widget->SetBounds(gfx::Rect(0, 0, 100, 100));
auto* host_view = new View();
host_view->SetPaintToLayer();
widget->GetContentsView()->AddChildView(host_view);
host_view->layer()->Add(ink_drop_ripple.GetRootLayer());
// Test a variety of scale factors and target transform sizes.
std::vector<float> dsfs({1.0f, 1.25f, 1.5f, 2.0f, 3.0f});
std::vector<int> target_sizes({5, 7, 11, 13, 31});
for (float dsf : dsfs) {
for (int target_size : target_sizes) {
SCOPED_TRACE(testing::Message()
<< "target_size=" << target_size << " dsf=" << dsf);
host_view->layer()->GetCompositor()->SetScaleAndSize(
dsf, gfx::Size(100, 100), viz::LocalSurfaceIdAllocation());
SquareInkDropRippleTestApi::InkDropTransforms transforms;
test_api.CalculateRectTransforms(gfx::Size(target_size, target_size), 0,
&transforms);
// Checks that a rectangle is integer-aligned modulo floating point error.
auto verify_bounds = [](const gfx::RectF& rect) {
float float_min_x = rect.x();
float float_min_y = rect.y();
float float_max_x = rect.right();
float float_max_y = rect.bottom();
int min_x = gfx::ToRoundedInt(float_min_x);
int min_y = gfx::ToRoundedInt(float_min_y);
int max_x = gfx::ToRoundedInt(float_max_x);
int max_y = gfx::ToRoundedInt(float_max_y);
EXPECT_LT(std::abs(min_x - float_min_x), 0.01f);
EXPECT_LT(std::abs(min_y - float_min_y), 0.01f);
EXPECT_LT(std::abs(max_x - float_max_x), 0.01f);
EXPECT_LT(std::abs(max_y - float_max_y), 0.01f);
};
// When you feed in the bounds of the rectangle layer delegate, no matter
// what the target size was you should get an integer aligned bounding
// box.
gfx::Transform transform = transforms[PaintedShape::HORIZONTAL_RECT];
gfx::RectF horizontal_rect(drawn_rect_bounds);
transform.TransformRect(&horizontal_rect);
horizontal_rect.Scale(dsf);
verify_bounds(horizontal_rect);
transform = transforms[PaintedShape::VERTICAL_RECT];
gfx::RectF vertical_rect(drawn_rect_bounds);
transform.TransformRect(&vertical_rect);
vertical_rect.Scale(dsf);
verify_bounds(vertical_rect);
}
}
widget->CloseNow();
}
} // namespace test
} // namespace views