summaryrefslogtreecommitdiffstats
path: root/ui
diff options
context:
space:
mode:
Diffstat (limited to 'ui')
-rw-r--r--ui/gfx/interpolated_transform.cc219
-rw-r--r--ui/gfx/interpolated_transform.h195
-rw-r--r--ui/gfx/interpolated_transform_unittest.cc112
-rw-r--r--ui/ui_gfx.gypi2
-rw-r--r--ui/ui_unittests.gypi1
5 files changed, 529 insertions, 0 deletions
diff --git a/ui/gfx/interpolated_transform.cc b/ui/gfx/interpolated_transform.cc
new file mode 100644
index 0000000..1f37f84
--- /dev/null
+++ b/ui/gfx/interpolated_transform.cc
@@ -0,0 +1,219 @@
+// Copyright (c) 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 "ui/gfx/interpolated_transform.h"
+
+#include "base/logging.h"
+#include "ui/base/animation/tween.h"
+
+namespace ui {
+
+///////////////////////////////////////////////////////////////////////////////
+// InterpolatedTransform
+//
+
+InterpolatedTransform::InterpolatedTransform()
+ : start_time_(0.0f),
+ end_time_(1.0f) {
+}
+
+InterpolatedTransform::InterpolatedTransform(float start_time,
+ float end_time)
+ : start_time_(start_time),
+ end_time_(end_time) {
+}
+
+InterpolatedTransform::~InterpolatedTransform() {}
+
+ui::Transform InterpolatedTransform::Interpolate(float t) const {
+ ui::Transform result = InterpolateButDoNotCompose(t);
+ if (child_.get()) {
+ result.ConcatTransform(child_->Interpolate(t));
+ }
+ return result;
+}
+
+void InterpolatedTransform::SetChild(InterpolatedTransform* child) {
+ child_.reset(child);
+}
+
+inline float InterpolatedTransform::ValueBetween(float time,
+ float start_value,
+ float end_value) const {
+ // can't handle NaN
+ DCHECK(time == time && start_time_ == start_time_ && end_time_ == end_time_);
+ if (time != time || start_time_ != start_time_ || end_time_ != end_time_)
+ return start_value;
+
+ // Ok if equal -- we'll get a step function. Note: if end_time_ ==
+ // start_time_ == x, then if none of the numbers are NaN, then it
+ // must be true that time < x or time >= x, so we will return early
+ // due to one of the following if statements.
+ DCHECK(end_time_ >= start_time_);
+
+ if (time < start_time_)
+ return start_value;
+
+ if (time >= end_time_)
+ return end_value;
+
+ float t = (time - start_time_) / (end_time_ - start_time_);
+ return static_cast<float>(Tween::ValueBetween(t, start_value, end_value));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// InterpolatedRotation
+//
+
+InterpolatedRotation::InterpolatedRotation(float start_degrees,
+ float end_degrees)
+ : InterpolatedTransform(),
+ start_degrees_(start_degrees),
+ end_degrees_(end_degrees) {
+}
+
+InterpolatedRotation::InterpolatedRotation(float start_degrees,
+ float end_degrees,
+ float start_time,
+ float end_time)
+ : InterpolatedTransform(start_time, end_time),
+ start_degrees_(start_degrees),
+ end_degrees_(end_degrees) {
+}
+
+InterpolatedRotation::~InterpolatedRotation() {}
+
+ui::Transform InterpolatedRotation::InterpolateButDoNotCompose(float t) const {
+ ui::Transform result;
+ result.SetRotate(ValueBetween(t, start_degrees_, end_degrees_));
+ return result;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// InterpolatedScale
+//
+
+InterpolatedScale::InterpolatedScale(float start_scale,
+ float end_scale)
+ : InterpolatedTransform(),
+ start_scale_(start_scale),
+ end_scale_(end_scale) {
+}
+
+InterpolatedScale::InterpolatedScale(float start_scale,
+ float end_scale,
+ float start_time,
+ float end_time)
+ : InterpolatedTransform(start_time, end_time),
+ start_scale_(start_scale),
+ end_scale_(end_scale) {
+}
+
+InterpolatedScale::~InterpolatedScale() {}
+
+ui::Transform InterpolatedScale::InterpolateButDoNotCompose(float t) const {
+ ui::Transform result;
+ float interpolated_scale = ValueBetween(t, start_scale_, end_scale_);
+ // TODO(vollick) 3d xforms.
+ result.SetScale(interpolated_scale, interpolated_scale);
+ return result;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// InterpolatedTranslation
+//
+
+InterpolatedTranslation::InterpolatedTranslation(const gfx::Point& start_pos,
+ const gfx::Point& end_pos)
+ : InterpolatedTransform(),
+ start_pos_(start_pos),
+ end_pos_(end_pos) {
+}
+
+InterpolatedTranslation::InterpolatedTranslation(const gfx::Point& start_pos,
+ const gfx::Point& end_pos,
+ float start_time,
+ float end_time)
+ : InterpolatedTransform(start_time, end_time),
+ start_pos_(start_pos),
+ end_pos_(end_pos) {
+}
+
+InterpolatedTranslation::~InterpolatedTranslation() {}
+
+ui::Transform
+InterpolatedTranslation::InterpolateButDoNotCompose(float t) const {
+ ui::Transform result;
+ // TODO(vollick) 3d xforms.
+ result.SetTranslate(ValueBetween(t, start_pos_.x(), end_pos_.x()),
+ ValueBetween(t, start_pos_.y(), end_pos_.y()));
+
+ return result;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// InterpolatedConstantTransform
+//
+
+InterpolatedConstantTransform::InterpolatedConstantTransform(
+ const ui::Transform& transform)
+ : InterpolatedTransform(),
+ transform_(transform) {
+}
+
+ui::Transform
+InterpolatedConstantTransform::InterpolateButDoNotCompose(float t) const {
+ return transform_;
+}
+
+InterpolatedConstantTransform::~InterpolatedConstantTransform() {}
+
+///////////////////////////////////////////////////////////////////////////////
+// InterpolatedTransformAboutPivot
+//
+
+InterpolatedTransformAboutPivot::InterpolatedTransformAboutPivot(
+ const gfx::Point& pivot,
+ InterpolatedTransform* transform)
+ : InterpolatedTransform() {
+ Init(pivot, transform);
+}
+
+InterpolatedTransformAboutPivot::InterpolatedTransformAboutPivot(
+ const gfx::Point& pivot,
+ InterpolatedTransform* transform,
+ float start_time,
+ float end_time)
+ : InterpolatedTransform() {
+ Init(pivot, transform);
+}
+
+InterpolatedTransformAboutPivot::~InterpolatedTransformAboutPivot() {}
+
+ui::Transform
+InterpolatedTransformAboutPivot::InterpolateButDoNotCompose(float t) const {
+ if (transform_.get()) {
+ return transform_->Interpolate(t);
+ }
+ return ui::Transform();
+}
+
+void InterpolatedTransformAboutPivot::Init(const gfx::Point& pivot,
+ InterpolatedTransform* xform) {
+ ui::Transform to_pivot;
+ ui::Transform from_pivot;
+ to_pivot.SetTranslate(-pivot.x(), -pivot.y());
+ from_pivot.SetTranslate(pivot.x(), pivot.y());
+
+ scoped_ptr<InterpolatedTransform> pre_transform(
+ new InterpolatedConstantTransform(to_pivot));
+ scoped_ptr<InterpolatedTransform> post_transform(
+ new InterpolatedConstantTransform(from_pivot));
+
+ pre_transform->SetChild(xform);
+ xform->SetChild(post_transform.release());
+ transform_.reset(pre_transform.release());
+}
+
+} // namespace ui
diff --git a/ui/gfx/interpolated_transform.h b/ui/gfx/interpolated_transform.h
new file mode 100644
index 0000000..2a0a6b5
--- /dev/null
+++ b/ui/gfx/interpolated_transform.h
@@ -0,0 +1,195 @@
+// Copyright (c) 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 UI_GFX_INTERPOLATED_TRANSFORM_H_
+#define UI_GFX_INTERPOLATED_TRANSFORM_H_
+#pragma once
+
+#include "ui/gfx/transform.h"
+#include "base/memory/scoped_ptr.h"
+#include "ui/gfx/point.h"
+
+namespace ui {
+
+///////////////////////////////////////////////////////////////////////////////
+// class InterpolatedTransform
+//
+// Abstract base class for transforms that animate over time. These
+// interpolated transforms can be combined to allow for more sophisticated
+// animations. For example, you might combine a rotation of 90 degrees between
+// times 0 and 1, with a scale from 1 to 0.3 between times 0 and 0.25 and a
+// scale from 0.3 to 1 from between times 0.75 and 1.
+//
+///////////////////////////////////////////////////////////////////////////////
+class InterpolatedTransform {
+ public:
+ InterpolatedTransform();
+ // The interpolated transform varies only when t in (start_time, end_time).
+ // If t <= start_time, Interpolate(t) will return the initial transform, and
+ // if t >= end_time, Interpolate(t) will return the final transform.
+ InterpolatedTransform(float start_time, float end_time);
+ virtual ~InterpolatedTransform();
+
+ // Returns the interpolated transform at time t. Note: not virtual.
+ ui::Transform Interpolate(float t) const;
+
+ // The Intepolate ultimately returns the product of our transform at time t
+ // and our child's transform at time t (if we have one).
+ //
+ // This function takes ownership of the passed InterpolatedTransform.
+ void SetChild(InterpolatedTransform* child);
+
+ protected:
+ // Calculates the interpolated transform without considering our child.
+ virtual ui::Transform InterpolateButDoNotCompose(float t) const = 0;
+
+ // If time in (start_time_, end_time_], this function linearly interpolates
+ // between start_value and end_value. More precisely it returns
+ // (1 - t) * start_value + t * end_value where
+ // t = (start_time_ - time) / (end_time_ - start_time_).
+ // If time < start_time_ it returns start_value, and if time >= end_time_
+ // it returns end_value.
+ float ValueBetween(float time, float start_value, float end_value) const;
+
+ private:
+ const float start_time_;
+ const float end_time_;
+
+ // The child transform. If you consider an interpolated transform as a
+ // function of t. If, without a child, we are f(t), and our child is
+ // g(t), then with a child we become f'(t) = f(t) * g(t). Using a child
+ // transform, we can chain collections of transforms together.
+ scoped_ptr<InterpolatedTransform> child_;
+
+ DISALLOW_COPY_AND_ASSIGN(InterpolatedTransform);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// class InterpolatedRotation
+//
+// Represents an animated rotation.
+//
+///////////////////////////////////////////////////////////////////////////////
+class InterpolatedRotation : public InterpolatedTransform {
+ public:
+ InterpolatedRotation(float start_degrees, float end_degrees);
+ InterpolatedRotation(float start_degrees,
+ float end_degrees,
+ float start_time,
+ float end_time);
+ virtual ~InterpolatedRotation();
+
+ protected:
+ virtual ui::Transform InterpolateButDoNotCompose(float t) const OVERRIDE;
+
+ private:
+ const float start_degrees_;
+ const float end_degrees_;
+
+ DISALLOW_COPY_AND_ASSIGN(InterpolatedRotation);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// class InterpolatedScale
+//
+// Represents an animated scale.
+//
+///////////////////////////////////////////////////////////////////////////////
+class InterpolatedScale : public InterpolatedTransform {
+ public:
+ InterpolatedScale(float start_scale, float end_scale);
+ InterpolatedScale(float start_scale,
+ float end_scale,
+ float start_time,
+ float end_time);
+ virtual ~InterpolatedScale();
+
+ protected:
+ virtual ui::Transform InterpolateButDoNotCompose(float t) const OVERRIDE;
+
+ private:
+ const float start_scale_;
+ const float end_scale_;
+
+ DISALLOW_COPY_AND_ASSIGN(InterpolatedScale);
+};
+
+class InterpolatedTranslation : public InterpolatedTransform {
+ public:
+ InterpolatedTranslation(const gfx::Point& start_pos,
+ const gfx::Point& end_pos);
+ InterpolatedTranslation(const gfx::Point& start_pos,
+ const gfx::Point& end_pos,
+ float start_time,
+ float end_time);
+ virtual ~InterpolatedTranslation();
+
+ protected:
+ virtual ui::Transform InterpolateButDoNotCompose(float t) const OVERRIDE;
+
+ private:
+ const gfx::Point start_pos_;
+ const gfx::Point end_pos_;
+
+ DISALLOW_COPY_AND_ASSIGN(InterpolatedTranslation);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// class InterpolatedConstantTransform
+//
+// Represents a transform that is constant over time. This is only useful when
+// composed with other interpolated transforms.
+//
+// See InterpolatedTransformAboutPivot for an example of its usage.
+//
+///////////////////////////////////////////////////////////////////////////////
+class InterpolatedConstantTransform : public InterpolatedTransform {
+ public:
+ InterpolatedConstantTransform(const ui::Transform& transform);
+ virtual ~InterpolatedConstantTransform();
+
+ protected:
+ virtual ui::Transform InterpolateButDoNotCompose(float t) const OVERRIDE;
+
+ private:
+ const ui::Transform transform_;
+
+ DISALLOW_COPY_AND_ASSIGN(InterpolatedConstantTransform);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// class InterpolatedTransformAboutPivot
+//
+// Represents an animated transform with a transformed origin. Essentially,
+// at each time, t, the interpolated transform is created by composing
+// P * T * P^-1 where P is a constant transform to the new origin.
+//
+///////////////////////////////////////////////////////////////////////////////
+class InterpolatedTransformAboutPivot : public InterpolatedTransform {
+ public:
+ // Takes ownership of the passed transform.
+ InterpolatedTransformAboutPivot(const gfx::Point& pivot,
+ InterpolatedTransform* transform);
+
+ // Takes ownership of the passed transform.
+ InterpolatedTransformAboutPivot(const gfx::Point& pivot,
+ InterpolatedTransform* transform,
+ float start_time,
+ float end_time);
+ virtual ~InterpolatedTransformAboutPivot();
+
+ protected:
+ virtual ui::Transform InterpolateButDoNotCompose(float t) const OVERRIDE;
+
+ private:
+ void Init(const gfx::Point& pivot, InterpolatedTransform* transform);
+
+ scoped_ptr<InterpolatedTransform> transform_;
+
+ DISALLOW_COPY_AND_ASSIGN(InterpolatedTransformAboutPivot);
+};
+
+} // namespace ui
+
+#endif // UI_GFX_INTERPOLATED_TRANSFORM_H_
diff --git a/ui/gfx/interpolated_transform_unittest.cc b/ui/gfx/interpolated_transform_unittest.cc
new file mode 100644
index 0000000..1aff853
--- /dev/null
+++ b/ui/gfx/interpolated_transform_unittest.cc
@@ -0,0 +1,112 @@
+// Copyright (c) 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 "ui/gfx/interpolated_transform.h"
+
+#include "base/basictypes.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const static float EPSILON = 1e-6f;
+
+bool ApproximatelyEqual(float lhs, float rhs) {
+ if (lhs == 0)
+ return fabs(rhs) < EPSILON;
+ if (rhs == 0)
+ return fabs(lhs) < EPSILON;
+ return fabs(lhs - rhs) / std::max(fabs(rhs), fabs(lhs)) < EPSILON;
+}
+
+bool ApproximatelyEqual(const ui::Transform& lhs, const ui::Transform& rhs) {
+ for (int i = 0; i < 9; ++i) {
+ if (!ApproximatelyEqual(lhs.matrix()[i], rhs.matrix()[i]))
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
+TEST(InterpolatedTransformTest, InterpolatedRotation) {
+ ui::InterpolatedRotation interpolated_rotation(0, 100);
+ ui::InterpolatedRotation interpolated_rotation_diff_start_end(
+ 0, 100, 100, 200);
+
+ for (int i = 0; i <= 100; ++i) {
+ ui::Transform rotation;
+ rotation.SetRotate(i);
+ ui::Transform interpolated = interpolated_rotation.Interpolate(i / 100.0f);
+ EXPECT_TRUE(ApproximatelyEqual(rotation, interpolated));
+ interpolated = interpolated_rotation_diff_start_end.Interpolate(i + 100);
+ EXPECT_TRUE(ApproximatelyEqual(rotation, interpolated));
+ }
+}
+
+TEST(InterpolatedTransformTest, InterpolatedScale) {
+ ui::InterpolatedScale interpolated_scale(0, 100);
+ ui::InterpolatedScale interpolated_scale_diff_start_end(
+ 0, 100, 100, 200);
+
+ for (int i = 0; i <= 100; ++i) {
+ ui::Transform scale;
+ scale.SetScale(i, i);
+ ui::Transform interpolated = interpolated_scale.Interpolate(i / 100.0f);
+ EXPECT_TRUE(ApproximatelyEqual(scale, interpolated));
+ interpolated = interpolated_scale_diff_start_end.Interpolate(i + 100);
+ EXPECT_TRUE(ApproximatelyEqual(scale, interpolated));
+ }
+}
+
+TEST(InterpolatedTransformTest, InterpolatedTranslate) {
+ ui::InterpolatedTranslation interpolated_xform(gfx::Point(0, 0),
+ gfx::Point(100, 100));
+
+ ui::InterpolatedTranslation interpolated_xform_diff_start_end(
+ gfx::Point(0, 0), gfx::Point(100, 100), 100, 200);
+
+ for (int i = 0; i <= 100; ++i) {
+ ui::Transform xform;
+ xform.SetTranslate(i, i);
+ ui::Transform interpolated = interpolated_xform.Interpolate(i / 100.0f);
+ EXPECT_TRUE(ApproximatelyEqual(xform, interpolated));
+ interpolated = interpolated_xform_diff_start_end.Interpolate(i + 100);
+ EXPECT_TRUE(ApproximatelyEqual(xform, interpolated));
+ }
+}
+
+TEST(InterpolatedTransformTest, InterpolatedRotationAboutPivot) {
+ gfx::Point pivot(100, 100);
+ gfx::Point above_pivot(100, 200);
+ ui::InterpolatedRotation rot(0, 90);
+ ui::InterpolatedTransformAboutPivot interpolated_xform(
+ pivot,
+ new ui::InterpolatedRotation(0, 90));
+ ui::Transform result = interpolated_xform.Interpolate(0.0f);
+ EXPECT_TRUE(ApproximatelyEqual(ui::Transform(), result));
+ result = interpolated_xform.Interpolate(1.0f);
+ gfx::Point expected_result = pivot;
+ EXPECT_TRUE(result.TransformPoint(&pivot));
+ EXPECT_EQ(expected_result, pivot);
+ expected_result = gfx::Point(0, 100);
+ EXPECT_TRUE(result.TransformPoint(&above_pivot));
+ EXPECT_EQ(expected_result, above_pivot);
+}
+
+TEST(InterpolatedTransformTest, InterpolatedScaleAboutPivot) {
+ gfx::Point pivot(100, 100);
+ gfx::Point above_pivot(100, 200);
+ ui::InterpolatedTransformAboutPivot interpolated_xform(
+ pivot,
+ new ui::InterpolatedScale(1, 2));
+ ui::Transform result = interpolated_xform.Interpolate(0.0f);
+ EXPECT_TRUE(ApproximatelyEqual(ui::Transform(), result));
+ result = interpolated_xform.Interpolate(1.0f);
+ gfx::Point expected_result = pivot;
+ EXPECT_TRUE(result.TransformPoint(&pivot));
+ EXPECT_EQ(expected_result, pivot);
+ expected_result = gfx::Point(100, 300);
+ EXPECT_TRUE(result.TransformPoint(&above_pivot));
+ EXPECT_EQ(expected_result, above_pivot);
+}
diff --git a/ui/ui_gfx.gypi b/ui/ui_gfx.gypi
index c9907c3..2d33805bf 100644
--- a/ui/ui_gfx.gypi
+++ b/ui/ui_gfx.gypi
@@ -81,6 +81,8 @@
'gfx/image/image_util.h',
'gfx/insets.cc',
'gfx/insets.h',
+ 'gfx/interpolated_transform.h',
+ 'gfx/interpolated_transform.cc',
'gfx/mac/nsimage_cache.h',
'gfx/mac/nsimage_cache.mm',
'gfx/mac/scoped_ns_disable_screen_updates.h',
diff --git a/ui/ui_unittests.gypi b/ui/ui_unittests.gypi
index 8ea6cb2..3fb1c4a 100644
--- a/ui/ui_unittests.gypi
+++ b/ui/ui_unittests.gypi
@@ -46,6 +46,7 @@
'gfx/image/image_unittest_util.h',
'gfx/image/image_unittest_util.cc',
'gfx/insets_unittest.cc',
+ 'gfx/interpolated_transform_unittest.cc',
'gfx/rect_unittest.cc',
'gfx/run_all_unittests.cc',
'gfx/skbitmap_operations_unittest.cc',