├── doc ├── demo.png └── settings.png ├── src ├── blurconfig.kcfgc ├── plugin.cpp ├── metadata.json ├── kcm │ ├── blur_config.h │ ├── CMakeLists.txt │ ├── blur_config.cpp │ ├── blur_config.desktop │ └── blur_config.ui ├── blur.kcfg ├── CMakeLists.txt ├── blurshader.h ├── blur.h ├── blurshader.cpp └── blur.cpp ├── uninstall.sh ├── install.sh ├── cmake └── Modules │ ├── PkgConfigGetVar.cmake │ └── Findkwineffects.cmake ├── CMakeLists.txt ├── README.md └── LICENSE /doc/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alban-Boissard/kwin-effects-blur-respect-rounded-decorations/HEAD/doc/demo.png -------------------------------------------------------------------------------- /src/blurconfig.kcfgc: -------------------------------------------------------------------------------- 1 | File=blur.kcfg 2 | ClassName=BlurConfig 3 | NameSpace=KWin 4 | Singleton=true 5 | Mutators=true 6 | -------------------------------------------------------------------------------- /doc/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alban-Boissard/kwin-effects-blur-respect-rounded-decorations/HEAD/doc/settings.png -------------------------------------------------------------------------------- /uninstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cd build 3 | sudo make uninstall && echo "Uninstalled successfully!" 4 | cd .. 5 | rm -rf build 6 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ORIGINAL_DIR=$(pwd) 4 | 5 | rm -rf build 6 | mkdir build 7 | cd build 8 | cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release 9 | make 10 | sudo make install 11 | 12 | cd $ORIGINAL_DIR 13 | -------------------------------------------------------------------------------- /src/plugin.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | SPDX-FileCopyrightText: 2021 Vlad Zahorodnii 3 | 4 | SPDX-License-Identifier: GPL-2.0-or-later 5 | */ 6 | 7 | #include "blur.h" 8 | 9 | namespace KWin 10 | { 11 | 12 | KWIN_EFFECT_FACTORY_SUPPORTED_ENABLED(BlurEffect, 13 | "metadata.json", 14 | return BlurEffect::supported();, 15 | return BlurEffect::enabledByDefault();) 16 | 17 | } // namespace KWin 18 | 19 | #include "plugin.moc" 20 | -------------------------------------------------------------------------------- /src/metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "KPlugin": { 3 | "Authors": [ 4 | { 5 | "Email": "", 6 | "Name": "Alban Boissard" 7 | } 8 | ], 9 | "Category": "Appearance", 10 | "Description": "Blur effect, respect rounded corners of windows decorations", 11 | "EnabledByDefault": false, 12 | "Id": "kwin4_effect_blur2", 13 | "License": "GPL", 14 | "Name": "Blur - Respect rounded corners", 15 | "ServiceTypes": [ 16 | "KWin/Effect" 17 | ] 18 | }, 19 | "X-KDE-ConfigModule": "kwin_blur2_config" 20 | } 21 | -------------------------------------------------------------------------------- /cmake/Modules/PkgConfigGetVar.cmake: -------------------------------------------------------------------------------- 1 | include (UsePkgConfig) 2 | 3 | macro (pkgconfig_getvar _package _var _output_variable) 4 | SET (${_output_variable}) 5 | 6 | if (PKGCONFIG_EXECUTABLE) 7 | exec_program (${PKGCONFIG_EXECUTABLE} 8 | ARGS ${_package} --exists 9 | RETURN_VALUE _return_VALUE 10 | OUTPUT_VARIABLE _pkgconfigDevNull 11 | ) 12 | 13 | if (NOT _return_VALUE) 14 | exec_program (${PKGCONFIG_EXECUTABLE} 15 | ARGS ${_package} --variable ${_var} 16 | OUTPUT_VARIABLE ${_output_variable} 17 | ) 18 | endif () 19 | endif () 20 | endmacro () 21 | -------------------------------------------------------------------------------- /src/kcm/blur_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | SPDX-FileCopyrightText: 2010 Fredrik Höglund 3 | 4 | SPDX-License-Identifier: GPL-2.0-or-later 5 | */ 6 | 7 | #ifndef BLUR_CONFIG_H 8 | #define BLUR_CONFIG_H 9 | 10 | #include 11 | #include "ui_blur_config.h" 12 | 13 | namespace KWin 14 | { 15 | 16 | class BlurEffectConfig : public KCModule 17 | { 18 | Q_OBJECT 19 | 20 | public: 21 | explicit BlurEffectConfig(QWidget *parent = nullptr, const QVariantList& args = QVariantList()); 22 | ~BlurEffectConfig() override; 23 | 24 | void save() override; 25 | 26 | private: 27 | ::Ui::BlurEffectConfig ui; 28 | }; 29 | 30 | } // namespace KWin 31 | 32 | #endif 33 | 34 | -------------------------------------------------------------------------------- /src/kcm/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(kcm_SRCS 2 | blur_config.cpp 3 | ) 4 | 5 | kconfig_add_kcfg_files(kcm_SRCS 6 | ../blurconfig.kcfgc 7 | ) 8 | 9 | qt5_wrap_ui(kcm_SRCS blur_config.ui) 10 | 11 | qt5_add_dbus_interface(kcm_SRCS ${KWIN_EFFECTS_INTERFACE} kwineffects_interface) 12 | 13 | add_library(kwin_blur2_config MODULE ${kcm_SRCS}) 14 | 15 | target_link_libraries(kwin_blur2_config 16 | Qt5::Core 17 | Qt5::DBus 18 | Qt5::Gui 19 | KF5::ConfigCore 20 | KF5::ConfigGui 21 | KF5::ConfigWidgets 22 | ) 23 | 24 | kcoreaddons_desktop_to_json(kwin_blur2_config blur_config.desktop SERVICE_TYPES kcmodule.desktop) 25 | 26 | install( 27 | TARGETS 28 | kwin_blur2_config 29 | 30 | DESTINATION 31 | ${PLUGIN_INSTALL_DIR}/kwin/effects/configs 32 | ) 33 | -------------------------------------------------------------------------------- /src/blur.kcfg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 15 10 | 11 | 12 | 5 13 | 14 | 15 | 10 16 | 17 | 18 | 0 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(kcm) 2 | 3 | set(effect_SRCS 4 | blur.cpp 5 | blurshader.cpp 6 | plugin.cpp 7 | ) 8 | 9 | kconfig_add_kcfg_files(effect_SRCS 10 | blurconfig.kcfgc 11 | ) 12 | 13 | find_package(KWaylandServer CONFIG REQUIRED) 14 | set_package_properties(KWaylandServer PROPERTIES 15 | TYPE REQUIRED 16 | PURPOSE "For Wayland integration" 17 | ) 18 | 19 | add_library(kwin4_effect_blur2 SHARED ${effect_SRCS}) 20 | 21 | target_link_libraries(kwin4_effect_blur2 22 | Qt5::Core 23 | Qt5::Gui 24 | KF5::ConfigCore 25 | KF5::ConfigGui 26 | KF5::CoreAddons 27 | KF5::WindowSystem 28 | Plasma::KWaylandServer 29 | kwineffects::kwineffects 30 | kwineffects::kwinglutils 31 | epoxy::epoxy 32 | ) 33 | 34 | install( 35 | TARGETS 36 | kwin4_effect_blur2 37 | 38 | DESTINATION 39 | ${PLUGIN_INSTALL_DIR}/kwin/effects/plugins/ 40 | ) 41 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | project(kwin-effects-yaml) 3 | 4 | set(KF_MIN_VERSION "5.78") 5 | 6 | set(CMAKE_CXX_STANDARD 14) 7 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 8 | set(CMAKE_CXX_EXTENSIONS OFF) 9 | 10 | find_package(ECM ${KF_MIN_VERSION} REQUIRED NO_MODULE) 11 | set(CMAKE_MODULE_PATH 12 | ${CMAKE_MODULE_PATH} 13 | ${ECM_MODULE_PATH} 14 | ${ECM_KDE_MODULE_DIR} 15 | ${ECM_KDE_MODULE_DIR}/kwin 16 | ${CMAKE_SOURCE_DIR}/cmake 17 | ${CMAKE_SOURCE_DIR}/cmake/Modules 18 | ) 19 | 20 | include(FeatureSummary) 21 | include(KDEInstallDirs) 22 | include(KDECMakeSettings) 23 | include(KDECompilerSettings NO_POLICY_SCOPE) 24 | 25 | find_package(Qt5 REQUIRED COMPONENTS 26 | Core 27 | DBus 28 | Gui 29 | ) 30 | 31 | 32 | 33 | 34 | find_package(KF5 ${KF_MIN_VERSION} REQUIRED COMPONENTS 35 | Config 36 | ConfigWidgets 37 | CoreAddons 38 | WindowSystem 39 | ) 40 | 41 | #find_package(kwin REQUIRED) 42 | 43 | find_package(kwineffects REQUIRED COMPONENTS 44 | kwineffects 45 | kwinglutils 46 | ) 47 | 48 | find_package(KWinDBusInterface CONFIG REQUIRED) 49 | 50 | find_package(epoxy REQUIRED) 51 | 52 | add_subdirectory(src) 53 | 54 | feature_summary(WHAT ALL) 55 | -------------------------------------------------------------------------------- /src/kcm/blur_config.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | SPDX-FileCopyrightText: 2010 Fredrik Höglund 3 | 4 | SPDX-License-Identifier: GPL-2.0-or-later 5 | */ 6 | 7 | #include "blur_config.h" 8 | // KConfigSkeleton 9 | #include "blurconfig.h" 10 | //#include 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | K_PLUGIN_FACTORY_WITH_JSON(BlurEffectConfigFactory, 17 | "blur_config.json", 18 | registerPlugin();) 19 | 20 | namespace KWin 21 | { 22 | 23 | BlurEffectConfig::BlurEffectConfig(QWidget *parent, const QVariantList &args) 24 | : KCModule(parent, args) 25 | { 26 | ui.setupUi(this); 27 | //BlurConfig::instance(KWIN_CONFIG); 28 | BlurConfig::instance("kwinrc"); 29 | addConfig(BlurConfig::self(), this); 30 | 31 | load(); 32 | } 33 | 34 | BlurEffectConfig::~BlurEffectConfig() 35 | { 36 | } 37 | 38 | void BlurEffectConfig::save() 39 | { 40 | KCModule::save(); 41 | 42 | OrgKdeKwinEffectsInterface interface(QStringLiteral("org.kde.KWin"), 43 | QStringLiteral("/Effects"), 44 | QDBusConnection::sessionBus()); 45 | interface.reconfigureEffect(QStringLiteral("kwin4_effect_blur2")); 46 | } 47 | 48 | } // namespace KWin 49 | 50 | #include "blur_config.moc" 51 | -------------------------------------------------------------------------------- /src/kcm/blur_config.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Type=Service 3 | X-KDE-ServiceTypes=KCModule 4 | 5 | X-KDE-Library=kwin_blur2_config 6 | X-KDE-ParentComponents=kwin4_effect_blur2 7 | 8 | Name=Blur - respect rounded corners 9 | Name[af]=Blur 10 | Name[ar]=غشاوة 11 | Name[az]=Yayğınlıq 12 | Name[be]=Blur 13 | Name[bg]=Замъгляване 14 | Name[bn]=ব্লার 15 | Name[bn_IN]=Blur (ব্লার) 16 | Name[bs]=Zamućenje 17 | Name[ca]=Difuminat 18 | Name[ca@valencia]=Difumina 19 | Name[cs]=Rozostření 20 | Name[csb]=Rozmazóné 21 | Name[da]=Slør 22 | Name[de]=Verwischen 23 | Name[el]=Θόλωμα 24 | Name[en_GB]=Blur 25 | Name[eo]=Malklarigi 26 | Name[es]=Desenfocar 27 | Name[et]=Hägu 28 | Name[eu]=Lausotu 29 | Name[fa]=محو 30 | Name[fi]=Sumennus 31 | Name[fr]=Flou - respecte les coins arrondis 32 | Name[fy]=Ferfagje 33 | Name[ga]=Blur 34 | Name[gl]=Desenfocar 35 | Name[gu]=ઝાંખું 36 | Name[he]=טשטוש 37 | Name[hi]=धुंधला करें 38 | Name[hne]=धुंधला करव 39 | Name[hr]=Mrlja 40 | Name[hsb]=Młowojty 41 | Name[hu]=Elmosódás 42 | Name[ia]=Obscura (Blur) 43 | Name[id]=Buram 44 | Name[is]=Móða 45 | Name[it]=Sfocatura 46 | Name[ja]=ぼかし 47 | Name[kk]=Бұлдыр 48 | Name[km]=ព្រិល​ 49 | Name[kn]=ಮಾಸಲುಗೊಳಿಸು (ಬ್ಲರ್) 50 | Name[ko]=흐리게 51 | Name[ku]=Blur 52 | Name[lt]=Suliejimas 53 | Name[lv]=Aizmiglot 54 | Name[mai]=धुंधला करू 55 | Name[mk]=Заматување 56 | Name[ml]=മങ്ങിയതാക്കുക 57 | Name[mr]=पुसट 58 | Name[nb]=Slør 59 | Name[nds]=Verwischen 60 | Name[ne]=धब्बा 61 | Name[nl]=Vervagen 62 | Name[nn]=Uklar 63 | Name[pa]=ਧੁੰਦਲਾ 64 | Name[pl]=Rozmycie 65 | Name[pt]=BlueFish 66 | Name[pt_BR]=Borrar 67 | Name[ro]=Estompare 68 | Name[ru]=Размытие 69 | Name[se]=Seagas 70 | Name[si]=අපැහැදිලි කිරීම 71 | Name[sk]=Rozmazať 72 | Name[sl]=Zabriši 73 | Name[sr]=Замућење 74 | Name[sr@ijekavian]=Замућење 75 | Name[sr@ijekavianlatin]=Zamućenje 76 | Name[sr@latin]=Zamućenje 77 | Name[sv]=Oskärpa 78 | Name[ta]=மங்கலாக 79 | Name[te]=బ్లర్ 80 | Name[th]=ทำให้ไม่ชัดเจน 81 | Name[tr]=Bulanıklaştırma 82 | Name[ug]=گۇڭگا 83 | Name[uk]=Розмивання 84 | Name[vi]=Mờ 85 | Name[wa]=Flou 86 | Name[x-test]=xxBlurxx 87 | Name[zh_CN]=窗口背景虚化 88 | Name[zh_TW]=模糊 89 | 90 | -------------------------------------------------------------------------------- /src/blurshader.h: -------------------------------------------------------------------------------- 1 | /* 2 | SPDX-FileCopyrightText: 2010 Fredrik Höglund 3 | SPDX-FileCopyrightText: 2018 Alex Nemeth 4 | 5 | SPDX-License-Identifier: GPL-2.0-or-later 6 | */ 7 | 8 | #ifndef BLURSHADER_H 9 | #define BLURSHADER_H 10 | 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | namespace KWin 20 | { 21 | 22 | class BlurShader : public QObject 23 | { 24 | Q_OBJECT 25 | 26 | public: 27 | BlurShader(QObject *parent = nullptr); 28 | ~BlurShader() override; 29 | 30 | bool isValid() const; 31 | 32 | enum SampleType { 33 | DownSampleType, 34 | UpSampleType, 35 | CopySampleType, 36 | NoiseSampleType, 37 | }; 38 | 39 | void bind(SampleType sampleType); 40 | void unbind(); 41 | 42 | void setModelViewProjectionMatrix(const QMatrix4x4 &matrix); 43 | void setOffset(float offset); 44 | void setTargetTextureSize(const QSize &renderTextureSize); 45 | void setNoiseTextureSize(const QSize &noiseTextureSize); 46 | void setTexturePosition(const QPoint &texPos); 47 | void setBlurRect(const QRect &blurRect, const QSize &screenSize); 48 | 49 | private: 50 | QScopedPointer m_shaderDownsample; 51 | QScopedPointer m_shaderUpsample; 52 | QScopedPointer m_shaderCopysample; 53 | QScopedPointer m_shaderNoisesample; 54 | 55 | int m_mvpMatrixLocationDownsample; 56 | int m_offsetLocationDownsample; 57 | int m_renderTextureSizeLocationDownsample; 58 | int m_halfpixelLocationDownsample; 59 | 60 | int m_mvpMatrixLocationUpsample; 61 | int m_offsetLocationUpsample; 62 | int m_renderTextureSizeLocationUpsample; 63 | int m_halfpixelLocationUpsample; 64 | 65 | int m_mvpMatrixLocationCopysample; 66 | int m_renderTextureSizeLocationCopysample; 67 | int m_blurRectLocationCopysample; 68 | 69 | int m_mvpMatrixLocationNoisesample; 70 | int m_offsetLocationNoisesample; 71 | int m_renderTextureSizeLocationNoisesample; 72 | int m_noiseTextureSizeLocationNoisesample; 73 | int m_texStartPosLocationNoisesample; 74 | int m_halfpixelLocationNoisesample; 75 | 76 | //Caching uniform values to aviod unnecessary setUniform calls 77 | int m_activeSampleType = -1; 78 | 79 | float m_offsetDownsample = 0.0; 80 | QMatrix4x4 m_matrixDownsample; 81 | 82 | float m_offsetUpsample = 0.0; 83 | QMatrix4x4 m_matrixUpsample; 84 | 85 | QMatrix4x4 m_matrixCopysample; 86 | 87 | float m_offsetNoisesample = 0.0; 88 | QVector2D m_noiseTextureSizeNoisesample; 89 | QMatrix4x4 m_matrixNoisesample; 90 | 91 | bool m_valid = false; 92 | 93 | Q_DISABLE_COPY(BlurShader); 94 | }; 95 | 96 | inline bool BlurShader::isValid() const 97 | { 98 | return m_valid; 99 | } 100 | 101 | } // namespace KWin 102 | 103 | #endif 104 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kwin blur effect - Respect rounded corners 2 | 3 | 4 | This kwin effect is a fork of the default kwin blur effect, with minimal changes 5 | to solve the ["plasma kornerbug"](https://bugs.kde.org/show_bug.cgi?id=395725). 6 | It works with all decorations, including aurorae one's, and take care of maximized windows, that are assumed to not have rounded corners. 7 | 8 | 9 | ![Screenshot](doc/demo.png) 10 | 11 | *Above left : Korner bug with the default blur effect* 12 | 13 | *Above right : with the present blur effect* 14 | 15 | *(Window decorations : [classikstyles](https://github.com/paulmcauley/classikstyles) by paulmcauly)* 16 | 17 | ## Usage 18 | 19 | After installing the effect, go to Settings --> Workspace behavior --> Desktop Effects. 20 | 21 | - Disable the default blur effect ("Blur") 22 | - Enable "Blur - Respect rounded corners" 23 | - Tweak the settings of the effect to match your window decorations. 24 | ![Settings](doc/settings.png) 25 | 26 | 27 | ## Limitations 28 | 29 | The effect only acts for window decorations. If the window has a decoration that have to be blurred and is not maximized, the rounded corners area are removed from the region "to be blurred". (before the blur occurs) 30 | 31 | It seems to be not compatible with [ligthlyShaders](https://github.com/a-parhom/LightlyShaders) or [shapecorners](https://sourceforge.net/projects/shapecorners/). 32 | 33 | 34 | ## Plasma version 35 | 36 | I only tested the effect on plasma 5.23.3, I don't know if it work with older versions (But it should, the changes from the default blur effect are very minimal) 37 | 38 | ## Installation 39 | 40 | ### Binary package 41 | 42 | If you build a binary package, or make the effect available from the "kde get new stuff" service, let me know ! 43 | 44 | ### Build from source 45 | 46 | *Section copied from* [Yet another magic lamp](https://github.com/zzag/kwin-effects-yet-another-magic-lamp) from zzag. 47 | 48 | You will need the following dependencies to build this effect: 49 | * CMake 50 | * any C++14 enabled compiler 51 | * Qt 52 | * libkwineffects 53 | * KDE Frameworks 5: 54 | - Config 55 | - CoreAddons 56 | - Extra CMake Modules 57 | - WindowSystem 58 | 59 | On Arch Linux 60 | 61 | ```sh 62 | sudo pacman -S cmake extra-cmake-modules kwin 63 | ``` 64 | 65 | On Fedora 66 | 67 | ```sh 68 | sudo dnf install cmake extra-cmake-modules kf5-kconfig-devel \ 69 | kf5-kcoreaddons-devel kf5-kwindowsystem-devel kwin-devel \ 70 | qt5-qtbase-devel libepoxy-devel kf5-kconfigwidgets-devel 71 | ``` 72 | On fedora 35, it seems you need the following dep too : 73 | ``` 74 | kwayland-server-devel 75 | kf5-kwayland-devel 76 | wayland-devel 77 | ``` 78 | On OpenSUSE 79 | 80 | ```sh 81 | sudo zypper install cmake extra-cmake-modules kconfig-devel \ 82 | kcoreaddons-devel kwindowsystem-devel kwin5-devel \ 83 | libqt5-qtbase-devel libepoxy-devel kconfigwidgets-devel \ 84 | kwayland-server-devel 85 | ``` 86 | 87 | On Ubuntu 88 | 89 | ```sh 90 | sudo apt install cmake extra-cmake-modules kwin-dev \ 91 | libkf5config-dev libkf5configwidgets-dev libkf5coreaddons-dev \ 92 | libkf5windowsystem-dev qtbase5-dev libkwaylandserver-dev libwayland-dev 93 | ``` 94 | 95 | ### After you installed all the required dependencies, clone the repo and use the provided scripts : 96 | 97 | 98 | Build and install: 99 | 100 | ```sh 101 | chmod +x install.sh 102 | ./install.sh 103 | ``` 104 | Uninstall build script: 105 | 106 | ```sh 107 | chmod +x uninstall.sh 108 | ./uninstall.sh 109 | ``` 110 | 111 | ## Contributing 112 | 113 | Any help is welcome. Feel free to fill an issue if you have one. 114 | -------------------------------------------------------------------------------- /cmake/Modules/Findkwineffects.cmake: -------------------------------------------------------------------------------- 1 | #.rst: 2 | # Findkwineffects 3 | # --------------- 4 | # 5 | # Try to find libkwineffects. 6 | # 7 | # This is a component-based find module, which makes use of the COMPONENTS 8 | # argument to find_modules. The following components are 9 | # available:: 10 | # 11 | # kwineffects 12 | # kwinglutils 13 | # kwinxrenderutils 14 | # 15 | # If no components are specified, this module will act as though all components 16 | # were passed to OPTIONAL_COMPONENTS. 17 | # 18 | # This module will define the following variables, independently of the 19 | # components searched for or found: 20 | # 21 | # ``kwineffects_FOUND`` 22 | # True if (the requestion version of) libkwineffects is available 23 | # ``kwineffects_TARGETS`` 24 | # A list of all targets imported by this module (note that there may be more 25 | # than the components that were requested) 26 | # ``kwineffects_LIBRARIES`` 27 | # This can be passed to target_link_libraries() instead of the imported 28 | # targets 29 | # ``kwineffects_INCLUDE_DIRS`` 30 | # This should be passed to target_include_directories() if the targets are 31 | # not used for linking 32 | # ``kwineffects_DEFINITIONS`` 33 | # This should be passed to target_compile_options() if the targets are not 34 | # used for linking 35 | # 36 | # For each searched-for components, ``kwineffects__FOUND`` will be 37 | # set to true if the corresponding libkwineffects library was found, and false 38 | # otherwise. If ``kwineffects__FOUND`` is true, the imported target 39 | # ``kwineffects::`` will be defined. 40 | 41 | #============================================================================= 42 | # Copyright 2019 Vlad Zagorodniy 43 | # 44 | # Redistribution and use in source and binary forms, with or without 45 | # modification, are permitted provided that the following conditions 46 | # are met: 47 | # 48 | # 1. Redistributions of source code must retain the copyright 49 | # notice, this list of conditions and the following disclaimer. 50 | # 2. Redistributions in binary form must reproduce the copyright 51 | # notice, this list of conditions and the following disclaimer in the 52 | # documentation and/or other materials provided with the distribution. 53 | # 3. The name of the author may not be used to endorse or promote products 54 | # derived from this software without specific prior written permission. 55 | # 56 | # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 57 | # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 58 | # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 59 | # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 60 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 61 | # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 62 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 63 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 64 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 65 | # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 66 | #============================================================================= 67 | 68 | include(ECMFindModuleHelpers) 69 | ecm_find_package_version_check(kwineffects) 70 | 71 | set(kwineffects_known_components 72 | kwineffects 73 | kwinglutils 74 | kwinxrenderutils 75 | ) 76 | set(kwineffects_default_components ${kwineffects_known_components}) 77 | 78 | set(kwineffects_kwineffects_header "kwineffects.h") 79 | set(kwineffects_kwineffects_lib "kwineffects") 80 | set(kwineffects_kwinglutils_header "kwinglutils.h") 81 | set(kwineffects_kwinglutils_lib "kwinglutils") 82 | set(kwineffects_kwinxrenderutils_header "kwinxrenderutils.h") 83 | set(kwineffects_kwinxrenderutils_lib "kwinxrenderutils") 84 | 85 | ecm_find_package_parse_components(kwineffects 86 | RESULT_VAR kwineffects_components 87 | KNOWN_COMPONENTS ${kwineffects_known_components} 88 | DEFAULT_COMPONENTS ${kwineffects_default_components} 89 | ) 90 | 91 | ecm_find_package_handle_library_components(kwineffects 92 | COMPONENTS ${kwineffects_components} 93 | ) 94 | 95 | find_package_handle_standard_args(kwineffects 96 | FOUND_VAR 97 | kwineffects_FOUND 98 | REQUIRED_VARS 99 | kwineffects_LIBRARIES 100 | HANDLE_COMPONENTS 101 | ) 102 | -------------------------------------------------------------------------------- /src/blur.h: -------------------------------------------------------------------------------- 1 | /* 2 | SPDX-FileCopyrightText: 2010 Fredrik Höglund 3 | SPDX-FileCopyrightText: 2018 Alex Nemeth 4 | 5 | SPDX-License-Identifier: GPL-2.0-or-later 6 | */ 7 | 8 | #ifndef BLUR_H 9 | #define BLUR_H 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | 21 | namespace KWin 22 | { 23 | 24 | static const int borderSize = 5; 25 | 26 | class BlurShader; 27 | 28 | class BlurEffect : public KWin::Effect 29 | { 30 | Q_OBJECT 31 | 32 | public: 33 | BlurEffect(); 34 | ~BlurEffect() override; 35 | 36 | static bool supported(); 37 | static bool enabledByDefault(); 38 | 39 | void reconfigure(ReconfigureFlags flags) override; 40 | void prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime) override; 41 | void prePaintWindow(EffectWindow* w, WindowPrePaintData& data, std::chrono::milliseconds presentTime) override; 42 | void drawWindow(EffectWindow *w, int mask, const QRegion ®ion, WindowPaintData &data) override; 43 | void paintEffectFrame(EffectFrame *frame, const QRegion ®ion, double opacity, double frameOpacity) override; 44 | 45 | bool provides(Feature feature) override; 46 | bool isActive() const override; 47 | 48 | int requestedEffectChainPosition() const override { 49 | return 75; 50 | } 51 | 52 | bool eventFilter(QObject *watched, QEvent *event) override; 53 | 54 | bool blocksDirectScanout() const override; 55 | 56 | public Q_SLOTS: 57 | void slotWindowAdded(KWin::EffectWindow *w); 58 | void slotWindowDeleted(KWin::EffectWindow *w); 59 | void slotPropertyNotify(KWin::EffectWindow *w, long atom); 60 | void slotScreenGeometryChanged(); 61 | 62 | private: 63 | QRect expand(const QRect &rect) const; 64 | QRegion expand(const QRegion ®ion) const; 65 | bool renderTargetsValid() const; 66 | void deleteFBOs(); 67 | void initBlurStrengthValues(); 68 | void updateTexture(); 69 | QRegion blurRegion(const EffectWindow *w) const; 70 | bool shouldBlur(const EffectWindow *w, int mask, const WindowPaintData &data) const; 71 | void updateBlurRegion(EffectWindow *w) const; 72 | void doBlur(const QRegion &shape, const QRect &screen, const float opacity, const QMatrix4x4 &screenProjection, bool isDock, QRect windowRect); 73 | void uploadRegion(QVector2D *&map, const QRegion ®ion, const int downSampleIterations); 74 | void uploadGeometry(GLVertexBuffer *vbo, const QRegion &blurRegion, const QRegion &windowRegion); 75 | void generateNoiseTexture(); 76 | 77 | void upscaleRenderToScreen(GLVertexBuffer *vbo, int vboStart, int blurRectCount, const QMatrix4x4 &screenProjection, QPoint windowPosition); 78 | void applyNoise(GLVertexBuffer *vbo, int vboStart, int blurRectCount, const QMatrix4x4 &screenProjection, QPoint windowPosition); 79 | void downSampleTexture(GLVertexBuffer *vbo, int blurRectCount); 80 | void upSampleTexture(GLVertexBuffer *vbo, int blurRectCount); 81 | void copyScreenSampleTexture(GLVertexBuffer *vbo, int blurRectCount, QRegion blurShape, const QMatrix4x4 &screenProjection); 82 | 83 | void updateCornersRegion(); 84 | 85 | private: 86 | BlurShader *m_shader; 87 | QVector m_renderTargets; 88 | QVector m_renderTextures; 89 | QStack m_renderTargetStack; 90 | 91 | QScopedPointer m_noiseTexture; 92 | 93 | bool m_renderTargetsValid; 94 | long net_wm_blur_region = 0; 95 | QRegion m_paintedArea; // keeps track of all painted areas (from bottom to top) 96 | QRegion m_currentBlur; // keeps track of the currently blured area of the windows(from bottom to top) 97 | 98 | int m_topRadius; // Radius of the rounded corners of the decoration 99 | int m_bottomRadius; 100 | QRegion m_topLeftCorner; // Regions we have to remove of the blured region 101 | QRegion m_topRightCorner; 102 | QRegion m_bottomLeftCorner; 103 | QRegion m_bottomRightCorner; 104 | 105 | int m_downSampleIterations; // number of times the texture will be downsized to half size 106 | int m_offset; 107 | int m_expandSize; 108 | int m_noiseStrength; 109 | int m_scalingFactor; 110 | 111 | struct OffsetStruct { 112 | float minOffset; 113 | float maxOffset; 114 | int expandSize; 115 | }; 116 | 117 | QVector blurOffsets; 118 | 119 | struct BlurValuesStruct { 120 | int iteration; 121 | float offset; 122 | }; 123 | 124 | QVector blurStrengthValues; 125 | 126 | QMap windowBlurChangedConnections; 127 | 128 | static KWaylandServer::BlurManagerInterface *s_blurManager; 129 | static QTimer *s_blurManagerRemoveTimer; 130 | }; 131 | 132 | inline 133 | bool BlurEffect::provides(Effect::Feature feature) 134 | { 135 | if (feature == Blur) { 136 | return true; 137 | } 138 | return KWin::Effect::provides(feature); 139 | } 140 | 141 | 142 | } // namespace KWin 143 | 144 | #endif 145 | 146 | -------------------------------------------------------------------------------- /src/kcm/blur_config.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | BlurEffectConfig 4 | 5 | 6 | 7 | 0 8 | 0 9 | 480 10 | 268 11 | 12 | 13 | 14 | 15 | 16 | 17 | Blur strength: 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | Qt::Horizontal 27 | 28 | 29 | QSizePolicy::Fixed 30 | 31 | 32 | 33 | 20 34 | 20 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | Light 43 | 44 | 45 | 46 | 47 | 48 | 49 | 1 50 | 51 | 52 | 15 53 | 54 | 55 | 1 56 | 57 | 58 | 1 59 | 60 | 61 | 10 62 | 63 | 64 | Qt::Horizontal 65 | 66 | 67 | QSlider::TicksBelow 68 | 69 | 70 | 71 | 72 | 73 | 74 | Strong 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | Noise strength: 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | Qt::Horizontal 93 | 94 | 95 | QSizePolicy::Fixed 96 | 97 | 98 | 99 | 20 100 | 20 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | Light 109 | 110 | 111 | 112 | 113 | 114 | 115 | 14 116 | 117 | 118 | 5 119 | 120 | 121 | 5 122 | 123 | 124 | Qt::Horizontal 125 | 126 | 127 | QSlider::TicksBelow 128 | 129 | 130 | 1 131 | 132 | 133 | 134 | 135 | 136 | 137 | Strong 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | Qt::Vertical 147 | 148 | 149 | QSizePolicy::Fixed 150 | 151 | 152 | 153 | 10 154 | 10 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | Qt::Horizontal 163 | 164 | 165 | 166 | 167 | 168 | 169 | Indicate the top corners radius of the window decorations: 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | Qt::Horizontal 179 | 180 | 181 | QSizePolicy::Fixed 182 | 183 | 184 | 185 | 20 186 | 20 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | Zero 195 | 196 | 197 | 198 | 199 | 200 | 201 | 30 202 | 203 | 204 | Qt::Horizontal 205 | 206 | 207 | QSlider::TicksBelow 208 | 209 | 210 | 2 211 | 212 | 213 | 214 | 215 | 216 | 217 | Large 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | Indicate the bottom corners radius of the window decorations: 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | Qt::Horizontal 236 | 237 | 238 | QSizePolicy::Fixed 239 | 240 | 241 | 242 | 20 243 | 20 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | Zero 252 | 253 | 254 | 255 | 256 | 257 | 258 | 30 259 | 260 | 261 | Qt::Horizontal 262 | 263 | 264 | QSlider::TicksBelow 265 | 266 | 267 | 2 268 | 269 | 270 | 271 | 272 | 273 | 274 | Large 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | Qt::Vertical 284 | 285 | 286 | 287 | 0 288 | 0 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | -------------------------------------------------------------------------------- /src/blurshader.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | SPDX-FileCopyrightText: 2010 Fredrik Höglund 3 | SPDX-FileCopyrightText: 2018 Alex Nemeth 4 | 5 | SPDX-License-Identifier: GPL-2.0-or-later 6 | */ 7 | 8 | #include "blurshader.h" 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | namespace KWin 17 | { 18 | 19 | BlurShader::BlurShader(QObject *parent) 20 | : QObject(parent) 21 | { 22 | const bool gles = GLPlatform::instance()->isGLES(); 23 | const bool glsl_140 = !gles && GLPlatform::instance()->glslVersion() >= kVersionNumber(1, 40); 24 | const bool core = glsl_140 || (gles && GLPlatform::instance()->glslVersion() >= kVersionNumber(3, 0)); 25 | 26 | QByteArray vertexSource; 27 | QByteArray fragmentDownSource; 28 | QByteArray fragmentUpSource; 29 | QByteArray fragmentCopySource; 30 | QByteArray fragmentNoiseSource; 31 | 32 | const QByteArray attribute = core ? "in" : "attribute"; 33 | const QByteArray texture2D = core ? "texture" : "texture2D"; 34 | const QByteArray fragColor = core ? "fragColor" : "gl_FragColor"; 35 | 36 | QString glHeaderString; 37 | 38 | if (gles) { 39 | if (core) { 40 | glHeaderString += "#version 300 es\n\n"; 41 | } 42 | 43 | glHeaderString += "precision highp float;\n"; 44 | } else if (glsl_140) { 45 | glHeaderString += "#version 140\n\n"; 46 | } 47 | 48 | QString glUniformString = "uniform sampler2D texUnit;\n" 49 | "uniform float offset;\n" 50 | "uniform vec2 renderTextureSize;\n" 51 | "uniform vec2 halfpixel;\n"; 52 | 53 | if (core) { 54 | glUniformString += "out vec4 fragColor;\n\n"; 55 | } 56 | 57 | // Vertex shader 58 | QTextStream streamVert(&vertexSource); 59 | 60 | streamVert << glHeaderString; 61 | 62 | streamVert << "uniform mat4 modelViewProjectionMatrix;\n"; 63 | streamVert << attribute << " vec4 vertex;\n\n"; 64 | streamVert << "\n"; 65 | streamVert << "void main(void)\n"; 66 | streamVert << "{\n"; 67 | streamVert << " gl_Position = modelViewProjectionMatrix * vertex;\n"; 68 | streamVert << "}\n"; 69 | 70 | streamVert.flush(); 71 | 72 | // Fragment shader (Dual Kawase Blur) - Downsample 73 | QTextStream streamFragDown(&fragmentDownSource); 74 | 75 | streamFragDown << glHeaderString << glUniformString; 76 | 77 | streamFragDown << "void main(void)\n"; 78 | streamFragDown << "{\n"; 79 | streamFragDown << " vec2 uv = vec2(gl_FragCoord.xy / renderTextureSize);\n"; 80 | streamFragDown << " \n"; 81 | streamFragDown << " vec4 sum = " << texture2D << "(texUnit, uv) * 4.0;\n"; 82 | streamFragDown << " sum += " << texture2D << "(texUnit, uv - halfpixel.xy * offset);\n"; 83 | streamFragDown << " sum += " << texture2D << "(texUnit, uv + halfpixel.xy * offset);\n"; 84 | streamFragDown << " sum += " << texture2D << "(texUnit, uv + vec2(halfpixel.x, -halfpixel.y) * offset);\n"; 85 | streamFragDown << " sum += " << texture2D << "(texUnit, uv - vec2(halfpixel.x, -halfpixel.y) * offset);\n"; 86 | streamFragDown << " \n"; 87 | streamFragDown << " " << fragColor << " = sum / 8.0;\n"; 88 | streamFragDown << "}\n"; 89 | 90 | streamFragDown.flush(); 91 | 92 | // Fragment shader (Dual Kawase Blur) - Upsample 93 | QTextStream streamFragUp(&fragmentUpSource); 94 | 95 | streamFragUp << glHeaderString << glUniformString; 96 | 97 | streamFragUp << "void main(void)\n"; 98 | streamFragUp << "{\n"; 99 | streamFragUp << " vec2 uv = vec2(gl_FragCoord.xy / renderTextureSize);\n"; 100 | streamFragUp << " \n"; 101 | streamFragUp << " vec4 sum = " << texture2D << "(texUnit, uv + vec2(-halfpixel.x * 2.0, 0.0) * offset);\n"; 102 | streamFragUp << " sum += " << texture2D << "(texUnit, uv + vec2(-halfpixel.x, halfpixel.y) * offset) * 2.0;\n"; 103 | streamFragUp << " sum += " << texture2D << "(texUnit, uv + vec2(0.0, halfpixel.y * 2.0) * offset);\n"; 104 | streamFragUp << " sum += " << texture2D << "(texUnit, uv + vec2(halfpixel.x, halfpixel.y) * offset) * 2.0;\n"; 105 | streamFragUp << " sum += " << texture2D << "(texUnit, uv + vec2(halfpixel.x * 2.0, 0.0) * offset);\n"; 106 | streamFragUp << " sum += " << texture2D << "(texUnit, uv + vec2(halfpixel.x, -halfpixel.y) * offset) * 2.0;\n"; 107 | streamFragUp << " sum += " << texture2D << "(texUnit, uv + vec2(0.0, -halfpixel.y * 2.0) * offset);\n"; 108 | streamFragUp << " sum += " << texture2D << "(texUnit, uv + vec2(-halfpixel.x, -halfpixel.y) * offset) * 2.0;\n"; 109 | streamFragUp << " \n"; 110 | streamFragUp << " " << fragColor << " = sum / 12.0;\n"; 111 | streamFragUp << "}\n"; 112 | 113 | streamFragUp.flush(); 114 | 115 | // Fragment shader - Copy texture 116 | QTextStream streamFragCopy(&fragmentCopySource); 117 | 118 | streamFragCopy << glHeaderString; 119 | 120 | streamFragCopy << "uniform sampler2D texUnit;\n"; 121 | streamFragCopy << "uniform vec2 renderTextureSize;\n"; 122 | streamFragCopy << "uniform vec4 blurRect;\n"; 123 | 124 | if (core) { 125 | streamFragCopy << "out vec4 fragColor;\n\n"; 126 | } 127 | 128 | streamFragCopy << "void main(void)\n"; 129 | streamFragCopy << "{\n"; 130 | streamFragCopy << " vec2 uv = vec2(gl_FragCoord.xy / renderTextureSize);\n"; 131 | streamFragCopy << " " << fragColor << " = " << texture2D << "(texUnit, clamp(uv, blurRect.xy, blurRect.zw));\n"; 132 | streamFragCopy << "}\n"; 133 | 134 | streamFragCopy.flush(); 135 | 136 | // Fragment shader - Noise tiling 137 | QTextStream streamFragNoise(&fragmentNoiseSource); 138 | 139 | streamFragNoise << glHeaderString << glUniformString; 140 | 141 | streamFragNoise << "uniform vec2 noiseTextureSize;\n"; 142 | streamFragNoise << "uniform vec2 texStartPos;\n"; 143 | 144 | streamFragNoise << "void main(void)\n"; 145 | streamFragNoise << "{\n"; 146 | streamFragNoise << " vec2 uvNoise = vec2((texStartPos.xy + gl_FragCoord.xy) / noiseTextureSize);\n"; 147 | streamFragNoise << " \n"; 148 | streamFragNoise << " " << fragColor << " = vec4(" << texture2D << "(texUnit, uvNoise).rrr, 0);\n"; 149 | streamFragNoise << "}\n"; 150 | 151 | streamFragNoise.flush(); 152 | 153 | m_shaderDownsample.reset(ShaderManager::instance()->loadShaderFromCode(vertexSource, fragmentDownSource)); 154 | m_shaderUpsample.reset(ShaderManager::instance()->loadShaderFromCode(vertexSource, fragmentUpSource)); 155 | m_shaderCopysample.reset(ShaderManager::instance()->loadShaderFromCode(vertexSource, fragmentCopySource)); 156 | m_shaderNoisesample.reset(ShaderManager::instance()->loadShaderFromCode(vertexSource, fragmentNoiseSource)); 157 | 158 | m_valid = m_shaderDownsample->isValid() && 159 | m_shaderUpsample->isValid() && 160 | m_shaderCopysample->isValid() && 161 | m_shaderNoisesample->isValid(); 162 | 163 | if (m_valid) { 164 | m_mvpMatrixLocationDownsample = m_shaderDownsample->uniformLocation("modelViewProjectionMatrix"); 165 | m_offsetLocationDownsample = m_shaderDownsample->uniformLocation("offset"); 166 | m_renderTextureSizeLocationDownsample = m_shaderDownsample->uniformLocation("renderTextureSize"); 167 | m_halfpixelLocationDownsample = m_shaderDownsample->uniformLocation("halfpixel"); 168 | 169 | m_mvpMatrixLocationUpsample = m_shaderUpsample->uniformLocation("modelViewProjectionMatrix"); 170 | m_offsetLocationUpsample = m_shaderUpsample->uniformLocation("offset"); 171 | m_renderTextureSizeLocationUpsample = m_shaderUpsample->uniformLocation("renderTextureSize"); 172 | m_halfpixelLocationUpsample = m_shaderUpsample->uniformLocation("halfpixel"); 173 | 174 | m_mvpMatrixLocationCopysample = m_shaderCopysample->uniformLocation("modelViewProjectionMatrix"); 175 | m_renderTextureSizeLocationCopysample = m_shaderCopysample->uniformLocation("renderTextureSize"); 176 | m_blurRectLocationCopysample = m_shaderCopysample->uniformLocation("blurRect"); 177 | 178 | m_mvpMatrixLocationNoisesample = m_shaderNoisesample->uniformLocation("modelViewProjectionMatrix"); 179 | m_offsetLocationNoisesample = m_shaderNoisesample->uniformLocation("offset"); 180 | m_renderTextureSizeLocationNoisesample = m_shaderNoisesample->uniformLocation("renderTextureSize"); 181 | m_noiseTextureSizeLocationNoisesample = m_shaderNoisesample->uniformLocation("noiseTextureSize"); 182 | m_texStartPosLocationNoisesample = m_shaderNoisesample->uniformLocation("texStartPos"); 183 | m_halfpixelLocationNoisesample = m_shaderNoisesample->uniformLocation("halfpixel"); 184 | 185 | QMatrix4x4 modelViewProjection; 186 | const QSize screenSize = effects->virtualScreenSize(); 187 | modelViewProjection.ortho(0, screenSize.width(), screenSize.height(), 0, 0, 65535); 188 | 189 | //Add default values to the uniforms of the shaders 190 | ShaderManager::instance()->pushShader(m_shaderDownsample.data()); 191 | m_shaderDownsample->setUniform(m_mvpMatrixLocationDownsample, modelViewProjection); 192 | m_shaderDownsample->setUniform(m_offsetLocationDownsample, float(1.0)); 193 | m_shaderDownsample->setUniform(m_renderTextureSizeLocationDownsample, QVector2D(1.0, 1.0)); 194 | m_shaderDownsample->setUniform(m_halfpixelLocationDownsample, QVector2D(1.0, 1.0)); 195 | ShaderManager::instance()->popShader(); 196 | 197 | ShaderManager::instance()->pushShader(m_shaderUpsample.data()); 198 | m_shaderUpsample->setUniform(m_mvpMatrixLocationUpsample, modelViewProjection); 199 | m_shaderUpsample->setUniform(m_offsetLocationUpsample, float(1.0)); 200 | m_shaderUpsample->setUniform(m_renderTextureSizeLocationUpsample, QVector2D(1.0, 1.0)); 201 | m_shaderUpsample->setUniform(m_halfpixelLocationUpsample, QVector2D(1.0, 1.0)); 202 | ShaderManager::instance()->popShader(); 203 | 204 | ShaderManager::instance()->pushShader(m_shaderCopysample.data()); 205 | m_shaderCopysample->setUniform(m_mvpMatrixLocationCopysample, modelViewProjection); 206 | m_shaderCopysample->setUniform(m_renderTextureSizeLocationCopysample, QVector2D(1.0, 1.0)); 207 | m_shaderCopysample->setUniform(m_blurRectLocationCopysample, QVector4D(1.0, 1.0, 1.0, 1.0)); 208 | ShaderManager::instance()->popShader(); 209 | 210 | ShaderManager::instance()->pushShader(m_shaderNoisesample.data()); 211 | m_shaderNoisesample->setUniform(m_mvpMatrixLocationNoisesample, modelViewProjection); 212 | m_shaderNoisesample->setUniform(m_offsetLocationNoisesample, float(1.0)); 213 | m_shaderNoisesample->setUniform(m_renderTextureSizeLocationNoisesample, QVector2D(1.0, 1.0)); 214 | m_shaderNoisesample->setUniform(m_noiseTextureSizeLocationNoisesample, QVector2D(1.0, 1.0)); 215 | m_shaderNoisesample->setUniform(m_texStartPosLocationNoisesample, QVector2D(1.0, 1.0)); 216 | m_shaderNoisesample->setUniform(m_halfpixelLocationNoisesample, QVector2D(1.0, 1.0)); 217 | 218 | ShaderManager::instance()->popShader(); 219 | } 220 | } 221 | 222 | BlurShader::~BlurShader() 223 | { 224 | } 225 | 226 | void BlurShader::setModelViewProjectionMatrix(const QMatrix4x4 &matrix) 227 | { 228 | if (!isValid()) { 229 | return; 230 | } 231 | 232 | switch (m_activeSampleType) { 233 | case CopySampleType: 234 | if (matrix == m_matrixCopysample) { 235 | return; 236 | } 237 | 238 | m_matrixCopysample = matrix; 239 | m_shaderCopysample->setUniform(m_mvpMatrixLocationCopysample, matrix); 240 | break; 241 | 242 | case UpSampleType: 243 | if (matrix == m_matrixUpsample) { 244 | return; 245 | } 246 | 247 | m_matrixUpsample = matrix; 248 | m_shaderUpsample->setUniform(m_mvpMatrixLocationUpsample, matrix); 249 | break; 250 | 251 | case DownSampleType: 252 | if (matrix == m_matrixDownsample) { 253 | return; 254 | } 255 | 256 | m_matrixDownsample = matrix; 257 | m_shaderDownsample->setUniform(m_mvpMatrixLocationDownsample, matrix); 258 | break; 259 | 260 | case NoiseSampleType: 261 | if (matrix == m_matrixNoisesample) { 262 | return; 263 | } 264 | 265 | m_matrixNoisesample = matrix; 266 | m_shaderNoisesample->setUniform(m_mvpMatrixLocationNoisesample, matrix); 267 | break; 268 | 269 | default: 270 | Q_UNREACHABLE(); 271 | break; 272 | } 273 | } 274 | 275 | void BlurShader::setOffset(float offset) 276 | { 277 | if (!isValid()) { 278 | return; 279 | } 280 | 281 | switch (m_activeSampleType) { 282 | case UpSampleType: 283 | if (offset == m_offsetUpsample) { 284 | return; 285 | } 286 | 287 | m_offsetUpsample = offset; 288 | m_shaderUpsample->setUniform(m_offsetLocationUpsample, offset); 289 | break; 290 | 291 | case DownSampleType: 292 | if (offset == m_offsetDownsample) { 293 | return; 294 | } 295 | 296 | m_offsetDownsample = offset; 297 | m_shaderDownsample->setUniform(m_offsetLocationDownsample, offset); 298 | break; 299 | 300 | case NoiseSampleType: 301 | if (offset == m_offsetNoisesample) { 302 | return; 303 | } 304 | 305 | m_offsetNoisesample = offset; 306 | m_shaderNoisesample->setUniform(m_offsetLocationNoisesample, offset); 307 | break; 308 | 309 | default: 310 | Q_UNREACHABLE(); 311 | break; 312 | } 313 | } 314 | 315 | void BlurShader::setTargetTextureSize(const QSize &renderTextureSize) 316 | { 317 | if (!isValid()) { 318 | return; 319 | } 320 | 321 | const QVector2D texSize(renderTextureSize.width(), renderTextureSize.height()); 322 | 323 | switch (m_activeSampleType) { 324 | case CopySampleType: 325 | m_shaderCopysample->setUniform(m_renderTextureSizeLocationCopysample, texSize); 326 | break; 327 | 328 | case UpSampleType: 329 | m_shaderUpsample->setUniform(m_renderTextureSizeLocationUpsample, texSize); 330 | m_shaderUpsample->setUniform(m_halfpixelLocationUpsample, QVector2D(0.5 / texSize.x(), 0.5 / texSize.y())); 331 | break; 332 | 333 | case DownSampleType: 334 | m_shaderDownsample->setUniform(m_renderTextureSizeLocationDownsample, texSize); 335 | m_shaderDownsample->setUniform(m_halfpixelLocationDownsample, QVector2D(0.5 / texSize.x(), 0.5 / texSize.y())); 336 | break; 337 | 338 | case NoiseSampleType: 339 | m_shaderNoisesample->setUniform(m_renderTextureSizeLocationNoisesample, texSize); 340 | m_shaderNoisesample->setUniform(m_halfpixelLocationNoisesample, QVector2D(0.5 / texSize.x(), 0.5 / texSize.y())); 341 | break; 342 | 343 | default: 344 | Q_UNREACHABLE(); 345 | break; 346 | } 347 | } 348 | 349 | void BlurShader::setNoiseTextureSize(const QSize &noiseTextureSize) 350 | { 351 | const QVector2D noiseTexSize(noiseTextureSize.width(), noiseTextureSize.height()); 352 | 353 | if (noiseTexSize != m_noiseTextureSizeNoisesample) { 354 | m_noiseTextureSizeNoisesample = noiseTexSize; 355 | m_shaderNoisesample->setUniform(m_noiseTextureSizeLocationNoisesample, noiseTexSize); 356 | } 357 | } 358 | 359 | void BlurShader::setTexturePosition(const QPoint &texPos) 360 | { 361 | m_shaderNoisesample->setUniform(m_texStartPosLocationNoisesample, QVector2D(-texPos.x(), texPos.y())); 362 | } 363 | 364 | void BlurShader::setBlurRect(const QRect &blurRect, const QSize &screenSize) 365 | { 366 | if (!isValid()) { 367 | return; 368 | } 369 | 370 | const QVector4D rect( 371 | blurRect.left() / float(screenSize.width()), 372 | 1.0 - blurRect.bottom() / float(screenSize.height()), 373 | blurRect.right() / float(screenSize.width()), 374 | 1.0 - blurRect.top() / float(screenSize.height()) 375 | ); 376 | 377 | m_shaderCopysample->setUniform(m_blurRectLocationCopysample, rect); 378 | } 379 | 380 | void BlurShader::bind(SampleType sampleType) 381 | { 382 | if (!isValid()) { 383 | return; 384 | } 385 | 386 | switch (sampleType) { 387 | case CopySampleType: 388 | ShaderManager::instance()->pushShader(m_shaderCopysample.data()); 389 | break; 390 | 391 | case UpSampleType: 392 | ShaderManager::instance()->pushShader(m_shaderUpsample.data()); 393 | break; 394 | 395 | case DownSampleType: 396 | ShaderManager::instance()->pushShader(m_shaderDownsample.data()); 397 | break; 398 | 399 | case NoiseSampleType: 400 | ShaderManager::instance()->pushShader(m_shaderNoisesample.data()); 401 | break; 402 | 403 | default: 404 | Q_UNREACHABLE(); 405 | break; 406 | } 407 | 408 | m_activeSampleType = sampleType; 409 | } 410 | 411 | void BlurShader::unbind() 412 | { 413 | ShaderManager::instance()->popShader(); 414 | } 415 | 416 | } // namespace KWin 417 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /src/blur.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | SPDX-FileCopyrightText: 2010 Fredrik Höglund 3 | SPDX-FileCopyrightText: 2011 Philipp Knechtges 4 | SPDX-FileCopyrightText: 2018 Alex Nemeth 5 | 6 | SPDX-License-Identifier: GPL-2.0-or-later 7 | */ 8 | 9 | #include "blur.h" 10 | #include "blurshader.h" 11 | // KConfigSkeleton 12 | #include "blurconfig.h" 13 | 14 | #include 15 | #include 16 | #include // for QGuiApplication 17 | #include 18 | #include 19 | #include 20 | #include // for ceil() 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | namespace KWin 29 | { 30 | 31 | static const QByteArray s_blurAtomName = QByteArrayLiteral("_KDE_NET_WM_BLUR_BEHIND_REGION"); 32 | 33 | KWaylandServer::BlurManagerInterface *BlurEffect::s_blurManager = nullptr; 34 | QTimer *BlurEffect::s_blurManagerRemoveTimer = nullptr; 35 | 36 | BlurEffect::BlurEffect() 37 | { 38 | initConfig(); 39 | m_shader = new BlurShader(this); 40 | 41 | initBlurStrengthValues(); 42 | reconfigure(ReconfigureAll); 43 | 44 | // ### Hackish way to announce support. 45 | // Should be included in _NET_SUPPORTED instead. 46 | if (m_shader && m_shader->isValid() && m_renderTargetsValid) { 47 | if (effects->xcbConnection()) { 48 | net_wm_blur_region = effects->announceSupportProperty(s_blurAtomName, this); 49 | } 50 | if (effects->waylandDisplay()) { 51 | if (!s_blurManagerRemoveTimer) { 52 | s_blurManagerRemoveTimer = new QTimer(qApp); 53 | s_blurManagerRemoveTimer->setSingleShot(true); 54 | s_blurManagerRemoveTimer->callOnTimeout([]() { 55 | s_blurManager->remove(); 56 | s_blurManager = nullptr; 57 | }); 58 | } 59 | s_blurManagerRemoveTimer->stop(); 60 | if (!s_blurManager) { 61 | s_blurManager = new KWaylandServer::BlurManagerInterface(effects->waylandDisplay(), s_blurManagerRemoveTimer); 62 | } 63 | } 64 | } 65 | 66 | connect(effects, &EffectsHandler::windowAdded, this, &BlurEffect::slotWindowAdded); 67 | connect(effects, &EffectsHandler::windowDeleted, this, &BlurEffect::slotWindowDeleted); 68 | connect(effects, &EffectsHandler::propertyNotify, this, &BlurEffect::slotPropertyNotify); 69 | connect(effects, &EffectsHandler::virtualScreenGeometryChanged, this, &BlurEffect::slotScreenGeometryChanged); 70 | connect(effects, &EffectsHandler::xcbConnectionChanged, this, 71 | [this] { 72 | if (m_shader && m_shader->isValid() && m_renderTargetsValid) { 73 | net_wm_blur_region = effects->announceSupportProperty(s_blurAtomName, this); 74 | } 75 | } 76 | ); 77 | 78 | // Fetch the blur regions for all windows 79 | const auto stackingOrder = effects->stackingOrder(); 80 | for (EffectWindow *window : stackingOrder) { 81 | slotWindowAdded(window); 82 | } 83 | } 84 | 85 | BlurEffect::~BlurEffect() 86 | { 87 | // When compositing is restarted, avoid removing the manager immediately. 88 | if (s_blurManager) { 89 | s_blurManagerRemoveTimer->start(1000); 90 | } 91 | deleteFBOs(); 92 | } 93 | 94 | void BlurEffect::slotScreenGeometryChanged() 95 | { 96 | effects->makeOpenGLContextCurrent(); 97 | updateTexture(); 98 | 99 | // Fetch the blur regions for all windows 100 | const auto stackingOrder = effects->stackingOrder(); 101 | for (EffectWindow *window : stackingOrder) { 102 | updateBlurRegion(window); 103 | } 104 | effects->doneOpenGLContextCurrent(); 105 | } 106 | 107 | bool BlurEffect::renderTargetsValid() const 108 | { 109 | return !m_renderTargets.isEmpty() && std::find_if(m_renderTargets.cbegin(), m_renderTargets.cend(), 110 | [](const GLRenderTarget *target) { 111 | return !target->valid(); 112 | }) == m_renderTargets.cend(); 113 | } 114 | 115 | void BlurEffect::deleteFBOs() 116 | { 117 | qDeleteAll(m_renderTargets); 118 | 119 | m_renderTargets.clear(); 120 | m_renderTextures.clear(); 121 | } 122 | 123 | void BlurEffect::updateTexture() 124 | { 125 | deleteFBOs(); 126 | 127 | /* Reserve memory for: 128 | * - The original sized texture (1) 129 | * - The downsized textures (m_downSampleIterations) 130 | * - The helper texture (1) 131 | */ 132 | m_renderTargets.reserve(m_downSampleIterations + 2); 133 | m_renderTextures.reserve(m_downSampleIterations + 2); 134 | 135 | GLenum textureFormat = GL_RGBA8; 136 | 137 | // Check the color encoding of the default framebuffer 138 | if (!GLPlatform::instance()->isGLES()) { 139 | GLuint prevFbo = 0; 140 | glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, reinterpret_cast(&prevFbo)); 141 | 142 | if (prevFbo != 0) { 143 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); 144 | } 145 | 146 | GLenum colorEncoding = GL_LINEAR; 147 | glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_BACK_LEFT, 148 | GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING, 149 | reinterpret_cast(&colorEncoding)); 150 | 151 | if (prevFbo != 0) { 152 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, prevFbo); 153 | } 154 | 155 | if (colorEncoding == GL_SRGB) { 156 | textureFormat = GL_SRGB8_ALPHA8; 157 | } 158 | } 159 | 160 | for (int i = 0; i <= m_downSampleIterations; i++) { 161 | m_renderTextures.append(GLTexture(textureFormat, effects->virtualScreenSize() / (1 << i))); 162 | m_renderTextures.last().setFilter(GL_LINEAR); 163 | m_renderTextures.last().setWrapMode(GL_CLAMP_TO_EDGE); 164 | 165 | m_renderTargets.append(new GLRenderTarget(m_renderTextures.last())); 166 | } 167 | 168 | // This last set is used as a temporary helper texture 169 | m_renderTextures.append(GLTexture(textureFormat, effects->virtualScreenSize())); 170 | m_renderTextures.last().setFilter(GL_LINEAR); 171 | m_renderTextures.last().setWrapMode(GL_CLAMP_TO_EDGE); 172 | 173 | m_renderTargets.append(new GLRenderTarget(m_renderTextures.last())); 174 | 175 | m_renderTargetsValid = renderTargetsValid(); 176 | 177 | // Prepare the stack for the rendering 178 | m_renderTargetStack.clear(); 179 | m_renderTargetStack.reserve(m_downSampleIterations * 2); 180 | 181 | // Upsample 182 | for (int i = 1; i < m_downSampleIterations; i++) { 183 | m_renderTargetStack.push(m_renderTargets[i]); 184 | } 185 | 186 | // Downsample 187 | for (int i = m_downSampleIterations; i > 0; i--) { 188 | m_renderTargetStack.push(m_renderTargets[i]); 189 | } 190 | 191 | // Copysample 192 | m_renderTargetStack.push(m_renderTargets[0]); 193 | 194 | // Generate the noise helper texture 195 | generateNoiseTexture(); 196 | } 197 | 198 | void BlurEffect::initBlurStrengthValues() 199 | { 200 | // This function creates an array of blur strength values that are evenly distributed 201 | 202 | // The range of the slider on the blur settings UI 203 | int numOfBlurSteps = 15; 204 | int remainingSteps = numOfBlurSteps; 205 | 206 | /* 207 | * Explanation for these numbers: 208 | * 209 | * The texture blur amount depends on the downsampling iterations and the offset value. 210 | * By changing the offset we can alter the blur amount without relying on further downsampling. 211 | * But there is a minimum and maximum value of offset per downsample iteration before we 212 | * get artifacts. 213 | * 214 | * The minOffset variable is the minimum offset value for an iteration before we 215 | * get blocky artifacts because of the downsampling. 216 | * 217 | * The maxOffset value is the maximum offset value for an iteration before we 218 | * get diagonal line artifacts because of the nature of the dual kawase blur algorithm. 219 | * 220 | * The expandSize value is the minimum value for an iteration before we reach the end 221 | * of a texture in the shader and sample outside of the area that was copied into the 222 | * texture from the screen. 223 | */ 224 | 225 | // {minOffset, maxOffset, expandSize} 226 | blurOffsets.append({1.0, 2.0, 10}); // Down sample size / 2 227 | blurOffsets.append({2.0, 3.0, 20}); // Down sample size / 4 228 | blurOffsets.append({2.0, 5.0, 50}); // Down sample size / 8 229 | blurOffsets.append({3.0, 8.0, 150}); // Down sample size / 16 230 | //blurOffsets.append({5.0, 10.0, 400}); // Down sample size / 32 231 | //blurOffsets.append({7.0, ?.0}); // Down sample size / 64 232 | 233 | float offsetSum = 0; 234 | 235 | for (int i = 0; i < blurOffsets.size(); i++) { 236 | offsetSum += blurOffsets[i].maxOffset - blurOffsets[i].minOffset; 237 | } 238 | 239 | for (int i = 0; i < blurOffsets.size(); i++) { 240 | int iterationNumber = std::ceil((blurOffsets[i].maxOffset - blurOffsets[i].minOffset) / offsetSum * numOfBlurSteps); 241 | remainingSteps -= iterationNumber; 242 | 243 | if (remainingSteps < 0) { 244 | iterationNumber += remainingSteps; 245 | } 246 | 247 | float offsetDifference = blurOffsets[i].maxOffset - blurOffsets[i].minOffset; 248 | 249 | for (int j = 1; j <= iterationNumber; j++) { 250 | // {iteration, offset} 251 | blurStrengthValues.append({i + 1, blurOffsets[i].minOffset + (offsetDifference / iterationNumber) * j}); 252 | } 253 | } 254 | } 255 | 256 | void BlurEffect::reconfigure(ReconfigureFlags flags) 257 | { 258 | Q_UNUSED(flags) 259 | 260 | BlurConfig::self()->read(); 261 | 262 | int blurStrength = BlurConfig::blurStrength() - 1; 263 | m_downSampleIterations = blurStrengthValues[blurStrength].iteration; 264 | m_offset = blurStrengthValues[blurStrength].offset; 265 | m_expandSize = blurOffsets[m_downSampleIterations - 1].expandSize; 266 | m_noiseStrength = BlurConfig::noiseStrength(); 267 | m_topRadius = BlurConfig::topDecorationRadius(); 268 | m_bottomRadius = BlurConfig::bottomDecorationRadius(); 269 | m_scalingFactor = qMax(1.0, QGuiApplication::primaryScreen()->logicalDotsPerInch() / 96.0); 270 | 271 | updateTexture(); 272 | updateCornersRegion(); 273 | 274 | // Update all windows for the blur to take effect 275 | effects->addRepaintFull(); 276 | } 277 | 278 | void BlurEffect::updateBlurRegion(EffectWindow *w) const 279 | { 280 | QRegion region; 281 | bool valid = false; 282 | 283 | if (net_wm_blur_region != XCB_ATOM_NONE) { 284 | const QByteArray value = w->readProperty(net_wm_blur_region, XCB_ATOM_CARDINAL, 32); 285 | if (value.size() > 0 && !(value.size() % (4 * sizeof(uint32_t)))) { 286 | const uint32_t *cardinals = reinterpret_cast(value.constData()); 287 | for (unsigned int i = 0; i < value.size() / sizeof(uint32_t);) { 288 | int x = cardinals[i++]; 289 | int y = cardinals[i++]; 290 | int w = cardinals[i++]; 291 | int h = cardinals[i++]; 292 | region += QRect(x, y, w, h); 293 | } 294 | } 295 | valid = !value.isNull(); 296 | } 297 | 298 | KWaylandServer::SurfaceInterface *surf = w->surface(); 299 | 300 | if (surf && surf->blur()) { 301 | region = surf->blur()->region(); 302 | valid = true; 303 | } 304 | 305 | if (auto internal = w->internalWindow()) { 306 | const auto property = internal->property("kwin_blur"); 307 | if (property.isValid()) { 308 | region = property.value(); 309 | valid = true; 310 | } 311 | } 312 | 313 | // If the specified blur region is empty, enable blur for the whole window. 314 | if (region.isEmpty() && valid) { 315 | // Set the data to a dummy value. 316 | // This is needed to be able to distinguish between the value not 317 | // being set, and being set to an empty region. 318 | w->setData(WindowBlurBehindRole, 1); 319 | } else { 320 | w->setData(WindowBlurBehindRole, region); 321 | } 322 | } 323 | 324 | void BlurEffect::slotWindowAdded(EffectWindow *w) 325 | { 326 | KWaylandServer::SurfaceInterface *surf = w->surface(); 327 | 328 | if (surf) { 329 | windowBlurChangedConnections[w] = connect(surf, &KWaylandServer::SurfaceInterface::blurChanged, this, [this, w] () { 330 | if (w) { 331 | updateBlurRegion(w); 332 | } 333 | }); 334 | } 335 | if (auto internal = w->internalWindow()) { 336 | internal->installEventFilter(this); 337 | } 338 | 339 | updateBlurRegion(w); 340 | } 341 | 342 | void BlurEffect::slotWindowDeleted(EffectWindow *w) 343 | { 344 | auto it = windowBlurChangedConnections.find(w); 345 | if (it == windowBlurChangedConnections.end()) { 346 | return; 347 | } 348 | disconnect(*it); 349 | windowBlurChangedConnections.erase(it); 350 | } 351 | 352 | 353 | void BlurEffect::slotPropertyNotify(EffectWindow *w, long atom) 354 | { 355 | if (w && atom == net_wm_blur_region && net_wm_blur_region != XCB_ATOM_NONE) { 356 | updateBlurRegion(w); 357 | } 358 | } 359 | 360 | bool BlurEffect::eventFilter(QObject *watched, QEvent *event) 361 | { 362 | auto internal = qobject_cast(watched); 363 | if (internal && event->type() == QEvent::DynamicPropertyChange) { 364 | QDynamicPropertyChangeEvent *pe = static_cast(event); 365 | if (pe->propertyName() == "kwin_blur") { 366 | if (auto w = effects->findWindow(internal)) { 367 | updateBlurRegion(w); 368 | } 369 | } 370 | } 371 | return false; 372 | } 373 | 374 | bool BlurEffect::enabledByDefault() 375 | { 376 | GLPlatform *gl = GLPlatform::instance(); 377 | 378 | if (gl->isIntel() && gl->chipClass() < SandyBridge) 379 | return false; 380 | if (gl->isPanfrost() && gl->chipClass() <= MaliT8XX) { 381 | return false; 382 | } 383 | if (gl->isSoftwareEmulation()) { 384 | return false; 385 | } 386 | 387 | return true; 388 | } 389 | 390 | bool BlurEffect::supported() 391 | { 392 | bool supported = effects->isOpenGLCompositing() && GLRenderTarget::supported() && GLRenderTarget::blitSupported(); 393 | 394 | if (supported) { 395 | int maxTexSize; 396 | glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexSize); 397 | 398 | const QSize screenSize = effects->virtualScreenSize(); 399 | if (screenSize.width() > maxTexSize || screenSize.height() > maxTexSize) 400 | supported = false; 401 | } 402 | return supported; 403 | } 404 | 405 | QRect BlurEffect::expand(const QRect &rect) const 406 | { 407 | return rect.adjusted(-m_expandSize, -m_expandSize, m_expandSize, m_expandSize); 408 | } 409 | 410 | QRegion BlurEffect::expand(const QRegion ®ion) const 411 | { 412 | QRegion expanded; 413 | 414 | for (const QRect &rect : region) { 415 | expanded += expand(rect); 416 | } 417 | 418 | return expanded; 419 | } 420 | 421 | void BlurEffect::updateCornersRegion() 422 | { 423 | QRegion square = QRegion(0, 0, m_topRadius, m_topRadius); 424 | QRegion circle = QRegion(0, 0, 2*m_topRadius, 2*m_topRadius, QRegion::RegionType::Ellipse); 425 | m_topLeftCorner = QRegion(0, 0, m_topRadius, m_topRadius); 426 | m_topRightCorner = QRegion(0, 0, m_topRadius, m_topRadius); 427 | 428 | m_topLeftCorner &= circle; 429 | m_topLeftCorner ^= square; 430 | circle.translate(-m_topRadius, 0); 431 | m_topRightCorner &= circle; 432 | m_topRightCorner ^= square; 433 | 434 | square = QRegion(0, 0, m_bottomRadius, m_bottomRadius); 435 | circle = QRegion(0, -m_bottomRadius, 2*m_bottomRadius, m_bottomRadius, QRegion::RegionType::Ellipse); 436 | m_bottomLeftCorner = QRegion(0, 0, m_bottomRadius, m_bottomRadius); 437 | m_bottomRightCorner = QRegion(0, 0, m_bottomRadius, m_bottomRadius); 438 | 439 | m_bottomLeftCorner &= circle; 440 | m_bottomLeftCorner ^= square; 441 | circle.translate(-m_bottomRadius, 0); 442 | m_bottomRightCorner &= circle; 443 | m_bottomRightCorner ^= square; 444 | } 445 | 446 | QRegion BlurEffect::blurRegion(const EffectWindow *w) const 447 | { 448 | QRegion region; 449 | 450 | const QVariant value = w->data(WindowBlurBehindRole); 451 | if (value.isValid()) { 452 | const QRegion appRegion = qvariant_cast(value); 453 | if (!appRegion.isEmpty()) { 454 | if (w->decorationHasAlpha() && effects->decorationSupportsBlurBehind()) { 455 | region = QRegion(w->rect()) - w->decorationInnerRect(); 456 | } 457 | region |= appRegion.translated(w->contentsRect().topLeft()) & 458 | w->decorationInnerRect(); 459 | } else { 460 | // An empty region means that the blur effect should be enabled 461 | // for the whole window. 462 | region = w->rect(); 463 | } 464 | } else if (w->decorationHasAlpha() && effects->decorationSupportsBlurBehind()) { 465 | // If the client hasn't specified a blur region, we'll only enable 466 | // the effect behind the decoration. 467 | region = QRegion(w->rect()) - w->decorationInnerRect(); 468 | } 469 | 470 | bool isMaximized = effects->clientArea(MaximizeArea, effects->activeScreen(), effects->currentDesktop()) == w->frameGeometry(); 471 | bool hasBluredDecoration = w->hasDecoration() && w->decorationHasAlpha() && effects->decorationSupportsBlurBehind(); 472 | 473 | if (m_topRadius && !isMaximized && hasBluredDecoration){ 474 | QPoint topRightPosition = QPoint(w->rect().width() - m_topRadius, 0); 475 | region -= m_topLeftCorner; 476 | region -= m_topRightCorner.translated(topRightPosition); 477 | } 478 | 479 | if (m_bottomRadius && !isMaximized && hasBluredDecoration){ 480 | QPoint bottomLeftPosition = QPoint(0, w->rect().height() - m_bottomRadius); 481 | QPoint bottomRightPosition = QPoint(w->rect().width() - m_bottomRadius, w->rect().height() - m_bottomRadius); 482 | region -= m_bottomLeftCorner.translated(bottomLeftPosition); 483 | region -= m_bottomRightCorner.translated(bottomRightPosition); 484 | } 485 | 486 | return region; 487 | } 488 | 489 | 490 | 491 | void BlurEffect::uploadRegion(QVector2D *&map, const QRegion ®ion, const int downSampleIterations) 492 | { 493 | for (int i = 0; i <= downSampleIterations; i++) { 494 | const int divisionRatio = (1 << i); 495 | 496 | for (const QRect &r : region) { 497 | const QVector2D topLeft( r.x() / divisionRatio, r.y() / divisionRatio); 498 | const QVector2D topRight( (r.x() + r.width()) / divisionRatio, r.y() / divisionRatio); 499 | const QVector2D bottomLeft( r.x() / divisionRatio, (r.y() + r.height()) / divisionRatio); 500 | const QVector2D bottomRight((r.x() + r.width()) / divisionRatio, (r.y() + r.height()) / divisionRatio); 501 | 502 | // First triangle 503 | *(map++) = topRight; 504 | *(map++) = topLeft; 505 | *(map++) = bottomLeft; 506 | 507 | // Second triangle 508 | *(map++) = bottomLeft; 509 | *(map++) = bottomRight; 510 | *(map++) = topRight; 511 | } 512 | } 513 | } 514 | 515 | void BlurEffect::uploadGeometry(GLVertexBuffer *vbo, const QRegion &blurRegion, const QRegion &windowRegion) 516 | { 517 | const int vertexCount = ((blurRegion.rectCount() * (m_downSampleIterations + 1)) + windowRegion.rectCount()) * 6; 518 | 519 | if (!vertexCount) 520 | return; 521 | 522 | QVector2D *map = (QVector2D *) vbo->map(vertexCount * sizeof(QVector2D)); 523 | 524 | uploadRegion(map, blurRegion, m_downSampleIterations); 525 | uploadRegion(map, windowRegion, 0); 526 | 527 | vbo->unmap(); 528 | 529 | const GLVertexAttrib layout[] = { 530 | { VA_Position, 2, GL_FLOAT, 0 }, 531 | { VA_TexCoord, 2, GL_FLOAT, 0 } 532 | }; 533 | 534 | vbo->setAttribLayout(layout, 2, sizeof(QVector2D)); 535 | } 536 | 537 | void BlurEffect::prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime) 538 | { 539 | m_paintedArea = QRegion(); 540 | m_currentBlur = QRegion(); 541 | 542 | effects->prePaintScreen(data, presentTime); 543 | } 544 | 545 | void BlurEffect::prePaintWindow(EffectWindow* w, WindowPrePaintData& data, std::chrono::milliseconds presentTime) 546 | { 547 | // this effect relies on prePaintWindow being called in the bottom to top order 548 | 549 | effects->prePaintWindow(w, data, presentTime); 550 | 551 | if (!w->isPaintingEnabled()) { 552 | return; 553 | } 554 | if (!m_shader || !m_shader->isValid()) { 555 | return; 556 | } 557 | 558 | const QRegion oldClip = data.clip; 559 | if (data.clip.intersects(m_currentBlur)) { 560 | // to blur an area partially we have to shrink the opaque area of a window 561 | QRegion newClip; 562 | for (const QRect &rect : data.clip) { 563 | newClip |= rect.adjusted(m_expandSize, m_expandSize, -m_expandSize, -m_expandSize); 564 | } 565 | data.clip = newClip; 566 | 567 | // we don't have to blur a region we don't see 568 | m_currentBlur -= newClip; 569 | } 570 | 571 | // if we have to paint a non-opaque part of this window that intersects with the 572 | // currently blurred region we have to redraw the whole region 573 | if ((data.paint - oldClip).intersects(m_currentBlur)) { 574 | data.paint |= m_currentBlur; 575 | } 576 | 577 | // in case this window has regions to be blurred 578 | const QRect screen = effects->virtualScreenGeometry(); 579 | const QRegion blurArea = blurRegion(w).translated(w->pos()) & screen; 580 | const QRegion expandedBlur = (w->isDock() ? blurArea : expand(blurArea)) & screen; 581 | 582 | // if this window or a window underneath the blurred area is painted again we have to 583 | // blur everything 584 | if (m_paintedArea.intersects(expandedBlur) || data.paint.intersects(blurArea)) { 585 | data.paint |= expandedBlur; 586 | // we have to check again whether we do not damage a blurred area 587 | // of a window 588 | if (expandedBlur.intersects(m_currentBlur)) { 589 | data.paint |= m_currentBlur; 590 | } 591 | } 592 | 593 | m_currentBlur |= expandedBlur; 594 | 595 | m_paintedArea -= data.clip; 596 | m_paintedArea |= data.paint; 597 | } 598 | 599 | bool BlurEffect::shouldBlur(const EffectWindow *w, int mask, const WindowPaintData &data) const 600 | { 601 | if (!m_renderTargetsValid || !m_shader || !m_shader->isValid()) 602 | return false; 603 | 604 | if (effects->activeFullScreenEffect() && !w->data(WindowForceBlurRole).toBool()) 605 | return false; 606 | 607 | if (w->isDesktop()) 608 | return false; 609 | 610 | bool scaled = !qFuzzyCompare(data.xScale(), 1.0) && !qFuzzyCompare(data.yScale(), 1.0); 611 | bool translated = data.xTranslation() || data.yTranslation(); 612 | 613 | if ((scaled || (translated || (mask & PAINT_WINDOW_TRANSFORMED))) && !w->data(WindowForceBlurRole).toBool()) 614 | return false; 615 | 616 | bool blurBehindDecos = effects->decorationsHaveAlpha() && 617 | effects->decorationSupportsBlurBehind(); 618 | 619 | if (!w->hasAlpha() && w->opacity() >= 1.0 && !(blurBehindDecos && w->hasDecoration())) 620 | return false; 621 | 622 | return true; 623 | } 624 | 625 | void BlurEffect::drawWindow(EffectWindow *w, int mask, const QRegion ®ion, WindowPaintData &data) 626 | { 627 | const QRect screen = GLRenderTarget::virtualScreenGeometry(); 628 | if (shouldBlur(w, mask, data)) { 629 | QRegion shape = region & blurRegion(w).translated(w->pos()) & screen; 630 | 631 | // let's do the evil parts - someone wants to blur behind a transformed window 632 | const bool translated = data.xTranslation() || data.yTranslation(); 633 | const bool scaled = data.xScale() != 1 || data.yScale() != 1; 634 | if (scaled) { 635 | QPoint pt = shape.boundingRect().topLeft(); 636 | QRegion scaledShape; 637 | for (QRect r : shape) { 638 | r.moveTo(pt.x() + (r.x() - pt.x()) * data.xScale() + data.xTranslation(), 639 | pt.y() + (r.y() - pt.y()) * data.yScale() + data.yTranslation()); 640 | r.setWidth(r.width() * data.xScale()); 641 | r.setHeight(r.height() * data.yScale()); 642 | scaledShape |= r; 643 | } 644 | shape = scaledShape & region; 645 | 646 | //Only translated, not scaled 647 | } else if (translated) { 648 | shape = shape.translated(data.xTranslation(), data.yTranslation()); 649 | shape = shape & region; 650 | } 651 | 652 | EffectWindow* modal = w->transientFor(); 653 | const bool transientForIsDock = (modal ? modal->isDock() : false); 654 | 655 | if (!shape.isEmpty()) { 656 | doBlur(shape, screen, data.opacity(), data.screenProjectionMatrix(), w->isDock() || transientForIsDock, w->frameGeometry()); 657 | } 658 | } 659 | effects->drawWindow(w, mask, region, data); 660 | } 661 | 662 | void BlurEffect::paintEffectFrame(EffectFrame *frame, const QRegion ®ion, double opacity, double frameOpacity) 663 | { 664 | const QRect screen = effects->virtualScreenGeometry(); 665 | bool valid = m_renderTargetsValid && m_shader && m_shader->isValid(); 666 | 667 | QRegion shape = frame->geometry().adjusted(-borderSize, -borderSize, borderSize, borderSize) & screen; 668 | 669 | if (valid && !shape.isEmpty() && region.intersects(shape.boundingRect()) && frame->style() != EffectFrameNone) { 670 | doBlur(shape, screen, opacity * frameOpacity, frame->screenProjectionMatrix(), false, frame->geometry()); 671 | } 672 | effects->paintEffectFrame(frame, region, opacity, frameOpacity); 673 | } 674 | 675 | void BlurEffect::generateNoiseTexture() 676 | { 677 | if (m_noiseStrength == 0) { 678 | return; 679 | } 680 | 681 | // Init randomness based on time 682 | qsrand((uint)QTime::currentTime().msec()); 683 | 684 | QImage noiseImage(QSize(256, 256), QImage::Format_Grayscale8); 685 | 686 | for (int y = 0; y < noiseImage.height(); y++) { 687 | uint8_t *noiseImageLine = (uint8_t *) noiseImage.scanLine(y); 688 | 689 | for (int x = 0; x < noiseImage.width(); x++) { 690 | noiseImageLine[x] = qrand() % m_noiseStrength; 691 | } 692 | } 693 | 694 | // The noise texture looks distorted when not scaled with integer 695 | noiseImage = noiseImage.scaled(noiseImage.size() * m_scalingFactor); 696 | 697 | m_noiseTexture.reset(new GLTexture(noiseImage)); 698 | m_noiseTexture->setFilter(GL_NEAREST); 699 | m_noiseTexture->setWrapMode(GL_REPEAT); 700 | } 701 | 702 | void BlurEffect::doBlur(const QRegion& shape, const QRect& screen, const float opacity, const QMatrix4x4 &screenProjection, bool isDock, QRect windowRect) 703 | { 704 | // Blur would not render correctly on a secondary monitor because of wrong coordinates 705 | // BUG: 393723 706 | const int xTranslate = -screen.x(); 707 | const int yTranslate = effects->virtualScreenSize().height() - screen.height() - screen.y(); 708 | 709 | const QRegion expandedBlurRegion = expand(shape) & expand(screen); 710 | 711 | const bool useSRGB = m_renderTextures.first().internalFormat() == GL_SRGB8_ALPHA8; 712 | 713 | // Upload geometry for the down and upsample iterations 714 | GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); 715 | vbo->reset(); 716 | 717 | uploadGeometry(vbo, expandedBlurRegion.translated(xTranslate, yTranslate), shape); 718 | vbo->bindArrays(); 719 | 720 | const QRect sourceRect = expandedBlurRegion.boundingRect() & screen; 721 | const QRect destRect = sourceRect.translated(xTranslate, yTranslate); 722 | 723 | GLRenderTarget::pushRenderTargets(m_renderTargetStack); 724 | int blurRectCount = expandedBlurRegion.rectCount() * 6; 725 | 726 | /* 727 | * If the window is a dock or panel we avoid the "extended blur" effect. 728 | * Extended blur is when windows that are not under the blurred area affect 729 | * the final blur result. 730 | * We want to avoid this on panels, because it looks really weird and ugly 731 | * when maximized windows or windows near the panel affect the dock blur. 732 | */ 733 | if (isDock) { 734 | m_renderTargets.last()->blitFromFramebuffer(sourceRect, destRect); 735 | 736 | if (useSRGB) { 737 | glEnable(GL_FRAMEBUFFER_SRGB); 738 | } 739 | 740 | const QRect screenRect = effects->virtualScreenGeometry(); 741 | QMatrix4x4 mvp; 742 | mvp.ortho(0, screenRect.width(), screenRect.height(), 0, 0, 65535); 743 | copyScreenSampleTexture(vbo, blurRectCount, shape.translated(xTranslate, yTranslate), mvp); 744 | } else { 745 | m_renderTargets.first()->blitFromFramebuffer(sourceRect, destRect); 746 | 747 | if (useSRGB) { 748 | glEnable(GL_FRAMEBUFFER_SRGB); 749 | } 750 | 751 | // Remove the m_renderTargets[0] from the top of the stack that we will not use 752 | GLRenderTarget::popRenderTarget(); 753 | } 754 | 755 | downSampleTexture(vbo, blurRectCount); 756 | upSampleTexture(vbo, blurRectCount); 757 | 758 | // Modulate the blurred texture with the window opacity if the window isn't opaque 759 | if (opacity < 1.0) { 760 | glEnable(GL_BLEND); 761 | #if 1 // bow shape, always above y = x 762 | float o = 1.0f-opacity; 763 | o = 1.0f - o*o; 764 | #else // sigmoid shape, above y = x for x > 0.5, below y = x for x < 0.5 765 | float o = 2.0f*opacity - 1.0f; 766 | o = 0.5f + o / (1.0f + qAbs(o)); 767 | #endif 768 | glBlendColor(0, 0, 0, o); 769 | glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA); 770 | } 771 | 772 | upscaleRenderToScreen(vbo, blurRectCount * (m_downSampleIterations + 1), shape.rectCount() * 6, screenProjection, windowRect.topLeft()); 773 | 774 | if (useSRGB) { 775 | glDisable(GL_FRAMEBUFFER_SRGB); 776 | } 777 | 778 | if (opacity < 1.0) { 779 | glDisable(GL_BLEND); 780 | } 781 | 782 | if (m_noiseStrength > 0) { 783 | // Apply an additive noise onto the blurred image. 784 | // The noise is useful to mask banding artifacts, which often happens due to the smooth color transitions in the 785 | // blurred image. 786 | // The noise is applied in perceptual space (i.e. after glDisable(GL_FRAMEBUFFER_SRGB)). This practice is also 787 | // seen in other application of noise synthesis (films, image codecs), and makes the noise less visible overall 788 | // (reduces graininess). 789 | glEnable(GL_BLEND); 790 | if (opacity < 1.0) { 791 | // We need to modulate the opacity of the noise as well; otherwise a thin layer would appear when applying 792 | // effects like fade out. 793 | // glBlendColor should have been set above. 794 | glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE); 795 | } else { 796 | // Add the shader's output directly to the pixels in framebuffer. 797 | glBlendFunc(GL_ONE, GL_ONE); 798 | } 799 | applyNoise(vbo, blurRectCount * (m_downSampleIterations + 1), shape.rectCount() * 6, screenProjection, windowRect.topLeft()); 800 | glDisable(GL_BLEND); 801 | } 802 | 803 | vbo->unbindArrays(); 804 | } 805 | 806 | void BlurEffect::upscaleRenderToScreen(GLVertexBuffer *vbo, int vboStart, int blurRectCount, const QMatrix4x4 &screenProjection, QPoint windowPosition) 807 | { 808 | m_renderTextures[1].bind(); 809 | 810 | m_shader->bind(BlurShader::UpSampleType); 811 | m_shader->setTargetTextureSize(m_renderTextures[0].size() * GLRenderTarget::virtualScreenScale()); 812 | 813 | m_shader->setOffset(m_offset); 814 | m_shader->setModelViewProjectionMatrix(screenProjection); 815 | 816 | //Render to the screen 817 | vbo->draw(GL_TRIANGLES, vboStart, blurRectCount); 818 | m_shader->unbind(); 819 | } 820 | 821 | void BlurEffect::applyNoise(GLVertexBuffer *vbo, int vboStart, int blurRectCount, const QMatrix4x4 &screenProjection, QPoint windowPosition) 822 | { 823 | m_shader->bind(BlurShader::NoiseSampleType); 824 | m_shader->setTargetTextureSize(m_renderTextures[0].size() * GLRenderTarget::virtualScreenScale()); 825 | m_shader->setNoiseTextureSize(m_noiseTexture->size() * GLRenderTarget::virtualScreenScale()); 826 | m_shader->setTexturePosition(windowPosition * GLRenderTarget::virtualScreenScale()); 827 | 828 | m_noiseTexture->bind(); 829 | 830 | m_shader->setOffset(m_offset); 831 | m_shader->setModelViewProjectionMatrix(screenProjection); 832 | 833 | vbo->draw(GL_TRIANGLES, vboStart, blurRectCount); 834 | m_shader->unbind(); 835 | } 836 | 837 | void BlurEffect::downSampleTexture(GLVertexBuffer *vbo, int blurRectCount) 838 | { 839 | QMatrix4x4 modelViewProjectionMatrix; 840 | 841 | m_shader->bind(BlurShader::DownSampleType); 842 | m_shader->setOffset(m_offset); 843 | 844 | for (int i = 1; i <= m_downSampleIterations; i++) { 845 | modelViewProjectionMatrix.setToIdentity(); 846 | modelViewProjectionMatrix.ortho(0, m_renderTextures[i].width(), m_renderTextures[i].height(), 0 , 0, 65535); 847 | 848 | m_shader->setModelViewProjectionMatrix(modelViewProjectionMatrix); 849 | m_shader->setTargetTextureSize(m_renderTextures[i].size()); 850 | 851 | //Copy the image from this texture 852 | m_renderTextures[i - 1].bind(); 853 | 854 | vbo->draw(GL_TRIANGLES, blurRectCount * i, blurRectCount); 855 | GLRenderTarget::popRenderTarget(); 856 | } 857 | 858 | m_shader->unbind(); 859 | } 860 | 861 | void BlurEffect::upSampleTexture(GLVertexBuffer *vbo, int blurRectCount) 862 | { 863 | QMatrix4x4 modelViewProjectionMatrix; 864 | 865 | m_shader->bind(BlurShader::UpSampleType); 866 | m_shader->setOffset(m_offset); 867 | 868 | for (int i = m_downSampleIterations - 1; i >= 1; i--) { 869 | modelViewProjectionMatrix.setToIdentity(); 870 | modelViewProjectionMatrix.ortho(0, m_renderTextures[i].width(), m_renderTextures[i].height(), 0 , 0, 65535); 871 | 872 | m_shader->setModelViewProjectionMatrix(modelViewProjectionMatrix); 873 | m_shader->setTargetTextureSize(m_renderTextures[i].size()); 874 | 875 | //Copy the image from this texture 876 | m_renderTextures[i + 1].bind(); 877 | 878 | vbo->draw(GL_TRIANGLES, blurRectCount * i, blurRectCount); 879 | GLRenderTarget::popRenderTarget(); 880 | } 881 | 882 | m_shader->unbind(); 883 | } 884 | 885 | void BlurEffect::copyScreenSampleTexture(GLVertexBuffer *vbo, int blurRectCount, QRegion blurShape, const QMatrix4x4 &screenProjection) 886 | { 887 | m_shader->bind(BlurShader::CopySampleType); 888 | 889 | m_shader->setModelViewProjectionMatrix(screenProjection); 890 | m_shader->setTargetTextureSize(effects->virtualScreenSize()); 891 | 892 | /* 893 | * This '1' sized adjustment is necessary do avoid windows affecting the blur that are 894 | * right next to this window. 895 | */ 896 | m_shader->setBlurRect(blurShape.boundingRect().adjusted(1, 1, -1, -1), effects->virtualScreenSize()); 897 | m_renderTextures.last().bind(); 898 | 899 | vbo->draw(GL_TRIANGLES, 0, blurRectCount); 900 | GLRenderTarget::popRenderTarget(); 901 | 902 | m_shader->unbind(); 903 | } 904 | 905 | bool BlurEffect::isActive() const 906 | { 907 | return !effects->isScreenLocked(); 908 | } 909 | 910 | bool BlurEffect::blocksDirectScanout() const 911 | { 912 | return false; 913 | } 914 | 915 | } // namespace KWin 916 | 917 | --------------------------------------------------------------------------------