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