diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm
index fb945a105e..67b1ea50a6 100644
--- a/native/Avalonia.Native/src/OSX/window.mm
+++ b/native/Avalonia.Native/src/OSX/window.mm
@@ -1338,6 +1338,12 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
}
_parent->BaseEvents->RunRenderPriorityJobs();
+
+ if (_parent == nullptr)
+ {
+ return;
+ }
+
_parent->BaseEvents->Paint();
}
diff --git a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
index 2752703e21..d5aedf7783 100644
--- a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
+++ b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
@@ -12,7 +12,7 @@
-
+
diff --git a/samples/ControlCatalog.NetCore/Program.cs b/samples/ControlCatalog.NetCore/Program.cs
index c1fcdd6216..675ea2e10f 100644
--- a/samples/ControlCatalog.NetCore/Program.cs
+++ b/samples/ControlCatalog.NetCore/Program.cs
@@ -7,12 +7,11 @@ using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Dialogs;
using Avalonia.Headless;
using Avalonia.LogicalTree;
-using Avalonia.Skia;
using Avalonia.ReactiveUI;
using Avalonia.Threading;
-using Avalonia.Dialogs;
namespace ControlCatalog.NetCore
{
diff --git a/samples/interop/NativeEmbedSample/NativeEmbedSample.csproj b/samples/interop/NativeEmbedSample/NativeEmbedSample.csproj
index 0e0e71fbb2..c25442b52c 100644
--- a/samples/interop/NativeEmbedSample/NativeEmbedSample.csproj
+++ b/samples/interop/NativeEmbedSample/NativeEmbedSample.csproj
@@ -12,7 +12,7 @@
-
+
Designer
diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs b/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs
index 77f23392e9..7f8d205949 100644
--- a/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs
@@ -438,7 +438,7 @@ namespace Avalonia.Controls
_lastMousePositionHeaders = mousePositionHeaders;
- if (args.Pointer.Captured != this)
+ if (args.Pointer.Captured != this && _dragMode == DragMode.Drag)
args.Pointer.Capture(this);
SetDragCursor(mousePosition);
diff --git a/src/Avalonia.Controls/ApiCompatBaseline.txt b/src/Avalonia.Controls/ApiCompatBaseline.txt
index af88c569a6..16c801201c 100644
--- a/src/Avalonia.Controls/ApiCompatBaseline.txt
+++ b/src/Avalonia.Controls/ApiCompatBaseline.txt
@@ -8,6 +8,12 @@ MembersMustExist : Member 'public void Avalonia.Controls.ListBox.Selection.set(A
TypesMustExist : Type 'Avalonia.Controls.SelectionModel' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'Avalonia.Controls.SelectionModelChildrenRequestedEventArgs' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'Avalonia.Controls.SelectionModelSelectionChangedEventArgs' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public Avalonia.StyledProperty Avalonia.StyledProperty Avalonia.Controls.SplitView.ContentProperty' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public Avalonia.StyledProperty Avalonia.StyledProperty Avalonia.Controls.SplitView.PaneProperty' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public Avalonia.Controls.IControl Avalonia.Controls.SplitView.Content.get()' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public void Avalonia.Controls.SplitView.Content.set(Avalonia.Controls.IControl)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public Avalonia.Controls.IControl Avalonia.Controls.SplitView.Pane.get()' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'public void Avalonia.Controls.SplitView.Pane.set(Avalonia.Controls.IControl)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.DirectProperty Avalonia.DirectProperty Avalonia.Controls.TreeView.SelectionProperty' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.Interactivity.RoutedEvent Avalonia.Interactivity.RoutedEvent Avalonia.Controls.TreeView.SelectionChangedEvent' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.Controls.ISelectionModel Avalonia.Controls.TreeView.Selection.get()' does not exist in the implementation but it does exist in the contract.
@@ -17,4 +23,4 @@ InterfacesShouldHaveSameMembers : Interface member 'public System.String[] Avalo
MembersMustExist : Member 'public Avalonia.DirectProperty Avalonia.DirectProperty Avalonia.Controls.Primitives.SelectingItemsControl.SelectionProperty' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'protected Avalonia.Controls.ISelectionModel Avalonia.Controls.Primitives.SelectingItemsControl.Selection.get()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'protected void Avalonia.Controls.Primitives.SelectingItemsControl.Selection.set(Avalonia.Controls.ISelectionModel)' does not exist in the implementation but it does exist in the contract.
-Total Issues: 18
+Total Issues: 24
diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs
index 6a6d37605d..078d8050bf 100644
--- a/src/Avalonia.Controls/Presenters/TextPresenter.cs
+++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs
@@ -4,6 +4,7 @@ using Avalonia.Media;
using Avalonia.Metadata;
using Avalonia.Threading;
using Avalonia.VisualTree;
+using Avalonia.Layout;
namespace Avalonia.Controls.Presenters
{
@@ -312,7 +313,24 @@ namespace Avalonia.Controls.Presenters
context.FillRectangle(background, new Rect(Bounds.Size));
}
- context.DrawText(Foreground, new Point(), FormattedText);
+ double top = 0;
+ var textSize = FormattedText.Bounds.Size;
+
+ if (Bounds.Height < textSize.Height)
+ {
+ switch (VerticalAlignment)
+ {
+ case VerticalAlignment.Center:
+ top += (Bounds.Height - textSize.Height) / 2;
+ break;
+
+ case VerticalAlignment.Bottom:
+ top += (Bounds.Height - textSize.Height);
+ break;
+ }
+ }
+
+ context.DrawText(Foreground, new Point(0, top), FormattedText);
}
public override void Render(DrawingContext context)
diff --git a/src/Avalonia.Controls/SplitView.cs b/src/Avalonia.Controls/SplitView.cs
index 8267efc466..4500d52484 100644
--- a/src/Avalonia.Controls/SplitView.cs
+++ b/src/Avalonia.Controls/SplitView.cs
@@ -9,6 +9,8 @@ using Avalonia.Platform;
using Avalonia.VisualTree;
using System;
using System.Reactive.Disposables;
+using Avalonia.Controls.Presenters;
+using Avalonia.Controls.Templates;
namespace Avalonia.Controls
{
@@ -78,7 +80,7 @@ namespace Avalonia.Controls
[PseudoClasses(":compactoverlay", ":compactinline", ":overlay", ":inline")]
[PseudoClasses(":left", ":right")]
[PseudoClasses(":lightdismiss")]
- public class SplitView : TemplatedControl
+ public class SplitView : ContentControl
{
/*
Pseudo classes & combos
@@ -87,12 +89,6 @@ namespace Avalonia.Controls
:left :right
*/
- ///
- /// Defines the property
- ///
- public static readonly StyledProperty ContentProperty =
- AvaloniaProperty.Register(nameof(Content));
-
///
/// Defines the property
///
@@ -133,8 +129,14 @@ namespace Avalonia.Controls
///
/// Defines the property
///
- public static readonly StyledProperty PaneProperty =
- AvaloniaProperty.Register(nameof(Pane));
+ public static readonly StyledProperty PaneProperty =
+ AvaloniaProperty.Register(nameof(Pane));
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty PaneTemplateProperty =
+ AvaloniaProperty.Register(nameof(PaneTemplate));
///
/// Defines the property
@@ -166,16 +168,7 @@ namespace Avalonia.Controls
CompactPaneLengthProperty.Changed.AddClassHandler((x, v) => x.OnCompactPaneLengthChanged(v));
PanePlacementProperty.Changed.AddClassHandler((x, v) => x.OnPanePlacementChanged(v));
DisplayModeProperty.Changed.AddClassHandler((x, v) => x.OnDisplayModeChanged(v));
- }
-
- ///
- /// Gets or sets the content of the SplitView
- ///
- [Content]
- public IControl Content
- {
- get => GetValue(ContentProperty);
- set => SetValue(ContentProperty, value);
+
}
///
@@ -265,11 +258,20 @@ namespace Avalonia.Controls
///
/// Gets or sets the Pane for the SplitView
///
- public IControl Pane
+ public object Pane
{
get => GetValue(PaneProperty);
set => SetValue(PaneProperty, value);
}
+
+ ///
+ /// Gets or sets the data template used to display the header content of the control.
+ ///
+ public IDataTemplate? PaneTemplate
+ {
+ get => GetValue(PaneTemplateProperty);
+ set => SetValue(PaneTemplateProperty, value);
+ }
///
/// Gets or sets whether WinUI equivalent LightDismissOverlayMode is enabled
@@ -312,6 +314,18 @@ namespace Avalonia.Controls
///
public event EventHandler PaneOpening;
+ protected override bool RegisterContentPresenter(IContentPresenter presenter)
+ {
+ var result = base.RegisterContentPresenter(presenter);
+
+ if (presenter.Name == "PART_PanePresenter")
+ {
+ return true;
+ }
+
+ return result;
+ }
+
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
diff --git a/src/Avalonia.Controls/TextBlock.cs b/src/Avalonia.Controls/TextBlock.cs
index d61519e697..31517ba59d 100644
--- a/src/Avalonia.Controls/TextBlock.cs
+++ b/src/Avalonia.Controls/TextBlock.cs
@@ -3,6 +3,7 @@ using Avalonia.LogicalTree;
using Avalonia.Media;
using Avalonia.Media.TextFormatting;
using Avalonia.Metadata;
+using Avalonia.Layout;
namespace Avalonia.Controls
{
@@ -427,14 +428,32 @@ namespace Avalonia.Controls
case TextAlignment.Center:
offsetX = (width - TextLayout.Size.Width) / 2;
break;
+
case TextAlignment.Right:
- offsetX = width - TextLayout.Size.Width;
+ offsetX = width - TextLayout.Size.Width;
break;
}
var padding = Padding;
- using (context.PushPostTransform(Matrix.CreateTranslation(padding.Left + offsetX, padding.Top)))
+ var top = padding.Top;
+ var textSize = TextLayout.Size;
+
+ if (Bounds.Height < textSize.Height)
+ {
+ switch (VerticalAlignment)
+ {
+ case VerticalAlignment.Center:
+ top += (Bounds.Height - textSize.Height) / 2;
+ break;
+
+ case VerticalAlignment.Bottom:
+ top += (Bounds.Height - textSize.Height);
+ break;
+ }
+ }
+
+ using (context.PushPostTransform(Matrix.CreateTranslation(padding.Left + offsetX, top)))
{
TextLayout.Draw(context);
}
diff --git a/src/Avalonia.Dialogs/ApiCompatBaseline.txt b/src/Avalonia.Dialogs/ApiCompatBaseline.txt
new file mode 100644
index 0000000000..fcc74cf864
--- /dev/null
+++ b/src/Avalonia.Dialogs/ApiCompatBaseline.txt
@@ -0,0 +1 @@
+Total Issues: 0
diff --git a/src/Avalonia.OpenGL/Angle/AngleEglInterface.cs b/src/Avalonia.OpenGL/Angle/AngleEglInterface.cs
index 8c9b028164..3a332f37ad 100644
--- a/src/Avalonia.OpenGL/Angle/AngleEglInterface.cs
+++ b/src/Avalonia.OpenGL/Angle/AngleEglInterface.cs
@@ -1,15 +1,13 @@
using System;
using System.Runtime.InteropServices;
using Avalonia.OpenGL.Egl;
-using Avalonia.Platform;
-using Avalonia.Platform.Interop;
namespace Avalonia.OpenGL.Angle
{
public class AngleEglInterface : EglInterface
{
- [DllImport("libegl.dll", CharSet = CharSet.Ansi)]
- static extern IntPtr eglGetProcAddress(string proc);
+ [DllImport("av_libGLESv2.dll", CharSet = CharSet.Ansi)]
+ static extern IntPtr EGL_GetProcAddress(string proc);
public AngleEglInterface() : base(LoadAngle())
{
@@ -20,14 +18,14 @@ namespace Avalonia.OpenGL.Angle
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
- var disp = eglGetProcAddress("eglGetPlatformDisplayEXT");
+ var disp = EGL_GetProcAddress("eglGetPlatformDisplayEXT");
if (disp == IntPtr.Zero)
{
throw new OpenGlException("libegl.dll doesn't have eglGetPlatformDisplayEXT entry point");
}
- return eglGetProcAddress;
+ return EGL_GetProcAddress;
}
throw new PlatformNotSupportedException();
diff --git a/src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs b/src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs
index 191fb53204..94e7728d68 100644
--- a/src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs
+++ b/src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs
@@ -82,7 +82,14 @@ namespace Avalonia.OpenGL.Angle
{
if (PlatformApi != AngleOptions.PlatformApi.DirectX11)
throw new InvalidOperationException("Current platform API is " + PlatformApi);
- return egl.CreatePBufferFromClientBuffer(EGL_D3D_TEXTURE_ANGLE, handle, new[] { EGL_NONE, EGL_NONE });
+ return egl.CreatePBufferFromClientBuffer(EGL_D3D_TEXTURE_ANGLE, handle, new[] { EGL_NONE, EGL_NONE });
+ }
+
+ public EglSurface WrapDirect3D11Texture(EglPlatformOpenGlInterface egl, IntPtr handle, int offsetX, int offsetY, int width, int height)
+ {
+ if (PlatformApi != AngleOptions.PlatformApi.DirectX11)
+ throw new InvalidOperationException("Current platform API is " + PlatformApi);
+ return egl.CreatePBufferFromClientBuffer(EGL_D3D_TEXTURE_ANGLE, handle, new[] { EGL_WIDTH, width, EGL_HEIGHT, height, EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE, EGL_TRUE, EGL_TEXTURE_OFFSET_X_ANGLE, offsetX, EGL_TEXTURE_OFFSET_Y_ANGLE, offsetY, EGL_NONE });
}
}
}
diff --git a/src/Avalonia.OpenGL/Egl/EglConsts.cs b/src/Avalonia.OpenGL/Egl/EglConsts.cs
index 58f5f1cef5..530d3401f6 100644
--- a/src/Avalonia.OpenGL/Egl/EglConsts.cs
+++ b/src/Avalonia.OpenGL/Egl/EglConsts.cs
@@ -205,5 +205,11 @@ namespace Avalonia.OpenGL.Egl
public const int EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE = 0x3200;
public const int EGL_D3D_TEXTURE_ANGLE = 0x33A3;
+
+
+ public const int EGL_TEXTURE_OFFSET_X_ANGLE = 0x3490;
+ public const int EGL_TEXTURE_OFFSET_Y_ANGLE = 0x3491;
+
+ public const int EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE = 0x33A6;
}
}
diff --git a/src/Avalonia.Themes.Default/SplitView.xaml b/src/Avalonia.Themes.Default/SplitView.xaml
index 2fb59cdbc4..18da3edb46 100644
--- a/src/Avalonia.Themes.Default/SplitView.xaml
+++ b/src/Avalonia.Themes.Default/SplitView.xaml
@@ -32,12 +32,12 @@
ClipToBounds="True"
HorizontalAlignment="Left"
ZIndex="100">
-
+
-
+
@@ -106,14 +106,14 @@
ClipToBounds="True"
HorizontalAlignment="Right"
ZIndex="100">
-
+
-
+
diff --git a/src/Avalonia.Themes.Fluent/SplitView.xaml b/src/Avalonia.Themes.Fluent/SplitView.xaml
index 71e92459f1..7d6f7a9b9a 100644
--- a/src/Avalonia.Themes.Fluent/SplitView.xaml
+++ b/src/Avalonia.Themes.Fluent/SplitView.xaml
@@ -32,12 +32,12 @@
ClipToBounds="True"
HorizontalAlignment="Left"
ZIndex="100">
-
+
-
+
@@ -106,14 +106,14 @@
ClipToBounds="True"
HorizontalAlignment="Right"
ZIndex="100">
-
+
-
+
diff --git a/src/Avalonia.Visuals/Rendering/RenderLayer.cs b/src/Avalonia.Visuals/Rendering/RenderLayer.cs
index 7a79e45716..768e832fe5 100644
--- a/src/Avalonia.Visuals/Rendering/RenderLayer.cs
+++ b/src/Avalonia.Visuals/Rendering/RenderLayer.cs
@@ -30,12 +30,12 @@ namespace Avalonia.Rendering
{
if (Size != size || Scaling != scaling)
{
- Bitmap.Dispose();
var resized = RefCountable.Create(drawingContext.CreateLayer(size));
using (var context = resized.Item.CreateDrawingContext(null))
{
- context.Clear(Colors.Transparent);
+ Bitmap.Dispose();
+ context.Clear(default);
Bitmap = resized;
Scaling = scaling;
diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs
index cbc896813f..a32b3327c2 100644
--- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs
+++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs
@@ -36,6 +36,7 @@ namespace Avalonia.Skia
private readonly SKPaint _fillPaint = new SKPaint();
private readonly SKPaint _boxShadowPaint = new SKPaint();
private static SKShader s_acrylicNoiseShader;
+ private readonly ISkiaGpuRenderSession _session;
///
/// Context create info.
@@ -76,6 +77,8 @@ namespace Avalonia.Skia
/// Skia GPU provider context (optional)
///
public ISkiaGpu Gpu;
+
+ public ISkiaGpuRenderSession CurrentSession;
}
///
@@ -96,6 +99,8 @@ namespace Avalonia.Skia
Surface = createInfo.Surface;
Canvas = createInfo.Canvas ?? createInfo.Surface?.Canvas;
+ _session = createInfo.CurrentSession;
+
if (Canvas == null)
{
throw new ArgumentException("Invalid create info - no Canvas provided", nameof(createInfo));
@@ -969,7 +974,8 @@ namespace Avalonia.Skia
Format = format,
DisableTextLcdRendering = !_canTextUseLcdRendering,
GrContext = _grContext,
- Gpu = _gpu
+ Gpu = _gpu,
+ Session = _session
};
return new SurfaceRenderTarget(createInfo);
diff --git a/src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs b/src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs
index 869c261f1b..aa86df7c23 100644
--- a/src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs
+++ b/src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs
@@ -21,7 +21,7 @@ namespace Avalonia.Skia
/// Creates an offscreen render target surface
///
/// size in pixels
- ISkiaSurface TryCreateSurface(PixelSize size);
+ ISkiaSurface TryCreateSurface(PixelSize size, ISkiaGpuRenderSession session);
}
public interface ISkiaSurface : IDisposable
diff --git a/src/Skia/Avalonia.Skia/Gpu/ISkiaGpuRenderSession.cs b/src/Skia/Avalonia.Skia/Gpu/ISkiaGpuRenderSession.cs
index a4e2bfed52..5faa6a41ec 100644
--- a/src/Skia/Avalonia.Skia/Gpu/ISkiaGpuRenderSession.cs
+++ b/src/Skia/Avalonia.Skia/Gpu/ISkiaGpuRenderSession.cs
@@ -22,5 +22,7 @@ namespace Avalonia.Skia
/// Scaling factor.
///
double ScaleFactor { get; }
+
+ GRSurfaceOrigin SurfaceOrigin { get; }
}
}
diff --git a/src/Skia/Avalonia.Skia/Gpu/OpenGl/FboSkiaSurface.cs b/src/Skia/Avalonia.Skia/Gpu/OpenGl/FboSkiaSurface.cs
index 9ee8d698ba..8ab275df63 100644
--- a/src/Skia/Avalonia.Skia/Gpu/OpenGl/FboSkiaSurface.cs
+++ b/src/Skia/Avalonia.Skia/Gpu/OpenGl/FboSkiaSurface.cs
@@ -14,7 +14,7 @@ namespace Avalonia.Skia
private int _texture;
private static readonly bool[] TrueFalse = new[] { true, false };
- public FboSkiaSurface(GRContext grContext, IGlContext glContext, PixelSize pixelSize)
+ public FboSkiaSurface(GRContext grContext, IGlContext glContext, PixelSize pixelSize, GRSurfaceOrigin surfaceOrigin)
{
_grContext = grContext;
_glContext = glContext;
@@ -93,7 +93,7 @@ namespace Avalonia.Skia
var target = new GRBackendRenderTarget(pixelSize.Width, pixelSize.Height, 0, 8,
new GRGlFramebufferInfo((uint)_fbo, SKColorType.Rgba8888.ToGlSizedFormat()));
Surface = SKSurface.Create(_grContext, target,
- GRSurfaceOrigin.BottomLeft, SKColorType.Rgba8888);
+ surfaceOrigin, SKColorType.Rgba8888);
CanBlit = gl.BlitFramebuffer != null;
}
diff --git a/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlRenderTarget.cs b/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlRenderTarget.cs
index 6df8df9a4c..b4c5619c85 100644
--- a/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlRenderTarget.cs
+++ b/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlRenderTarget.cs
@@ -39,6 +39,8 @@ namespace Avalonia.Skia
_backendRenderTarget = backendRenderTarget;
_surface = surface;
_glSession = glSession;
+
+ SurfaceOrigin = glSession.IsYFlipped ? GRSurfaceOrigin.TopLeft : GRSurfaceOrigin.BottomLeft;
}
public void Dispose()
{
@@ -48,6 +50,8 @@ namespace Avalonia.Skia
GrContext.Flush();
_glSession.Dispose();
}
+
+ public GRSurfaceOrigin SurfaceOrigin { get; }
public GRContext GrContext { get; }
public SKSurface SkSurface => _surface;
@@ -73,10 +77,6 @@ namespace Avalonia.Skia
$"Can't create drawing context for surface with {size} size and {scaling} scaling");
}
- gl.Viewport(0, 0, size.Width, size.Height);
- gl.ClearStencil(0);
- gl.ClearColor(0, 0, 0, 0);
- gl.Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
lock (_grContext)
{
_grContext.ResetContext();
@@ -89,6 +89,7 @@ namespace Avalonia.Skia
SKColorType.Rgba8888);
success = true;
+
return new GlGpuSession(_grContext, renderTarget, surface, glSession);
}
}
diff --git a/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs b/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs
index c02d813e24..a393f0b64d 100644
--- a/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs
+++ b/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs
@@ -47,7 +47,7 @@ namespace Avalonia.Skia
return null;
}
- public ISkiaSurface TryCreateSurface(PixelSize size)
+ public ISkiaSurface TryCreateSurface(PixelSize size, ISkiaGpuRenderSession session)
{
// Only windows platform needs our FBO trickery
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
@@ -62,7 +62,7 @@ namespace Avalonia.Skia
return null;
try
{
- var surface = new FboSkiaSurface(_grContext, _glContext, size);
+ var surface = new FboSkiaSurface(_grContext, _glContext, size, session?.SurfaceOrigin ?? GRSurfaceOrigin.TopLeft);
_canCreateSurfaces = true;
return surface;
}
diff --git a/src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs b/src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs
index 9992c9ba8c..6626546c0c 100644
--- a/src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs
+++ b/src/Skia/Avalonia.Skia/Gpu/SkiaGpuRenderTarget.cs
@@ -33,7 +33,8 @@ namespace Avalonia.Skia
Dpi = SkiaPlatform.DefaultDpi * session.ScaleFactor,
VisualBrushRenderer = visualBrushRenderer,
DisableTextLcdRendering = true,
- Gpu = _skiaGpu
+ Gpu = _skiaGpu,
+ CurrentSession = session
};
return new DrawingContextImpl(nfo, session);
diff --git a/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs b/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs
index 61b599a731..6347c64aed 100644
--- a/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs
+++ b/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs
@@ -51,7 +51,7 @@ namespace Avalonia.Skia
_grContext = createInfo.GrContext;
_gpu = createInfo.Gpu;
- _surface = _gpu?.TryCreateSurface(PixelSize);
+ _surface = _gpu?.TryCreateSurface(PixelSize, createInfo.Session);
if (_surface == null)
_surface = new SkiaSurfaceWrapper(CreateSurface(createInfo.GrContext, PixelSize.Width, PixelSize.Height,
createInfo.Format));
@@ -100,7 +100,7 @@ namespace Avalonia.Skia
VisualBrushRenderer = visualBrushRenderer,
DisableTextLcdRendering = _disableLcdRendering,
GrContext = _grContext,
- Gpu = _gpu
+ Gpu = _gpu,
};
return new DrawingContextImpl(createInfo, Disposable.Create(() => Version++));
@@ -218,6 +218,8 @@ namespace Avalonia.Skia
public GRContext GrContext;
public ISkiaGpu Gpu;
+
+ public ISkiaGpuRenderSession Session;
}
}
}
diff --git a/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj b/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj
index 7fc24d4d3d..5889d919df 100644
--- a/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj
+++ b/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj
@@ -6,7 +6,8 @@
-
+
+
diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionBlurHost.cs b/src/Windows/Avalonia.Win32/Composition/CompositionBlurHost.cs
new file mode 100644
index 0000000000..4c090e797c
--- /dev/null
+++ b/src/Windows/Avalonia.Win32/Composition/CompositionBlurHost.cs
@@ -0,0 +1,18 @@
+namespace Avalonia.Win32
+{
+ internal class CompositionBlurHost : IBlurHost
+ {
+ Windows.UI.Composition.Visual _blurVisual;
+
+ public CompositionBlurHost(Windows.UI.Composition.Visual blurVisual)
+ {
+ _blurVisual = blurVisual;
+ }
+
+ public void SetBlur(bool enabled)
+ {
+ _blurVisual.IsVisible = enabled;
+ }
+ }
+}
+
diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionConnector.cs b/src/Windows/Avalonia.Win32/Composition/CompositionConnector.cs
new file mode 100644
index 0000000000..fc18b623bf
--- /dev/null
+++ b/src/Windows/Avalonia.Win32/Composition/CompositionConnector.cs
@@ -0,0 +1,143 @@
+using System;
+using System.Runtime.InteropServices;
+using Avalonia.OpenGL;
+using Avalonia.OpenGL.Angle;
+using Avalonia.OpenGL.Egl;
+using Windows.UI.Composition;
+using Windows.UI.Composition.Interop;
+using WinRT;
+
+namespace Avalonia.Win32
+{
+ internal class CompositionConnector
+ {
+ private Compositor _compositor;
+ private Windows.System.DispatcherQueueController _dispatcherQueueController;
+ private CompositionGraphicsDevice _graphicsDevice;
+
+ internal enum DISPATCHERQUEUE_THREAD_APARTMENTTYPE
+ {
+ DQTAT_COM_NONE = 0,
+ DQTAT_COM_ASTA = 1,
+ DQTAT_COM_STA = 2
+ };
+
+ internal enum DISPATCHERQUEUE_THREAD_TYPE
+ {
+ DQTYPE_THREAD_DEDICATED = 1,
+ DQTYPE_THREAD_CURRENT = 2,
+ };
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct DispatcherQueueOptions
+ {
+ public int dwSize;
+
+ [MarshalAs(UnmanagedType.I4)]
+ public DISPATCHERQUEUE_THREAD_TYPE threadType;
+
+ [MarshalAs(UnmanagedType.I4)]
+ public DISPATCHERQUEUE_THREAD_APARTMENTTYPE apartmentType;
+ };
+
+ [DllImport("coremessaging.dll", EntryPoint = "CreateDispatcherQueueController", CharSet = CharSet.Unicode)]
+ internal static extern IntPtr CreateDispatcherQueueController(DispatcherQueueOptions options, out IntPtr dispatcherQueueController);
+
+ public CompositionConnector(EglPlatformOpenGlInterface egl)
+ {
+ EnsureDispatcherQueue();
+
+ if (_dispatcherQueueController != null)
+ _compositor = new Compositor();
+
+ var interop = _compositor.As();
+
+ var display = egl.Display as AngleWin32EglDisplay;
+
+ _graphicsDevice = interop.CreateGraphicsDevice(display.GetDirect3DDevice());
+ }
+
+ public ICompositionDrawingSurfaceInterop InitialiseWindowCompositionTree(IntPtr hwnd, out Windows.UI.Composition.Visual surfaceVisual, out IBlurHost blurHost)
+ {
+ var target = CreateDesktopWindowTarget(hwnd);
+
+ var surface = _graphicsDevice.CreateDrawingSurface(new Windows.Foundation.Size(0, 0),
+ Windows.Graphics.DirectX.DirectXPixelFormat.B8G8R8A8UIntNormalized,
+ Windows.Graphics.DirectX.DirectXAlphaMode.Premultiplied);
+
+ var surfaceInterop = surface.As();
+
+ var brush = _compositor.CreateSurfaceBrush(surface);
+ var visual = _compositor.CreateSpriteVisual();
+
+ visual.Brush = brush;
+ visual.RelativeSizeAdjustment = new System.Numerics.Vector2(1, 1);
+
+ var container = _compositor.CreateContainerVisual();
+
+ target.Root = container;
+
+ var blur = CreateBlur();
+
+ blurHost = new CompositionBlurHost(blur);
+
+ container.Children.InsertAtTop(blur);
+
+ container.Children.InsertAtTop(visual);
+
+ visual.CompositeMode = CompositionCompositeMode.SourceOver;
+
+ surfaceVisual = container;
+
+ return surfaceInterop;
+ }
+
+ private SpriteVisual CreateBlur()
+ {
+ var blurEffect = new GaussianBlurEffect(new CompositionEffectSourceParameter("backdrop"));
+ var blurEffectFactory = _compositor.CreateEffectFactory(blurEffect);
+
+ var blurBrush = blurEffectFactory.CreateBrush();
+ var backDropBrush = _compositor.CreateBackdropBrush();
+
+ blurBrush.SetSourceParameter("backdrop", backDropBrush);
+
+ var saturateEffect = new SaturationEffect(blurEffect);
+ var satEffectFactory = _compositor.CreateEffectFactory(saturateEffect);
+
+ var satBrush = satEffectFactory.CreateBrush();
+ satBrush.SetSourceParameter("backdrop", backDropBrush);
+
+ var visual = _compositor.CreateSpriteVisual();
+ visual.IsVisible = false;
+
+ visual.RelativeSizeAdjustment = new System.Numerics.Vector2(1.0f, 1.0f);
+ visual.Brush = satBrush;
+
+ return visual;
+ }
+
+ private CompositionTarget CreateDesktopWindowTarget(IntPtr window)
+ {
+ var interop = _compositor.As();
+
+ interop.CreateDesktopWindowTarget(window, false, out var windowTarget);
+ return Windows.UI.Composition.Desktop.DesktopWindowTarget.FromAbi(windowTarget);
+ }
+
+ private void EnsureDispatcherQueue()
+ {
+ if (_dispatcherQueueController == null)
+ {
+ DispatcherQueueOptions options = new DispatcherQueueOptions();
+ options.apartmentType = DISPATCHERQUEUE_THREAD_APARTMENTTYPE.DQTAT_COM_NONE;
+ options.threadType = DISPATCHERQUEUE_THREAD_TYPE.DQTYPE_THREAD_CURRENT;
+ options.dwSize = Marshal.SizeOf(typeof(DispatcherQueueOptions));
+
+ CreateDispatcherQueueController(options, out var queue);
+ _dispatcherQueueController = Windows.System.DispatcherQueueController.FromAbi(queue);
+ }
+ }
+ }
+}
+
diff --git a/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs b/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs
new file mode 100644
index 0000000000..441da93787
--- /dev/null
+++ b/src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs
@@ -0,0 +1,96 @@
+using System;
+using System.Runtime.InteropServices;
+using Avalonia.OpenGL;
+using Avalonia.OpenGL.Angle;
+using Avalonia.OpenGL.Egl;
+using Avalonia.OpenGL.Surfaces;
+using Windows.UI.Composition.Interop;
+
+namespace Avalonia.Win32
+{
+ internal class CompositionEglGlPlatformSurface : EglGlPlatformSurfaceBase
+ {
+ private EglPlatformOpenGlInterface _egl;
+ private readonly IEglWindowGlPlatformSurfaceInfo _info;
+ private ICompositionDrawingSurfaceInterop _surfaceInterop;
+ private Windows.UI.Composition.Visual _surface;
+
+ public CompositionEglGlPlatformSurface(EglPlatformOpenGlInterface egl, IEglWindowGlPlatformSurfaceInfo info) : base()
+ {
+ _egl = egl;
+ _info = info;
+ }
+
+ public IBlurHost AttachToCompositionTree(CompositionConnector connector, IntPtr hwnd)
+ {
+ using (_egl.PrimaryContext.MakeCurrent())
+ {
+ _surfaceInterop = connector.InitialiseWindowCompositionTree(hwnd, out _surface, out var blurHost);
+ return blurHost;
+ }
+ }
+
+ public override IGlPlatformSurfaceRenderTarget CreateGlRenderTarget()
+ {
+ return new CompositionRenderTarget(_egl, _surface, _surfaceInterop, _info);
+ }
+
+ class CompositionRenderTarget : EglPlatformSurfaceRenderTargetBase
+ {
+ private readonly EglPlatformOpenGlInterface _egl;
+ private readonly IEglWindowGlPlatformSurfaceInfo _info;
+ private PixelSize _currentSize;
+ private readonly ICompositionDrawingSurfaceInterop _surfaceInterop;
+ private static Guid s_Iid = Guid.Parse("6f15aaf2-d208-4e89-9ab4-489535d34f9c");
+ private Windows.UI.Composition.Visual _compositionVisual;
+
+ public CompositionRenderTarget(EglPlatformOpenGlInterface egl,
+ Windows.UI.Composition.Visual compositionVisual,
+ ICompositionDrawingSurfaceInterop interopSurface,
+ IEglWindowGlPlatformSurfaceInfo info)
+ : base(egl)
+ {
+ _egl = egl;
+ _surfaceInterop = interopSurface;
+ _info = info;
+ _currentSize = info.Size;
+ _compositionVisual = compositionVisual;
+
+ using (_egl.PrimaryContext.MakeCurrent())
+ {
+ _surfaceInterop.Resize(new POINT { X = _info.Size.Width, Y = _info.Size.Height });
+ }
+
+ _compositionVisual.Size = new System.Numerics.Vector2(_info.Size.Width, _info.Size.Height);
+ }
+
+ public override IGlPlatformSurfaceRenderingSession BeginDraw()
+ {
+ IntPtr texture;
+ EglSurface surface;
+
+ using (_egl.PrimaryEglContext.EnsureCurrent())
+ {
+ if (_info.Size != _currentSize)
+ {
+ _surfaceInterop.Resize(new POINT { X = _info.Size.Width, Y = _info.Size.Height });
+ _compositionVisual.Size = new System.Numerics.Vector2(_info.Size.Width, _info.Size.Height);
+ _currentSize = _info.Size;
+ }
+
+ var offset = new POINT();
+
+ _surfaceInterop.BeginDraw(
+ IntPtr.Zero,
+ ref s_Iid,
+ out texture, ref offset);
+
+ surface = (_egl.Display as AngleWin32EglDisplay).WrapDirect3D11Texture(_egl, texture, offset.X, offset.Y, _info.Size.Width, _info.Size.Height);
+ }
+
+ return base.BeginDraw(surface, _info, () => { _surfaceInterop.EndDraw(); Marshal.Release(texture); surface.Dispose(); }, true);
+ }
+ }
+ }
+}
+
diff --git a/src/Windows/Avalonia.Win32/Composition/D2DEffects.cs b/src/Windows/Avalonia.Win32/Composition/D2DEffects.cs
new file mode 100644
index 0000000000..1c761ee082
--- /dev/null
+++ b/src/Windows/Avalonia.Win32/Composition/D2DEffects.cs
@@ -0,0 +1,91 @@
+using System;
+
+
+namespace Avalonia.Win32
+{
+ class D2DEffects
+ {
+ public static readonly Guid CLSID_D2D12DAffineTransform = new Guid(0x6AA97485, 0x6354, 0x4CFC, 0x90, 0x8C, 0xE4, 0xA7, 0x4F, 0x62, 0xC9, 0x6C);
+
+ public static readonly Guid CLSID_D2D13DPerspectiveTransform = new Guid(0xC2844D0B, 0x3D86, 0x46E7, 0x85, 0xBA, 0x52, 0x6C, 0x92, 0x40, 0xF3, 0xFB);
+
+ public static readonly Guid CLSID_D2D13DTransform = new Guid(0xE8467B04, 0xEC61, 0x4B8A, 0xB5, 0xDE, 0xD4, 0xD7, 0x3D, 0xEB, 0xEA, 0x5A);
+
+ public static readonly Guid CLSID_D2D1ArithmeticComposite = new Guid(0xFC151437, 0x049A, 0x4784, 0xA2, 0x4A, 0xF1, 0xC4, 0xDA, 0xF2, 0x09, 0x87);
+
+ public static readonly Guid CLSID_D2D1Atlas = new Guid(0x913E2BE4, 0xFDCF, 0x4FE2, 0xA5, 0xF0, 0x24, 0x54, 0xF1, 0x4F, 0xF4, 0x08);
+
+ public static readonly Guid CLSID_D2D1BitmapSource = new Guid(0x5FB6C24D, 0xC6DD, 0x4231, 0x94, 0x4, 0x50, 0xF4, 0xD5, 0xC3, 0x25, 0x2D);
+
+ public static readonly Guid CLSID_D2D1Blend = new Guid(0x81C5B77B, 0x13F8, 0x4CDD, 0xAD, 0x20, 0xC8, 0x90, 0x54, 0x7A, 0xC6, 0x5D);
+
+ public static readonly Guid CLSID_D2D1Border = new Guid(0x2A2D49C0, 0x4ACF, 0x43C7, 0x8C, 0x6A, 0x7C, 0x4A, 0x27, 0x87, 0x4D, 0x27);
+
+ public static readonly Guid CLSID_D2D1Brightness = new Guid(0x8CEA8D1E, 0x77B0, 0x4986, 0xB3, 0xB9, 0x2F, 0x0C, 0x0E, 0xAE, 0x78, 0x87);
+
+ public static readonly Guid CLSID_D2D1ColorManagement = new Guid(0x1A28524C, 0xFDD6, 0x4AA4, 0xAE, 0x8F, 0x83, 0x7E, 0xB8, 0x26, 0x7B, 0x37);
+
+ public static readonly Guid CLSID_D2D1ColorMatrix = new Guid(0x921F03D6, 0x641C, 0x47DF, 0x85, 0x2D, 0xB4, 0xBB, 0x61, 0x53, 0xAE, 0x11);
+
+ public static readonly Guid CLSID_D2D1Composite = new Guid(0x48FC9F51, 0xF6AC, 0x48F1, 0x8B, 0x58, 0x3B, 0x28, 0xAC, 0x46, 0xF7, 0x6D);
+
+ public static readonly Guid CLSID_D2D1ConvolveMatrix = new Guid(0x407F8C08, 0x5533, 0x4331, 0xA3, 0x41, 0x23, 0xCC, 0x38, 0x77, 0x84, 0x3E);
+
+ public static readonly Guid CLSID_D2D1Crop = new Guid(0xE23F7110, 0x0E9A, 0x4324, 0xAF, 0x47, 0x6A, 0x2C, 0x0C, 0x46, 0xF3, 0x5B);
+
+ public static readonly Guid CLSID_D2D1DirectionalBlur = new Guid(0x174319A6, 0x58E9, 0x49B2, 0xBB, 0x63, 0xCA, 0xF2, 0xC8, 0x11, 0xA3, 0xDB);
+
+ public static readonly Guid CLSID_D2D1DiscreteTransfer = new Guid(0x90866FCD, 0x488E, 0x454B, 0xAF, 0x06, 0xE5, 0x04, 0x1B, 0x66, 0xC3, 0x6C);
+
+ public static readonly Guid CLSID_D2D1DisplacementMap = new Guid(0xEDC48364, 0x417, 0x4111, 0x94, 0x50, 0x43, 0x84, 0x5F, 0xA9, 0xF8, 0x90);
+
+ public static readonly Guid CLSID_D2D1DistantDiffuse = new Guid(0x3E7EFD62, 0xA32D, 0x46D4, 0xA8, 0x3C, 0x52, 0x78, 0x88, 0x9A, 0xC9, 0x54);
+
+ public static readonly Guid CLSID_D2D1DistantSpecular = new Guid(0x428C1EE5, 0x77B8, 0x4450, 0x8A, 0xB5, 0x72, 0x21, 0x9C, 0x21, 0xAB, 0xDA);
+
+ public static readonly Guid CLSID_D2D1DpiCompensation = new Guid(0x6C26C5C7, 0x34E0, 0x46FC, 0x9C, 0xFD, 0xE5, 0x82, 0x37, 0x6, 0xE2, 0x28);
+
+ public static readonly Guid CLSID_D2D1Flood = new Guid(0x61C23C20, 0xAE69, 0x4D8E, 0x94, 0xCF, 0x50, 0x07, 0x8D, 0xF6, 0x38, 0xF2);
+
+ public static readonly Guid CLSID_D2D1GammaTransfer = new Guid(0x409444C4, 0xC419, 0x41A0, 0xB0, 0xC1, 0x8C, 0xD0, 0xC0, 0xA1, 0x8E, 0x42);
+
+ public static readonly Guid CLSID_D2D1GaussianBlur = new Guid(0x1FEB6D69, 0x2FE6, 0x4AC9, 0x8C, 0x58, 0x1D, 0x7F, 0x93, 0xE7, 0xA6, 0xA5);
+
+ public static readonly Guid CLSID_D2D1Scale = new Guid(0x9DAF9369, 0x3846, 0x4D0E, 0xA4, 0x4E, 0xC, 0x60, 0x79, 0x34, 0xA5, 0xD7);
+
+ public static readonly Guid CLSID_D2D1Histogram = new Guid(0x881DB7D0, 0xF7EE, 0x4D4D, 0xA6, 0xD2, 0x46, 0x97, 0xAC, 0xC6, 0x6E, 0xE8);
+
+ public static readonly Guid CLSID_D2D1HueRotation = new Guid(0x0F4458EC, 0x4B32, 0x491B, 0x9E, 0x85, 0xBD, 0x73, 0xF4, 0x4D, 0x3E, 0xB6);
+
+ public static readonly Guid CLSID_D2D1LinearTransfer = new Guid(0xAD47C8FD, 0x63EF, 0x4ACC, 0x9B, 0x51, 0x67, 0x97, 0x9C, 0x03, 0x6C, 0x06);
+
+ public static readonly Guid CLSID_D2D1LuminanceToAlpha = new Guid(0x41251AB7, 0x0BEB, 0x46F8, 0x9D, 0xA7, 0x59, 0xE9, 0x3F, 0xCC, 0xE5, 0xDE);
+
+ public static readonly Guid CLSID_D2D1Morphology = new Guid(0xEAE6C40D, 0x626A, 0x4C2D, 0xBF, 0xCB, 0x39, 0x10, 0x01, 0xAB, 0xE2, 0x02);
+
+ public static readonly Guid CLSID_D2D1OpacityMetadata = new Guid(0x6C53006A, 0x4450, 0x4199, 0xAA, 0x5B, 0xAD, 0x16, 0x56, 0xFE, 0xCE, 0x5E);
+
+ public static readonly Guid CLSID_D2D1PointDiffuse = new Guid(0xB9E303C3, 0xC08C, 0x4F91, 0x8B, 0x7B, 0x38, 0x65, 0x6B, 0xC4, 0x8C, 0x20);
+
+ public static readonly Guid CLSID_D2D1PointSpecular = new Guid(0x09C3CA26, 0x3AE2, 0x4F09, 0x9E, 0xBC, 0xED, 0x38, 0x65, 0xD5, 0x3F, 0x22);
+
+ public static readonly Guid CLSID_D2D1Premultiply = new Guid(0x06EAB419, 0xDEED, 0x4018, 0x80, 0xD2, 0x3E, 0x1D, 0x47, 0x1A, 0xDE, 0xB2);
+
+ public static readonly Guid CLSID_D2D1Saturation = new Guid(0x5CB2D9CF, 0x327D, 0x459F, 0xA0, 0xCE, 0x40, 0xC0, 0xB2, 0x08, 0x6B, 0xF7);
+
+ public static readonly Guid CLSID_D2D1Shadow = new Guid(0xC67EA361, 0x1863, 0x4E69, 0x89, 0xDB, 0x69, 0x5D, 0x3E, 0x9A, 0x5B, 0x6B);
+
+ public static readonly Guid CLSID_D2D1SpotDiffuse = new Guid(0x818A1105, 0x7932, 0x44F4, 0xAA, 0x86, 0x08, 0xAE, 0x7B, 0x2F, 0x2C, 0x93);
+
+ public static readonly Guid CLSID_D2D1SpotSpecular = new Guid(0xEDAE421E, 0x7654, 0x4A37, 0x9D, 0xB8, 0x71, 0xAC, 0xC1, 0xBE, 0xB3, 0xC1);
+
+ public static readonly Guid CLSID_D2D1TableTransfer = new Guid(0x5BF818C3, 0x5E43, 0x48CB, 0xB6, 0x31, 0x86, 0x83, 0x96, 0xD6, 0xA1, 0xD4);
+
+ public static readonly Guid CLSID_D2D1Tile = new Guid(0xB0784138, 0x3B76, 0x4BC5, 0xB1, 0x3B, 0x0F, 0xA2, 0xAD, 0x02, 0x65, 0x9F);
+
+ public static readonly Guid CLSID_D2D1Turbulence = new Guid(0xCF2BB6AE, 0x889A, 0x4AD7, 0xBA, 0x29, 0xA2, 0xFD, 0x73, 0x2C, 0x9F, 0xC9);
+
+ public static readonly Guid CLSID_D2D1UnPremultiply = new Guid(0xFB9AC489, 0xAD8D, 0x41ED, 0x99, 0x99, 0xBB, 0x63, 0x47, 0xD1, 0x10, 0xF7);
+ }
+}
+
diff --git a/src/Windows/Avalonia.Win32/Composition/EffectBase.cs b/src/Windows/Avalonia.Win32/Composition/EffectBase.cs
new file mode 100644
index 0000000000..ca5b15971e
--- /dev/null
+++ b/src/Windows/Avalonia.Win32/Composition/EffectBase.cs
@@ -0,0 +1,45 @@
+using System;
+using Windows.Graphics.Effects;
+using Windows.Graphics.Effects.Interop;
+
+
+namespace Avalonia.Win32
+{
+ abstract class EffectBase : IGraphicsEffect, IGraphicsEffectSource, global::Windows.Graphics.Effects.Interop.IGraphicsEffectD2D1Interop
+ {
+ private IGraphicsEffectSource[] _sources;
+
+ public EffectBase(params IGraphicsEffectSource[] sources)
+ {
+ _sources = sources;
+ }
+
+ private IGraphicsEffectSource _source;
+
+ public virtual string Name { get; set; }
+
+ public abstract Guid EffectId { get; }
+
+ public abstract uint PropertyCount { get; }
+
+ public uint SourceCount => (uint)_sources.Length;
+
+ public IGraphicsEffectSource GetSource(uint index)
+ {
+ if(index < _sources.Length)
+ {
+ return _sources[index];
+ }
+
+ return null;
+ }
+
+ public uint GetNamedPropertyMapping(string name, out GRAPHICS_EFFECT_PROPERTY_MAPPING mapping)
+ {
+ throw new NotImplementedException();
+ }
+
+ public abstract object GetProperty(uint index);
+ }
+}
+
diff --git a/src/Windows/Avalonia.Win32/Composition/GRAPHICS_EFFECT_PROPERTY_MAPPING.cs b/src/Windows/Avalonia.Win32/Composition/GRAPHICS_EFFECT_PROPERTY_MAPPING.cs
new file mode 100644
index 0000000000..f5d5fc4ad3
--- /dev/null
+++ b/src/Windows/Avalonia.Win32/Composition/GRAPHICS_EFFECT_PROPERTY_MAPPING.cs
@@ -0,0 +1,18 @@
+namespace Windows.Graphics.Effects.Interop
+{
+ public enum GRAPHICS_EFFECT_PROPERTY_MAPPING
+ {
+ GRAPHICS_EFFECT_PROPERTY_MAPPING_UNKNOWN,
+ GRAPHICS_EFFECT_PROPERTY_MAPPING_DIRECT,
+ GRAPHICS_EFFECT_PROPERTY_MAPPING_VECTORX,
+ GRAPHICS_EFFECT_PROPERTY_MAPPING_VECTORY,
+ GRAPHICS_EFFECT_PROPERTY_MAPPING_VECTORZ,
+ GRAPHICS_EFFECT_PROPERTY_MAPPING_VECTORW,
+ GRAPHICS_EFFECT_PROPERTY_MAPPING_RECT_TO_VECTOR4,
+ GRAPHICS_EFFECT_PROPERTY_MAPPING_RADIANS_TO_DEGREES,
+ GRAPHICS_EFFECT_PROPERTY_MAPPING_COLORMATRIX_ALPHA_MODE,
+ GRAPHICS_EFFECT_PROPERTY_MAPPING_COLOR_TO_VECTOR3,
+ GRAPHICS_EFFECT_PROPERTY_MAPPING_COLOR_TO_VECTOR4
+ };
+}
+
diff --git a/src/Windows/Avalonia.Win32/Composition/GaussianBlurEffect.cs b/src/Windows/Avalonia.Win32/Composition/GaussianBlurEffect.cs
new file mode 100644
index 0000000000..342e68eeb4
--- /dev/null
+++ b/src/Windows/Avalonia.Win32/Composition/GaussianBlurEffect.cs
@@ -0,0 +1,57 @@
+using System;
+using Windows.Graphics.Effects;
+
+namespace Avalonia.Win32
+{
+ class GaussianBlurEffect : EffectBase
+ {
+ public GaussianBlurEffect(IGraphicsEffectSource source) : base(source)
+ {
+ }
+
+ enum D2D1_GAUSSIANBLUR_OPTIMIZATION
+ {
+ D2D1_GAUSSIANBLUR_OPTIMIZATION_SPEED,
+ D2D1_GAUSSIANBLUR_OPTIMIZATION_BALANCED,
+ D2D1_GAUSSIANBLUR_OPTIMIZATION_QUALITY,
+ D2D1_GAUSSIANBLUR_OPTIMIZATION_FORCE_DWORD
+ };
+
+ enum D2D1_BORDER_MODE
+ {
+ D2D1_BORDER_MODE_SOFT,
+ D2D1_BORDER_MODE_HARD,
+ D2D1_BORDER_MODE_FORCE_DWORD
+ };
+
+ enum D2D1GaussianBlurProp
+ {
+ D2D1_GAUSSIANBLUR_PROP_STANDARD_DEVIATION,
+ D2D1_GAUSSIANBLUR_PROP_OPTIMIZATION,
+ D2D1_GAUSSIANBLUR_PROP_BORDER_MODE,
+ D2D1_GAUSSIANBLUR_PROP_FORCE_DWORD
+ };
+
+ public override Guid EffectId => D2DEffects.CLSID_D2D1GaussianBlur;
+
+ public override uint PropertyCount => 3;
+
+ public override object GetProperty(uint index)
+ {
+ switch ((D2D1GaussianBlurProp)index)
+ {
+ case D2D1GaussianBlurProp.D2D1_GAUSSIANBLUR_PROP_STANDARD_DEVIATION:
+ return 30.0f;
+
+ case D2D1GaussianBlurProp.D2D1_GAUSSIANBLUR_PROP_OPTIMIZATION:
+ return (uint)D2D1_GAUSSIANBLUR_OPTIMIZATION.D2D1_GAUSSIANBLUR_OPTIMIZATION_BALANCED;
+
+ case D2D1GaussianBlurProp.D2D1_GAUSSIANBLUR_PROP_BORDER_MODE:
+ return (uint)D2D1_BORDER_MODE.D2D1_BORDER_MODE_HARD;
+ }
+
+ return null;
+ }
+ }
+}
+
diff --git a/src/Windows/Avalonia.Win32/Composition/IBlurHost.cs b/src/Windows/Avalonia.Win32/Composition/IBlurHost.cs
new file mode 100644
index 0000000000..6ab470d81c
--- /dev/null
+++ b/src/Windows/Avalonia.Win32/Composition/IBlurHost.cs
@@ -0,0 +1,8 @@
+namespace Avalonia.Win32
+{
+ internal interface IBlurHost
+ {
+ void SetBlur(bool enabled);
+ }
+}
+
diff --git a/src/Windows/Avalonia.Win32/Composition/ICompositionDrawingSurfaceInterop.cs b/src/Windows/Avalonia.Win32/Composition/ICompositionDrawingSurfaceInterop.cs
new file mode 100644
index 0000000000..2eac796376
--- /dev/null
+++ b/src/Windows/Avalonia.Win32/Composition/ICompositionDrawingSurfaceInterop.cs
@@ -0,0 +1,152 @@
+using System;
+using System.Runtime.InteropServices;
+using WinRT;
+
+namespace Windows.UI.Composition.Interop
+{
+ public struct POINT
+ {
+ public int X;
+ public int Y;
+ }
+
+ public struct RECT
+ {
+ public int left;
+ public int top;
+ public int right;
+ public int bottom;
+
+ public int Width => right - left;
+ public int Height => bottom - top;
+ }
+
+ [WindowsRuntimeType]
+ [Guid("FD04E6E3-FE0C-4C3C-AB19-A07601A576EE")]
+ public interface ICompositionDrawingSurfaceInterop
+ {
+ void BeginDraw(IntPtr updateRect, ref Guid iid, out IntPtr updateObject, ref POINT point);
+
+ void EndDraw();
+
+ void Resize(POINT sizePixels);
+
+ void ResumeDraw();
+
+ void Scroll(RECT scrollRect, RECT clipRect, int offsetX, int offsetY);
+
+ void SuspendDraw();
+ }
+}
+
+namespace ABI.Windows.UI.Composition.Interop
+{
+ using global::System;
+ using global::System.Runtime.InteropServices;
+ using global::Windows.UI.Composition;
+ using global::Windows.UI.Composition.Interop;
+
+ [Guid("FD04E6E3-FE0C-4C3C-AB19-A07601A576EE")]
+ internal class ICompositionDrawingSurfaceInterop : global::Windows.UI.Composition.Interop.ICompositionDrawingSurfaceInterop
+
+ {
+ [Guid("FD04E6E3-FE0C-4C3C-AB19-A07601A576EE")]
+ public unsafe struct Vftbl
+ {
+ public delegate int _BeginDraw(IntPtr ThisPtr, IntPtr updateRect, ref Guid iid, out IntPtr updateObject, ref POINT updateOffset);
+ public delegate int _EndDraw(IntPtr ThisPtr);
+ public delegate int _Resize(IntPtr ThisPtr, POINT sizePixels);
+ public delegate int _ResumeDraw(IntPtr ThisPtr);
+ public delegate int _Scroll(IntPtr ThisPtr, RECT scrollRect, RECT clipRect, int offsetX, int offsetY);
+ public delegate int _SuspendDraw(IntPtr ThisPtr);
+
+ internal global::WinRT.Interop.IUnknownVftbl IUnknownVftbl;
+ public _BeginDraw BeginDraw;
+ public _EndDraw EndDraw;
+ public _Resize Resize;
+ public _ResumeDraw ResumeDraw;
+ public _Scroll Scroll;
+ public _SuspendDraw SuspendDraw;
+
+ public static readonly Vftbl AbiToProjectionVftable;
+ public static readonly IntPtr AbiToProjectionVftablePtr;
+
+ static Vftbl()
+ {
+ AbiToProjectionVftable = new Vftbl
+ {
+ IUnknownVftbl = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl,
+
+ BeginDraw = Do_Abi_BeginDraw,
+ EndDraw = Do_Abi_EndDraw,
+ Resize = Do_Abi_Resize
+
+
+ };
+ AbiToProjectionVftablePtr = Marshal.AllocHGlobal(Marshal.SizeOf());
+ Marshal.StructureToPtr(AbiToProjectionVftable, AbiToProjectionVftablePtr, false);
+ }
+
+ private static int Do_Abi_BeginDraw(IntPtr ThisPtr, IntPtr updateRect, ref Guid iid, out IntPtr updateObject, ref POINT updateOffset)
+ {
+ updateObject = IntPtr.Zero;
+ return 0;
+ }
+
+ private static int Do_Abi_EndDraw(IntPtr ThisPtr)
+ {
+ return 0;
+ }
+
+ private static int Do_Abi_Resize(IntPtr ThisPtr, POINT sizePixels)
+ {
+ return 0;
+ }
+ }
+ internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr);
+
+ public static implicit operator ICompositionDrawingSurfaceInterop(IObjectReference obj) => (obj != null) ? new ICompositionDrawingSurfaceInterop(obj) : null;
+ protected readonly ObjectReference _obj;
+ public IObjectReference ObjRef { get => _obj; }
+ public IntPtr ThisPtr => _obj.ThisPtr;
+ public ObjectReference AsInterface() => _obj.As();
+ public A As() => _obj.AsType ();
+
+ public ICompositionDrawingSurfaceInterop(IObjectReference obj) : this(obj.As()) { }
+ internal ICompositionDrawingSurfaceInterop(ObjectReference obj)
+ {
+ _obj = obj;
+ }
+
+ public void BeginDraw(IntPtr updateRect, ref Guid iid, out IntPtr updateObject, ref POINT point)
+ {
+ Marshal.ThrowExceptionForHR(_obj.Vftbl.BeginDraw(ThisPtr, updateRect, ref iid, out updateObject, ref point));
+ }
+
+ public void EndDraw()
+ {
+ Marshal.ThrowExceptionForHR(_obj.Vftbl.EndDraw(ThisPtr));
+ }
+
+ public void Resize(POINT sizePixels)
+ {
+ Marshal.ThrowExceptionForHR(_obj.Vftbl.Resize(ThisPtr, sizePixels));
+ }
+
+ public void ResumeDraw()
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Scroll(RECT scrollRect, RECT clipRect, int offsetX, int offsetY)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void SuspendDraw()
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
+
diff --git a/src/Windows/Avalonia.Win32/Composition/ICompositorDesktopInterop.cs b/src/Windows/Avalonia.Win32/Composition/ICompositorDesktopInterop.cs
new file mode 100644
index 0000000000..1d4cd3450f
--- /dev/null
+++ b/src/Windows/Avalonia.Win32/Composition/ICompositorDesktopInterop.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Runtime.InteropServices;
+using WinRT;
+
+namespace Windows.UI.Composition.Desktop
+{
+ [WindowsRuntimeType]
+ [Guid("29E691FA-4567-4DCA-B319-D0F207EB6807")]
+ public interface ICompositorDesktopInterop
+ {
+ void CreateDesktopWindowTarget(IntPtr hwndTarget, bool isTopmost, out IntPtr test);
+ }
+}
+
diff --git a/src/Windows/Avalonia.Win32/Composition/ICompositorDesktopInterop1.cs b/src/Windows/Avalonia.Win32/Composition/ICompositorDesktopInterop1.cs
new file mode 100644
index 0000000000..1c3f06d679
--- /dev/null
+++ b/src/Windows/Avalonia.Win32/Composition/ICompositorDesktopInterop1.cs
@@ -0,0 +1,69 @@
+using WinRT;
+
+namespace ABI.Windows.UI.Composition.Desktop
+{
+ using global::System;
+ using global::System.Runtime.InteropServices;
+
+ [Guid("29E691FA-4567-4DCA-B319-D0F207EB6807")]
+ internal class ICompositorDesktopInterop : global::Windows.UI.Composition.Desktop.ICompositorDesktopInterop
+
+ {
+ [Guid("29E691FA-4567-4DCA-B319-D0F207EB6807")]
+ public struct Vftbl
+ {
+ public delegate int _CreateDesktopWindowTarget(IntPtr thisPtr, IntPtr hwndTarget, byte isTopMost, out IntPtr desktopWindowTarget);
+
+ internal global::WinRT.Interop.IUnknownVftbl IUnknownVftbl;
+ public _CreateDesktopWindowTarget CreateDesktopWindowTarget;
+
+
+ public static readonly Vftbl AbiToProjectionVftable;
+ public static readonly IntPtr AbiToProjectionVftablePtr;
+
+ static Vftbl()
+ {
+ AbiToProjectionVftable = new Vftbl
+ {
+ IUnknownVftbl = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl,
+ CreateDesktopWindowTarget = Do_Abi_Create_Desktop_Window_Target
+ };
+ AbiToProjectionVftablePtr = Marshal.AllocHGlobal(Marshal.SizeOf());
+ Marshal.StructureToPtr(AbiToProjectionVftable, AbiToProjectionVftablePtr, false);
+ }
+
+ private static int Do_Abi_Create_Desktop_Window_Target(IntPtr thisPtr, IntPtr hwndTarget, byte isTopMost, out IntPtr desktopWindowTarget)
+ {
+ try
+ {
+ ComWrappersSupport.FindObject(thisPtr).CreateDesktopWindowTarget(hwndTarget, isTopMost != 0, out desktopWindowTarget);
+ return 0;
+ }
+ catch (Exception ex)
+ {
+ desktopWindowTarget = IntPtr.Zero;
+ return Marshal.GetHRForException(ex);
+ }
+ }
+ }
+ internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr);
+
+ public static implicit operator ICompositorDesktopInterop(IObjectReference obj) => (obj != null) ? new ICompositorDesktopInterop(obj) : null;
+ protected readonly ObjectReference _obj;
+ public IObjectReference ObjRef { get => _obj; }
+ public IntPtr ThisPtr => _obj.ThisPtr;
+ public ObjectReference AsInterface() => _obj.As();
+ public A As() => _obj.AsType ();
+ public ICompositorDesktopInterop(IObjectReference obj) : this(obj.As()) { }
+ internal ICompositorDesktopInterop(ObjectReference obj)
+ {
+ _obj = obj;
+ }
+
+ public void CreateDesktopWindowTarget(IntPtr hwndTarget, bool isTopmost, out IntPtr test)
+ {
+ Marshal.ThrowExceptionForHR(_obj.Vftbl.CreateDesktopWindowTarget(ThisPtr, hwndTarget, isTopmost ? (byte)1 : (byte)0, out test));
+ }
+ }
+}
+
diff --git a/src/Windows/Avalonia.Win32/Composition/ICompositorInterop.cs b/src/Windows/Avalonia.Win32/Composition/ICompositorInterop.cs
new file mode 100644
index 0000000000..d9b25e497e
--- /dev/null
+++ b/src/Windows/Avalonia.Win32/Composition/ICompositorInterop.cs
@@ -0,0 +1,139 @@
+using System;
+using System.Runtime.InteropServices;
+using WinRT;
+
+namespace Windows.UI.Composition.Interop
+{
+ [WindowsRuntimeType]
+ [Guid("25297D5C-3AD4-4C9C-B5CF-E36A38512330")]
+ public interface ICompositorInterop
+ {
+ ICompositionSurface CreateCompositionSurfaceForHandle(IntPtr swapChain);
+
+ ICompositionSurface CreateCompositionSurfaceForSwapChain(IntPtr swapChain);
+
+ CompositionGraphicsDevice CreateGraphicsDevice(IntPtr renderingDevice);
+ }
+}
+
+namespace ABI.Windows.UI.Composition.Interop
+{
+ using global::System;
+ using global::System.Runtime.InteropServices;
+ using global::Windows.UI.Composition;
+
+ [Guid("25297D5C-3AD4-4C9C-B5CF-E36A38512330")]
+ internal class ICompositorInterop : global::Windows.UI.Composition.Interop.ICompositorInterop
+
+ {
+ [Guid("25297D5C-3AD4-4C9C-B5CF-E36A38512330")]
+ public struct Vftbl
+ {
+ public delegate int _CreateCompositionSurfaceForHandle(IntPtr ThisPtr, IntPtr swapChain, out IntPtr result);
+ public delegate int _CreateCompositionSurfaceForSwapChain(IntPtr ThisPtr, IntPtr swapChain, out IntPtr result);
+ public delegate int _CreateGraphicsDevice(IntPtr ThisPtr, IntPtr renderingDevice, out IntPtr result);
+
+ internal global::WinRT.Interop.IUnknownVftbl IUnknownVftbl;
+ public _CreateCompositionSurfaceForHandle CreateCompositionSurfaceForHandle;
+ public _CreateCompositionSurfaceForSwapChain CreateCompositionSurfaceForSwapChain;
+ public _CreateGraphicsDevice CreateGraphicsDevice;
+
+
+ public static readonly Vftbl AbiToProjectionVftable;
+ public static readonly IntPtr AbiToProjectionVftablePtr;
+
+ static Vftbl()
+ {
+ AbiToProjectionVftable = new Vftbl
+ {
+ IUnknownVftbl = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl,
+
+ CreateCompositionSurfaceForHandle = Do_Abi_Create_Composition_Surface_For_Handle,
+ CreateCompositionSurfaceForSwapChain = Do_Abi_Create_Composition_Surface_For_SwapChain,
+ CreateGraphicsDevice= Do_Abi_Create_Graphics_Device
+ };
+ AbiToProjectionVftablePtr = Marshal.AllocHGlobal(Marshal.SizeOf());
+ Marshal.StructureToPtr(AbiToProjectionVftable, AbiToProjectionVftablePtr, false);
+ }
+
+ private static int Do_Abi_Create_Composition_Surface_For_Handle(IntPtr thisPtr, IntPtr swapChain, out IntPtr surface)
+ {
+ try
+ {
+ surface = IntPtr.Zero;
+ //surface = ComWrappersSupport.FindObject(thisPtr).CreateCompositionSurfaceForHandle(swapChain);
+ return 0;
+ }
+ catch (Exception ex)
+ {
+ surface = IntPtr.Zero;
+ return Marshal.GetHRForException(ex);
+ }
+ }
+
+ private static int Do_Abi_Create_Composition_Surface_For_SwapChain(IntPtr thisPtr, IntPtr swapChain, out IntPtr surface)
+ {
+ try
+ {
+ surface = IntPtr.Zero;
+ //surface = ComWrappersSupport.FindObject(thisPtr).CreateCompositionSurfaceForSwapChain(swapChain);
+ return 0;
+ }
+ catch (Exception ex)
+ {
+ surface = IntPtr.Zero;
+ return Marshal.GetHRForException(ex);
+ }
+ }
+
+ private static int Do_Abi_Create_Graphics_Device(IntPtr thisPtr, IntPtr renderingDevice, out IntPtr graphicsDevice)
+ {
+ try
+ {
+ graphicsDevice = ComWrappersSupport.FindObject(thisPtr).CreateGraphicsDevice(renderingDevice).ThisPtr;
+ return 0;
+ }
+ catch (Exception ex)
+ {
+ graphicsDevice = IntPtr.Zero;
+ return Marshal.GetHRForException(ex);
+ }
+ }
+ }
+ internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr);
+
+ public static implicit operator ICompositorInterop(IObjectReference obj) => (obj != null) ? new ICompositorInterop(obj) : null;
+ protected readonly ObjectReference _obj;
+ public IObjectReference ObjRef { get => _obj; }
+ public IntPtr ThisPtr => _obj.ThisPtr;
+ public ObjectReference AsInterface() => _obj.As();
+ public A As() => _obj.AsType ();
+ public ICompositorInterop(IObjectReference obj) : this(obj.As()) { }
+ internal ICompositorInterop(ObjectReference obj)
+ {
+ _obj = obj;
+ }
+
+ public ICompositionSurface CreateCompositionSurfaceForHandle(IntPtr swapChain)
+ {
+ Marshal.ThrowExceptionForHR(_obj.Vftbl.CreateCompositionSurfaceForHandle(ThisPtr, swapChain, out var compositionSurface));
+
+ return null;
+ }
+
+ public ICompositionSurface CreateCompositionSurfaceForSwapChain(IntPtr swapChain)
+ {
+ Marshal.ThrowExceptionForHR(_obj.Vftbl.CreateCompositionSurfaceForSwapChain(ThisPtr, swapChain, out var compositionSurface));
+
+ return null;
+ }
+
+ public CompositionGraphicsDevice CreateGraphicsDevice(IntPtr renderingDevice)
+ {
+ Marshal.ThrowExceptionForHR(_obj.Vftbl.CreateGraphicsDevice(ThisPtr, renderingDevice, out var graphicsDevice));
+
+ return CompositionGraphicsDevice.FromAbi(graphicsDevice);
+ }
+ }
+}
+
diff --git a/src/Windows/Avalonia.Win32/Composition/IGraphicsEffectD2D1Interop.cs b/src/Windows/Avalonia.Win32/Composition/IGraphicsEffectD2D1Interop.cs
new file mode 100644
index 0000000000..74d3939a98
--- /dev/null
+++ b/src/Windows/Avalonia.Win32/Composition/IGraphicsEffectD2D1Interop.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Runtime.InteropServices;
+using WinRT;
+
+namespace Windows.Graphics.Effects.Interop
+{
+ [WindowsRuntimeType]
+ [Guid("2FC57384-A068-44D7-A331-30982FCF7177")]
+ public interface IGraphicsEffectD2D1Interop
+ {
+ Guid EffectId { get; }
+
+ uint GetNamedPropertyMapping(string name, out GRAPHICS_EFFECT_PROPERTY_MAPPING mapping);
+
+ object GetProperty(uint index);
+
+ uint PropertyCount { get; }
+
+ IGraphicsEffectSource GetSource(uint index);
+
+ uint SourceCount { get; }
+ }
+}
+
diff --git a/src/Windows/Avalonia.Win32/Composition/IGraphicsEffectD2D1Interop1.cs b/src/Windows/Avalonia.Win32/Composition/IGraphicsEffectD2D1Interop1.cs
new file mode 100644
index 0000000000..8466b05fb5
--- /dev/null
+++ b/src/Windows/Avalonia.Win32/Composition/IGraphicsEffectD2D1Interop1.cs
@@ -0,0 +1,217 @@
+using WinRT;
+
+namespace ABI.Windows.Graphics.Effects.Interop
+{
+ using global::System;
+ using global::System.Runtime.InteropServices;
+
+ [Guid("2FC57384-A068-44D7-A331-30982FCF7177")]
+ internal class IGraphicsEffectD2D1Interop : global::Windows.Graphics.Effects.Interop.IGraphicsEffectD2D1Interop
+
+ {
+ [Guid("2FC57384-A068-44D7-A331-30982FCF7177")]
+ public struct Vftbl
+ {
+ public delegate int _GetEffectId(IntPtr thisPtr, out Guid guid);
+ public delegate int _GetNamedPropertyMapping(IntPtr thisPtr, IntPtr name, IntPtr index, IntPtr mapping);
+ public delegate int _GetProperty(IntPtr thisPtr, uint index, out IntPtr value);
+ public unsafe delegate int _GetPropertyCount(IntPtr thisPtr, uint* count);
+ public delegate int _GetSource(IntPtr thisPtr, uint index, out IntPtr source);
+ public delegate int _GetSourceCount(IntPtr thisPtr, out uint count);
+
+ internal global::WinRT.Interop.IUnknownVftbl IUnknownVftbl;
+ public _GetEffectId GetEffectId;
+ public _GetNamedPropertyMapping GetNamedPropertyMapping;
+ public _GetPropertyCount GetPropertyCount;
+ public _GetProperty GetProperty;
+ public _GetSource GetSource;
+ public _GetSourceCount GetSourceCount;
+
+ public static readonly Vftbl AbiToProjectionVftable;
+ public static readonly IntPtr AbiToProjectionVftablePtr;
+
+ unsafe static Vftbl()
+ {
+ AbiToProjectionVftable = new Vftbl
+ {
+ IUnknownVftbl = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl,
+ GetEffectId = Do_Abi_Get_Effect_Id,
+ GetNamedPropertyMapping = Do_Abi_Get_Property_Mapping,
+ GetPropertyCount = Do_Abi_Get_Property_Count,
+ GetProperty = Do_Abi_Get_Property,
+ GetSource = Do_Abi_Get_Source,
+ GetSourceCount = Do_Abi_Get_Source_Count
+
+ };
+ AbiToProjectionVftablePtr = Marshal.AllocHGlobal(Marshal.SizeOf());
+ Marshal.StructureToPtr(AbiToProjectionVftable, AbiToProjectionVftablePtr, false);
+ }
+
+ private static int Do_Abi_Get_Effect_Id(IntPtr thisPtr, out Guid guid)
+ {
+ guid = default;
+
+ try
+ {
+ guid = ComWrappersSupport.FindObject(thisPtr).EffectId;
+ }
+ catch (Exception ex)
+ {
+ return Marshal.GetHRForException(ex);
+ }
+
+ return 0;
+ }
+
+ private static int Do_Abi_Get_Property_Mapping(IntPtr thisPtr, IntPtr name, IntPtr index, IntPtr mapping)
+ {
+ try
+ {
+ ComWrappersSupport.FindObject(thisPtr).GetNamedPropertyMapping(MarshalString.FromAbi(name), out var mappingResult);
+ }
+ catch (Exception ex)
+ {
+ return Marshal.GetHRForException(ex);
+ }
+
+ return 0;
+ }
+
+ private static int Do_Abi_Get_Property(IntPtr thisPtr, uint index, out IntPtr value)
+ {
+ value = default;
+
+ try
+ {
+ value = MarshalInspectable.CreateMarshaler(
+ ComWrappersSupport.FindObject(thisPtr).GetProperty(index))
+ .As(Guid.Parse("4BD682DD-7554-40E9-9A9B-82654EDE7E62"))
+ .GetRef();
+
+ }
+ catch (Exception ex)
+ {
+ return Marshal.GetHRForException(ex);
+ }
+
+ return 0;
+ }
+
+ unsafe private static int Do_Abi_Get_Property_Count(IntPtr thisPtr, uint* count)
+ {
+
+ try
+ {
+ var res = ComWrappersSupport.FindObject(thisPtr).PropertyCount;
+
+ if (count != null)
+ {
+ *count = res;
+ }
+ }
+ catch (Exception ex)
+ {
+ return Marshal.GetHRForException(ex);
+ }
+
+ return 0;
+ }
+
+ private static int Do_Abi_Get_Source(IntPtr thisPtr, uint index, out IntPtr value)
+ {
+ value = default;
+
+ try
+ {
+ var source = ComWrappersSupport.FindObject(thisPtr).GetSource(index);
+
+ value = MarshalInterface.FromManaged(source);
+ }
+ catch (Exception ex)
+ {
+ return Marshal.GetHRForException(ex);
+ }
+
+ return 0;
+ }
+
+ private static int Do_Abi_Get_Source_Count(IntPtr thisPtr, out uint count)
+ {
+ count = default;
+
+ try
+ {
+ count = ComWrappersSupport.FindObject(thisPtr).SourceCount;
+ }
+ catch (Exception ex)
+ {
+ return Marshal.GetHRForException(ex);
+ }
+
+ return 0;
+ }
+ }
+ internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr);
+
+ public static implicit operator IGraphicsEffectD2D1Interop(IObjectReference obj) => (obj != null) ? new IGraphicsEffectD2D1Interop(obj) : null;
+ protected readonly ObjectReference _obj;
+ public IObjectReference ObjRef { get => _obj; }
+ public IntPtr ThisPtr => _obj.ThisPtr;
+
+ public ObjectReference AsInterface() => _obj.As();
+ public A As() => _obj.AsType ();
+ public IGraphicsEffectD2D1Interop(IObjectReference obj) : this(obj.As()) { }
+ internal IGraphicsEffectD2D1Interop(ObjectReference obj)
+ {
+ _obj = obj;
+ }
+
+ public Guid EffectId
+ {
+ get
+ {
+ Marshal.ThrowExceptionForHR(_obj.Vftbl.GetEffectId(ThisPtr, out Guid guid));
+ return guid;
+ }
+ }
+
+ public uint PropertyCount
+ {
+ get
+ {
+ unsafe
+ {
+ uint count = default;
+ Marshal.ThrowExceptionForHR(_obj.Vftbl.GetPropertyCount(ThisPtr, &count));
+ return count;
+ }
+ }
+ }
+
+ public uint SourceCount
+ {
+ get
+ {
+ Marshal.ThrowExceptionForHR(_obj.Vftbl.GetSourceCount(ThisPtr, out uint count));
+ return count;
+ }
+ }
+
+ public uint GetNamedPropertyMapping(string name, out global::Windows.Graphics.Effects.Interop.GRAPHICS_EFFECT_PROPERTY_MAPPING mapping)
+ {
+ throw new NotImplementedException();
+ }
+
+ public object GetProperty(uint index)
+ {
+ // Marshal.ThrowExceptionForHR(_obj.Vftbl.GetProperty(ThisPtr, index, out IntPtr value));
+ throw new NotImplementedException();
+ }
+
+ public global::Windows.Graphics.Effects.IGraphicsEffectSource GetSource(uint index)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
+
diff --git a/src/Windows/Avalonia.Win32/Composition/SaturationEffect.cs b/src/Windows/Avalonia.Win32/Composition/SaturationEffect.cs
new file mode 100644
index 0000000000..90eca22d8e
--- /dev/null
+++ b/src/Windows/Avalonia.Win32/Composition/SaturationEffect.cs
@@ -0,0 +1,35 @@
+using System;
+using Windows.Graphics.Effects;
+
+
+namespace Avalonia.Win32
+{
+ class SaturationEffect : EffectBase
+ {
+ public SaturationEffect(IGraphicsEffect source) : base(source)
+ {
+ }
+
+ enum D2D1_SATURATION_PROP
+ {
+ D2D1_SATURATION_PROP_SATURATION,
+ D2D1_SATURATION_PROP_FORCE_DWORD
+ };
+
+ public override Guid EffectId => D2DEffects.CLSID_D2D1Saturation;
+
+ public override uint PropertyCount => 1;
+
+ public override object GetProperty(uint index)
+ {
+ switch ((D2D1_SATURATION_PROP)index)
+ {
+ case D2D1_SATURATION_PROP.D2D1_SATURATION_PROP_SATURATION:
+ return 2.0f;
+ }
+
+ return null;
+ }
+ }
+}
+
diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
index 5feb6c9e46..a527a35c52 100644
--- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
+++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
@@ -466,6 +466,7 @@ namespace Avalonia.Win32.Interop
WS_VSCROLL = 0x200000,
WS_EX_DLGMODALFRAME = 0x00000001,
WS_EX_NOPARENTNOTIFY = 0x00000004,
+ WS_EX_NOREDIRECTIONBITMAP = 0x00200000,
WS_EX_TOPMOST = 0x00000008,
WS_EX_ACCEPTFILES = 0x00000010,
WS_EX_TRANSPARENT = 0x00000020,
diff --git a/src/Windows/Avalonia.Win32/Win32GlManager.cs b/src/Windows/Avalonia.Win32/Win32GlManager.cs
index 523a059e0e..6a6ce2ec3c 100644
--- a/src/Windows/Avalonia.Win32/Win32GlManager.cs
+++ b/src/Windows/Avalonia.Win32/Win32GlManager.cs
@@ -7,8 +7,6 @@ namespace Avalonia.Win32
{
static class Win32GlManager
{
- private static bool s_attemptedToInitialize;
-
public static void Initialize()
{
AvaloniaLocator.CurrentMutable.Bind().ToLazy(() =>
@@ -19,9 +17,21 @@ namespace Avalonia.Win32
var wgl = WglPlatformOpenGlInterface.TryCreate();
return wgl;
}
-
+
if (opts?.AllowEglInitialization == true)
- return EglPlatformOpenGlInterface.TryCreate(() => new AngleWin32EglDisplay());
+ {
+ var egl = EglPlatformOpenGlInterface.TryCreate(() => new AngleWin32EglDisplay());
+
+ if (egl is { } &&
+ opts?.UseWindowsUIComposition == true &&
+ Win32Platform.WindowsVersion.Major >= 10 &&
+ Win32Platform.WindowsVersion.Build >= 16299)
+ {
+ AvaloniaLocator.CurrentMutable.BindToSelf(new CompositionConnector(egl));
+ }
+
+ return egl;
+ }
return null;
});
diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs
index 61c1a4f45e..5b16cae26e 100644
--- a/src/Windows/Avalonia.Win32/Win32Platform.cs
+++ b/src/Windows/Avalonia.Win32/Win32Platform.cs
@@ -12,6 +12,7 @@ using Avalonia.Controls.Platform;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.OpenGL;
+using Avalonia.OpenGL.Egl;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Threading;
@@ -46,6 +47,15 @@ namespace Avalonia
new GlVersion(GlProfileType.OpenGL, 4, 0),
new GlVersion(GlProfileType.OpenGL, 3, 2),
};
+
+ ///
+ /// Render Avalonia to a Texture inside the Windows.UI.Composition tree.
+ ///
+ ///
+ /// Supported on Windows 10 build 16299 and above. Ignored on other versions.
+ /// This is recommended if you need to use AcrylicBlur or acrylic in your applications.
+ ///
+ public bool UseWindowsUIComposition { get; set; } = true;
}
}
@@ -104,7 +114,7 @@ namespace Avalonia.Win32
.Bind().ToConstant(new WindowsMountedVolumeInfoProvider());
Win32GlManager.Initialize();
-
+
_uiThread = Thread.CurrentThread;
if (OleContext.Current != null)
diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs
index 7079a0120c..715c8fc01d 100644
--- a/src/Windows/Avalonia.Win32/WindowImpl.cs
+++ b/src/Windows/Avalonia.Win32/WindowImpl.cs
@@ -7,6 +7,7 @@ using Avalonia.Controls.Platform;
using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.OpenGL;
+using Avalonia.OpenGL.Angle;
using Avalonia.OpenGL.Egl;
using Avalonia.OpenGL.Surfaces;
using Avalonia.Platform;
@@ -48,6 +49,8 @@ namespace Avalonia.Win32
private Thickness _extendedMargins;
private Thickness _offScreenMargin;
private double _extendTitleBarHint = -1;
+ private bool _isUsingComposition;
+ private IBlurHost _blurHost;
#if USE_MANAGED_DRAG
private readonly ManagedWindowResizeDragHelper _managedDrag;
@@ -102,16 +105,37 @@ namespace Avalonia.Win32
};
_rendererLock = new ManagedDeferredRendererLock();
+ var glPlatform = AvaloniaLocator.Current.GetService();
+
+ var compositionConnector = AvaloniaLocator.Current.GetService();
+
+ _isUsingComposition = compositionConnector is { } &&
+ glPlatform is EglPlatformOpenGlInterface egl &&
+ egl.Display is AngleWin32EglDisplay angleDisplay &&
+ angleDisplay.PlatformApi == AngleOptions.PlatformApi.DirectX11;
CreateWindow();
_framebuffer = new FramebufferManager(_hwnd);
-
- var glPlatform = AvaloniaLocator.Current.GetService();
- if(glPlatform is EglPlatformOpenGlInterface egl)
- _gl = new EglGlPlatformSurface(egl, this);
- else if (glPlatform is WglPlatformOpenGlInterface wgl)
- _gl = new WglGlPlatformSurface(wgl.PrimaryContext, this);
+ if (glPlatform != null)
+ {
+ if (_isUsingComposition)
+ {
+ var cgl = new CompositionEglGlPlatformSurface(glPlatform as EglPlatformOpenGlInterface, this);
+ _blurHost = cgl.AttachToCompositionTree(compositionConnector, _hwnd);
+
+ _gl = cgl;
+
+ _isUsingComposition = true;
+ }
+ else
+ {
+ if (glPlatform is EglPlatformOpenGlInterface egl2)
+ _gl = new EglGlPlatformSurface(egl2, this);
+ else if (glPlatform is WglPlatformOpenGlInterface wgl)
+ _gl = new WglGlPlatformSurface(wgl.PrimaryContext, this);
+ }
+ }
Screen = new ScreenImpl();
@@ -328,54 +352,63 @@ namespace Avalonia.Win32
private WindowTransparencyLevel Win10EnableBlur(WindowTransparencyLevel transparencyLevel)
{
- bool canUseAcrylic = Win32Platform.WindowsVersion.Major > 10 || Win32Platform.WindowsVersion.Build >= 19628;
-
- var accent = new AccentPolicy();
- var accentStructSize = Marshal.SizeOf(accent);
-
- if (transparencyLevel == WindowTransparencyLevel.AcrylicBlur && !canUseAcrylic)
+ if (_isUsingComposition)
{
- transparencyLevel = WindowTransparencyLevel.Blur;
- }
+ _blurHost?.SetBlur(transparencyLevel >= WindowTransparencyLevel.Blur);
- switch (transparencyLevel)
+ return transparencyLevel;
+ }
+ else
{
- default:
- case WindowTransparencyLevel.None:
- accent.AccentState = AccentState.ACCENT_DISABLED;
- break;
+ bool canUseAcrylic = Win32Platform.WindowsVersion.Major > 10 || Win32Platform.WindowsVersion.Build >= 19628;
- case WindowTransparencyLevel.Transparent:
- accent.AccentState = AccentState.ACCENT_ENABLE_TRANSPARENTGRADIENT;
- break;
+ var accent = new AccentPolicy();
+ var accentStructSize = Marshal.SizeOf(accent);
- case WindowTransparencyLevel.Blur:
- accent.AccentState = AccentState.ACCENT_ENABLE_BLURBEHIND;
- break;
+ if (transparencyLevel == WindowTransparencyLevel.AcrylicBlur && !canUseAcrylic)
+ {
+ transparencyLevel = WindowTransparencyLevel.Blur;
+ }
- case WindowTransparencyLevel.AcrylicBlur:
- case (WindowTransparencyLevel.AcrylicBlur + 1): // hack-force acrylic.
- accent.AccentState = AccentState.ACCENT_ENABLE_ACRYLIC;
- transparencyLevel = WindowTransparencyLevel.AcrylicBlur;
- break;
- }
+ switch (transparencyLevel)
+ {
+ default:
+ case WindowTransparencyLevel.None:
+ accent.AccentState = AccentState.ACCENT_DISABLED;
+ break;
+
+ case WindowTransparencyLevel.Transparent:
+ accent.AccentState = AccentState.ACCENT_ENABLE_TRANSPARENTGRADIENT;
+ break;
+
+ case WindowTransparencyLevel.Blur:
+ accent.AccentState = AccentState.ACCENT_ENABLE_BLURBEHIND;
+ break;
+
+ case WindowTransparencyLevel.AcrylicBlur:
+ case (WindowTransparencyLevel.AcrylicBlur + 1): // hack-force acrylic.
+ accent.AccentState = AccentState.ACCENT_ENABLE_ACRYLIC;
+ transparencyLevel = WindowTransparencyLevel.AcrylicBlur;
+ break;
+ }
- accent.AccentFlags = 2;
- accent.GradientColor = 0x01000000;
+ accent.AccentFlags = 2;
+ accent.GradientColor = 0x01000000;
- var accentPtr = Marshal.AllocHGlobal(accentStructSize);
- Marshal.StructureToPtr(accent, accentPtr, false);
+ var accentPtr = Marshal.AllocHGlobal(accentStructSize);
+ Marshal.StructureToPtr(accent, accentPtr, false);
- var data = new WindowCompositionAttributeData();
- data.Attribute = WindowCompositionAttribute.WCA_ACCENT_POLICY;
- data.SizeOfData = accentStructSize;
- data.Data = accentPtr;
+ var data = new WindowCompositionAttributeData();
+ data.Attribute = WindowCompositionAttribute.WCA_ACCENT_POLICY;
+ data.SizeOfData = accentStructSize;
+ data.Data = accentPtr;
- SetWindowCompositionAttribute(_hwnd, ref data);
+ SetWindowCompositionAttribute(_hwnd, ref data);
- Marshal.FreeHGlobal(accentPtr);
+ Marshal.FreeHGlobal(accentPtr);
- return transparencyLevel;
+ return transparencyLevel;
+ }
}
public IEnumerable Surfaces => new object[] { Handle, _gl, _framebuffer };
@@ -622,7 +655,7 @@ namespace Avalonia.Win32
protected virtual IntPtr CreateWindowOverride(ushort atom)
{
return CreateWindowEx(
- 0,
+ _isUsingComposition ? (int)WindowStyles.WS_EX_NOREDIRECTIONBITMAP : 0,
atom,
null,
(int)WindowStyles.WS_OVERLAPPEDWINDOW | (int) WindowStyles.WS_CLIPCHILDREN,
diff --git a/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj b/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj
index 51d18e55d1..a2d27fd579 100644
--- a/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj
+++ b/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj
@@ -23,5 +23,8 @@
+
+
+
diff --git a/tests/Avalonia.RenderTests/Controls/TextBlockTests.cs b/tests/Avalonia.RenderTests/Controls/TextBlockTests.cs
index ab769e4ff0..53a2959848 100644
--- a/tests/Avalonia.RenderTests/Controls/TextBlockTests.cs
+++ b/tests/Avalonia.RenderTests/Controls/TextBlockTests.cs
@@ -40,5 +40,59 @@ namespace Avalonia.Direct2D1.RenderTests.Controls
await RenderToFile(target);
CompareImages();
}
+
+
+ [Win32Fact("Has text")]
+ public async Task RestrictedHeight_VerticalAlign()
+ {
+ IControl text(VerticalAlignment verticalAlingnment, bool clip = true, bool restrictHeight = true)
+ {
+ return new Border()
+ {
+ BorderBrush = Brushes.Blue,
+ BorderThickness = new Thickness(1),
+ VerticalAlignment = VerticalAlignment.Center,
+ HorizontalAlignment = HorizontalAlignment.Center,
+ Height = restrictHeight ? 20 : double.NaN,
+ Margin = new Thickness(1),
+ Child = new TextBlock
+ {
+ FontFamily = new FontFamily("Courier New"),
+ Background = Brushes.Red,
+ FontSize = 24,
+ Foreground = Brushes.Black,
+ Text = "L",
+ VerticalAlignment = verticalAlingnment,
+ ClipToBounds = clip
+ }
+ };
+ }
+ Decorator target = new Decorator
+ {
+ Padding = new Thickness(8),
+ Width = 180,
+ Height = 80,
+
+ Child = new StackPanel()
+ {
+ Orientation = Orientation.Horizontal,
+ Children =
+ {
+ text(VerticalAlignment.Stretch, restrictHeight: false),
+ text(VerticalAlignment.Center),
+ text(VerticalAlignment.Stretch),
+ text(VerticalAlignment.Top),
+ text(VerticalAlignment.Bottom),
+ text(VerticalAlignment.Center, clip:false),
+ text(VerticalAlignment.Stretch, clip:false),
+ text(VerticalAlignment.Top, clip:false),
+ text(VerticalAlignment.Bottom, clip:false),
+ }
+ }
+ };
+
+ await RenderToFile(target);
+ CompareImages();
+ }
}
}
diff --git a/tests/TestFiles/Direct2D1/Controls/TextBlock/RestrictedHeight_VerticalAlign.expected.png b/tests/TestFiles/Direct2D1/Controls/TextBlock/RestrictedHeight_VerticalAlign.expected.png
new file mode 100644
index 0000000000..c5a0a14e52
Binary files /dev/null and b/tests/TestFiles/Direct2D1/Controls/TextBlock/RestrictedHeight_VerticalAlign.expected.png differ
diff --git a/tests/TestFiles/Skia/Controls/TextBlock/RestrictedHeight_VerticalAlign.expected.png b/tests/TestFiles/Skia/Controls/TextBlock/RestrictedHeight_VerticalAlign.expected.png
new file mode 100644
index 0000000000..c5a0a14e52
Binary files /dev/null and b/tests/TestFiles/Skia/Controls/TextBlock/RestrictedHeight_VerticalAlign.expected.png differ