blob: b2aa2f9a4f26db1c469fcf1c652ac560f01e3462 [file] [log] [blame]
// Copyright 2015 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 "config.h"
#include "core/animation/PathSVGInterpolation.h"
#include "core/svg/SVGPathElement.h"
#include "core/svg/SVGPathSegArcAbs.h"
#include "core/svg/SVGPathSegArcRel.h"
#include "core/svg/SVGPathSegClosePath.h"
#include "core/svg/SVGPathSegCurvetoCubicAbs.h"
#include "core/svg/SVGPathSegCurvetoCubicRel.h"
#include "core/svg/SVGPathSegCurvetoCubicSmoothAbs.h"
#include "core/svg/SVGPathSegCurvetoCubicSmoothRel.h"
#include "core/svg/SVGPathSegCurvetoQuadraticAbs.h"
#include "core/svg/SVGPathSegCurvetoQuadraticRel.h"
#include "core/svg/SVGPathSegCurvetoQuadraticSmoothAbs.h"
#include "core/svg/SVGPathSegCurvetoQuadraticSmoothRel.h"
#include "core/svg/SVGPathSegLinetoAbs.h"
#include "core/svg/SVGPathSegLinetoHorizontalAbs.h"
#include "core/svg/SVGPathSegLinetoHorizontalRel.h"
#include "core/svg/SVGPathSegLinetoRel.h"
#include "core/svg/SVGPathSegLinetoVerticalAbs.h"
#include "core/svg/SVGPathSegLinetoVerticalRel.h"
#include "core/svg/SVGPathSegMovetoAbs.h"
#include "core/svg/SVGPathSegMovetoRel.h"
namespace blink {
namespace {
struct SubPathCoordinates {
double initialX = 0;
double initialY = 0;
double currentX = 0;
double currentY = 0;
};
SVGPathSegType absolutePathSegType(const SVGPathSeg& item)
{
return toAbsolutePathSegType(static_cast<SVGPathSegType>(item.pathSegType()));
}
bool isAbsolutePathSegType(const SVGPathSeg& item)
{
return isAbsolutePathSegType(static_cast<SVGPathSegType>(item.pathSegType()));
}
PassOwnPtr<InterpolableNumber> controlToInterpolableValue(double value, bool isAbsolute, double currentValue)
{
if (isAbsolute)
return InterpolableNumber::create(value);
return InterpolableNumber::create(currentValue + value);
}
double controlFromInterpolableValue(const InterpolableValue* number, bool isAbsolute, double currentValue)
{
double value = toInterpolableNumber(number)->value();
if (isAbsolute)
return value;
return value - currentValue;
}
PassOwnPtr<InterpolableNumber> specifiedToInterpolableValue(double value, bool isAbsolute, double& currentValue)
{
if (isAbsolute)
currentValue = value;
else
currentValue += value;
return InterpolableNumber::create(currentValue);
}
double specifiedFromInterpolableValue(const InterpolableValue* number, bool isAbsolute, double& currentValue)
{
double previousValue = currentValue;
currentValue = toInterpolableNumber(number)->value();
if (isAbsolute)
return currentValue;
return currentValue - previousValue;
}
PassOwnPtr<InterpolableValue> pathSegClosePathToInterpolableValue(const SVGPathSeg& item, SubPathCoordinates& coordinates)
{
coordinates.currentX = coordinates.initialX;
coordinates.currentY = coordinates.initialY;
// arbitrary
return InterpolableBool::create(false);
}
PassRefPtrWillBeRawPtr<SVGPathSeg> pathSegClosePathFromInterpolableValue(const InterpolableValue&, SVGPathSegType, SubPathCoordinates& coordinates, SVGPathElement* element)
{
coordinates.currentX = coordinates.initialX;
coordinates.currentY = coordinates.initialY;
return SVGPathSegClosePath::create(element);
}
PassOwnPtr<InterpolableValue> pathSegSingleCoordinateToInterpolableValue(const SVGPathSegSingleCoordinate& item, SubPathCoordinates& coordinates)
{
bool isAbsolute = isAbsolutePathSegType(item);
OwnPtr<InterpolableList> result = InterpolableList::create(2);
result->set(0, specifiedToInterpolableValue(item.x(), isAbsolute, coordinates.currentX));
result->set(1, specifiedToInterpolableValue(item.y(), isAbsolute, coordinates.currentY));
if (absolutePathSegType(item) == PathSegMoveToAbs) {
// Any upcoming 'closepath' commands bring us back to the location we have just moved to.
coordinates.initialX = coordinates.currentX;
coordinates.initialY = coordinates.currentY;
}
return result.release();
}
PassRefPtrWillBeRawPtr<SVGPathSeg> pathSegSingleCoordinateFromInterpolableValue(const InterpolableValue& value, SVGPathSegType segType, SubPathCoordinates& coordinates, SVGPathElement* element)
{
const InterpolableList& list = toInterpolableList(value);
bool isAbsolute = isAbsolutePathSegType(segType);
float x = specifiedFromInterpolableValue(list.get(0), isAbsolute, coordinates.currentX);
float y = specifiedFromInterpolableValue(list.get(1), isAbsolute, coordinates.currentY);
if (toAbsolutePathSegType(segType) == PathSegMoveToAbs) {
// Any upcoming 'closepath' commands bring us back to the location we have just moved to.
coordinates.initialX = coordinates.currentX;
coordinates.initialY = coordinates.currentY;
}
switch (segType) {
case PathSegMoveToAbs:
return SVGPathSegMovetoAbs::create(element, x, y);
case PathSegMoveToRel:
return SVGPathSegMovetoRel::create(element, x, y);
case PathSegLineToAbs:
return SVGPathSegLinetoAbs::create(element, x, y);
case PathSegLineToRel:
return SVGPathSegLinetoRel::create(element, x, y);
case PathSegCurveToQuadraticSmoothAbs:
return SVGPathSegCurvetoQuadraticSmoothAbs::create(element, x, y);
case PathSegCurveToQuadraticSmoothRel:
return SVGPathSegCurvetoQuadraticSmoothRel::create(element, x, y);
default:
ASSERT_NOT_REACHED();
return nullptr;
}
}
PassOwnPtr<InterpolableValue> pathSegCurvetoCubicToInterpolableValue(const SVGPathSegCurvetoCubic& item, SubPathCoordinates& coordinates)
{
bool isAbsolute = isAbsolutePathSegType(item);
OwnPtr<InterpolableList> result = InterpolableList::create(6);
result->set(0, controlToInterpolableValue(item.x1(), isAbsolute, coordinates.currentX));
result->set(1, controlToInterpolableValue(item.y1(), isAbsolute, coordinates.currentY));
result->set(2, controlToInterpolableValue(item.x2(), isAbsolute, coordinates.currentX));
result->set(3, controlToInterpolableValue(item.y2(), isAbsolute, coordinates.currentY));
result->set(4, specifiedToInterpolableValue(item.x(), isAbsolute, coordinates.currentX));
result->set(5, specifiedToInterpolableValue(item.y(), isAbsolute, coordinates.currentY));
return result.release();
}
PassRefPtrWillBeRawPtr<SVGPathSeg> pathSegCurvetoCubicFromInterpolableValue(const InterpolableValue& value, SVGPathSegType segType, SubPathCoordinates& coordinates, SVGPathElement* element)
{
const InterpolableList& list = toInterpolableList(value);
bool isAbsolute = isAbsolutePathSegType(segType);
float x1 = controlFromInterpolableValue(list.get(0), isAbsolute, coordinates.currentX);
float y1 = controlFromInterpolableValue(list.get(1), isAbsolute, coordinates.currentY);
float x2 = controlFromInterpolableValue(list.get(2), isAbsolute, coordinates.currentX);
float y2 = controlFromInterpolableValue(list.get(3), isAbsolute, coordinates.currentY);
float x = specifiedFromInterpolableValue(list.get(4), isAbsolute, coordinates.currentX);
float y = specifiedFromInterpolableValue(list.get(5), isAbsolute, coordinates.currentY);
switch (segType) {
case PathSegCurveToCubicAbs:
return SVGPathSegCurvetoCubicAbs::create(element, x, y, x1, y1, x2, y2);
case PathSegCurveToCubicRel:
return SVGPathSegCurvetoCubicRel::create(element, x, y, x1, y1, x2, y2);
default:
ASSERT_NOT_REACHED();
return nullptr;
}
}
PassOwnPtr<InterpolableValue> pathSegCurvetoQuadraticToInterpolableValue(const SVGPathSegCurvetoQuadratic& item, SubPathCoordinates& coordinates)
{
bool isAbsolute = isAbsolutePathSegType(item);
OwnPtr<InterpolableList> result = InterpolableList::create(4);
result->set(0, controlToInterpolableValue(item.x1(), isAbsolute, coordinates.currentX));
result->set(1, controlToInterpolableValue(item.y1(), isAbsolute, coordinates.currentY));
result->set(2, specifiedToInterpolableValue(item.x(), isAbsolute, coordinates.currentX));
result->set(3, specifiedToInterpolableValue(item.y(), isAbsolute, coordinates.currentY));
return result.release();
}
PassRefPtrWillBeRawPtr<SVGPathSeg> pathSegCurvetoQuadraticFromInterpolableValue(const InterpolableValue& value, SVGPathSegType segType, SubPathCoordinates& coordinates, SVGPathElement* element)
{
const InterpolableList& list = toInterpolableList(value);
bool isAbsolute = isAbsolutePathSegType(segType);
float x1 = controlFromInterpolableValue(list.get(0), isAbsolute, coordinates.currentX);
float y1 = controlFromInterpolableValue(list.get(1), isAbsolute, coordinates.currentY);
float x = specifiedFromInterpolableValue(list.get(2), isAbsolute, coordinates.currentX);
float y = specifiedFromInterpolableValue(list.get(3), isAbsolute, coordinates.currentY);
switch (segType) {
case PathSegCurveToQuadraticAbs:
return SVGPathSegCurvetoQuadraticAbs::create(element, x, y, x1, y1);
case PathSegCurveToQuadraticRel:
return SVGPathSegCurvetoQuadraticRel::create(element, x, y, x1, y1);
default:
ASSERT_NOT_REACHED();
return nullptr;
}
}
PassOwnPtr<InterpolableValue> pathSegArcToInterpolableValue(const SVGPathSegArc& item, SubPathCoordinates& coordinates)
{
bool isAbsolute = isAbsolutePathSegType(item);
OwnPtr<InterpolableList> result = InterpolableList::create(7);
result->set(0, specifiedToInterpolableValue(item.x(), isAbsolute, coordinates.currentX));
result->set(1, specifiedToInterpolableValue(item.y(), isAbsolute, coordinates.currentY));
result->set(2, InterpolableNumber::create(item.r1()));
result->set(3, InterpolableNumber::create(item.r2()));
result->set(4, InterpolableNumber::create(item.angle()));
result->set(5, InterpolableBool::create(item.largeArcFlag()));
result->set(6, InterpolableBool::create(item.sweepFlag()));
return result.release();
}
PassRefPtrWillBeRawPtr<SVGPathSeg> pathSegArcFromInterpolableValue(const InterpolableValue& value, SVGPathSegType segType, SubPathCoordinates& coordinates, SVGPathElement* element)
{
const InterpolableList& list = toInterpolableList(value);
bool isAbsolute = isAbsolutePathSegType(segType);
float x = specifiedFromInterpolableValue(list.get(0), isAbsolute, coordinates.currentX);
float y = specifiedFromInterpolableValue(list.get(1), isAbsolute, coordinates.currentY);
float r1 = toInterpolableNumber(list.get(2))->value();
float r2 = toInterpolableNumber(list.get(3))->value();
float angle = toInterpolableNumber(list.get(4))->value();
bool largeArcFlag = toInterpolableBool(list.get(5))->value();
bool sweepFlag = toInterpolableBool(list.get(6))->value();
switch (segType) {
case PathSegArcAbs:
return SVGPathSegArcAbs::create(element, x, y, r1, r2, angle, largeArcFlag, sweepFlag);
case PathSegArcRel:
return SVGPathSegArcRel::create(element, x, y, r1, r2, angle, largeArcFlag, sweepFlag);
default:
ASSERT_NOT_REACHED();
return nullptr;
}
}
PassOwnPtr<InterpolableValue> pathSegLinetoHorizontalToInterpolableValue(const SVGPathSegLinetoHorizontal& item, SubPathCoordinates& coordinates)
{
bool isAbsolute = isAbsolutePathSegType(item);
return specifiedToInterpolableValue(item.x(), isAbsolute, coordinates.currentX);
}
PassRefPtrWillBeRawPtr<SVGPathSeg> pathSegLinetoHorizontalFromInterpolableValue(const InterpolableValue& value, SVGPathSegType segType, SubPathCoordinates& coordinates, SVGPathElement* element)
{
bool isAbsolute = isAbsolutePathSegType(segType);
float x = specifiedFromInterpolableValue(&value, isAbsolute, coordinates.currentX);
switch (segType) {
case PathSegLineToHorizontalAbs:
return SVGPathSegLinetoHorizontalAbs::create(element, x);
case PathSegLineToHorizontalRel:
return SVGPathSegLinetoHorizontalRel::create(element, x);
default:
ASSERT_NOT_REACHED();
return nullptr;
}
}
PassOwnPtr<InterpolableValue> pathSegLinetoVerticalToInterpolableValue(const SVGPathSegLinetoVertical& item, SubPathCoordinates& coordinates)
{
bool isAbsolute = isAbsolutePathSegType(item);
return specifiedToInterpolableValue(item.y(), isAbsolute, coordinates.currentY);
}
PassRefPtrWillBeRawPtr<SVGPathSeg> pathSegLinetoVerticalFromInterpolableValue(const InterpolableValue& value, SVGPathSegType segType, SubPathCoordinates& coordinates, SVGPathElement* element)
{
bool isAbsolute = isAbsolutePathSegType(segType);
float y = specifiedFromInterpolableValue(&value, isAbsolute, coordinates.currentY);
switch (segType) {
case PathSegLineToVerticalAbs:
return SVGPathSegLinetoVerticalAbs::create(element, y);
case PathSegLineToVerticalRel:
return SVGPathSegLinetoVerticalRel::create(element, y);
default:
ASSERT_NOT_REACHED();
return nullptr;
}
}
PassOwnPtr<InterpolableValue> pathSegCurvetoCubicSmoothToInterpolableValue(const SVGPathSegCurvetoCubicSmooth& item, SubPathCoordinates& coordinates)
{
bool isAbsolute = isAbsolutePathSegType(item);
OwnPtr<InterpolableList> result = InterpolableList::create(4);
result->set(0, controlToInterpolableValue(item.x2(), isAbsolute, coordinates.currentX));
result->set(1, controlToInterpolableValue(item.y2(), isAbsolute, coordinates.currentY));
result->set(2, specifiedToInterpolableValue(item.x(), isAbsolute, coordinates.currentX));
result->set(3, specifiedToInterpolableValue(item.y(), isAbsolute, coordinates.currentY));
return result.release();
}
PassRefPtrWillBeRawPtr<SVGPathSeg> pathSegCurvetoCubicSmoothFromInterpolableValue(const InterpolableValue& value, SVGPathSegType segType, SubPathCoordinates& coordinates, SVGPathElement* element)
{
const InterpolableList& list = toInterpolableList(value);
bool isAbsolute = isAbsolutePathSegType(segType);
float x2 = controlFromInterpolableValue(list.get(0), isAbsolute, coordinates.currentX);
float y2 = controlFromInterpolableValue(list.get(1), isAbsolute, coordinates.currentY);
float x = specifiedFromInterpolableValue(list.get(2), isAbsolute, coordinates.currentX);
float y = specifiedFromInterpolableValue(list.get(3), isAbsolute, coordinates.currentY);
switch (segType) {
case PathSegCurveToCubicSmoothAbs:
return SVGPathSegCurvetoCubicSmoothAbs::create(element, x, y, x2, y2);
case PathSegCurveToCubicSmoothRel:
return SVGPathSegCurvetoCubicSmoothRel::create(element, x, y, x2, y2);
default:
ASSERT_NOT_REACHED();
return nullptr;
}
}
PassOwnPtr<InterpolableValue> pathSegToInterpolableValue(const SVGPathSeg& item, SubPathCoordinates& coordinates, SVGPathSegType* ptrSegType)
{
SVGPathSegType segType = static_cast<SVGPathSegType>(item.pathSegType());
if (ptrSegType)
*ptrSegType = segType;
switch (segType) {
case PathSegClosePath:
return pathSegClosePathToInterpolableValue(item, coordinates);
case PathSegMoveToAbs:
case PathSegMoveToRel:
case PathSegLineToAbs:
case PathSegLineToRel:
case PathSegCurveToQuadraticSmoothAbs:
case PathSegCurveToQuadraticSmoothRel:
return pathSegSingleCoordinateToInterpolableValue(static_cast<const SVGPathSegSingleCoordinate&>(item), coordinates);
case PathSegCurveToCubicAbs:
case PathSegCurveToCubicRel:
return pathSegCurvetoCubicToInterpolableValue(static_cast<const SVGPathSegCurvetoCubic&>(item), coordinates);
case PathSegCurveToQuadraticAbs:
case PathSegCurveToQuadraticRel:
return pathSegCurvetoQuadraticToInterpolableValue(static_cast<const SVGPathSegCurvetoQuadratic&>(item), coordinates);
case PathSegArcAbs:
case PathSegArcRel:
return pathSegArcToInterpolableValue(static_cast<const SVGPathSegArc&>(item), coordinates);
case PathSegLineToHorizontalAbs:
case PathSegLineToHorizontalRel:
return pathSegLinetoHorizontalToInterpolableValue(static_cast<const SVGPathSegLinetoHorizontal&>(item), coordinates);
case PathSegLineToVerticalAbs:
case PathSegLineToVerticalRel:
return pathSegLinetoVerticalToInterpolableValue(static_cast<const SVGPathSegLinetoVertical&>(item), coordinates);
case PathSegCurveToCubicSmoothAbs:
case PathSegCurveToCubicSmoothRel:
return pathSegCurvetoCubicSmoothToInterpolableValue(static_cast<const SVGPathSegCurvetoCubicSmooth&>(item), coordinates);
case PathSegUnknown:
ASSERT_NOT_REACHED();
}
ASSERT_NOT_REACHED();
return nullptr;
}
PassRefPtrWillBeRawPtr<SVGPathSeg> pathSegFromInterpolableValue(const InterpolableValue& value, SVGPathSegType segType, SubPathCoordinates& coordinates, SVGPathElement* element)
{
switch (segType) {
case PathSegClosePath:
return pathSegClosePathFromInterpolableValue(value, segType, coordinates, element);
case PathSegMoveToAbs:
case PathSegMoveToRel:
case PathSegLineToAbs:
case PathSegLineToRel:
case PathSegCurveToQuadraticSmoothAbs:
case PathSegCurveToQuadraticSmoothRel:
return pathSegSingleCoordinateFromInterpolableValue(value, segType, coordinates, element);
case PathSegCurveToCubicAbs:
case PathSegCurveToCubicRel:
return pathSegCurvetoCubicFromInterpolableValue(value, segType, coordinates, element);
case PathSegCurveToQuadraticAbs:
case PathSegCurveToQuadraticRel:
return pathSegCurvetoQuadraticFromInterpolableValue(value, segType, coordinates, element);
case PathSegArcAbs:
case PathSegArcRel:
return pathSegArcFromInterpolableValue(value, segType, coordinates, element);
case PathSegLineToHorizontalAbs:
case PathSegLineToHorizontalRel:
return pathSegLinetoHorizontalFromInterpolableValue(value, segType, coordinates, element);
case PathSegLineToVerticalAbs:
case PathSegLineToVerticalRel:
return pathSegLinetoVerticalFromInterpolableValue(value, segType, coordinates, element);
case PathSegCurveToCubicSmoothAbs:
case PathSegCurveToCubicSmoothRel:
return pathSegCurvetoCubicSmoothFromInterpolableValue(value, segType, coordinates, element);
case PathSegUnknown:
ASSERT_NOT_REACHED();
}
ASSERT_NOT_REACHED();
return nullptr;
}
} // namespace
PassRefPtr<PathSVGInterpolation> PathSVGInterpolation::maybeCreate(SVGPropertyBase* start, SVGPropertyBase* end, PassRefPtrWillBeRawPtr<SVGAnimatedPropertyBase> attribute)
{
ASSERT(start->type() == SVGPathSegList::classType());
ASSERT(end->type() == SVGPathSegList::classType());
SVGPathSegList* startList = static_cast<SVGPathSegList*>(start);
SVGPathSegList* endList = static_cast<SVGPathSegList*>(end);
size_t length = startList->length();
if (length != endList->length())
return nullptr;
Vector<SVGPathSegType> pathSegTypes(length);
OwnPtr<InterpolableList> startValue = InterpolableList::create(length);
OwnPtr<InterpolableList> endValue = InterpolableList::create(length);
SubPathCoordinates startCoordinates;
SubPathCoordinates endCoordinates;
for (size_t i = 0; i < length; i++) {
if (absolutePathSegType(*startList->at(i)) != absolutePathSegType(*endList->at(i)))
return nullptr;
// Like Firefox SMIL, we use the final path seg type.
startValue->set(i, pathSegToInterpolableValue(*startList->at(i), startCoordinates, nullptr));
endValue->set(i, pathSegToInterpolableValue(*endList->at(i), endCoordinates, &pathSegTypes.at(i)));
}
return adoptRef(new PathSVGInterpolation(startValue.release(), endValue.release(), attribute, pathSegTypes));
}
PassRefPtrWillBeRawPtr<SVGPropertyBase> PathSVGInterpolation::fromInterpolableValue(const InterpolableValue& value, const Vector<SVGPathSegType>& pathSegTypes, SVGPathElement* element)
{
const InterpolableList& listValue = toInterpolableList(value);
RefPtrWillBeRawPtr<SVGPathSegList> result = SVGPathSegList::create(element);
SubPathCoordinates coordinates;
for (size_t i = 0; i < listValue.length(); i++)
result->append(pathSegFromInterpolableValue(*listValue.get(i), pathSegTypes.at(i), coordinates, element));
return result.release();
}
PassRefPtrWillBeRawPtr<SVGPropertyBase> PathSVGInterpolation::interpolatedValue(SVGElement& element) const
{
return fromInterpolableValue(*m_cachedValue, m_pathSegTypes, toSVGPathElement(&element));
}
}