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