diff --git a/src/ImageSharp/Image.WrapMemory.cs b/src/ImageSharp/Image.WrapMemory.cs
index c24a932e1..77432c3ad 100644
--- a/src/ImageSharp/Image.WrapMemory.cs
+++ b/src/ImageSharp/Image.WrapMemory.cs
@@ -2,6 +2,8 @@
// Licensed under the Apache License, Version 2.0.
using System;
+using System.Buffers;
+
using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
@@ -41,6 +43,27 @@ namespace SixLabors.ImageSharp
/// allowing to view/manipulate it as an ImageSharp instance.
///
/// The pixel type
+ /// The
+ /// The pixel memory
+ /// The width of the memory image
+ /// The height of the memory image
+ /// An instance
+ public static Image WrapMemory(
+ Configuration config,
+ Memory pixelMemory,
+ int width,
+ int height)
+ where TPixel : struct, IPixel
+ {
+ return WrapMemory(config, pixelMemory, width, height, new ImageMetaData());
+ }
+
+ ///
+ /// Wraps an existing contigous memory area of 'width'x'height' pixels,
+ /// allowing to view/manipulate it as an ImageSharp instance.
+ /// The memory is being observed, the caller remains responsible for managing it's lifecycle.
+ ///
+ /// The pixel type
/// The pixel memory
/// The width of the memory image
/// The height of the memory image
@@ -51,7 +74,77 @@ namespace SixLabors.ImageSharp
int height)
where TPixel : struct, IPixel
{
- return WrapMemory(Configuration.Default, pixelMemory, width, height, new ImageMetaData());
+ return WrapMemory(Configuration.Default, pixelMemory, width, height);
+ }
+
+ ///
+ /// Wraps an existing contigous memory area of 'width'x'height' pixels,
+ /// allowing to view/manipulate it as an ImageSharp instance.
+ /// The ownership of the is being transfered to the new instance,
+ /// meaning that the caller is not allowed to dispose .
+ /// It will be disposed together with the result image.
+ ///
+ /// The pixel type
+ /// The
+ /// The that is being transfered to the image
+ /// The width of the memory image
+ /// The height of the memory image
+ /// The
+ /// An instance
+ public static Image WrapMemory(
+ Configuration config,
+ IMemoryOwner pixelMemoryOwner,
+ int width,
+ int height,
+ ImageMetaData metaData)
+ where TPixel : struct, IPixel
+ {
+ var memorySource = new MemorySource(pixelMemoryOwner, false);
+ return new Image(config, memorySource, width, height, metaData);
+ }
+
+ ///
+ /// Wraps an existing contigous memory area of 'width'x'height' pixels,
+ /// allowing to view/manipulate it as an ImageSharp instance.
+ /// The ownership of the is being transfered to the new instance,
+ /// meaning that the caller is not allowed to dispose .
+ /// It will be disposed together with the result image.
+ ///
+ /// The pixel type
+ /// The
+ /// The that is being transfered to the image
+ /// The width of the memory image
+ /// The height of the memory image
+ /// An instance
+ public static Image WrapMemory(
+ Configuration config,
+ IMemoryOwner pixelMemoryOwner,
+ int width,
+ int height)
+ where TPixel : struct, IPixel
+ {
+ return WrapMemory(config, pixelMemoryOwner, width, height, new ImageMetaData());
+ }
+
+ ///
+ /// Wraps an existing contigous memory area of 'width'x'height' pixels,
+ /// allowing to view/manipulate it as an ImageSharp instance.
+ /// The ownership of the is being transfered to the new instance,
+ /// meaning that the caller is not allowed to dispose .
+ /// It will be disposed together with the result image.
+ ///
+ /// The pixel type
+ /// The that is being transfered to the image
+ /// The width of the memory image
+ /// The height of the memory image
+ /// An instance
+ public static Image WrapMemory(
+ IMemoryOwner pixelMemoryOwner,
+ int width,
+ int height)
+ where TPixel : struct, IPixel
+ {
+ return WrapMemory(Configuration.Default, pixelMemoryOwner, width, height);
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Memory/MemorySource.cs b/src/ImageSharp/Memory/MemorySource.cs
index 27bca11c1..4253307ef 100644
--- a/src/ImageSharp/Memory/MemorySource.cs
+++ b/src/ImageSharp/Memory/MemorySource.cs
@@ -8,6 +8,7 @@ namespace SixLabors.Memory
{
///
/// Holds a that is either OWNED or CONSUMED.
+ /// When the memory is being owned, the instance is also known.
/// Implements content transfer logic in that depends on the ownership status.
/// This is needed to transfer the contents of a temporary
/// to a persistent without copying the buffer.
diff --git a/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs b/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs
index 57f757176..815684d84 100644
--- a/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs
+++ b/tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs
@@ -5,9 +5,11 @@ using System;
using System.Buffers;
using System.Drawing;
using System.Drawing.Imaging;
+using System.Runtime.CompilerServices;
+using SixLabors.ImageSharp.Advanced;
+using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats;
-using SixLabors.Memory;
using SixLabors.Shapes;
using SixLabors.ImageSharp.Processing;
using Xunit;
@@ -19,13 +21,17 @@ namespace SixLabors.ImageSharp.Tests
{
public class WrapMemory
{
+ ///
+ /// A exposing the locked pixel memory of a instance.
+ /// TODO: This should be an example in https://github.com/SixLabors/Samples
+ ///
class BitmapMemoryManager : MemoryManager
{
- private System.Drawing.Bitmap bitmap;
+ private readonly Bitmap bitmap;
- private BitmapData bmpData;
+ private readonly BitmapData bmpData;
- private int length;
+ private readonly int length;
public BitmapMemoryManager(Bitmap bitmap)
{
@@ -40,9 +46,21 @@ namespace SixLabors.ImageSharp.Tests
this.length = bitmap.Width * bitmap.Height;
}
+ public bool IsDisposed { get; private set; }
+
protected override void Dispose(bool disposing)
{
- this.bitmap.UnlockBits(this.bmpData);
+ if (this.IsDisposed)
+ {
+ return;
+ }
+
+ if (disposing)
+ {
+ this.bitmap.UnlockBits(this.bmpData);
+ }
+
+ this.IsDisposed = true;
}
public override unsafe Span GetSpan()
@@ -63,7 +81,26 @@ namespace SixLabors.ImageSharp.Tests
}
[Fact]
- public void WrapSystemDrawingBitmap()
+ public void WrapMemory_CreatedImageIsCorrect()
+ {
+ Configuration cfg = Configuration.Default.ShallowCopy();
+ var metaData = new ImageMetaData();
+
+ var array = new Rgba32[25];
+ var memory = new Memory(array);
+
+ using (var image = Image.WrapMemory(cfg, memory, 5, 5, metaData))
+ {
+ ref Rgba32 pixel0 = ref image.GetPixelSpan()[0];
+ Assert.True(Unsafe.AreSame(ref array[0], ref pixel0));
+
+ Assert.Equal(cfg, image.GetConfiguration());
+ Assert.Equal(metaData, image.MetaData);
+ }
+ }
+
+ [Fact]
+ public void WrapSystemDrawingBitmap_WhenObserved()
{
using (var bmp = new Bitmap(51, 23))
{
@@ -75,13 +112,41 @@ namespace SixLabors.ImageSharp.Tests
using (var image = Image.WrapMemory(memory, bmp.Width, bmp.Height))
{
+ Assert.Equal(memory, image.GetPixelMemory());
image.Mutate(c => c.Fill(bg).Fill(fg, new RectangularPolygon(10, 10, 10, 10)));
}
+
+ Assert.False(memoryManager.IsDisposed);
}
string fn = System.IO.Path.Combine(
TestEnvironment.ActualOutputDirectoryFullPath,
- "WrapSystemDrawingBitmap.bmp");
+ $"{nameof(this.WrapSystemDrawingBitmap_WhenObserved)}.bmp");
+
+ bmp.Save(fn, ImageFormat.Bmp);
+ }
+ }
+
+ [Fact]
+ public void WrapSystemDrawingBitmap_WhenOwned()
+ {
+ using (var bmp = new Bitmap(51, 23))
+ {
+ var memoryManager = new BitmapMemoryManager(bmp);
+ Bgra32 bg = NamedColors.Red;
+ Bgra32 fg = NamedColors.Green;
+
+ using (var image = Image.WrapMemory(memoryManager, bmp.Width, bmp.Height))
+ {
+ Assert.Equal(memoryManager.Memory, image.GetPixelMemory());
+ image.Mutate(c => c.Fill(bg).Fill(fg, new RectangularPolygon(10, 10, 10, 10)));
+ }
+
+ Assert.True(memoryManager.IsDisposed);
+
+ string fn = System.IO.Path.Combine(
+ TestEnvironment.ActualOutputDirectoryFullPath,
+ $"{nameof(this.WrapSystemDrawingBitmap_WhenOwned)}.bmp");
bmp.Save(fn, ImageFormat.Bmp);
}