QML 中的图像圆角

发布于 2024-11-08 16:41:38 字数 453 浏览 1 评论 0原文

令我惊讶的是,Image 组件没有 radius 属性。我尝试通过将图像放入圆角矩形中来模拟圆角,但它不会剪切角。

Rectangle {
    anchors.right: rectContentBg.left
    anchors.top: rectContentBg.top
    anchors.margins: 8

    radius: 8

    width: 64
    height: 64

    Image {
        id: imgAuthor

        opacity: 1
        smooth: false

        anchors.fill: parent

        source: "qrc:/res/sample_avatar.jpg"
    }
}

如何正确创建带有圆角的图像?

To my surprise, the Image component has no radius property. I tried emulating the rounded corners by putting the image in a rounded Rectangle, but it does not clip the corners.

Rectangle {
    anchors.right: rectContentBg.left
    anchors.top: rectContentBg.top
    anchors.margins: 8

    radius: 8

    width: 64
    height: 64

    Image {
        id: imgAuthor

        opacity: 1
        smooth: false

        anchors.fill: parent

        source: "qrc:/res/sample_avatar.jpg"
    }
}

How can I create an image with rounded corners properly?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

惜醉颜 2024-11-15 16:41:38

由于 QtGraphicalEffects 模块,从 Qt 5 开始就存在内置的官方解决方案,我很惊讶地发现没有人提供如此简单的解决方案。如果您的目标是 Qt 6.x,不幸的是,QtGraphicalEffects 已被弃用,因此请跳转到答案的第二部分,该部分提出了独立于 QtGraphicalEffects 的解决方案。

QtGraphicalEffects 解决方案

在其他效果中,OpacityMask 是用于此目的的类型。这个想法是用一个具有正确设置的半径矩形来掩盖源Image。 是使用 分层 的最简单示例:

Image {
    id: img
    property bool rounded: true
    property bool adapt: true

    layer.enabled: rounded
    layer.effect: OpacityMask {
        maskSource: Item {
            width: img.width
            height: img.height
            Rectangle {
                anchors.centerIn: parent
                width: img.adapt ? img.width : Math.min(img.width, img.height)
                height: img.adapt ? img.height : width
                radius: Math.min(width, height)
            }
        }
    }
}

这 最少的代码可以为方形图像产生很好的结果,但是
它还通过 adapt 变量考虑非方形图像。通过将标志设置为false,无论图像大小如何,生成的蒙版将始终是圆形。这是可能的,因为使用了外部 Item,它填充源并允许随意调整真实掩码(内部 矩形)的大小。 如果您只是瞄准填充源的遮罩,而不管其纵横比如何,您显然可以摆脱外部Item

这是一张可爱的猫图像,其格式为方形(),带有 adapt: true 的非方形格式(中心),最后是非方形格式和 adapt: false (right):

在此处输入图像描述

此解决方案的实现细节与基于着色器的答案非常相似在另一个好的答案中(参见OpacityMask的QML源代码,可以找到< a href="http://ftp.fau.de/qtproject/ministro/android/qt5/objects/5.1014-x86/qml/QtGraphicalEffects/OpacityMask.qml" rel="noreferrer">此处 - SourceProxy 只是返回一个格式良好的 ShaderEffectSource 来提供效果)。

无依赖解决方案

如果您不想 - 或不能 - 依赖于 QtGraphicalEffects 模块(好吧,依赖于 OpacityMask.qml< /code> 实际上),您可以使用着色器重新实现效果。除了已经提供的解决方案之外,另一种方法是使用 stepsmoothstepfwidth 函数。这是代码:

import QtQuick 2.5

Image {
    id: image

    property bool rounded: true
    property bool adapt: true

    layer.enabled: rounded
    layer.effect: ShaderEffect {
        property real adjustX: image.adapt ? Math.max(width / height, 1) : 1
        property real adjustY: image.adapt ? Math.max(1 / (width / height), 1) : 1

        fragmentShader: "
        #ifdef GL_ES
            precision lowp float;
        #endif // GL_ES
        varying highp vec2 qt_TexCoord0;
        uniform highp float qt_Opacity;
        uniform lowp sampler2D source;
        uniform lowp float adjustX;
        uniform lowp float adjustY;

        void main(void) {
            lowp float x, y;
            x = (qt_TexCoord0.x - 0.5) * adjustX;
            y = (qt_TexCoord0.y - 0.5) * adjustY;
            float delta = adjustX != 1.0 ? fwidth(y) / 2.0 : fwidth(x) / 2.0;
            gl_FragColor = texture2D(source, qt_TexCoord0).rgba
                * step(x * x + y * y, 0.25)
                * smoothstep((x * x + y * y) , 0.25 + delta, 0.25)
                * qt_Opacity;
        }"
    }
}

在此处输入图像描述

与第一种方法类似,添加了 roundedadapt 属性来控制效果的视觉外观,如上所述。

A built-in official solution exists as of Qt 5 thanks to the QtGraphicalEffects module and I'm quite surprised to find out that no one provided such a simple solution. If you are targeting Qt 6.x QtGraphicalEffects is unfortunately deprecated so jump to the second part of the answer which proposes a solution independent of QtGraphicalEffects.

QtGraphicalEffects solution

Among the other effects OpacityMask is the type to be exploited for this purpose. The idea is to mask the source Image with a Rectangle that has a correctly set radius. Here goes the simplest example using layering:

Image {
    id: img
    property bool rounded: true
    property bool adapt: true

    layer.enabled: rounded
    layer.effect: OpacityMask {
        maskSource: Item {
            width: img.width
            height: img.height
            Rectangle {
                anchors.centerIn: parent
                width: img.adapt ? img.width : Math.min(img.width, img.height)
                height: img.adapt ? img.height : width
                radius: Math.min(width, height)
            }
        }
    }
}

This minimum code produces a nice result for square images but
it also takes in account non-square images via the adapt variable. By setting the flag to false the produced mask will always be a circle, regardless of the image size. That is possible due to the usage of an external Item which fills the source and allows the real mask (the inner Rectangle) to be sized at please. You can obviously get rid of the external Item, if you simply aim to a mask that fills the source, regardless of the its aspect ratio.

Here is a cute cat image with a square format (left), a non-square format with adapt: true (center) and finally a non-square format and adapt: false (right):

enter image description here

The implementation details of this solution are very similar to those of the shader-based answer in the other nice answer (cfr. the QML source code for OpacityMask that can be found here - SourceProxy simply returns a well-formed ShaderEffectSource to feed the effect).

No-dep solution

If you don't want to - or can't - depend on the QtGraphicalEffects module (well, on the presence of OpacityMask.qml actually), you can reimplement the effect with shaders. Apart from the already provided solution another approach is to use step, smoothstep and fwidth functions. Here is the code:

import QtQuick 2.5

Image {
    id: image

    property bool rounded: true
    property bool adapt: true

    layer.enabled: rounded
    layer.effect: ShaderEffect {
        property real adjustX: image.adapt ? Math.max(width / height, 1) : 1
        property real adjustY: image.adapt ? Math.max(1 / (width / height), 1) : 1

        fragmentShader: "
        #ifdef GL_ES
            precision lowp float;
        #endif // GL_ES
        varying highp vec2 qt_TexCoord0;
        uniform highp float qt_Opacity;
        uniform lowp sampler2D source;
        uniform lowp float adjustX;
        uniform lowp float adjustY;

        void main(void) {
            lowp float x, y;
            x = (qt_TexCoord0.x - 0.5) * adjustX;
            y = (qt_TexCoord0.y - 0.5) * adjustY;
            float delta = adjustX != 1.0 ? fwidth(y) / 2.0 : fwidth(x) / 2.0;
            gl_FragColor = texture2D(source, qt_TexCoord0).rgba
                * step(x * x + y * y, 0.25)
                * smoothstep((x * x + y * y) , 0.25 + delta, 0.25)
                * qt_Opacity;
        }"
    }
}

enter image description here

Similarly to the first approach, rounded and adapt properties are added to control the visual appearance of the effect as discussed above.

撑一把青伞 2024-11-15 16:41:38

这段代码可以帮助你

Rectangle {
    width: 200
    height: 200

    color: "transparent"

    //this Rectangle is needed to keep the source image's fillMode
    Rectangle {
        id: imageSource

        anchors.fill: parent
        Image {
            anchors.fill: parent
            source: "your_image_file_path"

            fillMode: Image.PreserveAspectCrop
        }
        visible: false

        layer.enabled: true
    }

    Rectangle {
        id: maskLayer
        anchors.fill: parent
        radius: parent.width / 2

        color: "red"

        border.color: "black"

        layer.enabled: true
        layer.samplerName: "maskSource"
        layer.effect: ShaderEffect {

            property var colorSource: imageSource
            fragmentShader: "
                uniform lowp sampler2D colorSource;
                uniform lowp sampler2D maskSource;
                uniform lowp float qt_Opacity;
                varying highp vec2 qt_TexCoord0;
                void main() {
                    gl_FragColor =
                        texture2D(colorSource, qt_TexCoord0)
                        * texture2D(maskSource, qt_TexCoord0).a
                        * qt_Opacity;
                }
            "
        }

    }

    // only draw border line
    Rectangle {
        anchors.fill: parent

        radius: parent.width / 2

        border.color: "black"
        border.width: 2

        color: "transparent"
    }
}

This code would help you

Rectangle {
    width: 200
    height: 200

    color: "transparent"

    //this Rectangle is needed to keep the source image's fillMode
    Rectangle {
        id: imageSource

        anchors.fill: parent
        Image {
            anchors.fill: parent
            source: "your_image_file_path"

            fillMode: Image.PreserveAspectCrop
        }
        visible: false

        layer.enabled: true
    }

    Rectangle {
        id: maskLayer
        anchors.fill: parent
        radius: parent.width / 2

        color: "red"

        border.color: "black"

        layer.enabled: true
        layer.samplerName: "maskSource"
        layer.effect: ShaderEffect {

            property var colorSource: imageSource
            fragmentShader: "
                uniform lowp sampler2D colorSource;
                uniform lowp sampler2D maskSource;
                uniform lowp float qt_Opacity;
                varying highp vec2 qt_TexCoord0;
                void main() {
                    gl_FragColor =
                        texture2D(colorSource, qt_TexCoord0)
                        * texture2D(maskSource, qt_TexCoord0).a
                        * qt_Opacity;
                }
            "
        }

    }

    // only draw border line
    Rectangle {
        anchors.fill: parent

        radius: parent.width / 2

        border.color: "black"
        border.width: 2

        color: "transparent"
    }
}
迷雾森÷林ヴ 2024-11-15 16:41:38

当您的背景是纯色或从不移动图像时,制作圆角的一种快速方法是将您的 Image 与另一个图像(或与 BorderImage) 只绘制角点。

如果这不是一个选项,但您正在使用 OpenGL,则另一种方法是通过像素着色器将遮罩应用于图像。请参阅http://blog.qt.digia .com/blog/2011/05/03/qml-shadereffectitem-on-qgraphicsview/ 用于在 Qt 4 之上工作的插件。

最后,还可以编写 QDeclarativeImageProvider 对图像进行预处理以使角变圆。

When your background is a solid color or when you're never moving the image, a fast way to make rounded corners is to overlap your Image with another one (or with a BorderImage) that only draws the corners.

When this is not an option, but you are using OpenGL, then another way is to apply a mask to the image through a pixel shader. See http://blog.qt.digia.com/blog/2011/05/03/qml-shadereffectitem-on-qgraphicsview/ for a plugin that works on top of Qt 4.

Finally, it's also possible to write a QDeclarativeImageProvider that preprocesses your image to make the corners rounded.

桃酥萝莉 2024-11-15 16:41:38

虽然接受的答案和 来自 @fury 的答案对我来说同样有效(Qt 5.9.3),但他们都离开了当应用于光栅图像时,角落会出现一些像差(SVG 中没有)。在所有情况下对我来说最有效的是将OpacityMask应用于周围的项目,例如原始帖子中的矩形。

Rectangle {
    id: root;
    anchors.right: rectContentBg.left
    anchors.top: rectContentBg.top
    anchors.margins: 8

    radius: 8

    width: 64
    height: 64

    // apply rounded corners mask
    layer.enabled: true
    layer.effect: OpacityMask {
        maskSource: Rectangle {
            x: root.x; y: root.y
            width: root.width
            height: root.height
            radius: root.radius
        }
    }

    Image {
        id: imgAuthor
        opacity: 1
        smooth: false
        anchors.fill: parent
        source: "qrc:/res/sample_avatar.jpg"
    }
}

While both the accepted answer and the one from @fury worked equally well for me (Qt 5.9.3), they both left some aberrations in the corners when applied to raster images (didn't have those with SVG). What worked best for me in all cases was to apply the OpacityMask to a surrounding item, e.g. like the rectangle in the original post.

Rectangle {
    id: root;
    anchors.right: rectContentBg.left
    anchors.top: rectContentBg.top
    anchors.margins: 8

    radius: 8

    width: 64
    height: 64

    // apply rounded corners mask
    layer.enabled: true
    layer.effect: OpacityMask {
        maskSource: Rectangle {
            x: root.x; y: root.y
            width: root.width
            height: root.height
            radius: root.radius
        }
    }

    Image {
        id: imgAuthor
        opacity: 1
        smooth: false
        anchors.fill: parent
        source: "qrc:/res/sample_avatar.jpg"
    }
}
原野 2024-11-15 16:41:38

如果您有单色背景,则可以在顶部绘制圆角矩形的边框。

Image{
    id:img
}
Rectangle { // rounded corners for img
    anchors.fill: img
    color: "transparent"
    border.color: "blue" // color of background
    border.width: 4
    radius: 4
}

If you have a unicolor background, you can draw with the border of a rounded rectangle on top.

Image{
    id:img
}
Rectangle { // rounded corners for img
    anchors.fill: img
    color: "transparent"
    border.color: "blue" // color of background
    border.width: 4
    radius: 4
}
半边脸i 2024-11-15 16:41:38

QML 目前仅支持矩形裁剪,但您可能想看看 qt-components 项目中的 DeclarativeMaskedImage:

http://qt.gitorious.org/qt-components/qt-components/blobs/master/src/symbian/sdeclarativemaskedimage.h

QML currently supports only rectangular clipping, but you might want to take a look at DeclarativeMaskedImage in qt-components project:

http://qt.gitorious.org/qt-components/qt-components/blobs/master/src/symbian/sdeclarativemaskedimage.h

花辞树 2024-11-15 16:41:38

我知道我来晚了一点,但我通过谷歌搜索来到这里,所以我想我会帮助后代:) QtGraphicalEffects OpacityMask 应该更简单地做到这一点(我对图层效果方法有疑问)

Image {
    id: imgAuthor

    width: 64
    height: 64

    source: "qrc:/res/sample_avatar.jpg"

    visible: false // this is needed or the corners of the image will be visible underneath the opacity mask
}

OpacityMask {
    anchors.fill: imgAuthor
    source: imgAuthor
    maskSource: Rectangle {
        width: imgAuthor.width
        height: imgAuthor.height
        radius: 8
        visible: false // this also needs to be invisible or it will cover up the image
    }
}

I know I'm a little late to the party, but I got here by googling, so thought I'd help future generations :) QtGraphicalEffects OpacityMask should do this a bit more simply (I had issues with the layer effect approach)

Image {
    id: imgAuthor

    width: 64
    height: 64

    source: "qrc:/res/sample_avatar.jpg"

    visible: false // this is needed or the corners of the image will be visible underneath the opacity mask
}

OpacityMask {
    anchors.fill: imgAuthor
    source: imgAuthor
    maskSource: Rectangle {
        width: imgAuthor.width
        height: imgAuthor.height
        radius: 8
        visible: false // this also needs to be invisible or it will cover up the image
    }
}
擦肩而过的背影 2024-11-15 16:41:38

如果您有单色背景,则可以在顶部绘制圆角矩形的边框。

Item {
    property int radius: 0
    property color bgColor: "#000000"
    property int drawRadius: radius > 0 ? radius : width/2

    Image {
        anchors.fill: parent
        sourceSize: Qt.size(width, height)
        asynchronous: true
    }

    Canvas {
        anchors.fill: parent
        antialiasing: true
        onPaint: {
            var ctx = getContext("2d")
            ctx.fillStyle = bgColor
            ctx.beginPath()
            ctx.rect(0, 0, width, height)
            ctx.fill()

            ctx.beginPath()
            ctx.globalCompositeOperation = 'source-out'
            ctx.roundedRect(0, 0, width, height, drawRadius, drawRadius)
            ctx.fill()
        }
    }
}

If you have a unicolor background, you can draw with the border of a rounded rectangle on top.

Item {
    property int radius: 0
    property color bgColor: "#000000"
    property int drawRadius: radius > 0 ? radius : width/2

    Image {
        anchors.fill: parent
        sourceSize: Qt.size(width, height)
        asynchronous: true
    }

    Canvas {
        anchors.fill: parent
        antialiasing: true
        onPaint: {
            var ctx = getContext("2d")
            ctx.fillStyle = bgColor
            ctx.beginPath()
            ctx.rect(0, 0, width, height)
            ctx.fill()

            ctx.beginPath()
            ctx.globalCompositeOperation = 'source-out'
            ctx.roundedRect(0, 0, width, height, drawRadius, drawRadius)
            ctx.fill()
        }
    }
}

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文