Browse Source

Added support for Mica Light & Dark based on Win11

pull/12196/head
Lighto 3 years ago
parent
commit
43e1d99bdd
  1. 3
      samples/ControlCatalog/MainView.xaml
  2. 9
      src/Avalonia.Controls/WindowTransparencyLevel.cs
  3. 3
      src/Windows/Avalonia.Win32/WinRT/Composition/D2DEffects.cs
  4. 104
      src/Windows/Avalonia.Win32/WinRT/Composition/WinUIEffectBase.cs
  5. 27
      src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindow.cs
  6. 8
      src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionShared.cs
  7. 75
      src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionUtils.cs
  8. 3
      src/Windows/Avalonia.Win32/WinRT/IBlurHost.cs
  9. 23
      src/Windows/Avalonia.Win32/WinRT/WinRTPropertyValue.cs
  10. 22
      src/Windows/Avalonia.Win32/WindowImpl.cs

3
samples/ControlCatalog/MainView.xaml

@ -226,7 +226,8 @@
<WindowTransparencyLevel>Transparent</WindowTransparencyLevel>
<WindowTransparencyLevel>Blur</WindowTransparencyLevel>
<WindowTransparencyLevel>AcrylicBlur</WindowTransparencyLevel>
<WindowTransparencyLevel>Mica</WindowTransparencyLevel>
<WindowTransparencyLevel>MicaLight</WindowTransparencyLevel>
<WindowTransparencyLevel>MicaDark</WindowTransparencyLevel>
</ComboBox.Items>
</ComboBox>
<ComboBox x:Name="FlowDirection"

9
src/Avalonia.Controls/WindowTransparencyLevel.cs

@ -33,9 +33,14 @@ public readonly record struct WindowTransparencyLevel
public static WindowTransparencyLevel AcrylicBlur { get; } = new(nameof(AcrylicBlur));
/// <summary>
/// The window background is based on desktop wallpaper tint with a blur. This will only work on Windows 11
/// The window background is based on desktop wallpaper tint with a light blur. This will only work on Windows 11
/// </summary>
public static WindowTransparencyLevel Mica { get; } = new(nameof(Mica));
public static WindowTransparencyLevel MicaLight { get; } = new(nameof(MicaLight));
/// <summary>
/// The window background is based on desktop wallpaper tint with a dark blur. This will only work on Windows 11
/// </summary>
public static WindowTransparencyLevel MicaDark { get; } = new(nameof(MicaDark));
public override string ToString()
{

3
src/Windows/Avalonia.Win32/WinRT/Composition/D2DEffects.cs

@ -28,6 +28,9 @@ namespace Avalonia.Win32.WinRT.Composition
public static readonly Guid CLSID_D2D1Border =
new Guid(0x2A2D49C0, 0x4ACF, 0x43C7, 0x8C, 0x6A, 0x7C, 0x4A, 0x27, 0x87, 0x4D, 0x27);
public static readonly Guid CLSID_D2D1Opacity =
new Guid("811d79a4-de28-4454-8094-c64685f8bd4c");
public static readonly Guid CLSID_D2D1Brightness =
new Guid(0x8CEA8D1E, 0x77B0, 0x4986, 0xB3, 0xB9, 0x2F, 0x0C, 0x0E, 0xAE, 0x78, 0x87);

104
src/Windows/Avalonia.Win32/WinRT/Composition/WinUIEffectBase.cs

@ -52,6 +52,110 @@ namespace Avalonia.Win32.WinRT.Composition
_sources = null;
}
}
class BorderEffect : WinUIEffectBase
{
private readonly int _x;
private readonly int _y;
public override Guid EffectId => D2DEffects.CLSID_D2D1Border;
public override uint PropertyCount => 2;
public BorderEffect(int x, int y, params IGraphicsEffectSource[] _sources):base(_sources)
{
_x = x;
_y = y;
}
public override IPropertyValue? GetProperty(uint index)
{
if (index == 0)
return new WinRTPropertyValue((uint)_x);
if (index == 1)
return new WinRTPropertyValue((uint)_y);
return null;
}
}
class BlendEffect : WinUIEffectBase
{
private readonly int _mode;
public BlendEffect(int mode, params IGraphicsEffectSource[] _sources) : base(_sources)
{
_mode = mode;
}
public override Guid EffectId => D2DEffects.CLSID_D2D1Blend;
public override uint PropertyCount => 1;
public override IPropertyValue? GetProperty(uint index)
{
if (index == 0)
return new WinRTPropertyValue((uint)_mode);
return null;
}
}
class CompositeStepEffect : WinUIEffectBase
{
private readonly float _mode;
public CompositeStepEffect(int mode, params IGraphicsEffectSource[] _sources) : base(_sources)
{
_mode = mode;
}
public override Guid EffectId => D2DEffects.CLSID_D2D1Composite;
public override uint PropertyCount => 1;
public override IPropertyValue? GetProperty(uint index)
{
if (index == 0)
return new WinRTPropertyValue((uint)_mode);
return null;
}
}
class OpacityEffect : WinUIEffectBase
{
private readonly float _opacity;
public OpacityEffect(float opacity, params IGraphicsEffectSource[] _sources) : base(_sources)
{
_opacity = opacity;
}
public override Guid EffectId => D2DEffects.CLSID_D2D1Opacity;
public override uint PropertyCount => 1;
public override IPropertyValue? GetProperty(uint index)
{
if (index == 0)
return new WinRTPropertyValue(_opacity);
return null;
}
}
class ColorSourceEffect : WinUIEffectBase
{
private readonly float[] _color;
public ColorSourceEffect(float[] color)
{
_color = color;
}
public override Guid EffectId => D2DEffects.CLSID_D2D1Flood;
public override uint PropertyCount => 1;
public override IPropertyValue? GetProperty(uint index)
{
if (index == 0)
return new WinRTPropertyValue(_color);
return null;
}
}
internal class WinUIGaussianBlurEffect : WinUIEffectBase
{

27
src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositedWindow.cs

@ -12,7 +12,8 @@ internal class WinUiCompositedWindow : IDisposable
public EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo WindowInfo { get; }
private readonly WinUiCompositionShared _shared;
private readonly ICompositionRoundedRectangleGeometry? _compositionRoundedRectangleGeometry;
private readonly IVisual? _mica;
private readonly IVisual? _micaLight;
private readonly IVisual? _micaDark;
private readonly IVisual _blur;
private readonly IVisual _visual;
private PixelSize _size;
@ -25,7 +26,8 @@ internal class WinUiCompositedWindow : IDisposable
{
_compositionRoundedRectangleGeometry?.Dispose();
_blur.Dispose();
_mica?.Dispose();
_micaLight?.Dispose();
_micaDark?.Dispose();
_visual.Dispose();
_surfaceBrush.Dispose();
_target.Dispose();
@ -50,14 +52,20 @@ internal class WinUiCompositedWindow : IDisposable
_target.SetRoot(containerVisual);
_blur = WinUiCompositionUtils.CreateBlurVisual(shared.Compositor, shared.BlurBrush);
if (shared.MicaBrush != null)
if (shared.MicaBrushLight != null)
{
_mica = WinUiCompositionUtils.CreateBlurVisual(shared.Compositor, shared.MicaBrush);
containerChildren.InsertAtTop(_mica);
_micaLight = WinUiCompositionUtils.CreateBlurVisual(shared.Compositor, shared.MicaBrushLight);
containerChildren.InsertAtTop(_micaLight);
}
if (shared.MicaBrushDark != null)
{
_micaDark = WinUiCompositionUtils.CreateBlurVisual(shared.Compositor, shared.MicaBrushDark);
containerChildren.InsertAtTop(_micaDark);
}
_compositionRoundedRectangleGeometry =
WinUiCompositionUtils.ClipVisual(shared.Compositor, backdropCornerRadius, _blur, _mica);
WinUiCompositionUtils.ClipVisual(shared.Compositor, backdropCornerRadius, _blur, _micaLight, _micaDark);
containerChildren.InsertAtTop(_blur);
using var spriteVisual = shared.Compositor.CreateSpriteVisual();
@ -79,12 +87,13 @@ internal class WinUiCompositedWindow : IDisposable
{
lock (_shared.SyncRoot)
{
_blur.SetIsVisible(blurEffect == BlurEffect.Acrylic
|| blurEffect == BlurEffect.Mica && _mica == null ?
|| (blurEffect == BlurEffect.MicaLight && _micaLight == null) ||
(blurEffect == BlurEffect.MicaDark && _micaDark == null) ?
1 :
0);
_mica?.SetIsVisible(blurEffect == BlurEffect.Mica ? 1 : 0);
_micaLight?.SetIsVisible(blurEffect == BlurEffect.MicaLight ? 1 : 0);
_micaDark?.SetIsVisible(blurEffect == BlurEffect.MicaDark ? 1 : 0);
}
}

8
src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionShared.cs

@ -9,6 +9,8 @@ internal class WinUiCompositionShared : IDisposable
public ICompositor5 Compositor5 { get; }
public ICompositorDesktopInterop DesktopInterop { get; }
public ICompositionBrush BlurBrush { get; }
public ICompositionBrush? MicaBrushLight { get; }
public ICompositionBrush? MicaBrushDark { get; }
public ICompositionBrush? MicaBrush { get; }
public object SyncRoot { get; } = new();
@ -21,14 +23,16 @@ internal class WinUiCompositionShared : IDisposable
Compositor = compositor.CloneReference();
Compositor5 = compositor.QueryInterface<ICompositor5>();
BlurBrush = WinUiCompositionUtils.CreateAcrylicBlurBackdropBrush(compositor);
MicaBrush = WinUiCompositionUtils.CreateMicaBackdropBrush(compositor);
MicaBrushLight = WinUiCompositionUtils.CreateMicaBackdropBrush(compositor, 242, 0.6f);
MicaBrushDark = WinUiCompositionUtils.CreateMicaBackdropBrush(compositor, 32, 0.8f);
DesktopInterop = compositor.QueryInterface<ICompositorDesktopInterop>();
}
public void Dispose()
{
BlurBrush.Dispose();
MicaBrush?.Dispose();
MicaBrushLight?.Dispose();
MicaBrushDark?.Dispose();
DesktopInterop.Dispose();
Compositor.Dispose();
Compositor5.Dispose();

75
src/Windows/Avalonia.Win32/WinRT/Composition/WinUiCompositionUtils.cs

@ -1,3 +1,4 @@
using System;
using System.Numerics;
using MicroCom.Runtime;
@ -5,16 +6,75 @@ namespace Avalonia.Win32.WinRT.Composition;
internal static class WinUiCompositionUtils
{
public static ICompositionBrush? CreateMicaBackdropBrush(ICompositor compositor)
public static ICompositionBrush? CreateMicaBackdropBrush(ICompositor compositor, float color, float opacity)
{
if (Win32Platform.WindowsVersion.Build < 22000)
return null;
using var backDropParameterFactory =
NativeWinRTMethods.CreateActivationFactory<ICompositionEffectSourceParameterFactory>(
"Windows.UI.Composition.CompositionEffectSourceParameter");
var tint = new[] { color / 255f, color / 255f, color / 255f, 255f / 255f };
using var tintColorEffect = new ColorSourceEffect(tint);
using var tintOpacityEffect = new OpacityEffect(1.0f, tintColorEffect);
using var tintOpacityEffectFactory = compositor.CreateEffectFactory(tintOpacityEffect);
using var tintOpacityEffectBrushEffect = tintOpacityEffectFactory.CreateBrush();
using var tintOpacityEffectBrush = tintOpacityEffectBrushEffect.QueryInterface<ICompositionBrush>();
using var luminosityColorEffect = new ColorSourceEffect(tint);
using var luminosityOpacityEffect = new OpacityEffect(opacity, luminosityColorEffect);
using var luminosityOpacityEffectFactory = compositor.CreateEffectFactory(luminosityOpacityEffect);
using var luminosityOpacityEffectBrushEffect = luminosityOpacityEffectFactory.CreateBrush();
using var luminosityOpacityEffectBrush =
luminosityOpacityEffectBrushEffect.QueryInterface<ICompositionBrush>();
// using var backDropParameterAsSource = GetParameterSource("BlurredWallpaperBackdrop", backDropParameterFactory, out var backdropHandle);
// using var backdropCompositionBrsuh = backDropParameterAsSource.QueryInterface<ICompositionBrush>();
using var compositorWithBlurredWallpaperBackdropBrush =
compositor.QueryInterface<ICompositorWithBlurredWallpaperBackdropBrush>();
using var blurredWallpaperBackdropBrush =
compositorWithBlurredWallpaperBackdropBrush?.TryCreateBlurredWallpaperBackdropBrush();
return blurredWallpaperBackdropBrush?.QueryInterface<ICompositionBrush>();
using var micaBackdropBrush = blurredWallpaperBackdropBrush?.QueryInterface<ICompositionBrush>();
using var backgroundParameterAsSource =
GetParameterSource("Background", backDropParameterFactory, out var backgroundHandle);
using var foregroundParameterAsSource =
GetParameterSource("Foreground", backDropParameterFactory, out var foregroundHandle);
using var luminosityBlendEffect =
new BlendEffect(23, backgroundParameterAsSource, foregroundParameterAsSource);
using var luminosityBlendEffectFactory = compositor.CreateEffectFactory(luminosityBlendEffect);
using var luminosityBlendEffectBrush = luminosityBlendEffectFactory.CreateBrush();
using var luminosityBlendEffectBrush1 = luminosityBlendEffectBrush.QueryInterface<ICompositionBrush>();
luminosityBlendEffectBrush.SetSourceParameter(backgroundHandle, micaBackdropBrush);
luminosityBlendEffectBrush.SetSourceParameter(foregroundHandle, luminosityOpacityEffectBrush);
using var backgroundParameterAsSource1 =
GetParameterSource("Background", backDropParameterFactory, out var backgroundHandle1);
using var foregroundParameterAsSource1 =
GetParameterSource("Foreground", backDropParameterFactory, out var foregroundHandle1);
using var colorBlendEffect =
new BlendEffect(22, backgroundParameterAsSource1, foregroundParameterAsSource1);
using var colorBlendEffectFactory = compositor.CreateEffectFactory(colorBlendEffect);
using var colorBlendEffectBrush = colorBlendEffectFactory.CreateBrush();
colorBlendEffectBrush.SetSourceParameter(backgroundHandle1, luminosityBlendEffectBrush1);
colorBlendEffectBrush.SetSourceParameter(foregroundHandle1, tintOpacityEffectBrush);
// colorBlendEffectBrush.SetSourceParameter(backgroundHandle, micaBackdropBrush);
using var micaBackdropBrush1 = colorBlendEffectBrush.QueryInterface<ICompositionBrush>();
return micaBackdropBrush1.CloneReference();
}
public static ICompositionBrush CreateAcrylicBlurBackdropBrush(ICompositor compositor)
@ -97,4 +157,15 @@ internal static class WinUiCompositionUtils
brush?.Dispose();
}
}
private static IGraphicsEffectSource GetParameterSource(string name,
ICompositionEffectSourceParameterFactory backDropParameterFactory, out IntPtr handle)
{
var backdropString = new HStringInterop(name);
var backDropParameter =
backDropParameterFactory.Create(backdropString.Handle);
var backDropParameterAsSource = backDropParameter.QueryInterface<IGraphicsEffectSource>();
handle = backdropString.Handle;
return backDropParameterAsSource;
}
}

3
src/Windows/Avalonia.Win32/WinRT/IBlurHost.cs

@ -4,7 +4,8 @@
{
None,
Acrylic,
Mica
MicaLight,
MicaDark
}
internal interface IBlurHost

23
src/Windows/Avalonia.Win32/WinRT/WinRTPropertyValue.cs

@ -1,4 +1,5 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Avalonia.Win32.WinRT
@ -16,7 +17,15 @@ namespace Avalonia.Win32.WinRT
UInt32 = u;
Type = PropertyType.UInt32;
}
public WinRTPropertyValue(float[] uiColor)
{
Type = PropertyType.SingleArray;
_singleArray = uiColor;
}
private readonly float[]? _singleArray;
public PropertyType Type { get; }
public int IsNumericScalar { get; }
public byte UInt8 { get; }
@ -62,7 +71,17 @@ namespace Avalonia.Win32.WinRT
public unsafe ulong* GetUInt64Array(uint* __valueSize) => throw NotImplemented;
public unsafe float* GetSingleArray(uint* __valueSize) => throw NotImplemented;
public unsafe float* GetSingleArray(uint* __valueSize)
{
if (_singleArray == null)
throw NotImplemented;
*__valueSize = (uint)_singleArray.Length;
var allocCoTaskMem = Marshal.AllocCoTaskMem(_singleArray.Length * Unsafe.SizeOf<float>());
Marshal.Copy(_singleArray, 0, allocCoTaskMem, _singleArray.Length);
float* s = (float*)allocCoTaskMem;
return s;
}
public unsafe double* GetDoubleArray(uint* __valueSize) => throw NotImplemented;

22
src/Windows/Avalonia.Win32/WindowImpl.cs

@ -374,8 +374,10 @@ namespace Avalonia.Win32
SetTransparencyBlur(windowsVersion);
else if (level == WindowTransparencyLevel.AcrylicBlur)
SetTransparencyAcrylicBlur(windowsVersion);
else if (level == WindowTransparencyLevel.Mica)
SetTransparencyMica(windowsVersion);
else if (level == WindowTransparencyLevel.MicaLight)
SetTransparencyMicaLight(windowsVersion);
else if (level == WindowTransparencyLevel.MicaDark)
SetTransparencyMicaDark(windowsVersion);
TransparencyLevel = level;
return;
@ -418,7 +420,7 @@ namespace Avalonia.Win32
return windowsVersion >= WinUiCompositionShared.MinAcrylicVersion;
// Mica is supported on Windows >= 10.0.22000.
if (level == WindowTransparencyLevel.Mica)
if (level == WindowTransparencyLevel.MicaLight || level == WindowTransparencyLevel.MicaDark)
return windowsVersion >= WinUiCompositionShared.MinHostBackdropVersion;
return false;
@ -467,14 +469,24 @@ namespace Avalonia.Win32
_blurHost?.SetBlur(BlurEffect.Acrylic);
}
private void SetTransparencyMica(Version windowsVersion)
private void SetTransparencyMicaLight(Version windowsVersion)
{
// Mica only supported with composition on Windows >= 10.0.22000.
if (!_isUsingComposition || windowsVersion < WinUiCompositionShared.MinHostBackdropVersion)
return;
SetUseHostBackdropBrush(false);
_blurHost?.SetBlur(BlurEffect.Mica);
_blurHost?.SetBlur(BlurEffect.MicaLight);
}
private void SetTransparencyMicaDark(Version windowsVersion)
{
// Mica only supported with composition on Windows >= 10.0.22000.
if (!_isUsingComposition || windowsVersion < WinUiCompositionShared.MinHostBackdropVersion)
return;
SetUseHostBackdropBrush(false);
_blurHost?.SetBlur(BlurEffect.MicaDark);
}
private void SetAccentState(AccentState state)

Loading…
Cancel
Save