diff --git a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs index fd8364c03b..90409bb208 100644 --- a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs +++ b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs @@ -146,6 +146,8 @@ namespace Avalonia.Direct2D1 } if (s is IExternalDirect2DRenderTargetSurface external) return new ExternalRenderTarget(external, s_dwfactory); + if (s is IFramebufferPlatformSurface fb) + return new FramebufferShimRenderTarget(fb, s_imagingFactory, s_d2D1Factory, s_dwfactory); } throw new NotSupportedException("Don't know how to create a Direct2D1 renderer from any of provided surfaces"); } diff --git a/src/Windows/Avalonia.Direct2D1/FramebufferShimRenderTarget.cs b/src/Windows/Avalonia.Direct2D1/FramebufferShimRenderTarget.cs new file mode 100644 index 0000000000..d465af5322 --- /dev/null +++ b/src/Windows/Avalonia.Direct2D1/FramebufferShimRenderTarget.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Avalonia.Controls.Platform.Surfaces; +using Avalonia.Direct2D1.Media; +using Avalonia.Direct2D1.Media.Imaging; +using Avalonia.Platform; +using Avalonia.Rendering; +using Avalonia.Win32.Interop; +using SharpDX.Direct2D1; +using SharpDX.WIC; +using PixelFormat = Avalonia.Platform.PixelFormat; + +namespace Avalonia.Direct2D1 +{ + class FramebufferShimRenderTarget : IRenderTarget + { + private readonly IFramebufferPlatformSurface _surface; + private readonly ImagingFactory _imagingFactory; + private readonly Factory _d2DFactory; + private readonly SharpDX.DirectWrite.Factory _dwriteFactory; + + public FramebufferShimRenderTarget(IFramebufferPlatformSurface surface, + ImagingFactory imagingFactory, Factory d2dFactory, SharpDX.DirectWrite.Factory dwriteFactory) + { + _surface = surface; + _imagingFactory = imagingFactory; + _d2DFactory = d2dFactory; + _dwriteFactory = dwriteFactory; + } + + public void Dispose() + { + + } + + public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer) + { + var locked = _surface.Lock(); + if (locked.Format == PixelFormat.Rgb565) + { + locked.Dispose(); + throw new ArgumentException("Unsupported pixel format: " + locked.Format); + } + + return new FramebufferShim(locked, _imagingFactory, _d2DFactory, _dwriteFactory) + .CreateDrawingContext(visualBrushRenderer); + } + + class FramebufferShim : RenderTargetBitmapImpl + { + private readonly ILockedFramebuffer _target; + + public FramebufferShim(ILockedFramebuffer target, + ImagingFactory imagingFactory, Factory d2dFactory, SharpDX.DirectWrite.Factory dwriteFactory + ) : base(imagingFactory, d2dFactory, dwriteFactory, + target.Width, target.Height, target.Dpi.X, target.Dpi.Y, target.Format) + { + _target = target; + } + + public override IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer) + { + return base.CreateDrawingContext(visualBrushRenderer, () => + { + using (var l = WicImpl.Lock(BitmapLockFlags.Read)) + { + for (var y = 0; y < _target.Height; y++) + { + UnmanagedMethods.CopyMemory( + _target.Address + _target.RowBytes * y, + l.Data.DataPointer + l.Stride * y, + (uint) Math.Min(l.Stride, _target.RowBytes)); + } + } + Dispose(); + _target.Dispose(); + + }); + } + } + + } +} diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/RenderTargetBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/RenderTargetBitmapImpl.cs index 33736b02cb..0d6ed9f39f 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/RenderTargetBitmapImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/RenderTargetBitmapImpl.cs @@ -22,8 +22,9 @@ namespace Avalonia.Direct2D1.Media int width, int height, double dpiX, - double dpiY) - : base(imagingFactory, width, height) + double dpiY, + Platform.PixelFormat? pixelFormat = null) + : base(imagingFactory, width, height, pixelFormat) { var props = new RenderTargetProperties { @@ -45,9 +46,13 @@ namespace Avalonia.Direct2D1.Media base.Dispose(); } - public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer) + public virtual IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer) + => CreateDrawingContext(visualBrushRenderer, null); + + public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer, Action finishedCallback) { - return new DrawingContextImpl(visualBrushRenderer, _target, _dwriteFactory); + return new DrawingContextImpl(visualBrushRenderer, _target, _dwriteFactory, + finishedCallback: finishedCallback); } } } diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs index e817dd4812..bcce2496cd 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicBitmapImpl.cs @@ -74,6 +74,7 @@ namespace Avalonia.Direct2D1.Media public WicBitmapImpl(ImagingFactory factory, Platform.PixelFormat format, IntPtr data, int width, int height, int stride) { WicImpl = new Bitmap(factory, width, height, format.ToWic(), BitmapCreateCacheOption.CacheOnDemand); + _factory = factory; PixelFormat = format; using (var l = WicImpl.Lock(BitmapLockFlags.Write)) { diff --git a/tests/Avalonia.RenderTests/Media/BitmapTests.cs b/tests/Avalonia.RenderTests/Media/BitmapTests.cs index f01f78ae94..a7cd06a894 100644 --- a/tests/Avalonia.RenderTests/Media/BitmapTests.cs +++ b/tests/Avalonia.RenderTests/Media/BitmapTests.cs @@ -64,13 +64,13 @@ namespace Avalonia.Direct2D1.RenderTests.Media public void Deallocate() => Marshal.FreeHGlobal(Address); } - -#if AVALONIA_SKIA + [Theory] -#else - [Theory(Skip = "Framebuffer not supported")] + [InlineData(PixelFormat.Rgba8888), InlineData(PixelFormat.Bgra8888), +#if SKIA + InlineData(PixelFormat.Rgb565) #endif - [InlineData(PixelFormat.Rgba8888), InlineData(PixelFormat.Bgra8888), InlineData(PixelFormat.Rgb565)] + ] public void FramebufferRenderResultsShouldBeUsableAsBitmap(PixelFormat fmt) { var testName = nameof(FramebufferRenderResultsShouldBeUsableAsBitmap) + "_" + fmt; @@ -84,6 +84,7 @@ namespace Avalonia.Direct2D1.RenderTests.Media ctx.FillRectangle(Brushes.Chartreuse, new Rect(0, 0, 20, 100)); ctx.FillRectangle(Brushes.Crimson, new Rect(20, 0, 20, 100)); ctx.FillRectangle(Brushes.Gold, new Rect(40, 0, 20, 100)); + ctx.PopOpacity(); } var bmp = new Bitmap(fmt, fb.Address, fb.Width, fb.Height, fb.RowBytes); diff --git a/tests/TestFiles/Direct2D1/Media/Bitmap/FramebufferRenderResultsShouldBeUsableAsBitmap_Bgra8888.expected.png b/tests/TestFiles/Direct2D1/Media/Bitmap/FramebufferRenderResultsShouldBeUsableAsBitmap_Bgra8888.expected.png new file mode 100644 index 0000000000..19686464c5 Binary files /dev/null and b/tests/TestFiles/Direct2D1/Media/Bitmap/FramebufferRenderResultsShouldBeUsableAsBitmap_Bgra8888.expected.png differ diff --git a/tests/TestFiles/Direct2D1/Media/Bitmap/FramebufferRenderResultsShouldBeUsableAsBitmap_Rgb565.expected.png b/tests/TestFiles/Direct2D1/Media/Bitmap/FramebufferRenderResultsShouldBeUsableAsBitmap_Rgb565.expected.png new file mode 100644 index 0000000000..f3d20008a1 Binary files /dev/null and b/tests/TestFiles/Direct2D1/Media/Bitmap/FramebufferRenderResultsShouldBeUsableAsBitmap_Rgb565.expected.png differ diff --git a/tests/TestFiles/Direct2D1/Media/Bitmap/FramebufferRenderResultsShouldBeUsableAsBitmap_Rgba8888.expected.png b/tests/TestFiles/Direct2D1/Media/Bitmap/FramebufferRenderResultsShouldBeUsableAsBitmap_Rgba8888.expected.png new file mode 100644 index 0000000000..19686464c5 Binary files /dev/null and b/tests/TestFiles/Direct2D1/Media/Bitmap/FramebufferRenderResultsShouldBeUsableAsBitmap_Rgba8888.expected.png differ