diff --git a/samples/interop/Direct3DInteropSample/MainWindow.cs b/samples/interop/Direct3DInteropSample/MainWindow.cs index 3fe3b896f6..ffa0de0a36 100644 --- a/samples/interop/Direct3DInteropSample/MainWindow.cs +++ b/samples/interop/Direct3DInteropSample/MainWindow.cs @@ -253,7 +253,7 @@ namespace Direct3DInteropSample public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer) { - return new DrawingContextImpl(visualBrushRenderer, _window._d2dRenderTarget, + return new DrawingContextImpl(visualBrushRenderer, null, _window._d2dRenderTarget, AvaloniaLocator.Current.GetService(), AvaloniaLocator.Current.GetService()); } diff --git a/src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs b/src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs index 1bae88cb5f..3db4527bfb 100644 --- a/src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs +++ b/src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs @@ -80,6 +80,20 @@ namespace Avalonia.Platform /// The corner radius. void FillRectangle(IBrush brush, Rect rect, float cornerRadius = 0.0f); + /// + /// Creates a new that can be used as a render layer + /// for the current render target. + /// + /// The size of the layer in DIPs. + /// An + /// + /// Depending on the rendering backend used, a layer created via this method may be more + /// performant than a standard render target bitmap. In particular the Direct2D backend + /// has to do a format conversion each time a standard render target bitmap is rendered, + /// but a layer created via this method has no such overhead. + /// + IRenderTargetBitmapImpl CreateLayer(Size size); + /// /// Pushes a clip rectange. /// diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs index 0b01960d5b..5fcd0e42bd 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs @@ -194,6 +194,11 @@ namespace Avalonia.Rendering.SceneGraph } } + public IRenderTargetBitmapImpl CreateLayer(Size size) + { + throw new NotImplementedException(); + } + /// public void PopClip() { diff --git a/src/Gtk/Avalonia.Cairo/Media/DrawingContext.cs b/src/Gtk/Avalonia.Cairo/Media/DrawingContext.cs index 99b0a2ec73..7900f2e440 100644 --- a/src/Gtk/Avalonia.Cairo/Media/DrawingContext.cs +++ b/src/Gtk/Avalonia.Cairo/Media/DrawingContext.cs @@ -227,6 +227,12 @@ namespace Avalonia.Cairo.Media } } + public IRenderTargetBitmapImpl CreateLayer(Size size) + { + var surface = new Cairo.ImageSurface(Cairo.Format.Argb32, (int)size.Width, (int)size.Height); + return new RenderTargetBitmapImpl(surface); + } + /// /// Pushes a clip rectange. /// diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs index 7a83835b10..f01e9858e7 100644 --- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs @@ -349,6 +349,12 @@ namespace Avalonia.Skia } } + public IRenderTargetBitmapImpl CreateLayer(Size size) + { + var pixelSize = size * (_dpi / 96); + return new BitmapImpl((int)pixelSize.Width, (int)pixelSize.Height, _dpi); + } + public void PushClip(Rect clip) { Canvas.Save(); diff --git a/src/Windows/Avalonia.Direct2D1/ExternalRenderTarget.cs b/src/Windows/Avalonia.Direct2D1/ExternalRenderTarget.cs index d735c5f8bb..176cedd377 100644 --- a/src/Windows/Avalonia.Direct2D1/ExternalRenderTarget.cs +++ b/src/Windows/Avalonia.Direct2D1/ExternalRenderTarget.cs @@ -1,5 +1,6 @@ using System; using Avalonia.Direct2D1.Media; +using Avalonia.Direct2D1.Media.Imaging; using Avalonia.Platform; using Avalonia.Rendering; using SharpDX; @@ -7,7 +8,7 @@ using DirectWriteFactory = SharpDX.DirectWrite.Factory; namespace Avalonia.Direct2D1 { - class ExternalRenderTarget : IRenderTarget + class ExternalRenderTarget : IRenderTarget, ILayerFactory { private readonly IExternalDirect2DRenderTargetSurface _externalRenderTargetProvider; private readonly DirectWriteFactory _dwFactory; @@ -32,7 +33,7 @@ namespace Avalonia.Direct2D1 { var target = _externalRenderTargetProvider.GetOrCreateRenderTarget(); _externalRenderTargetProvider.BeforeDrawing(); - return new DrawingContextImpl(visualBrushRenderer, target, _dwFactory, _wicFactory, null, () => + return new DrawingContextImpl(visualBrushRenderer, null, target, _dwFactory, _wicFactory, null, () => { try { @@ -44,5 +45,15 @@ namespace Avalonia.Direct2D1 } }); } + + public IRenderTargetBitmapImpl CreateLayer(Size size) + { + var target = _externalRenderTargetProvider.GetOrCreateRenderTarget(); + return D2DRenderTargetBitmapImpl.CreateCompatible( + _wicFactory, + _dwFactory, + target, + size); + } } } diff --git a/src/Windows/Avalonia.Direct2D1/ICreateLayer.cs b/src/Windows/Avalonia.Direct2D1/ICreateLayer.cs deleted file mode 100644 index 9214377e6f..0000000000 --- a/src/Windows/Avalonia.Direct2D1/ICreateLayer.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; -using Avalonia.Platform; - -namespace Avalonia.Direct2D1 -{ - internal interface ICreateLayer - { - IRenderTargetBitmapImpl CreateLayer(int pixelWidth, int pixelHeight); - } -} diff --git a/src/Windows/Avalonia.Direct2D1/ILayerFactory.cs b/src/Windows/Avalonia.Direct2D1/ILayerFactory.cs new file mode 100644 index 0000000000..99f8d4f7ac --- /dev/null +++ b/src/Windows/Avalonia.Direct2D1/ILayerFactory.cs @@ -0,0 +1,10 @@ +using System; +using Avalonia.Platform; + +namespace Avalonia.Direct2D1 +{ + public interface ILayerFactory + { + IRenderTargetBitmapImpl CreateLayer(Size size); + } +} diff --git a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs index a7bb73e19f..6a72923ce3 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs @@ -18,6 +18,7 @@ namespace Avalonia.Direct2D1.Media public class DrawingContextImpl : IDrawingContextImpl, IDisposable { private readonly IVisualBrushRenderer _visualBrushRenderer; + private readonly ILayerFactory _layerFactory; private readonly SharpDX.Direct2D1.RenderTarget _renderTarget; private readonly SharpDX.DXGI.SwapChain1 _swapChain; private readonly Action _finishedCallback; @@ -29,12 +30,17 @@ namespace Avalonia.Direct2D1.Media /// /// The visual brush renderer. /// The render target to draw to. + /// + /// An object to use to create layers. May be null, in which case a + /// will created when a new layer is requested. + /// /// The DirectWrite factory. /// The WIC imaging factory. /// An optional swap chain associated with this drawing context. /// An optional delegate to be called when context is disposed. public DrawingContextImpl( IVisualBrushRenderer visualBrushRenderer, + ILayerFactory layerFactory, SharpDX.Direct2D1.RenderTarget renderTarget, SharpDX.DirectWrite.Factory directWriteFactory, SharpDX.WIC.ImagingFactory imagingFactory, @@ -42,6 +48,7 @@ namespace Avalonia.Direct2D1.Media Action finishedCallback = null) { _visualBrushRenderer = visualBrushRenderer; + _layerFactory = layerFactory; _renderTarget = renderTarget; _swapChain = swapChain; _finishedCallback = finishedCallback; @@ -285,6 +292,25 @@ namespace Avalonia.Direct2D1.Media } } + public IRenderTargetBitmapImpl CreateLayer(Size size) + { + if (_layerFactory != null) + { + return _layerFactory.CreateLayer(size); + } + else + { + var platform = AvaloniaLocator.Current.GetService(); + var dpi = new Vector(_renderTarget.DotsPerInch.Width, _renderTarget.DotsPerInch.Height); + var pixelSize = size * (dpi / 96); + return platform.CreateRenderTargetBitmap( + (int)pixelSize.Width, + (int)pixelSize.Height, + dpi.X, + dpi.Y); + } + } + /// /// Pushes a clip rectange. /// diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs index 6162d57bad..2843848fac 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs @@ -9,7 +9,7 @@ using DirectWriteFactory = SharpDX.DirectWrite.Factory; namespace Avalonia.Direct2D1.Media.Imaging { - public class D2DRenderTargetBitmapImpl : D2DBitmapImpl, IRenderTargetBitmapImpl, ICreateLayer + public class D2DRenderTargetBitmapImpl : D2DBitmapImpl, IRenderTargetBitmapImpl, ILayerFactory { private readonly DirectWriteFactory _dwriteFactory; private readonly BitmapRenderTarget _target; @@ -31,13 +31,12 @@ namespace Avalonia.Direct2D1.Media.Imaging ImagingFactory imagingFactory, DirectWriteFactory dwriteFactory, SharpDX.Direct2D1.RenderTarget renderTarget, - int pixelWidth, - int pixelHeight) + Size size) { var bitmapRenderTarget = new BitmapRenderTarget( renderTarget, CompatibleRenderTargetOptions.None, - new Size2F(pixelWidth, pixelHeight)); + new Size2F((float)size.Width, (float)size.Height)); return new D2DRenderTargetBitmapImpl(imagingFactory, dwriteFactory, bitmapRenderTarget); } @@ -45,14 +44,15 @@ namespace Avalonia.Direct2D1.Media.Imaging { return new DrawingContextImpl( visualBrushRenderer, + this, _target, _dwriteFactory, WicImagingFactory); } - public IRenderTargetBitmapImpl CreateLayer(int pixelWidth, int pixelHeight) + public IRenderTargetBitmapImpl CreateLayer(Size size) { - return CreateCompatible(WicImagingFactory, _dwriteFactory, _target, pixelWidth, pixelHeight); + return CreateCompatible(WicImagingFactory, _dwriteFactory, _target, size); } public override void Dispose() diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicRenderTargetBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicRenderTargetBitmapImpl.cs index 1540ee333a..f1065370b5 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicRenderTargetBitmapImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicRenderTargetBitmapImpl.cs @@ -47,7 +47,12 @@ namespace Avalonia.Direct2D1.Media public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer) { - return new DrawingContextImpl(visualBrushRenderer, _target, _dwriteFactory, WicImagingFactory); + return new DrawingContextImpl( + visualBrushRenderer, + null, + _target, + _dwriteFactory, + WicImagingFactory); } } } diff --git a/src/Windows/Avalonia.Direct2D1/RenderTarget.cs b/src/Windows/Avalonia.Direct2D1/RenderTarget.cs index cbeff8b2f1..6086b0c67c 100644 --- a/src/Windows/Avalonia.Direct2D1/RenderTarget.cs +++ b/src/Windows/Avalonia.Direct2D1/RenderTarget.cs @@ -3,6 +3,7 @@ using System; using Avalonia.Direct2D1.Media; +using Avalonia.Direct2D1.Media.Imaging; using Avalonia.Platform; using Avalonia.Rendering; using SharpDX.Direct2D1; @@ -11,7 +12,7 @@ using WicFactory = SharpDX.WIC.ImagingFactory; namespace Avalonia.Direct2D1 { - public class RenderTarget : IRenderTarget + public class RenderTarget : IRenderTarget, ILayerFactory { /// /// The render target. @@ -40,7 +41,16 @@ namespace Avalonia.Direct2D1 /// An . public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer) { - return new DrawingContextImpl(visualBrushRenderer, _renderTarget, DirectWriteFactory, WicFactory); + return new DrawingContextImpl(visualBrushRenderer, this, _renderTarget, DirectWriteFactory, WicFactory); + } + + public IRenderTargetBitmapImpl CreateLayer(Size size) + { + return D2DRenderTargetBitmapImpl.CreateCompatible( + WicFactory, + DirectWriteFactory, + _renderTarget, + size); } public void Dispose() diff --git a/src/Windows/Avalonia.Direct2D1/SwapChainRenderTarget.cs b/src/Windows/Avalonia.Direct2D1/SwapChainRenderTarget.cs index bd6555159c..0a23c63498 100644 --- a/src/Windows/Avalonia.Direct2D1/SwapChainRenderTarget.cs +++ b/src/Windows/Avalonia.Direct2D1/SwapChainRenderTarget.cs @@ -14,7 +14,7 @@ using Avalonia.Direct2D1.Media.Imaging; namespace Avalonia.Direct2D1 { - public abstract class SwapChainRenderTarget : IRenderTarget, ICreateLayer + public abstract class SwapChainRenderTarget : IRenderTarget, ILayerFactory { private Size2 _savedSize; private Size2F _savedDpi; @@ -56,13 +56,14 @@ namespace Avalonia.Direct2D1 return new DrawingContextImpl( visualBrushRenderer, + this, _deviceContext, DirectWriteFactory, WicImagingFactory, _swapChain); } - public IRenderTargetBitmapImpl CreateLayer(int pixelWidth, int pixelHeight) + public IRenderTargetBitmapImpl CreateLayer(Size size) { if (_deviceContext == null) { @@ -73,8 +74,7 @@ namespace Avalonia.Direct2D1 WicImagingFactory, DirectWriteFactory, _deviceContext, - pixelWidth, - pixelHeight); + size); } public void Dispose()