1 /*
2 Copyright (C) 2004, 2005, 2006, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3 2004, 2005 Rob Buis <buis@kde.org>
4 Copyright (C) 2007 Eric Seidel <eric@webkit.org>
5
6 This file is part of the WebKit project
7
8 This library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Library General Public
10 License as published by the Free Software Foundation; either
11 version 2 of the License, or (at your option) any later version.
12
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Library General Public License for more details.
17
18 You should have received a copy of the GNU Library General Public License
19 along with this library; see the file COPYING.LIB. If not, write to
20 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 Boston, MA 02110-1301, USA.
22 */
23
24 #include "config.h"
25
26 #if ENABLE(SVG)
27 #include "SVGPathSegList.h"
28
29 #include "FloatPoint.h"
30 #include "Path.h"
31 #include "PathTraversalState.h"
32 #include "SVGPathSegArc.h"
33 #include "SVGPathSegClosePath.h"
34 #include "SVGPathSegMoveto.h"
35 #include "SVGPathSegLineto.h"
36 #include "SVGPathSegLinetoHorizontal.h"
37 #include "SVGPathSegLinetoVertical.h"
38 #include "SVGPathSegCurvetoCubic.h"
39 #include "SVGPathSegCurvetoCubicSmooth.h"
40 #include "SVGPathSegCurvetoQuadratic.h"
41 #include "SVGPathSegCurvetoQuadraticSmooth.h"
42
43 namespace WebCore {
44
SVGPathSegList(const QualifiedName & attributeName)45 SVGPathSegList::SVGPathSegList(const QualifiedName& attributeName)
46 : SVGList<RefPtr<SVGPathSeg> >(attributeName)
47 {
48 }
49
~SVGPathSegList()50 SVGPathSegList::~SVGPathSegList()
51 {
52 }
53
getPathSegAtLength(double)54 unsigned SVGPathSegList::getPathSegAtLength(double)
55 {
56 // FIXME : to be useful this will need to support non-normalized SVGPathSegLists
57 ExceptionCode ec = 0;
58 int len = numberOfItems();
59 // FIXME: Eventually this will likely move to a "path applier"-like model, until then PathTraversalState is less useful as we could just use locals
60 PathTraversalState traversalState(PathTraversalState::TraversalSegmentAtLength);
61 for (int i = 0; i < len; ++i) {
62 SVGPathSeg* segment = getItem(i, ec).get();
63 float segmentLength = 0;
64 switch (segment->pathSegType()) {
65 case SVGPathSeg::PATHSEG_MOVETO_ABS:
66 {
67 SVGPathSegMovetoAbs* moveTo = static_cast<SVGPathSegMovetoAbs*>(segment);
68 segmentLength = traversalState.moveTo(FloatPoint(moveTo->x(), moveTo->y()));
69 break;
70 }
71 case SVGPathSeg::PATHSEG_LINETO_ABS:
72 {
73 SVGPathSegLinetoAbs* lineTo = static_cast<SVGPathSegLinetoAbs*>(segment);
74 segmentLength = traversalState.lineTo(FloatPoint(lineTo->x(), lineTo->y()));
75 break;
76 }
77 case SVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS:
78 {
79 SVGPathSegCurvetoCubicAbs* curveTo = static_cast<SVGPathSegCurvetoCubicAbs*>(segment);
80 segmentLength = traversalState.cubicBezierTo(FloatPoint(curveTo->x1(), curveTo->y1()),
81 FloatPoint(curveTo->x2(), curveTo->y2()),
82 FloatPoint(curveTo->x(), curveTo->y()));
83 break;
84 }
85 case SVGPathSeg::PATHSEG_CLOSEPATH:
86 segmentLength = traversalState.closeSubpath();
87 break;
88 default:
89 ASSERT(false); // FIXME: This only works with normalized/processed path data.
90 break;
91 }
92 traversalState.m_totalLength += segmentLength;
93 if ((traversalState.m_action == PathTraversalState::TraversalSegmentAtLength)
94 && (traversalState.m_totalLength > traversalState.m_desiredLength)) {
95 return traversalState.m_segmentIndex;
96 }
97 traversalState.m_segmentIndex++;
98 }
99
100 return 0; // The SVG spec is unclear as to what to return when the distance is not on the path
101 }
102
toPathData()103 Path SVGPathSegList::toPathData()
104 {
105 // FIXME : This should also support non-normalized PathSegLists
106 Path pathData;
107 ExceptionCode ec = 0;
108 int len = numberOfItems();
109 for (int i = 0; i < len; ++i) {
110 SVGPathSeg* segment = getItem(i, ec).get();
111 switch (segment->pathSegType()) {
112 case SVGPathSeg::PATHSEG_MOVETO_ABS:
113 {
114 SVGPathSegMovetoAbs* moveTo = static_cast<SVGPathSegMovetoAbs*>(segment);
115 pathData.moveTo(FloatPoint(moveTo->x(), moveTo->y()));
116 break;
117 }
118 case SVGPathSeg::PATHSEG_LINETO_ABS:
119 {
120 SVGPathSegLinetoAbs* lineTo = static_cast<SVGPathSegLinetoAbs*>(segment);
121 pathData.addLineTo(FloatPoint(lineTo->x(), lineTo->y()));
122 break;
123 }
124 case SVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS:
125 {
126 SVGPathSegCurvetoCubicAbs* curveTo = static_cast<SVGPathSegCurvetoCubicAbs*>(segment);
127 pathData.addBezierCurveTo(FloatPoint(curveTo->x1(), curveTo->y1()),
128 FloatPoint(curveTo->x2(), curveTo->y2()),
129 FloatPoint(curveTo->x(), curveTo->y()));
130 break;
131 }
132 case SVGPathSeg::PATHSEG_CLOSEPATH:
133 pathData.closeSubpath();
134 break;
135 default:
136 ASSERT(false); // FIXME: This only works with normalized/processed path data.
137 break;
138 }
139 }
140
141 return pathData;
142 }
143
blendFunc(float from,float to,float progress)144 static inline float blendFunc(float from, float to, float progress)
145 {
146 return (to - from) * progress + from;
147 }
148
149 #define BLENDPATHSEG1(class, attr1) \
150 class::create(blendFunc(static_cast<class*>(from)->attr1(), static_cast<class*>(to)->attr1(), progress))
151
152 #define BLENDPATHSEG2(class, attr1, attr2) \
153 class::create(blendFunc(static_cast<class*>(from)->attr1(), static_cast<class*>(to)->attr1(), progress), \
154 blendFunc(static_cast<class*>(from)->attr2(), static_cast<class*>(to)->attr2(), progress))
155
156 #define BLENDPATHSEG4(class, attr1, attr2, attr3, attr4) \
157 class::create(blendFunc(static_cast<class*>(from)->attr1(), static_cast<class*>(to)->attr1(), progress), \
158 blendFunc(static_cast<class*>(from)->attr2(), static_cast<class*>(to)->attr2(), progress), \
159 blendFunc(static_cast<class*>(from)->attr3(), static_cast<class*>(to)->attr3(), progress), \
160 blendFunc(static_cast<class*>(from)->attr4(), static_cast<class*>(to)->attr4(), progress))
161
162 #define BLENDPATHSEG6(class, attr1, attr2, attr3, attr4, attr5, attr6) \
163 class::create(blendFunc(static_cast<class*>(from)->attr1(), static_cast<class*>(to)->attr1(), progress), \
164 blendFunc(static_cast<class*>(from)->attr2(), static_cast<class*>(to)->attr2(), progress), \
165 blendFunc(static_cast<class*>(from)->attr3(), static_cast<class*>(to)->attr3(), progress), \
166 blendFunc(static_cast<class*>(from)->attr4(), static_cast<class*>(to)->attr4(), progress), \
167 blendFunc(static_cast<class*>(from)->attr5(), static_cast<class*>(to)->attr5(), progress), \
168 blendFunc(static_cast<class*>(from)->attr6(), static_cast<class*>(to)->attr6(), progress))
169
170 #define BLENDPATHSEG7(class, attr1, attr2, attr3, attr4, attr5, bool1, bool2) \
171 class::create(blendFunc(static_cast<class*>(from)->attr1(), static_cast<class*>(to)->attr1(), progress), \
172 blendFunc(static_cast<class*>(from)->attr2(), static_cast<class*>(to)->attr2(), progress), \
173 blendFunc(static_cast<class*>(from)->attr3(), static_cast<class*>(to)->attr3(), progress), \
174 blendFunc(static_cast<class*>(from)->attr4(), static_cast<class*>(to)->attr4(), progress), \
175 blendFunc(static_cast<class*>(from)->attr5(), static_cast<class*>(to)->attr5(), progress), \
176 static_cast<bool>(blendFunc(static_cast<class*>(from)->bool1(), static_cast<class*>(to)->bool1(), progress)), \
177 static_cast<bool>(blendFunc(static_cast<class*>(from)->bool2(), static_cast<class*>(to)->bool2(), progress)))
178
createAnimated(const SVGPathSegList * fromList,const SVGPathSegList * toList,float progress)179 PassRefPtr<SVGPathSegList> SVGPathSegList::createAnimated(const SVGPathSegList* fromList, const SVGPathSegList* toList, float progress)
180 {
181 unsigned itemCount = fromList->numberOfItems();
182 if (!itemCount || itemCount != toList->numberOfItems())
183 return 0;
184 RefPtr<SVGPathSegList> result = create(fromList->associatedAttributeName());
185 ExceptionCode ec;
186 for (unsigned n = 0; n < itemCount; ++n) {
187 SVGPathSeg* from = fromList->getItem(n, ec).get();
188 SVGPathSeg* to = toList->getItem(n, ec).get();
189 if (from->pathSegType() == SVGPathSeg::PATHSEG_UNKNOWN || from->pathSegType() != to->pathSegType())
190 return 0;
191 RefPtr<SVGPathSeg> segment = 0;
192 switch (static_cast<SVGPathSeg::SVGPathSegType>(from->pathSegType())) {
193 case SVGPathSeg::PATHSEG_CLOSEPATH:
194 segment = SVGPathSegClosePath::create();
195 break;
196 case SVGPathSeg::PATHSEG_LINETO_HORIZONTAL_ABS:
197 segment = BLENDPATHSEG1(SVGPathSegLinetoHorizontalAbs, x);
198 break;
199 case SVGPathSeg::PATHSEG_LINETO_HORIZONTAL_REL:
200 segment = BLENDPATHSEG1(SVGPathSegLinetoHorizontalRel, x);
201 break;
202 case SVGPathSeg::PATHSEG_LINETO_VERTICAL_ABS:
203 segment = BLENDPATHSEG1(SVGPathSegLinetoVerticalAbs, y);
204 break;
205 case SVGPathSeg::PATHSEG_LINETO_VERTICAL_REL:
206 segment = BLENDPATHSEG1(SVGPathSegLinetoVerticalRel, y);
207 break;
208 case SVGPathSeg::PATHSEG_MOVETO_ABS:
209 segment = BLENDPATHSEG2(SVGPathSegMovetoAbs, x, y);
210 break;
211 case SVGPathSeg::PATHSEG_MOVETO_REL:
212 segment = BLENDPATHSEG2(SVGPathSegMovetoRel, x, y);
213 break;
214 case SVGPathSeg::PATHSEG_LINETO_ABS:
215 segment = BLENDPATHSEG2(SVGPathSegLinetoAbs, x, y);
216 break;
217 case SVGPathSeg::PATHSEG_LINETO_REL:
218 segment = BLENDPATHSEG2(SVGPathSegLinetoRel, x, y);
219 break;
220 case SVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS:
221 segment = BLENDPATHSEG6(SVGPathSegCurvetoCubicAbs, x, y, x1, y1, x2, y2);
222 break;
223 case SVGPathSeg::PATHSEG_CURVETO_CUBIC_REL:
224 segment = BLENDPATHSEG6(SVGPathSegCurvetoCubicRel, x, y, x1, y1, x2, y2);
225 break;
226 case SVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
227 segment = BLENDPATHSEG4(SVGPathSegCurvetoCubicSmoothAbs, x, y, x2, y2);
228 break;
229 case SVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
230 segment = BLENDPATHSEG4(SVGPathSegCurvetoCubicSmoothRel, x, y, x2, y2);
231 break;
232 case SVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS:
233 segment = BLENDPATHSEG4(SVGPathSegCurvetoQuadraticAbs, x, y, x1, y1);
234 break;
235 case SVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL:
236 segment = BLENDPATHSEG4(SVGPathSegCurvetoQuadraticRel, x, y, x1, y1);
237 break;
238 case SVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:
239 segment = BLENDPATHSEG2(SVGPathSegCurvetoQuadraticSmoothAbs, x, y);
240 break;
241 case SVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:
242 segment = BLENDPATHSEG2(SVGPathSegCurvetoQuadraticSmoothRel, x, y);
243 break;
244 case SVGPathSeg::PATHSEG_ARC_ABS:
245 segment = BLENDPATHSEG7(SVGPathSegArcAbs, x, y, r1, r2, angle, largeArcFlag, sweepFlag);
246 break;
247 case SVGPathSeg::PATHSEG_ARC_REL:
248 segment = BLENDPATHSEG7(SVGPathSegArcRel, x, y, r1, r2, angle, largeArcFlag, sweepFlag);
249 break;
250 case SVGPathSeg::PATHSEG_UNKNOWN:
251 ASSERT_NOT_REACHED();
252 }
253 result->appendItem(segment, ec);
254 }
255 return result.release();
256 }
257
258 }
259
260 #endif // ENABLE(SVG)
261