diff --git a/modules/gui/qt/Makefile.am b/modules/gui/qt/Makefile.am index 54cb9c458ef661a29c629d67a231dc8d05077e16..daabd5e5508ec58959662bf2abce9148d22e4724 100644 --- a/modules/gui/qt/Makefile.am +++ b/modules/gui/qt/Makefile.am @@ -1301,7 +1301,8 @@ libqml_module_widgets_a_QML = \ widgets/qml/ProgressIndicator.qml \ widgets/qml/RectangularGlow.qml \ widgets/qml/ImageExt.qml \ - widgets/qml/ScrollBarExt.qml + widgets/qml/ScrollBarExt.qml \ + widgets/qml/FastBlend.qml if HAVE_QT65 libqml_module_widgets_a_QML += \ widgets/qml/DynamicShadow.qml \ @@ -1351,12 +1352,15 @@ libqt_plugin_la_SHADER := shaders/FadingEdge.frag \ shaders/Noise.frag \ shaders/RectFilter.frag \ shaders/SubTexture.vert \ - shaders/PlayerBlurredBackground.frag \ shaders/HollowRectangularGlow.frag \ shaders/RectangularGlow.frag \ shaders/SDFAARoundedTexture.frag \ shaders/SDFAARoundedTexture_cropsupport_bordersupport.frag \ - shaders/DitheredTexture.frag + shaders/DitheredTexture.frag \ + shaders/FastBlend.frag \ + shaders/FastBlend_additive.frag \ + shaders/FastBlend_multiply.frag \ + shaders/FastBlend_screen.frag if ENABLE_QT libqt_plugin_la_LIBADD += libqml_module_dialogs.a \ diff --git a/modules/gui/qt/meson.build b/modules/gui/qt/meson.build index 6a86ffb73b37efdc022ad410b349e95aba7c4c54..6c6b3dbac48f32607561789852faf539a3c5e59c 100644 --- a/modules/gui/qt/meson.build +++ b/modules/gui/qt/meson.build @@ -901,6 +901,7 @@ qml_modules += { qml_blureffect_file, 'widgets/qml/ImageExt.qml', 'widgets/qml/ScrollBarExt.qml', + 'widgets/qml/FastBlend.qml', ), } diff --git a/modules/gui/qt/player/qml/Player.qml b/modules/gui/qt/player/qml/Player.qml index e7067d26bb5263f544409e79c87bc7aff9b0ffaf..3cf92f2c26d16a37eae7b8cdef065d877bbc45b9 100644 --- a/modules/gui/qt/player/qml/Player.qml +++ b/modules/gui/qt/player/qml/Player.qml @@ -311,16 +311,13 @@ FocusScope { source: cover } - layer.enabled: true - layer.samplerName: "backgroundSource" - layer.effect: ShaderEffect { - readonly property color screenColor: bgtheme.bg.primary.alpha(.55) - readonly property color overlayColor: Qt.tint(bgtheme.fg.primary, bgtheme.bg.primary).alpha(0.4) + Widgets.FastBlend { + anchors.fill: parent - blending: false - cullMode: ShaderEffect.BackFaceCulling + color: Qt.rgba(0.5, 0.5, 0.5, 1.0) - fragmentShader: "qrc:///shaders/PlayerBlurredBackground.frag.qsb" + mode: bgtheme.palette.isDark ? Widgets.FastBlend.Mode.Multiply // multiply makes darker + : Widgets.FastBlend.Mode.Screen // screen (inverse multiply) makes lighter } } } diff --git a/modules/gui/qt/shaders/FastBlend.frag b/modules/gui/qt/shaders/FastBlend.frag new file mode 100644 index 0000000000000000000000000000000000000000..9139549edc4dab78bda0a1434f1711427eab33a5 --- /dev/null +++ b/modules/gui/qt/shaders/FastBlend.frag @@ -0,0 +1,45 @@ +#version 440 + +/***************************************************************************** + * Copyright (C) 2025 VLC authors and VideoLAN + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * ( at your option ) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +layout(location = 0) out vec4 fragColor; // premultiplied + +layout(std140, binding = 0) uniform buf { + mat4 qt_Matrix; + float qt_Opacity; +#if defined(MULTIPLY) || defined(SCREEN) + float grayscale; +#else + vec4 color; // premultiplied +#endif +}; + +void main() { +#if defined(ADDITIVE) + fragColor = color * qt_Opacity; + fragColor.a = 0.0; +#elif defined(MULTIPLY) + fragColor.rgb = vec3(0.0, 0.0, 0.0); + fragColor.a = (1.0 - (grayscale * qt_Opacity)); +#elif defined(SCREEN) + fragColor = vec4(grayscale, grayscale, grayscale, grayscale) * qt_Opacity; +#else + fragColor = color * qt_Opacity; +#endif +} diff --git a/modules/gui/qt/shaders/FastBlend_additive.frag b/modules/gui/qt/shaders/FastBlend_additive.frag new file mode 100644 index 0000000000000000000000000000000000000000..365a304fae7b5dd17e58823e80d4adc2491ccf87 --- /dev/null +++ b/modules/gui/qt/shaders/FastBlend_additive.frag @@ -0,0 +1,49 @@ +#version 440 + +// WARNING: This file must be in sync with FastBlend.frag +// TODO: Generate this shader at build time. +#define ADDITIVE + +/***************************************************************************** + * Copyright (C) 2025 VLC authors and VideoLAN + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * ( at your option ) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +layout(location = 0) out vec4 fragColor; // premultiplied + +layout(std140, binding = 0) uniform buf { + mat4 qt_Matrix; + float qt_Opacity; +#if defined(MULTIPLY) || defined(SCREEN) + float grayscale; +#else + vec4 color; // premultiplied +#endif +}; + +void main() { +#if defined(ADDITIVE) + fragColor = color * qt_Opacity; + fragColor.a = 0.0; +#elif defined(MULTIPLY) + fragColor.rgb = vec3(0.0, 0.0, 0.0); + fragColor.a = (1.0 - (grayscale * qt_Opacity)); +#elif defined(SCREEN) + fragColor = vec4(grayscale, grayscale, grayscale, grayscale) * qt_Opacity; +#else + fragColor = color * qt_Opacity; +#endif +} diff --git a/modules/gui/qt/shaders/FastBlend_multiply.frag b/modules/gui/qt/shaders/FastBlend_multiply.frag new file mode 100644 index 0000000000000000000000000000000000000000..40af5aea59f862508d24b8db288d54c0717b11cc --- /dev/null +++ b/modules/gui/qt/shaders/FastBlend_multiply.frag @@ -0,0 +1,49 @@ +#version 440 + +// WARNING: This file must be in sync with FastBlend.frag +// TODO: Generate this shader at build time. +#define MULTIPLY + +/***************************************************************************** + * Copyright (C) 2025 VLC authors and VideoLAN + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * ( at your option ) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +layout(location = 0) out vec4 fragColor; // premultiplied + +layout(std140, binding = 0) uniform buf { + mat4 qt_Matrix; + float qt_Opacity; +#if defined(MULTIPLY) || defined(SCREEN) + float grayscale; +#else + vec4 color; // premultiplied +#endif +}; + +void main() { +#if defined(ADDITIVE) + fragColor = color * qt_Opacity; + fragColor.a = 0.0; +#elif defined(MULTIPLY) + fragColor.rgb = vec3(0.0, 0.0, 0.0); + fragColor.a = (1.0 - (grayscale * qt_Opacity)); +#elif defined(SCREEN) + fragColor = vec4(grayscale, grayscale, grayscale, grayscale) * qt_Opacity; +#else + fragColor = color * qt_Opacity; +#endif +} diff --git a/modules/gui/qt/shaders/FastBlend_screen.frag b/modules/gui/qt/shaders/FastBlend_screen.frag new file mode 100644 index 0000000000000000000000000000000000000000..86edb25563bbbe1763babd57123701c4a04b51b6 --- /dev/null +++ b/modules/gui/qt/shaders/FastBlend_screen.frag @@ -0,0 +1,49 @@ +#version 440 + +// WARNING: This file must be in sync with FastBlend.frag +// TODO: Generate this shader at build time. +#define SCREEN + +/***************************************************************************** + * Copyright (C) 2025 VLC authors and VideoLAN + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * ( at your option ) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +layout(location = 0) out vec4 fragColor; // premultiplied + +layout(std140, binding = 0) uniform buf { + mat4 qt_Matrix; + float qt_Opacity; +#if defined(MULTIPLY) || defined(SCREEN) + float grayscale; +#else + vec4 color; // premultiplied +#endif +}; + +void main() { +#if defined(ADDITIVE) + fragColor = color * qt_Opacity; + fragColor.a = 0.0; +#elif defined(MULTIPLY) + fragColor.rgb = vec3(0.0, 0.0, 0.0); + fragColor.a = (1.0 - (grayscale * qt_Opacity)); +#elif defined(SCREEN) + fragColor = vec4(grayscale, grayscale, grayscale, grayscale) * qt_Opacity; +#else + fragColor = color * qt_Opacity; +#endif +} diff --git a/modules/gui/qt/shaders/PlayerBlurredBackground.frag b/modules/gui/qt/shaders/PlayerBlurredBackground.frag deleted file mode 100644 index 28074150ee98b918a5fc5be0c0d07c1055eba9e1..0000000000000000000000000000000000000000 --- a/modules/gui/qt/shaders/PlayerBlurredBackground.frag +++ /dev/null @@ -1,85 +0,0 @@ -#version 440 -/***************************************************************************** - * Copyright (C) 2024 VLC authors and VideoLAN - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * ( at your option ) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. - *****************************************************************************/ - -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the Qt Graphical Effects module. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#extension GL_GOOGLE_include_directive : enable - -#include "Common.glsl" - -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 screenColor; - vec4 overlayColor; -}; -layout(binding = 1) uniform sampler2D backgroundSource; - -void main() { - lowp vec4 result = vec4(0.0); - lowp vec4 colorP = fromPremult(texture(backgroundSource, qt_TexCoord0)); - lowp vec4 screenColorP = fromPremult(screenColor); - lowp vec4 overlayColorP = fromPremult(overlayColor); - - result = screen(colorP, screenColorP); - result = multiply(result, screenColorP); - result = normal(result, overlayColorP); - - fragColor = vec4(result.rgb, 1.0) * qt_Opacity; -} diff --git a/modules/gui/qt/shaders/meson.build b/modules/gui/qt/shaders/meson.build index 069230e334c725a44914d42801083646025332f2..446ba7758a4e8500624a92777fef63b565381056 100644 --- a/modules/gui/qt/shaders/meson.build +++ b/modules/gui/qt/shaders/meson.build @@ -13,12 +13,15 @@ shader_sources = [ 'Noise.frag', 'RectFilter.frag', 'SubTexture.vert', - 'PlayerBlurredBackground.frag', 'HollowRectangularGlow.frag', 'RectangularGlow.frag', 'SDFAARoundedTexture.frag', 'SDFAARoundedTexture_cropsupport_bordersupport.frag', - 'DitheredTexture.frag' + 'DitheredTexture.frag', + 'FastBlend.frag', + 'FastBlend_additive.frag', + 'FastBlend_multiply.frag', + 'FastBlend_screen.frag' ] shader_files = files(shader_sources) diff --git a/modules/gui/qt/shaders/shaders.qrc b/modules/gui/qt/shaders/shaders.qrc index c7c8a99c4edc2f5e143911f1b9234c9f84f0f9ee..769c728acd73d5438146581b2d7c0f21dc318cff 100644 --- a/modules/gui/qt/shaders/shaders.qrc +++ b/modules/gui/qt/shaders/shaders.qrc @@ -7,11 +7,14 @@ <file alias="Noise.frag.qsb">Noise.frag.qsb</file> <file alias="RectFilter.frag.qsb">RectFilter.frag.qsb</file> <file alias="SubTexture.vert.qsb">SubTexture.vert.qsb</file> - <file alias="PlayerBlurredBackground.frag.qsb">PlayerBlurredBackground.frag.qsb</file> <file alias="HollowRectangularGlow.frag.qsb">HollowRectangularGlow.frag.qsb</file> <file alias="RectangularGlow.frag.qsb">RectangularGlow.frag.qsb</file> <file alias="SDFAARoundedTexture.frag.qsb">SDFAARoundedTexture.frag.qsb</file> <file alias="SDFAARoundedTexture_cropsupport_bordersupport.frag.qsb">SDFAARoundedTexture_cropsupport_bordersupport.frag.qsb</file> <file alias="DitheredTexture.frag.qsb">DitheredTexture.frag.qsb</file> + <file alias="FastBlend.frag.qsb">FastBlend.frag.qsb</file> + <file alias="FastBlend_additive.frag.qsb">FastBlend_additive.frag.qsb</file> + <file alias="FastBlend_multiply.frag.qsb">FastBlend_multiply.frag.qsb</file> + <file alias="FastBlend_screen.frag.qsb">FastBlend_screen.frag.qsb</file> </qresource> </RCC> diff --git a/modules/gui/qt/widgets/qml/FastBlend.qml b/modules/gui/qt/widgets/qml/FastBlend.qml new file mode 100644 index 0000000000000000000000000000000000000000..f1baf9056dbeb9d254b4629bc00ba884745193d3 --- /dev/null +++ b/modules/gui/qt/widgets/qml/FastBlend.qml @@ -0,0 +1,65 @@ +/***************************************************************************** + * Copyright (C) 2025 VLC authors and VideoLAN + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * ( at your option ) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ +import QtQuick + +// Premultiplied color allows making use of various blending modes (with restrictions) +// without adjusting the pipeline state. This is not magic, but simple mathematics. +// For this to work, the graphics pipeline blend mode is assumed to be source-over +// which is the default mode of Qt Scene Graph. +ShaderEffect { + id: root + + enum Mode { + // default (S + D * (1 - S.a)), no restriction: + SourceOver, + // Additive (S + D), no restriction: + Additive, + // Multiply (S * D), only grayscale: + Multiply, + // Screen / Inverse Multiply (S + D - S * D), only grayscale: + Screen + } + + property int mode: FastBlend.Mode.SourceOver + + property color color + readonly property real grayscale: { + if (mode !== FastBlend.Mode.Multiply && mode !== FastBlend.Mode.Screen) + return 0.0 // no need to calculate + // Color to gray, `qGray()` algorithm: + return (root.color.r * 11 + root.color.g * 16 + root.color.b * 5) / 32 + } + + blending: true + supportsAtlasTextures: true // irrelevant for now. Do we want to support texture here? It should be trivial. + + z: 99 // this is assumed to be the source, which means that it needs to be on top of the destination + + fragmentShader: { + switch (mode) { + case FastBlend.Mode.Additive: + return "qrc:///shaders/FastBlend_additive.frag.qsb" + case FastBlend.Mode.Multiply: + return "qrc:///shaders/FastBlend_multiply.frag.qsb" + case FastBlend.Mode.Screen: + return "qrc:///shaders/FastBlend_screen.frag.qsb" + default: + return "qrc:///shaders/FastBlend.frag.qsb" + } + } +}