diff options
author | Henning Gruendl <henning.gruendl@qt.io> | 2024-04-16 11:28:23 +0200 |
---|---|---|
committer | Henning Gruendl <henning.gruendl@qt.io> | 2024-04-16 12:40:16 +0200 |
commit | 9af76f3b7da895df3c87772885108548380d87ed (patch) | |
tree | 53f9277d5eb66f34f852087529f6fa0b8e052fe4 | |
parent | 792cac6503ef3e8723e360fb928d822e02c576d5 (diff) |
Add DesignEffects
Change-Id: Ia0a67e86d679e4d6bfa47b0d7e9863759fbf4ff7
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
20 files changed, 1302 insertions, 1 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index d4c1f03..958155c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,6 +48,7 @@ find_package(Qt6 Quick Core Qml + ShaderTools ) set(PROJECT_VERSION ${Qt6_VERSION}) diff --git a/src/imports/CMakeLists.txt b/src/imports/CMakeLists.txt index eab1684..0ae89e6 100644 --- a/src/imports/CMakeLists.txt +++ b/src/imports/CMakeLists.txt @@ -1,5 +1,6 @@ add_subdirectory(compat) add_subdirectory(components) +add_subdirectory(designeffects) add_subdirectory(effects_qt6) add_subdirectory(flowview) add_subdirectory(logichelper) @@ -7,4 +8,3 @@ add_subdirectory(multitext) add_subdirectory(tools) add_subdirectory(application) add_subdirectory(utils) - diff --git a/src/imports/designeffects/CMakeLists.txt b/src/imports/designeffects/CMakeLists.txt new file mode 100644 index 0000000..e1ee5b4 --- /dev/null +++ b/src/imports/designeffects/CMakeLists.txt @@ -0,0 +1,39 @@ +qt_internal_add_qml_module(QuickStudioDesignEffects + URI "QtQuick.Studio.DesignEffects" + VERSION "${PROJECT_VERSION}" + DESIGNER_SUPPORTED + NO_SYNC_QT + PAST_MAJOR_VERSIONS 1 + QML_FILES + DesignBackgroundBlurPrivate.qml + DesignDropShadow.qml + DesignDropShadowPrivate.qml + DesignEffect.qml + DesignEffectPrivate.qml + DesignInnerShadow.qml + DesignInnerShadowPrivate.qml + DesignLayerBlurPrivate.qml +) + +qt_internal_add_shaders(QuickStudioDesignEffects "designeffectsshaders" + BATCHABLE + PRECOMPILE + OPTIMIZED + PREFIX + "/qt-project.org/imports/QtQuick/Studio/DesignEffects" + FILES + "shaders/dropShadow.frag" + "shaders/dropShadowClip.frag" + "shaders/gaussianBlur.frag" + "shaders/innerShadow.frag" + "shaders/innerShadowClip.frag" + "shaders/opacityMask.frag" +) + +#qt_internal_add_docs(QuickStudioComponents +# doc/qtquickstudiodesigneffects.qdocconf +#) + +#if(QT_FEATURE_quick_designer AND QT_BUILD_SHARED_LIBS) +# add_subdirectory(designer) +#endif() diff --git a/src/imports/designeffects/DesignBackgroundBlurPrivate.qml b/src/imports/designeffects/DesignBackgroundBlurPrivate.qml new file mode 100644 index 0000000..2c13187 --- /dev/null +++ b/src/imports/designeffects/DesignBackgroundBlurPrivate.qml @@ -0,0 +1,177 @@ +/**************************************************************************** +** +** Copyright (C) 2024 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Quick Studio Components. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick + +Item { + id: root + + property real radius: 10 + + /*required*/ property Item source + /*required*/ property Item background + + readonly property real sourceRotation: root.source.rotation + + readonly property size textureSize: Qt.size(root.targetRect.width, + root.targetRect.height) + readonly property vector2d pixelSize: Qt.vector2d(1.0 / root.textureSize.width, + 1.0 / root.textureSize.height) + readonly property real sigma: root.radius / 2.7 + + visible: true + + width: root.source.width + height: root.source.height + + rotation: -root.sourceRotation + + property rect targetRect: Qt.rect(0, 0, 0, 0) + + Connections { + target: root.background + function onXChanged() { root.sizeStuff() } + function onYChanged() { root.sizeStuff() } + function onWidthChanged() { root.sizeStuff() } + function onHeightChanged() { root.sizeStuff() } + } + + Connections { + target: root.source + + function onXChanged() { root.sizeStuff() } + function onYChanged() { root.sizeStuff() } + function onWidthChanged() { root.sizeStuff() } + function onHeightChanged() { root.sizeStuff() } + function onRotationChanged() { root.sizeStuff() } + } + + onSourceChanged: root.sizeStuff() + onBackgroundChanged: root.sizeStuff() + + function sizeStuff() { + if (root.background === null) + return + + let tRect = Qt.rect(0, 0, root.source.width, root.source.height) + root.targetRect = root.background.mapFromItem(root.source, tRect) + } + + //Component.onCompleted: console.log("Background Blur created!") + + // TODO + // Check if target and background overlap + // Check if target is actually transparent + + ShaderEffectSource { + id: shaderEffectSource + visible: false + width: root.width + height: root.height + sourceItem: root.background + sourceRect: root.targetRect + } + + ShaderEffect { + id: blurHorizontal + + property real blurKernel: root.radius + property real sigma: root.sigma + property var src: shaderEffectSource + property vector2d pixelSize: root.pixelSize.times(Qt.vector2d(1, 0)) + property bool useOffscreenColor: false + property color offscreenColor: "transparent" + + visible: false + + width: root.textureSize.width + height: root.textureSize.height + anchors.centerIn: parent + + layer.enabled: true + fragmentShader: "shaders/gaussianBlur.frag.qsb" + } + + ShaderEffect { + id: blurVertical + + property real blurKernel: root.radius + property real sigma: root.sigma + property var src: blurHorizontal + property vector2d pixelSize: root.pixelSize.times(Qt.vector2d(0, 1)) + property bool useOffscreenColor: false + property color offscreenColor: "transparent" + + visible: false + + width: root.textureSize.width + height: root.textureSize.height + anchors.centerIn: parent + + layer.enabled: true + fragmentShader: "shaders/gaussianBlur.frag.qsb" + } + + Item { + id: wrapper + anchors.centerIn: parent + + width: root.textureSize.width + height: root.textureSize.height + + visible: false + layer.enabled: true + + ShaderEffectSource { + id: shaderEffectSource2 + visible: true + anchors.centerIn: parent + rotation: root.sourceRotation + + width: root.source.width + height: root.source.height + sourceItem: root.source + } + } + + ShaderEffect { + id: mask + + property var source: blurVertical + property var maskSource: wrapper + + visible: true + + width: root.textureSize.width + height: root.textureSize.height + + anchors.centerIn: parent + fragmentShader: "shaders/opacityMask.frag.qsb" + } +} diff --git a/src/imports/designeffects/DesignDropShadow.qml b/src/imports/designeffects/DesignDropShadow.qml new file mode 100644 index 0000000..dbee640 --- /dev/null +++ b/src/imports/designeffects/DesignDropShadow.qml @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2024 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Quick Studio Components. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick +import QtQuick.Controls + +QtObject { + property real blur: 4 + property int offsetX: 0 + property int offsetY: 4 + property int spread: 0 + property color color: "#3f000000" // black 25% + property bool visible: true + + property string type: "DropShadow" +} diff --git a/src/imports/designeffects/DesignDropShadowPrivate.qml b/src/imports/designeffects/DesignDropShadowPrivate.qml new file mode 100644 index 0000000..a47081f --- /dev/null +++ b/src/imports/designeffects/DesignDropShadowPrivate.qml @@ -0,0 +1,199 @@ +/**************************************************************************** +** +** Copyright (C) 2024 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Quick Studio Components. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick + +Item { + id: root + + property int horizontalOffset: 10 + property int verticalOffset: 30 + property int spread: 0 + property color color: "black" + property real radius: 10 + + required property Item source + + readonly property real sourceRotation: root.source.rotation + readonly property real radiusCeiled: Math.ceil(root.radius) + + readonly property size orginialTextureSize: Qt.size(root.width + root.spread * 2, + root.height + root.spread * 2) + readonly property real sigma: root.radius / 2.7 + + visible: true + + width: root.source.width + height: root.source.height + + //Component.onCompleted: console.log("Drop Shadow created!") + + onSourceRotationChanged: root.calculateOffset() + onRadiusCeiledChanged: root.calculateOffset() + onSpreadChanged: root.calculateOffset() + onHorizontalOffsetChanged: root.calculateOffset() + onVerticalOffsetChanged: root.calculateOffset() + + signal geometryChanged() + + property point __offset: Qt.point(0, 0) + + function calculateOffset() { + let mat = Qt.matrix4x4() + mat.translate(Qt.vector3d(-root.spread, -root.spread, 0)) + mat.translate(Qt.vector3d(-root.radiusCeiled, -root.radiusCeiled, 0)) + mat.rotate(-root.sourceRotation, Qt.vector3d(0, 0, 1)) + root.__offset = mat.map(Qt.point(root.horizontalOffset, root.verticalOffset)) + root.__offset = Qt.point(Math.round(root.__offset.x), Math.round(root.__offset.y)) + + root.geometryChanged() + } + + readonly property bool copyActive: root.source instanceof Rectangle && root.spread !== 0 + + Rectangle { + id: sourceCopy + visible: false + width: Math.max(0, root.source.width + root.spread * 2) + height: Math.max(0, root.source.height + root.spread * 2) + radius: Math.max(0, root.source.radius !== 0 ? root.source.radius + root.spread : 0) + color: "black" + } + + ShaderEffectSource { + id: shaderEffectSource + visible: false + width: root.orginialTextureSize.width + height: root.orginialTextureSize.height + sourceItem: root.copyActive ? sourceCopy : root.source + } + + ShaderEffect { + id: shadow + + property color color: root.color + property var src: shaderEffectSource + + visible: false + + width: root.orginialTextureSize.width + height: root.orginialTextureSize.height + + layer.enabled: true + layer.sourceRect: Qt.rect(-root.radiusCeiled, -root.radiusCeiled, + root.bluredTextureSize.width, root.bluredTextureSize.height) + + fragmentShader: "shaders/dropShadow.frag.qsb" + } + + readonly property size bluredTextureSize: Qt.size(root.orginialTextureSize.width + root.radiusCeiled * 2, + root.orginialTextureSize.height + root.radiusCeiled * 2) + + readonly property vector2d bluredPixelSize: Qt.vector2d(1.0 / root.bluredTextureSize.width, + 1.0 / root.bluredTextureSize.height) + + ShaderEffect { + id: blurHorizontal + + property real blurKernel: root.radius + property real sigma: root.sigma + property var src: shadow + property vector2d pixelSize: root.bluredPixelSize.times(Qt.vector2d(1, 0)) + property bool useOffscreenColor: false + property color offscreenColor: "transparent" + + visible: false + + width: root.bluredTextureSize.width + height: root.bluredTextureSize.height + + layer.enabled: true + layer.smooth: true // Otherwise bluring artifacts + + fragmentShader: "shaders/gaussianBlur.frag.qsb" + } + + ShaderEffect { + id: blurVertical + + property real blurKernel: root.radius + property real sigma: root.sigma + property var src: blurHorizontal + property vector2d pixelSize: root.bluredPixelSize.times(Qt.vector2d(0, 1)) + property bool useOffscreenColor: false + property color offscreenColor: "transparent" + + visible: false + + width: root.bluredTextureSize.width + height: root.bluredTextureSize.height + + layer.enabled: true + layer.sourceRect: Qt.rect(Math.min(0, -root.__offset.x), + Math.min(0, -root.__offset.y), + root.offsetTextureSize.width, + root.offsetTextureSize.height) + + fragmentShader: "shaders/gaussianBlur.frag.qsb" + } + + readonly property size offsetTextureSize: Qt.size(root.bluredTextureSize.width + Math.abs(root.__offset.x), + root.bluredTextureSize.height + Math.abs(root.__offset.y)) + + ShaderEffectSource { + id: originalSource + visible: false + width: root.offsetTextureSize.width + height: root.offsetTextureSize.height + sourceItem: root.source + sourceRect: Qt.rect(Math.min(0, root.__offset.x), + Math.min(0, root.__offset.y), + root.offsetTextureSize.width, + root.offsetTextureSize.height) + } + + ShaderEffect { + id: result + property var shadow: blurVertical + property var original: originalSource + + visible: true + + x: -Math.max(0, -root.__offset.x) + y: -Math.max(0, -root.__offset.y) + width: root.offsetTextureSize.width + height: root.offsetTextureSize.height + + fragmentShader: "shaders/dropShadowClip.frag.qsb" + } + + readonly property rect boundingBox: Qt.rect(result.x, result.y, result.width, result.height) + + onBoundingBoxChanged: root.geometryChanged() +} diff --git a/src/imports/designeffects/DesignEffect.qml b/src/imports/designeffects/DesignEffect.qml new file mode 100644 index 0000000..d20c613 --- /dev/null +++ b/src/imports/designeffects/DesignEffect.qml @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2024 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Quick Studio Components. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick + +Item { + id: root + + // Use visible property to show and hide the effect. + visible: true + + // This is an internal property used to manage the effect. Do not modify. + property Item __oldParent: null + + // This is the main source for the effect. Set internally to the current parent item. Do not modify. + property Item source: null + + property list<QtObject> effects + + property bool layerBlurVisible: true + property real layerBlurRadius: 0 + property bool backgroundBlurVisible: true + property real backgroundBlurRadius: 0 + + property Item backgroundLayer: null + + property var _isEffectItem: true + + onParentChanged: { + if (root.__oldParent && root.__oldParent !== root.parent) { + root.__oldParent.layer.enabled = false + root.__oldParent.layer.effect = null + root.source = null + root.__oldParent.update() + root.__oldParent = null + } + + if (root.parent) { + root.__oldParent = root.parent + if (root.visible) { + root.parent.layer.enabled = true + root.parent.layer.effect = effectComponent + } + root.source = root.parent + } + } + + onVisibleChanged: { + if (root.visible) { + root.source = root.parent + root.parent.layer.enabled = true + root.parent.layer.effect = effectComponent + } else { + root.parent.layer.enabled = false + root.parent.layer.effect = null + root.source = null + } + root.parent.update() + } + + Component { + id: effectComponent + + DesignEffectPrivate { + id: effect + property bool __effect: true + source: root.source + + effects: root.effects + layerBlurVisible: root.layerBlurVisible + layerBlurRadius: root.layerBlurRadius + backgroundBlurVisible: root.backgroundBlurVisible + backgroundBlurRadius: root.backgroundBlurRadius + background: root.backgroundLayer + } + } +} + diff --git a/src/imports/designeffects/DesignEffectPrivate.qml b/src/imports/designeffects/DesignEffectPrivate.qml new file mode 100644 index 0000000..14b3072 --- /dev/null +++ b/src/imports/designeffects/DesignEffectPrivate.qml @@ -0,0 +1,176 @@ +/**************************************************************************** +** +** Copyright (C) 2024 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Quick Studio Components. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick +import Qt.labs.qmlmodels + +Item { + id: root + + required property Item source + + property list<QtObject> effects + + property Item background: null + + property bool layerBlurVisible: true + property real layerBlurRadius: 0 + property bool backgroundBlurVisible: true + property real backgroundBlurRadius: 0 + + onSourceChanged: root.source.antialiasing = false // Workaround + + width: root.source.width + height: root.source.height + + Component.onCompleted: root.calculateBoundingBox() + + property rect effectBoundingBox: Qt.rect(0, 0, 0, 0) + + function calculateBoundingBox() { + let x = 0 + let y = 0 + let width = root.width + let height = root.height + + for (let i = 0; i < repeater.count; ++i) { + let item = repeater.itemAt(i) + if (item === null || !(item instanceof DesignDropShadowPrivate)) + continue + + let childRect = item.boundingBox + + x = Math.min(x, childRect.x) + y = Math.min(y, childRect.y) + width = Math.max(width, childRect.width) + height = Math.max(height, childRect.height) + + width += Math.max(0, -x) // TODO Understand + height += Math.max(0, -y) + } + + root.effectBoundingBox = Qt.rect(x, y, width, height) + } + + DesignLayerBlurPrivate { + id: layerBlur + visible: root.layerBlurVisible + radius: root.layerBlurRadius + + source: layerBlurSource + + x: root.effectBoundingBox.x - Math.ceil(layerBlur.radius) + y: root.effectBoundingBox.y - Math.ceil(layerBlur.radius) + } + + ShaderEffectSource { + id: layerBlurSource + visible: false + width: root.effectBoundingBox.width + height: root.effectBoundingBox.height + sourceItem: wrapper + sourceRect: root.effectBoundingBox + hideSource: root.layerBlurVisible + + //samples: 4 // Workaround + //smooth: true + } + + Item { + id: wrapper + anchors.fill: parent + + DesignBackgroundBlurPrivate { + visible: root.backgroundBlurVisible + && root.background !== null + && root.backgroundBlurRadius !== 0 + + source: root.source + + background: root.background + radius: root.backgroundBlurRadius + } + + ShaderEffectSource { + id: shaderEffectSource + visible: true + width: root.width + height: root.height + sourceItem: root.source + hideSource: true + z: 1 + //samples: 4 // Workaround + } + + Repeater { + id: repeater + model: root.effects + + delegate: DelegateChooser { + role: "type" + + DelegateChoice { + roleValue: "DropShadow" + DesignDropShadowPrivate { + required property var modelData + + source: root.source + + horizontalOffset: modelData.offsetX + verticalOffset: modelData.offsetY + spread: modelData.spread + color: modelData.color + radius: modelData.blur + visible: modelData.visible + + onGeometryChanged: root.calculateBoundingBox() + } + } + + DelegateChoice { + roleValue: "InnerShadow" + DesignInnerShadowPrivate { + required property var modelData + + source: root.source + + horizontalOffset: modelData.offsetX + verticalOffset: modelData.offsetY + spread: modelData.spread + color: modelData.color + radius: modelData.blur + visible: modelData.visible + + z: 10 + } + } + } + } + } +} diff --git a/src/imports/designeffects/DesignInnerShadow.qml b/src/imports/designeffects/DesignInnerShadow.qml new file mode 100644 index 0000000..a6c9616 --- /dev/null +++ b/src/imports/designeffects/DesignInnerShadow.qml @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2024 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Quick Studio Components. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick +import QtQuick.Controls + +QtObject { + property real blur: 4 + property int offsetX: 0 + property int offsetY: 4 + property int spread: 0 + property color color: "#3f000000" // black 25% + property bool visible: true + + property string type: "InnerShadow" +} diff --git a/src/imports/designeffects/DesignInnerShadowPrivate.qml b/src/imports/designeffects/DesignInnerShadowPrivate.qml new file mode 100644 index 0000000..f753a54 --- /dev/null +++ b/src/imports/designeffects/DesignInnerShadowPrivate.qml @@ -0,0 +1,179 @@ +/**************************************************************************** +** +** Copyright (C) 2024 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Quick Studio Components. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick + +Item { + id: root + + property int horizontalOffset: 10 + property int verticalOffset: 30 + property int spread: 0 + property color color: "black" + property real radius: 10 + + required property Item source + + readonly property real sourceRotation: root.source.rotation + + readonly property size textureSize: Qt.size(Math.max(root.width, root.width - root.spread * 2) + Math.abs(root.__offset.x), + Math.max(root.height, root.height - root.spread * 2) + Math.abs(root.__offset.y)) + readonly property vector2d pixelSize: Qt.vector2d(1.0 / root.textureSize.width, + 1.0 / root.textureSize.height) + readonly property real sigma: root.radius / 2.7 + + visible: true + + width: root.source.width + height: root.source.height + + //Component.onCompleted: console.log("Inner Shadow created!") + + onSourceRotationChanged: root.calculateOffset() + onSpreadChanged: root.calculateOffset() + onHorizontalOffsetChanged: root.calculateOffset() + onVerticalOffsetChanged: root.calculateOffset() + + property point __offset: Qt.point(0, 0) + + function calculateOffset() { + let mat = Qt.matrix4x4() + mat.rotate(-root.sourceRotation, Qt.vector3d(0, 0, 1)) + root.__offset = mat.map(Qt.point(root.horizontalOffset, root.verticalOffset)) + root.__offset = Qt.point(Math.round(root.__offset.x), Math.round(root.__offset.y)) + } + + readonly property bool copyActive: root.source instanceof Rectangle && root.spread !== 0 + + Rectangle { + id: sourceCopy + visible: false + width: Math.max(0, root.source.width - root.spread * 2) + height: Math.max(0, root.source.height - root.spread * 2) + radius: Math.max(0, root.source.radius !== 0 ? root.source.radius - root.spread : 0) + color: "black" + } + + ShaderEffectSource { + id: shaderEffectSource + visible: false + width: root.textureSize.width + height: root.textureSize.height + sourceItem: root.copyActive ? sourceCopy : root.source + sourceRect: Qt.rect(Math.min(0, -root.__offset.x) - Math.max(0, root.spread), + Math.min(0, -root.__offset.y) - Math.max(0, root.spread), + root.textureSize.width, + root.textureSize.height) + } + + ShaderEffect { + id: shadow + + property color color: root.color + property var src: shaderEffectSource + + visible: false + + width: root.textureSize.width + height: root.textureSize.height + + layer.enabled: true + + fragmentShader: "shaders/innerShadow.frag.qsb" + } + + ShaderEffect { + id: blurHorizontal + + property real blurKernel: root.radius + property real sigma: root.sigma + property var src: shadow + property vector2d pixelSize: root.pixelSize.times(Qt.vector2d(1, 0)) + property bool useOffscreenColor: true + property color offscreenColor: root.color + + visible: false + + width: root.textureSize.width + height: root.textureSize.height + + layer.enabled: true + layer.smooth: true // Otherwise bluring artifacts + + fragmentShader: "shaders/gaussianBlur.frag.qsb" + } + + ShaderEffect { + id: blurVertical + + property real blurKernel: root.radius + property real sigma: root.sigma + property var src: blurHorizontal + property vector2d pixelSize: root.pixelSize.times(Qt.vector2d(0, 1)) + property bool useOffscreenColor: true + property color offscreenColor: root.color + + visible: false + + width: root.textureSize.width + height: root.textureSize.height + + layer.enabled: true + layer.smooth: true // Otherwise bluring artifacts + + fragmentShader: "shaders/gaussianBlur.frag.qsb" + } + + ShaderEffectSource { + id: originalSource + visible: false + hideSource: true + width: root.textureSize.width + height: root.textureSize.height + sourceItem: root.source + sourceRect: Qt.rect(Math.min(0, root.__offset.x) + Math.min(0, root.spread), + Math.min(0, root.__offset.y) + Math.min(0, root.spread), + root.textureSize.width, + root.textureSize.height) + } + + ShaderEffect { + property var shadow: blurVertical + property var original: originalSource + + visible: true + + x: -Math.max(0, -root.__offset.x) + Math.min(0, root.spread) + y: -Math.max(0, -root.__offset.y) + Math.min(0, root.spread) + width: root.textureSize.width + height: root.textureSize.height + + fragmentShader: "shaders/innerShadowClip.frag.qsb" + } +} diff --git a/src/imports/designeffects/DesignLayerBlurPrivate.qml b/src/imports/designeffects/DesignLayerBlurPrivate.qml new file mode 100644 index 0000000..addbfb6 --- /dev/null +++ b/src/imports/designeffects/DesignLayerBlurPrivate.qml @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2024 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Quick Studio Components. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick + +Item { + id: root + + property real radius: 10 + + required property Item source + + readonly property real radiusCeiled: Math.ceil(root.radius) + + readonly property size textureSize: Qt.size(root.width + root.radiusCeiled * 2, + root.height + root.radiusCeiled * 2) + readonly property vector2d pixelSize: Qt.vector2d(1.0 / root.textureSize.width, + 1.0 / root.textureSize.height) + readonly property real sigma: root.radius / 2.7 + + visible: true + + width: root.source?.width + height: root.source?.height + + //Component.onCompleted: console.log("Layer Blur created!") + + ShaderEffectSource { + id: shaderEffectSource + visible: false + width: root.width + height: root.height + sourceItem: root.source + sourceRect: Qt.rect(-root.radiusCeiled, -root.radiusCeiled, + root.textureSize.width, root.textureSize.height) + } + + ShaderEffect { + id: blurHorizontal + + property real blurKernel: root.radius + property real sigma: root.sigma + property var src: shaderEffectSource + property vector2d pixelSize: root.pixelSize.times(Qt.vector2d(1, 0)) + property bool useOffscreenColor: false + property color offscreenColor: "transparent" + + visible: false + + width: root.textureSize.width + height: root.textureSize.height + + layer.enabled: true + layer.smooth: true // Otherwise bluring artifacts + + fragmentShader: "shaders/gaussianBlur.frag.qsb" + } + + ShaderEffect { + id: blurVertical + + property real blurKernel: root.radius + property real sigma: root.sigma + property var src: blurHorizontal + property vector2d pixelSize: root.pixelSize.times(Qt.vector2d(0, 1)) + property bool useOffscreenColor: false + property color offscreenColor: "transparent" + + width: root.textureSize.width + height: root.textureSize.height + + visible: true + + fragmentShader: "shaders/gaussianBlur.frag.qsb" + } +} diff --git a/src/imports/designeffects/plugins.qmltypes b/src/imports/designeffects/plugins.qmltypes new file mode 100644 index 0000000..aac3a1a --- /dev/null +++ b/src/imports/designeffects/plugins.qmltypes @@ -0,0 +1,13 @@ +import QtQuick.tooling 1.2 + +// This file describes the plugin-supplied types contained in the library. +// It is used for QML tooling purposes only. +// +// This file was auto-generated by: +// 'qmlplugindump -nonrelocatable -dependencies dependencies.json QtQuick.Controls 2.15' + +Module { + dependencies: [ + "QtQuick 2.11" + ] +} diff --git a/src/imports/designeffects/qmldir b/src/imports/designeffects/qmldir new file mode 100644 index 0000000..101ad7c --- /dev/null +++ b/src/imports/designeffects/qmldir @@ -0,0 +1,9 @@ +DesignBackgroundBlurPrivate 1.0 DesignBackgroundBlurPrivate.qml +DesignDropShadow 1.0 DesignDropShadow.qml +DesignDropShadowPrivate 1.0 DesignDropShadowPrivate.qml +DesignEffect 1.0 DesignEffect.qml +DesignEffectPrivate 1.0 DesignEffectPrivate.qml +DesignInnerShadow 1.0 DesignInnerShadow.qml +DesignInnerShadowPrivate 1.0 DesignInnerShadowPrivate.qml +DesignLayerBlurPrivate 1.0 DesignLayerBlurPrivate.qml + diff --git a/src/imports/designeffects/qtstudiocomponentsplugin.cpp b/src/imports/designeffects/qtstudiocomponentsplugin.cpp new file mode 100644 index 0000000..b9ddd62 --- /dev/null +++ b/src/imports/designeffects/qtstudiocomponentsplugin.cpp @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2024 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Quick Designer Components. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtQml/qqmlextensionplugin.h> + +QT_BEGIN_NAMESPACE + +class QtStudioDesignEffectsPlugin: public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) + +public: + QtStudioDesignEffectsPlugin(QObject *parent = nullptr); + void registerTypes(const char *uri) override; +}; + +QtStudioDesignEffectsPlugin::QtStudioDesignEffectsPlugin(QObject *parent) + : QQmlExtensionPlugin(parent) +{ +} + +void QtStudioDesignEffectsPlugin::registerTypes(const char *) +{ +} + +QT_END_NAMESPACE + +#include "qtstudiodesigneffectsplugin.moc" diff --git a/src/imports/designeffects/shaders/dropShadow.frag b/src/imports/designeffects/shaders/dropShadow.frag new file mode 100644 index 0000000..a675e22 --- /dev/null +++ b/src/imports/designeffects/shaders/dropShadow.frag @@ -0,0 +1,19 @@ +#version 440 +layout(location = 0) in vec2 qt_TexCoord0; +layout(location = 0) out vec4 fragColor; +layout(std140, binding = 0) uniform buf { + mat4 qt_Matrix; + float qt_Opacity; + + vec4 color; +}; +layout(binding = 1) uniform sampler2D src; + +void main() { + vec4 p = texture(src, qt_TexCoord0); + + if (p.a == 0) // Otherwise background is affected + discard; + + fragColor = color * qt_Opacity; +} diff --git a/src/imports/designeffects/shaders/dropShadowClip.frag b/src/imports/designeffects/shaders/dropShadowClip.frag new file mode 100644 index 0000000..7efce6f --- /dev/null +++ b/src/imports/designeffects/shaders/dropShadowClip.frag @@ -0,0 +1,19 @@ +#version 440 +layout(location = 0) in vec2 qt_TexCoord0; +layout(location = 0) out vec4 fragColor; +layout(std140, binding = 0) uniform buf { + mat4 qt_Matrix; + float qt_Opacity; +}; +layout(binding = 1) uniform sampler2D shadow; +layout(binding = 2) uniform sampler2D original; + +void main() { + vec4 o = texture(original, qt_TexCoord0); // original + + if (o.a != 0.0) + discard; + + vec4 s = texture(shadow, qt_TexCoord0); // shadow + fragColor = s; +} diff --git a/src/imports/designeffects/shaders/gaussianBlur.frag b/src/imports/designeffects/shaders/gaussianBlur.frag new file mode 100644 index 0000000..0926676 --- /dev/null +++ b/src/imports/designeffects/shaders/gaussianBlur.frag @@ -0,0 +1,52 @@ +#version 440 +layout(location = 0) in vec2 qt_TexCoord0; +layout(location = 0) out vec4 fragColor; +layout(std140, binding = 0) uniform buf { + mat4 qt_Matrix; + float qt_Opacity; + + float blurKernel; + float sigma; + vec2 pixelSize; + int useOffscreenColor; // bool + vec4 offscreenColor; +}; +layout(binding = 1) uniform sampler2D src; + +const float PI = 3.14159265359; +const float sqrtDoublePI = sqrt(2.0 * PI); + +vec4 gaussianBlur(sampler2D tex, int miplevel) { + vec4 col = vec4(0.0); + + float sum = 0; + + float k = ceil(blurKernel); + + // Normalize kernel weights + for (float i = -k; i <= k; ++i) { + sum += exp(-0.5 * pow(i / sigma, 2.0)) / (sqrtDoublePI * sigma); + } + + for (float i = -k; i <= k; ++i) { + vec2 coord = qt_TexCoord0 + (pixelSize * float(i)); + float weight = exp(-0.5 * pow(i / sigma, 2.0)) / (sqrtDoublePI * sigma); + + if (useOffscreenColor != 0 + && (coord.x > 1.0 || coord.y > 1.0 + || coord.x < 0.0 || coord.y < 0.0)) { + col += offscreenColor * weight / sum; + } else { + col += texture(tex, coord) * weight / sum; + } + } + + return col; +} + +void main() { + vec4 p = (blurKernel > 0) ? gaussianBlur(src, 0) + : texture(src, qt_TexCoord0); + + fragColor = p * qt_Opacity; +} diff --git a/src/imports/designeffects/shaders/innerShadow.frag b/src/imports/designeffects/shaders/innerShadow.frag new file mode 100644 index 0000000..22c8f06 --- /dev/null +++ b/src/imports/designeffects/shaders/innerShadow.frag @@ -0,0 +1,19 @@ +#version 440 +layout(location = 0) in vec2 qt_TexCoord0; +layout(location = 0) out vec4 fragColor; +layout(std140, binding = 0) uniform buf { + mat4 qt_Matrix; + float qt_Opacity; + + vec4 color; +}; +layout(binding = 1) uniform sampler2D src; + +void main() { + vec4 p = texture(src, qt_TexCoord0); + + if (p.a != 0) + discard; + + fragColor = color * qt_Opacity; +} diff --git a/src/imports/designeffects/shaders/innerShadowClip.frag b/src/imports/designeffects/shaders/innerShadowClip.frag new file mode 100644 index 0000000..8f97a11 --- /dev/null +++ b/src/imports/designeffects/shaders/innerShadowClip.frag @@ -0,0 +1,27 @@ +#version 440 +layout(location = 0) in vec2 qt_TexCoord0; +layout(location = 0) out vec4 fragColor; +layout(std140, binding = 0) uniform buf { + mat4 qt_Matrix; + float qt_Opacity; +}; +layout(binding = 1) uniform sampler2D shadow; +layout(binding = 2) uniform sampler2D original; + +void main() { + vec4 o = texture(original, qt_TexCoord0); // original + + if (o.a == 0) + discard; + + vec4 s = texture(shadow, qt_TexCoord0); // shadow + + if (s.a == 0) + discard; + + fragColor = s; + //} else { + // fragColor.rgb = mix(s.rgb, o.rgb, (1.0 - s.a)); + // fragColor.a = s.a + (o.a * (1.0 - s.a)); + //} +} diff --git a/src/imports/designeffects/shaders/opacityMask.frag b/src/imports/designeffects/shaders/opacityMask.frag new file mode 100644 index 0000000..f453666 --- /dev/null +++ b/src/imports/designeffects/shaders/opacityMask.frag @@ -0,0 +1,27 @@ +#version 440 + +layout(location = 0) in vec2 qt_TexCoord0; +layout(location = 0) out vec4 fragColor; + +layout(std140, binding = 0) uniform buf { + // qt_Matrix and qt_Opacity must always be both present + // if the built-in vertex shader is used. + mat4 qt_Matrix; + float qt_Opacity; +}; + +layout(binding = 1) uniform sampler2D source; +layout(binding = 2) uniform sampler2D maskSource; + +void main() +{ + if (texture(maskSource, qt_TexCoord0.st).a == 0.0) { + fragColor = vec4(0); + } else { + fragColor = texture(source, qt_TexCoord0.st); + } + + //fragColor = texture(source, qt_TexCoord0.st) + // * (texture(maskSource, qt_TexCoord0.st).a) + // * qt_Opacity; +} |