SVG should support transform-origin and relative values
[WebKit-https.git] / Source / WebCore / svg / SVGStyledTransformableElement.cpp
1 /*
2  * Copyright (C) 2004, 2005, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3  * Copyright (C) 2004, 2005, 2006 Rob Buis <buis@kde.org>
4  *
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.
9  *
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.
14  *
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.
19  */
20
21 #include "config.h"
22
23 #if ENABLE(SVG)
24 #include "SVGStyledTransformableElement.h"
25
26 #include "AffineTransform.h"
27 #include "Attribute.h"
28 #include "RenderSVGPath.h"
29 #include "RenderSVGResource.h"
30 #include "SVGElementInstance.h"
31 #include "SVGNames.h"
32 #include "SVGPathData.h"
33
34 namespace WebCore {
35
36 // Animated property definitions
37 DEFINE_ANIMATED_TRANSFORM_LIST(SVGStyledTransformableElement, SVGNames::transformAttr, Transform, transform)
38
39 BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGStyledTransformableElement)
40     REGISTER_LOCAL_ANIMATED_PROPERTY(transform)
41     REGISTER_PARENT_ANIMATED_PROPERTIES(SVGStyledLocatableElement)
42 END_REGISTER_ANIMATED_PROPERTIES
43
44 SVGStyledTransformableElement::SVGStyledTransformableElement(const QualifiedName& tagName, Document* document, ConstructionType constructionType)
45     : SVGStyledLocatableElement(tagName, document, constructionType)
46 {
47     registerAnimatedPropertiesForSVGStyledTransformableElement();
48 }
49
50 SVGStyledTransformableElement::~SVGStyledTransformableElement()
51 {
52 }
53
54 AffineTransform SVGStyledTransformableElement::getCTM(StyleUpdateStrategy styleUpdateStrategy)
55 {
56     return SVGLocatable::computeCTM(this, SVGLocatable::NearestViewportScope, styleUpdateStrategy);
57 }
58
59 AffineTransform SVGStyledTransformableElement::getScreenCTM(StyleUpdateStrategy styleUpdateStrategy)
60 {
61     return SVGLocatable::computeCTM(this, SVGLocatable::ScreenScope, styleUpdateStrategy);
62 }
63
64 AffineTransform SVGStyledTransformableElement::animatedLocalTransform() const
65 {
66     AffineTransform matrix;
67     RenderStyle* style = renderer() ? renderer()->style() : 0;
68
69     // If CSS property was set, use that, otherwise fallback to attribute (if set).
70     if (style && style->hasTransform()) {
71         // Note: objectBoundingBox is an emptyRect for elements like pattern or clipPath.
72         // See the "Object bounding box units" section of http://dev.w3.org/csswg/css3-transforms/
73         TransformationMatrix transform;
74         style->applyTransform(transform, renderer()->objectBoundingBox());
75
76         // Flatten any 3D transform.
77         matrix = transform.toAffineTransform();
78     } else
79         transform().concatenate(matrix);
80
81     if (m_supplementalTransform)
82         return *m_supplementalTransform * matrix;
83     return matrix;
84 }
85
86 AffineTransform* SVGStyledTransformableElement::supplementalTransform()
87 {
88     if (!m_supplementalTransform)
89         m_supplementalTransform = adoptPtr(new AffineTransform);
90     return m_supplementalTransform.get();
91 }
92
93 bool SVGStyledTransformableElement::isSupportedAttribute(const QualifiedName& attrName)
94 {
95     DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
96     if (supportedAttributes.isEmpty())
97         supportedAttributes.add(SVGNames::transformAttr);
98     return supportedAttributes.contains<QualifiedName, SVGAttributeHashTranslator>(attrName);
99 }
100
101 void SVGStyledTransformableElement::parseAttribute(Attribute* attr)
102 {
103     if (!isSupportedAttribute(attr->name())) {
104         SVGStyledLocatableElement::parseAttribute(attr);
105         return;
106     }
107
108     if (attr->name() == SVGNames::transformAttr) {
109         SVGTransformList newList;
110         if (!SVGTransformable::parseTransformAttribute(newList, attr->value()))
111             newList.clear();
112         detachAnimatedTransformListWrappers(newList.size());
113         setTransformBaseValue(newList);
114         return;
115     }
116
117     ASSERT_NOT_REACHED();
118 }
119
120 void SVGStyledTransformableElement::svgAttributeChanged(const QualifiedName& attrName)
121 {
122     if (!isSupportedAttribute(attrName)) {
123         SVGStyledLocatableElement::svgAttributeChanged(attrName);
124         return;
125     }
126
127     SVGElementInstance::InvalidationGuard invalidationGuard(this);
128
129     RenderObject* object = renderer();
130     if (!object)
131         return;
132
133     if (attrName == SVGNames::transformAttr) {
134         object->setNeedsTransformUpdate();
135         RenderSVGResource::markForLayoutAndParentResourceInvalidation(object);
136         return;
137     }
138
139     ASSERT_NOT_REACHED();
140 }
141
142 SVGElement* SVGStyledTransformableElement::nearestViewportElement() const
143 {
144     return SVGTransformable::nearestViewportElement(this);
145 }
146
147 SVGElement* SVGStyledTransformableElement::farthestViewportElement() const
148 {
149     return SVGTransformable::farthestViewportElement(this);
150 }
151
152 FloatRect SVGStyledTransformableElement::getBBox(StyleUpdateStrategy styleUpdateStrategy)
153 {
154     return SVGTransformable::getBBox(this, styleUpdateStrategy);
155 }
156
157 RenderObject* SVGStyledTransformableElement::createRenderer(RenderArena* arena, RenderStyle*)
158 {
159     // By default, any subclass is expected to do path-based drawing
160     return new (arena) RenderSVGPath(this);
161 }
162
163 void SVGStyledTransformableElement::toClipPath(Path& path)
164 {
165     updatePathFromGraphicsElement(this, path);
166     // FIXME: How do we know the element has done a layout?
167     path.transform(animatedLocalTransform());
168 }
169
170 }
171
172 #endif // ENABLE(SVG)