// Copyright (c) 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 "ui/compositor/layer_animation_sequence.h" #include #include #include "base/trace_event/trace_event.h" #include "cc/animation/animation_id_provider.h" #include "ui/compositor/layer_animation_delegate.h" #include "ui/compositor/layer_animation_element.h" #include "ui/compositor/layer_animation_observer.h" namespace ui { LayerAnimationSequence::LayerAnimationSequence() : properties_(LayerAnimationElement::UNKNOWN), is_cyclic_(false), last_element_(0), waiting_for_group_start_(false), animation_group_id_(0), last_progressed_fraction_(0.0), weak_ptr_factory_(this) { } LayerAnimationSequence::LayerAnimationSequence(LayerAnimationElement* element) : properties_(LayerAnimationElement::UNKNOWN), is_cyclic_(false), last_element_(0), waiting_for_group_start_(false), animation_group_id_(0), last_progressed_fraction_(0.0), weak_ptr_factory_(this) { AddElement(element); } LayerAnimationSequence::~LayerAnimationSequence() { FOR_EACH_OBSERVER(LayerAnimationObserver, observers_, DetachedFromSequence(this, true)); } void LayerAnimationSequence::Start(LayerAnimationDelegate* delegate) { DCHECK(start_time_ != base::TimeTicks()); last_progressed_fraction_ = 0.0; if (elements_.empty()) return; NotifyStarted(); elements_[0]->set_requested_start_time(start_time_); elements_[0]->Start(delegate, animation_group_id_); } void LayerAnimationSequence::Progress(base::TimeTicks now, LayerAnimationDelegate* delegate) { DCHECK(start_time_ != base::TimeTicks()); bool redraw_required = false; if (elements_.empty()) return; if (last_element_ == 0) last_start_ = start_time_; size_t current_index = last_element_ % elements_.size(); base::TimeDelta element_duration; while (is_cyclic_ || last_element_ < elements_.size()) { elements_[current_index]->set_requested_start_time(last_start_); if (!elements_[current_index]->IsFinished(now, &element_duration)) break; // Let the element we're passing finish. if (elements_[current_index]->ProgressToEnd(delegate)) redraw_required = true; last_start_ += element_duration; ++last_element_; last_progressed_fraction_ = elements_[current_index]->last_progressed_fraction(); current_index = last_element_ % elements_.size(); } if (is_cyclic_ || last_element_ < elements_.size()) { if (!elements_[current_index]->Started()) { animation_group_id_ = cc::AnimationIdProvider::NextGroupId(); elements_[current_index]->Start(delegate, animation_group_id_); } base::WeakPtr alive(weak_ptr_factory_.GetWeakPtr()); if (elements_[current_index]->Progress(now, delegate)) redraw_required = true; if (!alive) return; last_progressed_fraction_ = elements_[current_index]->last_progressed_fraction(); } // Since the delegate may be deleted due to the notifications below, it is // important that we schedule a draw before sending them. if (redraw_required) delegate->ScheduleDrawForAnimation(); if (!is_cyclic_ && last_element_ == elements_.size()) { last_element_ = 0; waiting_for_group_start_ = false; animation_group_id_ = 0; NotifyEnded(); } } bool LayerAnimationSequence::IsFinished(base::TimeTicks time) { if (is_cyclic_ || waiting_for_group_start_) return false; if (elements_.empty()) return true; if (last_element_ == 0) last_start_ = start_time_; base::TimeTicks current_start = last_start_; size_t current_index = last_element_; base::TimeDelta element_duration; while (current_index < elements_.size()) { elements_[current_index]->set_requested_start_time(current_start); if (!elements_[current_index]->IsFinished(time, &element_duration)) break; current_start += element_duration; ++current_index; } return (current_index == elements_.size()); } void LayerAnimationSequence::ProgressToEnd(LayerAnimationDelegate* delegate) { bool redraw_required = false; if (elements_.empty()) return; size_t current_index = last_element_ % elements_.size(); while (current_index < elements_.size()) { if (elements_[current_index]->ProgressToEnd(delegate)) redraw_required = true; last_progressed_fraction_ = elements_[current_index]->last_progressed_fraction(); ++current_index; ++last_element_; } if (redraw_required) delegate->ScheduleDrawForAnimation(); if (!is_cyclic_) { last_element_ = 0; waiting_for_group_start_ = false; animation_group_id_ = 0; NotifyEnded(); } } void LayerAnimationSequence::GetTargetValue( LayerAnimationElement::TargetValue* target) const { if (is_cyclic_) return; for (size_t i = last_element_; i < elements_.size(); ++i) elements_[i]->GetTargetValue(target); } void LayerAnimationSequence::Abort(LayerAnimationDelegate* delegate) { size_t current_index = last_element_ % elements_.size(); while (current_index < elements_.size()) { elements_[current_index]->Abort(delegate); ++current_index; } last_element_ = 0; waiting_for_group_start_ = false; NotifyAborted(); } void LayerAnimationSequence::AddElement(LayerAnimationElement* element) { properties_ |= element->properties(); elements_.push_back(make_linked_ptr(element)); } bool LayerAnimationSequence::HasConflictingProperty( LayerAnimationElement::AnimatableProperties other) const { return (properties_ & other) != LayerAnimationElement::UNKNOWN; } bool LayerAnimationSequence::IsFirstElementThreaded() const { if (!elements_.empty()) return elements_[0]->IsThreaded(); return false; } void LayerAnimationSequence::AddObserver(LayerAnimationObserver* observer) { if (!observers_.HasObserver(observer)) { observers_.AddObserver(observer); observer->AttachedToSequence(this); } } void LayerAnimationSequence::RemoveObserver(LayerAnimationObserver* observer) { observers_.RemoveObserver(observer); observer->DetachedFromSequence(this, true); } void LayerAnimationSequence::OnThreadedAnimationStarted( const cc::AnimationEvent& event) { if (elements_.empty() || event.group_id != animation_group_id_) return; size_t current_index = last_element_ % elements_.size(); LayerAnimationElement::AnimatableProperties element_properties = elements_[current_index]->properties(); LayerAnimationElement::AnimatableProperty event_property = LayerAnimationElement::ToAnimatableProperty(event.target_property); DCHECK(element_properties & event_property); elements_[current_index]->set_effective_start_time(event.monotonic_time); } void LayerAnimationSequence::OnScheduled() { NotifyScheduled(); } void LayerAnimationSequence::OnAnimatorDestroyed() { if (observers_.might_have_observers()) { base::ObserverListBase::Iterator it(&observers_); LayerAnimationObserver* obs; while ((obs = it.GetNext()) != NULL) { if (!obs->RequiresNotificationWhenAnimatorDestroyed()) { // Remove the observer, but do not allow notifications to be sent. observers_.RemoveObserver(obs); obs->DetachedFromSequence(this, false); } } } } size_t LayerAnimationSequence::size() const { return elements_.size(); } LayerAnimationElement* LayerAnimationSequence::FirstElement() const { if (elements_.empty()) { return NULL; } return elements_[0].get(); } void LayerAnimationSequence::NotifyScheduled() { FOR_EACH_OBSERVER(LayerAnimationObserver, observers_, OnLayerAnimationScheduled(this)); } void LayerAnimationSequence::NotifyStarted() { FOR_EACH_OBSERVER(LayerAnimationObserver, observers_, OnLayerAnimationStarted(this)); } void LayerAnimationSequence::NotifyEnded() { FOR_EACH_OBSERVER(LayerAnimationObserver, observers_, OnLayerAnimationEnded(this)); } void LayerAnimationSequence::NotifyAborted() { FOR_EACH_OBSERVER(LayerAnimationObserver, observers_, OnLayerAnimationAborted(this)); } LayerAnimationElement* LayerAnimationSequence::CurrentElement() const { if (elements_.empty()) return NULL; size_t current_index = last_element_ % elements_.size(); return elements_[current_index].get(); } } // namespace ui