1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtDeclaractive module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qdeclarativesvgparser_p.h"
44 #include <QtCore/qmath.h>
45 #include <QtCore/qvarlengtharray.h>
46 #include <QtCore/qstring.h>
50 static const double Q_PI = 3.14159265358979323846; // pi
52 //copied from QtSvg (qsvghandler.cpp).
53 Q_CORE_EXPORT double qstrtod(const char *s00, char const **se, bool *ok);
54 // '0' is 0x30 and '9' is 0x39
55 static inline bool isDigit(ushort ch)
57 static quint16 magic = 0x3ff;
58 return ((ch >> 4) == 3) && (magic >> (ch & 15));
61 static qreal toDouble(const QChar *&str)
63 const int maxLen = 255;//technically doubles can go til 308+ but whatever
67 if (*str == QLatin1Char('-')) {
70 } else if (*str == QLatin1Char('+')) {
73 while (isDigit(str->unicode()) && pos < maxLen) {
74 temp[pos++] = str->toLatin1();
77 if (*str == QLatin1Char('.') && pos < maxLen) {
81 while (isDigit(str->unicode()) && pos < maxLen) {
82 temp[pos++] = str->toLatin1();
85 bool exponent = false;
86 if ((*str == QLatin1Char('e') || *str == QLatin1Char('E')) && pos < maxLen) {
90 if ((*str == QLatin1Char('-') || *str == QLatin1Char('+')) && pos < maxLen) {
91 temp[pos++] = str->toLatin1();
94 while (isDigit(str->unicode()) && pos < maxLen) {
95 temp[pos++] = str->toLatin1();
103 if (!exponent && pos < 10) {
105 const char *t = temp;
111 while(*t && *t != '.') {
125 val = ((qreal)ival)/((qreal)div);
132 #if defined(Q_WS_QWS) && !defined(Q_OS_VXWORKS)
133 if(sizeof(qreal) == sizeof(float))
134 val = strtof(temp, 0);
139 val = qstrtod(temp, 0, &ok);
145 static inline void parseNumbersArray(const QChar *&str, QVarLengthArray<qreal, 8> &points)
147 while (str->isSpace())
149 while (isDigit(str->unicode()) ||
150 *str == QLatin1Char('-') || *str == QLatin1Char('+') ||
151 *str == QLatin1Char('.')) {
153 points.append(toDouble(str));
155 while (str->isSpace())
157 if (*str == QLatin1Char(','))
160 //eat the rest of space
161 while (str->isSpace())
166 static void pathArcSegment(QPainterPath &path,
168 qreal th0, qreal th1,
169 qreal rx, qreal ry, qreal xAxisRotation)
172 qreal a00, a01, a10, a11;
173 qreal x1, y1, x2, y2, x3, y3;
177 sinTh = qSin(xAxisRotation * (Q_PI / 180.0));
178 cosTh = qCos(xAxisRotation * (Q_PI / 180.0));
185 thHalf = 0.5 * (th1 - th0);
186 t = (8.0 / 3.0) * qSin(thHalf * 0.5) * qSin(thHalf * 0.5) / qSin(thHalf);
187 x1 = xc + qCos(th0) - t * qSin(th0);
188 y1 = yc + qSin(th0) + t * qCos(th0);
191 x2 = x3 + t * qSin(th1);
192 y2 = y3 - t * qCos(th1);
194 path.cubicTo(a00 * x1 + a01 * y1, a10 * x1 + a11 * y1,
195 a00 * x2 + a01 * y2, a10 * x2 + a11 * y2,
196 a00 * x3 + a01 * y3, a10 * x3 + a11 * y3);
199 void QDeclarativeSvgParser::pathArc(QPainterPath &path,
202 qreal x_axis_rotation,
207 qreal curx, qreal cury)
209 qreal sin_th, cos_th;
210 qreal a00, a01, a10, a11;
211 qreal x0, y0, x1, y1, xc, yc;
212 qreal d, sfactor, sfactor_sq;
213 qreal th0, th1, th_arc;
215 qreal dx, dy, dx1, dy1, Pr1, Pr2, Px, Py, check;
220 sin_th = qSin(x_axis_rotation * (Q_PI / 180.0));
221 cos_th = qCos(x_axis_rotation * (Q_PI / 180.0));
223 dx = (curx - x) / 2.0;
224 dy = (cury - y) / 2.0;
225 dx1 = cos_th * dx + sin_th * dy;
226 dy1 = -sin_th * dx + cos_th * dy;
231 /* Spec : check if radii are large enough */
232 check = Px / Pr1 + Py / Pr2;
234 rx = rx * qSqrt(check);
235 ry = ry * qSqrt(check);
242 x0 = a00 * curx + a01 * cury;
243 y0 = a10 * curx + a11 * cury;
244 x1 = a00 * x + a01 * y;
245 y1 = a10 * x + a11 * y;
246 /* (x0, y0) is current point in transformed coordinate space.
247 (x1, y1) is new point in transformed coordinate space.
249 The arc fits a unit-radius circle in this space.
251 d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
252 sfactor_sq = 1.0 / d - 0.25;
253 if (sfactor_sq < 0) sfactor_sq = 0;
254 sfactor = qSqrt(sfactor_sq);
255 if (sweep_flag == large_arc_flag) sfactor = -sfactor;
256 xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
257 yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);
258 /* (xc, yc) is center of the circle. */
260 th0 = qAtan2(y0 - yc, x0 - xc);
261 th1 = qAtan2(y1 - yc, x1 - xc);
264 if (th_arc < 0 && sweep_flag)
266 else if (th_arc > 0 && !sweep_flag)
269 n_segs = qCeil(qAbs(th_arc / (Q_PI * 0.5 + 0.001)));
271 for (i = 0; i < n_segs; i++) {
272 pathArcSegment(path, xc, yc,
273 th0 + i * th_arc / n_segs,
274 th0 + (i + 1) * th_arc / n_segs,
275 rx, ry, x_axis_rotation);
280 bool QDeclarativeSvgParser::parsePathDataFast(const QString &dataStr, QPainterPath &path)
282 qreal x0 = 0, y0 = 0; // starting point
283 qreal x = 0, y = 0; // current point
286 const QChar *str = dataStr.constData();
287 const QChar *end = str + dataStr.size();
290 while (str->isSpace())
292 QChar pathElem = *str;
295 *const_cast<QChar *>(end) = 0; // parseNumbersArray requires 0-termination that QStringRef cannot guarantee
296 QVarLengthArray<qreal, 8> arg;
297 parseNumbersArray(str, arg);
298 *const_cast<QChar *>(end) = endc;
299 if (pathElem == QLatin1Char('z') || pathElem == QLatin1Char('Z'))
300 arg.append(0);//dummy
301 const qreal *num = arg.constData();
302 int count = arg.count();
304 qreal offsetX = x; // correction offsets
305 qreal offsetY = y; // for relative commands
306 switch (pathElem.unicode()) {
313 x = x0 = num[0] + offsetX;
314 y = y0 = num[1] + offsetY;
319 // As per 1.2 spec 8.3.2 The "moveto" commands
320 // If a 'moveto' is followed by multiple pairs of coordinates without explicit commands,
321 // the subsequent pairs shall be treated as implicit 'lineto' commands.
322 pathElem = QLatin1Char('l');
337 // As per 1.2 spec 8.3.2 The "moveto" commands
338 // If a 'moveto' is followed by multiple pairs of coordinates without explicit commands,
339 // the subsequent pairs shall be treated as implicit 'lineto' commands.
340 pathElem = QLatin1Char('L');
347 count--; // skip dummy
358 x = num[0] + offsetX;
359 y = num[1] + offsetY;
380 x = num[0] + offsetX;
394 y = num[0] + offsetY;
413 QPointF c1(num[0] + offsetX, num[1] + offsetY);
414 QPointF c2(num[2] + offsetX, num[3] + offsetY);
415 QPointF e(num[4] + offsetX, num[5] + offsetY);
418 path.cubicTo(c1, c2, e);
430 QPointF c1(num[0], num[1]);
431 QPointF c2(num[2], num[3]);
432 QPointF e(num[4], num[5]);
435 path.cubicTo(c1, c2, e);
448 if (lastMode == 'c' || lastMode == 'C' ||
449 lastMode == 's' || lastMode == 'S')
450 c1 = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
453 QPointF c2(num[0] + offsetX, num[1] + offsetY);
454 QPointF e(num[2] + offsetX, num[3] + offsetY);
457 path.cubicTo(c1, c2, e);
470 if (lastMode == 'c' || lastMode == 'C' ||
471 lastMode == 's' || lastMode == 'S')
472 c1 = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
475 QPointF c2(num[0], num[1]);
476 QPointF e(num[2], num[3]);
479 path.cubicTo(c1, c2, e);
491 QPointF c(num[0] + offsetX, num[1] + offsetY);
492 QPointF e(num[2] + offsetX, num[3] + offsetY);
507 QPointF c(num[0], num[1]);
508 QPointF e(num[2], num[3]);
523 QPointF e(num[0] + offsetX, num[1] + offsetY);
527 if (lastMode == 'q' || lastMode == 'Q' ||
528 lastMode == 't' || lastMode == 'T')
529 c = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
544 QPointF e(num[0], num[1]);
548 if (lastMode == 'q' || lastMode == 'Q' ||
549 lastMode == 't' || lastMode == 'T')
550 c = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
567 qreal xAxisRotation = (*num++);
568 qreal largeArcFlag = (*num++);
569 qreal sweepFlag = (*num++);
570 qreal ex = (*num++) + offsetX;
571 qreal ey = (*num++) + offsetY;
575 pathArc(path, rx, ry, xAxisRotation, int(largeArcFlag),
576 int(sweepFlag), ex, ey, curx, cury);
590 qreal xAxisRotation = (*num++);
591 qreal largeArcFlag = (*num++);
592 qreal sweepFlag = (*num++);
598 pathArc(path, rx, ry, xAxisRotation, int(largeArcFlag),
599 int(sweepFlag), ex, ey, curx, cury);
608 lastMode = pathElem.toLatin1();