Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qpainterpath.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qpainterpath.h"
6
7#include <qbitmap.h>
8#include <qdebug.h>
9#include <qiodevice.h>
10#include <qlist.h>
11#include <qpen.h>
12#include <qpolygon.h>
13#include <qtextlayout.h>
14#include <qvarlengtharray.h>
15#include <qmath.h>
16
17#include <private/qbezier_p.h>
18#include <private/qfontengine_p.h>
19#include <private/qnumeric_p.h>
20#include <private/qobject_p.h>
21#include <private/qpathclipper_p.h>
22#include <private/qstroker_p.h>
23#include <private/qtextengine_p.h>
24
25#include <cmath>
26
27#include <limits.h>
28
29#if 0
30#include <performance.h>
31#else
32#define PM_INIT
33#define PM_MEASURE(x)
34#define PM_DISPLAY
35#endif
36
38
39static inline bool isValidCoord(qreal c)
40{
41 if (sizeof(qreal) >= sizeof(double))
42 return qIsFinite(c) && fabs(c) < 1e128;
43 else
44 return qIsFinite(c) && fabsf(float(c)) < 1e16f;
45}
46
47static bool hasValidCoords(QPointF p)
48{
49 return isValidCoord(p.x()) && isValidCoord(p.y());
50}
51
52static bool hasValidCoords(QRectF r)
53{
54 return isValidCoord(r.x()) && isValidCoord(r.y()) && isValidCoord(r.width()) && isValidCoord(r.height());
55}
56
57// This value is used to determine the length of control point vectors
58// when approximating arc segments as curves. The factor is multiplied
59// with the radius of the circle.
60
61// #define QPP_DEBUG
62// #define QPP_STROKE_DEBUG
63//#define QPP_FILLPOLYGONS_DEBUG
64
65QPainterPath qt_stroke_dash(const QPainterPath &path, qreal *dashes, int dashCount);
66
67void qt_find_ellipse_coords(const QRectF &r, qreal angle, qreal length,
68 QPointF* startPoint, QPointF *endPoint)
69{
70 if (r.isNull()) {
71 if (startPoint)
72 *startPoint = QPointF();
73 if (endPoint)
74 *endPoint = QPointF();
75 return;
76 }
77
78 qreal w2 = r.width() / 2;
79 qreal h2 = r.height() / 2;
80
81 qreal angles[2] = { angle, angle + length };
82 QPointF *points[2] = { startPoint, endPoint };
83
84 for (int i = 0; i < 2; ++i) {
85 if (!points[i])
86 continue;
87
88 qreal theta = angles[i] - 360 * qFloor(angles[i] / 360);
89 qreal t = theta / 90;
90 // truncate
91 int quadrant = int(t);
92 t -= quadrant;
93
94 t = qt_t_for_arc_angle(90 * t);
95
96 // swap x and y?
97 if (quadrant & 1)
98 t = 1 - t;
99
100 qreal a, b, c, d;
101 QBezier::coefficients(t, a, b, c, d);
102 QPointF p(a + b + c*QT_PATH_KAPPA, d + c + b*QT_PATH_KAPPA);
103
104 // left quadrants
105 if (quadrant == 1 || quadrant == 2)
106 p.rx() = -p.x();
107
108 // top quadrants
109 if (quadrant == 0 || quadrant == 1)
110 p.ry() = -p.y();
111
112 *points[i] = r.center() + QPointF(w2 * p.x(), h2 * p.y());
113 }
114}
115
116#ifdef QPP_DEBUG
117static void qt_debug_path(const QPainterPath &path)
118{
119 const char *names[] = {
120 "MoveTo ",
121 "LineTo ",
122 "CurveTo ",
123 "CurveToData"
124 };
125
126 printf("\nQPainterPath: elementCount=%d\n", path.elementCount());
127 for (int i=0; i<path.elementCount(); ++i) {
128 const QPainterPath::Element &e = path.elementAt(i);
129 Q_ASSERT(e.type >= 0 && e.type <= QPainterPath::CurveToDataElement);
130 printf(" - %3d:: %s, (%.2f, %.2f)\n", i, names[e.type], e.x, e.y);
131 }
132}
133#endif
134
135/*!
136 \class QPainterPath
137 \ingroup painting
138 \inmodule QtGui
139
140 \reentrant
141
142 \brief The QPainterPath class provides a container for painting operations,
143 enabling graphical shapes to be constructed and reused.
144
145 A painter path is an object composed of a number of graphical
146 building blocks, such as rectangles, ellipses, lines, and curves.
147 Building blocks can be joined in closed subpaths, for example as a
148 rectangle or an ellipse. A closed path has coinciding start and
149 end points. Or they can exist independently as unclosed subpaths,
150 such as lines and curves.
151
152 A QPainterPath object can be used for filling, outlining, and
153 clipping. To generate fillable outlines for a given painter path,
154 use the QPainterPathStroker class. The main advantage of painter
155 paths over normal drawing operations is that complex shapes only
156 need to be created once; then they can be drawn many times using
157 only calls to the QPainter::drawPath() function.
158
159 QPainterPath provides a collection of functions that can be used
160 to obtain information about the path and its elements. In addition
161 it is possible to reverse the order of the elements using the
162 toReversed() function. There are also several functions to convert
163 this painter path object into a polygon representation.
164
165 \section1 Composing a QPainterPath
166
167 A QPainterPath object can be constructed as an empty path, with a
168 given start point, or as a copy of another QPainterPath object.
169 Once created, lines and curves can be added to the path using the
170 lineTo(), arcTo(), cubicTo() and quadTo() functions. The lines and
171 curves stretch from the currentPosition() to the position passed
172 as argument.
173
174 The currentPosition() of the QPainterPath object is always the end
175 position of the last subpath that was added (or the initial start
176 point). Use the moveTo() function to move the currentPosition()
177 without adding a component. The moveTo() function implicitly
178 starts a new subpath, and closes the previous one. Another way of
179 starting a new subpath is to call the closeSubpath() function
180 which closes the current path by adding a line from the
181 currentPosition() back to the path's start position. Note that the
182 new path will have (0, 0) as its initial currentPosition().
183
184 QPainterPath class also provides several convenience functions to
185 add closed subpaths to a painter path: addEllipse(), addPath(),
186 addRect(), addRegion() and addText(). The addPolygon() function
187 adds an \e unclosed subpath. In fact, these functions are all
188 collections of moveTo(), lineTo() and cubicTo() operations.
189
190 In addition, a path can be added to the current path using the
191 connectPath() function. But note that this function will connect
192 the last element of the current path to the first element of given
193 one by adding a line.
194
195 Below is a code snippet that shows how a QPainterPath object can
196 be used:
197
198 \table 70%
199 \row
200 \li \inlineimage qpainterpath-construction.png
201 \li
202 \snippet code/src_gui_painting_qpainterpath.cpp 0
203 \endtable
204
205 The painter path is initially empty when constructed. We first add
206 a rectangle, which is a closed subpath. Then we add two bezier
207 curves which together form a closed subpath even though they are
208 not closed individually. Finally we draw the entire path. The path
209 is filled using the default fill rule, Qt::OddEvenFill. Qt
210 provides two methods for filling paths:
211
212 \table
213 \header
214 \li Qt::OddEvenFill
215 \li Qt::WindingFill
216 \row
217 \li \inlineimage qt-fillrule-oddeven.png
218 \li \inlineimage qt-fillrule-winding.png
219 \endtable
220
221 See the Qt::FillRule documentation for the definition of the
222 rules. A painter path's currently set fill rule can be retrieved
223 using the fillRule() function, and altered using the setFillRule()
224 function.
225
226 \section1 QPainterPath Information
227
228 The QPainterPath class provides a collection of functions that
229 returns information about the path and its elements.
230
231 The currentPosition() function returns the end point of the last
232 subpath that was added (or the initial start point). The
233 elementAt() function can be used to retrieve the various subpath
234 elements, the \e number of elements can be retrieved using the
235 elementCount() function, and the isEmpty() function tells whether
236 this QPainterPath object contains any elements at all.
237
238 The controlPointRect() function returns the rectangle containing
239 all the points and control points in this path. This function is
240 significantly faster to compute than the exact boundingRect()
241 which returns the bounding rectangle of this painter path with
242 floating point precision.
243
244 Finally, QPainterPath provides the contains() function which can
245 be used to determine whether a given point or rectangle is inside
246 the path, and the intersects() function which determines if any of
247 the points inside a given rectangle also are inside this path.
248
249 \section1 QPainterPath Conversion
250
251 For compatibility reasons, it might be required to simplify the
252 representation of a painter path: QPainterPath provides the
253 toFillPolygon(), toFillPolygons() and toSubpathPolygons()
254 functions which convert the painter path into a polygon. The
255 toFillPolygon() returns the painter path as one single polygon,
256 while the two latter functions return a list of polygons.
257
258 The toFillPolygons() and toSubpathPolygons() functions are
259 provided because it is usually faster to draw several small
260 polygons than to draw one large polygon, even though the total
261 number of points drawn is the same. The difference between the two
262 is the \e number of polygons they return: The toSubpathPolygons()
263 creates one polygon for each subpath regardless of intersecting
264 subpaths (i.e. overlapping bounding rectangles), while the
265 toFillPolygons() functions creates only one polygon for
266 overlapping subpaths.
267
268 The toFillPolygon() and toFillPolygons() functions first convert
269 all the subpaths to polygons, then uses a rewinding technique to
270 make sure that overlapping subpaths can be filled using the
271 correct fill rule. Note that rewinding inserts additional lines in
272 the polygon so the outline of the fill polygon does not match the
273 outline of the path.
274
275 \section1 Examples
276
277 Qt provides the \l {painting/painterpaths}{Painter Paths Example}
278 and the \l {painting/deform}{Vector Deformation example} which are
279 located in Qt's example directory.
280
281 The \l {painting/painterpaths}{Painter Paths Example} shows how
282 painter paths can be used to build complex shapes for rendering
283 and lets the user experiment with the filling and stroking. The
284 \l {painting/deform}{Vector Deformation Example} shows how to use
285 QPainterPath to draw text.
286
287 \table
288 \header
289 \li \l {painting/painterpaths}{Painter Paths Example}
290 \li \l {painting/deform}{Vector Deformation Example}
291 \row
292 \li \inlineimage qpainterpath-example.png
293 \li \inlineimage qpainterpath-demo.png
294 \endtable
295
296 \sa QPainterPathStroker, QPainter, QRegion, {Painter Paths Example}
297*/
298
299/*!
300 \enum QPainterPath::ElementType
301
302 This enum describes the types of elements used to connect vertices
303 in subpaths.
304
305 Note that elements added as closed subpaths using the
306 addEllipse(), addPath(), addPolygon(), addRect(), addRegion() and
307 addText() convenience functions, is actually added to the path as
308 a collection of separate elements using the moveTo(), lineTo() and
309 cubicTo() functions.
310
311 \value MoveToElement A new subpath. See also moveTo().
312 \value LineToElement A line. See also lineTo().
313 \value CurveToElement A curve. See also cubicTo() and quadTo().
314 \value CurveToDataElement The extra data required to describe a curve in
315 a CurveToElement element.
316
317 \sa elementAt(), elementCount()
318*/
319
320/*!
321 \class QPainterPath::Element
322 \inmodule QtGui
323
324 \brief The QPainterPath::Element class specifies the position and
325 type of a subpath.
326
327 Once a QPainterPath object is constructed, subpaths like lines and
328 curves can be added to the path (creating
329 QPainterPath::LineToElement and QPainterPath::CurveToElement
330 components).
331
332 The lines and curves stretch from the currentPosition() to the
333 position passed as argument. The currentPosition() of the
334 QPainterPath object is always the end position of the last subpath
335 that was added (or the initial start point). The moveTo() function
336 can be used to move the currentPosition() without adding a line or
337 curve, creating a QPainterPath::MoveToElement component.
338
339 \sa QPainterPath
340*/
341
342/*!
343 \variable QPainterPath::Element::x
344 \brief the x coordinate of the element's position.
345
346 \sa {operator QPointF()}
347*/
348
349/*!
350 \variable QPainterPath::Element::y
351 \brief the y coordinate of the element's position.
352
353 \sa {operator QPointF()}
354*/
355
356/*!
357 \variable QPainterPath::Element::type
358 \brief the type of element
359
360 \sa isCurveTo(), isLineTo(), isMoveTo()
361*/
362
363/*!
364 \fn bool QPainterPath::Element::operator==(const Element &other) const
365 \since 4.2
366
367 Returns \c true if this element is equal to \a other;
368 otherwise returns \c false.
369
370 \sa operator!=()
371*/
372
373/*!
374 \fn bool QPainterPath::Element::operator!=(const Element &other) const
375 \since 4.2
376
377 Returns \c true if this element is not equal to \a other;
378 otherwise returns \c false.
379
380 \sa operator==()
381*/
382
383/*!
384 \fn bool QPainterPath::Element::isCurveTo () const
385
386 Returns \c true if the element is a curve, otherwise returns \c false.
387
388 \sa type, QPainterPath::CurveToElement
389*/
390
391/*!
392 \fn bool QPainterPath::Element::isLineTo () const
393
394 Returns \c true if the element is a line, otherwise returns \c false.
395
396 \sa type, QPainterPath::LineToElement
397*/
398
399/*!
400 \fn bool QPainterPath::Element::isMoveTo () const
401
402 Returns \c true if the element is moving the current position,
403 otherwise returns \c false.
404
405 \sa type, QPainterPath::MoveToElement
406*/
407
408/*!
409 \fn QPainterPath::Element::operator QPointF () const
410
411 Returns the element's position.
412
413 \sa x, y
414*/
415
416/*!
417 \fn void QPainterPath::addEllipse(qreal x, qreal y, qreal width, qreal height)
418 \overload
419
420 Creates an ellipse within the bounding rectangle defined by its top-left
421 corner at (\a x, \a y), \a width and \a height, and adds it to the
422 painter path as a closed subpath.
423*/
424
425/*!
426 \since 4.4
427
428 \fn void QPainterPath::addEllipse(const QPointF &center, qreal rx, qreal ry)
429 \overload
430
431 Creates an ellipse positioned at \a{center} with radii \a{rx} and \a{ry},
432 and adds it to the painter path as a closed subpath.
433*/
434
435/*!
436 \fn void QPainterPath::addText(qreal x, qreal y, const QFont &font, const QString &text)
437 \overload
438
439 Adds the given \a text to this path as a set of closed subpaths created
440 from the \a font supplied. The subpaths are positioned so that the left
441 end of the text's baseline lies at the point specified by (\a x, \a y).
442*/
443
444/*!
445 \fn int QPainterPath::elementCount() const
446
447 Returns the number of path elements in the painter path.
448
449 \sa ElementType, elementAt(), isEmpty()
450*/
451
452int QPainterPath::elementCount() const
453{
454 return d_ptr ? d_ptr->elements.size() : 0;
455}
456
457/*!
458 \fn QPainterPath::Element QPainterPath::elementAt(int index) const
459
460 Returns the element at the given \a index in the painter path.
461
462 \sa ElementType, elementCount(), isEmpty()
463*/
464
465QPainterPath::Element QPainterPath::elementAt(int i) const
466{
467 Q_ASSERT(d_ptr);
468 Q_ASSERT(i >= 0 && i < elementCount());
469 return d_ptr->elements.at(i);
470}
471
472/*!
473 \fn void QPainterPath::setElementPositionAt(int index, qreal x, qreal y)
474 \since 4.2
475
476 Sets the x and y coordinate of the element at index \a index to \a
477 x and \a y.
478*/
479
480void QPainterPath::setElementPositionAt(int i, qreal x, qreal y)
481{
482 Q_ASSERT(d_ptr);
483 Q_ASSERT(i >= 0 && i < elementCount());
484 setDirty(true);
485 QPainterPath::Element &e = d_ptr->elements[i];
486 e.x = x;
487 e.y = y;
488}
489
490
491/*###
492 \fn QPainterPath &QPainterPath::operator +=(const QPainterPath &other)
493
494 Appends the \a other painter path to this painter path and returns a
495 reference to the result.
496*/
497
498/*!
499 Constructs an empty QPainterPath object.
500*/
501QPainterPath::QPainterPath() noexcept
502 : d_ptr(nullptr)
503{
504}
505
506/*!
507 \fn QPainterPath::QPainterPath(const QPainterPath &path)
508
509 Creates a QPainterPath object that is a copy of the given \a path.
510
511 \sa operator=()
512*/
513QPainterPath::QPainterPath(const QPainterPath &other)
514 : d_ptr(other.d_ptr ? new QPainterPathPrivate(*other.d_ptr) : nullptr)
515{
516}
517
518/*!
519 \fn QPainterPath::QPainterPath(QPainterPath &&other)
520 \since 6.10
521
522 Move-constructs a new painter path from \a other.
523
524 The moved-from object \a other is placed in the default-constructed state.
525*/
526
527/*!
528 Creates a QPainterPath object with the given \a startPoint as its
529 current position.
530*/
531
532QPainterPath::QPainterPath(const QPointF &startPoint)
533 : d_ptr(new QPainterPathPrivate(startPoint))
534{
535}
536
537/*!
538 \internal
539*/
540void QPainterPath::ensureData_helper()
541{
542 Q_ASSERT(d_ptr == nullptr);
543 QPainterPathPrivate *data = new QPainterPathPrivate;
544 data->elements.reserve(16);
545 QPainterPath::Element e = { 0, 0, QPainterPath::MoveToElement };
546 data->elements << e;
547 d_ptr = data;
548 Q_ASSERT(d_ptr != nullptr);
549}
550
551/*!
552 \fn QPainterPath &QPainterPath::operator=(const QPainterPath &path)
553
554 Assigns the given \a path to this painter path.
555
556 \sa QPainterPath()
557*/
558QPainterPath &QPainterPath::operator=(const QPainterPath &other)
559{
560 QPainterPath copy(other);
561 swap(copy);
562 return *this;
563}
564
565/*!
566 \fn QPainterPath &QPainterPath::operator=(QPainterPath &&other)
567
568 Move-assigns \a other to this QPainterPath instance.
569
570 \since 5.2
571*/
572
573/*!
574 \fn void QPainterPath::swap(QPainterPath &other)
575 \since 4.8
576 \memberswap{painer path}
577*/
578
579/*!
580 Destroys this QPainterPath object.
581*/
582QPainterPath::~QPainterPath()
583{
584 delete d_ptr;
585}
586
587/*!
588 Clears the path elements stored.
589
590 This allows the path to reuse previous memory allocations.
591
592 \sa reserve(), capacity()
593 \since 5.13
594*/
595void QPainterPath::clear()
596{
597 if (!d_ptr)
598 return;
599
600 setDirty(true);
601 d_func()->clear();
602 d_func()->elements.append( {0, 0, MoveToElement} );
603}
604
605/*!
606 Reserves a given amount of elements in QPainterPath's internal memory.
607
608 Attempts to allocate memory for at least \a size elements.
609
610 \sa clear(), capacity(), QList::reserve()
611 \since 5.13
612*/
613void QPainterPath::reserve(int size)
614{
615 Q_D(QPainterPath);
616 if ((!d && size > 0) || (d && d->elements.capacity() < size)) {
617 ensureData();
618 setDirty(true);
619 d_func()->elements.reserve(size);
620 }
621}
622
623/*!
624 Returns the number of elements allocated by the QPainterPath.
625
626 \sa clear(), reserve()
627 \since 5.13
628*/
629int QPainterPath::capacity() const
630{
631 Q_D(QPainterPath);
632 if (d)
633 return d->elements.capacity();
634
635 return 0;
636}
637
638/*!
639 Closes the current subpath by drawing a line to the beginning of
640 the subpath, automatically starting a new path. The current point
641 of the new path is (0, 0).
642
643 If the subpath does not contain any elements, this function does
644 nothing.
645
646 \sa moveTo(), {QPainterPath#Composing a QPainterPath}{Composing
647 a QPainterPath}
648 */
649void QPainterPath::closeSubpath()
650{
651#ifdef QPP_DEBUG
652 printf("QPainterPath::closeSubpath()\n");
653#endif
654 if (isEmpty())
655 return;
656 setDirty(true);
657
658 d_func()->close();
659}
660
661/*!
662 \fn void QPainterPath::moveTo(qreal x, qreal y)
663
664 \overload
665
666 Moves the current position to (\a{x}, \a{y}) and starts a new
667 subpath, implicitly closing the previous path.
668*/
669
670/*!
671 \fn void QPainterPath::moveTo(const QPointF &point)
672
673 Moves the current point to the given \a point, implicitly starting
674 a new subpath and closing the previous one.
675
676 \sa closeSubpath(), {QPainterPath#Composing a
677 QPainterPath}{Composing a QPainterPath}
678*/
679void QPainterPath::moveTo(const QPointF &p)
680{
681#ifdef QPP_DEBUG
682 printf("QPainterPath::moveTo() (%.2f,%.2f)\n", p.x(), p.y());
683#endif
684
685 if (!hasValidCoords(p)) {
686#ifndef QT_NO_DEBUG
687 qWarning("QPainterPath::moveTo: Adding point with invalid coordinates, ignoring call");
688#endif
689 return;
690 }
691
692 ensureData();
693 setDirty(true);
694
695 QPainterPathPrivate *d = d_func();
696 Q_ASSERT(!d->elements.isEmpty());
697
698 d->require_moveTo = false;
699
700 if (d->elements.constLast().type == MoveToElement) {
701 d->elements.last().x = p.x();
702 d->elements.last().y = p.y();
703 } else {
704 Element elm = { p.x(), p.y(), MoveToElement };
705 d->elements.append(elm);
706 }
707 d->cStart = d->elements.size() - 1;
708}
709
710/*!
711 \fn void QPainterPath::lineTo(qreal x, qreal y)
712
713 \overload
714
715 Draws a line from the current position to the point (\a{x},
716 \a{y}).
717*/
718
719/*!
720 \fn void QPainterPath::lineTo(const QPointF &endPoint)
721
722 Adds a straight line from the current position to the given \a
723 endPoint. After the line is drawn, the current position is updated
724 to be at the end point of the line.
725
726 \sa addPolygon(), addRect(), {QPainterPath#Composing a
727 QPainterPath}{Composing a QPainterPath}
728 */
729void QPainterPath::lineTo(const QPointF &p)
730{
731#ifdef QPP_DEBUG
732 printf("QPainterPath::lineTo() (%.2f,%.2f)\n", p.x(), p.y());
733#endif
734
735 if (!hasValidCoords(p)) {
736#ifndef QT_NO_DEBUG
737 qWarning("QPainterPath::lineTo: Adding point with invalid coordinates, ignoring call");
738#endif
739 return;
740 }
741
742 ensureData();
743 setDirty(true);
744
745 QPainterPathPrivate *d = d_func();
746 Q_ASSERT(!d->elements.isEmpty());
747 d->maybeMoveTo();
748 if (p == QPointF(d->elements.constLast()))
749 return;
750 Element elm = { p.x(), p.y(), LineToElement };
751 d->elements.append(elm);
752
753 d->convex = d->elements.size() == 3 || (d->elements.size() == 4 && d->isClosed());
754}
755
756/*!
757 \fn void QPainterPath::cubicTo(qreal c1X, qreal c1Y, qreal c2X,
758 qreal c2Y, qreal endPointX, qreal endPointY);
759
760 \overload
761
762 Adds a cubic Bezier curve between the current position and the end
763 point (\a{endPointX}, \a{endPointY}) with control points specified
764 by (\a{c1X}, \a{c1Y}) and (\a{c2X}, \a{c2Y}).
765*/
766
767/*!
768 \fn void QPainterPath::cubicTo(const QPointF &c1, const QPointF &c2, const QPointF &endPoint)
769
770 Adds a cubic Bezier curve between the current position and the
771 given \a endPoint using the control points specified by \a c1, and
772 \a c2.
773
774 After the curve is added, the current position is updated to be at
775 the end point of the curve.
776
777 \table 100%
778 \row
779 \li \inlineimage qpainterpath-cubicto.png
780 \li
781 \snippet code/src_gui_painting_qpainterpath.cpp 1
782 \endtable
783
784 \sa quadTo(), {QPainterPath#Composing a QPainterPath}{Composing
785 a QPainterPath}
786*/
787void QPainterPath::cubicTo(const QPointF &c1, const QPointF &c2, const QPointF &e)
788{
789#ifdef QPP_DEBUG
790 printf("QPainterPath::cubicTo() (%.2f,%.2f), (%.2f,%.2f), (%.2f,%.2f)\n",
791 c1.x(), c1.y(), c2.x(), c2.y(), e.x(), e.y());
792#endif
793
794 if (!hasValidCoords(c1) || !hasValidCoords(c2) || !hasValidCoords(e)) {
795#ifndef QT_NO_DEBUG
796 qWarning("QPainterPath::cubicTo: Adding point with invalid coordinates, ignoring call");
797#endif
798 return;
799 }
800
801 ensureData();
802 setDirty(true);
803
804 QPainterPathPrivate *d = d_func();
805 Q_ASSERT(!d->elements.isEmpty());
806
807
808 // Abort on empty curve as a stroker cannot handle this and the
809 // curve is irrelevant anyway.
810 if (d->elements.constLast() == c1 && c1 == c2 && c2 == e)
811 return;
812
813 d->maybeMoveTo();
814
815 Element ce1 = { c1.x(), c1.y(), CurveToElement };
816 Element ce2 = { c2.x(), c2.y(), CurveToDataElement };
817 Element ee = { e.x(), e.y(), CurveToDataElement };
818 d->elements << ce1 << ce2 << ee;
819}
820
821/*!
822 \fn void QPainterPath::quadTo(qreal cx, qreal cy, qreal endPointX, qreal endPointY);
823
824 \overload
825
826 Adds a quadratic Bezier curve between the current point and the endpoint
827 (\a{endPointX}, \a{endPointY}) with the control point specified by
828 (\a{cx}, \a{cy}).
829*/
830
831/*!
832 \fn void QPainterPath::quadTo(const QPointF &c, const QPointF &endPoint)
833
834 Adds a quadratic Bezier curve between the current position and the
835 given \a endPoint with the control point specified by \a c.
836
837 After the curve is added, the current point is updated to be at
838 the end point of the curve.
839
840 \sa cubicTo(), {QPainterPath#Composing a QPainterPath}{Composing a
841 QPainterPath}
842*/
843void QPainterPath::quadTo(const QPointF &c, const QPointF &e)
844{
845#ifdef QPP_DEBUG
846 printf("QPainterPath::quadTo() (%.2f,%.2f), (%.2f,%.2f)\n",
847 c.x(), c.y(), e.x(), e.y());
848#endif
849
850 if (!hasValidCoords(c) || !hasValidCoords(e)) {
851#ifndef QT_NO_DEBUG
852 qWarning("QPainterPath::quadTo: Adding point with invalid coordinates, ignoring call");
853#endif
854 return;
855 }
856
857 ensureData();
858 setDirty(true);
859
860 Q_D(QPainterPath);
861 Q_ASSERT(!d->elements.isEmpty());
862 const QPainterPath::Element &elm = d->elements.at(elementCount()-1);
863 QPointF prev(elm.x, elm.y);
864
865 // Abort on empty curve as a stroker cannot handle this and the
866 // curve is irrelevant anyway.
867 if (prev == c && c == e)
868 return;
869
870 QPointF c1((prev.x() + 2*c.x()) / 3, (prev.y() + 2*c.y()) / 3);
871 QPointF c2((e.x() + 2*c.x()) / 3, (e.y() + 2*c.y()) / 3);
872 cubicTo(c1, c2, e);
873}
874
875/*!
876 \fn void QPainterPath::arcTo(qreal x, qreal y, qreal width, qreal
877 height, qreal startAngle, qreal sweepLength)
878
879 \overload
880
881 Creates an arc that occupies the rectangle QRectF(\a x, \a y, \a
882 width, \a height), beginning at the specified \a startAngle and
883 extending \a sweepLength degrees counter-clockwise.
884
885*/
886
887/*!
888 \fn void QPainterPath::arcTo(const QRectF &rectangle, qreal startAngle, qreal sweepLength)
889
890 Creates an arc that occupies the given \a rectangle, beginning at
891 the specified \a startAngle and extending \a sweepLength degrees
892 counter-clockwise.
893
894 Angles are specified in degrees. Clockwise arcs can be specified
895 using negative angles.
896
897 Note that this function connects the starting point of the arc to
898 the current position if they are not already connected. After the
899 arc has been added, the current position is the last point in
900 arc. To draw a line back to the first point, use the
901 closeSubpath() function.
902
903 \table 100%
904 \row
905 \li \inlineimage qpainterpath-arcto.png
906 \li
907 \snippet code/src_gui_painting_qpainterpath.cpp 2
908 \endtable
909
910 \sa arcMoveTo(), addEllipse(), QPainter::drawArc(), QPainter::drawPie(),
911 {QPainterPath#Composing a QPainterPath}{Composing a
912 QPainterPath}
913*/
914void QPainterPath::arcTo(const QRectF &rect, qreal startAngle, qreal sweepLength)
915{
916#ifdef QPP_DEBUG
917 printf("QPainterPath::arcTo() (%.2f, %.2f, %.2f, %.2f, angle=%.2f, sweep=%.2f\n",
918 rect.x(), rect.y(), rect.width(), rect.height(), startAngle, sweepLength);
919#endif
920
921 if (!hasValidCoords(rect) || !isValidCoord(startAngle) || !isValidCoord(sweepLength)) {
922#ifndef QT_NO_DEBUG
923 qWarning("QPainterPath::arcTo: Adding point with invalid coordinates, ignoring call");
924#endif
925 return;
926 }
927
928 if (rect.isNull())
929 return;
930
931 ensureData();
932 setDirty(true);
933
934 int point_count;
935 QPointF pts[15];
936 QPointF curve_start = qt_curves_for_arc(rect, startAngle, sweepLength, pts, &point_count);
937
938 lineTo(curve_start);
939 for (int i=0; i<point_count; i+=3) {
940 cubicTo(pts[i].x(), pts[i].y(),
941 pts[i+1].x(), pts[i+1].y(),
942 pts[i+2].x(), pts[i+2].y());
943 }
944
945}
946
947
948/*!
949 \fn void QPainterPath::arcMoveTo(qreal x, qreal y, qreal width, qreal height, qreal angle)
950 \overload
951 \since 4.2
952
953 Creates a move to that lies on the arc that occupies the
954 QRectF(\a x, \a y, \a width, \a height) at \a angle.
955*/
956
957
958/*!
959 \fn void QPainterPath::arcMoveTo(const QRectF &rectangle, qreal angle)
960 \since 4.2
961
962 Creates a move to that lies on the arc that occupies the given \a
963 rectangle at \a angle.
964
965 Angles are specified in degrees. Clockwise arcs can be specified
966 using negative angles.
967
968 \sa moveTo(), arcTo()
969*/
970
971void QPainterPath::arcMoveTo(const QRectF &rect, qreal angle)
972{
973 if (rect.isNull())
974 return;
975
976 QPointF pt;
977 qt_find_ellipse_coords(rect, angle, 0, &pt, nullptr);
978 moveTo(pt);
979}
980
981
982
983/*!
984 \fn QPointF QPainterPath::currentPosition() const
985
986 Returns the current position of the path.
987*/
988QPointF QPainterPath::currentPosition() const
989{
990 return !d_ptr || d_func()->elements.isEmpty()
991 ? QPointF()
992 : QPointF(d_func()->elements.constLast().x, d_func()->elements.constLast().y);
993}
994
995
996/*!
997 \fn void QPainterPath::addRect(qreal x, qreal y, qreal width, qreal height)
998
999 \overload
1000
1001 Adds a rectangle at position (\a{x}, \a{y}), with the given \a
1002 width and \a height, as a closed subpath.
1003*/
1004
1005/*!
1006 \fn void QPainterPath::addRect(const QRectF &rectangle)
1007
1008 Adds the given \a rectangle to this path as a closed subpath.
1009
1010 The \a rectangle is added as a clockwise set of lines. The painter
1011 path's current position after the \a rectangle has been added is
1012 at the top-left corner of the rectangle.
1013
1014 \table 100%
1015 \row
1016 \li \inlineimage qpainterpath-addrectangle.png
1017 \li
1018 \snippet code/src_gui_painting_qpainterpath.cpp 3
1019 \endtable
1020
1021 \sa addRegion(), lineTo(), {QPainterPath#Composing a
1022 QPainterPath}{Composing a QPainterPath}
1023*/
1024void QPainterPath::addRect(const QRectF &r)
1025{
1026 if (!hasValidCoords(r)) {
1027#ifndef QT_NO_DEBUG
1028 qWarning("QPainterPath::addRect: Adding point with invalid coordinates, ignoring call");
1029#endif
1030 return;
1031 }
1032
1033 if (r.isNull())
1034 return;
1035
1036 ensureData();
1037 setDirty(true);
1038
1039 bool first = d_func()->elements.size() < 2;
1040
1041 moveTo(r.x(), r.y());
1042
1043 Element l1 = { r.x() + r.width(), r.y(), LineToElement };
1044 Element l2 = { r.x() + r.width(), r.y() + r.height(), LineToElement };
1045 Element l3 = { r.x(), r.y() + r.height(), LineToElement };
1046 Element l4 = { r.x(), r.y(), LineToElement };
1047
1048 d_func()->elements << l1 << l2 << l3 << l4;
1049 d_func()->require_moveTo = true;
1050 d_func()->convex = first;
1051}
1052
1053/*!
1054 Adds the given \a polygon to the path as an (unclosed) subpath.
1055
1056 Note that the current position after the polygon has been added,
1057 is the last point in \a polygon. To draw a line back to the first
1058 point, use the closeSubpath() function.
1059
1060 \table 100%
1061 \row
1062 \li \inlineimage qpainterpath-addpolygon.png
1063 \li
1064 \snippet code/src_gui_painting_qpainterpath.cpp 4
1065 \endtable
1066
1067 \sa lineTo(), {QPainterPath#Composing a QPainterPath}{Composing
1068 a QPainterPath}
1069*/
1070void QPainterPath::addPolygon(const QPolygonF &polygon)
1071{
1072 if (polygon.isEmpty())
1073 return;
1074
1075 ensureData();
1076 setDirty(true);
1077
1078 moveTo(polygon.constFirst());
1079 for (int i=1; i<polygon.size(); ++i) {
1080 Element elm = { polygon.at(i).x(), polygon.at(i).y(), LineToElement };
1081 d_func()->elements << elm;
1082 }
1083}
1084
1085/*!
1086 \fn void QPainterPath::addEllipse(const QRectF &boundingRectangle)
1087
1088 Creates an ellipse within the specified \a boundingRectangle
1089 and adds it to the painter path as a closed subpath.
1090
1091 The ellipse is composed of a clockwise curve, starting and
1092 finishing at zero degrees (the 3 o'clock position).
1093
1094 \table 100%
1095 \row
1096 \li \inlineimage qpainterpath-addellipse.png
1097 \li
1098 \snippet code/src_gui_painting_qpainterpath.cpp 5
1099 \endtable
1100
1101 \sa arcTo(), QPainter::drawEllipse(), {QPainterPath#Composing a
1102 QPainterPath}{Composing a QPainterPath}
1103*/
1104void QPainterPath::addEllipse(const QRectF &boundingRect)
1105{
1106 if (!hasValidCoords(boundingRect)) {
1107#ifndef QT_NO_DEBUG
1108 qWarning("QPainterPath::addEllipse: Adding point with invalid coordinates, ignoring call");
1109#endif
1110 return;
1111 }
1112
1113 if (boundingRect.isNull())
1114 return;
1115
1116 ensureData();
1117 setDirty(true);
1118
1119 bool first = d_func()->elements.size() < 2;
1120
1121 QPointF pts[12];
1122 int point_count;
1123 QPointF start = qt_curves_for_arc(boundingRect, 0, -360, pts, &point_count);
1124
1125 moveTo(start);
1126 cubicTo(pts[0], pts[1], pts[2]); // 0 -> 270
1127 cubicTo(pts[3], pts[4], pts[5]); // 270 -> 180
1128 cubicTo(pts[6], pts[7], pts[8]); // 180 -> 90
1129 cubicTo(pts[9], pts[10], pts[11]); // 90 - >0
1130 d_func()->require_moveTo = true;
1131
1132 d_func()->convex = first;
1133}
1134
1135/*!
1136 \fn void QPainterPath::addText(const QPointF &point, const QFont &font, const QString &text)
1137
1138 Adds the given \a text to this path as a set of closed subpaths
1139 created from the \a font supplied. The subpaths are positioned so
1140 that the left end of the text's baseline lies at the specified \a
1141 point.
1142
1143 Some fonts may yield overlapping subpaths and will require the
1144 \c Qt::WindingFill fill rule for correct rendering.
1145
1146 \table 100%
1147 \row
1148 \li \inlineimage qpainterpath-addtext.png
1149 \li
1150 \snippet code/src_gui_painting_qpainterpath.cpp 6
1151 \endtable
1152
1153 \sa QPainter::drawText(), {QPainterPath#Composing a
1154 QPainterPath}{Composing a QPainterPath}, setFillRule()
1155*/
1156void QPainterPath::addText(const QPointF &point, const QFont &f, const QString &text)
1157{
1158 if (text.isEmpty())
1159 return;
1160
1161 ensureData();
1162 setDirty(true);
1163
1164 QTextLayout layout(text, f);
1165 layout.setCacheEnabled(true);
1166
1167 QTextOption opt = layout.textOption();
1168 opt.setUseDesignMetrics(true);
1169 layout.setTextOption(opt);
1170
1171 QTextEngine *eng = layout.engine();
1172 layout.beginLayout();
1173 QTextLine line = layout.createLine();
1174 Q_UNUSED(line);
1175 layout.endLayout();
1176 const QScriptLine &sl = eng->lines[0];
1177 if (!sl.length || !eng->layoutData)
1178 return;
1179
1180 int nItems = eng->layoutData->items.size();
1181
1182 qreal x(point.x());
1183 qreal y(point.y());
1184
1185 QVarLengthArray<int> visualOrder(nItems);
1186 QVarLengthArray<uchar> levels(nItems);
1187 for (int i = 0; i < nItems; ++i)
1188 levels[i] = eng->layoutData->items.at(i).analysis.bidiLevel;
1189 QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
1190
1191 for (int i = 0; i < nItems; ++i) {
1192 int item = visualOrder[i];
1193 const QScriptItem &si = eng->layoutData->items.at(item);
1194
1195 if (si.analysis.flags < QScriptAnalysis::TabOrObject) {
1196 QGlyphLayout glyphs = eng->shapedGlyphs(&si);
1197 QFontEngine *fe = eng->fontEngine(si);
1198 Q_ASSERT(fe);
1199 fe->addOutlineToPath(x, y, glyphs, this,
1200 si.analysis.bidiLevel % 2
1201 ? QTextItem::RenderFlags(QTextItem::RightToLeft)
1202 : QTextItem::RenderFlags{});
1203
1204 const qreal lw = fe->lineThickness().toReal();
1205 if (f.d->underline) {
1206 qreal pos = fe->underlinePosition().toReal();
1207 addRect(x, y + pos, si.width.toReal(), lw);
1208 }
1209 if (f.d->overline) {
1210 qreal pos = fe->ascent().toReal() + 1;
1211 addRect(x, y - pos, si.width.toReal(), lw);
1212 }
1213 if (f.d->strikeOut) {
1214 qreal pos = fe->ascent().toReal() / 3;
1215 addRect(x, y - pos, si.width.toReal(), lw);
1216 }
1217 }
1218 x += si.width.toReal();
1219 }
1220}
1221
1222/*!
1223 \fn void QPainterPath::addPath(const QPainterPath &path)
1224
1225 Adds the given \a path to \e this path as a closed subpath.
1226
1227 \sa connectPath(), {QPainterPath#Composing a
1228 QPainterPath}{Composing a QPainterPath}
1229*/
1230void QPainterPath::addPath(const QPainterPath &other)
1231{
1232 if (other.isEmpty())
1233 return;
1234
1235 ensureData();
1236 setDirty(true);
1237
1238 QPainterPathPrivate *d = d_func();
1239 // Remove last moveto so we don't get multiple moveto's
1240 if (d->elements.constLast().type == MoveToElement)
1241 d->elements.remove(d->elements.size()-1);
1242
1243 // Locate where our own current subpath will start after the other path is added.
1244 int cStart = d->elements.size() + other.d_func()->cStart;
1245 d->elements += other.d_func()->elements;
1246 d->cStart = cStart;
1247
1248 d->require_moveTo = other.d_func()->isClosed();
1249}
1250
1251
1252/*!
1253 \fn void QPainterPath::connectPath(const QPainterPath &path)
1254
1255 Connects the given \a path to \e this path by adding a line from the
1256 last element of this path to the first element of the given path.
1257
1258 \sa addPath(), {QPainterPath#Composing a QPainterPath}{Composing
1259 a QPainterPath}
1260*/
1261void QPainterPath::connectPath(const QPainterPath &other)
1262{
1263 if (other.isEmpty())
1264 return;
1265
1266 ensureData();
1267 setDirty(true);
1268
1269 QPainterPathPrivate *d = d_func();
1270 // Remove last moveto so we don't get multiple moveto's
1271 if (d->elements.constLast().type == MoveToElement)
1272 d->elements.remove(d->elements.size()-1);
1273
1274 // Locate where our own current subpath will start after the other path is added.
1275 int cStart = d->elements.size() + other.d_func()->cStart;
1276 int first = d->elements.size();
1277 d->elements += other.d_func()->elements;
1278
1279 if (first != 0)
1280 d->elements[first].type = LineToElement;
1281
1282 // avoid duplicate points
1283 if (first > 0 && QPointF(d->elements.at(first)) == QPointF(d->elements.at(first - 1))) {
1284 d->elements.remove(first--);
1285 --cStart;
1286 }
1287
1288 if (cStart != first)
1289 d->cStart = cStart;
1290}
1291
1292/*!
1293 Adds the given \a region to the path by adding each rectangle in
1294 the region as a separate closed subpath.
1295
1296 \sa addRect(), {QPainterPath#Composing a QPainterPath}{Composing
1297 a QPainterPath}
1298*/
1299void QPainterPath::addRegion(const QRegion &region)
1300{
1301 ensureData();
1302 setDirty(true);
1303
1304 for (const QRect &rect : region)
1305 addRect(rect);
1306}
1307
1308
1309/*!
1310 Returns the painter path's currently set fill rule.
1311
1312 \sa setFillRule()
1313*/
1314Qt::FillRule QPainterPath::fillRule() const
1315{
1316 return d_func() && d_func()->hasWindingFill ? Qt::WindingFill : Qt::OddEvenFill;
1317}
1318
1319/*!
1320 \fn void QPainterPath::setFillRule(Qt::FillRule fillRule)
1321
1322 Sets the fill rule of the painter path to the given \a
1323 fillRule. Qt provides two methods for filling paths:
1324
1325 \table
1326 \header
1327 \li Qt::OddEvenFill (default)
1328 \li Qt::WindingFill
1329 \row
1330 \li \inlineimage qt-fillrule-oddeven.png
1331 \li \inlineimage qt-fillrule-winding.png
1332 \endtable
1333
1334 \sa fillRule()
1335*/
1336void QPainterPath::setFillRule(Qt::FillRule fillRule)
1337{
1338 ensureData();
1339 const bool isWindingRequested = (fillRule == Qt::WindingFill);
1340 if (d_func()->hasWindingFill == isWindingRequested)
1341 return;
1342 setDirty(true);
1343
1344 d_func()->hasWindingFill = isWindingRequested;
1345}
1346
1347#define QT_BEZIER_A(bezier, coord) 3 * (-bezier.coord##1
1348 + 3*bezier.coord##2
1349 - 3*bezier.coord##3
1350 +bezier.coord##4)
1351
1352#define QT_BEZIER_B(bezier, coord) 6 * (bezier.coord##1
1353 - 2*bezier.coord##2
1354 + bezier.coord##3)
1355
1356#define QT_BEZIER_C(bezier, coord) 3 * (- bezier.coord##1
1357 + bezier.coord##2)
1358
1359#define QT_BEZIER_CHECK_T(bezier, t)
1360 if (t >= 0 && t <= 1) {
1361 QPointF p(b.pointAt(t));
1362 if (p.x() < minx) minx = p.x();
1363 else if (p.x() > maxx) maxx = p.x();
1364 if (p.y() < miny) miny = p.y();
1365 else if (p.y() > maxy) maxy = p.y();
1366 }
1367
1368
1369static QRectF qt_painterpath_bezier_extrema(const QBezier &b)
1370{
1371 qreal minx, miny, maxx, maxy;
1372
1373 // initialize with end points
1374 if (b.x1 < b.x4) {
1375 minx = b.x1;
1376 maxx = b.x4;
1377 } else {
1378 minx = b.x4;
1379 maxx = b.x1;
1380 }
1381 if (b.y1 < b.y4) {
1382 miny = b.y1;
1383 maxy = b.y4;
1384 } else {
1385 miny = b.y4;
1386 maxy = b.y1;
1387 }
1388
1389 // Update for the X extrema
1390 {
1391 qreal ax = QT_BEZIER_A(b, x);
1392 qreal bx = QT_BEZIER_B(b, x);
1393 qreal cx = QT_BEZIER_C(b, x);
1394 // specialcase quadratic curves to avoid div by zero
1395 if (qFuzzyIsNull(ax)) {
1396
1397 // linear curves are covered by initialization.
1398 if (!qFuzzyIsNull(bx)) {
1399 qreal t = -cx / bx;
1400 QT_BEZIER_CHECK_T(b, t);
1401 }
1402
1403 } else {
1404 const qreal tx = bx * bx - 4 * ax * cx;
1405
1406 if (tx >= 0) {
1407 qreal temp = qSqrt(tx);
1408 qreal rcp = 1 / (2 * ax);
1409 qreal t1 = (-bx + temp) * rcp;
1410 QT_BEZIER_CHECK_T(b, t1);
1411
1412 qreal t2 = (-bx - temp) * rcp;
1413 QT_BEZIER_CHECK_T(b, t2);
1414 }
1415 }
1416 }
1417
1418 // Update for the Y extrema
1419 {
1420 qreal ay = QT_BEZIER_A(b, y);
1421 qreal by = QT_BEZIER_B(b, y);
1422 qreal cy = QT_BEZIER_C(b, y);
1423
1424 // specialcase quadratic curves to avoid div by zero
1425 if (qFuzzyIsNull(ay)) {
1426
1427 // linear curves are covered by initialization.
1428 if (!qFuzzyIsNull(by)) {
1429 qreal t = -cy / by;
1430 QT_BEZIER_CHECK_T(b, t);
1431 }
1432
1433 } else {
1434 const qreal ty = by * by - 4 * ay * cy;
1435
1436 if (ty > 0) {
1437 qreal temp = qSqrt(ty);
1438 qreal rcp = 1 / (2 * ay);
1439 qreal t1 = (-by + temp) * rcp;
1440 QT_BEZIER_CHECK_T(b, t1);
1441
1442 qreal t2 = (-by - temp) * rcp;
1443 QT_BEZIER_CHECK_T(b, t2);
1444 }
1445 }
1446 }
1447 return QRectF(minx, miny, maxx - minx, maxy - miny);
1448}
1449
1450/*!
1451 Returns the bounding rectangle of this painter path as a rectangle with
1452 floating point precision.
1453
1454 \sa controlPointRect()
1455*/
1456QRectF QPainterPath::boundingRect() const
1457{
1458 if (!d_ptr)
1459 return QRectF();
1460 QPainterPathPrivate *d = d_func();
1461
1462 if (d->dirtyBounds)
1463 computeBoundingRect();
1464 return d->bounds;
1465}
1466
1467/*!
1468 Returns the rectangle containing all the points and control points
1469 in this path.
1470
1471 This function is significantly faster to compute than the exact
1472 boundingRect(), and the returned rectangle is always a superset of
1473 the rectangle returned by boundingRect().
1474
1475 \sa boundingRect()
1476*/
1477QRectF QPainterPath::controlPointRect() const
1478{
1479 if (!d_ptr)
1480 return QRectF();
1481 QPainterPathPrivate *d = d_func();
1482
1483 if (d->dirtyControlBounds)
1484 computeControlPointRect();
1485 return d->controlBounds;
1486}
1487
1488
1489/*!
1490 \fn bool QPainterPath::isEmpty() const
1491
1492 Returns \c true if either there are no elements in this path, or if the only
1493 element is a MoveToElement; otherwise returns \c false.
1494
1495 \sa elementCount()
1496*/
1497
1498bool QPainterPath::isEmpty() const
1499{
1500 return !d_ptr || (d_ptr->elements.size() == 1 && d_ptr->elements.constFirst().type == MoveToElement);
1501}
1502
1503/*!
1504 Creates and returns a reversed copy of the path.
1505
1506 It is the order of the elements that is reversed: If a
1507 QPainterPath is composed by calling the moveTo(), lineTo() and
1508 cubicTo() functions in the specified order, the reversed copy is
1509 composed by calling cubicTo(), lineTo() and moveTo().
1510*/
1511QPainterPath QPainterPath::toReversed() const
1512{
1513 Q_D(const QPainterPath);
1514 QPainterPath rev;
1515
1516 if (isEmpty()) {
1517 rev = *this;
1518 return rev;
1519 }
1520
1521 rev.moveTo(d->elements.at(d->elements.size()-1).x, d->elements.at(d->elements.size()-1).y);
1522
1523 for (int i=d->elements.size()-1; i>=1; --i) {
1524 const QPainterPath::Element &elm = d->elements.at(i);
1525 const QPainterPath::Element &prev = d->elements.at(i-1);
1526 switch (elm.type) {
1527 case LineToElement:
1528 rev.lineTo(prev.x, prev.y);
1529 break;
1530 case MoveToElement:
1531 rev.moveTo(prev.x, prev.y);
1532 break;
1533 case CurveToDataElement:
1534 {
1535 Q_ASSERT(i>=3);
1536 const QPainterPath::Element &cp1 = d->elements.at(i-2);
1537 const QPainterPath::Element &sp = d->elements.at(i-3);
1538 Q_ASSERT(prev.type == CurveToDataElement);
1539 Q_ASSERT(cp1.type == CurveToElement);
1540 rev.cubicTo(prev.x, prev.y, cp1.x, cp1.y, sp.x, sp.y);
1541 i -= 2;
1542 break;
1543 }
1544 default:
1545 Q_ASSERT(!"qt_reversed_path");
1546 break;
1547 }
1548 }
1549 //qt_debug_path(rev);
1550 return rev;
1551}
1552
1553/*!
1554 Converts the path into a list of polygons using the QTransform
1555 \a matrix, and returns the list.
1556
1557 This function creates one polygon for each subpath regardless of
1558 intersecting subpaths (i.e. overlapping bounding rectangles). To
1559 make sure that such overlapping subpaths are filled correctly, use
1560 the toFillPolygons() function instead.
1561
1562 \sa toFillPolygons(), toFillPolygon(), {QPainterPath#QPainterPath
1563 Conversion}{QPainterPath Conversion}
1564*/
1565QList<QPolygonF> QPainterPath::toSubpathPolygons(const QTransform &matrix) const
1566{
1567
1568 Q_D(const QPainterPath);
1569 QList<QPolygonF> flatCurves;
1570 if (isEmpty())
1571 return flatCurves;
1572
1573 QPolygonF current;
1574 for (int i=0; i<elementCount(); ++i) {
1575 const QPainterPath::Element &e = d->elements.at(i);
1576 switch (e.type) {
1577 case QPainterPath::MoveToElement:
1578 if (current.size() > 1)
1579 flatCurves += current;
1580 current.clear();
1581 current.reserve(16);
1582 current += QPointF(e.x, e.y) * matrix;
1583 break;
1584 case QPainterPath::LineToElement:
1585 current += QPointF(e.x, e.y) * matrix;
1586 break;
1587 case QPainterPath::CurveToElement: {
1588 Q_ASSERT(d->elements.at(i+1).type == QPainterPath::CurveToDataElement);
1589 Q_ASSERT(d->elements.at(i+2).type == QPainterPath::CurveToDataElement);
1590 QBezier bezier = QBezier::fromPoints(QPointF(d->elements.at(i-1).x, d->elements.at(i-1).y) * matrix,
1591 QPointF(e.x, e.y) * matrix,
1592 QPointF(d->elements.at(i+1).x, d->elements.at(i+1).y) * matrix,
1593 QPointF(d->elements.at(i+2).x, d->elements.at(i+2).y) * matrix);
1594 bezier.addToPolygon(&current);
1595 i+=2;
1596 break;
1597 }
1598 case QPainterPath::CurveToDataElement:
1599 Q_ASSERT(!"QPainterPath::toSubpathPolygons(), bad element type");
1600 break;
1601 }
1602 }
1603
1604 if (current.size()>1)
1605 flatCurves += current;
1606
1607 return flatCurves;
1608}
1609
1610/*!
1611 Converts the path into a list of polygons using the
1612 QTransform \a matrix, and returns the list.
1613
1614 The function differs from the toFillPolygon() function in that it
1615 creates several polygons. It is provided because it is usually
1616 faster to draw several small polygons than to draw one large
1617 polygon, even though the total number of points drawn is the same.
1618
1619 The toFillPolygons() function differs from the toSubpathPolygons()
1620 function in that it create only polygon for subpaths that have
1621 overlapping bounding rectangles.
1622
1623 Like the toFillPolygon() function, this function uses a rewinding
1624 technique to make sure that overlapping subpaths can be filled
1625 using the correct fill rule. Note that rewinding inserts addition
1626 lines in the polygons so the outline of the fill polygon does not
1627 match the outline of the path.
1628
1629 \sa toSubpathPolygons(), toFillPolygon(),
1630 {QPainterPath#QPainterPath Conversion}{QPainterPath Conversion}
1631*/
1632QList<QPolygonF> QPainterPath::toFillPolygons(const QTransform &matrix) const
1633{
1634
1635 QList<QPolygonF> polys;
1636
1637 QList<QPolygonF> subpaths = toSubpathPolygons(matrix);
1638 int count = subpaths.size();
1639
1640 if (count == 0)
1641 return polys;
1642
1643 QList<QRectF> bounds;
1644 bounds.reserve(count);
1645 for (int i=0; i<count; ++i)
1646 bounds += subpaths.at(i).boundingRect();
1647
1648#ifdef QPP_FILLPOLYGONS_DEBUG
1649 printf("QPainterPath::toFillPolygons, subpathCount=%d\n", count);
1650 for (int i=0; i<bounds.size(); ++i)
1651 qDebug() << " bounds" << i << bounds.at(i);
1652#endif
1653
1654 QList< QList<int> > isects;
1655 isects.resize(count);
1656
1657 // find all intersections
1658 for (int j=0; j<count; ++j) {
1659 if (subpaths.at(j).size() <= 2)
1660 continue;
1661 QRectF cbounds = bounds.at(j);
1662 for (int i=0; i<count; ++i) {
1663 if (cbounds.intersects(bounds.at(i))) {
1664 isects[j] << i;
1665 }
1666 }
1667 }
1668
1669#ifdef QPP_FILLPOLYGONS_DEBUG
1670 printf("Intersections before flattening:\n");
1671 for (int i = 0; i < count; ++i) {
1672 printf("%d: ", i);
1673 for (int j = 0; j < isects[i].size(); ++j) {
1674 printf("%d ", isects[i][j]);
1675 }
1676 printf("\n");
1677 }
1678#endif
1679
1680 // flatten the sets of intersections
1681 for (int i=0; i<count; ++i) {
1682 const QList<int> &current_isects = isects.at(i);
1683 for (int j=0; j<current_isects.size(); ++j) {
1684 int isect_j = current_isects.at(j);
1685 if (isect_j == i)
1686 continue;
1687 const QList<int> &isects_j = isects.at(isect_j);
1688 for (int k = 0, size = isects_j.size(); k < size; ++k) {
1689 int isect_k = isects_j.at(k);
1690 if (isect_k != i && !isects.at(i).contains(isect_k)) {
1691 isects[i] += isect_k;
1692 }
1693 }
1694 isects[isect_j].clear();
1695 }
1696 }
1697
1698#ifdef QPP_FILLPOLYGONS_DEBUG
1699 printf("Intersections after flattening:\n");
1700 for (int i = 0; i < count; ++i) {
1701 printf("%d: ", i);
1702 for (int j = 0; j < isects[i].size(); ++j) {
1703 printf("%d ", isects[i][j]);
1704 }
1705 printf("\n");
1706 }
1707#endif
1708
1709 // Join the intersected subpaths as rewinded polygons
1710 for (int i=0; i<count; ++i) {
1711 const QList<int> &subpath_list = isects.at(i);
1712 if (!subpath_list.isEmpty()) {
1713 QPolygonF buildUp;
1714 for (int j=0; j<subpath_list.size(); ++j) {
1715 const QPolygonF &subpath = subpaths.at(subpath_list.at(j));
1716 buildUp += subpath;
1717 if (!subpath.isClosed())
1718 buildUp += subpath.first();
1719 if (!buildUp.isClosed())
1720 buildUp += buildUp.constFirst();
1721 }
1722 polys += buildUp;
1723 }
1724 }
1725
1726 return polys;
1727}
1728
1729//same as qt_polygon_isect_line in qpolygon.cpp
1730static void qt_painterpath_isect_line(const QPointF &p1,
1731 const QPointF &p2,
1732 const QPointF &pos,
1733 int *winding)
1734{
1735 qreal x1 = p1.x();
1736 qreal y1 = p1.y();
1737 qreal x2 = p2.x();
1738 qreal y2 = p2.y();
1739 qreal y = pos.y();
1740
1741 int dir = 1;
1742
1743 if (qFuzzyCompare(y1, y2)) {
1744 // ignore horizontal lines according to scan conversion rule
1745 return;
1746 } else if (y2 < y1) {
1747 qreal x_tmp = x2; x2 = x1; x1 = x_tmp;
1748 qreal y_tmp = y2; y2 = y1; y1 = y_tmp;
1749 dir = -1;
1750 }
1751
1752 if (y >= y1 && y < y2) {
1753 qreal x = x1 + ((x2 - x1) / (y2 - y1)) * (y - y1);
1754
1755 // count up the winding number if we're
1756 if (x<=pos.x()) {
1757 (*winding) += dir;
1758 }
1759 }
1760}
1761
1762static void qt_painterpath_isect_curve(const QBezier &bezier, const QPointF &pt,
1763 int *winding, int depth = 0)
1764{
1765 qreal y = pt.y();
1766 qreal x = pt.x();
1767 QRectF bounds = bezier.bounds();
1768
1769 // potential intersection, divide and try again...
1770 // Please note that a sideeffect of the bottom exclusion is that
1771 // horizontal lines are dropped, but this is correct according to
1772 // scan conversion rules.
1773 if (y >= bounds.y() && y < bounds.y() + bounds.height()) {
1774
1775 // hit lower limit... This is a rough threshold, but its a
1776 // tradeoff between speed and precision.
1777 const qreal lower_bound = qreal(.001);
1778 if (depth == 32 || (bounds.width() < lower_bound && bounds.height() < lower_bound)) {
1779 // We make the assumption here that the curve starts to
1780 // approximate a line after while (i.e. that it doesn't
1781 // change direction drastically during its slope)
1782 if (bezier.pt1().x() <= x) {
1783 (*winding) += (bezier.pt4().y() > bezier.pt1().y() ? 1 : -1);
1784 }
1785 return;
1786 }
1787
1788 // split curve and try again...
1789 const auto halves = bezier.split();
1790 qt_painterpath_isect_curve(halves.first, pt, winding, depth + 1);
1791 qt_painterpath_isect_curve(halves.second, pt, winding, depth + 1);
1792 }
1793}
1794
1795/*!
1796 \fn bool QPainterPath::contains(const QPointF &point) const
1797
1798 Returns \c true if the given \a point is inside the path, otherwise
1799 returns \c false.
1800
1801 \sa intersects()
1802*/
1803bool QPainterPath::contains(const QPointF &pt) const
1804{
1805 if (isEmpty() || !controlPointRect().contains(pt))
1806 return false;
1807
1808 QPainterPathPrivate *d = d_func();
1809
1810 int winding_number = 0;
1811
1812 QPointF last_pt;
1813 QPointF last_start;
1814 for (int i=0; i<d->elements.size(); ++i) {
1815 const Element &e = d->elements.at(i);
1816
1817 switch (e.type) {
1818
1819 case MoveToElement:
1820 if (i > 0) // implicitly close all paths.
1821 qt_painterpath_isect_line(last_pt, last_start, pt, &winding_number);
1822 last_start = last_pt = e;
1823 break;
1824
1825 case LineToElement:
1826 qt_painterpath_isect_line(last_pt, e, pt, &winding_number);
1827 last_pt = e;
1828 break;
1829
1830 case CurveToElement:
1831 {
1832 const QPainterPath::Element &cp2 = d->elements.at(++i);
1833 const QPainterPath::Element &ep = d->elements.at(++i);
1834 qt_painterpath_isect_curve(QBezier::fromPoints(last_pt, e, cp2, ep),
1835 pt, &winding_number);
1836 last_pt = ep;
1837
1838 }
1839 break;
1840
1841 default:
1842 break;
1843 }
1844 }
1845
1846 // implicitly close last subpath
1847 if (last_pt != last_start)
1848 qt_painterpath_isect_line(last_pt, last_start, pt, &winding_number);
1849
1850 return (d->hasWindingFill
1851 ? (winding_number != 0)
1852 : ((winding_number % 2) != 0));
1853}
1854
1856
1857static bool qt_painterpath_isect_line_rect(qreal x1, qreal y1, qreal x2, qreal y2,
1858 const QRectF &rect)
1859{
1860 qreal left = rect.left();
1861 qreal right = rect.right();
1862 qreal top = rect.top();
1863 qreal bottom = rect.bottom();
1864
1865 // clip the lines, after cohen-sutherland, see e.g. http://www.nondot.org/~sabre/graphpro/line6.html
1866 int p1 = ((x1 < left) << Left)
1867 | ((x1 > right) << Right)
1868 | ((y1 < top) << Top)
1869 | ((y1 > bottom) << Bottom);
1870 int p2 = ((x2 < left) << Left)
1871 | ((x2 > right) << Right)
1872 | ((y2 < top) << Top)
1873 | ((y2 > bottom) << Bottom);
1874
1875 if (p1 & p2)
1876 // completely inside
1877 return false;
1878
1879 if (p1 | p2) {
1880 qreal dx = x2 - x1;
1881 qreal dy = y2 - y1;
1882
1883 // clip x coordinates
1884 if (x1 < left) {
1885 y1 += dy/dx * (left - x1);
1886 x1 = left;
1887 } else if (x1 > right) {
1888 y1 -= dy/dx * (x1 - right);
1889 x1 = right;
1890 }
1891 if (x2 < left) {
1892 y2 += dy/dx * (left - x2);
1893 x2 = left;
1894 } else if (x2 > right) {
1895 y2 -= dy/dx * (x2 - right);
1896 x2 = right;
1897 }
1898
1899 p1 = ((y1 < top) << Top)
1900 | ((y1 > bottom) << Bottom);
1901 p2 = ((y2 < top) << Top)
1902 | ((y2 > bottom) << Bottom);
1903
1904 if (p1 & p2)
1905 return false;
1906
1907 // clip y coordinates
1908 if (y1 < top) {
1909 x1 += dx/dy * (top - y1);
1910 y1 = top;
1911 } else if (y1 > bottom) {
1912 x1 -= dx/dy * (y1 - bottom);
1913 y1 = bottom;
1914 }
1915 if (y2 < top) {
1916 x2 += dx/dy * (top - y2);
1917 y2 = top;
1918 } else if (y2 > bottom) {
1919 x2 -= dx/dy * (y2 - bottom);
1920 y2 = bottom;
1921 }
1922
1923 p1 = ((x1 < left) << Left)
1924 | ((x1 > right) << Right);
1925 p2 = ((x2 < left) << Left)
1926 | ((x2 > right) << Right);
1927
1928 if (p1 & p2)
1929 return false;
1930
1931 return true;
1932 }
1933 return false;
1934}
1935
1936static bool qt_isect_curve_horizontal(const QBezier &bezier, qreal y, qreal x1, qreal x2, int depth = 0)
1937{
1938 QRectF bounds = bezier.bounds();
1939
1940 if (y >= bounds.top() && y < bounds.bottom()
1941 && bounds.right() >= x1 && bounds.left() < x2) {
1942 const qreal lower_bound = qreal(.01);
1943 if (depth == 32 || (bounds.width() < lower_bound && bounds.height() < lower_bound))
1944 return true;
1945
1946 const auto halves = bezier.split();
1947 if (qt_isect_curve_horizontal(halves.first, y, x1, x2, depth + 1)
1948 || qt_isect_curve_horizontal(halves.second, y, x1, x2, depth + 1))
1949 return true;
1950 }
1951 return false;
1952}
1953
1954static bool qt_isect_curve_vertical(const QBezier &bezier, qreal x, qreal y1, qreal y2, int depth = 0)
1955{
1956 QRectF bounds = bezier.bounds();
1957
1958 if (x >= bounds.left() && x < bounds.right()
1959 && bounds.bottom() >= y1 && bounds.top() < y2) {
1960 const qreal lower_bound = qreal(.01);
1961 if (depth == 32 || (bounds.width() < lower_bound && bounds.height() < lower_bound))
1962 return true;
1963
1964 const auto halves = bezier.split();
1965 if (qt_isect_curve_vertical(halves.first, x, y1, y2, depth + 1)
1966 || qt_isect_curve_vertical(halves.second, x, y1, y2, depth + 1))
1967 return true;
1968 }
1969 return false;
1970}
1971
1972static bool pointOnEdge(const QRectF &rect, const QPointF &point)
1973{
1974 if ((point.x() == rect.left() || point.x() == rect.right()) &&
1975 (point.y() >= rect.top() && point.y() <= rect.bottom()))
1976 return true;
1977 if ((point.y() == rect.top() || point.y() == rect.bottom()) &&
1978 (point.x() >= rect.left() && point.x() <= rect.right()))
1979 return true;
1980 return false;
1981}
1982
1983/*
1984 Returns \c true if any lines or curves cross the four edges in of rect
1985*/
1986static bool qt_painterpath_check_crossing(const QPainterPath *path, const QRectF &rect)
1987{
1988 QPointF last_pt;
1989 QPointF last_start;
1990 enum { OnRect, InsideRect, OutsideRect} edgeStatus = OnRect;
1991 for (int i=0; i<path->elementCount(); ++i) {
1992 const QPainterPath::Element &e = path->elementAt(i);
1993
1994 switch (e.type) {
1995
1996 case QPainterPath::MoveToElement:
1997 if (i > 0
1998 && qFuzzyCompare(last_pt.x(), last_start.x())
1999 && qFuzzyCompare(last_pt.y(), last_start.y())
2000 && qt_painterpath_isect_line_rect(last_pt.x(), last_pt.y(),
2001 last_start.x(), last_start.y(), rect))
2002 return true;
2003 last_start = last_pt = e;
2004 break;
2005
2006 case QPainterPath::LineToElement:
2007 if (qt_painterpath_isect_line_rect(last_pt.x(), last_pt.y(), e.x, e.y, rect))
2008 return true;
2009 last_pt = e;
2010 break;
2011
2012 case QPainterPath::CurveToElement:
2013 {
2014 QPointF cp2 = path->elementAt(++i);
2015 QPointF ep = path->elementAt(++i);
2016 QBezier bezier = QBezier::fromPoints(last_pt, e, cp2, ep);
2017 if (qt_isect_curve_horizontal(bezier, rect.top(), rect.left(), rect.right())
2018 || qt_isect_curve_horizontal(bezier, rect.bottom(), rect.left(), rect.right())
2019 || qt_isect_curve_vertical(bezier, rect.left(), rect.top(), rect.bottom())
2020 || qt_isect_curve_vertical(bezier, rect.right(), rect.top(), rect.bottom()))
2021 return true;
2022 last_pt = ep;
2023 }
2024 break;
2025
2026 default:
2027 break;
2028 }
2029 // Handle crossing the edges of the rect at the end-points of individual sub-paths.
2030 // A point on on the edge itself is considered neither inside nor outside for this purpose.
2031 if (!pointOnEdge(rect, last_pt)) {
2032 bool contained = rect.contains(last_pt);
2033 switch (edgeStatus) {
2034 case OutsideRect:
2035 if (contained)
2036 return true;
2037 break;
2038 case InsideRect:
2039 if (!contained)
2040 return true;
2041 break;
2042 case OnRect:
2043 edgeStatus = contained ? InsideRect : OutsideRect;
2044 break;
2045 }
2046 } else {
2047 if (last_pt == last_start)
2048 edgeStatus = OnRect;
2049 }
2050 }
2051
2052 // implicitly close last subpath
2053 if (last_pt != last_start
2054 && qt_painterpath_isect_line_rect(last_pt.x(), last_pt.y(),
2055 last_start.x(), last_start.y(), rect))
2056 return true;
2057
2058 return false;
2059}
2060
2061/*!
2062 \fn bool QPainterPath::intersects(const QRectF &rectangle) const
2063
2064 Returns \c true if any point in the given \a rectangle intersects the
2065 path; otherwise returns \c false.
2066
2067 There is an intersection if any of the lines making up the
2068 rectangle crosses a part of the path or if any part of the
2069 rectangle overlaps with any area enclosed by the path. This
2070 function respects the current fillRule to determine what is
2071 considered inside the path.
2072
2073 \sa contains()
2074*/
2075bool QPainterPath::intersects(const QRectF &rect) const
2076{
2077 if (elementCount() == 1 && rect.contains(elementAt(0)))
2078 return true;
2079
2080 if (isEmpty())
2081 return false;
2082
2083 QRectF cp = controlPointRect();
2084 QRectF rn = rect.normalized();
2085
2086 // QRectF::intersects returns false if one of the rects is a null rect
2087 // which would happen for a painter path consisting of a vertical or
2088 // horizontal line
2089 if (qMax(rn.left(), cp.left()) > qMin(rn.right(), cp.right())
2090 || qMax(rn.top(), cp.top()) > qMin(rn.bottom(), cp.bottom()))
2091 return false;
2092
2093 // If any path element cross the rect its bound to be an intersection
2094 if (qt_painterpath_check_crossing(this, rect))
2095 return true;
2096
2097 if (contains(rect.center()))
2098 return true;
2099
2100 Q_D(QPainterPath);
2101
2102 // Check if the rectangle surrounds any subpath...
2103 for (int i=0; i<d->elements.size(); ++i) {
2104 const Element &e = d->elements.at(i);
2105 if (e.type == QPainterPath::MoveToElement && rect.contains(e))
2106 return true;
2107 }
2108
2109 return false;
2110}
2111
2112/*!
2113 Translates all elements in the path by (\a{dx}, \a{dy}).
2114
2115 \since 4.6
2116 \sa translated()
2117*/
2118void QPainterPath::translate(qreal dx, qreal dy)
2119{
2120 if (!d_ptr || (dx == 0 && dy == 0))
2121 return;
2122
2123 int elementsLeft = d_ptr->elements.size();
2124 if (elementsLeft <= 0)
2125 return;
2126
2127 setDirty(true);
2128 QPainterPath::Element *element = d_func()->elements.data();
2129 Q_ASSERT(element);
2130 while (elementsLeft--) {
2131 element->x += dx;
2132 element->y += dy;
2133 ++element;
2134 }
2135}
2136
2137/*!
2138 \fn void QPainterPath::translate(const QPointF &offset)
2139 \overload
2140 \since 4.6
2141
2142 Translates all elements in the path by the given \a offset.
2143
2144 \sa translated()
2145*/
2146
2147/*!
2148 Returns a copy of the path that is translated by (\a{dx}, \a{dy}).
2149
2150 \since 4.6
2151 \sa translate()
2152*/
2153QPainterPath QPainterPath::translated(qreal dx, qreal dy) const
2154{
2155 QPainterPath copy(*this);
2156 copy.translate(dx, dy);
2157 return copy;
2158}
2159
2160/*!
2161 \fn QPainterPath QPainterPath::translated(const QPointF &offset) const;
2162 \overload
2163 \since 4.6
2164
2165 Returns a copy of the path that is translated by the given \a offset.
2166
2167 \sa translate()
2168*/
2169
2170/*!
2171 \fn bool QPainterPath::contains(const QRectF &rectangle) const
2172
2173 Returns \c true if the given \a rectangle is inside the path,
2174 otherwise returns \c false.
2175*/
2176bool QPainterPath::contains(const QRectF &rect) const
2177{
2178 Q_D(QPainterPath);
2179
2180 // the path is empty or the control point rect doesn't completely
2181 // cover the rectangle we abort stratight away.
2182 if (isEmpty() || !controlPointRect().contains(rect))
2183 return false;
2184
2185 // if there are intersections, chances are that the rect is not
2186 // contained, except if we have winding rule, in which case it
2187 // still might.
2188 if (qt_painterpath_check_crossing(this, rect)) {
2189 if (fillRule() == Qt::OddEvenFill) {
2190 return false;
2191 } else {
2192 // Do some wague sampling in the winding case. This is not
2193 // precise but it should mostly be good enough.
2194 if (!contains(rect.topLeft()) ||
2195 !contains(rect.topRight()) ||
2196 !contains(rect.bottomRight()) ||
2197 !contains(rect.bottomLeft()))
2198 return false;
2199 }
2200 }
2201
2202 // If there exists a point inside that is not part of the path its
2203 // because: rectangle lies completely outside path or a subpath
2204 // excludes parts of the rectangle. Both cases mean that the rect
2205 // is not contained
2206 if (!contains(rect.center()))
2207 return false;
2208
2209 // If there are any subpaths inside this rectangle we need to
2210 // check if they are still contained as a result of the fill
2211 // rule. This can only be the case for WindingFill though. For
2212 // OddEvenFill the rect will never be contained if it surrounds a
2213 // subpath. (the case where two subpaths are completely identical
2214 // can be argued but we choose to neglect it).
2215 for (int i=0; i<d->elements.size(); ++i) {
2216 const Element &e = d->elements.at(i);
2217 if (e.type == QPainterPath::MoveToElement && rect.contains(e)) {
2218 if (fillRule() == Qt::OddEvenFill)
2219 return false;
2220
2221 bool stop = false;
2222 for (; !stop && i<d->elements.size(); ++i) {
2223 const Element &el = d->elements.at(i);
2224 switch (el.type) {
2225 case MoveToElement:
2226 stop = true;
2227 break;
2228 case LineToElement:
2229 if (!contains(el))
2230 return false;
2231 break;
2232 case CurveToElement:
2233 if (!contains(d->elements.at(i+2)))
2234 return false;
2235 i += 2;
2236 break;
2237 default:
2238 break;
2239 }
2240 }
2241
2242 // compensate for the last ++i in the inner for
2243 --i;
2244 }
2245 }
2246
2247 return true;
2248}
2249
2250static inline bool epsilonCompare(const QPointF &a, const QPointF &b, const QSizeF &epsilon)
2251{
2252 return qAbs(a.x() - b.x()) <= epsilon.width()
2253 && qAbs(a.y() - b.y()) <= epsilon.height();
2254}
2255
2256/*!
2257 Returns \c true if this painterpath is equal to the given \a path.
2258
2259 Note that comparing paths may involve a per element comparison
2260 which can be slow for complex paths.
2261
2262 \sa operator!=()
2263*/
2264
2265bool QPainterPath::operator==(const QPainterPath &path) const
2266{
2267 QPainterPathPrivate *d = d_func();
2268 QPainterPathPrivate *other_d = path.d_func();
2269 if (other_d == d) {
2270 return true;
2271 } else if (!d || !other_d) {
2272 if (!other_d && isEmpty() && elementAt(0) == QPointF() && !d->hasWindingFill)
2273 return true;
2274 if (!d && path.isEmpty() && path.elementAt(0) == QPointF() && !other_d->hasWindingFill)
2275 return true;
2276 return false;
2277 }
2278 else if (d->hasWindingFill != other_d->hasWindingFill)
2279 return false;
2280 else if (d->elements.size() != other_d->elements.size())
2281 return false;
2282
2283 const qreal qt_epsilon = sizeof(qreal) == sizeof(double) ? 1e-12 : qreal(1e-5);
2284
2285 QSizeF epsilon = boundingRect().size();
2286 epsilon.rwidth() *= qt_epsilon;
2287 epsilon.rheight() *= qt_epsilon;
2288
2289 for (int i = 0; i < d->elements.size(); ++i)
2290 if (d->elements.at(i).type != other_d->elements.at(i).type
2291 || !epsilonCompare(d->elements.at(i), other_d->elements.at(i), epsilon))
2292 return false;
2293
2294 return true;
2295}
2296
2297/*!
2298 Returns \c true if this painter path differs from the given \a path.
2299
2300 Note that comparing paths may involve a per element comparison
2301 which can be slow for complex paths.
2302
2303 \sa operator==()
2304*/
2305
2306bool QPainterPath::operator!=(const QPainterPath &path) const
2307{
2308 return !(*this==path);
2309}
2310
2311/*!
2312 \since 4.5
2313
2314 Returns the intersection of this path and the \a other path.
2315
2316 \sa intersected(), operator&=(), united(), operator|()
2317*/
2318QPainterPath QPainterPath::operator&(const QPainterPath &other) const
2319{
2320 return intersected(other);
2321}
2322
2323/*!
2324 \since 4.5
2325
2326 Returns the union of this path and the \a other path.
2327
2328 \sa united(), operator|=(), intersected(), operator&()
2329*/
2330QPainterPath QPainterPath::operator|(const QPainterPath &other) const
2331{
2332 return united(other);
2333}
2334
2335/*!
2336 \since 4.5
2337
2338 Returns the union of this path and the \a other path. This function is equivalent
2339 to operator|().
2340
2341 \sa united(), operator+=(), operator-()
2342*/
2343QPainterPath QPainterPath::operator+(const QPainterPath &other) const
2344{
2345 return united(other);
2346}
2347
2348/*!
2349 \since 4.5
2350
2351 Subtracts the \a other path from a copy of this path, and returns the copy.
2352
2353 \sa subtracted(), operator-=(), operator+()
2354*/
2355QPainterPath QPainterPath::operator-(const QPainterPath &other) const
2356{
2357 return subtracted(other);
2358}
2359
2360/*!
2361 \since 4.5
2362
2363 Intersects this path with \a other and returns a reference to this path.
2364
2365 \sa intersected(), operator&(), operator|=()
2366*/
2367QPainterPath &QPainterPath::operator&=(const QPainterPath &other)
2368{
2369 return *this = (*this & other);
2370}
2371
2372/*!
2373 \since 4.5
2374
2375 Unites this path with \a other and returns a reference to this path.
2376
2377 \sa united(), operator|(), operator&=()
2378*/
2379QPainterPath &QPainterPath::operator|=(const QPainterPath &other)
2380{
2381 return *this = (*this | other);
2382}
2383
2384/*!
2385 \since 4.5
2386
2387 Unites this path with \a other, and returns a reference to this path. This
2388 is equivalent to operator|=().
2389
2390 \sa united(), operator+(), operator-=()
2391*/
2392QPainterPath &QPainterPath::operator+=(const QPainterPath &other)
2393{
2394 return *this = (*this + other);
2395}
2396
2397/*!
2398 \since 4.5
2399
2400 Subtracts \a other from this path, and returns a reference to this
2401 path.
2402
2403 \sa subtracted(), operator-(), operator+=()
2404*/
2405QPainterPath &QPainterPath::operator-=(const QPainterPath &other)
2406{
2407 return *this = (*this - other);
2408}
2409
2410#ifndef QT_NO_DATASTREAM
2411/*!
2412 \fn QDataStream &operator<<(QDataStream &stream, const QPainterPath &path)
2413 \relates QPainterPath
2414
2415 Writes the given painter \a path to the given \a stream, and
2416 returns a reference to the \a stream.
2417
2418 \sa {Serializing Qt Data Types}
2419*/
2420QDataStream &operator<<(QDataStream &s, const QPainterPath &p)
2421{
2422 if (p.isEmpty()) {
2423 s << 0;
2424 return s;
2425 }
2426
2427 s << p.elementCount();
2428 for (int i=0; i < p.d_func()->elements.size(); ++i) {
2429 const QPainterPath::Element &e = p.d_func()->elements.at(i);
2430 s << int(e.type);
2431 s << double(e.x) << double(e.y);
2432 }
2433 s << p.d_func()->cStart;
2434 s << int(p.fillRule());
2435 return s;
2436}
2437
2438/*!
2439 \fn QDataStream &operator>>(QDataStream &stream, QPainterPath &path)
2440 \relates QPainterPath
2441
2442 Reads a painter path from the given \a stream into the specified \a path,
2443 and returns a reference to the \a stream.
2444
2445 \sa {Serializing Qt Data Types}
2446*/
2447QDataStream &operator>>(QDataStream &s, QPainterPath &p)
2448{
2449 bool errorDetected = false;
2450 int size;
2451 s >> size;
2452
2453 if (size == 0) {
2454 p = {};
2455 return s;
2456 }
2457
2458 p.ensureData(); // in case if p.d_func() == 0
2459 p.setDirty(true);
2460 p.d_func()->elements.clear();
2461 for (int i=0; i<size; ++i) {
2462 int type;
2463 double x, y;
2464 s >> type;
2465 s >> x;
2466 s >> y;
2467 Q_ASSERT(type >= 0 && type <= 3);
2468 if (!isValidCoord(qreal(x)) || !isValidCoord(qreal(y))) {
2469#ifndef QT_NO_DEBUG
2470 qWarning("QDataStream::operator>>: Invalid QPainterPath coordinates read, skipping it");
2471#endif
2472 errorDetected = true;
2473 continue;
2474 }
2475 QPainterPath::Element elm = { qreal(x), qreal(y), QPainterPath::ElementType(type) };
2476 p.d_func()->elements.append(elm);
2477 }
2478 s >> p.d_func()->cStart;
2479 int fillRule;
2480 s >> fillRule;
2481 Q_ASSERT(fillRule == Qt::OddEvenFill || fillRule == Qt::WindingFill);
2482 p.d_func()->hasWindingFill = (Qt::FillRule(fillRule) == Qt::WindingFill);
2483 if (errorDetected || p.d_func()->elements.isEmpty())
2484 p = QPainterPath(); // Better than to return path with possibly corrupt datastructure, which would likely cause crash
2485 return s;
2486}
2487#endif // QT_NO_DATASTREAM
2488
2489
2490/*******************************************************************************
2491 * class QPainterPathStroker
2492 */
2493
2494void qt_path_stroke_move_to(qfixed x, qfixed y, void *data)
2495{
2496 ((QPainterPath *) data)->moveTo(qt_fixed_to_real(x), qt_fixed_to_real(y));
2497}
2498
2499void qt_path_stroke_line_to(qfixed x, qfixed y, void *data)
2500{
2501 ((QPainterPath *) data)->lineTo(qt_fixed_to_real(x), qt_fixed_to_real(y));
2502}
2503
2504void qt_path_stroke_cubic_to(qfixed c1x, qfixed c1y,
2505 qfixed c2x, qfixed c2y,
2506 qfixed ex, qfixed ey,
2507 void *data)
2508{
2509 ((QPainterPath *) data)->cubicTo(qt_fixed_to_real(c1x), qt_fixed_to_real(c1y),
2510 qt_fixed_to_real(c2x), qt_fixed_to_real(c2y),
2511 qt_fixed_to_real(ex), qt_fixed_to_real(ey));
2512}
2513
2514/*!
2515 \since 4.1
2516 \class QPainterPathStroker
2517 \ingroup painting
2518 \inmodule QtGui
2519
2520 \brief The QPainterPathStroker class is used to generate fillable
2521 outlines for a given painter path.
2522
2523 By calling the createStroke() function, passing a given
2524 QPainterPath as argument, a new painter path representing the
2525 outline of the given path is created. The newly created painter
2526 path can then be filled to draw the original painter path's
2527 outline.
2528
2529 You can control the various design aspects (width, cap styles,
2530 join styles and dash pattern) of the outlining using the following
2531 functions:
2532
2533 \list
2534 \li setWidth()
2535 \li setCapStyle()
2536 \li setJoinStyle()
2537 \li setDashPattern()
2538 \endlist
2539
2540 The setDashPattern() function accepts both a Qt::PenStyle object
2541 and a list representation of the pattern as argument.
2542
2543 In addition you can specify a curve's threshold, controlling the
2544 granularity with which a curve is drawn, using the
2545 setCurveThreshold() function. The default threshold is a well
2546 adjusted value (0.25), and normally you should not need to modify
2547 it. However, you can make the curve's appearance smoother by
2548 decreasing its value.
2549
2550 You can also control the miter limit for the generated outline
2551 using the setMiterLimit() function. The miter limit describes how
2552 far from each join the miter join can extend. The limit is
2553 specified in the units of width so the pixelwise miter limit will
2554 be \c {miterlimit * width}. This value is only used if the join
2555 style is Qt::MiterJoin.
2556
2557 The painter path generated by the createStroke() function should
2558 only be used for outlining the given painter path. Otherwise it
2559 may cause unexpected behavior. Generated outlines also require the
2560 Qt::WindingFill rule which is set by default.
2561
2562 \sa QPen, QBrush
2563*/
2564
2566 : dashOffset(0)
2567{
2568 stroker.setMoveToHook(qt_path_stroke_move_to);
2569 stroker.setLineToHook(qt_path_stroke_line_to);
2570 stroker.setCubicToHook(qt_path_stroke_cubic_to);
2571}
2572
2573/*!
2574 Creates a new stroker.
2575 */
2576QPainterPathStroker::QPainterPathStroker()
2577 : d_ptr(new QPainterPathStrokerPrivate)
2578{
2579}
2580
2581/*!
2582 Creates a new stroker based on \a pen.
2583
2584 \since 5.3
2585 */
2586QPainterPathStroker::QPainterPathStroker(const QPen &pen)
2587 : d_ptr(new QPainterPathStrokerPrivate)
2588{
2589 setWidth(pen.widthF());
2590 setCapStyle(pen.capStyle());
2591 setJoinStyle(pen.joinStyle());
2592 setMiterLimit(pen.miterLimit());
2593 setDashOffset(pen.dashOffset());
2594
2595 if (pen.style() == Qt::CustomDashLine)
2596 setDashPattern(pen.dashPattern());
2597 else
2598 setDashPattern(pen.style());
2599}
2600
2601/*!
2602 Destroys the stroker.
2603*/
2604QPainterPathStroker::~QPainterPathStroker()
2605{
2606}
2607
2608
2609/*!
2610 Generates a new path that is a fillable area representing the
2611 outline of the given \a path.
2612
2613 The various design aspects of the outline are based on the
2614 stroker's properties: width(), capStyle(), joinStyle(),
2615 dashPattern(), curveThreshold() and miterLimit().
2616
2617 The generated path should only be used for outlining the given
2618 painter path. Otherwise it may cause unexpected
2619 behavior. Generated outlines also require the Qt::WindingFill rule
2620 which is set by default.
2621*/
2622QPainterPath QPainterPathStroker::createStroke(const QPainterPath &path) const
2623{
2624 QPainterPathStrokerPrivate *d = const_cast<QPainterPathStrokerPrivate *>(d_func());
2625 QPainterPath stroke;
2626 if (path.isEmpty())
2627 return path;
2628 if (d->dashPattern.isEmpty()) {
2629 d->stroker.strokePath(path, &stroke, QTransform());
2630 } else {
2631 QDashStroker dashStroker(&d->stroker);
2632 dashStroker.setDashPattern(d->dashPattern);
2633 dashStroker.setDashOffset(d->dashOffset);
2634 dashStroker.setClipRect(d->stroker.clipRect());
2635 dashStroker.strokePath(path, &stroke, QTransform());
2636 }
2637 stroke.setFillRule(Qt::WindingFill);
2638 return stroke;
2639}
2640
2641/*!
2642 Sets the width of the generated outline painter path to \a width.
2643
2644 The generated outlines will extend approximately 50% of \a width
2645 to each side of the given input path's original outline.
2646*/
2647void QPainterPathStroker::setWidth(qreal width)
2648{
2649 Q_D(QPainterPathStroker);
2650 if (width <= 0)
2651 width = 1;
2652 d->stroker.setStrokeWidth(qt_real_to_fixed(width));
2653}
2654
2655/*!
2656 Returns the width of the generated outlines.
2657*/
2658qreal QPainterPathStroker::width() const
2659{
2660 return qt_fixed_to_real(d_func()->stroker.strokeWidth());
2661}
2662
2663
2664/*!
2665 Sets the cap style of the generated outlines to \a style. If a
2666 dash pattern is set, each segment of the pattern is subject to the
2667 cap \a style.
2668*/
2669void QPainterPathStroker::setCapStyle(Qt::PenCapStyle style)
2670{
2671 d_func()->stroker.setCapStyle(style);
2672}
2673
2674
2675/*!
2676 Returns the cap style of the generated outlines.
2677*/
2678Qt::PenCapStyle QPainterPathStroker::capStyle() const
2679{
2680 return d_func()->stroker.capStyle();
2681}
2682
2683/*!
2684 Sets the join style of the generated outlines to \a style.
2685*/
2686void QPainterPathStroker::setJoinStyle(Qt::PenJoinStyle style)
2687{
2688 d_func()->stroker.setJoinStyle(style);
2689}
2690
2691/*!
2692 Returns the join style of the generated outlines.
2693*/
2694Qt::PenJoinStyle QPainterPathStroker::joinStyle() const
2695{
2696 return d_func()->stroker.joinStyle();
2697}
2698
2699/*!
2700 Sets the miter limit of the generated outlines to \a limit.
2701
2702 The miter limit describes how far from each join the miter join
2703 can extend. The limit is specified in units of the currently set
2704 width. So the pixelwise miter limit will be \c { miterlimit *
2705 width}.
2706
2707 This value is only used if the join style is Qt::MiterJoin.
2708*/
2709void QPainterPathStroker::setMiterLimit(qreal limit)
2710{
2711 d_func()->stroker.setMiterLimit(qt_real_to_fixed(limit));
2712}
2713
2714/*!
2715 Returns the miter limit for the generated outlines.
2716*/
2717qreal QPainterPathStroker::miterLimit() const
2718{
2719 return qt_fixed_to_real(d_func()->stroker.miterLimit());
2720}
2721
2722
2723/*!
2724 Specifies the curve flattening \a threshold, controlling the
2725 granularity with which the generated outlines' curve is drawn.
2726
2727 The default threshold is a well adjusted value (0.25), and
2728 normally you should not need to modify it. However, you can make
2729 the curve's appearance smoother by decreasing its value.
2730*/
2731void QPainterPathStroker::setCurveThreshold(qreal threshold)
2732{
2733 d_func()->stroker.setCurveThreshold(qt_real_to_fixed(threshold));
2734}
2735
2736/*!
2737 Returns the curve flattening threshold for the generated
2738 outlines.
2739*/
2740qreal QPainterPathStroker::curveThreshold() const
2741{
2742 return qt_fixed_to_real(d_func()->stroker.curveThreshold());
2743}
2744
2745/*!
2746 Sets the dash pattern for the generated outlines to \a style.
2747*/
2748void QPainterPathStroker::setDashPattern(Qt::PenStyle style)
2749{
2750 d_func()->dashPattern = QDashStroker::patternForStyle(style);
2751}
2752
2753/*!
2754 \overload
2755
2756 Sets the dash pattern for the generated outlines to \a
2757 dashPattern. This function makes it possible to specify custom
2758 dash patterns.
2759
2760 Each element in the list contains the lengths of the dashes and spaces
2761 in the stroke, beginning with the first dash in the first element, the
2762 first space in the second element, and alternating between dashes and
2763 spaces for each following pair of elements.
2764
2765 The list can contain an odd number of elements, in which case the last
2766 element will be extended by the length of the first element when the
2767 pattern repeats.
2768*/
2769void QPainterPathStroker::setDashPattern(const QList<qreal> &dashPattern)
2770{
2771 d_func()->dashPattern.clear();
2772 for (int i=0; i<dashPattern.size(); ++i)
2773 d_func()->dashPattern << qt_real_to_fixed(dashPattern.at(i));
2774}
2775
2776/*!
2777 Returns the dash pattern for the generated outlines.
2778*/
2779QList<qreal> QPainterPathStroker::dashPattern() const
2780{
2781 return d_func()->dashPattern;
2782}
2783
2784/*!
2785 Returns the dash offset for the generated outlines.
2786 */
2787qreal QPainterPathStroker::dashOffset() const
2788{
2789 return d_func()->dashOffset;
2790}
2791
2792/*!
2793 Sets the dash offset for the generated outlines to \a offset.
2794
2795 See the documentation for QPen::setDashOffset() for a description of the
2796 dash offset.
2797 */
2798void QPainterPathStroker::setDashOffset(qreal offset)
2799{
2800 d_func()->dashOffset = offset;
2801}
2802
2803/*!
2804 Converts the path into a polygon using the QTransform
2805 \a matrix, and returns the polygon.
2806
2807 The polygon is created by first converting all subpaths to
2808 polygons, then using a rewinding technique to make sure that
2809 overlapping subpaths can be filled using the correct fill rule.
2810
2811 Note that rewinding inserts addition lines in the polygon so
2812 the outline of the fill polygon does not match the outline of
2813 the path.
2814
2815 \sa toSubpathPolygons(), toFillPolygons(),
2816 {QPainterPath#QPainterPath Conversion}{QPainterPath Conversion}
2817*/
2818QPolygonF QPainterPath::toFillPolygon(const QTransform &matrix) const
2819{
2820 const QList<QPolygonF> flats = toSubpathPolygons(matrix);
2821 QPolygonF polygon;
2822 if (flats.isEmpty())
2823 return polygon;
2824 QPointF first = flats.first().first();
2825 for (int i=0; i<flats.size(); ++i) {
2826 polygon += flats.at(i);
2827 if (!flats.at(i).isClosed())
2828 polygon += flats.at(i).first();
2829 if (i > 0)
2830 polygon += first;
2831 }
2832 return polygon;
2833}
2834
2835/*!
2836 Returns true if caching is enabled; otherwise returns false.
2837
2838 \since 6.10
2839 \sa setCachingEnabled()
2840*/
2841bool QPainterPath::isCachingEnabled() const
2842{
2843 Q_D(QPainterPath);
2844 return d && d->cacheEnabled;
2845}
2846
2847/*!
2848 Enables or disables length caching according to the value of \a enabled.
2849
2850 Enabling caching speeds up repeated calls to the member functions involving path length
2851 and percentage values, such as length(), percentAtLength(), pointAtPercent() etc., at the cost
2852 of some extra memory usage for storage of intermediate calculations. By default it is disabled.
2853
2854 Disabling caching will release any allocated cache memory.
2855
2856 \since 6.10
2857 \sa isCachingEnabled(), length(), percentAtLength(), pointAtPercent(), trimmed()
2858*/
2859void QPainterPath::setCachingEnabled(bool enabled)
2860{
2861 ensureData();
2862 if (d_func()->cacheEnabled == enabled)
2863 return;
2864 setDirty(true);
2865 QPainterPathPrivate *d = d_func();
2866 d->cacheEnabled = enabled;
2867 if (!enabled) {
2868 d->m_runLengths.clear();
2869 d->m_runLengths.squeeze();
2870 }
2871}
2872
2873//derivative of the equation
2874static inline qreal slopeAt(qreal t, qreal a, qreal b, qreal c, qreal d)
2875{
2876 return 3*t*t*(d - 3*c + 3*b - a) + 6*t*(c - 2*b + a) + 3*(b - a);
2877}
2878
2879/*!
2880 Returns the length of the current path.
2881*/
2882qreal QPainterPath::length() const
2883{
2884 Q_D(QPainterPath);
2885 if (isEmpty())
2886 return 0;
2887 if (d->cacheEnabled) {
2888 if (d->dirtyRunLengths)
2889 d->computeRunLengths();
2890 return d->m_runLengths.last();
2891 }
2892
2893 qreal len = 0;
2894 for (int i=1; i<d->elements.size(); ++i) {
2895 const Element &e = d->elements.at(i);
2896
2897 switch (e.type) {
2898 case MoveToElement:
2899 break;
2900 case LineToElement:
2901 {
2902 len += QLineF(d->elements.at(i-1), e).length();
2903 break;
2904 }
2905 case CurveToElement:
2906 {
2907 QBezier b = QBezier::fromPoints(d->elements.at(i-1),
2908 e,
2909 d->elements.at(i+1),
2910 d->elements.at(i+2));
2911 len += b.length();
2912 i += 2;
2913 break;
2914 }
2915 default:
2916 break;
2917 }
2918 }
2919 return len;
2920}
2921
2922/*!
2923 Returns percentage of the whole path at the specified length \a len.
2924
2925 Note that similarly to other percent methods, the percentage measurement
2926 is not linear with regards to the length, if curves are present
2927 in the path. When curves are present the percentage argument is mapped
2928 to the t parameter of the Bezier equations.
2929*/
2930qreal QPainterPath::percentAtLength(qreal len) const
2931{
2932 Q_D(QPainterPath);
2933 if (isEmpty() || len <= 0)
2934 return 0;
2935
2936 qreal totalLength = length();
2937 if (len > totalLength)
2938 return 1;
2939
2940 Q_ASSERT(totalLength != 0);
2941
2942 if (d->cacheEnabled) {
2943 const int ei = qMax(d->elementAtT(len / totalLength), 1); // Skip initial MoveTo
2944 qreal res = 0;
2945 const QPainterPath::Element &e = d->elements[ei];
2946 switch (e.type) {
2947 case QPainterPath::LineToElement:
2948 res = len / totalLength;
2949 break;
2950 case CurveToElement:
2951 {
2952 QBezier b = QBezier::fromPoints(d->elements.at(ei-1),
2953 e,
2954 d->elements.at(ei+1),
2955 d->elements.at(ei+2));
2956 qreal prevLen = d->m_runLengths[ei - 1];
2957 qreal blen = d->m_runLengths[ei] - prevLen;
2958 qreal elemRes = b.tAtLength(len - prevLen);
2959 res = (elemRes * blen + prevLen) / totalLength;
2960 break;
2961 }
2962 default:
2963 Q_UNREACHABLE();
2964 }
2965 return res;
2966 }
2967
2968 qreal curLen = 0;
2969 for (int i=1; i<d->elements.size(); ++i) {
2970 const Element &e = d->elements.at(i);
2971
2972 switch (e.type) {
2973 case MoveToElement:
2974 break;
2975 case LineToElement:
2976 {
2977 QLineF line(d->elements.at(i-1), e);
2978 qreal llen = line.length();
2979 curLen += llen;
2980 if (curLen >= len) {
2981 return len/totalLength ;
2982 }
2983
2984 break;
2985 }
2986 case CurveToElement:
2987 {
2988 QBezier b = QBezier::fromPoints(d->elements.at(i-1),
2989 e,
2990 d->elements.at(i+1),
2991 d->elements.at(i+2));
2992 qreal blen = b.length();
2993 qreal prevLen = curLen;
2994 curLen += blen;
2995
2996 if (curLen >= len) {
2997 qreal res = b.tAtLength(len - prevLen);
2998 return (res * blen + prevLen)/totalLength;
2999 }
3000
3001 i += 2;
3002 break;
3003 }
3004 default:
3005 break;
3006 }
3007 }
3008
3009 return 0;
3010}
3011
3012static inline QBezier uncached_bezierAtT(const QPainterPath &path, qreal t, qreal *startingLength,
3013 qreal *bezierLength)
3014{
3015 *startingLength = 0;
3016 if (t > 1)
3017 return QBezier();
3018
3019 qreal curLen = 0;
3020 qreal totalLength = path.length();
3021
3022 const int lastElement = path.elementCount() - 1;
3023 for (int i=0; i <= lastElement; ++i) {
3024 const QPainterPath::Element &e = path.elementAt(i);
3025
3026 switch (e.type) {
3027 case QPainterPath::MoveToElement:
3028 break;
3029 case QPainterPath::LineToElement:
3030 {
3031 QLineF line(path.elementAt(i-1), e);
3032 qreal llen = line.length();
3033 curLen += llen;
3034 if (i == lastElement || curLen/totalLength >= t) {
3035 *bezierLength = llen;
3036 QPointF a = path.elementAt(i-1);
3037 QPointF delta = e - a;
3038 return QBezier::fromPoints(a, a + delta / 3, a + 2 * delta / 3, e);
3039 }
3040 break;
3041 }
3042 case QPainterPath::CurveToElement:
3043 {
3044 QBezier b = QBezier::fromPoints(path.elementAt(i-1),
3045 e,
3046 path.elementAt(i+1),
3047 path.elementAt(i+2));
3048 qreal blen = b.length();
3049 curLen += blen;
3050
3051 if (i + 2 == lastElement || curLen/totalLength >= t) {
3052 *bezierLength = blen;
3053 return b;
3054 }
3055
3056 i += 2;
3057 break;
3058 }
3059 default:
3060 break;
3061 }
3062 *startingLength = curLen;
3063 }
3064 return QBezier();
3065}
3066
3067QBezier QPainterPathPrivate::bezierAtT(const QPainterPath &path, qreal t, qreal *startingLength,
3068 qreal *bezierLength) const
3069{
3070 Q_ASSERT(t >= 0 && t <= 1);
3071 QPainterPathPrivate *d = path.d_func();
3072 if (!path.isEmpty() && d->cacheEnabled) {
3073 const int ei = qMax(d->elementAtT(t), 1); // Avoid the initial MoveTo element
3074 const qreal prevRunLength = d->m_runLengths[ei - 1];
3075 *startingLength = prevRunLength;
3076 *bezierLength = d->m_runLengths[ei] - prevRunLength;
3077 const QPointF prev = d->elements[ei - 1];
3078 const QPainterPath::Element &e = d->elements[ei];
3079 switch (e.type) {
3080 case QPainterPath::LineToElement:
3081 {
3082 QPointF delta = (e - prev) / 3;
3083 return QBezier::fromPoints(prev, prev + delta, prev + 2 * delta, e);
3084 }
3085 case QPainterPath::CurveToElement:
3086 return QBezier::fromPoints(prev, e, elements[ei + 1], elements[ei + 2]);
3087 break;
3088 default:
3089 Q_UNREACHABLE();
3090 }
3091 }
3092
3093 return uncached_bezierAtT(path, t, startingLength, bezierLength);
3094}
3095
3096/*!
3097 Returns the point at at the percentage \a t of the current path.
3098 The argument \a t has to be between 0 and 1.
3099
3100 Note that similarly to other percent methods, the percentage measurement
3101 is not linear with regards to the length, if curves are present
3102 in the path. When curves are present the percentage argument is mapped
3103 to the t parameter of the Bezier equations.
3104*/
3105QPointF QPainterPath::pointAtPercent(qreal t) const
3106{
3107 if (t < 0 || t > 1) {
3108 qWarning("QPainterPath::pointAtPercent accepts only values between 0 and 1");
3109 return QPointF();
3110 }
3111
3112 if (!d_ptr || d_ptr->elements.size() == 0)
3113 return QPointF();
3114
3115 if (d_ptr->elements.size() == 1)
3116 return d_ptr->elements.at(0);
3117
3118 qreal totalLength = length();
3119 qreal curLen = 0;
3120 qreal bezierLen = 0;
3121 QBezier b = d_ptr->bezierAtT(*this, t, &curLen, &bezierLen);
3122 Q_ASSERT(bezierLen != 0);
3123 qreal realT = (totalLength * t - curLen) / bezierLen;
3124
3125 return b.pointAt(qBound(qreal(0), realT, qreal(1)));
3126}
3127
3128/*!
3129 Returns the angle of the path tangent at the percentage \a t.
3130 The argument \a t has to be between 0 and 1.
3131
3132 Positive values for the angles mean counter-clockwise while negative values
3133 mean the clockwise direction. Zero degrees is at the 3 o'clock position.
3134
3135 Note that similarly to the other percent methods, the percentage measurement
3136 is not linear with regards to the length if curves are present
3137 in the path. When curves are present the percentage argument is mapped
3138 to the t parameter of the Bezier equations.
3139*/
3140qreal QPainterPath::angleAtPercent(qreal t) const
3141{
3142 if (t < 0 || t > 1) {
3143 qWarning("QPainterPath::angleAtPercent accepts only values between 0 and 1");
3144 return 0;
3145 }
3146
3147 if (isEmpty())
3148 return 0;
3149
3150 qreal totalLength = length();
3151 qreal curLen = 0;
3152 qreal bezierLen = 0;
3153 QBezier bez = d_ptr->bezierAtT(*this, t, &curLen, &bezierLen);
3154 Q_ASSERT(bezierLen != 0);
3155 qreal realT = (totalLength * t - curLen) / bezierLen;
3156
3157 qreal m1 = slopeAt(realT, bez.x1, bez.x2, bez.x3, bez.x4);
3158 qreal m2 = slopeAt(realT, bez.y1, bez.y2, bez.y3, bez.y4);
3159
3160 return QLineF(0, 0, m1, m2).angle();
3161}
3162
3163
3164/*!
3165 Returns the slope of the path at the percentage \a t. The
3166 argument \a t has to be between 0 and 1.
3167
3168 Note that similarly to other percent methods, the percentage measurement
3169 is not linear with regards to the length, if curves are present
3170 in the path. When curves are present the percentage argument is mapped
3171 to the t parameter of the Bezier equations.
3172*/
3173qreal QPainterPath::slopeAtPercent(qreal t) const
3174{
3175 if (t < 0 || t > 1) {
3176 qWarning("QPainterPath::slopeAtPercent accepts only values between 0 and 1");
3177 return 0;
3178 }
3179
3180 if (isEmpty())
3181 return 0;
3182
3183 qreal totalLength = length();
3184 qreal curLen = 0;
3185 qreal bezierLen = 0;
3186 QBezier bez = d_ptr->bezierAtT(*this, t, &curLen, &bezierLen);
3187 Q_ASSERT(bezierLen != 0);
3188 qreal realT = (totalLength * t - curLen) / bezierLen;
3189
3190 qreal m1 = slopeAt(realT, bez.x1, bez.x2, bez.x3, bez.x4);
3191 qreal m2 = slopeAt(realT, bez.y1, bez.y2, bez.y3, bez.y4);
3192 //tangent line
3193 qreal slope = 0;
3194
3195 if (m1)
3196 slope = m2/m1;
3197 else {
3198 if (std::numeric_limits<qreal>::has_infinity) {
3199 slope = (m2 < 0) ? -std::numeric_limits<qreal>::infinity()
3200 : std::numeric_limits<qreal>::infinity();
3201 } else {
3202 if (sizeof(qreal) == sizeof(double)) {
3203 return 1.79769313486231570e+308;
3204 } else {
3205 return ((qreal)3.40282346638528860e+38);
3206 }
3207 }
3208 }
3209
3210 return slope;
3211}
3212
3213/*!
3214 \since 6.10
3215
3216 Returns the section of the path between the length fractions \a fromFraction and \a toFraction.
3217 The effective range of the fractions are from 0, denoting the start point of the path, to 1,
3218 denoting its end point. The fractions are linear with respect to path length, in contrast to the
3219 percentage \e t values.
3220
3221 The value of \a offset will be added to the fraction values. If that causes an over- or underflow
3222 of the [0, 1] range, the values will be wrapped around, as will the resulting path. The effective
3223 range of the offset is between -1 and 1.
3224
3225 Repeated calls to this function can be optimized by {enabling caching}{setCachingEnabled()}.
3226
3227 \sa length(), percentAtLength(), setCachingEnabled()
3228*/
3229
3230QPainterPath QPainterPath::trimmed(qreal fromFraction, qreal toFraction, qreal offset) const
3231{
3232 if (isEmpty())
3233 return *this;
3234
3235 // We need length caching enabled for the calculations.
3236 if (!isCachingEnabled()) {
3237 QPainterPath copy(*this);
3238 copy.setCachingEnabled(true);
3239 return copy.trimmed(fromFraction, toFraction, offset);
3240 }
3241
3242 qreal f1 = qBound(qreal(0), fromFraction, qreal(1));
3243 qreal f2 = qBound(qreal(0), toFraction, qreal(1));
3244 if (f1 > f2)
3245 qSwap(f1, f2);
3246 if (qFuzzyCompare(f2 - f1, qreal(1))) // Shortcut for no trimming
3247 return *this;
3248
3249 QPainterPath res;
3250 if (qFuzzyCompare(f1, f2))
3251 return res;
3252 res.setFillRule(fillRule());
3253
3254 if (offset) {
3255 qreal dummy;
3256 offset = std::modf(offset, &dummy); // Use only the fractional part of offset, range <-1, 1>
3257
3258 qreal of1 = f1 + offset;
3259 qreal of2 = f2 + offset;
3260 if (offset < 0) {
3261 f1 = of1 < 0 ? of1 + 1 : of1;
3262 f2 = of2 + 1 > 1 ? of2 : of2 + 1;
3263 } else if (offset > 0) {
3264 f1 = of1 - 1 < 0 ? of1 : of1 - 1;
3265 f2 = of2 > 1 ? of2 - 1 : of2;
3266 }
3267 }
3268 const bool wrapping = (f1 > f2);
3269 //qDebug() << "ADJ:" << f1 << f2 << wrapping << "(" << of1 << of2 << ")";
3270
3271 QPainterPathPrivate *d = d_func();
3272 if (d->dirtyRunLengths)
3273 d->computeRunLengths();
3274 const qreal totalLength = d->m_runLengths.last();
3275 if (qFuzzyIsNull(totalLength))
3276 return res;
3277
3278 const qreal l1 = f1 * totalLength;
3279 const qreal l2 = f2 * totalLength;
3280 const int e1 = d->elementAtLength(l1);
3281 const bool mustTrimE1 = !qFuzzyCompare(d->m_runLengths.at(e1), l1);
3282 const int e2 = d->elementAtLength(l2);
3283 const bool mustTrimE2 = !qFuzzyCompare(d->m_runLengths.at(e2), l2);
3284
3285 //qDebug() << "Trim [" << f1 << f2 << "] e1:" << e1 << mustTrimE1 << "e2:" << e2 << mustTrimE2 << "wrapping:" << wrapping;
3286 if (e1 == e2 && !wrapping && mustTrimE1 && mustTrimE2) {
3287 // Entire result is one element, clipped in both ends
3288 d->appendSliceOfElement(&res, e1, l1, l2);
3289 } else {
3290 // Add partial start element (or just its end point, being the start of the next)
3291 if (mustTrimE1)
3292 d->appendEndOfElement(&res, e1, l1);
3293 else
3294 res.moveTo(d->endPointOfElement(e1));
3295
3296 // Add whole elements between start and end
3297 int firstWholeElement = e1 + 1;
3298 int lastWholeElement = (mustTrimE2 ? e2 - 1 : e2);
3299 if (!wrapping) {
3300 d->appendElementRange(&res, firstWholeElement, lastWholeElement);
3301 } else {
3302 int lastIndex = d->elements.size() - 1;
3303 d->appendElementRange(&res, firstWholeElement, lastIndex);
3304 bool isClosed = (QPointF(d->elements.at(0)) == QPointF(d->elements.at(lastIndex)));
3305 // If closed we can skip the initial moveto
3306 d->appendElementRange(&res, (isClosed ? 1 : 0), lastWholeElement);
3307 }
3308
3309 // Partial end element
3310 if (mustTrimE2)
3311 d->appendStartOfElement(&res, e2, l2);
3312 }
3313
3314 return res;
3315}
3316
3317void QPainterPathPrivate::appendTrimmedElement(QPainterPath *to, int elemIdx, int trimFlags,
3318 qreal startLen, qreal endLen)
3319{
3320 Q_ASSERT(cacheEnabled);
3321 Q_ASSERT(!dirtyRunLengths);
3322
3323 if (elemIdx <= 0 || elemIdx >= elements.size())
3324 return;
3325
3326 const qreal prevLen = m_runLengths.at(elemIdx - 1);
3327 const qreal elemLen = m_runLengths.at(elemIdx) - prevLen;
3328 const qreal len1 = startLen - prevLen;
3329 const qreal len2 = endLen - prevLen;
3330 if (qFuzzyIsNull(elemLen))
3331 return;
3332
3333 const QPointF pp = elements.at(elemIdx - 1);
3334 const QPainterPath::Element e = elements.at(elemIdx);
3335 if (e.isLineTo()) {
3336 QLineF l(pp, e);
3337 QPointF p1 = (trimFlags & TrimStart) ? l.pointAt(len1 / elemLen) : pp;
3338 QPointF p2 = (trimFlags & TrimEnd) ? l.pointAt(len2 / elemLen) : e;
3339 if (to->isEmpty())
3340 to->moveTo(p1);
3341 to->lineTo(p2);
3342 } else if (e.isCurveTo()) {
3343 Q_ASSERT(elemIdx < elements.size() - 2);
3344 QBezier b = QBezier::fromPoints(pp, e, elements.at(elemIdx + 1), elements.at(elemIdx + 2));
3345 qreal t1 = (trimFlags & TrimStart) ? b.tAtLength(len1) : 0.0; // or simply len1/elemLen to trim by t instead of len
3346 qreal t2 = (trimFlags & TrimEnd) ? b.tAtLength(len2) : 1.0;
3347 QBezier c = b.getSubRange(t1, t2);
3348 if (to->isEmpty())
3349 to->moveTo(c.pt1());
3350 to->cubicTo(c.pt2(), c.pt3(), c.pt4());
3351 } else {
3352 Q_UNREACHABLE();
3353 }
3354}
3355
3356void QPainterPathPrivate::appendElementRange(QPainterPath *to, int first, int last)
3357{
3358 if (first < 0 || first >= elements.size() || last < 0 || last >= elements.size())
3359 return;
3360
3361 // (Could optimize by direct copy of elements, but must ensure correct state flags)
3362 for (int i = first; i <= last; i++) {
3363 const QPainterPath::Element &e = elements.at(i);
3364 switch (e.type) {
3365 case QPainterPath::MoveToElement:
3366 to->moveTo(e);
3367 break;
3368 case QPainterPath::LineToElement:
3369 to->lineTo(e);
3370 break;
3371 case QPainterPath::CurveToElement:
3372 Q_ASSERT(i < elements.size() - 2);
3373 to->cubicTo(e, elements.at(i + 1), elements.at(i + 2));
3374 i += 2;
3375 break;
3376 default:
3377 // 'first' may point to CurveToData element, just skip it
3378 break;
3379 }
3380 }
3381}
3382
3383
3384/*!
3385 \since 4.4
3386
3387 Adds the given rectangle \a rect with rounded corners to the path.
3388
3389 The \a xRadius and \a yRadius arguments specify the radii of
3390 the ellipses defining the corners of the rounded rectangle.
3391 When \a mode is Qt::RelativeSize, \a xRadius and
3392 \a yRadius are specified in percentage of half the rectangle's
3393 width and height respectively, and should be in the range 0.0 to 100.0.
3394
3395 \sa addRect()
3396*/
3397void QPainterPath::addRoundedRect(const QRectF &rect, qreal xRadius, qreal yRadius,
3398 Qt::SizeMode mode)
3399{
3400 QRectF r = rect.normalized();
3401
3402 if (r.isNull())
3403 return;
3404
3405 if (mode == Qt::AbsoluteSize) {
3406 qreal w = r.width() / 2;
3407 qreal h = r.height() / 2;
3408
3409 if (w == 0) {
3410 xRadius = 0;
3411 } else {
3412 xRadius = 100 * qMin(xRadius, w) / w;
3413 }
3414 if (h == 0) {
3415 yRadius = 0;
3416 } else {
3417 yRadius = 100 * qMin(yRadius, h) / h;
3418 }
3419 } else {
3420 if (xRadius > 100) // fix ranges
3421 xRadius = 100;
3422
3423 if (yRadius > 100)
3424 yRadius = 100;
3425 }
3426
3427 if (xRadius <= 0 || yRadius <= 0) { // add normal rectangle
3428 addRect(r);
3429 return;
3430 }
3431
3432 qreal x = r.x();
3433 qreal y = r.y();
3434 qreal w = r.width();
3435 qreal h = r.height();
3436 qreal rxx2 = w*xRadius/100;
3437 qreal ryy2 = h*yRadius/100;
3438
3439 ensureData();
3440 setDirty(true);
3441
3442 bool first = d_func()->elements.size() < 2;
3443
3444 arcMoveTo(x, y, rxx2, ryy2, 180);
3445 arcTo(x, y, rxx2, ryy2, 180, -90);
3446 arcTo(x+w-rxx2, y, rxx2, ryy2, 90, -90);
3447 arcTo(x+w-rxx2, y+h-ryy2, rxx2, ryy2, 0, -90);
3448 arcTo(x, y+h-ryy2, rxx2, ryy2, 270, -90);
3449 closeSubpath();
3450
3451 d_func()->require_moveTo = true;
3452 d_func()->convex = first;
3453}
3454
3455/*!
3456 \fn void QPainterPath::addRoundedRect(qreal x, qreal y, qreal w, qreal h, qreal xRadius, qreal yRadius, Qt::SizeMode mode = Qt::AbsoluteSize);
3457 \since 4.4
3458 \overload
3459
3460 Adds the given rectangle \a x, \a y, \a w, \a h with rounded corners to the path.
3461 */
3462
3463/*!
3464 \since 4.3
3465
3466 Returns a path which is the union of this path's fill area and \a p's fill area.
3467
3468 Set operations on paths will treat the paths as areas. Non-closed
3469 paths will be treated as implicitly closed.
3470 Bezier curves may be flattened to line segments due to numerical instability of
3471 doing bezier curve intersections.
3472
3473 \sa intersected(), subtracted()
3474*/
3475QPainterPath QPainterPath::united(const QPainterPath &p) const
3476{
3477 if (isEmpty() || p.isEmpty())
3478 return isEmpty() ? p : *this;
3479 QPathClipper clipper(*this, p);
3480 return clipper.clip(QPathClipper::BoolOr);
3481}
3482
3483/*!
3484 \since 4.3
3485
3486 Returns a path which is the intersection of this path's fill area and \a p's fill area.
3487 Bezier curves may be flattened to line segments due to numerical instability of
3488 doing bezier curve intersections.
3489*/
3490QPainterPath QPainterPath::intersected(const QPainterPath &p) const
3491{
3492 if (isEmpty() || p.isEmpty())
3493 return QPainterPath();
3494 QPathClipper clipper(*this, p);
3495 return clipper.clip(QPathClipper::BoolAnd);
3496}
3497
3498/*!
3499 \since 4.3
3500
3501 Returns a path which is \a p's fill area subtracted from this path's fill area.
3502
3503 Set operations on paths will treat the paths as areas. Non-closed
3504 paths will be treated as implicitly closed.
3505 Bezier curves may be flattened to line segments due to numerical instability of
3506 doing bezier curve intersections.
3507*/
3508QPainterPath QPainterPath::subtracted(const QPainterPath &p) const
3509{
3510 if (isEmpty() || p.isEmpty())
3511 return *this;
3512 QPathClipper clipper(*this, p);
3513 return clipper.clip(QPathClipper::BoolSub);
3514}
3515
3516/*!
3517 \since 4.4
3518
3519 Returns a simplified version of this path. This implies merging all subpaths that intersect,
3520 and returning a path containing no intersecting edges. Consecutive parallel lines will also
3521 be merged. The simplified path will always use the default fill rule, Qt::OddEvenFill.
3522 Bezier curves may be flattened to line segments due to numerical instability of
3523 doing bezier curve intersections.
3524*/
3525QPainterPath QPainterPath::simplified() const
3526{
3527 if (isEmpty())
3528 return *this;
3529 QPathClipper clipper(*this, QPainterPath());
3530 return clipper.clip(QPathClipper::Simplify);
3531}
3532
3533/*!
3534 \since 4.3
3535
3536 Returns \c true if the current path intersects at any point the given path \a p.
3537 Also returns \c true if the current path contains or is contained by any part of \a p.
3538
3539 Set operations on paths will treat the paths as areas. Non-closed
3540 paths will be treated as implicitly closed.
3541
3542 \sa contains()
3543 */
3544bool QPainterPath::intersects(const QPainterPath &p) const
3545{
3546 if (p.elementCount() == 1)
3547 return contains(p.elementAt(0));
3548 if (isEmpty() || p.isEmpty())
3549 return false;
3550 QPathClipper clipper(*this, p);
3551 return clipper.intersect();
3552}
3553
3554/*!
3555 \since 4.3
3556
3557 Returns \c true if the given path \a p is contained within
3558 the current path. Returns \c false if any edges of the current path and
3559 \a p intersect.
3560
3561 Set operations on paths will treat the paths as areas. Non-closed
3562 paths will be treated as implicitly closed.
3563
3564 \sa intersects()
3565 */
3566bool QPainterPath::contains(const QPainterPath &p) const
3567{
3568 if (p.elementCount() == 1)
3569 return contains(p.elementAt(0));
3570 if (isEmpty() || p.isEmpty())
3571 return false;
3572 QPathClipper clipper(*this, p);
3573 return clipper.contains();
3574}
3575
3576void QPainterPath::setDirty(bool dirty)
3577{
3578 d_func()->pathConverter.reset();
3579 d_func()->dirtyBounds = dirty;
3580 d_func()->dirtyControlBounds = dirty;
3581 d_func()->dirtyRunLengths = dirty;
3582 d_func()->convex = false;
3583}
3584
3585void QPainterPath::computeBoundingRect() const
3586{
3587 QPainterPathPrivate *d = d_func();
3588 d->dirtyBounds = false;
3589 if (!d_ptr) {
3590 d->bounds = QRect();
3591 return;
3592 }
3593
3594 qreal minx, maxx, miny, maxy;
3595 minx = maxx = d->elements.at(0).x;
3596 miny = maxy = d->elements.at(0).y;
3597 for (int i=1; i<d->elements.size(); ++i) {
3598 const Element &e = d->elements.at(i);
3599
3600 switch (e.type) {
3601 case MoveToElement:
3602 case LineToElement:
3603 if (e.x > maxx) maxx = e.x;
3604 else if (e.x < minx) minx = e.x;
3605 if (e.y > maxy) maxy = e.y;
3606 else if (e.y < miny) miny = e.y;
3607 break;
3608 case CurveToElement:
3609 {
3610 QBezier b = QBezier::fromPoints(d->elements.at(i-1),
3611 e,
3612 d->elements.at(i+1),
3613 d->elements.at(i+2));
3614 QRectF r = qt_painterpath_bezier_extrema(b);
3615 qreal right = r.right();
3616 qreal bottom = r.bottom();
3617 if (r.x() < minx) minx = r.x();
3618 if (right > maxx) maxx = right;
3619 if (r.y() < miny) miny = r.y();
3620 if (bottom > maxy) maxy = bottom;
3621 i += 2;
3622 }
3623 break;
3624 default:
3625 break;
3626 }
3627 }
3628 d->bounds = QRectF(minx, miny, maxx - minx, maxy - miny);
3629}
3630
3631
3632void QPainterPath::computeControlPointRect() const
3633{
3634 QPainterPathPrivate *d = d_func();
3635 d->dirtyControlBounds = false;
3636 if (!d_ptr) {
3637 d->controlBounds = QRect();
3638 return;
3639 }
3640
3641 qreal minx, maxx, miny, maxy;
3642 minx = maxx = d->elements.at(0).x;
3643 miny = maxy = d->elements.at(0).y;
3644 for (int i=1; i<d->elements.size(); ++i) {
3645 const Element &e = d->elements.at(i);
3646 if (e.x > maxx) maxx = e.x;
3647 else if (e.x < minx) minx = e.x;
3648 if (e.y > maxy) maxy = e.y;
3649 else if (e.y < miny) miny = e.y;
3650 }
3651 d->controlBounds = QRectF(minx, miny, maxx - minx, maxy - miny);
3652}
3653
3655{
3656 Q_ASSERT(!elements.isEmpty());
3657
3658 m_runLengths.clear();
3659 const int numElems = elements.size();
3660 m_runLengths.reserve(numElems);
3661
3662 QPointF runPt = elements[0];
3663 qreal runLen = 0.0;
3664 for (int i = 0; i < numElems; i++) {
3665 QPainterPath::Element e = elements[i];
3666 switch (e.type) {
3667 case QPainterPath::LineToElement:
3668 runLen += QLineF(runPt, e).length();
3669 runPt = e;
3670 break;
3671 case QPainterPath::CurveToElement: {
3672 Q_ASSERT(i < numElems - 2);
3673 QPainterPath::Element ee = elements[i + 2];
3674 runLen += QBezier::fromPoints(runPt, e, elements[i + 1], ee).length();
3675 runPt = ee;
3676 break;
3677 }
3678 case QPainterPath::MoveToElement:
3679 runPt = e;
3680 break;
3681 case QPainterPath::CurveToDataElement:
3682 break;
3683 }
3684 m_runLengths.append(runLen);
3685 }
3686 Q_ASSERT(m_runLengths.size() == elements.size());
3687
3688 dirtyRunLengths = false;
3689}
3690
3691#ifndef QT_NO_DEBUG_STREAM
3692QDebug operator<<(QDebug s, const QPainterPath &p)
3693{
3694 QDebugStateSaver saver(s);
3695 s.nospace() << "QPainterPath: Element count=" << p.elementCount() << Qt::endl;
3696 const char *types[] = {"MoveTo", "LineTo", "CurveTo", "CurveToData"};
3697 for (int i=0; i<p.elementCount(); ++i) {
3698 s.nospace() << " -> " << types[p.elementAt(i).type] << "(x=" << p.elementAt(i).x << ", y=" << p.elementAt(i).y << ')' << Qt::endl;
3699 }
3700 return s;
3701}
3702#endif
3703
3704QT_END_NAMESPACE
QBezier bezierAtT(const QPainterPath &path, qreal t, qreal *startingLength, qreal *bezierLength) const
Combined button and popup list for selecting options.
QDebug operator<<(QDebug dbg, const QFileInfo &fi)
static bool hasValidCoords(QRectF r)
static qreal slopeAt(qreal t, qreal a, qreal b, qreal c, qreal d)
void qt_path_stroke_move_to(qfixed x, qfixed y, void *data)
static bool qt_painterpath_check_crossing(const QPainterPath *path, const QRectF &rect)
static QBezier uncached_bezierAtT(const QPainterPath &path, qreal t, qreal *startingLength, qreal *bezierLength)
static bool qt_isect_curve_horizontal(const QBezier &bezier, qreal y, qreal x1, qreal x2, int depth=0)
static void qt_painterpath_isect_line(const QPointF &p1, const QPointF &p2, const QPointF &pos, int *winding)
static QRectF qt_painterpath_bezier_extrema(const QBezier &b)
#define QT_BEZIER_CHECK_T(bezier, t)
PainterDirections
@ Right
@ Top
@ Bottom
@ Left
static bool qt_painterpath_isect_line_rect(qreal x1, qreal y1, qreal x2, qreal y2, const QRectF &rect)
#define QT_BEZIER_C(bezier, coord)
void qt_find_ellipse_coords(const QRectF &r, qreal angle, qreal length, QPointF *startPoint, QPointF *endPoint)
void qt_path_stroke_cubic_to(qfixed c1x, qfixed c1y, qfixed c2x, qfixed c2y, qfixed ex, qfixed ey, void *data)
#define QT_BEZIER_A(bezier, coord)
static bool epsilonCompare(const QPointF &a, const QPointF &b, const QSizeF &epsilon)
#define QT_BEZIER_B(bezier, coord)
static QT_BEGIN_NAMESPACE bool isValidCoord(qreal c)
static void qt_painterpath_isect_curve(const QBezier &bezier, const QPointF &pt, int *winding, int depth=0)
static bool qt_isect_curve_vertical(const QBezier &bezier, qreal x, qreal y1, qreal y2, int depth=0)
static bool pointOnEdge(const QRectF &rect, const QPointF &point)
QPainterPath qt_stroke_dash(const QPainterPath &path, qreal *dashes, int dashCount)
static bool hasValidCoords(QPointF p)
void qt_path_stroke_line_to(qfixed x, qfixed y, void *data)
QDataStream & operator<<(QDataStream &stream, const QImage &image)
[0]
Definition qimage.cpp:4009
QDataStream & operator>>(QDataStream &stream, QImage &image)
Definition qimage.cpp:4035