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