#include "cc/animation/layer_animation_controller.h"
#include <algorithm>
+#include <vector>
#include "cc/animation/animation.h"
#include "cc/animation/animation_delegate.h"
#include "cc/animation/animation_registrar.h"
#include "cc/animation/keyframed_animation_curve.h"
#include "cc/animation/layer_animation_value_observer.h"
+#include "cc/animation/layer_animation_value_provider.h"
+#include "cc/animation/scroll_offset_animation_curve.h"
#include "cc/base/scoped_ptr_algorithm.h"
#include "cc/output/filter_operations.h"
#include "ui/gfx/box_f.h"
namespace cc {
LayerAnimationController::LayerAnimationController(int id)
- : force_sync_(false),
- registrar_(0),
+ : registrar_(0),
id_(id),
is_active_(false),
- last_tick_time_(0),
- layer_animation_delegate_(NULL) {}
+ value_provider_(NULL),
+ layer_animation_delegate_(NULL),
+ needs_to_start_animations_(false) {
+}
LayerAnimationController::~LayerAnimationController() {
if (registrar_)
}
void LayerAnimationController::PauseAnimation(int animation_id,
- double time_offset) {
- for (size_t i = 0; i < active_animations_.size(); ++i) {
- if (active_animations_[i]->id() == animation_id) {
- active_animations_[i]->SetRunState(
- Animation::Paused, time_offset + active_animations_[i]->start_time());
+ base::TimeDelta time_offset) {
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (animations_[i]->id() == animation_id) {
+ animations_[i]->SetRunState(Animation::Paused,
+ time_offset + animations_[i]->start_time());
}
}
}
};
void LayerAnimationController::RemoveAnimation(int animation_id) {
- ScopedPtrVector<Animation>& animations = active_animations_;
- animations.erase(cc::remove_if(&animations,
- animations.begin(),
- animations.end(),
- HasAnimationId(animation_id)),
- animations.end());
+ animations_.erase(cc::remove_if(&animations_,
+ animations_.begin(),
+ animations_.end(),
+ HasAnimationId(animation_id)),
+ animations_.end());
UpdateActivation(NormalActivation);
}
void LayerAnimationController::RemoveAnimation(
int animation_id,
Animation::TargetProperty target_property) {
- ScopedPtrVector<Animation>& animations = active_animations_;
- animations.erase(cc::remove_if(&animations,
- animations.begin(),
- animations.end(),
- HasAnimationIdAndProperty(animation_id,
- target_property)),
- animations.end());
+ animations_.erase(
+ cc::remove_if(&animations_,
+ animations_.begin(),
+ animations_.end(),
+ HasAnimationIdAndProperty(animation_id, target_property)),
+ animations_.end());
UpdateActivation(NormalActivation);
}
+void LayerAnimationController::AbortAnimations(
+ Animation::TargetProperty target_property) {
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (animations_[i]->target_property() == target_property &&
+ !animations_[i]->is_finished())
+ animations_[i]->SetRunState(Animation::Aborted, last_tick_time_);
+ }
+}
+
// Ensures that the list of active animations on the main thread and the impl
// thread are kept in sync.
void LayerAnimationController::PushAnimationUpdatesTo(
LayerAnimationController* controller_impl) {
DCHECK(this != controller_impl);
- if (force_sync_) {
- ReplaceImplThreadAnimations(controller_impl);
- force_sync_ = false;
- } else {
- PurgeAnimationsMarkedForDeletion();
- PushNewAnimationsToImplThread(controller_impl);
+ if (!has_any_animation() && !controller_impl->has_any_animation())
+ return;
+ PurgeAnimationsMarkedForDeletion();
+ PushNewAnimationsToImplThread(controller_impl);
- // Remove finished impl side animations only after pushing,
- // and only after the animations are deleted on the main thread
- // this insures we will never push an animation twice.
- RemoveAnimationsCompletedOnMainThread(controller_impl);
+ // Remove finished impl side animations only after pushing,
+ // and only after the animations are deleted on the main thread
+ // this insures we will never push an animation twice.
+ RemoveAnimationsCompletedOnMainThread(controller_impl);
- PushPropertiesToImplThread(controller_impl);
- }
+ PushPropertiesToImplThread(controller_impl);
controller_impl->UpdateActivation(NormalActivation);
UpdateActivation(NormalActivation);
}
-void LayerAnimationController::Animate(double monotonic_time) {
+void LayerAnimationController::Animate(base::TimeTicks monotonic_time) {
+ DCHECK(!monotonic_time.is_null());
if (!HasValueObserver())
return;
- StartAnimationsWaitingForNextTick(monotonic_time);
- StartAnimationsWaitingForStartTime(monotonic_time);
- StartAnimationsWaitingForTargetAvailability(monotonic_time);
- ResolveConflicts(monotonic_time);
+ if (needs_to_start_animations_)
+ StartAnimations(monotonic_time);
TickAnimations(monotonic_time);
last_tick_time_ = monotonic_time;
}
void LayerAnimationController::AccumulatePropertyUpdates(
- double monotonic_time,
+ base::TimeTicks monotonic_time,
AnimationEventsVector* events) {
if (!events)
return;
- for (size_t i = 0; i < active_animations_.size(); ++i) {
- Animation* animation = active_animations_[i];
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ Animation* animation = animations_[i];
if (!animation->is_impl_only())
continue;
+ if (!animation->InEffect(monotonic_time))
+ continue;
+
+ double trimmed = animation->TrimTimeToCurrentIteration(monotonic_time);
switch (animation->target_property()) {
case Animation::Opacity: {
AnimationEvent event(AnimationEvent::PropertyUpdate,
animation->group(),
Animation::Opacity,
monotonic_time);
- event.opacity = animation->curve()->ToFloatAnimationCurve()->GetValue(
- monotonic_time);
+ const FloatAnimationCurve* float_animation_curve =
+ animation->curve()->ToFloatAnimationCurve();
+ event.opacity = float_animation_curve->GetValue(trimmed);
event.is_impl_only = true;
events->push_back(event);
break;
animation->group(),
Animation::Transform,
monotonic_time);
- event.transform =
- animation->curve()->ToTransformAnimationCurve()->GetValue(
- monotonic_time);
+ const TransformAnimationCurve* transform_animation_curve =
+ animation->curve()->ToTransformAnimationCurve();
+ event.transform = transform_animation_curve->GetValue(trimmed);
event.is_impl_only = true;
events->push_back(event);
break;
animation->group(),
Animation::Filter,
monotonic_time);
- event.filters = animation->curve()->ToFilterAnimationCurve()->GetValue(
- monotonic_time);
+ const FilterAnimationCurve* filter_animation_curve =
+ animation->curve()->ToFilterAnimationCurve();
+ event.filters = filter_animation_curve->GetValue(trimmed);
event.is_impl_only = true;
events->push_back(event);
break;
case Animation::BackgroundColor: { break; }
+ case Animation::ScrollOffset: {
+ // Impl-side changes to scroll offset are already sent back to the
+ // main thread (e.g. for user-driven scrolling), so a PropertyUpdate
+ // isn't needed.
+ break;
+ }
+
case Animation::TargetPropertyEnumSize:
NOTREACHED();
}
if (!HasActiveValueObserver())
return;
+ DCHECK(last_tick_time_ != base::TimeTicks());
if (start_ready_animations)
PromoteStartedAnimations(last_tick_time_, events);
MarkFinishedAnimations(last_tick_time_);
MarkAnimationsForDeletion(last_tick_time_, events);
- if (start_ready_animations) {
- StartAnimationsWaitingForTargetAvailability(last_tick_time_);
+ if (needs_to_start_animations_ && start_ready_animations) {
+ StartAnimations(last_tick_time_);
PromoteStartedAnimations(last_tick_time_, events);
}
UpdateActivation(NormalActivation);
}
+struct AffectsNoObservers {
+ bool operator()(Animation* animation) const {
+ return !animation->affects_active_observers() &&
+ !animation->affects_pending_observers();
+ }
+};
+
+void LayerAnimationController::ActivateAnimations() {
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ animations_[i]->set_affects_active_observers(
+ animations_[i]->affects_pending_observers());
+ }
+ animations_.erase(cc::remove_if(&animations_,
+ animations_.begin(),
+ animations_.end(),
+ AffectsNoObservers()),
+ animations_.end());
+ UpdateActivation(NormalActivation);
+}
+
void LayerAnimationController::AddAnimation(scoped_ptr<Animation> animation) {
- active_animations_.push_back(animation.Pass());
+ animations_.push_back(animation.Pass());
+ needs_to_start_animations_ = true;
UpdateActivation(NormalActivation);
}
Animation* LayerAnimationController::GetAnimation(
int group_id,
Animation::TargetProperty target_property) const {
- for (size_t i = 0; i < active_animations_.size(); ++i)
- if (active_animations_[i]->group() == group_id &&
- active_animations_[i]->target_property() == target_property)
- return active_animations_[i];
+ for (size_t i = 0; i < animations_.size(); ++i)
+ if (animations_[i]->group() == group_id &&
+ animations_[i]->target_property() == target_property)
+ return animations_[i];
return 0;
}
Animation* LayerAnimationController::GetAnimation(
Animation::TargetProperty target_property) const {
- for (size_t i = 0; i < active_animations_.size(); ++i) {
- size_t index = active_animations_.size() - i - 1;
- if (active_animations_[index]->target_property() == target_property)
- return active_animations_[index];
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ size_t index = animations_.size() - i - 1;
+ if (animations_[index]->target_property() == target_property)
+ return animations_[index];
}
return 0;
}
bool LayerAnimationController::HasActiveAnimation() const {
- for (size_t i = 0; i < active_animations_.size(); ++i) {
- if (!active_animations_[i]->is_finished())
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (!animations_[i]->is_finished())
return true;
}
return false;
bool LayerAnimationController::IsAnimatingProperty(
Animation::TargetProperty target_property) const {
- for (size_t i = 0; i < active_animations_.size(); ++i) {
- if (!active_animations_[i]->is_finished() &&
- active_animations_[i]->target_property() == target_property)
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (!animations_[i]->is_finished() &&
+ animations_[i]->InEffect(last_tick_time_) &&
+ animations_[i]->target_property() == target_property)
return true;
}
return false;
}
void LayerAnimationController::NotifyAnimationStarted(
- const AnimationEvent& event,
- double wall_clock_time) {
+ const AnimationEvent& event) {
if (event.is_impl_only) {
FOR_EACH_OBSERVER(LayerAnimationEventObserver, event_observers_,
OnAnimationStarted(event));
if (layer_animation_delegate_)
- layer_animation_delegate_->NotifyAnimationStarted(wall_clock_time);
-
+ layer_animation_delegate_->NotifyAnimationStarted(event.monotonic_time,
+ event.target_property);
return;
}
- for (size_t i = 0; i < active_animations_.size(); ++i) {
- if (active_animations_[i]->group() == event.group_id &&
- active_animations_[i]->target_property() == event.target_property &&
- active_animations_[i]->needs_synchronized_start_time()) {
- active_animations_[i]->set_needs_synchronized_start_time(false);
- active_animations_[i]->set_start_time(event.monotonic_time);
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (animations_[i]->group() == event.group_id &&
+ animations_[i]->target_property() == event.target_property &&
+ animations_[i]->needs_synchronized_start_time()) {
+ animations_[i]->set_needs_synchronized_start_time(false);
+ if (!animations_[i]->has_set_start_time())
+ animations_[i]->set_start_time(event.monotonic_time);
FOR_EACH_OBSERVER(LayerAnimationEventObserver, event_observers_,
OnAnimationStarted(event));
if (layer_animation_delegate_)
- layer_animation_delegate_->NotifyAnimationStarted(wall_clock_time);
+ layer_animation_delegate_->NotifyAnimationStarted(
+ event.monotonic_time, event.target_property);
return;
}
}
void LayerAnimationController::NotifyAnimationFinished(
- const AnimationEvent& event,
- double wall_clock_time) {
+ const AnimationEvent& event) {
if (event.is_impl_only) {
if (layer_animation_delegate_)
- layer_animation_delegate_->NotifyAnimationFinished(wall_clock_time);
+ layer_animation_delegate_->NotifyAnimationFinished(event.monotonic_time,
+ event.target_property);
return;
}
- for (size_t i = 0; i < active_animations_.size(); ++i) {
- if (active_animations_[i]->group() == event.group_id &&
- active_animations_[i]->target_property() == event.target_property) {
- active_animations_[i]->set_received_finished_event(true);
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (animations_[i]->group() == event.group_id &&
+ animations_[i]->target_property() == event.target_property) {
+ animations_[i]->set_received_finished_event(true);
if (layer_animation_delegate_)
- layer_animation_delegate_->NotifyAnimationFinished(wall_clock_time);
+ layer_animation_delegate_->NotifyAnimationFinished(
+ event.monotonic_time, event.target_property);
return;
}
}
}
+void LayerAnimationController::NotifyAnimationAborted(
+ const AnimationEvent& event) {
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (animations_[i]->group() == event.group_id &&
+ animations_[i]->target_property() == event.target_property) {
+ animations_[i]->SetRunState(Animation::Aborted, event.monotonic_time);
+ }
+ }
+}
+
void LayerAnimationController::NotifyAnimationPropertyUpdate(
const AnimationEvent& event) {
+ bool notify_active_observers = true;
+ bool notify_pending_observers = true;
switch (event.target_property) {
case Animation::Opacity:
- NotifyObserversOpacityAnimated(event.opacity);
+ NotifyObserversOpacityAnimated(
+ event.opacity, notify_active_observers, notify_pending_observers);
break;
case Animation::Transform:
- NotifyObserversTransformAnimated(event.transform);
+ NotifyObserversTransformAnimated(
+ event.transform, notify_active_observers, notify_pending_observers);
break;
default:
NOTREACHED();
event_observers_.RemoveObserver(observer);
}
-bool LayerAnimationController::AnimatedBoundsForBox(const gfx::BoxF& box,
- gfx::BoxF* bounds) {
+bool LayerAnimationController::HasFilterAnimationThatInflatesBounds() const {
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (!animations_[i]->is_finished() &&
+ animations_[i]->target_property() == Animation::Filter &&
+ animations_[i]
+ ->curve()
+ ->ToFilterAnimationCurve()
+ ->HasFilterThatMovesPixels())
+ return true;
+ }
+
+ return false;
+}
+
+bool LayerAnimationController::HasTransformAnimationThatInflatesBounds() const {
+ return IsAnimatingProperty(Animation::Transform);
+}
+
+bool LayerAnimationController::FilterAnimationBoundsForBox(
+ const gfx::BoxF& box, gfx::BoxF* bounds) const {
+ // TODO(avallee): Implement.
+ return false;
+}
+
+bool LayerAnimationController::TransformAnimationBoundsForBox(
+ const gfx::BoxF& box,
+ gfx::BoxF* bounds) const {
+ DCHECK(HasTransformAnimationThatInflatesBounds())
+ << "TransformAnimationBoundsForBox will give incorrect results if there "
+ << "are no transform animations affecting bounds, non-animated transform "
+ << "is not known";
+
// Compute bounds based on animations for which is_finished() is false.
// Do nothing if there are no such animations; in this case, it is assumed
// that callers will take care of computing bounds based on the owning layer's
// actual transform.
*bounds = gfx::BoxF();
- for (size_t i = 0; i < active_animations_.size(); ++i) {
- if (active_animations_[i]->is_finished() ||
- active_animations_[i]->target_property() != Animation::Transform)
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (animations_[i]->is_finished() ||
+ animations_[i]->target_property() != Animation::Transform)
continue;
const TransformAnimationCurve* transform_animation_curve =
- active_animations_[i]->curve()->ToTransformAnimationCurve();
+ animations_[i]->curve()->ToTransformAnimationCurve();
gfx::BoxF animation_bounds;
bool success =
transform_animation_curve->AnimatedBoundsForBox(box, &animation_bounds);
return true;
}
+bool LayerAnimationController::HasAnimationThatAffectsScale() const {
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (animations_[i]->is_finished() ||
+ animations_[i]->target_property() != Animation::Transform)
+ continue;
+
+ const TransformAnimationCurve* transform_animation_curve =
+ animations_[i]->curve()->ToTransformAnimationCurve();
+ if (transform_animation_curve->AffectsScale())
+ return true;
+ }
+
+ return false;
+}
+
+bool LayerAnimationController::HasOnlyTranslationTransforms() const {
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (animations_[i]->is_finished() ||
+ animations_[i]->target_property() != Animation::Transform)
+ continue;
+
+ const TransformAnimationCurve* transform_animation_curve =
+ animations_[i]->curve()->ToTransformAnimationCurve();
+ if (!transform_animation_curve->IsTranslation())
+ return false;
+ }
+
+ return true;
+}
+
+bool LayerAnimationController::MaximumTargetScale(float* max_scale) const {
+ *max_scale = 0.f;
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (animations_[i]->is_finished() ||
+ animations_[i]->target_property() != Animation::Transform)
+ continue;
+
+ bool forward_direction = true;
+ switch (animations_[i]->direction()) {
+ case Animation::Normal:
+ case Animation::Alternate:
+ forward_direction = animations_[i]->playback_rate() >= 0.0;
+ break;
+ case Animation::Reverse:
+ case Animation::AlternateReverse:
+ forward_direction = animations_[i]->playback_rate() < 0.0;
+ break;
+ }
+
+ const TransformAnimationCurve* transform_animation_curve =
+ animations_[i]->curve()->ToTransformAnimationCurve();
+ float animation_scale = 0.f;
+ if (!transform_animation_curve->MaximumTargetScale(forward_direction,
+ &animation_scale))
+ return false;
+ *max_scale = std::max(*max_scale, animation_scale);
+ }
+
+ return true;
+}
+
void LayerAnimationController::PushNewAnimationsToImplThread(
LayerAnimationController* controller_impl) const {
// Any new animations owned by the main thread's controller are cloned and
// add to the impl thread's controller.
- for (size_t i = 0; i < active_animations_.size(); ++i) {
+ for (size_t i = 0; i < animations_.size(); ++i) {
// If the animation is already running on the impl thread, there is no
// need to copy it over.
- if (controller_impl->GetAnimation(active_animations_[i]->group(),
- active_animations_[i]->target_property()))
+ if (controller_impl->GetAnimation(animations_[i]->group(),
+ animations_[i]->target_property()))
continue;
// If the animation is not running on the impl thread, it does not
// have already notified that it has started and the main thread animation
// will no longer need
// a synchronized start time.
- if (!active_animations_[i]->needs_synchronized_start_time())
+ if (!animations_[i]->needs_synchronized_start_time())
continue;
+ // Scroll animations always start at the current scroll offset.
+ if (animations_[i]->target_property() == Animation::ScrollOffset) {
+ gfx::Vector2dF current_scroll_offset;
+ if (controller_impl->value_provider_) {
+ current_scroll_offset =
+ controller_impl->value_provider_->ScrollOffsetForAnimation();
+ } else {
+ // The owning layer isn't yet in the active tree, so the main thread
+ // scroll offset will be up-to-date.
+ current_scroll_offset = value_provider_->ScrollOffsetForAnimation();
+ }
+ animations_[i]->curve()->ToScrollOffsetAnimationCurve()->SetInitialValue(
+ current_scroll_offset);
+ }
+
// The new animation should be set to run as soon as possible.
Animation::RunState initial_run_state =
Animation::WaitingForTargetAvailability;
- double start_time = 0;
- scoped_ptr<Animation> to_add(active_animations_[i]->CloneAndInitialize(
- initial_run_state, start_time));
+ scoped_ptr<Animation> to_add(
+ animations_[i]->CloneAndInitialize(initial_run_state));
DCHECK(!to_add->needs_synchronized_start_time());
+ to_add->set_affects_active_observers(false);
controller_impl->AddAnimation(to_add.Pass());
}
}
-struct IsCompleted {
- explicit IsCompleted(const LayerAnimationController& main_thread_controller)
- : main_thread_controller_(main_thread_controller) {}
- bool operator()(Animation* animation) const {
- if (animation->is_impl_only()) {
- return (animation->run_state() == Animation::WaitingForDeletion);
- } else {
- return !main_thread_controller_.GetAnimation(
- animation->group(),
- animation->target_property());
- }
+static bool IsCompleted(
+ Animation* animation,
+ const LayerAnimationController* main_thread_controller) {
+ if (animation->is_impl_only()) {
+ return (animation->run_state() == Animation::WaitingForDeletion);
+ } else {
+ return !main_thread_controller->GetAnimation(animation->group(),
+ animation->target_property());
}
+}
- private:
- const LayerAnimationController& main_thread_controller_;
-};
+static bool AffectsActiveOnlyAndIsWaitingForDeletion(Animation* animation) {
+ return animation->run_state() == Animation::WaitingForDeletion &&
+ !animation->affects_pending_observers();
+}
void LayerAnimationController::RemoveAnimationsCompletedOnMainThread(
LayerAnimationController* controller_impl) const {
- // Delete all impl thread animations for which there is no corresponding
- // main thread animation. Each iteration,
- // controller->active_animations_.size() is decremented or i is incremented
- // guaranteeing progress towards loop termination.
- ScopedPtrVector<Animation>& animations =
- controller_impl->active_animations_;
+ // Animations removed on the main thread should no longer affect pending
+ // observers, and should stop affecting active observers after the next call
+ // to ActivateAnimations. If already WaitingForDeletion, they can be removed
+ // immediately.
+ ScopedPtrVector<Animation>& animations = controller_impl->animations_;
+ for (size_t i = 0; i < animations.size(); ++i) {
+ if (IsCompleted(animations[i], this))
+ animations[i]->set_affects_pending_observers(false);
+ }
animations.erase(cc::remove_if(&animations,
animations.begin(),
animations.end(),
- IsCompleted(*this)),
+ AffectsActiveOnlyAndIsWaitingForDeletion),
animations.end());
}
void LayerAnimationController::PushPropertiesToImplThread(
LayerAnimationController* controller_impl) const {
- for (size_t i = 0; i < active_animations_.size(); ++i) {
- Animation* current_impl =
- controller_impl->GetAnimation(
- active_animations_[i]->group(),
- active_animations_[i]->target_property());
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ Animation* current_impl = controller_impl->GetAnimation(
+ animations_[i]->group(), animations_[i]->target_property());
if (current_impl)
- active_animations_[i]->PushPropertiesTo(current_impl);
- }
-}
-
-void LayerAnimationController::StartAnimationsWaitingForNextTick(
- double monotonic_time) {
- for (size_t i = 0; i < active_animations_.size(); ++i) {
- if (active_animations_[i]->run_state() == Animation::WaitingForNextTick)
- active_animations_[i]->SetRunState(Animation::Starting, monotonic_time);
+ animations_[i]->PushPropertiesTo(current_impl);
}
}
-void LayerAnimationController::StartAnimationsWaitingForStartTime(
- double monotonic_time) {
- for (size_t i = 0; i < active_animations_.size(); ++i) {
- if (active_animations_[i]->run_state() == Animation::WaitingForStartTime &&
- active_animations_[i]->start_time() <= monotonic_time)
- active_animations_[i]->SetRunState(Animation::Starting, monotonic_time);
- }
-}
-
-void LayerAnimationController::StartAnimationsWaitingForTargetAvailability(
- double monotonic_time) {
- // First collect running properties.
- TargetProperties blocked_properties;
- for (size_t i = 0; i < active_animations_.size(); ++i) {
- if (active_animations_[i]->run_state() == Animation::Starting ||
- active_animations_[i]->run_state() == Animation::Running)
- blocked_properties.insert(active_animations_[i]->target_property());
+void LayerAnimationController::StartAnimations(base::TimeTicks monotonic_time) {
+ DCHECK(needs_to_start_animations_);
+ needs_to_start_animations_ = false;
+ // First collect running properties affecting each type of observer.
+ TargetProperties blocked_properties_for_active_observers;
+ TargetProperties blocked_properties_for_pending_observers;
+ std::vector<size_t> animations_waiting_for_target;
+
+ animations_waiting_for_target.reserve(animations_.size());
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (animations_[i]->run_state() == Animation::Starting ||
+ animations_[i]->run_state() == Animation::Running) {
+ if (animations_[i]->affects_active_observers()) {
+ blocked_properties_for_active_observers.insert(
+ animations_[i]->target_property());
+ }
+ if (animations_[i]->affects_pending_observers()) {
+ blocked_properties_for_pending_observers.insert(
+ animations_[i]->target_property());
+ }
+ } else if (animations_[i]->run_state() ==
+ Animation::WaitingForTargetAvailability) {
+ animations_waiting_for_target.push_back(i);
+ }
}
- for (size_t i = 0; i < active_animations_.size(); ++i) {
- if (active_animations_[i]->run_state() ==
- Animation::WaitingForTargetAvailability) {
+ for (size_t i = 0; i < animations_waiting_for_target.size(); ++i) {
// Collect all properties for animations with the same group id (they
// should all also be in the list of animations).
+ size_t animation_index = animations_waiting_for_target[i];
+ Animation* animation_waiting_for_target = animations_[animation_index];
+ // Check for the run state again even though the animation was waiting
+ // for target because it might have changed the run state while handling
+ // previous animation in this loop (if they belong to same group).
+ if (animation_waiting_for_target->run_state() ==
+ Animation::WaitingForTargetAvailability) {
TargetProperties enqueued_properties;
- enqueued_properties.insert(active_animations_[i]->target_property());
- for (size_t j = i + 1; j < active_animations_.size(); ++j) {
- if (active_animations_[i]->group() == active_animations_[j]->group())
- enqueued_properties.insert(active_animations_[j]->target_property());
+ bool affects_active_observers =
+ animation_waiting_for_target->affects_active_observers();
+ bool affects_pending_observers =
+ animation_waiting_for_target->affects_pending_observers();
+ enqueued_properties.insert(
+ animation_waiting_for_target->target_property());
+ for (size_t j = animation_index + 1; j < animations_.size(); ++j) {
+ if (animation_waiting_for_target->group() == animations_[j]->group()) {
+ enqueued_properties.insert(animations_[j]->target_property());
+ affects_active_observers |=
+ animations_[j]->affects_active_observers();
+ affects_pending_observers |=
+ animations_[j]->affects_pending_observers();
+ }
}
// Check to see if intersection of the list of properties affected by
- // the group and the list of currently blocked properties is null. In
- // any case, the group's target properties need to be added to the list
- // of blocked properties.
+ // the group and the list of currently blocked properties is null, taking
+ // into account the type(s) of observers affected by the group. In any
+ // case, the group's target properties need to be added to the lists of
+ // blocked properties.
bool null_intersection = true;
for (TargetProperties::iterator p_iter = enqueued_properties.begin();
p_iter != enqueued_properties.end();
++p_iter) {
- if (!blocked_properties.insert(*p_iter).second)
+ if (affects_active_observers &&
+ !blocked_properties_for_active_observers.insert(*p_iter).second)
+ null_intersection = false;
+ if (affects_pending_observers &&
+ !blocked_properties_for_pending_observers.insert(*p_iter).second)
null_intersection = false;
}
// If the intersection is null, then we are free to start the animations
// in the group.
if (null_intersection) {
- active_animations_[i]->SetRunState(
- Animation::Starting, monotonic_time);
- for (size_t j = i + 1; j < active_animations_.size(); ++j) {
- if (active_animations_[i]->group() ==
- active_animations_[j]->group()) {
- active_animations_[j]->SetRunState(
- Animation::Starting, monotonic_time);
+ animation_waiting_for_target->SetRunState(Animation::Starting,
+ monotonic_time);
+ for (size_t j = animation_index + 1; j < animations_.size(); ++j) {
+ if (animation_waiting_for_target->group() ==
+ animations_[j]->group()) {
+ animations_[j]->SetRunState(Animation::Starting, monotonic_time);
}
}
+ } else {
+ needs_to_start_animations_ = true;
}
}
}
}
void LayerAnimationController::PromoteStartedAnimations(
- double monotonic_time,
+ base::TimeTicks monotonic_time,
AnimationEventsVector* events) {
- for (size_t i = 0; i < active_animations_.size(); ++i) {
- if (active_animations_[i]->run_state() == Animation::Starting) {
- active_animations_[i]->SetRunState(Animation::Running, monotonic_time);
- if (!active_animations_[i]->has_set_start_time())
- active_animations_[i]->set_start_time(monotonic_time);
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (animations_[i]->run_state() == Animation::Starting &&
+ animations_[i]->affects_active_observers()) {
+ animations_[i]->SetRunState(Animation::Running, monotonic_time);
+ if (!animations_[i]->has_set_start_time() &&
+ !animations_[i]->needs_synchronized_start_time())
+ animations_[i]->set_start_time(monotonic_time);
if (events) {
- AnimationEvent started_event(
- AnimationEvent::Started,
- id_,
- active_animations_[i]->group(),
- active_animations_[i]->target_property(),
- monotonic_time);
- started_event.is_impl_only = active_animations_[i]->is_impl_only();
- events->push_back(started_event);
+ AnimationEvent started_event(AnimationEvent::Started,
+ id_,
+ animations_[i]->group(),
+ animations_[i]->target_property(),
+ monotonic_time);
+ started_event.is_impl_only = animations_[i]->is_impl_only();
+ if (started_event.is_impl_only)
+ NotifyAnimationStarted(started_event);
+ else
+ events->push_back(started_event);
}
}
}
}
-void LayerAnimationController::MarkFinishedAnimations(double monotonic_time) {
- for (size_t i = 0; i < active_animations_.size(); ++i) {
- if (active_animations_[i]->IsFinishedAt(monotonic_time) &&
- active_animations_[i]->run_state() != Animation::WaitingForDeletion)
- active_animations_[i]->SetRunState(Animation::Finished, monotonic_time);
- }
-}
-
-void LayerAnimationController::ResolveConflicts(double monotonic_time) {
- // Find any animations that are animating the same property and resolve the
- // confict. We could eventually blend, but for now we'll just abort the
- // previous animation (where 'previous' means: (1) has a prior start time or
- // (2) has an equal start time, but was added to the queue earlier, i.e.,
- // has a lower index in active_animations_).
- for (size_t i = 0; i < active_animations_.size(); ++i) {
- if (active_animations_[i]->run_state() == Animation::Starting ||
- active_animations_[i]->run_state() == Animation::Running) {
- for (size_t j = i + 1; j < active_animations_.size(); ++j) {
- if ((active_animations_[j]->run_state() == Animation::Starting ||
- active_animations_[j]->run_state() == Animation::Running) &&
- active_animations_[i]->target_property() ==
- active_animations_[j]->target_property()) {
- if (active_animations_[i]->start_time() >
- active_animations_[j]->start_time()) {
- active_animations_[j]->SetRunState(Animation::Aborted,
- monotonic_time);
- } else {
- active_animations_[i]->SetRunState(Animation::Aborted,
- monotonic_time);
- }
- }
- }
- }
+void LayerAnimationController::MarkFinishedAnimations(
+ base::TimeTicks monotonic_time) {
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (animations_[i]->IsFinishedAt(monotonic_time) &&
+ animations_[i]->run_state() != Animation::Aborted &&
+ animations_[i]->run_state() != Animation::WaitingForDeletion)
+ animations_[i]->SetRunState(Animation::Finished, monotonic_time);
}
}
void LayerAnimationController::MarkAnimationsForDeletion(
- double monotonic_time, AnimationEventsVector* events) {
+ base::TimeTicks monotonic_time,
+ AnimationEventsVector* events) {
+ bool marked_animations_for_deletions = false;
+ std::vector<size_t> animations_with_same_group_id;
+
+ animations_with_same_group_id.reserve(animations_.size());
// Non-aborted animations are marked for deletion after a corresponding
// AnimationEvent::Finished event is sent or received. This means that if
// we don't have an events vector, we must ensure that non-aborted animations
// have received a finished event before marking them for deletion.
- for (size_t i = 0; i < active_animations_.size(); i++) {
- int group_id = active_animations_[i]->group();
+ for (size_t i = 0; i < animations_.size(); i++) {
+ int group_id = animations_[i]->group();
+ if (animations_[i]->run_state() == Animation::Aborted) {
+ if (events && !animations_[i]->is_impl_only()) {
+ AnimationEvent aborted_event(AnimationEvent::Aborted,
+ id_,
+ group_id,
+ animations_[i]->target_property(),
+ monotonic_time);
+ events->push_back(aborted_event);
+ }
+ animations_[i]->SetRunState(Animation::WaitingForDeletion,
+ monotonic_time);
+ marked_animations_for_deletions = true;
+ continue;
+ }
+
bool all_anims_with_same_id_are_finished = false;
// Since deleting an animation on the main thread leads to its deletion
// on the impl thread, we only mark a Finished main thread animation for
// deletion once it has received a Finished event from the impl thread.
bool animation_i_will_send_or_has_received_finish_event =
- events || active_animations_[i]->received_finished_event();
+ events || animations_[i]->received_finished_event();
// If an animation is finished, and not already marked for deletion,
// find out if all other animations in the same group are also finished.
- if (active_animations_[i]->run_state() == Animation::Aborted ||
- (active_animations_[i]->run_state() == Animation::Finished &&
- animation_i_will_send_or_has_received_finish_event)) {
+ if (animations_[i]->run_state() == Animation::Finished &&
+ animation_i_will_send_or_has_received_finish_event) {
+ // Clear the animations_with_same_group_id if it was added for
+ // the previous animation's iteration.
+ if (animations_with_same_group_id.size() > 0)
+ animations_with_same_group_id.clear();
all_anims_with_same_id_are_finished = true;
- for (size_t j = 0; j < active_animations_.size(); ++j) {
+ for (size_t j = 0; j < animations_.size(); ++j) {
bool animation_j_will_send_or_has_received_finish_event =
- events || active_animations_[j]->received_finished_event();
- if (group_id == active_animations_[j]->group() &&
- (!active_animations_[j]->is_finished() ||
- (active_animations_[j]->run_state() == Animation::Finished &&
- !animation_j_will_send_or_has_received_finish_event))) {
- all_anims_with_same_id_are_finished = false;
- break;
+ events || animations_[j]->received_finished_event();
+ if (group_id == animations_[j]->group()) {
+ if (!animations_[j]->is_finished() ||
+ (animations_[j]->run_state() == Animation::Finished &&
+ !animation_j_will_send_or_has_received_finish_event)) {
+ all_anims_with_same_id_are_finished = false;
+ break;
+ } else if (j >= i &&
+ animations_[j]->run_state() != Animation::Aborted) {
+ // Mark down the animations which belong to the same group
+ // and is not yet aborted. If this current iteration finds that all
+ // animations with same ID are finished, then the marked
+ // animations below will be set to WaitingForDeletion in next
+ // iteration.
+ animations_with_same_group_id.push_back(j);
+ }
}
}
}
// We now need to remove all animations with the same group id as
// group_id (and send along animation finished notifications, if
// necessary).
- for (size_t j = i; j < active_animations_.size(); j++) {
- if (group_id == active_animations_[j]->group()) {
+ for (size_t j = 0; j < animations_with_same_group_id.size(); j++) {
+ size_t animation_index = animations_with_same_group_id[j];
if (events) {
AnimationEvent finished_event(
AnimationEvent::Finished,
id_,
- active_animations_[j]->group(),
- active_animations_[j]->target_property(),
+ animations_[animation_index]->group(),
+ animations_[animation_index]->target_property(),
monotonic_time);
- finished_event.is_impl_only = active_animations_[j]->is_impl_only();
- events->push_back(finished_event);
+ finished_event.is_impl_only =
+ animations_[animation_index]->is_impl_only();
+ if (finished_event.is_impl_only)
+ NotifyAnimationFinished(finished_event);
+ else
+ events->push_back(finished_event);
}
- active_animations_[j]->SetRunState(Animation::WaitingForDeletion,
- monotonic_time);
- }
+ animations_[animation_index]->SetRunState(
+ Animation::WaitingForDeletion, monotonic_time);
}
+ marked_animations_for_deletions = true;
}
}
+ if (marked_animations_for_deletions)
+ NotifyObserversAnimationWaitingForDeletion();
}
static bool IsWaitingForDeletion(Animation* animation) {
}
void LayerAnimationController::PurgeAnimationsMarkedForDeletion() {
- ScopedPtrVector<Animation>& animations = active_animations_;
- animations.erase(cc::remove_if(&animations,
- animations.begin(),
- animations.end(),
- IsWaitingForDeletion),
- animations.end());
+ animations_.erase(cc::remove_if(&animations_,
+ animations_.begin(),
+ animations_.end(),
+ IsWaitingForDeletion),
+ animations_.end());
}
-void LayerAnimationController::ReplaceImplThreadAnimations(
- LayerAnimationController* controller_impl) const {
- controller_impl->active_animations_.clear();
- for (size_t i = 0; i < active_animations_.size(); ++i) {
- scoped_ptr<Animation> to_add;
- if (active_animations_[i]->needs_synchronized_start_time()) {
- // We haven't received an animation started notification yet, so it
- // is important that we add it in a 'waiting' and not 'running' state.
- Animation::RunState initial_run_state =
- Animation::WaitingForTargetAvailability;
- double start_time = 0;
- to_add = active_animations_[i]->CloneAndInitialize(
- initial_run_state, start_time).Pass();
- } else {
- to_add = active_animations_[i]->Clone().Pass();
- }
+void LayerAnimationController::TickAnimations(base::TimeTicks monotonic_time) {
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (animations_[i]->run_state() == Animation::Starting ||
+ animations_[i]->run_state() == Animation::Running ||
+ animations_[i]->run_state() == Animation::Paused) {
+ if (!animations_[i]->InEffect(monotonic_time))
+ continue;
- controller_impl->AddAnimation(to_add.Pass());
- }
-}
-
-void LayerAnimationController::TickAnimations(double monotonic_time) {
- for (size_t i = 0; i < active_animations_.size(); ++i) {
- if (active_animations_[i]->run_state() == Animation::Starting ||
- active_animations_[i]->run_state() == Animation::Running ||
- active_animations_[i]->run_state() == Animation::Paused) {
double trimmed =
- active_animations_[i]->TrimTimeToCurrentIteration(monotonic_time);
+ animations_[i]->TrimTimeToCurrentIteration(monotonic_time);
- switch (active_animations_[i]->target_property()) {
+ switch (animations_[i]->target_property()) {
case Animation::Transform: {
const TransformAnimationCurve* transform_animation_curve =
- active_animations_[i]->curve()->ToTransformAnimationCurve();
+ animations_[i]->curve()->ToTransformAnimationCurve();
const gfx::Transform transform =
transform_animation_curve->GetValue(trimmed);
- NotifyObserversTransformAnimated(transform);
+ NotifyObserversTransformAnimated(
+ transform,
+ animations_[i]->affects_active_observers(),
+ animations_[i]->affects_pending_observers());
break;
}
case Animation::Opacity: {
const FloatAnimationCurve* float_animation_curve =
- active_animations_[i]->curve()->ToFloatAnimationCurve();
- const float opacity = float_animation_curve->GetValue(trimmed);
- NotifyObserversOpacityAnimated(opacity);
+ animations_[i]->curve()->ToFloatAnimationCurve();
+ const float opacity = std::max(
+ std::min(float_animation_curve->GetValue(trimmed), 1.0f), 0.f);
+ NotifyObserversOpacityAnimated(
+ opacity,
+ animations_[i]->affects_active_observers(),
+ animations_[i]->affects_pending_observers());
break;
}
case Animation::Filter: {
const FilterAnimationCurve* filter_animation_curve =
- active_animations_[i]->curve()->ToFilterAnimationCurve();
+ animations_[i]->curve()->ToFilterAnimationCurve();
const FilterOperations filter =
filter_animation_curve->GetValue(trimmed);
- NotifyObserversFilterAnimated(filter);
+ NotifyObserversFilterAnimated(
+ filter,
+ animations_[i]->affects_active_observers(),
+ animations_[i]->affects_pending_observers());
break;
}
break;
}
+ case Animation::ScrollOffset: {
+ const ScrollOffsetAnimationCurve* scroll_offset_animation_curve =
+ animations_[i]->curve()->ToScrollOffsetAnimationCurve();
+ const gfx::Vector2dF scroll_offset =
+ scroll_offset_animation_curve->GetValue(trimmed);
+ NotifyObserversScrollOffsetAnimated(
+ scroll_offset,
+ animations_[i]->affects_active_observers(),
+ animations_[i]->affects_pending_observers());
+ break;
+ }
+
// Do nothing for sentinel value.
case Animation::TargetPropertyEnumSize:
NOTREACHED();
void LayerAnimationController::UpdateActivation(UpdateActivationType type) {
bool force = type == ForceActivation;
if (registrar_) {
- if (!active_animations_.empty() && (!is_active_ || force))
+ bool was_active = is_active_;
+ is_active_ = false;
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (animations_[i]->run_state() != Animation::WaitingForDeletion) {
+ is_active_ = true;
+ break;
+ }
+ }
+
+ if (is_active_ && (!was_active || force))
registrar_->DidActivateAnimationController(this);
- else if (active_animations_.empty() && (is_active_ || force))
+ else if (!is_active_ && (was_active || force))
registrar_->DidDeactivateAnimationController(this);
- is_active_ = !active_animations_.empty();
}
}
-void LayerAnimationController::NotifyObserversOpacityAnimated(float opacity) {
- FOR_EACH_OBSERVER(LayerAnimationValueObserver,
- value_observers_,
- OnOpacityAnimated(opacity));
+void LayerAnimationController::NotifyObserversOpacityAnimated(
+ float opacity,
+ bool notify_active_observers,
+ bool notify_pending_observers) {
+ if (value_observers_.might_have_observers()) {
+ ObserverListBase<LayerAnimationValueObserver>::Iterator it(
+ value_observers_);
+ LayerAnimationValueObserver* obs;
+ while ((obs = it.GetNext()) != NULL) {
+ if ((notify_active_observers && notify_pending_observers) ||
+ (notify_active_observers && obs->IsActive()) ||
+ (notify_pending_observers && !obs->IsActive()))
+ obs->OnOpacityAnimated(opacity);
+ }
+ }
}
void LayerAnimationController::NotifyObserversTransformAnimated(
- const gfx::Transform& transform) {
- FOR_EACH_OBSERVER(LayerAnimationValueObserver,
- value_observers_,
- OnTransformAnimated(transform));
+ const gfx::Transform& transform,
+ bool notify_active_observers,
+ bool notify_pending_observers) {
+ if (value_observers_.might_have_observers()) {
+ ObserverListBase<LayerAnimationValueObserver>::Iterator it(
+ value_observers_);
+ LayerAnimationValueObserver* obs;
+ while ((obs = it.GetNext()) != NULL) {
+ if ((notify_active_observers && notify_pending_observers) ||
+ (notify_active_observers && obs->IsActive()) ||
+ (notify_pending_observers && !obs->IsActive()))
+ obs->OnTransformAnimated(transform);
+ }
+ }
}
void LayerAnimationController::NotifyObserversFilterAnimated(
- const FilterOperations& filters) {
+ const FilterOperations& filters,
+ bool notify_active_observers,
+ bool notify_pending_observers) {
+ if (value_observers_.might_have_observers()) {
+ ObserverListBase<LayerAnimationValueObserver>::Iterator it(
+ value_observers_);
+ LayerAnimationValueObserver* obs;
+ while ((obs = it.GetNext()) != NULL) {
+ if ((notify_active_observers && notify_pending_observers) ||
+ (notify_active_observers && obs->IsActive()) ||
+ (notify_pending_observers && !obs->IsActive()))
+ obs->OnFilterAnimated(filters);
+ }
+ }
+}
+
+void LayerAnimationController::NotifyObserversScrollOffsetAnimated(
+ const gfx::Vector2dF& scroll_offset,
+ bool notify_active_observers,
+ bool notify_pending_observers) {
+ if (value_observers_.might_have_observers()) {
+ ObserverListBase<LayerAnimationValueObserver>::Iterator it(
+ value_observers_);
+ LayerAnimationValueObserver* obs;
+ while ((obs = it.GetNext()) != NULL) {
+ if ((notify_active_observers && notify_pending_observers) ||
+ (notify_active_observers && obs->IsActive()) ||
+ (notify_pending_observers && !obs->IsActive()))
+ obs->OnScrollOffsetAnimated(scroll_offset);
+ }
+ }
+}
+
+void LayerAnimationController::NotifyObserversAnimationWaitingForDeletion() {
FOR_EACH_OBSERVER(LayerAnimationValueObserver,
value_observers_,
- OnFilterAnimated(filters));
+ OnAnimationWaitingForDeletion());
}
bool LayerAnimationController::HasValueObserver() {