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