Browse Source

Merge pull request #861 from kekekeks/multi-render-target

Added support for multiple drawing methods for window implementations and "framebuffer"
pull/867/head
Steven Kirk 9 years ago
committed by GitHub
parent
commit
0a89f6c36e
  1. 5
      src/Android/Avalonia.Android/Platform/SkiaPlatform/WindowImpl.cs
  2. 3
      src/Avalonia.Controls/Avalonia.Controls.csproj
  3. 13
      src/Avalonia.Controls/Platform/ITopLevelImpl.cs
  4. 19
      src/Avalonia.Controls/Platform/Surfaces/IFramebufferPlatformSurface.cs
  5. 37
      src/Avalonia.Controls/Platform/Surfaces/ILockedFramebuffer.cs
  6. 15
      src/Avalonia.Controls/Platform/Surfaces/PixelFormat.cs
  7. 2
      src/Avalonia.Controls/TopLevel.cs
  8. 7
      src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs
  9. 22
      src/Gtk/Avalonia.Cairo/CairoPlatform.cs
  10. 19
      src/Gtk/Avalonia.Cairo/RenderTarget.cs
  11. 2
      src/Gtk/Avalonia.Gtk/Avalonia.Gtk.csproj
  12. 39
      src/Gtk/Avalonia.Gtk/FramebufferManager.cs
  13. 58
      src/Gtk/Avalonia.Gtk/PixbufFramebuffer.cs
  14. 2
      src/Gtk/Avalonia.Gtk/WindowImpl.cs
  15. 26
      src/Gtk/Avalonia.Gtk/WindowImplBase.cs
  16. 26
      src/Skia/Avalonia.Skia.Android/AndroidPlatformRenderInterface.cs
  17. 1
      src/Skia/Avalonia.Skia.Android/Avalonia.Skia.Android.csproj
  18. 16
      src/Skia/Avalonia.Skia.Android/RenderTarget.cs
  19. 2
      src/Skia/Avalonia.Skia.Android/SkiaRenderView.cs
  20. 5
      src/Skia/Avalonia.Skia.Desktop/Avalonia.Skia.Desktop.csproj
  21. 21
      src/Skia/Avalonia.Skia.Desktop/PlatformRenderInterfaceDesktop.cs
  22. 1
      src/Skia/Avalonia.Skia.iOS/Avalonia.Skia.iOS.csproj
  23. 18
      src/Skia/Avalonia.Skia.iOS/PlatformRenderingInterfaceIos.cs
  24. 5
      src/Skia/Avalonia.Skia.iOS/RenderTarget.cs
  25. 1
      src/Skia/Avalonia.Skia/Avalonia.Skia.projitems
  26. 77
      src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs
  27. 8
      src/Skia/Avalonia.Skia/PlatformRenderInterface.cs
  28. 2
      src/Skia/Avalonia.Skia/WindowDrawingContextImpl.cs
  29. 19
      src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
  30. 14
      src/Windows/Avalonia.Direct2D1/HwndRenderTarget.cs
  31. 2
      src/Windows/Avalonia.Win32/Avalonia.Win32.csproj
  32. 41
      src/Windows/Avalonia.Win32/FramebufferManager.cs
  33. 48
      src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
  34. 121
      src/Windows/Avalonia.Win32/WindowFramebuffer.cs
  35. 9
      src/Windows/Avalonia.Win32/WindowImpl.cs
  36. 3
      src/iOS/Avalonia.iOS/AvaloniaView.cs
  37. 2
      tests/Avalonia.Input.UnitTests/InputElement_HitTesting.cs
  38. 2
      tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs

5
src/Android/Avalonia.Android/Platform/SkiaPlatform/WindowImpl.cs

@ -9,6 +9,7 @@ using Avalonia.Input.Raw;
using Avalonia.Platform;
using Avalonia.Skia.Android;
using System;
using System.Collections.Generic;
using Avalonia.Controls;
namespace Avalonia.Android.Platform.SkiaPlatform
@ -95,6 +96,8 @@ namespace Avalonia.Android.Platform.SkiaPlatform
IPlatformHandle ITopLevelImpl.Handle => this;
public IEnumerable<object> Surfaces => new object[] { this };
public void Activate()
{
}
@ -188,5 +191,5 @@ namespace Avalonia.Android.Platform.SkiaPlatform
{
// No window icons for mobile platforms
}
}
}
}

3
src/Avalonia.Controls/Avalonia.Controls.csproj

@ -57,6 +57,9 @@
<Compile Include="HotkeyManager.cs" />
<Compile Include="IApplicationLifecycle.cs" />
<Compile Include="IScrollable.cs" />
<Compile Include="Platform\Surfaces\IFramebufferPlatformSurface.cs" />
<Compile Include="Platform\Surfaces\ILockedFramebuffer.cs" />
<Compile Include="Platform\Surfaces\PixelFormat.cs" />
<Compile Include="PointEventArgs.cs" />
<Compile Include="Embedding\EmbeddableControlRoot.cs" />
<Compile Include="Platform\IEmbeddableWindowImpl.cs" />

13
src/Avalonia.Controls/Platform/ITopLevelImpl.cs

@ -2,6 +2,7 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Collections.Generic;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Input.Raw;
@ -37,6 +38,18 @@ namespace Avalonia.Platform
/// </summary>
IPlatformHandle Handle { get; }
/// <summary>
/// The list of native platform's surfaces that can be consumed by rendering subsystems.
/// </summary>
/// <remarks>
/// Rendering platform will check that list and see if it can utilize one of them to output.
/// It should be enough to expose a native window handle via IPlatformHandle
/// and add support for framebuffer (even if it's emulated one) via IFramebufferPlatformSurface.
/// If you have some rendering platform that's tied to your particular windowing platform,
/// just expose some toolkit-specific object (e. g. Func&lt;Gdk.Drawable&gt; in case of GTK#+Cairo)
/// </remarks>
IEnumerable<object> Surfaces { get; }
/// <summary>
/// Gets or sets a method called when the window is activated (receives focus).
/// </summary>

19
src/Avalonia.Controls/Platform/Surfaces/IFramebufferPlatformSurface.cs

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Avalonia.Controls.Platform.Surfaces
{
public interface IFramebufferPlatformSurface
{
/// <summary>
/// Provides a framebuffer descriptor for drawing.
/// </summary>
/// <remarks>
/// Contents should be drawn on actual window after disposing
/// </remarks>
ILockedFramebuffer Lock();
}
}

37
src/Avalonia.Controls/Platform/Surfaces/ILockedFramebuffer.cs

@ -0,0 +1,37 @@
using System;
namespace Avalonia.Controls.Platform.Surfaces
{
public interface ILockedFramebuffer : IDisposable
{
/// <summary>
/// Address of the first pixel
/// </summary>
IntPtr Address { get; }
/// <summary>
/// Framebuffer width
/// </summary>
int Width { get; }
/// <summary>
/// Framebuffer height
/// </summary>
int Height { get; }
/// <summary>
/// Number of bytes per row
/// </summary>
int RowBytes { get; }
/// <summary>
/// DPI of underling screen
/// </summary>
Size Dpi { get; }
/// <summary>
/// Pixel format
/// </summary>
PixelFormat Format { get; }
}
}

15
src/Avalonia.Controls/Platform/Surfaces/PixelFormat.cs

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Avalonia.Controls.Platform.Surfaces
{
public enum PixelFormat
{
Rgb565,
Rgba8888,
Bgra8888
}
}

2
src/Avalonia.Controls/TopLevel.cs

@ -237,7 +237,7 @@ namespace Avalonia.Controls
/// <inheritdoc/>
IRenderTarget IRenderRoot.CreateRenderTarget()
{
return _renderInterface.CreateRenderTarget(PlatformImpl.Handle);
return _renderInterface.CreateRenderTarget(PlatformImpl.Surfaces);
}
/// <inheritdoc/>

7
src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs

@ -1,6 +1,7 @@
// 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.Collections.Generic;
using System.IO;
using Avalonia.Media;
@ -40,9 +41,11 @@ namespace Avalonia.Platform
/// <summary>
/// Creates a renderer.
/// </summary>
/// <param name="handle">The platform handle for the renderer.</param>
/// <param name="surfaces">
/// The list of native platform surfaces that can be used for output.
/// </param>
/// <returns>An <see cref="IRenderTarget"/>.</returns>
IRenderTarget CreateRenderTarget(IPlatformHandle handle);
IRenderTarget CreateRenderTarget(IEnumerable<object> surfaces);
/// <summary>
/// Creates a render target bitmap implementation.

22
src/Gtk/Avalonia.Cairo/CairoPlatform.cs

@ -2,6 +2,8 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Cairo.Media;
using Avalonia.Cairo.Media.Imaging;
using Avalonia.Media;
@ -50,24 +52,14 @@ namespace Avalonia.Cairo
return new FormattedTextImpl(s_pangoContext, text, fontFamily, fontSize, fontStyle, textAlignment, fontWeight);
}
public IRenderTarget CreateRenderTarget(IPlatformHandle handle)
public IRenderTarget CreateRenderTarget(IEnumerable<object> surfaces)
{
var window = handle as Gtk.Window;
if (window != null)
{
window.DoubleBuffered = true;
return new RenderTarget(window);
}
var area = handle as Gtk.DrawingArea;
if (area != null)
{
area.DoubleBuffered = true;
return new RenderTarget(area);
}
var accessor = surfaces?.OfType<Func<Gdk.Drawable>>().FirstOrDefault();
if(accessor!=null)
return new RenderTarget(accessor);
throw new NotSupportedException(string.Format(
"Don't know how to create a Cairo renderer from a '{0}' handle which isn't Gtk.Window or Gtk.DrawingArea",
handle.HandleDescriptor));
"Don't know how to create a Cairo renderer from any of the provided surfaces."));
}
public IRenderTargetBitmapImpl CreateRenderTargetBitmap(int width, int height)

19
src/Gtk/Avalonia.Cairo/RenderTarget.cs

@ -20,8 +20,8 @@ namespace Avalonia.Cairo
public class RenderTarget : IRenderTarget
{
private readonly Surface _surface;
private readonly Gtk.Window _window;
private readonly Gtk.DrawingArea _area;
private readonly Func<Gdk.Drawable> _drawableAccessor;
/// <summary>
/// Initializes a new instance of the <see cref="RenderTarget"/> class.
@ -29,9 +29,9 @@ namespace Avalonia.Cairo
/// <param name="window">The window.</param>
/// <param name="width">The width of the window.</param>
/// <param name="height">The height of the window.</param>
public RenderTarget(Gtk.Window window)
public RenderTarget(Func<Gdk.Drawable> drawable)
{
_window = window;
_drawableAccessor = drawable;
}
public RenderTarget(ImageSurface surface)
@ -39,11 +39,6 @@ namespace Avalonia.Cairo
_surface = surface;
}
public RenderTarget(DrawingArea area)
{
_area = area;
}
/// <summary>
/// Creates a cairo surface that targets a platform-specific resource.
/// </summary>
@ -52,12 +47,10 @@ namespace Avalonia.Cairo
public IDrawingContextImpl CreateMediaDrawingContext()
{
if (_window != null)
return new Media.DrawingContext(_window.GdkWindow);
if (_drawableAccessor != null)
return new Media.DrawingContext(_drawableAccessor());
if (_surface != null)
return new Media.DrawingContext(_surface);
if (_area != null)
return new Media.DrawingContext(_area.GdkWindow);
throw new InvalidOperationException("Unspecified render target");
}

2
src/Gtk/Avalonia.Gtk/Avalonia.Gtk.csproj

@ -48,7 +48,9 @@
<Compile Include="ClipboardImpl.cs" />
<Compile Include="EmbeddableImpl.cs" />
<Compile Include="Embedding\GtkAvaloniaControlHost.cs" />
<Compile Include="FramebufferManager.cs" />
<Compile Include="IconImpl.cs" />
<Compile Include="PixbufFramebuffer.cs" />
<Compile Include="SystemDialogImpl.cs" />
<Compile Include="CursorFactory.cs" />
<Compile Include="GtkExtensions.cs" />

39
src/Gtk/Avalonia.Gtk/FramebufferManager.cs

@ -0,0 +1,39 @@
using System;
using Avalonia.Controls.Platform.Surfaces;
namespace Avalonia.Gtk
{
class FramebufferManager : IFramebufferPlatformSurface, IDisposable
{
private readonly WindowImplBase _window;
private PixbufFramebuffer _fb;
public FramebufferManager(WindowImplBase window)
{
_window = window;
}
public void Dispose()
{
_fb?.Deallocate();
}
public ILockedFramebuffer Lock()
{
if(_window.CurrentDrawable == null)
throw new InvalidOperationException("Window is not in drawing state");
var drawable = _window.CurrentDrawable;
var width = (int) _window.ClientSize.Width;
var height = (int) _window.ClientSize.Height;
if (_fb == null || _fb.Width != width ||
_fb.Height != height)
{
_fb?.Dispose();
_fb = new PixbufFramebuffer(width, height);
}
_fb.SetDrawable(drawable);
return _fb;
}
}
}

58
src/Gtk/Avalonia.Gtk/PixbufFramebuffer.cs

@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Platform;
using Gdk;
namespace Avalonia.Gtk
{
class PixbufFramebuffer : ILockedFramebuffer
{
private Pixbuf _pixbuf;
private Drawable _drawable;
public PixbufFramebuffer(int width, int height)
{
_pixbuf = new Pixbuf(Gdk.Colorspace.Rgb, false, 8, width, height);
}
public void SetDrawable(Drawable drawable)
{
_drawable = drawable;
}
public void Deallocate()
{
_pixbuf.Dispose();
_pixbuf = null;
}
public void Dispose()
{
using (var gc = new Gdk.GC(_drawable))
_drawable.DrawPixbuf(gc, _pixbuf, 0, 0, 0, 0, Width, Height, RgbDither.None, 0, 0);
_drawable = null;
}
public IntPtr Address => _pixbuf.Pixels;
public int Width => _pixbuf.Width;
public int Height => _pixbuf.Height;
public int RowBytes => _pixbuf.Rowstride;
//TODO: Proper DPI detect
public Size Dpi => new Size(96, 96);
public PixelFormat Format
{
get
{
if (AvaloniaLocator.Current.GetService<IRuntimePlatform>().GetRuntimeInfo().OperatingSystem ==
OperatingSystemType.WinNT)
return PixelFormat.Bgra8888;
return PixelFormat.Rgba8888;
}
}
}
}

2
src/Gtk/Avalonia.Gtk/WindowImpl.cs

@ -10,7 +10,7 @@ namespace Avalonia.Gtk
{
private Gtk.Window _window;
private Gtk.Window Window => _window ?? (_window = (Gtk.Window) Widget);
public WindowImpl(Gtk.WindowType type) : base(new PlatformHandleAwareWindow(type))
{
Init();

26
src/Gtk/Avalonia.Gtk/WindowImplBase.cs

@ -2,14 +2,11 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Reactive.Disposables;
using System.Runtime.InteropServices;
using Gdk;
using Avalonia.Controls;
using System.Collections.Generic;
using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.Platform;
using Avalonia.Input;
using Avalonia.Threading;
using Gdk;
using Action = System.Action;
using WindowEdge = Avalonia.Controls.WindowEdge;
@ -21,8 +18,7 @@ namespace Avalonia.Gtk
{
private IInputRoot _inputRoot;
protected Gtk.Widget _window;
public Gtk.Widget Widget => _window;
private FramebufferManager _framebuffer;
private Gtk.IMContext _imContext;
@ -33,6 +29,7 @@ namespace Avalonia.Gtk
protected WindowImplBase(Gtk.Widget window)
{
_window = window;
_framebuffer = new FramebufferManager(this);
Init();
}
@ -43,7 +40,6 @@ namespace Avalonia.Gtk
_imContext = new Gtk.IMMulticontext();
_imContext.Commit += ImContext_Commit;
_window.Realized += OnRealized;
_window.DoubleBuffered = false;
_window.Realize();
_window.ButtonPressEvent += OnButtonPressEvent;
_window.ButtonReleaseEvent += OnButtonReleaseEvent;
@ -57,6 +53,8 @@ namespace Avalonia.Gtk
}
public IPlatformHandle Handle { get; private set; }
public Gtk.Widget Widget => _window;
public Gdk.Drawable CurrentDrawable { get; private set; }
void OnRealized (object sender, EventArgs eventArgs)
{
@ -127,6 +125,13 @@ namespace Avalonia.Gtk
public Action<double> ScalingChanged { get; set; }
public IEnumerable<object> Surfaces => new object[]
{
Handle,
new Func<Gdk.Drawable>(() => CurrentDrawable),
_framebuffer
};
public IPopupImpl CreatePopup()
{
return new PopupImpl();
@ -283,7 +288,9 @@ namespace Avalonia.Gtk
void OnExposeEvent(object o, Gtk.ExposeEventArgs args)
{
CurrentDrawable = args.Event.Window;
Paint(args.Event.Area.ToAvalonia());
CurrentDrawable = null;
args.RetVal = true;
}
@ -306,6 +313,7 @@ namespace Avalonia.Gtk
public void Dispose()
{
_framebuffer.Dispose();
_window.Hide();
_window.Dispose();
_window = null;

26
src/Skia/Avalonia.Skia.Android/AndroidPlatformRenderInterface.cs

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Avalonia.Platform;
namespace Avalonia.Skia
{
partial class PlatformRenderInterface
{
public IRenderTarget CreateRenderTarget(IEnumerable<object> surfaces)
{
var surfaceView = surfaces?.OfType<SurfaceView>().FirstOrDefault();
if (surfaceView == null)
throw new ArgumentException("Avalonia.Skia.Android is only capable of drawing on SurfaceView");
return new WindowRenderTarget(surfaceView);
}
}
}

1
src/Skia/Avalonia.Skia.Android/Avalonia.Skia.Android.csproj

@ -87,6 +87,7 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Compile Include="AndroidPlatformRenderInterface.cs" />
<Compile Include="AndroidRenderTarget.cs" />
<Compile Include="RenderTarget.cs" />
<Compile Include="SkiaRenderView.cs" />

16
src/Skia/Avalonia.Skia.Android/RenderTarget.cs

@ -26,14 +26,14 @@ namespace Avalonia.Skia
internal class WindowRenderTarget : RenderTarget
{
private readonly IPlatformHandle _hwnd;
private readonly SurfaceView _surfaceView;
Bitmap _bitmap;
int Width { get; set; }
int Height { get; set; }
public WindowRenderTarget(IPlatformHandle hwnd)
public WindowRenderTarget(SurfaceView surfaceView)
{
_hwnd = hwnd;
_surfaceView = surfaceView;
FixSize();
}
@ -63,9 +63,8 @@ namespace Avalonia.Skia
private void GetPlatformWindowSize(out int w, out int h)
{
var surfaceView = _hwnd as SurfaceView;
w = surfaceView.Width;
h = surfaceView.Height;
w = _surfaceView.Width;
h = _surfaceView.Height;
}
public override DrawingContext CreateDrawingContext()
@ -85,11 +84,10 @@ namespace Avalonia.Skia
public void Present()
{
var surfaceView = _hwnd as SurfaceView;
Canvas canvas = null;
try
{
canvas = surfaceView.Holder.LockCanvas(null);
canvas = _surfaceView.Holder.LockCanvas(null);
_bitmap.UnlockPixels();
canvas.DrawBitmap(_bitmap, 0, 0, null);
}
@ -99,7 +97,7 @@ namespace Avalonia.Skia
finally
{
if (canvas != null)
surfaceView.Holder.UnlockCanvasAndPost(canvas);
_surfaceView.Holder.UnlockCanvasAndPost(canvas);
}
_bitmap.UnlockPixels();

2
src/Skia/Avalonia.Skia.Android/SkiaRenderView.cs

@ -22,7 +22,7 @@ namespace Avalonia.Skia.Android
{
_renderTarget =
AvaloniaLocator.Current.GetService<IPlatformRenderInterface>()
.CreateRenderTarget(this);
.CreateRenderTarget(new object[]{this});
}
protected override void Draw()

5
src/Skia/Avalonia.Skia.Desktop/Avalonia.Skia.Desktop.csproj

@ -72,11 +72,8 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="PlatformRenderInterfaceDesktop.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="..\..\Windows\Avalonia.Win32\Interop\UnmanagedMethods.cs">
<Link>UnmanagedMethods.cs</Link>
</Compile>
<Compile Include="RenderTarget.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Avalonia.Animation\Avalonia.Animation.csproj">

21
src/Skia/Avalonia.Skia.Desktop/PlatformRenderInterfaceDesktop.cs

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Platform;
namespace Avalonia.Skia
{
partial class PlatformRenderInterface
{
public IRenderTarget CreateRenderTarget(IEnumerable<object> surfaces)
{
var fb = surfaces?.OfType<IFramebufferPlatformSurface>().FirstOrDefault();
if (fb == null)
throw new Exception("Avalonia.Skia.Deskop currently only supports framebuffer render target");
return new FramebufferRenderTarget(fb);
}
}
}

1
src/Skia/Avalonia.Skia.iOS/Avalonia.Skia.iOS.csproj

@ -37,6 +37,7 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Compile Include="PlatformRenderingInterfaceIos.cs" />
<Compile Include="RenderTarget.cs" />
<Compile Include="SkiaView.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />

18
src/Skia/Avalonia.Skia.iOS/PlatformRenderingInterfaceIos.cs

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Avalonia.Platform;
using Foundation;
using UIKit;
namespace Avalonia.Skia
{
partial class PlatformRenderInterface
{
public IRenderTarget CreateRenderTarget(IEnumerable<object> surfaces)
{
return new WindowRenderTarget();
}
}
}

5
src/Skia/Avalonia.Skia.iOS/RenderTarget.cs

@ -26,14 +26,13 @@ namespace Avalonia.Skia
internal class WindowRenderTarget : RenderTarget
{
private readonly IPlatformHandle _hwnd;
SKBitmap _bitmap;
int Width { get; set; }
int Height { get; set; }
public WindowRenderTarget(IPlatformHandle hwnd)
public WindowRenderTarget()
{
_hwnd = hwnd;
FixSize();
}

1
src/Skia/Avalonia.Skia/Avalonia.Skia.projitems

@ -12,6 +12,7 @@
<Compile Include="$(MSBuildThisFileDirectory)BitmapImpl.cs" />
<Compile Include="$(MSBuildThisFileDirectory)DrawingContextImpl.cs" />
<Compile Include="$(MSBuildThisFileDirectory)FormattedTextImpl.cs" />
<Compile Include="$(MSBuildThisFileDirectory)FramebufferRenderTarget.cs" />
<Compile Include="$(MSBuildThisFileDirectory)PlatformRenderInterface.cs" />
<Compile Include="$(MSBuildThisFileDirectory)SkiaPlatform.cs" />
<Compile Include="$(MSBuildThisFileDirectory)SkiaSharpExtensions.cs" />

77
src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs

@ -0,0 +1,77 @@
using System;
using System.Collections.Generic;
using System.Text;
using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Media;
using Avalonia.Platform;
using SkiaSharp;
namespace Avalonia.Skia
{
public class FramebufferRenderTarget : IRenderTarget
{
private readonly IFramebufferPlatformSurface _surface;
public FramebufferRenderTarget(IFramebufferPlatformSurface surface)
{
_surface = surface;
}
public void Dispose()
{
//Nothing to do here, since we don't own framebuffer
}
class FramebufferDrawingContextImpl : DrawingContextImpl
{
private readonly SKCanvas _canvas;
private readonly SKSurface _surface;
private readonly ILockedFramebuffer _framebuffer;
public FramebufferDrawingContextImpl(SKCanvas canvas, SKSurface surface, ILockedFramebuffer framebuffer) : base(canvas)
{
_canvas = canvas;
_surface = surface;
_framebuffer = framebuffer;
}
public override void Dispose()
{
_canvas.Dispose();
_surface.Dispose();
_framebuffer.Dispose();
base.Dispose();
}
}
SKColorType TranslatePixelFormat(PixelFormat fmt)
{
if(fmt == PixelFormat.Rgb565)
return SKColorType.Rgb565;
if(fmt == PixelFormat.Bgra8888)
return SKColorType.Bgra8888;
if (fmt == PixelFormat.Rgba8888)
return SKColorType.Rgba8888;
throw new ArgumentException("Unknown pixel format: " + fmt);
}
public DrawingContext CreateDrawingContext()
{
var fb = _surface.Lock();
SKImageInfo nfo = new SKImageInfo(fb.Width, fb.Height, TranslatePixelFormat(fb.Format),
SKAlphaType.Opaque);
var surface = SKSurface.Create(nfo, fb.Address, fb.RowBytes);
if (surface == null)
throw new Exception("Unable to create a surface for pixel format " + fb.Format);
var canvas = surface.Canvas;
canvas.RestoreToCount(0);
canvas.Save();
canvas.Clear(SKColors.Red);
canvas.ResetMatrix();
return new DrawingContext(new FramebufferDrawingContextImpl(canvas, surface, fb),
Matrix.CreateScale(fb.Dpi.Width / 96, fb.Dpi.Height / 96));
}
}
}

8
src/Skia/Avalonia.Skia/PlatformRenderInterface.cs

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using Avalonia.Media;
using Avalonia.Platform;
@ -7,7 +8,7 @@ using SkiaSharp;
namespace Avalonia.Skia
{
public class PlatformRenderInterface : IPlatformRenderInterface, IRendererFactory
public partial class PlatformRenderInterface : IPlatformRenderInterface, IRendererFactory
{
public IBitmapImpl CreateBitmap(int width, int height)
{
@ -63,10 +64,5 @@ namespace Avalonia.Skia
return new BitmapImpl(width, height);
}
public IRenderTarget CreateRenderTarget(IPlatformHandle handle)
{
return new WindowRenderTarget(handle);
}
}
}

2
src/Skia/Avalonia.Skia/WindowDrawingContextImpl.cs

@ -1,6 +1,7 @@
namespace Avalonia.Skia
{
#if !DESKTOP
// not sure we need this yet
internal class WindowDrawingContextImpl : DrawingContextImpl
{
@ -18,4 +19,5 @@ namespace Avalonia.Skia
_target.Present();
}
}
#endif
}

19
src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs

@ -2,11 +2,14 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Avalonia.Direct2D1.Media;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Controls;
using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Rendering;
namespace Avalonia
@ -99,18 +102,16 @@ namespace Avalonia.Direct2D1
return new Renderer(root, renderLoop);
}
public IRenderTarget CreateRenderTarget(IPlatformHandle handle)
public IRenderTarget CreateRenderTarget(IEnumerable<object> surfaces)
{
if (handle.HandleDescriptor == "HWND")
var nativeWindow = surfaces?.OfType<IPlatformHandle>().FirstOrDefault();
if (nativeWindow != null)
{
return new HwndRenderTarget(handle.Handle);
}
else
{
throw new NotSupportedException(string.Format(
"Don't know how to create a Direct2D1 renderer from a '{0}' handle",
handle.HandleDescriptor));
if(nativeWindow.HandleDescriptor != "HWND")
throw new NotSupportedException("Don't know how to create a Direct2D1 renderer from " + nativeWindow.HandleDescriptor);
return new HwndRenderTarget(nativeWindow);
}
throw new NotSupportedException("Don't know how to create a Direct2D1 renderer from any of provided surfaces");
}
public IRenderTargetBitmapImpl CreateRenderTargetBitmap(int width, int height)

14
src/Windows/Avalonia.Direct2D1/HwndRenderTarget.cs

@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Platform;
using Avalonia.Win32.Interop;
using SharpDX;
using SharpDX.DXGI;
@ -11,16 +13,16 @@ namespace Avalonia.Direct2D1
{
class HwndRenderTarget : SwapChainRenderTarget
{
private readonly IntPtr _hwnd;
private readonly IPlatformHandle _window;
public HwndRenderTarget(IntPtr hwnd)
public HwndRenderTarget(IPlatformHandle window)
{
_hwnd = hwnd;
_window = window;
}
protected override SwapChain1 CreateSwapChain(Factory2 dxgiFactory, SwapChainDescription1 swapChainDesc)
{
return new SwapChain1(dxgiFactory, DxgiDevice, _hwnd, ref swapChainDesc);
return new SwapChain1(dxgiFactory, DxgiDevice, _window.Handle, ref swapChainDesc);
}
protected override Size2F GetWindowDpi()
@ -30,7 +32,7 @@ namespace Avalonia.Direct2D1
uint dpix, dpiy;
var monitor = UnmanagedMethods.MonitorFromWindow(
_hwnd,
_window.Handle,
UnmanagedMethods.MONITOR.MONITOR_DEFAULTTONEAREST);
if (UnmanagedMethods.GetDpiForMonitor(
@ -49,7 +51,7 @@ namespace Avalonia.Direct2D1
protected override Size2 GetWindowSize()
{
UnmanagedMethods.RECT rc;
UnmanagedMethods.GetClientRect(_hwnd, out rc);
UnmanagedMethods.GetClientRect(_window.Handle, out rc);
return new Size2(rc.right - rc.left, rc.bottom - rc.top);
}
}

2
src/Windows/Avalonia.Win32/Avalonia.Win32.csproj

@ -69,6 +69,8 @@
<SubType>Component</SubType>
</Compile>
<Compile Include="Embedding\WpfAvaloniaControlHost.cs" />
<Compile Include="WindowFramebuffer.cs" />
<Compile Include="FramebufferManager.cs" />
<Compile Include="IconImpl.cs" />
<Compile Include="RenderLoop.cs" />
<Compile Include="SystemDialogImpl.cs" />

41
src/Windows/Avalonia.Win32/FramebufferManager.cs

@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Win32.Interop;
namespace Avalonia.Win32
{
class FramebufferManager : IFramebufferPlatformSurface, IDisposable
{
private readonly IntPtr _hwnd;
private WindowFramebuffer _fb;
public FramebufferManager(IntPtr hwnd)
{
_hwnd = hwnd;
}
public ILockedFramebuffer Lock()
{
UnmanagedMethods.RECT rc;
UnmanagedMethods.GetClientRect(_hwnd, out rc);
var width = rc.right - rc.left;
var height = rc.bottom - rc.top;
if (_fb == null || _fb.Width != width || _fb.Height != height)
{
_fb?.Deallocate();
_fb = null;
_fb = new WindowFramebuffer(_hwnd, width, height);
}
return _fb;
}
public void Dispose()
{
_fb?.Deallocate();
}
}
}

48
src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs

@ -568,6 +568,27 @@ namespace Avalonia.Win32.Interop
public byte rgbReserved;
}
[StructLayout(LayoutKind.Sequential)]
public struct BITMAPINFOHEADER
{
public uint biSize;
public int biWidth;
public int biHeight;
public ushort biPlanes;
public ushort biBitCount;
public uint biCompression;
public uint biSizeImage;
public int biXPelsPerMeter;
public int biYPelsPerMeter;
public uint biClrUsed;
public uint biClrImportant;
public void Init()
{
biSize = (uint)Marshal.SizeOf(this);
}
}
[StructLayout(LayoutKind.Sequential)]
public struct BITMAPINFO
{
@ -859,6 +880,31 @@ namespace Avalonia.Win32.Interop
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("gdi32.dll")]
public static extern int SetDIBitsToDevice(IntPtr hdc, int XDest, int YDest, uint
dwWidth, uint dwHeight, int XSrc, int YSrc, uint uStartScan, uint cScanLines,
IntPtr lpvBits, [In] ref BITMAPINFOHEADER lpbmi, uint fuColorUse);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(IntPtr hObject);
[DllImport("gdi32.dll", SetLastError = true)]
public static extern IntPtr CreateDIBSection(IntPtr hDC, ref BITMAPINFOHEADER pBitmapInfo, int un, out IntPtr lplpVoid, IntPtr handle, int dw);
[DllImport("gdi32.dll")]
public static extern int DeleteObject(IntPtr hObject);
[DllImport("gdi32.dll", SetLastError = true)]
public static extern IntPtr CreateCompatibleDC(IntPtr hdc);
[DllImport("gdi32.dll")]
public static extern IntPtr SelectObject(IntPtr hdc, IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr CreateFileMapping(IntPtr hFile,
IntPtr lpFileMappingAttributes,
uint flProtect,
uint dwMaximumSizeHigh,
uint dwMaximumSizeLow,
string lpName);
public enum MONITOR
{
MONITOR_DEFAULTTONULL = 0x00000000,
@ -1177,6 +1223,7 @@ namespace Avalonia.Win32.Interop
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
uint SetFilter([MarshalAs(UnmanagedType.Interface)] IntPtr pFilter);
}
@ -1197,5 +1244,6 @@ namespace Avalonia.Win32.Interop
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
uint Compare([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi, [In] uint hint, out int piOrder);
}
}

121
src/Windows/Avalonia.Win32/WindowFramebuffer.cs

@ -0,0 +1,121 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Interop;
using System.Windows.Media;
using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Win32.Interop;
using PixelFormat = Avalonia.Controls.Platform.Surfaces.PixelFormat;
namespace Avalonia.Win32
{
public class WindowFramebuffer : ILockedFramebuffer
{
private readonly IntPtr _handle;
private IntPtr _pBitmap;
private UnmanagedMethods.BITMAPINFOHEADER _bmpInfo;
public WindowFramebuffer(IntPtr handle, int width, int height)
{
if (width <= 0)
throw new ArgumentException("width is less than zero");
if (height <= 0)
throw new ArgumentException("height is less than zero");
_handle = handle;
_bmpInfo.Init();
_bmpInfo.biPlanes = 1;
_bmpInfo.biBitCount = 32;
_bmpInfo.Init();
_bmpInfo.biWidth = width;
_bmpInfo.biHeight = -height;
_pBitmap = Marshal.AllocHGlobal(width * height * 4);
}
~WindowFramebuffer()
{
Deallocate();
}
public IntPtr Address => _pBitmap;
public int RowBytes => Width * 4;
public PixelFormat Format => PixelFormat.Bgra8888;
public Size Dpi
{
get
{
if (UnmanagedMethods.ShCoreAvailable)
{
uint dpix, dpiy;
var monitor = UnmanagedMethods.MonitorFromWindow(_handle,
UnmanagedMethods.MONITOR.MONITOR_DEFAULTTONEAREST);
if (UnmanagedMethods.GetDpiForMonitor(
monitor,
UnmanagedMethods.MONITOR_DPI_TYPE.MDT_EFFECTIVE_DPI,
out dpix,
out dpiy) == 0)
{
return new Size(dpix, dpiy);
}
}
return new Size(96, 96);
}
}
public int Width => _bmpInfo.biWidth;
public int Height => -_bmpInfo.biHeight;
public void DrawToDevice(IntPtr hDC, int destX = 0, int destY = 0, int srcX = 0, int srcY = 0, int width = -1,
int height = -1)
{
if(_pBitmap == IntPtr.Zero)
throw new ObjectDisposedException("Framebuffer");
if (width == -1)
width = Width;
if (height == -1)
height = Height;
UnmanagedMethods.SetDIBitsToDevice(hDC, destX, destY, (uint) width, (uint) height, srcX, srcY,
0, (uint)Height, _pBitmap, ref _bmpInfo, 0);
}
public bool DrawToWindow(IntPtr hWnd, int destX = 0, int destY = 0, int srcX = 0, int srcY = 0, int width = -1,
int height = -1)
{
if (_pBitmap == IntPtr.Zero)
throw new ObjectDisposedException("Framebuffer");
if (hWnd == IntPtr.Zero)
return false;
IntPtr hDC = UnmanagedMethods.GetDC(hWnd);
if (hDC == IntPtr.Zero)
return false;
DrawToDevice(hDC, destX, destY, srcX, srcY, width, height);
UnmanagedMethods.ReleaseDC(hWnd, hDC);
return true;
}
public void Dispose()
{
//It's not an *actual* dispose. This call means "We are done drawing"
DrawToWindow(_handle);
}
public void Deallocate()
{
if (_pBitmap != IntPtr.Zero)
{
Marshal.FreeHGlobal(_pBitmap);
_pBitmap = IntPtr.Zero;
}
}
}
}

9
src/Windows/Avalonia.Win32/WindowImpl.cs

@ -11,6 +11,7 @@ using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Runtime.InteropServices;
using Avalonia.Controls;
using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Input.Raw;
using Avalonia.Platform;
using Avalonia.Win32.Input;
@ -35,10 +36,12 @@ namespace Avalonia.Win32
private bool _decorated = true;
private double _scaling = 1;
private WindowState _showWindowState;
private FramebufferManager _framebuffer;
public WindowImpl()
{
CreateWindow();
_framebuffer = new FramebufferManager(_hwnd);
s_instances.Add(this);
}
@ -161,6 +164,11 @@ namespace Avalonia.Win32
}
}
public IEnumerable<object> Surfaces => new object[]
{
Handle, _framebuffer
};
public void Activate()
{
UnmanagedMethods.SetActiveWindow(_hwnd);
@ -174,6 +182,7 @@ namespace Avalonia.Win32
public void Dispose()
{
s_instances.Remove(this);
_framebuffer.Dispose();
UnmanagedMethods.DestroyWindow(_hwnd);
}

3
src/iOS/Avalonia.iOS/AvaloniaView.cs

@ -149,6 +149,9 @@ namespace Avalonia.iOS
}
public Size MaxClientSize => Bounds.Size.ToAvalonia();
public IEnumerable<object> Surfaces => new object[] { this };
public void SetTitle(string title)
{
//Not supported

2
tests/Avalonia.Input.UnitTests/InputElement_HitTesting.cs

@ -340,7 +340,7 @@ namespace Avalonia.Input.UnitTests
throw new NotImplementedException();
}
public IRenderTarget CreateRenderTarget(IPlatformHandle handle)
public IRenderTarget CreateRenderTarget(IEnumerable<object> surfaces)
{
throw new NotImplementedException();
}

2
tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs

@ -20,7 +20,7 @@ namespace Avalonia.Visuals.UnitTests.VisualTree
throw new NotImplementedException();
}
public IRenderTarget CreateRenderTarget(IPlatformHandle handle)
public IRenderTarget CreateRenderTarget(IEnumerable<object> surfaces)
{
throw new NotImplementedException();
}

Loading…
Cancel
Save