1 // Copyright 2014 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.
5 #include "mojo/services/view_manager/scheduled_animation_group.h"
9 #include "mojo/converters/geometry/geometry_type_converters.h"
10 #include "mojo/converters/transform/transform_type_converters.h"
11 #include "mojo/services/view_manager/server_view.h"
13 using mojo::ANIMATION_PROPERTY_NONE
;
14 using mojo::ANIMATION_PROPERTY_OPACITY
;
15 using mojo::ANIMATION_PROPERTY_TRANSFORM
;
16 using mojo::AnimationProperty
;
18 namespace view_manager
{
21 using Sequences
= std::vector
<ScheduledAnimationSequence
>;
23 // Gets the value of |property| from |view| into |value|.
24 void GetValueFromView(const ServerView
* view
,
25 AnimationProperty property
,
26 ScheduledAnimationValue
* value
) {
28 case ANIMATION_PROPERTY_NONE
:
31 case ANIMATION_PROPERTY_OPACITY
:
32 value
->float_value
= view
->opacity();
34 case ANIMATION_PROPERTY_TRANSFORM
:
35 value
->transform
= view
->transform();
40 // Sets the value of |property| from |value| into |view|.
41 void SetViewPropertyFromValue(ServerView
* view
,
42 AnimationProperty property
,
43 const ScheduledAnimationValue
& value
) {
45 case ANIMATION_PROPERTY_NONE
:
47 case ANIMATION_PROPERTY_OPACITY
:
48 view
->SetOpacity(value
.float_value
);
50 case ANIMATION_PROPERTY_TRANSFORM
:
51 view
->SetTransform(value
.transform
);
56 // Sets the value of |property| into |view| between two points.
57 void SetViewPropertyFromValueBetween(ServerView
* view
,
58 AnimationProperty property
,
60 gfx::Tween::Type tween_type
,
61 const ScheduledAnimationValue
& start
,
62 const ScheduledAnimationValue
& target
) {
63 const double tween_value
= gfx::Tween::CalculateValue(tween_type
, value
);
65 case ANIMATION_PROPERTY_NONE
:
67 case ANIMATION_PROPERTY_OPACITY
:
68 view
->SetOpacity(gfx::Tween::FloatValueBetween(
69 tween_value
, start
.float_value
, target
.float_value
));
71 case ANIMATION_PROPERTY_TRANSFORM
:
72 view
->SetTransform(gfx::Tween::TransformValueBetween(
73 tween_value
, start
.transform
, target
.transform
));
78 gfx::Tween::Type
AnimationTypeToTweenType(mojo::AnimationTweenType type
) {
80 case mojo::ANIMATION_TWEEN_TYPE_LINEAR
:
81 return gfx::Tween::LINEAR
;
82 case mojo::ANIMATION_TWEEN_TYPE_EASE_IN
:
83 return gfx::Tween::EASE_IN
;
84 case mojo::ANIMATION_TWEEN_TYPE_EASE_OUT
:
85 return gfx::Tween::EASE_OUT
;
86 case mojo::ANIMATION_TWEEN_TYPE_EASE_IN_OUT
:
87 return gfx::Tween::EASE_IN_OUT
;
89 return gfx::Tween::LINEAR
;
92 void ConvertToScheduledValue(const mojo::AnimationValue
& transport_value
,
93 ScheduledAnimationValue
* value
) {
94 value
->float_value
= transport_value
.float_value
;
95 value
->transform
= transport_value
.transform
.To
<gfx::Transform
>();
98 void ConvertToScheduledElement(const mojo::AnimationElement
& transport_element
,
99 ScheduledAnimationElement
* element
) {
100 element
->property
= transport_element
.property
;
102 base::TimeDelta::FromMicroseconds(transport_element
.duration
);
103 element
->tween_type
= AnimationTypeToTweenType(transport_element
.tween_type
);
104 if (transport_element
.property
!= ANIMATION_PROPERTY_NONE
) {
105 if (transport_element
.start_value
.get()) {
106 element
->is_start_valid
= true;
107 ConvertToScheduledValue(*transport_element
.start_value
,
108 &(element
->start_value
));
110 element
->is_start_valid
= false;
112 ConvertToScheduledValue(*transport_element
.target_value
,
113 &(element
->target_value
));
117 bool IsAnimationValueValid(AnimationProperty property
,
118 const mojo::AnimationValue
& value
) {
120 case ANIMATION_PROPERTY_NONE
:
123 case ANIMATION_PROPERTY_OPACITY
:
124 return value
.float_value
>= 0.f
&& value
.float_value
<= 1.f
;
125 case ANIMATION_PROPERTY_TRANSFORM
:
126 return value
.transform
.get() && value
.transform
->matrix
.size() == 16u;
131 bool IsAnimationElementValid(const mojo::AnimationElement
& element
) {
132 if (element
.property
== ANIMATION_PROPERTY_NONE
)
133 return true; // None is a pause and doesn't need any values.
134 if (element
.start_value
.get() &&
135 !IsAnimationValueValid(element
.property
, *element
.start_value
))
137 // For all other properties we require a target.
138 return element
.target_value
.get() &&
139 IsAnimationValueValid(element
.property
, *element
.target_value
);
142 bool IsAnimationSequenceValid(const mojo::AnimationSequence
& sequence
) {
143 if (sequence
.elements
.size() == 0u)
146 for (size_t i
= 0; i
< sequence
.elements
.size(); ++i
) {
147 if (!IsAnimationElementValid(*sequence
.elements
[i
]))
153 bool IsAnimationGroupValid(const mojo::AnimationGroup
& transport_group
) {
154 if (transport_group
.sequences
.size() == 0u)
156 for (size_t i
= 0; i
< transport_group
.sequences
.size(); ++i
) {
157 if (!IsAnimationSequenceValid(*transport_group
.sequences
[i
]))
163 // If the start value for |element| isn't valid, the value for the property
164 // is obtained from |view| and placed into |element|.
165 void GetStartValueFromViewIfNecessary(const ServerView
* view
,
166 ScheduledAnimationElement
* element
) {
167 if (element
->property
!= ANIMATION_PROPERTY_NONE
&&
168 !element
->is_start_valid
) {
169 GetValueFromView(view
, element
->property
, &(element
->start_value
));
173 void GetScheduledAnimationProperties(const Sequences
& sequences
,
174 std::set
<AnimationProperty
>* properties
) {
175 for (const ScheduledAnimationSequence
& sequence
: sequences
) {
176 for (const ScheduledAnimationElement
& element
: sequence
.elements
)
177 properties
->insert(element
.property
);
181 void SetPropertyToTargetProperty(ServerView
* view
,
182 mojo::AnimationProperty property
,
183 const Sequences
& sequences
) {
184 // NOTE: this doesn't deal with |cycle_count| quite right, but I'm honestly
185 // not sure we really want to support the same property in multiple sequences
186 // animating at once so I'm not dealing.
187 base::TimeDelta max_end_duration
;
188 scoped_ptr
<ScheduledAnimationValue
> value
;
189 for (const ScheduledAnimationSequence
& sequence
: sequences
) {
190 base::TimeDelta duration
;
191 for (const ScheduledAnimationElement
& element
: sequence
.elements
) {
192 if (element
.property
!= property
)
195 duration
+= element
.duration
;
196 if (duration
> max_end_duration
) {
197 max_end_duration
= duration
;
198 value
.reset(new ScheduledAnimationValue(element
.target_value
));
203 SetViewPropertyFromValue(view
, property
, *value
);
206 void ConvertSequenceToScheduled(
207 const mojo::AnimationSequence
& transport_sequence
,
209 ScheduledAnimationSequence
* sequence
) {
210 sequence
->run_until_stopped
= transport_sequence
.cycle_count
== 0u;
211 sequence
->cycle_count
= transport_sequence
.cycle_count
;
212 DCHECK_NE(0u, transport_sequence
.elements
.size());
213 sequence
->elements
.resize(transport_sequence
.elements
.size());
215 base::TimeTicks element_start_time
= now
;
216 for (size_t i
= 0; i
< transport_sequence
.elements
.size(); ++i
) {
217 ConvertToScheduledElement(*(transport_sequence
.elements
[i
].get()),
218 &(sequence
->elements
[i
]));
219 sequence
->elements
[i
].start_time
= element_start_time
;
220 sequence
->duration
+= sequence
->elements
[i
].duration
;
221 element_start_time
+= sequence
->elements
[i
].duration
;
225 bool AdvanceSequence(ServerView
* view
,
226 ScheduledAnimationSequence
* sequence
,
227 base::TimeTicks now
) {
228 ScheduledAnimationElement
* element
=
229 &(sequence
->elements
[sequence
->current_index
]);
230 while (element
->start_time
+ element
->duration
< now
) {
231 SetViewPropertyFromValue(view
, element
->property
, element
->target_value
);
232 if (++sequence
->current_index
== sequence
->elements
.size()) {
233 if (!sequence
->run_until_stopped
&& --sequence
->cycle_count
== 0) {
234 SetViewPropertyFromValue(view
, element
->property
,
235 element
->target_value
);
239 sequence
->current_index
= 0;
241 sequence
->elements
[sequence
->current_index
].start_time
=
242 element
->start_time
+ element
->duration
;
243 element
= &(sequence
->elements
[sequence
->current_index
]);
244 GetStartValueFromViewIfNecessary(view
, element
);
246 // It's possible for the delta between now and |last_tick_time_| to be very
247 // big (could happen if machine sleeps and is woken up much later). Normally
248 // the repeat count is smallish, so we don't bother optimizing it. OTOH if
249 // a sequence repeats forever we optimize it lest we get stuck in this loop
250 // for a very long time.
251 if (sequence
->run_until_stopped
&& sequence
->current_index
== 0) {
252 element
->start_time
=
253 now
- base::TimeDelta::FromMicroseconds(
254 (now
- element
->start_time
).InMicroseconds() %
255 sequence
->duration
.InMicroseconds());
263 ScheduledAnimationValue::ScheduledAnimationValue() {
265 ScheduledAnimationValue::~ScheduledAnimationValue() {
268 ScheduledAnimationElement::ScheduledAnimationElement()
269 : property(ANIMATION_PROPERTY_OPACITY
),
270 tween_type(gfx::Tween::EASE_IN
),
271 is_start_valid(false) {
273 ScheduledAnimationElement::~ScheduledAnimationElement() {
276 ScheduledAnimationSequence::ScheduledAnimationSequence()
277 : run_until_stopped(false), cycle_count(0), current_index(0u) {
279 ScheduledAnimationSequence::~ScheduledAnimationSequence() {
282 ScheduledAnimationGroup::~ScheduledAnimationGroup() {
286 scoped_ptr
<ScheduledAnimationGroup
> ScheduledAnimationGroup::Create(
290 const mojo::AnimationGroup
& transport_group
) {
291 if (!IsAnimationGroupValid(transport_group
))
294 scoped_ptr
<ScheduledAnimationGroup
> group(
295 new ScheduledAnimationGroup(view
, id
, now
));
296 group
->sequences_
.resize(transport_group
.sequences
.size());
297 for (size_t i
= 0; i
< transport_group
.sequences
.size(); ++i
) {
298 const mojo::AnimationSequence
& transport_sequence(
299 *(transport_group
.sequences
[i
]));
300 DCHECK_NE(0u, transport_sequence
.elements
.size());
301 ConvertSequenceToScheduled(transport_sequence
, now
, &group
->sequences_
[i
]);
306 void ScheduledAnimationGroup::ObtainStartValues() {
307 for (ScheduledAnimationSequence
& sequence
: sequences_
)
308 GetStartValueFromViewIfNecessary(view_
, &(sequence
.elements
[0]));
311 void ScheduledAnimationGroup::SetValuesToTargetValuesForPropertiesNotIn(
312 const ScheduledAnimationGroup
& other
) {
313 std::set
<AnimationProperty
> our_properties
;
314 GetScheduledAnimationProperties(sequences_
, &our_properties
);
316 std::set
<AnimationProperty
> other_properties
;
317 GetScheduledAnimationProperties(other
.sequences_
, &other_properties
);
319 for (AnimationProperty property
: our_properties
) {
320 if (other_properties
.count(property
) == 0 &&
321 property
!= ANIMATION_PROPERTY_NONE
) {
322 SetPropertyToTargetProperty(view_
, property
, sequences_
);
327 bool ScheduledAnimationGroup::Tick(base::TimeTicks time
) {
328 for (Sequences::iterator i
= sequences_
.begin(); i
!= sequences_
.end();) {
329 if (!AdvanceSequence(view_
, &(*i
), time
)) {
330 i
= sequences_
.erase(i
);
333 const ScheduledAnimationElement
& active_element(
334 i
->elements
[i
->current_index
]);
335 const double percent
=
336 (time
- active_element
.start_time
).InMillisecondsF() /
337 active_element
.duration
.InMillisecondsF();
338 SetViewPropertyFromValueBetween(
339 view_
, active_element
.property
, percent
, active_element
.tween_type
,
340 active_element
.start_value
, active_element
.target_value
);
343 return sequences_
.empty();
346 ScheduledAnimationGroup::ScheduledAnimationGroup(ServerView
* view
,
348 base::TimeTicks time_scheduled
)
349 : view_(view
), id_(id
), time_scheduled_(time_scheduled
) {
352 } // namespace view_manager