Replace 'i < len-1 && func(i+1)' by 'i+1 < len && func(i+1)'
[profile/ivi/qtbase.git] / src / gui / painting / qstroker.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtGui 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 "private/qstroker_p.h"
43 #include "private/qbezier_p.h"
44 #include "private/qmath_p.h"
45 #include "qline.h"
46 #include "qtransform.h"
47 #include <qmath.h>
48
49 QT_BEGIN_NAMESPACE
50
51 // #define QPP_STROKE_DEBUG
52
53 class QSubpathForwardIterator
54 {
55 public:
56     QSubpathForwardIterator(const QDataBuffer<QStrokerOps::Element> *path)
57         : m_path(path), m_pos(0) { }
58     inline int position() const { return m_pos; }
59     inline bool hasNext() const { return m_pos < m_path->size(); }
60     inline QStrokerOps::Element next() { Q_ASSERT(hasNext()); return m_path->at(m_pos++); }
61
62 private:
63     const QDataBuffer<QStrokerOps::Element> *m_path;
64     int m_pos;
65 };
66
67 class QSubpathBackwardIterator
68 {
69 public:
70     QSubpathBackwardIterator(const QDataBuffer<QStrokerOps::Element> *path)
71         : m_path(path), m_pos(path->size() - 1) { }
72
73     inline int position() const { return m_pos; }
74
75     inline bool hasNext() const { return m_pos >= 0; }
76
77     inline QStrokerOps::Element next()
78     {
79         Q_ASSERT(hasNext());
80
81         QStrokerOps::Element ce = m_path->at(m_pos);   // current element
82
83         if (m_pos == m_path->size() - 1) {
84             --m_pos;
85             ce.type = QPainterPath::MoveToElement;
86             return ce;
87         }
88
89         const QStrokerOps::Element &pe = m_path->at(m_pos + 1); // previous element
90
91         switch (pe.type) {
92         case QPainterPath::LineToElement:
93             ce.type = QPainterPath::LineToElement;
94             break;
95         case QPainterPath::CurveToDataElement:
96             // First control point?
97             if (ce.type == QPainterPath::CurveToElement) {
98                 ce.type = QPainterPath::CurveToDataElement;
99             } else { // Second control point then
100                 ce.type = QPainterPath::CurveToElement;
101             }
102             break;
103         case QPainterPath::CurveToElement:
104             ce.type = QPainterPath::CurveToDataElement;
105             break;
106         default:
107             qWarning("QSubpathReverseIterator::next: Case %d unhandled", ce.type);
108             break;
109         }
110         --m_pos;
111
112         return ce;
113     }
114
115 private:
116     const QDataBuffer<QStrokerOps::Element> *m_path;
117     int m_pos;
118 };
119
120 class QSubpathFlatIterator
121 {
122 public:
123     QSubpathFlatIterator(const QDataBuffer<QStrokerOps::Element> *path, qreal threshold)
124         : m_path(path), m_pos(0), m_curve_index(-1), m_curve_threshold(threshold) { }
125
126     inline bool hasNext() const { return m_curve_index >= 0 || m_pos < m_path->size(); }
127
128     QStrokerOps::Element next()
129     {
130         Q_ASSERT(hasNext());
131
132         if (m_curve_index >= 0) {
133             QStrokerOps::Element e = { QPainterPath::LineToElement,
134                                        qt_real_to_fixed(m_curve.at(m_curve_index).x()),
135                                        qt_real_to_fixed(m_curve.at(m_curve_index).y())
136                                        };
137             ++m_curve_index;
138             if (m_curve_index >= m_curve.size())
139                 m_curve_index = -1;
140             return e;
141         }
142
143         QStrokerOps::Element e = m_path->at(m_pos);
144         if (e.isCurveTo()) {
145             Q_ASSERT(m_pos > 0);
146             Q_ASSERT(m_pos < m_path->size());
147
148             m_curve = QBezier::fromPoints(QPointF(qt_fixed_to_real(m_path->at(m_pos-1).x),
149                                                   qt_fixed_to_real(m_path->at(m_pos-1).y)),
150                                           QPointF(qt_fixed_to_real(e.x),
151                                                   qt_fixed_to_real(e.y)),
152                                           QPointF(qt_fixed_to_real(m_path->at(m_pos+1).x),
153                                                   qt_fixed_to_real(m_path->at(m_pos+1).y)),
154                                           QPointF(qt_fixed_to_real(m_path->at(m_pos+2).x),
155                                                   qt_fixed_to_real(m_path->at(m_pos+2).y))).toPolygon(m_curve_threshold);
156             m_curve_index = 1;
157             e.type = QPainterPath::LineToElement;
158             e.x = m_curve.at(0).x();
159             e.y = m_curve.at(0).y();
160             m_pos += 2;
161         }
162         Q_ASSERT(e.isLineTo() || e.isMoveTo());
163         ++m_pos;
164         return e;
165     }
166
167 private:
168     const QDataBuffer<QStrokerOps::Element> *m_path;
169     int m_pos;
170     QPolygonF m_curve;
171     int m_curve_index;
172     qreal m_curve_threshold;
173 };
174
175 template <class Iterator> bool qt_stroke_side(Iterator *it, QStroker *stroker,
176                                               bool capFirst, QLineF *startTangent);
177
178 /*******************************************************************************
179  * QLineF::angle gives us the smalles angle between two lines. Here we
180  * want to identify the line's angle direction on the unit circle.
181  */
182 static inline qreal adapted_angle_on_x(const QLineF &line)
183 {
184     qreal angle = line.angle(QLineF(0, 0, 1, 0));
185     if (line.dy() > 0)
186         angle = 360 - angle;
187     return angle;
188 }
189
190 QStrokerOps::QStrokerOps()
191     : m_elements(0)
192     , m_curveThreshold(qt_real_to_fixed(0.25))
193     , m_dashThreshold(qt_real_to_fixed(0.25))
194     , m_customData(0)
195     , m_moveTo(0)
196     , m_lineTo(0)
197     , m_cubicTo(0)
198 {
199 }
200
201 QStrokerOps::~QStrokerOps()
202 {
203 }
204
205 /*!
206     Prepares the stroker. Call this function once before starting a
207     stroke by calling moveTo, lineTo or cubicTo.
208
209     The \a customData is passed back through that callback functions
210     and can be used by the user to for instance maintain state
211     information.
212 */
213 void QStrokerOps::begin(void *customData)
214 {
215     m_customData = customData;
216     m_elements.reset();
217 }
218
219
220 /*!
221     Finishes the stroke. Call this function once when an entire
222     primitive has been stroked.
223 */
224 void QStrokerOps::end()
225 {
226     if (m_elements.size() > 1)
227         processCurrentSubpath();
228     m_customData = 0;
229 }
230
231 /*!
232     Convenience function that decomposes \a path into begin(),
233     moveTo(), lineTo(), curevTo() and end() calls.
234
235     The \a customData parameter is used in the callback functions
236
237     The \a matrix is used to transform the points before input to the
238     stroker.
239
240     \sa begin()
241 */
242 void QStrokerOps::strokePath(const QPainterPath &path, void *customData, const QTransform &matrix)
243 {
244     if (path.isEmpty())
245         return;
246
247     setCurveThresholdFromTransform(QTransform());
248     begin(customData);
249     int count = path.elementCount();
250     if (matrix.isIdentity()) {
251         for (int i=0; i<count; ++i) {
252             const QPainterPath::Element &e = path.elementAt(i);
253             switch (e.type) {
254             case QPainterPath::MoveToElement:
255                 moveTo(qt_real_to_fixed(e.x), qt_real_to_fixed(e.y));
256                 break;
257             case QPainterPath::LineToElement:
258                 lineTo(qt_real_to_fixed(e.x), qt_real_to_fixed(e.y));
259                 break;
260             case QPainterPath::CurveToElement:
261                 {
262                     const QPainterPath::Element &cp2 = path.elementAt(++i);
263                     const QPainterPath::Element &ep = path.elementAt(++i);
264                     cubicTo(qt_real_to_fixed(e.x), qt_real_to_fixed(e.y),
265                             qt_real_to_fixed(cp2.x), qt_real_to_fixed(cp2.y),
266                             qt_real_to_fixed(ep.x), qt_real_to_fixed(ep.y));
267                 }
268                 break;
269             default:
270                 break;
271             }
272         }
273     } else {
274         for (int i=0; i<count; ++i) {
275             const QPainterPath::Element &e = path.elementAt(i);
276             QPointF pt = QPointF(e.x, e.y) * matrix;
277             switch (e.type) {
278             case QPainterPath::MoveToElement:
279                 moveTo(qt_real_to_fixed(pt.x()), qt_real_to_fixed(pt.y()));
280                 break;
281             case QPainterPath::LineToElement:
282                 lineTo(qt_real_to_fixed(pt.x()), qt_real_to_fixed(pt.y()));
283                 break;
284             case QPainterPath::CurveToElement:
285                 {
286                     QPointF cp2 = ((QPointF) path.elementAt(++i)) * matrix;
287                     QPointF ep = ((QPointF) path.elementAt(++i)) * matrix;
288                     cubicTo(qt_real_to_fixed(pt.x()), qt_real_to_fixed(pt.y()),
289                             qt_real_to_fixed(cp2.x()), qt_real_to_fixed(cp2.y()),
290                             qt_real_to_fixed(ep.x()), qt_real_to_fixed(ep.y()));
291                 }
292                 break;
293             default:
294                 break;
295             }
296         }
297     }
298     end();
299 }
300
301 /*!
302     Convenience function for stroking a polygon of the \a pointCount
303     first points in \a points. If \a implicit_close is set to true a
304     line is implictly drawn between the first and last point in the
305     polygon. Typically true for polygons and false for polylines.
306
307     The \a matrix is used to transform the points before they enter the
308     stroker.
309
310     \sa begin()
311 */
312
313 void QStrokerOps::strokePolygon(const QPointF *points, int pointCount, bool implicit_close,
314                                 void *data, const QTransform &matrix)
315 {
316     if (!pointCount)
317         return;
318
319     setCurveThresholdFromTransform(QTransform());
320     begin(data);
321     if (matrix.isIdentity()) {
322         moveTo(qt_real_to_fixed(points[0].x()), qt_real_to_fixed(points[0].y()));
323         for (int i=1; i<pointCount; ++i)
324             lineTo(qt_real_to_fixed(points[i].x()),
325                    qt_real_to_fixed(points[i].y()));
326         if (implicit_close)
327             lineTo(qt_real_to_fixed(points[0].x()), qt_real_to_fixed(points[0].y()));
328     } else {
329         QPointF start = points[0] * matrix;
330         moveTo(qt_real_to_fixed(start.x()), qt_real_to_fixed(start.y()));
331         for (int i=1; i<pointCount; ++i) {
332             QPointF pt = points[i] * matrix;
333             lineTo(qt_real_to_fixed(pt.x()), qt_real_to_fixed(pt.y()));
334         }
335         if (implicit_close)
336             lineTo(qt_real_to_fixed(start.x()), qt_real_to_fixed(start.y()));
337     }
338     end();
339 }
340
341 /*!
342     Convenience function for stroking an ellipse with bounding rect \a
343     rect. The \a matrix is used to transform the coordinates before
344     they enter the stroker.
345 */
346 void QStrokerOps::strokeEllipse(const QRectF &rect, void *data, const QTransform &matrix)
347 {
348     int count = 0;
349     QPointF pts[12];
350     QPointF start = qt_curves_for_arc(rect, 0, -360, pts, &count);
351     Q_ASSERT(count == 12); // a perfect circle..
352
353     if (!matrix.isIdentity()) {
354         start = start * matrix;
355         for (int i=0; i<12; ++i) {
356             pts[i] = pts[i] * matrix;
357         }
358     }
359
360     setCurveThresholdFromTransform(QTransform());
361     begin(data);
362     moveTo(qt_real_to_fixed(start.x()), qt_real_to_fixed(start.y()));
363     for (int i=0; i<12; i+=3) {
364         cubicTo(qt_real_to_fixed(pts[i].x()), qt_real_to_fixed(pts[i].y()),
365                 qt_real_to_fixed(pts[i+1].x()), qt_real_to_fixed(pts[i+1].y()),
366                 qt_real_to_fixed(pts[i+2].x()), qt_real_to_fixed(pts[i+2].y()));
367     }
368     end();
369 }
370
371
372 QStroker::QStroker()
373     : m_capStyle(SquareJoin), m_joinStyle(FlatJoin),
374       m_back1X(0), m_back1Y(0),
375       m_back2X(0), m_back2Y(0)
376 {
377     m_strokeWidth = qt_real_to_fixed(1);
378     m_miterLimit = qt_real_to_fixed(2);
379 }
380
381 QStroker::~QStroker()
382 {
383 }
384
385 Qt::PenCapStyle QStroker::capForJoinMode(LineJoinMode mode)
386 {
387     if (mode == FlatJoin) return Qt::FlatCap;
388     else if (mode == SquareJoin) return Qt::SquareCap;
389     else return Qt::RoundCap;
390 }
391
392 QStroker::LineJoinMode QStroker::joinModeForCap(Qt::PenCapStyle style)
393 {
394     if (style == Qt::FlatCap) return FlatJoin;
395     else if (style == Qt::SquareCap) return SquareJoin;
396     else return RoundCap;
397 }
398
399 Qt::PenJoinStyle QStroker::joinForJoinMode(LineJoinMode mode)
400 {
401     if (mode == FlatJoin) return Qt::BevelJoin;
402     else if (mode == MiterJoin) return Qt::MiterJoin;
403     else if (mode == SvgMiterJoin) return Qt::SvgMiterJoin;
404     else return Qt::RoundJoin;
405 }
406
407 QStroker::LineJoinMode QStroker::joinModeForJoin(Qt::PenJoinStyle joinStyle)
408 {
409     if (joinStyle == Qt::BevelJoin) return FlatJoin;
410     else if (joinStyle == Qt::MiterJoin) return MiterJoin;
411     else if (joinStyle == Qt::SvgMiterJoin) return SvgMiterJoin;
412     else return RoundJoin;
413 }
414
415
416 /*!
417     This function is called to stroke the currently built up
418     subpath. The subpath is cleared when the function completes.
419 */
420 void QStroker::processCurrentSubpath()
421 {
422     Q_ASSERT(!m_elements.isEmpty());
423     Q_ASSERT(m_elements.first().type == QPainterPath::MoveToElement);
424     Q_ASSERT(m_elements.size() > 1);
425
426     QSubpathForwardIterator fwit(&m_elements);
427     QSubpathBackwardIterator bwit(&m_elements);
428
429     QLineF fwStartTangent, bwStartTangent;
430
431     bool fwclosed = qt_stroke_side(&fwit, this, false, &fwStartTangent);
432     bool bwclosed = qt_stroke_side(&bwit, this, !fwclosed, &bwStartTangent);
433
434     if (!bwclosed)
435         joinPoints(m_elements.at(0).x, m_elements.at(0).y, fwStartTangent, m_capStyle);
436 }
437
438
439 /*!
440     \internal
441 */
442 void QStroker::joinPoints(qfixed focal_x, qfixed focal_y, const QLineF &nextLine, LineJoinMode join)
443 {
444 #ifdef QPP_STROKE_DEBUG
445     printf(" -----> joinPoints: around=(%.0f, %.0f), next_p1=(%.0f, %.f) next_p2=(%.0f, %.f)\n",
446            qt_fixed_to_real(focal_x),
447            qt_fixed_to_real(focal_y),
448            nextLine.x1(), nextLine.y1(), nextLine.x2(), nextLine.y2());
449 #endif
450     // points connected already, don't join
451
452 #if !defined (QFIXED_26_6) && !defined (Q_FIXED_32_32)
453     if (qFuzzyCompare(m_back1X, nextLine.x1()) && qFuzzyCompare(m_back1Y, nextLine.y1()))
454         return;
455 #else
456     if (m_back1X == qt_real_to_fixed(nextLine.x1())
457         && m_back1Y == qt_real_to_fixed(nextLine.y1())) {
458         return;
459     }
460 #endif
461
462     if (join == FlatJoin) {
463         QLineF prevLine(qt_fixed_to_real(m_back2X), qt_fixed_to_real(m_back2Y),
464                         qt_fixed_to_real(m_back1X), qt_fixed_to_real(m_back1Y));
465         QPointF isect;
466         QLineF::IntersectType type = prevLine.intersect(nextLine, &isect);
467         QLineF shortCut(prevLine.p2(), nextLine.p1());
468         qreal angle = shortCut.angleTo(prevLine);
469         if (type == QLineF::BoundedIntersection || (angle > 90 && !qFuzzyCompare(angle, (qreal)90))) {
470             emitLineTo(focal_x, focal_y);
471             emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
472             return;
473         }
474         emitLineTo(qt_real_to_fixed(nextLine.x1()),
475                    qt_real_to_fixed(nextLine.y1()));
476
477     } else {
478         QLineF prevLine(qt_fixed_to_real(m_back2X), qt_fixed_to_real(m_back2Y),
479                         qt_fixed_to_real(m_back1X), qt_fixed_to_real(m_back1Y));
480
481         QPointF isect;
482         QLineF::IntersectType type = prevLine.intersect(nextLine, &isect);
483
484         if (join == MiterJoin) {
485             qreal appliedMiterLimit = qt_fixed_to_real(m_strokeWidth * m_miterLimit);
486
487             // If we are on the inside, do the short cut...
488             QLineF shortCut(prevLine.p2(), nextLine.p1());
489             qreal angle = shortCut.angleTo(prevLine);
490             if (type == QLineF::BoundedIntersection || (angle > 90 && !qFuzzyCompare(angle, (qreal)90))) {
491                 emitLineTo(focal_x, focal_y);
492                 emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
493                 return;
494             }
495             QLineF miterLine(QPointF(qt_fixed_to_real(m_back1X),
496                                      qt_fixed_to_real(m_back1Y)), isect);
497             if (type == QLineF::NoIntersection || miterLine.length() > appliedMiterLimit) {
498                 QLineF l1(prevLine);
499                 l1.setLength(appliedMiterLimit);
500                 l1.translate(prevLine.dx(), prevLine.dy());
501
502                 QLineF l2(nextLine);
503                 l2.setLength(appliedMiterLimit);
504                 l2.translate(-l2.dx(), -l2.dy());
505
506                 emitLineTo(qt_real_to_fixed(l1.x2()), qt_real_to_fixed(l1.y2()));
507                 emitLineTo(qt_real_to_fixed(l2.x1()), qt_real_to_fixed(l2.y1()));
508                 emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
509             } else {
510                 emitLineTo(qt_real_to_fixed(isect.x()), qt_real_to_fixed(isect.y()));
511                 emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
512             }
513
514         } else if (join == SquareJoin) {
515             qfixed offset = m_strokeWidth / 2;
516
517             QLineF l1(prevLine);
518             l1.translate(l1.dx(), l1.dy());
519             l1.setLength(qt_fixed_to_real(offset));
520             QLineF l2(nextLine.p2(), nextLine.p1());
521             l2.translate(l2.dx(), l2.dy());
522             l2.setLength(qt_fixed_to_real(offset));
523             emitLineTo(qt_real_to_fixed(l1.x2()), qt_real_to_fixed(l1.y2()));
524             emitLineTo(qt_real_to_fixed(l2.x2()), qt_real_to_fixed(l2.y2()));
525             emitLineTo(qt_real_to_fixed(l2.x1()), qt_real_to_fixed(l2.y1()));
526
527         } else if (join == RoundJoin) {
528             qfixed offset = m_strokeWidth / 2;
529
530             QLineF shortCut(prevLine.p2(), nextLine.p1());
531             qreal angle = shortCut.angleTo(prevLine);
532             if (type == QLineF::BoundedIntersection || (angle > 90 && !qFuzzyCompare(angle, (qreal)90))) {
533                 emitLineTo(focal_x, focal_y);
534                 emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
535                 return;
536             }
537             qreal l1_on_x = adapted_angle_on_x(prevLine);
538             qreal l2_on_x = adapted_angle_on_x(nextLine);
539
540             qreal sweepLength = qAbs(l2_on_x - l1_on_x);
541
542             int point_count;
543             QPointF curves[15];
544
545             QPointF curve_start =
546                 qt_curves_for_arc(QRectF(qt_fixed_to_real(focal_x - offset),
547                                          qt_fixed_to_real(focal_y - offset),
548                                          qt_fixed_to_real(offset * 2),
549                                          qt_fixed_to_real(offset * 2)),
550                                   l1_on_x + 90, -sweepLength,
551                                   curves, &point_count);
552
553 //             // line to the beginning of the arc segment, (should not be needed).
554 //             emitLineTo(qt_real_to_fixed(curve_start.x()), qt_real_to_fixed(curve_start.y()));
555             Q_UNUSED(curve_start);
556
557             for (int i=0; i<point_count; i+=3) {
558                 emitCubicTo(qt_real_to_fixed(curves[i].x()),
559                             qt_real_to_fixed(curves[i].y()),
560                             qt_real_to_fixed(curves[i+1].x()),
561                             qt_real_to_fixed(curves[i+1].y()),
562                             qt_real_to_fixed(curves[i+2].x()),
563                             qt_real_to_fixed(curves[i+2].y()));
564             }
565
566             // line to the end of the arc segment, (should also not be needed).
567             emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
568
569         // Same as round join except we know its 180 degrees. Can also optimize this
570         // later based on the addEllipse logic
571         } else if (join == RoundCap) {
572             qfixed offset = m_strokeWidth / 2;
573
574             // first control line
575             QLineF l1 = prevLine;
576             l1.translate(l1.dx(), l1.dy());
577             l1.setLength(QT_PATH_KAPPA * offset);
578
579             // second control line, find through normal between prevLine and focal.
580             QLineF l2(qt_fixed_to_real(focal_x), qt_fixed_to_real(focal_y),
581                       prevLine.x2(), prevLine.y2());
582             l2.translate(-l2.dy(), l2.dx());
583             l2.setLength(QT_PATH_KAPPA * offset);
584
585             emitCubicTo(qt_real_to_fixed(l1.x2()),
586                         qt_real_to_fixed(l1.y2()),
587                         qt_real_to_fixed(l2.x2()),
588                         qt_real_to_fixed(l2.y2()),
589                         qt_real_to_fixed(l2.x1()),
590                         qt_real_to_fixed(l2.y1()));
591
592             // move so that it matches
593             l2 = QLineF(l2.x1(), l2.y1(), l2.x1()-l2.dx(), l2.y1()-l2.dy());
594
595             // last line is parallel to l1 so just shift it down.
596             l1.translate(nextLine.x1() - l1.x1(), nextLine.y1() - l1.y1());
597
598             emitCubicTo(qt_real_to_fixed(l2.x2()),
599                         qt_real_to_fixed(l2.y2()),
600                         qt_real_to_fixed(l1.x2()),
601                         qt_real_to_fixed(l1.y2()),
602                         qt_real_to_fixed(l1.x1()),
603                         qt_real_to_fixed(l1.y1()));
604         } else if (join == SvgMiterJoin) {
605             QLineF shortCut(prevLine.p2(), nextLine.p1());
606             qreal angle = shortCut.angleTo(prevLine);
607             if (type == QLineF::BoundedIntersection || (angle > 90 && !qFuzzyCompare(angle, (qreal)90))) {
608                 emitLineTo(focal_x, focal_y);
609                 emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
610                 return;
611             }
612             QLineF miterLine(QPointF(qt_fixed_to_real(focal_x),
613                                      qt_fixed_to_real(focal_y)), isect);
614             if (type == QLineF::NoIntersection || miterLine.length() > qt_fixed_to_real(m_strokeWidth * m_miterLimit) / 2) {
615                 emitLineTo(qt_real_to_fixed(nextLine.x1()),
616                            qt_real_to_fixed(nextLine.y1()));
617             } else {
618                 emitLineTo(qt_real_to_fixed(isect.x()), qt_real_to_fixed(isect.y()));
619                 emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
620             }
621         } else {
622             Q_ASSERT(!"QStroker::joinPoints(), bad join style...");
623         }
624     }
625 }
626
627
628 /*
629    Strokes a subpath side using the \a it as source. Results are put into
630    \a stroke. The function returns true if the subpath side was closed.
631    If \a capFirst is true, we will use capPoints instead of joinPoints to
632    connect the first segment, other segments will be joined using joinPoints.
633    This is to put capping in order...
634 */
635 template <class Iterator> bool qt_stroke_side(Iterator *it,
636                                               QStroker *stroker,
637                                               bool capFirst,
638                                               QLineF *startTangent)
639 {
640     // Used in CurveToElement section below.
641     const int MAX_OFFSET = 16;
642     QBezier offsetCurves[MAX_OFFSET];
643
644     Q_ASSERT(it->hasNext()); // The initaial move to
645     QStrokerOps::Element first_element = it->next();
646     Q_ASSERT(first_element.isMoveTo());
647
648     qfixed2d start = first_element;
649
650 #ifdef QPP_STROKE_DEBUG
651     qDebug(" -> (side) [%.2f, %.2f], startPos=%d",
652            qt_fixed_to_real(start.x),
653            qt_fixed_to_real(start.y));
654 #endif
655
656     qfixed2d prev = start;
657
658     bool first = true;
659
660     qfixed offset = stroker->strokeWidth() / 2;
661
662     while (it->hasNext()) {
663         QStrokerOps::Element e = it->next();
664
665         // LineToElement
666         if (e.isLineTo()) {
667 #ifdef QPP_STROKE_DEBUG
668             qDebug("\n ---> (side) lineto [%.2f, %.2f]", e.x, e.y);
669 #endif
670             QLineF line(qt_fixed_to_real(prev.x), qt_fixed_to_real(prev.y),
671                         qt_fixed_to_real(e.x), qt_fixed_to_real(e.y));
672             if (line.p1() != line.p2()) {
673                 QLineF normal = line.normalVector();
674                 normal.setLength(offset);
675                 line.translate(normal.dx(), normal.dy());
676
677                 // If we are starting a new subpath, move to correct starting point.
678                 if (first) {
679                     if (capFirst)
680                         stroker->joinPoints(prev.x, prev.y, line, stroker->capStyleMode());
681                     else
682                         stroker->emitMoveTo(qt_real_to_fixed(line.x1()), qt_real_to_fixed(line.y1()));
683                     *startTangent = line;
684                     first = false;
685                 } else {
686                     stroker->joinPoints(prev.x, prev.y, line, stroker->joinStyleMode());
687                 }
688
689                 // Add the stroke for this line.
690                 stroker->emitLineTo(qt_real_to_fixed(line.x2()),
691                                     qt_real_to_fixed(line.y2()));
692                 prev = e;
693             }
694
695         // CurveToElement
696         } else if (e.isCurveTo()) {
697             QStrokerOps::Element cp2 = it->next(); // control point 2
698             QStrokerOps::Element ep = it->next();  // end point
699
700 #ifdef QPP_STROKE_DEBUG
701             qDebug("\n ---> (side) cubicTo [%.2f, %.2f]",
702                    qt_fixed_to_real(ep.x),
703                    qt_fixed_to_real(ep.y));
704 #endif
705
706             QBezier bezier =
707                 QBezier::fromPoints(QPointF(qt_fixed_to_real(prev.x), qt_fixed_to_real(prev.y)),
708                                     QPointF(qt_fixed_to_real(e.x), qt_fixed_to_real(e.y)),
709                                     QPointF(qt_fixed_to_real(cp2.x), qt_fixed_to_real(cp2.y)),
710                                     QPointF(qt_fixed_to_real(ep.x), qt_fixed_to_real(ep.y)));
711
712             int count = bezier.shifted(offsetCurves,
713                                        MAX_OFFSET,
714                                        offset,
715                                        stroker->curveThreshold());
716
717             if (count) {
718                 // If we are starting a new subpath, move to correct starting point
719                 QLineF tangent = bezier.startTangent();
720                 tangent.translate(offsetCurves[0].pt1() - bezier.pt1());
721                 if (first) {
722                     QPointF pt = offsetCurves[0].pt1();
723                     if (capFirst) {
724                         stroker->joinPoints(prev.x, prev.y,
725                                             tangent,
726                                             stroker->capStyleMode());
727                     } else {
728                         stroker->emitMoveTo(qt_real_to_fixed(pt.x()),
729                                             qt_real_to_fixed(pt.y()));
730                     }
731                     *startTangent = tangent;
732                     first = false;
733                 } else {
734                     stroker->joinPoints(prev.x, prev.y,
735                                         tangent,
736                                         stroker->joinStyleMode());
737                 }
738
739                 // Add these beziers
740                 for (int i=0; i<count; ++i) {
741                     QPointF cp1 = offsetCurves[i].pt2();
742                     QPointF cp2 = offsetCurves[i].pt3();
743                     QPointF ep = offsetCurves[i].pt4();
744                     stroker->emitCubicTo(qt_real_to_fixed(cp1.x()), qt_real_to_fixed(cp1.y()),
745                                          qt_real_to_fixed(cp2.x()), qt_real_to_fixed(cp2.y()),
746                                          qt_real_to_fixed(ep.x()), qt_real_to_fixed(ep.y()));
747                 }
748             }
749
750             prev = ep;
751         }
752     }
753
754     if (start == prev) {
755         // closed subpath, join first and last point
756 #ifdef QPP_STROKE_DEBUG
757         qDebug("\n ---> (side) closed subpath");
758 #endif
759         stroker->joinPoints(prev.x, prev.y, *startTangent, stroker->joinStyleMode());
760         return true;
761     } else {
762 #ifdef QPP_STROKE_DEBUG
763         qDebug("\n ---> (side) open subpath");
764 #endif
765         return false;
766     }
767 }
768
769 /*!
770     \internal
771
772     For a given angle in the range [0 .. 90], finds the corresponding parameter t
773     of the prototype cubic bezier arc segment
774     b = fromPoints(QPointF(1, 0), QPointF(1, KAPPA), QPointF(KAPPA, 1), QPointF(0, 1));
775
776     From the bezier equation:
777     b.pointAt(t).x() = (1-t)^3 + t*(1-t)^2 + t^2*(1-t)*KAPPA
778     b.pointAt(t).y() = t*(1-t)^2 * KAPPA + t^2*(1-t) + t^3
779
780     Third degree coefficients:
781     b.pointAt(t).x() = at^3 + bt^2 + ct + d
782     where a = 2-3*KAPPA, b = 3*(KAPPA-1), c = 0, d = 1
783
784     b.pointAt(t).y() = at^3 + bt^2 + ct + d
785     where a = 3*KAPPA-2, b = 6*KAPPA+3, c = 3*KAPPA, d = 0
786
787     Newton's method to find the zero of a function:
788     given a function f(x) and initial guess x_0
789     x_1 = f(x_0) / f'(x_0)
790     x_2 = f(x_1) / f'(x_1)
791     etc...
792 */
793
794 qreal qt_t_for_arc_angle(qreal angle)
795 {
796     if (qFuzzyIsNull(angle))
797         return 0;
798
799     if (qFuzzyCompare(angle, qreal(90)))
800         return 1;
801
802     qreal radians = Q_PI * angle / 180;
803     qreal cosAngle = qCos(radians);
804     qreal sinAngle = qSin(radians);
805
806     // initial guess
807     qreal tc = angle / 90;
808     // do some iterations of newton's method to approximate cosAngle
809     // finds the zero of the function b.pointAt(tc).x() - cosAngle
810     tc -= ((((2-3*QT_PATH_KAPPA) * tc + 3*(QT_PATH_KAPPA-1)) * tc) * tc + 1 - cosAngle) // value
811          / (((6-9*QT_PATH_KAPPA) * tc + 6*(QT_PATH_KAPPA-1)) * tc); // derivative
812     tc -= ((((2-3*QT_PATH_KAPPA) * tc + 3*(QT_PATH_KAPPA-1)) * tc) * tc + 1 - cosAngle) // value
813          / (((6-9*QT_PATH_KAPPA) * tc + 6*(QT_PATH_KAPPA-1)) * tc); // derivative
814
815     // initial guess
816     qreal ts = tc;
817     // do some iterations of newton's method to approximate sinAngle
818     // finds the zero of the function b.pointAt(tc).y() - sinAngle
819     ts -= ((((3*QT_PATH_KAPPA-2) * ts -  6*QT_PATH_KAPPA + 3) * ts + 3*QT_PATH_KAPPA) * ts - sinAngle)
820          / (((9*QT_PATH_KAPPA-6) * ts + 12*QT_PATH_KAPPA - 6) * ts + 3*QT_PATH_KAPPA);
821     ts -= ((((3*QT_PATH_KAPPA-2) * ts -  6*QT_PATH_KAPPA + 3) * ts + 3*QT_PATH_KAPPA) * ts - sinAngle)
822          / (((9*QT_PATH_KAPPA-6) * ts + 12*QT_PATH_KAPPA - 6) * ts + 3*QT_PATH_KAPPA);
823
824     // use the average of the t that best approximates cosAngle
825     // and the t that best approximates sinAngle
826     qreal t = 0.5 * (tc + ts);
827
828 #if 0
829     printf("angle: %f, t: %f\n", angle, t);
830     qreal a, b, c, d;
831     bezierCoefficients(t, a, b, c, d);
832     printf("cosAngle: %.10f, value: %.10f\n", cosAngle, a + b + c * QT_PATH_KAPPA);
833     printf("sinAngle: %.10f, value: %.10f\n", sinAngle, b * QT_PATH_KAPPA + c + d);
834 #endif
835
836     return t;
837 }
838
839 Q_GUI_EXPORT void qt_find_ellipse_coords(const QRectF &r, qreal angle, qreal length,
840                             QPointF* startPoint, QPointF *endPoint);
841
842 /*!
843     \internal
844
845     Creates a number of curves for a given arc definition. The arc is
846     defined an arc along the ellipses that fits into \a rect starting
847     at \a startAngle and an arc length of \a sweepLength.
848
849     The function has three out parameters. The return value is the
850     starting point of the arc. The \a curves array represents the list
851     of cubicTo elements up to a maximum of \a point_count. There are of course
852     3 points pr curve.
853 */
854 QPointF qt_curves_for_arc(const QRectF &rect, qreal startAngle, qreal sweepLength,
855                        QPointF *curves, int *point_count)
856 {
857     Q_ASSERT(point_count);
858     Q_ASSERT(curves);
859
860     *point_count = 0;
861     if (qt_is_nan(rect.x()) || qt_is_nan(rect.y()) || qt_is_nan(rect.width()) || qt_is_nan(rect.height())
862         || qt_is_nan(startAngle) || qt_is_nan(sweepLength)) {
863         qWarning("QPainterPath::arcTo: Adding arc where a parameter is NaN, results are undefined");
864         return QPointF();
865     }
866
867     if (rect.isNull()) {
868         return QPointF();
869     }
870
871     qreal x = rect.x();
872     qreal y = rect.y();
873
874     qreal w = rect.width();
875     qreal w2 = rect.width() / 2;
876     qreal w2k = w2 * QT_PATH_KAPPA;
877
878     qreal h = rect.height();
879     qreal h2 = rect.height() / 2;
880     qreal h2k = h2 * QT_PATH_KAPPA;
881
882     QPointF points[16] =
883     {
884         // start point
885         QPointF(x + w, y + h2),
886
887         // 0 -> 270 degrees
888         QPointF(x + w, y + h2 + h2k),
889         QPointF(x + w2 + w2k, y + h),
890         QPointF(x + w2, y + h),
891
892         // 270 -> 180 degrees
893         QPointF(x + w2 - w2k, y + h),
894         QPointF(x, y + h2 + h2k),
895         QPointF(x, y + h2),
896
897         // 180 -> 90 degrees
898         QPointF(x, y + h2 - h2k),
899         QPointF(x + w2 - w2k, y),
900         QPointF(x + w2, y),
901
902         // 90 -> 0 degrees
903         QPointF(x + w2 + w2k, y),
904         QPointF(x + w, y + h2 - h2k),
905         QPointF(x + w, y + h2)
906     };
907
908     if (sweepLength > 360) sweepLength = 360;
909     else if (sweepLength < -360) sweepLength = -360;
910
911     // Special case fast paths
912     if (startAngle == 0.0) {
913         if (sweepLength == 360.0) {
914             for (int i = 11; i >= 0; --i)
915                 curves[(*point_count)++] = points[i];
916             return points[12];
917         } else if (sweepLength == -360.0) {
918             for (int i = 1; i <= 12; ++i)
919                 curves[(*point_count)++] = points[i];
920             return points[0];
921         }
922     }
923
924     int startSegment = int(qFloor(startAngle / 90));
925     int endSegment = int(qFloor((startAngle + sweepLength) / 90));
926
927     qreal startT = (startAngle - startSegment * 90) / 90;
928     qreal endT = (startAngle + sweepLength - endSegment * 90) / 90;
929
930     int delta = sweepLength > 0 ? 1 : -1;
931     if (delta < 0) {
932         startT = 1 - startT;
933         endT = 1 - endT;
934     }
935
936     // avoid empty start segment
937     if (qFuzzyIsNull(startT - qreal(1))) {
938         startT = 0;
939         startSegment += delta;
940     }
941
942     // avoid empty end segment
943     if (qFuzzyIsNull(endT)) {
944         endT = 1;
945         endSegment -= delta;
946     }
947
948     startT = qt_t_for_arc_angle(startT * 90);
949     endT = qt_t_for_arc_angle(endT * 90);
950
951     const bool splitAtStart = !qFuzzyIsNull(startT);
952     const bool splitAtEnd = !qFuzzyIsNull(endT - qreal(1));
953
954     const int end = endSegment + delta;
955
956     // empty arc?
957     if (startSegment == end) {
958         const int quadrant = 3 - ((startSegment % 4) + 4) % 4;
959         const int j = 3 * quadrant;
960         return delta > 0 ? points[j + 3] : points[j];
961     }
962
963     QPointF startPoint, endPoint;
964     qt_find_ellipse_coords(rect, startAngle, sweepLength, &startPoint, &endPoint);
965
966     for (int i = startSegment; i != end; i += delta) {
967         const int quadrant = 3 - ((i % 4) + 4) % 4;
968         const int j = 3 * quadrant;
969
970         QBezier b;
971         if (delta > 0)
972             b = QBezier::fromPoints(points[j + 3], points[j + 2], points[j + 1], points[j]);
973         else
974             b = QBezier::fromPoints(points[j], points[j + 1], points[j + 2], points[j + 3]);
975
976         // empty arc?
977         if (startSegment == endSegment && qFuzzyCompare(startT, endT))
978             return startPoint;
979
980         if (i == startSegment) {
981             if (i == endSegment && splitAtEnd)
982                 b = b.bezierOnInterval(startT, endT);
983             else if (splitAtStart)
984                 b = b.bezierOnInterval(startT, 1);
985         } else if (i == endSegment && splitAtEnd) {
986             b = b.bezierOnInterval(0, endT);
987         }
988
989         // push control points
990         curves[(*point_count)++] = b.pt2();
991         curves[(*point_count)++] = b.pt3();
992         curves[(*point_count)++] = b.pt4();
993     }
994
995     Q_ASSERT(*point_count > 0);
996     curves[*(point_count)-1] = endPoint;
997
998     return startPoint;
999 }
1000
1001
1002 static inline void qdashstroker_moveTo(qfixed x, qfixed y, void *data) {
1003     ((QStroker *) data)->moveTo(x, y);
1004 }
1005
1006 static inline void qdashstroker_lineTo(qfixed x, qfixed y, void *data) {
1007     ((QStroker *) data)->lineTo(x, y);
1008 }
1009
1010 static inline void qdashstroker_cubicTo(qfixed, qfixed, qfixed, qfixed, qfixed, qfixed, void *) {
1011     Q_ASSERT(0);
1012 //     ((QStroker *) data)->cubicTo(c1x, c1y, c2x, c2y, ex, ey);
1013 }
1014
1015
1016 /*******************************************************************************
1017  * QDashStroker members
1018  */
1019 QDashStroker::QDashStroker(QStroker *stroker)
1020     : m_stroker(stroker), m_dashOffset(0), m_stroke_width(1), m_miter_limit(1)
1021 {
1022     if (m_stroker) {
1023         setMoveToHook(qdashstroker_moveTo);
1024         setLineToHook(qdashstroker_lineTo);
1025         setCubicToHook(qdashstroker_cubicTo);
1026     }
1027 }
1028
1029 QVector<qfixed> QDashStroker::patternForStyle(Qt::PenStyle style)
1030 {
1031     const qfixed space = 2;
1032     const qfixed dot = 1;
1033     const qfixed dash = 4;
1034
1035     QVector<qfixed> pattern;
1036
1037     switch (style) {
1038     case Qt::DashLine:
1039         pattern << dash << space;
1040         break;
1041     case Qt::DotLine:
1042         pattern << dot << space;
1043         break;
1044     case Qt::DashDotLine:
1045         pattern << dash << space << dot << space;
1046         break;
1047     case Qt::DashDotDotLine:
1048         pattern << dash << space << dot << space << dot << space;
1049         break;
1050     default:
1051         break;
1052     }
1053
1054     return pattern;
1055 }
1056
1057 static inline bool lineRectIntersectsRect(qfixed2d p1, qfixed2d p2, const qfixed2d &tl, const qfixed2d &br)
1058 {
1059     return ((p1.x > tl.x || p2.x > tl.x) && (p1.x < br.x || p2.x < br.x)
1060         && (p1.y > tl.y || p2.y > tl.y) && (p1.y < br.y || p2.y < br.y));
1061 }
1062
1063 // If the line intersects the rectangle, this function will return true.
1064 static bool lineIntersectsRect(qfixed2d p1, qfixed2d p2, const qfixed2d &tl, const qfixed2d &br)
1065 {
1066     if (!lineRectIntersectsRect(p1, p2, tl, br))
1067         return false;
1068     if (p1.x == p2.x || p1.y == p2.y)
1069         return true;
1070
1071     if (p1.y > p2.y)
1072         qSwap(p1, p2); // make p1 above p2
1073     qfixed2d u;
1074     qfixed2d v;
1075     qfixed2d w = {p2.x - p1.x, p2.y - p1.y};
1076     if (p1.x < p2.x) {
1077         // backslash
1078         u.x = tl.x - p1.x; u.y = br.y - p1.y;
1079         v.x = br.x - p1.x; v.y = tl.y - p1.y;
1080     } else {
1081         // slash
1082         u.x = tl.x - p1.x; u.y = tl.y - p1.y;
1083         v.x = br.x - p1.x; v.y = br.y - p1.y;
1084     }
1085 #if defined(QFIXED_IS_26_6) || defined(QFIXED_IS_16_16)
1086     qint64 val1 = qint64(u.x) * qint64(w.y) - qint64(u.y) * qint64(w.x);
1087     qint64 val2 = qint64(v.x) * qint64(w.y) - qint64(v.y) * qint64(w.x);
1088     return (val1 < 0 && val2 > 0) || (val1 > 0 && val2 < 0);
1089 #elif defined(QFIXED_IS_32_32)
1090     // Cannot do proper test because it may overflow.
1091     return true;
1092 #else
1093     qreal val1 = u.x * w.y - u.y * w.x;
1094     qreal val2 = v.x * w.y - v.y * w.x;
1095     return (val1 < 0 && val2 > 0) || (val1 > 0 && val2 < 0);
1096 #endif
1097 }
1098
1099 void QDashStroker::processCurrentSubpath()
1100 {
1101     int dashCount = qMin(m_dashPattern.size(), 32);
1102     qfixed dashes[32];
1103
1104     if (m_stroker) {
1105         m_customData = m_stroker;
1106         m_stroke_width = m_stroker->strokeWidth();
1107         m_miter_limit = m_stroker->miterLimit();
1108     }
1109
1110     qreal longestLength = 0;
1111     qreal sumLength = 0;
1112     for (int i=0; i<dashCount; ++i) {
1113         dashes[i] = qMax(m_dashPattern.at(i), qreal(0)) * m_stroke_width;
1114         sumLength += dashes[i];
1115         if (dashes[i] > longestLength)
1116             longestLength = dashes[i];
1117     }
1118
1119     if (qFuzzyIsNull(sumLength))
1120         return;
1121
1122     qreal invSumLength = qreal(1) / sumLength;
1123
1124     Q_ASSERT(dashCount > 0);
1125
1126     dashCount = dashCount & -2; // Round down to even number
1127
1128     int idash = 0; // Index to current dash
1129     qreal pos = 0; // The position on the curve, 0 <= pos <= path.length
1130     qreal elen = 0; // element length
1131     qreal doffset = m_dashOffset * m_stroke_width;
1132
1133     // make sure doffset is in range [0..sumLength)
1134     doffset -= qFloor(doffset * invSumLength) * sumLength;
1135
1136     while (doffset >= dashes[idash]) {
1137         doffset -= dashes[idash];
1138         if (++idash >= dashCount)
1139             idash = 0;
1140     }
1141
1142     qreal estart = 0; // The elements starting position
1143     qreal estop = 0; // The element stop position
1144
1145     QLineF cline;
1146
1147     QPainterPath dashPath;
1148
1149     QSubpathFlatIterator it(&m_elements, m_dashThreshold);
1150     qfixed2d prev = it.next();
1151
1152     bool clipping = !m_clip_rect.isEmpty();
1153     qfixed2d move_to_pos = prev;
1154     qfixed2d line_to_pos;
1155
1156     // Pad to avoid clipping the borders of thick pens.
1157     qfixed padding = qt_real_to_fixed(qMax(m_stroke_width, m_miter_limit) * longestLength);
1158     qfixed2d clip_tl = { qt_real_to_fixed(m_clip_rect.left()) - padding,
1159                          qt_real_to_fixed(m_clip_rect.top()) - padding };
1160     qfixed2d clip_br = { qt_real_to_fixed(m_clip_rect.right()) + padding ,
1161                          qt_real_to_fixed(m_clip_rect.bottom()) + padding };
1162
1163     bool hasMoveTo = false;
1164     while (it.hasNext()) {
1165         QStrokerOps::Element e = it.next();
1166
1167         Q_ASSERT(e.isLineTo());
1168         cline = QLineF(qt_fixed_to_real(prev.x),
1169                        qt_fixed_to_real(prev.y),
1170                        qt_fixed_to_real(e.x),
1171                        qt_fixed_to_real(e.y));
1172         elen = cline.length();
1173
1174         estop = estart + elen;
1175
1176         bool done = pos >= estop;
1177
1178         if (clipping) {
1179             // Check if the entire line can be clipped away.
1180             if (!lineIntersectsRect(prev, e, clip_tl, clip_br)) {
1181                 // Cut away full dash sequences.
1182                 elen -= qFloor(elen * invSumLength) * sumLength;
1183                 // Update dash offset.
1184                 while (!done) {
1185                     qreal dpos = pos + dashes[idash] - doffset - estart;
1186
1187                     Q_ASSERT(dpos >= 0);
1188
1189                     if (dpos > elen) { // dash extends this line
1190                         doffset = dashes[idash] - (dpos - elen); // subtract the part already used
1191                         pos = estop; // move pos to next path element
1192                         done = true;
1193                     } else { // Dash is on this line
1194                         pos = dpos + estart;
1195                         done = pos >= estop;
1196                         if (++idash >= dashCount)
1197                             idash = 0;
1198                         doffset = 0; // full segment so no offset on next.
1199                     }
1200                 }
1201                 hasMoveTo = false;
1202                 move_to_pos = e;
1203             }
1204         }
1205
1206         // Dash away...
1207         while (!done) {
1208             QPointF p2;
1209
1210             bool has_offset = doffset > 0;
1211             bool evenDash = (idash & 1) == 0;
1212             qreal dpos = pos + dashes[idash] - doffset - estart;
1213
1214             Q_ASSERT(dpos >= 0);
1215
1216             if (dpos > elen) { // dash extends this line
1217                 doffset = dashes[idash] - (dpos - elen); // subtract the part already used
1218                 pos = estop; // move pos to next path element
1219                 done = true;
1220                 p2 = cline.p2();
1221             } else { // Dash is on this line
1222                 p2 = cline.pointAt(dpos/elen);
1223                 pos = dpos + estart;
1224                 done = pos >= estop;
1225                 if (++idash >= dashCount)
1226                     idash = 0;
1227                 doffset = 0; // full segment so no offset on next.
1228             }
1229
1230             if (evenDash) {
1231                 line_to_pos.x = qt_real_to_fixed(p2.x());
1232                 line_to_pos.y = qt_real_to_fixed(p2.y());
1233
1234                 if (!clipping
1235                     || lineRectIntersectsRect(move_to_pos, line_to_pos, clip_tl, clip_br))
1236                 {
1237                     // If we have an offset, we're continuing a dash
1238                     // from a previous element and should only
1239                     // continue the current dash, without starting a
1240                     // new subpath.
1241                     if (!has_offset || !hasMoveTo) {
1242                         emitMoveTo(move_to_pos.x, move_to_pos.y);
1243                         hasMoveTo = true;
1244                     }
1245
1246                     emitLineTo(line_to_pos.x, line_to_pos.y);
1247                 } else {
1248                     hasMoveTo = false;
1249                 }
1250                 move_to_pos = line_to_pos;
1251             } else {
1252                 move_to_pos.x = qt_real_to_fixed(p2.x());
1253                 move_to_pos.y = qt_real_to_fixed(p2.y());
1254             }
1255         }
1256
1257         // Shuffle to the next cycle...
1258         estart = estop;
1259         prev = e;
1260     }
1261
1262 }
1263
1264 QT_END_NAMESPACE