Browse Source

Added WritableBitmap DrawingContext support.

feature/writablebitmap-drawingcontext
Steven Kirk 5 years ago
parent
commit
9a9be96e45
  1. 3
      src/Avalonia.Visuals/ApiCompatBaseline.txt
  2. 8
      src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs
  3. 5
      src/Avalonia.Visuals/Platform/IWriteableBitmapImpl.cs
  4. 24
      src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs
  5. 23
      src/Windows/Avalonia.Direct2D1/Media/Imaging/WriteableWicBitmapImpl.cs
  6. 81
      tests/Avalonia.RenderTests/Media/WriteableBitmapTests.cs
  7. BIN
      tests/TestFiles/Direct2D1/Media/WriteableBitmap/WriteableBitmap_DrawingContext.expected.png
  8. BIN
      tests/TestFiles/Direct2D1/Media/WriteableBitmap/WriteableBitmap_DrawingContext_Two_Draws.expected.png
  9. BIN
      tests/TestFiles/Skia/Media/WriteableBitmap/WriteableBitmap_DrawingContext.expected.png
  10. BIN
      tests/TestFiles/Skia/Media/WriteableBitmap/WriteableBitmap_DrawingContext_Two_Draws.expected.png

3
src/Avalonia.Visuals/ApiCompatBaseline.txt

@ -63,4 +63,5 @@ InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalon
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IGlyphRunImpl Avalonia.Platform.IPlatformRenderInterface.CreateGlyphRun(Avalonia.Media.GlyphRun)' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IGlyphRunImpl Avalonia.Platform.IPlatformRenderInterface.CreateGlyphRun(Avalonia.Media.GlyphRun, System.Double)' is present in the contract but not in the implementation.
MembersMustExist : Member 'public Avalonia.Platform.IGlyphRunImpl Avalonia.Platform.IPlatformRenderInterface.CreateGlyphRun(Avalonia.Media.GlyphRun, System.Double)' does not exist in the implementation but it does exist in the contract.
Total Issues: 64
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.IDrawingContextImpl Avalonia.Platform.IWriteableBitmapImpl.CreateDrawingContext(Avalonia.Rendering.IVisualBrushRenderer)' is present in the implementation but not in the contract.
Total Issues: 65

8
src/Avalonia.Visuals/Media/Imaging/WriteableBitmap.cs

@ -36,6 +36,14 @@ namespace Avalonia.Media.Imaging
public ILockedFramebuffer Lock() => ((IWriteableBitmapImpl) PlatformImpl.Item).Lock();
/// <summary>
/// Creates a <see cref="DrawingContext"/> to render into the <see cref="WriteableBitmap"/>.
/// </summary>
public DrawingContext CreateDrawingContext()
{
return new DrawingContext(((IWriteableBitmapImpl)PlatformImpl.Item).CreateDrawingContext(null));
}
private static IBitmapImpl CreatePlatformImpl(PixelSize size, in Vector dpi, PixelFormat? format, AlphaFormat? alphaFormat)
{
var ri = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>();

5
src/Avalonia.Visuals/Platform/IWriteableBitmapImpl.cs

@ -1,10 +1,13 @@
namespace Avalonia.Platform
using Avalonia.Rendering;
namespace Avalonia.Platform
{
/// <summary>
/// Defines the platform-specific interface for a <see cref="Avalonia.Media.Imaging.WriteableBitmap"/>.
/// </summary>
public interface IWriteableBitmapImpl : IBitmapImpl
{
IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer);
ILockedFramebuffer Lock();
}
}

24
src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs

@ -1,7 +1,9 @@
using System;
using System.IO;
using System.Reactive.Disposables;
using System.Threading;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Skia.Helpers;
using SkiaSharp;
@ -88,6 +90,28 @@ namespace Avalonia.Skia
}
}
public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer)
{
Monitor.Enter(_lock);
var pixmap = _bitmap.PeekPixels();
var surface = SKSurface.Create(pixmap);
var createInfo = new DrawingContextImpl.CreateInfo
{
Canvas = surface.Canvas,
Dpi = Dpi,
VisualBrushRenderer = visualBrushRenderer
};
return new DrawingContextImpl(createInfo, Disposable.Create(() =>
{
_bitmap.NotifyPixelsChanged();
surface.Dispose();
pixmap.Dispose();
Monitor.Exit(_lock);
}));
}
/// <inheritdoc />
public ILockedFramebuffer Lock() => new BitmapFramebuffer(this, _bitmap);

23
src/Windows/Avalonia.Direct2D1/Media/Imaging/WriteableWicBitmapImpl.cs

@ -1,5 +1,7 @@
using System;
using Avalonia.Platform;
using Avalonia.Rendering;
using SharpDX.Direct2D1;
using SharpDX.WIC;
using PixelFormat = Avalonia.Platform.PixelFormat;
@ -12,6 +14,27 @@ namespace Avalonia.Direct2D1.Media.Imaging
{
}
public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer)
{
if (WicImpl.PixelFormat != SharpDX.WIC.PixelFormat.Format32bppPBGRA &&
WicImpl.PixelFormat != SharpDX.WIC.PixelFormat.Format32bppPRGBA)
throw new NotSupportedException("Direct2D only supports drawing to bitmaps with premultiplied alpha.");
var renderTarget = new WicRenderTarget(
Direct2D1Platform.Direct2D1Factory,
WicImpl,
new RenderTargetProperties
{
DpiX = (float)Dpi.X,
DpiY = (float)Dpi.Y,
});
return new DrawingContextImpl(visualBrushRenderer, null, renderTarget, finishedCallback: () =>
{
Version++;
});
}
class LockedBitmap : ILockedFramebuffer
{
private readonly WriteableWicBitmapImpl _parent;

81
tests/Avalonia.RenderTests/Media/WriteableBitmapTests.cs

@ -0,0 +1,81 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
using Avalonia.Controls;
using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Controls.Shapes;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
using Xunit;
#if AVALONIA_SKIA
namespace Avalonia.Skia.RenderTests
#else
namespace Avalonia.Direct2D1.RenderTests.Media
#endif
{
public class WriteableBitmapTests : TestBase
{
public WriteableBitmapTests()
: base(@"Media\WriteableBitmap")
{
Directory.CreateDirectory(OutputPath);
}
[Fact]
public void WriteableBitmap_DrawingContext()
{
using var target = new WriteableBitmap(
new PixelSize(100, 100),
new Vector(96, 96),
PixelFormat.Bgra8888,
AlphaFormat.Premul);
using (var context = target.CreateDrawingContext())
{
var geometry = new PolylineGeometry(
new[] { new Point(5, 0), new Point(8, 8), new Point(0, 3), new Point(10, 3), new Point(2, 8) },
true);
context.FillRectangle(Brushes.White, new Rect(0, 0, 100, 100));
context.PushPostTransform(Matrix.CreateScale(10, 12));
context.DrawGeometry(Brushes.Violet, null, geometry);
}
var testName = nameof(WriteableBitmap_DrawingContext);
target.Save(System.IO.Path.Combine(OutputPath, testName + ".out.png"));
CompareImagesNoRenderer(testName);
}
[Fact]
public void WriteableBitmap_DrawingContext_Two_Draws()
{
using var target = new WriteableBitmap(
new PixelSize(100, 100),
new Vector(96, 96),
PixelFormat.Bgra8888,
AlphaFormat.Premul);
using (var context = target.CreateDrawingContext())
{
var geometry = new PolylineGeometry(
new[] { new Point(5, 0), new Point(8, 8), new Point(0, 3), new Point(10, 3), new Point(2, 8) },
true);
context.FillRectangle(Brushes.White, new Rect(0, 0, 100, 100));
context.PushPostTransform(Matrix.CreateScale(10, 12));
context.DrawGeometry(Brushes.Violet, null, geometry);
}
using (var context = target.CreateDrawingContext())
{
var geometry = new EllipseGeometry(new Rect(40, 40, 20, 20));
context.DrawGeometry(Brushes.Red, null, geometry);
}
var testName = nameof(WriteableBitmap_DrawingContext_Two_Draws);
target.Save(System.IO.Path.Combine(OutputPath, testName + ".out.png"));
CompareImagesNoRenderer(testName);
}
}
}

BIN
tests/TestFiles/Direct2D1/Media/WriteableBitmap/WriteableBitmap_DrawingContext.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
tests/TestFiles/Direct2D1/Media/WriteableBitmap/WriteableBitmap_DrawingContext_Two_Draws.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
tests/TestFiles/Skia/Media/WriteableBitmap/WriteableBitmap_DrawingContext.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
tests/TestFiles/Skia/Media/WriteableBitmap/WriteableBitmap_DrawingContext_Two_Draws.expected.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Loading…
Cancel
Save