Chromium Code Reviews| Index: Source/core/svg/SVGTransformList.cpp |
| diff --git a/Source/core/svg/SVGTransformList.cpp b/Source/core/svg/SVGTransformList.cpp |
| index f09a01046a584f43484f63b0bdf25faa63d2809c..e95a6fb958402e31825b162787bc6e879bef622a 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 Nikolas Zimmermann <zimmermann@kde.org> |
|
pdr.
2014/02/18 00:53:57
We lost a 2008 for Niko
kouhei (in TOK)
2014/02/18 02:09:15
Done.
|
| + * 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,340 @@ |
| */ |
| #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}; |
| + String aaa(ptr, end-ptr); |
|
fs
2014/02/17 14:14:31
Nit: Not used/needed.
kouhei (in TOK)
2014/02/18 02:09:15
Done.
|
| + 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) |
| +{ |
| + String(ptr, end-ptr).show(); |
|
fs
2014/02/17 14:14:31
Nit: Drop.
kouhei (in TOK)
2014/02/18 02:09:15
Done.
|
| + |
| + 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); |
|
fs
2014/02/17 14:14:31
Could also be moved into the if-block, since it's
kouhei (in TOK)
2014/02/18 02:09:15
Done.
|
| + } |
| + |
| + 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(); |
| + if (it != itEnd) { |
|
fs
2014/02/17 14:14:31
Alternatively:
while (it != itEnd) {
builder.ap
kouhei (in TOK)
2014/02/18 02:09:15
Done.
|
| + builder.append(it->valueAsString()); |
| + ++it; |
| + |
| + for (; it != itEnd; ++it) { |
| + builder.append(' '); |
| + builder.append(it->valueAsString()); |
| + } |
| } |
| return builder.toString(); |
| } |
| -void SVGTransformList::parse(const String& transform) |
| +void SVGTransformList::setValueAsString(const String& value, ExceptionState& exceptionState) |
| +{ |
| + 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(); |
| + exceptionState.throwDOMException(SyntaxError, "Problem parsing transform list=\""+value+"\""); |
| + } |
| +} |
| + |
| +PassRefPtr<NewSVGPropertyBase> SVGTransformList::cloneForAnimation(const String& value) const |
| +{ |
| + ASSERT_NOT_REACHED(); |
| +} |
| + |
| +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 = 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(); |
| +} |
| + |
| +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) |
| { |
| - 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. |
| + 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(); |
| - } else if (transform.is8Bit()) { |
| - const LChar* ptr = transform.characters8(); |
| - const LChar* end = ptr + transform.length(); |
| - if (!parseTransformAttribute(*this, ptr, end)) |
| - 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 { |
| - const UChar* ptr = transform.characters16(); |
| - const UChar* end = ptr + transform.length(); |
| - if (!parseTransformAttribute(*this, ptr, end)) |
| - clear(); |
| + append(currentTransform); |
| } |
| } |
| -} // namespace WebCore |
| +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(); |
| +} |
| + |
| +} |