blob: 25e38cc6dc697e99f9e94098907ee7c6e46233a2 [file] [log] [blame]
// Copyright 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "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/geometry/box_f.h"
#include "ui/gfx/transform.h"
namespace cc {
LayerAnimationController::LayerAnimationController(int id)
: registrar_(0),
id_(id),
is_active_(false),
value_provider_(nullptr),
layer_animation_delegate_(nullptr),
needs_to_start_animations_(false),
scroll_offset_animation_was_interrupted_(false) {
}
LayerAnimationController::~LayerAnimationController() {
if (registrar_)
registrar_->UnregisterAnimationController(this);
}
scoped_refptr<LayerAnimationController> LayerAnimationController::Create(
int id) {
return make_scoped_refptr(new LayerAnimationController(id));
}
void LayerAnimationController::PauseAnimation(int animation_id,
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());
}
}
}
struct HasAnimationId {
explicit HasAnimationId(int id) : id_(id) {}
bool operator()(Animation* animation) const {
return animation->id() == id_;
}
private:
int id_;
};
void LayerAnimationController::RemoveAnimation(int animation_id) {
auto animations_to_remove =
animations_.remove_if(HasAnimationId(animation_id));
for (auto it = animations_to_remove; it != animations_.end(); ++it) {
if ((*it)->target_property() == Animation::SCROLL_OFFSET) {
scroll_offset_animation_was_interrupted_ = true;
break;
}
}
animations_.erase(animations_to_remove, animations_.end());
UpdateActivation(NORMAL_ACTIVATION);
}
struct HasAnimationIdAndProperty {
HasAnimationIdAndProperty(int id, Animation::TargetProperty target_property)
: id_(id), target_property_(target_property) {}
bool operator()(Animation* animation) const {
return animation->id() == id_ &&
animation->target_property() == target_property_;
}
private:
int id_;
Animation::TargetProperty target_property_;
};
void LayerAnimationController::RemoveAnimation(
int animation_id,
Animation::TargetProperty target_property) {
auto animations_to_remove = animations_.remove_if(
HasAnimationIdAndProperty(animation_id, target_property));
if (target_property == Animation::SCROLL_OFFSET &&
animations_to_remove != animations_.end())
scroll_offset_animation_was_interrupted_ = true;
animations_.erase(animations_to_remove, animations_.end());
UpdateActivation(NORMAL_ACTIVATION);
}
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 (!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);
PushPropertiesToImplThread(controller_impl);
controller_impl->UpdateActivation(NORMAL_ACTIVATION);
UpdateActivation(NORMAL_ACTIVATION);
}
void LayerAnimationController::Animate(base::TimeTicks monotonic_time) {
DCHECK(!monotonic_time.is_null());
if (!HasValueObserver())
return;
if (needs_to_start_animations_)
StartAnimations(monotonic_time);
TickAnimations(monotonic_time);
last_tick_time_ = monotonic_time;
}
void LayerAnimationController::AccumulatePropertyUpdates(
base::TimeTicks monotonic_time,
AnimationEventsVector* events) {
if (!events)
return;
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;
base::TimeDelta trimmed =
animation->TrimTimeToCurrentIteration(monotonic_time);
switch (animation->target_property()) {
case Animation::OPACITY: {
AnimationEvent event(AnimationEvent::PROPERTY_UPDATE, id_,
animation->group(), Animation::OPACITY,
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;
}
case Animation::TRANSFORM: {
AnimationEvent event(AnimationEvent::PROPERTY_UPDATE, id_,
animation->group(), Animation::TRANSFORM,
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;
}
case Animation::FILTER: {
AnimationEvent event(AnimationEvent::PROPERTY_UPDATE, id_,
animation->group(), Animation::FILTER,
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::BACKGROUND_COLOR: {
break;
}
case Animation::SCROLL_OFFSET: {
// Impl-side changes to scroll offset are already sent back to the
// main thread (e.g. for user-driven scrolling), so a PROPERTY_UPDATE
// isn't needed.
break;
}
}
}
}
void LayerAnimationController::UpdateState(bool start_ready_animations,
AnimationEventsVector* events) {
if (!HasActiveValueObserver())
return;
// Animate hasn't been called, this happens if an observer has been added
// between the Commit and Draw phases.
if (last_tick_time_ == base::TimeTicks())
return;
if (start_ready_animations)
PromoteStartedAnimations(last_tick_time_, events);
MarkFinishedAnimations(last_tick_time_);
MarkAnimationsForDeletion(last_tick_time_, events);
if (needs_to_start_animations_ && start_ready_animations) {
StartAnimations(last_tick_time_);
PromoteStartedAnimations(last_tick_time_, events);
}
AccumulatePropertyUpdates(last_tick_time_, events);
UpdateActivation(NORMAL_ACTIVATION);
}
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());
scroll_offset_animation_was_interrupted_ = false;
UpdateActivation(NORMAL_ACTIVATION);
}
void LayerAnimationController::AddAnimation(scoped_ptr<Animation> animation) {
animations_.push_back(animation.Pass());
needs_to_start_animations_ = true;
UpdateActivation(NORMAL_ACTIVATION);
}
Animation* LayerAnimationController::GetAnimation(
Animation::TargetProperty 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];
}
return 0;
}
Animation* LayerAnimationController::GetAnimationById(int animation_id) const {
for (size_t i = 0; i < animations_.size(); ++i)
if (animations_[i]->id() == animation_id)
return animations_[i];
return nullptr;
}
bool LayerAnimationController::HasActiveAnimation() const {
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 < 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::SetAnimationRegistrar(
AnimationRegistrar* registrar) {
if (registrar_ == registrar)
return;
if (registrar_)
registrar_->UnregisterAnimationController(this);
registrar_ = registrar;
if (registrar_)
registrar_->RegisterAnimationController(this);
UpdateActivation(FORCE_ACTIVATION);
}
void LayerAnimationController::NotifyAnimationStarted(
const AnimationEvent& event) {
if (event.is_impl_only) {
FOR_EACH_OBSERVER(LayerAnimationEventObserver, event_observers_,
OnAnimationStarted(event));
if (layer_animation_delegate_)
layer_animation_delegate_->NotifyAnimationStarted(
event.monotonic_time, event.target_property, event.group_id);
return;
}
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(
event.monotonic_time, event.target_property, event.group_id);
return;
}
}
}
void LayerAnimationController::NotifyAnimationFinished(
const AnimationEvent& event) {
if (event.is_impl_only) {
if (layer_animation_delegate_)
layer_animation_delegate_->NotifyAnimationFinished(
event.monotonic_time, event.target_property, event.group_id);
return;
}
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(
event.monotonic_time, event.target_property, event.group_id);
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, notify_active_observers, notify_pending_observers);
break;
case Animation::TRANSFORM:
NotifyObserversTransformAnimated(
event.transform, notify_active_observers, notify_pending_observers);
break;
default:
NOTREACHED();
}
}
void LayerAnimationController::AddValueObserver(
LayerAnimationValueObserver* observer) {
if (!value_observers_.HasObserver(observer))
value_observers_.AddObserver(observer);
}
void LayerAnimationController::RemoveValueObserver(
LayerAnimationValueObserver* observer) {
value_observers_.RemoveObserver(observer);
}
void LayerAnimationController::AddEventObserver(
LayerAnimationEventObserver* observer) {
if (!event_observers_.HasObserver(observer))
event_observers_.AddObserver(observer);
}
void LayerAnimationController::RemoveEventObserver(
LayerAnimationEventObserver* observer) {
event_observers_.RemoveObserver(observer);
}
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 < animations_.size(); ++i) {
if (animations_[i]->is_finished() ||
animations_[i]->target_property() != Animation::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 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::AnimationsPreserveAxisAlignment() 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->PreservesAxisAlignment())
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::DIRECTION_NORMAL:
case Animation::DIRECTION_ALTERNATE:
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;
}
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 < animations_.size(); ++i) {
// If the animation is already running on the impl thread, there is no
// need to copy it over.
if (controller_impl->GetAnimationById(animations_[i]->id()))
continue;
// If the animation is not running on the impl thread, it does not
// necessarily mean that it needs to be copied over and started; it may
// have already finished. In this case, the impl thread animation will
// have already notified that it has started and the main thread animation
// will no longer need
// a 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::SCROLL_OFFSET) {
gfx::ScrollOffset 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::WAITING_FOR_TARGET_AVAILABILITY;
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());
}
}
static bool IsCompleted(
Animation* animation,
const LayerAnimationController* main_thread_controller) {
if (animation->is_impl_only()) {
return (animation->run_state() == Animation::WAITING_FOR_DELETION);
} else {
return !main_thread_controller->GetAnimationById(animation->id());
}
}
static bool AffectsActiveOnlyAndIsWaitingForDeletion(Animation* animation) {
return animation->run_state() == Animation::WAITING_FOR_DELETION &&
!animation->affects_pending_observers();
}
void LayerAnimationController::RemoveAnimationsCompletedOnMainThread(
LayerAnimationController* controller_impl) const {
// 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 WAITING_FOR_DELETION, 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(),
AffectsActiveOnlyAndIsWaitingForDeletion),
animations.end());
}
void LayerAnimationController::PushPropertiesToImplThread(
LayerAnimationController* controller_impl) {
for (size_t i = 0; i < animations_.size(); ++i) {
Animation* current_impl =
controller_impl->GetAnimationById(animations_[i]->id());
if (current_impl)
animations_[i]->PushPropertiesTo(current_impl);
}
controller_impl->scroll_offset_animation_was_interrupted_ =
scroll_offset_animation_was_interrupted_;
scroll_offset_animation_was_interrupted_ = false;
}
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::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];
// 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_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, 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 (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) {
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(
base::TimeTicks monotonic_time,
AnimationEventsVector* events) {
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_, 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(
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::WAITING_FOR_DELETION)
animations_[i]->SetRunState(Animation::FINISHED, monotonic_time);
}
}
void LayerAnimationController::MarkAnimationsForDeletion(
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 < 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::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 =
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 (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 =
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 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, 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)
NotifyAnimationFinished(finished_event);
else
events->push_back(finished_event);
}
animations_[animation_index]->SetRunState(
Animation::WAITING_FOR_DELETION, monotonic_time);
}
marked_animations_for_deletions = true;
}
}
if (marked_animations_for_deletions)
NotifyObserversAnimationWaitingForDeletion();
}
static bool IsWaitingForDeletion(Animation* animation) {
return animation->run_state() == Animation::WAITING_FOR_DELETION;
}
void LayerAnimationController::PurgeAnimationsMarkedForDeletion() {
animations_.erase(cc::remove_if(&animations_,
animations_.begin(),
animations_.end(),
IsWaitingForDeletion),
animations_.end());
}
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;
base::TimeDelta trimmed =
animations_[i]->TrimTimeToCurrentIteration(monotonic_time);
switch (animations_[i]->target_property()) {
case Animation::TRANSFORM: {
const TransformAnimationCurve* transform_animation_curve =
animations_[i]->curve()->ToTransformAnimationCurve();
const gfx::Transform transform =
transform_animation_curve->GetValue(trimmed);
NotifyObserversTransformAnimated(
transform,
animations_[i]->affects_active_observers(),
animations_[i]->affects_pending_observers());
break;
}
case Animation::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);
NotifyObserversOpacityAnimated(
opacity,
animations_[i]->affects_active_observers(),
animations_[i]->affects_pending_observers());
break;
}
case Animation::FILTER: {
const FilterAnimationCurve* filter_animation_curve =
animations_[i]->curve()->ToFilterAnimationCurve();
const FilterOperations filter =
filter_animation_curve->GetValue(trimmed);
NotifyObserversFilterAnimated(
filter,
animations_[i]->affects_active_observers(),
animations_[i]->affects_pending_observers());
break;
}
case Animation::BACKGROUND_COLOR: {
// Not yet implemented.
break;
}
case Animation::SCROLL_OFFSET: {
const ScrollOffsetAnimationCurve* scroll_offset_animation_curve =
animations_[i]->curve()->ToScrollOffsetAnimationCurve();
const gfx::ScrollOffset scroll_offset =
scroll_offset_animation_curve->GetValue(trimmed);
NotifyObserversScrollOffsetAnimated(
scroll_offset,
animations_[i]->affects_active_observers(),
animations_[i]->affects_pending_observers());
break;
}
}
}
}
}
void LayerAnimationController::UpdateActivation(UpdateActivationType type) {
bool force = type == FORCE_ACTIVATION;
if (registrar_) {
bool was_active = is_active_;
is_active_ = false;
for (size_t i = 0; i < animations_.size(); ++i) {
if (animations_[i]->run_state() != Animation::WAITING_FOR_DELETION) {
is_active_ = true;
break;
}
}
if (is_active_ && (!was_active || force))
registrar_->DidActivateAnimationController(this);
else if (!is_active_ && (was_active || force))
registrar_->DidDeactivateAnimationController(this);
}
}
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()) != nullptr) {
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,
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()) != nullptr) {
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,
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()) != nullptr) {
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::ScrollOffset& 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()) != nullptr) {
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_,
OnAnimationWaitingForDeletion());
}
bool LayerAnimationController::HasValueObserver() {
if (value_observers_.might_have_observers()) {
ObserverListBase<LayerAnimationValueObserver>::Iterator it(
&value_observers_);
return it.GetNext() != nullptr;
}
return false;
}
bool LayerAnimationController::HasActiveValueObserver() {
if (value_observers_.might_have_observers()) {
ObserverListBase<LayerAnimationValueObserver>::Iterator it(
&value_observers_);
LayerAnimationValueObserver* obs;
while ((obs = it.GetNext()) != nullptr)
if (obs->IsActive())
return true;
}
return false;
}
} // namespace cc