Coverage Report

Created: 2026-05-16 07:21

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