Browse Source

Ref-counting infrastructure for bitmaps

pull/1277/head
Nikita Tsukanov 9 years ago
parent
commit
72f766e5d6
  1. 143
      src/Avalonia.Base/Utilities/Ref.cs
  2. 2
      src/Avalonia.Controls/WindowIcon.cs
  3. 41
      src/Avalonia.Visuals/Media/Imaging/Bitmap.cs
  4. 6
      src/Avalonia.Visuals/Media/Imaging/IBitmap.cs
  5. 16
      src/Avalonia.Visuals/Media/Imaging/RenderTargetBitmap.cs
  6. 5
      src/Avalonia.Visuals/Media/Imaging/WritableBitmap.cs
  7. 3
      src/Avalonia.Visuals/Platform/IBitmapImpl.cs
  8. 5
      src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs
  9. 21
      src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
  10. 9
      src/Avalonia.Visuals/Rendering/RenderLayer.cs
  11. 5
      src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs
  12. 18
      src/Avalonia.Visuals/Rendering/SceneGraph/ImageNode.cs
  13. 13
      src/Skia/Avalonia.Skia/DrawingContextImpl.cs
  14. 9
      src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
  15. 4
      src/Windows/Avalonia.Direct2D1/Media/ImageBrushImpl.cs

143
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) + ">");
}
}
}
}
}
}

2
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)

41
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);
}
}
}

6
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.

16
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);
}
}

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

3
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.

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

21
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);
}
}
}

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

5
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();

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

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

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

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

Loading…
Cancel
Save