From 8f205040dcf882f5c0e0f8d301291d6fad198527 Mon Sep 17 00:00:00 2001 From: James Turner Date: Sun, 17 Jun 2018 16:54:09 +0100 Subject: [PATCH] Work on clipping in remote canvas Different canvas clip reference frames are handled now, and updating the clip nodes should not longer crash. Unfortunately, clips set on groups don't work yet, further work is needed here. --- utils/fgqcanvas/canvasitem.cpp | 150 +++++++++++++++++++++++----- utils/fgqcanvas/canvasitem.h | 16 ++- utils/fgqcanvas/fgcanvaselement.cpp | 19 +++- utils/fgqcanvas/fgcanvaselement.h | 2 + utils/fgqcanvas/fgcanvaspath.cpp | 9 +- utils/fgqcanvas/fgcanvastext.cpp | 8 +- utils/fgqcanvas/fgqcanvasimage.cpp | 9 +- 7 files changed, 156 insertions(+), 57 deletions(-) diff --git a/utils/fgqcanvas/canvasitem.cpp b/utils/fgqcanvas/canvasitem.cpp index 4b68d765f..bf035f39f 100644 --- a/utils/fgqcanvas/canvasitem.cpp +++ b/utils/fgqcanvas/canvasitem.cpp @@ -51,52 +51,148 @@ void CanvasItem::setTransform(const QMatrix4x4 &mat) m_localTransform->setTransform(mat); } -void CanvasItem::setGlobalClip(const QRectF &clip) +void CanvasItem::setClip(const QRectF &clip, ReferenceFrame rf) { + if (m_hasClip && (clip == m_clipRect) && (rf == m_clipReferenceFrame)) { + return; + } + m_hasClip = true; - m_globalClipRect = clip; + m_clipRect = clip; + m_clipReferenceFrame = rf; update(); } +void CanvasItem::setClipReferenceFrameItem(QQuickItem *refItem) +{ + m_clipReferenceFrameItem = refItem; +} + void CanvasItem::clearClip() { m_hasClip = false; update(); } -QSGNode *CanvasItem::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *) +QSGNode *CanvasItem::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *d) { - if (m_hasClip && oldNode && (oldNode->type() == QSGNode::BasicNodeType)) { - delete oldNode; + QSGNode* realOldNode = oldNode; + QSGClipNode* oldClip = nullptr; + + if (oldNode && (oldNode->type() == QSGNode::ClipNodeType)) { + Q_ASSERT(oldNode->childCount() == 1); + realOldNode = oldNode->childAtIndex(0); + oldClip = static_cast(oldNode); } - if (!m_hasClip && m_clipNode) { - oldNode = new QSGNode; - } - - updateClipNode(); - return m_hasClip ? m_clipNode : oldNode; -} - -QSGClipNode *CanvasItem::updateClipNode() -{ - if (!m_hasClip) { - if (m_clipNode) { - delete m_clipNode; - m_clipNode = nullptr; - } + QSGNode* contentNode = updateRealPaintNode(realOldNode, d); + if (!contentNode) { return nullptr; } - if (!m_clipNode) { - m_clipNode = new QSGClipNode(); + QSGNode* clipNode = updateClipNode(oldClip, contentNode); + return clipNode ? clipNode : contentNode; +} + +QSGNode *CanvasItem::updateRealPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *d) +{ + if (oldNode) { + return oldNode; } - // transform global rect to local - QRectF localRect(mapFromGlobal(m_globalClipRect.topLeft()), - mapFromGlobal(m_globalClipRect.bottomRight())); - m_clipNode->setClipRect(localRect); - return m_clipNode; + return new QSGNode(); +} + +QRectF checkRectangularClip(QPointF* vertices) +{ + // order is TL / BL / TR / BR to match updateRectGeometry + const double top = vertices[0].y(); + const double left = vertices[0].x(); + const double bottom = vertices[1].y(); + const double right = vertices[2].x(); + + if (vertices[1].x() != left) return {}; + if (vertices[2].y() != top) return {}; + if ((vertices[3].x() != right) || (vertices[3].y() != bottom)) + return {}; + + return QRectF(vertices[0], vertices[3]); +} + +QSGClipNode* CanvasItem::updateClipNode(QSGClipNode* oldClipNode, QSGNode* contentNode) +{ + Q_ASSERT(contentNode); + if (!m_hasClip) { + return nullptr; + } + + QSGGeometry* clipGeometry = nullptr; + QSGClipNode* clipNode = oldClipNode; + + if (!clipNode) { + clipNode = new QSGClipNode(); + clipGeometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 4); + clipGeometry->setDrawingMode(GL_TRIANGLE_STRIP); + clipNode->setGeometry(clipGeometry); + clipNode->setFlag(QSGNode::OwnsGeometry); + clipNode->appendChildNode(contentNode); + } else { + if (clipNode->childCount() == 1) { + const auto existingChild = clipNode->childAtIndex(0); + if (existingChild == contentNode) { + qInfo() << "optimise for this case!"; + } + } + + clipNode->removeAllChildNodes(); + clipNode->appendChildNode(contentNode); + clipGeometry = clipNode->geometry(); + Q_ASSERT(clipGeometry); + } + + QPointF clipVertices[4], + inVertices[4] = {m_clipRect.topLeft(), m_clipRect.bottomLeft(), + m_clipRect.topRight(), m_clipRect.bottomRight()}; + QRectF rectClip; + + switch (m_clipReferenceFrame) { + case ReferenceFrame::GLOBAL: + case ReferenceFrame::PARENT: + Q_ASSERT(m_clipReferenceFrameItem); + for (int i=0; i<4; ++i) { + clipVertices[i] = mapFromItem(m_clipReferenceFrameItem, inVertices[i]); + } + rectClip = checkRectangularClip(clipVertices); + break; + + case ReferenceFrame::LOCAL: + // local ref-frame clip is always rectangular + rectClip = m_clipRect; + for (int i=0; i<4; ++i) { + clipVertices[i] = inVertices[i]; + } + break; + } + + clipNode->setIsRectangular(!rectClip.isNull()); + qInfo() << "\nobj:" << objectName(); + if (!rectClip.isNull()) { + qInfo() << "have rectangular clip for:" << m_clipRect << (int) m_clipReferenceFrame << rectClip; + clipNode->setClipRect(rectClip); + } else { + qInfo() << "haved rotated clip" << m_clipRect << (int) m_clipReferenceFrame; + qInfo() << "final local clip points:" << clipVertices[0] << clipVertices[1] + << clipVertices[2] << clipVertices[3]; + } + + QSGGeometry::Point2D *v = clipGeometry->vertexDataAsPoint2D(); + for (int i=0; i<4; ++i) { + v[i].x = clipVertices[i].x(); + v[i].y = clipVertices[i].y(); + } + clipGeometry->markVertexDataDirty(); + clipNode->markDirty(QSGNode::DirtyGeometry); + return clipNode; } #include "canvasitem.moc" diff --git a/utils/fgqcanvas/canvasitem.h b/utils/fgqcanvas/canvasitem.h index 8071c9df5..1ce091d14 100644 --- a/utils/fgqcanvas/canvasitem.h +++ b/utils/fgqcanvas/canvasitem.h @@ -19,6 +19,7 @@ #define CANVASITEM_H #include +#include "fgcanvaselement.h" class LocalTransform; class QSGClipNode; @@ -31,23 +32,28 @@ public: void setTransform(const QMatrix4x4& mat); - void setGlobalClip(const QRectF &clip); + void setClip(const QRectF &clip, ReferenceFrame rf); + + void setClipReferenceFrameItem(QQuickItem* refItem); void clearClip(); - QSGNode* updatePaintNode(QSGNode *, UpdatePaintNodeData *) override; + QSGNode* updatePaintNode(QSGNode *, UpdatePaintNodeData *) override final; signals: public slots: protected: - QSGClipNode* updateClipNode(); + virtual QSGNode *updateRealPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *d); private: + QSGClipNode* updateClipNode(QSGClipNode* oldClipNode, QSGNode* contentNode); + LocalTransform* m_localTransform; - QRectF m_globalClipRect; + QRectF m_clipRect; bool m_hasClip = false; - QSGClipNode* m_clipNode = nullptr; + ReferenceFrame m_clipReferenceFrame = ReferenceFrame::GLOBAL; + QQuickItem* m_clipReferenceFrameItem = nullptr; }; #endif // CANVASITEM_H diff --git a/utils/fgqcanvas/fgcanvaselement.cpp b/utils/fgqcanvas/fgcanvaselement.cpp index 9f4af0d13..333962724 100644 --- a/utils/fgqcanvas/fgcanvaselement.cpp +++ b/utils/fgqcanvas/fgcanvaselement.cpp @@ -127,9 +127,15 @@ void FGCanvasElement::polish() _clipDirty = false; if (qq) { if (_hasClip) { - // qq->setGlobalClip(_clipRect); + if (_clipFrame == ReferenceFrame::GLOBAL) { + qq->setClipReferenceFrameItem(rootGroup()->quickItem()); + } else if (_clipFrame == ReferenceFrame::PARENT) { + qq->setClipReferenceFrameItem(parentGroup()->quickItem()); + } + qq->setObjectName(_propertyRoot->path()); + qq->setClip(_clipRect, _clipFrame); } else { - // qq->clearClip(); + qq->clearClip(); } } } @@ -244,6 +250,15 @@ const FGCanvasGroup *FGCanvasElement::parentGroup() const return _parent; } +const FGCanvasGroup *FGCanvasElement::rootGroup() const +{ + if (!_parent) { + return qobject_cast(this); + } + + return _parent->rootGroup(); +} + CanvasConnection *FGCanvasElement::connection() const { if (_parent) diff --git a/utils/fgqcanvas/fgcanvaselement.h b/utils/fgqcanvas/fgcanvaselement.h index 8775cdcb7..963c5dafb 100644 --- a/utils/fgqcanvas/fgcanvaselement.h +++ b/utils/fgqcanvas/fgcanvaselement.h @@ -59,6 +59,8 @@ public: const FGCanvasGroup* parentGroup() const; + const FGCanvasGroup* rootGroup() const; + CanvasConnection* connection() const; static bool isStyleProperty(QByteArray name); diff --git a/utils/fgqcanvas/fgcanvaspath.cpp b/utils/fgqcanvas/fgcanvaspath.cpp index 4d4dcfd25..8f0ee82b0 100644 --- a/utils/fgqcanvas/fgcanvaspath.cpp +++ b/utils/fgqcanvas/fgcanvaspath.cpp @@ -61,7 +61,7 @@ public: update(); // request a paint node update } - virtual QSGNode* updatePaintNode(QSGNode* oldNode, QQuickItem::UpdatePaintNodeData *) + QSGNode* updateRealPaintNode(QSGNode* oldNode, QQuickItem::UpdatePaintNodeData *) override { if (m_path.isEmpty()) { return nullptr; @@ -169,13 +169,6 @@ public: strokeGeom->setFlag(QSGNode::OwnsMaterial); } - QSGClipNode* clip = updateClipNode(); - if (clip) { - if (fillGeom) clip->appendChildNode(fillGeom); - if (strokeGeom) clip->appendChildNode(strokeGeom); - return clip; - } - if (fillGeom && strokeGeom) { QSGNode* groupNode = new QSGNode; groupNode->appendChildNode(fillGeom); diff --git a/utils/fgqcanvas/fgcanvastext.cpp b/utils/fgqcanvas/fgcanvastext.cpp index 002e1e2ad..102594b98 100644 --- a/utils/fgqcanvas/fgcanvastext.cpp +++ b/utils/fgqcanvas/fgcanvastext.cpp @@ -73,7 +73,7 @@ public: update(); } - virtual QSGNode* updatePaintNode(QSGNode* oldNode, QQuickItem::UpdatePaintNodeData *data) + QSGNode* updateRealPaintNode(QSGNode* oldNode, QQuickItem::UpdatePaintNodeData *data) override { if (!m_textNode) { @@ -88,12 +88,6 @@ public: m_color, QQuickText::Normal); - QSGNode* clip = updateClipNode(); - if (clip) { - clip->appendChildNode(m_textNode); - return clip; - } - return m_textNode; } diff --git a/utils/fgqcanvas/fgqcanvasimage.cpp b/utils/fgqcanvas/fgqcanvasimage.cpp index 64d51c43e..7750af5f5 100644 --- a/utils/fgqcanvas/fgqcanvasimage.cpp +++ b/utils/fgqcanvas/fgqcanvasimage.cpp @@ -65,7 +65,7 @@ public: update(); } - virtual QSGNode* updatePaintNode(QSGNode* oldNode, QQuickItem::UpdatePaintNodeData *data) + QSGNode* updateRealPaintNode(QSGNode* oldNode, QQuickItem::UpdatePaintNodeData *data) override { if (m_pixmap.isNull()) { return nullptr; @@ -85,13 +85,6 @@ public: } m_texture = window()->createTextureFromImage(m_pixmap.toImage(), QQuickWindow::TextureCanUseAtlas); texNode->setTexture(m_texture); - - QSGNode* clip = updateClipNode(); - if (clip) { - clip->appendChildNode(texNode); - return clip; - } - return texNode; }