LayerAnimation.cpp [plain text]
#include "config.h"
#include "LayerAnimation.h"
#include "IdentityTransformOperation.h"
#include "LayerCompositingThread.h"
#include "TransformationMatrix.h"
#include "UnitBezier.h"
#include <algorithm>
namespace WebCore {
using namespace std;
static inline double solveEpsilon(double duration)
{
return 1.0 / (200.0 * duration);
}
static inline double solveCubicBezierFunction(double p1x, double p1y, double p2x, double p2y, double t, double duration)
{
UnitBezier bezier(p1x, p1y, p2x, p2y);
return bezier.solve(t, solveEpsilon(duration));
}
static inline double solveStepsFunction(int numSteps, bool stepAtStart, double t)
{
if (stepAtStart)
return min(1.0, (floor(numSteps * t) + 1) / numSteps);
return floor(numSteps * t) / numSteps;
}
static const TimingFunction* timingFunctionForAnimationValue(const AnimationValue* animValue, const LayerAnimation* anim)
{
if (animValue->timingFunction())
return animValue->timingFunction();
if (anim->timingFunction())
return anim->timingFunction();
return CubicBezierTimingFunction::defaultTimingFunction();
}
static double progress(double elapsedTime, const LayerAnimation* layerAnimation, double scale, double offset, const TimingFunction* tf)
{
double dur = layerAnimation->duration();
if (layerAnimation->iterationCount() > 0)
dur *= layerAnimation->iterationCount();
if (!layerAnimation->duration())
return 1.0;
if (layerAnimation->iterationCount() > 0 && elapsedTime >= dur)
return (layerAnimation->iterationCount() % 2) ? 1.0 : 0.0;
double fractionalTime = elapsedTime / layerAnimation->duration();
int integralTime = static_cast<int>(fractionalTime);
fractionalTime -= integralTime;
if ((layerAnimation->direction() == Animation::AnimationDirectionAlternate) && (integralTime & 1))
fractionalTime = 1 - fractionalTime;
if (scale != 1 || offset)
fractionalTime = (fractionalTime - offset) * scale;
if (!tf)
tf = layerAnimation->timingFunction();
if (tf->isCubicBezierTimingFunction()) {
const CubicBezierTimingFunction* ctf = static_cast<const CubicBezierTimingFunction*>(tf);
return solveCubicBezierFunction(ctf->x1(), ctf->y1(), ctf->x2(), ctf->y2(), fractionalTime, layerAnimation->duration());
}
if (tf->isStepsTimingFunction()) {
const StepsTimingFunction* stf = static_cast<const StepsTimingFunction*>(tf);
return solveStepsFunction(stf->numberOfSteps(), stf->stepAtStart(), fractionalTime);
}
return fractionalTime;
}
static void fetchIntervalEndpoints(double elapsedTime, const LayerAnimation* layerAnimation, const AnimationValue*& fromValue, const AnimationValue*& toValue, double& prog)
{
if (layerAnimation->duration() && layerAnimation->iterationCount() != Animation::IterationCountInfinite)
elapsedTime = min(elapsedTime, layerAnimation->duration() * layerAnimation->iterationCount());
double fractionalTime = layerAnimation->duration() ? (elapsedTime / layerAnimation->duration()) : 1;
if (fractionalTime < 0)
fractionalTime = 0;
int iteration = static_cast<int>(fractionalTime);
if (layerAnimation->iterationCount() != Animation::IterationCountInfinite)
iteration = min(iteration, layerAnimation->iterationCount() - 1);
fractionalTime -= iteration;
bool reversing = (layerAnimation->direction() == Animation::AnimationDirectionAlternate) && (iteration & 1);
if (reversing)
fractionalTime = 1 - fractionalTime;
size_t numKeyframes = layerAnimation->valueCount();
if (!numKeyframes)
return;
ASSERT(!layerAnimation->valueAt(0)->keyTime());
ASSERT(layerAnimation->valueAt(layerAnimation->valueCount() - 1)->keyTime() == 1);
int prevIndex = -1;
int nextIndex = -1;
for (size_t i = 0; i < numKeyframes; ++i) {
const AnimationValue* currKeyframe = layerAnimation->valueAt(i);
if (fractionalTime < currKeyframe->keyTime()) {
nextIndex = i;
break;
}
prevIndex = i;
}
double scale = 1;
double offset = 0;
if (prevIndex == -1)
prevIndex = 0;
if (nextIndex == -1)
nextIndex = layerAnimation->valueCount() - 1;
const AnimationValue* prevKeyframe = layerAnimation->valueAt(prevIndex);
const AnimationValue* nextKeyframe = layerAnimation->valueAt(nextIndex);
fromValue = prevKeyframe;
toValue = nextKeyframe;
offset = prevKeyframe->keyTime();
scale = 1.0 / (nextKeyframe->keyTime() - prevKeyframe->keyTime());
const TimingFunction* timingFunction = timingFunctionForAnimationValue(prevKeyframe, layerAnimation);
prog = progress(elapsedTime, layerAnimation, scale, offset, timingFunction);
}
void LayerAnimation::apply(LayerCompositingThread* layer, double elapsedTime)
{
const AnimationValue* from = 0;
const AnimationValue* to = 0;
double progress = 0.0;
fetchIntervalEndpoints(elapsedTime, this, from, to, progress);
switch (property()) {
case AnimatedPropertyWebkitTransform:
layer->setTransform(blendTransform(static_cast<const TransformAnimationValue*>(from)->value(), static_cast<const TransformAnimationValue*>(to)->value(), progress));
break;
case AnimatedPropertyOpacity:
layer->setOpacity(blendOpacity(static_cast<const FloatAnimationValue*>(from)->value(), static_cast<const FloatAnimationValue*>(to)->value(), progress));
break;
case AnimatedPropertyBackgroundColor:
case AnimatedPropertyWebkitFilter:
case AnimatedPropertyInvalid:
ASSERT_NOT_REACHED();
break;
}
}
TransformationMatrix LayerAnimation::blendTransform(const TransformOperations* from, const TransformOperations* to, double progress) const
{
TransformationMatrix t;
if (m_transformFunctionListValid) {
Vector<TransformOperation*> result;
Vector<RefPtr<TransformOperation> > owned;
unsigned fromSize = from->operations().size();
unsigned toSize = to->operations().size();
unsigned size = max(fromSize, toSize);
for (unsigned i = 0; i < size; i++) {
TransformOperation* fromOp = (i < fromSize) ? from->operations()[i].get() : 0;
TransformOperation* toOp = (i < toSize) ? to->operations()[i].get() : 0;
RefPtr<TransformOperation> blendedOp = toOp ? toOp->blend(fromOp, progress) : (fromOp ? fromOp->blend(0, progress, true) : PassRefPtr<TransformOperation>(0));
if (blendedOp) {
result.append(blendedOp.get());
owned.append(blendedOp);
} else {
RefPtr<TransformOperation> identityOp = IdentityTransformOperation::create();
owned.append(identityOp);
if (progress > 0.5)
result.append(toOp ? toOp : identityOp.get());
else
result.append(fromOp ? fromOp : identityOp.get());
}
}
IntSize sz = boxSize();
for (unsigned i = 0; i < result.size(); ++i)
result[i]->apply(t, sz);
} else {
TransformationMatrix fromT;
from->apply(boxSize(), fromT);
to->apply(boxSize(), t);
t.blend(fromT, progress);
}
return t;
}
float LayerAnimation::blendOpacity(float from, float to, double progress) const
{
float opacity = from + (to - from) * progress;
return max(0.0f, min(opacity, 1.0f));
}
void LayerAnimation::validateTransformLists()
{
m_transformFunctionListValid = false;
if (m_values.size() < 2 || property() != AnimatedPropertyWebkitTransform)
return;
size_t numKeyframes = m_values.size();
size_t firstNonEmptyTransformKeyframeIndex = numKeyframes;
for (size_t i = 0; i < numKeyframes; ++i) {
const TransformAnimationValue* currentKeyframe = static_cast<const TransformAnimationValue*>(m_values.at(i));
if (currentKeyframe->value()->size()) {
firstNonEmptyTransformKeyframeIndex = i;
break;
}
}
if (firstNonEmptyTransformKeyframeIndex == numKeyframes)
return;
const TransformOperations* firstVal = static_cast<const TransformAnimationValue*>(m_values.at(firstNonEmptyTransformKeyframeIndex))->value();
for (size_t i = firstNonEmptyTransformKeyframeIndex + 1; i < numKeyframes; ++i) {
const TransformAnimationValue* currentKeyframe = static_cast<const TransformAnimationValue*>(m_values.at(i));
const TransformOperations* val = currentKeyframe->value();
if (val->operations().isEmpty())
continue;
if (firstVal->operations().size() != val->operations().size())
return;
for (size_t j = 0; j < firstVal->operations().size(); ++j) {
if (!firstVal->operations()[j]->isSameType(*val->operations()[j]))
return;
}
}
m_transformFunctionListValid = true;
}
}