/src/qt/qtbase/src/gui/painting/qpainterpath.cpp
C++ | 1677 lines | 688 code | 197 blank | 792 comment | 158 complexity | d05844663bf298624e8adb45c03f9be9 MD5 | raw file
- /****************************************************************************
- **
- ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
- ** Contact: http://www.qt-project.org/legal
- **
- ** This file is part of the QtGui module of the Qt Toolkit.
- **
- ** $QT_BEGIN_LICENSE:LGPL$
- ** Commercial License Usage
- ** Licensees holding valid commercial Qt licenses may use this file in
- ** accordance with the commercial license agreement provided with the
- ** Software or, alternatively, in accordance with the terms contained in
- ** a written agreement between you and Digia. For licensing terms and
- ** conditions see http://qt.digia.com/licensing. For further information
- ** use the contact form at http://qt.digia.com/contact-us.
- **
- ** GNU Lesser General Public License Usage
- ** Alternatively, this file may be used under the terms of the GNU Lesser
- ** General Public License version 2.1 as published by the Free Software
- ** Foundation and appearing in the file LICENSE.LGPL included in the
- ** packaging of this file. Please review the following information to
- ** ensure the GNU Lesser General Public License version 2.1 requirements
- ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
- **
- ** In addition, as a special exception, Digia gives you certain additional
- ** rights. These rights are described in the Digia Qt LGPL Exception
- ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
- **
- ** GNU General Public License Usage
- ** Alternatively, this file may be used under the terms of the GNU
- ** General Public License version 3.0 as published by the Free Software
- ** Foundation and appearing in the file LICENSE.GPL included in the
- ** packaging of this file. Please review the following information to
- ** ensure the GNU General Public License version 3.0 requirements will be
- ** met: http://www.gnu.org/copyleft/gpl.html.
- **
- **
- ** $QT_END_LICENSE$
- **
- ****************************************************************************/
- #include "qpainterpath.h"
- #include "qpainterpath_p.h"
- #include <qbitmap.h>
- #include <qdebug.h>
- #include <qiodevice.h>
- #include <qlist.h>
- #include <qmatrix.h>
- #include <qpen.h>
- #include <qpolygon.h>
- #include <qtextlayout.h>
- #include <qvarlengtharray.h>
- #include <qmath.h>
- #include <private/qbezier_p.h>
- #include <private/qfontengine_p.h>
- #include <private/qnumeric_p.h>
- #include <private/qobject_p.h>
- #include <private/qpathclipper_p.h>
- #include <private/qstroker_p.h>
- #include <private/qtextengine_p.h>
- #include <limits.h>
- #if 0
- #include <performance.h>
- #else
- #define PM_INIT
- #define PM_MEASURE(x)
- #define PM_DISPLAY
- #endif
- QT_BEGIN_NAMESPACE
- struct QPainterPathPrivateDeleter
- {
- static inline void cleanup(QPainterPathPrivate *d)
- {
- // note - we must up-cast to QPainterPathData since QPainterPathPrivate
- // has a non-virtual destructor!
- if (d && !d->ref.deref())
- delete static_cast<QPainterPathData *>(d);
- }
- };
- // This value is used to determine the length of control point vectors
- // when approximating arc segments as curves. The factor is multiplied
- // with the radius of the circle.
- // #define QPP_DEBUG
- // #define QPP_STROKE_DEBUG
- //#define QPP_FILLPOLYGONS_DEBUG
- QPainterPath qt_stroke_dash(const QPainterPath &path, qreal *dashes, int dashCount);
- void qt_find_ellipse_coords(const QRectF &r, qreal angle, qreal length,
- QPointF* startPoint, QPointF *endPoint)
- {
- if (r.isNull()) {
- if (startPoint)
- *startPoint = QPointF();
- if (endPoint)
- *endPoint = QPointF();
- return;
- }
- qreal w2 = r.width() / 2;
- qreal h2 = r.height() / 2;
- qreal angles[2] = { angle, angle + length };
- QPointF *points[2] = { startPoint, endPoint };
- for (int i = 0; i < 2; ++i) {
- if (!points[i])
- continue;
- qreal theta = angles[i] - 360 * qFloor(angles[i] / 360);
- qreal t = theta / 90;
- // truncate
- int quadrant = int(t);
- t -= quadrant;
- t = qt_t_for_arc_angle(90 * t);
- // swap x and y?
- if (quadrant & 1)
- t = 1 - t;
- qreal a, b, c, d;
- QBezier::coefficients(t, a, b, c, d);
- QPointF p(a + b + c*QT_PATH_KAPPA, d + c + b*QT_PATH_KAPPA);
- // left quadrants
- if (quadrant == 1 || quadrant == 2)
- p.rx() = -p.x();
- // top quadrants
- if (quadrant == 0 || quadrant == 1)
- p.ry() = -p.y();
- *points[i] = r.center() + QPointF(w2 * p.x(), h2 * p.y());
- }
- }
- #ifdef QPP_DEBUG
- static void qt_debug_path(const QPainterPath &path)
- {
- const char *names[] = {
- "MoveTo ",
- "LineTo ",
- "CurveTo ",
- "CurveToData"
- };
- printf("\nQPainterPath: elementCount=%d\n", path.elementCount());
- for (int i=0; i<path.elementCount(); ++i) {
- const QPainterPath::Element &e = path.elementAt(i);
- Q_ASSERT(e.type >= 0 && e.type <= QPainterPath::CurveToDataElement);
- printf(" - %3d:: %s, (%.2f, %.2f)\n", i, names[e.type], e.x, e.y);
- }
- }
- #endif
- /*!
- \class QPainterPath
- \ingroup painting
- \ingroup shared
- \inmodule QtGui
- \brief The QPainterPath class provides a container for painting operations,
- enabling graphical shapes to be constructed and reused.
- A painter path is an object composed of a number of graphical
- building blocks, such as rectangles, ellipses, lines, and curves.
- Building blocks can be joined in closed subpaths, for example as a
- rectangle or an ellipse. A closed path has coinciding start and
- end points. Or they can exist independently as unclosed subpaths,
- such as lines and curves.
- A QPainterPath object can be used for filling, outlining, and
- clipping. To generate fillable outlines for a given painter path,
- use the QPainterPathStroker class. The main advantage of painter
- paths over normal drawing operations is that complex shapes only
- need to be created once; then they can be drawn many times using
- only calls to the QPainter::drawPath() function.
- QPainterPath provides a collection of functions that can be used
- to obtain information about the path and its elements. In addition
- it is possible to reverse the order of the elements using the
- toReversed() function. There are also several functions to convert
- this painter path object into a polygon representation.
- \tableofcontents
- \section1 Composing a QPainterPath
- A QPainterPath object can be constructed as an empty path, with a
- given start point, or as a copy of another QPainterPath object.
- Once created, lines and curves can be added to the path using the
- lineTo(), arcTo(), cubicTo() and quadTo() functions. The lines and
- curves stretch from the currentPosition() to the position passed
- as argument.
- The currentPosition() of the QPainterPath object is always the end
- position of the last subpath that was added (or the initial start
- point). Use the moveTo() function to move the currentPosition()
- without adding a component. The moveTo() function implicitly
- starts a new subpath, and closes the previous one. Another way of
- starting a new subpath is to call the closeSubpath() function
- which closes the current path by adding a line from the
- currentPosition() back to the path's start position. Note that the
- new path will have (0, 0) as its initial currentPosition().
- QPainterPath class also provides several convenience functions to
- add closed subpaths to a painter path: addEllipse(), addPath(),
- addRect(), addRegion() and addText(). The addPolygon() function
- adds an \e unclosed subpath. In fact, these functions are all
- collections of moveTo(), lineTo() and cubicTo() operations.
- In addition, a path can be added to the current path using the
- connectPath() function. But note that this function will connect
- the last element of the current path to the first element of given
- one by adding a line.
- Below is a code snippet that shows how a QPainterPath object can
- be used:
- \table 100%
- \row
- \li \inlineimage qpainterpath-construction.png
- \li
- \snippet code/src_gui_painting_qpainterpath.cpp 0
- \endtable
- The painter path is initially empty when constructed. We first add
- a rectangle, which is a closed subpath. Then we add two bezier
- curves which together form a closed subpath even though they are
- not closed individually. Finally we draw the entire path. The path
- is filled using the default fill rule, Qt::OddEvenFill. Qt
- provides two methods for filling paths:
- \table
- \header
- \li Qt::OddEvenFill
- \li Qt::WindingFill
- \row
- \li \inlineimage qt-fillrule-oddeven.png
- \li \inlineimage qt-fillrule-winding.png
- \endtable
- See the Qt::FillRule documentation for the definition of the
- rules. A painter path's currently set fill rule can be retrieved
- using the fillRule() function, and altered using the setFillRule()
- function.
- \section1 QPainterPath Information
- The QPainterPath class provides a collection of functions that
- returns information about the path and its elements.
- The currentPosition() function returns the end point of the last
- subpath that was added (or the initial start point). The
- elementAt() function can be used to retrieve the various subpath
- elements, the \e number of elements can be retrieved using the
- elementCount() function, and the isEmpty() function tells whether
- this QPainterPath object contains any elements at all.
- The controlPointRect() function returns the rectangle containing
- all the points and control points in this path. This function is
- significantly faster to compute than the exact boundingRect()
- which returns the bounding rectangle of this painter path with
- floating point precision.
- Finally, QPainterPath provides the contains() function which can
- be used to determine whether a given point or rectangle is inside
- the path, and the intersects() function which determines if any of
- the points inside a given rectangle also are inside this path.
- \section1 QPainterPath Conversion
- For compatibility reasons, it might be required to simplify the
- representation of a painter path: QPainterPath provides the
- toFillPolygon(), toFillPolygons() and toSubpathPolygons()
- functions which convert the painter path into a polygon. The
- toFillPolygon() returns the painter path as one single polygon,
- while the two latter functions return a list of polygons.
- The toFillPolygons() and toSubpathPolygons() functions are
- provided because it is usually faster to draw several small
- polygons than to draw one large polygon, even though the total
- number of points drawn is the same. The difference between the two
- is the \e number of polygons they return: The toSubpathPolygons()
- creates one polygon for each subpath regardless of intersecting
- subpaths (i.e. overlapping bounding rectangles), while the
- toFillPolygons() functions creates only one polygon for
- overlapping subpaths.
- The toFillPolygon() and toFillPolygons() functions first convert
- all the subpaths to polygons, then uses a rewinding technique to
- make sure that overlapping subpaths can be filled using the
- correct fill rule. Note that rewinding inserts additional lines in
- the polygon so the outline of the fill polygon does not match the
- outline of the path.
- \section1 Examples
- Qt provides the \l {painting/painterpaths}{Painter Paths Example}
- and the \l {painting/deform}{Vector Deformation example} which are
- located in Qt's example directory.
- The \l {painting/painterpaths}{Painter Paths Example} shows how
- painter paths can be used to build complex shapes for rendering
- and lets the user experiment with the filling and stroking. The
- \l {painting/deform}{Vector Deformation Example} shows how to use
- QPainterPath to draw text.
- \table
- \header
- \li \l {painting/painterpaths}{Painter Paths Example}
- \li \l {painting/deform}{Vector Deformation Example}
- \row
- \li \inlineimage qpainterpath-example.png
- \li \inlineimage qpainterpath-demo.png
- \endtable
- \sa QPainterPathStroker, QPainter, QRegion, {Painter Paths Example}
- */
- /*!
- \enum QPainterPath::ElementType
- This enum describes the types of elements used to connect vertices
- in subpaths.
- Note that elements added as closed subpaths using the
- addEllipse(), addPath(), addPolygon(), addRect(), addRegion() and
- addText() convenience functions, is actually added to the path as
- a collection of separate elements using the moveTo(), lineTo() and
- cubicTo() functions.
- \value MoveToElement A new subpath. See also moveTo().
- \value LineToElement A line. See also lineTo().
- \value CurveToElement A curve. See also cubicTo() and quadTo().
- \value CurveToDataElement The extra data required to describe a curve in
- a CurveToElement element.
- \sa elementAt(), elementCount()
- */
- /*!
- \class QPainterPath::Element
- \inmodule QtGui
- \brief The QPainterPath::Element class specifies the position and
- type of a subpath.
- Once a QPainterPath object is constructed, subpaths like lines and
- curves can be added to the path (creating
- QPainterPath::LineToElement and QPainterPath::CurveToElement
- components).
- The lines and curves stretch from the currentPosition() to the
- position passed as argument. The currentPosition() of the
- QPainterPath object is always the end position of the last subpath
- that was added (or the initial start point). The moveTo() function
- can be used to move the currentPosition() without adding a line or
- curve, creating a QPainterPath::MoveToElement component.
- \sa QPainterPath
- */
- /*!
- \variable QPainterPath::Element::x
- \brief the x coordinate of the element's position.
- \sa {operator QPointF()}
- */
- /*!
- \variable QPainterPath::Element::y
- \brief the y coordinate of the element's position.
- \sa {operator QPointF()}
- */
- /*!
- \variable QPainterPath::Element::type
- \brief the type of element
- \sa isCurveTo(), isLineTo(), isMoveTo()
- */
- /*!
- \fn bool QPainterPath::Element::operator==(const Element &other) const
- \since 4.2
- Returns \c true if this element is equal to \a other;
- otherwise returns \c false.
- \sa operator!=()
- */
- /*!
- \fn bool QPainterPath::Element::operator!=(const Element &other) const
- \since 4.2
- Returns \c true if this element is not equal to \a other;
- otherwise returns \c false.
- \sa operator==()
- */
- /*!
- \fn bool QPainterPath::Element::isCurveTo () const
- Returns \c true if the element is a curve, otherwise returns \c false.
- \sa type, QPainterPath::CurveToElement
- */
- /*!
- \fn bool QPainterPath::Element::isLineTo () const
- Returns \c true if the element is a line, otherwise returns \c false.
- \sa type, QPainterPath::LineToElement
- */
- /*!
- \fn bool QPainterPath::Element::isMoveTo () const
- Returns \c true if the element is moving the current position,
- otherwise returns \c false.
- \sa type, QPainterPath::MoveToElement
- */
- /*!
- \fn QPainterPath::Element::operator QPointF () const
- Returns the element's position.
- \sa x, y
- */
- /*!
- \fn void QPainterPath::addEllipse(qreal x, qreal y, qreal width, qreal height)
- \overload
- Creates an ellipse within the bounding rectangle defined by its top-left
- corner at (\a x, \a y), \a width and \a height, and adds it to the
- painter path as a closed subpath.
- */
- /*!
- \since 4.4
- \fn void QPainterPath::addEllipse(const QPointF ¢er, qreal rx, qreal ry)
- \overload
- Creates an ellipse positioned at \a{center} with radii \a{rx} and \a{ry},
- and adds it to the painter path as a closed subpath.
- */
- /*!
- \fn void QPainterPath::addText(qreal x, qreal y, const QFont &font, const QString &text)
- \overload
- Adds the given \a text to this path as a set of closed subpaths created
- from the \a font supplied. The subpaths are positioned so that the left
- end of the text's baseline lies at the point specified by (\a x, \a y).
- */
- /*!
- \fn int QPainterPath::elementCount() const
- Returns the number of path elements in the painter path.
- \sa ElementType, elementAt(), isEmpty()
- */
- int QPainterPath::elementCount() const
- {
- return d_ptr ? d_ptr->elements.size() : 0;
- }
- /*!
- \fn QPainterPath::Element QPainterPath::elementAt(int index) const
- Returns the element at the given \a index in the painter path.
- \sa ElementType, elementCount(), isEmpty()
- */
- QPainterPath::Element QPainterPath::elementAt(int i) const
- {
- Q_ASSERT(d_ptr);
- Q_ASSERT(i >= 0 && i < elementCount());
- return d_ptr->elements.at(i);
- }
- /*!
- \fn void QPainterPath::setElementPositionAt(int index, qreal x, qreal y)
- \since 4.2
- Sets the x and y coordinate of the element at index \a index to \a
- x and \a y.
- */
- void QPainterPath::setElementPositionAt(int i, qreal x, qreal y)
- {
- Q_ASSERT(d_ptr);
- Q_ASSERT(i >= 0 && i < elementCount());
- detach();
- QPainterPath::Element &e = d_ptr->elements[i];
- e.x = x;
- e.y = y;
- }
- /*###
- \fn QPainterPath &QPainterPath::operator +=(const QPainterPath &other)
- Appends the \a other painter path to this painter path and returns a
- reference to the result.
- */
- /*!
- Constructs an empty QPainterPath object.
- */
- QPainterPath::QPainterPath()
- : d_ptr(0)
- {
- }
- /*!
- \fn QPainterPath::QPainterPath(const QPainterPath &path)
- Creates a QPainterPath object that is a copy of the given \a path.
- \sa operator=()
- */
- QPainterPath::QPainterPath(const QPainterPath &other)
- : d_ptr(other.d_ptr.data())
- {
- if (d_ptr)
- d_ptr->ref.ref();
- }
- /*!
- Creates a QPainterPath object with the given \a startPoint as its
- current position.
- */
- QPainterPath::QPainterPath(const QPointF &startPoint)
- : d_ptr(new QPainterPathData)
- {
- Element e = { startPoint.x(), startPoint.y(), MoveToElement };
- d_func()->elements << e;
- }
- void QPainterPath::detach()
- {
- if (d_ptr->ref.load() != 1)
- detach_helper();
- setDirty(true);
- }
- /*!
- \internal
- */
- void QPainterPath::detach_helper()
- {
- QPainterPathPrivate *data = new QPainterPathData(*d_func());
- d_ptr.reset(data);
- }
- /*!
- \internal
- */
- void QPainterPath::ensureData_helper()
- {
- QPainterPathPrivate *data = new QPainterPathData;
- data->elements.reserve(16);
- QPainterPath::Element e = { 0, 0, QPainterPath::MoveToElement };
- data->elements << e;
- d_ptr.reset(data);
- Q_ASSERT(d_ptr != 0);
- }
- /*!
- \fn QPainterPath &QPainterPath::operator=(const QPainterPath &path)
- Assigns the given \a path to this painter path.
- \sa QPainterPath()
- */
- QPainterPath &QPainterPath::operator=(const QPainterPath &other)
- {
- if (other.d_func() != d_func()) {
- QPainterPathPrivate *data = other.d_func();
- if (data)
- data->ref.ref();
- d_ptr.reset(data);
- }
- return *this;
- }
- /*!
- \fn QPainterPath &QPainterPath::operator=(QPainterPath &&other)
- Move-assigns \a other to this QPainterPath instance.
- \since 5.2
- */
- /*!
- \fn void QPainterPath::swap(QPainterPath &other)
- \since 4.8
- Swaps painter path \a other with this painter path. This operation is very
- fast and never fails.
- */
- /*!
- Destroys this QPainterPath object.
- */
- QPainterPath::~QPainterPath()
- {
- }
- /*!
- Closes the current subpath by drawing a line to the beginning of
- the subpath, automatically starting a new path. The current point
- of the new path is (0, 0).
- If the subpath does not contain any elements, this function does
- nothing.
- \sa moveTo(), {QPainterPath#Composing a QPainterPath}{Composing
- a QPainterPath}
- */
- void QPainterPath::closeSubpath()
- {
- #ifdef QPP_DEBUG
- printf("QPainterPath::closeSubpath()\n");
- #endif
- if (isEmpty())
- return;
- detach();
- d_func()->close();
- }
- /*!
- \fn void QPainterPath::moveTo(qreal x, qreal y)
- \overload
- Moves the current position to (\a{x}, \a{y}) and starts a new
- subpath, implicitly closing the previous path.
- */
- /*!
- \fn void QPainterPath::moveTo(const QPointF &point)
- Moves the current point to the given \a point, implicitly starting
- a new subpath and closing the previous one.
- \sa closeSubpath(), {QPainterPath#Composing a
- QPainterPath}{Composing a QPainterPath}
- */
- void QPainterPath::moveTo(const QPointF &p)
- {
- #ifdef QPP_DEBUG
- printf("QPainterPath::moveTo() (%.2f,%.2f)\n", p.x(), p.y());
- #endif
- if (!qt_is_finite(p.x()) || !qt_is_finite(p.y())) {
- #ifndef QT_NO_DEBUG
- qWarning("QPainterPath::moveTo: Adding point where x or y is NaN or Inf, ignoring call");
- #endif
- return;
- }
- ensureData();
- detach();
- QPainterPathData *d = d_func();
- Q_ASSERT(!d->elements.isEmpty());
- d->require_moveTo = false;
- if (d->elements.last().type == MoveToElement) {
- d->elements.last().x = p.x();
- d->elements.last().y = p.y();
- } else {
- Element elm = { p.x(), p.y(), MoveToElement };
- d->elements.append(elm);
- }
- d->cStart = d->elements.size() - 1;
- }
- /*!
- \fn void QPainterPath::lineTo(qreal x, qreal y)
- \overload
- Draws a line from the current position to the point (\a{x},
- \a{y}).
- */
- /*!
- \fn void QPainterPath::lineTo(const QPointF &endPoint)
- Adds a straight line from the current position to the given \a
- endPoint. After the line is drawn, the current position is updated
- to be at the end point of the line.
- \sa addPolygon(), addRect(), {QPainterPath#Composing a
- QPainterPath}{Composing a QPainterPath}
- */
- void QPainterPath::lineTo(const QPointF &p)
- {
- #ifdef QPP_DEBUG
- printf("QPainterPath::lineTo() (%.2f,%.2f)\n", p.x(), p.y());
- #endif
- if (!qt_is_finite(p.x()) || !qt_is_finite(p.y())) {
- #ifndef QT_NO_DEBUG
- qWarning("QPainterPath::lineTo: Adding point where x or y is NaN or Inf, ignoring call");
- #endif
- return;
- }
- ensureData();
- detach();
- QPainterPathData *d = d_func();
- Q_ASSERT(!d->elements.isEmpty());
- d->maybeMoveTo();
- if (p == QPointF(d->elements.last()))
- return;
- Element elm = { p.x(), p.y(), LineToElement };
- d->elements.append(elm);
- d->convex = d->elements.size() == 3 || (d->elements.size() == 4 && d->isClosed());
- }
- /*!
- \fn void QPainterPath::cubicTo(qreal c1X, qreal c1Y, qreal c2X,
- qreal c2Y, qreal endPointX, qreal endPointY);
- \overload
- Adds a cubic Bezier curve between the current position and the end
- point (\a{endPointX}, \a{endPointY}) with control points specified
- by (\a{c1X}, \a{c1Y}) and (\a{c2X}, \a{c2Y}).
- */
- /*!
- \fn void QPainterPath::cubicTo(const QPointF &c1, const QPointF &c2, const QPointF &endPoint)
- Adds a cubic Bezier curve between the current position and the
- given \a endPoint using the control points specified by \a c1, and
- \a c2.
- After the curve is added, the current position is updated to be at
- the end point of the curve.
- \table 100%
- \row
- \li \inlineimage qpainterpath-cubicto.png
- \li
- \snippet code/src_gui_painting_qpainterpath.cpp 1
- \endtable
- \sa quadTo(), {QPainterPath#Composing a QPainterPath}{Composing
- a QPainterPath}
- */
- void QPainterPath::cubicTo(const QPointF &c1, const QPointF &c2, const QPointF &e)
- {
- #ifdef QPP_DEBUG
- printf("QPainterPath::cubicTo() (%.2f,%.2f), (%.2f,%.2f), (%.2f,%.2f)\n",
- c1.x(), c1.y(), c2.x(), c2.y(), e.x(), e.y());
- #endif
- if (!qt_is_finite(c1.x()) || !qt_is_finite(c1.y()) || !qt_is_finite(c2.x()) || !qt_is_finite(c2.y())
- || !qt_is_finite(e.x()) || !qt_is_finite(e.y())) {
- #ifndef QT_NO_DEBUG
- qWarning("QPainterPath::cubicTo: Adding point where x or y is NaN or Inf, ignoring call");
- #endif
- return;
- }
- ensureData();
- detach();
- QPainterPathData *d = d_func();
- Q_ASSERT(!d->elements.isEmpty());
- // Abort on empty curve as a stroker cannot handle this and the
- // curve is irrelevant anyway.
- if (d->elements.last() == c1 && c1 == c2 && c2 == e)
- return;
- d->maybeMoveTo();
- Element ce1 = { c1.x(), c1.y(), CurveToElement };
- Element ce2 = { c2.x(), c2.y(), CurveToDataElement };
- Element ee = { e.x(), e.y(), CurveToDataElement };
- d->elements << ce1 << ce2 << ee;
- }
- /*!
- \fn void QPainterPath::quadTo(qreal cx, qreal cy, qreal endPointX, qreal endPointY);
- \overload
- Adds a quadratic Bezier curve between the current point and the endpoint
- (\a{endPointX}, \a{endPointY}) with the control point specified by
- (\a{cx}, \a{cy}).
- */
- /*!
- \fn void QPainterPath::quadTo(const QPointF &c, const QPointF &endPoint)
- Adds a quadratic Bezier curve between the current position and the
- given \a endPoint with the control point specified by \a c.
- After the curve is added, the current point is updated to be at
- the end point of the curve.
- \sa cubicTo(), {QPainterPath#Composing a QPainterPath}{Composing a
- QPainterPath}
- */
- void QPainterPath::quadTo(const QPointF &c, const QPointF &e)
- {
- #ifdef QPP_DEBUG
- printf("QPainterPath::quadTo() (%.2f,%.2f), (%.2f,%.2f)\n",
- c.x(), c.y(), e.x(), e.y());
- #endif
- if (!qt_is_finite(c.x()) || !qt_is_finite(c.y()) || !qt_is_finite(e.x()) || !qt_is_finite(e.y())) {
- #ifndef QT_NO_DEBUG
- qWarning("QPainterPath::quadTo: Adding point where x or y is NaN or Inf, ignoring call");
- #endif
- return;
- }
- ensureData();
- detach();
- Q_D(QPainterPath);
- Q_ASSERT(!d->elements.isEmpty());
- const QPainterPath::Element &elm = d->elements.at(elementCount()-1);
- QPointF prev(elm.x, elm.y);
- // Abort on empty curve as a stroker cannot handle this and the
- // curve is irrelevant anyway.
- if (prev == c && c == e)
- return;
- QPointF c1((prev.x() + 2*c.x()) / 3, (prev.y() + 2*c.y()) / 3);
- QPointF c2((e.x() + 2*c.x()) / 3, (e.y() + 2*c.y()) / 3);
- cubicTo(c1, c2, e);
- }
- /*!
- \fn void QPainterPath::arcTo(qreal x, qreal y, qreal width, qreal
- height, qreal startAngle, qreal sweepLength)
- \overload
- Creates an arc that occupies the rectangle QRectF(\a x, \a y, \a
- width, \a height), beginning at the specified \a startAngle and
- extending \a sweepLength degrees counter-clockwise.
- */
- /*!
- \fn void QPainterPath::arcTo(const QRectF &rectangle, qreal startAngle, qreal sweepLength)
- Creates an arc that occupies the given \a rectangle, beginning at
- the specified \a startAngle and extending \a sweepLength degrees
- counter-clockwise.
- Angles are specified in degrees. Clockwise arcs can be specified
- using negative angles.
- Note that this function connects the starting point of the arc to
- the current position if they are not already connected. After the
- arc has been added, the current position is the last point in
- arc. To draw a line back to the first point, use the
- closeSubpath() function.
- \table 100%
- \row
- \li \inlineimage qpainterpath-arcto.png
- \li
- \snippet code/src_gui_painting_qpainterpath.cpp 2
- \endtable
- \sa arcMoveTo(), addEllipse(), QPainter::drawArc(), QPainter::drawPie(),
- {QPainterPath#Composing a QPainterPath}{Composing a
- QPainterPath}
- */
- void QPainterPath::arcTo(const QRectF &rect, qreal startAngle, qreal sweepLength)
- {
- #ifdef QPP_DEBUG
- printf("QPainterPath::arcTo() (%.2f, %.2f, %.2f, %.2f, angle=%.2f, sweep=%.2f\n",
- rect.x(), rect.y(), rect.width(), rect.height(), startAngle, sweepLength);
- #endif
- if ((!qt_is_finite(rect.x()) && !qt_is_finite(rect.y())) || !qt_is_finite(rect.width()) || !qt_is_finite(rect.height())
- || !qt_is_finite(startAngle) || !qt_is_finite(sweepLength)) {
- #ifndef QT_NO_DEBUG
- qWarning("QPainterPath::arcTo: Adding arc where a parameter is NaN or Inf, ignoring call");
- #endif
- return;
- }
- if (rect.isNull())
- return;
- ensureData();
- detach();
- int point_count;
- QPointF pts[15];
- QPointF curve_start = qt_curves_for_arc(rect, startAngle, sweepLength, pts, &point_count);
- lineTo(curve_start);
- for (int i=0; i<point_count; i+=3) {
- cubicTo(pts[i].x(), pts[i].y(),
- pts[i+1].x(), pts[i+1].y(),
- pts[i+2].x(), pts[i+2].y());
- }
- }
- /*!
- \fn void QPainterPath::arcMoveTo(qreal x, qreal y, qreal width, qreal height, qreal angle)
- \overload
- \since 4.2
- Creates a move to that lies on the arc that occupies the
- QRectF(\a x, \a y, \a width, \a height) at \a angle.
- */
- /*!
- \fn void QPainterPath::arcMoveTo(const QRectF &rectangle, qreal angle)
- \since 4.2
- Creates a move to that lies on the arc that occupies the given \a
- rectangle at \a angle.
- Angles are specified in degrees. Clockwise arcs can be specified
- using negative angles.
- \sa moveTo(), arcTo()
- */
- void QPainterPath::arcMoveTo(const QRectF &rect, qreal angle)
- {
- if (rect.isNull())
- return;
- QPointF pt;
- qt_find_ellipse_coords(rect, angle, 0, &pt, 0);
- moveTo(pt);
- }
- /*!
- \fn QPointF QPainterPath::currentPosition() const
- Returns the current position of the path.
- */
- QPointF QPainterPath::currentPosition() const
- {
- return !d_ptr || d_func()->elements.isEmpty()
- ? QPointF()
- : QPointF(d_func()->elements.last().x, d_func()->elements.last().y);
- }
- /*!
- \fn void QPainterPath::addRect(qreal x, qreal y, qreal width, qreal height)
- \overload
- Adds a rectangle at position (\a{x}, \a{y}), with the given \a
- width and \a height, as a closed subpath.
- */
- /*!
- \fn void QPainterPath::addRect(const QRectF &rectangle)
- Adds the given \a rectangle to this path as a closed subpath.
- The \a rectangle is added as a clockwise set of lines. The painter
- path's current position after the \a rectangle has been added is
- at the top-left corner of the rectangle.
- \table 100%
- \row
- \li \inlineimage qpainterpath-addrectangle.png
- \li
- \snippet code/src_gui_painting_qpainterpath.cpp 3
- \endtable
- \sa addRegion(), lineTo(), {QPainterPath#Composing a
- QPainterPath}{Composing a QPainterPath}
- */
- void QPainterPath::addRect(const QRectF &r)
- {
- if (!qt_is_finite(r.x()) || !qt_is_finite(r.y()) || !qt_is_finite(r.width()) || !qt_is_finite(r.height())) {
- #ifndef QT_NO_DEBUG
- qWarning("QPainterPath::addRect: Adding rect where a parameter is NaN or Inf, ignoring call");
- #endif
- return;
- }
- if (r.isNull())
- return;
- ensureData();
- detach();
- bool first = d_func()->elements.size() < 2;
- d_func()->elements.reserve(d_func()->elements.size() + 5);
- moveTo(r.x(), r.y());
- Element l1 = { r.x() + r.width(), r.y(), LineToElement };
- Element l2 = { r.x() + r.width(), r.y() + r.height(), LineToElement };
- Element l3 = { r.x(), r.y() + r.height(), LineToElement };
- Element l4 = { r.x(), r.y(), LineToElement };
- d_func()->elements << l1 << l2 << l3 << l4;
- d_func()->require_moveTo = true;
- d_func()->convex = first;
- }
- /*!
- Adds the given \a polygon to the path as an (unclosed) subpath.
- Note that the current position after the polygon has been added,
- is the last point in \a polygon. To draw a line back to the first
- point, use the closeSubpath() function.
- \table 100%
- \row
- \li \inlineimage qpainterpath-addpolygon.png
- \li
- \snippet code/src_gui_painting_qpainterpath.cpp 4
- \endtable
- \sa lineTo(), {QPainterPath#Composing a QPainterPath}{Composing
- a QPainterPath}
- */
- void QPainterPath::addPolygon(const QPolygonF &polygon)
- {
- if (polygon.isEmpty())
- return;
- ensureData();
- detach();
- d_func()->elements.reserve(d_func()->elements.size() + polygon.size());
- moveTo(polygon.first());
- for (int i=1; i<polygon.size(); ++i) {
- Element elm = { polygon.at(i).x(), polygon.at(i).y(), LineToElement };
- d_func()->elements << elm;
- }
- }
- /*!
- \fn void QPainterPath::addEllipse(const QRectF &boundingRectangle)
- Creates an ellipse within the specified \a boundingRectangle
- and adds it to the painter path as a closed subpath.
- The ellipse is composed of a clockwise curve, starting and
- finishing at zero degrees (the 3 o'clock position).
- \table 100%
- \row
- \li \inlineimage qpainterpath-addellipse.png
- \li
- \snippet code/src_gui_painting_qpainterpath.cpp 5
- \endtable
- \sa arcTo(), QPainter::drawEllipse(), {QPainterPath#Composing a
- QPainterPath}{Composing a QPainterPath}
- */
- void QPainterPath::addEllipse(const QRectF &boundingRect)
- {
- if (!qt_is_finite(boundingRect.x()) || !qt_is_finite(boundingRect.y())
- || !qt_is_finite(boundingRect.width()) || !qt_is_finite(boundingRect.height())) {
- #ifndef QT_NO_DEBUG
- qWarning("QPainterPath::addEllipse: Adding ellipse where a parameter is NaN or Inf, ignoring call");
- #endif
- return;
- }
- if (boundingRect.isNull())
- return;
- ensureData();
- detach();
- Q_D(QPainterPath);
- bool first = d_func()->elements.size() < 2;
- d->elements.reserve(d->elements.size() + 13);
- QPointF pts[12];
- int point_count;
- QPointF start = qt_curves_for_arc(boundingRect, 0, -360, pts, &point_count);
- moveTo(start);
- cubicTo(pts[0], pts[1], pts[2]); // 0 -> 270
- cubicTo(pts[3], pts[4], pts[5]); // 270 -> 180
- cubicTo(pts[6], pts[7], pts[8]); // 180 -> 90
- cubicTo(pts[9], pts[10], pts[11]); // 90 - >0
- d_func()->require_moveTo = true;
- d_func()->convex = first;
- }
- /*!
- \fn void QPainterPath::addText(const QPointF &point, const QFont &font, const QString &text)
- Adds the given \a text to this path as a set of closed subpaths
- created from the \a font supplied. The subpaths are positioned so
- that the left end of the text's baseline lies at the specified \a
- point.
- \table 100%
- \row
- \li \inlineimage qpainterpath-addtext.png
- \li
- \snippet code/src_gui_painting_qpainterpath.cpp 6
- \endtable
- \sa QPainter::drawText(), {QPainterPath#Composing a
- QPainterPath}{Composing a QPainterPath}
- */
- void QPainterPath::addText(const QPointF &point, const QFont &f, const QString &text)
- {
- if (text.isEmpty())
- return;
- ensureData();
- detach();
- QTextLayout layout(text, f);
- layout.setCacheEnabled(true);
- QTextEngine *eng = layout.engine();
- layout.beginLayout();
- QTextLine line = layout.createLine();
- Q_UNUSED(line);
- layout.endLayout();
- const QScriptLine &sl = eng->lines[0];
- if (!sl.length || !eng->layoutData)
- return;
- int nItems = eng->layoutData->items.size();
- qreal x(point.x());
- qreal y(point.y());
- QVarLengthArray<int> visualOrder(nItems);
- QVarLengthArray<uchar> levels(nItems);
- for (int i = 0; i < nItems; ++i)
- levels[i] = eng->layoutData->items[i].analysis.bidiLevel;
- QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
- for (int i = 0; i < nItems; ++i) {
- int item = visualOrder[i];
- QScriptItem &si = eng->layoutData->items[item];
- if (si.analysis.flags < QScriptAnalysis::TabOrObject) {
- QGlyphLayout glyphs = eng->shapedGlyphs(&si);
- QFontEngine *fe = f.d->engineForScript(si.analysis.script);
- Q_ASSERT(fe);
- fe->addOutlineToPath(x, y, glyphs, this,
- si.analysis.bidiLevel % 2
- ? QTextItem::RenderFlags(QTextItem::RightToLeft)
- : QTextItem::RenderFlags(0));
- const qreal lw = fe->lineThickness().toReal();
- if (f.d->underline) {
- qreal pos = fe->underlinePosition().toReal();
- addRect(x, y + pos, si.width.toReal(), lw);
- }
- if (f.d->overline) {
- qreal pos = fe->ascent().toReal() + 1;
- addRect(x, y - pos, si.width.toReal(), lw);
- }
- if (f.d->strikeOut) {
- qreal pos = fe->ascent().toReal() / 3;
- addRect(x, y - pos, si.width.toReal(), lw);
- }
- }
- x += si.width.toReal();
- }
- }
- /*!
- \fn void QPainterPath::addPath(const QPainterPath &path)
- Adds the given \a path to \e this path as a closed subpath.
- \sa connectPath(), {QPainterPath#Composing a
- QPainterPath}{Composing a QPainterPath}
- */
- void QPainterPath::addPath(const QPainterPath &other)
- {
- if (other.isEmpty())
- return;
- ensureData();
- detach();
- QPainterPathData *d = reinterpret_cast<QPainterPathData *>(d_func());
- // Remove last moveto so we don't get multiple moveto's
- if (d->elements.last().type == MoveToElement)
- d->elements.remove(d->elements.size()-1);
- // Locate where our own current subpath will start after the other path is added.
- int cStart = d->elements.size() + other.d_func()->cStart;
- d->elements += other.d_func()->elements;
- d->cStart = cStart;
- d->require_moveTo = other.d_func()->isClosed();
- }
- /*!
- \fn void QPainterPath::connectPath(const QPainterPath &path)
- Connects the given \a path to \e this path by adding a line from the
- last element of this path to the first element of the given path.
- \sa addPath(), {QPainterPath#Composing a QPainterPath}{Composing
- a QPainterPath}
- */
- void QPainterPath::connectPath(const QPainterPath &other)
- {
- if (other.isEmpty())
- return;
- ensureData();
- detach();
- QPainterPathData *d = reinterpret_cast<QPainterPathData *>(d_func());
- // Remove last moveto so we don't get multiple moveto's
- if (d->elements.last().type == MoveToElement)
- d->elements.remove(d->elements.size()-1);
- // Locate where our own current subpath will start after the other path is added.
- int cStart = d->elements.size() + other.d_func()->cStart;
- int first = d->elements.size();
- d->elements += other.d_func()->elements;
- if (first != 0)
- d->elements[first].type = LineToElement;
- // avoid duplicate points
- if (first > 0 && QPointF(d->elements[first]) == QPointF(d->elements[first - 1])) {
- d->elements.remove(first--);
- --cStart;
- }
- if (cStart != first)
- d->cStart = cStart;
- }
- /*!
- Adds the given \a region to the path by adding each rectangle in
- the region as a separate closed subpath.
- \sa addRect(), {QPainterPath#Composing a QPainterPath}{Composing
- a QPainterPath}
- */
- void QPainterPath::addRegion(const QRegion ®ion)
- {
- ensureData();
- detach();
- QVector<QRect> rects = region.rects();
- d_func()->elements.reserve(rects.size() * 5);
- for (int i=0; i<rects.size(); ++i)
- addRect(rects.at(i));
- }
- /*!
- Returns the painter path's currently set fill rule.
- \sa setFillRule()
- */
- Qt::FillRule QPainterPath::fillRule() const
- {
- return isEmpty() ? Qt::OddEvenFill : d_func()->fillRule;
- }
- /*!
- \fn void QPainterPath::setFillRule(Qt::FillRule fillRule)
- Sets the fill rule of the painter path to the given \a
- fillRule. Qt provides two methods for filling paths:
- \table
- \header
- \li Qt::OddEvenFill (default)
- \li Qt::WindingFill
- \row
- \li \inlineimage qt-fillrule-oddeven.png
- \li \inlineimage qt-fillrule-winding.png
- \endtable
- \sa fillRule()
- */
- void QPainterPath::setFillRule(Qt::FillRule fillRule)
- {
- ensureData();
- if (d_func()->fillRule == fillRule)
- return;
- detach();
- d_func()->fillRule = fillRule;
- }
- #define QT_BEZIER_A(bezier, coord) 3 * (-bezier.coord##1 \
- + 3*bezier.coord##2 \
- - 3*bezier.coord##3 \
- +bezier.coord##4)
- #define QT_BEZIER_B(bezier, coord) 6 * (bezier.coord##1 \
- - 2*bezier.coord##2 \
- + bezier.coord##3)
- #define QT_BEZIER_C(bezier, coord) 3 * (- bezier.coord##1 \
- + bezier.coord##2)
- #define QT_BEZIER_CHECK_T(bezier, t) \
- if (t >= 0 && t <= 1) { \
- QPointF p(b.pointAt(t)); \
- if (p.x() < minx) minx = p.x(); \
- else if (p.x() > maxx) maxx = p.x(); \
- if (p.y() < miny) miny = p.y(); \
- else if (p.y() > maxy) maxy = p.y(); \
- }
- static QRectF qt_painterpath_bezier_extrema(const QBezier &b)
- {
- qreal minx, miny, maxx, maxy;
- // initialize with end points
- if (b.x1 < b.x4) {
- minx = b.x1;
- maxx = b.x4;
- } else {
- minx = b.x4;
- maxx = b.x1;
- }
- if (b.y1 < b.y4) {
- miny = b.y1;
- maxy = b.y4;
- } else {
- miny = b.y4;
- maxy = b.y1;
- }
- // Update for the X extrema
- {
- qreal ax = QT_BEZIER_A(b, x);
- qreal bx = QT_BEZIER_B(b, x);
- qreal cx = QT_BEZIER_C(b, x);
- // specialcase quadratic curves to avoid div by zero
- if (qFuzzyIsNull(ax)) {
- // linear curves are covered by initialization.
- if (!qFuzzyIsNull(bx)) {
- qreal t = -cx / bx;
- QT_BEZIER_CHECK_T(b, t);
- }
- } else {
- const qreal tx = bx * bx - 4 * ax * cx;
- if (tx >= 0) {
- qreal temp = qSqrt(tx);
- qreal rcp = 1 / (2 * ax);
- qreal t1 = (-bx + temp) * rcp;
- QT_BEZIER_CHECK_T(b, t1);
- qreal t2 = (-bx - temp) * rcp;
- QT_BEZIER_CHECK_T(b, t2);
- }
- }
- }
- // Update for the Y extrema
- {
- qreal ay = QT_BEZIER_A(b, y);
- qreal by = QT_BEZIER_B(b, y);
- qreal cy = QT_BEZIER_C(b, y);
- // specialcase quadratic curves to avoid div by zero
- if (qFuzzyIsNull(ay)) {
- // linear curves are covered by initialization.
- if (!qFuzzyIsNull(by)) {
- qreal t = -cy / by;
- QT_BEZIER_CHECK_T(b, t);
- }
- } else {
- const qreal ty = by * by - 4 * ay * cy;
- if (ty > 0) {
- qreal temp = qSqrt(ty);
- qreal rcp = 1 / (2 * ay);
- qreal t1 = (-by + temp) * rcp;
- QT_BEZIER_CHECK_T(b, t1);
- qreal t2 = (-by - temp) * rcp;
- QT_BEZIER_CHECK_T(b, t2);
- }
- }
- }
- return QRectF(minx, miny, maxx - minx, maxy - miny);
- }
- /*!
- Returns the bounding rectangle of this painter path as a rectangle with
- floating point precision.
- \sa controlPointRect()
- */
- QRectF QPainterPath::boundingRect() const
- {
- if (!d_ptr)
- return QRectF();
- QPainterPathData *d = d_func();
- if (d->dirtyBounds)
- computeBoundingRect();
- return d->bounds;
- }
- /*!
- Returns the rectangle containing all the points and control points
- in this path.
- This function is significantly faster to compute than the exact
- boundingRect(), and the returned rectangle is always a superset of
- the rectangle returned by boundingRect().
- \sa boundingRect()
- */
- QRectF QPainterPath::controlPointRect() const
- {
- if (!d_ptr)
- return QRectF();
- QPainterPathData *d = d_func();
- if (d->dirtyControlBounds)
- computeControlPointRect();
- return d->controlBounds;
- }
- /*!
- \fn bool QPainterPath::isEmpty() const
- Returns \c true if either there are no elements in this path, or if the only
- element is a MoveToElement; otherwise returns \c false.
- \sa elementCount()
- */
- bool QPainterPath::isEmpty() const
- {
- return !d_ptr || (d_ptr->elements.size() == 1 && d_ptr->elements.first().type == MoveToElement);
- }
- /*!
- Creates and returns a reversed copy of the path.
- It is the order of the elements that is reversed: If a
- QPainterPath is composed by calling the moveTo(), lineTo() and
- cubicTo() functions in the specified order, the reversed copy is
- composed by calling cubicTo(), lineTo() and moveTo().
- */
- QPainterPath QPainterPath::toReversed() const
- {
- Q_D(const QPainterPath);
- QPainterPath rev;
- if (isEmpty()) {
- rev = *this;
- return rev;
- }
- rev.moveTo(d->elements.at(d->elements.size()-1).x, d->elements.at(d->elements.size()-1).y);
- for (int i=d->elements.size()-1; i>=1; --i) {
- const QPainterPath::Element &elm = d->elements.at(i);
- const QPainterPath::Element &prev = d->elements.at(i-1);
- switch (elm.type) {
- case LineToElement:
- rev.lineTo(prev.x, prev.y);
- break;
- case MoveToElement:
- rev.moveTo(prev.x, prev.y);
- break;
- case CurveToDataElement:
- {
- Q_ASSERT(i>=3);
- const QPainterPath::Element &cp1 = d->elements.at(i-2);
- const QPainterPath::Element &sp = d->elements.at(i-3);
- Q_ASSERT(prev.type == CurveToDataElement);
- Q_ASSERT(cp1.type == CurveToElement);
- rev.cubicTo(prev.x, prev.y, cp1.x, cp1.y, sp.x, sp.y);
- i -= 2;
- break;
- }
- default:
- Q_ASSERT(!"qt_reversed_path");
- break;
- }
- }
- //qt_debug_path(rev);
- return rev;
- }
- /*!
- Converts the path into a list of polygons using the QTransform
- \a matrix, and returns the list.
- This function creates one polygon for each subpath regardless of
- intersecting subpaths (i.e. overlapping bounding rectangles). To
- make sure that such overlapping subpaths are filled correctly, use
- the toFillPolygons() function instead.
- \sa toFillPolygons(), toFillPolygon(), {QPainterPath#QPainterPath
- Conversion}{QPainterPath Conversion}
- */
- QList<QPolygonF> QPainterPath::toSubpathPolygons(const QTransform &matrix) const
- {
- Q_D(const QPainterPath);
- QList<QPolygonF> flatCurves;
- if (isEmpty())
- return flatCurves;
- QPolygonF current;
- for (int i=0; i<elementCount(); ++i) {
- const QPainterPath::Element &e = d->elements.at(i);
- switch (e.type) {
- case QPainterPath::MoveToElement:
- if (current.size() > 1)
- flatCurves += current;
- current.clear();
- current.reserve(16);
- current += QPointF(e.x, e.y) * matrix;
- break;
- case QPainterPath::LineToElement:
- current += QPointF(e.x, e.y) * matrix;
- break;
- case QPainterPath::CurveToElement: {
- Q_ASSERT(d->elements.at(i+1).type == QPainterPath::CurveToDataElement);
- Q_ASSERT(d->elements.at(i+2).type == QPainterPath::CurveToDataElement);
- QBezier bezier = QBezier::fromPoints(QPointF(d->elements.at(i-1).x, d->elements.at(i-1).y) * matrix,
- QPointF(e.x, e.y) * matrix,
- QPointF(d->elements.at(i+1).x, d->elements.at(i+1).y) * matrix,
- QPointF(d->elements.at(i+2).x, d->elements.at(i+2).y) * matrix);
- bezier.addToPolygon(¤t);
- i+=2;
- break;
- }
- case QPainterPath::CurveToDataElement:
- Q_ASSERT(!"QPainterPath::toSubpathPolygons(), bad element type");
- break;
- }
- }
- if (current.size()>1)
- flatCurves += current;
- return flatCurves;
- }
- /*!
- \overload
- */
- QList<QPolygonF> QPainterPath::toSubpathPolygons(const QMatrix &matrix) const
- {
- return toSubpathPolygons(QTransform(matrix));
- }
- /*!
- Converts the path into a list of polygons using the
- QTransform \a matrix, and returns the list.
- The function differs from the toFillPolygon() function in that it
- creates several polygons. It is provided because it is usually
- faster to draw several small polygons than to draw one large
- polygon, even though the total number of points drawn is the same.
- The toFillPolygons() function differs from the toSubpathPolygons()
- function in that it create only polygon for subpaths that have
- overlapping bounding rectangles.
- Like the toFillPolygon() function, this function uses a rewinding
- technique to make sure that overlapping subpaths can be filled
- using the correct fill rule. Note that rewinding inserts addition
- lines in the polygons so the outline of the fill polygon does not
- match the outline of the path.
- \sa toSubpathPolygons(), toFillPolygon(),
- {QPainterPath#QPainterPath Conversion}{QPainterPath Conversion}
- */
- QList<QPolygonF> QPainterPath::toFillPolygons(const QTransform &matrix) const
- {
- QList<QPolygonF> polys;
- QList<QPolygonF> subpaths = toSubpathPolygons(matrix);
- int count = subpaths.size();
- if (count == 0)
- return polys;
- QList<QRectF> bounds;
- for (int i=0; i<count; ++i)
- bounds += subpaths.at(i).boundingRect();
- #ifdef QPP_FILLPOLYGONS_DEBUG
- printf("QPainterPath::toFillPolygons, subpathCount=%d\n", count);
- for (int i=0; i<bounds.size(); ++i)
- qDebug() << " bounds" << i << bounds.at(i);
- #endif
- QVector< QList<int> > isects;
- isects.resize(count);
- // find all intersections
- for (int j=0; j<count; ++j) {
- if (subpaths.at(j).size() <= 2)
- continue;
- QRectF cbounds = bounds.at(j);
- for (int i=0; i<count; ++i) {
- if (cbounds.intersects(bounds.at(i))) {
- isects[j] << i;
- }
- }
- }
- #ifdef QPP_FILLPOLYGONS_DEBUG
- printf("Intersections before flattening:\n");
- for (int i = 0; i < count; ++i) {
- printf("%d: ", i);
- for (int j = 0; j < isects[i].size(); ++j) {
- printf("%d ", isects[i][