QML Custom Shape Clip
-
Hi
I need an Item that clip children's in custom shape like circle.
I write CircleClip in C++ and create a QSGClipNode inside it. but QSGClipNode don't clip item's that append in qml environment. for example I add a rectangle(brown) to clip node in C++ and clipping work. but when I add another rectangle(green) inside qml clipping not work.
I need clipping item's in this manner because it use OpenGl stencil method and it's performance needed.
the sceneGraph tree dump is as follows
RootNode 0x1a973b80d10 "" )
--TransformNode( 0x1a97459fb20 identity "QQuickItem(QQuickRootItem:)" )
----TransformNode( 0x1a9745a1ce0 identity "QQuickItem(QQuickItem:)" )
------TransformNode( 0x1a9745a0f60 det= -1 "" )
--------ClipNode( 0x1a973b67bd0 children= 1 is rect? no ) ""
----------GeometryNode( 0x1a9000aea50 strip #V: 4 #I: 0 x1= -235 y1= -160 x2= 236 y2= 160 materialtype= 0x7ffec389a571 ) ""
------TransformNode( 0x1a9745a1f20 translate 0 80 0 "QQuickItem(QQuickRectangle:)" )
--------GeometryNode( 0x1a97120c720 strip #V: 4 #I: 4 x1= 0 y1= 0 x2= 471 y2= 160 materialtype= 0x7ffec389afc0 ) "internalrectangle"I uploded project in Github.
https://github.com/hoss291069/qml-CircleClip.git](https://github.com/hoss291069/qml-CircleClip.git)CircleClip.cpp
CircleClip::CircleClip() { setFlag(ItemHasContents, true); if(initialized==false) { double d=2.0f/steps*M_PIF; double t=M_PIF_2; for(int i=0;i<=steps;++i,t-=d) { qsgUintCircle[i].set(cosf(t),sinf(t)); } initialized=true; } QObject::connect(this,&QQuickItem::widthChanged,this,&QQuickItem::update); QObject::connect(this,&QQuickItem::heightChanged,this,&QQuickItem::update); } QSGNode* CircleClip::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) { QSGTransformNode* rootNode; QSGClipNode * clipNode; QSGGeometry* geometry; QSGSimpleRectNode* rectNode; if (!oldNode) { rootNode= new QSGTransformNode(); clipNode= new QSGClipNode(); rectNode= new QSGSimpleRectNode(); rectNode->setColor("brown"); clipNode->setIsRectangular(false); geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 1000*3+12); clipNode->setGeometry(geometry); geometry->setDrawingMode(QSGGeometry::DrawTriangles); clipNode->setFlag(QSGNode::OwnsGeometry); rootNode->appendChildNode(clipNode); clipNode->appendChildNode(rectNode); } else { rootNode = static_cast<QSGTransformNode *>(oldNode); clipNode = static_cast<QSGClipNode *>(rootNode->firstChild()); rectNode = static_cast<QSGSimpleRectNode *>(clipNode->firstChild()); geometry = clipNode->geometry(); } QMatrix4x4 n{1,0,0,static_cast<float>(width()/2.0), 0,-1,0,static_cast<float>(height()/2.0), 0,0,1,0, 0,0,0,1 }; rootNode->setMatrix(n); rootNode->markDirty(QSGNode::DirtyMatrix); rectNode->setRect(QRect(-width()/2,-height()/2,width(),height())); rectNode->markDirty(QSGNode::DirtyGeometry); QSGGeometry::Point2D* points = geometry->vertexDataAsPoint2D(); int r_px=width()>height() ? height()/2.0 : width()/2.0; int k=0; for(int i=0;i<250;i++,k+=3) { points[k+0].set(width()/2.0,height()/2.0); points[k+1].set(qsgUintCircle[i].x *r_px , qsgUintCircle[i].y * r_px); points[k+2].set(qsgUintCircle[i+1].x *r_px , qsgUintCircle[i+1].y * r_px); } for(int i=250;i<500;i++,k+=3) { points[k+0].set(width()/2.0,-height()/2.0); points[k+1].set(qsgUintCircle[i].x *r_px , qsgUintCircle[i].y * r_px); points[k+2].set(qsgUintCircle[i+1].x *r_px , qsgUintCircle[i+1].y * r_px); } for(int i=500;i<750;i++,k+=3) { points[k+0].set(-width()/2.0,-height()/2.0); points[k+1].set(qsgUintCircle[i].x *r_px , qsgUintCircle[i].y * r_px); points[k+2].set(qsgUintCircle[i+1].x *r_px , qsgUintCircle[i+1].y * r_px); } for(int i=750;i<1000;i++,k+=3) { points[k+0].set(-width()/2.0,height()/2.0); points[k+1].set(qsgUintCircle[i].x *r_px , qsgUintCircle[i].y * r_px); points[k+2].set(qsgUintCircle[i+1].x *r_px , qsgUintCircle[i+1].y * r_px); } points[k++].set(width()/2.0,height()/2.0); points[k++].set(r_px ,0); points[k++].set(width()/2.0 ,-height()/2.0); points[k++].set(width()/2.0,-height()/2.0); points[k++].set(0 ,-r_px); points[k++].set(-width()/2.0 ,-height()/2.0); points[k++].set(-width()/2.0,height()/2.0); points[k++].set(0 ,r_px); points[k++].set(width()/2.0 ,height()/2.0); points[k++].set(-width()/2.0,height()/2.0); points[k++].set(-r_px ,0); points[k++].set(-width()/2.0 ,-height()/2.0); return rootNode; }
main.qml
import QtQuick 2.12 import QtQuick.Window 2.12 import Test 1.0 Window { visible: true width: 640 height: 480 title: qsTr("Hello World") CircleClip{ anchors.fill: parent; Rectangle { id:greenRect width:parent.width height: parent.height/2 anchors.centerIn: parent color:"green" } } }
-
I think the problem is because you are nesting your code. Instead, swap CircleClip and Rectangle around and keep inline, don't nest.
This works;
import QtQuick 2.12 import QtQuick.Window 2.12 import Test 1.0 Window { visible: true width: 640 height: 480 title: qsTr("Clipping") Rectangle { id: greenRect width: parent.width height: parent.height / 2 anchors.centerIn: parent color: "green" } CircleClip { anchors.fill: parent } }
IF the problem is not because of nesting code, I'd be happy to hear from someone :)
-
-
@Danima
basically according to tree dump the problem is that the CircleClip child is not descendant of ClipNode.
QQuickItem for each item create one TransformNode to map coordinate system. All item's children are descendant of TransformNode.
I want a way to some thing like replace or embed ClipNode with TransformNode .