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