| // Copyright 2012 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 "cc/active_animation.h" |
| |
| #include <cmath> |
| |
| #include "base/debug/trace_event.h" |
| #include "base/string_util.h" |
| #include "cc/animation_curve.h" |
| |
| namespace { |
| |
| // This should match the RunState enum. |
| static const char* const s_runStateNames[] = { |
| "WaitingForNextTick", |
| "WaitingForTargetAvailability", |
| "WaitingForStartTime", |
| "WaitingForDeletion", |
| "Running", |
| "Paused", |
| "Finished", |
| "Aborted" |
| }; |
| |
| COMPILE_ASSERT(static_cast<int>(cc::ActiveAnimation::RunStateEnumSize) == arraysize(s_runStateNames), RunState_names_match_enum); |
| |
| // This should match the TargetProperty enum. |
| static const char* const s_targetPropertyNames[] = { |
| "Transform", |
| "Opacity" |
| }; |
| |
| COMPILE_ASSERT(static_cast<int>(cc::ActiveAnimation::TargetPropertyEnumSize) == arraysize(s_targetPropertyNames), TargetProperty_names_match_enum); |
| |
| } // namespace |
| |
| namespace cc { |
| |
| scoped_ptr<ActiveAnimation> ActiveAnimation::create(scoped_ptr<AnimationCurve> curve, int animationId, int groupId, TargetProperty targetProperty) |
| { |
| return make_scoped_ptr(new ActiveAnimation(curve.Pass(), animationId, groupId, targetProperty)); |
| } |
| |
| ActiveAnimation::ActiveAnimation(scoped_ptr<AnimationCurve> curve, int animationId, int groupId, TargetProperty targetProperty) |
| : m_curve(curve.Pass()) |
| , m_id(animationId) |
| , m_group(groupId) |
| , m_targetProperty(targetProperty) |
| , m_runState(WaitingForTargetAvailability) |
| , m_iterations(1) |
| , m_startTime(0) |
| , m_alternatesDirection(false) |
| , m_timeOffset(0) |
| , m_needsSynchronizedStartTime(false) |
| , m_suspended(false) |
| , m_pauseTime(0) |
| , m_totalPausedTime(0) |
| , m_isControllingInstance(false) |
| { |
| } |
| |
| ActiveAnimation::~ActiveAnimation() |
| { |
| if (m_runState == Running || m_runState == Paused) |
| setRunState(Aborted, 0); |
| } |
| |
| void ActiveAnimation::setRunState(RunState runState, double monotonicTime) |
| { |
| if (m_suspended) |
| return; |
| |
| char nameBuffer[256]; |
| base::snprintf(nameBuffer, sizeof(nameBuffer), "%s-%d%s", s_targetPropertyNames[m_targetProperty], m_group, m_isControllingInstance ? "(impl)" : ""); |
| |
| bool isWaitingToStart = m_runState == WaitingForNextTick |
| || m_runState == WaitingForTargetAvailability |
| || m_runState == WaitingForStartTime; |
| |
| if (isWaitingToStart && runState == Running) |
| TRACE_EVENT_ASYNC_BEGIN1("cc", "ActiveAnimation", this, "Name", TRACE_STR_COPY(nameBuffer)); |
| |
| bool wasFinished = isFinished(); |
| |
| const char* oldRunStateName = s_runStateNames[m_runState]; |
| |
| if (runState == Running && m_runState == Paused) |
| m_totalPausedTime += monotonicTime - m_pauseTime; |
| else if (runState == Paused) |
| m_pauseTime = monotonicTime; |
| m_runState = runState; |
| |
| const char* newRunStateName = s_runStateNames[runState]; |
| |
| if (!wasFinished && isFinished()) |
| TRACE_EVENT_ASYNC_END0("cc", "ActiveAnimation", this); |
| |
| char stateBuffer[256]; |
| base::snprintf(stateBuffer, sizeof(stateBuffer), "%s->%s", oldRunStateName, newRunStateName); |
| |
| TRACE_EVENT_INSTANT2("cc", "LayerAnimationController::setRunState", "Name", TRACE_STR_COPY(nameBuffer), "State", TRACE_STR_COPY(stateBuffer)); |
| } |
| |
| void ActiveAnimation::suspend(double monotonicTime) |
| { |
| setRunState(Paused, monotonicTime); |
| m_suspended = true; |
| } |
| |
| void ActiveAnimation::resume(double monotonicTime) |
| { |
| m_suspended = false; |
| setRunState(Running, monotonicTime); |
| } |
| |
| bool ActiveAnimation::isFinishedAt(double monotonicTime) const |
| { |
| if (isFinished()) |
| return true; |
| |
| if (m_needsSynchronizedStartTime) |
| return false; |
| |
| return m_runState == Running |
| && m_iterations >= 0 |
| && m_iterations * m_curve->duration() <= monotonicTime - startTime() - m_totalPausedTime; |
| } |
| |
| double ActiveAnimation::trimTimeToCurrentIteration(double monotonicTime) const |
| { |
| double trimmed = monotonicTime + m_timeOffset; |
| |
| // If we're paused, time is 'stuck' at the pause time. |
| if (m_runState == Paused) |
| trimmed = m_pauseTime; |
| |
| // Returned time should always be relative to the start time and should subtract |
| // all time spent paused. |
| trimmed -= m_startTime + m_totalPausedTime; |
| |
| // Zero is always the start of the animation. |
| if (trimmed <= 0) |
| return 0; |
| |
| // Always return zero if we have no iterations. |
| if (!m_iterations) |
| return 0; |
| |
| // Don't attempt to trim if we have no duration. |
| if (m_curve->duration() <= 0) |
| return 0; |
| |
| // If less than an iteration duration, just return trimmed. |
| if (trimmed < m_curve->duration()) |
| return trimmed; |
| |
| // If greater than or equal to the total duration, return iteration duration. |
| if (m_iterations >= 0 && trimmed >= m_curve->duration() * m_iterations) { |
| if (m_alternatesDirection && !(m_iterations % 2)) |
| return 0; |
| return m_curve->duration(); |
| } |
| |
| // We need to know the current iteration if we're alternating. |
| int iteration = static_cast<int>(trimmed / m_curve->duration()); |
| |
| // Calculate x where trimmed = x + n * m_curve->duration() for some positive integer n. |
| trimmed = fmod(trimmed, m_curve->duration()); |
| |
| // If we're alternating and on an odd iteration, reverse the direction. |
| if (m_alternatesDirection && iteration % 2 == 1) |
| return m_curve->duration() - trimmed; |
| |
| return trimmed; |
| } |
| |
| scoped_ptr<ActiveAnimation> ActiveAnimation::clone(InstanceType instanceType) const |
| { |
| return cloneAndInitialize(instanceType, m_runState, m_startTime); |
| } |
| |
| scoped_ptr<ActiveAnimation> ActiveAnimation::cloneAndInitialize(InstanceType instanceType, RunState initialRunState, double startTime) const |
| { |
| scoped_ptr<ActiveAnimation> toReturn(new ActiveAnimation(m_curve->clone(), m_id, m_group, m_targetProperty)); |
| toReturn->m_runState = initialRunState; |
| toReturn->m_iterations = m_iterations; |
| toReturn->m_startTime = startTime; |
| toReturn->m_pauseTime = m_pauseTime; |
| toReturn->m_totalPausedTime = m_totalPausedTime; |
| toReturn->m_timeOffset = m_timeOffset; |
| toReturn->m_alternatesDirection = m_alternatesDirection; |
| toReturn->m_isControllingInstance = instanceType == ControllingInstance; |
| return toReturn.Pass(); |
| } |
| |
| void ActiveAnimation::pushPropertiesTo(ActiveAnimation* other) const |
| { |
| // Currently, we only push changes due to pausing and resuming animations on the main thread. |
| if (m_runState == ActiveAnimation::Paused || other->m_runState == ActiveAnimation::Paused) { |
| other->m_runState = m_runState; |
| other->m_pauseTime = m_pauseTime; |
| other->m_totalPausedTime = m_totalPausedTime; |
| } |
| } |
| |
| } // namespace cc |