Rename SVGPathParserFactory to SVGPathUtilities and remove the obsolete singleton
[WebKit-https.git] / Source / WebCore / svg / SVGPathElement.cpp
1 /*
2  * Copyright (C) 2004, 2005, 2006, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3  * Copyright (C) 2004, 2005, 2006, 2007 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 "SVGPathElement.h"
25
26 #include "Attribute.h"
27 #include "RenderSVGPath.h"
28 #include "RenderSVGResource.h"
29 #include "SVGElementInstance.h"
30 #include "SVGNames.h"
31 #include "SVGPathSegArc.h"
32 #include "SVGPathSegClosePath.h"
33 #include "SVGPathSegCurvetoCubic.h"
34 #include "SVGPathSegCurvetoCubicSmooth.h"
35 #include "SVGPathSegCurvetoQuadratic.h"
36 #include "SVGPathSegCurvetoQuadraticSmooth.h"
37 #include "SVGPathSegLineto.h"
38 #include "SVGPathSegLinetoHorizontal.h"
39 #include "SVGPathSegLinetoVertical.h"
40 #include "SVGPathSegList.h"
41 #include "SVGPathSegListBuilder.h"
42 #include "SVGPathSegListPropertyTearOff.h"
43 #include "SVGPathSegMoveto.h"
44 #include "SVGPathUtilities.h"
45 #include "SVGSVGElement.h"
46
47 namespace WebCore {
48
49 // Define custom animated property 'd'.
50 const SVGPropertyInfo* SVGPathElement::dPropertyInfo()
51 {
52     static const SVGPropertyInfo* s_propertyInfo = 0;
53     if (!s_propertyInfo) {
54         s_propertyInfo = new SVGPropertyInfo(AnimatedPath,
55                                              SVGNames::dAttr,
56                                              SVGNames::dAttr.localName(),
57                                              &SVGPathElement::synchronizeD,
58                                              &SVGPathElement::lookupOrCreateDWrapper);
59     }
60     return s_propertyInfo;
61 }
62
63 // Animated property definitions
64 DEFINE_ANIMATED_NUMBER(SVGPathElement, SVGNames::pathLengthAttr, PathLength, pathLength)
65 DEFINE_ANIMATED_BOOLEAN(SVGPathElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired)
66
67 BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGPathElement)
68     REGISTER_LOCAL_ANIMATED_PROPERTY(d)
69     REGISTER_LOCAL_ANIMATED_PROPERTY(pathLength)
70     REGISTER_LOCAL_ANIMATED_PROPERTY(externalResourcesRequired)
71     REGISTER_PARENT_ANIMATED_PROPERTIES(SVGStyledTransformableElement)
72     REGISTER_PARENT_ANIMATED_PROPERTIES(SVGTests)
73 END_REGISTER_ANIMATED_PROPERTIES
74
75 inline SVGPathElement::SVGPathElement(const QualifiedName& tagName, Document* document)
76     : SVGStyledTransformableElement(tagName, document)
77     , m_pathByteStream(SVGPathByteStream::create())
78     , m_pathSegList(PathSegUnalteredRole)
79     , m_cachedBBoxRectIsValid(false)
80     , m_isAnimValObserved(false)
81 {
82     ASSERT(hasTagName(SVGNames::pathTag));
83     registerAnimatedPropertiesForSVGPathElement();
84 }
85
86 PassRefPtr<SVGPathElement> SVGPathElement::create(const QualifiedName& tagName, Document* document)
87 {
88     return adoptRef(new SVGPathElement(tagName, document));
89 }
90
91 float SVGPathElement::getTotalLength()
92 {
93     float totalLength = 0;
94     getTotalLengthOfSVGPathByteStream(pathByteStream(), totalLength);
95     return totalLength;
96 }
97
98 FloatPoint SVGPathElement::getPointAtLength(float length)
99 {
100     FloatPoint point;
101     getPointAtLengthOfSVGPathByteStream(pathByteStream(), length, point);
102     return point;
103 }
104
105 unsigned SVGPathElement::getPathSegAtLength(float length)
106 {
107     unsigned pathSeg = 0;
108     getSVGPathSegAtLengthFromSVGPathByteStream(pathByteStream(), length, pathSeg);
109     return pathSeg;
110 }
111
112 PassRefPtr<SVGPathSegClosePath> SVGPathElement::createSVGPathSegClosePath(SVGPathSegRole role)
113 {
114     return SVGPathSegClosePath::create(this, role);
115 }
116
117 PassRefPtr<SVGPathSegMovetoAbs> SVGPathElement::createSVGPathSegMovetoAbs(float x, float y, SVGPathSegRole role)
118 {
119     return SVGPathSegMovetoAbs::create(this, role, x, y);
120 }
121
122 PassRefPtr<SVGPathSegMovetoRel> SVGPathElement::createSVGPathSegMovetoRel(float x, float y, SVGPathSegRole role)
123 {
124     return SVGPathSegMovetoRel::create(this, role, x, y);
125 }
126
127 PassRefPtr<SVGPathSegLinetoAbs> SVGPathElement::createSVGPathSegLinetoAbs(float x, float y, SVGPathSegRole role)
128 {
129     return SVGPathSegLinetoAbs::create(this, role, x, y);
130 }
131
132 PassRefPtr<SVGPathSegLinetoRel> SVGPathElement::createSVGPathSegLinetoRel(float x, float y, SVGPathSegRole role)
133 {
134     return SVGPathSegLinetoRel::create(this, role, x, y);
135 }
136
137 PassRefPtr<SVGPathSegCurvetoCubicAbs> SVGPathElement::createSVGPathSegCurvetoCubicAbs(float x, float y, float x1, float y1, float x2, float y2, SVGPathSegRole role)
138 {
139     return SVGPathSegCurvetoCubicAbs::create(this, role, x, y, x1, y1, x2, y2);
140 }
141
142 PassRefPtr<SVGPathSegCurvetoCubicRel> SVGPathElement::createSVGPathSegCurvetoCubicRel(float x, float y, float x1, float y1, float x2, float y2, SVGPathSegRole role)
143 {
144     return SVGPathSegCurvetoCubicRel::create(this, role, x, y, x1, y1, x2, y2);
145 }
146
147 PassRefPtr<SVGPathSegCurvetoQuadraticAbs> SVGPathElement::createSVGPathSegCurvetoQuadraticAbs(float x, float y, float x1, float y1, SVGPathSegRole role)
148 {
149     return SVGPathSegCurvetoQuadraticAbs::create(this, role, x, y, x1, y1);
150 }
151
152 PassRefPtr<SVGPathSegCurvetoQuadraticRel> SVGPathElement::createSVGPathSegCurvetoQuadraticRel(float x, float y, float x1, float y1, SVGPathSegRole role)
153 {
154     return SVGPathSegCurvetoQuadraticRel::create(this, role, x, y, x1, y1);
155 }
156
157 PassRefPtr<SVGPathSegArcAbs> SVGPathElement::createSVGPathSegArcAbs(float x, float y, float r1, float r2, float angle, bool largeArcFlag, bool sweepFlag, SVGPathSegRole role)
158 {
159     return SVGPathSegArcAbs::create(this, role, x, y, r1, r2, angle, largeArcFlag, sweepFlag);
160 }
161
162 PassRefPtr<SVGPathSegArcRel> SVGPathElement::createSVGPathSegArcRel(float x, float y, float r1, float r2, float angle, bool largeArcFlag, bool sweepFlag, SVGPathSegRole role)
163 {
164     return SVGPathSegArcRel::create(this, role, x, y, r1, r2, angle, largeArcFlag, sweepFlag);
165 }
166
167 PassRefPtr<SVGPathSegLinetoHorizontalAbs> SVGPathElement::createSVGPathSegLinetoHorizontalAbs(float x, SVGPathSegRole role)
168 {
169     return SVGPathSegLinetoHorizontalAbs::create(this, role, x);
170 }
171
172 PassRefPtr<SVGPathSegLinetoHorizontalRel> SVGPathElement::createSVGPathSegLinetoHorizontalRel(float x, SVGPathSegRole role)
173 {
174     return SVGPathSegLinetoHorizontalRel::create(this, role, x);
175 }
176
177 PassRefPtr<SVGPathSegLinetoVerticalAbs> SVGPathElement::createSVGPathSegLinetoVerticalAbs(float y, SVGPathSegRole role)
178 {
179     return SVGPathSegLinetoVerticalAbs::create(this, role, y);
180 }
181
182 PassRefPtr<SVGPathSegLinetoVerticalRel> SVGPathElement::createSVGPathSegLinetoVerticalRel(float y, SVGPathSegRole role)
183 {
184     return SVGPathSegLinetoVerticalRel::create(this, role, y);
185 }
186
187 PassRefPtr<SVGPathSegCurvetoCubicSmoothAbs> SVGPathElement::createSVGPathSegCurvetoCubicSmoothAbs(float x, float y, float x2, float y2, SVGPathSegRole role)
188 {
189     return SVGPathSegCurvetoCubicSmoothAbs::create(this, role, x, y, x2, y2);
190 }
191
192 PassRefPtr<SVGPathSegCurvetoCubicSmoothRel> SVGPathElement::createSVGPathSegCurvetoCubicSmoothRel(float x, float y, float x2, float y2, SVGPathSegRole role)
193 {
194     return SVGPathSegCurvetoCubicSmoothRel::create(this, role, x, y, x2, y2);
195 }
196
197 PassRefPtr<SVGPathSegCurvetoQuadraticSmoothAbs> SVGPathElement::createSVGPathSegCurvetoQuadraticSmoothAbs(float x, float y, SVGPathSegRole role)
198 {
199     return SVGPathSegCurvetoQuadraticSmoothAbs::create(this, role, x, y);
200 }
201
202 PassRefPtr<SVGPathSegCurvetoQuadraticSmoothRel> SVGPathElement::createSVGPathSegCurvetoQuadraticSmoothRel(float x, float y, SVGPathSegRole role)
203 {
204     return SVGPathSegCurvetoQuadraticSmoothRel::create(this, role, x, y);
205 }
206
207 bool SVGPathElement::isSupportedAttribute(const QualifiedName& attrName)
208 {
209     DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
210     if (supportedAttributes.isEmpty()) {
211         SVGTests::addSupportedAttributes(supportedAttributes);
212         SVGLangSpace::addSupportedAttributes(supportedAttributes);
213         SVGExternalResourcesRequired::addSupportedAttributes(supportedAttributes);
214         supportedAttributes.add(SVGNames::dAttr);
215         supportedAttributes.add(SVGNames::pathLengthAttr);
216     }
217     return supportedAttributes.contains<QualifiedName, SVGAttributeHashTranslator>(attrName);
218 }
219
220 void SVGPathElement::parseAttribute(Attribute* attr)
221 {
222     if (!isSupportedAttribute(attr->name())) {
223         SVGStyledTransformableElement::parseAttribute(attr);
224         return;
225     }
226
227     if (attr->name() == SVGNames::dAttr) {
228         if (!buildSVGPathByteStreamFromString(attr->value(), m_pathByteStream.get(), UnalteredParsing))
229             document()->accessSVGExtensions()->reportError("Problem parsing d=\"" + attr->value() + "\"");
230         return;
231     }
232
233     if (attr->name() == SVGNames::pathLengthAttr) {
234         setPathLengthBaseValue(attr->value().toFloat());
235         if (pathLengthBaseValue() < 0)
236             document()->accessSVGExtensions()->reportError("A negative value for path attribute <pathLength> is not allowed");
237         return;
238     }
239
240     if (SVGTests::parseAttribute(attr))
241         return;
242     if (SVGLangSpace::parseAttribute(attr))
243         return;
244     if (SVGExternalResourcesRequired::parseAttribute(attr))
245         return;
246
247     ASSERT_NOT_REACHED();
248 }
249
250 void SVGPathElement::svgAttributeChanged(const QualifiedName& attrName)
251 {
252     if (!isSupportedAttribute(attrName)) {
253         SVGStyledTransformableElement::svgAttributeChanged(attrName);
254         return;
255     }
256
257     SVGElementInstance::InvalidationGuard invalidationGuard(this);
258     
259     if (SVGTests::handleAttributeChange(this, attrName))
260         return;
261
262     RenderSVGPath* renderer = static_cast<RenderSVGPath*>(this->renderer());
263
264     if (attrName == SVGNames::dAttr) {
265         if (m_pathSegList.shouldSynchronize && !SVGAnimatedProperty::lookupWrapper<SVGPathElement, SVGAnimatedPathSegListPropertyTearOff, true>(this, dPropertyInfo())->isAnimating()) {
266             SVGPathSegList newList(PathSegUnalteredRole);
267             buildSVGPathSegListFromByteStream(m_pathByteStream.get(), this, newList, UnalteredParsing);
268             m_pathSegList.value = newList;
269             m_cachedBBoxRectIsValid = false;
270         }
271
272         if (renderer)
273             renderer->setNeedsShapeUpdate();
274     }
275
276     if (renderer)
277         RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
278 }
279
280 SVGPathByteStream* SVGPathElement::pathByteStream() const
281 {
282     SVGAnimatedProperty* property = SVGAnimatedProperty::lookupWrapper<SVGPathElement, SVGAnimatedPathSegListPropertyTearOff, true>(this, dPropertyInfo());
283     if (!property || !property->isAnimating())
284         return m_pathByteStream.get();
285     return static_cast<SVGAnimatedPathSegListPropertyTearOff*>(property)->animatedPathByteStream();
286 }
287
288 PassRefPtr<SVGAnimatedProperty> SVGPathElement::lookupOrCreateDWrapper(void* contextElement)
289 {
290     ASSERT(contextElement);
291     SVGPathElement* ownerType = static_cast<SVGPathElement*>(contextElement);
292
293     if (SVGAnimatedProperty* property = SVGAnimatedProperty::lookupWrapper<SVGPathElement, SVGAnimatedPathSegListPropertyTearOff, true>(ownerType, dPropertyInfo()))
294         return property;
295
296     // Build initial SVGPathSegList.
297     buildSVGPathSegListFromByteStream(ownerType->m_pathByteStream.get(), ownerType, ownerType->m_pathSegList.value, UnalteredParsing);
298
299     return SVGAnimatedProperty::lookupOrCreateWrapper<SVGPathElement, SVGAnimatedPathSegListPropertyTearOff, SVGPathSegList, true>
300            (ownerType, dPropertyInfo(), ownerType->m_pathSegList.value);
301 }
302
303 void SVGPathElement::synchronizeD(void* contextElement)
304 {
305     ASSERT(contextElement);
306     SVGPathElement* ownerType = static_cast<SVGPathElement*>(contextElement);
307     if (!ownerType->m_pathSegList.shouldSynchronize)
308         return;
309     SVGAnimatedPropertySynchronizer<true>::synchronize(ownerType, dPropertyInfo()->attributeName, ownerType->m_pathSegList.value.valueAsString());
310 }
311
312 SVGPathSegListPropertyTearOff* SVGPathElement::pathSegList()
313 {
314     m_pathSegList.shouldSynchronize = true;
315     return static_cast<SVGPathSegListPropertyTearOff*>(static_pointer_cast<SVGAnimatedPathSegListPropertyTearOff>(lookupOrCreateDWrapper(this))->baseVal());
316 }
317
318 SVGPathSegListPropertyTearOff* SVGPathElement::normalizedPathSegList()
319 {
320     // FIXME: https://bugs.webkit.org/show_bug.cgi?id=15412 - Implement normalized path segment lists!
321     return 0;
322 }
323
324 SVGPathSegListPropertyTearOff* SVGPathElement::animatedPathSegList()
325 {
326     m_pathSegList.shouldSynchronize = true;
327     m_isAnimValObserved = true;
328     return static_cast<SVGPathSegListPropertyTearOff*>(static_pointer_cast<SVGAnimatedPathSegListPropertyTearOff>(lookupOrCreateDWrapper(this))->animVal());
329 }
330
331 SVGPathSegListPropertyTearOff* SVGPathElement::animatedNormalizedPathSegList()
332 {
333     // FIXME: https://bugs.webkit.org/show_bug.cgi?id=15412 - Implement normalized path segment lists!
334     return 0;
335 }
336
337 void SVGPathElement::pathSegListChanged(SVGPathSegRole role)
338 {
339     switch (role) {
340     case PathSegNormalizedRole:
341         // FIXME: https://bugs.webkit.org/show_bug.cgi?id=15412 - Implement normalized path segment lists!
342         break;
343     case PathSegUnalteredRole:
344         buildSVGPathByteStreamFromSVGPathSegList(m_pathSegList.value, m_pathByteStream.get(), UnalteredParsing);
345         break;
346     case PathSegUndefinedRole:
347         return;
348     }
349
350     invalidateSVGAttributes();
351     
352     m_cachedBBoxRectIsValid = false;
353
354     RenderSVGPath* renderer = static_cast<RenderSVGPath*>(this->renderer());
355     if (!renderer)
356         return;
357
358     renderer->setNeedsShapeUpdate();
359     RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer);
360 }
361
362 FloatRect SVGPathElement::getBBox(StyleUpdateStrategy styleUpdateStrategy)
363 {
364     if (styleUpdateStrategy == AllowStyleUpdate)
365         this->document()->updateLayoutIgnorePendingStylesheets();
366
367     RenderSVGPath* renderer = static_cast<RenderSVGPath*>(this->renderer());
368
369     // FIXME: Eventually we should support getBBox for detached elements.
370     if (!renderer)
371         return FloatRect();
372
373     if (!m_cachedBBoxRectIsValid) {
374         m_cachedBBoxRect = renderer->path().boundingRect();
375         m_cachedBBoxRectIsValid = true;
376     }
377     
378     return m_cachedBBoxRect;
379 }
380
381 RenderObject* SVGPathElement::createRenderer(RenderArena* arena, RenderStyle*)
382 {
383     // By default, any subclass is expected to do path-based drawing
384     return new (arena) RenderSVGPath(this);
385 }
386
387 }
388
389 #endif // ENABLE(SVG)