diff --git a/src/Avalonia.Visuals/Platform/IBitmapImpl.cs b/src/Avalonia.Visuals/Platform/IBitmapImpl.cs
index d60838452e..6d19205b8f 100644
--- a/src/Avalonia.Visuals/Platform/IBitmapImpl.cs
+++ b/src/Avalonia.Visuals/Platform/IBitmapImpl.cs
@@ -20,6 +20,11 @@ namespace Avalonia.Platform
/// Gets the height of the bitmap, in pixels.
///
int PixelHeight { get; }
+
+ ///
+ /// Version of the pixel data
+ ///
+ int Version { get; }
///
/// Saves the bitmap to a file.
diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/ImageNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/ImageNode.cs
index 1abd966a9f..e1bdcaab3b 100644
--- a/src/Avalonia.Visuals/Rendering/SceneGraph/ImageNode.cs
+++ b/src/Avalonia.Visuals/Rendering/SceneGraph/ImageNode.cs
@@ -30,6 +30,7 @@ namespace Avalonia.Rendering.SceneGraph
SourceRect = sourceRect;
DestRect = destRect;
BitmapInterpolationMode = bitmapInterpolationMode;
+ SourceVersion = Source.Item.Version;
}
///
@@ -42,6 +43,11 @@ namespace Avalonia.Rendering.SceneGraph
///
public IRef Source { get; }
+ ///
+ /// Source bitmap Version
+ ///
+ public int SourceVersion { get; }
+
///
/// Gets the draw opacity.
///
@@ -83,6 +89,7 @@ namespace Avalonia.Rendering.SceneGraph
{
return transform == Transform &&
Equals(source.Item, Source.Item) &&
+ source.Item.Version == SourceVersion &&
opacity == Opacity &&
sourceRect == SourceRect &&
destRect == DestRect &&
@@ -92,8 +99,6 @@ namespace Avalonia.Rendering.SceneGraph
///
public override void Render(IDrawingContextImpl context)
{
- // TODO: Probably need to introduce some kind of locking mechanism in the case of
- // WriteableBitmap.
context.Transform = Transform;
context.DrawImage(Source, Opacity, SourceRect, DestRect, BitmapInterpolationMode);
}
diff --git a/src/Skia/Avalonia.Skia/ImmutableBitmap.cs b/src/Skia/Avalonia.Skia/ImmutableBitmap.cs
index 332b8547bf..9eb443caa3 100644
--- a/src/Skia/Avalonia.Skia/ImmutableBitmap.cs
+++ b/src/Skia/Avalonia.Skia/ImmutableBitmap.cs
@@ -65,6 +65,8 @@ namespace Avalonia.Skia
///
public int PixelHeight { get; }
+ public int Version { get; } = 1;
+
///
public void Dispose()
{
diff --git a/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs b/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs
index 914dc7d05a..a1e9f1d31e 100644
--- a/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs
+++ b/src/Skia/Avalonia.Skia/SurfaceRenderTarget.cs
@@ -3,6 +3,7 @@
using System;
using System.IO;
+using System.Reactive.Disposables;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Skia.Helpers;
@@ -79,7 +80,7 @@ namespace Avalonia.Skia
GrContext = _grContext
};
- return new DrawingContextImpl(createInfo);
+ return new DrawingContextImpl(createInfo, Disposable.Create(() => Version++));
}
///
@@ -88,6 +89,8 @@ namespace Avalonia.Skia
///
public int PixelHeight { get; }
+ public int Version { get; private set; } = 1;
+
///
public void Save(string fileName)
{
diff --git a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs
index 915aa77b52..8ad454461c 100644
--- a/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs
+++ b/src/Skia/Avalonia.Skia/WriteableBitmapImpl.cs
@@ -3,6 +3,7 @@
using System;
using System.IO;
+using System.Threading;
using Avalonia.Platform;
using Avalonia.Skia.Helpers;
using SkiaSharp;
@@ -16,6 +17,7 @@ namespace Avalonia.Skia
{
private static readonly SKBitmapReleaseDelegate s_releaseDelegate = ReleaseProc;
private readonly SKBitmap _bitmap;
+ private readonly object _lock = new object();
///
/// Create new writeable bitmap.
@@ -55,10 +57,13 @@ namespace Avalonia.Skia
///
public int PixelHeight { get; }
+ public int Version { get; private set; } = 1;
+
///
public void Draw(DrawingContextImpl context, SKRect sourceRect, SKRect destRect, SKPaint paint)
{
- context.Canvas.DrawBitmap(_bitmap, sourceRect, destRect, paint);
+ lock (_lock)
+ context.Canvas.DrawBitmap(_bitmap, sourceRect, destRect, paint);
}
///
@@ -86,7 +91,7 @@ namespace Avalonia.Skia
}
///
- public ILockedFramebuffer Lock() => new BitmapFramebuffer(_bitmap);
+ public ILockedFramebuffer Lock() => new BitmapFramebuffer(this, _bitmap);
///
/// Get snapshot as image.
@@ -94,7 +99,8 @@ namespace Avalonia.Skia
/// Image snapshot.
public SKImage GetSnapshot()
{
- return SKImage.FromPixels(_bitmap.Info, _bitmap.GetPixels(), _bitmap.RowBytes);
+ lock (_lock)
+ return SKImage.FromPixels(_bitmap.Info, _bitmap.GetPixels(), _bitmap.RowBytes);
}
///
@@ -112,22 +118,28 @@ namespace Avalonia.Skia
///
private class BitmapFramebuffer : ILockedFramebuffer
{
+ private WriteableBitmapImpl _parent;
private SKBitmap _bitmap;
///
/// Create framebuffer from given bitmap.
///
/// Bitmap.
- public BitmapFramebuffer(SKBitmap bitmap)
+ public BitmapFramebuffer(WriteableBitmapImpl parent, SKBitmap bitmap)
{
+ _parent = parent;
_bitmap = bitmap;
+ Monitor.Enter(parent._lock);
}
///
public void Dispose()
{
_bitmap.NotifyPixelsChanged();
+ _parent.Version++;
+ Monitor.Exit(_parent._lock);
_bitmap = null;
+ _parent = null;
}
///
@@ -149,4 +161,4 @@ namespace Avalonia.Skia
public PixelFormat Format => _bitmap.ColorType.ToPixelFormat();
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/BitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/BitmapImpl.cs
index 30af01283a..265f6f205a 100644
--- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/BitmapImpl.cs
+++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/BitmapImpl.cs
@@ -9,6 +9,7 @@ namespace Avalonia.Direct2D1.Media
{
public abstract int PixelWidth { get; }
public abstract int PixelHeight { get; }
+ public int Version { get; protected set; } = 1;
public abstract OptionalDispose GetDirect2DBitmap(SharpDX.Direct2D1.RenderTarget target);
diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs
index 3646d9e9e1..bb43256657 100644
--- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs
+++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/D2DRenderTargetBitmapImpl.cs
@@ -35,7 +35,7 @@ namespace Avalonia.Direct2D1.Media.Imaging
public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer)
{
- return new DrawingContextImpl(visualBrushRenderer, this, _renderTarget);
+ return new DrawingContextImpl(visualBrushRenderer, this, _renderTarget, null, () => Version++);
}
public IRenderTargetBitmapImpl CreateLayer(Size size)
diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicRenderTargetBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicRenderTargetBitmapImpl.cs
index aa8b3ead42..d620266086 100644
--- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicRenderTargetBitmapImpl.cs
+++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WicRenderTargetBitmapImpl.cs
@@ -44,7 +44,11 @@ namespace Avalonia.Direct2D1.Media
public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer, Action finishedCallback)
{
- return new DrawingContextImpl(visualBrushRenderer, null, _renderTarget, finishedCallback: finishedCallback);
+ return new DrawingContextImpl(visualBrushRenderer, null, _renderTarget, finishedCallback: () =>
+ {
+ Version++;
+ finishedCallback?.Invoke();
+ });
}
}
}
diff --git a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WriteableWicBitmapImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WriteableWicBitmapImpl.cs
index 5ca78ef278..7930266f75 100644
--- a/src/Windows/Avalonia.Direct2D1/Media/Imaging/WriteableWicBitmapImpl.cs
+++ b/src/Windows/Avalonia.Direct2D1/Media/Imaging/WriteableWicBitmapImpl.cs
@@ -17,11 +17,13 @@ namespace Avalonia.Direct2D1.Media.Imaging
class LockedBitmap : ILockedFramebuffer
{
+ private readonly WriteableWicBitmapImpl _parent;
private readonly BitmapLock _lock;
private readonly PixelFormat _format;
- public LockedBitmap(BitmapLock l, PixelFormat format)
+ public LockedBitmap(WriteableWicBitmapImpl parent, BitmapLock l, PixelFormat format)
{
+ _parent = parent;
_lock = l;
_format = format;
}
@@ -30,6 +32,7 @@ namespace Avalonia.Direct2D1.Media.Imaging
public void Dispose()
{
_lock.Dispose();
+ _parent.Version++;
}
public IntPtr Address => _lock.Data.DataPointer;
@@ -41,6 +44,7 @@ namespace Avalonia.Direct2D1.Media.Imaging
}
- public ILockedFramebuffer Lock() => new LockedBitmap(WicImpl.Lock(BitmapLockFlags.Write), PixelFormat.Value);
+ public ILockedFramebuffer Lock() =>
+ new LockedBitmap(this, WicImpl.Lock(BitmapLockFlags.Write), PixelFormat.Value);
}
}