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 "core/rendering/svg/SVGPathData.h"
22
23#include "core/SVGNames.h"
24#include "core/svg/SVGCircleElement.h"
25#include "core/svg/SVGEllipseElement.h"
26#include "core/svg/SVGLineElement.h"
27#include "core/svg/SVGPathElement.h"
28#include "core/svg/SVGPathUtilities.h"
29#include "core/svg/SVGPolygonElement.h"
30#include "core/svg/SVGPolylineElement.h"
31#include "core/svg/SVGRectElement.h"
32#include "platform/graphics/Path.h"
33#include "wtf/HashMap.h"
34
35namespace blink {
36
37using namespace SVGNames;
38
39static void updatePathFromCircleElement(SVGElement* element, Path& path)
40{
41    SVGCircleElement* circle = toSVGCircleElement(element);
42
43    SVGLengthContext lengthContext(element);
44    float r = circle->r()->currentValue()->value(lengthContext);
45    if (r > 0)
46        path.addEllipse(FloatRect(circle->cx()->currentValue()->value(lengthContext) - r, circle->cy()->currentValue()->value(lengthContext) - r, r * 2, r * 2));
47}
48
49static void updatePathFromEllipseElement(SVGElement* element, Path& path)
50{
51    SVGEllipseElement* ellipse = toSVGEllipseElement(element);
52
53    SVGLengthContext lengthContext(element);
54    float rx = ellipse->rx()->currentValue()->value(lengthContext);
55    if (rx < 0)
56        return;
57    float ry = ellipse->ry()->currentValue()->value(lengthContext);
58    if (ry < 0)
59        return;
60    if (!rx && !ry)
61        return;
62
63    path.addEllipse(FloatRect(ellipse->cx()->currentValue()->value(lengthContext) - rx, ellipse->cy()->currentValue()->value(lengthContext) - ry, rx * 2, ry * 2));
64}
65
66static void updatePathFromLineElement(SVGElement* element, Path& path)
67{
68    SVGLineElement* line = toSVGLineElement(element);
69
70    SVGLengthContext lengthContext(element);
71    path.moveTo(FloatPoint(line->x1()->currentValue()->value(lengthContext), line->y1()->currentValue()->value(lengthContext)));
72    path.addLineTo(FloatPoint(line->x2()->currentValue()->value(lengthContext), line->y2()->currentValue()->value(lengthContext)));
73}
74
75static void updatePathFromPathElement(SVGElement* element, Path& path)
76{
77    buildPathFromByteStream(toSVGPathElement(element)->pathByteStream(), path);
78}
79
80static void updatePathFromPolylineElement(SVGElement* element, Path& path)
81{
82    RefPtr<SVGPointList> points = toSVGPolyElement(element)->points()->currentValue();
83    if (points->isEmpty())
84        return;
85
86    SVGPointList::ConstIterator it = points->begin();
87    SVGPointList::ConstIterator itEnd = points->end();
88    ASSERT(it != itEnd);
89    path.moveTo(it->value());
90    ++it;
91
92    for (; it != itEnd; ++it)
93        path.addLineTo(it->value());
94}
95
96static void updatePathFromPolygonElement(SVGElement* element, Path& path)
97{
98    updatePathFromPolylineElement(element, path);
99    path.closeSubpath();
100}
101
102static void updatePathFromRectElement(SVGElement* element, Path& path)
103{
104    SVGRectElement* rect = toSVGRectElement(element);
105
106    SVGLengthContext lengthContext(element);
107    float width = rect->width()->currentValue()->value(lengthContext);
108    if (width < 0)
109        return;
110    float height = rect->height()->currentValue()->value(lengthContext);
111    if (height < 0)
112        return;
113    if (!width && !height)
114        return;
115    float x = rect->x()->currentValue()->value(lengthContext);
116    float y = rect->y()->currentValue()->value(lengthContext);
117    float rx = rect->rx()->currentValue()->value(lengthContext);
118    float ry = rect->ry()->currentValue()->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
127        path.addRoundedRect(FloatRect(x, y, width, height), FloatSize(rx, ry));
128        return;
129    }
130
131    path.addRect(FloatRect(x, y, width, height));
132}
133
134void updatePathFromGraphicsElement(SVGElement* element, Path& path)
135{
136    ASSERT(element);
137    ASSERT(path.isEmpty());
138
139    typedef void (*PathUpdateFunction)(SVGElement*, Path&);
140    static HashMap<StringImpl*, PathUpdateFunction>* map = 0;
141    if (!map) {
142        map = new HashMap<StringImpl*, PathUpdateFunction>;
143        map->set(circleTag.localName().impl(), updatePathFromCircleElement);
144        map->set(ellipseTag.localName().impl(), updatePathFromEllipseElement);
145        map->set(lineTag.localName().impl(), updatePathFromLineElement);
146        map->set(pathTag.localName().impl(), updatePathFromPathElement);
147        map->set(polygonTag.localName().impl(), updatePathFromPolygonElement);
148        map->set(polylineTag.localName().impl(), updatePathFromPolylineElement);
149        map->set(rectTag.localName().impl(), updatePathFromRectElement);
150    }
151
152    if (PathUpdateFunction pathUpdateFunction = map->get(element->localName().impl()))
153        (*pathUpdateFunction)(element, path);
154}
155
156} // namespace blink
157