Search code examples
qmlqtquick2blur

Adding blur effect on a part of image in QML


I am trying to add some blur effect on an image in my QtQucick app. Following is a simple snippet that can load an image, and put on it a custom polygon. What should I add to my code to make inside of the polygon looks blurry!

Thanks in advance.

import QtQuick 2.15
import QtQuick.Window 2.15

Window
{
    id: main_window

    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    property var mpoints: [Qt.point(20, 20), Qt.point(20, 300), Qt.point(300, 300), Qt.point(300, 20)]

    Image
    {
        id: m_image

        anchors.fill: parent

        source: "file:///C:/Users/LeXela/Pictures/test.jpg"
        cache: false
    }

    Canvas
    {
        anchors.fill: parent

        onPaint:
        {
            var ctx = getContext("2d");
            ctx.clearRect(0, 0, main_window.width, main_window.height);

            ctx.globalCompositeOperation = "source-over";

            ctx.fillStyle = "white";
            ctx.globalAlpha = 0.6;

            ctx.beginPath();
            ctx.moveTo(mpoints[0].x, mpoints[0].y)
            for(let i = 1; i < mpoints.length; i++)
                ctx.lineTo(mpoints[i].x, mpoints[i].y);
            ctx.closePath();

            ctx.fill();
            ctx.stroke();
        }
    }
}

Solution

  • You can render the Image twice with the second instance (1) sampled at a lower resolution by setting sourceSize, (2) then, you can render a clipped version of the lower resolution image by defining a polygon using ShapePath and using that with OpacityMask:

    import QtQuick
    import QtQuick.Controls
    import QtQuick.Shapes
    import Qt5Compat.GraphicalEffects
    Page {
        property list<point> mpoints: [Qt.point(100, 150), Qt.point(300, 150), Qt.point(200, 250)]
    
        Image
        {
            id: img
            width: 400
            height: 350
            source: "butterfly.png"
        }
        
        Image {
            id: blur
            width: img.width
            height: img.height
            source: img.source
            sourceSize: Qt.size(32, 32)
            visible: false
        }
    
        Item {
            id: mask
            width: img.width
            height: img.height
            Shape {
                id: polygon
                ShapePath {
                    fillColor: "red"
                    strokeColor: "red"
                    startX: mpoints[0].x; startY: mpoints[0].y
                    PathLine { x: mpoints[1].x; y: mpoints[1].y }
                    PathLine { x: mpoints[2].x; y: mpoints[2].y }
                }
            }
            visible: false
        }
        
        OpacityMask {
            anchors.fill: mask
            source: blur
            maskSource: mask
        }
    }
    
    // butterfly.png : https://raw.githubusercontent.com/stephenquan/stephenquan.github.io/master/images/qt/Original_butterfly.png
    

    You can Try it Online!

    Here's a version where the polygon vertices can move:

    import QtQuick
    import QtQuick.Controls
    import QtQuick.Layouts
    import QtQuick.Shapes
    import Qt5Compat.GraphicalEffects
    
    Page {
        property list<point> mpoints: [Qt.point(100, 150), Qt.point(200, 50), Qt.point(300, 150), Qt.point(200, 250)]
        property int msize: 20
        
        Image
        {
            id: img
            width: 400
            height: 350
            source: "butterfly.png"
        }
        
        Item {
            id: blur
            anchors.fill: parent
            Image {
                width: img.width
                height: img.height
                source: img.source
                sourceSize: Qt.size(img.width / blurSlider.value, img.height / blurSlider.value)
            }
            visible: false
        }
        
        Item {
            id: polygon
            anchors.fill: parent
            visible: false
            Repeater {
                model: mpoints.length - 2
                Shape {
                    property point firstPoint: mpoints[index]
                    property point secondPoint: mpoints[index+1]
                    property point lastPoint: mpoints[mpoints.length - 1]
                    ShapePath {
                        strokeColor: "orange"
                        fillColor: "orange"
                        startX: firstPoint.x; startY: firstPoint.y
                        PathLine { x: secondPoint.x; y: secondPoint.y }
                        PathLine { x: lastPoint.x; y: lastPoint.y }
                    }
                }
            }
        }
        
        Repeater {
            model: mpoints.length
            Shape {
                property point thisPoint: mpoints[index]
                property point nextPoint: mpoints[(index + 1) % mpoints.length]
                ShapePath {
                    strokeColor: "blue"
                    strokeWidth: 2
                    startX: thisPoint.x; startY: thisPoint.y
                    PathLine { x: nextPoint.x; y: nextPoint.y }
                }
            }
        }
        
        OpacityMask {
            anchors.fill: parent
            source: blur
            maskSource: polygon
            invert: invertCheckBox.checked
        }
        
        Repeater {
            model: mpoints
            Rectangle {
                x: modelData.x -msize / 2
                y: modelData.y -msize / 2
                width: msize
                height: msize
                color: "transparent"
                border.color: "red"
                border.width: 2
                DragHandler {
                    onGrabChanged: {
                        switch (transition) {
                            case 1:
                            break;
                            case 2:
                            Qt.callLater(movePoint, index, parent.x + msize / 2, parent.y + msize / 2);
                            parent.x = -msize / 2;
                            parent.y = -msize / 2;
                            break;
                        }
                    }
                }
            }
        }
        
        footer: Frame {
            RowLayout {
                CheckBox { id: invertCheckBox; text: "Invert" }
                Slider { id: blurSlider; from: 1; to: 10; value: 10 }
                Label { text: "Blur %1".arg(blurSlider.value.toFixed(1)) }
            }
        }
        
        function movePoint(index, newx, newy) {
            mpoints[index] = Qt.point(newx, newy);
        }    
    }
    
    // butterfly.png : https://raw.githubusercontent.com/stephenquan/stephenquan.github.io/master/images/qt/Original_butterfly.png
    

    You can Try it Online!