├── .gitignore ├── LICENSE ├── README.md ├── native └── Win10 │ ├── Win10 │ └── blur_main.cpp │ └── x64 │ └── Release │ └── javafxblur.dll ├── pom.xml ├── res ├── example.gif └── example2.gif └── src ├── main ├── header │ └── com_kieferlam_javafxblur_NativeBlur.h └── java │ └── com │ └── kieferlam │ └── javafxblur │ ├── Blur.java │ └── NativeBlur.java └── test └── java └── PrimaryStageBlurTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | .idea 3 | *.iml 4 | target 5 | *.suo 6 | *.db 7 | *.sln 8 | *.vcxproj* 9 | *.tlog 10 | *.log 11 | *.obj 12 | *.exp 13 | *.lib 14 | *.iobj 15 | *.ipdb 16 | *.ipch 17 | out 18 | release -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Kiefer Lam 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JavaFX Blur 2 | This library provides methods to apply a blur effect to a JavaFX stage using JNI to call the native window manager functions. 3 | 4 | 5 | 6 | 7 | ## Install 8 | To install, simply add the jar file as a dependency on your favourite IDE (add jar to classpath) and add the library files (.dll) to the library path. 9 | 10 | ## Usage 11 | The module load method should be called at the start of your program to ensure the native functions have been loaded before the external function calls. 12 | 13 | ```java 14 | public static void main(String[] args){ 15 | Blur.loadBlurLibrary(); 16 | // Launch JavaFX application 17 | } 18 | ``` 19 | 20 | Once your JavaFX stage is shown, you can call the blur method to apply the blur effect. The stage must be initialised as transparent (`StageStyle.TRANSPARENT`) or JavaFX will have an opaque window decoration background so the blur effect will not be visible. 21 | 22 | You might also need to set the backgrounds of the panes and scenes as transparent/none. 23 | ```java 24 | public void start(Stage primaryStage) { 25 | AnchorPane root = new AnchorPane(); 26 | root.setBackground(Background.EMPTY); 27 | 28 | Scene scene = new Scene(root, 640.0, 480.0); 29 | scene.setFill(Color.TRANSPARENT); 30 | 31 | primaryStage.initStyle(StageStyle.TRANSPARENT); 32 | 33 | primaryStage.setScene(scene); 34 | primaryStage.show(); 35 | 36 | // Blur effect must be called after the stage is visible 37 | Blur.applyBlur(primaryStage, Blur.ACRYLIC); 38 | } 39 | ``` 40 | 41 | The second argument of `applyBlur(stage, blurType)` decides which method of blur should be used. The following blur types are available: 42 | 43 | - TRANSPARENT 44 | - No blur, the window background is just transparent. 45 | - BLUR_BEHIND 46 | - Slight blur behind window (see example image) 47 | - ACRYLIC 48 | - Windows acrylic blur. Stronger blur than `BLUR_BEHIND`. 49 | 50 | ## Compatibility 51 | So far, only the native functions for Windows 10 have been provided. -------------------------------------------------------------------------------- /native/Win10/Win10/blur_main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "com_kieferlam_javafxblur_NativeBlur.h" 5 | 6 | std::string target = ""; 7 | int targetAccentState = 0; 8 | 9 | void SetWindowBlur(HWND hWnd, int accentState) 10 | { 11 | const HINSTANCE hModule = LoadLibrary(TEXT("user32.dll")); 12 | if (hModule) 13 | { 14 | struct ACCENTPOLICY 15 | { 16 | int nAccentState; 17 | int nFlags; 18 | int nColor; 19 | int nAnimationId; 20 | }; 21 | struct WINCOMPATTRDATA 22 | { 23 | int nAttribute; 24 | PVOID pData; 25 | ULONG ulDataSize; 26 | }; 27 | typedef BOOL(WINAPI*pSetWindowCompositionAttribute)(HWND, WINCOMPATTRDATA*); 28 | const pSetWindowCompositionAttribute SetWindowCompositionAttribute = (pSetWindowCompositionAttribute)GetProcAddress(hModule, "SetWindowCompositionAttribute"); 29 | if (SetWindowCompositionAttribute) 30 | { 31 | ACCENTPOLICY policy = { accentState, 0, 0x00FFFFFF, 0 }; // ACCENT_ENABLE_BLURBEHIND=3 / Colour in ARGB 32 | WINCOMPATTRDATA data = { 19, &policy, sizeof(ACCENTPOLICY) }; // WCA_ACCENT_POLICY=19 33 | SetWindowCompositionAttribute(hWnd, &data); 34 | 35 | 36 | } 37 | FreeLibrary(hModule); 38 | } 39 | } 40 | void enableBlur(HWND hWnd) { 41 | SetWindowBlur(hWnd, targetAccentState); 42 | } 43 | 44 | BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lParam) 45 | { 46 | char String[255]; 47 | 48 | if (!hWnd) 49 | return TRUE; // Not a window 50 | if (!IsWindowVisible(hWnd)) 51 | return TRUE; // Not visible 52 | if (!SendMessage(hWnd, WM_GETTEXT, sizeof(String), (LPARAM)String)) 53 | return TRUE; // No window title 54 | 55 | char pszClassName[64]; 56 | GetClassName(hWnd, pszClassName, 64); 57 | if (_stricmp(pszClassName, "shell_traywnd") && _stricmp(pszClassName, "progman")) { 58 | char windowTitle[64]; 59 | GetWindowText(hWnd, windowTitle, 64); 60 | if (!target.compare(windowTitle)) { 61 | enableBlur(hWnd); 62 | } 63 | } 64 | 65 | return 1; 66 | } 67 | 68 | 69 | JNIEXPORT void JNICALL Java_com_kieferlam_javafxblur_NativeBlur__1extApplyBlur 70 | (JNIEnv *env, jobject, jstring title, jint accentState) { 71 | 72 | const jchar *nativeString = env->GetStringChars(title, 0); 73 | targetAccentState = (int)accentState; 74 | 75 | char chars[256]; 76 | 77 | for (int i = 0; i < 256; ++i) { 78 | const jchar c = nativeString[i]; 79 | if (c == 0) break; 80 | chars[i] = c; 81 | } 82 | 83 | std::string string(chars); 84 | 85 | target = string; 86 | 87 | EnumWindows(EnumWindowsProc, 0); 88 | } -------------------------------------------------------------------------------- /native/Win10/x64/Release/javafxblur.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kieferlam/JavaFX-Blur/1cbc4e89dd8c203735e75a3851c7fe1284da2bfc/native/Win10/x64/Release/javafxblur.dll -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.kieferlam 8 | javafxblur 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 1.8 13 | 1.8 14 | 11 15 | 13 16 | 17 | 18 | 19 | 20 | org.openjfx 21 | javafx-controls 22 | ${javafx.version} 23 | 24 | 25 | 26 | 27 | 28 | org.apache.maven.plugins 29 | maven-compiler-plugin 30 | 3.8.1 31 | 32 | ${maven.compiler.release} 33 | 34 | 35 | 36 | org.openjfx 37 | javafx-maven-plugin 38 | 0.0.3 39 | 40 | org.openjfx.App 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /res/example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kieferlam/JavaFX-Blur/1cbc4e89dd8c203735e75a3851c7fe1284da2bfc/res/example.gif -------------------------------------------------------------------------------- /res/example2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kieferlam/JavaFX-Blur/1cbc4e89dd8c203735e75a3851c7fe1284da2bfc/res/example2.gif -------------------------------------------------------------------------------- /src/main/header/com_kieferlam_javafxblur_NativeBlur.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | /* Header for class com_kieferlam_javafxblur_NativeBlur */ 4 | 5 | #ifndef _Included_com_kieferlam_javafxblur_NativeBlur 6 | #define _Included_com_kieferlam_javafxblur_NativeBlur 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | /* 11 | * Class: com_kieferlam_javafxblur_NativeBlur 12 | * Method: _extApplyBlur 13 | * Signature: (Ljava/lang/String;I)V 14 | */ 15 | JNIEXPORT void JNICALL Java_com_kieferlam_javafxblur_NativeBlur__1extApplyBlur 16 | (JNIEnv *, jobject, jstring, jint); 17 | 18 | #ifdef __cplusplus 19 | } 20 | #endif 21 | #endif 22 | -------------------------------------------------------------------------------- /src/main/java/com/kieferlam/javafxblur/Blur.java: -------------------------------------------------------------------------------- 1 | package com.kieferlam.javafxblur; 2 | 3 | import javafx.stage.Stage; 4 | 5 | /** 6 | * Singleton handler enum class Blur. 7 | * This class provides global methods to load and apply blur effects to a JavaFX stage. 8 | */ 9 | public enum Blur { 10 | NONE(0), BLUR_BEHIND(3), ACRYLIC(4); 11 | private final int accentState; 12 | 13 | Blur(int accentState) { 14 | this.accentState = accentState; 15 | } 16 | 17 | private static final String BLUR_TARGET_PREFIX = "_JFX"; 18 | private static final NativeBlur _extBlur = new NativeBlur(); 19 | 20 | /** 21 | * Loads the required blur library. 22 | * This should be called at the very start of your main function. 23 | * The "javafxblur" library file should be added to your library path. 24 | */ 25 | public static void loadBlurLibrary(){ 26 | System.loadLibrary("javafxblur"); 27 | } 28 | 29 | private static void _extApplyBlur(String target, int accentState){ 30 | _extBlur._extApplyBlur(target, accentState); 31 | } 32 | 33 | /** 34 | * Calls the external (native) function to apply the blur effect to a JavaFX stage. 35 | * The JavaFX stage must be visible before this function is called. 36 | * If the stage is ever hidden (destroyed, not minimised), this function must be called again once visible. 37 | * @param stage 38 | */ 39 | public static void applyBlur(Stage stage, Blur blur){ 40 | if (!stage.isShowing()) System.err.println("Warning: blur effect was called on a hidden stage!"); 41 | String stageTitle = stage.getTitle(); 42 | String targetTitle = BLUR_TARGET_PREFIX + (System.currentTimeMillis() % 1000); 43 | stage.setTitle(targetTitle); 44 | _extApplyBlur(targetTitle, blur.accentState); 45 | stage.setTitle(stageTitle); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/kieferlam/javafxblur/NativeBlur.java: -------------------------------------------------------------------------------- 1 | package com.kieferlam.javafxblur; 2 | 3 | public class NativeBlur { 4 | 5 | protected NativeBlur(){} 6 | 7 | protected native void _extApplyBlur(String target, int accentState); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/test/java/PrimaryStageBlurTest.java: -------------------------------------------------------------------------------- 1 | import com.kieferlam.javafxblur.Blur; 2 | import javafx.application.Application; 3 | import javafx.beans.property.SimpleStringProperty; 4 | import javafx.beans.property.StringProperty; 5 | import javafx.geometry.Insets; 6 | import javafx.geometry.Point2D; 7 | import javafx.scene.Scene; 8 | import javafx.scene.control.Button; 9 | import javafx.scene.input.MouseEvent; 10 | import javafx.scene.layout.*; 11 | import javafx.scene.paint.Color; 12 | import javafx.scene.text.Font; 13 | import javafx.scene.text.Text; 14 | import javafx.stage.Stage; 15 | import javafx.stage.StageStyle; 16 | 17 | import java.util.concurrent.atomic.AtomicReference; 18 | 19 | public class PrimaryStageBlurTest extends Application { 20 | 21 | public static final Color titleBarColour = new Color(Color.SLATEGREY.getRed(), Color.SLATEGREY.getGreen(), Color.SLATEGREY.getBlue(), 0.5); 22 | public static final Background titleBarBackground = new Background(new BackgroundFill(titleBarColour, CornerRadii.EMPTY, Insets.EMPTY)); 23 | public static final Color highlightColour = new Color(Color.SLATEGREY.getRed(), Color.SLATEGREY.getGreen(), Color.SLATEGREY.getBlue(), 0.9); 24 | public static final Background highlightBackground = new Background(new BackgroundFill(highlightColour, CornerRadii.EMPTY, Insets.EMPTY)); 25 | public static final Color textColor = Color.GHOSTWHITE; 26 | 27 | public static void main(String[] args) { 28 | Blur.loadBlurLibrary(); 29 | launch(args); 30 | } 31 | 32 | public static Pane createTitleBar(Stage primaryStage, StringProperty titleProperty) { 33 | AnchorPane titlebar = new AnchorPane(); 34 | 35 | titlebar.setBackground(titleBarBackground); 36 | 37 | Text titleText = new Text(0, 0, titleProperty.getValue()); 38 | titleText.textProperty().bind(titleProperty); 39 | titleText.setFill(textColor); 40 | titleText.setFont(new Font(14.0)); 41 | HBox titleTextContainer = new HBox(); 42 | titleTextContainer.getChildren().add(titleText); 43 | 44 | HBox titlebarControls = new HBox(); 45 | Button minBtn = new Button("Min"); 46 | minBtn.setBackground(titleBarBackground); 47 | Button maxBtn = new Button("Max"); 48 | maxBtn.setBackground(titleBarBackground); 49 | Button closeBtn = new Button("Close"); 50 | closeBtn.setBackground(titleBarBackground); 51 | 52 | closeBtn.setOnAction(event -> primaryStage.close()); 53 | 54 | titlebarControls.getChildren().addAll(minBtn, maxBtn, closeBtn); 55 | titlebarControls.getChildren().forEach(node -> { 56 | ((Button)node).setTextFill(textColor); 57 | node.addEventHandler(MouseEvent.MOUSE_ENTERED, mouseEvent -> ((Button) node).setBackground(highlightBackground)); 58 | node.addEventHandler(MouseEvent.MOUSE_EXITED, mouseEvent -> ((Button) node).setBackground(titleBarBackground)); 59 | }); 60 | 61 | titlebar.getChildren().add(titleTextContainer); 62 | titlebar.getChildren().add(titlebarControls); 63 | 64 | AtomicReference stageDragOffset = new AtomicReference<>(new Point2D(0.0, 0.0)); 65 | titlebar.addEventHandler(MouseEvent.MOUSE_PRESSED, mouseEvent -> stageDragOffset.set(new Point2D(mouseEvent.getSceneX(), mouseEvent.getSceneY()))); 66 | titlebar.addEventHandler(MouseEvent.MOUSE_DRAGGED, mouseEvent ->{ 67 | primaryStage.setX(mouseEvent.getScreenX() - stageDragOffset.get().getX()); 68 | primaryStage.setY(mouseEvent.getScreenY() - stageDragOffset.get().getY()); 69 | }); 70 | 71 | AnchorPane.setLeftAnchor(titleTextContainer, 5.0); 72 | AnchorPane.setRightAnchor(titlebarControls, 0.0); 73 | 74 | return titlebar; 75 | } 76 | 77 | public static Pane createContentPane(Stage primaryStage){ 78 | BorderPane pane = new BorderPane(); 79 | 80 | pane.setBackground(new Background(new BackgroundFill(Color.gray(0.2, 0.5), CornerRadii.EMPTY, Insets.EMPTY))); 81 | 82 | Text content = new Text("Content"); 83 | content.setFont(new Font(72.0)); 84 | content.setFill(textColor); 85 | 86 | pane.setCenter(content); 87 | 88 | pane.prefWidthProperty().bind(primaryStage.widthProperty()); 89 | pane.prefHeightProperty().bind(primaryStage.heightProperty()); 90 | 91 | return pane; 92 | } 93 | 94 | public void start(Stage primaryStage) { 95 | Pane titlebar = createTitleBar(primaryStage, new SimpleStringProperty("JavaFX Blur Test")); 96 | 97 | Pane contentPane = createContentPane(primaryStage); 98 | 99 | AnchorPane root = new AnchorPane(); 100 | root.getChildren().addAll(titlebar, contentPane); 101 | AnchorPane.setTopAnchor(titlebar, 0.0); 102 | AnchorPane.setTopAnchor(contentPane, 24.0); 103 | 104 | root.setBackground(Background.EMPTY); 105 | Scene scene = new Scene(root, 640.0, 480.0); 106 | scene.setFill(Color.TRANSPARENT); 107 | 108 | titlebar.prefWidthProperty().bind(scene.widthProperty()); 109 | titlebar.prefHeight(24.0); 110 | 111 | primaryStage.initStyle(StageStyle.TRANSPARENT); 112 | primaryStage.setTitle("JavaFX"); 113 | 114 | primaryStage.setScene(scene); 115 | primaryStage.show(); 116 | 117 | Blur.applyBlur(primaryStage, Blur.ACRYLIC); 118 | } 119 | } 120 | --------------------------------------------------------------------------------