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 "SVGCircleElement.h"
25#include "SVGEllipseElement.h"
26#include "SVGLineElement.h"
27#include "SVGNames.h"
28#include "SVGPathElement.h"
29#include "SVGPathUtilities.h"
30#include "SVGPolygonElement.h"
31#include "SVGPolylineElement.h"
32#include "SVGRectElement.h"
33#include <wtf/HashMap.h>
34
35namespace WebCore {
36
37static void updatePathFromCircleElement(SVGElement* element, Path& path)
38{
39    ASSERT(isSVGCircleElement(element));
40    SVGCircleElement* circle = toSVGCircleElement(element);
41
42    SVGLengthContext lengthContext(element);
43    float r = circle->r().value(lengthContext);
44    if (r > 0)
45        path.addEllipse(FloatRect(circle->cx().value(lengthContext) - r, circle->cy().value(lengthContext) - r, r * 2, r * 2));
46}
47
48static void updatePathFromEllipseElement(SVGElement* element, Path& path)
49{
50    SVGEllipseElement* ellipse = toSVGEllipseElement(element);
51
52    SVGLengthContext lengthContext(element);
53    float rx = ellipse->rx().value(lengthContext);
54    if (rx <= 0)
55        return;
56    float ry = ellipse->ry().value(lengthContext);
57    if (ry <= 0)
58        return;
59    path.addEllipse(FloatRect(ellipse->cx().value(lengthContext) - rx, ellipse->cy().value(lengthContext) - ry, rx * 2, ry * 2));
60}
61
62static void updatePathFromLineElement(SVGElement* element, Path& path)
63{
64    SVGLineElement* line = toSVGLineElement(element);
65
66    SVGLengthContext lengthContext(element);
67    path.moveTo(FloatPoint(line->x1().value(lengthContext), line->y1().value(lengthContext)));
68    path.addLineTo(FloatPoint(line->x2().value(lengthContext), line->y2().value(lengthContext)));
69}
70
71static void updatePathFromPathElement(SVGElement* element, Path& path)
72{
73    buildPathFromByteStream(toSVGPathElement(element)->pathByteStream(), path);
74}
75
76static void updatePathFromPolygonElement(SVGElement* element, Path& path)
77{
78    SVGPointList& points = toSVGPolygonElement(element)->animatedPoints()->values();
79    if (points.isEmpty())
80        return;
81
82    path.moveTo(points.first());
83
84    unsigned size = points.size();
85    for (unsigned i = 1; i < size; ++i)
86        path.addLineTo(points.at(i));
87
88    path.closeSubpath();
89}
90
91static void updatePathFromPolylineElement(SVGElement* element, Path& path)
92{
93    SVGPointList& points = toSVGPolylineElement(element)->animatedPoints()->values();
94    if (points.isEmpty())
95        return;
96
97    path.moveTo(points.first());
98
99    unsigned size = points.size();
100    for (unsigned i = 1; i < size; ++i)
101        path.addLineTo(points.at(i));
102}
103
104static void updatePathFromRectElement(SVGElement* element, Path& path)
105{
106    SVGRectElement* rect = toSVGRectElement(element);
107
108    SVGLengthContext lengthContext(element);
109    float width = rect->width().value(lengthContext);
110    if (width <= 0)
111        return;
112    float height = rect->height().value(lengthContext);
113    if (height <= 0)
114        return;
115    float x = rect->x().value(lengthContext);
116    float y = rect->y().value(lengthContext);
117    float rx = rect->rx().value(lengthContext);
118    float ry = rect->ry().value(lengthContext);
119    bool hasRx = rx > 0;
120    bool hasRy = ry > 0;
121    if (hasRx || hasRy) {
122        if (!hasRx)
123            rx = ry;
124        else if (!hasRy)
125            ry = rx;
126        // FIXME: We currently enforce using beziers here, as at least on CoreGraphics/Lion, as
127        // the native method uses a different line dash origin, causing svg/custom/dashOrigin.svg to fail.
128        // See bug https://bugs.webkit.org/show_bug.cgi?id=79932 which tracks this issue.
129        path.addRoundedRect(FloatRect(x, y, width, height), FloatSize(rx, ry), Path::PreferBezierRoundedRect);
130        return;
131    }
132
133    path.addRect(FloatRect(x, y, width, height));
134}
135
136void updatePathFromGraphicsElement(SVGElement* element, Path& path)
137{
138    ASSERT(element);
139    ASSERT(path.isEmpty());
140
141    typedef void (*PathUpdateFunction)(SVGElement*, Path&);
142    static HashMap<AtomicStringImpl*, PathUpdateFunction>* map = 0;
143    if (!map) {
144        map = new HashMap<AtomicStringImpl*, PathUpdateFunction>;
145        map->set(SVGNames::circleTag.localName().impl(), &updatePathFromCircleElement);
146        map->set(SVGNames::ellipseTag.localName().impl(), &updatePathFromEllipseElement);
147        map->set(SVGNames::lineTag.localName().impl(), &updatePathFromLineElement);
148        map->set(SVGNames::pathTag.localName().impl(), &updatePathFromPathElement);
149        map->set(SVGNames::polygonTag.localName().impl(), &updatePathFromPolygonElement);
150        map->set(SVGNames::polylineTag.localName().impl(), &updatePathFromPolylineElement);
151        map->set(SVGNames::rectTag.localName().impl(), &updatePathFromRectElement);
152    }
153
154    if (PathUpdateFunction pathUpdateFunction = map->get(element->localName().impl()))
155        (*pathUpdateFunction)(element, path);
156}
157
158} // namespace WebCore
159