Image coins arrondis en QML

a ma surprise, le Image composant n'a pas d' radius propriété. J'ai essayé d'émuler les coins arrondis en mettant l'image dans un arrondi Rectangle, mais il ne coupe pas les coins.

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"
    }
}

Comment puis-je créer une image avec des coins arrondis correctement?

28
demandé sur BaCaRoZzo 2011-05-23 00:58:50

7 réponses

une solution officielle intégrée existe à partir de Qt 5 grâce à la QtGraphicalEffects module et je suis assez surpris de découvrir que personne n'a fourni une solution aussi simple.

Parmi les autres effets OpacityMask est le type à exploiter à cet effet. L'idée est de masquer la source Image avec un Rectangle qui a correctement défini radius. Voici l'exemple le plus simple en utilisant la superposition:

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

ce code minimum produit un bon résultat pour des images carrées mais il prend également en compte les images non carrées via le adapt variable. Par le paramétrage de l'indicateur false le produit masque sera toujours un cercle, quelle que soit la taille de l'image. C'est possible grâce à l'utilisation d'une externe Item qui remplit la source et permet le réel masque (l'intérieur Rectangle) à dimensionner s'il vous plaît. vous pouvez évidemment vous débarrasser de l'extérieur Item, si vous visez simplement un masque qui remplit la source, indépendamment de son aspect ratio.

Voici une image de chat mignon avec un format carré (gauche), un format non carré avec adapt: true ( center) et enfin un format non carré et adapt: false (droit):

enter image description here

les détails de mise en oeuvre de cette solution sont très similaires à ceux de la réponse basée sur shader dans l'autre belle réponse(cfr. le code source QML pour OpacityMask qui peut être trouvé ici -SourceProxy renvoie simplement unShaderEffectSource pour nourrir l'effet).

si vous ne voulez pas dépendre du QtGraphicalEffects module (bien, sur la présence de OpacityMask.qml en fait), vous pouvez réimposer l'effet avec des shaders. En dehors de la solution déjà fournie une autre approche est d'utiliser step,smoothstep et fwidth fonctions. Ici, c'est le 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

comme pour la première approche,rounded et adapt propriétés sont ajoutées pour contrôler l'aspect visuel de l'effet, comme discuté ci-dessus.

36
répondu BaCaRoZzo 2017-12-17 13:58:47

quand votre arrière-plan est d'une couleur unie ou quand vous ne déplacez jamais l'image, un moyen rapide pour faire des coins arrondis est de chevaucher votre Image avec un autre (ou avec un BorderImage) qui ne dessine que les coins.

quand ce n'est pas une option, mais que vous utilisez OpenGL, alors une autre façon est d'appliquer un masque à l'image à travers un pixel shader. Voir http://blog.qt.digia.com/blog/2011/05/03/qml-shadereffectitem-on-qgraphicsview/ pour un plugin qui fonctionne au-dessus de l'intervalle Qt 4.

enfin, il est aussi possible d'écrire un QDeclarativeImageProvider qui pré-traite votre image pour faire les coins arrondis.

7
répondu Thorbjørn Lindeijer 2013-05-05 13:27:02

QML ne supporte actuellement que le découpage rectangulaire, mais vous pouvez jeter un oeil à DeclarativeMaskedImage dans le projet Qt-components:

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

5
répondu chalup 2011-05-23 06:14:25

si vous avez un fond unicolore, vous pouvez dessiner avec la bordure d'un rectangle arrondi sur le dessus.

Image{
    id:img
}
Rectangle { // rounded corners for img
    anchors.fill: img
    color: "transparent"
    border.color: "blue" // color of background
    border.width: 4
    radius: 4
}
5
répondu Helmut S 2012-01-25 18:18:13

ce code vous aiderait

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"
    }
}
5
répondu DenimPowell 2015-06-26 10:30:54

je sais que je suis un peu en retard à la fête, mais je suis arrivé ici en googling, donc j'ai pensé que je pourrais aider les générations futures:) Qtgraphaleffects OpacityMask devrait faire ceci un peu plus simplement (j'ai eu des problèmes avec l'approche de l'effet de couche)

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
    }
}
2
répondu fury 2017-06-09 15:58:24

alors que la réponse acceptée et celui de @fury fonctionnait aussi bien pour moi (Qt 5.9.3), ils ont tous les deux laissé quelques aberrations dans les coins lorsqu'ils étaient appliqués aux images matricielles (ils n'avaient pas ceux avec SVG). Ce qui a fonctionné le mieux pour moi dans tous les cas était d'appliquer le OpacityMask à un objet environnant, par exemple comme le rectangle dans le poteau original.

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"
    }
}
1
répondu Max Paperno 2017-12-27 09:16:48