tcw3-calc, a Rust application
Many modern UI designs feature a blurry window background. This page is a collection of my findings on regard to how to achieve this effect in your application in a standard or non-standard way.
This page does not intend to answer basic questions such as why this effect requires cooperation with a compositor and why applications can't just implement the effect, or provide an end-user-level guide.
The new UI design introduced in Windows Vista, dubbed Windows Aero Glass, applied a transparency effect on a window border1. This is implemented by a newly-introduced desktop compositor named Desktop Window Manager (DWM). DWM provides an API that allows applications to control the composition in limited ways such as enabling/disabling the desktop composition and customizing the region within a window where the blur-behind effect is applied.
This UI design remained until Windows 8, where it was replaced by a completely new UI design language named Metro. It was much simpler and lost most of Windows Aero's transparency effects including the frosted glass effect of window borders. DWM was still in use, but even more, they decided to remove the option of disabling desktop composition2. Systems with graphics hardware incapable of desktop composition are supplemented with WARP (Windows Advanced Rasterization Platform), a very performant software rasterizer.
It's Windows 8 where DirectComposition, a new API for interacting with the desktop compositor was added. Unlike the DWM API, DirectComposition offers applications a full capability to construct a tree of visual contents to be directly composited by DWM.
Windows 10 changed the UI design once again. The blur effect made its way back as a part of its renewed design language. Meanwhile, the UWP namespace
Windows.UI.Composition was added in some version of Windows 10, available for both of UWP and Win32 applications. This API may seem like a higher-level interface to DWM, but actually exposes more functionalities than DirectComposition does.
Windows.UI.Composition offers an API for creating an effect graph (which is a subset of what Direct2D can do) to apply on an application-provided visual or an image behind a window.
In Windows terminology, this is called a non-client region.
It's still possible to disable desktop composition by editing a registry, but this is not supported by Microsoft and breaks many applications, rendering the system unusable.
DwmExtendFrameIntoClientArea function enlarges specified sides of a window border to overlap with a client region. On Windows 7 and earlier, this meant the enlarged portion would have a frosty glass effect. However, it doesn't work that way as of Windows 8 where window borders don't have this effect anymore—the borders are rendered just as an opaque, solid color.
Rafael Rivera, “Adding the "Aero Glass" blur to your Windows 10 apps”
user32.dll provides an undocumented function named
SetWindowCompositionAttribute that lets you enable the blur-behind effect in Windows 10. See 3 for the usage.
Windows.UI.Composition, also known as the Visual layer, is a retained-mode graphics API provided as a part of UWP APIs. Although it's a part of UWP APIs, it's also designed to be used from non-UWP applications, and Microsoft provides a usage guide for each of WPF, Windows Forms, and Win32 applications.
A visual tree constructed through
Windows.UI.Composition is rendered by DWM. This provides many advantages such as that applications don't have to include a component for composition by themselves, that the UI can be rendered more efficiently because there is no need for an intermediate buffer to hold the contents of a window, that a hidden portion of a window doesn't have to be rendered at all, and finally that it makes it possible to have a layer that applies an effect on a background image originating from other applications.
Windows.UI.Composition is an extremely flexible API, but that means you need to take many steps to get desired results. The following diagram summarizes the objects and their relationship created by the steps described below.
The following is the outline of the initialization steps to use
Windows.UI.Composition in a non-UWP application. Each step is thoroughly explained by the aforementioned guide by Microsoft.
- Create a
CreateDispatcherQueueControllerfunction. The dispatch queue will be associated to the calling thread.
- Create a
Compositoron the same thread as where you called
- Create a
ICompositorDesktopInteropcan be obtained by
ICompositorDesktopInterop::CreateDesktopWindowTarget is defined as follows:
// IID: 29E691FA-4567-4DCA-B319-D0F207EB6807 // Defined in `windows.ui.composition.interop.h` ;
isTopmost specifies one of two predefined composition layers:
FALSE for the one between the target window and the target window's child windows, and
TRUE for the one above the child windows.
Notice that there are no layers behind the target window. This means that to achieve the backdrop blur effect, you have to draw the foreground contents in a child window or as a top composition layer.
Before creating a
Visual, you first need to create an effect graph.
- Create a
CompositionEffectSourceParameterrepresenting an abstract input to the graph. You specify the name of the input.
"backdrop"would be appropriate in this case. You'll need the name later when you assign the actual input.
- Create a
GaussianBlurEffectrepresenting the node for a Gaussian blur effect. Assign the
CompositionEffectSourceParameterobject you just created to the
GaussianBlurEffectis a part of Win2D, a separately distributed library, so you will have to get it by NuGet or other means. Actually, you can make one by yourself. See the section Creating A Custom Effect Class for how to do that.
- Configure some other properties of the
- Create a
CompositorEffectFactoryfrom the root node (in this case, the
GaussianBlurEffectobject) by calling
Make sure your window has a transparent portion. For a Win32 application, the easiest way to see the effect is to give the window a
WS_EX_NOREDIRECTIONBITMAP extended style, which will completely remove a redirection bitmap from the window, only leaving composition layers.
- Create a
- Assign this
CompositionBackdropBrushas an input to the effect graph by calling
There is a similarly named method called
CreateHostBackdropBrush, but the documentation is unclear on their difference. I tried it in a Win32 app, but it didn't work. Maybe it's for a UWP app.
Now you need to create a visual to display this brush. Create and compose objects in the following way:
- Create a
- Set the visual to a desired size by assigning the
- Assign the
CompositionEffectBrushobject to the
Finally, assign the
SpriteVisual to the
Root property of the
DesktopWindowTarget you created earlier to attach the visual to the window.
Like the Win32 API, the composition API represent coordinates in physical pixels. However, effect graphs don't seem to follow this rule. Gaussian blur radii are always calculated in logical pixels, regardless of the target window's DPI or a scaling transformation applied to the sprite visual.
🔗Simulating The Acrylic Material
The Acrylic material is a part of UWP's design language. As explained on this page, it's more than just a Gaussian blur:
We fine-tuned acrylic’s key components to arrive at its unique appearance and properties. We started with translucency, blur and noise to add visual depth and dimension to flat surfaces. We added an exclusion blend mode layer to ensure contrast and legibility of UI placed on an acrylic background.
🔗Creating A Custom Effect Class
ICompositor::CreateEffectFactory, you need to provide an effect graph comprised of “supported effect types”, which are all defined in Win2D instead of the UWP API. Linking to Win2D can be difficult when you are using a non-standard development tool, e.g., when you are writing a Win32 application in Rust. So a question arises: How does the system recognize those supported effect types? What does a custom type need to disguise as one of those types? The documentation mentions the existence of “a Win2D effect description format”, but doesn't go deeper than that.
To answer this question, I did some experiments and found that the following interfaces are essential to the effect type:
The following methods are required:
IUnknown::*: They are required to support a COM class.
IGraphicsEffect::get_Name: I'm not sure why this method is used. You can just return a null pointer.
IGraphicsEffectD2D1Interop::GetEffectId: It should return the CLSID of the Direct2D effect the class represents (e.g.,
IGraphicsEffectD2D1Interop::GetPropertyCount: It should return the number of properties the Direct2D effect has.
IGraphicsEffectD2D1Interop::GetProperty: It should return a property value for a given property index (e.g.,
IGraphicsEffectD2D1Interop::GetSourceCount: It should return the number of inputs the effect accepts. It's usually constant for each effect type.
IGraphicsEffectD2D1Interop::GetSource: It should return a
IGraphicsEffectSourceobject representing the input at a given index in range
[0, GetSourceCount() - 1]. It can be
E_UNEXPECTED if it detects that some of them are incorrectly implemented.
A Rust implementation can be found in my project's source code.
DirectComposition looks like a lower-level API of
Windows.UI.Composition seems to be indeed backed by DirectComposition as I observed during the performance profiling of an application, the API surface exposed by DirectComposition is quite limited.
A Microsoft employee stated5 that they “do not have any immediate plans to add HostBackdropBrush to the DirectComposition API set.” Thus, DirectComposition can't be used to achieve the backdrop blur effect.
“We do not have any immediate plans to add HostBackdropBrush to the DirectComposition API set. [⋯]” — msftdavid commented on Jun 1, 2017
macOS has included a compositing window manager starting from the very first Mac OS X (macOS was called Mac OS X back then), but much of its potential has remained undocumented and hidden. The compositor has had a backdrop blur capability since Mac OS X 10.4 Tiger6.
The usage of the blur effect in standard UI elements was quite limited in earlier versions of macOS, but gradually increased in later versions. In macOS 10.6 Snow Leopard, sheets and menus acquired this effect, although it was quite subtle7. In macOS 10.15 Catalina, the following UI elements have the blur effect: Dock, menus, and sidebars, and a certain type of windows, and the effect is more emphasized than earlier versions.
The compositor includes a rendering path for software rendering used in uncommon circumstances such as when the system was booted from macOS Recovery or when forcefully enabled using Quartz Debug, a debugging tool distributed as a part of Xcode Tools. Unlike DWM, the software rendering path uses its own rendering code rather than a generic software renderer implementing a common 3D graphics API (although macOS does have one for OpenGL). Backdrop blur is not implemented by the software rendering path and renders as normal transparency or as opaque.
Some people reported high CPU usage when using the blur effect in iTerm28. This can be partly attributed to the fact that iTerm2 sets
[NSColor clearColor] when some panels are transparent, which does much harm to performance9. There may be other causes (I tried, but I haven't been able to figure out them yet), but in any case, it's definitely not because the effect is rendered on CPU. The effect is in fact rendered on GPU.
“Correction; OS X has had this ability since at least 10.4, which was the first version that blurminal worked with.” Posted by u/unregisteredusr
“[⋯] OSX has had this feature since 10.6, but you can only see it in sheets and menus, which are just barely transparent. [⋯]” Posted by u/TomorrowPlusX
“[⋯] The blurring always incurs a substantial cpu load increase. [⋯]” — unphased commented on Jan 4, 2018
See the section
NSVisualEffectView is the only supported way to achieve this effect. When added to a window,
NSVisualEffectView punches through other views and reveals the blurred image from the background. You can specify the appearance of the view by choosing one of the predefined materials defined in
NSVisualEffectMaterial. Some materials correspond to common UI elements such as a sidebar. Note that the appearance is affected by the user's system preference as well as the containing window's focus state (e.g., the view turns opaque when the window is inactive).
CGSSetWindowBackgroundBlurRadius function applies a blur effect on non-fully-transparent pixels of a window's contents. This solution is used by iTerm2 and even by
Terminal.app. The usage is quite simple:
typedef void *CGSConnection; extern OSStatus ; extern CGSConnection ; CGSConnection connection = ; ;
You also have to remove the default opaque background:
window.backgroundColor = ; window.opaque = NO;
The alpha value must not be zero for two reasons: (1) Fully transparent pixels will not receive the blur effect. (2) It has serious performance implications. When the alpha value of
NSWindow.backgroundColor is set to zero,
NSWindow enters a special mode for irregularly-shaped windows. In this mode, a window shadow is generated on-the-fly based on the window contents, and this is extremely slow.
Terminal.app avoids this by setting the alpha value to a slightly higher value.
Note that this is a private API, meaning applications using it will be rejected by Mac App Store10.
“Dont use this solution. My App rejected due to use of this API.” – Mrug Oct 21 '15 at 10:30
Unfortunately, the situation surrounding Linux systems is not good. To my knowledge, there is no reliable, portable way to enable the backdrop blur effect.
_KDE_NET_WM_BLUR_BEHIND_REGION X11 atom, implemented by KWin, turns on the effect. You can set this even from a command-line using
for; do ; done
compton seems to provide the effect as a global option
On Windows, use
Windows.UI.Composition, which is flexible enough to support every specific use case. If you can't afford the extra coding effort or need to support older versions of Windows, consider using
On macOS, use
CGSSetWindowBackgroundBlurRadius if you need a (somewhat) high level of customization and can tolerate the use of a private API. Otherwise, use
For Linux, design your UI not to rely on a backdrop blur effect. Or, use
_KDE_NET_WM_BLUR_BEHIND_REGION with a notice to the user saying the effect works only if supported by their window manager.