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

Side by Side 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 unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "cc/animation/animation_player.h" 5 #include "cc/animation/animation_player.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "cc/animation/animation_delegate.h" 9 #include "cc/animation/animation_delegate.h"
10 #include "cc/animation/animation_host.h" 10 #include "cc/animation/animation_host.h"
11 #include "cc/animation/animation_timeline.h" 11 #include "cc/animation/animation_timeline.h"
12 #include "cc/animation/element_animations.h" 12 #include "cc/animation/scroll_offset_animation_curve.h"
13 13
14 namespace cc { 14 namespace cc {
15 15
16 scoped_refptr<AnimationPlayer> AnimationPlayer::Create(int id) { 16 scoped_refptr<AnimationPlayer> AnimationPlayer::Create(int id) {
17 return make_scoped_refptr(new AnimationPlayer(id)); 17 return make_scoped_refptr(new AnimationPlayer(id));
18 } 18 }
19 19
20 AnimationPlayer::AnimationPlayer(int id) 20 AnimationPlayer::AnimationPlayer(int id)
21 : animation_host_(), 21 : animation_host_(),
22 animation_timeline_(), 22 animation_timeline_(),
23 element_animations_(), 23 element_animations_(),
24 animation_delegate_(), 24 animation_delegate_(),
25 id_(id), 25 id_(id),
26 needs_push_properties_(false) { 26 needs_push_properties_(false),
27 needs_to_start_animations_(false) {
27 DCHECK(id_); 28 DCHECK(id_);
28 } 29 }
29 30
30 AnimationPlayer::~AnimationPlayer() { 31 AnimationPlayer::~AnimationPlayer() {
31 DCHECK(!animation_timeline_); 32 DCHECK(!animation_timeline_);
32 DCHECK(!element_animations_); 33 DCHECK(!element_animations_);
33 } 34 }
34 35
35 scoped_refptr<AnimationPlayer> AnimationPlayer::CreateImplInstance() const { 36 scoped_refptr<AnimationPlayer> AnimationPlayer::CreateImplInstance() const {
36 scoped_refptr<AnimationPlayer> player = AnimationPlayer::Create(id()); 37 scoped_refptr<AnimationPlayer> player = AnimationPlayer::Create(id());
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
97 // Destroy ElementAnimations or release it if it's still needed. 98 // Destroy ElementAnimations or release it if it's still needed.
98 animation_host_->UnregisterPlayerForElement(element_id_, this); 99 animation_host_->UnregisterPlayerForElement(element_id_, this);
99 } 100 }
100 101
101 void AnimationPlayer::BindElementAnimations() { 102 void AnimationPlayer::BindElementAnimations() {
102 DCHECK(!element_animations_); 103 DCHECK(!element_animations_);
103 element_animations_ = 104 element_animations_ =
104 animation_host_->GetElementAnimationsForElementId(element_id_); 105 animation_host_->GetElementAnimationsForElementId(element_id_);
105 DCHECK(element_animations_); 106 DCHECK(element_animations_);
106 107
107 // Pass all accumulated animations to ElementAnimations.
108 for (auto& animation : animations_) 108 for (auto& animation : animations_)
109 element_animations_->AddAnimation(std::move(animation)); 109 AnimationAddedForProperty(animation->target_property());
110 110
111 if (!animations_.empty())
112 SetNeedsCommit();
113 SetNeedsPushProperties(); 111 SetNeedsPushProperties();
114
115 animations_.clear();
116 } 112 }
117 113
118 void AnimationPlayer::UnbindElementAnimations() { 114 void AnimationPlayer::UnbindElementAnimations() {
119 SetNeedsPushProperties(); 115 SetNeedsPushProperties();
120
121 element_animations_ = nullptr; 116 element_animations_ = nullptr;
122 DCHECK(animations_.empty());
123 } 117 }
124 118
125 void AnimationPlayer::AddAnimation(std::unique_ptr<Animation> animation) { 119 void AnimationPlayer::AddAnimation(std::unique_ptr<Animation> animation) {
126 DCHECK(animation->target_property() != TargetProperty::SCROLL_OFFSET || 120 DCHECK(animation->target_property() != TargetProperty::SCROLL_OFFSET ||
127 (animation_host_ && animation_host_->SupportsScrollAnimations())); 121 (animation_host_ && animation_host_->SupportsScrollAnimations()));
122 DCHECK(!animation->is_impl_only() ||
123 animation->target_property() == TargetProperty::SCROLL_OFFSET);
128 124
125 TargetProperty::Type target_property = animation->target_property();
126 animations_.push_back(std::move(animation));
129 if (element_animations_) { 127 if (element_animations_) {
130 element_animations_->AddAnimation(std::move(animation)); 128 AnimationAddedForProperty(target_property);
131 SetNeedsCommit();
132 SetNeedsPushProperties(); 129 SetNeedsPushProperties();
133 } else {
134 animations_.push_back(std::move(animation));
135 } 130 }
136 } 131 }
137 132
133 void AnimationPlayer::AnimationAddedForProperty(
134 TargetProperty::Type target_property) {
135 DCHECK(element_animations_);
136
137 SetNeedsCommit();
138 needs_to_start_animations_ = true;
139
140 element_animations_->UpdateActivationNormal();
141 element_animations_->UpdateClientAnimationState(target_property);
142 }
143
138 void AnimationPlayer::PauseAnimation(int animation_id, double time_offset) { 144 void AnimationPlayer::PauseAnimation(int animation_id, double time_offset) {
139 DCHECK(element_animations_); 145 const base::TimeDelta time_delta = base::TimeDelta::FromSecondsD(time_offset);
140 element_animations_->PauseAnimation( 146
141 animation_id, base::TimeDelta::FromSecondsD(time_offset)); 147 for (size_t i = 0; i < animations_.size(); ++i) {
142 SetNeedsCommit(); 148 if (animations_[i]->id() == animation_id) {
143 SetNeedsPushProperties(); 149 animations_[i]->SetRunState(Animation::PAUSED,
150 time_delta + animations_[i]->start_time() +
151 animations_[i]->time_offset());
152 }
153 }
154
155 if (element_animations_) {
156 SetNeedsCommit();
157 SetNeedsPushProperties();
158 }
144 } 159 }
145 160
146 void AnimationPlayer::RemoveAnimation(int animation_id) { 161 void AnimationPlayer::RemoveAnimation(int animation_id) {
162 bool removed_transform_animation = false;
163 bool removed_opacity_animation = false;
164 bool removed_filter_animation = false;
165 // Since we want to use the animations that we're going to remove, we need to
166 // use a stable_parition here instead of remove_if. Remove_if leaves the
167 // removed items in an unspecified state.
168 auto animations_to_remove = std::stable_partition(
169 animations_.begin(), animations_.end(),
170 [animation_id](const std::unique_ptr<Animation>& animation) {
171 return animation->id() != animation_id;
172 });
173 for (auto it = animations_to_remove; it != animations_.end(); ++it) {
174 if ((*it)->target_property() == TargetProperty::SCROLL_OFFSET) {
175 if (element_animations_)
176 element_animations_->SetScrollOffsetAnimationWasInterrupted();
177 } else if ((*it)->target_property() == TargetProperty::TRANSFORM &&
178 !(*it)->is_finished()) {
179 removed_transform_animation = true;
180 } else if ((*it)->target_property() == TargetProperty::OPACITY &&
181 !(*it)->is_finished()) {
182 removed_opacity_animation = true;
183 } else if ((*it)->target_property() == TargetProperty::FILTER &&
184 !(*it)->is_finished()) {
185 removed_filter_animation = true;
186 }
187 }
188
189 animations_.erase(animations_to_remove, animations_.end());
190
147 if (element_animations_) { 191 if (element_animations_) {
148 element_animations_->RemoveAnimation(animation_id); 192 element_animations_->UpdateActivationNormal();
193 element_animations_->UpdateClientAnimationState(removed_transform_animation,
194 removed_opacity_animation,
195 removed_filter_animation);
149 SetNeedsCommit(); 196 SetNeedsCommit();
150 SetNeedsPushProperties(); 197 SetNeedsPushProperties();
151 } else {
152 auto animations_to_remove = std::remove_if(
153 animations_.begin(), animations_.end(),
154 [animation_id](const std::unique_ptr<Animation>& animation) {
155 return animation->id() == animation_id;
156 });
157 animations_.erase(animations_to_remove, animations_.end());
158 } 198 }
159 } 199 }
160 200
161 void AnimationPlayer::AbortAnimation(int animation_id) { 201 void AnimationPlayer::AbortAnimation(int animation_id) {
162 DCHECK(element_animations_); 202 if (Animation* animation = GetAnimationById(animation_id)) {
163 element_animations_->AbortAnimation(animation_id); 203 if (!animation->is_finished()) {
164 SetNeedsCommit(); 204 animation->SetRunState(Animation::ABORTED, last_tick_time_);
165 SetNeedsPushProperties(); 205 if (element_animations_)
206 element_animations_->UpdateClientAnimationState(
207 animation->target_property());
208 }
209 }
210
211 if (element_animations_) {
212 SetNeedsCommit();
213 SetNeedsPushProperties();
214 }
166 } 215 }
167 216
168 void AnimationPlayer::AbortAnimations(TargetProperty::Type target_property, 217 void AnimationPlayer::AbortAnimations(TargetProperty::Type target_property,
169 bool needs_completion) { 218 bool needs_completion) {
219 if (needs_completion)
220 DCHECK(target_property == TargetProperty::SCROLL_OFFSET);
221
222 bool aborted_animation = false;
223 for (size_t i = 0; i < animations_.size(); ++i) {
224 if (animations_[i]->target_property() == target_property &&
225 !animations_[i]->is_finished()) {
226 // Currently only impl-only scroll offset animations can be completed on
227 // the main thread.
228 if (needs_completion && animations_[i]->is_impl_only()) {
229 animations_[i]->SetRunState(Animation::ABORTED_BUT_NEEDS_COMPLETION,
230 last_tick_time_);
231 } else {
232 animations_[i]->SetRunState(Animation::ABORTED, last_tick_time_);
233 }
234 aborted_animation = true;
235 }
236 }
237
170 if (element_animations_) { 238 if (element_animations_) {
171 element_animations_->AbortAnimations(target_property, needs_completion); 239 if (aborted_animation)
240 element_animations_->UpdateClientAnimationState(target_property);
172 SetNeedsCommit(); 241 SetNeedsCommit();
173 SetNeedsPushProperties(); 242 SetNeedsPushProperties();
174 } else {
175 auto animations_to_remove = std::remove_if(
176 animations_.begin(), animations_.end(),
177 [target_property](const std::unique_ptr<Animation>& animation) {
178 return animation->target_property() == target_property;
179 });
180 animations_.erase(animations_to_remove, animations_.end());
181 } 243 }
182 } 244 }
183 245
184 void AnimationPlayer::PushPropertiesTo(AnimationPlayer* player_impl) { 246 void AnimationPlayer::PushPropertiesTo(AnimationPlayer* player_impl) {
185 if (!needs_push_properties_) 247 if (!needs_push_properties_)
186 return; 248 return;
187 needs_push_properties_ = false; 249 needs_push_properties_ = false;
188 250
251 // Create or destroy ElementAnimations.
189 if (element_id_ != player_impl->element_id()) { 252 if (element_id_ != player_impl->element_id()) {
190 if (player_impl->element_id()) 253 if (player_impl->element_id())
191 player_impl->DetachElement(); 254 player_impl->DetachElement();
192 if (element_id_) 255 if (element_id_)
193 player_impl->AttachElement(element_id_); 256 player_impl->AttachElement(element_id_);
194 } 257 }
258
259 if (!has_any_animation() && !player_impl->has_any_animation())
260 return;
261
262 MarkAbortedAnimationsForDeletion(player_impl);
263 PurgeAnimationsMarkedForDeletion();
264 PushNewAnimationsToImplThread(player_impl);
265
266 // Remove finished impl side animations only after pushing,
267 // and only after the animations are deleted on the main thread
268 // this insures we will never push an animation twice.
269 RemoveAnimationsCompletedOnMainThread(player_impl);
270
271 PushPropertiesToImplThread(player_impl);
195 } 272 }
196 273
197 void AnimationPlayer::NotifyAnimationStarted( 274 bool AnimationPlayer::NotifyAnimationStarted(const AnimationEvent& event) {
198 base::TimeTicks monotonic_time, 275 DCHECK(!event.is_impl_only);
199 TargetProperty::Type target_property, 276
200 int group) { 277 for (size_t i = 0; i < animations_.size(); ++i) {
201 if (animation_delegate_) 278 if (animations_[i]->group() == event.group_id &&
202 animation_delegate_->NotifyAnimationStarted(monotonic_time, target_property, 279 animations_[i]->target_property() == event.target_property &&
203 group); 280 animations_[i]->needs_synchronized_start_time()) {
281 animations_[i]->set_needs_synchronized_start_time(false);
282 if (!animations_[i]->has_set_start_time())
283 animations_[i]->set_start_time(event.monotonic_time);
284
285 if (animation_delegate_) {
286 animation_delegate_->NotifyAnimationStarted(
287 event.monotonic_time, event.target_property, event.group_id);
288 }
289 return true;
290 }
291 }
292
293 return false;
204 } 294 }
205 295
206 void AnimationPlayer::NotifyAnimationFinished( 296 bool AnimationPlayer::NotifyAnimationFinished(const AnimationEvent& event) {
207 base::TimeTicks monotonic_time, 297 DCHECK(!event.is_impl_only);
208 TargetProperty::Type target_property, 298
209 int group) { 299 for (size_t i = 0; i < animations_.size(); ++i) {
210 if (animation_delegate_) 300 if (animations_[i]->group() == event.group_id &&
211 animation_delegate_->NotifyAnimationFinished(monotonic_time, 301 animations_[i]->target_property() == event.target_property) {
212 target_property, group); 302 animations_[i]->set_received_finished_event(true);
303
304 if (animation_delegate_) {
305 animation_delegate_->NotifyAnimationFinished(
306 event.monotonic_time, event.target_property, event.group_id);
307 }
308 return true;
309 }
310 }
311
312 return false;
213 } 313 }
214 314
215 void AnimationPlayer::NotifyAnimationAborted( 315 bool AnimationPlayer::NotifyAnimationFinishedForTesting(
216 base::TimeTicks monotonic_time,
217 TargetProperty::Type target_property, 316 TargetProperty::Type target_property,
218 int group) { 317 int group_id) {
219 if (animation_delegate_) 318 AnimationEvent event(AnimationEvent::FINISHED, element_id_, group_id,
220 animation_delegate_->NotifyAnimationAborted(monotonic_time, target_property, 319 target_property, base::TimeTicks());
221 group); 320 return NotifyAnimationFinished(event);
222 } 321 }
223 322
224 void AnimationPlayer::NotifyAnimationWaitingForDeletion() { 323 bool AnimationPlayer::NotifyAnimationAborted(const AnimationEvent& event) {
225 // We need to purge animations marked for deletion. 324 DCHECK(!event.is_impl_only);
226 SetNeedsPushProperties(); 325
326 for (size_t i = 0; i < animations_.size(); ++i) {
327 if (animations_[i]->group() == event.group_id &&
328 animations_[i]->target_property() == event.target_property) {
329 animations_[i]->SetRunState(Animation::ABORTED, event.monotonic_time);
330 animations_[i]->set_received_finished_event(true);
331 if (animation_delegate_) {
332 animation_delegate_->NotifyAnimationAborted(
333 event.monotonic_time, event.target_property, event.group_id);
334 }
335 return true;
336 }
337 }
338
339 return false;
227 } 340 }
228 341
229 void AnimationPlayer::NotifyAnimationTakeover( 342 void AnimationPlayer::NotifyAnimationTakeover(const AnimationEvent& event) {
230 base::TimeTicks monotonic_time, 343 DCHECK(!event.is_impl_only);
231 TargetProperty::Type target_property, 344 DCHECK(event.target_property == TargetProperty::SCROLL_OFFSET);
232 double animation_start_time, 345
233 std::unique_ptr<AnimationCurve> curve) {
234 // We need to purge animations marked for deletion on CT. 346 // We need to purge animations marked for deletion on CT.
235 SetNeedsPushProperties(); 347 SetNeedsPushProperties();
236 348
237 if (animation_delegate_) { 349 if (animation_delegate_) {
238 DCHECK(curve); 350 DCHECK(event.curve);
351 std::unique_ptr<AnimationCurve> animation_curve = event.curve->Clone();
239 animation_delegate_->NotifyAnimationTakeover( 352 animation_delegate_->NotifyAnimationTakeover(
240 monotonic_time, target_property, animation_start_time, 353 event.monotonic_time, event.target_property, event.animation_start_time,
241 std::move(curve)); 354 std::move(animation_curve));
242 } 355 }
243 } 356 }
244 357
245 void AnimationPlayer::SetNeedsCommit() { 358 void AnimationPlayer::SetNeedsCommit() {
246 DCHECK(animation_host_); 359 DCHECK(animation_host_);
247 animation_host_->SetNeedsCommit(); 360 animation_host_->SetNeedsCommit();
248 } 361 }
249 362
250 void AnimationPlayer::SetNeedsPushProperties() { 363 void AnimationPlayer::SetNeedsPushProperties() {
251 needs_push_properties_ = true; 364 needs_push_properties_ = true;
252 365
253 DCHECK(animation_timeline_); 366 DCHECK(animation_timeline_);
254 animation_timeline_->SetNeedsPushProperties(); 367 animation_timeline_->SetNeedsPushProperties();
255 368
256 DCHECK(element_animations_); 369 DCHECK(element_animations_);
257 element_animations_->SetNeedsPushProperties(); 370 element_animations_->SetNeedsPushProperties();
258 } 371 }
259 372
373 bool AnimationPlayer::HasActiveAnimation() const {
374 for (size_t i = 0; i < animations_.size(); ++i) {
375 if (!animations_[i]->is_finished())
376 return true;
377 }
378 return false;
379 }
380
381 bool AnimationPlayer::HasNonDeletedAnimation() const {
382 for (size_t i = 0; i < animations_.size(); ++i) {
383 if (animations_[i]->run_state() != Animation::WAITING_FOR_DELETION)
384 return true;
385 }
386
387 return false;
388 }
389
390 void AnimationPlayer::StartAnimations(base::TimeTicks monotonic_time) {
391 DCHECK(needs_to_start_animations_);
392 needs_to_start_animations_ = false;
393 // First collect running properties affecting each type of element.
394 TargetProperties blocked_properties_for_active_elements;
395 TargetProperties blocked_properties_for_pending_elements;
396 std::vector<size_t> animations_waiting_for_target;
397
398 animations_waiting_for_target.reserve(animations_.size());
399 for (size_t i = 0; i < animations_.size(); ++i) {
400 if (animations_[i]->run_state() == Animation::STARTING ||
401 animations_[i]->run_state() == Animation::RUNNING) {
402 if (animations_[i]->affects_active_elements()) {
403 blocked_properties_for_active_elements[animations_[i]
404 ->target_property()] = true;
405 }
406 if (animations_[i]->affects_pending_elements()) {
407 blocked_properties_for_pending_elements[animations_[i]
408 ->target_property()] = true;
409 }
410 } else if (animations_[i]->run_state() ==
411 Animation::WAITING_FOR_TARGET_AVAILABILITY) {
412 animations_waiting_for_target.push_back(i);
413 }
414 }
415
416 for (size_t i = 0; i < animations_waiting_for_target.size(); ++i) {
417 // Collect all properties for animations with the same group id (they
418 // should all also be in the list of animations).
419 size_t animation_index = animations_waiting_for_target[i];
420 Animation* animation_waiting_for_target =
421 animations_[animation_index].get();
422 // Check for the run state again even though the animation was waiting
423 // for target because it might have changed the run state while handling
424 // previous animation in this loop (if they belong to same group).
425 if (animation_waiting_for_target->run_state() ==
426 Animation::WAITING_FOR_TARGET_AVAILABILITY) {
427 TargetProperties enqueued_properties;
428 bool affects_active_elements =
429 animation_waiting_for_target->affects_active_elements();
430 bool affects_pending_elements =
431 animation_waiting_for_target->affects_pending_elements();
432 enqueued_properties[animation_waiting_for_target->target_property()] =
433 true;
434 for (size_t j = animation_index + 1; j < animations_.size(); ++j) {
435 if (animation_waiting_for_target->group() == animations_[j]->group()) {
436 enqueued_properties[animations_[j]->target_property()] = true;
437 affects_active_elements |= animations_[j]->affects_active_elements();
438 affects_pending_elements |=
439 animations_[j]->affects_pending_elements();
440 }
441 }
442
443 // Check to see if intersection of the list of properties affected by
444 // the group and the list of currently blocked properties is null, taking
445 // into account the type(s) of elements affected by the group. In any
446 // case, the group's target properties need to be added to the lists of
447 // blocked properties.
448 bool null_intersection = true;
449 static_assert(TargetProperty::FIRST_TARGET_PROPERTY == 0,
450 "TargetProperty must be 0-based enum");
451 for (int property = TargetProperty::FIRST_TARGET_PROPERTY;
452 property <= TargetProperty::LAST_TARGET_PROPERTY; ++property) {
453 if (enqueued_properties[property]) {
454 if (affects_active_elements) {
455 if (blocked_properties_for_active_elements[property])
456 null_intersection = false;
457 else
458 blocked_properties_for_active_elements[property] = true;
459 }
460 if (affects_pending_elements) {
461 if (blocked_properties_for_pending_elements[property])
462 null_intersection = false;
463 else
464 blocked_properties_for_pending_elements[property] = true;
465 }
466 }
467 }
468
469 // If the intersection is null, then we are free to start the animations
470 // in the group.
471 if (null_intersection) {
472 animation_waiting_for_target->SetRunState(Animation::STARTING,
473 monotonic_time);
474 for (size_t j = animation_index + 1; j < animations_.size(); ++j) {
475 if (animation_waiting_for_target->group() ==
476 animations_[j]->group()) {
477 animations_[j]->SetRunState(Animation::STARTING, monotonic_time);
478 }
479 }
480 } else {
481 needs_to_start_animations_ = true;
482 }
483 }
484 }
485 }
486
487 void AnimationPlayer::PromoteStartedAnimations(base::TimeTicks monotonic_time,
488 AnimationEvents* events) {
489 for (size_t i = 0; i < animations_.size(); ++i) {
490 if (animations_[i]->run_state() == Animation::STARTING &&
491 animations_[i]->affects_active_elements()) {
492 animations_[i]->SetRunState(Animation::RUNNING, monotonic_time);
493 if (!animations_[i]->has_set_start_time() &&
494 !animations_[i]->needs_synchronized_start_time())
495 animations_[i]->set_start_time(monotonic_time);
496 if (events) {
497 base::TimeTicks start_time;
498 if (animations_[i]->has_set_start_time())
499 start_time = animations_[i]->start_time();
500 else
501 start_time = monotonic_time;
502 AnimationEvent started_event(
503 AnimationEvent::STARTED, element_id_, animations_[i]->group(),
504 animations_[i]->target_property(), start_time);
505 started_event.is_impl_only = animations_[i]->is_impl_only();
506 if (started_event.is_impl_only) {
507 // Notify delegate directly, do not record the event.
508 if (animation_delegate_) {
509 animation_delegate_->NotifyAnimationStarted(
510 started_event.monotonic_time, started_event.target_property,
511 started_event.group_id);
512 }
513 } else {
514 events->events_.push_back(started_event);
515 }
516 }
517 }
518 }
519 }
520
521 void AnimationPlayer::MarkAnimationsForDeletion(base::TimeTicks monotonic_time,
522 AnimationEvents* events) {
523 bool marked_animations_for_deletions = false;
524 std::vector<size_t> animations_with_same_group_id;
525
526 animations_with_same_group_id.reserve(animations_.size());
527 // Non-aborted animations are marked for deletion after a corresponding
528 // AnimationEvent::FINISHED event is sent or received. This means that if
529 // we don't have an events vector, we must ensure that non-aborted animations
530 // have received a finished event before marking them for deletion.
531 for (size_t i = 0; i < animations_.size(); i++) {
532 int group_id = animations_[i]->group();
533 if (animations_[i]->run_state() == Animation::ABORTED) {
534 if (events && !animations_[i]->is_impl_only()) {
535 AnimationEvent aborted_event(
536 AnimationEvent::ABORTED, element_id_, group_id,
537 animations_[i]->target_property(), monotonic_time);
538 events->events_.push_back(aborted_event);
539 }
540 // If on the compositor or on the main thread and received finish event,
541 // animation can be marked for deletion.
542 if (events || animations_[i]->received_finished_event()) {
543 animations_[i]->SetRunState(Animation::WAITING_FOR_DELETION,
544 monotonic_time);
545 marked_animations_for_deletions = true;
546 }
547 continue;
548 }
549
550 // If running on the compositor and need to complete an aborted animation
551 // on the main thread.
552 if (events &&
553 animations_[i]->run_state() ==
554 Animation::ABORTED_BUT_NEEDS_COMPLETION) {
555 AnimationEvent aborted_event(AnimationEvent::TAKEOVER, element_id_,
556 group_id, animations_[i]->target_property(),
557 monotonic_time);
558 aborted_event.animation_start_time =
559 (animations_[i]->start_time() - base::TimeTicks()).InSecondsF();
560 const ScrollOffsetAnimationCurve* scroll_offset_animation_curve =
561 animations_[i]->curve()->ToScrollOffsetAnimationCurve();
562 aborted_event.curve = scroll_offset_animation_curve->Clone();
563 // Notify the compositor that the animation is finished.
564 if (animation_delegate_) {
565 animation_delegate_->NotifyAnimationFinished(
566 aborted_event.monotonic_time, aborted_event.target_property,
567 aborted_event.group_id);
568 }
569 // Notify main thread.
570 events->events_.push_back(aborted_event);
571
572 // Remove the animation from the compositor.
573 animations_[i]->SetRunState(Animation::WAITING_FOR_DELETION,
574 monotonic_time);
575 marked_animations_for_deletions = true;
576 continue;
577 }
578
579 bool all_anims_with_same_id_are_finished = false;
580
581 // Since deleting an animation on the main thread leads to its deletion
582 // on the impl thread, we only mark a FINISHED main thread animation for
583 // deletion once it has received a FINISHED event from the impl thread.
584 bool animation_i_will_send_or_has_received_finish_event =
585 animations_[i]->is_controlling_instance() ||
586 animations_[i]->is_impl_only() ||
587 animations_[i]->received_finished_event();
588 // If an animation is finished, and not already marked for deletion,
589 // find out if all other animations in the same group are also finished.
590 if (animations_[i]->run_state() == Animation::FINISHED &&
591 animation_i_will_send_or_has_received_finish_event) {
592 // Clear the animations_with_same_group_id if it was added for
593 // the previous animation's iteration.
594 if (animations_with_same_group_id.size() > 0)
595 animations_with_same_group_id.clear();
596 all_anims_with_same_id_are_finished = true;
597 for (size_t j = 0; j < animations_.size(); ++j) {
598 bool animation_j_will_send_or_has_received_finish_event =
599 animations_[j]->is_controlling_instance() ||
600 animations_[j]->is_impl_only() ||
601 animations_[j]->received_finished_event();
602 if (group_id == animations_[j]->group()) {
603 if (!animations_[j]->is_finished() ||
604 (animations_[j]->run_state() == Animation::FINISHED &&
605 !animation_j_will_send_or_has_received_finish_event)) {
606 all_anims_with_same_id_are_finished = false;
607 break;
608 } else if (j >= i &&
609 animations_[j]->run_state() != Animation::ABORTED) {
610 // Mark down the animations which belong to the same group
611 // and is not yet aborted. If this current iteration finds that all
612 // animations with same ID are finished, then the marked
613 // animations below will be set to WAITING_FOR_DELETION in next
614 // iteration.
615 animations_with_same_group_id.push_back(j);
616 }
617 }
618 }
619 }
620 if (all_anims_with_same_id_are_finished) {
621 // We now need to remove all animations with the same group id as
622 // group_id (and send along animation finished notifications, if
623 // necessary).
624 for (size_t j = 0; j < animations_with_same_group_id.size(); j++) {
625 size_t animation_index = animations_with_same_group_id[j];
626 if (events) {
627 AnimationEvent finished_event(
628 AnimationEvent::FINISHED, element_id_,
629 animations_[animation_index]->group(),
630 animations_[animation_index]->target_property(), monotonic_time);
631 finished_event.is_impl_only =
632 animations_[animation_index]->is_impl_only();
633 if (finished_event.is_impl_only) {
634 // Notify delegate directly, do not record the event.
635 if (animation_delegate_) {
636 animation_delegate_->NotifyAnimationFinished(
637 finished_event.monotonic_time, finished_event.target_property,
638 finished_event.group_id);
639 }
640 } else {
641 events->events_.push_back(finished_event);
642 }
643 }
644 animations_[animation_index]->SetRunState(
645 Animation::WAITING_FOR_DELETION, monotonic_time);
646 }
647 marked_animations_for_deletions = true;
648 }
649 }
650
651 // Notify about animations waiting for deletion.
652 // We need to purge animations marked for deletion, which happens in
653 // PushProperties().
654 if (marked_animations_for_deletions)
655 SetNeedsPushProperties();
656 }
657
658 void AnimationPlayer::TickAnimations(base::TimeTicks monotonic_time) {
659 DCHECK(element_animations_);
660
661 for (size_t i = 0; i < animations_.size(); ++i) {
662 if (animations_[i]->run_state() == Animation::STARTING ||
663 animations_[i]->run_state() == Animation::RUNNING ||
664 animations_[i]->run_state() == Animation::PAUSED) {
665 if (!animations_[i]->InEffect(monotonic_time))
666 continue;
667
668 base::TimeDelta trimmed =
669 animations_[i]->TrimTimeToCurrentIteration(monotonic_time);
670
671 switch (animations_[i]->target_property()) {
672 case TargetProperty::TRANSFORM: {
673 const TransformAnimationCurve* transform_animation_curve =
674 animations_[i]->curve()->ToTransformAnimationCurve();
675 const gfx::Transform transform =
676 transform_animation_curve->GetValue(trimmed);
677 element_animations_->NotifyClientTransformAnimated(
678 transform, animations_[i]->affects_active_elements(),
679 animations_[i]->affects_pending_elements());
680 break;
681 }
682
683 case TargetProperty::OPACITY: {
684 const FloatAnimationCurve* float_animation_curve =
685 animations_[i]->curve()->ToFloatAnimationCurve();
686 const float opacity = std::max(
687 std::min(float_animation_curve->GetValue(trimmed), 1.0f), 0.f);
688 element_animations_->NotifyClientOpacityAnimated(
689 opacity, animations_[i]->affects_active_elements(),
690 animations_[i]->affects_pending_elements());
691 break;
692 }
693
694 case TargetProperty::FILTER: {
695 const FilterAnimationCurve* filter_animation_curve =
696 animations_[i]->curve()->ToFilterAnimationCurve();
697 const FilterOperations filter =
698 filter_animation_curve->GetValue(trimmed);
699 element_animations_->NotifyClientFilterAnimated(
700 filter, animations_[i]->affects_active_elements(),
701 animations_[i]->affects_pending_elements());
702 break;
703 }
704
705 case TargetProperty::BACKGROUND_COLOR: {
706 // Not yet implemented.
707 break;
708 }
709
710 case TargetProperty::SCROLL_OFFSET: {
711 const ScrollOffsetAnimationCurve* scroll_offset_animation_curve =
712 animations_[i]->curve()->ToScrollOffsetAnimationCurve();
713 const gfx::ScrollOffset scroll_offset =
714 scroll_offset_animation_curve->GetValue(trimmed);
715 element_animations_->NotifyClientScrollOffsetAnimated(
716 scroll_offset, animations_[i]->affects_active_elements(),
717 animations_[i]->affects_pending_elements());
718 break;
719 }
720 }
721 }
722 }
723
724 last_tick_time_ = monotonic_time;
725 }
726
727 void AnimationPlayer::MarkFinishedAnimations(base::TimeTicks monotonic_time) {
728 bool finished_transform_animation = false;
729 bool finished_opacity_animation = false;
730 bool finished_filter_animation = false;
731 for (size_t i = 0; i < animations_.size(); ++i) {
732 if (!animations_[i]->is_finished() &&
733 animations_[i]->IsFinishedAt(monotonic_time)) {
734 animations_[i]->SetRunState(Animation::FINISHED, monotonic_time);
735 if (animations_[i]->target_property() == TargetProperty::TRANSFORM)
736 finished_transform_animation = true;
737 else if (animations_[i]->target_property() == TargetProperty::OPACITY)
738 finished_opacity_animation = true;
739 else if (animations_[i]->target_property() == TargetProperty::FILTER)
740 finished_filter_animation = true;
741 }
742 }
743
744 DCHECK(element_animations_);
745 element_animations_->UpdateClientAnimationState(finished_transform_animation,
746 finished_opacity_animation,
747 finished_filter_animation);
748 }
749
750 void AnimationPlayer::ActivateAnimations(bool* changed_transform_animation,
751 bool* changed_opacity_animation,
752 bool* changed_filter_animation) {
753 for (size_t i = 0; i < animations_.size(); ++i) {
754 if (animations_[i]->affects_active_elements() !=
755 animations_[i]->affects_pending_elements()) {
756 if (animations_[i]->target_property() == TargetProperty::TRANSFORM)
757 *changed_transform_animation = true;
758 else if (animations_[i]->target_property() == TargetProperty::OPACITY)
759 *changed_opacity_animation = true;
760 else if (animations_[i]->target_property() == TargetProperty::FILTER)
761 *changed_filter_animation = true;
762 }
763 animations_[i]->set_affects_active_elements(
764 animations_[i]->affects_pending_elements());
765 }
766 auto affects_no_elements = [](const std::unique_ptr<Animation>& animation) {
767 return !animation->affects_active_elements() &&
768 !animation->affects_pending_elements();
769 };
770 animations_.erase(std::remove_if(animations_.begin(), animations_.end(),
771 affects_no_elements),
772 animations_.end());
773 }
774
775 bool AnimationPlayer::HasFilterAnimationThatInflatesBounds() const {
776 for (size_t i = 0; i < animations_.size(); ++i) {
777 if (!animations_[i]->is_finished() &&
778 animations_[i]->target_property() == TargetProperty::FILTER &&
779 animations_[i]
780 ->curve()
781 ->ToFilterAnimationCurve()
782 ->HasFilterThatMovesPixels())
783 return true;
784 }
785
786 return false;
787 }
788
789 bool AnimationPlayer::HasTransformAnimationThatInflatesBounds() const {
790 return IsCurrentlyAnimatingProperty(TargetProperty::TRANSFORM,
791 ElementListType::ACTIVE) ||
792 IsCurrentlyAnimatingProperty(TargetProperty::TRANSFORM,
793 ElementListType::PENDING);
794 }
795
796 bool AnimationPlayer::TransformAnimationBoundsForBox(const gfx::BoxF& box,
797 gfx::BoxF* bounds) const {
798 DCHECK(HasTransformAnimationThatInflatesBounds())
799 << "TransformAnimationBoundsForBox will give incorrect results if there "
800 << "are no transform animations affecting bounds, non-animated transform "
801 << "is not known";
802
803 // Compute bounds based on animations for which is_finished() is false.
804 // Do nothing if there are no such animations; in this case, it is assumed
805 // that callers will take care of computing bounds based on the owning layer's
806 // actual transform.
807 *bounds = gfx::BoxF();
808 for (size_t i = 0; i < animations_.size(); ++i) {
809 if (animations_[i]->is_finished() ||
810 animations_[i]->target_property() != TargetProperty::TRANSFORM)
811 continue;
812
813 const TransformAnimationCurve* transform_animation_curve =
814 animations_[i]->curve()->ToTransformAnimationCurve();
815 gfx::BoxF animation_bounds;
816 bool success =
817 transform_animation_curve->AnimatedBoundsForBox(box, &animation_bounds);
818 if (!success)
819 return false;
820 bounds->Union(animation_bounds);
821 }
822
823 return true;
824 }
825
826 bool AnimationPlayer::HasAnimationThatAffectsScale() const {
827 for (size_t i = 0; i < animations_.size(); ++i) {
828 if (animations_[i]->is_finished() ||
829 animations_[i]->target_property() != TargetProperty::TRANSFORM)
830 continue;
831
832 const TransformAnimationCurve* transform_animation_curve =
833 animations_[i]->curve()->ToTransformAnimationCurve();
834 if (transform_animation_curve->AffectsScale())
835 return true;
836 }
837
838 return false;
839 }
840
841 bool AnimationPlayer::HasOnlyTranslationTransforms(
842 ElementListType list_type) const {
843 for (size_t i = 0; i < animations_.size(); ++i) {
844 if (animations_[i]->is_finished() ||
845 animations_[i]->target_property() != TargetProperty::TRANSFORM)
846 continue;
847
848 if ((list_type == ElementListType::ACTIVE &&
849 !animations_[i]->affects_active_elements()) ||
850 (list_type == ElementListType::PENDING &&
851 !animations_[i]->affects_pending_elements()))
852 continue;
853
854 const TransformAnimationCurve* transform_animation_curve =
855 animations_[i]->curve()->ToTransformAnimationCurve();
856 if (!transform_animation_curve->IsTranslation())
857 return false;
858 }
859
860 return true;
861 }
862
863 bool AnimationPlayer::AnimationsPreserveAxisAlignment() const {
864 for (size_t i = 0; i < animations_.size(); ++i) {
865 if (animations_[i]->is_finished() ||
866 animations_[i]->target_property() != TargetProperty::TRANSFORM)
867 continue;
868
869 const TransformAnimationCurve* transform_animation_curve =
870 animations_[i]->curve()->ToTransformAnimationCurve();
871 if (!transform_animation_curve->PreservesAxisAlignment())
872 return false;
873 }
874
875 return true;
876 }
877
878 bool AnimationPlayer::AnimationStartScale(ElementListType list_type,
879 float* start_scale) const {
880 *start_scale = 0.f;
881 for (size_t i = 0; i < animations_.size(); ++i) {
882 if (animations_[i]->is_finished() ||
883 animations_[i]->target_property() != TargetProperty::TRANSFORM)
884 continue;
885
886 if ((list_type == ElementListType::ACTIVE &&
887 !animations_[i]->affects_active_elements()) ||
888 (list_type == ElementListType::PENDING &&
889 !animations_[i]->affects_pending_elements()))
890 continue;
891
892 bool forward_direction = true;
893 switch (animations_[i]->direction()) {
894 case Animation::Direction::NORMAL:
895 case Animation::Direction::ALTERNATE_NORMAL:
896 forward_direction = animations_[i]->playback_rate() >= 0.0;
897 break;
898 case Animation::Direction::REVERSE:
899 case Animation::Direction::ALTERNATE_REVERSE:
900 forward_direction = animations_[i]->playback_rate() < 0.0;
901 break;
902 }
903
904 const TransformAnimationCurve* transform_animation_curve =
905 animations_[i]->curve()->ToTransformAnimationCurve();
906 float animation_start_scale = 0.f;
907 if (!transform_animation_curve->AnimationStartScale(forward_direction,
908 &animation_start_scale))
909 return false;
910 *start_scale = std::max(*start_scale, animation_start_scale);
911 }
912 return true;
913 }
914
915 bool AnimationPlayer::MaximumTargetScale(ElementListType list_type,
916 float* max_scale) const {
917 *max_scale = 0.f;
918 for (size_t i = 0; i < animations_.size(); ++i) {
919 if (animations_[i]->is_finished() ||
920 animations_[i]->target_property() != TargetProperty::TRANSFORM)
921 continue;
922
923 if ((list_type == ElementListType::ACTIVE &&
924 !animations_[i]->affects_active_elements()) ||
925 (list_type == ElementListType::PENDING &&
926 !animations_[i]->affects_pending_elements()))
927 continue;
928
929 bool forward_direction = true;
930 switch (animations_[i]->direction()) {
931 case Animation::Direction::NORMAL:
932 case Animation::Direction::ALTERNATE_NORMAL:
933 forward_direction = animations_[i]->playback_rate() >= 0.0;
934 break;
935 case Animation::Direction::REVERSE:
936 case Animation::Direction::ALTERNATE_REVERSE:
937 forward_direction = animations_[i]->playback_rate() < 0.0;
938 break;
939 }
940
941 const TransformAnimationCurve* transform_animation_curve =
942 animations_[i]->curve()->ToTransformAnimationCurve();
943 float animation_scale = 0.f;
944 if (!transform_animation_curve->MaximumTargetScale(forward_direction,
945 &animation_scale))
946 return false;
947 *max_scale = std::max(*max_scale, animation_scale);
948 }
949
950 return true;
951 }
952
953 bool AnimationPlayer::IsPotentiallyAnimatingProperty(
954 TargetProperty::Type target_property,
955 ElementListType list_type) const {
956 for (size_t i = 0; i < animations_.size(); ++i) {
957 if (!animations_[i]->is_finished() &&
958 animations_[i]->target_property() == target_property) {
959 if ((list_type == ElementListType::ACTIVE &&
960 animations_[i]->affects_active_elements()) ||
961 (list_type == ElementListType::PENDING &&
962 animations_[i]->affects_pending_elements()))
963 return true;
964 }
965 }
966 return false;
967 }
968
969 bool AnimationPlayer::IsCurrentlyAnimatingProperty(
970 TargetProperty::Type target_property,
971 ElementListType list_type) const {
972 for (size_t i = 0; i < animations_.size(); ++i) {
973 if (!animations_[i]->is_finished() &&
974 animations_[i]->InEffect(last_tick_time_) &&
975 animations_[i]->target_property() == target_property) {
976 if ((list_type == ElementListType::ACTIVE &&
977 animations_[i]->affects_active_elements()) ||
978 (list_type == ElementListType::PENDING &&
979 animations_[i]->affects_pending_elements()))
980 return true;
981 }
982 }
983 return false;
984 }
985
986 bool AnimationPlayer::HasElementInActiveList() const {
987 DCHECK(element_animations_);
988 return element_animations_->has_element_in_active_list();
989 }
990
991 gfx::ScrollOffset AnimationPlayer::ScrollOffsetForAnimation() const {
992 DCHECK(element_animations_);
993 return element_animations_->ScrollOffsetForAnimation();
994 }
995
996 Animation* AnimationPlayer::GetAnimation(
997 TargetProperty::Type target_property) const {
998 for (size_t i = 0; i < animations_.size(); ++i) {
999 size_t index = animations_.size() - i - 1;
1000 if (animations_[index]->target_property() == target_property)
1001 return animations_[index].get();
1002 }
1003 return nullptr;
1004 }
1005
1006 Animation* AnimationPlayer::GetAnimationById(int animation_id) const {
1007 for (size_t i = 0; i < animations_.size(); ++i)
1008 if (animations_[i]->id() == animation_id)
1009 return animations_[i].get();
1010 return nullptr;
1011 }
1012
1013 void AnimationPlayer::MarkAbortedAnimationsForDeletion(
1014 AnimationPlayer* animation_player_impl) const {
1015 bool aborted_transform_animation = false;
1016 bool aborted_opacity_animation = false;
1017 bool aborted_filter_animation = false;
1018 auto& animations_impl = animation_player_impl->animations_;
1019 for (const auto& animation_impl : animations_impl) {
1020 // If the animation has been aborted on the main thread, mark it for
1021 // deletion.
1022 if (Animation* animation = GetAnimationById(animation_impl->id())) {
1023 if (animation->run_state() == Animation::ABORTED) {
1024 animation_impl->SetRunState(Animation::WAITING_FOR_DELETION,
1025 animation_player_impl->last_tick_time_);
1026 animation->SetRunState(Animation::WAITING_FOR_DELETION,
1027 last_tick_time_);
1028 if (animation_impl->target_property() == TargetProperty::TRANSFORM)
1029 aborted_transform_animation = true;
1030 else if (animation_impl->target_property() == TargetProperty::OPACITY)
1031 aborted_opacity_animation = true;
1032 else if (animation_impl->target_property() == TargetProperty::FILTER)
1033 aborted_filter_animation = true;
1034 }
1035 }
1036 }
1037
1038 if (element_animations_)
1039 element_animations_->SetNeedsUpdateImplClientState(
1040 aborted_transform_animation, aborted_opacity_animation,
1041 aborted_filter_animation);
1042 }
1043
1044 void AnimationPlayer::PurgeAnimationsMarkedForDeletion() {
1045 animations_.erase(
1046 std::remove_if(animations_.begin(), animations_.end(),
1047 [](const std::unique_ptr<Animation>& animation) {
1048 return animation->run_state() ==
1049 Animation::WAITING_FOR_DELETION;
1050 }),
1051 animations_.end());
1052 }
1053
1054 void AnimationPlayer::PushNewAnimationsToImplThread(
1055 AnimationPlayer* animation_player_impl) const {
1056 // Any new animations owned by the main thread's ElementAnimations are cloned
1057 // and added to the impl thread's ElementAnimations.
1058 for (size_t i = 0; i < animations_.size(); ++i) {
1059 // If the animation is already running on the impl thread, there is no
1060 // need to copy it over.
1061 if (animation_player_impl->GetAnimationById(animations_[i]->id()))
1062 continue;
1063
1064 if (animations_[i]->target_property() == TargetProperty::SCROLL_OFFSET &&
1065 !animations_[i]
1066 ->curve()
1067 ->ToScrollOffsetAnimationCurve()
1068 ->HasSetInitialValue()) {
1069 gfx::ScrollOffset current_scroll_offset;
1070 if (animation_player_impl->HasElementInActiveList()) {
1071 current_scroll_offset =
1072 animation_player_impl->ScrollOffsetForAnimation();
1073 } else {
1074 // The owning layer isn't yet in the active tree, so the main thread
1075 // scroll offset will be up to date.
1076 current_scroll_offset = ScrollOffsetForAnimation();
1077 }
1078 animations_[i]->curve()->ToScrollOffsetAnimationCurve()->SetInitialValue(
1079 current_scroll_offset);
1080 }
1081
1082 // The new animation should be set to run as soon as possible.
1083 Animation::RunState initial_run_state =
1084 Animation::WAITING_FOR_TARGET_AVAILABILITY;
1085 std::unique_ptr<Animation> to_add(
1086 animations_[i]->CloneAndInitialize(initial_run_state));
1087 DCHECK(!to_add->needs_synchronized_start_time());
1088 to_add->set_affects_active_elements(false);
1089 animation_player_impl->AddAnimation(std::move(to_add));
1090 }
1091 }
1092
1093 static bool IsCompleted(Animation* animation,
1094 const AnimationPlayer* main_thread_player) {
1095 if (animation->is_impl_only()) {
1096 return (animation->run_state() == Animation::WAITING_FOR_DELETION);
1097 } else {
1098 return !main_thread_player->GetAnimationById(animation->id());
1099 }
1100 }
1101
1102 void AnimationPlayer::RemoveAnimationsCompletedOnMainThread(
1103 AnimationPlayer* animation_player_impl) const {
1104 bool removed_transform_animation = false;
1105 bool removed_opacity_animation = false;
1106 bool removed_filter_animation = false;
1107 // Animations removed on the main thread should no longer affect pending
1108 // elements, and should stop affecting active elements after the next call
1109 // to ActivateAnimations. If already WAITING_FOR_DELETION, they can be removed
1110 // immediately.
1111 auto& animations = animation_player_impl->animations_;
1112 for (const auto& animation : animations) {
1113 if (IsCompleted(animation.get(), this)) {
1114 animation->set_affects_pending_elements(false);
1115 if (animation->target_property() == TargetProperty::TRANSFORM)
1116 removed_transform_animation = true;
1117 else if (animation->target_property() == TargetProperty::OPACITY)
1118 removed_opacity_animation = true;
1119 else if (animation->target_property() == TargetProperty::FILTER)
1120 removed_filter_animation = true;
1121 }
1122 }
1123 auto affects_active_only_and_is_waiting_for_deletion =
1124 [](const std::unique_ptr<Animation>& animation) {
1125 return animation->run_state() == Animation::WAITING_FOR_DELETION &&
1126 !animation->affects_pending_elements();
1127 };
1128 animations.erase(
1129 std::remove_if(animations.begin(), animations.end(),
1130 affects_active_only_and_is_waiting_for_deletion),
1131 animations.end());
1132
1133 if (element_animations_)
1134 element_animations_->SetNeedsUpdateImplClientState(
1135 removed_transform_animation, removed_opacity_animation,
1136 removed_filter_animation);
1137 }
1138
1139 void AnimationPlayer::PushPropertiesToImplThread(
1140 AnimationPlayer* animation_player_impl) {
1141 for (size_t i = 0; i < animations_.size(); ++i) {
1142 Animation* current_impl =
1143 animation_player_impl->GetAnimationById(animations_[i]->id());
1144 if (current_impl)
1145 animations_[i]->PushPropertiesTo(current_impl);
1146 }
1147 }
1148
260 } // namespace cc 1149 } // namespace cc
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698