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(); |
+} |
+ |
+} |