Make QRegion not need to be friends with QVector
[profile/ivi/qtbase.git] / src / gui / painting / qstroker.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
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.
16 **
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.
20 **
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.
28 **
29 ** Other Usage
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.
32 **
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         // don't join empty subpaths
760         if (!first)
761             stroker->joinPoints(prev.x, prev.y, *startTangent, stroker->joinStyleMode());
762         return true;
763     } else {
764 #ifdef QPP_STROKE_DEBUG
765         qDebug("\n ---> (side) open subpath");
766 #endif
767         return false;
768     }
769 }
770
771 /*!
772     \internal
773
774     For a given angle in the range [0 .. 90], finds the corresponding parameter t
775     of the prototype cubic bezier arc segment
776     b = fromPoints(QPointF(1, 0), QPointF(1, KAPPA), QPointF(KAPPA, 1), QPointF(0, 1));
777
778     From the bezier equation:
779     b.pointAt(t).x() = (1-t)^3 + t*(1-t)^2 + t^2*(1-t)*KAPPA
780     b.pointAt(t).y() = t*(1-t)^2 * KAPPA + t^2*(1-t) + t^3
781
782     Third degree coefficients:
783     b.pointAt(t).x() = at^3 + bt^2 + ct + d
784     where a = 2-3*KAPPA, b = 3*(KAPPA-1), c = 0, d = 1
785
786     b.pointAt(t).y() = at^3 + bt^2 + ct + d
787     where a = 3*KAPPA-2, b = 6*KAPPA+3, c = 3*KAPPA, d = 0
788
789     Newton's method to find the zero of a function:
790     given a function f(x) and initial guess x_0
791     x_1 = f(x_0) / f'(x_0)
792     x_2 = f(x_1) / f'(x_1)
793     etc...
794 */
795
796 qreal qt_t_for_arc_angle(qreal angle)
797 {
798     if (qFuzzyIsNull(angle))
799         return 0;
800
801     if (qFuzzyCompare(angle, qreal(90)))
802         return 1;
803
804     qreal radians = Q_PI * angle / 180;
805     qreal cosAngle = qCos(radians);
806     qreal sinAngle = qSin(radians);
807
808     // initial guess
809     qreal tc = angle / 90;
810     // do some iterations of newton's method to approximate cosAngle
811     // finds the zero of the function b.pointAt(tc).x() - cosAngle
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     tc -= ((((2-3*QT_PATH_KAPPA) * tc + 3*(QT_PATH_KAPPA-1)) * tc) * tc + 1 - cosAngle) // value
815          / (((6-9*QT_PATH_KAPPA) * tc + 6*(QT_PATH_KAPPA-1)) * tc); // derivative
816
817     // initial guess
818     qreal ts = tc;
819     // do some iterations of newton's method to approximate sinAngle
820     // finds the zero of the function b.pointAt(tc).y() - sinAngle
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     ts -= ((((3*QT_PATH_KAPPA-2) * ts -  6*QT_PATH_KAPPA + 3) * ts + 3*QT_PATH_KAPPA) * ts - sinAngle)
824          / (((9*QT_PATH_KAPPA-6) * ts + 12*QT_PATH_KAPPA - 6) * ts + 3*QT_PATH_KAPPA);
825
826     // use the average of the t that best approximates cosAngle
827     // and the t that best approximates sinAngle
828     qreal t = 0.5 * (tc + ts);
829
830 #if 0
831     printf("angle: %f, t: %f\n", angle, t);
832     qreal a, b, c, d;
833     bezierCoefficients(t, a, b, c, d);
834     printf("cosAngle: %.10f, value: %.10f\n", cosAngle, a + b + c * QT_PATH_KAPPA);
835     printf("sinAngle: %.10f, value: %.10f\n", sinAngle, b * QT_PATH_KAPPA + c + d);
836 #endif
837
838     return t;
839 }
840
841 Q_GUI_EXPORT void qt_find_ellipse_coords(const QRectF &r, qreal angle, qreal length,
842                             QPointF* startPoint, QPointF *endPoint);
843
844 /*!
845     \internal
846
847     Creates a number of curves for a given arc definition. The arc is
848     defined an arc along the ellipses that fits into \a rect starting
849     at \a startAngle and an arc length of \a sweepLength.
850
851     The function has three out parameters. The return value is the
852     starting point of the arc. The \a curves array represents the list
853     of cubicTo elements up to a maximum of \a point_count. There are of course
854     3 points pr curve.
855 */
856 QPointF qt_curves_for_arc(const QRectF &rect, qreal startAngle, qreal sweepLength,
857                        QPointF *curves, int *point_count)
858 {
859     Q_ASSERT(point_count);
860     Q_ASSERT(curves);
861
862     *point_count = 0;
863     if (qt_is_nan(rect.x()) || qt_is_nan(rect.y()) || qt_is_nan(rect.width()) || qt_is_nan(rect.height())
864         || qt_is_nan(startAngle) || qt_is_nan(sweepLength)) {
865         qWarning("QPainterPath::arcTo: Adding arc where a parameter is NaN, results are undefined");
866         return QPointF();
867     }
868
869     if (rect.isNull()) {
870         return QPointF();
871     }
872
873     qreal x = rect.x();
874     qreal y = rect.y();
875
876     qreal w = rect.width();
877     qreal w2 = rect.width() / 2;
878     qreal w2k = w2 * QT_PATH_KAPPA;
879
880     qreal h = rect.height();
881     qreal h2 = rect.height() / 2;
882     qreal h2k = h2 * QT_PATH_KAPPA;
883
884     QPointF points[16] =
885     {
886         // start point
887         QPointF(x + w, y + h2),
888
889         // 0 -> 270 degrees
890         QPointF(x + w, y + h2 + h2k),
891         QPointF(x + w2 + w2k, y + h),
892         QPointF(x + w2, y + h),
893
894         // 270 -> 180 degrees
895         QPointF(x + w2 - w2k, y + h),
896         QPointF(x, y + h2 + h2k),
897         QPointF(x, y + h2),
898
899         // 180 -> 90 degrees
900         QPointF(x, y + h2 - h2k),
901         QPointF(x + w2 - w2k, y),
902         QPointF(x + w2, y),
903
904         // 90 -> 0 degrees
905         QPointF(x + w2 + w2k, y),
906         QPointF(x + w, y + h2 - h2k),
907         QPointF(x + w, y + h2)
908     };
909
910     if (sweepLength > 360) sweepLength = 360;
911     else if (sweepLength < -360) sweepLength = -360;
912
913     // Special case fast paths
914     if (startAngle == 0.0) {
915         if (sweepLength == 360.0) {
916             for (int i = 11; i >= 0; --i)
917                 curves[(*point_count)++] = points[i];
918             return points[12];
919         } else if (sweepLength == -360.0) {
920             for (int i = 1; i <= 12; ++i)
921                 curves[(*point_count)++] = points[i];
922             return points[0];
923         }
924     }
925
926     int startSegment = int(qFloor(startAngle / 90));
927     int endSegment = int(qFloor((startAngle + sweepLength) / 90));
928
929     qreal startT = (startAngle - startSegment * 90) / 90;
930     qreal endT = (startAngle + sweepLength - endSegment * 90) / 90;
931
932     int delta = sweepLength > 0 ? 1 : -1;
933     if (delta < 0) {
934         startT = 1 - startT;
935         endT = 1 - endT;
936     }
937
938     // avoid empty start segment
939     if (qFuzzyIsNull(startT - qreal(1))) {
940         startT = 0;
941         startSegment += delta;
942     }
943
944     // avoid empty end segment
945     if (qFuzzyIsNull(endT)) {
946         endT = 1;
947         endSegment -= delta;
948     }
949
950     startT = qt_t_for_arc_angle(startT * 90);
951     endT = qt_t_for_arc_angle(endT * 90);
952
953     const bool splitAtStart = !qFuzzyIsNull(startT);
954     const bool splitAtEnd = !qFuzzyIsNull(endT - qreal(1));
955
956     const int end = endSegment + delta;
957
958     // empty arc?
959     if (startSegment == end) {
960         const int quadrant = 3 - ((startSegment % 4) + 4) % 4;
961         const int j = 3 * quadrant;
962         return delta > 0 ? points[j + 3] : points[j];
963     }
964
965     QPointF startPoint, endPoint;
966     qt_find_ellipse_coords(rect, startAngle, sweepLength, &startPoint, &endPoint);
967
968     for (int i = startSegment; i != end; i += delta) {
969         const int quadrant = 3 - ((i % 4) + 4) % 4;
970         const int j = 3 * quadrant;
971
972         QBezier b;
973         if (delta > 0)
974             b = QBezier::fromPoints(points[j + 3], points[j + 2], points[j + 1], points[j]);
975         else
976             b = QBezier::fromPoints(points[j], points[j + 1], points[j + 2], points[j + 3]);
977
978         // empty arc?
979         if (startSegment == endSegment && qFuzzyCompare(startT, endT))
980             return startPoint;
981
982         if (i == startSegment) {
983             if (i == endSegment && splitAtEnd)
984                 b = b.bezierOnInterval(startT, endT);
985             else if (splitAtStart)
986                 b = b.bezierOnInterval(startT, 1);
987         } else if (i == endSegment && splitAtEnd) {
988             b = b.bezierOnInterval(0, endT);
989         }
990
991         // push control points
992         curves[(*point_count)++] = b.pt2();
993         curves[(*point_count)++] = b.pt3();
994         curves[(*point_count)++] = b.pt4();
995     }
996
997     Q_ASSERT(*point_count > 0);
998     curves[*(point_count)-1] = endPoint;
999
1000     return startPoint;
1001 }
1002
1003
1004 static inline void qdashstroker_moveTo(qfixed x, qfixed y, void *data) {
1005     ((QStroker *) data)->moveTo(x, y);
1006 }
1007
1008 static inline void qdashstroker_lineTo(qfixed x, qfixed y, void *data) {
1009     ((QStroker *) data)->lineTo(x, y);
1010 }
1011
1012 static inline void qdashstroker_cubicTo(qfixed, qfixed, qfixed, qfixed, qfixed, qfixed, void *) {
1013     Q_ASSERT(0);
1014 //     ((QStroker *) data)->cubicTo(c1x, c1y, c2x, c2y, ex, ey);
1015 }
1016
1017
1018 /*******************************************************************************
1019  * QDashStroker members
1020  */
1021 QDashStroker::QDashStroker(QStroker *stroker)
1022     : m_stroker(stroker), m_dashOffset(0), m_stroke_width(1), m_miter_limit(1)
1023 {
1024     if (m_stroker) {
1025         setMoveToHook(qdashstroker_moveTo);
1026         setLineToHook(qdashstroker_lineTo);
1027         setCubicToHook(qdashstroker_cubicTo);
1028     }
1029 }
1030
1031 QDashStroker::~QDashStroker()
1032 {
1033 }
1034
1035 QVector<qfixed> QDashStroker::patternForStyle(Qt::PenStyle style)
1036 {
1037     const qfixed space = 2;
1038     const qfixed dot = 1;
1039     const qfixed dash = 4;
1040
1041     QVector<qfixed> pattern;
1042
1043     switch (style) {
1044     case Qt::DashLine:
1045         pattern << dash << space;
1046         break;
1047     case Qt::DotLine:
1048         pattern << dot << space;
1049         break;
1050     case Qt::DashDotLine:
1051         pattern << dash << space << dot << space;
1052         break;
1053     case Qt::DashDotDotLine:
1054         pattern << dash << space << dot << space << dot << space;
1055         break;
1056     default:
1057         break;
1058     }
1059
1060     return pattern;
1061 }
1062
1063 static inline bool lineRectIntersectsRect(qfixed2d p1, qfixed2d p2, const qfixed2d &tl, const qfixed2d &br)
1064 {
1065     return ((p1.x > tl.x || p2.x > tl.x) && (p1.x < br.x || p2.x < br.x)
1066         && (p1.y > tl.y || p2.y > tl.y) && (p1.y < br.y || p2.y < br.y));
1067 }
1068
1069 // If the line intersects the rectangle, this function will return true.
1070 static bool lineIntersectsRect(qfixed2d p1, qfixed2d p2, const qfixed2d &tl, const qfixed2d &br)
1071 {
1072     if (!lineRectIntersectsRect(p1, p2, tl, br))
1073         return false;
1074     if (p1.x == p2.x || p1.y == p2.y)
1075         return true;
1076
1077     if (p1.y > p2.y)
1078         qSwap(p1, p2); // make p1 above p2
1079     qfixed2d u;
1080     qfixed2d v;
1081     qfixed2d w = {p2.x - p1.x, p2.y - p1.y};
1082     if (p1.x < p2.x) {
1083         // backslash
1084         u.x = tl.x - p1.x; u.y = br.y - p1.y;
1085         v.x = br.x - p1.x; v.y = tl.y - p1.y;
1086     } else {
1087         // slash
1088         u.x = tl.x - p1.x; u.y = tl.y - p1.y;
1089         v.x = br.x - p1.x; v.y = br.y - p1.y;
1090     }
1091 #if defined(QFIXED_IS_26_6) || defined(QFIXED_IS_16_16)
1092     qint64 val1 = qint64(u.x) * qint64(w.y) - qint64(u.y) * qint64(w.x);
1093     qint64 val2 = qint64(v.x) * qint64(w.y) - qint64(v.y) * qint64(w.x);
1094     return (val1 < 0 && val2 > 0) || (val1 > 0 && val2 < 0);
1095 #elif defined(QFIXED_IS_32_32)
1096     // Cannot do proper test because it may overflow.
1097     return true;
1098 #else
1099     qreal val1 = u.x * w.y - u.y * w.x;
1100     qreal val2 = v.x * w.y - v.y * w.x;
1101     return (val1 < 0 && val2 > 0) || (val1 > 0 && val2 < 0);
1102 #endif
1103 }
1104
1105 void QDashStroker::processCurrentSubpath()
1106 {
1107     int dashCount = qMin(m_dashPattern.size(), 32);
1108     qfixed dashes[32];
1109
1110     if (m_stroker) {
1111         m_customData = m_stroker;
1112         m_stroke_width = m_stroker->strokeWidth();
1113         m_miter_limit = m_stroker->miterLimit();
1114     }
1115
1116     qreal longestLength = 0;
1117     qreal sumLength = 0;
1118     for (int i=0; i<dashCount; ++i) {
1119         dashes[i] = qMax(m_dashPattern.at(i), qreal(0)) * m_stroke_width;
1120         sumLength += dashes[i];
1121         if (dashes[i] > longestLength)
1122             longestLength = dashes[i];
1123     }
1124
1125     if (qFuzzyIsNull(sumLength))
1126         return;
1127
1128     qreal invSumLength = qreal(1) / sumLength;
1129
1130     Q_ASSERT(dashCount > 0);
1131
1132     dashCount = dashCount & -2; // Round down to even number
1133
1134     int idash = 0; // Index to current dash
1135     qreal pos = 0; // The position on the curve, 0 <= pos <= path.length
1136     qreal elen = 0; // element length
1137     qreal doffset = m_dashOffset * m_stroke_width;
1138
1139     // make sure doffset is in range [0..sumLength)
1140     doffset -= qFloor(doffset * invSumLength) * sumLength;
1141
1142     while (doffset >= dashes[idash]) {
1143         doffset -= dashes[idash];
1144         if (++idash >= dashCount)
1145             idash = 0;
1146     }
1147
1148     qreal estart = 0; // The elements starting position
1149     qreal estop = 0; // The element stop position
1150
1151     QLineF cline;
1152
1153     QPainterPath dashPath;
1154
1155     QSubpathFlatIterator it(&m_elements, m_dashThreshold);
1156     qfixed2d prev = it.next();
1157
1158     bool clipping = !m_clip_rect.isEmpty();
1159     qfixed2d move_to_pos = prev;
1160     qfixed2d line_to_pos;
1161
1162     // Pad to avoid clipping the borders of thick pens.
1163     qfixed padding = qt_real_to_fixed(qMax(m_stroke_width, m_miter_limit) * longestLength);
1164     qfixed2d clip_tl = { qt_real_to_fixed(m_clip_rect.left()) - padding,
1165                          qt_real_to_fixed(m_clip_rect.top()) - padding };
1166     qfixed2d clip_br = { qt_real_to_fixed(m_clip_rect.right()) + padding ,
1167                          qt_real_to_fixed(m_clip_rect.bottom()) + padding };
1168
1169     bool hasMoveTo = false;
1170     while (it.hasNext()) {
1171         QStrokerOps::Element e = it.next();
1172
1173         Q_ASSERT(e.isLineTo());
1174         cline = QLineF(qt_fixed_to_real(prev.x),
1175                        qt_fixed_to_real(prev.y),
1176                        qt_fixed_to_real(e.x),
1177                        qt_fixed_to_real(e.y));
1178         elen = cline.length();
1179
1180         estop = estart + elen;
1181
1182         bool done = pos >= estop;
1183
1184         if (clipping) {
1185             // Check if the entire line can be clipped away.
1186             if (!lineIntersectsRect(prev, e, clip_tl, clip_br)) {
1187                 // Cut away full dash sequences.
1188                 elen -= qFloor(elen * invSumLength) * sumLength;
1189                 // Update dash offset.
1190                 while (!done) {
1191                     qreal dpos = pos + dashes[idash] - doffset - estart;
1192
1193                     Q_ASSERT(dpos >= 0);
1194
1195                     if (dpos > elen) { // dash extends this line
1196                         doffset = dashes[idash] - (dpos - elen); // subtract the part already used
1197                         pos = estop; // move pos to next path element
1198                         done = true;
1199                     } else { // Dash is on this line
1200                         pos = dpos + estart;
1201                         done = pos >= estop;
1202                         if (++idash >= dashCount)
1203                             idash = 0;
1204                         doffset = 0; // full segment so no offset on next.
1205                     }
1206                 }
1207                 hasMoveTo = false;
1208                 move_to_pos = e;
1209             }
1210         }
1211
1212         // Dash away...
1213         while (!done) {
1214             QPointF p2;
1215
1216             bool has_offset = doffset > 0;
1217             bool evenDash = (idash & 1) == 0;
1218             qreal dpos = pos + dashes[idash] - doffset - estart;
1219
1220             Q_ASSERT(dpos >= 0);
1221
1222             if (dpos > elen) { // dash extends this line
1223                 doffset = dashes[idash] - (dpos - elen); // subtract the part already used
1224                 pos = estop; // move pos to next path element
1225                 done = true;
1226                 p2 = cline.p2();
1227             } else { // Dash is on this line
1228                 p2 = cline.pointAt(dpos/elen);
1229                 pos = dpos + estart;
1230                 done = pos >= estop;
1231                 if (++idash >= dashCount)
1232                     idash = 0;
1233                 doffset = 0; // full segment so no offset on next.
1234             }
1235
1236             if (evenDash) {
1237                 line_to_pos.x = qt_real_to_fixed(p2.x());
1238                 line_to_pos.y = qt_real_to_fixed(p2.y());
1239
1240                 if (!clipping
1241                     || lineRectIntersectsRect(move_to_pos, line_to_pos, clip_tl, clip_br))
1242                 {
1243                     // If we have an offset, we're continuing a dash
1244                     // from a previous element and should only
1245                     // continue the current dash, without starting a
1246                     // new subpath.
1247                     if (!has_offset || !hasMoveTo) {
1248                         emitMoveTo(move_to_pos.x, move_to_pos.y);
1249                         hasMoveTo = true;
1250                     }
1251
1252                     emitLineTo(line_to_pos.x, line_to_pos.y);
1253                 } else {
1254                     hasMoveTo = false;
1255                 }
1256                 move_to_pos = line_to_pos;
1257             } else {
1258                 move_to_pos.x = qt_real_to_fixed(p2.x());
1259                 move_to_pos.y = qt_real_to_fixed(p2.y());
1260             }
1261         }
1262
1263         // Shuffle to the next cycle...
1264         estart = estop;
1265         prev = e;
1266     }
1267
1268 }
1269
1270 QT_END_NAMESPACE