Browse Source

Ref-counting infrastructure for bitmaps

Nikita Tsukanov 8 years ago
parent
commit
72f766e5d6

+ 143 - 0
src/Avalonia.Base/Utilities/Ref.cs

@@ -0,0 +1,143 @@
+using System;
+using System.Runtime.ConstrainedExecution;
+using System.Threading;
+
+namespace Avalonia.Utilities
+{
+    public interface IRef<out T> : IDisposable where T : class
+    {
+        T Item { get; }
+        IRef<T> Clone();
+        IRef<TResult> CloneAs<TResult>() where TResult : class;
+    }
+
+    
+
+    public static class RefCountable
+    {
+        public static IRef<T> Create<T>(T item) where T : class, IDisposable
+        {
+            return new Ref<T>(item, new RefCounter(item));
+        }
+
+        public static IRef<T> CreateUnownedNotClonable<T>(T item) where T : class
+            => new TempRef<T>(item);
+
+        class TempRef<T> : IRef<T> where T : class
+        {
+            public void Dispose()
+            {
+                
+            }
+
+            public TempRef(T item)
+            {
+                Item = item;
+            }
+            
+            public T Item { get; }
+            public IRef<T> Clone() => throw new NotSupportedException();
+
+            public IRef<TResult> CloneAs<TResult>() where TResult : class
+                => throw new NotSupportedException();
+        }
+        
+        class RefCounter
+        {
+            private IDisposable _item;
+            private volatile int _refs;
+            private object _lock = new object();
+
+            public RefCounter(IDisposable item)
+            {
+                _item = item;
+            }
+
+            public void AddRef()
+            {
+                Interlocked.Increment(ref _refs);
+            }
+
+            public void Release()
+            {
+                if (Interlocked.Decrement(ref _refs) == 0)
+                {
+                    lock (_lock)
+                    {
+                        _item?.Dispose();
+                        _item = null;
+                    }
+                }
+
+            }
+        }
+
+        class Ref<T> : CriticalFinalizerObject, IRef<T> where T : class
+        {
+            private T _item;
+            private RefCounter _counter;
+            private object _lock = new object();
+
+            public Ref(T item, RefCounter counter)
+            {
+                _item = item;
+                _counter = counter;
+                Thread.MemoryBarrier();
+                _counter.AddRef();
+            }
+
+            public void Dispose()
+            {
+                lock (_lock)
+                {
+                    if (_item != null)
+                    {
+                        _counter.Release();
+                        _item = null;
+                    }
+                    GC.SuppressFinalize(this);
+                }
+            }
+
+            ~Ref()
+            {
+                _counter?.Release();
+            }
+
+            public T Item
+            {
+                get
+                {
+                    lock (_lock)
+                    {
+                        return _item;
+                    }
+                }
+            }
+
+            public IRef<T> Clone()
+            {
+                lock (_lock)
+                {
+                    if (_item != null)
+                        return new Ref<T>(_item, _counter);
+                    throw new ObjectDisposedException("Ref<" + typeof(T) + ">");
+                }
+            }
+
+            public IRef<TResult> CloneAs<TResult>() where TResult : class
+            {
+                lock (_lock)
+                {
+                    lock (_lock)
+                    {
+                        if (_item != null)
+                            return new Ref<TResult>((TResult) (object) _item, _counter);
+                        throw new ObjectDisposedException("Ref<" + typeof(T) + ">");
+                    }
+                }
+            }
+        }
+    }
+
+}

+ 1 - 1
src/Avalonia.Controls/WindowIcon.cs

@@ -16,7 +16,7 @@ namespace Avalonia.Controls
     {
         public WindowIcon(IBitmap bitmap)
         {
-            PlatformImpl = AvaloniaLocator.Current.GetService<IPlatformIconLoader>().LoadIcon(bitmap.PlatformImpl);
+            PlatformImpl = AvaloniaLocator.Current.GetService<IPlatformIconLoader>().LoadIcon(bitmap.PlatformImpl.Item);
         }
 
         public WindowIcon(string fileName)

+ 27 - 14
src/Avalonia.Visuals/Media/Imaging/Bitmap.cs

@@ -4,6 +4,7 @@
 using System;
 using System.IO;
 using Avalonia.Platform;
+using Avalonia.Utilities;
 
 namespace Avalonia.Media.Imaging
 {
@@ -19,7 +20,7 @@ namespace Avalonia.Media.Imaging
         public Bitmap(string fileName)
         {
             IPlatformRenderInterface factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>();
-            PlatformImpl = factory.LoadBitmap(fileName);
+            PlatformImpl = RefCountable.Create(factory.LoadBitmap(fileName));
         }
 
         /// <summary>
@@ -29,18 +30,33 @@ namespace Avalonia.Media.Imaging
         public Bitmap(Stream stream)
         {
             IPlatformRenderInterface factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>();
-            PlatformImpl = factory.LoadBitmap(stream);
+            PlatformImpl = RefCountable.Create(factory.LoadBitmap(stream));
         }
 
         /// <summary>
         /// Initializes a new instance of the <see cref="Bitmap"/> class.
         /// </summary>
         /// <param name="impl">A platform-specific bitmap implementation.</param>
+        protected Bitmap(IRef<IBitmapImpl> impl)
+        {
+            PlatformImpl = impl.Clone();
+        }
+        
+        /// <summary>
+        /// Initializes a new instance of the <see cref="Bitmap"/> class.
+        /// </summary>
+        /// <param name="impl">A platform-specific bitmap implementation. Bitmap class takes the ownership.</param>
         protected Bitmap(IBitmapImpl impl)
         {
-            PlatformImpl = impl;
+            PlatformImpl = RefCountable.Create(impl);
         }
-
+        
+        /// <inheritdoc/>
+        public virtual void Dispose()
+        {
+            PlatformImpl.Dispose();
+        }
+        
         /// <summary>
         /// Initializes a new instance of the <see cref="Bitmap"/> class.
         /// </summary>
@@ -51,27 +67,24 @@ namespace Avalonia.Media.Imaging
         /// <param name="stride">Bytes per row</param>
         public Bitmap(PixelFormat format, IntPtr data, int width, int height, int stride)
         {
-            PlatformImpl = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>()
-                .LoadBitmap(format, data, width, height, stride);
+            PlatformImpl = RefCountable.Create(AvaloniaLocator.Current.GetService<IPlatformRenderInterface>()
+                .LoadBitmap(format, data, width, height, stride));
         }
 
         /// <summary>
         /// Gets the width of the bitmap, in pixels.
         /// </summary>
-        public int PixelWidth => PlatformImpl.PixelWidth;
+        public int PixelWidth => PlatformImpl.Item.PixelWidth;
 
         /// <summary>
         /// Gets the height of the bitmap, in pixels.
         /// </summary>
-        public int PixelHeight => PlatformImpl.PixelHeight;
+        public int PixelHeight => PlatformImpl.Item.PixelHeight;
 
         /// <summary>
         /// Gets the platform-specific bitmap implementation.
         /// </summary>
-        public IBitmapImpl PlatformImpl
-        {
-            get;
-        }
+        public IRef<IBitmapImpl> PlatformImpl { get; }
 
         /// <summary>
         /// Saves the bitmap to a file.
@@ -79,12 +92,12 @@ namespace Avalonia.Media.Imaging
         /// <param name="fileName">The filename.</param>
         public void Save(string fileName)
         {
-            PlatformImpl.Save(fileName);
+            PlatformImpl.Item.Save(fileName);
         }
 
         public void Save(Stream stream)
         {
-            PlatformImpl.Save(stream);
+            PlatformImpl.Item.Save(stream);
         }
     }
 }

+ 4 - 2
src/Avalonia.Visuals/Media/Imaging/IBitmap.cs

@@ -1,15 +1,17 @@
 // Copyright (c) The Avalonia Project. All rights reserved.
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
+using System;
 using System.IO;
 using Avalonia.Platform;
+using Avalonia.Utilities;
 
 namespace Avalonia.Media.Imaging
 {
     /// <summary>
     /// Represents a bitmap image.
     /// </summary>
-    public interface IBitmap
+    public interface IBitmap : IDisposable
     {
         /// <summary>
         /// Gets the width of the bitmap, in pixels.
@@ -24,7 +26,7 @@ namespace Avalonia.Media.Imaging
         /// <summary>
         /// Gets the platform-specific bitmap implementation.
         /// </summary>
-        IBitmapImpl PlatformImpl { get; }
+        IRef<IBitmapImpl> PlatformImpl { get; }
 
         /// <summary>
         /// Saves the bitmap to a file.

+ 12 - 4
src/Avalonia.Visuals/Media/Imaging/RenderTargetBitmap.cs

@@ -2,8 +2,10 @@
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
 using System;
+using System.Runtime.CompilerServices;
 using Avalonia.Platform;
 using Avalonia.Rendering;
+using Avalonia.Utilities;
 using Avalonia.VisualTree;
 
 namespace Avalonia.Media.Imaging
@@ -21,14 +23,19 @@ namespace Avalonia.Media.Imaging
         /// <param name="dpiX">The horizontal DPI of the bitmap.</param>
         /// <param name="dpiY">The vertical DPI of the bitmap.</param>
         public RenderTargetBitmap(int pixelWidth, int pixelHeight, double dpiX = 96, double dpiY = 96)
-            : base(CreateImpl(pixelWidth, pixelHeight, dpiX, dpiY))
+           : this(RefCountable.Create(CreateImpl(pixelWidth, pixelHeight, dpiX, dpiY)))
         {
         }
 
+        private RenderTargetBitmap(IRef<IRenderTargetBitmapImpl> impl) : base(impl)
+        {
+            PlatformImpl = impl;
+        }
+
         /// <summary>
         /// Gets the platform-specific bitmap implementation.
         /// </summary>
-        public new IRenderTargetBitmapImpl PlatformImpl => (IRenderTargetBitmapImpl)base.PlatformImpl;
+        public new IRef<IRenderTargetBitmapImpl> PlatformImpl { get; }
 
         /// <summary>
         /// Disposes of the bitmap.
@@ -36,6 +43,7 @@ namespace Avalonia.Media.Imaging
         public void Dispose()
         {
             PlatformImpl.Dispose();
+            base.Dispose();
         }
 
         /// <summary>
@@ -52,13 +60,13 @@ namespace Avalonia.Media.Imaging
         /// <param name="dpiX">The horizontal DPI of the bitmap.</param>
         /// <param name="dpiY">The vertical DPI of the bitmap.</param>
         /// <returns>The platform-specific implementation.</returns>
-        private static IBitmapImpl CreateImpl(int width, int height, double dpiX, double dpiY)
+        private static IRenderTargetBitmapImpl CreateImpl(int width, int height, double dpiX, double dpiY)
         {
             IPlatformRenderInterface factory = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>();
             return factory.CreateRenderTargetBitmap(width, height, dpiX, dpiY);
         }
 
         /// <inheritdoc/>
-        public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer vbr) => PlatformImpl.CreateDrawingContext(vbr);
+        public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer vbr) => PlatformImpl.Item.CreateDrawingContext(vbr);
     }
 }

+ 3 - 2
src/Avalonia.Visuals/Media/Imaging/WritableBitmap.cs

@@ -4,6 +4,7 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 using Avalonia.Platform;
+using Avalonia.Utilities;
 
 namespace Avalonia.Media.Imaging
 {
@@ -16,7 +17,7 @@ namespace Avalonia.Media.Imaging
             : base(AvaloniaLocator.Current.GetService<IPlatformRenderInterface>().CreateWritableBitmap(width, height, format))
         {
         }
-
-        public ILockedFramebuffer Lock() => ((IWritableBitmapImpl) PlatformImpl).Lock();
+        
+        public ILockedFramebuffer Lock() => ((IWritableBitmapImpl) PlatformImpl.Item).Lock();
     }
 }

+ 2 - 1
src/Avalonia.Visuals/Platform/IBitmapImpl.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;
 using System.IO;
 
 namespace Avalonia.Platform
@@ -8,7 +9,7 @@ namespace Avalonia.Platform
     /// <summary>
     /// Defines the platform-specific interface for a <see cref="Avalonia.Media.Imaging.Bitmap"/>.
     /// </summary>
-    public interface IBitmapImpl
+    public interface IBitmapImpl : IDisposable
     {
         /// <summary>
         /// Gets the width of the bitmap, in pixels.

+ 3 - 2
src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs

@@ -3,6 +3,7 @@
 
 using System;
 using Avalonia.Media;
+using Avalonia.Utilities;
 
 namespace Avalonia.Platform
 {
@@ -29,7 +30,7 @@ namespace Avalonia.Platform
         /// <param name="opacity">The opacity to draw with.</param>
         /// <param name="sourceRect">The rect in the image to draw.</param>
         /// <param name="destRect">The rect in the output to draw to.</param>
-        void DrawImage(IBitmapImpl source, double opacity, Rect sourceRect, Rect destRect);
+        void DrawImage(IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect);
 
         /// <summary>
         /// Draws a bitmap image.
@@ -38,7 +39,7 @@ namespace Avalonia.Platform
         /// <param name="opacityMask">The opacity mask to draw with.</param>
         /// <param name="opacityMaskRect">The destination rect for the opacity mask.</param>
         /// <param name="destRect">The rect in the output to draw to.</param>
-        void DrawImage(IBitmapImpl source, IBrush opacityMask, Rect opacityMaskRect, Rect destRect);
+        void DrawImage(IRef<IBitmapImpl> source, IBrush opacityMask, Rect opacityMaskRect, Rect destRect);
 
         /// <summary>
         /// Draws a line.

+ 11 - 10
src/Avalonia.Visuals/Rendering/DeferredRenderer.cs

@@ -12,6 +12,7 @@ using System.IO;
 using Avalonia.Media.Immutable;
 using System.Threading;
 using System.Linq;
+using Avalonia.Utilities;
 
 namespace Avalonia.Rendering
 {
@@ -31,7 +32,7 @@ namespace Avalonia.Rendering
         private Scene _scene;
         private IRenderTarget _renderTarget;
         private DirtyVisuals _dirty;
-        private IRenderTargetBitmapImpl _overlay;
+        private IRef<IRenderTargetBitmapImpl> _overlay;
         private bool _updateQueued;
         private object _rendering = new object();
         private int _lastSceneId = -1;
@@ -267,7 +268,7 @@ namespace Avalonia.Rendering
 
                     if (node != null)
                     {
-                        using (var context = renderTarget.CreateDrawingContext(this))
+                        using (var context = renderTarget.Item.CreateDrawingContext(this))
                         {
                             foreach (var rect in layer.Dirty)
                             {
@@ -294,7 +295,7 @@ namespace Avalonia.Rendering
             {
                 var overlay = GetOverlay(parentContent, scene.Size, scene.Scaling);
 
-                using (var context = overlay.CreateDrawingContext(this))
+                using (var context = overlay.Item.CreateDrawingContext(this))
                 {
                     context.Clear(Colors.Transparent);
                     RenderDirtyRects(context);
@@ -323,7 +324,7 @@ namespace Avalonia.Rendering
             foreach (var layer in scene.Layers)
             {
                 var bitmap = _layers[layer.LayerRoot].Bitmap;
-                var sourceRect = new Rect(0, 0, bitmap.PixelWidth, bitmap.PixelHeight);
+                var sourceRect = new Rect(0, 0, bitmap.Item.PixelWidth, bitmap.Item.PixelHeight);
 
                 if (layer.GeometryClip != null)
                 {
@@ -347,7 +348,7 @@ namespace Avalonia.Rendering
 
             if (_overlay != null)
             {
-                var sourceRect = new Rect(0, 0, _overlay.PixelWidth, _overlay.PixelHeight);
+                var sourceRect = new Rect(0, 0, _overlay.Item.PixelWidth, _overlay.Item.PixelHeight);
                 context.DrawImage(_overlay, 0.5, sourceRect, clientRect);
             }
 
@@ -420,7 +421,7 @@ namespace Avalonia.Rendering
             }
         }
 
-        private IRenderTargetBitmapImpl GetOverlay(
+        private IRef<IRenderTargetBitmapImpl> GetOverlay(
             IDrawingContextImpl parentContext,
             Size size,
             double scaling)
@@ -428,11 +429,11 @@ namespace Avalonia.Rendering
             var pixelSize = size * scaling;
 
             if (_overlay == null ||
-                _overlay.PixelWidth != pixelSize.Width ||
-                _overlay.PixelHeight != pixelSize.Height)
+                _overlay.Item.PixelWidth != pixelSize.Width ||
+                _overlay.Item.PixelHeight != pixelSize.Height)
             {
                 _overlay?.Dispose();
-                _overlay = parentContext.CreateLayer(size);
+                _overlay = RefCountable.Create(parentContext.CreateLayer(size));
             }
 
             return _overlay;
@@ -445,7 +446,7 @@ namespace Avalonia.Rendering
             foreach (var layer in _layers)
             {
                 var fileName = Path.Combine(DebugFramesPath, $"frame-{id}-layer-{index++}.png");
-                layer.Bitmap.Save(fileName);
+                layer.Bitmap.Item.Save(fileName);
             }
         }
     }

+ 5 - 4
src/Avalonia.Visuals/Rendering/RenderLayer.cs

@@ -1,6 +1,7 @@
 using System;
 using Avalonia.Media;
 using Avalonia.Platform;
+using Avalonia.Utilities;
 using Avalonia.VisualTree;
 
 namespace Avalonia.Rendering
@@ -16,13 +17,13 @@ namespace Avalonia.Rendering
             IVisual layerRoot)
         {
             _drawingContext = drawingContext;
-            Bitmap = drawingContext.CreateLayer(size);
+            Bitmap = RefCountable.Create(drawingContext.CreateLayer(size));
             Size = size;
             Scaling = scaling;
             LayerRoot = layerRoot;
         }
 
-        public IRenderTargetBitmapImpl Bitmap { get; private set; }
+        public IRef<IRenderTargetBitmapImpl> Bitmap { get; private set; }
         public double Scaling { get; private set; }
         public Size Size { get; private set; }
         public IVisual LayerRoot { get; }
@@ -31,9 +32,9 @@ namespace Avalonia.Rendering
         {
             if (Size != size || Scaling != scaling)
             {
-                var resized = _drawingContext.CreateLayer(size);
+                var resized = RefCountable.Create(_drawingContext.CreateLayer(size));
 
-                using (var context = resized.CreateDrawingContext(null))
+                using (var context = resized.Item.CreateDrawingContext(null))
                 {
                     context.Clear(Colors.Transparent);
                     context.DrawImage(Bitmap, 1, new Rect(Size), new Rect(Size));

+ 3 - 2
src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs

@@ -5,6 +5,7 @@ using System;
 using System.Collections.Generic;
 using Avalonia.Media;
 using Avalonia.Platform;
+using Avalonia.Utilities;
 using Avalonia.VisualTree;
 
 namespace Avalonia.Rendering.SceneGraph
@@ -113,7 +114,7 @@ namespace Avalonia.Rendering.SceneGraph
         }
 
         /// <inheritdoc/>
-        public void DrawImage(IBitmapImpl source, double opacity, Rect sourceRect, Rect destRect)
+        public void DrawImage(IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect)
         {
             var next = NextDrawAs<ImageNode>();
 
@@ -128,7 +129,7 @@ namespace Avalonia.Rendering.SceneGraph
         }
 
         /// <inheritdoc/>
-        public void DrawImage(IBitmapImpl source, IBrush opacityMask, Rect opacityMaskRect, Rect sourceRect)
+        public void DrawImage(IRef<IBitmapImpl> source, IBrush opacityMask, Rect opacityMaskRect, Rect sourceRect)
         {
             // This method is currently only used to composite layers so shouldn't be called here.
             throw new NotSupportedException();

+ 12 - 6
src/Avalonia.Visuals/Rendering/SceneGraph/ImageNode.cs

@@ -3,13 +3,14 @@
 
 using System;
 using Avalonia.Platform;
+using Avalonia.Utilities;
 
 namespace Avalonia.Rendering.SceneGraph
 {
     /// <summary>
     /// A node in the scene graph which represents an image draw.
     /// </summary>
-    internal class ImageNode : DrawOperation
+    internal class ImageNode : DrawOperation, IDisposable
     {
         /// <summary>
         /// Initializes a new instance of the <see cref="ImageNode"/> class.
@@ -19,11 +20,11 @@ namespace Avalonia.Rendering.SceneGraph
         /// <param name="opacity">The draw opacity.</param>
         /// <param name="sourceRect">The source rect.</param>
         /// <param name="destRect">The destination rect.</param>
-        public ImageNode(Matrix transform, IBitmapImpl source, double opacity, Rect sourceRect, Rect destRect)
+        public ImageNode(Matrix transform, IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect)
             : base(destRect, transform, null)
         {
             Transform = transform;
-            Source = source;
+            Source = source.Clone();
             Opacity = opacity;
             SourceRect = sourceRect;
             DestRect = destRect;
@@ -37,7 +38,7 @@ namespace Avalonia.Rendering.SceneGraph
         /// <summary>
         /// Gets the image to draw.
         /// </summary>
-        public IBitmapImpl Source { get; }
+        public IRef<IBitmapImpl> Source { get; }
 
         /// <summary>
         /// Gets the draw opacity.
@@ -67,10 +68,10 @@ namespace Avalonia.Rendering.SceneGraph
         /// The properties of the other draw operation are passed in as arguments to prevent
         /// allocation of a not-yet-constructed draw operation object.
         /// </remarks>
-        public bool Equals(Matrix transform, IBitmapImpl source, double opacity, Rect sourceRect, Rect destRect)
+        public bool Equals(Matrix transform, IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect)
         {
             return transform == Transform &&
-                Equals(source, Source) &&
+                Equals(source.Item, Source.Item) &&
                 opacity == Opacity &&
                 sourceRect == SourceRect &&
                 destRect == DestRect;
@@ -87,5 +88,10 @@ namespace Avalonia.Rendering.SceneGraph
 
         /// <inheritdoc/>
         public override bool HitTest(Point p) => Bounds.Contains(p);
+
+        public void Dispose()
+        {
+            Source?.Dispose();
+        }
     }
 }

+ 7 - 6
src/Skia/Avalonia.Skia/DrawingContextImpl.cs

@@ -6,6 +6,7 @@ using System.Linq;
 using Avalonia.Platform;
 using Avalonia.Rendering;
 using Avalonia.Rendering.Utilities;
+using Avalonia.Utilities;
 
 namespace Avalonia.Skia
 {
@@ -39,9 +40,9 @@ namespace Avalonia.Skia
             Canvas.Clear(color.ToSKColor());
         }
 
-        public void DrawImage(IBitmapImpl source, double opacity, Rect sourceRect, Rect destRect)
+        public void DrawImage(IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect)
         {
-            var impl = (BitmapImpl)source;
+            var impl = (BitmapImpl)source.Item;
             var s = sourceRect.ToSKRect();
             var d = destRect.ToSKRect();
             using (var paint = new SKPaint()
@@ -51,10 +52,10 @@ namespace Avalonia.Skia
             }
         }
 
-        public void DrawImage(IBitmapImpl source, IBrush opacityMask, Rect opacityMaskRect, Rect destRect)
+        public void DrawImage(IRef<IBitmapImpl> source, IBrush opacityMask, Rect opacityMaskRect, Rect destRect)
         {
             PushOpacityMask(opacityMask, opacityMaskRect);
-            DrawImage(source, 1, new Rect(0, 0, source.PixelWidth, source.PixelHeight), destRect);
+            DrawImage(source, 1, new Rect(0, 0, source.Item.PixelWidth, source.Item.PixelHeight), destRect);
             PopOpacityMask();
         }
 
@@ -237,7 +238,7 @@ namespace Avalonia.Skia
             }
             else
             {
-                tileBrushImage = (BitmapImpl)((tileBrush as IImageBrush)?.Source?.PlatformImpl);
+                tileBrushImage = (BitmapImpl)((tileBrush as IImageBrush)?.Source?.PlatformImpl.Item);
             }
 
             if (tileBrush != null && tileBrushImage != null)
@@ -252,7 +253,7 @@ namespace Avalonia.Skia
                     context.Clear(Colors.Transparent);
                     context.PushClip(calc.IntermediateClip);
                     context.Transform = calc.IntermediateTransform;
-                    context.DrawImage(tileBrushImage, 1, rect, rect);
+                    context.DrawImage(RefCountable.CreateUnownedNotClonable(tileBrushImage), 1, rect, rect);
                     context.PopClip();
                 }
 

+ 5 - 4
src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs

@@ -6,6 +6,7 @@ using System.Collections.Generic;
 using Avalonia.Media;
 using Avalonia.Platform;
 using Avalonia.Rendering;
+using Avalonia.Utilities;
 using SharpDX;
 using SharpDX.Direct2D1;
 using SharpDX.Mathematics.Interop;
@@ -100,9 +101,9 @@ namespace Avalonia.Direct2D1.Media
         /// <param name="opacity">The opacity to draw with.</param>
         /// <param name="sourceRect">The rect in the image to draw.</param>
         /// <param name="destRect">The rect in the output to draw to.</param>
-        public void DrawImage(IBitmapImpl source, double opacity, Rect sourceRect, Rect destRect)
+        public void DrawImage(IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect)
         {
-            using (var d2d = ((BitmapImpl)source).GetDirect2DBitmap(_renderTarget))
+            using (var d2d = ((BitmapImpl)source.Item).GetDirect2DBitmap(_renderTarget))
             {
                 _renderTarget.DrawBitmap(
                     d2d.Value,
@@ -120,9 +121,9 @@ namespace Avalonia.Direct2D1.Media
         /// <param name="opacityMask">The opacity mask to draw with.</param>
         /// <param name="opacityMaskRect">The destination rect for the opacity mask.</param>
         /// <param name="destRect">The rect in the output to draw to.</param>
-        public void DrawImage(IBitmapImpl source, IBrush opacityMask, Rect opacityMaskRect, Rect destRect)
+        public void DrawImage(IRef<IBitmapImpl> source, IBrush opacityMask, Rect opacityMaskRect, Rect destRect)
         {
-            using (var d2dSource = ((BitmapImpl)source).GetDirect2DBitmap(_renderTarget))
+            using (var d2dSource = ((BitmapImpl)source.Item).GetDirect2DBitmap(_renderTarget))
             using (var sourceBrush = new BitmapBrush(_renderTarget, d2dSource.Value))
             using (var d2dOpacityMask = CreateBrush(opacityMask, opacityMaskRect.Size))
             using (var geometry = new SharpDX.Direct2D1.RectangleGeometry(_renderTarget.Factory, destRect.ToDirect2D()))

+ 3 - 1
src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs

@@ -4,6 +4,7 @@
 using System;
 using Avalonia.Media;
 using Avalonia.Rendering.Utilities;
+using Avalonia.Utilities;
 using SharpDX.Direct2D1;
 
 namespace Avalonia.Direct2D1.Media
@@ -100,7 +101,8 @@ namespace Avalonia.Direct2D1.Media
                 context.Clear(Colors.Transparent);
                 context.PushClip(calc.IntermediateClip);
                 context.Transform = calc.IntermediateTransform;
-                context.DrawImage(bitmap, 1, rect, rect);
+                
+                context.DrawImage(RefCountable.CreateUnownedNotClonable(bitmap), 1, rect, rect);
                 context.PopClip();
             }