From 284347912953d212fdc21a88fe9d8aa60b4c90d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kaj=20Gr=C3=B6nholm?= Date: Fri, 3 Feb 2023 15:11:37 +0200 Subject: [PATCH] Add custom MultiEffect into testbed example MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add CustomMultiEffect QQEM project and the effect exported from it into the MultiEffect testbed example. This can be used to validate the QQEM MultiEffect node compatibility with the Qt Quick MultiEffect. Task-number: QTBUG-109740 Change-Id: I596bf54316ab70b85b1ace33a433a4109c43b239 Reviewed-by: Kaj Grönholm (cherry picked from commit 3b5aea849fc1c84ebf2c368735c967ab4302d599) Reviewed-by: Qt Cherry-pick Bot --- .../quick/multieffect/testbed/CMakeLists.txt | 6 + examples/quick/multieffect/testbed/qml.qrc | 6 + .../qml/CustomMultiEffect/BlurHelper.qml | 66 +++ .../CustomMultiEffect/CustomMultiEffect.qep | 377 ++++++++++++++++++ .../CustomMultiEffect/CustomMultiEffect.qml | 98 +++++ .../qml/CustomMultiEffect/bluritems.frag.qsb | Bin 0 -> 1563 bytes .../qml/CustomMultiEffect/bluritems.vert.qsb | Bin 0 -> 1934 bytes .../custommultieffect.frag.qsb | Bin 0 -> 6495 bytes .../custommultieffect.vert.qsb | Bin 0 -> 5369 bytes .../multieffect/testbed/qml/Settings.qml | 1 + .../multieffect/testbed/qml/SettingsView.qml | 7 + .../multieffect/testbed/qml/WarningsView.qml | 7 + .../quick/multieffect/testbed/qml/main.qml | 37 +- 13 files changed, 602 insertions(+), 3 deletions(-) create mode 100644 examples/quick/multieffect/testbed/qml/CustomMultiEffect/BlurHelper.qml create mode 100644 examples/quick/multieffect/testbed/qml/CustomMultiEffect/CustomMultiEffect.qep create mode 100644 examples/quick/multieffect/testbed/qml/CustomMultiEffect/CustomMultiEffect.qml create mode 100644 examples/quick/multieffect/testbed/qml/CustomMultiEffect/bluritems.frag.qsb create mode 100644 examples/quick/multieffect/testbed/qml/CustomMultiEffect/bluritems.vert.qsb create mode 100644 examples/quick/multieffect/testbed/qml/CustomMultiEffect/custommultieffect.frag.qsb create mode 100644 examples/quick/multieffect/testbed/qml/CustomMultiEffect/custommultieffect.vert.qsb diff --git a/examples/quick/multieffect/testbed/CMakeLists.txt b/examples/quick/multieffect/testbed/CMakeLists.txt index 71c448e1c6..3a21a0c1fd 100644 --- a/examples/quick/multieffect/testbed/CMakeLists.txt +++ b/examples/quick/multieffect/testbed/CMakeLists.txt @@ -49,6 +49,8 @@ qt_add_qml_module(testbedexample "qml/SettingsComponentSlider.qml" "qml/SettingsComponentCheckBox.qml" "qml/SettingsComponentColorSelector.qml" + "qml/CustomMultiEffect/BlurHelper.qml" + "qml/CustomMultiEffect/CustomMultiEffect.qml" RESOURCES "qml/images/pause.png" "qml/images/play.png" @@ -57,6 +59,10 @@ qt_add_qml_module(testbedexample "qml/images/arrow.png" "qml/images/Built_with_Qt.png" "qml/images/Built_with_Qt_RGB_logo.png" + "qml/CustomMultiEffect/bluritems.frag.qsb" + "qml/CustomMultiEffect/bluritems.vert.qsb" + "qml/CustomMultiEffect/custommultieffect.frag.qsb" + "qml/CustomMultiEffect/custommultieffect.vert.qsb" ) install(TARGETS testbedexample diff --git a/examples/quick/multieffect/testbed/qml.qrc b/examples/quick/multieffect/testbed/qml.qrc index ab21cc8b0f..4c23cff873 100644 --- a/examples/quick/multieffect/testbed/qml.qrc +++ b/examples/quick/multieffect/testbed/qml.qrc @@ -21,5 +21,11 @@ qml/images/arrow.png qml/images/Built_with_Qt.png qml/images/Built_with_Qt_RGB_logo.png + qml/CustomMultiEffect/BlurHelper.qml + qml/CustomMultiEffect/bluritems.frag.qsb + qml/CustomMultiEffect/bluritems.vert.qsb + qml/CustomMultiEffect/custommultieffect.frag.qsb + qml/CustomMultiEffect/CustomMultiEffect.qml + qml/CustomMultiEffect/custommultieffect.vert.qsb diff --git a/examples/quick/multieffect/testbed/qml/CustomMultiEffect/BlurHelper.qml b/examples/quick/multieffect/testbed/qml/CustomMultiEffect/BlurHelper.qml new file mode 100644 index 0000000000..8e7622e557 --- /dev/null +++ b/examples/quick/multieffect/testbed/qml/CustomMultiEffect/BlurHelper.qml @@ -0,0 +1,66 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: BSD-3-Clause + +import QtQuick + +Item { + id: rootItem + property alias blurSrc1: blurredItemSource1 + property alias blurSrc2: blurredItemSource2 + property alias blurSrc3: blurredItemSource3 + property alias blurSrc4: blurredItemSource4 + property alias blurSrc5: blurredItemSource5 + + component BlurItem : ShaderEffect { + property vector2d offset: Qt.vector2d((1.0 + rootItem.blurMultiplier) / width, + (1.0 + rootItem.blurMultiplier) / height) + visible: false + layer.enabled: true + layer.smooth: true + vertexShader: "bluritems.vert.qsb" + fragmentShader: "bluritems.frag.qsb" + } + + QtObject { + id: priv + property bool useBlurItem1: true + property bool useBlurItem2: rootItem.blurMax > 2 + property bool useBlurItem3: rootItem.blurMax > 8 + property bool useBlurItem4: rootItem.blurMax > 16 + property bool useBlurItem5: rootItem.blurMax > 32 + } + + BlurItem { + id: blurredItemSource1 + property var src: priv.useBlurItem1 ? source : null + // Size of the first blurred item is by default half of the source. + // Increase for quality and decrease for performance & more blur. + readonly property int blurItemSize: 8 + width: src ? Math.ceil(src.width / 16) * blurItemSize : 0 + height: src ? Math.ceil(src.height / 16) * blurItemSize : 0 + } + BlurItem { + id: blurredItemSource2 + property var src: priv.useBlurItem2 ? blurredItemSource1 : null + width: blurredItemSource1.width / 2 + height: blurredItemSource1.height / 2 + } + BlurItem { + id: blurredItemSource3 + property var src: priv.useBlurItem3 ? blurredItemSource2 : null + width: blurredItemSource2.width / 2 + height: blurredItemSource2.height / 2 + } + BlurItem { + id: blurredItemSource4 + property var src: priv.useBlurItem4 ? blurredItemSource3 : null + width: blurredItemSource3.width / 2 + height: blurredItemSource3.height / 2 + } + BlurItem { + id: blurredItemSource5 + property var src: priv.useBlurItem5 ? blurredItemSource4 : null + width: blurredItemSource4.width / 2 + height: blurredItemSource4.height / 2 + } +} diff --git a/examples/quick/multieffect/testbed/qml/CustomMultiEffect/CustomMultiEffect.qep b/examples/quick/multieffect/testbed/qml/CustomMultiEffect/CustomMultiEffect.qep new file mode 100644 index 0000000000..9f22c176a2 --- /dev/null +++ b/examples/quick/multieffect/testbed/qml/CustomMultiEffect/CustomMultiEffect.qep @@ -0,0 +1,377 @@ +{ + "QEP": { + "QQEM": "0.42", + "connections": [ + { + "fromId": 2, + "toId": 1 + }, + { + "fromId": 0, + "toId": 3 + }, + { + "fromId": 3, + "toId": 2 + } + ], + "exportDirectory": ".", + "exportFlags": 11, + "exportName": "CustomMultiEffect", + "nodes": [ + { + "fragmentCode": [ + "void main() {", + " fragColor = texture(iSource, texCoord);", + " @nodes", + " fragColor = fragColor * qt_Opacity;", + "}" + ], + "name": "Main", + "nodeId": 0, + "type": 0, + "vertexCode": [ + "void main() {", + " texCoord = qt_MultiTexCoord0;", + " fragCoord = qt_Vertex.xy;", + " vec2 vertCoord = qt_Vertex.xy;", + " @nodes", + " gl_Position = qt_Matrix * vec4(vertCoord, 0.0, 1.0);", + "}" + ], + "x": 140, + "y": 35.25 + }, + { + "name": "Output", + "nodeId": 1, + "type": 1, + "x": 140, + "y": 589.75 + }, + { + "description": "This node matches to features and API of the Qt Quick MultiEffect element. This makes it easy to build customized MultiEffects.", + "fragmentCode": [ + "{", + " vec4 color = fragColor;", + "#if (BLUR_ENABLED)", + " // blur", + " vec4 blurredColor = texture(iSource, texCoord) * blurWeight1[0];", + " blurredColor += texture(iSourceBlur1, texCoord) * blurWeight1[1];", + "#if (BLUR_HELPER_MAX_LEVEL > 2)", + " blurredColor += texture(iSourceBlur2, texCoord) * blurWeight1[2];", + "#endif", + "#if (BLUR_HELPER_MAX_LEVEL > 8)", + " blurredColor += texture(iSourceBlur3, texCoord) * blurWeight1[3];", + "#endif", + "#if (BLUR_HELPER_MAX_LEVEL > 16)", + " blurredColor += texture(iSourceBlur4, texCoord) * blurWeight2[0];", + "#endif", + "#if (BLUR_HELPER_MAX_LEVEL > 32)", + " blurredColor += texture(iSourceBlur5, texCoord) * blurWeight2[1];", + "#endif", + " color = blurredColor;", + "#endif", + " // contrast, brightness, saturation and colorization", + " color.rgb = (color.rgb - 0.5 * color.a) * (1.0 + contrast) + 0.5 * color.a;", + " color.rgb += brightness * color.a;", + " float gray = dot(color.rgb, vec3(0.299, 0.587, 0.114));", + " float colorizationAlpha = colorization * colorizationColor.a;", + " color.rgb = mix(color.rgb, gray * colorizationColor.rgb, colorizationAlpha);", + " color.rgb = mix(vec3(gray), color.rgb, 1.0 + saturation);", + "#if (SHADOW_ENABLED)", + " // shadow", + " float shadow = texture(iSource, shadowTexCoord).a * shadowBlurWeight1[0];", + " shadow += texture(iSourceBlur1, shadowTexCoord).a * shadowBlurWeight1[1];", + "#if (BLUR_HELPER_MAX_LEVEL > 2)", + " shadow += texture(iSourceBlur2, shadowTexCoord).a * shadowBlurWeight1[2];", + "#endif", + "#if (BLUR_HELPER_MAX_LEVEL > 8)", + " shadow += texture(iSourceBlur3, shadowTexCoord).a * shadowBlurWeight1[3];", + "#endif", + "#if (BLUR_HELPER_MAX_LEVEL > 16)", + " shadow += texture(iSourceBlur4, shadowTexCoord).a * shadowBlurWeight2[0];", + "#endif", + "#if (BLUR_HELPER_MAX_LEVEL > 32)", + " shadow += texture(iSourceBlur5, shadowTexCoord).a * shadowBlurWeight2[1];", + "#endif", + " shadow *= (shadowColor.a * shadowOpacity);", + " float saa = (1.0 - color.a) * (1.0 - shadow);", + " color.rgb = mix(shadowColor.rgb * shadow, color.rgb, color.a + saa);", + " color.a = 1.0 - saa;", + "#endif", + "#if (MASK_ENABLED)", + " // mask", + " vec4 maskTexture = texture(maskSource, texCoord);", + " float alphaMask = maskTexture.a;", + " const float mSLow = 1.0 + maskSpreadAtMin;", + " const float mSUp = 1.0 + maskSpreadAtMax;", + " float m1 = smoothstep(maskThresholdMin * mSLow - (mSLow - 0.999), maskThresholdMin * mSLow, alphaMask);", + " float m2 = smoothstep((1.0 - maskThresholdMax) * mSUp - (mSUp - 0.999), (1.0 - maskThresholdMax) * mSUp, (1.0 - alphaMask));", + " float mm = m1 * m2;", + " color *= (1.0 - float(maskInverted)) * mm + float(maskInverted) * (1.0 - mm);", + "#endif", + " fragColor = color;", + "}" + ], + "name": "MultiEffect", + "nodeId": 2, + "properties": [ + { + "defaultValue": "0", + "maxValue": "1", + "minValue": "-1", + "name": "contrast", + "type": "float", + "value": "0" + }, + { + "defaultValue": "0", + "maxValue": "1", + "minValue": "-1", + "name": "brightness", + "type": "float", + "value": "0" + }, + { + "defaultValue": "0", + "maxValue": "1", + "minValue": "-1", + "name": "saturation", + "type": "float", + "value": "0" + }, + { + "defaultValue": "0", + "maxValue": "1", + "minValue": "0", + "name": "colorization", + "type": "float", + "value": "0" + }, + { + "defaultValue": "1, 0, 0, 1", + "name": "colorizationColor", + "type": "color", + "value": "1, 0, 0, 1" + }, + { + "defaultValue": "1", + "description": "Enables the blur effect.", + "name": "BLUR_ENABLED", + "type": "define", + "value": "1" + }, + { + "defaultValue": "32", + "maxValue": "64", + "minValue": "0", + "name": "blurMax", + "type": "int", + "value": "32" + }, + { + "defaultValue": "0", + "maxValue": "1", + "minValue": "0", + "name": "blur", + "type": "float", + "value": "0" + }, + { + "defaultValue": "0", + "description": "Enables the shadow effect.", + "name": "SHADOW_ENABLED", + "type": "define", + "value": "1" + }, + { + "defaultValue": "1", + "description": "This property defines how much blur (radius) is applied to the shadow.\n\nThe value ranges from 0.0 (no blur) to 1.0 (full blur). By default, the property is set to \\c 0.0 (no change). The amount of full blur is affected by blurHelperBlurMultiplier.", + "maxValue": "1", + "minValue": "0", + "name": "shadowBlur", + "type": "float", + "value": "1" + }, + { + "defaultValue": "1", + "maxValue": "1", + "minValue": "0", + "name": "shadowOpacity", + "type": "float", + "value": "1" + }, + { + "defaultValue": "0, 0, 0, 1", + "name": "shadowColor", + "type": "color", + "value": "0, 0, 0, 1" + }, + { + "defaultValue": "1", + "maxValue": "1.2", + "minValue": "0.8", + "name": "shadowScale", + "type": "float", + "value": "1" + }, + { + "defaultValue": "0", + "maxValue": "30", + "minValue": "-30", + "name": "shadowHorizontalOffset", + "type": "float", + "value": "0" + }, + { + "defaultValue": "0", + "maxValue": "30", + "minValue": "-30", + "name": "shadowVerticalOffset", + "type": "float", + "value": "0" + }, + { + "defaultValue": "1", + "description": "Enables the mask effect.", + "name": "MASK_ENABLED", + "type": "define", + "value": "1" + }, + { + "defaultValue": "", + "description": "Source item for the mask effect. By default the alpha channel of the source item is used for masking but this can be easily adjusted in the shader.", + "name": "maskSource", + "type": "image", + "value": "" + }, + { + "defaultValue": "0", + "description": "This property defines a lower threshold value for the mask pixels. The mask pixels that have an alpha value below this property are used to completely mask away the corresponding pixels from the source item. The mask pixels that have a higher alpha value are used to alphablend the source item to the display.\n\nThe value ranges from 0.0 (alpha value 0) to 1.0 (alpha value 255). By default, the property is set to 0.0.", + "maxValue": "1", + "minValue": "0", + "name": "maskThresholdMin", + "type": "float", + "value": "0" + }, + { + "defaultValue": "0", + "description": "This property defines the smoothness of the mask edges near the maskThresholdMin. Setting higher spread values softens the transition from the transparent mask pixels towards opaque mask pixels by adding interpolated values between them.\n\nThe value ranges from 0.0 (sharp mask edge) to 1.0 (smooth mask edge). By default, the property is set to 0.0.", + "maxValue": "1", + "minValue": "0", + "name": "maskSpreadAtMin", + "type": "float", + "value": "0" + }, + { + "defaultValue": "1", + "description": "This property defines an upper threshold value for the mask pixels. The mask pixels that have an alpha value below this property are used to completely mask away the corresponding pixels from the source item. The mask pixels that have a higher alpha value are used to alphablend the source item to the display.\n\nThe value ranges from 0.0 (alpha value 0) to 1.0 (alpha value 255). By default, the property is set to 1.0.", + "maxValue": "1", + "minValue": "0", + "name": "maskThresholdMax", + "type": "float", + "value": "1" + }, + { + "defaultValue": "0", + "description": "This property defines the smoothness of the mask edges near the maskThresholdMax. Using higher spread values softens the transition from the transparent mask pixels towards opaque mask pixels by adding interpolated values between them.\n\nThe value ranges from 0.0 (sharp mask edge) to 1.0 (smooth mask edge). By default, the property is set to 0.0.", + "maxValue": "1", + "minValue": "0", + "name": "maskSpreadAtMax", + "type": "float", + "value": "0" + }, + { + "defaultValue": "false", + "description": "This property switches the mask to the opposite side; instead of masking away the content outside maskThresholdMin and maskThresholdMax, content between them will get masked away.\n\nBy default, the property is set to false.", + "name": "maskInverted", + "type": "bool", + "value": "false" + } + ], + "type": 2, + "vertexCode": [ + "out vec4 blurWeight1;", + "out vec2 blurWeight2;", + "out vec2 shadowTexCoord;", + "out vec4 shadowBlurWeight1;", + "out vec2 shadowBlurWeight2;", + "", + "float blurWeight(float v) {", + " return max(0.0, min(1.0, 1.0 - v * 2.0));", + "}", + "", + "@main", + "{", + "#if (BLUR_ENABLED)", + " float blurLod = sqrt(blur * (blurMax / 64.0)) * 1.2 - 0.2;", + " float bw1 = blurWeight(abs(blurLod - 0.1));", + " float bw2 = blurWeight(abs(blurLod - 0.3));", + " float bw3 = blurWeight(abs(blurLod - 0.5));", + " float bw4 = blurWeight(abs(blurLod - 0.7));", + " float bw5 = blurWeight(abs(blurLod - 0.9));", + " float bw6 = blurWeight(abs(blurLod - 1.1));", + "", + " float bsum = bw1 + bw2 + bw3 + bw4 + bw5 + bw6;", + " blurWeight1 = vec4(bw1 / bsum, bw2 / bsum, bw3 / bsum, bw4 / bsum);", + " blurWeight2 = vec2(bw5 / bsum, bw6 / bsum);", + "#endif", + "#if (SHADOW_ENABLED)", + " float shadowBlurLod = sqrt(shadowBlur * (blurMax / 64.0)) * 1.2 - 0.2;", + " float sbw1 = blurWeight(abs(shadowBlurLod - 0.1));", + " float sbw2 = blurWeight(abs(shadowBlurLod - 0.3));", + " float sbw3 = blurWeight(abs(shadowBlurLod - 0.5));", + " float sbw4 = blurWeight(abs(shadowBlurLod - 0.7));", + " float sbw5 = blurWeight(abs(shadowBlurLod - 0.9));", + " float sbw6 = blurWeight(abs(shadowBlurLod - 1.1));", + "", + " float sbsum = sbw1 + sbw2 + sbw3 + sbw4 + sbw5 + sbw6;", + " shadowBlurWeight1 = vec4(sbw1 / sbsum, sbw2 / sbsum, sbw3 / sbsum, sbw4 / sbsum);", + " shadowBlurWeight2 = vec2(sbw5 / sbsum, sbw6 / sbsum);", + "", + " vec2 shadowOffset = vec2(shadowHorizontalOffset / iResolution.x, shadowVerticalOffset / iResolution.y);", + " float invertedScale = 1.0 / shadowScale;", + " float s = (1.0 - invertedScale) * 0.5;", + " vec2 shadowCenterOffset = vec2(s);", + " shadowTexCoord = qt_MultiTexCoord0 - shadowOffset;", + " shadowTexCoord = (shadowTexCoord * invertedScale) + shadowCenterOffset;", + "#endif", + "}" + ], + "x": 105, + "y": 327.5 + }, + { + "description": "This node is required e.g. for FastBlur and DropShadow. It generates blurred iSourceBlur[1..6] samplers to be available for shaders.", + "fragmentCode": [ + "@blursources" + ], + "name": "BlurHelper", + "nodeId": 3, + "properties": [ + { + "defaultValue": "64", + "description": "This property defines the maximum pixel radius that blur with value 1.0 will reach.\n\nMeaningful range of this value is from 2 (subtle blur) to 64 (high blur). By default, the property is set to 32. For the most optimal performance, select as small value as you need.\n\nNote: This affects to both blur and shadow effects.", + "name": "BLUR_HELPER_MAX_LEVEL", + "type": "define", + "value": "64" + }, + { + "defaultValue": "0", + "description": "This property defines a multiplier for extending the blur radius.\n\nThe value ranges from 0.0 (not multiplied) to inf. By default, the property is set to 0.0. Incresing the multiplier extends the blur radius, but decreases the blur quality. This is more performant option for a bigger blur radius than BLUR_HELPER_MAX_LEVEL as it doesn't increase the amount of texture lookups.\n\nNote: This affects to both blur and shadow effects.", + "maxValue": "2", + "minValue": "0", + "name": "blurMultiplier", + "type": "float", + "value": "0" + } + ], + "type": 2, + "x": 105, + "y": 188.875 + } + ], + "version": 1 + } +} diff --git a/examples/quick/multieffect/testbed/qml/CustomMultiEffect/CustomMultiEffect.qml b/examples/quick/multieffect/testbed/qml/CustomMultiEffect/CustomMultiEffect.qml new file mode 100644 index 0000000000..66b77fe880 --- /dev/null +++ b/examples/quick/multieffect/testbed/qml/CustomMultiEffect/CustomMultiEffect.qml @@ -0,0 +1,98 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// Created with Qt Quick Effect Maker (version 0.42), Fri Feb 3 15:59:56 2023 + +import QtQuick + +Item { + id: rootItem + + // This is the main source for the effect + property var source: null + + property real contrast: 0 + property real brightness: 0 + property real saturation: 0 + property real colorization: 0 + property color colorizationColor: Qt.rgba(1, 0, 0, 1) + property int blurMax: 32 + property real blur: 0 + // This property defines how much blur (radius) is applied to the shadow. + // + // The value ranges from 0.0 (no blur) to 1.0 (full blur). By default, the property is set to \c 0.0 (no change). The amount of full blur is affected by blurHelperBlurMultiplier. + property real shadowBlur: 1 + property real shadowOpacity: 1 + property color shadowColor: Qt.rgba(0, 0, 0, 1) + property real shadowScale: 1 + property real shadowHorizontalOffset: 0 + property real shadowVerticalOffset: 0 + // Source item for the mask effect. By default the alpha channel of the source item is used for masking but this can be easily adjusted in the shader. + property var maskSource: null + // This property defines a lower threshold value for the mask pixels. The mask pixels that have an alpha value below this property are used to completely mask away the corresponding pixels from the source item. The mask pixels that have a higher alpha value are used to alphablend the source item to the display. + // + // The value ranges from 0.0 (alpha value 0) to 1.0 (alpha value 255). By default, the property is set to 0.0. + property real maskThresholdMin: 0 + // This property defines the smoothness of the mask edges near the maskThresholdMin. Setting higher spread values softens the transition from the transparent mask pixels towards opaque mask pixels by adding interpolated values between them. + // + // The value ranges from 0.0 (sharp mask edge) to 1.0 (smooth mask edge). By default, the property is set to 0.0. + property real maskSpreadAtMin: 0 + // This property defines an upper threshold value for the mask pixels. The mask pixels that have an alpha value below this property are used to completely mask away the corresponding pixels from the source item. The mask pixels that have a higher alpha value are used to alphablend the source item to the display. + // + // The value ranges from 0.0 (alpha value 0) to 1.0 (alpha value 255). By default, the property is set to 1.0. + property real maskThresholdMax: 1 + // This property defines the smoothness of the mask edges near the maskThresholdMax. Using higher spread values softens the transition from the transparent mask pixels towards opaque mask pixels by adding interpolated values between them. + // + // The value ranges from 0.0 (sharp mask edge) to 1.0 (smooth mask edge). By default, the property is set to 0.0. + property real maskSpreadAtMax: 0 + // This property switches the mask to the opposite side; instead of masking away the content outside maskThresholdMin and maskThresholdMax, content between them will get masked away. + // + // By default, the property is set to false. + property bool maskInverted: false + // This property defines a multiplier for extending the blur radius. + // + // The value ranges from 0.0 (not multiplied) to inf. By default, the property is set to 0.0. Incresing the multiplier extends the blur radius, but decreases the blur quality. This is more performant option for a bigger blur radius than BLUR_HELPER_MAX_LEVEL as it doesn't increase the amount of texture lookups. + // + // Note: This affects to both blur and shadow effects. + property real blurMultiplier: 0 + + BlurHelper { + id: blurHelper + anchors.fill: parent + property int blurMax: 64 + property real blurMultiplier: rootItem.blurMultiplier + } + ShaderEffect { + readonly property var iSource: source + readonly property vector3d iResolution: Qt.vector3d(width, height, 1.0) + readonly property var iSourceBlur1: blurHelper.blurSrc1 + readonly property var iSourceBlur2: blurHelper.blurSrc2 + readonly property var iSourceBlur3: blurHelper.blurSrc3 + readonly property var iSourceBlur4: blurHelper.blurSrc4 + readonly property var iSourceBlur5: blurHelper.blurSrc5 + readonly property real contrast: parent.contrast + readonly property real brightness: parent.brightness + readonly property real saturation: parent.saturation + readonly property real colorization: parent.colorization + readonly property color colorizationColor: parent.colorizationColor + readonly property int blurMax: parent.blurMax + readonly property real blur: parent.blur + readonly property real shadowBlur: parent.shadowBlur + readonly property real shadowOpacity: parent.shadowOpacity + readonly property color shadowColor: parent.shadowColor + readonly property real shadowScale: parent.shadowScale + readonly property real shadowHorizontalOffset: parent.shadowHorizontalOffset + readonly property real shadowVerticalOffset: parent.shadowVerticalOffset + readonly property var maskSource: parent.maskSource + readonly property real maskThresholdMin: parent.maskThresholdMin + readonly property real maskSpreadAtMin: parent.maskSpreadAtMin + readonly property real maskThresholdMax: parent.maskThresholdMax + readonly property real maskSpreadAtMax: parent.maskSpreadAtMax + readonly property bool maskInverted: parent.maskInverted + readonly property real blurMultiplier: parent.blurMultiplier + + vertexShader: 'custommultieffect.vert.qsb' + fragmentShader: 'custommultieffect.frag.qsb' + width: parent.width + height: parent.height + } +} diff --git a/examples/quick/multieffect/testbed/qml/CustomMultiEffect/bluritems.frag.qsb b/examples/quick/multieffect/testbed/qml/CustomMultiEffect/bluritems.frag.qsb new file mode 100644 index 0000000000000000000000000000000000000000..054745d2772cc20a02b5594dfbd5533d68920e4f GIT binary patch literal 1563 zcmV+$2ITnw02g3*ob6cMj?+dIo+P_07$`qw`7aD?yG?eNU^}6z2w71r6;Oc|v4~b$ ztt_wO1hJ^cPE*1<~wK3-*{%i z7(2iio1=D;#mr@UY=Z^x7qSl1fm?>g{-lRxpGgUWBuX<&m?Tl2S)xG_l>&)UBGm<~ z&3cJkKamt(7Wslw2iPtffh6`U)=Xx4q+lwE=I3CM#U6t87_@p|8)JtFaa%zjmz`52 zvdM-ZV#_4o(ASoru6J2YOX}uUo+= z&gkXc(C%#n{UGGq+YWwL_1ezP6iLrl1x+5idmBLzc61ONMQ&K_2aYdfr=ScKWfY-I z6=fEo8Y-$WwiZ1ZxW3}4=vAft9R9brquB1bK<(_TaVPMB^pkKebE}D(q1$yu6xMLc zcoe#3=T>6FI*&pJFvKQMWDkaYH^i|m=wT#@l>llKx6~|bZT{+YOHD)+j6%m1sc7?R zwF>I$LQhp{UIQ2E>@ZQI&ISj9L{J#5O>p>k}zxCz~EG+g;vL}hk zItG$*FPC$ifVK$jJhaQumKp9#l>~#Y`n%9O3g1_0K1p98ok*jOOUn2n(Q^De(Pg49 z6I~&CS&_d7bdCD+2|Yiim&TZsx4^_cDtRTs-Bx&I;@wN;tBiR#_iE0!Rq_p=cL7yS z=t^QM_D@3(&qN&B8NlQG@{Gjc^B~!T?HoJAG#bxuv68+3`tWR^k-Xeik=G*nDDiN< zW2CQ4_~T6GR^X97K|Dz>=IHY|`ZCe0Kpz;#`P+5W{{iJ`f$&H#6Ynh0RhAwJE>XOz zWaBZiafR%yk&cyQF2P#_dX3^KZ9w`O(I{UhEb9CS<|woDNQ8F$2Ap})jqyK3HnoAq zIKD$MY*YMBQj9(asf8rZ0&`N4IhB(+O>vYmX9$D*?}&Gnbe&Q3oXhFCK=GG)E)wQ~ zqUSx*by3lCDW`{1-lU!@gyD*wtE3CZejwia#K#=3L-Y<2uT8c5A;s?#z+>F6Q?9NN z26LbjU+OeSPA8qq#QOs>__3&T`A$RPdp3SbKaDp!qpAN>JJ;X2XT3e!6RtutP37Fr zjyOCf0lx{Sot_uPa0=YjrPE>XwKcH61l{9fJxpPZ(+g5C)mI{kRY$L>`6eV8lY=&E znX@ut#ZVqs|E4CY;|ZN@?})Q)Qygv;vtyioOH|5MY{xFQ0Ab1Fu7ZpZ38f2%6ldao z&?*=(iU!Q0fkt*9TVb?twjSMo{M6bAgDA4@`@`&YN4>S&kWdj^bhQwd^L}dTKqf!=4-KLPfHc5MX5%)PDB}k;ST-zufYA zBPzW~>^%68? z$C|_$lQ?4<&YZ-V({PPR-2ay}teMZ01I@4L&AsuTIMBT1`IUDK<>yZE`!&lw^iyi? NRJ1Ra{RQs+m;$!}4JZHr literal 0 HcmV?d00001 diff --git a/examples/quick/multieffect/testbed/qml/CustomMultiEffect/bluritems.vert.qsb b/examples/quick/multieffect/testbed/qml/CustomMultiEffect/bluritems.vert.qsb new file mode 100644 index 0000000000000000000000000000000000000000..6aa0eb1d4edc4b0c3b38cef201e55123f8ba339d GIT binary patch literal 1934 zcmV;92XXiS03$_sob6g)Q`<%mKQJUzkmeux)3i;LkS2Bj$C3;oI8X?rA#Dg02u zjj?5-)>uTADKx`Fr_;B->0vN+chPsm8lZP=cX)kB#y^(Nt z)BAu)3ZQ-Y1_hBy1&Nf_M52>D-$^v*s2JCN6Or1AX!Om9hFcM7t%=&FZnRD6wkrCv zRWoTSAha>j0V~Xh14+VliW7p?>wv$odR`5g)ncVyGUPkkhEu5MHK$ZDH>V|EUo*E$ zH}O~c`7|tynpxeFtA%Z&1`EiLU8`Kg;&UfmcCW()=xT#YkUV1G86Bp+=vP>Uaz0nq})Wp+E*NrROx^;wj#vvM^+z^RBcPzq721$ zx(%t;0cl6+ZC90$v~^|j9P{FYHn!o^?JAn`V%6(TG5XH`-LF=OmyPfItde=(U-*$< zxbI>Y5Q&f48iavr1W=6vY7xN0O~m0Q;+@( zKfI9O=lzGP57V%_NN2}zX|oUAR}ybOf1LItxI5t-11^TKB;4b0&%?b2_fzx*NNm4I zTqu7?GWuHp`7E{dx^izr9->cChwm3B1YH;5W&~X~=@wkRL_>$%=NZ1l`mvyVS&)-L zY$%VD%FLO#|TaeMN&6vrUhqx_U~*uFu&2Hlu? zU0ePpj{3hr(@VBE?tL`=)6m*SV?PTB9EVdhb~rv$G&fF@eikIm( zH6aIM#G9oYBuU5lZ$;!_g7lpKc1#W?sg29IrX1WMOiqY#iu~UZVwjG|!RMssG0eo| zV3yi=j_;D~tdN5_;=M~b7$+U)zZa2%FG$b%@5kg|f!cWg9+2&V;Qz~r{(VJy?*Cy- z{}!o@%a59JyhNBsLJZ5~e@V!}*A&|_4&zM*99uv+oTLD=?5>+WHFNx z*BK#ctu;Zjsla&^V!ftjT>7V!w<=+Y!{jeUoT23cO@T!kY=~Hu(UwYFgBsVM<(lFX zS0xpv)#V2(>-wB+)oS{K>Q3;qQasw5l-KS(nOj;~na{L2)$@@`;P6nSTBkP5a73Pn`Uo}VG}K3Eu_vLE^6I)UrU$&7;Tfg=mNl&$E^2hf6q&S=7iwq!0tCcy`=KD&_y7O^ literal 0 HcmV?d00001 diff --git a/examples/quick/multieffect/testbed/qml/CustomMultiEffect/custommultieffect.frag.qsb b/examples/quick/multieffect/testbed/qml/CustomMultiEffect/custommultieffect.frag.qsb new file mode 100644 index 0000000000000000000000000000000000000000..370cf501dc6f9effc54a78fe3d4b9981680f862e GIT binary patch literal 6495 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 B@-6@X literal 0 HcmV?d00001 diff --git a/examples/quick/multieffect/testbed/qml/CustomMultiEffect/custommultieffect.vert.qsb b/examples/quick/multieffect/testbed/qml/CustomMultiEffect/custommultieffect.vert.qsb new file mode 100644 index 0000000000000000000000000000000000000000..c522c25aee0b23da56c471dd5c86877e8d2f4649 GIT binary patch literal 5369 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
Gn&_c9L#T!)qDpe zW`2XT&!C;HwX?N$w${$p+Syt=TWe=)o>=W{t(~p?-^|wNpO8tq+1kas#`&(hTxy8T z_gcP@o2b}4a(pb|?Ct9N@>7Mk)f=*1)3a%co_C|K<TAtT}To2a_9)DY4WaX&p^s(2D~(}K3{l(I+d zCLhG(s{6IYX~?6}lXWj1kmh*_S~l(trlsFrg(uziI`gF0UaNp~+G`t-J_ixBbQxz} zJGO1!zB9kEQMc{<=Gt_ti51nX2XEw&H@s!z@bLB<6Iyo!w2{WNWJd&q8g4a>SZ*(b zhH}$uPYA8JdLH3&k{&O5j^J`q9*%JfbZKc%IR6gFWo7~#ZU_vW_4w59p=)D1c4~w6 zuG2o{C_Bd-WpS%5YX===&q)VOzWg~o?m*4Y@W3PQJMhRGNmu!VSy%aFX;=9y&pgWK z|Iot>adxL3rnmEQ>|q8uALkxsjPr5uVaB*XCm(VRI2UIha`v6o=|?`~^y9ow#4|ep z@pjqyJpu8>Iu}PEKBwpL48#}fJRXAhVx7-Z5MQwWkz)|d<8yuxf?0jiqY%%o!#OU1 z^$F6IHD-c~Z=2;~Dc{aL#&`S^rF@01)h5ng<#jr(KV)a;Zu@<7mDUhV+cZibpsdoo zE(A2NhV*+|$djY_xY~}?&DeY_eOIdGXFi_y&g;p`PQdjp^vM8j(0UM%zn#=B8DJM4 zPaCghs;8v|-p;s7KXmJ2p8rB?(VJIJ^TwMh#Kx=n`I%1Uj#+A5Y}ub(RMr6q4Si`B~5bsBD0riyFmul~vF zqR|$)h}8;HC7b8*rI@f93V18SIoms>H#5d33yoNQDw&@y6i2P(Zi&)KL8+L>aoz=c zaRbTS1$uSElb*I;-cTcnMtXuqf5^GR=xEC>HRy$dTB2|F?vS$wjb(XR-tz~Imo&ZU z>xsronLJ}|Yt*zE+P~~*>`buZ4Z+38n!fc#dUSLemqhZlZlxA>fHXQ@^Z)uqm3l~4b}mk7f_$ILlbTb0ab+~o+I>2&u8dl(-522Hl@lG+ zs5bI7<;aQmHi5@Bf!6T=^fZod>b?UC^PQH!;~e!?i?*w*%Y2&>?ryQG!)q?at1ixe X56kd>avNE>Pp>eOCqDlTHOPXi4a(S0 literal 0 HcmV?d00001 diff --git a/examples/quick/multieffect/testbed/qml/Settings.qml b/examples/quick/multieffect/testbed/qml/Settings.qml index 6139bdc6f4..05947c2ab4 100644 --- a/examples/quick/multieffect/testbed/qml/Settings.qml +++ b/examples/quick/multieffect/testbed/qml/Settings.qml @@ -18,6 +18,7 @@ QtObject { property bool animateMovement: true property bool showShader: false property bool showItemSize: false + property bool showCustomMultiEffect: false property bool autoPaddingEnabled: true property rect paddingRect: Qt.rect(0, 0, 0, 0) diff --git a/examples/quick/multieffect/testbed/qml/SettingsView.qml b/examples/quick/multieffect/testbed/qml/SettingsView.qml index 9b753298dd..789f7685c2 100644 --- a/examples/quick/multieffect/testbed/qml/SettingsView.qml +++ b/examples/quick/multieffect/testbed/qml/SettingsView.qml @@ -125,6 +125,13 @@ Item { settings.autoPaddingEnabled = checked; } } + SettingsComponentCheckBox { + text: "Show Custom MultiEffect" + checked: settings.showCustomMultiEffect + onToggled: { + settings.showCustomMultiEffect = checked; + } + } Item { width: 1 height: 20 * dp diff --git a/examples/quick/multieffect/testbed/qml/WarningsView.qml b/examples/quick/multieffect/testbed/qml/WarningsView.qml index 880a75b885..c25ad5fccd 100644 --- a/examples/quick/multieffect/testbed/qml/WarningsView.qml +++ b/examples/quick/multieffect/testbed/qml/WarningsView.qml @@ -28,4 +28,11 @@ Item { anchors.leftMargin: 16 text: qsTr("Item resized!") } + WarningsItem { + id: customEffectWarning + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + text: qsTr("Note: Custom MultiEffect doesn't support all the MultiEffect features, like paddings and disabling effects.") + opacity: settings.showCustomMultiEffect + } } diff --git a/examples/quick/multieffect/testbed/qml/main.qml b/examples/quick/multieffect/testbed/qml/main.qml index 6d34a47bfa..8fc761169e 100644 --- a/examples/quick/multieffect/testbed/qml/main.qml +++ b/examples/quick/multieffect/testbed/qml/main.qml @@ -4,6 +4,7 @@ import QtQuick import QtQuick.Window import QtQuick.Effects +import "CustomMultiEffect" Rectangle { id: mainWindow @@ -77,9 +78,9 @@ Rectangle { MultiEffect { id: quickMultiEffect anchors.fill: testSourceItem + visible: !settings.showCustomMultiEffect source: testSourceItem maskSource: testMaskItem - autoPaddingEnabled: settings.autoPaddingEnabled paddingRect: settings.paddingRect brightness: settings.brightnessEnabled ? settings.brightness : 0 @@ -106,12 +107,42 @@ Rectangle { maskSpreadAtMax: settings.maskSpreadAtMax onItemSizeChanged: { - warningsView.showSizeWarning(); + if (visible) + warningsView.showSizeWarning(); } onShaderChanged: { - warningsView.showShaderWarning(); + if (visible) + warningsView.showShaderWarning(); } } + + // For comparison, custom effect created with QQEM and the MultiEffect node. + CustomMultiEffect { + id: customMultiEffect + anchors.fill: testSourceItem + visible: settings.showCustomMultiEffect + source: testSourceItem + maskSource: testMaskItem + brightness: settings.brightnessEnabled ? settings.brightness : 0 + contrast: settings.contrastEnabled ? settings.contrast : 0 + saturation: settings.saturationEnabled ? settings.saturation : 0 + colorizationColor: settings.colorizationColor + colorization: settings.colorizationEnabled ? settings.colorization : 0 + blur: settings.blur + blurMax: settings.blurMax + blurMultiplier: settings.blurMultiplier + shadowOpacity: settings.shadowOpacity + shadowBlur: settings.shadowBlur + shadowHorizontalOffset: settings.shadowHorizontalOffset + shadowVerticalOffset: settings.shadowVerticalOffset + shadowColor: settings.shadowColor + shadowScale: settings.shadowScale + maskInverted: settings.maskInverted + maskThresholdMin: settings.maskThresholdMin + maskSpreadAtMin: settings.maskSpreadAtMin + maskThresholdMax: settings.maskThresholdMax + maskSpreadAtMax: settings.maskSpreadAtMax + } } SettingsView {