[Sync] Fix invalidations on Android.
[chromium-blink-merge.git] / mojo / services / view_manager / scheduled_animation_group.cc
blob0f27c424d34b76bec5ee9a31093358c94633d71b
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"
7 #include <set>
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 {
19 namespace {
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) {
27 switch (property) {
28 case ANIMATION_PROPERTY_NONE:
29 NOTREACHED();
30 break;
31 case ANIMATION_PROPERTY_OPACITY:
32 value->float_value = view->opacity();
33 break;
34 case ANIMATION_PROPERTY_TRANSFORM:
35 value->transform = view->transform();
36 break;
40 // Sets the value of |property| from |value| into |view|.
41 void SetViewPropertyFromValue(ServerView* view,
42 AnimationProperty property,
43 const ScheduledAnimationValue& value) {
44 switch (property) {
45 case ANIMATION_PROPERTY_NONE:
46 break;
47 case ANIMATION_PROPERTY_OPACITY:
48 view->SetOpacity(value.float_value);
49 break;
50 case ANIMATION_PROPERTY_TRANSFORM:
51 view->SetTransform(value.transform);
52 break;
56 // Sets the value of |property| into |view| between two points.
57 void SetViewPropertyFromValueBetween(ServerView* view,
58 AnimationProperty property,
59 double value,
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);
64 switch (property) {
65 case ANIMATION_PROPERTY_NONE:
66 break;
67 case ANIMATION_PROPERTY_OPACITY:
68 view->SetOpacity(gfx::Tween::FloatValueBetween(
69 tween_value, start.float_value, target.float_value));
70 break;
71 case ANIMATION_PROPERTY_TRANSFORM:
72 view->SetTransform(gfx::Tween::TransformValueBetween(
73 tween_value, start.transform, target.transform));
74 break;
78 gfx::Tween::Type AnimationTypeToTweenType(mojo::AnimationTweenType type) {
79 switch (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;
101 element->duration =
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));
109 } else {
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) {
119 switch (property) {
120 case ANIMATION_PROPERTY_NONE:
121 NOTREACHED();
122 return false;
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;
128 return false;
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))
136 return false;
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)
144 return false;
146 for (size_t i = 0; i < sequence.elements.size(); ++i) {
147 if (!IsAnimationElementValid(*sequence.elements[i]))
148 return false;
150 return true;
153 bool IsAnimationGroupValid(const mojo::AnimationGroup& transport_group) {
154 if (transport_group.sequences.size() == 0u)
155 return false;
156 for (size_t i = 0; i < transport_group.sequences.size(); ++i) {
157 if (!IsAnimationSequenceValid(*transport_group.sequences[i]))
158 return false;
160 return true;
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)
193 continue;
195 duration += element.duration;
196 if (duration > max_end_duration) {
197 max_end_duration = duration;
198 value.reset(new ScheduledAnimationValue(element.target_value));
202 if (value.get())
203 SetViewPropertyFromValue(view, property, *value);
206 void ConvertSequenceToScheduled(
207 const mojo::AnimationSequence& transport_sequence,
208 base::TimeTicks now,
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);
236 return false;
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());
258 return true;
261 } // namespace
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() {
285 // static
286 scoped_ptr<ScheduledAnimationGroup> ScheduledAnimationGroup::Create(
287 ServerView* view,
288 base::TimeTicks now,
289 uint32_t id,
290 const mojo::AnimationGroup& transport_group) {
291 if (!IsAnimationGroupValid(transport_group))
292 return nullptr;
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]);
303 return group.Pass();
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);
331 continue;
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);
341 ++i;
343 return sequences_.empty();
346 ScheduledAnimationGroup::ScheduledAnimationGroup(ServerView* view,
347 uint32_t id,
348 base::TimeTicks time_scheduled)
349 : view_(view), id_(id), time_scheduled_(time_scheduled) {
352 } // namespace view_manager