blob: 6b223596ab25affeb7533902e5a66b843c1a27bd [file] [log] [blame]
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "config.h"
#include "AndroidAnimation.h"
#if USE(ACCELERATED_COMPOSITING)
#include "Animation.h"
#include "GraphicsLayerAndroid.h"
#include "Timer.h"
#include "TimingFunction.h"
#include "TranslateTransformOperation.h"
#include "UnitBezier.h"
#include <wtf/CurrentTime.h>
#include <cutils/log.h>
#include <wtf/text/CString.h>
#undef XLOGC
#define XLOGC(...) android_printLog(ANDROID_LOG_DEBUG, "AndroidAnimation", __VA_ARGS__)
#ifdef DEBUG
#undef XLOG
#define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "AndroidAnimation", __VA_ARGS__)
#else
#undef XLOG
#define XLOG(...)
#endif // DEBUG
namespace WebCore {
static int gUniqueId;
static long gDebugAndroidAnimationInstances;
long AndroidAnimation::instancesCount()
{
return gDebugAndroidAnimationInstances;
}
AndroidAnimation::AndroidAnimation(AnimatedPropertyID type,
const Animation* animation,
KeyframeValueList* operations,
double beginTime)
: m_beginTime(beginTime)
, m_duration(animation->duration())
, m_fillsBackwards(animation->fillsBackwards())
, m_fillsForwards(animation->fillsForwards())
, m_iterationCount(animation->iterationCount())
, m_direction(animation->direction())
, m_timingFunction(animation->timingFunction())
, m_type(type)
, m_operations(operations)
, m_uniqueId(++gUniqueId)
, m_hasFinished(false)
{
ASSERT(m_timingFunction);
gDebugAndroidAnimationInstances++;
}
AndroidAnimation::~AndroidAnimation()
{
gDebugAndroidAnimationInstances--;
}
void AndroidAnimation::suggestBeginTime(double time)
{
if (m_beginTime <= 0.000001) // overflow or not yet set
m_beginTime = time;
}
double AndroidAnimation::elapsedTime(double time)
{
double elapsedTime = (m_beginTime < 0.000001) ? 0 : time - m_beginTime;
if (m_duration <= 0)
m_duration = 0.000001;
if (elapsedTime < 0) // animation not yet started.
return 0;
return elapsedTime;
}
bool AndroidAnimation::checkIterationsAndProgress(double time, float* finalProgress)
{
double progress = elapsedTime(time);
double dur = m_duration;
if (m_iterationCount > 0)
dur *= m_iterationCount;
if (m_duration <= 0)
return false;
// If not infinite, return false if we are done
if (m_iterationCount > 0 && progress > dur) {
*finalProgress = 1.0;
if (!m_hasFinished) {
// first time past duration, continue with progress 1.0 so the
// element's final position lines up with it's last keyframe
m_hasFinished = true;
return true;
}
return false;
}
double fractionalTime = progress / m_duration;
int integralTime = static_cast<int>(fractionalTime);
fractionalTime -= integralTime;
if ((m_direction == Animation::AnimationDirectionAlternate) && (integralTime & 1))
fractionalTime = 1 - fractionalTime;
*finalProgress = fractionalTime;
return true;
}
double AndroidAnimation::applyTimingFunction(float from, float to, double progress,
const TimingFunction* tf)
{
double fractionalTime = progress;
double offset = from;
double scale = 1.0 / (to - from);
if (scale != 1 || offset)
fractionalTime = (fractionalTime - offset) * scale;
const TimingFunction* timingFunction = tf;
if (!timingFunction)
timingFunction = m_timingFunction.get();
if (timingFunction && timingFunction->isCubicBezierTimingFunction()) {
const CubicBezierTimingFunction* bezierFunction = static_cast<const CubicBezierTimingFunction*>(timingFunction);
UnitBezier bezier(bezierFunction->x1(),
bezierFunction->y1(),
bezierFunction->x2(),
bezierFunction->y2());
if (m_duration > 0)
fractionalTime = bezier.solve(fractionalTime, 1.0f / (200.0f * m_duration));
} else if (timingFunction && timingFunction->isStepsTimingFunction()) {
const StepsTimingFunction* stepFunction = static_cast<const StepsTimingFunction*>(timingFunction);
if (stepFunction->stepAtStart()) {
fractionalTime = (floor(stepFunction->numberOfSteps() * fractionalTime) + 1) / stepFunction->numberOfSteps();
if (fractionalTime > 1.0)
fractionalTime = 1.0;
} else {
fractionalTime = floor(stepFunction->numberOfSteps() * fractionalTime) / stepFunction->numberOfSteps();
}
}
return fractionalTime;
}
bool AndroidAnimation::evaluate(LayerAndroid* layer, double time)
{
float progress;
if (!checkIterationsAndProgress(time, &progress)
&& !(m_fillsBackwards || m_fillsForwards))
return false;
if (progress < 0) {
// The animation hasn't started yet
if (m_fillsBackwards || m_beginTime <= 0.000001) {
// in this case we want to apply the initial keyframe to the layer
applyForProgress(layer, 0);
}
// we still want to be evaluated until we get progress > 0
return true;
}
if (progress > 1) {
if (!m_fillsForwards)
return false;
progress = 1;
}
if (!m_operations->size())
return false;
applyForProgress(layer, progress);
return true;
}
PassRefPtr<AndroidOpacityAnimation> AndroidOpacityAnimation::create(
const Animation* animation,
KeyframeValueList* operations,
double beginTime)
{
return adoptRef(new AndroidOpacityAnimation(animation, operations,
beginTime));
}
AndroidOpacityAnimation::AndroidOpacityAnimation(const Animation* animation,
KeyframeValueList* operations,
double beginTime)
: AndroidAnimation(AnimatedPropertyOpacity, animation, operations, beginTime)
{
}
void AndroidAnimation::pickValues(double progress, int* start, int* end)
{
float distance = -1;
unsigned int foundAt = 0;
for (unsigned int i = 0; i < m_operations->size(); i++) {
const AnimationValue* value = m_operations->at(i);
float key = value->keyTime();
float d = progress - key;
if (distance == -1 || (d >= 0 && d < distance && i + 1 < m_operations->size())) {
distance = d;
foundAt = i;
}
}
*start = foundAt;
if (foundAt + 1 < m_operations->size())
*end = foundAt + 1;
else
*end = foundAt;
}
void AndroidOpacityAnimation::applyForProgress(LayerAndroid* layer, float progress)
{
// First, we need to get the from and to values
int from, to;
pickValues(progress, &from, &to);
FloatAnimationValue* fromValue = (FloatAnimationValue*) m_operations->at(from);
FloatAnimationValue* toValue = (FloatAnimationValue*) m_operations->at(to);
XLOG("[layer %d] opacity fromValue %x, key %.2f, toValue %x, key %.2f for progress %.2f",
layer->uniqueId(),
fromValue, fromValue->keyTime(),
toValue, toValue->keyTime(), progress);
// We now have the correct two values to work with, let's compute the
// progress value
const TimingFunction* timingFunction = fromValue->timingFunction();
progress = applyTimingFunction(fromValue->keyTime(), toValue->keyTime(),
progress, timingFunction);
float value = fromValue->value() + ((toValue->value() - fromValue->value()) * progress);
layer->setOpacity(value);
}
PassRefPtr<AndroidTransformAnimation> AndroidTransformAnimation::create(
const Animation* animation,
KeyframeValueList* operations,
double beginTime)
{
return adoptRef(new AndroidTransformAnimation(animation, operations, beginTime));
}
AndroidTransformAnimation::AndroidTransformAnimation(const Animation* animation,
KeyframeValueList* operations,
double beginTime)
: AndroidAnimation(AnimatedPropertyWebkitTransform, animation, operations, beginTime)
{
}
void AndroidTransformAnimation::applyForProgress(LayerAndroid* layer, float progress)
{
// First, we need to get the from and to values
int from, to;
pickValues(progress, &from, &to);
TransformAnimationValue* fromValue = (TransformAnimationValue*) m_operations->at(from);
TransformAnimationValue* toValue = (TransformAnimationValue*) m_operations->at(to);
XLOG("[layer %d] fromValue %x, key %.2f, toValue %x, key %.2f for progress %.2f",
layer->uniqueId(),
fromValue, fromValue->keyTime(),
toValue, toValue->keyTime(), progress);
// We now have the correct two values to work with, let's compute the
// progress value
const TimingFunction* timingFunction = fromValue->timingFunction();
float p = applyTimingFunction(fromValue->keyTime(), toValue->keyTime(),
progress, timingFunction);
XLOG("progress %.2f => %.2f from: %.2f to: %.2f", progress, p, fromValue->keyTime(),
toValue->keyTime());
progress = p;
// With both values and the progress, we also need to check out that
// the operations are compatible (i.e. we are animating the same number
// of values; if not we do a matrix blend)
TransformationMatrix transformMatrix;
bool valid = true;
unsigned int fromSize = fromValue->value()->size();
if (fromSize) {
if (toValue->value()->size() != fromSize)
valid = false;
else {
for (unsigned int j = 0; j < fromSize && valid; j++) {
if (!fromValue->value()->operations()[j]->isSameType(
*toValue->value()->operations()[j]))
valid = false;
}
}
}
IntSize size(layer->getSize().width(), layer->getSize().height());
if (valid) {
for (size_t i = 0; i < toValue->value()->size(); ++i)
toValue->value()->operations()[i]->blend(fromValue->value()->at(i),
progress)->apply(transformMatrix, size);
} else {
TransformationMatrix source;
fromValue->value()->apply(size, source);
toValue->value()->apply(size, transformMatrix);
transformMatrix.blend(source, progress);
}
// Set the final transform on the layer
layer->setTransform(transformMatrix);
}
} // namespace WebCore
#endif // USE(ACCELERATED_COMPOSITING)