瀏覽代碼

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

Added support for multiple drawing methods for window implementations and "framebuffer"
Steven Kirk 8 年之前
父節點
當前提交
0a89f6c36e
共有 38 個文件被更改,包括 631 次插入82 次删除
  1. 4 1
      src/Android/Avalonia.Android/Platform/SkiaPlatform/WindowImpl.cs
  2. 3 0
      src/Avalonia.Controls/Avalonia.Controls.csproj
  3. 13 0
      src/Avalonia.Controls/Platform/ITopLevelImpl.cs
  4. 19 0
      src/Avalonia.Controls/Platform/Surfaces/IFramebufferPlatformSurface.cs
  5. 37 0
      src/Avalonia.Controls/Platform/Surfaces/ILockedFramebuffer.cs
  6. 15 0
      src/Avalonia.Controls/Platform/Surfaces/PixelFormat.cs
  7. 1 1
      src/Avalonia.Controls/TopLevel.cs
  8. 5 2
      src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs
  9. 7 15
      src/Gtk/Avalonia.Cairo/CairoPlatform.cs
  10. 6 13
      src/Gtk/Avalonia.Cairo/RenderTarget.cs
  11. 2 0
      src/Gtk/Avalonia.Gtk/Avalonia.Gtk.csproj
  12. 39 0
      src/Gtk/Avalonia.Gtk/FramebufferManager.cs
  13. 58 0
      src/Gtk/Avalonia.Gtk/PixbufFramebuffer.cs
  14. 1 1
      src/Gtk/Avalonia.Gtk/WindowImpl.cs
  15. 17 9
      src/Gtk/Avalonia.Gtk/WindowImplBase.cs
  16. 26 0
      src/Skia/Avalonia.Skia.Android/AndroidPlatformRenderInterface.cs
  17. 1 0
      src/Skia/Avalonia.Skia.Android/Avalonia.Skia.Android.csproj
  18. 7 9
      src/Skia/Avalonia.Skia.Android/RenderTarget.cs
  19. 1 1
      src/Skia/Avalonia.Skia.Android/SkiaRenderView.cs
  20. 1 4
      src/Skia/Avalonia.Skia.Desktop/Avalonia.Skia.Desktop.csproj
  21. 21 0
      src/Skia/Avalonia.Skia.Desktop/PlatformRenderInterfaceDesktop.cs
  22. 1 0
      src/Skia/Avalonia.Skia.iOS/Avalonia.Skia.iOS.csproj
  23. 18 0
      src/Skia/Avalonia.Skia.iOS/PlatformRenderingInterfaceIos.cs
  24. 2 3
      src/Skia/Avalonia.Skia.iOS/RenderTarget.cs
  25. 1 0
      src/Skia/Avalonia.Skia/Avalonia.Skia.projitems
  26. 77 0
      src/Skia/Avalonia.Skia/FramebufferRenderTarget.cs
  27. 2 6
      src/Skia/Avalonia.Skia/PlatformRenderInterface.cs
  28. 2 0
      src/Skia/Avalonia.Skia/WindowDrawingContextImpl.cs
  29. 10 9
      src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs
  30. 8 6
      src/Windows/Avalonia.Direct2D1/HwndRenderTarget.cs
  31. 2 0
      src/Windows/Avalonia.Win32/Avalonia.Win32.csproj
  32. 41 0
      src/Windows/Avalonia.Win32/FramebufferManager.cs
  33. 48 0
      src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
  34. 121 0
      src/Windows/Avalonia.Win32/WindowFramebuffer.cs
  35. 9 0
      src/Windows/Avalonia.Win32/WindowImpl.cs
  36. 3 0
      src/iOS/Avalonia.iOS/AvaloniaView.cs
  37. 1 1
      tests/Avalonia.Input.UnitTests/InputElement_HitTesting.cs
  38. 1 1
      tests/Avalonia.Visuals.UnitTests/VisualTree/MockRenderInterface.cs

+ 4 - 1
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 - 0
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 - 0
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 - 0
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 - 0
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 - 0
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
+    }
+}

+ 1 - 1
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/>

+ 5 - 2
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.

+ 7 - 15
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)

+ 6 - 13
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 - 0
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 - 0
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 - 0
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;
+            }
+        }
+    }
+}
+

+ 1 - 1
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();

+ 17 - 9
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 - 0
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 - 0
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" />

+ 7 - 9
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();

+ 1 - 1
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()

+ 1 - 4
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 - 0
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 - 0
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 - 0
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();
+        }
+    }
+}

+ 2 - 3
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 - 0
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 - 0
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));
+        }
+    }
+}

+ 2 - 6
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 - 0
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
 }

+ 10 - 9
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)

+ 8 - 6
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 - 0
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 - 0
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 - 0
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 - 0
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 - 0
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 - 0
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

+ 1 - 1
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();
             }

+ 1 - 1
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();
         }