layer_animation_sequence.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "ui/compositor/layer_animation_sequence.h"
6
7#include <algorithm>
8#include <iterator>
9
10#include "base/debug/trace_event.h"
11#include "cc/animation/animation_id_provider.h"
12#include "ui/compositor/layer_animation_delegate.h"
13#include "ui/compositor/layer_animation_element.h"
14#include "ui/compositor/layer_animation_observer.h"
15
16namespace ui {
17
18LayerAnimationSequence::LayerAnimationSequence()
19    : properties_(LayerAnimationElement::UNKNOWN),
20      is_cyclic_(false),
21      last_element_(0),
22      waiting_for_group_start_(false),
23      animation_group_id_(0),
24      last_progressed_fraction_(0.0),
25      weak_ptr_factory_(this) {
26}
27
28LayerAnimationSequence::LayerAnimationSequence(LayerAnimationElement* element)
29    : properties_(LayerAnimationElement::UNKNOWN),
30      is_cyclic_(false),
31      last_element_(0),
32      waiting_for_group_start_(false),
33      animation_group_id_(0),
34      last_progressed_fraction_(0.0),
35      weak_ptr_factory_(this) {
36  AddElement(element);
37}
38
39LayerAnimationSequence::~LayerAnimationSequence() {
40  FOR_EACH_OBSERVER(LayerAnimationObserver,
41                    observers_,
42                    DetachedFromSequence(this, true));
43}
44
45void LayerAnimationSequence::Start(LayerAnimationDelegate* delegate) {
46  DCHECK(start_time_ != base::TimeTicks());
47  last_progressed_fraction_ = 0.0;
48  if (elements_.empty())
49    return;
50
51  elements_[0]->set_requested_start_time(start_time_);
52  elements_[0]->Start(delegate, animation_group_id_);
53}
54
55void LayerAnimationSequence::Progress(base::TimeTicks now,
56                                      LayerAnimationDelegate* delegate) {
57  DCHECK(start_time_ != base::TimeTicks());
58  bool redraw_required = false;
59
60  if (elements_.empty())
61    return;
62
63  if (last_element_ == 0)
64    last_start_ = start_time_;
65
66  size_t current_index = last_element_ % elements_.size();
67  base::TimeDelta element_duration;
68  while (is_cyclic_ || last_element_ < elements_.size()) {
69    elements_[current_index]->set_requested_start_time(last_start_);
70    if (!elements_[current_index]->IsFinished(now, &element_duration))
71      break;
72
73    // Let the element we're passing finish.
74    if (elements_[current_index]->ProgressToEnd(delegate))
75      redraw_required = true;
76    last_start_ += element_duration;
77    ++last_element_;
78    last_progressed_fraction_ =
79        elements_[current_index]->last_progressed_fraction();
80    current_index = last_element_ % elements_.size();
81  }
82
83  if (is_cyclic_ || last_element_ < elements_.size()) {
84    if (!elements_[current_index]->Started()) {
85      animation_group_id_ = cc::AnimationIdProvider::NextGroupId();
86      elements_[current_index]->Start(delegate, animation_group_id_);
87    }
88    base::WeakPtr<LayerAnimationSequence> alive(weak_ptr_factory_.GetWeakPtr());
89    if (elements_[current_index]->Progress(now, delegate))
90      redraw_required = true;
91    if (!alive)
92      return;
93    last_progressed_fraction_ =
94        elements_[current_index]->last_progressed_fraction();
95  }
96
97  // Since the delegate may be deleted due to the notifications below, it is
98  // important that we schedule a draw before sending them.
99  if (redraw_required)
100    delegate->ScheduleDrawForAnimation();
101
102  if (!is_cyclic_ && last_element_ == elements_.size()) {
103    last_element_ = 0;
104    waiting_for_group_start_ = false;
105    animation_group_id_ = 0;
106    NotifyEnded();
107  }
108}
109
110bool LayerAnimationSequence::IsFinished(base::TimeTicks time) {
111  if (is_cyclic_ || waiting_for_group_start_)
112    return false;
113
114  if (elements_.empty())
115    return true;
116
117  if (last_element_ == 0)
118    last_start_ = start_time_;
119
120  base::TimeTicks current_start = last_start_;
121  size_t current_index = last_element_;
122  base::TimeDelta element_duration;
123  while (current_index < elements_.size()) {
124    elements_[current_index]->set_requested_start_time(current_start);
125    if (!elements_[current_index]->IsFinished(time, &element_duration))
126      break;
127
128    current_start += element_duration;
129    ++current_index;
130  }
131
132  return (current_index == elements_.size());
133}
134
135void LayerAnimationSequence::ProgressToEnd(LayerAnimationDelegate* delegate) {
136  bool redraw_required = false;
137
138  if (elements_.empty())
139    return;
140
141  size_t current_index = last_element_ % elements_.size();
142  while (current_index < elements_.size()) {
143    if (elements_[current_index]->ProgressToEnd(delegate))
144      redraw_required = true;
145    last_progressed_fraction_ =
146        elements_[current_index]->last_progressed_fraction();
147    ++current_index;
148    ++last_element_;
149  }
150
151  if (redraw_required)
152    delegate->ScheduleDrawForAnimation();
153
154  if (!is_cyclic_) {
155    last_element_ = 0;
156    waiting_for_group_start_ = false;
157    animation_group_id_ = 0;
158    NotifyEnded();
159  }
160}
161
162void LayerAnimationSequence::GetTargetValue(
163    LayerAnimationElement::TargetValue* target) const {
164  if (is_cyclic_)
165    return;
166
167  for (size_t i = last_element_; i < elements_.size(); ++i)
168    elements_[i]->GetTargetValue(target);
169}
170
171void LayerAnimationSequence::Abort(LayerAnimationDelegate* delegate) {
172  size_t current_index = last_element_ % elements_.size();
173  while (current_index < elements_.size()) {
174    elements_[current_index]->Abort(delegate);
175    ++current_index;
176  }
177  last_element_ = 0;
178  waiting_for_group_start_ = false;
179  NotifyAborted();
180}
181
182void LayerAnimationSequence::AddElement(LayerAnimationElement* element) {
183  properties_ |= element->properties();
184  elements_.push_back(make_linked_ptr(element));
185}
186
187bool LayerAnimationSequence::HasConflictingProperty(
188    LayerAnimationElement::AnimatableProperties other) const {
189  return (properties_ & other) != LayerAnimationElement::UNKNOWN;
190}
191
192bool LayerAnimationSequence::IsFirstElementThreaded() const {
193  if (!elements_.empty())
194    return elements_[0]->IsThreaded();
195
196  return false;
197}
198
199void LayerAnimationSequence::AddObserver(LayerAnimationObserver* observer) {
200  if (!observers_.HasObserver(observer)) {
201    observers_.AddObserver(observer);
202    observer->AttachedToSequence(this);
203  }
204}
205
206void LayerAnimationSequence::RemoveObserver(LayerAnimationObserver* observer) {
207  observers_.RemoveObserver(observer);
208  observer->DetachedFromSequence(this, true);
209}
210
211void LayerAnimationSequence::OnThreadedAnimationStarted(
212    const cc::AnimationEvent& event) {
213  if (elements_.empty() || event.group_id != animation_group_id_)
214    return;
215
216  size_t current_index = last_element_ % elements_.size();
217  LayerAnimationElement::AnimatableProperties element_properties =
218    elements_[current_index]->properties();
219  LayerAnimationElement::AnimatableProperty event_property =
220      LayerAnimationElement::ToAnimatableProperty(event.target_property);
221  DCHECK(element_properties & event_property);
222  elements_[current_index]->set_effective_start_time(
223      base::TimeTicks::FromInternalValue(
224          event.monotonic_time * base::Time::kMicrosecondsPerSecond));
225}
226
227void LayerAnimationSequence::OnScheduled() {
228  NotifyScheduled();
229}
230
231void LayerAnimationSequence::OnAnimatorDestroyed() {
232  if (observers_.might_have_observers()) {
233    ObserverListBase<LayerAnimationObserver>::Iterator it(observers_);
234    LayerAnimationObserver* obs;
235    while ((obs = it.GetNext()) != NULL) {
236      if (!obs->RequiresNotificationWhenAnimatorDestroyed()) {
237        // Remove the observer, but do not allow notifications to be sent.
238        observers_.RemoveObserver(obs);
239        obs->DetachedFromSequence(this, false);
240      }
241    }
242  }
243}
244
245size_t LayerAnimationSequence::size() const {
246  return elements_.size();
247}
248
249LayerAnimationElement* LayerAnimationSequence::FirstElement() const {
250  if (elements_.empty()) {
251    return NULL;
252  }
253
254  return elements_[0].get();
255}
256
257void LayerAnimationSequence::NotifyScheduled() {
258  FOR_EACH_OBSERVER(LayerAnimationObserver,
259                    observers_,
260                    OnLayerAnimationScheduled(this));
261}
262
263void LayerAnimationSequence::NotifyEnded() {
264  FOR_EACH_OBSERVER(LayerAnimationObserver,
265                    observers_,
266                    OnLayerAnimationEnded(this));
267}
268
269void LayerAnimationSequence::NotifyAborted() {
270  FOR_EACH_OBSERVER(LayerAnimationObserver,
271                    observers_,
272                    OnLayerAnimationAborted(this));
273}
274
275LayerAnimationElement* LayerAnimationSequence::CurrentElement() const {
276  if (elements_.empty())
277    return NULL;
278
279  size_t current_index = last_element_ % elements_.size();
280  return elements_[current_index].get();
281}
282
283}  // namespace ui
284