Skip to content

Commit

Permalink
Add support for inverse transform animations.
Browse files Browse the repository at this point in the history
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

Review URL: https://codereview.chromium.org/22861008

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@220479 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
danakj@chromium.org committed Aug 30, 2013
1 parent 277a566 commit c215f6a
Show file tree
Hide file tree
Showing 8 changed files with 326 additions and 6 deletions.
1 change: 1 addition & 0 deletions ui/compositor/compositor.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
133 changes: 133 additions & 0 deletions ui/compositor/layer_animation_element.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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 ------------------------------------------
Expand Down Expand Up @@ -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() {
}

Expand Down Expand Up @@ -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(
Expand Down
17 changes: 14 additions & 3 deletions ui/compositor/layer_animation_element.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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_; }
Expand All @@ -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
Expand All @@ -215,7 +226,7 @@ class COMPOSITOR_EXPORT LayerAnimationElement {

double last_progressed_fraction_;

DISALLOW_COPY_AND_ASSIGN(LayerAnimationElement);
DISALLOW_ASSIGN(LayerAnimationElement);
};

} // namespace ui
Expand Down
33 changes: 32 additions & 1 deletion ui/compositor/layer_animation_element_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down
11 changes: 10 additions & 1 deletion ui/compositor/test/test_utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,22 @@

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) {
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 <<")";
}
}

if (::testing::Test::HasFailure()) {
ADD_FAILURE() << "Expected matrix:\n"
<< lhs.ToString() << "\n"
<< "Actual matrix:\n"
<< rhs.ToString();
}
}

void CheckApproximatelyEqual(const gfx::Rect& lhs, const gfx::Rect& rhs) {
Expand Down
48 changes: 48 additions & 0 deletions ui/compositor/transform_animation_curve_adapter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,52 @@ 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);
DCHECK(base_transform.GetInverse(&to_return));

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
Loading

0 comments on commit c215f6a

Please sign in to comment.