Une solution officielle intégrée existe depuis Qt 5 grâce au module QtGraphicalEffects
et je suis assez surpris de constater que personne n'a fourni une solution aussi simple. Si vous ciblez Qt 6.x QtGraphicalEffects
est malheureusement obsolète donc passez à la deuxième partie de la réponse qui propose une solution indépendante de QtGraphicalEffects
.
Solution QtGraphicalEffects
Parmi les autres effets, OpacityMask
est le type à exploiter à cette fin. L'idée est de masquer l'Image
source avec un Rectangle
ayant un rayon
correctement défini. Voici le plus simple exemple en utilisant le calque:
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 minimal produit un joli résultat pour les images carrées mais prend également en compte les images non carrées via la variable adapt
. En définissant le drapeau sur false
, le masque produit sera toujours un cercle, indépendamment de la taille de l'image. Cela est possible grâce à l'utilisation d'un Item
externe qui remplit la source et permet au véritable masque (le Rectangle
interne) d'être dimensionné en conséquence. Vous pouvez bien sûr vous débarrasser de l'Item
externe si vous visez simplement un masque qui remplit la source, indépendamment de son rapport hauteur/largeur.
Voici une jolie image de chat avec un format carré (à gauche), un format non carré avec adapt: true
(au centre) et enfin un format non carré et adapt: false
(à droite) :
Les détails de mise en œuvre de cette solution sont très similaires à ceux de la réponse basée sur les shaders dans l'autre bonne réponse (voir le code source QML pour OpacityMask
qui peut être trouvé ici - SourceProxy
renvoie simplement un ShaderEffectSource
bien formé pour alimenter l'effet).
Solution sans dépendance
Si vous ne voulez pas - ou ne pouvez pas - dépendre du module QtGraphicalEffects
(enfin, de la présence de OpacityMask.qml``step
, smoothstep
et fwidth
. Voici 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;
}"
}
}
De manière similaire à la première approche, les propriétés rounded
et adapt
sont ajoutées pour contrôler l'apparence visuelle de l'effet comme discuté précédemment.