Commit ca690f9a authored by avallee@chromium.org's avatar avallee@chromium.org

Add support for inverse transform animations.

This allows creation of a transform and its inverse that would be used
to counter-animate some child layer.

BUG=270857

Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=220124

R=vollick@chromium.org, wittman@chromium.org

Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=220479

Review URL: https://chromiumcodereview.appspot.com/22861008

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@221309 0039d316-1c4b-4281-b951-d872f2087c98
parent 89187032
......@@ -123,6 +123,7 @@
'test/test_layer_animation_observer.h',
'test/test_utils.cc',
'test/test_utils.h',
'transform_animation_curve_adapter_unittest.cc',
],
'conditions': [
# osmesa GL implementation is used on linux.
......
......@@ -394,6 +394,10 @@ class ThreadedLayerAnimationElement : public LayerAnimationElement {
}
protected:
explicit ThreadedLayerAnimationElement(const LayerAnimationElement& element)
: LayerAnimationElement(element) {
}
virtual bool OnProgress(double t,
LayerAnimationDelegate* delegate) OVERRIDE {
if (t < 1.0)
......@@ -563,6 +567,117 @@ class ThreadedTransformTransition : public ThreadedLayerAnimationElement {
DISALLOW_COPY_AND_ASSIGN(ThreadedTransformTransition);
};
// InverseTransformTransision --------------------------------------------------
class InverseTransformTransition : public ThreadedLayerAnimationElement {
public:
InverseTransformTransition(const gfx::Transform& base_transform,
const LayerAnimationElement* uninverted_transition)
: ThreadedLayerAnimationElement(*uninverted_transition),
base_transform_(base_transform),
uninverted_transition_(CheckAndCast(uninverted_transition)) {
}
virtual ~InverseTransformTransition() {}
protected:
virtual void OnStart(LayerAnimationDelegate* delegate) OVERRIDE {
gfx::Transform start(delegate->GetTransformForAnimation());
effective_start_ = base_transform_ * start;
TargetValue target;
uninverted_transition_->GetTargetValue(&target);
base_target_ = target.transform;
set_tween_type(uninverted_transition_->tween_type());
float device_scale_factor = delegate->GetDeviceScaleFactor();
const gfx::Transform cc_base_start = Layer::ConvertTransformToCCTransform(
base_transform_,
device_scale_factor);
const gfx::Transform cc_base_target = Layer::ConvertTransformToCCTransform(
base_target_,
device_scale_factor);
TransformAnimationCurveAdapter base_curve(tween_type(),
cc_base_start,
cc_base_target,
duration());
const gfx::Transform cc_start = Layer::ConvertTransformToCCTransform(
start, device_scale_factor);
animation_curve_.reset(new InverseTransformCurveAdapter(
base_curve, cc_start, duration()));
computed_target_transform_ = ComputeWithBaseTransform(effective_start_,
base_target_);
}
virtual void OnAbort(LayerAnimationDelegate* delegate) OVERRIDE {
if (delegate && Started()) {
ThreadedLayerAnimationElement::OnAbort(delegate);
delegate->SetTransformFromAnimation(ComputeCurrentTransform());
}
}
virtual void OnEnd(LayerAnimationDelegate* delegate) OVERRIDE {
delegate->SetTransformFromAnimation(computed_target_transform_);
}
virtual scoped_ptr<cc::Animation> CreateCCAnimation() OVERRIDE {
scoped_ptr<cc::Animation> animation(
cc::Animation::Create(animation_curve_->Clone(),
animation_id(),
animation_group_id(),
cc::Animation::Transform));
return animation.Pass();
}
virtual void OnGetTarget(TargetValue* target) const OVERRIDE {
target->transform = computed_target_transform_;
}
private:
gfx::Transform ComputeCurrentTransform() const {
gfx::Transform base_current = Tween::ValueBetween(
Tween::CalculateValue(tween_type(), last_progressed_fraction()),
base_transform_,
base_target_);
return ComputeWithBaseTransform(effective_start_, base_current);
}
gfx::Transform ComputeWithBaseTransform(gfx::Transform start,
gfx::Transform target) const {
gfx::Transform to_return(gfx::Transform::kSkipInitialization);
DCHECK(target.GetInverse(&to_return));
to_return.PreconcatTransform(start);
return to_return;
}
static AnimatableProperties GetProperties() {
AnimatableProperties properties;
properties.insert(LayerAnimationElement::TRANSFORM);
return properties;
}
static const ThreadedTransformTransition* CheckAndCast(
const LayerAnimationElement* element) {
const AnimatableProperties& properties = element->properties();
DCHECK(properties.find(TRANSFORM) != properties.end());
return static_cast<const ThreadedTransformTransition*>(element);
}
gfx::Transform effective_start_;
gfx::Transform computed_target_transform_;
const gfx::Transform base_transform_;
gfx::Transform base_target_;
scoped_ptr<cc::AnimationCurve> animation_curve_;
const ThreadedTransformTransition* const uninverted_transition_;
DISALLOW_COPY_AND_ASSIGN(InverseTransformTransition);
};
} // namespace
// LayerAnimationElement::TargetValue ------------------------------------------
......@@ -601,6 +716,17 @@ LayerAnimationElement::LayerAnimationElement(
last_progressed_fraction_(0.0) {
}
LayerAnimationElement::LayerAnimationElement(
const LayerAnimationElement &element)
: first_frame_(element.first_frame_),
properties_(element.properties_),
duration_(element.duration_),
tween_type_(element.tween_type_),
animation_id_(cc::AnimationIdProvider::NextAnimationId()),
animation_group_id_(element.animation_group_id_),
last_progressed_fraction_(element.last_progressed_fraction_) {
}
LayerAnimationElement::~LayerAnimationElement() {
}
......@@ -728,6 +854,13 @@ LayerAnimationElement* LayerAnimationElement::CreateTransformElement(
return new ThreadedTransformTransition(transform, duration);
}
// static
LayerAnimationElement* LayerAnimationElement::CreateInverseTransformElement(
const gfx::Transform& base_transform,
const LayerAnimationElement* uninverted_transition) {
return new InverseTransformTransition(base_transform, uninverted_transition);
}
// static
LayerAnimationElement*
LayerAnimationElement::CreateInterpolatedTransformElement(
......
......@@ -57,6 +57,7 @@ class COMPOSITOR_EXPORT LayerAnimationElement {
LayerAnimationElement(const AnimatableProperties& properties,
base::TimeDelta duration);
virtual ~LayerAnimationElement();
// Creates an element that transitions to the given transform. The caller owns
......@@ -65,6 +66,14 @@ class COMPOSITOR_EXPORT LayerAnimationElement {
const gfx::Transform& transform,
base::TimeDelta duration);
// Creates an element that counters a transition to the given transform.
// This element maintains the invariant uninverted_transition->at(t) *
// this->at(t) == base_transform * this->at(t_start) for any t. The caller
// owns the return value.
static LayerAnimationElement* CreateInverseTransformElement(
const gfx::Transform& base_transform,
const LayerAnimationElement* uninverted_transition);
// Creates an element that transitions to another in a way determined by an
// interpolated transform. The element accepts ownership of the interpolated
// transform. NB: at every step, the interpolated transform clobbers the
......@@ -178,6 +187,8 @@ class COMPOSITOR_EXPORT LayerAnimationElement {
int animation_group_id() const { return animation_group_id_; }
void set_animation_group_id(int id) { animation_group_id_ = id; }
base::TimeDelta duration() const { return duration_; }
// The fraction of the animation that has been completed after the last
// call made to {Progress, ProgressToEnd}.
double last_progressed_fraction() const { return last_progressed_fraction_; }
......@@ -190,11 +201,11 @@ class COMPOSITOR_EXPORT LayerAnimationElement {
virtual void OnGetTarget(TargetValue* target) const = 0;
virtual void OnAbort(LayerAnimationDelegate* delegate) = 0;
base::TimeDelta duration() const { return duration_; }
// Actually start the animation, dispatching to another thread if needed.
virtual void RequestEffectiveStart(LayerAnimationDelegate* delegate);
LayerAnimationElement(const LayerAnimationElement& element);
private:
// For debugging purposes, we sometimes alter the duration we actually use.
// For example, during tests we often set duration = 0, and it is sometimes
......@@ -215,7 +226,7 @@ class COMPOSITOR_EXPORT LayerAnimationElement {
double last_progressed_fraction_;
DISALLOW_COPY_AND_ASSIGN(LayerAnimationElement);
DISALLOW_ASSIGN(LayerAnimationElement);
};
} // namespace ui
......
......@@ -10,6 +10,7 @@
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/compositor/layer_animation_delegate.h"
#include "ui/compositor/scoped_animation_duration_scale_mode.h"
#include "ui/compositor/test/test_layer_animation_delegate.h"
#include "ui/compositor/test/test_utils.h"
#include "ui/gfx/rect.h"
......@@ -23,7 +24,7 @@ namespace {
// that the element can be reused after it completes.
TEST(LayerAnimationElementTest, TransformElement) {
TestLayerAnimationDelegate delegate;
gfx::Transform start_transform, target_transform, middle_transform;
gfx::Transform start_transform, target_transform;
start_transform.Rotate(-30.0);
target_transform.Rotate(30.0);
base::TimeTicks start_time;
......@@ -65,6 +66,36 @@ TEST(LayerAnimationElementTest, TransformElement) {
CheckApproximatelyEqual(target_transform, target_value.transform);
}
// Ensures that duration is copied correctly.
TEST(LayerAnimationElementTest, InverseElementDurationNoScale) {
gfx::Transform transform;
base::TimeDelta delta;
scoped_ptr<LayerAnimationElement> base_element(
LayerAnimationElement::CreateTransformElement(transform, delta));
scoped_ptr<LayerAnimationElement> inverse_element(
LayerAnimationElement::CreateInverseTransformElement(transform,
base_element.get()));
EXPECT_EQ(base_element->duration(), inverse_element->duration());
}
// Ensures that duration is copied correctly and not double scaled.
TEST(LayerAnimationElementTest, InverseElementDurationScaled) {
gfx::Transform transform;
base::TimeDelta delta;
ScopedAnimationDurationScaleMode faster_duration(
ScopedAnimationDurationScaleMode::FAST_DURATION);
scoped_ptr<LayerAnimationElement> base_element(
LayerAnimationElement::CreateTransformElement(transform, delta));
scoped_ptr<LayerAnimationElement> inverse_element(
LayerAnimationElement::CreateInverseTransformElement(transform,
base_element.get()));
EXPECT_EQ(base_element->duration(), inverse_element->duration());
}
// Check that the bounds element progresses the delegate as expected and
// that the element can be reused after it completes.
TEST(LayerAnimationElementTest, BoundsElement) {
......
......@@ -10,13 +10,23 @@
namespace ui {
//TODO(avallee): Make this into a predicate and add some matrix pretty printing.
void CheckApproximatelyEqual(const gfx::Transform& lhs,
const gfx::Transform& rhs) {
unsigned int errors = 0;
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
EXPECT_FLOAT_EQ(lhs.matrix().get(i, j), rhs.matrix().get(i, j));
EXPECT_FLOAT_EQ(lhs.matrix().get(i, j), rhs.matrix().get(i, j))
<< "(i, j) = (" << i << ", " << j << "), error count: " << ++errors;
}
}
if (errors) {
ADD_FAILURE() << "Expected matrix:\n"
<< lhs.ToString() << "\n"
<< "Actual matrix:\n"
<< rhs.ToString();
}
}
void CheckApproximatelyEqual(const gfx::Rect& lhs, const gfx::Rect& rhs) {
......
......@@ -60,4 +60,53 @@ bool TransformAnimationCurveAdapter::AnimatedBoundsForBox(
return false;
}
InverseTransformCurveAdapter::InverseTransformCurveAdapter(
TransformAnimationCurveAdapter base_curve,
gfx::Transform initial_value,
base::TimeDelta duration)
: base_curve_(base_curve),
initial_value_(initial_value),
duration_(duration) {
effective_initial_value_ = base_curve_.GetValue(0.0) * initial_value_;
}
InverseTransformCurveAdapter::~InverseTransformCurveAdapter() {
}
double InverseTransformCurveAdapter::Duration() const {
return duration_.InSeconds();
}
scoped_ptr<cc::AnimationCurve> InverseTransformCurveAdapter::Clone() const {
scoped_ptr<InverseTransformCurveAdapter> to_return(
new InverseTransformCurveAdapter(base_curve_,
initial_value_,
duration_));
return to_return.PassAs<cc::AnimationCurve>();
}
gfx::Transform InverseTransformCurveAdapter::GetValue(
double t) const {
if (t <= 0.0)
return initial_value_;
gfx::Transform base_transform = base_curve_.GetValue(t);
// Invert base
gfx::Transform to_return(gfx::Transform::kSkipInitialization);
bool is_invertible = base_transform.GetInverse(&to_return);
DCHECK(is_invertible);
to_return.PreconcatTransform(effective_initial_value_);
return to_return;
}
bool InverseTransformCurveAdapter::AnimatedBoundsForBox(
const gfx::BoxF& box,
gfx::BoxF* bounds) const {
// TODO(ajuma): Once cc::TransformOperation::BlendedBoundsForBox supports
// computing bounds for TransformOperationMatrix, use that to compute
// the bounds we need here.
return false;
}
} // namespace ui
......@@ -8,12 +8,14 @@
#include "base/time/time.h"
#include "cc/animation/animation_curve.h"
#include "ui/base/animation/tween.h"
#include "ui/compositor/compositor_export.h"
#include "ui/gfx/transform.h"
#include "ui/gfx/transform_util.h"
namespace ui {
class TransformAnimationCurveAdapter :public cc::TransformAnimationCurve {
class COMPOSITOR_EXPORT TransformAnimationCurveAdapter
: public cc::TransformAnimationCurve {
public:
TransformAnimationCurveAdapter(Tween::Type tween_type,
gfx::Transform intial_value,
......@@ -36,6 +38,32 @@ class TransformAnimationCurveAdapter :public cc::TransformAnimationCurve {
gfx::DecomposedTransform decomposed_initial_value_;
gfx::DecomposedTransform decomposed_target_value_;
base::TimeDelta duration_;
DISALLOW_ASSIGN(TransformAnimationCurveAdapter);
};
class COMPOSITOR_EXPORT InverseTransformCurveAdapter
: public cc::TransformAnimationCurve {
public:
InverseTransformCurveAdapter(TransformAnimationCurveAdapter base_curve,
gfx::Transform initial_value,
base::TimeDelta duration);
virtual ~InverseTransformCurveAdapter();
virtual double Duration() const OVERRIDE;
virtual scoped_ptr<AnimationCurve> Clone() const OVERRIDE;
virtual gfx::Transform GetValue(double t) const OVERRIDE;
virtual bool AnimatedBoundsForBox(const gfx::BoxF& box,
gfx::BoxF* bounds) const OVERRIDE;
private:
TransformAnimationCurveAdapter base_curve_;
gfx::Transform initial_value_;
gfx::Transform effective_initial_value_;
base::TimeDelta duration_;
DISALLOW_ASSIGN(InverseTransformCurveAdapter);
};
} // namespace ui
......
// 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 "ui/compositor/transform_animation_curve_adapter.h"
#include <sstream>
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/compositor/test/test_utils.h"
namespace ui {
namespace {
// Check that the inverse transform curve gives the gives a transform that when
// applied on top of the parent transform gives the original transform
TEST(InverseTransformCurveAdapterTest, InversesTransform) {
gfx::Transform parent_start, parent_target;
parent_start.Scale(0.5, 3.0);
parent_start.Translate(-20.0, 30.0);
parent_target.Translate(0, 100);
gfx::Transform child_transform;
child_transform.Rotate(-30.0);
base::TimeDelta duration = base::TimeDelta::FromSeconds(1);
const gfx::Transform effective_child_transform =
parent_start * child_transform;
TransformAnimationCurveAdapter parent_curve(Tween::LINEAR,
parent_start,
parent_target,
duration);
InverseTransformCurveAdapter child_curve(parent_curve,
child_transform,
duration);
static const int kSteps = 1000;
double step = 1.0 / kSteps;
for (int i = 0; i <= kSteps ; ++i) {
std::ostringstream message;
message << "Step " << i << " of " << kSteps;
SCOPED_TRACE(message.str());
gfx::Transform progress_parent_transform =
parent_curve.GetValue(i*step);
gfx::Transform progress_child_transform =
child_curve.GetValue(i*step);
CheckApproximatelyEqual(effective_child_transform,
progress_parent_transform *
progress_child_transform);
}
}
} // namespace
} // namespace ui
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment