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

Unified Diff: cc/animation/animation_player.cc

Issue 2335043002: CC Animation: Move animations_ from ElementAnimations to AnimationPlayer. (Closed)
Patch Set: Clean it up harder. Rework UpdateClientAnimationState. Created 4 years, 3 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: cc/animation/animation_player.cc
diff --git a/cc/animation/animation_player.cc b/cc/animation/animation_player.cc
index bef51a07390a6c1f822681804232856ad22c3950..de5801e74f423e2638bb7402114a7811deec5cc3 100644
--- a/cc/animation/animation_player.cc
+++ b/cc/animation/animation_player.cc
@@ -9,7 +9,7 @@
#include "cc/animation/animation_delegate.h"
#include "cc/animation/animation_host.h"
#include "cc/animation/animation_timeline.h"
-#include "cc/animation/element_animations.h"
+#include "cc/animation/scroll_offset_animation_curve.h"
namespace cc {
@@ -23,7 +23,8 @@ AnimationPlayer::AnimationPlayer(int id)
element_animations_(),
animation_delegate_(),
id_(id),
- needs_push_properties_(false) {
+ needs_push_properties_(false),
+ needs_to_start_animations_(false) {
DCHECK(id_);
}
@@ -104,80 +105,141 @@ void AnimationPlayer::BindElementAnimations() {
animation_host_->GetElementAnimationsForElementId(element_id_);
DCHECK(element_animations_);
- // Pass all accumulated animations to ElementAnimations.
for (auto& animation : animations_)
- element_animations_->AddAnimation(std::move(animation));
+ AnimationAddedForProperty(animation->target_property());
- if (!animations_.empty())
- SetNeedsCommit();
SetNeedsPushProperties();
-
- animations_.clear();
}
void AnimationPlayer::UnbindElementAnimations() {
SetNeedsPushProperties();
-
element_animations_ = nullptr;
- DCHECK(animations_.empty());
}
void AnimationPlayer::AddAnimation(std::unique_ptr<Animation> animation) {
DCHECK(animation->target_property() != TargetProperty::SCROLL_OFFSET ||
(animation_host_ && animation_host_->SupportsScrollAnimations()));
+ DCHECK(!animation->is_impl_only() ||
+ animation->target_property() == TargetProperty::SCROLL_OFFSET);
+ TargetProperty::Type target_property = animation->target_property();
+ animations_.push_back(std::move(animation));
if (element_animations_) {
- element_animations_->AddAnimation(std::move(animation));
- SetNeedsCommit();
+ AnimationAddedForProperty(target_property);
SetNeedsPushProperties();
- } else {
- animations_.push_back(std::move(animation));
}
}
-void AnimationPlayer::PauseAnimation(int animation_id, double time_offset) {
+void AnimationPlayer::AnimationAddedForProperty(
+ TargetProperty::Type target_property) {
DCHECK(element_animations_);
- element_animations_->PauseAnimation(
- animation_id, base::TimeDelta::FromSecondsD(time_offset));
+
SetNeedsCommit();
- SetNeedsPushProperties();
+ needs_to_start_animations_ = true;
+
+ element_animations_->UpdateActivationNormal();
+ element_animations_->UpdateClientAnimationState(target_property);
+}
+
+void AnimationPlayer::PauseAnimation(int animation_id, double time_offset) {
+ const base::TimeDelta time_delta = base::TimeDelta::FromSecondsD(time_offset);
+
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (animations_[i]->id() == animation_id) {
+ animations_[i]->SetRunState(Animation::PAUSED,
+ time_delta + animations_[i]->start_time() +
+ animations_[i]->time_offset());
+ }
+ }
+
+ if (element_animations_) {
+ SetNeedsCommit();
+ SetNeedsPushProperties();
+ }
}
void AnimationPlayer::RemoveAnimation(int animation_id) {
+ bool removed_transform_animation = false;
+ bool removed_opacity_animation = false;
+ bool removed_filter_animation = false;
+ // Since we want to use the animations that we're going to remove, we need to
+ // use a stable_parition here instead of remove_if. Remove_if leaves the
+ // removed items in an unspecified state.
+ auto animations_to_remove = std::stable_partition(
+ animations_.begin(), animations_.end(),
+ [animation_id](const std::unique_ptr<Animation>& animation) {
+ return animation->id() != animation_id;
+ });
+ for (auto it = animations_to_remove; it != animations_.end(); ++it) {
+ if ((*it)->target_property() == TargetProperty::SCROLL_OFFSET) {
+ if (element_animations_)
+ element_animations_->SetScrollOffsetAnimationWasInterrupted();
+ } else if ((*it)->target_property() == TargetProperty::TRANSFORM &&
+ !(*it)->is_finished()) {
+ removed_transform_animation = true;
+ } else if ((*it)->target_property() == TargetProperty::OPACITY &&
+ !(*it)->is_finished()) {
+ removed_opacity_animation = true;
+ } else if ((*it)->target_property() == TargetProperty::FILTER &&
+ !(*it)->is_finished()) {
+ removed_filter_animation = true;
+ }
+ }
+
+ animations_.erase(animations_to_remove, animations_.end());
+
if (element_animations_) {
- element_animations_->RemoveAnimation(animation_id);
+ element_animations_->UpdateActivationNormal();
+ element_animations_->UpdateClientAnimationState(removed_transform_animation,
+ removed_opacity_animation,
+ removed_filter_animation);
SetNeedsCommit();
SetNeedsPushProperties();
- } else {
- auto animations_to_remove = std::remove_if(
- animations_.begin(), animations_.end(),
- [animation_id](const std::unique_ptr<Animation>& animation) {
- return animation->id() == animation_id;
- });
- animations_.erase(animations_to_remove, animations_.end());
}
}
void AnimationPlayer::AbortAnimation(int animation_id) {
- DCHECK(element_animations_);
- element_animations_->AbortAnimation(animation_id);
- SetNeedsCommit();
- SetNeedsPushProperties();
+ if (Animation* animation = GetAnimationById(animation_id)) {
+ if (!animation->is_finished()) {
+ animation->SetRunState(Animation::ABORTED, last_tick_time_);
+ if (element_animations_)
+ element_animations_->UpdateClientAnimationState(
+ animation->target_property());
+ }
+ }
+
+ if (element_animations_) {
+ SetNeedsCommit();
+ SetNeedsPushProperties();
+ }
}
void AnimationPlayer::AbortAnimations(TargetProperty::Type target_property,
bool needs_completion) {
+ if (needs_completion)
+ DCHECK(target_property == TargetProperty::SCROLL_OFFSET);
+
+ bool aborted_animation = false;
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (animations_[i]->target_property() == target_property &&
+ !animations_[i]->is_finished()) {
+ // Currently only impl-only scroll offset animations can be completed on
+ // the main thread.
+ if (needs_completion && animations_[i]->is_impl_only()) {
+ animations_[i]->SetRunState(Animation::ABORTED_BUT_NEEDS_COMPLETION,
+ last_tick_time_);
+ } else {
+ animations_[i]->SetRunState(Animation::ABORTED, last_tick_time_);
+ }
+ aborted_animation = true;
+ }
+ }
+
if (element_animations_) {
- element_animations_->AbortAnimations(target_property, needs_completion);
+ if (aborted_animation)
+ element_animations_->UpdateClientAnimationState(target_property);
SetNeedsCommit();
SetNeedsPushProperties();
- } else {
- auto animations_to_remove = std::remove_if(
- animations_.begin(), animations_.end(),
- [target_property](const std::unique_ptr<Animation>& animation) {
- return animation->target_property() == target_property;
- });
- animations_.erase(animations_to_remove, animations_.end());
}
}
@@ -186,59 +248,110 @@ void AnimationPlayer::PushPropertiesTo(AnimationPlayer* player_impl) {
return;
needs_push_properties_ = false;
+ // Create or destroy ElementAnimations.
if (element_id_ != player_impl->element_id()) {
if (player_impl->element_id())
player_impl->DetachElement();
if (element_id_)
player_impl->AttachElement(element_id_);
}
+
+ if (!has_any_animation() && !player_impl->has_any_animation())
+ return;
+
+ MarkAbortedAnimationsForDeletion(player_impl);
+ PurgeAnimationsMarkedForDeletion();
+ PushNewAnimationsToImplThread(player_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(player_impl);
+
+ PushPropertiesToImplThread(player_impl);
}
-void AnimationPlayer::NotifyAnimationStarted(
- base::TimeTicks monotonic_time,
- TargetProperty::Type target_property,
- int group) {
- if (animation_delegate_)
- animation_delegate_->NotifyAnimationStarted(monotonic_time, target_property,
- group);
+bool AnimationPlayer::NotifyAnimationStarted(const AnimationEvent& event) {
+ DCHECK(!event.is_impl_only);
+
+ 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);
+
+ if (animation_delegate_) {
+ animation_delegate_->NotifyAnimationStarted(
+ event.monotonic_time, event.target_property, event.group_id);
+ }
+ return true;
+ }
+ }
+
+ return false;
}
-void AnimationPlayer::NotifyAnimationFinished(
- base::TimeTicks monotonic_time,
- TargetProperty::Type target_property,
- int group) {
- if (animation_delegate_)
- animation_delegate_->NotifyAnimationFinished(monotonic_time,
- target_property, group);
+bool AnimationPlayer::NotifyAnimationFinished(const AnimationEvent& event) {
+ DCHECK(!event.is_impl_only);
+
+ 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 (animation_delegate_) {
+ animation_delegate_->NotifyAnimationFinished(
+ event.monotonic_time, event.target_property, event.group_id);
+ }
+ return true;
+ }
+ }
+
+ return false;
}
-void AnimationPlayer::NotifyAnimationAborted(
- base::TimeTicks monotonic_time,
+bool AnimationPlayer::NotifyAnimationFinishedForTesting(
TargetProperty::Type target_property,
- int group) {
- if (animation_delegate_)
- animation_delegate_->NotifyAnimationAborted(monotonic_time, target_property,
- group);
+ int group_id) {
+ AnimationEvent event(AnimationEvent::FINISHED, element_id_, group_id,
+ target_property, base::TimeTicks());
+ return NotifyAnimationFinished(event);
}
-void AnimationPlayer::NotifyAnimationWaitingForDeletion() {
- // We need to purge animations marked for deletion.
- SetNeedsPushProperties();
+bool AnimationPlayer::NotifyAnimationAborted(const AnimationEvent& event) {
+ DCHECK(!event.is_impl_only);
+
+ 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);
+ animations_[i]->set_received_finished_event(true);
+ if (animation_delegate_) {
+ animation_delegate_->NotifyAnimationAborted(
+ event.monotonic_time, event.target_property, event.group_id);
+ }
+ return true;
+ }
+ }
+
+ return false;
}
-void AnimationPlayer::NotifyAnimationTakeover(
- base::TimeTicks monotonic_time,
- TargetProperty::Type target_property,
- double animation_start_time,
- std::unique_ptr<AnimationCurve> curve) {
+void AnimationPlayer::NotifyAnimationTakeover(const AnimationEvent& event) {
+ DCHECK(!event.is_impl_only);
+ DCHECK(event.target_property == TargetProperty::SCROLL_OFFSET);
+
// We need to purge animations marked for deletion on CT.
SetNeedsPushProperties();
if (animation_delegate_) {
- DCHECK(curve);
+ DCHECK(event.curve);
+ std::unique_ptr<AnimationCurve> animation_curve = event.curve->Clone();
animation_delegate_->NotifyAnimationTakeover(
- monotonic_time, target_property, animation_start_time,
- std::move(curve));
+ event.monotonic_time, event.target_property, event.animation_start_time,
+ std::move(animation_curve));
}
}
@@ -257,4 +370,780 @@ void AnimationPlayer::SetNeedsPushProperties() {
element_animations_->SetNeedsPushProperties();
}
+bool AnimationPlayer::HasActiveAnimation() const {
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (!animations_[i]->is_finished())
+ return true;
+ }
+ return false;
+}
+
+bool AnimationPlayer::HasNonDeletedAnimation() const {
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (animations_[i]->run_state() != Animation::WAITING_FOR_DELETION)
+ return true;
+ }
+
+ return false;
+}
+
+void AnimationPlayer::StartAnimations(base::TimeTicks monotonic_time) {
+ DCHECK(needs_to_start_animations_);
+ needs_to_start_animations_ = false;
+ // First collect running properties affecting each type of element.
+ TargetProperties blocked_properties_for_active_elements;
+ TargetProperties blocked_properties_for_pending_elements;
+ 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_elements()) {
+ blocked_properties_for_active_elements[animations_[i]
+ ->target_property()] = true;
+ }
+ if (animations_[i]->affects_pending_elements()) {
+ blocked_properties_for_pending_elements[animations_[i]
+ ->target_property()] = true;
+ }
+ } else if (animations_[i]->run_state() ==
+ Animation::WAITING_FOR_TARGET_AVAILABILITY) {
+ animations_waiting_for_target.push_back(i);
+ }
+ }
+
+ 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].get();
+ // 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::WAITING_FOR_TARGET_AVAILABILITY) {
+ TargetProperties enqueued_properties;
+ bool affects_active_elements =
+ animation_waiting_for_target->affects_active_elements();
+ bool affects_pending_elements =
+ animation_waiting_for_target->affects_pending_elements();
+ enqueued_properties[animation_waiting_for_target->target_property()] =
+ true;
+ for (size_t j = animation_index + 1; j < animations_.size(); ++j) {
+ if (animation_waiting_for_target->group() == animations_[j]->group()) {
+ enqueued_properties[animations_[j]->target_property()] = true;
+ affects_active_elements |= animations_[j]->affects_active_elements();
+ affects_pending_elements |=
+ animations_[j]->affects_pending_elements();
+ }
+ }
+
+ // Check to see if intersection of the list of properties affected by
+ // the group and the list of currently blocked properties is null, taking
+ // into account the type(s) of elements 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;
+ static_assert(TargetProperty::FIRST_TARGET_PROPERTY == 0,
+ "TargetProperty must be 0-based enum");
+ for (int property = TargetProperty::FIRST_TARGET_PROPERTY;
+ property <= TargetProperty::LAST_TARGET_PROPERTY; ++property) {
+ if (enqueued_properties[property]) {
+ if (affects_active_elements) {
+ if (blocked_properties_for_active_elements[property])
+ null_intersection = false;
+ else
+ blocked_properties_for_active_elements[property] = true;
+ }
+ if (affects_pending_elements) {
+ if (blocked_properties_for_pending_elements[property])
+ null_intersection = false;
+ else
+ blocked_properties_for_pending_elements[property] = true;
+ }
+ }
+ }
+
+ // If the intersection is null, then we are free to start the animations
+ // in the group.
+ if (null_intersection) {
+ 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 AnimationPlayer::PromoteStartedAnimations(base::TimeTicks monotonic_time,
+ AnimationEvents* events) {
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (animations_[i]->run_state() == Animation::STARTING &&
+ animations_[i]->affects_active_elements()) {
+ 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) {
+ base::TimeTicks start_time;
+ if (animations_[i]->has_set_start_time())
+ start_time = animations_[i]->start_time();
+ else
+ start_time = monotonic_time;
+ AnimationEvent started_event(
+ AnimationEvent::STARTED, element_id_, animations_[i]->group(),
+ animations_[i]->target_property(), start_time);
+ started_event.is_impl_only = animations_[i]->is_impl_only();
+ if (started_event.is_impl_only) {
+ // Notify delegate directly, do not record the event.
+ if (animation_delegate_) {
+ animation_delegate_->NotifyAnimationStarted(
+ started_event.monotonic_time, started_event.target_property,
+ started_event.group_id);
+ }
+ } else {
+ events->events_.push_back(started_event);
+ }
+ }
+ }
+ }
+}
+
+void AnimationPlayer::MarkAnimationsForDeletion(base::TimeTicks monotonic_time,
+ AnimationEvents* 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 < 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, element_id_, group_id,
+ animations_[i]->target_property(), monotonic_time);
+ events->events_.push_back(aborted_event);
+ }
+ // If on the compositor or on the main thread and received finish event,
+ // animation can be marked for deletion.
+ if (events || animations_[i]->received_finished_event()) {
+ animations_[i]->SetRunState(Animation::WAITING_FOR_DELETION,
+ monotonic_time);
+ marked_animations_for_deletions = true;
+ }
+ continue;
+ }
+
+ // If running on the compositor and need to complete an aborted animation
+ // on the main thread.
+ if (events &&
+ animations_[i]->run_state() ==
+ Animation::ABORTED_BUT_NEEDS_COMPLETION) {
+ AnimationEvent aborted_event(AnimationEvent::TAKEOVER, element_id_,
+ group_id, animations_[i]->target_property(),
+ monotonic_time);
+ aborted_event.animation_start_time =
+ (animations_[i]->start_time() - base::TimeTicks()).InSecondsF();
+ const ScrollOffsetAnimationCurve* scroll_offset_animation_curve =
+ animations_[i]->curve()->ToScrollOffsetAnimationCurve();
+ aborted_event.curve = scroll_offset_animation_curve->Clone();
+ // Notify the compositor that the animation is finished.
+ if (animation_delegate_) {
+ animation_delegate_->NotifyAnimationFinished(
+ aborted_event.monotonic_time, aborted_event.target_property,
+ aborted_event.group_id);
+ }
+ // Notify main thread.
+ events->events_.push_back(aborted_event);
+
+ // Remove the animation from the compositor.
+ animations_[i]->SetRunState(Animation::WAITING_FOR_DELETION,
+ 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 =
+ animations_[i]->is_controlling_instance() ||
+ animations_[i]->is_impl_only() ||
+ 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 (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 < animations_.size(); ++j) {
+ bool animation_j_will_send_or_has_received_finish_event =
+ animations_[j]->is_controlling_instance() ||
+ animations_[j]->is_impl_only() ||
+ 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 WAITING_FOR_DELETION in next
+ // iteration.
+ animations_with_same_group_id.push_back(j);
+ }
+ }
+ }
+ }
+ if (all_anims_with_same_id_are_finished) {
+ // 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 = 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, element_id_,
+ animations_[animation_index]->group(),
+ animations_[animation_index]->target_property(), monotonic_time);
+ finished_event.is_impl_only =
+ animations_[animation_index]->is_impl_only();
+ if (finished_event.is_impl_only) {
+ // Notify delegate directly, do not record the event.
+ if (animation_delegate_) {
+ animation_delegate_->NotifyAnimationFinished(
+ finished_event.monotonic_time, finished_event.target_property,
+ finished_event.group_id);
+ }
+ } else {
+ events->events_.push_back(finished_event);
+ }
+ }
+ animations_[animation_index]->SetRunState(
+ Animation::WAITING_FOR_DELETION, monotonic_time);
+ }
+ marked_animations_for_deletions = true;
+ }
+ }
+
+ // Notify about animations waiting for deletion.
+ // We need to purge animations marked for deletion, which happens in
+ // PushProperties().
+ if (marked_animations_for_deletions)
+ SetNeedsPushProperties();
+}
+
+void AnimationPlayer::TickAnimations(base::TimeTicks monotonic_time) {
+ DCHECK(element_animations_);
+
+ 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;
+
+ base::TimeDelta trimmed =
+ animations_[i]->TrimTimeToCurrentIteration(monotonic_time);
+
+ switch (animations_[i]->target_property()) {
+ case TargetProperty::TRANSFORM: {
+ const TransformAnimationCurve* transform_animation_curve =
+ animations_[i]->curve()->ToTransformAnimationCurve();
+ const gfx::Transform transform =
+ transform_animation_curve->GetValue(trimmed);
+ element_animations_->NotifyClientTransformAnimated(
+ transform, animations_[i]->affects_active_elements(),
+ animations_[i]->affects_pending_elements());
+ break;
+ }
+
+ case TargetProperty::OPACITY: {
+ const FloatAnimationCurve* float_animation_curve =
+ animations_[i]->curve()->ToFloatAnimationCurve();
+ const float opacity = std::max(
+ std::min(float_animation_curve->GetValue(trimmed), 1.0f), 0.f);
+ element_animations_->NotifyClientOpacityAnimated(
+ opacity, animations_[i]->affects_active_elements(),
+ animations_[i]->affects_pending_elements());
+ break;
+ }
+
+ case TargetProperty::FILTER: {
+ const FilterAnimationCurve* filter_animation_curve =
+ animations_[i]->curve()->ToFilterAnimationCurve();
+ const FilterOperations filter =
+ filter_animation_curve->GetValue(trimmed);
+ element_animations_->NotifyClientFilterAnimated(
+ filter, animations_[i]->affects_active_elements(),
+ animations_[i]->affects_pending_elements());
+ break;
+ }
+
+ case TargetProperty::BACKGROUND_COLOR: {
+ // Not yet implemented.
+ break;
+ }
+
+ case TargetProperty::SCROLL_OFFSET: {
+ const ScrollOffsetAnimationCurve* scroll_offset_animation_curve =
+ animations_[i]->curve()->ToScrollOffsetAnimationCurve();
+ const gfx::ScrollOffset scroll_offset =
+ scroll_offset_animation_curve->GetValue(trimmed);
+ element_animations_->NotifyClientScrollOffsetAnimated(
+ scroll_offset, animations_[i]->affects_active_elements(),
+ animations_[i]->affects_pending_elements());
+ break;
+ }
+ }
+ }
+ }
+
+ last_tick_time_ = monotonic_time;
+}
+
+void AnimationPlayer::MarkFinishedAnimations(base::TimeTicks monotonic_time) {
+ bool finished_transform_animation = false;
+ bool finished_opacity_animation = false;
+ bool finished_filter_animation = false;
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (!animations_[i]->is_finished() &&
+ animations_[i]->IsFinishedAt(monotonic_time)) {
+ animations_[i]->SetRunState(Animation::FINISHED, monotonic_time);
+ if (animations_[i]->target_property() == TargetProperty::TRANSFORM)
+ finished_transform_animation = true;
+ else if (animations_[i]->target_property() == TargetProperty::OPACITY)
+ finished_opacity_animation = true;
+ else if (animations_[i]->target_property() == TargetProperty::FILTER)
+ finished_filter_animation = true;
+ }
+ }
+
+ DCHECK(element_animations_);
+ element_animations_->UpdateClientAnimationState(finished_transform_animation,
+ finished_opacity_animation,
+ finished_filter_animation);
+}
+
+void AnimationPlayer::ActivateAnimations(bool* changed_transform_animation,
+ bool* changed_opacity_animation,
+ bool* changed_filter_animation) {
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (animations_[i]->affects_active_elements() !=
+ animations_[i]->affects_pending_elements()) {
+ if (animations_[i]->target_property() == TargetProperty::TRANSFORM)
+ *changed_transform_animation = true;
+ else if (animations_[i]->target_property() == TargetProperty::OPACITY)
+ *changed_opacity_animation = true;
+ else if (animations_[i]->target_property() == TargetProperty::FILTER)
+ *changed_filter_animation = true;
+ }
+ animations_[i]->set_affects_active_elements(
+ animations_[i]->affects_pending_elements());
+ }
+ auto affects_no_elements = [](const std::unique_ptr<Animation>& animation) {
+ return !animation->affects_active_elements() &&
+ !animation->affects_pending_elements();
+ };
+ animations_.erase(std::remove_if(animations_.begin(), animations_.end(),
+ affects_no_elements),
+ animations_.end());
+}
+
+bool AnimationPlayer::HasFilterAnimationThatInflatesBounds() const {
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (!animations_[i]->is_finished() &&
+ animations_[i]->target_property() == TargetProperty::FILTER &&
+ animations_[i]
+ ->curve()
+ ->ToFilterAnimationCurve()
+ ->HasFilterThatMovesPixels())
+ return true;
+ }
+
+ return false;
+}
+
+bool AnimationPlayer::HasTransformAnimationThatInflatesBounds() const {
+ return IsCurrentlyAnimatingProperty(TargetProperty::TRANSFORM,
+ ElementListType::ACTIVE) ||
+ IsCurrentlyAnimatingProperty(TargetProperty::TRANSFORM,
+ ElementListType::PENDING);
+}
+
+bool AnimationPlayer::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 < animations_.size(); ++i) {
+ if (animations_[i]->is_finished() ||
+ animations_[i]->target_property() != TargetProperty::TRANSFORM)
+ continue;
+
+ const TransformAnimationCurve* transform_animation_curve =
+ animations_[i]->curve()->ToTransformAnimationCurve();
+ gfx::BoxF animation_bounds;
+ bool success =
+ transform_animation_curve->AnimatedBoundsForBox(box, &animation_bounds);
+ if (!success)
+ return false;
+ bounds->Union(animation_bounds);
+ }
+
+ return true;
+}
+
+bool AnimationPlayer::HasAnimationThatAffectsScale() const {
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (animations_[i]->is_finished() ||
+ animations_[i]->target_property() != TargetProperty::TRANSFORM)
+ continue;
+
+ const TransformAnimationCurve* transform_animation_curve =
+ animations_[i]->curve()->ToTransformAnimationCurve();
+ if (transform_animation_curve->AffectsScale())
+ return true;
+ }
+
+ return false;
+}
+
+bool AnimationPlayer::HasOnlyTranslationTransforms(
+ ElementListType list_type) const {
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (animations_[i]->is_finished() ||
+ animations_[i]->target_property() != TargetProperty::TRANSFORM)
+ continue;
+
+ if ((list_type == ElementListType::ACTIVE &&
+ !animations_[i]->affects_active_elements()) ||
+ (list_type == ElementListType::PENDING &&
+ !animations_[i]->affects_pending_elements()))
+ continue;
+
+ const TransformAnimationCurve* transform_animation_curve =
+ animations_[i]->curve()->ToTransformAnimationCurve();
+ if (!transform_animation_curve->IsTranslation())
+ return false;
+ }
+
+ return true;
+}
+
+bool AnimationPlayer::AnimationsPreserveAxisAlignment() const {
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (animations_[i]->is_finished() ||
+ animations_[i]->target_property() != TargetProperty::TRANSFORM)
+ continue;
+
+ const TransformAnimationCurve* transform_animation_curve =
+ animations_[i]->curve()->ToTransformAnimationCurve();
+ if (!transform_animation_curve->PreservesAxisAlignment())
+ return false;
+ }
+
+ return true;
+}
+
+bool AnimationPlayer::AnimationStartScale(ElementListType list_type,
+ float* start_scale) const {
+ *start_scale = 0.f;
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (animations_[i]->is_finished() ||
+ animations_[i]->target_property() != TargetProperty::TRANSFORM)
+ continue;
+
+ if ((list_type == ElementListType::ACTIVE &&
+ !animations_[i]->affects_active_elements()) ||
+ (list_type == ElementListType::PENDING &&
+ !animations_[i]->affects_pending_elements()))
+ continue;
+
+ bool forward_direction = true;
+ switch (animations_[i]->direction()) {
+ case Animation::Direction::NORMAL:
+ case Animation::Direction::ALTERNATE_NORMAL:
+ forward_direction = animations_[i]->playback_rate() >= 0.0;
+ break;
+ case Animation::Direction::REVERSE:
+ case Animation::Direction::ALTERNATE_REVERSE:
+ forward_direction = animations_[i]->playback_rate() < 0.0;
+ break;
+ }
+
+ const TransformAnimationCurve* transform_animation_curve =
+ animations_[i]->curve()->ToTransformAnimationCurve();
+ float animation_start_scale = 0.f;
+ if (!transform_animation_curve->AnimationStartScale(forward_direction,
+ &animation_start_scale))
+ return false;
+ *start_scale = std::max(*start_scale, animation_start_scale);
+ }
+ return true;
+}
+
+bool AnimationPlayer::MaximumTargetScale(ElementListType list_type,
+ 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() != TargetProperty::TRANSFORM)
+ continue;
+
+ if ((list_type == ElementListType::ACTIVE &&
+ !animations_[i]->affects_active_elements()) ||
+ (list_type == ElementListType::PENDING &&
+ !animations_[i]->affects_pending_elements()))
+ continue;
+
+ bool forward_direction = true;
+ switch (animations_[i]->direction()) {
+ case Animation::Direction::NORMAL:
+ case Animation::Direction::ALTERNATE_NORMAL:
+ forward_direction = animations_[i]->playback_rate() >= 0.0;
+ break;
+ case Animation::Direction::REVERSE:
+ case Animation::Direction::ALTERNATE_REVERSE:
+ 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;
+}
+
+bool AnimationPlayer::IsPotentiallyAnimatingProperty(
+ TargetProperty::Type target_property,
+ ElementListType list_type) const {
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ if (!animations_[i]->is_finished() &&
+ animations_[i]->target_property() == target_property) {
+ if ((list_type == ElementListType::ACTIVE &&
+ animations_[i]->affects_active_elements()) ||
+ (list_type == ElementListType::PENDING &&
+ animations_[i]->affects_pending_elements()))
+ return true;
+ }
+ }
+ return false;
+}
+
+bool AnimationPlayer::IsCurrentlyAnimatingProperty(
+ TargetProperty::Type target_property,
+ ElementListType list_type) const {
+ 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) {
+ if ((list_type == ElementListType::ACTIVE &&
+ animations_[i]->affects_active_elements()) ||
+ (list_type == ElementListType::PENDING &&
+ animations_[i]->affects_pending_elements()))
+ return true;
+ }
+ }
+ return false;
+}
+
+bool AnimationPlayer::HasElementInActiveList() const {
+ DCHECK(element_animations_);
+ return element_animations_->has_element_in_active_list();
+}
+
+gfx::ScrollOffset AnimationPlayer::ScrollOffsetForAnimation() const {
+ DCHECK(element_animations_);
+ return element_animations_->ScrollOffsetForAnimation();
+}
+
+Animation* AnimationPlayer::GetAnimation(
+ TargetProperty::Type target_property) const {
+ 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].get();
+ }
+ return nullptr;
+}
+
+Animation* AnimationPlayer::GetAnimationById(int animation_id) const {
+ for (size_t i = 0; i < animations_.size(); ++i)
+ if (animations_[i]->id() == animation_id)
+ return animations_[i].get();
+ return nullptr;
+}
+
+void AnimationPlayer::MarkAbortedAnimationsForDeletion(
+ AnimationPlayer* animation_player_impl) const {
+ bool aborted_transform_animation = false;
+ bool aborted_opacity_animation = false;
+ bool aborted_filter_animation = false;
+ auto& animations_impl = animation_player_impl->animations_;
+ for (const auto& animation_impl : animations_impl) {
+ // If the animation has been aborted on the main thread, mark it for
+ // deletion.
+ if (Animation* animation = GetAnimationById(animation_impl->id())) {
+ if (animation->run_state() == Animation::ABORTED) {
+ animation_impl->SetRunState(Animation::WAITING_FOR_DELETION,
+ animation_player_impl->last_tick_time_);
+ animation->SetRunState(Animation::WAITING_FOR_DELETION,
+ last_tick_time_);
+ if (animation_impl->target_property() == TargetProperty::TRANSFORM)
+ aborted_transform_animation = true;
+ else if (animation_impl->target_property() == TargetProperty::OPACITY)
+ aborted_opacity_animation = true;
+ else if (animation_impl->target_property() == TargetProperty::FILTER)
+ aborted_filter_animation = true;
+ }
+ }
+ }
+
+ if (element_animations_)
+ element_animations_->SetNeedsUpdateImplClientState(
+ aborted_transform_animation, aborted_opacity_animation,
+ aborted_filter_animation);
+}
+
+void AnimationPlayer::PurgeAnimationsMarkedForDeletion() {
+ animations_.erase(
+ std::remove_if(animations_.begin(), animations_.end(),
+ [](const std::unique_ptr<Animation>& animation) {
+ return animation->run_state() ==
+ Animation::WAITING_FOR_DELETION;
+ }),
+ animations_.end());
+}
+
+void AnimationPlayer::PushNewAnimationsToImplThread(
+ AnimationPlayer* animation_player_impl) const {
+ // Any new animations owned by the main thread's ElementAnimations are cloned
+ // and added to the impl thread's ElementAnimations.
+ 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 (animation_player_impl->GetAnimationById(animations_[i]->id()))
+ continue;
+
+ if (animations_[i]->target_property() == TargetProperty::SCROLL_OFFSET &&
+ !animations_[i]
+ ->curve()
+ ->ToScrollOffsetAnimationCurve()
+ ->HasSetInitialValue()) {
+ gfx::ScrollOffset current_scroll_offset;
+ if (animation_player_impl->HasElementInActiveList()) {
+ current_scroll_offset =
+ animation_player_impl->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 = 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::WAITING_FOR_TARGET_AVAILABILITY;
+ std::unique_ptr<Animation> to_add(
+ animations_[i]->CloneAndInitialize(initial_run_state));
+ DCHECK(!to_add->needs_synchronized_start_time());
+ to_add->set_affects_active_elements(false);
+ animation_player_impl->AddAnimation(std::move(to_add));
+ }
+}
+
+static bool IsCompleted(Animation* animation,
+ const AnimationPlayer* main_thread_player) {
+ if (animation->is_impl_only()) {
+ return (animation->run_state() == Animation::WAITING_FOR_DELETION);
+ } else {
+ return !main_thread_player->GetAnimationById(animation->id());
+ }
+}
+
+void AnimationPlayer::RemoveAnimationsCompletedOnMainThread(
+ AnimationPlayer* animation_player_impl) const {
+ bool removed_transform_animation = false;
+ bool removed_opacity_animation = false;
+ bool removed_filter_animation = false;
+ // Animations removed on the main thread should no longer affect pending
+ // elements, and should stop affecting active elements after the next call
+ // to ActivateAnimations. If already WAITING_FOR_DELETION, they can be removed
+ // immediately.
+ auto& animations = animation_player_impl->animations_;
+ for (const auto& animation : animations) {
+ if (IsCompleted(animation.get(), this)) {
+ animation->set_affects_pending_elements(false);
+ if (animation->target_property() == TargetProperty::TRANSFORM)
+ removed_transform_animation = true;
+ else if (animation->target_property() == TargetProperty::OPACITY)
+ removed_opacity_animation = true;
+ else if (animation->target_property() == TargetProperty::FILTER)
+ removed_filter_animation = true;
+ }
+ }
+ auto affects_active_only_and_is_waiting_for_deletion =
+ [](const std::unique_ptr<Animation>& animation) {
+ return animation->run_state() == Animation::WAITING_FOR_DELETION &&
+ !animation->affects_pending_elements();
+ };
+ animations.erase(
+ std::remove_if(animations.begin(), animations.end(),
+ affects_active_only_and_is_waiting_for_deletion),
+ animations.end());
+
+ if (element_animations_)
+ element_animations_->SetNeedsUpdateImplClientState(
+ removed_transform_animation, removed_opacity_animation,
+ removed_filter_animation);
+}
+
+void AnimationPlayer::PushPropertiesToImplThread(
+ AnimationPlayer* animation_player_impl) {
+ for (size_t i = 0; i < animations_.size(); ++i) {
+ Animation* current_impl =
+ animation_player_impl->GetAnimationById(animations_[i]->id());
+ if (current_impl)
+ animations_[i]->PushPropertiesTo(current_impl);
+ }
+}
+
} // namespace cc

Powered by Google App Engine
This is Rietveld 408576698