diff --git a/.github/workflows/arch-linux.yml b/.github/workflows/arch-linux.yml index b83973770..10e8df95c 100644 --- a/.github/workflows/arch-linux.yml +++ b/.github/workflows/arch-linux.yml @@ -2,12 +2,13 @@ name: Arch Linux on: push: - branches: [ "main", "develop" ] + branches: [ main ] + paths-ignore: [ "**.md" ] pull_request: - branches: [ "main", "develop" ] - -env: - BUILD_TYPE: Release + branches: [ main ] + paths-ignore: [ "**.md" ] + schedule: + - cron: "0 0 * * *" jobs: build: @@ -17,17 +18,17 @@ jobs: options: --user root steps: + - name: Check out repository + uses: actions/checkout@v4 + - name: Refresh Packages run: pacman -Syu --noconfirm - name: Install Dependencies - run: pacman -S --noconfirm base-devel git extra-cmake-modules qt6-tools kwin - - - name: Check out repository - uses: actions/checkout@v4 + run: pacman -S --needed --noconfirm base-devel git extra-cmake-modules qt6-tools kwin - name: Configure CMake - run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + run: cmake -B ${{github.workspace}}/build - name: Build - run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} + run: cmake --build ${{github.workspace}}/build -j diff --git a/.github/workflows/fedora-40.yml b/.github/workflows/fedora-40.yml new file mode 100644 index 000000000..a6088af26 --- /dev/null +++ b/.github/workflows/fedora-40.yml @@ -0,0 +1,31 @@ +name: Fedora 40 + +on: + push: + branches: [ main ] + paths-ignore: [ "**.md" ] + pull_request: + branches: [ main ] + paths-ignore: [ "**.md" ] + schedule: + - cron: "0 0 * * *" + +jobs: + build: + runs-on: ubuntu-latest + container: + image: fedora:40 + options: --user root + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Install Dependencies + run: dnf -y install git cmake extra-cmake-modules gcc-g++ kf6-kwindowsystem-devel plasma-workspace-devel libplasma-devel qt6-qtbase-private-devel qt6-qtbase-devel cmake kwin-devel extra-cmake-modules kwin-devel kf6-knotifications-devel kf6-kio-devel kf6-kcrash-devel kf6-ki18n-devel kf6-kguiaddons-devel libepoxy-devel kf6-kglobalaccel-devel kf6-kcmutils-devel kf6-kconfigwidgets-devel kf6-kdeclarative-devel kdecoration-devel kf6-kglobalaccel kf6-kdeclarative libplasma kf6-kio qt6-qtbase kf6-kguiaddons kf6-ki18n wayland-devel + + - name: Configure CMake + run: cmake -B ${{github.workspace}}/build + + - name: Build + run: cmake --build ${{github.workspace}}/build -j diff --git a/.github/workflows/fedora-41.yml b/.github/workflows/fedora-41.yml new file mode 100644 index 000000000..05b9b49cc --- /dev/null +++ b/.github/workflows/fedora-41.yml @@ -0,0 +1,31 @@ +name: Fedora 41 + +on: + push: + branches: [ main ] + paths-ignore: [ "**.md" ] + pull_request: + branches: [ main ] + paths-ignore: [ "**.md" ] + schedule: + - cron: "0 0 * * *" + +jobs: + build: + runs-on: ubuntu-latest + container: + image: fedora:41 + options: --user root + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Install Dependencies + run: dnf -y install git cmake extra-cmake-modules gcc-g++ kf6-kwindowsystem-devel plasma-workspace-devel libplasma-devel qt6-qtbase-private-devel qt6-qtbase-devel cmake kwin-devel extra-cmake-modules kwin-devel kf6-knotifications-devel kf6-kio-devel kf6-kcrash-devel kf6-ki18n-devel kf6-kguiaddons-devel libepoxy-devel kf6-kglobalaccel-devel kf6-kcmutils-devel kf6-kconfigwidgets-devel kf6-kdeclarative-devel kdecoration-devel kf6-kglobalaccel kf6-kdeclarative libplasma kf6-kio qt6-qtbase kf6-kguiaddons kf6-ki18n wayland-devel + + - name: Configure CMake + run: cmake -B ${{github.workspace}}/build + + - name: Build + run: cmake --build ${{github.workspace}}/build -j diff --git a/.github/workflows/neon-unstable.yml b/.github/workflows/neon-unstable.yml new file mode 100644 index 000000000..a26cf18eb --- /dev/null +++ b/.github/workflows/neon-unstable.yml @@ -0,0 +1,34 @@ +name: KDE Neon (unstable) + +on: + push: + branches: [ main ] + paths-ignore: [ "**.md" ] + pull_request: + branches: [ main ] + paths-ignore: [ "**.md" ] + schedule: + - cron: "0 0 * * *" + +jobs: + build: + runs-on: ubuntu-latest + container: + image: invent-registry.kde.org/neon/docker-images/plasma:unstable + options: --user root + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Refresh Packages + run: apt update + + - name: Install Dependencies + run: apt install -y git cmake g++ extra-cmake-modules qt6-tools-dev kwin-dev libkf6configwidgets-dev gettext libkf6crash-dev libkf6globalaccel-dev libkf6kio-dev libkf6service-dev libkf6notifications-dev libkf6kcmutils-dev libkdecorations3-dev + + - name: Configure CMake + run: cmake -B ${{github.workspace}}/build + + - name: Build + run: cmake --build ${{github.workspace}}/build -j diff --git a/.github/workflows/nixos-plasma-6-0-5.yml b/.github/workflows/nixos-plasma-6-0-5.yml new file mode 100644 index 000000000..d9a848726 --- /dev/null +++ b/.github/workflows/nixos-plasma-6-0-5.yml @@ -0,0 +1,32 @@ +name: NixOS (Plasma 6.0.5) + +on: + push: + branches: [ main ] + paths-ignore: [ "**.md" ] + pull_request: + branches: [ main ] + paths-ignore: [ "**.md" ] + schedule: + - cron: "0 0 * * *" + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Install Nix + uses: cachix/install-nix-action@v27 + with: + nix_path: nixpkgs=channel:e8c38b73aeb218e27163376a2d617e61a2ad9b59 + + - name: Patch flake.nix + run: sed -i 's/nixos-unstable/e8c38b73aeb218e27163376a2d617e61a2ad9b59/g' flake.nix + + - name: Update flake.lock + run: nix flake update + + - name: Build + run: nix build diff --git a/.github/workflows/nixos-plasma-6-1-5.yml b/.github/workflows/nixos-plasma-6-1-5.yml new file mode 100644 index 000000000..ea6420dd6 --- /dev/null +++ b/.github/workflows/nixos-plasma-6-1-5.yml @@ -0,0 +1,32 @@ +name: NixOS (Plasma 6.1.5) + +on: + push: + branches: [ main ] + paths-ignore: [ "**.md" ] + pull_request: + branches: [ main ] + paths-ignore: [ "**.md" ] + schedule: + - cron: "0 0 * * *" + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Install Nix + uses: cachix/install-nix-action@v27 + with: + nix_path: nixpkgs=channel:33bca54e5e23eac33b668ebc71d576203f3ccf3b + + - name: Patch flake.nix + run: sed -i 's/nixos-unstable/33bca54e5e23eac33b668ebc71d576203f3ccf3b/g' flake.nix + + - name: Update flake.lock + run: nix flake update + + - name: Build + run: nix build diff --git a/.github/workflows/nixos-stable.yml b/.github/workflows/nixos-stable.yml deleted file mode 100644 index a38811bf3..000000000 --- a/.github/workflows/nixos-stable.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: NixOS (nixpkgs stable) - -on: - push: - branches: [ main ] - paths-ignore: [ '**.md', '**.png' ] - pull_request: - branches: [ main ] - paths: [ '**', '!**/**.md', '!**/**.png', '!**/**.yml', '**/nixos-stable.yml' ] - schedule: - - cron: "0 0 * * *" - -env: - BUILD_TYPE: Release - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: cachix/install-nix-action@v27 - with: - nix_path: nixpkgs=channel:nixos-24.05 - - run: sed -i 's/nixos-unstable/nixos-24.05/g' flake.nix - - run: nix flake update - - run: nix build diff --git a/.github/workflows/nixos-unstable.yml b/.github/workflows/nixos-unstable.yml index 0d2ab7123..61fbde6e8 100644 --- a/.github/workflows/nixos-unstable.yml +++ b/.github/workflows/nixos-unstable.yml @@ -3,23 +3,26 @@ name: NixOS (nixpkgs unstable) on: push: branches: [ main ] - paths-ignore: [ '**.md', '**.png' ] + paths-ignore: [ "**.md" ] pull_request: branches: [ main ] - paths: [ '**', '!**/**.md', '!**/**.png', '!**/**.yml', '**/nixos-unstable.yml' ] + paths-ignore: [ "**.md" ] schedule: - cron: "0 0 * * *" -env: - BUILD_TYPE: Release - jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: cachix/install-nix-action@v27 - with: - nix_path: nixpkgs=channel:nixos-unstable - - run: nix flake update - - run: nix build + - name: Check out repository + uses: actions/checkout@v4 + + - name: Install Nix + uses: cachix/install-nix-action@v27 + with: + nix_path: nixpkgs=channel:nixos-unstable + - name: Update flake.lock + run: nix flake update + + - name: Build + run: nix build diff --git a/.github/workflows/opensuse-tumbleweed.yml b/.github/workflows/opensuse-tumbleweed.yml new file mode 100644 index 000000000..e9fd58afb --- /dev/null +++ b/.github/workflows/opensuse-tumbleweed.yml @@ -0,0 +1,34 @@ +name: openSUSE Tumbleweed + +on: + push: + branches: [ main ] + paths-ignore: [ "**.md" ] + pull_request: + branches: [ main ] + paths-ignore: [ "**.md" ] + schedule: + - cron: "0 0 * * *" + +jobs: + build: + runs-on: ubuntu-latest + container: + image: opensuse/tumbleweed + options: --user root + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Refresh Packages + run: zypper ref + + - name: Install Dependencies + run: zypper in -y git cmake-full gcc-c++ kf6-extra-cmake-modules kcoreaddons-devel kguiaddons-devel kconfigwidgets-devel kwindowsystem-devel ki18n-devel kiconthemes-devel kpackage-devel frameworkintegration-devel kcmutils-devel kirigami2-devel "cmake(KF6Config)" "cmake(KF6CoreAddons)" "cmake(KF6FrameworkIntegration)" "cmake(KF6GuiAddons)" "cmake(KF6I18n)" "cmake(KF6KCMUtils)" "cmake(KF6KirigamiPlatform)" "cmake(KF6WindowSystem)" "cmake(Qt6Core)" "cmake(Qt6DBus)" "cmake(Qt6Quick)" "cmake(Qt6Svg)" "cmake(Qt6Widgets)" "cmake(Qt6Xml)" "cmake(Qt6UiTools)" "cmake(KF6Crash)" "cmake(KF6GlobalAccel)" "cmake(KF6KIO)" "cmake(KF6Service)" "cmake(KF6Notifications)" libepoxy-devel kwin6-devel + + - name: Configure CMake + run: cmake -B ${{github.workspace}}/build + + - name: Build + run: cmake --build ${{github.workspace}}/build -j diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b23d566f..f000a3967 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,13 @@ set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) +set(CPACK_PACKAGE_NAME "kwin-better-blur") +set(CPACK_PACKAGING_INSTALL_PREFIX "/usr") +set(CPACK_PACKAGE_FILE_NAME "kwin-better-blur") +set(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}") +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Fork of the KWin Blur effect for KDE Plasma 6 with additional features (including force blur) and bug fixes") +include(CPack) + if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose Release or Debug" FORCE) endif() @@ -66,13 +73,22 @@ find_package(KWin REQUIRED COMPONENTS kwineffects ) -if(${KWin_VERSION} VERSION_LESS_EQUAL 6.0.5) - add_compile_definitions(KWIN_6_0) -elseif(${KWin_VERSION} VERSION_LESS_EQUAL 6.1.5) - add_compile_definitions(KWIN_6_1) +if(${KWin_VERSION} VERSION_GREATER_EQUAL 6.0.90) + add_compile_definitions(KWIN_6_1_OR_GREATER) +endif() +if(${KWin_VERSION} VERSION_GREATER_EQUAL 6.1.90) + add_compile_definitions(KWIN_6_2_OR_GREATER) +endif() + +find_package(KDecoration2) +find_package(KDecoration3) +if (${KDecoration3_FOUND}) + add_compile_definitions(KDECORATION3) +endif() +if(NOT ${KDecoration3_FOUND} AND NOT ${KDecoration2_FOUND}) + message(FATAL_ERROR "Could not find KDecoration2 or KDecoration3.") endif() -find_package(KDecoration2 REQUIRED) find_package(KWinDBusInterface CONFIG REQUIRED) diff --git a/README.md b/README.md index bad8506d6..8f8b69c08 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,9 @@ Fixes for blur-related Plasma bugs that haven't been patched yet. # Building from source +> [!NOTE] +> On Fedora Kinoite and other distributions based on it, the effect must be built in a container. + ### Dependencies - CMake - Extra CMake Modules @@ -74,7 +77,7 @@ Fixes for blur-related Plasma bugs that haven't been patched yet.
- Fedora + Fedora 40, 41
``` @@ -98,10 +101,29 @@ cd kwin-effects-forceblur mkdir build cd build cmake ../ -DCMAKE_INSTALL_PREFIX=/usr -make -sudo make install +make -j ``` +### Installation +
+ Fedora Kinoite +
+ + ```sh + cpack -V -G RPM + exit # exit the container + sudo rpm-ostree install kwin-effects-forceblur/build/kwin-better-blur.rpm + ``` +
+
+ Other distributions +
+ + ```sh + sudo make install + ``` +
+ Remove the *build* directory when rebuilding the effect. # Usage diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2ce5f865c..e659aa2a5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -28,8 +28,11 @@ target_link_libraries(forceblur PRIVATE KWin::kwin KF6::ConfigGui - - KDecoration2::KDecoration ) +if (${KDecoration3_FOUND}) + target_link_libraries(forceblur PRIVATE KDecoration3::KDecoration) +else() + target_link_libraries(forceblur PRIVATE KDecoration2::KDecoration) +endif() install(TARGETS forceblur DESTINATION ${KDE_INSTALL_PLUGINDIR}/kwin/effects/plugins) diff --git a/src/blur.cpp b/src/blur.cpp index b2afd92ef..b921c74b9 100644 --- a/src/blur.cpp +++ b/src/blur.cpp @@ -22,6 +22,12 @@ #include "wayland/display.h" #include "wayland/surface.h" +#ifdef KWIN_6_2_OR_GREATER +#include "scene/decorationitem.h" +#include "scene/surfaceitem.h" +#include "scene/windowitem.h" +#endif + #include #include #include @@ -35,7 +41,12 @@ #include #include +#ifdef KDECORATION3 +#include +#else #include +#endif + #include Q_LOGGING_CATEGORY(KWIN_BLUR, "kwin_better_blur", QtWarningMsg) @@ -69,6 +80,8 @@ BlurEffect::BlurEffect() m_downsamplePass.mvpMatrixLocation = m_downsamplePass.shader->uniformLocation("modelViewProjectionMatrix"); m_downsamplePass.offsetLocation = m_downsamplePass.shader->uniformLocation("offset"); m_downsamplePass.halfpixelLocation = m_downsamplePass.shader->uniformLocation("halfpixel"); + m_downsamplePass.transformColorsLocation = m_downsamplePass.shader->uniformLocation("transformColors"); + m_downsamplePass.colorMatrixLocation = m_downsamplePass.shader->uniformLocation("colorMatrix"); } m_upsamplePass.shader = ShaderManager::instance()->generateShaderFromFile(ShaderTrait::MapTexture, @@ -227,6 +240,7 @@ void BlurEffect::reconfigure(ReconfigureFlags flags) m_offset = blurStrengthValues[m_settings.general.blurStrength].offset; m_expandSize = blurOffsets[m_iterationCount - 1].expandSize; m_fakeBlurTextures.clear(); + m_colorMatrix = colorMatrix(m_settings.general.brightness, m_settings.general.saturation, m_settings.general.contrast); for (EffectWindow *w : effects->stackingOrder()) { updateBlurRegion(w); @@ -294,6 +308,9 @@ void BlurEffect::updateBlurRegion(EffectWindow *w, bool geometryChanged) BlurEffectData &data = m_windows[w]; data.content = content; data.frame = frame; +#ifdef KWIN_6_2_OR_GREATER + data.windowEffect = ItemEffect(w->windowItem()); +#endif } else if (!geometryChanged) { // Blur may disappear if this method is called when window geometry changes if (auto it = m_windows.find(w); it != m_windows.end()) { effects->makeOpenGLContextCurrent(); @@ -417,7 +434,13 @@ void BlurEffect::setupDecorationConnections(EffectWindow *w) return; } - connect(w->decoration(), &KDecoration2::Decoration::blurRegionChanged, this, [this, w]() { + connect(w->decoration(), +#ifdef KDECORATION3 + &KDecoration3::Decoration::blurRegionChanged +#else + &KDecoration2::Decoration::blurRegionChanged +#endif + , this, [this, w]() { updateBlurRegion(w); }); } @@ -443,10 +466,10 @@ bool BlurEffect::enabledByDefault() bool BlurEffect::supported() { -#ifdef KWIN_6_0 - return effects->isOpenGLCompositing() && GLFramebuffer::supported() && GLFramebuffer::blitSupported(); -#else +#ifdef KWIN_6_1_OR_GREATER return effects->openglContext() && (effects->openglContext()->supportsBlits() || effects->waylandDisplay()); +#else + return effects->isOpenGLCompositing() && GLFramebuffer::supported() && GLFramebuffer::blitSupported(); #endif } @@ -461,7 +484,12 @@ QRegion BlurEffect::decorationBlurRegion(const EffectWindow *w) const return QRegion(); } - QRegion decorationRegion = QRegion(w->decoration()->rect()) - w->contentsRect().toRect(); + QRect decorationRect = w->decoration()->rect() +#ifdef KDECORATION3 + .toAlignedRect() +#endif + ; + QRegion decorationRegion = QRegion(decorationRect) - w->contentsRect().toRect(); //! we return only blurred regions that belong to decoration region return decorationRegion.intersected(w->decoration()->blurRegion()); } @@ -801,6 +829,10 @@ void BlurEffect::blur(BlurRenderData &renderInfo, const RenderTarget &renderTarg GLTexture *fakeBlurTexture = nullptr; if (w && hasFakeBlur(w)) { fakeBlurTexture = ensureFakeBlurTexture(m_currentScreen, renderTarget); + if (fakeBlurTexture) { + renderInfo.textures.clear(); + renderInfo.framebuffers.clear(); + } } if (!fakeBlurTexture @@ -982,6 +1014,8 @@ void BlurEffect::blur(BlurRenderData &renderInfo, const RenderTarget &renderTarg m_downsamplePass.shader->setUniform(m_downsamplePass.mvpMatrixLocation, projectionMatrix); m_downsamplePass.shader->setUniform(m_downsamplePass.offsetLocation, float(m_offset)); + m_downsamplePass.shader->setUniform(m_downsamplePass.colorMatrixLocation, m_colorMatrix); + m_downsamplePass.shader->setUniform(m_downsamplePass.transformColorsLocation, true); for (size_t i = 1; i < renderInfo.framebuffers.size(); ++i) { const auto &read = renderInfo.framebuffers[i - 1]; @@ -995,6 +1029,10 @@ void BlurEffect::blur(BlurRenderData &renderInfo, const RenderTarget &renderTarg GLFramebuffer::pushFramebuffer(draw.get()); vbo->draw(GL_TRIANGLES, 0, 6); + + if (i == 1) { + m_downsamplePass.shader->setUniform(m_downsamplePass.transformColorsLocation, false); + } } ShaderManager::instance()->popShader(); @@ -1030,18 +1068,19 @@ void BlurEffect::blur(BlurRenderData &renderInfo, const RenderTarget &renderTarg const auto &read = renderInfo.framebuffers[1]; if (m_settings.general.noiseStrength > 0) { - if (const auto *noiseTexture = ensureNoiseTexture()) { + if (auto *noiseTexture = ensureNoiseTexture()) { m_upsamplePass.shader->setUniform(m_upsamplePass.noiseLocation, true); m_upsamplePass.shader->setUniform(m_upsamplePass.noiseTextureSizeLocation, QVector2D(noiseTexture->width(), noiseTexture->height())); + glUniform1i(m_upsamplePass.noiseTextureLocation, 1); glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, noiseTexture->texture()); + noiseTexture->bind(); } } glUniform1i(m_upsamplePass.textureLocation, 0); glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, read->colorAttachment()->texture()); + read->colorAttachment()->bind(); m_upsamplePass.shader->setUniform(m_upsamplePass.topCornerRadiusLocation, topCornerRadius); m_upsamplePass.shader->setUniform(m_upsamplePass.bottomCornerRadiusLocation, bottomCornerRadius); @@ -1103,10 +1142,10 @@ GLTexture *BlurEffect::wallpaper(EffectWindow *desktop, const qreal &scale, cons const RenderViewport renderViewport(desktop->frameGeometry(), scale, renderTarget); WindowPaintData data; -#ifdef KWIN_6_0 +#ifndef KWIN_6_1_OR_GREATER QMatrix4x4 projectionMatrix; - projectionMatrix.ortho(geometry); - data.setProjectionMatrix(projectionMatrix); + projectionMatrix.ortho(geometry); + data.setProjectionMatrix(projectionMatrix); #endif GLFramebuffer::pushFramebuffer(desktopFramebuffer.get()); @@ -1152,7 +1191,7 @@ GLTexture *BlurEffect::createFakeBlurTextureWayland(const Output *output, const auto *shader = ShaderManager::instance()->pushShader(ShaderTrait::MapTexture | ShaderTrait::TransformColorspace); shader->setColorspaceUniforms( ColorDescription::sRGB, renderTarget.colorDescription() -#if !(defined(KWIN_6_1) || defined(KWIN_6_0)) +#ifdef KWIN_6_2_OR_GREATER , RenderingIntent::RelativeColorimetricWithBPC #endif ); @@ -1228,6 +1267,38 @@ GLTexture *BlurEffect::createFakeBlurTextureX11(const GLenum &textureFormat) return compositeTexture.release(); } +QMatrix4x4 BlurEffect::colorMatrix(const float &brightness, const float &saturation, const float &contrast) const +{ + QMatrix4x4 saturationMatrix; + if (saturation != 1.0) { + const qreal r = (1.0 - saturation) * .2126; + const qreal g = (1.0 - saturation) * .7152; + const qreal b = (1.0 - saturation) * .0722; + + saturationMatrix = QMatrix4x4(r + saturation, r, r, 0.0, + g, g + saturation, g, 0.0, + b, b, b + saturation, 0.0, + 0, 0, 0, 1.0); + } + + QMatrix4x4 brightnessMatrix; + if (brightness != 1.0) { + brightnessMatrix.scale(brightness, brightness, brightness); + } + + QMatrix4x4 contrastMatrix; + if (contrast != 1.0) { + const float transl = (1.0 - contrast) / 2.0; + + contrastMatrix = QMatrix4x4(contrast, 0, 0, 0.0, + 0, contrast, 0, 0.0, + 0, 0, contrast, 0.0, + transl, transl, transl, 1.0); + } + + return contrastMatrix * saturationMatrix * brightnessMatrix; +} + bool BlurEffect::isActive() const { return m_valid && !effects->isScreenLocked(); diff --git a/src/blur.h b/src/blur.h index d84ed42a8..6f426b5cb 100644 --- a/src/blur.h +++ b/src/blur.h @@ -9,6 +9,10 @@ #include "effect/effect.h" #include "opengl/glutils.h" +#ifdef KWIN_6_2_OR_GREATER +#include "scene/item.h" +#endif + #include "settings.h" #include "window.h" @@ -16,6 +20,7 @@ #include + namespace KWin { @@ -40,6 +45,10 @@ struct BlurEffectData /// The render data per screen. Screens can have different color spaces. std::unordered_map render; +#ifdef KWIN_6_2_OR_GREATER + ItemEffect windowEffect; +#endif + bool hasWindowBehind; }; @@ -88,6 +97,7 @@ public Q_SLOTS: bool shouldForceBlur(const EffectWindow *w) const; void updateBlurRegion(EffectWindow *w, bool geometryChanged = false); bool hasFakeBlur(EffectWindow *w); + QMatrix4x4 colorMatrix(const float &brightness, const float &saturation, const float &contrast) const; /* * @param w The pointer to the window being blurred, nullptr if an image is being blurred. @@ -130,6 +140,8 @@ public Q_SLOTS: int mvpMatrixLocation; int offsetLocation; int halfpixelLocation; + int transformColorsLocation; + int colorMatrixLocation; } m_downsamplePass; struct @@ -149,8 +161,6 @@ public Q_SLOTS: int antialiasingLocation; int blurSizeLocation; int opacityLocation; - - } m_upsamplePass; struct @@ -206,6 +216,8 @@ public Q_SLOTS: // Windows to blur even when transformed. QList m_blurWhenTransformed; + QMatrix4x4 m_colorMatrix; + QMap windowBlurChangedConnections; QMap windowFrameGeometryChangedConnections; QMap screenChangedConnections; diff --git a/src/blur.kcfg b/src/blur.kcfg index 32512e873..9d62fa9d4 100644 --- a/src/blur.kcfg +++ b/src/blur.kcfg @@ -73,5 +73,14 @@ class3 true + + 1.0 + + + 1.0 + + + 1.0 + diff --git a/src/kcm/blur_config.ui b/src/kcm/blur_config.ui index cfd50e11a..00a1e1859 100644 --- a/src/kcm/blur_config.ui +++ b/src/kcm/blur_config.ui @@ -7,7 +7,7 @@ 0 0 480 - 200 + 240 @@ -179,6 +179,82 @@ + + + + Qt::Vertical + + + + 0 + 10 + + + + + + + + + + Brightness + + + + + + + 0.0 + + + 0.1 + + + + + + + + + + + Saturation + + + + + + + 0.0 + + + 0.1 + + + + + + + + + + + Contrast + + + + + + + 0.0 + + + 0.1 + + + + + diff --git a/src/settings.cpp b/src/settings.cpp index 88eb0db6d..a38307e44 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -11,6 +11,9 @@ void BlurSettings::read() general.blurStrength = BlurConfig::blurStrength() - 1; general.noiseStrength = BlurConfig::noiseStrength(); general.windowOpacityAffectsBlur = BlurConfig::transparentBlur(); + general.brightness = BlurConfig::brightness(); + general.saturation = BlurConfig::saturation(); + general.contrast = BlurConfig::contrast(); forceBlur.windowClasses = BlurConfig::windowClasses().split("\n"); forceBlur.windowClassMatchingMode = BlurConfig::blurMatching() ? WindowClassMatchingMode::Whitelist : WindowClassMatchingMode::Blacklist; diff --git a/src/settings.h b/src/settings.h index aa72cba94..7d97fd39e 100644 --- a/src/settings.h +++ b/src/settings.h @@ -24,6 +24,9 @@ struct GeneralSettings int blurStrength; int noiseStrength; bool windowOpacityAffectsBlur; + float brightness; + float saturation; + float contrast; }; struct ForceBlurSettings diff --git a/src/shaders/downsample.frag b/src/shaders/downsample.frag index d83b7133b..2e0c81028 100644 --- a/src/shaders/downsample.frag +++ b/src/shaders/downsample.frag @@ -2,6 +2,9 @@ uniform sampler2D texUnit; uniform float offset; uniform vec2 halfpixel; +uniform bool transformColors; +uniform mat4 colorMatrix; + varying vec2 uv; void main(void) @@ -11,6 +14,11 @@ void main(void) sum += texture2D(texUnit, uv + halfpixel.xy * offset); sum += texture2D(texUnit, uv + vec2(halfpixel.x, -halfpixel.y) * offset); sum += texture2D(texUnit, uv - vec2(halfpixel.x, -halfpixel.y) * offset); + sum /= 8.0; + + if (transformColors) { + sum *= colorMatrix; + } - gl_FragColor = sum / 8.0; + gl_FragColor = sum; } diff --git a/src/shaders/downsample_core.frag b/src/shaders/downsample_core.frag index 8f16f232a..32f1fa462 100644 --- a/src/shaders/downsample_core.frag +++ b/src/shaders/downsample_core.frag @@ -4,6 +4,9 @@ uniform sampler2D texUnit; uniform float offset; uniform vec2 halfpixel; +uniform bool transformColors; +uniform mat4 colorMatrix; + in vec2 uv; out vec4 fragColor; @@ -15,6 +18,11 @@ void main(void) sum += texture(texUnit, uv + halfpixel.xy * offset); sum += texture(texUnit, uv + vec2(halfpixel.x, -halfpixel.y) * offset); sum += texture(texUnit, uv - vec2(halfpixel.x, -halfpixel.y) * offset); + sum /= 8.0; + + if (transformColors) { + sum *= colorMatrix; + } - fragColor = sum / 8.0; + fragColor = sum; }