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