From 34fccf47ecf8ab2db53b23013f4f402c175ce9de Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Wed, 30 Nov 2022 12:12:03 +0100 Subject: Implement all four map items with Shape With a master switch in qdeclarativegeomapitembase_p.h, this is enabled by default. For now the old method is still available by just commenting out the define. The old path can be removed in follow-up patches; for now keeping both so one can compare and debug if further issues arise. Pick-to: 6.5 Change-Id: I01c44ee8a07d7d5f7eb018be33ac5d161ba90e2a Reviewed-by: Matthias Rauter Reviewed-by: Volker Hilsheimer --- CMakeLists.txt | 2 +- src/location/CMakeLists.txt | 1 + .../quickmapitems/qdeclarativecirclemapitem.cpp | 115 ++++++++++++++++++--- .../quickmapitems/qdeclarativecirclemapitem_p_p.h | 20 ++-- .../quickmapitems/qdeclarativegeomapitembase.cpp | 22 +++- .../quickmapitems/qdeclarativegeomapitembase_p.h | 29 ++++++ .../quickmapitems/qdeclarativepolygonmapitem.cpp | 94 +++++++++++++++-- .../quickmapitems/qdeclarativepolygonmapitem_p_p.h | 28 ++++- .../quickmapitems/qdeclarativepolylinemapitem.cpp | 114 ++++++++++++++++++-- .../qdeclarativepolylinemapitem_p_p.h | 22 ++-- .../quickmapitems/qdeclarativerectanglemapitem.cpp | 94 +++++++++++++++-- .../qdeclarativerectanglemapitem_p_p.h | 26 +++-- src/location/quickmapitems/qgeomapitemgeometry.cpp | 2 + src/location/quickmapitems/qgeomapitemgeometry_p.h | 6 +- 14 files changed, 501 insertions(+), 74 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c186a19..7bc5110f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,7 +28,7 @@ endif() # Need to search for positioning only after we make sure that it's not WASM. # Otherwise we'll get an "QtPositioning not found" error in WASM build. find_package(Qt6 ${PROJECT_VERSION} CONFIG REQUIRED COMPONENTS - Qml Quick Network Test QuickTest Positioning PositioningQuick + Qml Quick Network Test QuickTest Positioning PositioningQuick QuickShapesPrivate ) find_package(Qt6 ${PROJECT_VERSION} QUIET CONFIG OPTIONAL_COMPONENTS ShaderTools diff --git a/src/location/CMakeLists.txt b/src/location/CMakeLists.txt index e0eca263..dce71dfe 100644 --- a/src/location/CMakeLists.txt +++ b/src/location/CMakeLists.txt @@ -86,6 +86,7 @@ qt_internal_add_module(Location Qt::Core Qt::Positioning Qt::PositioningQuick + Qt::QuickShapesPrivate PRIVATE_MODULE_INTERFACE Qt::CorePrivate Qt::QuickPrivate diff --git a/src/location/quickmapitems/qdeclarativecirclemapitem.cpp b/src/location/quickmapitems/qdeclarativecirclemapitem.cpp index 5156b5e9..727e2eb0 100644 --- a/src/location/quickmapitems/qdeclarativecirclemapitem.cpp +++ b/src/location/quickmapitems/qdeclarativecirclemapitem.cpp @@ -16,6 +16,8 @@ #include #include +#include + QT_BEGIN_NAMESPACE /*! @@ -109,11 +111,12 @@ QGeoMapCircleGeometry::QGeoMapCircleGeometry() /*! \internal */ -void QGeoMapCircleGeometry::updateScreenPointsInvert(const QList &circlePath, const QGeoMap &map) +void QGeoMapCircleGeometry::updateSourceAndScreenPointsInvert(const QList &circlePath, const QGeoMap &map) { const QGeoProjectionWebMercator &p = static_cast(map.geoProjection()); // Not checking for !screenDirty anymore, as everything is now recalculated. clear(); + srcPath_ = QPainterPath(); if (map.viewportWidth() == 0 || map.viewportHeight() == 0 || circlePath.size() < 3) // a circle requires at least 3 points; return; @@ -187,23 +190,28 @@ void QGeoMapCircleGeometry::updateScreenPointsInvert(const QList &path: clippedPaths) { QDoubleVector2D lastAddedPoint; for (qsizetype i = 0; i < path.size(); ++i) { QDoubleVector2D point = p.wrappedMapProjectionToItemPosition(path.at(i)); //point = point - origin; // Do this using ppi.translate() + const QDoubleVector2D pt = point - origin; + if (qMax(pt.x(), pt.y()) > maxCoord_) + maxCoord_ = qMax(pt.x(), pt.y()); + if (i == 0) { - ppi.moveTo(point.toPointF()); + srcPath_.moveTo(point.toPointF()); lastAddedPoint = point; } else if ((point - lastAddedPoint).manhattanLength() > 3 || i == path.size() - 1) { - ppi.lineTo(point.toPointF()); + srcPath_.lineTo(point.toPointF()); lastAddedPoint = point; } } - ppi.closeSubpath(); + srcPath_.closeSubpath(); } + + QPainterPath ppi = srcPath_; ppi.translate(-1 * origin.toPointF()); QTriangleSet ts = qTriangulate(ppi); @@ -225,7 +233,7 @@ void QGeoMapCircleGeometry::updateScreenPointsInvert(const QListsetObjectName("_qt_map_item_shape"); + m_shape->setZ(-1); + m_shape->setContainsMode(QQuickShape::FillContains); + + m_shapePath = new QQuickShapePath(m_shape); + m_painterPath = new QDeclarativeGeoMapPainterPath(m_shapePath); + + auto pathElements = m_shapePath->pathElements(); + pathElements.append(&pathElements, m_painterPath); -QDeclarativeCircleMapItemPrivateCPU::~QDeclarativeCircleMapItemPrivateCPU() {} + auto shapePaths = m_shape->data(); + shapePaths.append(&shapePaths, m_shapePath); +#endif +} + +QDeclarativeCircleMapItemPrivateCPU::~QDeclarativeCircleMapItemPrivateCPU() +{ +#ifdef MAPITEMS_USE_SHAPES + delete m_shape; +#endif +} bool QDeclarativeCircleMapItemPrivate::preserveCircleGeometry (QList &path, const QGeoCoordinate ¢er, qreal distance, const QGeoProjectionWebMercator &p) @@ -597,9 +632,13 @@ void QDeclarativeCircleMapItemPrivateCPU::updatePolish() { if (!m_circle.m_circle.isValid()) { m_geometry.clear(); - m_borderGeometry.clear(); m_circle.setWidth(0); m_circle.setHeight(0); +#ifdef MAPITEMS_USE_SHAPES + m_shape->setVisible(false); +#else + m_borderGeometry.clear(); +#endif return; } @@ -619,16 +658,18 @@ void QDeclarativeCircleMapItemPrivateCPU::updatePolish() m_geometry.setPreserveGeometry(preserve, m_leftBound); bool invertedCircle = false; - if (crossEarthPole(m_circle.m_circle.center(), m_circle.m_circle.radius()) - && circlePath.size() == pathCount) { + if (crossEarthPole(m_circle.m_circle.center(), m_circle.m_circle.radius()) && circlePath.size() == pathCount) { // invert fill area for really huge circles - m_geometry.updateScreenPointsInvert(circlePath, *m_circle.map()); + m_geometry.updateSourceAndScreenPointsInvert(circlePath, *m_circle.map()); invertedCircle = true; } else { m_geometry.updateSourcePoints(*m_circle.map(), circlePath); - m_geometry.updateScreenPoints(*m_circle.map(), m_circle.m_border.width()); } +#ifndef MAPITEMS_USE_SHAPES + if (!invertedCircle) + m_geometry.updateScreenPoints(*m_circle.map(), m_circle.m_border.width()); + m_borderGeometry.clear(); QList geoms; geoms << &m_geometry; @@ -665,9 +706,33 @@ void QDeclarativeCircleMapItemPrivateCPU::updatePolish() m_borderGeometry.clear(); } } - - QRectF combined = QGeoMapItemGeometry::translateToCommonOrigin(geoms); - +#endif + +#ifdef MAPITEMS_USE_SHAPES + m_circle.setShapeTriangulationScale(m_shape, m_geometry.maxCoord()); + + const bool hasBorder = m_circle.m_border.color().alpha() != 0 && m_circle.m_border.width() > 0; + const float borderWidth = hasBorder ? m_circle.m_border.width() : 0.0f; + m_shapePath->setStrokeColor(hasBorder ? m_circle.m_border.color() : Qt::transparent); + m_shapePath->setStrokeWidth(hasBorder ? borderWidth : -1.0f); + m_shapePath->setFillColor(m_circle.color()); + + const QRectF bb = m_geometry.sourceBoundingBox(); + QPainterPath path = m_geometry.srcPath(); + path.translate(-bb.left() + borderWidth, -bb.top() + borderWidth); + path.closeSubpath(); + m_painterPath->setPath(path); + + m_circle.setSize(invertedCircle || !preserve + ? bb.size() + : bb.size() + QSize(2 * borderWidth, 2 * borderWidth)); + m_shape->setSize(m_circle.size()); + m_shape->setOpacity(m_circle.zoomLevelOpacity()); + m_shape->setVisible(true); + + m_circle.setPositionOnMap(m_geometry.origin(), -1 * bb.topLeft() + QPointF(borderWidth, borderWidth)); +#else + const QRectF combined = QGeoMapItemGeometry::translateToCommonOrigin(geoms); if (invertedCircle || !preserve) { m_circle.setWidth(combined.width()); m_circle.setHeight(combined.height()); @@ -675,15 +740,24 @@ void QDeclarativeCircleMapItemPrivateCPU::updatePolish() m_circle.setWidth(combined.width() + 2 * m_circle.m_border.width()); // ToDo: Fix this! m_circle.setHeight(combined.height() + 2 * m_circle.m_border.width()); } - // No offsetting here, even in normal case, because first point offset is already translated m_circle.setPositionOnMap(m_geometry.origin(), m_geometry.firstPointOffset()); +#endif } QSGNode *QDeclarativeCircleMapItemPrivateCPU::updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) { Q_UNUSED(data); +#ifdef MAPITEMS_USE_SHAPES + delete oldNode; + if (m_geometry.isScreenDirty() || m_circle.m_dirtyMaterial) { + m_geometry.setPreserveGeometry(false); + m_geometry.markClean(); + m_circle.m_dirtyMaterial = false; + } + return nullptr; +#else if (!m_node || !oldNode) { // Apparently the QSG might delete the nodes if they become invisible m_node = new MapPolygonNode(); if (oldNode) { @@ -703,11 +777,18 @@ QSGNode *QDeclarativeCircleMapItemPrivateCPU::updateMapItemPaintNode(QSGNode *ol m_borderGeometry.markClean(); m_circle.m_dirtyMaterial = false; } + return m_node; +#endif } + bool QDeclarativeCircleMapItemPrivateCPU::contains(const QPointF &point) const { +#ifdef MAPITEMS_USE_SHAPES + return m_shape->contains(m_circle.mapToItem(m_shape, point)); +#else return (m_geometry.contains(point) || m_borderGeometry.contains(point)); +#endif } QT_END_NAMESPACE diff --git a/src/location/quickmapitems/qdeclarativecirclemapitem_p_p.h b/src/location/quickmapitems/qdeclarativecirclemapitem_p_p.h index e933cb70..e4e3a59c 100644 --- a/src/location/quickmapitems/qdeclarativecirclemapitem_p_p.h +++ b/src/location/quickmapitems/qdeclarativecirclemapitem_p_p.h @@ -22,12 +22,15 @@ QT_BEGIN_NAMESPACE +class QQuickShape; +class QQuickShapePath; + class Q_LOCATION_PRIVATE_EXPORT QGeoMapCircleGeometry : public QGeoMapPolygonGeometry { public: QGeoMapCircleGeometry(); - void updateScreenPointsInvert(const QList &circlePath, const QGeoMap &map); + void updateSourceAndScreenPointsInvert(const QList &circlePath, const QGeoMap &map); }; class Q_LOCATION_PRIVATE_EXPORT QDeclarativeCircleMapItemPrivate @@ -83,12 +86,7 @@ public: class Q_LOCATION_PRIVATE_EXPORT QDeclarativeCircleMapItemPrivateCPU: public QDeclarativeCircleMapItemPrivate { public: - - QDeclarativeCircleMapItemPrivateCPU(QDeclarativeCircleMapItem &circle) - : QDeclarativeCircleMapItemPrivate(circle) - { - } - + QDeclarativeCircleMapItemPrivateCPU(QDeclarativeCircleMapItem &circle); ~QDeclarativeCircleMapItemPrivateCPU() override; void onLinePropertiesChanged() override @@ -100,7 +98,9 @@ public: { // preserveGeometry is cleared in updateMapItemPaintNode m_geometry.markSourceDirty(); +#ifndef MAPITEMS_USE_SHAPES m_borderGeometry.markSourceDirty(); +#endif m_circle.polishAndUpdate(); } void onMapSet() override @@ -126,8 +126,14 @@ public: bool contains(const QPointF &point) const override; QGeoMapCircleGeometry m_geometry; +#ifdef MAPITEMS_USE_SHAPES + QQuickShape *m_shape = nullptr; + QQuickShapePath *m_shapePath = nullptr; + QDeclarativeGeoMapPainterPath *m_painterPath = nullptr; +#else QGeoMapPolylineGeometry m_borderGeometry; MapPolygonNode *m_node = nullptr; +#endif }; QT_END_NAMESPACE diff --git a/src/location/quickmapitems/qdeclarativegeomapitembase.cpp b/src/location/quickmapitems/qdeclarativegeomapitembase.cpp index 88bf801d..c5b1cc8c 100644 --- a/src/location/quickmapitems/qdeclarativegeomapitembase.cpp +++ b/src/location/quickmapitems/qdeclarativegeomapitembase.cpp @@ -13,6 +13,8 @@ #include #include +#include + QT_BEGIN_NAMESPACE QDeclarativeGeoMapItemBase::QDeclarativeGeoMapItemBase(QQuickItem *parent) @@ -43,7 +45,9 @@ void QDeclarativeGeoMapItemBase::afterChildrenChanged() bool printedWarning = false; for (auto *i : kids) { if (i->flags() & QQuickItem::ItemHasContents - && !qobject_cast(i)) { + && !qobject_cast(i) + && i->objectName() != QStringLiteral("_qt_map_item_shape")) + { if (!printedWarning) { qmlWarning(this) << "Geographic map items do not support child items"; printedWarning = true; @@ -199,6 +203,22 @@ float QDeclarativeGeoMapItemBase::zoomLevelOpacity() const return 0.0; } +void QDeclarativeGeoMapItemBase::setShapeTriangulationScale(QQuickShape *shape, qreal maxCoord) const +{ + const qreal zoom = qMax(0.01, quickMap_->zoomLevel()); + qreal scale = 1 / zoom; + + // cater also for QTriangulator's 65536 coordinate limit due to the fixed point math + qint64 coord = qint64(maxCoord); + const qint64 COORD_LIMIT = (1 << 21) / 32; // 65536 (where 32 is Q_FIXED_POINT_SCALE) + while (coord > COORD_LIMIT) { + coord /= COORD_LIMIT; + scale /= COORD_LIMIT; + } + + QQuickShapePrivate::get(shape)->triangulationScale = scale; +} + /*! \internal */ diff --git a/src/location/quickmapitems/qdeclarativegeomapitembase_p.h b/src/location/quickmapitems/qdeclarativegeomapitembase_p.h index 899738d0..18624060 100644 --- a/src/location/quickmapitems/qdeclarativegeomapitembase_p.h +++ b/src/location/quickmapitems/qdeclarativegeomapitembase_p.h @@ -25,6 +25,11 @@ #include #include #include +#include + +// Master switch to make rectangle, circle, polygon, and polyline use +// QQuickShape instead of a custom scenegraph node. +#define MAPITEMS_USE_SHAPES QT_BEGIN_NAMESPACE @@ -94,6 +99,8 @@ public: return res; } + void setShapeTriangulationScale(QQuickShape *shape, qreal maxCoord) const; + Q_SIGNALS: void mapItemOpacityChanged(); Q_REVISION(12) void addTransitionFinished(); @@ -134,6 +141,28 @@ private: friend class QDeclarativeGeoMapItemTransitionManager; }; +class QDeclarativeGeoMapPainterPath : public QQuickCurve +{ + Q_OBJECT +public: + QDeclarativeGeoMapPainterPath(QObject *parent = nullptr) : QQuickCurve(parent) {} + QPainterPath path() const + { + return m_path; + } + void setPath(const QPainterPath &path) + { + m_path = path; + emit changed(); + } + void addToPath(QPainterPath &path, const QQuickPathData &) override + { + path.addPath(m_path); + } +private: + QPainterPath m_path; +}; + QT_END_NAMESPACE #endif diff --git a/src/location/quickmapitems/qdeclarativepolygonmapitem.cpp b/src/location/quickmapitems/qdeclarativepolygonmapitem.cpp index 480b12cc..897ef22d 100644 --- a/src/location/quickmapitems/qdeclarativepolygonmapitem.cpp +++ b/src/location/quickmapitems/qdeclarativepolygonmapitem.cpp @@ -187,6 +187,7 @@ void QGeoMapPolygonGeometry::updateSourcePoints(const QGeoMap &map, } // 3) + maxCoord_ = 0.0; QDoubleVector2D origin = p.wrappedMapProjectionToItemPosition(leftBoundWrapped); for (const QList &path: clippedPaths) { QDoubleVector2D lastAddedPoint; @@ -194,6 +195,9 @@ void QGeoMapPolygonGeometry::updateSourcePoints(const QGeoMap &map, QDoubleVector2D point = p.wrappedMapProjectionToItemPosition(path.at(i)); point = point - origin; // (0,0) if point == geoLeftBound_ + if (qMax(point.x(), point.y()) > maxCoord_) + maxCoord_ = qMax(point.x(), point.y()); + if (i == 0) { srcPath_.moveTo(point.toPointF()); lastAddedPoint = point; @@ -214,6 +218,7 @@ void QGeoMapPolygonGeometry::updateSourcePoints(const QGeoMap &map, sourceBounds_ = srcPath_.boundingRect(); } +#ifndef MAPITEMS_USE_SHAPES /*! \internal */ @@ -284,37 +289,71 @@ void QGeoMapPolygonGeometry::updateScreenPoints(const QGeoMap &map, qreal stroke if (strokeWidth != 0.0) this->translate(QPointF(strokeWidth, strokeWidth)); } +#endif /* * QDeclarativePolygonMapItem Private Implementations */ -QDeclarativePolygonMapItemPrivate::~QDeclarativePolygonMapItemPrivate() {} +QDeclarativePolygonMapItemPrivate::~QDeclarativePolygonMapItemPrivate() +{ +} + +QDeclarativePolygonMapItemPrivateCPU::QDeclarativePolygonMapItemPrivateCPU(QDeclarativePolygonMapItem &polygon) + : QDeclarativePolygonMapItemPrivate(polygon) +{ +#ifdef MAPITEMS_USE_SHAPES + m_shape = new QQuickShape(&m_poly); + m_shape->setObjectName("_qt_map_item_shape"); + m_shape->setZ(-1); + m_shape->setContainsMode(QQuickShape::FillContains); + + m_shapePath = new QQuickShapePath(m_shape); + m_painterPath = new QDeclarativeGeoMapPainterPath(m_shapePath); + + auto pathElements = m_shapePath->pathElements(); + pathElements.append(&pathElements, m_painterPath); -QDeclarativePolygonMapItemPrivateCPU::~QDeclarativePolygonMapItemPrivateCPU() {} + auto shapePaths = m_shape->data(); + shapePaths.append(&shapePaths, m_shapePath); +#endif +} + +QDeclarativePolygonMapItemPrivateCPU::~QDeclarativePolygonMapItemPrivateCPU() +{ +#ifdef MAPITEMS_USE_SHAPES + delete m_shape; +#endif +} void QDeclarativePolygonMapItemPrivateCPU::updatePolish() { if (m_poly.m_geopoly.perimeter().length() == 0) { // Possibly cleared m_geometry.clear(); - m_borderGeometry.clear(); m_poly.setWidth(0); m_poly.setHeight(0); +#ifdef MAPITEMS_USE_SHAPES + m_shape->setVisible(false); +#else + m_borderGeometry.clear(); +#endif return; } const QGeoMap *map = m_poly.map(); const qreal borderWidth = m_poly.m_border.width(); - const QGeoProjectionWebMercator &p = static_cast(map->geoProjection()); QScopedValueRollback rollback(m_poly.m_updatingGeometry); m_poly.m_updatingGeometry = true; m_geometry.updateSourcePoints(*map, m_geopathProjected); + +#ifndef MAPITEMS_USE_SHAPES m_geometry.updateScreenPoints(*map, borderWidth); + const QGeoProjectionWebMercator &p = static_cast(map->geoProjection()); QList geoms; geoms << &m_geometry; - m_borderGeometry.clear(); + m_borderGeometry.clear(); if (m_poly.m_border.color().alpha() != 0 && borderWidth > 0) { QList closedPath = m_geopathProjected; closedPath << closedPath.first(); @@ -338,19 +377,49 @@ void QDeclarativePolygonMapItemPrivateCPU::updatePolish() m_borderGeometry.clear(); } } +#endif + + const QRectF bb = m_geometry.sourceBoundingBox(); + +#ifdef MAPITEMS_USE_SHAPES + m_poly.setShapeTriangulationScale(m_shape, m_geometry.maxCoord()); + + const bool hasBorder = m_poly.m_border.color().alpha() != 0 && m_poly.m_border.width() > 0; + m_shapePath->setStrokeColor(hasBorder ? m_poly.m_border.color() : Qt::transparent); + m_shapePath->setStrokeWidth(hasBorder ? borderWidth : -1.0f); + m_shapePath->setFillColor(m_poly.color()); - QRectF combined = QGeoMapItemGeometry::translateToCommonOrigin(geoms); + QPainterPath path = m_geometry.srcPath(); + path.translate(-bb.left() + borderWidth, -bb.top() + borderWidth); + path.closeSubpath(); + m_painterPath->setPath(path); + + m_poly.setSize(bb.size() + QSize(2 * borderWidth, 2 * borderWidth)); + m_shape->setSize(m_poly.size()); + m_shape->setOpacity(m_poly.zoomLevelOpacity()); + m_shape->setVisible(true); +#else + const QRectF combined = QGeoMapItemGeometry::translateToCommonOrigin(geoms); m_poly.setWidth(combined.width() + 2 * borderWidth); m_poly.setHeight(combined.height() + 2 * borderWidth); +#endif - m_poly.setPositionOnMap(m_geometry.origin(), -1 * m_geometry.sourceBoundingBox().topLeft() - + QPointF(borderWidth, borderWidth)); + m_poly.setPositionOnMap(m_geometry.origin(), -1 * bb.topLeft() + QPointF(borderWidth, borderWidth)); } QSGNode *QDeclarativePolygonMapItemPrivateCPU::updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) { Q_UNUSED(data); +#ifdef MAPITEMS_USE_SHAPES + delete oldNode; + if (m_geometry.isScreenDirty() || m_poly.m_dirtyMaterial) { + m_geometry.setPreserveGeometry(false); + m_geometry.markClean(); + m_poly.m_dirtyMaterial = false; + } + return nullptr; +#else if (!m_node || !oldNode) { m_node = new MapPolygonNode(); if (oldNode) { @@ -376,12 +445,18 @@ QSGNode *QDeclarativePolygonMapItemPrivateCPU::updateMapItemPaintNode(QSGNode *o m_borderGeometry.markClean(); m_poly.m_dirtyMaterial = false; } + return m_node; +#endif } bool QDeclarativePolygonMapItemPrivateCPU::contains(const QPointF &point) const { +#ifdef MAPITEMS_USE_SHAPES + return m_shape->contains(m_poly.mapToItem(m_shape, point)); +#else return (m_geometry.contains(point) || m_borderGeometry.contains(point)); +#endif } /* @@ -624,6 +699,8 @@ void QDeclarativePolygonMapItem::geometryChange(const QRectF &newGeometry, const ////////////////////////////////////////////////////////////////////// +#ifndef MAPITEMS_USE_SHAPES + MapPolygonNode::MapPolygonNode() : border_(new MapPolylineNode()), geometry_(QSGGeometry::defaultAttributes_Point2D(), 0) @@ -674,5 +751,6 @@ void MapPolygonNode::update(const QColor &fillColor, const QColor &borderColor, } } +#endif QT_END_NAMESPACE diff --git a/src/location/quickmapitems/qdeclarativepolygonmapitem_p_p.h b/src/location/quickmapitems/qdeclarativepolygonmapitem_p_p.h index 8595bd7b..80f2159c 100644 --- a/src/location/quickmapitems/qdeclarativepolygonmapitem_p_p.h +++ b/src/location/quickmapitems/qdeclarativepolygonmapitem_p_p.h @@ -32,6 +32,9 @@ QT_BEGIN_NAMESPACE +class QQuickShape; +class QQuickShapePath; + class Q_LOCATION_PRIVATE_EXPORT QGeoMapPolygonGeometry : public QGeoMapItemGeometry { public: @@ -42,13 +45,20 @@ public: void updateSourcePoints(const QGeoMap &map, const QList &path); +#ifndef MAPITEMS_USE_SHAPES void updateScreenPoints(const QGeoMap &map, qreal strokeWidth = 0.0); +#endif + + QPainterPath srcPath() const { return srcPath_; } + qreal maxCoord() const { return maxCoord_; } protected: QPainterPath srcPath_; + qreal maxCoord_ = 0.0; bool assumeSimple_ = false; }; +#ifndef MAPITEMS_USE_SHAPES class Q_LOCATION_PRIVATE_EXPORT MapPolygonNode : public MapItemGeometryNode { @@ -64,6 +74,7 @@ private: MapPolylineNode *border_; QSGGeometry geometry_; }; +#endif class Q_LOCATION_PRIVATE_EXPORT QDeclarativePolygonMapItemPrivate { @@ -92,12 +103,9 @@ public: class Q_LOCATION_PRIVATE_EXPORT QDeclarativePolygonMapItemPrivateCPU: public QDeclarativePolygonMapItemPrivate { public: - QDeclarativePolygonMapItemPrivateCPU(QDeclarativePolygonMapItem &polygon) - : QDeclarativePolygonMapItemPrivate(polygon) - { - } - + QDeclarativePolygonMapItemPrivateCPU(QDeclarativePolygonMapItem &polygon); ~QDeclarativePolygonMapItemPrivateCPU() override; + void onLinePropertiesChanged() override { // mark dirty just in case we're a width change @@ -107,7 +115,9 @@ public: { // preserveGeometry is cleared in updateMapItemPaintNode m_geometry.markSourceDirty(); +#ifndef MAPITEMS_USE_SHAPES m_borderGeometry.markSourceDirty(); +#endif m_poly.polishAndUpdate(); } void regenerateCache() @@ -130,7 +140,9 @@ public: void preserveGeometry() { m_geometry.setPreserveGeometry(true, m_poly.m_geopoly.boundingGeoRectangle().topLeft()); +#ifndef MAPITEMS_USE_SHAPES m_borderGeometry.setPreserveGeometry(true, m_poly.m_geopoly.boundingGeoRectangle().topLeft()); +#endif } void afterViewportChanged() override { @@ -165,8 +177,14 @@ public: QList m_geopathProjected; QGeoMapPolygonGeometry m_geometry; +#ifdef MAPITEMS_USE_SHAPES + QQuickShape *m_shape = nullptr; + QQuickShapePath *m_shapePath = nullptr; + QDeclarativeGeoMapPainterPath *m_painterPath = nullptr; +#else QGeoMapPolylineGeometry m_borderGeometry; MapPolygonNode *m_node = nullptr; +#endif }; QT_END_NAMESPACE diff --git a/src/location/quickmapitems/qdeclarativepolylinemapitem.cpp b/src/location/quickmapitems/qdeclarativepolylinemapitem.cpp index 774228c8..fda978ea 100644 --- a/src/location/quickmapitems/qdeclarativepolylinemapitem.cpp +++ b/src/location/quickmapitems/qdeclarativepolylinemapitem.cpp @@ -458,6 +458,34 @@ void QGeoMapPolylineGeometry::updateSourcePoints(const QGeoMap &map, // 3) pathToScreen(map, clippedPaths, leftBoundWrapped); + + srcPath_ = QPainterPath(); + maxCoord_ = 0.0; + const int elemCount = srcPointTypes_.count(); + for (int i = 0; i < elemCount; ++i) { + switch (srcPointTypes_[i]) { + case QPainterPath::MoveToElement: + { + const qreal x = srcPoints_[2 * i]; + const qreal y = srcPoints_[2 * i + 1]; + if (qMax(x, y) > maxCoord_) + maxCoord_ = qMax(x, y); + srcPath_.moveTo(x, y); + } + break; + case QPainterPath::LineToElement: + { + const qreal x = srcPoints_[2 * i]; + const qreal y = srcPoints_[2 * i + 1]; + if (qMax(x, y) > maxCoord_) + maxCoord_ = qMax(x, y); + srcPath_.lineTo(x, y); + } + break; + default: + break; + } + } } // *** SCREEN CLIPPING *** // @@ -650,8 +678,15 @@ void QGeoMapPolylineGeometry::updateScreenPoints(const QGeoMap &map, } screenBounds_ = bb; + const QPointF strokeOffset = (adjustTranslation) ? QPointF(strokeWidth, strokeWidth) * 0.5: QPointF(); - this->translate( -1 * sourceBounds_.topLeft() + strokeOffset); + const QPointF offset = -1 * sourceBounds_.topLeft() + strokeOffset; + for (qsizetype i = 0; i < screenVertices_.size(); ++i) + screenVertices_[i] += offset; + + firstPointOffset_ += offset; + screenOutline_.translate(offset); + screenBounds_.translate(offset); } void QGeoMapPolylineGeometry::clearSource() @@ -682,10 +717,36 @@ bool QGeoMapPolylineGeometry::contains(const QPointF &point) const * QDeclarativePolygonMapItem Private Implementations */ -QDeclarativePolylineMapItemPrivate::~QDeclarativePolylineMapItemPrivate() {} +QDeclarativePolylineMapItemPrivate::~QDeclarativePolylineMapItemPrivate() +{ +} + +QDeclarativePolylineMapItemPrivateCPU::QDeclarativePolylineMapItemPrivateCPU(QDeclarativePolylineMapItem &poly) + : QDeclarativePolylineMapItemPrivate(poly) +{ +#ifdef MAPITEMS_USE_SHAPES + m_shape = new QQuickShape(&m_poly); + m_shape->setObjectName("_qt_map_item_shape"); + m_shape->setZ(-1); + m_shape->setContainsMode(QQuickShape::FillContains); -QDeclarativePolylineMapItemPrivateCPU::~QDeclarativePolylineMapItemPrivateCPU() {} + m_shapePath = new QQuickShapePath(m_shape); + m_painterPath = new QDeclarativeGeoMapPainterPath(m_shapePath); + + auto pathElements = m_shapePath->pathElements(); + pathElements.append(&pathElements, m_painterPath); + + auto shapePaths = m_shape->data(); + shapePaths.append(&shapePaths, m_shapePath); +#endif +} +QDeclarativePolylineMapItemPrivateCPU::~QDeclarativePolylineMapItemPrivateCPU() +{ +#ifdef MAPITEMS_USE_SHAPES + delete m_shape; +#endif +} void QDeclarativePolylineMapItemPrivateCPU::regenerateCache() { @@ -712,6 +773,9 @@ void QDeclarativePolylineMapItemPrivateCPU::updatePolish() m_geometry.clear(); m_poly.setWidth(0); m_poly.setHeight(0); +#ifdef MAPITEMS_USE_SHAPES + m_shape->setVisible(false); +#endif return; } QScopedValueRollback rollback(m_poly.m_updatingGeometry); @@ -721,19 +785,38 @@ void QDeclarativePolylineMapItemPrivateCPU::updatePolish() const qreal borderWidth = m_poly.m_line.width(); m_geometry.updateSourcePoints(*map, m_geopathProjected, m_poly.m_geopath.boundingGeoRectangle().topLeft()); - m_geometry.updateScreenPoints(*map, borderWidth); - m_poly.setWidth(m_geometry.sourceBoundingBox().width() + borderWidth); - m_poly.setHeight(m_geometry.sourceBoundingBox().height() + borderWidth); + // still needed even with Shapes, due to contains() + m_geometry.updateScreenPoints(*map, borderWidth); + const QRectF bb = m_geometry.sourceBoundingBox(); + m_poly.setSize(bb.size() + QSizeF(borderWidth, borderWidth)); // it has to be shifted so that the center of the line is on the correct geocoord - m_poly.setPositionOnMap(m_geometry.origin(), -1 * m_geometry.sourceBoundingBox().topLeft() - + QPointF(borderWidth, borderWidth) * 0.5 ); + m_poly.setPositionOnMap(m_geometry.origin(), -1 * bb.topLeft() + QPointF(borderWidth, borderWidth) * 0.5); + +#ifdef MAPITEMS_USE_SHAPES + m_poly.setShapeTriangulationScale(m_shape, m_geometry.maxCoord_); + + m_shapePath->setStrokeColor(m_poly.m_line.color()); + m_shapePath->setStrokeWidth(borderWidth); + m_shapePath->setFillColor(Qt::transparent); + + QPainterPath path = m_geometry.srcPath(); + path.translate(-bb.left() + borderWidth * 0.5, -bb.top() + borderWidth * 0.5); + m_painterPath->setPath(path); + + m_shape->setSize(m_poly.size()); + m_shape->setOpacity(m_poly.zoomLevelOpacity()); + m_shape->setVisible(true); +#endif } QSGNode *QDeclarativePolylineMapItemPrivateCPU::updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData * /*data*/) { +#ifdef MAPITEMS_USE_SHAPES + delete oldNode; +#else if (!m_node || !oldNode) { m_node = new MapPolylineNode(); if (oldNode) { @@ -743,18 +826,29 @@ QSGNode *QDeclarativePolylineMapItemPrivateCPU::updateMapItemPaintNode(QSGNode * } else { m_node = static_cast(oldNode); } +#endif //TODO: update only material if (m_geometry.isScreenDirty() || m_poly.m_dirtyMaterial || !oldNode) { +#ifndef MAPITEMS_USE_SHAPES m_node->update(m_poly.m_line.color(), &m_geometry); +#endif m_geometry.setPreserveGeometry(false); m_geometry.markClean(); m_poly.m_dirtyMaterial = false; } +#ifdef MAPITEMS_USE_SHAPES + return nullptr; +#else return m_node; +#endif } + bool QDeclarativePolylineMapItemPrivateCPU::contains(const QPointF &point) const { + // With Shapes, do not just call + // m_shape->contains(m_poly.mapToItem(m_shape, point)) because that can + // only do FillContains at best, whereas the polyline relies on stroking. return m_geometry.contains(point); } @@ -1085,6 +1179,8 @@ void QDeclarativePolylineMapItem::setGeoShape(const QGeoShape &shape) ////////////////////////////////////////////////////////////////////// +#ifndef MAPITEMS_USE_SHAPES + /*! \internal */ @@ -1184,4 +1280,6 @@ void MapPolylineNode::update(const QColor &fillColor, } } +#endif + QT_END_NAMESPACE diff --git a/src/location/quickmapitems/qdeclarativepolylinemapitem_p_p.h b/src/location/quickmapitems/qdeclarativepolylinemapitem_p_p.h index 1155a77f..9e8992ad 100644 --- a/src/location/quickmapitems/qdeclarativepolylinemapitem_p_p.h +++ b/src/location/quickmapitems/qdeclarativepolylinemapitem_p_p.h @@ -28,7 +28,8 @@ QT_BEGIN_NAMESPACE -class QSGMaterialShader; +class QQuickShape; +class QQuickShapePath; class Q_LOCATION_PRIVATE_EXPORT QGeoMapPolylineGeometry : public QGeoMapItemGeometry { @@ -55,9 +56,13 @@ public: const QList > &clippedPaths, const QDoubleVector2D &leftBoundWrapped); + QPainterPath srcPath() const { return srcPath_; } + public: QList srcPoints_; QList srcPointTypes_; + QPainterPath srcPath_; + qreal maxCoord_ = 0.0; #ifdef QT_LOCATION_DEBUG QList m_wrappedPath; @@ -69,6 +74,7 @@ public: friend class QDeclarativeRectangleMapItem; }; +#ifndef MAPITEMS_USE_SHAPES class Q_LOCATION_PRIVATE_EXPORT VisibleNode { public: @@ -103,6 +109,7 @@ protected: QSGFlatColorMaterial fill_material_; QSGGeometry geometry_; }; +#endif class Q_LOCATION_PRIVATE_EXPORT QDeclarativePolylineMapItemPrivate { @@ -131,12 +138,9 @@ public: class Q_LOCATION_PRIVATE_EXPORT QDeclarativePolylineMapItemPrivateCPU: public QDeclarativePolylineMapItemPrivate { public: - QDeclarativePolylineMapItemPrivateCPU(QDeclarativePolylineMapItem &poly) - : QDeclarativePolylineMapItemPrivate(poly) - { - } - + QDeclarativePolylineMapItemPrivateCPU(QDeclarativePolylineMapItem &poly); ~QDeclarativePolylineMapItemPrivateCPU() override; + void onLinePropertiesChanged() override { // mark dirty just in case we're a width change @@ -186,7 +190,13 @@ public: QList m_geopathProjected; QGeoMapPolylineGeometry m_geometry; +#ifdef MAPITEMS_USE_SHAPES + QQuickShape *m_shape = nullptr; + QQuickShapePath *m_shapePath = nullptr; + QDeclarativeGeoMapPainterPath *m_painterPath = nullptr; +#else MapPolylineNode *m_node = nullptr; +#endif }; QT_END_NAMESPACE diff --git a/src/location/quickmapitems/qdeclarativerectanglemapitem.cpp b/src/location/quickmapitems/qdeclarativerectanglemapitem.cpp index b7f3cdda..eae60410 100644 --- a/src/location/quickmapitems/qdeclarativerectanglemapitem.cpp +++ b/src/location/quickmapitems/qdeclarativerectanglemapitem.cpp @@ -312,22 +312,56 @@ void QDeclarativeRectangleMapItem::geometryChange(const QRectF &newGeometry, con // call to this function. } -QDeclarativeRectangleMapItemPrivate::~QDeclarativeRectangleMapItemPrivate() {} +QDeclarativeRectangleMapItemPrivate::QDeclarativeRectangleMapItemPrivate(QDeclarativeRectangleMapItem &rect) + : m_rect(rect) +{ +} + +QDeclarativeRectangleMapItemPrivate::~QDeclarativeRectangleMapItemPrivate() +{ +} + +QDeclarativeRectangleMapItemPrivateCPU::QDeclarativeRectangleMapItemPrivateCPU(QDeclarativeRectangleMapItem &rect) + : QDeclarativeRectangleMapItemPrivate(rect) +{ +#ifdef MAPITEMS_USE_SHAPES + m_shape = new QQuickShape(&m_rect); + m_shape->setObjectName("_qt_map_item_shape"); + m_shape->setZ(-1); + m_shape->setContainsMode(QQuickShape::FillContains); + + m_shapePath = new QQuickShapePath(m_shape); + m_painterPath = new QDeclarativeGeoMapPainterPath(m_shapePath); -QDeclarativeRectangleMapItemPrivateCPU::~QDeclarativeRectangleMapItemPrivateCPU() {} + auto pathElements = m_shapePath->pathElements(); + pathElements.append(&pathElements, m_painterPath); + + auto shapePaths = m_shape->data(); + shapePaths.append(&shapePaths, m_shapePath); +#endif +} + +QDeclarativeRectangleMapItemPrivateCPU::~QDeclarativeRectangleMapItemPrivateCPU() +{ +#ifdef MAPITEMS_USE_SHAPES + delete m_shape; +#endif +} void QDeclarativeRectangleMapItemPrivateCPU::updatePolish() { if (!m_rect.topLeft().isValid() || !m_rect.bottomRight().isValid()) { m_geometry.clear(); - m_borderGeometry.clear(); m_rect.setWidth(0); m_rect.setHeight(0); +#ifdef MAPITEMS_USE_SHAPES + m_shape->setVisible(false); +#else + m_borderGeometry.clear(); +#endif return; } - const QGeoProjectionWebMercator &p = static_cast(m_rect.map()->geoProjection()); - QScopedValueRollback rollback(m_rect.m_updatingGeometry); m_rect.m_updatingGeometry = true; @@ -335,12 +369,15 @@ void QDeclarativeRectangleMapItemPrivateCPU::updatePolish() const QList pathMercator_ = QGeoMapItemGeometry::pathMercator(perimeter); m_geometry.setPreserveGeometry(true, m_rect.m_rectangle.topLeft()); m_geometry.updateSourcePoints(*m_rect.map(), pathMercator_); + +#ifndef MAPITEMS_USE_SHAPES m_geometry.updateScreenPoints(*m_rect.map(), m_rect.m_border.width()); + const QGeoProjectionWebMercator &p = static_cast(m_rect.map()->geoProjection()); QList geoms; geoms << &m_geometry; - m_borderGeometry.clear(); + m_borderGeometry.clear(); if (m_rect.m_border.color().alpha() != 0 && m_rect.m_border.width() > 0) { QList closedPath = pathMercator_; closedPath << closedPath.first(); @@ -357,24 +394,55 @@ void QDeclarativeRectangleMapItemPrivateCPU::updatePolish() borderLeftBoundWrapped = p.geoToWrappedMapProjection(geometryOrigin); m_borderGeometry.pathToScreen(*m_rect.map(), clippedPaths, borderLeftBoundWrapped); m_borderGeometry.updateScreenPoints(*m_rect.map(), m_rect.m_border.width()); - geoms << &m_borderGeometry; } else { m_borderGeometry.clear(); } } - - QRectF combined = QGeoMapItemGeometry::translateToCommonOrigin(geoms); +#endif + +#ifdef MAPITEMS_USE_SHAPES + m_rect.setShapeTriangulationScale(m_shape, m_geometry.maxCoord()); + + const bool hasBorder = m_rect.m_border.color().alpha() != 0 && m_rect.m_border.width() > 0; + m_shapePath->setStrokeColor(hasBorder ? m_rect.m_border.color() : Qt::transparent); + const float borderWidth = hasBorder ? m_rect.m_border.width() : 0.0f; + m_shapePath->setStrokeWidth(hasBorder ? borderWidth : -1.0f); + m_shapePath->setFillColor(m_rect.color()); + + const QRectF bb = m_geometry.sourceBoundingBox(); + QPainterPath path = m_geometry.srcPath(); + path.translate(-bb.left() + borderWidth, -bb.top() + borderWidth); + path.closeSubpath(); + m_painterPath->setPath(path); + + m_rect.setSize(bb.size() + QSize(2 * borderWidth, 2 * borderWidth)); + m_shape->setSize(m_rect.size()); + m_shape->setOpacity(m_rect.zoomLevelOpacity()); + m_shape->setVisible(true); + + m_rect.setPositionOnMap(m_geometry.origin(), -1 * bb.topLeft() + QPointF(borderWidth, borderWidth)); +#else + const QRectF combined = QGeoMapItemGeometry::translateToCommonOrigin(geoms); m_rect.setWidth(combined.width() + 2 * m_rect.m_border.width()); // ToDo: fix this! 2 is incorrect m_rect.setHeight(combined.height() + 2 * m_rect.m_border.width()); - m_rect.setPositionOnMap(m_geometry.origin(), m_geometry.firstPointOffset()); +#endif } QSGNode *QDeclarativeRectangleMapItemPrivateCPU::updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) { Q_UNUSED(data); +#ifdef MAPITEMS_USE_SHAPES + delete oldNode; + if (m_geometry.isScreenDirty() || m_rect.m_dirtyMaterial) { + m_geometry.setPreserveGeometry(false); + m_geometry.markClean(); + m_rect.m_dirtyMaterial = false; + } + return nullptr; +#else if (!m_node || !oldNode) { m_node = new MapPolygonNode(); if (oldNode) { @@ -394,12 +462,18 @@ QSGNode *QDeclarativeRectangleMapItemPrivateCPU::updateMapItemPaintNode(QSGNode m_borderGeometry.markClean(); m_rect.m_dirtyMaterial = false; } + return m_node; +#endif } bool QDeclarativeRectangleMapItemPrivateCPU::contains(const QPointF &point) const { +#ifdef MAPITEMS_USE_SHAPES + return m_shape->contains(m_rect.mapToItem(m_shape, point)); +#else return (m_geometry.contains(point) || m_borderGeometry.contains(point)); +#endif } QT_END_NAMESPACE diff --git a/src/location/quickmapitems/qdeclarativerectanglemapitem_p_p.h b/src/location/quickmapitems/qdeclarativerectanglemapitem_p_p.h index bf299d89..e7007a80 100644 --- a/src/location/quickmapitems/qdeclarativerectanglemapitem_p_p.h +++ b/src/location/quickmapitems/qdeclarativerectanglemapitem_p_p.h @@ -23,14 +23,14 @@ QT_BEGIN_NAMESPACE +class QQuickShape; +class QQuickShapePath; + class Q_LOCATION_PRIVATE_EXPORT QDeclarativeRectangleMapItemPrivate { Q_DISABLE_COPY_MOVE(QDeclarativeRectangleMapItemPrivate) public: - QDeclarativeRectangleMapItemPrivate(QDeclarativeRectangleMapItem &rect) - : m_rect(rect) - { - } + QDeclarativeRectangleMapItemPrivate(QDeclarativeRectangleMapItem &rect); virtual ~QDeclarativeRectangleMapItemPrivate(); virtual void onLinePropertiesChanged() = 0; @@ -49,11 +49,7 @@ public: class Q_LOCATION_PRIVATE_EXPORT QDeclarativeRectangleMapItemPrivateCPU: public QDeclarativeRectangleMapItemPrivate { public: - QDeclarativeRectangleMapItemPrivateCPU(QDeclarativeRectangleMapItem &rect) - : QDeclarativeRectangleMapItemPrivate(rect) - { - } - + QDeclarativeRectangleMapItemPrivateCPU(QDeclarativeRectangleMapItem &rect); ~QDeclarativeRectangleMapItemPrivateCPU() override; void onLinePropertiesChanged() override @@ -64,7 +60,9 @@ public: void markSourceDirtyAndUpdate() override { m_geometry.markSourceDirty(); +#ifndef MAPITEMS_USE_SHAPES m_borderGeometry.markSourceDirty(); +#endif m_rect.polishAndUpdate(); } void onMapSet() override @@ -78,13 +76,17 @@ public: void onItemGeometryChanged() override { m_geometry.setPreserveGeometry(true, m_rect.m_rectangle.topLeft()); +#ifndef MAPITEMS_USE_SHAPES m_borderGeometry.setPreserveGeometry(true, m_rect.m_rectangle.topLeft()); +#endif markSourceDirtyAndUpdate(); } void afterViewportChanged() override { m_geometry.setPreserveGeometry(true, m_rect.m_rectangle.topLeft()); +#ifndef MAPITEMS_USE_SHAPES m_borderGeometry.setPreserveGeometry(true, m_rect.m_rectangle.topLeft()); +#endif markSourceDirtyAndUpdate(); } void updatePolish() override; @@ -92,8 +94,14 @@ public: bool contains(const QPointF &point) const override; QGeoMapPolygonGeometry m_geometry; +#ifdef MAPITEMS_USE_SHAPES + QQuickShape *m_shape = nullptr; + QQuickShapePath *m_shapePath = nullptr; + QDeclarativeGeoMapPainterPath *m_painterPath = nullptr; +#else QGeoMapPolylineGeometry m_borderGeometry; MapPolygonNode *m_node = nullptr; +#endif }; QT_END_NAMESPACE diff --git a/src/location/quickmapitems/qgeomapitemgeometry.cpp b/src/location/quickmapitems/qgeomapitemgeometry.cpp index 0a5fcadb..dda09601 100644 --- a/src/location/quickmapitems/qgeomapitemgeometry.cpp +++ b/src/location/quickmapitems/qgeomapitemgeometry.cpp @@ -21,6 +21,7 @@ QGeoMapItemGeometry::~QGeoMapItemGeometry() } +#ifndef MAPITEMS_USE_SHAPES /*! \internal */ @@ -87,5 +88,6 @@ QRectF QGeoMapItemGeometry::translateToCommonOrigin(const QList +#include #include #include #include @@ -86,7 +87,6 @@ public: inline void clearBounds() { sourceBounds_ = screenBounds_ = QRectF(); firstPointOffset_ = QPointF(); } inline QPointF firstPointOffset() const { return firstPointOffset_; } - void translate(const QPointF &offset); inline const QGeoCoordinate &origin() const { return srcOrigin_; } @@ -119,9 +119,11 @@ public: inline void clear() { firstPointOffset_ = QPointF(0,0); screenVertices_.clear(); screenIndices_.clear(); } +#ifndef MAPITEMS_USE_SHAPES + void translate(const QPointF &offset); void allocateAndFill(QSGGeometry *geom) const; - static QRectF translateToCommonOrigin(const QList &geoms); +#endif mutable bool m_dataChanged = false; -- cgit v1.2.1