Generate more SVG type checks and conversions.
[WebKit-https.git] / Source / WebCore / rendering / svg / SVGPathData.cpp
1 /*
2  * Copyright (C) Research In Motion Limited 2011. All rights reserved.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #include "config.h"
21 #include "SVGPathData.h"
22
23 #if ENABLE(SVG)
24 #include "Path.h"
25 #include "SVGCircleElement.h"
26 #include "SVGEllipseElement.h"
27 #include "SVGLineElement.h"
28 #include "SVGNames.h"
29 #include "SVGPathElement.h"
30 #include "SVGPathUtilities.h"
31 #include "SVGPolygonElement.h"
32 #include "SVGPolylineElement.h"
33 #include "SVGRectElement.h"
34 #include <wtf/HashMap.h>
35
36 namespace WebCore {
37
38 static void updatePathFromCircleElement(SVGElement* element, Path& path)
39 {
40     ASSERT(isSVGCircleElement(element));
41     SVGCircleElement* circle = toSVGCircleElement(element);
42
43     SVGLengthContext lengthContext(element);
44     float r = circle->r().value(lengthContext);
45     if (r > 0)
46         path.addEllipse(FloatRect(circle->cx().value(lengthContext) - r, circle->cy().value(lengthContext) - r, r * 2, r * 2));
47 }
48
49 static void updatePathFromEllipseElement(SVGElement* element, Path& path)
50 {
51     ASSERT(element->hasTagName(SVGNames::ellipseTag));
52     SVGEllipseElement* ellipse = static_cast<SVGEllipseElement*>(element);
53
54     SVGLengthContext lengthContext(element);
55     float rx = ellipse->rx().value(lengthContext);
56     if (rx <= 0)
57         return;
58     float ry = ellipse->ry().value(lengthContext);
59     if (ry <= 0)
60         return;
61     path.addEllipse(FloatRect(ellipse->cx().value(lengthContext) - rx, ellipse->cy().value(lengthContext) - ry, rx * 2, ry * 2));
62 }
63
64 static void updatePathFromLineElement(SVGElement* element, Path& path)
65 {
66     ASSERT(element->hasTagName(SVGNames::lineTag));
67     SVGLineElement* line = static_cast<SVGLineElement*>(element);
68
69     SVGLengthContext lengthContext(element);
70     path.moveTo(FloatPoint(line->x1().value(lengthContext), line->y1().value(lengthContext)));
71     path.addLineTo(FloatPoint(line->x2().value(lengthContext), line->y2().value(lengthContext)));
72 }
73
74 static void updatePathFromPathElement(SVGElement* element, Path& path)
75 {
76     buildPathFromByteStream(toSVGPathElement(element)->pathByteStream(), path);
77 }
78
79 static void updatePathFromPolygonElement(SVGElement* element, Path& path)
80 {
81     ASSERT(element->hasTagName(SVGNames::polygonTag));
82     SVGPolygonElement* polygon = static_cast<SVGPolygonElement*>(element);
83
84     SVGPointList& points = polygon->pointList();
85     if (points.isEmpty())
86         return;
87
88     path.moveTo(points.first());
89
90     unsigned size = points.size();
91     for (unsigned i = 1; i < size; ++i)
92         path.addLineTo(points.at(i));
93
94     path.closeSubpath();
95 }
96
97 static void updatePathFromPolylineElement(SVGElement* element, Path& path)
98 {
99     ASSERT(element->hasTagName(SVGNames::polylineTag));
100     SVGPolylineElement* polyline = static_cast<SVGPolylineElement*>(element);
101
102     SVGPointList& points = polyline->pointList();
103     if (points.isEmpty())
104         return;
105
106     path.moveTo(points.first());
107
108     unsigned size = points.size();
109     for (unsigned i = 1; i < size; ++i)
110         path.addLineTo(points.at(i));
111 }
112
113 static void updatePathFromRectElement(SVGElement* element, Path& path)
114 {
115     SVGRectElement* rect = toSVGRectElement(element);
116
117     SVGLengthContext lengthContext(element);
118     float width = rect->width().value(lengthContext);
119     if (width <= 0)
120         return;
121     float height = rect->height().value(lengthContext);
122     if (height <= 0)
123         return;
124     float x = rect->x().value(lengthContext);
125     float y = rect->y().value(lengthContext);
126     bool hasRx = rect->hasAttribute(SVGNames::rxAttr);
127     bool hasRy = rect->hasAttribute(SVGNames::ryAttr);
128     if (hasRx || hasRy) {
129         float rx = rect->rx().value(lengthContext);
130         float ry = rect->ry().value(lengthContext);
131         if (!hasRx)
132             rx = ry;
133         else if (!hasRy)
134             ry = rx;
135         // FIXME: We currently enforce using beziers here, as at least on CoreGraphics/Lion, as
136         // the native method uses a different line dash origin, causing svg/custom/dashOrigin.svg to fail.
137         // See bug https://bugs.webkit.org/show_bug.cgi?id=79932 which tracks this issue.
138         path.addRoundedRect(FloatRect(x, y, width, height), FloatSize(rx, ry), Path::PreferBezierRoundedRect);
139         return;
140     }
141
142     path.addRect(FloatRect(x, y, width, height));
143 }
144
145 void updatePathFromGraphicsElement(SVGElement* element, Path& path)
146 {
147     ASSERT(element);
148     ASSERT(path.isEmpty());
149
150     typedef void (*PathUpdateFunction)(SVGElement*, Path&);
151     static HashMap<AtomicStringImpl*, PathUpdateFunction>* map = 0;
152     if (!map) {
153         map = new HashMap<AtomicStringImpl*, PathUpdateFunction>;
154         map->set(SVGNames::circleTag.localName().impl(), updatePathFromCircleElement);
155         map->set(SVGNames::ellipseTag.localName().impl(), updatePathFromEllipseElement);
156         map->set(SVGNames::lineTag.localName().impl(), updatePathFromLineElement);
157         map->set(SVGNames::pathTag.localName().impl(), updatePathFromPathElement);
158         map->set(SVGNames::polygonTag.localName().impl(), updatePathFromPolygonElement);
159         map->set(SVGNames::polylineTag.localName().impl(), updatePathFromPolylineElement);
160         map->set(SVGNames::rectTag.localName().impl(), updatePathFromRectElement);
161     }
162
163     if (PathUpdateFunction pathUpdateFunction = map->get(element->localName().impl()))
164         (*pathUpdateFunction)(element, path);
165 }
166
167 } // namespace WebCore
168
169 #endif // ENABLE(SVG)