initial import
[vuplus_webkit] / Source / WebCore / svg / SVGPathParser.cpp
1 /*
2  * Copyright (C) 2002, 2003 The Karbon Developers
3  * Copyright (C) 2006 Alexander Kellett <lypanov@kde.org>
4  * Copyright (C) 2006, 2007 Rob Buis <buis@kde.org>
5  * Copyright (C) 2007, 2009 Apple Inc. All rights reserved.
6  * Copyright (C) Research In Motion Limited 2010. All rights reserved.
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 "SVGPathParser.h"
28
29 #include "AffineTransform.h"
30 #include <wtf/MathExtras.h>
31
32 static const float gOneOverThree = 1 / 3.f;
33
34 namespace WebCore {
35
36 SVGPathParser::SVGPathParser()
37     : m_consumer(0)
38 {
39 }
40
41 void SVGPathParser::parseClosePathSegment()
42 {
43     // Reset m_currentPoint for the next path.
44     if (m_pathParsingMode == NormalizedParsing)
45         m_currentPoint = m_subPathPoint;
46     m_closePath = true;
47     m_consumer->closePath();
48 }
49
50 bool SVGPathParser::parseMoveToSegment()
51 {
52     FloatPoint targetPoint;
53     if (!m_source->parseMoveToSegment(targetPoint))
54         return false;
55
56     if (m_pathParsingMode == NormalizedParsing) {
57         if (m_mode == RelativeCoordinates)
58             m_currentPoint += targetPoint;
59         else
60             m_currentPoint = targetPoint;
61         m_subPathPoint = m_currentPoint;
62         m_consumer->moveTo(m_currentPoint, m_closePath, AbsoluteCoordinates);
63     } else
64         m_consumer->moveTo(targetPoint, m_closePath, m_mode);
65     m_closePath = false;
66     return true;
67 }
68
69 bool SVGPathParser::parseLineToSegment()
70 {
71     FloatPoint targetPoint;
72     if (!m_source->parseLineToSegment(targetPoint))
73         return false;
74
75     if (m_pathParsingMode == NormalizedParsing) {
76         if (m_mode == RelativeCoordinates)
77             m_currentPoint += targetPoint;
78         else
79             m_currentPoint = targetPoint;
80         m_consumer->lineTo(m_currentPoint, AbsoluteCoordinates);
81     } else
82         m_consumer->lineTo(targetPoint, m_mode);
83     return true;
84 }
85
86 bool SVGPathParser::parseLineToHorizontalSegment()
87 {
88     float toX;
89     if (!m_source->parseLineToHorizontalSegment(toX))
90         return false;
91
92     if (m_pathParsingMode == NormalizedParsing) {
93         if (m_mode == RelativeCoordinates)
94             m_currentPoint.move(toX, 0);
95         else
96             m_currentPoint.setX(toX);
97         m_consumer->lineTo(m_currentPoint, AbsoluteCoordinates);
98     } else
99         m_consumer->lineToHorizontal(toX, m_mode);
100     return true;
101 }
102
103 bool SVGPathParser::parseLineToVerticalSegment()
104 {
105     float toY;
106     if (!m_source->parseLineToVerticalSegment(toY))
107         return false;
108
109     if (m_pathParsingMode == NormalizedParsing) {
110         if (m_mode == RelativeCoordinates)
111             m_currentPoint.move(0, toY);
112         else
113             m_currentPoint.setY(toY);
114         m_consumer->lineTo(m_currentPoint, AbsoluteCoordinates);
115     } else
116         m_consumer->lineToVertical(toY, m_mode);
117     return true;
118 }
119
120 bool SVGPathParser::parseCurveToCubicSegment()
121 {
122     FloatPoint point1;
123     FloatPoint point2;
124     FloatPoint targetPoint;
125     if (!m_source->parseCurveToCubicSegment(point1, point2, targetPoint))
126         return false;
127
128     if (m_pathParsingMode == NormalizedParsing) {
129         if (m_mode == RelativeCoordinates) {
130             point1 += m_currentPoint;
131             point2 += m_currentPoint;
132             targetPoint += m_currentPoint;
133         }
134         m_consumer->curveToCubic(point1, point2, targetPoint, AbsoluteCoordinates);
135
136         m_controlPoint = point2;
137         m_currentPoint = targetPoint;
138     } else
139         m_consumer->curveToCubic(point1, point2, targetPoint, m_mode);
140     return true;
141 }
142
143 bool SVGPathParser::parseCurveToCubicSmoothSegment()
144 {
145     FloatPoint point2;
146     FloatPoint targetPoint;
147     if (!m_source->parseCurveToCubicSmoothSegment(point2, targetPoint))
148         return false;
149
150     if (m_lastCommand != PathSegCurveToCubicAbs
151         && m_lastCommand != PathSegCurveToCubicRel
152         && m_lastCommand != PathSegCurveToCubicSmoothAbs
153         && m_lastCommand != PathSegCurveToCubicSmoothRel)
154         m_controlPoint = m_currentPoint;
155
156     if (m_pathParsingMode == NormalizedParsing) {
157         FloatPoint point1 = m_currentPoint;
158         point1.scale(2, 2);
159         point1.move(-m_controlPoint.x(), -m_controlPoint.y());
160         if (m_mode == RelativeCoordinates) {
161             point2 += m_currentPoint;
162             targetPoint += m_currentPoint;
163         }
164
165         m_consumer->curveToCubic(point1, point2, targetPoint, AbsoluteCoordinates);
166
167         m_controlPoint = point2;
168         m_currentPoint = targetPoint;
169     } else
170         m_consumer->curveToCubicSmooth(point2, targetPoint, m_mode);
171     return true;
172 }
173
174 bool SVGPathParser::parseCurveToQuadraticSegment()
175 {
176     FloatPoint point1;
177     FloatPoint targetPoint;
178     if (!m_source->parseCurveToQuadraticSegment(point1, targetPoint))
179         return false;
180
181     if (m_pathParsingMode == NormalizedParsing) {
182         m_controlPoint = point1;
183         FloatPoint point1 = m_currentPoint;
184         point1.move(2 * m_controlPoint.x(), 2 * m_controlPoint.y());
185         FloatPoint point2(targetPoint.x() + 2 * m_controlPoint.x(), targetPoint.y() + 2 * m_controlPoint.y());
186         if (m_mode == RelativeCoordinates) {
187             point1.move(2 * m_currentPoint.x(), 2 * m_currentPoint.y());
188             point2.move(3 * m_currentPoint.x(), 3 * m_currentPoint.y());
189             targetPoint += m_currentPoint;
190         }
191         point1.scale(gOneOverThree, gOneOverThree);
192         point2.scale(gOneOverThree, gOneOverThree);
193
194         m_consumer->curveToCubic(point1, point2, targetPoint, AbsoluteCoordinates);
195
196         if (m_mode == RelativeCoordinates)
197             m_controlPoint += m_currentPoint;
198         m_currentPoint = targetPoint;
199     } else
200         m_consumer->curveToQuadratic(point1, targetPoint, m_mode);
201     return true;
202 }
203
204 bool SVGPathParser::parseCurveToQuadraticSmoothSegment()
205 {
206     FloatPoint targetPoint;
207     if (!m_source->parseCurveToQuadraticSmoothSegment(targetPoint))
208         return false;
209
210     if (m_lastCommand != PathSegCurveToQuadraticAbs
211         && m_lastCommand != PathSegCurveToQuadraticRel
212         && m_lastCommand != PathSegCurveToQuadraticSmoothAbs
213         && m_lastCommand != PathSegCurveToQuadraticSmoothRel)
214         m_controlPoint = m_currentPoint;
215
216     if (m_pathParsingMode == NormalizedParsing) {
217         FloatPoint cubicPoint = m_currentPoint;
218         cubicPoint.scale(2, 2);
219         cubicPoint.move(-m_controlPoint.x(), -m_controlPoint.y());
220         FloatPoint point1(m_currentPoint.x() + 2 * cubicPoint.x(), m_currentPoint.y() + 2 * cubicPoint.y());
221         FloatPoint point2(targetPoint.x() + 2 * cubicPoint.x(), targetPoint.y() + 2 * cubicPoint.y());
222         if (m_mode == RelativeCoordinates) {
223             point2 += m_currentPoint;
224             targetPoint += m_currentPoint;
225         }
226         point1.scale(gOneOverThree, gOneOverThree);
227         point2.scale(gOneOverThree, gOneOverThree);
228
229         m_consumer->curveToCubic(point1, point2, targetPoint, AbsoluteCoordinates);
230
231         m_controlPoint = cubicPoint;
232         m_currentPoint = targetPoint;
233     } else
234         m_consumer->curveToQuadraticSmooth(targetPoint, m_mode);
235     return true;
236 }
237
238 bool SVGPathParser::parseArcToSegment()
239 {
240     float rx;
241     float ry;
242     float angle;
243     bool largeArc;
244     bool sweep;
245     FloatPoint targetPoint;
246     if (!m_source->parseArcToSegment(rx, ry, angle, largeArc, sweep, targetPoint))
247         return false;
248
249     // If rx = 0 or ry = 0 then this arc is treated as a straight line segment (a "lineto") joining the endpoints.
250     // http://www.w3.org/TR/SVG/implnote.html#ArcOutOfRangeParameters
251     rx = fabsf(rx);
252     ry = fabsf(ry);
253     if (!rx || !ry) {
254         if (m_pathParsingMode == NormalizedParsing) {
255             if (m_mode == RelativeCoordinates)
256                 m_currentPoint += targetPoint;
257             else
258                 m_currentPoint = targetPoint;
259             m_consumer->lineTo(m_currentPoint, AbsoluteCoordinates);
260         } else
261             m_consumer->lineTo(targetPoint, m_mode);
262         return true;
263     }
264
265     if (m_pathParsingMode == NormalizedParsing) {
266         FloatPoint point1 = m_currentPoint;
267         if (m_mode == RelativeCoordinates)
268             targetPoint += m_currentPoint;
269         m_currentPoint = targetPoint;
270         return decomposeArcToCubic(angle, rx, ry, point1, targetPoint, largeArc, sweep);
271     }
272     m_consumer->arcTo(rx, ry, angle, largeArc, sweep, targetPoint, m_mode);
273     return true;
274 }
275
276 bool SVGPathParser::parsePathDataFromSource(PathParsingMode pathParsingMode)
277 {
278     ASSERT(m_source);
279     ASSERT(m_consumer);
280
281     m_pathParsingMode = pathParsingMode;
282
283     m_controlPoint = FloatPoint();
284     m_currentPoint = FloatPoint();
285     m_subPathPoint = FloatPoint();
286     m_closePath = true;
287
288     // Skip any leading spaces.
289     if (!m_source->moveToNextToken())
290         return false;
291
292     SVGPathSegType command;
293     m_source->parseSVGSegmentType(command);
294     m_lastCommand = PathSegUnknown;
295
296     // Path must start with moveto.
297     if (command != PathSegMoveToAbs && command != PathSegMoveToRel)
298         return false;
299
300     while (true) {
301         // Skip spaces between command and first coordinate.
302         m_source->moveToNextToken();
303         m_mode = AbsoluteCoordinates;
304         switch (command) {
305         case PathSegMoveToRel:
306             m_mode = RelativeCoordinates;
307         case PathSegMoveToAbs:
308             if (!parseMoveToSegment())
309                 return false;
310             break;
311         case PathSegLineToRel:
312             m_mode = RelativeCoordinates;
313         case PathSegLineToAbs:
314             if (!parseLineToSegment())
315                 return false;
316             break;
317         case PathSegLineToHorizontalRel:
318             m_mode = RelativeCoordinates;
319         case PathSegLineToHorizontalAbs:
320             if (!parseLineToHorizontalSegment())
321                 return false;
322             break;
323         case PathSegLineToVerticalRel:
324             m_mode = RelativeCoordinates;
325         case PathSegLineToVerticalAbs:
326             if (!parseLineToVerticalSegment())
327                 return false;
328             break;
329         case PathSegClosePath:
330             parseClosePathSegment();
331             break;
332         case PathSegCurveToCubicRel:
333             m_mode = RelativeCoordinates;
334         case PathSegCurveToCubicAbs:
335             if (!parseCurveToCubicSegment())
336                 return false;
337             break;
338         case PathSegCurveToCubicSmoothRel:
339             m_mode = RelativeCoordinates;
340         case PathSegCurveToCubicSmoothAbs:
341             if (!parseCurveToCubicSmoothSegment())
342                 return false;
343             break;
344         case PathSegCurveToQuadraticRel:
345             m_mode = RelativeCoordinates;
346         case PathSegCurveToQuadraticAbs:
347             if (!parseCurveToQuadraticSegment())
348                 return false;
349             break;
350         case PathSegCurveToQuadraticSmoothRel:
351             m_mode = RelativeCoordinates;
352         case PathSegCurveToQuadraticSmoothAbs:
353             if (!parseCurveToQuadraticSmoothSegment())
354                 return false;
355             break;
356         case PathSegArcRel:
357             m_mode = RelativeCoordinates;
358         case PathSegArcAbs:
359             if (!parseArcToSegment())
360                 return false;
361             break;
362         default:
363             return false;
364         }
365         if (!m_consumer->continueConsuming())
366             return true;
367
368         m_lastCommand = command;
369
370         if (!m_source->hasMoreData())
371             return true;
372
373         command = m_source->nextCommand(command);
374
375         if (m_lastCommand != PathSegCurveToCubicAbs
376             && m_lastCommand != PathSegCurveToCubicRel
377             && m_lastCommand != PathSegCurveToCubicSmoothAbs
378             && m_lastCommand != PathSegCurveToCubicSmoothRel
379             && m_lastCommand != PathSegCurveToQuadraticAbs
380             && m_lastCommand != PathSegCurveToQuadraticRel
381             && m_lastCommand != PathSegCurveToQuadraticSmoothAbs
382             && m_lastCommand != PathSegCurveToQuadraticSmoothRel)
383             m_controlPoint = m_currentPoint;
384
385         m_consumer->incrementPathSegmentCount();
386     }
387
388     return false;
389 }
390
391 void SVGPathParser::cleanup()
392 {
393     ASSERT(m_source);
394     ASSERT(m_consumer);
395
396     m_consumer->cleanup();
397     m_source = 0;
398     m_consumer = 0;
399 }
400
401 // This works by converting the SVG arc to "simple" beziers.
402 // Partly adapted from Niko's code in kdelibs/kdecore/svgicons.
403 // See also SVG implementation notes: http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter
404 bool SVGPathParser::decomposeArcToCubic(float angle, float rx, float ry, FloatPoint& point1, FloatPoint& point2, bool largeArcFlag, bool sweepFlag)
405 {
406     FloatSize midPointDistance = point1 - point2;
407     midPointDistance.scale(0.5f);
408
409     AffineTransform pointTransform;
410     pointTransform.rotate(-angle);
411
412     FloatPoint transformedMidPoint = pointTransform.mapPoint(FloatPoint(midPointDistance.width(), midPointDistance.height()));
413     float squareRx = rx * rx;
414     float squareRy = ry * ry;
415     float squareX = transformedMidPoint.x() * transformedMidPoint.x();
416     float squareY = transformedMidPoint.y() * transformedMidPoint.y();
417
418     // Check if the radii are big enough to draw the arc, scale radii if not.
419     // http://www.w3.org/TR/SVG/implnote.html#ArcCorrectionOutOfRangeRadii
420     float radiiScale = squareX / squareRx + squareY / squareRy;
421     if (radiiScale > 1) {
422         rx *= sqrtf(radiiScale);
423         ry *= sqrtf(radiiScale);
424     }
425
426     pointTransform.makeIdentity();
427     pointTransform.scale(1 / rx, 1 / ry);
428     pointTransform.rotate(-angle);
429
430     point1 = pointTransform.mapPoint(point1);
431     point2 = pointTransform.mapPoint(point2);
432     FloatSize delta = point2 - point1;
433
434     float d = delta.width() * delta.width() + delta.height() * delta.height();
435     float scaleFactorSquared = std::max(1 / d - 0.25f, 0.f);
436
437     float scaleFactor = sqrtf(scaleFactorSquared);
438     if (sweepFlag == largeArcFlag)
439         scaleFactor = -scaleFactor;
440
441     delta.scale(scaleFactor);
442     FloatPoint centerPoint = FloatPoint(0.5f * (point1.x() + point2.x()) - delta.height(),
443                                         0.5f * (point1.y() + point2.y()) + delta.width());
444
445     float theta1 = atan2f(point1.y() - centerPoint.y(), point1.x() - centerPoint.x());
446     float theta2 = atan2f(point2.y() - centerPoint.y(), point2.x() - centerPoint.x());
447
448     float thetaArc = theta2 - theta1;
449     if (thetaArc < 0 && sweepFlag)
450         thetaArc += 2 * piFloat;
451     else if (thetaArc > 0 && !sweepFlag)
452         thetaArc -= 2 * piFloat;
453
454     pointTransform.makeIdentity();
455     pointTransform.rotate(angle);
456     pointTransform.scale(rx, ry);
457
458     // Some results of atan2 on some platform implementations are not exact enough. So that we get more
459     // cubic curves than expected here. Adding 0.001f reduces the count of sgements to the correct count.
460     int segments = ceilf(fabsf(thetaArc / (piOverTwoFloat + 0.001f)));
461     for (int i = 0; i < segments; ++i) {
462         float startTheta = theta1 + i * thetaArc / segments;
463         float endTheta = theta1 + (i + 1) * thetaArc / segments;
464
465         float t = (8 / 6.f) * tanf(0.25f * (endTheta - startTheta));
466         if (!isfinite(t))
467             return false;
468         float sinStartTheta = sinf(startTheta);
469         float cosStartTheta = cosf(startTheta);
470         float sinEndTheta = sinf(endTheta);
471         float cosEndTheta = cosf(endTheta);
472
473         point1 = FloatPoint(cosStartTheta - t * sinStartTheta, sinStartTheta + t * cosStartTheta);
474         point1.move(centerPoint.x(), centerPoint.y());
475         FloatPoint targetPoint = FloatPoint(cosEndTheta, sinEndTheta);
476         targetPoint.move(centerPoint.x(), centerPoint.y());
477         point2 = targetPoint;
478         point2.move(t * sinEndTheta, -t * cosEndTheta);
479
480         m_consumer->curveToCubic(pointTransform.mapPoint(point1), pointTransform.mapPoint(point2),
481                                  pointTransform.mapPoint(targetPoint), AbsoluteCoordinates);
482     }
483     return true;
484 }
485
486 }
487
488 #endif // ENABLE(SVG)