/* Copyright (C) 2004, 2005 Nikolas Zimmermann 2004, 2005, 2006, 2007 Rob Buis Copyright (C) 2007 Eric Seidel Copyright (C) 2008 Apple Inc. All Rights Reserved. This file is part of the WebKit project This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "config.h" #if ENABLE(SVG) && ENABLE(SVG_ANIMATION) #include "SVGAnimateTransformElement.h" #include "AffineTransform.h" #include "Attribute.h" #include "RenderObject.h" #include "RenderSVGResource.h" #include "SVGAngle.h" #include "SVGElementInstance.h" #include "SVGGradientElement.h" #include "SVGParserUtilities.h" #include "SVGSVGElement.h" #include "SVGStyledTransformableElement.h" #include "SVGTextElement.h" #include "SVGTransform.h" #include "SVGTransformList.h" #include "SVGUseElement.h" #include #include using namespace std; namespace WebCore { SVGAnimateTransformElement::SVGAnimateTransformElement(const QualifiedName& tagName, Document* doc) : SVGAnimationElement(tagName, doc) , m_type(SVGTransform::SVG_TRANSFORM_UNKNOWN) , m_baseIndexInTransformList(0) { } SVGAnimateTransformElement::~SVGAnimateTransformElement() { } bool SVGAnimateTransformElement::hasValidTarget() const { SVGElement* targetElement = this->targetElement(); return SVGAnimationElement::hasValidTarget() && (targetElement->isStyledTransformable() || targetElement->hasTagName(SVGNames::textTag) || targetElement->hasTagName(SVGNames::linearGradientTag) || targetElement->hasTagName(SVGNames::radialGradientTag)); } void SVGAnimateTransformElement::parseMappedAttribute(Attribute* attr) { if (attr->name() == SVGNames::typeAttr) { if (attr->value() == "translate") m_type = SVGTransform::SVG_TRANSFORM_TRANSLATE; else if (attr->value() == "scale") m_type = SVGTransform::SVG_TRANSFORM_SCALE; else if (attr->value() == "rotate") m_type = SVGTransform::SVG_TRANSFORM_ROTATE; else if (attr->value() == "skewX") m_type = SVGTransform::SVG_TRANSFORM_SKEWX; else if (attr->value() == "skewY") m_type = SVGTransform::SVG_TRANSFORM_SKEWY; } else SVGAnimationElement::parseMappedAttribute(attr); } static PassRefPtr transformListFor(SVGElement* element) { ASSERT(element); if (element->isStyledTransformable()) return static_cast(element)->transform(); if (element->hasTagName(SVGNames::textTag)) return static_cast(element)->transform(); if (element->hasTagName(SVGNames::linearGradientTag) || element->hasTagName(SVGNames::radialGradientTag)) return static_cast(element)->gradientTransform(); return 0; } void SVGAnimateTransformElement::resetToBaseValue(const String& baseValue) { if (!hasValidTarget()) return; if (targetElement()->hasTagName(SVGNames::linearGradientTag) || targetElement()->hasTagName(SVGNames::radialGradientTag)) { targetElement()->setAttribute(SVGNames::gradientTransformAttr, baseValue.isEmpty() ? "matrix(1 0 0 1 0 0)" : baseValue); return; } if (baseValue.isEmpty()) { ExceptionCode ec; RefPtr list = transformListFor(targetElement()); list->clear(ec); } else targetElement()->setAttribute(SVGNames::transformAttr, baseValue); } void SVGAnimateTransformElement::calculateAnimatedValue(float percentage, unsigned repeat, SVGSMILElement* resultElement) { if (!hasValidTarget()) return; SVGElement* targetElement = resultElement->targetElement(); RefPtr transformList = transformListFor(targetElement); ASSERT(transformList); ExceptionCode ec; if (!isAdditive()) transformList->clear(ec); if (isAccumulated() && repeat) { SVGTransform accumulatedTransform = SVGTransformDistance(m_fromTransform, m_toTransform).scaledDistance(repeat).addToSVGTransform(SVGTransform()); transformList->appendItem(accumulatedTransform, ec); } SVGTransform transform = SVGTransformDistance(m_fromTransform, m_toTransform).scaledDistance(percentage).addToSVGTransform(m_fromTransform); transformList->appendItem(transform, ec); } bool SVGAnimateTransformElement::calculateFromAndToValues(const String& fromString, const String& toString) { m_fromTransform = parseTransformValue(fromString); if (!m_fromTransform.isValid()) return false; m_toTransform = parseTransformValue(toString); return m_toTransform.isValid(); } bool SVGAnimateTransformElement::calculateFromAndByValues(const String& fromString, const String& byString) { m_fromTransform = parseTransformValue(fromString); if (!m_fromTransform.isValid()) return false; m_toTransform = SVGTransformDistance::addSVGTransforms(m_fromTransform, parseTransformValue(byString)); return m_toTransform.isValid(); } SVGTransform SVGAnimateTransformElement::parseTransformValue(const String& value) const { if (value.isEmpty()) return SVGTransform(m_type); SVGTransform result; // FIXME: This is pretty dumb but parseTransformValue() wants those parenthesis. String parseString("(" + value + ")"); const UChar* ptr = parseString.characters(); SVGTransformable::parseTransformValue(m_type, ptr, ptr + parseString.length(), result); // ignoring return value return result; } void SVGAnimateTransformElement::applyResultsToTarget() { if (!hasValidTarget()) return; // We accumulate to the target element transform list so there is not much to do here. SVGElement* targetElement = this->targetElement(); if (!targetElement) return; if (RenderObject* renderer = targetElement->renderer()) { renderer->setNeedsTransformUpdate(); RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer); } // ...except in case where we have additional instances in trees. const HashSet& instances = targetElement->instancesForElement(); RefPtr transformList = transformListFor(targetElement); const HashSet::const_iterator end = instances.end(); for (HashSet::const_iterator it = instances.begin(); it != end; ++it) { SVGElement* shadowTreeElement = (*it)->shadowTreeElement(); ASSERT(shadowTreeElement); if (shadowTreeElement->isStyledTransformable()) static_cast(shadowTreeElement)->setTransformBaseValue(transformList.get()); else if (shadowTreeElement->hasTagName(SVGNames::textTag)) static_cast(shadowTreeElement)->setTransformBaseValue(transformList.get()); else if (shadowTreeElement->hasTagName(SVGNames::linearGradientTag) || shadowTreeElement->hasTagName(SVGNames::radialGradientTag)) static_cast(shadowTreeElement)->setGradientTransformBaseValue(transformList.get()); if (RenderObject* renderer = shadowTreeElement->renderer()) { renderer->setNeedsTransformUpdate(); RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer); } } } float SVGAnimateTransformElement::calculateDistance(const String& fromString, const String& toString) { // 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. SVGTransform from = parseTransformValue(fromString); if (!from.isValid()) return -1.f; SVGTransform to = parseTransformValue(toString); if (!to.isValid() || from.type() != to.type()) return -1.f; if (to.type() == SVGTransform::SVG_TRANSFORM_TRANSLATE) { FloatSize diff = to.translate() - from.translate(); return sqrtf(diff.width() * diff.width() + diff.height() * diff.height()); } if (to.type() == SVGTransform::SVG_TRANSFORM_ROTATE) return fabsf(to.angle() - from.angle()); if (to.type() == SVGTransform::SVG_TRANSFORM_SCALE) { FloatSize diff = to.scale() - from.scale(); return sqrtf(diff.width() * diff.width() + diff.height() * diff.height()); } return -1.f; } } // vim:ts=4:noet #endif // ENABLE(SVG)