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