blob: 2e4c5eb69acca03feaf80e8f88e3b8d206ae9ba8 [file] [log] [blame]
// Copyright 2015 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/animation.h"
#include <inttypes.h>
#include <algorithm>
#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "cc/animation/animation_delegate.h"
#include "cc/animation/animation_events.h"
#include "cc/animation/animation_host.h"
#include "cc/animation/animation_timeline.h"
#include "cc/animation/keyframe_effect.h"
#include "cc/animation/scroll_offset_animation_curve.h"
#include "cc/animation/transform_operations.h"
#include "cc/trees/property_animation_state.h"
namespace cc {
scoped_refptr<Animation> Animation::Create(int id) {
return base::WrapRefCounted(new Animation(id));
}
Animation::Animation(int id)
: animation_host_(),
animation_timeline_(),
animation_delegate_(),
id_(id),
ticking_keyframe_effects_count(0) {
DCHECK(id_);
}
Animation::~Animation() {
DCHECK(!animation_timeline_);
}
scoped_refptr<Animation> Animation::CreateImplInstance() const {
return Animation::Create(id());
}
void Animation::SetAnimationHost(AnimationHost* animation_host) {
animation_host_ = animation_host;
}
void Animation::SetAnimationTimeline(AnimationTimeline* timeline) {
if (animation_timeline_ == timeline)
return;
// We need to unregister keyframe_effect to manage ElementAnimations and
// observers properly.
if (!element_to_keyframe_effect_id_map_.empty() && animation_host_) {
// Destroy ElementAnimations or release it if it's still needed.
UnregisterKeyframeEffects();
}
animation_timeline_ = timeline;
// Register animation only if layer AND host attached. Unlike the
// SingleKeyframeEffectAnimation case, all keyframe_effects have been attached
// to their corresponding elements.
if (!element_to_keyframe_effect_id_map_.empty() && animation_host_) {
RegisterKeyframeEffects();
}
}
bool Animation::has_element_animations() const {
return !element_to_keyframe_effect_id_map_.empty();
}
scoped_refptr<ElementAnimations> Animation::element_animations(
KeyframeEffectId keyframe_effect_id) const {
return GetKeyframeEffectById(keyframe_effect_id)->element_animations();
}
void Animation::AttachElementForKeyframeEffect(
ElementId element_id,
KeyframeEffectId keyframe_effect_id) {
DCHECK(GetKeyframeEffectById(keyframe_effect_id));
GetKeyframeEffectById(keyframe_effect_id)->AttachElement(element_id);
element_to_keyframe_effect_id_map_[element_id].emplace(keyframe_effect_id);
// Register animation only if layer AND host attached.
if (animation_host_) {
// Create ElementAnimations or re-use existing.
RegisterKeyframeEffect(element_id, keyframe_effect_id);
}
}
void Animation::DetachElementForKeyframeEffect(
ElementId element_id,
KeyframeEffectId keyframe_effect_id) {
DCHECK(GetKeyframeEffectById(keyframe_effect_id));
DCHECK_EQ(GetKeyframeEffectById(keyframe_effect_id)->element_id(),
element_id);
UnregisterKeyframeEffect(element_id, keyframe_effect_id);
GetKeyframeEffectById(keyframe_effect_id)->DetachElement();
element_to_keyframe_effect_id_map_[element_id].erase(keyframe_effect_id);
}
void Animation::DetachElement() {
if (animation_host_) {
// Destroy ElementAnimations or release it if it's still needed.
UnregisterKeyframeEffects();
}
for (auto pair = element_to_keyframe_effect_id_map_.begin();
pair != element_to_keyframe_effect_id_map_.end();) {
for (auto keyframe_effect = pair->second.begin();
keyframe_effect != pair->second.end();) {
GetKeyframeEffectById(*keyframe_effect)->DetachElement();
keyframe_effect = pair->second.erase(keyframe_effect);
}
pair = element_to_keyframe_effect_id_map_.erase(pair);
}
DCHECK_EQ(element_to_keyframe_effect_id_map_.size(), 0u);
}
void Animation::RegisterKeyframeEffect(ElementId element_id,
KeyframeEffectId keyframe_effect_id) {
DCHECK(animation_host_);
KeyframeEffect* keyframe_effect = GetKeyframeEffectById(keyframe_effect_id);
DCHECK(!keyframe_effect->has_bound_element_animations());
if (!keyframe_effect->has_attached_element())
return;
animation_host_->RegisterKeyframeEffectForElement(element_id,
keyframe_effect);
}
void Animation::UnregisterKeyframeEffect(ElementId element_id,
KeyframeEffectId keyframe_effect_id) {
DCHECK(animation_host_);
KeyframeEffect* keyframe_effect = GetKeyframeEffectById(keyframe_effect_id);
DCHECK(keyframe_effect);
if (keyframe_effect->has_attached_element() &&
keyframe_effect->has_bound_element_animations()) {
animation_host_->UnregisterKeyframeEffectForElement(element_id,
keyframe_effect);
}
}
void Animation::RegisterKeyframeEffects() {
for (auto& element_id_keyframe_effect_id :
element_to_keyframe_effect_id_map_) {
const ElementId element_id = element_id_keyframe_effect_id.first;
const std::unordered_set<KeyframeEffectId>& keyframe_effect_ids =
element_id_keyframe_effect_id.second;
for (auto& keyframe_effect_id : keyframe_effect_ids)
RegisterKeyframeEffect(element_id, keyframe_effect_id);
}
}
void Animation::UnregisterKeyframeEffects() {
for (auto& element_id_keyframe_effect_id :
element_to_keyframe_effect_id_map_) {
const ElementId element_id = element_id_keyframe_effect_id.first;
const std::unordered_set<KeyframeEffectId>& keyframe_effect_ids =
element_id_keyframe_effect_id.second;
for (auto& keyframe_effect_id : keyframe_effect_ids)
UnregisterKeyframeEffect(element_id, keyframe_effect_id);
}
animation_host_->RemoveFromTicking(this);
}
void Animation::PushAttachedKeyframeEffectsToImplThread(
Animation* animation_impl) const {
for (auto& keyframe_effect : keyframe_effects_) {
KeyframeEffect* keyframe_effect_impl =
animation_impl->GetKeyframeEffectById(keyframe_effect->id());
if (keyframe_effect_impl)
continue;
std::unique_ptr<KeyframeEffect> to_add =
keyframe_effect->CreateImplInstance();
animation_impl->AddKeyframeEffect(std::move(to_add));
}
}
void Animation::PushPropertiesToImplThread(Animation* animation_impl) {
for (auto& keyframe_effect : keyframe_effects_) {
if (KeyframeEffect* keyframe_effect_impl =
animation_impl->GetKeyframeEffectById(keyframe_effect->id())) {
keyframe_effect->PushPropertiesTo(keyframe_effect_impl);
}
}
}
void Animation::AddKeyframeModelForKeyframeEffect(
std::unique_ptr<KeyframeModel> keyframe_model,
KeyframeEffectId keyframe_effect_id) {
DCHECK(GetKeyframeEffectById(keyframe_effect_id));
GetKeyframeEffectById(keyframe_effect_id)
->AddKeyframeModel(std::move(keyframe_model));
}
void Animation::PauseKeyframeModelForKeyframeEffect(
int keyframe_model_id,
double time_offset,
KeyframeEffectId keyframe_effect_id) {
DCHECK(GetKeyframeEffectById(keyframe_effect_id));
GetKeyframeEffectById(keyframe_effect_id)
->PauseKeyframeModel(keyframe_model_id, time_offset);
}
void Animation::RemoveKeyframeModelForKeyframeEffect(
int keyframe_model_id,
KeyframeEffectId keyframe_effect_id) {
DCHECK(GetKeyframeEffectById(keyframe_effect_id));
GetKeyframeEffectById(keyframe_effect_id)
->RemoveKeyframeModel(keyframe_model_id);
}
void Animation::AbortKeyframeModelForKeyframeEffect(
int keyframe_model_id,
KeyframeEffectId keyframe_effect_id) {
DCHECK(GetKeyframeEffectById(keyframe_effect_id));
GetKeyframeEffectById(keyframe_effect_id)
->AbortKeyframeModel(keyframe_model_id);
}
void Animation::AbortKeyframeModelsWithProperty(
TargetProperty::Type target_property,
bool needs_completion) {
for (auto& keyframe_effect : keyframe_effects_)
keyframe_effect->AbortKeyframeModelsWithProperty(target_property,
needs_completion);
}
void Animation::PushPropertiesTo(Animation* animation_impl) {
// In general when pushing proerties to impl thread we first push attached
// properties to impl followed by removing the detached ones. However, we
// never remove individual keyframe effect from an animation so there is no
// need to remove the detached ones.
PushAttachedKeyframeEffectsToImplThread(animation_impl);
PushPropertiesToImplThread(animation_impl);
}
void Animation::Tick(base::TimeTicks monotonic_time) {
DCHECK(!monotonic_time.is_null());
for (auto& keyframe_effect : keyframe_effects_)
keyframe_effect->Tick(monotonic_time);
}
void Animation::UpdateState(bool start_ready_animations,
AnimationEvents* events) {
for (auto& keyframe_effect : keyframe_effects_) {
keyframe_effect->UpdateState(start_ready_animations, events);
keyframe_effect->UpdateTickingState();
}
}
void Animation::AddToTicking() {
++ticking_keyframe_effects_count;
if (ticking_keyframe_effects_count > 1)
return;
DCHECK(animation_host_);
animation_host_->AddToTicking(this);
}
void Animation::RemoveFromTicking() {
DCHECK_GE(ticking_keyframe_effects_count, 0);
if (!ticking_keyframe_effects_count)
return;
--ticking_keyframe_effects_count;
DCHECK(animation_host_);
DCHECK_GE(ticking_keyframe_effects_count, 0);
if (ticking_keyframe_effects_count)
return;
animation_host_->RemoveFromTicking(this);
}
void Animation::NotifyKeyframeModelStarted(const AnimationEvent& event) {
if (animation_delegate_) {
animation_delegate_->NotifyAnimationStarted(
event.monotonic_time, event.target_property, event.group_id);
}
}
void Animation::NotifyKeyframeModelFinished(const AnimationEvent& event) {
if (animation_delegate_) {
animation_delegate_->NotifyAnimationFinished(
event.monotonic_time, event.target_property, event.group_id);
}
}
void Animation::NotifyKeyframeModelAborted(const AnimationEvent& event) {
if (animation_delegate_) {
animation_delegate_->NotifyAnimationAborted(
event.monotonic_time, event.target_property, event.group_id);
}
}
void Animation::NotifyKeyframeModelTakeover(const AnimationEvent& event) {
DCHECK(event.target_property == TargetProperty::SCROLL_OFFSET);
if (animation_delegate_) {
DCHECK(event.curve);
std::unique_ptr<AnimationCurve> animation_curve = event.curve->Clone();
animation_delegate_->NotifyAnimationTakeover(
event.monotonic_time, event.target_property, event.animation_start_time,
std::move(animation_curve));
}
}
size_t Animation::TickingKeyframeModelsCount() const {
size_t count = 0;
for (auto& keyframe_effect : keyframe_effects_)
count += keyframe_effect->TickingKeyframeModelsCount();
return count;
}
void Animation::SetNeedsCommit() {
DCHECK(animation_host_);
animation_host_->SetNeedsCommit();
}
void Animation::SetNeedsPushProperties() {
if (!animation_timeline_)
return;
animation_timeline_->SetNeedsPushProperties();
}
void Animation::ActivateKeyframeEffects() {
for (auto& keyframe_effect : keyframe_effects_) {
keyframe_effect->ActivateKeyframeEffects();
keyframe_effect->UpdateTickingState();
}
}
KeyframeModel* Animation::GetKeyframeModelForKeyframeEffect(
TargetProperty::Type target_property,
KeyframeEffectId keyframe_effect_id) const {
DCHECK(GetKeyframeEffectById(keyframe_effect_id));
return GetKeyframeEffectById(keyframe_effect_id)
->GetKeyframeModel(target_property);
}
std::string Animation::ToString() const {
std::string output = base::StringPrintf("Animation{id=%d", id_);
for (const auto& keyframe_effect : keyframe_effects_) {
output +=
base::StringPrintf(", element_id=%s, keyframe_models=[%s]",
keyframe_effect->element_id().ToString().c_str(),
keyframe_effect->KeyframeModelsToString().c_str());
}
return output + "}";
}
bool Animation::IsWorkletAnimation() const {
return false;
}
void Animation::AddKeyframeEffect(
std::unique_ptr<KeyframeEffect> keyframe_effect) {
keyframe_effect->SetAnimation(this);
keyframe_effects_.push_back(std::move(keyframe_effect));
SetNeedsPushProperties();
}
KeyframeEffect* Animation::GetKeyframeEffectById(
KeyframeEffectId keyframe_effect_id) const {
// May return nullptr when syncing keyframe_effects_ to impl.
return keyframe_effects_.size() > keyframe_effect_id
? keyframe_effects_[keyframe_effect_id].get()
: nullptr;
}
} // namespace cc