qml

Unable to create QML 6 Resizable Image with correct Rounded Corners


I am trying to create an image with a rounded corner for a KDE Plasma Widget I am working on, but the dimensions of the mask don't match with my rounded rectangle. According to the resources I found, I need to use MultiEffects. Sure enough it works but I need the image to also 1) be resizable in the widget and 2) keep the rounded corners even if the fill setting for Image changes.

I tried the following:

Image {
    id: sourceItem
    fillMode: Image.PreserveAspectFit
    source: "/home/tjaart/Pictures/Wallpaper/04191_heartlake_1920x1080.jpg"
    anchors.fill: parent
    visible: false
    layer.enabled: true
}

MultiEffect {
    source: sourceItem
    anchors.fill: sourceItem
    maskEnabled: true
    maskSource: mask
}

Item {
    id: mask
    width: sourceItem.paintedWidth
    height: sourceItem.paintedHeight
    layer.enabled: true
    visible: true
    anchors.centerIn: sourceItem
    Rectangle {
        width: sourceItem.paintedWidth
        height: sourceItem.paintedHeight
        radius: 90
        color: "green"
        opacity: 0.5
    }
}

Image with mask

I tinted the rectangle I used to create the mask in the example image (also in the source). The rounded corner is clipped according the to the size of the Image element and not the real size of the image on screen. That is why it is clipping the image along dimensions that don't match with the rectangle in green, which is what I want to achieve.


Solution

  • The problem is compounded by the fact that:

    1. You're using fillMode: Image.PreserveAspectFit
    2. The mask dimensions are different than your source image dimensions

    A simple solution is to correct your Mask dimensions to match your source Image but adjust the Rectangle to just the dimensions of the aspected fitted image, i.e.

        Item {
            id: mask
            anchors.fill: sourceItem
            layer.enabled: true
            visible: false
            Rectangle {
                width: sourceItem.paintedWidth
                height: sourceItem.paintedHeight
                anchors.centerIn: parent
                radius: 90
                color: "green"
                opacity: 0.5
            }
        }
    

    However, the sourceItem, the MultiEffect, and the Mask has a lot of wasted space since the preseve aspect fit will mean there could be a large letter box effect which is included in all images.

    Instead, you can change how you compute the sourceItem width x height to take into account that you know the aspect ratio of the source image. This will optimize the sizes of the Image, MultiEffect and the mask, i.e.

        Image {
            id: sourceItem
            // manual implementation of 1920:1080 preserve aspect fit ...
            width: parent.height * 1920 > parent.width * 1080 ? parent.width : parent.height * 1920 / 1080
            height: width * 1080 / 1920
            anchors.fill: parent
            source: "https://w0.peakpx.com/wallpaper/252/964/HD-wallpaper-heartlake-sunshine-lake-mountains-landscape.jpg"
            visible: false
            layer.enabled: true
        }
    
        MultiEffect {
            source: sourceItem
            anchors.fill: sourceItem
            anchors.centerIn: parent
            maskEnabled: true
            maskSource: mask
        }
    
        Item {
            id: mask
            anchors.fill: sourceItem
            layer.enabled: true
            visible: false
            Rectangle {
                anchors.fill: parent
                radius: 90
                color: "green"
                opacity: 0.5
            }
        }