Изображение закругленными углами в QML - PullRequest
32 голосов
/ 23 мая 2011

К моему удивлению, компонент Image не имеет свойства radius. Я попытался эмулировать скругленные углы, поместив изображение в скругленный Rectangle, но оно не обрезает углы.

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

Как правильно создать изображение со скругленными углами?

Ответы [ 7 ]

41 голосов
/ 22 сентября 2015

Начиная с Qt 5, существует встроенное официальное решение благодаря модулю QtGraphicalEffects, и я очень удивлен, обнаружив, что никто не предоставил такое простое решение.

Среди других эффектов OpacityMask это тип, который будет использоваться для этой цели.Идея состоит в том, чтобы замаскировать источник Image с Rectangle, который имеет правильно установленный radius.Вот самый простой пример использования 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)
            }
        }
    }
}

Этот минимальный код дает хороший результат для квадратных изображений, но он также учитывает неквадратные изображения через переменную adapt.При установке флажка false полученная маска всегда будет кругом, независимо от размера изображения.Это возможно благодаря использованию внешнего Item, который заполняет источник и позволяет определять размер реальной маски (внутренней Rectangle) по желанию. Очевидно, что вы можете избавиться от внешнего Item, если просто нацелитесь на маску, которая заполняет источник, независимо от его соотношения сторон .

Вот изображение милого кота с квадратным форматом ( слева ), неквадратным форматом с adapt: true ( center ) и, наконец, не квадратнымформат и adapt: false ( вправо ):

enter image description here

Детали реализации этого решения очень похожи на детали шейдерана основе ответа в другом хороший ответ (ср. исходный код QML для OpacityMask, который можно найти здесь - SourceProxy просто возвращает правильно сформированный ShaderEffectSource в

Если вы не хотите зависеть от модуля QtGraphicalEffects (ну, на самом деле, от наличия OpacityMask.qml), вы можете переопределить эффект с помощью шейдеров.Помимо уже предоставленного решения, другой подход заключается в использовании функций step, smoothstep и fwidth.Вот код:

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

Аналогично первому подходу, свойства rounded и adapt добавлены для управления визуальным оформлением.эффекта, как описано выше.

7 голосов
/ 23 мая 2011

Если ваш фон сплошного цвета или вы никогда не двигаете изображение, быстрый способ сделать закругленные углы - это перекрыть ваш Image с другим (или с BorderImage ), который рисует только углы.

Если это не вариант, но вы используете OpenGL, тогда другой способ - применить маску к изображению с помощью пиксельного шейдера. Смотрите http://blog.qt.digia.com/blog/2011/05/03/qml-shadereffectitem-on-qgraphicsview/ для плагина, который работает поверх Qt 4.

Наконец, также возможно написать QDeclarativeImageProvider, который предварительно обрабатывает ваше изображение, чтобы сделать углы скругленными.

6 голосов
/ 26 июня 2015

Этот код поможет вам

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 голосов
/ 25 января 2012

Если у вас есть одноцветный фон, вы можете рисовать с границей прямоугольника с закругленными углами сверху.

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 голосов
/ 23 мая 2011

QML в настоящее время поддерживает только прямоугольное отсечение, но вы можете взглянуть на DeclarativeMaskedImage в проекте qt-components:

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

4 голосов
/ 09 июня 2017

Я знаю, что немного опоздал на вечеринку, но я пришел сюда, погуглив, поэтому подумал, что помогу будущим поколениям :) 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
    }
}
2 голосов
/ 27 декабря 2017

В то время как принятый ответ и ответ из @ 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"
    }
}
...