blob: 61a1d036cfda52ba4ea8b71e51bb2c7b17adbcb6 [file] [log] [blame]
/*
* Copyright (C) 2019 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include "SVGAnimationAdditiveListFunction.h"
#include "SVGLengthList.h"
#include "SVGNumberList.h"
#include "SVGPointList.h"
#include "SVGTransformDistance.h"
#include "SVGTransformList.h"
namespace WebCore {
class SVGElement;
class SVGAnimationLengthListFunction : public SVGAnimationAdditiveListFunction<SVGLengthList> {
using Base = SVGAnimationAdditiveListFunction<SVGLengthList>;
public:
SVGAnimationLengthListFunction(AnimationMode animationMode, CalcMode calcMode, bool isAccumulated, bool isAdditive, SVGLengthMode lengthMode)
: Base(animationMode, calcMode, isAccumulated, isAdditive, lengthMode)
{
}
void setFromAndToValues(SVGElement*, const String& from, const String& to) override
{
m_from->parse(from);
m_to->parse(to);
}
void setToAtEndOfDurationValue(const String& toAtEndOfDuration) override
{
m_toAtEndOfDuration->parse(toAtEndOfDuration);
}
void animate(SVGElement* targetElement, float progress, unsigned repeatCount, RefPtr<SVGLengthList>& animated)
{
if (!adjustAnimatedList(m_animationMode, progress, animated))
return;
const Vector<Ref<SVGLength>>& fromItems = m_animationMode == AnimationMode::To ? animated->items() : m_from->items();
const Vector<Ref<SVGLength>>& toItems = m_to->items();
const Vector<Ref<SVGLength>>& toAtEndOfDurationItems = toAtEndOfDuration()->items();
Vector<Ref<SVGLength>>& animatedItems = animated->items();
SVGLengthMode lengthMode = animated->lengthMode();
SVGLengthContext lengthContext(targetElement);
for (unsigned i = 0; i < toItems.size(); ++i) {
SVGLengthType lengthType = (i < fromItems.size() && progress < 0.5 ? fromItems : toItems)[i]->value().lengthType();
float from = i < fromItems.size() ? fromItems[i]->value().value(lengthContext) : 0;
float to = toItems[i]->value().value(lengthContext);
float toAtEndOfDuration = i < toAtEndOfDurationItems.size() ? toAtEndOfDurationItems[i]->value().value(lengthContext) : 0;
float value = animatedItems[i]->value().value(lengthContext);
value = Base::animate(progress, repeatCount, from, to, toAtEndOfDuration, value);
animatedItems[i]->value().setValue(lengthContext, value, lengthType, lengthMode);
}
}
private:
void addFromAndToValues(SVGElement* targetElement) override
{
const Vector<Ref<SVGLength>>& fromItems = m_from->items();
const Vector<Ref<SVGLength>>& toItems = m_to->items();
if (!fromItems.size() || fromItems.size() != toItems.size())
return;
SVGLengthContext lengthContext(targetElement);
for (unsigned i = 0; i < fromItems.size(); ++i) {
const SVGLengthValue& fromValue = fromItems[i]->value();
SVGLengthValue& toValue = toItems[i]->value();
toValue.setValue(lengthContext, toValue.value(lengthContext) + fromValue.value(lengthContext));
}
}
};
class SVGAnimationNumberListFunction : public SVGAnimationAdditiveListFunction<SVGNumberList> {
public:
using Base = SVGAnimationAdditiveListFunction<SVGNumberList>;
using Base::Base;
void setFromAndToValues(SVGElement*, const String& from, const String& to) override
{
m_from->parse(from);
m_to->parse(to);
}
void setToAtEndOfDurationValue(const String& toAtEndOfDuration) override
{
m_toAtEndOfDuration->parse(toAtEndOfDuration);
}
void animate(SVGElement*, float progress, unsigned repeatCount, RefPtr<SVGNumberList>& animated)
{
if (!adjustAnimatedList(m_animationMode, progress, animated))
return;
auto& fromItems = m_animationMode == AnimationMode::To ? animated->items() : m_from->items();
auto& toItems = m_to->items();
auto& toAtEndOfDurationItems = toAtEndOfDuration()->items();
auto& animatedItems = animated->items();
for (unsigned i = 0; i < toItems.size(); ++i) {
float from = i < fromItems.size() ? fromItems[i]->value() : 0;
float to = toItems[i]->value();
float toAtEndOfDuration = i < toAtEndOfDurationItems.size() ? toAtEndOfDurationItems[i]->value() : 0;
float& value = animatedItems[i]->value();
value = Base::animate(progress, repeatCount, from, to, toAtEndOfDuration, value);
}
}
private:
void addFromAndToValues(SVGElement*) override
{
const Vector<Ref<SVGNumber>>& fromItems = m_from->items();
Vector<Ref<SVGNumber>>& toItems = m_to->items();
if (!fromItems.size() || fromItems.size() != toItems.size())
return;
for (unsigned i = 0; i < fromItems.size(); ++i)
toItems[i]->setValue(fromItems[i]->value() + toItems[i]->value());
}
};
class SVGAnimationPointListFunction : public SVGAnimationAdditiveListFunction<SVGPointList> {
public:
using Base = SVGAnimationAdditiveListFunction<SVGPointList>;
using Base::Base;
void setFromAndToValues(SVGElement*, const String& from, const String& to) override
{
m_from->parse(from);
m_to->parse(to);
}
void setToAtEndOfDurationValue(const String& toAtEndOfDuration) override
{
m_toAtEndOfDuration->parse(toAtEndOfDuration);
}
void animate(SVGElement*, float progress, unsigned repeatCount, RefPtr<SVGPointList>& animated)
{
if (!adjustAnimatedList(m_animationMode, progress, animated))
return;
auto& fromItems = m_animationMode == AnimationMode::To ? animated->items() : m_from->items();
auto& toItems = m_to->items();
auto& toAtEndOfDurationItems = toAtEndOfDuration()->items();
auto& animatedItems = animated->items();
for (unsigned i = 0; i < toItems.size(); ++i) {
FloatPoint from = i < fromItems.size() ? fromItems[i]->value() : FloatPoint();
FloatPoint to = toItems[i]->value();
FloatPoint toAtEndOfDuration = i < toAtEndOfDurationItems.size() ? toAtEndOfDurationItems[i]->value() : FloatPoint();
FloatPoint& animated = animatedItems[i]->value();
float animatedX = Base::animate(progress, repeatCount, from.x(), to.x(), toAtEndOfDuration.x(), animated.x());
float animatedY = Base::animate(progress, repeatCount, from.y(), to.y(), toAtEndOfDuration.y(), animated.y());
animated = { animatedX, animatedY };
}
}
private:
void addFromAndToValues(SVGElement*) override
{
const Vector<Ref<SVGPoint>>& fromItems = m_from->items();
Vector<Ref<SVGPoint>>& toItems = m_to->items();
if (!fromItems.size() || fromItems.size() != toItems.size())
return;
for (unsigned i = 0; i < fromItems.size(); ++i)
toItems[i]->setValue(fromItems[i]->value() + toItems[i]->value());
}
};
class SVGAnimationTransformListFunction : public SVGAnimationAdditiveListFunction<SVGTransformList> {
public:
using Base = SVGAnimationAdditiveListFunction<SVGTransformList>;
using Base::Base;
void setFromAndToValues(SVGElement*, const String& from, const String& to) override
{
m_from->parse(from);
m_to->parse(to);
}
void setToAtEndOfDurationValue(const String& toAtEndOfDuration) override
{
m_toAtEndOfDuration->parse(toAtEndOfDuration);
}
void animate(SVGElement*, float progress, unsigned repeatCount, RefPtr<SVGTransformList>& animated)
{
// Pass false to 'resizeAnimatedIfNeeded', as the special post-multiplication behavior of <animateTransform> needs to be respected below.
if (!adjustAnimatedList(m_animationMode, progress, animated, false))
return;
// Spec: To animations provide specific functionality to get a smooth change from the underlying
// value to the ‘to’ attribute value, which conflicts mathematically with the requirement for
// additive transform animations to be post-multiplied. As a consequence, in SVG 1.1 the behavior
// of to animations for ‘animateTransform’ is undefined.
const Vector<Ref<SVGTransform>>& fromItems = m_from->items();
const Vector<Ref<SVGTransform>>& toItems = m_to->items();
const Vector<Ref<SVGTransform>>& toAtEndOfDurationItems = toAtEndOfDuration()->items();
Vector<Ref<SVGTransform>>& animatedItems = animated->items();
// Never resize the animatedList to the m_to size, instead either clear the list
// or append to it.
if (!animatedItems.isEmpty() && (!m_isAdditive || m_animationMode == AnimationMode::To))
animatedItems.clear();
auto fromItemsSize = fromItems.size();
static const AffineTransform zerosAffineTransform = { 0, 0, 0, 0, 0, 0 };
const SVGTransformValue& to = toItems[0]->value();
const SVGTransformValue zerosTransform = SVGTransformValue(to.type(), zerosAffineTransform);
const SVGTransformValue& from = fromItemsSize ? fromItems[0]->value() : zerosTransform;
SVGTransformValue current = SVGTransformDistance(from, to).scaledDistance(progress).addToSVGTransform(from);
if (m_isAccumulated && repeatCount) {
const SVGTransformValue& toAtEndOfDuration = toAtEndOfDurationItems.size() ? toAtEndOfDurationItems[0]->value() : zerosTransform;
animatedItems.append(SVGTransform::create(SVGTransformDistance::addSVGTransforms(current, toAtEndOfDuration, repeatCount)));
} else
animatedItems.append(SVGTransform::create(current));
}
private:
void addFromAndToValues(SVGElement*) override
{
const Vector<Ref<SVGTransform>>& fromItems = m_from->items();
Vector<Ref<SVGTransform>>& toItems = m_to->items();
if (!fromItems.size() || fromItems.size() != toItems.size())
return;
ASSERT(fromItems.size() == 1);
const Ref<SVGTransform>& from = fromItems[0];
Ref<SVGTransform>& to = toItems[0];
to->setValue(SVGTransformDistance::addSVGTransforms(from->value(), to->value()));
}
};
}