| Index: Source/core/svg/SVGTransformList.cpp
|
| diff --git a/Source/core/svg/SVGTransformList.cpp b/Source/core/svg/SVGTransformList.cpp
|
| index f09a01046a584f43484f63b0bdf25faa63d2809c..4fb46f70340f24a11a944dbba79cdab5a9a0f8d9 100644
|
| --- a/Source/core/svg/SVGTransformList.cpp
|
| +++ b/Source/core/svg/SVGTransformList.cpp
|
| @@ -1,6 +1,9 @@
|
| /*
|
| * Copyright (C) 2004, 2005, 2008 Nikolas Zimmermann <zimmermann@kde.org>
|
| - * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
|
| + * Copyright (C) 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org>
|
| + * Copyright (C) 2007 Eric Seidel <eric@webkit.org>
|
| + * Copyright (C) 2008 Apple Inc. All rights reserved.
|
| + * Copyright (C) Research In Motion Limited 2012. All rights reserved.
|
| *
|
| * This library is free software; you can redistribute it and/or
|
| * modify it under the terms of the GNU Library General Public
|
| @@ -19,77 +22,335 @@
|
| */
|
|
|
| #include "config.h"
|
| +
|
| #include "core/svg/SVGTransformList.h"
|
|
|
| +#include "SVGNames.h"
|
| +#include "core/svg/SVGAnimateTransformElement.h"
|
| +#include "core/svg/SVGAnimatedNumber.h"
|
| #include "core/svg/SVGParserUtilities.h"
|
| -#include "core/svg/SVGSVGElement.h"
|
| -#include "platform/transforms/AffineTransform.h"
|
| +#include "core/svg/SVGTransformDistance.h"
|
| #include "wtf/text/StringBuilder.h"
|
| +#include "wtf/text/WTFString.h"
|
|
|
| namespace WebCore {
|
|
|
| -SVGTransform SVGTransformList::createSVGTransformFromMatrix(const SVGMatrix& matrix) const
|
| +inline PassRefPtr<SVGTransformList> toSVGTransformList(PassRefPtr<NewSVGPropertyBase> passBase)
|
| +{
|
| + RefPtr<NewSVGPropertyBase> base = passBase;
|
| + ASSERT(base->type() == SVGTransformList::classType());
|
| + return static_pointer_cast<SVGTransformList>(base.release());
|
| +}
|
| +
|
| +SVGTransformList::SVGTransformList()
|
| +{
|
| +}
|
| +
|
| +SVGTransformList::~SVGTransformList()
|
| {
|
| - return SVGSVGElement::createSVGTransformFromMatrix(matrix);
|
| }
|
|
|
| -SVGTransform SVGTransformList::consolidate()
|
| +PassRefPtr<SVGTransform> SVGTransformList::consolidate()
|
| {
|
| AffineTransform matrix;
|
| if (!concatenate(matrix))
|
| - return SVGTransform();
|
| + return SVGTransform::create();
|
|
|
| - SVGTransform transform(matrix);
|
| + RefPtr<SVGTransform> transform = SVGTransform::create(matrix);
|
| clear();
|
| - append(transform);
|
| - return transform;
|
| + return appendItem(transform);
|
| }
|
|
|
| bool SVGTransformList::concatenate(AffineTransform& result) const
|
| {
|
| - unsigned size = this->size();
|
| - if (!size)
|
| + if (isEmpty())
|
| return false;
|
|
|
| - for (unsigned i = 0; i < size; ++i)
|
| - result *= at(i).matrix();
|
| + ConstIterator it = begin();
|
| + ConstIterator itEnd = end();
|
| + for (; it != itEnd; ++it)
|
| + result *= it->matrix();
|
|
|
| return true;
|
| }
|
|
|
| +PassRefPtr<SVGTransformList> SVGTransformList::clone()
|
| +{
|
| + RefPtr<SVGTransformList> svgTransformList = SVGTransformList::create();
|
| + svgTransformList->deepCopy(this);
|
| + return svgTransformList.release();
|
| +}
|
| +
|
| +namespace {
|
| +
|
| +template<typename CharType>
|
| +int parseTransformParamList(const CharType*& ptr, const CharType* end, float* values, int required, int optional)
|
| +{
|
| + int parsedParams = 0;
|
| + int maxPossibleParams = required + optional;
|
| +
|
| + bool trailingDelimiter = false;
|
| +
|
| + skipOptionalSVGSpaces(ptr, end);
|
| + while (parsedParams < maxPossibleParams) {
|
| + if (!parseNumber(ptr, end, values[parsedParams], false))
|
| + break;
|
| +
|
| + ++parsedParams;
|
| +
|
| + if (skipOptionalSVGSpaces(ptr, end) && *ptr == ',') {
|
| + ++ptr;
|
| + skipOptionalSVGSpaces(ptr, end);
|
| +
|
| + trailingDelimiter = true;
|
| + } else {
|
| + trailingDelimiter = false;
|
| + }
|
| + }
|
| +
|
| + if (trailingDelimiter || !(parsedParams == required || parsedParams == maxPossibleParams))
|
| + return -1;
|
| +
|
| + return parsedParams;
|
| +}
|
| +
|
| +// These should be kept in sync with enum SVGTransformType
|
| +static const int requiredValuesForType[] = {0, 6, 1, 1, 1, 1, 1};
|
| +static const int optionalValuesForType[] = {0, 0, 1, 1, 2, 0, 0};
|
| +
|
| +template<typename CharType>
|
| +PassRefPtr<SVGTransform> parseTransformOfType(unsigned type, const CharType*& ptr, const CharType* end)
|
| +{
|
| + if (type == SVG_TRANSFORM_UNKNOWN)
|
| + return 0;
|
| +
|
| + int valueCount = 0;
|
| + float values[] = {0, 0, 0, 0, 0, 0};
|
| + if ((valueCount = parseTransformParamList(ptr, end, values, requiredValuesForType[type], optionalValuesForType[type])) < 0) {
|
| + return 0;
|
| + }
|
| +
|
| + RefPtr<SVGTransform> transform = SVGTransform::create();
|
| +
|
| + switch (type) {
|
| + case SVG_TRANSFORM_SKEWX:
|
| + transform->setSkewX(values[0]);
|
| + break;
|
| + case SVG_TRANSFORM_SKEWY:
|
| + transform->setSkewY(values[0]);
|
| + break;
|
| + case SVG_TRANSFORM_SCALE:
|
| + if (valueCount == 1) // Spec: if only one param given, assume uniform scaling
|
| + transform->setScale(values[0], values[0]);
|
| + else
|
| + transform->setScale(values[0], values[1]);
|
| + break;
|
| + case SVG_TRANSFORM_TRANSLATE:
|
| + if (valueCount == 1) // Spec: if only one param given, assume 2nd param to be 0
|
| + transform->setTranslate(values[0], 0);
|
| + else
|
| + transform->setTranslate(values[0], values[1]);
|
| + break;
|
| + case SVG_TRANSFORM_ROTATE:
|
| + if (valueCount == 1)
|
| + transform->setRotate(values[0], 0, 0);
|
| + else
|
| + transform->setRotate(values[0], values[1], values[2]);
|
| + break;
|
| + case SVG_TRANSFORM_MATRIX:
|
| + transform->setMatrix(AffineTransform(values[0], values[1], values[2], values[3], values[4], values[5]));
|
| + break;
|
| + }
|
| +
|
| + return transform.release();
|
| +}
|
| +
|
| +}
|
| +
|
| +template<typename CharType>
|
| +bool SVGTransformList::parseInternal(const CharType*& ptr, const CharType* end)
|
| +{
|
| + clear();
|
| +
|
| + bool delimParsed = false;
|
| + while (ptr < end) {
|
| + delimParsed = false;
|
| + SVGTransformType transformType = SVG_TRANSFORM_UNKNOWN;
|
| + skipOptionalSVGSpaces(ptr, end);
|
| +
|
| + if (!parseAndSkipTransformType(ptr, end, transformType))
|
| + return false;
|
| +
|
| + if (!skipOptionalSVGSpaces(ptr, end) || *ptr != '(')
|
| + return false;
|
| + ptr++;
|
| +
|
| + RefPtr<SVGTransform> transform = parseTransformOfType(transformType, ptr, end);
|
| + if (!transform)
|
| + return false;
|
| +
|
| + if (!skipOptionalSVGSpaces(ptr, end) || *ptr != ')')
|
| + return false;
|
| + ptr++;
|
| +
|
| + append(transform.release());
|
| +
|
| + skipOptionalSVGSpaces(ptr, end);
|
| + if (ptr < end && *ptr == ',') {
|
| + delimParsed = true;
|
| + ++ptr;
|
| + skipOptionalSVGSpaces(ptr, end);
|
| + }
|
| + }
|
| +
|
| + return !delimParsed;
|
| +}
|
| +
|
| +bool SVGTransformList::parse(const UChar*& ptr, const UChar* end)
|
| +{
|
| + return parseInternal(ptr, end);
|
| +}
|
| +
|
| +bool SVGTransformList::parse(const LChar*& ptr, const LChar* end)
|
| +{
|
| + return parseInternal(ptr, end);
|
| +}
|
| +
|
| String SVGTransformList::valueAsString() const
|
| {
|
| StringBuilder builder;
|
| - unsigned size = this->size();
|
| - for (unsigned i = 0; i < size; ++i) {
|
| - if (i > 0)
|
| - builder.append(' ');
|
|
|
| - builder.append(at(i).valueAsString());
|
| + ConstIterator it = begin();
|
| + ConstIterator itEnd = end();
|
| + while (it != itEnd) {
|
| + builder.append(it->valueAsString());
|
| + ++it;
|
| + if (it != itEnd)
|
| + builder.append(' ');
|
| }
|
|
|
| return builder.toString();
|
| }
|
|
|
| -void SVGTransformList::parse(const String& transform)
|
| +void SVGTransformList::setValueAsString(const String& value, ExceptionState& exceptionState)
|
| {
|
| - if (transform.isEmpty()) {
|
| - // FIXME: The parseTransformAttribute function secretly calls clear()
|
| - // based on a |mode| parameter. We should study whether we should
|
| - // remove the |mode| parameter and force callers to call clear()
|
| - // themselves.
|
| + if (value.isEmpty()) {
|
| + clear();
|
| + return;
|
| + }
|
| +
|
| + bool valid = false;
|
| + if (value.is8Bit()) {
|
| + const LChar* ptr = value.characters8();
|
| + const LChar* end = ptr + value.length();
|
| + valid = parse(ptr, end);
|
| + } else {
|
| + const UChar* ptr = value.characters16();
|
| + const UChar* end = ptr + value.length();
|
| + valid = parse(ptr, end);
|
| + }
|
| +
|
| + if (!valid) {
|
| clear();
|
| - } else if (transform.is8Bit()) {
|
| - const LChar* ptr = transform.characters8();
|
| - const LChar* end = ptr + transform.length();
|
| - if (!parseTransformAttribute(*this, ptr, end))
|
| - clear();
|
| + exceptionState.throwDOMException(SyntaxError, "Problem parsing transform list=\""+value+"\"");
|
| + }
|
| +}
|
| +
|
| +PassRefPtr<NewSVGPropertyBase> SVGTransformList::cloneForAnimation(const String& value) const
|
| +{
|
| + ASSERT_NOT_REACHED();
|
| + return 0;
|
| +}
|
| +
|
| +PassRefPtr<SVGTransformList> SVGTransformList::create(SVGTransformType transformType, const String& value)
|
| +{
|
| + RefPtr<SVGTransform> transform;
|
| + if (value.isEmpty()) {
|
| + } else if (value.is8Bit()) {
|
| + const LChar* ptr = value.characters8();
|
| + const LChar* end = ptr + value.length();
|
| + transform = parseTransformOfType(transformType, ptr, end);
|
| } else {
|
| - const UChar* ptr = transform.characters16();
|
| - const UChar* end = ptr + transform.length();
|
| - if (!parseTransformAttribute(*this, ptr, end))
|
| - clear();
|
| + const UChar* ptr = value.characters16();
|
| + const UChar* end = ptr + value.length();
|
| + transform = parseTransformOfType(transformType, ptr, end);
|
| }
|
| +
|
| + RefPtr<SVGTransformList> svgTransformList = SVGTransformList::create();
|
| + if (transform)
|
| + svgTransformList->append(transform);
|
| + return svgTransformList.release();
|
| }
|
|
|
| -} // namespace WebCore
|
| +void SVGTransformList::add(PassRefPtr<NewSVGPropertyBase> other, SVGElement* contextElement)
|
| +{
|
| + if (isEmpty())
|
| + return;
|
| +
|
| + RefPtr<SVGTransformList> otherList = toSVGTransformList(other);
|
| + if (numberOfItems() != otherList->numberOfItems())
|
| + return;
|
| +
|
| + ASSERT(numberOfItems() == 1);
|
| + RefPtr<SVGTransform> fromTransform = at(0);
|
| + RefPtr<SVGTransform> toTransform = otherList->at(0);
|
| +
|
| + ASSERT(fromTransform->transformType() == toTransform->transformType());
|
| + clear();
|
| + append(SVGTransformDistance::addSVGTransforms(fromTransform, toTransform));
|
| +}
|
| +
|
| +void SVGTransformList::calculateAnimatedValue(SVGAnimationElement* animationElement, float percentage, unsigned repeatCount, PassRefPtr<NewSVGPropertyBase> fromValue, PassRefPtr<NewSVGPropertyBase> toValue, PassRefPtr<NewSVGPropertyBase> toAtEndOfDurationValue, SVGElement* contextElement)
|
| +{
|
| + ASSERT(animationElement);
|
| + bool isToAnimation = animationElement->animationMode() == ToAnimation;
|
| +
|
| + // 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
|
| + // FIXME: This is not taken into account yet.
|
| + RefPtr<SVGTransformList> fromList = isToAnimation ? this : toSVGTransformList(fromValue);
|
| + RefPtr<SVGTransformList> toList = toSVGTransformList(toValue);
|
| + RefPtr<SVGTransformList> toAtEndOfDurationList = toSVGTransformList(toAtEndOfDurationValue);
|
| +
|
| + size_t fromListSize = fromList->numberOfItems();
|
| + size_t toListSize = toList->numberOfItems();
|
| +
|
| + if (!toListSize)
|
| + return;
|
| +
|
| + // Never resize the animatedTransformList to the toList size, instead either clear the list or append to it.
|
| + if (!isEmpty() && !animationElement->isAdditive())
|
| + clear();
|
| +
|
| + RefPtr<SVGTransform> toTransform = toList->at(0);
|
| + RefPtr<SVGTransform> effectiveFrom = fromListSize ? fromList->at(0) : SVGTransform::create(toTransform->transformType(), SVGTransform::ConstructZeroTransform);
|
| + RefPtr<SVGTransform> currentTransform = SVGTransformDistance(effectiveFrom, toTransform).scaledDistance(percentage).addToSVGTransform(effectiveFrom);
|
| + if (animationElement->isAccumulated() && repeatCount) {
|
| + RefPtr<SVGTransform> effectiveToAtEnd = !toAtEndOfDurationList->isEmpty() ? toAtEndOfDurationList->at(0) : SVGTransform::create(toTransform->transformType(), SVGTransform::ConstructZeroTransform);
|
| + append(SVGTransformDistance::addSVGTransforms(currentTransform, effectiveToAtEnd, repeatCount));
|
| + } else {
|
| + append(currentTransform);
|
| + }
|
| +}
|
| +
|
| +float SVGTransformList::calculateDistance(PassRefPtr<NewSVGPropertyBase> toValue, SVGElement*)
|
| +{
|
| + // FIXME: This is not correct in all cases. The spec demands that each component (translate x and y for example)
|
| + // is paced separately. To implement this we need to treat each component as individual animation everywhere.
|
| +
|
| + RefPtr<SVGTransformList> toList = toSVGTransformList(toValue);
|
| + if (isEmpty() || numberOfItems() != toList->numberOfItems())
|
| + return -1;
|
| +
|
| + ASSERT(numberOfItems() == 1);
|
| + if (at(0)->transformType() == toList->at(0)->transformType())
|
| + return -1;
|
| +
|
| + // Spec: http://www.w3.org/TR/SVG/animate.html#complexDistances
|
| + // Paced animations assume a notion of distance between the various animation values defined by the ‘to’, ‘from’, ‘by’ and ‘values’ attributes.
|
| + // Distance is defined only for scalar types (such as <length>), colors and the subset of transformation types that are supported by ‘animateTransform’.
|
| + return SVGTransformDistance(at(0), toList->at(0)).distance();
|
| +}
|
| +
|
| +}
|
|
|