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#include "Path.h"
24#include "RenderElement.h"
25#include "RenderStyle.h"
26#include "SVGCircleElement.h"
27#include "SVGEllipseElement.h"
28#include "SVGLengthContext.h"
29#include "SVGLineElement.h"
30#include "SVGNames.h"
31#include "SVGPathElement.h"
32#include "SVGPathUtilities.h"
33#include "SVGPoint.h"
34#include "SVGPointList.h"
35#include "SVGPolygonElement.h"
36#include "SVGPolylineElement.h"
37#include "SVGRectElement.h"
38#include <wtf/HashMap.h>
39
40namespace WebCore {
41
42static Path pathFromCircleElement(const SVGElement& element)
43{
44 ASSERT(is<SVGCircleElement>(element));
45
46 RenderElement* renderer = element.renderer();
47 if (!renderer)
48 return { };
49
50 Path path;
51 auto& style = renderer->style();
52 SVGLengthContext lengthContext(&element);
53 float r = lengthContext.valueForLength(style.svgStyle().r());
54 if (r > 0) {
55 float cx = lengthContext.valueForLength(style.svgStyle().cx(), LengthModeWidth);
56 float cy = lengthContext.valueForLength(style.svgStyle().cy(), LengthModeHeight);
57 path.addEllipse(FloatRect(cx - r, cy - r, r * 2, r * 2));
58 }
59 return path;
60}
61
62static Path pathFromEllipseElement(const SVGElement& element)
63{
64 RenderElement* renderer = element.renderer();
65 if (!renderer)
66 return { };
67
68 auto& style = renderer->style();
69 SVGLengthContext lengthContext(&element);
70 float rx = lengthContext.valueForLength(style.svgStyle().rx(), LengthModeWidth);
71 if (rx <= 0)
72 return { };
73
74 float ry = lengthContext.valueForLength(style.svgStyle().ry(), LengthModeHeight);
75 if (ry <= 0)
76 return { };
77
78 Path path;
79 float cx = lengthContext.valueForLength(style.svgStyle().cx(), LengthModeWidth);
80 float cy = lengthContext.valueForLength(style.svgStyle().cy(), LengthModeHeight);
81 path.addEllipse(FloatRect(cx - rx, cy - ry, rx * 2, ry * 2));
82 return path;
83}
84
85static Path pathFromLineElement(const SVGElement& element)
86{
87 Path path;
88 const auto& line = downcast<SVGLineElement>(element);
89
90 SVGLengthContext lengthContext(&element);
91 path.moveTo(FloatPoint(line.x1().value(lengthContext), line.y1().value(lengthContext)));
92 path.addLineTo(FloatPoint(line.x2().value(lengthContext), line.y2().value(lengthContext)));
93 return path;
94}
95
96static Path pathFromPathElement(const SVGElement& element)
97{
98 return downcast<SVGPathElement>(element).path();
99}
100
101static Path pathFromPolygonElement(const SVGElement& element)
102{
103 auto& points = downcast<SVGPolygonElement>(element).points().items();
104 if (points.isEmpty())
105 return { };
106
107 Path path;
108 path.moveTo(points.first()->value());
109
110 unsigned size = points.size();
111 for (unsigned i = 1; i < size; ++i)
112 path.addLineTo(points.at(i)->value());
113
114 path.closeSubpath();
115 return path;
116}
117
118static Path pathFromPolylineElement(const SVGElement& element)
119{
120 auto& points = downcast<SVGPolylineElement>(element).points().items();
121 if (points.isEmpty())
122 return { };
123
124 Path path;
125 path.moveTo(points.first()->value());
126
127 unsigned size = points.size();
128 for (unsigned i = 1; i < size; ++i)
129 path.addLineTo(points.at(i)->value());
130 return path;
131}
132
133static Path pathFromRectElement(const SVGElement& element)
134{
135 RenderElement* renderer = element.renderer();
136 if (!renderer)
137 return { };
138
139 auto& style = renderer->style();
140 SVGLengthContext lengthContext(&element);
141 float width = lengthContext.valueForLength(style.width(), LengthModeWidth);
142 if (width <= 0)
143 return { };
144
145 float height = lengthContext.valueForLength(style.height(), LengthModeHeight);
146 if (height <= 0)
147 return { };
148
149 Path path;
150 float x = lengthContext.valueForLength(style.svgStyle().x(), LengthModeWidth);
151 float y = lengthContext.valueForLength(style.svgStyle().y(), LengthModeHeight);
152 float rx = lengthContext.valueForLength(style.svgStyle().rx(), LengthModeWidth);
153 float ry = lengthContext.valueForLength(style.svgStyle().ry(), LengthModeHeight);
154 bool hasRx = rx > 0;
155 bool hasRy = ry > 0;
156 if (hasRx || hasRy) {
157 if (!hasRx)
158 rx = ry;
159 else if (!hasRy)
160 ry = rx;
161 // FIXME: We currently enforce using beziers here, as at least on CoreGraphics/Lion, as
162 // the native method uses a different line dash origin, causing svg/custom/dashOrigin.svg to fail.
163 // See bug https://bugs.webkit.org/show_bug.cgi?id=79932 which tracks this issue.
164 path.addRoundedRect(FloatRect(x, y, width, height), FloatSize(rx, ry), Path::PreferBezierRoundedRect);
165 return path;
166 }
167
168 path.addRect(FloatRect(x, y, width, height));
169 return path;
170}
171
172Path pathFromGraphicsElement(const SVGElement* element)
173{
174 ASSERT(element);
175
176 typedef Path (*PathFromFunction)(const SVGElement&);
177 static HashMap<AtomicStringImpl*, PathFromFunction>* map = 0;
178 if (!map) {
179 map = new HashMap<AtomicStringImpl*, PathFromFunction>;
180 map->set(SVGNames::circleTag->localName().impl(), pathFromCircleElement);
181 map->set(SVGNames::ellipseTag->localName().impl(), pathFromEllipseElement);
182 map->set(SVGNames::lineTag->localName().impl(), pathFromLineElement);
183 map->set(SVGNames::pathTag->localName().impl(), pathFromPathElement);
184 map->set(SVGNames::polygonTag->localName().impl(), pathFromPolygonElement);
185 map->set(SVGNames::polylineTag->localName().impl(), pathFromPolylineElement);
186 map->set(SVGNames::rectTag->localName().impl(), pathFromRectElement);
187 }
188
189 if (PathFromFunction pathFromFunction = map->get(element->localName().impl()))
190 return (*pathFromFunction)(*element);
191
192 return { };
193}
194
195} // namespace WebCore
196