cdf575d7fba072a34973509fd120475db64e8460
[profile/ivi/qtdeclarative.git] / src / quick / util / qdeclarativesvgparser.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
6 **
7 ** This file is part of the QtDeclaractive module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qdeclarativesvgparser_p.h"
43
44 #include <QtCore/qmath.h>
45 #include <QtCore/qvarlengtharray.h>
46 #include <QtCore/qstring.h>
47
48 QT_BEGIN_NAMESPACE
49
50 static const double Q_PI   = 3.14159265358979323846;   // pi
51
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)
56 {
57     static quint16 magic = 0x3ff;
58     return ((ch >> 4) == 3) && (magic >> (ch & 15));
59 }
60
61 static qreal toDouble(const QChar *&str)
62 {
63     const int maxLen = 255;//technically doubles can go til 308+ but whatever
64     char temp[maxLen+1];
65     int pos = 0;
66
67     if (*str == QLatin1Char('-')) {
68         temp[pos++] = '-';
69         ++str;
70     } else if (*str == QLatin1Char('+')) {
71         ++str;
72     }
73     while (isDigit(str->unicode()) && pos < maxLen) {
74         temp[pos++] = str->toLatin1();
75         ++str;
76     }
77     if (*str == QLatin1Char('.') && pos < maxLen) {
78         temp[pos++] = '.';
79         ++str;
80     }
81     while (isDigit(str->unicode()) && pos < maxLen) {
82         temp[pos++] = str->toLatin1();
83         ++str;
84     }
85     bool exponent = false;
86     if ((*str == QLatin1Char('e') || *str == QLatin1Char('E')) && pos < maxLen) {
87         exponent = true;
88         temp[pos++] = 'e';
89         ++str;
90         if ((*str == QLatin1Char('-') || *str == QLatin1Char('+')) && pos < maxLen) {
91             temp[pos++] = str->toLatin1();
92             ++str;
93         }
94         while (isDigit(str->unicode()) && pos < maxLen) {
95             temp[pos++] = str->toLatin1();
96             ++str;
97         }
98     }
99
100     temp[pos] = '\0';
101
102     qreal val;
103     if (!exponent && pos < 10) {
104         int ival = 0;
105         const char *t = temp;
106         bool neg = false;
107         if(*t == '-') {
108             neg = true;
109             ++t;
110         }
111         while(*t && *t != '.') {
112             ival *= 10;
113             ival += (*t) - '0';
114             ++t;
115         }
116         if(*t == '.') {
117             ++t;
118             int div = 1;
119             while(*t) {
120                 ival *= 10;
121                 ival += (*t) - '0';
122                 div *= 10;
123                 ++t;
124             }
125             val = ((qreal)ival)/((qreal)div);
126         } else {
127             val = ival;
128         }
129         if (neg)
130             val = -val;
131     } else {
132 #if defined(Q_WS_QWS) && !defined(Q_OS_VXWORKS)
133         if(sizeof(qreal) == sizeof(float))
134             val = strtof(temp, 0);
135         else
136 #endif
137         {
138             bool ok = false;
139             val = qstrtod(temp, 0, &ok);
140         }
141     }
142     return val;
143
144 }
145 static inline void parseNumbersArray(const QChar *&str, QVarLengthArray<qreal, 8> &points)
146 {
147     while (str->isSpace())
148         ++str;
149     while (isDigit(str->unicode()) ||
150            *str == QLatin1Char('-') || *str == QLatin1Char('+') ||
151            *str == QLatin1Char('.')) {
152
153         points.append(toDouble(str));
154
155         while (str->isSpace())
156             ++str;
157         if (*str == QLatin1Char(','))
158             ++str;
159
160         //eat the rest of space
161         while (str->isSpace())
162             ++str;
163     }
164 }
165
166 static void pathArcSegment(QPainterPath &path,
167                            qreal xc, qreal yc,
168                            qreal th0, qreal th1,
169                            qreal rx, qreal ry, qreal xAxisRotation)
170 {
171     qreal sinTh, cosTh;
172     qreal a00, a01, a10, a11;
173     qreal x1, y1, x2, y2, x3, y3;
174     qreal t;
175     qreal thHalf;
176
177     sinTh = qSin(xAxisRotation * (Q_PI / 180.0));
178     cosTh = qCos(xAxisRotation * (Q_PI / 180.0));
179
180     a00 =  cosTh * rx;
181     a01 = -sinTh * ry;
182     a10 =  sinTh * rx;
183     a11 =  cosTh * ry;
184
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);
189     x3 = xc + qCos(th1);
190     y3 = yc + qSin(th1);
191     x2 = x3 + t * qSin(th1);
192     y2 = y3 - t * qCos(th1);
193
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);
197 }
198
199 void QDeclarativeSvgParser::pathArc(QPainterPath &path,
200                     qreal               rx,
201                     qreal               ry,
202                     qreal               x_axis_rotation,
203                     int         large_arc_flag,
204                     int         sweep_flag,
205                     qreal               x,
206                     qreal               y,
207                     qreal curx, qreal cury)
208 {
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;
214     int i, n_segs;
215     qreal dx, dy, dx1, dy1, Pr1, Pr2, Px, Py, check;
216
217     rx = qAbs(rx);
218     ry = qAbs(ry);
219
220     sin_th = qSin(x_axis_rotation * (Q_PI / 180.0));
221     cos_th = qCos(x_axis_rotation * (Q_PI / 180.0));
222
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;
227     Pr1 = rx * rx;
228     Pr2 = ry * ry;
229     Px = dx1 * dx1;
230     Py = dy1 * dy1;
231     /* Spec : check if radii are large enough */
232     check = Px / Pr1 + Py / Pr2;
233     if (check > 1) {
234         rx = rx * qSqrt(check);
235         ry = ry * qSqrt(check);
236     }
237
238     a00 =  cos_th / rx;
239     a01 =  sin_th / rx;
240     a10 = -sin_th / ry;
241     a11 =  cos_th / ry;
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.
248
249        The arc fits a unit-radius circle in this space.
250     */
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. */
259
260     th0 = qAtan2(y0 - yc, x0 - xc);
261     th1 = qAtan2(y1 - yc, x1 - xc);
262
263     th_arc = th1 - th0;
264     if (th_arc < 0 && sweep_flag)
265         th_arc += 2 * Q_PI;
266     else if (th_arc > 0 && !sweep_flag)
267         th_arc -= 2 * Q_PI;
268
269     n_segs = qCeil(qAbs(th_arc / (Q_PI * 0.5 + 0.001)));
270
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);
276     }
277 }
278
279
280 bool QDeclarativeSvgParser::parsePathDataFast(const QString &dataStr, QPainterPath &path)
281 {
282     qreal x0 = 0, y0 = 0;              // starting point
283     qreal x = 0, y = 0;                // current point
284     char lastMode = 0;
285     QPointF ctrlPt;
286     const QChar *str = dataStr.constData();
287     const QChar *end = str + dataStr.size();
288
289     while (str != end) {
290         while (str->isSpace())
291             ++str;
292         QChar pathElem = *str;
293         ++str;
294         QChar endc = *end;
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();
303         while (count > 0) {
304             qreal offsetX = x;        // correction offsets
305             qreal offsetY = y;        // for relative commands
306             switch (pathElem.unicode()) {
307             case 'm': {
308                 if (count < 2) {
309                     num++;
310                     count--;
311                     break;
312                 }
313                 x = x0 = num[0] + offsetX;
314                 y = y0 = num[1] + offsetY;
315                 num += 2;
316                 count -= 2;
317                 path.moveTo(x0, y0);
318
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');
323             }
324                 break;
325             case 'M': {
326                 if (count < 2) {
327                     num++;
328                     count--;
329                     break;
330                 }
331                 x = x0 = num[0];
332                 y = y0 = num[1];
333                 num += 2;
334                 count -= 2;
335                 path.moveTo(x0, y0);
336
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');
341             }
342                 break;
343             case 'z':
344             case 'Z': {
345                 x = x0;
346                 y = y0;
347                 count--; // skip dummy
348                 num++;
349                 path.closeSubpath();
350             }
351                 break;
352             case 'l': {
353                 if (count < 2) {
354                     num++;
355                     count--;
356                     break;
357                 }
358                 x = num[0] + offsetX;
359                 y = num[1] + offsetY;
360                 num += 2;
361                 count -= 2;
362                 path.lineTo(x, y);
363
364             }
365                 break;
366             case 'L': {
367                 if (count < 2) {
368                     num++;
369                     count--;
370                     break;
371                 }
372                 x = num[0];
373                 y = num[1];
374                 num += 2;
375                 count -= 2;
376                 path.lineTo(x, y);
377             }
378                 break;
379             case 'h': {
380                 x = num[0] + offsetX;
381                 num++;
382                 count--;
383                 path.lineTo(x, y);
384             }
385                 break;
386             case 'H': {
387                 x = num[0];
388                 num++;
389                 count--;
390                 path.lineTo(x, y);
391             }
392                 break;
393             case 'v': {
394                 y = num[0] + offsetY;
395                 num++;
396                 count--;
397                 path.lineTo(x, y);
398             }
399                 break;
400             case 'V': {
401                 y = num[0];
402                 num++;
403                 count--;
404                 path.lineTo(x, y);
405             }
406                 break;
407             case 'c': {
408                 if (count < 6) {
409                     num += count;
410                     count = 0;
411                     break;
412                 }
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);
416                 num += 6;
417                 count -= 6;
418                 path.cubicTo(c1, c2, e);
419                 ctrlPt = c2;
420                 x = e.x();
421                 y = e.y();
422                 break;
423             }
424             case 'C': {
425                 if (count < 6) {
426                     num += count;
427                     count = 0;
428                     break;
429                 }
430                 QPointF c1(num[0], num[1]);
431                 QPointF c2(num[2], num[3]);
432                 QPointF e(num[4], num[5]);
433                 num += 6;
434                 count -= 6;
435                 path.cubicTo(c1, c2, e);
436                 ctrlPt = c2;
437                 x = e.x();
438                 y = e.y();
439                 break;
440             }
441             case 's': {
442                 if (count < 4) {
443                     num += count;
444                     count = 0;
445                     break;
446                 }
447                 QPointF c1;
448                 if (lastMode == 'c' || lastMode == 'C' ||
449                     lastMode == 's' || lastMode == 'S')
450                     c1 = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
451                 else
452                     c1 = QPointF(x, y);
453                 QPointF c2(num[0] + offsetX, num[1] + offsetY);
454                 QPointF e(num[2] + offsetX, num[3] + offsetY);
455                 num += 4;
456                 count -= 4;
457                 path.cubicTo(c1, c2, e);
458                 ctrlPt = c2;
459                 x = e.x();
460                 y = e.y();
461                 break;
462             }
463             case 'S': {
464                 if (count < 4) {
465                     num += count;
466                     count = 0;
467                     break;
468                 }
469                 QPointF c1;
470                 if (lastMode == 'c' || lastMode == 'C' ||
471                     lastMode == 's' || lastMode == 'S')
472                     c1 = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
473                 else
474                     c1 = QPointF(x, y);
475                 QPointF c2(num[0], num[1]);
476                 QPointF e(num[2], num[3]);
477                 num += 4;
478                 count -= 4;
479                 path.cubicTo(c1, c2, e);
480                 ctrlPt = c2;
481                 x = e.x();
482                 y = e.y();
483                 break;
484             }
485             case 'q': {
486                 if (count < 4) {
487                     num += count;
488                     count = 0;
489                     break;
490                 }
491                 QPointF c(num[0] + offsetX, num[1] + offsetY);
492                 QPointF e(num[2] + offsetX, num[3] + offsetY);
493                 num += 4;
494                 count -= 4;
495                 path.quadTo(c, e);
496                 ctrlPt = c;
497                 x = e.x();
498                 y = e.y();
499                 break;
500             }
501             case 'Q': {
502                 if (count < 4) {
503                     num += count;
504                     count = 0;
505                     break;
506                 }
507                 QPointF c(num[0], num[1]);
508                 QPointF e(num[2], num[3]);
509                 num += 4;
510                 count -= 4;
511                 path.quadTo(c, e);
512                 ctrlPt = c;
513                 x = e.x();
514                 y = e.y();
515                 break;
516             }
517             case 't': {
518                 if (count < 2) {
519                     num += count;
520                     count = 0;
521                     break;
522                 }
523                 QPointF e(num[0] + offsetX, num[1] + offsetY);
524                 num += 2;
525                 count -= 2;
526                 QPointF c;
527                 if (lastMode == 'q' || lastMode == 'Q' ||
528                     lastMode == 't' || lastMode == 'T')
529                     c = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
530                 else
531                     c = QPointF(x, y);
532                 path.quadTo(c, e);
533                 ctrlPt = c;
534                 x = e.x();
535                 y = e.y();
536                 break;
537             }
538             case 'T': {
539                 if (count < 2) {
540                     num += count;
541                     count = 0;
542                     break;
543                 }
544                 QPointF e(num[0], num[1]);
545                 num += 2;
546                 count -= 2;
547                 QPointF c;
548                 if (lastMode == 'q' || lastMode == 'Q' ||
549                     lastMode == 't' || lastMode == 'T')
550                     c = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
551                 else
552                     c = QPointF(x, y);
553                 path.quadTo(c, e);
554                 ctrlPt = c;
555                 x = e.x();
556                 y = e.y();
557                 break;
558             }
559             case 'a': {
560                 if (count < 7) {
561                     num += count;
562                     count = 0;
563                     break;
564                 }
565                 qreal rx = (*num++);
566                 qreal ry = (*num++);
567                 qreal xAxisRotation = (*num++);
568                 qreal largeArcFlag  = (*num++);
569                 qreal sweepFlag = (*num++);
570                 qreal ex = (*num++) + offsetX;
571                 qreal ey = (*num++) + offsetY;
572                 count -= 7;
573                 qreal curx = x;
574                 qreal cury = y;
575                 pathArc(path, rx, ry, xAxisRotation, int(largeArcFlag),
576                         int(sweepFlag), ex, ey, curx, cury);
577
578                 x = ex;
579                 y = ey;
580             }
581                 break;
582             case 'A': {
583                 if (count < 7) {
584                     num += count;
585                     count = 0;
586                     break;
587                 }
588                 qreal rx = (*num++);
589                 qreal ry = (*num++);
590                 qreal xAxisRotation = (*num++);
591                 qreal largeArcFlag  = (*num++);
592                 qreal sweepFlag = (*num++);
593                 qreal ex = (*num++);
594                 qreal ey = (*num++);
595                 count -= 7;
596                 qreal curx = x;
597                 qreal cury = y;
598                 pathArc(path, rx, ry, xAxisRotation, int(largeArcFlag),
599                         int(sweepFlag), ex, ey, curx, cury);
600
601                 x = ex;
602                 y = ey;
603             }
604                 break;
605             default:
606                 return false;
607             }
608             lastMode = pathElem.toLatin1();
609         }
610     }
611     return true;
612 }
613
614 QT_END_NAMESPACE