1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
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:
QSubpathForwardIterator(const QDataBuffer<QStrokerOps::Element> * path)56     QSubpathForwardIterator(const QDataBuffer<QStrokerOps::Element> *path)
57         : m_path(path), m_pos(0) { }
position() const58     inline int position() const { return m_pos; }
hasNext() const59     inline bool hasNext() const { return m_pos < m_path->size(); }
next()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:
QSubpathBackwardIterator(const QDataBuffer<QStrokerOps::Element> * path)70     QSubpathBackwardIterator(const QDataBuffer<QStrokerOps::Element> *path)
71         : m_path(path), m_pos(path->size() - 1) { }
72 
position() const73     inline int position() const { return m_pos; }
74 
hasNext() const75     inline bool hasNext() const { return m_pos >= 0; }
76 
next()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:
QSubpathFlatIterator(const QDataBuffer<QStrokerOps::Element> * path,qreal threshold)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 
hasNext() const126     inline bool hasNext() const { return m_curve_index >= 0 || m_pos < m_path->size(); }
127 
next()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  */
adapted_angle_on_x(const QLineF & line)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 
QStrokerOps()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 
~QStrokerOps()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 */
begin(void * customData)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 */
end()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 */
strokePath(const QPainterPath & path,void * customData,const QTransform & matrix)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 
strokePolygon(const QPointF * points,int pointCount,bool implicit_close,void * data,const QTransform & matrix)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 */
strokeEllipse(const QRectF & rect,void * data,const QTransform & matrix)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 
QStroker()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 
~QStroker()381 QStroker::~QStroker()
382 {
383 }
384 
capForJoinMode(LineJoinMode mode)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 
joinModeForCap(Qt::PenCapStyle style)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 
joinForJoinMode(LineJoinMode mode)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 
joinModeForJoin(Qt::PenJoinStyle joinStyle)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 */
processCurrentSubpath()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 */
joinPoints(qfixed focal_x,qfixed focal_y,const QLineF & nextLine,LineJoinMode join)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 */
qt_stroke_side(Iterator * it,QStroker * stroker,bool capFirst,QLineF * startTangent)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 
qt_t_for_arc_angle(qreal angle)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 */
qt_curves_for_arc(const QRectF & rect,qreal startAngle,qreal sweepLength,QPointF * curves,int * point_count)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 
qdashstroker_moveTo(qfixed x,qfixed y,void * data)1004 static inline void qdashstroker_moveTo(qfixed x, qfixed y, void *data) {
1005     ((QStroker *) data)->moveTo(x, y);
1006 }
1007 
qdashstroker_lineTo(qfixed x,qfixed y,void * data)1008 static inline void qdashstroker_lineTo(qfixed x, qfixed y, void *data) {
1009     ((QStroker *) data)->lineTo(x, y);
1010 }
1011 
qdashstroker_cubicTo(qfixed,qfixed,qfixed,qfixed,qfixed,qfixed,void *)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  */
QDashStroker(QStroker * stroker)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 
patternForStyle(Qt::PenStyle style)1031 QVector<qfixed> QDashStroker::patternForStyle(Qt::PenStyle style)
1032 {
1033     const qfixed space = 2;
1034     const qfixed dot = 1;
1035     const qfixed dash = 4;
1036 
1037     QVector<qfixed> pattern;
1038 
1039     switch (style) {
1040     case Qt::DashLine:
1041         pattern << dash << space;
1042         break;
1043     case Qt::DotLine:
1044         pattern << dot << space;
1045         break;
1046     case Qt::DashDotLine:
1047         pattern << dash << space << dot << space;
1048         break;
1049     case Qt::DashDotDotLine:
1050         pattern << dash << space << dot << space << dot << space;
1051         break;
1052     default:
1053         break;
1054     }
1055 
1056     return pattern;
1057 }
1058 
lineRectIntersectsRect(qfixed2d p1,qfixed2d p2,const qfixed2d & tl,const qfixed2d & br)1059 static inline bool lineRectIntersectsRect(qfixed2d p1, qfixed2d p2, const qfixed2d &tl, const qfixed2d &br)
1060 {
1061     return ((p1.x > tl.x || p2.x > tl.x) && (p1.x < br.x || p2.x < br.x)
1062         && (p1.y > tl.y || p2.y > tl.y) && (p1.y < br.y || p2.y < br.y));
1063 }
1064 
1065 // If the line intersects the rectangle, this function will return true.
lineIntersectsRect(qfixed2d p1,qfixed2d p2,const qfixed2d & tl,const qfixed2d & br)1066 static bool lineIntersectsRect(qfixed2d p1, qfixed2d p2, const qfixed2d &tl, const qfixed2d &br)
1067 {
1068     if (!lineRectIntersectsRect(p1, p2, tl, br))
1069         return false;
1070     if (p1.x == p2.x || p1.y == p2.y)
1071         return true;
1072 
1073     if (p1.y > p2.y)
1074         qSwap(p1, p2); // make p1 above p2
1075     qfixed2d u;
1076     qfixed2d v;
1077     qfixed2d w = {p2.x - p1.x, p2.y - p1.y};
1078     if (p1.x < p2.x) {
1079         // backslash
1080         u.x = tl.x - p1.x; u.y = br.y - p1.y;
1081         v.x = br.x - p1.x; v.y = tl.y - p1.y;
1082     } else {
1083         // slash
1084         u.x = tl.x - p1.x; u.y = tl.y - p1.y;
1085         v.x = br.x - p1.x; v.y = br.y - p1.y;
1086     }
1087 #if defined(QFIXED_IS_26_6) || defined(QFIXED_IS_16_16)
1088     qint64 val1 = qint64(u.x) * qint64(w.y) - qint64(u.y) * qint64(w.x);
1089     qint64 val2 = qint64(v.x) * qint64(w.y) - qint64(v.y) * qint64(w.x);
1090     return (val1 < 0 && val2 > 0) || (val1 > 0 && val2 < 0);
1091 #elif defined(QFIXED_IS_32_32)
1092     // Cannot do proper test because it may overflow.
1093     return true;
1094 #else
1095     qreal val1 = u.x * w.y - u.y * w.x;
1096     qreal val2 = v.x * w.y - v.y * w.x;
1097     return (val1 < 0 && val2 > 0) || (val1 > 0 && val2 < 0);
1098 #endif
1099 }
1100 
processCurrentSubpath()1101 void QDashStroker::processCurrentSubpath()
1102 {
1103     int dashCount = qMin(m_dashPattern.size(), 32);
1104     qfixed dashes[32];
1105 
1106     if (m_stroker) {
1107         m_customData = m_stroker;
1108         m_stroke_width = m_stroker->strokeWidth();
1109         m_miter_limit = m_stroker->miterLimit();
1110     }
1111 
1112     qreal longestLength = 0;
1113     qreal sumLength = 0;
1114     for (int i=0; i<dashCount; ++i) {
1115         dashes[i] = qMax(m_dashPattern.at(i), qreal(0)) * m_stroke_width;
1116         sumLength += dashes[i];
1117         if (dashes[i] > longestLength)
1118             longestLength = dashes[i];
1119     }
1120 
1121     if (qFuzzyIsNull(sumLength))
1122         return;
1123 
1124     qreal invSumLength = qreal(1) / sumLength;
1125 
1126     Q_ASSERT(dashCount > 0);
1127 
1128     dashCount = dashCount & -2; // Round down to even number
1129 
1130     int idash = 0; // Index to current dash
1131     qreal pos = 0; // The position on the curve, 0 <= pos <= path.length
1132     qreal elen = 0; // element length
1133     qreal doffset = m_dashOffset * m_stroke_width;
1134 
1135     // make sure doffset is in range [0..sumLength)
1136     doffset -= qFloor(doffset * invSumLength) * sumLength;
1137 
1138     while (doffset >= dashes[idash]) {
1139         doffset -= dashes[idash];
1140         if (++idash >= dashCount)
1141             idash = 0;
1142     }
1143 
1144     qreal estart = 0; // The elements starting position
1145     qreal estop = 0; // The element stop position
1146 
1147     QLineF cline;
1148 
1149     QPainterPath dashPath;
1150 
1151     QSubpathFlatIterator it(&m_elements, m_dashThreshold);
1152     qfixed2d prev = it.next();
1153 
1154     bool clipping = !m_clip_rect.isEmpty();
1155     qfixed2d move_to_pos = prev;
1156     qfixed2d line_to_pos;
1157 
1158     // Pad to avoid clipping the borders of thick pens.
1159     qfixed padding = qt_real_to_fixed(qMax(m_stroke_width, m_miter_limit) * longestLength);
1160     qfixed2d clip_tl = { qt_real_to_fixed(m_clip_rect.left()) - padding,
1161                          qt_real_to_fixed(m_clip_rect.top()) - padding };
1162     qfixed2d clip_br = { qt_real_to_fixed(m_clip_rect.right()) + padding ,
1163                          qt_real_to_fixed(m_clip_rect.bottom()) + padding };
1164 
1165     bool hasMoveTo = false;
1166     while (it.hasNext()) {
1167         QStrokerOps::Element e = it.next();
1168 
1169         Q_ASSERT(e.isLineTo());
1170         cline = QLineF(qt_fixed_to_real(prev.x),
1171                        qt_fixed_to_real(prev.y),
1172                        qt_fixed_to_real(e.x),
1173                        qt_fixed_to_real(e.y));
1174         elen = cline.length();
1175 
1176         estop = estart + elen;
1177 
1178         bool done = pos >= estop;
1179 
1180         if (clipping) {
1181             // Check if the entire line can be clipped away.
1182             if (!lineIntersectsRect(prev, e, clip_tl, clip_br)) {
1183                 // Cut away full dash sequences.
1184                 elen -= qFloor(elen * invSumLength) * sumLength;
1185                 // Update dash offset.
1186                 while (!done) {
1187                     qreal dpos = pos + dashes[idash] - doffset - estart;
1188 
1189                     Q_ASSERT(dpos >= 0);
1190 
1191                     if (dpos > elen) { // dash extends this line
1192                         doffset = dashes[idash] - (dpos - elen); // subtract the part already used
1193                         pos = estop; // move pos to next path element
1194                         done = true;
1195                     } else { // Dash is on this line
1196                         pos = dpos + estart;
1197                         done = pos >= estop;
1198                         if (++idash >= dashCount)
1199                             idash = 0;
1200                         doffset = 0; // full segment so no offset on next.
1201                     }
1202                 }
1203                 hasMoveTo = false;
1204                 move_to_pos = e;
1205             }
1206         }
1207 
1208         // Dash away...
1209         while (!done) {
1210             QPointF p2;
1211 
1212             bool has_offset = doffset > 0;
1213             bool evenDash = (idash & 1) == 0;
1214             qreal dpos = pos + dashes[idash] - doffset - estart;
1215 
1216             Q_ASSERT(dpos >= 0);
1217 
1218             if (dpos > elen) { // dash extends this line
1219                 doffset = dashes[idash] - (dpos - elen); // subtract the part already used
1220                 pos = estop; // move pos to next path element
1221                 done = true;
1222                 p2 = cline.p2();
1223             } else { // Dash is on this line
1224                 p2 = cline.pointAt(dpos/elen);
1225                 pos = dpos + estart;
1226                 done = pos >= estop;
1227                 if (++idash >= dashCount)
1228                     idash = 0;
1229                 doffset = 0; // full segment so no offset on next.
1230             }
1231 
1232             if (evenDash) {
1233                 line_to_pos.x = qt_real_to_fixed(p2.x());
1234                 line_to_pos.y = qt_real_to_fixed(p2.y());
1235 
1236                 if (!clipping
1237                     || lineRectIntersectsRect(move_to_pos, line_to_pos, clip_tl, clip_br))
1238                 {
1239                     // If we have an offset, we're continuing a dash
1240                     // from a previous element and should only
1241                     // continue the current dash, without starting a
1242                     // new subpath.
1243                     if (!has_offset || !hasMoveTo) {
1244                         emitMoveTo(move_to_pos.x, move_to_pos.y);
1245                         hasMoveTo = true;
1246                     }
1247 
1248                     emitLineTo(line_to_pos.x, line_to_pos.y);
1249                 } else {
1250                     hasMoveTo = false;
1251                 }
1252                 move_to_pos = line_to_pos;
1253             } else {
1254                 move_to_pos.x = qt_real_to_fixed(p2.x());
1255                 move_to_pos.y = qt_real_to_fixed(p2.y());
1256             }
1257         }
1258 
1259         // Shuffle to the next cycle...
1260         estart = estop;
1261         prev = e;
1262     }
1263 
1264 }
1265 
1266 QT_END_NAMESPACE
1267