diff options
Diffstat (limited to 'chromium/components/mus/ws/scheduled_animation_group.cc')
-rw-r--r-- | chromium/components/mus/ws/scheduled_animation_group.cc | 356 |
1 files changed, 356 insertions, 0 deletions
diff --git a/chromium/components/mus/ws/scheduled_animation_group.cc b/chromium/components/mus/ws/scheduled_animation_group.cc new file mode 100644 index 00000000000..d76b7895597 --- /dev/null +++ b/chromium/components/mus/ws/scheduled_animation_group.cc @@ -0,0 +1,356 @@ +// Copyright 2014 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 "components/mus/ws/scheduled_animation_group.h" + +#include <set> + +#include "components/mus/ws/server_window.h" + +using mus::mojom::AnimationProperty; + +namespace mus { +namespace ws { +namespace { + +using Sequences = std::vector<ScheduledAnimationSequence>; + +// Gets the value of |property| from |window| into |value|. +void GetValueFromWindow(const ServerWindow* window, + AnimationProperty property, + ScheduledAnimationValue* value) { + switch (property) { + case AnimationProperty::NONE: + NOTREACHED(); + break; + case AnimationProperty::OPACITY: + value->float_value = window->opacity(); + break; + case AnimationProperty::TRANSFORM: + value->transform = window->transform(); + break; + } +} + +// Sets the value of |property| from |value| into |window|. +void SetWindowPropertyFromValue(ServerWindow* window, + AnimationProperty property, + const ScheduledAnimationValue& value) { + switch (property) { + case AnimationProperty::NONE: + break; + case AnimationProperty::OPACITY: + window->SetOpacity(value.float_value); + break; + case AnimationProperty::TRANSFORM: + window->SetTransform(value.transform); + break; + } +} + +// Sets the value of |property| into |window| between two points. +void SetWindowPropertyFromValueBetween(ServerWindow* window, + AnimationProperty property, + double value, + gfx::Tween::Type tween_type, + const ScheduledAnimationValue& start, + const ScheduledAnimationValue& target) { + const double tween_value = gfx::Tween::CalculateValue(tween_type, value); + switch (property) { + case AnimationProperty::NONE: + break; + case AnimationProperty::OPACITY: + window->SetOpacity(gfx::Tween::FloatValueBetween( + tween_value, start.float_value, target.float_value)); + break; + case AnimationProperty::TRANSFORM: + window->SetTransform(gfx::Tween::TransformValueBetween( + tween_value, start.transform, target.transform)); + break; + } +} + +// TODO(mfomitchev): Use struct traits for this? +gfx::Tween::Type AnimationTypeToTweenType(mojom::AnimationTweenType type) { + switch (type) { + case mojom::AnimationTweenType::LINEAR: + return gfx::Tween::LINEAR; + case mojom::AnimationTweenType::EASE_IN: + return gfx::Tween::EASE_IN; + case mojom::AnimationTweenType::EASE_OUT: + return gfx::Tween::EASE_OUT; + case mojom::AnimationTweenType::EASE_IN_OUT: + return gfx::Tween::EASE_IN_OUT; + } + return gfx::Tween::LINEAR; +} + +void ConvertToScheduledValue(const mojom::AnimationValue& transport_value, + ScheduledAnimationValue* value) { + value->float_value = transport_value.float_value; + value->transform = transport_value.transform; +} + +void ConvertToScheduledElement(const mojom::AnimationElement& transport_element, + ScheduledAnimationElement* element) { + element->property = transport_element.property; + element->duration = + base::TimeDelta::FromMicroseconds(transport_element.duration); + element->tween_type = AnimationTypeToTweenType(transport_element.tween_type); + if (transport_element.property != AnimationProperty::NONE) { + if (transport_element.start_value.get()) { + element->is_start_valid = true; + ConvertToScheduledValue(*transport_element.start_value, + &(element->start_value)); + } else { + element->is_start_valid = false; + } + ConvertToScheduledValue(*transport_element.target_value, + &(element->target_value)); + } +} + +bool IsAnimationValueValid(AnimationProperty property, + const mojom::AnimationValue& value) { + bool result; + switch (property) { + case AnimationProperty::NONE: + NOTREACHED(); + return false; + case AnimationProperty::OPACITY: + result = value.float_value >= 0.f && value.float_value <= 1.f; + DVLOG_IF(1, !result) << "Invalid AnimationValue for opacity: " + << value.float_value; + return result; + case AnimationProperty::TRANSFORM: + return true; + } + return false; +} + +bool IsAnimationElementValid(const mojom::AnimationElement& element) { + if (element.property == AnimationProperty::NONE) + return true; // None is a pause and doesn't need any values. + if (element.start_value.get() && + !IsAnimationValueValid(element.property, *element.start_value)) + return false; + // For all other properties we require a target. + return element.target_value.get() && + IsAnimationValueValid(element.property, *element.target_value); +} + +bool IsAnimationSequenceValid(const mojom::AnimationSequence& sequence) { + if (sequence.elements.size() == 0) { + DVLOG(1) << "Empty or null AnimationSequence - invalid."; + return false; + } + + for (size_t i = 0; i < sequence.elements.size(); ++i) { + if (!IsAnimationElementValid(*sequence.elements[i])) + return false; + } + return true; +} + +bool IsAnimationGroupValid(const mojom::AnimationGroup& transport_group) { + if (transport_group.sequences.size() == 0) { + DVLOG(1) << "Empty or null AnimationGroup - invalid; window_id=" + << transport_group.window_id; + return false; + } + for (size_t i = 0; i < transport_group.sequences.size(); ++i) { + if (!IsAnimationSequenceValid(*transport_group.sequences[i])) + return false; + } + return true; +} + +// If the start value for |element| isn't valid, the value for the property +// is obtained from |window| and placed into |element|. +void GetStartValueFromWindowIfNecessary(const ServerWindow* window, + ScheduledAnimationElement* element) { + if (element->property != AnimationProperty::NONE && + !element->is_start_valid) { + GetValueFromWindow(window, element->property, &(element->start_value)); + } +} + +void GetScheduledAnimationProperties(const Sequences& sequences, + std::set<AnimationProperty>* properties) { + for (const ScheduledAnimationSequence& sequence : sequences) { + for (const ScheduledAnimationElement& element : sequence.elements) + properties->insert(element.property); + } +} + +// Set |window|'s specified |property| to the value resulting from running all +// the |sequences|. +void SetPropertyToTargetProperty(ServerWindow* window, + mojom::AnimationProperty property, + const Sequences& sequences) { + // NOTE: this doesn't deal with |cycle_count| quite right, but I'm honestly + // not sure we really want to support the same property in multiple sequences + // animating at once so I'm not dealing. + base::TimeDelta max_end_duration; + std::unique_ptr<ScheduledAnimationValue> value; + for (const ScheduledAnimationSequence& sequence : sequences) { + base::TimeDelta duration; + for (const ScheduledAnimationElement& element : sequence.elements) { + if (element.property != property) + continue; + + duration += element.duration; + if (duration > max_end_duration) { + max_end_duration = duration; + value.reset(new ScheduledAnimationValue(element.target_value)); + } + } + } + if (value.get()) + SetWindowPropertyFromValue(window, property, *value); +} + +void ConvertSequenceToScheduled( + const mojom::AnimationSequence& transport_sequence, + base::TimeTicks now, + ScheduledAnimationSequence* sequence) { + sequence->run_until_stopped = transport_sequence.cycle_count == 0u; + sequence->cycle_count = transport_sequence.cycle_count; + DCHECK_NE(0u, transport_sequence.elements.size()); + sequence->elements.resize(transport_sequence.elements.size()); + + base::TimeTicks element_start_time = now; + for (size_t i = 0; i < transport_sequence.elements.size(); ++i) { + ConvertToScheduledElement(*(transport_sequence.elements[i].get()), + &(sequence->elements[i])); + sequence->elements[i].start_time = element_start_time; + sequence->duration += sequence->elements[i].duration; + element_start_time += sequence->elements[i].duration; + } +} + +bool AdvanceSequence(ServerWindow* window, + ScheduledAnimationSequence* sequence, + base::TimeTicks now) { + ScheduledAnimationElement* element = + &(sequence->elements[sequence->current_index]); + while (element->start_time + element->duration < now) { + SetWindowPropertyFromValue(window, element->property, + element->target_value); + if (++sequence->current_index == sequence->elements.size()) { + if (!sequence->run_until_stopped && --sequence->cycle_count == 0) { + return false; + } + + sequence->current_index = 0; + } + sequence->elements[sequence->current_index].start_time = + element->start_time + element->duration; + element = &(sequence->elements[sequence->current_index]); + GetStartValueFromWindowIfNecessary(window, element); + + // It's possible for the delta between now and |last_tick_time_| to be very + // big (could happen if machine sleeps and is woken up much later). Normally + // the repeat count is smallish, so we don't bother optimizing it. OTOH if + // a sequence repeats forever we optimize it lest we get stuck in this loop + // for a very long time. + if (sequence->run_until_stopped && sequence->current_index == 0) { + element->start_time = + now - base::TimeDelta::FromMicroseconds( + (now - element->start_time).InMicroseconds() % + sequence->duration.InMicroseconds()); + } + } + return true; +} + +} // namespace + +ScheduledAnimationValue::ScheduledAnimationValue() : float_value(0) {} +ScheduledAnimationValue::~ScheduledAnimationValue() {} + +ScheduledAnimationElement::ScheduledAnimationElement() + : property(AnimationProperty::OPACITY), + tween_type(gfx::Tween::EASE_IN), + is_start_valid(false) {} +ScheduledAnimationElement::ScheduledAnimationElement( + const ScheduledAnimationElement& other) = default; +ScheduledAnimationElement::~ScheduledAnimationElement() {} + +ScheduledAnimationSequence::ScheduledAnimationSequence() + : run_until_stopped(false), cycle_count(0), current_index(0u) {} +ScheduledAnimationSequence::ScheduledAnimationSequence( + const ScheduledAnimationSequence& other) = default; +ScheduledAnimationSequence::~ScheduledAnimationSequence() {} + +ScheduledAnimationGroup::~ScheduledAnimationGroup() {} + +// static +std::unique_ptr<ScheduledAnimationGroup> ScheduledAnimationGroup::Create( + ServerWindow* window, + base::TimeTicks now, + uint32_t id, + const mojom::AnimationGroup& transport_group) { + if (!IsAnimationGroupValid(transport_group)) + return nullptr; + + std::unique_ptr<ScheduledAnimationGroup> group( + new ScheduledAnimationGroup(window, id, now)); + group->sequences_.resize(transport_group.sequences.size()); + for (size_t i = 0; i < transport_group.sequences.size(); ++i) { + const mojom::AnimationSequence& transport_sequence( + *(transport_group.sequences[i])); + DCHECK_NE(0u, transport_sequence.elements.size()); + ConvertSequenceToScheduled(transport_sequence, now, &group->sequences_[i]); + } + return group; +} + +void ScheduledAnimationGroup::ObtainStartValues() { + for (ScheduledAnimationSequence& sequence : sequences_) + GetStartValueFromWindowIfNecessary(window_, &(sequence.elements[0])); +} + +void ScheduledAnimationGroup::SetValuesToTargetValuesForPropertiesNotIn( + const ScheduledAnimationGroup& other) { + std::set<AnimationProperty> our_properties; + GetScheduledAnimationProperties(sequences_, &our_properties); + + std::set<AnimationProperty> other_properties; + GetScheduledAnimationProperties(other.sequences_, &other_properties); + + for (AnimationProperty property : our_properties) { + if (other_properties.count(property) == 0 && + property != AnimationProperty::NONE) { + SetPropertyToTargetProperty(window_, property, sequences_); + } + } +} + +bool ScheduledAnimationGroup::Tick(base::TimeTicks time) { + for (Sequences::iterator i = sequences_.begin(); i != sequences_.end();) { + if (!AdvanceSequence(window_, &(*i), time)) { + i = sequences_.erase(i); + continue; + } + const ScheduledAnimationElement& active_element( + i->elements[i->current_index]); + const double percent = + (time - active_element.start_time).InMillisecondsF() / + active_element.duration.InMillisecondsF(); + SetWindowPropertyFromValueBetween( + window_, active_element.property, percent, active_element.tween_type, + active_element.start_value, active_element.target_value); + ++i; + } + return sequences_.empty(); +} + +ScheduledAnimationGroup::ScheduledAnimationGroup(ServerWindow* window, + uint32_t id, + base::TimeTicks time_scheduled) + : window_(window), id_(id), time_scheduled_(time_scheduled) {} + +} // namespace ws +} // namespace mus |