Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

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.
    circle clip.png

    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 :)

    Capture.JPG



  • It doesn't Clip green rectangle. It just mask that and if my CircleClip doesn't have a brown rectangle the green rectangle Can be seen.
    Basically clipping term used when item's are inside clipper item. and I need children inside CircleClip to be clipped.



  • @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 .


Log in to reply