Clip QML Types with Context2D



  • The documentations talks about clipping images with a path drew with Contex2D:

    http://doc.qt.io/qt-5/qml-qtquick-context2d.html#clip-method

    is it possible to do the same but clipping any QML Type?
    I mean, instead of the Image, I want to put any object (Text, Rectangle, ListView, etc....) but only the area inside the path should be visible.



  • @Mark81 Hi! There is OpacityMask QML. Although it doesn't take a path but an image as source for the mask.



  • @Wieland Yes, I already use OpacityMask with (i.e.) a Rectangle with radius. It would be nice to use a generic path. Perhaps a workaround is to programmatically create an image with the desired path and use it as maskSource.



  • Out of curiosity, I implemented a QQuickPaintedItem-based PathMask type. The result looks like this:

    PathMask

    The code is:
    pathmask.h

    #ifndef PATHMASK_H
    #define PATHMASK_H
    
    #include <QObject>
    #include <QQuickItem>
    #include <QQuickPaintedItem>
    #include <QPainter>
    #include <QPainterPath>
    
    class PathMask : public QQuickPaintedItem
    {
        Q_OBJECT
    public:
        explicit PathMask(QQuickItem *parent = Q_NULLPTR);
        ~PathMask();
        virtual void paint(QPainter *painter);
    
        Q_INVOKABLE void beginPath();
        Q_INVOKABLE void moveTo(qreal x, qreal y);
        Q_INVOKABLE void lineTo(qreal x, qreal y);
        Q_INVOKABLE void closePath();
    
    private:
        QPainterPath *m_path{Q_NULLPTR};
    };
    
    #endif // PATHMASK_H
    

    pathmask.cpp

    #include "pathmask.h"
    
    #include <QPainterPath>
    #include <QtMath>
    
    PathMask::PathMask(QQuickItem *parent) :
        QQuickPaintedItem(parent)
    {
    }
    
    PathMask::~PathMask()
    {
        delete m_path;
    }
    
    void PathMask::paint(QPainter *painter)
    {
        if (m_path) {
            painter->setRenderHint(QPainter::Antialiasing);
            painter->setBrush( QBrush(QColor("black")) );
            painter->drawPath( *m_path );
        }
    }
    
    void PathMask::beginPath()
    {
        delete m_path;
        m_path = new QPainterPath;
        update();
    }
    
    void PathMask::moveTo(qreal x, qreal y)
    {
        Q_ASSERT(m_path);
        m_path->moveTo(x,y);
    }
    
    void PathMask::lineTo(qreal x, qreal y)
    {
        Q_ASSERT(m_path);
        m_path->lineTo(x, y);
    }
    
    void PathMask::closePath()
    {
        Q_ASSERT(m_path);
        m_path->closeSubpath();
        update();
    }
    

    main.qml

    import QtQuick 2.5
    import QtQuick.Controls 2.0
    import QtQuick.Controls.Material 2.0
    import QtGraphicalEffects 1.0
    
    import io.qt.forum 1.0
    
    ApplicationWindow {
        title: "PathMask Demo"
        visible: true
        width: 400
        height: 400
        color: "black"
    
        Image {
            id: src
            anchors.fill: parent
            visible: false
            source: "file:///home/patrick/Downloads/f22.jpg"
        }
    
        PathMask {
            id: mask
            anchors.fill: parent
            visible: false
        }
    
        Component.onCompleted: {
            mask.beginPath()
            mask.moveTo(360, 200);
            var Pi = 3.141592653589793238463;
            for (var i = 1; i < 5; ++i) {
                mask.lineTo(200 + 160 * Math.cos(0.8 * i * Pi),
                            200 + 160 * Math.sin(0.8 * i * Pi));
            }
            mask.closePath();
        }
    
        OpacityMask {
            anchors.fill: src
            source: src
            maskSource: mask
        }
    
    }