2 * Copyright (C) 2004, 2005, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3 * Copyright (C) 2004, 2005, 2006 Rob Buis <buis@kde.org>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public License
16 * along with this library; see the file COPYING.LIB. If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
23 #include "core/svg/SVGGraphicsElement.h"
26 #include "core/rendering/svg/RenderSVGPath.h"
27 #include "core/rendering/svg/RenderSVGResource.h"
28 #include "core/rendering/svg/SVGPathData.h"
29 #include "core/svg/SVGElementInstance.h"
30 #include "platform/transforms/AffineTransform.h"
34 // Animated property definitions
35 DEFINE_ANIMATED_TRANSFORM_LIST(SVGGraphicsElement, SVGNames::transformAttr, Transform, transform)
37 BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGGraphicsElement)
38 REGISTER_LOCAL_ANIMATED_PROPERTY(transform)
39 REGISTER_PARENT_ANIMATED_PROPERTIES(SVGElement)
40 END_REGISTER_ANIMATED_PROPERTIES
42 SVGGraphicsElement::SVGGraphicsElement(const QualifiedName& tagName, Document& document, ConstructionType constructionType)
43 : SVGElement(tagName, document, constructionType)
46 registerAnimatedPropertiesForSVGGraphicsElement();
49 SVGGraphicsElement::~SVGGraphicsElement()
53 AffineTransform SVGGraphicsElement::getTransformToElement(SVGElement* target, ExceptionState& exceptionState)
55 AffineTransform ctm = getCTM(AllowStyleUpdate);
57 if (target && target->isSVGGraphicsElement()) {
58 AffineTransform targetCTM = toSVGGraphicsElement(target)->getCTM(AllowStyleUpdate);
59 if (!targetCTM.isInvertible()) {
60 exceptionState.throwDOMException(InvalidStateError, "The target transformation is not invertable.");
63 ctm = targetCTM.inverse() * ctm;
69 static AffineTransform computeCTM(SVGGraphicsElement* element, SVGElement::CTMScope mode, SVGGraphicsElement::StyleUpdateStrategy styleUpdateStrategy)
72 if (styleUpdateStrategy == SVGGraphicsElement::AllowStyleUpdate)
73 element->document().updateLayoutIgnorePendingStylesheets();
77 SVGElement* stopAtElement = mode == SVGGraphicsElement::NearestViewportScope ? element->nearestViewportElement() : 0;
78 for (Element* currentElement = element; currentElement; currentElement = currentElement->parentOrShadowHostElement()) {
79 if (!currentElement->isSVGElement())
82 ctm = toSVGElement(currentElement)->localCoordinateSpaceTransform(mode).multiply(ctm);
84 // For getCTM() computation, stop at the nearest viewport element
85 if (currentElement == stopAtElement)
92 AffineTransform SVGGraphicsElement::getCTM(StyleUpdateStrategy styleUpdateStrategy)
94 return computeCTM(this, NearestViewportScope, styleUpdateStrategy);
97 AffineTransform SVGGraphicsElement::getScreenCTM(StyleUpdateStrategy styleUpdateStrategy)
99 return computeCTM(this, ScreenScope, styleUpdateStrategy);
102 AffineTransform SVGGraphicsElement::animatedLocalTransform() const
104 AffineTransform matrix;
105 RenderStyle* style = renderer() ? renderer()->style() : 0;
107 // If CSS property was set, use that, otherwise fallback to attribute (if set).
108 if (style && style->hasTransform()) {
109 // Note: objectBoundingBox is an emptyRect for elements like pattern or clipPath.
110 // See the "Object bounding box units" section of http://dev.w3.org/csswg/css3-transforms/
111 TransformationMatrix transform;
112 style->applyTransform(transform, renderer()->objectBoundingBox());
114 // Flatten any 3D transform.
115 matrix = transform.toAffineTransform();
117 // CSS bakes the zoom factor into lengths, including translation components.
118 // In order to align CSS & SVG transforms, we need to invert this operation.
119 float zoom = style->effectiveZoom();
121 matrix.setE(matrix.e() / zoom);
122 matrix.setF(matrix.f() / zoom);
125 transformCurrentValue().concatenate(matrix);
128 if (m_supplementalTransform)
129 return *m_supplementalTransform * matrix;
133 AffineTransform* SVGGraphicsElement::supplementalTransform()
135 if (!m_supplementalTransform)
136 m_supplementalTransform = adoptPtr(new AffineTransform);
137 return m_supplementalTransform.get();
140 bool SVGGraphicsElement::isSupportedAttribute(const QualifiedName& attrName)
142 DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
143 if (supportedAttributes.isEmpty()) {
144 SVGTests::addSupportedAttributes(supportedAttributes);
145 supportedAttributes.add(SVGNames::transformAttr);
147 return supportedAttributes.contains<SVGAttributeHashTranslator>(attrName);
150 void SVGGraphicsElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
152 if (!isSupportedAttribute(name)) {
153 SVGElement::parseAttribute(name, value);
157 if (name == SVGNames::transformAttr) {
158 SVGTransformList newList;
159 newList.parse(value);
160 detachAnimatedTransformListWrappers(newList.size());
161 setTransformBaseValue(newList);
163 } else if (SVGTests::parseAttribute(name, value)) {
167 ASSERT_NOT_REACHED();
170 void SVGGraphicsElement::svgAttributeChanged(const QualifiedName& attrName)
172 if (!isSupportedAttribute(attrName)) {
173 SVGElement::svgAttributeChanged(attrName);
177 SVGElementInstance::InvalidationGuard invalidationGuard(this);
179 // Reattach so the isValid() check will be run again during renderer creation.
180 if (SVGTests::isKnownAttribute(attrName)) {
181 lazyReattachIfAttached();
185 RenderObject* object = renderer();
189 if (attrName == SVGNames::transformAttr) {
190 object->setNeedsTransformUpdate();
191 RenderSVGResource::markForLayoutAndParentResourceInvalidation(object);
195 ASSERT_NOT_REACHED();
198 static bool isViewportElement(Node* node)
200 return (node->hasTagName(SVGNames::svgTag)
201 || node->hasTagName(SVGNames::symbolTag)
202 || node->hasTagName(SVGNames::foreignObjectTag)
203 || node->hasTagName(SVGNames::imageTag));
206 SVGElement* SVGGraphicsElement::nearestViewportElement() const
208 for (Element* current = parentOrShadowHostElement(); current; current = current->parentOrShadowHostElement()) {
209 if (isViewportElement(current))
210 return toSVGElement(current);
216 SVGElement* SVGGraphicsElement::farthestViewportElement() const
218 SVGElement* farthest = 0;
219 for (Element* current = parentOrShadowHostElement(); current; current = current->parentOrShadowHostElement()) {
220 if (isViewportElement(current))
221 farthest = toSVGElement(current);
226 FloatRect SVGGraphicsElement::getBBox()
228 document().updateLayoutIgnorePendingStylesheets();
230 // FIXME: Eventually we should support getBBox for detached elements.
234 return renderer()->objectBoundingBox();
237 PassRefPtr<SVGRectTearOff> SVGGraphicsElement::getBBoxFromJavascript()
239 return SVGRectTearOff::create(SVGRect::create(getBBox()), 0, PropertyIsNotAnimVal);
242 RenderObject* SVGGraphicsElement::createRenderer(RenderStyle*)
244 // By default, any subclass is expected to do path-based drawing
245 return new RenderSVGPath(this);
248 void SVGGraphicsElement::toClipPath(Path& path)
250 updatePathFromGraphicsElement(this, path);
251 // FIXME: How do we know the element has done a layout?
252 path.transform(animatedLocalTransform());