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 (QtPrivate::fuzzyCompare(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, last_start)
1999 && qt_painterpath_isect_line_rect(last_pt.x(), last_pt.y(),
2000 last_start.x(), last_start.y(), rect))
2001 return true;
2002 last_start = last_pt = e;
2003 break;
2004
2005 case QPainterPath::LineToElement:
2006 if (qt_painterpath_isect_line_rect(last_pt.x(), last_pt.y(), e.x, e.y, rect))
2007 return true;
2008 last_pt = e;
2009 break;
2010
2011 case QPainterPath::CurveToElement:
2012 {
2013 QPointF cp2 = path->elementAt(++i);
2014 QPointF ep = path->elementAt(++i);
2015 QBezier bezier = QBezier::fromPoints(last_pt, e, cp2, ep);
2016 if (qt_isect_curve_horizontal(bezier, rect.top(), rect.left(), rect.right())
2017 || qt_isect_curve_horizontal(bezier, rect.bottom(), rect.left(), rect.right())
2018 || qt_isect_curve_vertical(bezier, rect.left(), rect.top(), rect.bottom())
2019 || qt_isect_curve_vertical(bezier, rect.right(), rect.top(), rect.bottom()))
2020 return true;
2021 last_pt = ep;
2022 }
2023 break;
2024
2025 default:
2026 break;
2027 }
2028 // Handle crossing the edges of the rect at the end-points of individual sub-paths.
2029 // A point on on the edge itself is considered neither inside nor outside for this purpose.
2030 if (!pointOnEdge(rect, last_pt)) {
2031 bool contained = rect.contains(last_pt);
2032 switch (edgeStatus) {
2033 case OutsideRect:
2034 if (contained)
2035 return true;
2036 break;
2037 case InsideRect:
2038 if (!contained)
2039 return true;
2040 break;
2041 case OnRect:
2042 edgeStatus = contained ? InsideRect : OutsideRect;
2043 break;
2044 }
2045 } else {
2046 if (last_pt == last_start)
2047 edgeStatus = OnRect;
2048 }
2049 }
2050
2051 // implicitly close last subpath
2052 if (last_pt != last_start
2053 && qt_painterpath_isect_line_rect(last_pt.x(), last_pt.y(),
2054 last_start.x(), last_start.y(), rect))
2055 return true;
2056
2057 return false;
2058}
2059
2060/*!
2061 \fn bool QPainterPath::intersects(const QRectF &rectangle) const
2062
2063 Returns \c true if any point in the given \a rectangle intersects the
2064 path; otherwise returns \c false.
2065
2066 There is an intersection if any of the lines making up the
2067 rectangle crosses a part of the path or if any part of the
2068 rectangle overlaps with any area enclosed by the path. This
2069 function respects the current fillRule to determine what is
2070 considered inside the path.
2071
2072 \sa contains()
2073*/
2074bool QPainterPath::intersects(const QRectF &rect) const
2075{
2076 if (elementCount() == 1 && rect.contains(elementAt(0)))
2077 return true;
2078
2079 if (isEmpty())
2080 return false;
2081
2082 QRectF cp = controlPointRect();
2083 QRectF rn = rect.normalized();
2084
2085 // QRectF::intersects returns false if one of the rects is a null rect
2086 // which would happen for a painter path consisting of a vertical or
2087 // horizontal line
2088 if (qMax(rn.left(), cp.left()) > qMin(rn.right(), cp.right())
2089 || qMax(rn.top(), cp.top()) > qMin(rn.bottom(), cp.bottom()))
2090 return false;
2091
2092 // If any path element cross the rect its bound to be an intersection
2093 if (qt_painterpath_check_crossing(this, rect))
2094 return true;
2095
2096 if (contains(rect.center()))
2097 return true;
2098
2099 Q_D(QPainterPath);
2100
2101 // Check if the rectangle surrounds any subpath...
2102 for (int i=0; i<d->elements.size(); ++i) {
2103 const Element &e = d->elements.at(i);
2104 if (e.type == QPainterPath::MoveToElement && rect.contains(e))
2105 return true;
2106 }
2107
2108 return false;
2109}
2110
2111/*!
2112 Translates all elements in the path by (\a{dx}, \a{dy}).
2113
2114 \since 4.6
2115 \sa translated()
2116*/
2117void QPainterPath::translate(qreal dx, qreal dy)
2118{
2119 if (!d_ptr || (dx == 0 && dy == 0))
2120 return;
2121
2122 int elementsLeft = d_ptr->elements.size();
2123 if (elementsLeft <= 0)
2124 return;
2125
2126 setDirty(true);
2127 QPainterPath::Element *element = d_func()->elements.data();
2128 Q_ASSERT(element);
2129 while (elementsLeft--) {
2130 element->x += dx;
2131 element->y += dy;
2132 ++element;
2133 }
2134}
2135
2136/*!
2137 \fn void QPainterPath::translate(const QPointF &offset)
2138 \overload
2139 \since 4.6
2140
2141 Translates all elements in the path by the given \a offset.
2142
2143 \sa translated()
2144*/
2145
2146/*!
2147 Returns a copy of the path that is translated by (\a{dx}, \a{dy}).
2148
2149 \since 4.6
2150 \sa translate()
2151*/
2152QPainterPath QPainterPath::translated(qreal dx, qreal dy) const
2153{
2154 QPainterPath copy(*this);
2155 copy.translate(dx, dy);
2156 return copy;
2157}
2158
2159/*!
2160 \fn QPainterPath QPainterPath::translated(const QPointF &offset) const;
2161 \overload
2162 \since 4.6
2163
2164 Returns a copy of the path that is translated by the given \a offset.
2165
2166 \sa translate()
2167*/
2168
2169/*!
2170 \fn bool QPainterPath::contains(const QRectF &rectangle) const
2171
2172 Returns \c true if the given \a rectangle is inside the path,
2173 otherwise returns \c false.
2174*/
2175bool QPainterPath::contains(const QRectF &rect) const
2176{
2177 Q_D(QPainterPath);
2178
2179 // the path is empty or the control point rect doesn't completely
2180 // cover the rectangle we abort stratight away.
2181 if (isEmpty() || !controlPointRect().contains(rect))
2182 return false;
2183
2184 // if there are intersections, chances are that the rect is not
2185 // contained, except if we have winding rule, in which case it
2186 // still might.
2187 if (qt_painterpath_check_crossing(this, rect)) {
2188 if (fillRule() == Qt::OddEvenFill) {
2189 return false;
2190 } else {
2191 // Do some wague sampling in the winding case. This is not
2192 // precise but it should mostly be good enough.
2193 if (!contains(rect.topLeft()) ||
2194 !contains(rect.topRight()) ||
2195 !contains(rect.bottomRight()) ||
2196 !contains(rect.bottomLeft()))
2197 return false;
2198 }
2199 }
2200
2201 // If there exists a point inside that is not part of the path its
2202 // because: rectangle lies completely outside path or a subpath
2203 // excludes parts of the rectangle. Both cases mean that the rect
2204 // is not contained
2205 if (!contains(rect.center()))
2206 return false;
2207
2208 // If there are any subpaths inside this rectangle we need to
2209 // check if they are still contained as a result of the fill
2210 // rule. This can only be the case for WindingFill though. For
2211 // OddEvenFill the rect will never be contained if it surrounds a
2212 // subpath. (the case where two subpaths are completely identical
2213 // can be argued but we choose to neglect it).
2214 for (int i=0; i<d->elements.size(); ++i) {
2215 const Element &e = d->elements.at(i);
2216 if (e.type == QPainterPath::MoveToElement && rect.contains(e)) {
2217 if (fillRule() == Qt::OddEvenFill)
2218 return false;
2219
2220 bool stop = false;
2221 for (; !stop && i<d->elements.size(); ++i) {
2222 const Element &el = d->elements.at(i);
2223 switch (el.type) {
2224 case MoveToElement:
2225 stop = true;
2226 break;
2227 case LineToElement:
2228 if (!contains(el))
2229 return false;
2230 break;
2231 case CurveToElement:
2232 if (!contains(d->elements.at(i+2)))
2233 return false;
2234 i += 2;
2235 break;
2236 default:
2237 break;
2238 }
2239 }
2240
2241 // compensate for the last ++i in the inner for
2242 --i;
2243 }
2244 }
2245
2246 return true;
2247}
2248
2249static inline bool epsilonCompare(const QPointF &a, const QPointF &b, const QSizeF &epsilon)
2250{
2251 return qAbs(a.x() - b.x()) <= epsilon.width()
2252 && qAbs(a.y() - b.y()) <= epsilon.height();
2253}
2254
2255/*!
2256 Returns \c true if this painterpath is equal to the given \a path.
2257
2258 Note that comparing paths may involve a per element comparison
2259 which can be slow for complex paths.
2260
2261 \sa operator!=()
2262*/
2263
2264bool QPainterPath::operator==(const QPainterPath &path) const
2265{
2266 QPainterPathPrivate *d = d_func();
2267 QPainterPathPrivate *other_d = path.d_func();
2268 if (other_d == d) {
2269 return true;
2270 } else if (!d || !other_d) {
2271 if (!other_d && isEmpty() && elementAt(0) == QPointF() && !d->hasWindingFill)
2272 return true;
2273 if (!d && path.isEmpty() && path.elementAt(0) == QPointF() && !other_d->hasWindingFill)
2274 return true;
2275 return false;
2276 }
2277 else if (d->hasWindingFill != other_d->hasWindingFill)
2278 return false;
2279 else if (d->elements.size() != other_d->elements.size())
2280 return false;
2281
2282 const qreal qt_epsilon = sizeof(qreal) == sizeof(double) ? 1e-12 : qreal(1e-5);
2283
2284 QSizeF epsilon = boundingRect().size();
2285 epsilon.rwidth() *= qt_epsilon;
2286 epsilon.rheight() *= qt_epsilon;
2287
2288 for (int i = 0; i < d->elements.size(); ++i)
2289 if (d->elements.at(i).type != other_d->elements.at(i).type
2290 || !epsilonCompare(d->elements.at(i), other_d->elements.at(i), epsilon))
2291 return false;
2292
2293 return true;
2294}
2295
2296/*!
2297 Returns \c true if this painter path differs from the given \a path.
2298
2299 Note that comparing paths may involve a per element comparison
2300 which can be slow for complex paths.
2301
2302 \sa operator==()
2303*/
2304
2305bool QPainterPath::operator!=(const QPainterPath &path) const
2306{
2307 return !(*this==path);
2308}
2309
2310/*!
2311 \since 4.5
2312
2313 Returns the intersection of this path and the \a other path.
2314
2315 \sa intersected(), operator&=(), united(), operator|()
2316*/
2317QPainterPath QPainterPath::operator&(const QPainterPath &other) const
2318{
2319 return intersected(other);
2320}
2321
2322/*!
2323 \since 4.5
2324
2325 Returns the union of this path and the \a other path.
2326
2327 \sa united(), operator|=(), intersected(), operator&()
2328*/
2329QPainterPath QPainterPath::operator|(const QPainterPath &other) const
2330{
2331 return united(other);
2332}
2333
2334/*!
2335 \since 4.5
2336
2337 Returns the union of this path and the \a other path. This function is equivalent
2338 to operator|().
2339
2340 \sa united(), operator+=(), operator-()
2341*/
2342QPainterPath QPainterPath::operator+(const QPainterPath &other) const
2343{
2344 return united(other);
2345}
2346
2347/*!
2348 \since 4.5
2349
2350 Subtracts the \a other path from a copy of this path, and returns the copy.
2351
2352 \sa subtracted(), operator-=(), operator+()
2353*/
2354QPainterPath QPainterPath::operator-(const QPainterPath &other) const
2355{
2356 return subtracted(other);
2357}
2358
2359/*!
2360 \since 4.5
2361
2362 Intersects this path with \a other and returns a reference to this path.
2363
2364 \sa intersected(), operator&(), operator|=()
2365*/
2366QPainterPath &QPainterPath::operator&=(const QPainterPath &other)
2367{
2368 return *this = (*this & other);
2369}
2370
2371/*!
2372 \since 4.5
2373
2374 Unites this path with \a other and returns a reference to this path.
2375
2376 \sa united(), operator|(), operator&=()
2377*/
2378QPainterPath &QPainterPath::operator|=(const QPainterPath &other)
2379{
2380 return *this = (*this | other);
2381}
2382
2383/*!
2384 \since 4.5
2385
2386 Unites this path with \a other, and returns a reference to this path. This
2387 is equivalent to operator|=().
2388
2389 \sa united(), operator+(), operator-=()
2390*/
2391QPainterPath &QPainterPath::operator+=(const QPainterPath &other)
2392{
2393 return *this = (*this + other);
2394}
2395
2396/*!
2397 \since 4.5
2398
2399 Subtracts \a other from this path, and returns a reference to this
2400 path.
2401
2402 \sa subtracted(), operator-(), operator+=()
2403*/
2404QPainterPath &QPainterPath::operator-=(const QPainterPath &other)
2405{
2406 return *this = (*this - other);
2407}
2408
2409#ifndef QT_NO_DATASTREAM
2410/*!
2411 \fn QDataStream &operator<<(QDataStream &stream, const QPainterPath &path)
2412 \relates QPainterPath
2413
2414 Writes the given painter \a path to the given \a stream, and
2415 returns a reference to the \a stream.
2416
2417 \sa {Serializing Qt Data Types}
2418*/
2419QDataStream &operator<<(QDataStream &s, const QPainterPath &p)
2420{
2421 if (p.isEmpty()) {
2422 s << 0;
2423 return s;
2424 }
2425
2426 s << p.elementCount();
2427 for (int i=0; i < p.d_func()->elements.size(); ++i) {
2428 const QPainterPath::Element &e = p.d_func()->elements.at(i);
2429 s << int(e.type);
2430 s << double(e.x) << double(e.y);
2431 }
2432 s << p.d_func()->cStart;
2433 s << int(p.fillRule());
2434 return s;
2435}
2436
2437/*!
2438 \fn QDataStream &operator>>(QDataStream &stream, QPainterPath &path)
2439 \relates QPainterPath
2440
2441 Reads a painter path from the given \a stream into the specified \a path,
2442 and returns a reference to the \a stream.
2443
2444 \sa {Serializing Qt Data Types}
2445*/
2446QDataStream &operator>>(QDataStream &s, QPainterPath &p)
2447{
2448 bool errorDetected = false;
2449 int size;
2450 s >> size;
2451
2452 if (size == 0) {
2453 p = {};
2454 return s;
2455 }
2456
2457 p.ensureData(); // in case if p.d_func() == 0
2458 p.setDirty(true);
2459 p.d_func()->elements.clear();
2460 for (int i=0; i<size; ++i) {
2461 int type;
2462 double x, y;
2463 s >> type;
2464 s >> x;
2465 s >> y;
2466 Q_ASSERT(type >= 0 && type <= 3);
2467 if (!isValidCoord(qreal(x)) || !isValidCoord(qreal(y))) {
2468#ifndef QT_NO_DEBUG
2469 qWarning("QDataStream::operator>>: Invalid QPainterPath coordinates read, skipping it");
2470#endif
2471 errorDetected = true;
2472 continue;
2473 }
2474 QPainterPath::Element elm = { qreal(x), qreal(y), QPainterPath::ElementType(type) };
2475 p.d_func()->elements.append(elm);
2476 }
2477 s >> p.d_func()->cStart;
2478 int fillRule;
2479 s >> fillRule;
2480 Q_ASSERT(fillRule == Qt::OddEvenFill || fillRule == Qt::WindingFill);
2481 p.d_func()->hasWindingFill = (Qt::FillRule(fillRule) == Qt::WindingFill);
2482 if (errorDetected || p.d_func()->elements.isEmpty())
2483 p = QPainterPath(); // Better than to return path with possibly corrupt datastructure, which would likely cause crash
2484 return s;
2485}
2486#endif // QT_NO_DATASTREAM
2487
2488
2489/*******************************************************************************
2490 * class QPainterPathStroker
2491 */
2492
2493void qt_path_stroke_move_to(qfixed x, qfixed y, void *data)
2494{
2495 ((QPainterPath *) data)->moveTo(qt_fixed_to_real(x), qt_fixed_to_real(y));
2496}
2497
2498void qt_path_stroke_line_to(qfixed x, qfixed y, void *data)
2499{
2500 ((QPainterPath *) data)->lineTo(qt_fixed_to_real(x), qt_fixed_to_real(y));
2501}
2502
2503void qt_path_stroke_cubic_to(qfixed c1x, qfixed c1y,
2504 qfixed c2x, qfixed c2y,
2505 qfixed ex, qfixed ey,
2506 void *data)
2507{
2508 ((QPainterPath *) data)->cubicTo(qt_fixed_to_real(c1x), qt_fixed_to_real(c1y),
2509 qt_fixed_to_real(c2x), qt_fixed_to_real(c2y),
2510 qt_fixed_to_real(ex), qt_fixed_to_real(ey));
2511}
2512
2513/*!
2514 \since 4.1
2515 \class QPainterPathStroker
2516 \ingroup painting
2517 \inmodule QtGui
2518
2519 \brief The QPainterPathStroker class is used to generate fillable
2520 outlines for a given painter path.
2521
2522 By calling the createStroke() function, passing a given
2523 QPainterPath as argument, a new painter path representing the
2524 outline of the given path is created. The newly created painter
2525 path can then be filled to draw the original painter path's
2526 outline.
2527
2528 You can control the various design aspects (width, cap styles,
2529 join styles and dash pattern) of the outlining using the following
2530 functions:
2531
2532 \list
2533 \li setWidth()
2534 \li setCapStyle()
2535 \li setJoinStyle()
2536 \li setDashPattern()
2537 \endlist
2538
2539 The setDashPattern() function accepts both a Qt::PenStyle object
2540 and a list representation of the pattern as argument.
2541
2542 In addition you can specify a curve's threshold, controlling the
2543 granularity with which a curve is drawn, using the
2544 setCurveThreshold() function. The default threshold is a well
2545 adjusted value (0.25), and normally you should not need to modify
2546 it. However, you can make the curve's appearance smoother by
2547 decreasing its value.
2548
2549 You can also control the miter limit for the generated outline
2550 using the setMiterLimit() function. The miter limit describes how
2551 far from each join the miter join can extend. The limit is
2552 specified in the units of width so the pixelwise miter limit will
2553 be \c {miterlimit * width}. This value is only used if the join
2554 style is Qt::MiterJoin.
2555
2556 The painter path generated by the createStroke() function should
2557 only be used for outlining the given painter path. Otherwise it
2558 may cause unexpected behavior. Generated outlines also require the
2559 Qt::WindingFill rule which is set by default.
2560
2561 \sa QPen, QBrush
2562*/
2563
2565 : dashOffset(0)
2566{
2567 stroker.setMoveToHook(qt_path_stroke_move_to);
2568 stroker.setLineToHook(qt_path_stroke_line_to);
2569 stroker.setCubicToHook(qt_path_stroke_cubic_to);
2570}
2571
2572/*!
2573 Creates a new stroker.
2574 */
2575QPainterPathStroker::QPainterPathStroker()
2576 : d_ptr(new QPainterPathStrokerPrivate)
2577{
2578}
2579
2580/*!
2581 Creates a new stroker based on \a pen.
2582
2583 \since 5.3
2584 */
2585QPainterPathStroker::QPainterPathStroker(const QPen &pen)
2586 : d_ptr(new QPainterPathStrokerPrivate)
2587{
2588 setWidth(pen.widthF());
2589 setCapStyle(pen.capStyle());
2590 setJoinStyle(pen.joinStyle());
2591 setMiterLimit(pen.miterLimit());
2592 setDashOffset(pen.dashOffset());
2593
2594 if (pen.style() == Qt::CustomDashLine)
2595 setDashPattern(pen.dashPattern());
2596 else
2597 setDashPattern(pen.style());
2598}
2599
2600/*!
2601 Destroys the stroker.
2602*/
2603QPainterPathStroker::~QPainterPathStroker()
2604{
2605}
2606
2607
2608/*!
2609 Generates a new path that is a fillable area representing the
2610 outline of the given \a path.
2611
2612 The various design aspects of the outline are based on the
2613 stroker's properties: width(), capStyle(), joinStyle(),
2614 dashPattern(), curveThreshold() and miterLimit().
2615
2616 The generated path should only be used for outlining the given
2617 painter path. Otherwise it may cause unexpected
2618 behavior. Generated outlines also require the Qt::WindingFill rule
2619 which is set by default.
2620*/
2621QPainterPath QPainterPathStroker::createStroke(const QPainterPath &path) const
2622{
2623 QPainterPathStrokerPrivate *d = const_cast<QPainterPathStrokerPrivate *>(d_func());
2624 QPainterPath stroke;
2625 if (path.isEmpty())
2626 return path;
2627 if (d->dashPattern.isEmpty()) {
2628 d->stroker.strokePath(path, &stroke, QTransform());
2629 } else {
2630 QDashStroker dashStroker(&d->stroker);
2631 dashStroker.setDashPattern(d->dashPattern);
2632 dashStroker.setDashOffset(d->dashOffset);
2633 dashStroker.setClipRect(d->stroker.clipRect());
2634 dashStroker.strokePath(path, &stroke, QTransform());
2635 }
2636 stroke.setFillRule(Qt::WindingFill);
2637 return stroke;
2638}
2639
2640/*!
2641 Sets the width of the generated outline painter path to \a width.
2642
2643 The generated outlines will extend approximately 50% of \a width
2644 to each side of the given input path's original outline.
2645*/
2646void QPainterPathStroker::setWidth(qreal width)
2647{
2648 Q_D(QPainterPathStroker);
2649 if (width <= 0)
2650 width = 1;
2651 d->stroker.setStrokeWidth(qt_real_to_fixed(width));
2652}
2653
2654/*!
2655 Returns the width of the generated outlines.
2656*/
2657qreal QPainterPathStroker::width() const
2658{
2659 return qt_fixed_to_real(d_func()->stroker.strokeWidth());
2660}
2661
2662
2663/*!
2664 Sets the cap style of the generated outlines to \a style. If a
2665 dash pattern is set, each segment of the pattern is subject to the
2666 cap \a style.
2667*/
2668void QPainterPathStroker::setCapStyle(Qt::PenCapStyle style)
2669{
2670 d_func()->stroker.setCapStyle(style);
2671}
2672
2673
2674/*!
2675 Returns the cap style of the generated outlines.
2676*/
2677Qt::PenCapStyle QPainterPathStroker::capStyle() const
2678{
2679 return d_func()->stroker.capStyle();
2680}
2681
2682/*!
2683 Sets the join style of the generated outlines to \a style.
2684*/
2685void QPainterPathStroker::setJoinStyle(Qt::PenJoinStyle style)
2686{
2687 d_func()->stroker.setJoinStyle(style);
2688}
2689
2690/*!
2691 Returns the join style of the generated outlines.
2692*/
2693Qt::PenJoinStyle QPainterPathStroker::joinStyle() const
2694{
2695 return d_func()->stroker.joinStyle();
2696}
2697
2698/*!
2699 Sets the miter limit of the generated outlines to \a limit.
2700
2701 The miter limit describes how far from each join the miter join
2702 can extend. The limit is specified in units of the currently set
2703 width. So the pixelwise miter limit will be \c { miterlimit *
2704 width}.
2705
2706 This value is only used if the join style is Qt::MiterJoin.
2707*/
2708void QPainterPathStroker::setMiterLimit(qreal limit)
2709{
2710 d_func()->stroker.setMiterLimit(qt_real_to_fixed(limit));
2711}
2712
2713/*!
2714 Returns the miter limit for the generated outlines.
2715*/
2716qreal QPainterPathStroker::miterLimit() const
2717{
2718 return qt_fixed_to_real(d_func()->stroker.miterLimit());
2719}
2720
2721
2722/*!
2723 Specifies the curve flattening \a threshold, controlling the
2724 granularity with which the generated outlines' curve is drawn.
2725
2726 The default threshold is a well adjusted value (0.25), and
2727 normally you should not need to modify it. However, you can make
2728 the curve's appearance smoother by decreasing its value.
2729*/
2730void QPainterPathStroker::setCurveThreshold(qreal threshold)
2731{
2732 d_func()->stroker.setCurveThreshold(qt_real_to_fixed(threshold));
2733}
2734
2735/*!
2736 Returns the curve flattening threshold for the generated
2737 outlines.
2738*/
2739qreal QPainterPathStroker::curveThreshold() const
2740{
2741 return qt_fixed_to_real(d_func()->stroker.curveThreshold());
2742}
2743
2744/*!
2745 Sets the dash pattern for the generated outlines to \a style.
2746*/
2747void QPainterPathStroker::setDashPattern(Qt::PenStyle style)
2748{
2749 d_func()->dashPattern = QDashStroker::patternForStyle(style);
2750}
2751
2752/*!
2753 \overload
2754
2755 Sets the dash pattern for the generated outlines to \a
2756 dashPattern. This function makes it possible to specify custom
2757 dash patterns.
2758
2759 Each element in the list contains the lengths of the dashes and spaces
2760 in the stroke, beginning with the first dash in the first element, the
2761 first space in the second element, and alternating between dashes and
2762 spaces for each following pair of elements.
2763
2764 The list can contain an odd number of elements, in which case the last
2765 element will be extended by the length of the first element when the
2766 pattern repeats.
2767*/
2768void QPainterPathStroker::setDashPattern(const QList<qreal> &dashPattern)
2769{
2770 d_func()->dashPattern.clear();
2771 for (int i=0; i<dashPattern.size(); ++i)
2772 d_func()->dashPattern << qt_real_to_fixed(dashPattern.at(i));
2773}
2774
2775/*!
2776 Returns the dash pattern for the generated outlines.
2777*/
2778QList<qreal> QPainterPathStroker::dashPattern() const
2779{
2780 return d_func()->dashPattern;
2781}
2782
2783/*!
2784 Returns the dash offset for the generated outlines.
2785 */
2786qreal QPainterPathStroker::dashOffset() const
2787{
2788 return d_func()->dashOffset;
2789}
2790
2791/*!
2792 Sets the dash offset for the generated outlines to \a offset.
2793
2794 See the documentation for QPen::setDashOffset() for a description of the
2795 dash offset.
2796 */
2797void QPainterPathStroker::setDashOffset(qreal offset)
2798{
2799 d_func()->dashOffset = offset;
2800}
2801
2802/*!
2803 Converts the path into a polygon using the QTransform
2804 \a matrix, and returns the polygon.
2805
2806 The polygon is created by first converting all subpaths to
2807 polygons, then using a rewinding technique to make sure that
2808 overlapping subpaths can be filled using the correct fill rule.
2809
2810 Note that rewinding inserts addition lines in the polygon so
2811 the outline of the fill polygon does not match the outline of
2812 the path.
2813
2814 \sa toSubpathPolygons(), toFillPolygons(),
2815 {QPainterPath#QPainterPath Conversion}{QPainterPath Conversion}
2816*/
2817QPolygonF QPainterPath::toFillPolygon(const QTransform &matrix) const
2818{
2819 const QList<QPolygonF> flats = toSubpathPolygons(matrix);
2820 QPolygonF polygon;
2821 if (flats.isEmpty())
2822 return polygon;
2823 QPointF first = flats.first().first();
2824 for (int i=0; i<flats.size(); ++i) {
2825 polygon += flats.at(i);
2826 if (!flats.at(i).isClosed())
2827 polygon += flats.at(i).first();
2828 if (i > 0)
2829 polygon += first;
2830 }
2831 return polygon;
2832}
2833
2834/*!
2835 Returns true if caching is enabled; otherwise returns false.
2836
2837 \since 6.10
2838 \sa setCachingEnabled()
2839*/
2840bool QPainterPath::isCachingEnabled() const
2841{
2842 Q_D(QPainterPath);
2843 return d && d->cacheEnabled;
2844}
2845
2846/*!
2847 Enables or disables length caching according to the value of \a enabled.
2848
2849 Enabling caching speeds up repeated calls to the member functions involving path length
2850 and percentage values, such as length(), percentAtLength(), pointAtPercent() etc., at the cost
2851 of some extra memory usage for storage of intermediate calculations. By default it is disabled.
2852
2853 Disabling caching will release any allocated cache memory.
2854
2855 \since 6.10
2856 \sa isCachingEnabled(), length(), percentAtLength(), pointAtPercent(), trimmed()
2857*/
2858void QPainterPath::setCachingEnabled(bool enabled)
2859{
2860 ensureData();
2861 if (d_func()->cacheEnabled == enabled)
2862 return;
2863 setDirty(true);
2864 QPainterPathPrivate *d = d_func();
2865 d->cacheEnabled = enabled;
2866 if (!enabled) {
2867 d->m_runLengths.clear();
2868 d->m_runLengths.squeeze();
2869 }
2870}
2871
2872//derivative of the equation
2873static inline qreal slopeAt(qreal t, qreal a, qreal b, qreal c, qreal d)
2874{
2875 return 3*t*t*(d - 3*c + 3*b - a) + 6*t*(c - 2*b + a) + 3*(b - a);
2876}
2877
2878/*!
2879 Returns the length of the current path.
2880*/
2881qreal QPainterPath::length() const
2882{
2883 Q_D(QPainterPath);
2884 if (isEmpty())
2885 return 0;
2886 if (d->cacheEnabled) {
2887 if (d->dirtyRunLengths)
2888 d->computeRunLengths();
2889 return d->m_runLengths.last();
2890 }
2891
2892 qreal len = 0;
2893 for (int i=1; i<d->elements.size(); ++i) {
2894 const Element &e = d->elements.at(i);
2895
2896 switch (e.type) {
2897 case MoveToElement:
2898 break;
2899 case LineToElement:
2900 {
2901 len += QLineF(d->elements.at(i-1), e).length();
2902 break;
2903 }
2904 case CurveToElement:
2905 {
2906 QBezier b = QBezier::fromPoints(d->elements.at(i-1),
2907 e,
2908 d->elements.at(i+1),
2909 d->elements.at(i+2));
2910 len += b.length();
2911 i += 2;
2912 break;
2913 }
2914 default:
2915 break;
2916 }
2917 }
2918 return len;
2919}
2920
2921/*!
2922 Returns percentage of the whole path at the specified length \a len.
2923
2924 Note that similarly to other percent methods, the percentage measurement
2925 is not linear with regards to the length, if curves are present
2926 in the path. When curves are present the percentage argument is mapped
2927 to the t parameter of the Bezier equations.
2928*/
2929qreal QPainterPath::percentAtLength(qreal len) const
2930{
2931 Q_D(QPainterPath);
2932 if (isEmpty() || len <= 0)
2933 return 0;
2934
2935 qreal totalLength = length();
2936 if (len > totalLength)
2937 return 1;
2938
2939 Q_ASSERT(totalLength != 0);
2940
2941 if (d->cacheEnabled) {
2942 const int ei = qMax(d->elementAtT(len / totalLength), 1); // Skip initial MoveTo
2943 qreal res = 0;
2944 const QPainterPath::Element &e = d->elements[ei];
2945 switch (e.type) {
2946 case QPainterPath::LineToElement:
2947 res = len / totalLength;
2948 break;
2949 case CurveToElement:
2950 {
2951 QBezier b = QBezier::fromPoints(d->elements.at(ei-1),
2952 e,
2953 d->elements.at(ei+1),
2954 d->elements.at(ei+2));
2955 qreal prevLen = d->m_runLengths[ei - 1];
2956 qreal blen = d->m_runLengths[ei] - prevLen;
2957 qreal elemRes = b.tAtLength(len - prevLen);
2958 res = (elemRes * blen + prevLen) / totalLength;
2959 break;
2960 }
2961 default:
2962 Q_UNREACHABLE();
2963 }
2964 return res;
2965 }
2966
2967 qreal curLen = 0;
2968 for (int i=1; i<d->elements.size(); ++i) {
2969 const Element &e = d->elements.at(i);
2970
2971 switch (e.type) {
2972 case MoveToElement:
2973 break;
2974 case LineToElement:
2975 {
2976 QLineF line(d->elements.at(i-1), e);
2977 qreal llen = line.length();
2978 curLen += llen;
2979 if (curLen >= len) {
2980 return len/totalLength ;
2981 }
2982
2983 break;
2984 }
2985 case CurveToElement:
2986 {
2987 QBezier b = QBezier::fromPoints(d->elements.at(i-1),
2988 e,
2989 d->elements.at(i+1),
2990 d->elements.at(i+2));
2991 qreal blen = b.length();
2992 qreal prevLen = curLen;
2993 curLen += blen;
2994
2995 if (curLen >= len) {
2996 qreal res = b.tAtLength(len - prevLen);
2997 return (res * blen + prevLen)/totalLength;
2998 }
2999
3000 i += 2;
3001 break;
3002 }
3003 default:
3004 break;
3005 }
3006 }
3007
3008 return 0;
3009}
3010
3011static inline QBezier uncached_bezierAtT(const QPainterPath &path, qreal t, qreal *startingLength,
3012 qreal *bezierLength)
3013{
3014 *startingLength = 0;
3015 if (t > 1)
3016 return QBezier();
3017
3018 qreal curLen = 0;
3019 qreal totalLength = path.length();
3020
3021 const int lastElement = path.elementCount() - 1;
3022 for (int i=0; i <= lastElement; ++i) {
3023 const QPainterPath::Element &e = path.elementAt(i);
3024
3025 switch (e.type) {
3026 case QPainterPath::MoveToElement:
3027 break;
3028 case QPainterPath::LineToElement:
3029 {
3030 QLineF line(path.elementAt(i-1), e);
3031 qreal llen = line.length();
3032 curLen += llen;
3033 if (i == lastElement || curLen/totalLength >= t) {
3034 *bezierLength = llen;
3035 QPointF a = path.elementAt(i-1);
3036 QPointF delta = e - a;
3037 return QBezier::fromPoints(a, a + delta / 3, a + 2 * delta / 3, e);
3038 }
3039 break;
3040 }
3041 case QPainterPath::CurveToElement:
3042 {
3043 QBezier b = QBezier::fromPoints(path.elementAt(i-1),
3044 e,
3045 path.elementAt(i+1),
3046 path.elementAt(i+2));
3047 qreal blen = b.length();
3048 curLen += blen;
3049
3050 if (i + 2 == lastElement || curLen/totalLength >= t) {
3051 *bezierLength = blen;
3052 return b;
3053 }
3054
3055 i += 2;
3056 break;
3057 }
3058 default:
3059 break;
3060 }
3061 *startingLength = curLen;
3062 }
3063 return QBezier();
3064}
3065
3066QBezier QPainterPathPrivate::bezierAtT(const QPainterPath &path, qreal t, qreal *startingLength,
3067 qreal *bezierLength) const
3068{
3069 Q_ASSERT(t >= 0 && t <= 1);
3070 QPainterPathPrivate *d = path.d_func();
3071 if (!path.isEmpty() && d->cacheEnabled) {
3072 const int ei = qMax(d->elementAtT(t), 1); // Avoid the initial MoveTo element
3073 const qreal prevRunLength = d->m_runLengths[ei - 1];
3074 *startingLength = prevRunLength;
3075 *bezierLength = d->m_runLengths[ei] - prevRunLength;
3076 const QPointF prev = d->elements[ei - 1];
3077 const QPainterPath::Element &e = d->elements[ei];
3078 switch (e.type) {
3079 case QPainterPath::LineToElement:
3080 {
3081 QPointF delta = (e - prev) / 3;
3082 return QBezier::fromPoints(prev, prev + delta, prev + 2 * delta, e);
3083 }
3084 case QPainterPath::CurveToElement:
3085 return QBezier::fromPoints(prev, e, elements[ei + 1], elements[ei + 2]);
3086 break;
3087 default:
3088 Q_UNREACHABLE();
3089 }
3090 }
3091
3092 return uncached_bezierAtT(path, t, startingLength, bezierLength);
3093}
3094
3095/*!
3096 Returns the point at at the percentage \a t of the current path.
3097 The argument \a t has to be between 0 and 1.
3098
3099 Note that similarly to other percent methods, the percentage measurement
3100 is not linear with regards to the length, if curves are present
3101 in the path. When curves are present the percentage argument is mapped
3102 to the t parameter of the Bezier equations.
3103*/
3104QPointF QPainterPath::pointAtPercent(qreal t) const
3105{
3106 if (t < 0 || t > 1) {
3107 qWarning("QPainterPath::pointAtPercent accepts only values between 0 and 1");
3108 return QPointF();
3109 }
3110
3111 if (!d_ptr || d_ptr->elements.size() == 0)
3112 return QPointF();
3113
3114 if (d_ptr->elements.size() == 1)
3115 return d_ptr->elements.at(0);
3116
3117 qreal totalLength = length();
3118 qreal curLen = 0;
3119 qreal bezierLen = 0;
3120 QBezier b = d_ptr->bezierAtT(*this, t, &curLen, &bezierLen);
3121 Q_ASSERT(bezierLen != 0);
3122 qreal realT = (totalLength * t - curLen) / bezierLen;
3123
3124 return b.pointAt(qBound(qreal(0), realT, qreal(1)));
3125}
3126
3127/*!
3128 Returns the angle of the path tangent at the percentage \a t.
3129 The argument \a t has to be between 0 and 1.
3130
3131 Positive values for the angles mean counter-clockwise while negative values
3132 mean the clockwise direction. Zero degrees is at the 3 o'clock position.
3133
3134 Note that similarly to the other percent methods, the percentage measurement
3135 is not linear with regards to the length if curves are present
3136 in the path. When curves are present the percentage argument is mapped
3137 to the t parameter of the Bezier equations.
3138*/
3139qreal QPainterPath::angleAtPercent(qreal t) const
3140{
3141 if (t < 0 || t > 1) {
3142 qWarning("QPainterPath::angleAtPercent accepts only values between 0 and 1");
3143 return 0;
3144 }
3145
3146 if (isEmpty())
3147 return 0;
3148
3149 qreal totalLength = length();
3150 qreal curLen = 0;
3151 qreal bezierLen = 0;
3152 QBezier bez = d_ptr->bezierAtT(*this, t, &curLen, &bezierLen);
3153 Q_ASSERT(bezierLen != 0);
3154 qreal realT = (totalLength * t - curLen) / bezierLen;
3155
3156 qreal m1 = slopeAt(realT, bez.x1, bez.x2, bez.x3, bez.x4);
3157 qreal m2 = slopeAt(realT, bez.y1, bez.y2, bez.y3, bez.y4);
3158
3159 return QLineF(0, 0, m1, m2).angle();
3160}
3161
3162
3163/*!
3164 Returns the slope of the path at the percentage \a t. The
3165 argument \a t has to be between 0 and 1.
3166
3167 Note that similarly to other percent methods, the percentage measurement
3168 is not linear with regards to the length, if curves are present
3169 in the path. When curves are present the percentage argument is mapped
3170 to the t parameter of the Bezier equations.
3171*/
3172qreal QPainterPath::slopeAtPercent(qreal t) const
3173{
3174 if (t < 0 || t > 1) {
3175 qWarning("QPainterPath::slopeAtPercent accepts only values between 0 and 1");
3176 return 0;
3177 }
3178
3179 if (isEmpty())
3180 return 0;
3181
3182 qreal totalLength = length();
3183 qreal curLen = 0;
3184 qreal bezierLen = 0;
3185 QBezier bez = d_ptr->bezierAtT(*this, t, &curLen, &bezierLen);
3186 Q_ASSERT(bezierLen != 0);
3187 qreal realT = (totalLength * t - curLen) / bezierLen;
3188
3189 qreal m1 = slopeAt(realT, bez.x1, bez.x2, bez.x3, bez.x4);
3190 qreal m2 = slopeAt(realT, bez.y1, bez.y2, bez.y3, bez.y4);
3191 //tangent line
3192 qreal slope = 0;
3193
3194 if (m1)
3195 slope = m2/m1;
3196 else {
3197 if (std::numeric_limits<qreal>::has_infinity) {
3198 slope = (m2 < 0) ? -std::numeric_limits<qreal>::infinity()
3199 : std::numeric_limits<qreal>::infinity();
3200 } else {
3201 if (sizeof(qreal) == sizeof(double)) {
3202 return 1.79769313486231570e+308;
3203 } else {
3204 return ((qreal)3.40282346638528860e+38);
3205 }
3206 }
3207 }
3208
3209 return slope;
3210}
3211
3212/*!
3213 \since 6.10
3214
3215 Returns the section of the path between the length fractions \a fromFraction and \a toFraction.
3216 The effective range of the fractions are from 0, denoting the start point of the path, to 1,
3217 denoting its end point. The fractions are linear with respect to path length, in contrast to the
3218 percentage \e t values.
3219
3220 The value of \a offset will be added to the fraction values. If that causes an over- or underflow
3221 of the [0, 1] range, the values will be wrapped around, as will the resulting path. The effective
3222 range of the offset is between -1 and 1.
3223
3224 Repeated calls to this function can be optimized by {enabling caching}{setCachingEnabled()}.
3225
3226 \sa length(), percentAtLength(), setCachingEnabled()
3227*/
3228
3229QPainterPath QPainterPath::trimmed(qreal fromFraction, qreal toFraction, qreal offset) const
3230{
3231 if (isEmpty())
3232 return *this;
3233
3234 // We need length caching enabled for the calculations.
3235 if (!isCachingEnabled()) {
3236 QPainterPath copy(*this);
3237 copy.setCachingEnabled(true);
3238 return copy.trimmed(fromFraction, toFraction, offset);
3239 }
3240
3241 qreal f1 = qBound(qreal(0), fromFraction, qreal(1));
3242 qreal f2 = qBound(qreal(0), toFraction, qreal(1));
3243 if (qFuzzyIsNull(f1 - f2)) // ie. f1 == f2 (even if one of them is 0.0)
3244 return QPainterPath();
3245 if (f1 > f2)
3246 qSwap(f1, f2);
3247 if (qFuzzyCompare(f2 - f1, qreal(1))) // Shortcut for no trimming
3248 return *this;
3249
3250 QPainterPath res;
3251 res.setFillRule(fillRule());
3252
3253 if (offset) {
3254 qreal dummy;
3255 offset = std::modf(offset, &dummy); // Use only the fractional part of offset, range <-1, 1>
3256
3257 qreal of1 = f1 + offset;
3258 qreal of2 = f2 + offset;
3259 if (offset < 0) {
3260 f1 = of1 < 0 ? of1 + 1 : of1;
3261 f2 = of2 + 1 > 1 ? of2 : of2 + 1;
3262 } else if (offset > 0) {
3263 f1 = of1 - 1 < 0 ? of1 : of1 - 1;
3264 f2 = of2 > 1 ? of2 - 1 : of2;
3265 }
3266 }
3267 const bool wrapping = (f1 > f2);
3268 //qDebug() << "ADJ:" << f1 << f2 << wrapping << "(" << of1 << of2 << ")";
3269
3270 QPainterPathPrivate *d = d_func();
3271 if (d->dirtyRunLengths)
3272 d->computeRunLengths();
3273 const qreal totalLength = d->m_runLengths.last();
3274 if (qFuzzyIsNull(totalLength))
3275 return res;
3276
3277 const qreal l1 = f1 * totalLength;
3278 const qreal l2 = f2 * totalLength;
3279 const int e1 = d->elementAtLength(l1);
3280 const bool mustTrimE1 = !QtPrivate::fuzzyCompare(d->m_runLengths.at(e1), l1);
3281 const int e2 = d->elementAtLength(l2);
3282 const bool mustTrimE2 = !QtPrivate::fuzzyCompare(d->m_runLengths.at(e2), l2);
3283
3284 //qDebug() << "Trim [" << f1 << f2 << "] e1:" << e1 << mustTrimE1 << "e2:" << e2 << mustTrimE2 << "wrapping:" << wrapping;
3285 if (e1 == e2 && !wrapping && mustTrimE1 && mustTrimE2) {
3286 // Entire result is one element, clipped in both ends
3287 d->appendSliceOfElement(&res, e1, l1, l2);
3288 } else {
3289 // Add partial start element (or just its end point, being the start of the next)
3290 if (mustTrimE1)
3291 d->appendEndOfElement(&res, e1, l1);
3292 else
3293 res.moveTo(d->endPointOfElement(e1));
3294
3295 // Add whole elements between start and end
3296 int firstWholeElement = e1 + 1;
3297 int lastWholeElement = (mustTrimE2 ? e2 - 1 : e2);
3298 if (!wrapping) {
3299 d->appendElementRange(&res, firstWholeElement, lastWholeElement);
3300 } else {
3301 int lastIndex = d->elements.size() - 1;
3302 d->appendElementRange(&res, firstWholeElement, lastIndex);
3303 bool isClosed = (QPointF(d->elements.at(0)) == QPointF(d->elements.at(lastIndex)));
3304 // If closed we can skip the initial moveto
3305 d->appendElementRange(&res, (isClosed ? 1 : 0), lastWholeElement);
3306 }
3307
3308 // Partial end element
3309 if (mustTrimE2)
3310 d->appendStartOfElement(&res, e2, l2);
3311 }
3312
3313 return res;
3314}
3315
3316void QPainterPathPrivate::appendTrimmedElement(QPainterPath *to, int elemIdx, int trimFlags,
3317 qreal startLen, qreal endLen)
3318{
3319 Q_ASSERT(cacheEnabled);
3320 Q_ASSERT(!dirtyRunLengths);
3321
3322 if (elemIdx <= 0 || elemIdx >= elements.size())
3323 return;
3324
3325 const qreal prevLen = m_runLengths.at(elemIdx - 1);
3326 const qreal elemLen = m_runLengths.at(elemIdx) - prevLen;
3327 const qreal len1 = startLen - prevLen;
3328 const qreal len2 = endLen - prevLen;
3329 if (qFuzzyIsNull(elemLen))
3330 return;
3331
3332 const QPointF pp = elements.at(elemIdx - 1);
3333 const QPainterPath::Element e = elements.at(elemIdx);
3334 if (e.isLineTo()) {
3335 QLineF l(pp, e);
3336 QPointF p1 = (trimFlags & TrimStart) ? l.pointAt(len1 / elemLen) : pp;
3337 QPointF p2 = (trimFlags & TrimEnd) ? l.pointAt(len2 / elemLen) : e;
3338 if (to->isEmpty())
3339 to->moveTo(p1);
3340 to->lineTo(p2);
3341 } else if (e.isCurveTo()) {
3342 Q_ASSERT(elemIdx < elements.size() - 2);
3343 QBezier b = QBezier::fromPoints(pp, e, elements.at(elemIdx + 1), elements.at(elemIdx + 2));
3344 qreal t1 = (trimFlags & TrimStart) ? b.tAtLength(len1) : 0.0; // or simply len1/elemLen to trim by t instead of len
3345 qreal t2 = (trimFlags & TrimEnd) ? b.tAtLength(len2) : 1.0;
3346 QBezier c = b.getSubRange(t1, t2);
3347 if (to->isEmpty())
3348 to->moveTo(c.pt1());
3349 to->cubicTo(c.pt2(), c.pt3(), c.pt4());
3350 } else {
3351 Q_UNREACHABLE();
3352 }
3353}
3354
3355void QPainterPathPrivate::appendElementRange(QPainterPath *to, int first, int last)
3356{
3357 if (first < 0 || first >= elements.size() || last < 0 || last >= elements.size())
3358 return;
3359
3360 // (Could optimize by direct copy of elements, but must ensure correct state flags)
3361 for (int i = first; i <= last; i++) {
3362 const QPainterPath::Element &e = elements.at(i);
3363 switch (e.type) {
3364 case QPainterPath::MoveToElement:
3365 to->moveTo(e);
3366 break;
3367 case QPainterPath::LineToElement:
3368 to->lineTo(e);
3369 break;
3370 case QPainterPath::CurveToElement:
3371 Q_ASSERT(i < elements.size() - 2);
3372 to->cubicTo(e, elements.at(i + 1), elements.at(i + 2));
3373 i += 2;
3374 break;
3375 default:
3376 // 'first' may point to CurveToData element, just skip it
3377 break;
3378 }
3379 }
3380}
3381
3382
3383/*!
3384 \since 4.4
3385
3386 Adds the given rectangle \a rect with rounded corners to the path.
3387
3388 The \a xRadius and \a yRadius arguments specify the radii of
3389 the ellipses defining the corners of the rounded rectangle.
3390 When \a mode is Qt::RelativeSize, \a xRadius and
3391 \a yRadius are specified in percentage of half the rectangle's
3392 width and height respectively, and should be in the range 0.0 to 100.0.
3393
3394 \sa addRect()
3395*/
3396void QPainterPath::addRoundedRect(const QRectF &rect, qreal xRadius, qreal yRadius,
3397 Qt::SizeMode mode)
3398{
3399 QRectF r = rect.normalized();
3400
3401 if (r.isNull())
3402 return;
3403
3404 if (mode == Qt::AbsoluteSize) {
3405 qreal w = r.width() / 2;
3406 qreal h = r.height() / 2;
3407
3408 if (w == 0) {
3409 xRadius = 0;
3410 } else {
3411 xRadius = 100 * qMin(xRadius, w) / w;
3412 }
3413 if (h == 0) {
3414 yRadius = 0;
3415 } else {
3416 yRadius = 100 * qMin(yRadius, h) / h;
3417 }
3418 } else {
3419 if (xRadius > 100) // fix ranges
3420 xRadius = 100;
3421
3422 if (yRadius > 100)
3423 yRadius = 100;
3424 }
3425
3426 if (xRadius <= 0 || yRadius <= 0) { // add normal rectangle
3427 addRect(r);
3428 return;
3429 }
3430
3431 qreal x = r.x();
3432 qreal y = r.y();
3433 qreal w = r.width();
3434 qreal h = r.height();
3435 qreal rxx2 = w*xRadius/100;
3436 qreal ryy2 = h*yRadius/100;
3437
3438 ensureData();
3439 setDirty(true);
3440
3441 bool first = d_func()->elements.size() < 2;
3442
3443 arcMoveTo(x, y, rxx2, ryy2, 180);
3444 arcTo(x, y, rxx2, ryy2, 180, -90);
3445 arcTo(x+w-rxx2, y, rxx2, ryy2, 90, -90);
3446 arcTo(x+w-rxx2, y+h-ryy2, rxx2, ryy2, 0, -90);
3447 arcTo(x, y+h-ryy2, rxx2, ryy2, 270, -90);
3448 closeSubpath();
3449
3450 d_func()->require_moveTo = true;
3451 d_func()->convex = first;
3452}
3453
3454/*!
3455 \fn void QPainterPath::addRoundedRect(qreal x, qreal y, qreal w, qreal h, qreal xRadius, qreal yRadius, Qt::SizeMode mode = Qt::AbsoluteSize);
3456 \since 4.4
3457 \overload
3458
3459 Adds the given rectangle \a x, \a y, \a w, \a h with rounded corners to the path.
3460 */
3461
3462/*!
3463 \since 4.3
3464
3465 Returns a path which is the union of this path's fill area and \a p's fill area.
3466
3467 Set operations on paths will treat the paths as areas. Non-closed
3468 paths will be treated as implicitly closed.
3469 Bezier curves may be flattened to line segments due to numerical instability of
3470 doing bezier curve intersections.
3471
3472 \sa intersected(), subtracted()
3473*/
3474QPainterPath QPainterPath::united(const QPainterPath &p) const
3475{
3476 if (isEmpty() || p.isEmpty())
3477 return isEmpty() ? p : *this;
3478 QPathClipper clipper(*this, p);
3479 return clipper.clip(QPathClipper::BoolOr);
3480}
3481
3482/*!
3483 \since 4.3
3484
3485 Returns a path which is the intersection of this path's fill area and \a p's fill area.
3486 Bezier curves may be flattened to line segments due to numerical instability of
3487 doing bezier curve intersections.
3488*/
3489QPainterPath QPainterPath::intersected(const QPainterPath &p) const
3490{
3491 if (isEmpty() || p.isEmpty())
3492 return QPainterPath();
3493 QPathClipper clipper(*this, p);
3494 return clipper.clip(QPathClipper::BoolAnd);
3495}
3496
3497/*!
3498 \since 4.3
3499
3500 Returns a path which is \a p's fill area subtracted from this path's fill area.
3501
3502 Set operations on paths will treat the paths as areas. Non-closed
3503 paths will be treated as implicitly closed.
3504 Bezier curves may be flattened to line segments due to numerical instability of
3505 doing bezier curve intersections.
3506*/
3507QPainterPath QPainterPath::subtracted(const QPainterPath &p) const
3508{
3509 if (isEmpty() || p.isEmpty())
3510 return *this;
3511 QPathClipper clipper(*this, p);
3512 return clipper.clip(QPathClipper::BoolSub);
3513}
3514
3515/*!
3516 \since 4.4
3517
3518 Returns a simplified version of this path. This implies merging all subpaths that intersect,
3519 and returning a path containing no intersecting edges. Consecutive parallel lines will also
3520 be merged. The simplified path will always use the default fill rule, Qt::OddEvenFill.
3521 Bezier curves may be flattened to line segments due to numerical instability of
3522 doing bezier curve intersections.
3523*/
3524QPainterPath QPainterPath::simplified() const
3525{
3526 if (isEmpty())
3527 return *this;
3528 QPathClipper clipper(*this, QPainterPath());
3529 return clipper.clip(QPathClipper::Simplify);
3530}
3531
3532/*!
3533 \since 4.3
3534
3535 Returns \c true if the current path intersects at any point the given path \a p.
3536 Also returns \c true if the current path contains or is contained by any part of \a p.
3537
3538 Set operations on paths will treat the paths as areas. Non-closed
3539 paths will be treated as implicitly closed.
3540
3541 \sa contains()
3542 */
3543bool QPainterPath::intersects(const QPainterPath &p) const
3544{
3545 if (p.elementCount() == 1)
3546 return contains(p.elementAt(0));
3547 if (isEmpty() || p.isEmpty())
3548 return false;
3549 QPathClipper clipper(*this, p);
3550 return clipper.intersect();
3551}
3552
3553/*!
3554 \since 4.3
3555
3556 Returns \c true if the given path \a p is contained within
3557 the current path. Returns \c false if any edges of the current path and
3558 \a p intersect.
3559
3560 Set operations on paths will treat the paths as areas. Non-closed
3561 paths will be treated as implicitly closed.
3562
3563 \sa intersects()
3564 */
3565bool QPainterPath::contains(const QPainterPath &p) const
3566{
3567 if (p.elementCount() == 1)
3568 return contains(p.elementAt(0));
3569 if (isEmpty() || p.isEmpty())
3570 return false;
3571 QPathClipper clipper(*this, p);
3572 return clipper.contains();
3573}
3574
3575void QPainterPath::setDirty(bool dirty)
3576{
3577 d_func()->pathConverter.reset();
3578 d_func()->dirtyBounds = dirty;
3579 d_func()->dirtyControlBounds = dirty;
3580 d_func()->dirtyRunLengths = dirty;
3581 d_func()->convex = false;
3582}
3583
3584void QPainterPath::computeBoundingRect() const
3585{
3586 QPainterPathPrivate *d = d_func();
3587 d->dirtyBounds = false;
3588 if (!d_ptr) {
3589 d->bounds = QRect();
3590 return;
3591 }
3592
3593 qreal minx, maxx, miny, maxy;
3594 minx = maxx = d->elements.at(0).x;
3595 miny = maxy = d->elements.at(0).y;
3596 for (int i=1; i<d->elements.size(); ++i) {
3597 const Element &e = d->elements.at(i);
3598
3599 switch (e.type) {
3600 case MoveToElement:
3601 case LineToElement:
3602 if (e.x > maxx) maxx = e.x;
3603 else if (e.x < minx) minx = e.x;
3604 if (e.y > maxy) maxy = e.y;
3605 else if (e.y < miny) miny = e.y;
3606 break;
3607 case CurveToElement:
3608 {
3609 QBezier b = QBezier::fromPoints(d->elements.at(i-1),
3610 e,
3611 d->elements.at(i+1),
3612 d->elements.at(i+2));
3613 QRectF r = qt_painterpath_bezier_extrema(b);
3614 qreal right = r.right();
3615 qreal bottom = r.bottom();
3616 if (r.x() < minx) minx = r.x();
3617 if (right > maxx) maxx = right;
3618 if (r.y() < miny) miny = r.y();
3619 if (bottom > maxy) maxy = bottom;
3620 i += 2;
3621 }
3622 break;
3623 default:
3624 break;
3625 }
3626 }
3627 d->bounds = QRectF(minx, miny, maxx - minx, maxy - miny);
3628}
3629
3630
3631void QPainterPath::computeControlPointRect() const
3632{
3633 QPainterPathPrivate *d = d_func();
3634 d->dirtyControlBounds = false;
3635 if (!d_ptr) {
3636 d->controlBounds = QRect();
3637 return;
3638 }
3639
3640 qreal minx, maxx, miny, maxy;
3641 minx = maxx = d->elements.at(0).x;
3642 miny = maxy = d->elements.at(0).y;
3643 for (int i=1; i<d->elements.size(); ++i) {
3644 const Element &e = d->elements.at(i);
3645 if (e.x > maxx) maxx = e.x;
3646 else if (e.x < minx) minx = e.x;
3647 if (e.y > maxy) maxy = e.y;
3648 else if (e.y < miny) miny = e.y;
3649 }
3650 d->controlBounds = QRectF(minx, miny, maxx - minx, maxy - miny);
3651}
3652
3654{
3655 Q_ASSERT(!elements.isEmpty());
3656
3657 m_runLengths.clear();
3658 const int numElems = elements.size();
3659 m_runLengths.reserve(numElems);
3660
3661 QPointF runPt = elements[0];
3662 qreal runLen = 0.0;
3663 for (int i = 0; i < numElems; i++) {
3664 QPainterPath::Element e = elements[i];
3665 switch (e.type) {
3666 case QPainterPath::LineToElement:
3667 runLen += QLineF(runPt, e).length();
3668 runPt = e;
3669 break;
3670 case QPainterPath::CurveToElement: {
3671 Q_ASSERT(i < numElems - 2);
3672 QPainterPath::Element ee = elements[i + 2];
3673 runLen += QBezier::fromPoints(runPt, e, elements[i + 1], ee).length();
3674 runPt = ee;
3675 break;
3676 }
3677 case QPainterPath::MoveToElement:
3678 runPt = e;
3679 break;
3680 case QPainterPath::CurveToDataElement:
3681 break;
3682 }
3683 m_runLengths.append(runLen);
3684 }
3685 Q_ASSERT(m_runLengths.size() == elements.size());
3686
3687 dirtyRunLengths = false;
3688}
3689
3690#ifndef QT_NO_DEBUG_STREAM
3691QDebug operator<<(QDebug s, const QPainterPath &p)
3692{
3693 QDebugStateSaver saver(s);
3694 s.nospace() << "QPainterPath: Element count=" << p.elementCount() << Qt::endl;
3695 const char *types[] = {"MoveTo", "LineTo", "CurveTo", "CurveToData"};
3696 for (int i=0; i<p.elementCount(); ++i) {
3697 s.nospace() << " -> " << types[p.elementAt(i).type] << "(x=" << p.elementAt(i).x << ", y=" << p.elementAt(i).y << ')' << Qt::endl;
3698 }
3699 return s;
3700}
3701#endif
3702
3703QT_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