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 if (d->stroker.curveThresholdExplicitlySet())
2688 dashStroker.setCurveThreshold(d->stroker.curveThreshold());
2689 dashStroker.strokePath(path, &stroke, QTransform());
2690 }
2691 stroke.setFillRule(Qt::WindingFill);
2692 return stroke;
2693}
2694
2695/*!
2696 Sets the width of the generated outline painter path to \a width.
2697
2698 The generated outlines will extend approximately 50% of \a width
2699 to each side of the given input path's original outline.
2700*/
2701void QPainterPathStroker::setWidth(qreal width)
2702{
2703 Q_D(QPainterPathStroker);
2704 if (width <= 0)
2705 width = 1;
2706 d->stroker.setStrokeWidth(qt_real_to_fixed(width));
2707}
2708
2709/*!
2710 Returns the width of the generated outlines.
2711*/
2712qreal QPainterPathStroker::width() const
2713{
2714 return qt_fixed_to_real(d_func()->stroker.strokeWidth());
2715}
2716
2717
2718/*!
2719 Sets the cap style of the generated outlines to \a style. If a
2720 dash pattern is set, each segment of the pattern is subject to the
2721 cap \a style.
2722*/
2723void QPainterPathStroker::setCapStyle(Qt::PenCapStyle style)
2724{
2725 d_func()->stroker.setCapStyle(style);
2726}
2727
2728
2729/*!
2730 Returns the cap style of the generated outlines.
2731*/
2732Qt::PenCapStyle QPainterPathStroker::capStyle() const
2733{
2734 return d_func()->stroker.capStyle();
2735}
2736
2737/*!
2738 Sets the join style of the generated outlines to \a style.
2739*/
2740void QPainterPathStroker::setJoinStyle(Qt::PenJoinStyle style)
2741{
2742 d_func()->stroker.setJoinStyle(style);
2743}
2744
2745/*!
2746 Returns the join style of the generated outlines.
2747*/
2748Qt::PenJoinStyle QPainterPathStroker::joinStyle() const
2749{
2750 return d_func()->stroker.joinStyle();
2751}
2752
2753/*!
2754 Sets the miter limit of the generated outlines to \a limit.
2755
2756 The miter limit describes how far from each join the miter join
2757 can extend. The limit is specified in units of the currently set
2758 width. So the pixelwise miter limit will be \c { miterlimit *
2759 width}.
2760
2761 This value is only used if the join style is Qt::MiterJoin.
2762*/
2763void QPainterPathStroker::setMiterLimit(qreal limit)
2764{
2765 d_func()->stroker.setMiterLimit(qt_real_to_fixed(limit));
2766}
2767
2768/*!
2769 Returns the miter limit for the generated outlines.
2770*/
2771qreal QPainterPathStroker::miterLimit() const
2772{
2773 return qt_fixed_to_real(d_func()->stroker.miterLimit());
2774}
2775
2776
2777/*!
2778 Specifies the curve flattening \a threshold, controlling the
2779 granularity with which the generated outlines' curve is drawn.
2780
2781 The default threshold is a well adjusted value (0.25), and
2782 normally you should not need to modify it. However, you can make
2783 the curve's appearance smoother by decreasing its value.
2784*/
2785void QPainterPathStroker::setCurveThreshold(qreal threshold)
2786{
2787 d_func()->stroker.setCurveThreshold(qt_real_to_fixed(threshold));
2788}
2789
2790/*!
2791 Returns the curve flattening threshold for the generated
2792 outlines.
2793*/
2794qreal QPainterPathStroker::curveThreshold() const
2795{
2796 return qt_fixed_to_real(d_func()->stroker.curveThreshold());
2797}
2798
2799/*!
2800 Sets the dash pattern for the generated outlines to \a style.
2801*/
2802void QPainterPathStroker::setDashPattern(Qt::PenStyle style)
2803{
2804 d_func()->dashPattern = QDashStroker::patternForStyle(style);
2805}
2806
2807/*!
2808 \overload
2809
2810 Sets the dash pattern for the generated outlines to \a
2811 dashPattern. This function makes it possible to specify custom
2812 dash patterns.
2813
2814 Each element in the list contains the lengths of the dashes and spaces
2815 in the stroke, beginning with the first dash in the first element, the
2816 first space in the second element, and alternating between dashes and
2817 spaces for each following pair of elements.
2818
2819 The list can contain an odd number of elements, in which case the last
2820 element will be extended by the length of the first element when the
2821 pattern repeats.
2822*/
2823void QPainterPathStroker::setDashPattern(const QList<qreal> &dashPattern)
2824{
2825 d_func()->dashPattern.clear();
2826 for (int i=0; i<dashPattern.size(); ++i)
2827 d_func()->dashPattern << qt_real_to_fixed(dashPattern.at(i));
2828}
2829
2830/*!
2831 Returns the dash pattern for the generated outlines.
2832*/
2833QList<qreal> QPainterPathStroker::dashPattern() const
2834{
2835 return d_func()->dashPattern;
2836}
2837
2838/*!
2839 Returns the dash offset for the generated outlines.
2840 */
2841qreal QPainterPathStroker::dashOffset() const
2842{
2843 return d_func()->dashOffset;
2844}
2845
2846/*!
2847 Sets the dash offset for the generated outlines to \a offset.
2848
2849 See the documentation for QPen::setDashOffset() for a description of the
2850 dash offset.
2851 */
2852void QPainterPathStroker::setDashOffset(qreal offset)
2853{
2854 d_func()->dashOffset = offset;
2855}
2856
2857/*!
2858 Converts the path into a polygon using the QTransform
2859 \a matrix, and returns the polygon.
2860
2861 The polygon is created by first converting all subpaths to
2862 polygons, then using a rewinding technique to make sure that
2863 overlapping subpaths can be filled using the correct fill rule.
2864
2865 Note that rewinding inserts addition lines in the polygon so
2866 the outline of the fill polygon does not match the outline of
2867 the path.
2868
2869 \sa toSubpathPolygons(), toFillPolygons(),
2870 {QPainterPath#QPainterPath Conversion}{QPainterPath Conversion}
2871*/
2872QPolygonF QPainterPath::toFillPolygon(const QTransform &matrix) const
2873{
2874 const QList<QPolygonF> flats = toSubpathPolygons(matrix);
2875 QPolygonF polygon;
2876 if (flats.isEmpty())
2877 return polygon;
2878 QPointF first = flats.first().first();
2879 for (int i=0; i<flats.size(); ++i) {
2880 polygon += flats.at(i);
2881 if (!flats.at(i).isClosed())
2882 polygon += flats.at(i).first();
2883 if (i > 0)
2884 polygon += first;
2885 }
2886 return polygon;
2887}
2888
2889/*!
2890 Returns true if caching is enabled; otherwise returns false.
2891
2892 \since 6.10
2893 \sa setCachingEnabled()
2894*/
2895bool QPainterPath::isCachingEnabled() const
2896{
2897 Q_D(QPainterPath);
2898 return d && d->cacheEnabled;
2899}
2900
2901/*!
2902 Enables or disables length caching according to the value of \a enabled.
2903
2904 Enabling caching speeds up repeated calls to the member functions involving path length
2905 and percentage values, such as length(), percentAtLength(), pointAtPercent() etc., at the cost
2906 of some extra memory usage for storage of intermediate calculations. By default it is disabled.
2907
2908 Disabling caching will release any allocated cache memory.
2909
2910 \since 6.10
2911 \sa isCachingEnabled(), length(), percentAtLength(), pointAtPercent(), trimmed()
2912*/
2913void QPainterPath::setCachingEnabled(bool enabled)
2914{
2915 ensureData();
2916 if (d_func()->cacheEnabled == enabled)
2917 return;
2918 setDirty(true);
2919 QPainterPathPrivate *d = d_func();
2920 d->cacheEnabled = enabled;
2921 if (!enabled) {
2922 d->m_runLengths.clear();
2923 d->m_runLengths.squeeze();
2924 }
2925}
2926
2927//derivative of the equation
2928static inline qreal slopeAt(qreal t, qreal a, qreal b, qreal c, qreal d)
2929{
2930 return 3*t*t*(d - 3*c + 3*b - a) + 6*t*(c - 2*b + a) + 3*(b - a);
2931}
2932
2933/*!
2934 Returns the length of the current path.
2935*/
2936qreal QPainterPath::length() const
2937{
2938 Q_D(QPainterPath);
2939 if (isEmpty())
2940 return 0;
2941 if (d->cacheEnabled) {
2942 if (d->dirtyRunLengths)
2943 d->computeRunLengths();
2944 return d->m_runLengths.last();
2945 }
2946
2947 qreal len = 0;
2948 for (int i=1; i<d->elements.size(); ++i) {
2949 const Element &e = d->elements.at(i);
2950
2951 switch (e.type) {
2952 case MoveToElement:
2953 break;
2954 case LineToElement:
2955 {
2956 len += QLineF(d->elements.at(i-1), e).length();
2957 break;
2958 }
2959 case CurveToElement:
2960 {
2961 QBezier b = QBezier::fromPoints(d->elements.at(i-1),
2962 e,
2963 d->elements.at(i+1),
2964 d->elements.at(i+2));
2965 len += b.length();
2966 i += 2;
2967 break;
2968 }
2969 default:
2970 break;
2971 }
2972 }
2973 return len;
2974}
2975
2976/*!
2977 Returns percentage of the whole path at the specified length \a len.
2978
2979 Note that similarly to other percent methods, the percentage measurement
2980 is not linear with regards to the length, if curves are present
2981 in the path. When curves are present the percentage argument is mapped
2982 to the t parameter of the Bezier equations.
2983*/
2984qreal QPainterPath::percentAtLength(qreal len) const
2985{
2986 Q_D(QPainterPath);
2987 if (isEmpty() || len <= 0)
2988 return 0;
2989
2990 qreal totalLength = length();
2991 if (len > totalLength)
2992 return 1;
2993
2994 Q_ASSERT(totalLength != 0);
2995
2996 if (d->cacheEnabled) {
2997 const int ei = qMax(d->elementAtT(len / totalLength), 1); // Skip initial MoveTo
2998 qreal res = 0;
2999 const QPainterPath::Element &e = d->elements[ei];
3000 switch (e.type) {
3001 case QPainterPath::LineToElement:
3002 res = len / totalLength;
3003 break;
3004 case CurveToElement:
3005 {
3006 QBezier b = QBezier::fromPoints(d->elements.at(ei-1),
3007 e,
3008 d->elements.at(ei+1),
3009 d->elements.at(ei+2));
3010 qreal prevLen = d->m_runLengths[ei - 1];
3011 qreal blen = d->m_runLengths[ei] - prevLen;
3012 qreal elemRes = b.tAtLength(len - prevLen);
3013 res = (elemRes * blen + prevLen) / totalLength;
3014 break;
3015 }
3016 default:
3017 Q_UNREACHABLE();
3018 }
3019 return res;
3020 }
3021
3022 qreal curLen = 0;
3023 for (int i=1; i<d->elements.size(); ++i) {
3024 const Element &e = d->elements.at(i);
3025
3026 switch (e.type) {
3027 case MoveToElement:
3028 break;
3029 case LineToElement:
3030 {
3031 QLineF line(d->elements.at(i-1), e);
3032 qreal llen = line.length();
3033 curLen += llen;
3034 if (curLen >= len) {
3035 return len/totalLength ;
3036 }
3037
3038 break;
3039 }
3040 case CurveToElement:
3041 {
3042 QBezier b = QBezier::fromPoints(d->elements.at(i-1),
3043 e,
3044 d->elements.at(i+1),
3045 d->elements.at(i+2));
3046 qreal blen = b.length();
3047 qreal prevLen = curLen;
3048 curLen += blen;
3049
3050 if (curLen >= len) {
3051 qreal res = b.tAtLength(len - prevLen);
3052 return (res * blen + prevLen)/totalLength;
3053 }
3054
3055 i += 2;
3056 break;
3057 }
3058 default:
3059 break;
3060 }
3061 }
3062
3063 return 0;
3064}
3065
3066static inline QBezier uncached_bezierAtT(const QPainterPath &path, qreal t, qreal *startingLength,
3067 qreal *bezierLength)
3068{
3069 *startingLength = 0;
3070 if (t > 1)
3071 return QBezier();
3072
3073 qreal curLen = 0;
3074 qreal totalLength = path.length();
3075
3076 const int lastElement = path.elementCount() - 1;
3077 for (int i=0; i <= lastElement; ++i) {
3078 const QPainterPath::Element &e = path.elementAt(i);
3079
3080 switch (e.type) {
3081 case QPainterPath::MoveToElement:
3082 break;
3083 case QPainterPath::LineToElement:
3084 {
3085 QLineF line(path.elementAt(i-1), e);
3086 qreal llen = line.length();
3087 curLen += llen;
3088 if (i == lastElement || curLen/totalLength >= t) {
3089 *bezierLength = llen;
3090 QPointF a = path.elementAt(i-1);
3091 QPointF delta = e - a;
3092 return QBezier::fromPoints(a, a + delta / 3, a + 2 * delta / 3, e);
3093 }
3094 break;
3095 }
3096 case QPainterPath::CurveToElement:
3097 {
3098 QBezier b = QBezier::fromPoints(path.elementAt(i-1),
3099 e,
3100 path.elementAt(i+1),
3101 path.elementAt(i+2));
3102 qreal blen = b.length();
3103 curLen += blen;
3104
3105 if (i + 2 == lastElement || curLen/totalLength >= t) {
3106 *bezierLength = blen;
3107 return b;
3108 }
3109
3110 i += 2;
3111 break;
3112 }
3113 default:
3114 break;
3115 }
3116 *startingLength = curLen;
3117 }
3118 return QBezier();
3119}
3120
3121QBezier QPainterPathPrivate::bezierAtT(const QPainterPath &path, qreal t, qreal *startingLength,
3122 qreal *bezierLength) const
3123{
3124 Q_ASSERT(t >= 0 && t <= 1);
3125 QPainterPathPrivate *d = path.d_func();
3126 if (!path.isEmpty() && d->cacheEnabled) {
3127 const int ei = qMax(d->elementAtT(t), 1); // Avoid the initial MoveTo element
3128 const qreal prevRunLength = d->m_runLengths[ei - 1];
3129 *startingLength = prevRunLength;
3130 *bezierLength = d->m_runLengths[ei] - prevRunLength;
3131 const QPointF prev = d->elements[ei - 1];
3132 const QPainterPath::Element &e = d->elements[ei];
3133 switch (e.type) {
3134 case QPainterPath::LineToElement:
3135 {
3136 QPointF delta = (e - prev) / 3;
3137 return QBezier::fromPoints(prev, prev + delta, prev + 2 * delta, e);
3138 }
3139 case QPainterPath::CurveToElement:
3140 return QBezier::fromPoints(prev, e, elements[ei + 1], elements[ei + 2]);
3141 break;
3142 default:
3143 Q_UNREACHABLE();
3144 }
3145 }
3146
3147 return uncached_bezierAtT(path, t, startingLength, bezierLength);
3148}
3149
3150/*!
3151 Returns the point at at the percentage \a t of the current path.
3152 The argument \a t has to be between 0 and 1.
3153
3154 Note that similarly to other percent methods, the percentage measurement
3155 is not linear with regards to the length, if curves are present
3156 in the path. When curves are present the percentage argument is mapped
3157 to the t parameter of the Bezier equations.
3158*/
3159QPointF QPainterPath::pointAtPercent(qreal t) const
3160{
3161 if (t < 0 || t > 1) {
3162 qWarning("QPainterPath::pointAtPercent accepts only values between 0 and 1");
3163 return QPointF();
3164 }
3165
3166 if (!d_ptr || d_ptr->elements.size() == 0)
3167 return QPointF();
3168
3169 if (d_ptr->elements.size() == 1)
3170 return d_ptr->elements.at(0);
3171
3172 qreal totalLength = length();
3173 qreal curLen = 0;
3174 qreal bezierLen = 0;
3175 QBezier b = d_ptr->bezierAtT(*this, t, &curLen, &bezierLen);
3176 Q_ASSERT(bezierLen != 0);
3177 qreal realT = (totalLength * t - curLen) / bezierLen;
3178
3179 return b.pointAt(qBound(qreal(0), realT, qreal(1)));
3180}
3181
3182/*!
3183 Returns the angle of the path tangent at the percentage \a t.
3184 The argument \a t has to be between 0 and 1.
3185
3186 Positive values for the angles mean counter-clockwise while negative values
3187 mean the clockwise direction. Zero degrees is at the 3 o'clock position.
3188
3189 Note that similarly to the other percent methods, the percentage measurement
3190 is not linear with regards to the length if curves are present
3191 in the path. When curves are present the percentage argument is mapped
3192 to the t parameter of the Bezier equations.
3193*/
3194qreal QPainterPath::angleAtPercent(qreal t) const
3195{
3196 if (t < 0 || t > 1) {
3197 qWarning("QPainterPath::angleAtPercent accepts only values between 0 and 1");
3198 return 0;
3199 }
3200
3201 if (isEmpty())
3202 return 0;
3203
3204 qreal totalLength = length();
3205 qreal curLen = 0;
3206 qreal bezierLen = 0;
3207 QBezier bez = d_ptr->bezierAtT(*this, t, &curLen, &bezierLen);
3208 Q_ASSERT(bezierLen != 0);
3209 qreal realT = (totalLength * t - curLen) / bezierLen;
3210
3211 qreal m1 = slopeAt(realT, bez.x1, bez.x2, bez.x3, bez.x4);
3212 qreal m2 = slopeAt(realT, bez.y1, bez.y2, bez.y3, bez.y4);
3213
3214 return QLineF(0, 0, m1, m2).angle();
3215}
3216
3217
3218/*!
3219 Returns the slope of the path at the percentage \a t. The
3220 argument \a t has to be between 0 and 1.
3221
3222 Note that similarly to other percent methods, the percentage measurement
3223 is not linear with regards to the length, if curves are present
3224 in the path. When curves are present the percentage argument is mapped
3225 to the t parameter of the Bezier equations.
3226*/
3227qreal QPainterPath::slopeAtPercent(qreal t) const
3228{
3229 if (t < 0 || t > 1) {
3230 qWarning("QPainterPath::slopeAtPercent accepts only values between 0 and 1");
3231 return 0;
3232 }
3233
3234 if (isEmpty())
3235 return 0;
3236
3237 qreal totalLength = length();
3238 qreal curLen = 0;
3239 qreal bezierLen = 0;
3240 QBezier bez = d_ptr->bezierAtT(*this, t, &curLen, &bezierLen);
3241 Q_ASSERT(bezierLen != 0);
3242 qreal realT = (totalLength * t - curLen) / bezierLen;
3243
3244 qreal m1 = slopeAt(realT, bez.x1, bez.x2, bez.x3, bez.x4);
3245 qreal m2 = slopeAt(realT, bez.y1, bez.y2, bez.y3, bez.y4);
3246 //tangent line
3247 qreal slope = 0;
3248
3249 if (m1)
3250 slope = m2/m1;
3251 else {
3252 if (std::numeric_limits<qreal>::has_infinity) {
3253 slope = (m2 < 0) ? -std::numeric_limits<qreal>::infinity()
3254 : std::numeric_limits<qreal>::infinity();
3255 } else {
3256 if (sizeof(qreal) == sizeof(double)) {
3257 return 1.79769313486231570e+308;
3258 } else {
3259 return ((qreal)3.40282346638528860e+38);
3260 }
3261 }
3262 }
3263
3264 return slope;
3265}
3266
3267/*!
3268 \since 6.10
3269
3270 Returns the section of the path between the length fractions \a fromFraction and \a toFraction.
3271 The effective range of the fractions are from 0, denoting the start point of the path, to 1,
3272 denoting its end point. The fractions are linear with respect to path length, in contrast to the
3273 percentage \e t values.
3274
3275 The value of \a offset will be added to the fraction values. If that causes an over- or underflow
3276 of the [0, 1] range, the values will be wrapped around, as will the resulting path. The effective
3277 range of the offset is between -1 and 1.
3278
3279 Repeated calls to this function can be optimized by {enabling caching}{setCachingEnabled()}.
3280
3281 \sa length(), percentAtLength(), setCachingEnabled()
3282*/
3283
3284QPainterPath QPainterPath::trimmed(qreal fromFraction, qreal toFraction, qreal offset) const
3285{
3286 if (isEmpty())
3287 return *this;
3288
3289 // We need length caching enabled for the calculations.
3290 if (!isCachingEnabled()) {
3291 QPainterPath copy(*this);
3292 copy.setCachingEnabled(true);
3293 return copy.trimmed(fromFraction, toFraction, offset);
3294 }
3295
3296 qreal f1 = qBound(qreal(0), fromFraction, qreal(1));
3297 qreal f2 = qBound(qreal(0), toFraction, qreal(1));
3298 if (qFuzzyIsNull(f1 - f2)) // ie. f1 == f2 (even if one of them is 0.0)
3299 return QPainterPath();
3300 if (f1 > f2)
3301 qSwap(f1, f2);
3302 if (qFuzzyCompare(f2 - f1, qreal(1))) // Shortcut for no trimming
3303 return *this;
3304
3305 QPainterPath res;
3306 res.setFillRule(fillRule());
3307
3308 if (offset) {
3309 qreal dummy;
3310 offset = std::modf(offset, &dummy); // Use only the fractional part of offset, range <-1, 1>
3311
3312 qreal of1 = f1 + offset;
3313 qreal of2 = f2 + offset;
3314 if (offset < 0) {
3315 f1 = of1 < 0 ? of1 + 1 : of1;
3316 f2 = of2 + 1 > 1 ? of2 : of2 + 1;
3317 } else if (offset > 0) {
3318 f1 = of1 - 1 < 0 ? of1 : of1 - 1;
3319 f2 = of2 > 1 ? of2 - 1 : of2;
3320 }
3321 }
3322 const bool wrapping = (f1 > f2);
3323 //qDebug() << "ADJ:" << f1 << f2 << wrapping << "(" << of1 << of2 << ")";
3324
3325 QPainterPathPrivate *d = d_func();
3326 if (d->dirtyRunLengths)
3327 d->computeRunLengths();
3328 const qreal totalLength = d->m_runLengths.last();
3329 if (qFuzzyIsNull(totalLength))
3330 return res;
3331
3332 const qreal l1 = f1 * totalLength;
3333 const qreal l2 = f2 * totalLength;
3334 const int e1 = d->elementAtLength(l1);
3335 const bool mustTrimE1 = !QtPrivate::fuzzyCompare(d->m_runLengths.at(e1), l1);
3336 const int e2 = d->elementAtLength(l2);
3337 const bool mustTrimE2 = !QtPrivate::fuzzyCompare(d->m_runLengths.at(e2), l2);
3338
3339 //qDebug() << "Trim [" << f1 << f2 << "] e1:" << e1 << mustTrimE1 << "e2:" << e2 << mustTrimE2 << "wrapping:" << wrapping;
3340 if (e1 == e2 && !wrapping && mustTrimE1 && mustTrimE2) {
3341 // Entire result is one element, clipped in both ends
3342 d->appendSliceOfElement(&res, e1, l1, l2);
3343 } else {
3344 // Add partial start element (or just its end point, being the start of the next)
3345 if (mustTrimE1)
3346 d->appendEndOfElement(&res, e1, l1);
3347 else
3348 res.moveTo(d->endPointOfElement(e1));
3349
3350 // Add whole elements between start and end
3351 int firstWholeElement = e1 + 1;
3352 int lastWholeElement = (mustTrimE2 ? e2 - 1 : e2);
3353 if (!wrapping) {
3354 d->appendElementRange(&res, firstWholeElement, lastWholeElement);
3355 } else {
3356 int lastIndex = d->elements.size() - 1;
3357 d->appendElementRange(&res, firstWholeElement, lastIndex);
3358 bool isClosed = (QPointF(d->elements.at(0)) == QPointF(d->elements.at(lastIndex)));
3359 // If closed we can skip the initial moveto
3360 d->appendElementRange(&res, (isClosed ? 1 : 0), lastWholeElement);
3361 }
3362
3363 // Partial end element
3364 if (mustTrimE2)
3365 d->appendStartOfElement(&res, e2, l2);
3366 }
3367
3368 return res;
3369}
3370
3371void QPainterPathPrivate::appendTrimmedElement(QPainterPath *to, int elemIdx, int trimFlags,
3372 qreal startLen, qreal endLen)
3373{
3374 Q_ASSERT(cacheEnabled);
3375 Q_ASSERT(!dirtyRunLengths);
3376
3377 if (elemIdx <= 0 || elemIdx >= elements.size())
3378 return;
3379
3380 const qreal prevLen = m_runLengths.at(elemIdx - 1);
3381 const qreal elemLen = m_runLengths.at(elemIdx) - prevLen;
3382 const qreal len1 = startLen - prevLen;
3383 const qreal len2 = endLen - prevLen;
3384 if (qFuzzyIsNull(elemLen))
3385 return;
3386
3387 const QPointF pp = elements.at(elemIdx - 1);
3388 const QPainterPath::Element e = elements.at(elemIdx);
3389 if (e.isLineTo()) {
3390 QLineF l(pp, e);
3391 QPointF p1 = (trimFlags & TrimStart) ? l.pointAt(len1 / elemLen) : pp;
3392 QPointF p2 = (trimFlags & TrimEnd) ? l.pointAt(len2 / elemLen) : e;
3393 if (to->isEmpty())
3394 to->moveTo(p1);
3395 to->lineTo(p2);
3396 } else if (e.isCurveTo()) {
3397 Q_ASSERT(elemIdx < elements.size() - 2);
3398 QBezier b = QBezier::fromPoints(pp, e, elements.at(elemIdx + 1), elements.at(elemIdx + 2));
3399 qreal t1 = (trimFlags & TrimStart) ? b.tAtLength(len1) : 0.0; // or simply len1/elemLen to trim by t instead of len
3400 qreal t2 = (trimFlags & TrimEnd) ? b.tAtLength(len2) : 1.0;
3401 QBezier c = b.getSubRange(t1, t2);
3402 if (to->isEmpty())
3403 to->moveTo(c.pt1());
3404 to->cubicTo(c.pt2(), c.pt3(), c.pt4());
3405 } else {
3406 Q_UNREACHABLE();
3407 }
3408}
3409
3410void QPainterPathPrivate::appendElementRange(QPainterPath *to, int first, int last)
3411{
3412 if (first < 0 || first >= elements.size() || last < 0 || last >= elements.size())
3413 return;
3414
3415 // (Could optimize by direct copy of elements, but must ensure correct state flags)
3416 for (int i = first; i <= last; i++) {
3417 const QPainterPath::Element &e = elements.at(i);
3418 switch (e.type) {
3419 case QPainterPath::MoveToElement:
3420 to->moveTo(e);
3421 break;
3422 case QPainterPath::LineToElement:
3423 to->lineTo(e);
3424 break;
3425 case QPainterPath::CurveToElement:
3426 Q_ASSERT(i < elements.size() - 2);
3427 to->cubicTo(e, elements.at(i + 1), elements.at(i + 2));
3428 i += 2;
3429 break;
3430 default:
3431 // 'first' may point to CurveToData element, just skip it
3432 break;
3433 }
3434 }
3435}
3436
3437
3438/*!
3439 \since 4.4
3440
3441 Adds the given rectangle \a rect with rounded corners to the path.
3442
3443 The \a xRadius and \a yRadius arguments specify the radii of
3444 the ellipses defining the corners of the rounded rectangle.
3445 When \a mode is Qt::RelativeSize, \a xRadius and
3446 \a yRadius are specified in percentage of half the rectangle's
3447 width and height respectively, and should be in the range 0.0 to 100.0.
3448
3449 \sa addRect()
3450*/
3451void QPainterPath::addRoundedRect(const QRectF &rect, qreal xRadius, qreal yRadius,
3452 Qt::SizeMode mode)
3453{
3454 QRectF r = rect.normalized();
3455
3456 if (r.isNull())
3457 return;
3458
3459 if (mode == Qt::AbsoluteSize) {
3460 qreal w = r.width() / 2;
3461 qreal h = r.height() / 2;
3462
3463 if (w == 0) {
3464 xRadius = 0;
3465 } else {
3466 xRadius = 100 * qMin(xRadius, w) / w;
3467 }
3468 if (h == 0) {
3469 yRadius = 0;
3470 } else {
3471 yRadius = 100 * qMin(yRadius, h) / h;
3472 }
3473 } else {
3474 if (xRadius > 100) // fix ranges
3475 xRadius = 100;
3476
3477 if (yRadius > 100)
3478 yRadius = 100;
3479 }
3480
3481 if (xRadius <= 0 || yRadius <= 0) { // add normal rectangle
3482 addRect(r);
3483 return;
3484 }
3485
3486 qreal x = r.x();
3487 qreal y = r.y();
3488 qreal w = r.width();
3489 qreal h = r.height();
3490 qreal rxx2 = w*xRadius/100;
3491 qreal ryy2 = h*yRadius/100;
3492
3493 ensureData();
3494 setDirty(true);
3495
3496 bool first = d_func()->elements.size() < 2;
3497
3498 arcMoveTo(x, y, rxx2, ryy2, 180);
3499 arcTo(x, y, rxx2, ryy2, 180, -90);
3500 arcTo(x+w-rxx2, y, rxx2, ryy2, 90, -90);
3501 arcTo(x+w-rxx2, y+h-ryy2, rxx2, ryy2, 0, -90);
3502 arcTo(x, y+h-ryy2, rxx2, ryy2, 270, -90);
3503 closeSubpath();
3504
3505 d_func()->require_moveTo = true;
3506 d_func()->convex = first;
3507}
3508
3509/*!
3510 \fn void QPainterPath::addRoundedRect(qreal x, qreal y, qreal w, qreal h, qreal xRadius, qreal yRadius, Qt::SizeMode mode = Qt::AbsoluteSize);
3511 \since 4.4
3512 \overload
3513
3514 Adds the given rectangle \a x, \a y, \a w, \a h with rounded corners to the path.
3515 */
3516
3517/*!
3518 \since 4.3
3519
3520 Returns a path which is the union of this path's fill area and \a p's fill area.
3521
3522 Set operations on paths will treat the paths as areas. Non-closed
3523 paths will be treated as implicitly closed.
3524 Bezier curves may be flattened to line segments due to numerical instability of
3525 doing bezier curve intersections.
3526
3527 \sa intersected(), subtracted()
3528*/
3529QPainterPath QPainterPath::united(const QPainterPath &p) const
3530{
3531 if (isEmpty() || p.isEmpty())
3532 return isEmpty() ? p : *this;
3533 QPathClipper clipper(*this, p);
3534 return clipper.clip(QPathClipper::BoolOr);
3535}
3536
3537/*!
3538 \since 4.3
3539
3540 Returns a path which is the intersection of this path's fill area and \a p's fill area.
3541 Bezier curves may be flattened to line segments due to numerical instability of
3542 doing bezier curve intersections.
3543*/
3544QPainterPath QPainterPath::intersected(const QPainterPath &p) const
3545{
3546 if (isEmpty() || p.isEmpty())
3547 return QPainterPath();
3548 QPathClipper clipper(*this, p);
3549 return clipper.clip(QPathClipper::BoolAnd);
3550}
3551
3552/*!
3553 \since 4.3
3554
3555 Returns a path which is \a p's fill area subtracted from this path's fill area.
3556
3557 Set operations on paths will treat the paths as areas. Non-closed
3558 paths will be treated as implicitly closed.
3559 Bezier curves may be flattened to line segments due to numerical instability of
3560 doing bezier curve intersections.
3561*/
3562QPainterPath QPainterPath::subtracted(const QPainterPath &p) const
3563{
3564 if (isEmpty() || p.isEmpty())
3565 return *this;
3566 QPathClipper clipper(*this, p);
3567 return clipper.clip(QPathClipper::BoolSub);
3568}
3569
3570/*!
3571 \since 4.4
3572
3573 Returns a simplified version of this path. This implies merging all subpaths that intersect,
3574 and returning a path containing no intersecting edges. Consecutive parallel lines will also
3575 be merged. The simplified path will always use the default fill rule, Qt::OddEvenFill.
3576 Bezier curves may be flattened to line segments due to numerical instability of
3577 doing bezier curve intersections.
3578*/
3579QPainterPath QPainterPath::simplified() const
3580{
3581 if (isEmpty())
3582 return *this;
3583 QPathClipper clipper(*this, QPainterPath());
3584 return clipper.clip(QPathClipper::Simplify);
3585}
3586
3587/*!
3588 \since 4.3
3589
3590 Returns \c true if the current path intersects at any point the given path \a p.
3591 Also returns \c true if the current path contains or is contained by any part of \a p.
3592
3593 Set operations on paths will treat the paths as areas. Non-closed
3594 paths will be treated as implicitly closed.
3595
3596 \sa contains()
3597 */
3598bool QPainterPath::intersects(const QPainterPath &p) const
3599{
3600 if (p.elementCount() == 1)
3601 return contains(p.elementAt(0));
3602 if (isEmpty() || p.isEmpty())
3603 return false;
3604 QPathClipper clipper(*this, p);
3605 return clipper.intersect();
3606}
3607
3608/*!
3609 \since 4.3
3610
3611 Returns \c true if the given path \a p is contained within
3612 the current path. Returns \c false if any edges of the current path and
3613 \a p intersect.
3614
3615 Set operations on paths will treat the paths as areas. Non-closed
3616 paths will be treated as implicitly closed.
3617
3618 \sa intersects()
3619 */
3620bool QPainterPath::contains(const QPainterPath &p) const
3621{
3622 if (p.elementCount() == 1)
3623 return contains(p.elementAt(0));
3624 if (isEmpty() || p.isEmpty())
3625 return false;
3626 QPathClipper clipper(*this, p);
3627 return clipper.contains();
3628}
3629
3630void QPainterPath::setDirty(bool dirty)
3631{
3632 d_func()->pathConverter.reset();
3633 d_func()->dirtyBounds = dirty;
3634 d_func()->dirtyControlBounds = dirty;
3635 d_func()->dirtyRunLengths = dirty;
3636 d_func()->convex = false;
3637}
3638
3639void QPainterPath::computeBoundingRect() const
3640{
3641 QPainterPathPrivate *d = d_func();
3642 d->dirtyBounds = false;
3643 if (!d_ptr) {
3644 d->bounds = QRect();
3645 return;
3646 }
3647
3648 qreal minx, maxx, miny, maxy;
3649 minx = maxx = d->elements.at(0).x;
3650 miny = maxy = d->elements.at(0).y;
3651 for (int i=1; i<d->elements.size(); ++i) {
3652 const Element &e = d->elements.at(i);
3653
3654 switch (e.type) {
3655 case MoveToElement:
3656 case LineToElement:
3657 if (e.x > maxx) maxx = e.x;
3658 else if (e.x < minx) minx = e.x;
3659 if (e.y > maxy) maxy = e.y;
3660 else if (e.y < miny) miny = e.y;
3661 break;
3662 case CurveToElement:
3663 {
3664 QBezier b = QBezier::fromPoints(d->elements.at(i-1),
3665 e,
3666 d->elements.at(i+1),
3667 d->elements.at(i+2));
3668 QRectF r = qt_painterpath_bezier_extrema(b);
3669 qreal right = r.right();
3670 qreal bottom = r.bottom();
3671 if (r.x() < minx) minx = r.x();
3672 if (right > maxx) maxx = right;
3673 if (r.y() < miny) miny = r.y();
3674 if (bottom > maxy) maxy = bottom;
3675 i += 2;
3676 }
3677 break;
3678 default:
3679 break;
3680 }
3681 }
3682 d->bounds = QRectF(minx, miny, maxx - minx, maxy - miny);
3683}
3684
3685
3686void QPainterPath::computeControlPointRect() const
3687{
3688 QPainterPathPrivate *d = d_func();
3689 d->dirtyControlBounds = false;
3690 if (!d_ptr) {
3691 d->controlBounds = QRect();
3692 return;
3693 }
3694
3695 qreal minx, maxx, miny, maxy;
3696 minx = maxx = d->elements.at(0).x;
3697 miny = maxy = d->elements.at(0).y;
3698 for (int i=1; i<d->elements.size(); ++i) {
3699 const Element &e = d->elements.at(i);
3700 if (e.x > maxx) maxx = e.x;
3701 else if (e.x < minx) minx = e.x;
3702 if (e.y > maxy) maxy = e.y;
3703 else if (e.y < miny) miny = e.y;
3704 }
3705 d->controlBounds = QRectF(minx, miny, maxx - minx, maxy - miny);
3706}
3707
3709{
3710 Q_ASSERT(!elements.isEmpty());
3711
3712 m_runLengths.clear();
3713 const int numElems = elements.size();
3714 m_runLengths.reserve(numElems);
3715
3716 QPointF runPt = elements[0];
3717 qreal runLen = 0.0;
3718 for (int i = 0; i < numElems; i++) {
3719 QPainterPath::Element e = elements[i];
3720 switch (e.type) {
3721 case QPainterPath::LineToElement:
3722 runLen += QLineF(runPt, e).length();
3723 runPt = e;
3724 break;
3725 case QPainterPath::CurveToElement: {
3726 Q_ASSERT(i < numElems - 2);
3727 QPainterPath::Element ee = elements[i + 2];
3728 runLen += QBezier::fromPoints(runPt, e, elements[i + 1], ee).length();
3729 runPt = ee;
3730 break;
3731 }
3732 case QPainterPath::MoveToElement:
3733 runPt = e;
3734 break;
3735 case QPainterPath::CurveToDataElement:
3736 break;
3737 }
3738 m_runLengths.append(runLen);
3739 }
3740 Q_ASSERT(m_runLengths.size() == elements.size());
3741
3742 dirtyRunLengths = false;
3743}
3744
3745#ifndef QT_NO_DEBUG_STREAM
3746QDebug operator<<(QDebug s, const QPainterPath &p)
3747{
3748 QDebugStateSaver saver(s);
3749 s.nospace() << "QPainterPath: Element count=" << p.elementCount() << Qt::endl;
3750 const char *types[] = {"MoveTo", "LineTo", "CurveTo", "CurveToData"};
3751 for (int i=0; i<p.elementCount(); ++i) {
3752 s.nospace() << " -> " << types[p.elementAt(i).type] << "(x=" << p.elementAt(i).x << ", y=" << p.elementAt(i).y << ')' << Qt::endl;
3753 }
3754 return s;
3755}
3756#endif
3757
3758QT_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:4018
QDataStream & operator>>(QDataStream &stream, QImage &image)
Definition qimage.cpp:4044