diff --git a/src/Avalonia.Base/Platform/IRuntimePlatform.cs b/src/Avalonia.Base/Platform/IRuntimePlatform.cs
index e1a09f094d..66253dc5b2 100644
--- a/src/Avalonia.Base/Platform/IRuntimePlatform.cs
+++ b/src/Avalonia.Base/Platform/IRuntimePlatform.cs
@@ -14,6 +14,15 @@ namespace Avalonia.Platform
IDisposable StartSystemTimer(TimeSpan interval, Action tick);
string GetStackTrace();
RuntimePlatformInfo GetRuntimeInfo();
+ IUnmanagedBlob AllocBlob(int size);
+ }
+
+ public interface IUnmanagedBlob : IDisposable
+ {
+ IntPtr Address { get; }
+ int Size { get; }
+ bool IsDisposed { get; }
+
}
public struct RuntimePlatformInfo
diff --git a/src/Avalonia.DotNetCoreRuntime/Avalonia.DotNetCoreRuntime.csproj b/src/Avalonia.DotNetCoreRuntime/Avalonia.DotNetCoreRuntime.csproj
index eade213c4c..53b2c997d0 100644
--- a/src/Avalonia.DotNetCoreRuntime/Avalonia.DotNetCoreRuntime.csproj
+++ b/src/Avalonia.DotNetCoreRuntime/Avalonia.DotNetCoreRuntime.csproj
@@ -2,6 +2,7 @@
netcoreapp2.0
false
+ $(DefineConstants);DOTNETCORE
bin\$(Configuration)\Avalonia.DotNetCoreRuntime.XML
@@ -21,5 +22,5 @@
-
+
\ No newline at end of file
diff --git a/src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs b/src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs
index 182da7df58..4ebb080dd0 100644
--- a/src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs
+++ b/src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs
@@ -16,7 +16,7 @@ namespace Avalonia.Gtk3
{
private readonly WindowBaseImpl _impl;
private readonly GtkWidget _widget;
- private CairoSurface _surface;
+ private ManagedCairoSurface _surface;
private int _factor;
private object _lock = new object();
public ImageSurfaceFramebuffer(WindowBaseImpl impl, int width, int height, int factor)
@@ -26,13 +26,13 @@ namespace Avalonia.Gtk3
_factor = factor;
width *= _factor;
height *= _factor;
- _surface = Native.CairoImageSurfaceCreate(1, width, height);
+ _surface = new ManagedCairoSurface(width, height);
Width = width;
Height = height;
- Address = Native.CairoImageSurfaceGetData(_surface);
- RowBytes = Native.CairoImageSurfaceGetStride(_surface);
- Native.CairoSurfaceFlush(_surface);
+ Address = _surface.Buffer;
+ RowBytes = _surface.Stride;
+ Native.CairoSurfaceFlush(_surface.Surface);
}
static void Draw(IntPtr context, CairoSurface surface, double factor)
@@ -83,12 +83,12 @@ namespace Avalonia.Gtk3
class RenderOp : IDeferredRenderOperation
{
private readonly GtkWidget _widget;
- private CairoSurface _surface;
+ private ManagedCairoSurface _surface;
private readonly double _factor;
private readonly int _width;
private readonly int _height;
- public RenderOp(GtkWidget widget, CairoSurface _surface, double factor, int width, int height)
+ public RenderOp(GtkWidget widget, ManagedCairoSurface _surface, double factor, int width, int height)
{
_widget = widget;
this._surface = _surface;
@@ -105,7 +105,7 @@ namespace Avalonia.Gtk3
public void RenderNow()
{
- DrawToWidget(_widget, _surface, _width, _height, _factor);
+ DrawToWidget(_widget, _surface.Surface, _width, _height, _factor);
}
}
@@ -116,9 +116,9 @@ namespace Avalonia.Gtk3
if (Dispatcher.UIThread.CheckAccess())
{
if (_impl.CurrentCairoContext != IntPtr.Zero)
- Draw(_impl.CurrentCairoContext, _surface, _factor);
+ Draw(_impl.CurrentCairoContext, _surface.Surface, _factor);
else
- DrawToWidget(_widget, _surface, Width, Height, _factor);
+ DrawToWidget(_widget, _surface.Surface, Width, Height, _factor);
_surface.Dispose();
}
else
diff --git a/src/Gtk/Avalonia.Gtk3/Interop/ManagedCairoSurface.cs b/src/Gtk/Avalonia.Gtk3/Interop/ManagedCairoSurface.cs
new file mode 100644
index 0000000000..2cde99afa5
--- /dev/null
+++ b/src/Gtk/Avalonia.Gtk3/Interop/ManagedCairoSurface.cs
@@ -0,0 +1,38 @@
+using System;
+using System.Runtime.InteropServices;
+using Avalonia.Platform;
+
+namespace Avalonia.Gtk3.Interop
+{
+ class ManagedCairoSurface : IDisposable
+ {
+ public IntPtr Buffer { get; private set; }
+ public CairoSurface Surface { get; private set; }
+ public int Stride { get; private set; }
+ private int _size;
+ private IRuntimePlatform _plat;
+ private IUnmanagedBlob _blob;
+
+ public ManagedCairoSurface(int width, int height)
+ {
+ _plat = AvaloniaLocator.Current.GetService();
+ Stride = width * 4;
+ _size = height * Stride;
+ _blob = _plat.AllocBlob(_size * 2);
+ Buffer = _blob.Address;
+ Surface = Native.CairoImageSurfaceCreateForData(Buffer, 1, width, height, Stride);
+ }
+
+ public void Dispose()
+ {
+
+ if (Buffer != IntPtr.Zero)
+ {
+ Surface.Dispose();
+ _blob.Dispose();
+ Buffer = IntPtr.Zero;
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs
index c96fb23366..433094e0ae 100644
--- a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs
+++ b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs
@@ -160,6 +160,9 @@ namespace Avalonia.Gtk3.Interop
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
public delegate CairoSurface cairo_image_surface_create(int format, int width, int height);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
+ public delegate CairoSurface cairo_image_surface_create_for_data(IntPtr data, int format, int width, int height, int stride);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
public delegate IntPtr cairo_image_surface_get_data(CairoSurface surface);
@@ -459,6 +462,7 @@ namespace Avalonia.Gtk3.Interop
public static D.gdk_cairo_create GdkCairoCreate;
public static D.cairo_image_surface_create CairoImageSurfaceCreate;
+ public static D.cairo_image_surface_create_for_data CairoImageSurfaceCreateForData;
public static D.cairo_image_surface_get_data CairoImageSurfaceGetData;
public static D.cairo_image_surface_get_stride CairoImageSurfaceGetStride;
public static D.cairo_surface_mark_dirty CairoSurfaceMarkDirty;
diff --git a/src/Shared/PlatformSupport/StandardRuntimePlatform.cs b/src/Shared/PlatformSupport/StandardRuntimePlatform.cs
index 757cf52853..d648b630bd 100644
--- a/src/Shared/PlatformSupport/StandardRuntimePlatform.cs
+++ b/src/Shared/PlatformSupport/StandardRuntimePlatform.cs
@@ -2,8 +2,11 @@
// 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.Reflection;
using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using System.Threading;
using Avalonia.Platform;
@@ -18,8 +21,135 @@ namespace Avalonia.Shared.PlatformSupport
return new Timer(_ => tick(), null, interval, interval);
}
+ public string GetStackTrace() => Environment.StackTrace;
+
+ public IUnmanagedBlob AllocBlob(int size) => new UnmanagedBlob(this, size);
+
+ class UnmanagedBlob : IUnmanagedBlob
+ {
+ private readonly StandardRuntimePlatform _plat;
+#if DEBUG
+ private static readonly List Backtraces = new List();
+ private static Thread GCThread;
+ private readonly string _backtrace;
- public string GetStackTrace() => Environment.StackTrace;
+ class GCThreadDetector
+ {
+ ~GCThreadDetector()
+ {
+ GCThread = Thread.CurrentThread;
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ static void Spawn() => new GCThreadDetector();
+
+ static UnmanagedBlob()
+ {
+ Spawn();
+ GC.WaitForPendingFinalizers();
+ }
+
+#endif
+
+ public UnmanagedBlob(StandardRuntimePlatform plat, int size)
+ {
+ _plat = plat;
+ Address = plat.Alloc(size);
+ GC.AddMemoryPressure(size);
+ Size = size;
+#if DEBUG
+ _backtrace = Environment.StackTrace;
+ Backtraces.Add(_backtrace);
+#endif
+ }
+
+ void DoDispose()
+ {
+ if (!IsDisposed)
+ {
+ Backtraces.Remove(_backtrace);
+ _plat.Free(Address, Size);
+ GC.RemoveMemoryPressure(Size);
+ IsDisposed = true;
+ Address = IntPtr.Zero;
+ }
+ }
+
+ public void Dispose()
+ {
+#if DEBUG
+ if (Thread.CurrentThread.ManagedThreadId == GCThread?.ManagedThreadId)
+ {
+ Console.Error.WriteLine("Native blob disposal from finalizer thread\nBacktrace: "
+ + Environment.StackTrace
+ + "\n\nBlob created by " + _backtrace);
+ }
+#endif
+ DoDispose();
+ GC.SuppressFinalize(this);
+ }
+
+ ~UnmanagedBlob()
+ {
+#if DEBUG
+ Console.Error.WriteLine("Undisposed native blob created by " + _backtrace);
+#endif
+ DoDispose();
+ }
+
+ public IntPtr Address { get; private set; }
+ public int Size { get; private set; }
+ public bool IsDisposed { get; private set; }
+ }
+
+
+
+#if FULLDOTNET || DOTNETCORE
+ [DllImport("libc", SetLastError = true)]
+ private static extern IntPtr mmap(IntPtr addr, IntPtr length, int prot, int flags, int fd, IntPtr offset);
+ [DllImport("libc", SetLastError = true)]
+ private static extern int munmap(IntPtr addr, IntPtr length);
+ [DllImport("libc", SetLastError = true)]
+ private static extern long sysconf(int name);
+
+ private bool? _useMmap;
+ private bool UseMmap
+ => _useMmap ?? ((_useMmap = GetRuntimeInfo().OperatingSystem == OperatingSystemType.Linux)).Value;
+
+ IntPtr Alloc(int size)
+ {
+ if (UseMmap)
+ {
+ var rv = mmap(IntPtr.Zero, new IntPtr(size), 3, 0x22, -1, IntPtr.Zero);
+ if (rv.ToInt64() == -1 || (ulong) rv.ToInt64() == 0xffffffff)
+ {
+ var errno = Marshal.GetLastWin32Error();
+ throw new Exception("Unable to allocate memory: " + errno);
+ }
+ return rv;
+ }
+ else
+ return Marshal.AllocHGlobal(size);
+ }
+
+ void Free(IntPtr ptr, int len)
+ {
+ if (UseMmap)
+ {
+ if (munmap(ptr, new IntPtr(len)) == -1)
+ {
+ var errno = Marshal.GetLastWin32Error();
+ throw new Exception("Unable to free memory: " + errno);
+ }
+ }
+ else
+ Marshal.FreeHGlobal(ptr);
+ }
+#else
+ IntPtr Alloc(int size) => Marshal.AllocHGlobal(size);
+ void Free(IntPtr ptr, int len) => Marshal.FreeHGlobal(ptr);
+#endif
}
}
\ No newline at end of file
diff --git a/src/Skia/Avalonia.Skia/BitmapImpl.cs b/src/Skia/Avalonia.Skia/BitmapImpl.cs
index ce3efded11..d13190deff 100644
--- a/src/Skia/Avalonia.Skia/BitmapImpl.cs
+++ b/src/Skia/Avalonia.Skia/BitmapImpl.cs
@@ -20,6 +20,13 @@ namespace Avalonia.Skia
_dpi = new Vector(96, 96);
}
+ static void ReleaseProc(IntPtr address, object ctx)
+ {
+ ((IUnmanagedBlob) ctx).Dispose();
+ }
+
+ private static readonly SKBitmapReleaseDelegate ReleaseDelegate = ReleaseProc;
+
public BitmapImpl(int width, int height, Vector dpi, PixelFormat? fmt = null)
{
PixelHeight = height;
@@ -29,6 +36,12 @@ namespace Avalonia.Skia
var runtime = AvaloniaLocator.Current?.GetService()?.GetRuntimeInfo();
if (runtime?.IsDesktop == true && runtime?.OperatingSystem == OperatingSystemType.Linux)
colorType = SKColorType.Bgra8888;
+
+ Bitmap = new SKBitmap();
+ var nfo = new SKImageInfo(width, height, colorType, SKAlphaType.Premul);
+ var plat = AvaloniaLocator.Current.GetService();
+ var blob = plat.AllocBlob(nfo.BytesSize);
+ Bitmap.InstallPixels(nfo, blob.Address, nfo.RowBytes, null, ReleaseDelegate, blob);
Bitmap = new SKBitmap(width, height, colorType, SKAlphaType.Premul);
Bitmap.Erase(SKColor.Empty);
}