qml - учебник - qt quick




Изображение округлых углов в QML (5)

К моему удивлению, компонент 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"
    }
}

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



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

Среди других эффектов OpacityMask - это тип, который будет использоваться для этой цели. Идея состоит в том, чтобы замаскировать исходное Image Rectangle с правильным radius . Вот пример простейшего примера:

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центре ) и, наконец, неквадратный формат и adapt: false ( справа ):

Детали реализации этого решения очень похожи на данные ответа на основе шейдера в другом приятном ответе (cfr. Исходный код QML для OpacityMask который можно найти here - 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;
        }"
    }
}

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


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

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

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


Хотя и принятый ответ, и один из @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"
    }
}

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