Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(110)

Unified Diff: ui/gfx/compositor/layer_animator.cc

Issue 8247009: Explicit animation support (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Added new preemption strategy: replace queued animations. Created 9 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ui/gfx/compositor/layer_animator.cc
diff --git a/ui/gfx/compositor/layer_animator.cc b/ui/gfx/compositor/layer_animator.cc
index 5f2c63cebe3e75700e6aa514066550aa6456bc99..683bf173ecf9f6a1f1a5c5b2ad64462ebec2b19d 100644
--- a/ui/gfx/compositor/layer_animator.cc
+++ b/ui/gfx/compositor/layer_animator.cc
@@ -4,183 +4,443 @@
#include "ui/gfx/compositor/layer_animator.h"
+#include "base/debug/trace_event.h"
#include "base/logging.h"
-#include "base/stl_util.h"
-#include "ui/base/animation/animation_container.h"
+#include "base/memory/scoped_ptr.h"
#include "ui/base/animation/animation.h"
-#include "ui/base/animation/tween.h"
#include "ui/gfx/compositor/compositor.h"
#include "ui/gfx/compositor/layer.h"
-#include "ui/gfx/compositor/layer_animator_delegate.h"
-#include "ui/gfx/transform.h"
-#include "ui/gfx/rect.h"
+#include "ui/gfx/compositor/layer_animation_sequence.h"
+
+namespace ui {
+
+class LayerAnimator;
namespace {
-void SetMatrixElement(SkMatrix44& matrix, int index, SkMScalar value) {
- int row = index / 4;
- int col = index % 4;
- matrix.set(row, col, value);
+static const int kDefaultTransitionDurationMs = 250;
+static const int kDefaultFramerateHz = 10;
+
+class LayerAnimation : public Animation {
+ public:
+ // The layer animation does not own the animator.
+ explicit LayerAnimation(LayerAnimator* animator)
+ : Animation(base::TimeDelta::FromMilliseconds(kDefaultFramerateHz)),
+ animator_(animator) {
+ }
+ virtual ~LayerAnimation() {}
+
+ private:
+ // Implementation of Animation
+ double GetCurrentValue() const { return 0.0; }
sky 2011/10/19 00:06:33 virtual/OVERRIDE
+ virtual void Step(base::TimeTicks time_now) { animator_->Step(time_now); }
sky 2011/10/19 00:06:33 OVERRIDE
+
+ LayerAnimator* animator_;
+};
sky 2011/10/19 00:06:33 DISALLOW_COPY_AND_ASSIGN
+
+} // namespace;
+
+/* static */
sky 2011/10/19 00:06:33 /* static */ -> // static
+LayerAnimator* LayerAnimator::CreateDefaultAnimator() {
+ return new LayerAnimator(base::TimeDelta::FromMilliseconds(0));
}
-SkMScalar GetMatrixElement(const SkMatrix44& matrix, int index) {
- int row = index / 4;
- int col = index % 4;
- return matrix.get(row, col);
+/* static */
+LayerAnimator* LayerAnimator::CreateImplicitAnimator() {
+ return new LayerAnimator(
+ base::TimeDelta::FromMilliseconds(kDefaultTransitionDurationMs));
}
-} // anonymous namespace
+LayerAnimator::LayerAnimator(base::TimeDelta transition_duration)
+ : delegate_(NULL),
+ preemption_strategy_(IMMEDIATELY_SET_NEW_TARGET),
+ transition_duration_(transition_duration),
+ ALLOW_THIS_IN_INITIALIZER_LIST(animation_(new LayerAnimation(this))) {
+}
-namespace ui {
+LayerAnimator::~LayerAnimator() {
+ ClearAnimations();
+}
-LayerAnimator::LayerAnimator(Layer* layer)
- : layer_(layer),
- got_initial_tick_(false) {
+void LayerAnimator::SetTransform(const Transform& transform) {
+ StartAnimation(new LayerAnimationSequence(
+ LayerAnimationElement::CreateTransformElement(transform,
+ transition_duration_)));
}
-LayerAnimator::~LayerAnimator() {
+void LayerAnimator::SetBounds(const gfx::Rect& bounds) {
+ StartAnimation(new LayerAnimationSequence(
+ LayerAnimationElement::CreateBoundsElement(bounds,
+ transition_duration_)));
}
-void LayerAnimator::SetAnimation(Animation* animation) {
- animation_.reset(animation);
- if (animation_.get()) {
- static ui::AnimationContainer* container = NULL;
- if (!container) {
- container = new AnimationContainer;
- container->AddRef();
+void LayerAnimator::SetOpacity(float opacity) {
+ StartAnimation(new LayerAnimationSequence(
+ LayerAnimationElement::CreateOpacityElement(opacity,
+ transition_duration_)));
+}
+
+void LayerAnimator::SetDelegate(LayerAnimationDelegate* delegate) {
+ delegate_ = delegate;
+ if (!delegate_) {
sky 2011/10/19 15:43:25 remove {} Why does no delegate imply you need to c
+ ClearAnimations();
+ }
+}
+
+void LayerAnimator::StartAnimation(LayerAnimationSequence* animation) {
+ if (!StartSequenceImmediately(animation)) {
+ // Attempt to preempt a running animation.
+ switch (preemption_strategy_) {
+ case IMMEDIATELY_SET_NEW_TARGET:
+ ImmediatelySetNewTarget(animation);
+ break;
+ case IMMEDIATELY_ANIMATE_TO_NEW_TARGET:
+ ImmediatelyAnimateToNewTarget(animation);
+ break;
+ case ENQUEUE_NEW_ANIMATION:
+ EnqueueNewAnimation(animation);
+ break;
+ case REPLACE_QUEUED_ANIMATIONS:
+ ReplaceQueuedAnimations(animation);
+ break;
+ case BLEND_WITH_CURRENT_ANIMATION: {
+ // TODO(vollick) Add support for blended sequences and use them here.
+ NOTIMPLEMENTED();
+ break;
+ }
}
- animation_->set_delegate(this);
- animation_->SetContainer(container);
sky 2011/10/19 00:06:33 Make all animations share the same container like
- got_initial_tick_ = false;
}
+ FinishAnyAnimationWithZeroDuration();
}
-void LayerAnimator::AnimateToPoint(const gfx::Point& target) {
- StopAnimating(LOCATION);
- const gfx::Rect& layer_bounds = layer_->bounds();
- if (target == layer_bounds.origin())
- return; // Already there.
+void LayerAnimator::ScheduleAnimation(LayerAnimationSequence* animation) {
+ animation_queue_.push_back(animation);
+ ProcessQueue();
+}
- Params& element = elements_[LOCATION];
- element.location.target_x = target.x();
- element.location.target_y = target.y();
- element.location.start_x = layer_bounds.origin().x();
- element.location.start_y = layer_bounds.origin().y();
+void LayerAnimator::ScheduleTogether(
+ const std::vector<LayerAnimationSequence*>& animations) {
sky 2011/10/19 15:43:25 nit: spacing
+ // Collect all the affected properties.
+ LayerAnimationElement::AnimatableProperties animated_properties;
+ std::vector<LayerAnimationSequence*>::const_iterator iter =
sky 2011/10/19 15:43:25 move iterator into for loop.
+ animations.begin();
+ while (iter != animations.end()) {
+ LayerAnimationElement::AnimatableProperties::const_iterator prop_iter =
sky 2011/10/19 15:43:25 Can this be animated_properties.insert((*iter)->pr
+ (*iter)->properties().begin();
+ while (prop_iter != (*iter)->properties().end()) {
+ animated_properties.insert(*prop_iter);
+ ++prop_iter;
+ }
+ ++iter;
+ }
+
+ // Scheduling a zero duration pause that affects all the animated properties
+ // will prevent any of the sequences from animating until all the properties
+ // are free.
sky 2011/10/19 15:43:25 free? Do you mean existing animations done?
+ ScheduleAnimation(
+ new LayerAnimationSequence(
+ LayerAnimationElement::CreatePauseElement(animated_properties,
+ base::TimeDelta())));
+
+ // These animations (provided they don't animate any common properties) will
+ // now animate together if trivially scheduled.
+ iter = animations.begin();
+ while (iter != animations.end()) {
+ ScheduleAnimation(*iter);
+ ++iter;
+ }
}
-void LayerAnimator::AnimateTransform(const Transform& transform) {
- StopAnimating(TRANSFORM);
- const Transform& layer_transform = layer_->transform();
- if (transform == layer_transform)
- return; // Already there.
+bool LayerAnimator::IsAnimating() const {
+ return animation_queue_.size() > 0;
sky 2011/10/19 15:43:25 !empty(). You can inline this if you want.
+}
+
+void LayerAnimator::StopAnimatingProperty(
+ LayerAnimationElement::AnimatableProperty property) {
+ while (true) {
+ RunningAnimation* running = GetRunningAnimation(property);
+ if (!running)
+ break;
+ FinishAnimation(running->sequence);
+ }
+}
+
+void LayerAnimator::StopAnimating() {
+ while (IsAnimating())
+ FinishAnimation(running_animations_[0].sequence);
+}
- Params& element = elements_[TRANSFORM];
- for (int i = 0; i < 16; ++i) {
- element.transform.start[i] =
- GetMatrixElement(layer_transform.matrix(), i);
- element.transform.target[i] =
- GetMatrixElement(transform.matrix(), i);
+void LayerAnimator::Step(base::TimeTicks now) {
+ TRACE_EVENT0("LayerAnimator", "Step");
+ last_step_time_ = now;
+ RunningAnimations running_animations_copy = running_animations_;
sky 2011/10/19 15:43:25 Document why you need to make a copy.
+ RunningAnimations::iterator iter = running_animations_copy.begin();
sky 2011/10/19 15:43:25 move iterator into for lop.
+ while (iter != running_animations_copy.end()) {
+ base::TimeDelta delta = now - (*iter).start_time;
+ if (delta >= (*iter).sequence->duration() &&
+ !(*iter).sequence->is_cyclic()) {
+ FinishAnimation((*iter).sequence);
+ } else {
+ (*iter).sequence->Progress(delta, delegate());
+ }
+ ++iter;
}
}
-void LayerAnimator::AnimateOpacity(float target_opacity) {
- StopAnimating(OPACITY);
- if (layer_->opacity() == target_opacity)
+void LayerAnimator::SetAnimationForTest(Animation* animation) {
+ animation_.reset(animation);
+}
+
+void LayerAnimator::UpdateAnimationState() {
+ if (!animation_.get())
return;
- Params& element = elements_[OPACITY];
- element.opacity.start = layer_->opacity();
- element.opacity.target = target_opacity;
+ if (IsAnimating())
+ animation_->Start();
+ else
+ animation_->Stop();
}
-gfx::Point LayerAnimator::GetTargetPoint() {
- return IsAnimating(LOCATION) ?
- gfx::Point(elements_[LOCATION].location.target_x,
- elements_[LOCATION].location.target_y) :
- layer_->bounds().origin();
+void LayerAnimator::RemoveAnimation(LayerAnimationSequence* sequence) {
+ // First remove from running animations
+ RunningAnimations::iterator iter = running_animations_.begin();
sky 2011/10/19 15:43:25 move into for loop.
+ while (iter != running_animations_.end()) {
+ if ((*iter).sequence == sequence) {
+ running_animations_.erase(iter);
+ break;
+ }
+ ++iter;
+ }
+
+ // Then remove from the queue
+ AnimationQueue::iterator queue_iter = animation_queue_.begin();
+ while (queue_iter != animation_queue_.end()) {
+ if ((*queue_iter) == sequence) {
+ animation_queue_.erase(queue_iter);
+ break;
+ }
+ ++queue_iter;
+ }
}
-float LayerAnimator::GetTargetOpacity() {
- return IsAnimating(OPACITY) ?
- elements_[OPACITY].opacity.target : layer_->opacity();
+void LayerAnimator::FinishAnimation(LayerAnimationSequence* sequence) {
+ sequence->Progress(sequence->duration(), delegate());
+ RemoveAnimation(sequence);
+ ProcessQueue();
+ UpdateAnimationState();
}
-ui::Transform LayerAnimator::GetTargetTransform() {
- if (IsAnimating(TRANSFORM)) {
- Transform transform;
- for (int i = 0; i < 16; ++i) {
- SetMatrixElement(transform.matrix(), i,
- elements_[TRANSFORM].transform.target[i]);
+void LayerAnimator::FinishAnyAnimationWithZeroDuration() {
+ // Special case: if we've started a 0 duration animation, just finish it now
+ // and get rid of it.
+ RunningAnimations copy = running_animations_;
+ RunningAnimations::iterator iter = copy.begin();
+ while (iter != copy.end()) {
+ if ((*iter).sequence->duration() == base::TimeDelta()) {
+ (*iter).sequence->Progress((*iter).sequence->duration(), delegate());
+ RemoveAnimation((*iter).sequence);
}
- return transform;
+ ++iter;
}
- return layer_->transform();
+ ProcessQueue();
+ UpdateAnimationState();
}
-bool LayerAnimator::IsAnimating(AnimationProperty property) const {
- return elements_.count(property) > 0;
+void LayerAnimator::ClearAnimations() {
+ RunningAnimations::iterator iter = running_animations_.begin();
+ while (iter != running_animations_.end()) {
+ (*iter).sequence->Abort();
+ ++iter;
+ }
+ running_animations_.clear();
+ animation_queue_.reset();
+ UpdateAnimationState();
}
-bool LayerAnimator::IsRunning() const {
- return animation_.get() && animation_->is_animating();
+LayerAnimator::RunningAnimation* LayerAnimator::GetRunningAnimation(
+ LayerAnimationElement::AnimatableProperty property) {
+ RunningAnimations::iterator iter = running_animations_.begin();
sky 2011/10/19 15:43:25 move into for loop.
+ while (iter != running_animations_.end()) {
+ if ((*iter).sequence->properties().find(property) !=
+ (*iter).sequence->properties().end())
+ return &(*iter);
+ ++iter;
+ }
+ return NULL;
}
-void LayerAnimator::AnimationProgressed(const ui::Animation* animation) {
- got_initial_tick_ = true;
- for (Elements::const_iterator i = elements_.begin(); i != elements_.end();
- ++i) {
- switch (i->first) {
- case LOCATION: {
- const gfx::Rect& current_bounds(layer_->bounds());
- gfx::Rect new_bounds = animation_->CurrentValueBetween(
- gfx::Rect(gfx::Point(i->second.location.start_x,
- i->second.location.start_y),
- current_bounds.size()),
- gfx::Rect(gfx::Point(i->second.location.target_x,
- i->second.location.target_y),
- current_bounds.size()));
- delegate()->SetBoundsFromAnimator(new_bounds);
+void LayerAnimator::AddToQueueIfNotPresent(LayerAnimationSequence* animation) {
+ // If we don't have the animation in the queue yet, add it.
+ bool found_sequence = false;
+ AnimationQueue::iterator queue_iter = animation_queue_.begin();
+ while (queue_iter != animation_queue_.end()) {
+ if ((*queue_iter) == animation) {
+ found_sequence = true;
+ break;
+ }
+ ++queue_iter;
+ }
+
+ if (!found_sequence)
+ animation_queue_.insert(animation_queue_.begin(), animation);
+}
+
+void LayerAnimator::RemoveAllAnimationsWithACommonProperty(
+ LayerAnimationSequence* sequence,
+ bool abort) {
+ // For all the running animations, if they animate the same property,
+ // progress them to the end and remove them.
+ RunningAnimations copy = running_animations_;
+ RunningAnimations::const_iterator iter = copy.begin();
+ while (iter != copy.end()) {
+ if ((*iter).sequence->HasCommonProperty(sequence->properties())) {
+ // Finish the animation.
+ if (abort)
+ (*iter).sequence->Abort();
+ else
+ (*iter).sequence->Progress((*iter).sequence->duration(), delegate());
+ RemoveAnimation((*iter).sequence);
+ }
+ ++iter;
+ }
+ // Same for the queued animations.
+ AnimationQueue::size_type i = 0;
+ while (i < animation_queue_.size()) {
+ if (animation_queue_[i]->HasCommonProperty(sequence->properties())) {
+ // Finish the animation.
+ if (abort)
+ animation_queue_[i]->Abort();
+ else
+ animation_queue_[i]->Progress(animation_queue_[i]->duration(),
+ delegate());
+ RemoveAnimation(animation_queue_[i]);
+ } else {
+ ++i;
+ }
+ }
+}
+
+void LayerAnimator::ImmediatelySetNewTarget(LayerAnimationSequence* sequence) {
+ const bool abort = false;
+ RemoveAllAnimationsWithACommonProperty(sequence, abort);
+ sequence->Progress(sequence->duration(), delegate());
+ RemoveAnimation(sequence);
+}
+
+void LayerAnimator::ImmediatelyAnimateToNewTarget(
+ LayerAnimationSequence* sequence) {
+ const bool abort = true;
+ RemoveAllAnimationsWithACommonProperty(sequence, abort);
+ AddToQueueIfNotPresent(sequence);
+ StartSequenceImmediately(sequence);
+}
+
+void LayerAnimator::EnqueueNewAnimation(LayerAnimationSequence* sequence) {
+ // It is assumed that if there was no conflicting animation, we would
+ // not have been called. No need to check for a collision; just
+ // add to the queue.
+ animation_queue_.push_back(sequence);
+ ProcessQueue();
+}
+
+void LayerAnimator::ReplaceQueuedAnimations(LayerAnimationSequence* sequence) {
+ // Remove all animations that aren't running.
+ AnimationQueue::size_type i = 0;
+ while (i < animation_queue_.size()) {
+ bool is_running = false;
+ RunningAnimations::const_iterator iter =
+ running_animations_.begin();
+ while (iter != running_animations_.end()) {
+ if ((*iter).sequence == animation_queue_[i]) {
+ is_running = true;
break;
}
+ ++iter;
+ }
+ if (!is_running)
+ RemoveAnimation(animation_queue_[i]);
+ else
+ ++i;
+ }
+ animation_queue_.push_back(sequence);
+ ProcessQueue();
+}
- case TRANSFORM: {
- Transform transform;
- for (int j = 0; j < 16; ++j) {
- SkMScalar value = animation_->CurrentValueBetween(
- i->second.transform.start[j],
- i->second.transform.target[j]);
- SetMatrixElement(transform.matrix(), j, value);
- }
- delegate()->SetTransformFromAnimator(transform);
- break;
+void LayerAnimator::ProcessQueue() {
+ bool started_sequence = false;
+
+ do {
+ started_sequence = false;
+
+ // Build a list of all currently animated properties.
+ LayerAnimationElement::AnimatableProperties animated;
+ RunningAnimations::const_iterator iter = running_animations_.begin();
+ while (iter != running_animations_.end()) {
+ LayerAnimationElement::AnimatableProperties::const_iterator prop_iter =
+ (*iter).sequence->properties().begin();
+ while (prop_iter != (*iter).sequence->properties().end()) {
+ animated.insert(*prop_iter);
+ ++prop_iter;
}
+ ++iter;
+ }
- case OPACITY: {
- delegate()->SetOpacityFromAnimator(animation_->CurrentValueBetween(
- i->second.opacity.start, i->second.opacity.target));
+ // Try to find an animation that doesn't conflict with an animated
+ // property or a property that will be animated before it.
+ AnimationQueue::iterator queue_iter = animation_queue_.begin();
+ while (queue_iter != animation_queue_.end()) {
+ if (!(*queue_iter)->HasCommonProperty(animated)) {
+ StartSequenceImmediately(*queue_iter);
+ started_sequence = true;
break;
}
-
- default:
- NOTREACHED();
+ // Animation couldn't be started. Add its properties to the collection so
+ // that we don't start a conflicting animation. For example, if our queue
+ // has the elements { {T,B}, {B} } (that is, an element that animates both
+ // the transform and the bounds followed by an element that animates the
+ // bounds), and we're currently animating the transform, we can't start
+ // the first element because it animates the transform, too. We cannot
+ // start the second element, either, because the first element animates
+ // bounds too, and needs to go first.
+ LayerAnimationElement::AnimatableProperties::const_iterator prop_iter =
+ (*queue_iter)->properties().begin();
+ while (prop_iter != (*queue_iter)->properties().end()) {
+ animated.insert(*prop_iter);
+ ++prop_iter;
+ }
+ ++queue_iter;
}
- }
- layer_->ScheduleDraw();
-}
-void LayerAnimator::AnimationEnded(const ui::Animation* animation) {
- AnimationProgressed(animation);
+ // If we started a sequence, try again. We may be able to start several.
+ } while (started_sequence);
}
-void LayerAnimator::StopAnimating(AnimationProperty property) {
- if (!IsAnimating(property))
- return;
+bool LayerAnimator::StartSequenceImmediately(LayerAnimationSequence* sequence) {
+ // Ensure that no one is animating one of the sequence's properties already.
+ RunningAnimations::const_iterator iter = running_animations_.begin();
+ while (iter != running_animations_.end()) {
+ if ((*iter).sequence->HasCommonProperty(sequence->properties()))
+ return false;
+ ++iter;
+ }
- elements_.erase(property);
-}
+ // All clear, actually start the sequence. Note: base::TimeTicks::Now has
+ // a resolution that can be as bad as 15ms. If this causes glitches in the
+ // animations, this can be switched to HighResNow() (animation uses Now()
+ // internally).
+ base::TimeTicks start_time = IsAnimating()
+ ? last_step_time_
+ : base::TimeTicks::Now();
sky 2011/10/19 15:43:25 For the animating case this effectively starts the
+
+ running_animations_.push_back(RunningAnimation(sequence, start_time));
+
+ // Need to keep a reference to the animation.
+ AddToQueueIfNotPresent(sequence);
+
+ // Ensure that animations get stepped at their start time.
+ Step(start_time);
-LayerAnimatorDelegate* LayerAnimator::delegate() {
- return static_cast<LayerAnimatorDelegate*>(layer_);
+ return true;
}
} // namespace ui

Powered by Google App Engine
This is Rietveld 408576698