Browse Source
Conflicts: src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs src/Windows/Avalonia.Direct2D1/HwndRenderTarget.cs src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs src/Windows/Avalonia.Direct2D1/Media/Imaging/BitmapImpl.cs src/Windows/Avalonia.Direct2D1/RenderTarget.csscenegraph-after-breakage
49 changed files with 860 additions and 330 deletions
@ -1,130 +1,24 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Platform; |
|||
using SharpDX.WIC; |
|||
using SharpDX.Direct2D1; |
|||
|
|||
namespace Avalonia.Direct2D1.Media |
|||
{ |
|||
/// <summary>
|
|||
/// A Direct2D implementation of a <see cref="Avalonia.Media.Imaging.Bitmap"/>.
|
|||
/// </summary>
|
|||
public class BitmapImpl : IBitmapImpl |
|||
public abstract class BitmapImpl : IBitmapImpl, IDisposable |
|||
{ |
|||
private readonly ImagingFactory _factory; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="BitmapImpl"/> class.
|
|||
/// </summary>
|
|||
/// <param name="factory">The WIC imaging factory to use.</param>
|
|||
/// <param name="fileName">The filename of the bitmap to load.</param>
|
|||
public BitmapImpl(ImagingFactory factory, string fileName) |
|||
{ |
|||
_factory = factory; |
|||
|
|||
using (BitmapDecoder decoder = new BitmapDecoder(factory, fileName, DecodeOptions.CacheOnDemand)) |
|||
{ |
|||
WicImpl = new Bitmap(factory, decoder.GetFrame(0), BitmapCreateCacheOption.CacheOnDemand); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="BitmapImpl"/> class.
|
|||
/// </summary>
|
|||
/// <param name="factory">The WIC imaging factory to use.</param>
|
|||
/// <param name="stream">The stream to read the bitmap from.</param>
|
|||
public BitmapImpl(ImagingFactory factory, Stream stream) |
|||
{ |
|||
_factory = factory; |
|||
|
|||
using (BitmapDecoder decoder = new BitmapDecoder(factory, stream, DecodeOptions.CacheOnLoad)) |
|||
{ |
|||
WicImpl = new Bitmap(factory, decoder.GetFrame(0), BitmapCreateCacheOption.CacheOnLoad); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="BitmapImpl"/> class.
|
|||
/// </summary>
|
|||
/// <param name="factory">The WIC imaging factory to use.</param>
|
|||
/// <param name="width">The width of the bitmap.</param>
|
|||
/// <param name="height">The height of the bitmap.</param>
|
|||
public BitmapImpl(ImagingFactory factory, int width, int height) |
|||
{ |
|||
_factory = factory; |
|||
WicImpl = new Bitmap( |
|||
factory, |
|||
width, |
|||
height, |
|||
PixelFormat.Format32bppPBGRA, |
|||
BitmapCreateCacheOption.CacheOnLoad); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the width of the bitmap, in pixels.
|
|||
/// </summary>
|
|||
public int PixelWidth => WicImpl.Size.Width; |
|||
|
|||
/// <summary>
|
|||
/// Gets the height of the bitmap, in pixels.
|
|||
/// </summary>
|
|||
public int PixelHeight => WicImpl.Size.Height; |
|||
public abstract Bitmap GetDirect2DBitmap(SharpDX.Direct2D1.RenderTarget target); |
|||
public abstract int PixelWidth { get; } |
|||
public abstract int PixelHeight { get; } |
|||
public abstract void Save(string fileName); |
|||
public abstract void Save(Stream stream); |
|||
|
|||
public virtual void Dispose() |
|||
{ |
|||
WicImpl.Dispose(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the WIC implementation of the bitmap.
|
|||
/// </summary>
|
|||
public Bitmap WicImpl |
|||
{ |
|||
get; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets a Direct2D bitmap to use on the specified render target.
|
|||
/// </summary>
|
|||
/// <param name="renderTarget">The render target.</param>
|
|||
/// <returns>The Direct2D bitmap.</returns>
|
|||
public SharpDX.Direct2D1.Bitmap GetDirect2DBitmap(SharpDX.Direct2D1.RenderTarget renderTarget) |
|||
{ |
|||
FormatConverter converter = new FormatConverter(_factory); |
|||
converter.Initialize(WicImpl, PixelFormat.Format32bppPBGRA); |
|||
return SharpDX.Direct2D1.Bitmap.FromWicBitmap(renderTarget, converter); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Saves the bitmap to a file.
|
|||
/// </summary>
|
|||
/// <param name="fileName">The filename.</param>
|
|||
public void Save(string fileName) |
|||
{ |
|||
if (Path.GetExtension(fileName) != ".png") |
|||
{ |
|||
// Yeah, we need to support other formats.
|
|||
throw new NotSupportedException("Use PNG, stoopid."); |
|||
} |
|||
|
|||
using (FileStream s = new FileStream(fileName, FileMode.Create)) |
|||
{ |
|||
Save(s); |
|||
} |
|||
} |
|||
|
|||
public void Save(Stream stream) |
|||
{ |
|||
PngBitmapEncoder encoder = new PngBitmapEncoder(_factory); |
|||
encoder.Initialize(stream); |
|||
|
|||
BitmapFrameEncode frame = new BitmapFrameEncode(encoder); |
|||
frame.Initialize(); |
|||
frame.WriteSource(WicImpl); |
|||
frame.Commit(); |
|||
encoder.Commit(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,57 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Platform; |
|||
using SharpDX.Direct2D1; |
|||
|
|||
namespace Avalonia.Direct2D1.Media |
|||
{ |
|||
/// <summary>
|
|||
/// A Direct2D Bitmap implementation that uses a GPU memory bitmap as its image.
|
|||
/// </summary>
|
|||
public class D2DBitmapImpl : BitmapImpl |
|||
{ |
|||
private Bitmap _direct2D; |
|||
|
|||
/// <summary>
|
|||
/// Initialize a new instance of the <see cref="BitmapImpl"/> class
|
|||
/// with a bitmap backed by GPU memory.
|
|||
/// </summary>
|
|||
/// <param name="d2DBitmap">The GPU bitmap.</param>
|
|||
/// <remarks>
|
|||
/// This bitmap must be either from the same render target,
|
|||
/// or if the render target is a <see cref="SharpDX.Direct2D1.DeviceContext"/>,
|
|||
/// the device associated with this context, to be renderable.
|
|||
/// </remarks>
|
|||
public D2DBitmapImpl(Bitmap d2DBitmap) |
|||
{ |
|||
if (d2DBitmap == null) throw new ArgumentNullException(nameof(d2DBitmap)); |
|||
|
|||
_direct2D = d2DBitmap; |
|||
} |
|||
|
|||
public override Bitmap GetDirect2DBitmap(SharpDX.Direct2D1.RenderTarget target) => _direct2D; |
|||
|
|||
public override int PixelWidth => _direct2D.PixelSize.Width; |
|||
public override int PixelHeight => _direct2D.PixelSize.Height; |
|||
|
|||
public override void Save(string fileName) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public override void Save(Stream stream) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public override void Dispose() |
|||
{ |
|||
base.Dispose(); |
|||
_direct2D.Dispose(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,130 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
using System.IO; |
|||
using Avalonia.Platform; |
|||
using SharpDX.WIC; |
|||
|
|||
namespace Avalonia.Direct2D1.Media |
|||
{ |
|||
/// <summary>
|
|||
/// A WIC implementation of a <see cref="Avalonia.Media.Imaging.Bitmap"/>.
|
|||
/// </summary>
|
|||
public class WicBitmapImpl : BitmapImpl |
|||
{ |
|||
private readonly ImagingFactory _factory; |
|||
|
|||
private SharpDX.Direct2D1.Bitmap _direct2D; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="WicBitmapImpl"/> class.
|
|||
/// </summary>
|
|||
/// <param name="factory">The WIC imaging factory to use.</param>
|
|||
/// <param name="fileName">The filename of the bitmap to load.</param>
|
|||
public WicBitmapImpl(ImagingFactory factory, string fileName) |
|||
{ |
|||
_factory = factory; |
|||
|
|||
using (BitmapDecoder decoder = new BitmapDecoder(factory, fileName, DecodeOptions.CacheOnDemand)) |
|||
{ |
|||
WicImpl = new Bitmap(factory, decoder.GetFrame(0), BitmapCreateCacheOption.CacheOnDemand); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="WicBitmapImpl"/> class.
|
|||
/// </summary>
|
|||
/// <param name="factory">The WIC imaging factory to use.</param>
|
|||
/// <param name="stream">The stream to read the bitmap from.</param>
|
|||
public WicBitmapImpl(ImagingFactory factory, Stream stream) |
|||
{ |
|||
_factory = factory; |
|||
|
|||
using (BitmapDecoder decoder = new BitmapDecoder(factory, stream, DecodeOptions.CacheOnLoad)) |
|||
{ |
|||
WicImpl = new Bitmap(factory, decoder.GetFrame(0), BitmapCreateCacheOption.CacheOnLoad); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="WicBitmapImpl"/> class.
|
|||
/// </summary>
|
|||
/// <param name="factory">The WIC imaging factory to use.</param>
|
|||
/// <param name="width">The width of the bitmap.</param>
|
|||
/// <param name="height">The height of the bitmap.</param>
|
|||
public WicBitmapImpl(ImagingFactory factory, int width, int height) |
|||
{ |
|||
_factory = factory; |
|||
WicImpl = new Bitmap( |
|||
factory, |
|||
width, |
|||
height, |
|||
PixelFormat.Format32bppPBGRA, |
|||
BitmapCreateCacheOption.CacheOnLoad); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the width of the bitmap, in pixels.
|
|||
/// </summary>
|
|||
public override int PixelWidth => WicImpl.Size.Width; |
|||
|
|||
/// <summary>
|
|||
/// Gets the height of the bitmap, in pixels.
|
|||
/// </summary>
|
|||
public override int PixelHeight => WicImpl.Size.Height; |
|||
|
|||
public override void Dispose() |
|||
{ |
|||
WicImpl.Dispose(); |
|||
_direct2D?.Dispose(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the WIC implementation of the bitmap.
|
|||
/// </summary>
|
|||
public Bitmap WicImpl { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets a Direct2D bitmap to use on the specified render target.
|
|||
/// </summary>
|
|||
/// <param name="renderTarget">The render target.</param>
|
|||
/// <returns>The Direct2D bitmap.</returns>
|
|||
public override SharpDX.Direct2D1.Bitmap GetDirect2DBitmap(SharpDX.Direct2D1.RenderTarget renderTarget) |
|||
{ |
|||
FormatConverter converter = new FormatConverter(_factory); |
|||
converter.Initialize(WicImpl, PixelFormat.Format32bppPBGRA); |
|||
return SharpDX.Direct2D1.Bitmap.FromWicBitmap(renderTarget, converter); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Saves the bitmap to a file.
|
|||
/// </summary>
|
|||
/// <param name="fileName">The filename.</param>
|
|||
public override void Save(string fileName) |
|||
{ |
|||
if (Path.GetExtension(fileName) != ".png") |
|||
{ |
|||
// Yeah, we need to support other formats.
|
|||
throw new NotSupportedException("Use PNG, stoopid."); |
|||
} |
|||
|
|||
using (FileStream s = new FileStream(fileName, FileMode.Create)) |
|||
{ |
|||
Save(s); |
|||
} |
|||
} |
|||
|
|||
public override void Save(Stream stream) |
|||
{ |
|||
PngBitmapEncoder encoder = new PngBitmapEncoder(_factory); |
|||
encoder.Initialize(stream); |
|||
|
|||
BitmapFrameEncode frame = new BitmapFrameEncode(encoder); |
|||
frame.Initialize(); |
|||
frame.WriteSource(WicImpl); |
|||
frame.Commit(); |
|||
encoder.Commit(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,136 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Direct2D1.Media; |
|||
using Avalonia.Media; |
|||
using Avalonia.Platform; |
|||
using Avalonia.Win32.Interop; |
|||
using SharpDX; |
|||
using SharpDX.Direct2D1; |
|||
using SharpDX.DXGI; |
|||
using AlphaMode = SharpDX.Direct2D1.AlphaMode; |
|||
using Device = SharpDX.Direct2D1.Device; |
|||
using Factory = SharpDX.Direct2D1.Factory; |
|||
using Factory2 = SharpDX.DXGI.Factory2; |
|||
|
|||
namespace Avalonia.Direct2D1 |
|||
{ |
|||
public abstract class SwapChainRenderTarget : IRenderTarget |
|||
{ |
|||
private Size2 _savedSize; |
|||
private Size2F _savedDpi; |
|||
private DeviceContext _deviceContext; |
|||
private SwapChain1 _swapChain; |
|||
|
|||
protected SwapChainRenderTarget() |
|||
{ |
|||
DxgiDevice = AvaloniaLocator.Current.GetService<SharpDX.DXGI.Device>(); |
|||
D2DDevice = AvaloniaLocator.Current.GetService<Device>(); |
|||
Direct2DFactory = AvaloniaLocator.Current.GetService<Factory>(); |
|||
DirectWriteFactory = AvaloniaLocator.Current.GetService<SharpDX.DirectWrite.Factory>(); |
|||
} |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Gets the Direct2D factory.
|
|||
/// </summary>
|
|||
public Factory Direct2DFactory |
|||
{ |
|||
get; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the DirectWrite factory.
|
|||
/// </summary>
|
|||
public SharpDX.DirectWrite.Factory DirectWriteFactory |
|||
{ |
|||
get; |
|||
} |
|||
|
|||
protected SharpDX.DXGI.Device DxgiDevice { get; } |
|||
|
|||
public Device D2DDevice { get; } |
|||
|
|||
/// <summary>
|
|||
/// Creates a drawing context for a rendering session.
|
|||
/// </summary>
|
|||
/// <returns>An <see cref="Avalonia.Media.DrawingContext"/>.</returns>
|
|||
public IDrawingContextImpl CreateDrawingContext() |
|||
{ |
|||
var size = GetWindowSize(); |
|||
var dpi = GetWindowDpi(); |
|||
|
|||
if (size != _savedSize || dpi != _savedDpi) |
|||
{ |
|||
_savedSize = size; |
|||
_savedDpi = dpi; |
|||
CreateSwapChain(); |
|||
} |
|||
|
|||
return new DrawingContextImpl(_deviceContext, DirectWriteFactory, _swapChain); |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
_deviceContext?.Dispose(); |
|||
_swapChain?.Dispose(); |
|||
} |
|||
|
|||
private void CreateSwapChain() |
|||
{ |
|||
using (var dxgiAdaptor = DxgiDevice.Adapter) |
|||
using (var dxgiFactory = dxgiAdaptor.GetParent<Factory2>()) |
|||
{ |
|||
_deviceContext?.Dispose(); |
|||
_deviceContext = new DeviceContext(D2DDevice, DeviceContextOptions.None) {DotsPerInch = _savedDpi}; |
|||
|
|||
|
|||
var swapChainDesc = new SwapChainDescription1 |
|||
{ |
|||
Width = _savedSize.Width, |
|||
Height = _savedSize.Height, |
|||
Format = Format.B8G8R8A8_UNorm, |
|||
Stereo = false, |
|||
SampleDescription = new SampleDescription |
|||
{ |
|||
Count = 1, |
|||
Quality = 0, |
|||
}, |
|||
Usage = Usage.RenderTargetOutput, |
|||
BufferCount = 2, |
|||
Scaling = Scaling.None, |
|||
SwapEffect = SwapEffect.FlipSequential, |
|||
Flags = 0, |
|||
}; |
|||
|
|||
_swapChain?.Dispose(); |
|||
_swapChain = CreateSwapChain(dxgiFactory, swapChainDesc); |
|||
|
|||
using (var dxgiBackBuffer = _swapChain.GetBackBuffer<Surface>(0)) |
|||
using (var d2dBackBuffer = new Bitmap1( |
|||
_deviceContext, |
|||
dxgiBackBuffer, |
|||
new BitmapProperties1( |
|||
new PixelFormat |
|||
{ |
|||
AlphaMode = AlphaMode.Ignore, |
|||
Format = Format.B8G8R8A8_UNorm |
|||
}, |
|||
_savedDpi.Width, |
|||
_savedDpi.Height, |
|||
BitmapOptions.Target | BitmapOptions.CannotDraw))) |
|||
{ |
|||
_deviceContext.Target = d2dBackBuffer; |
|||
} |
|||
} |
|||
} |
|||
|
|||
protected abstract SwapChain1 CreateSwapChain(Factory2 dxgiFactory, SwapChainDescription1 swapChainDesc); |
|||
|
|||
protected abstract Size2F GetWindowDpi(); |
|||
|
|||
protected abstract Size2 GetWindowSize(); |
|||
} |
|||
} |
|||
@ -0,0 +1,63 @@ |
|||
// Copyright (c) The Avalonia Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
using Moq; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Data; |
|||
using Avalonia.Markup.Xaml.Data; |
|||
using Avalonia.Styling; |
|||
using Xunit; |
|||
using System.Reactive.Disposables; |
|||
|
|||
namespace Avalonia.Markup.Xaml.UnitTests.Data |
|||
{ |
|||
public class BindingTests_Self |
|||
{ |
|||
[Fact] |
|||
public void Binding_To_Property_On_Self_Should_Work() |
|||
{ |
|||
var target = new TextBlock |
|||
{ |
|||
Tag = "Hello World!", |
|||
[!TextBlock.TextProperty] = new Binding("Tag") |
|||
{ |
|||
RelativeSource = new RelativeSource(RelativeSourceMode.Self) |
|||
}, |
|||
}; |
|||
|
|||
Assert.Equal("Hello World!", target.Text); |
|||
} |
|||
|
|||
[Fact] |
|||
public void TwoWay_Binding_To_Property_On_Self_Should_Work() |
|||
{ |
|||
var target = new TextBlock |
|||
{ |
|||
Tag = "Hello World!", |
|||
[!TextBlock.TextProperty] = new Binding("Tag", BindingMode.TwoWay) |
|||
{ |
|||
RelativeSource = new RelativeSource(RelativeSourceMode.Self) |
|||
}, |
|||
}; |
|||
|
|||
Assert.Equal("Hello World!", target.Text); |
|||
target.Text = "Goodbye cruel world :("; |
|||
Assert.Equal("Goodbye cruel world :(", target.Text); |
|||
} |
|||
|
|||
private Mock<IControl> CreateTarget( |
|||
ITemplatedControl templatedParent = null, |
|||
string text = null) |
|||
{ |
|||
var result = new Mock<IControl>(); |
|||
|
|||
result.Setup(x => x.GetValue(Control.TemplatedParentProperty)).Returns(templatedParent); |
|||
result.Setup(x => x.GetValue((AvaloniaProperty)Control.TemplatedParentProperty)).Returns(templatedParent); |
|||
result.Setup(x => x.GetValue((AvaloniaProperty)TextBox.TextProperty)).Returns(text); |
|||
result.Setup(x => x.Bind(It.IsAny<AvaloniaProperty>(), It.IsAny<IObservable<object>>(), It.IsAny<BindingPriority>())) |
|||
.Returns(Disposable.Empty); |
|||
return result; |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue