Coverage Report

Created: 2025-07-23 06:48

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