diff --git a/src/ImageSharp/Image.WrapMemory.cs b/src/ImageSharp/Image.WrapMemory.cs
new file mode 100644
index 000000000..5abc4e132
--- /dev/null
+++ b/src/ImageSharp/Image.WrapMemory.cs
@@ -0,0 +1,59 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using SixLabors.ImageSharp.Memory;
+using SixLabors.ImageSharp.MetaData;
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp
+{
+ ///
+ /// Adds static methods allowing wrapping an existing memory area as an image.
+ ///
+ public static partial class Image
+ {
+ // TODO: This is a WIP API, should be public when finished.
+
+ ///
+ /// Wraps an existing contigous memory area of 'width'x'height' pixels,
+ /// 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
+ /// The
+ /// An instance
+ internal static Image WrapMemory(
+ Configuration config,
+ Memory pixelMemory,
+ int width,
+ int height,
+ ImageMetaData metaData)
+ where TPixel : struct, IPixel
+ {
+ var buffer = new ConsumedBuffer(pixelMemory);
+ return new Image(config, buffer, width, height, metaData);
+ }
+
+ ///
+ /// Wraps an existing contigous memory area of 'width'x'height' pixels,
+ /// allowing to view/manipulate it as an ImageSharp instance.
+ ///
+ /// The pixel type
+ /// The pixel memory
+ /// The width of the memory image
+ /// The height of the memory image
+ /// An instance
+ internal static Image WrapMemory(
+ Memory pixelMemory,
+ int width,
+ int height)
+ where TPixel : struct, IPixel
+ {
+ return WrapMemory(Configuration.Default, pixelMemory, width, height, new ImageMetaData());
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/ImageFrameCollection.cs b/src/ImageSharp/ImageFrameCollection.cs
index c101b48d3..181ffbce3 100644
--- a/src/ImageSharp/ImageFrameCollection.cs
+++ b/src/ImageSharp/ImageFrameCollection.cs
@@ -5,6 +5,7 @@ using System;
using System.Collections;
using System.Collections.Generic;
using SixLabors.ImageSharp.Advanced;
+using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp
@@ -29,6 +30,16 @@ namespace SixLabors.ImageSharp
this.frames.Add(new ImageFrame(parent.GetConfiguration(), width, height, backgroundColor));
}
+ internal ImageFrameCollection(Image parent, int width, int height, IBuffer consumedBuffer)
+ {
+ Guard.NotNull(parent, nameof(parent));
+
+ this.parent = parent;
+
+ // Frames are already cloned within the caller
+ this.frames.Add(new ImageFrame(parent.GetConfiguration(), width, height, consumedBuffer));
+ }
+
internal ImageFrameCollection(Image parent, IEnumerable> frames)
{
Guard.NotNull(parent, nameof(parent));
diff --git a/src/ImageSharp/ImageFrame{TPixel}.cs b/src/ImageSharp/ImageFrame{TPixel}.cs
index 328252344..a41a2bf3b 100644
--- a/src/ImageSharp/ImageFrame{TPixel}.cs
+++ b/src/ImageSharp/ImageFrame{TPixel}.cs
@@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp
/// The height of the image in pixels.
/// The meta data.
internal ImageFrame(Configuration configuration, int width, int height, ImageFrameMetaData metaData)
- : this(configuration, width, height, default, metaData)
+ : this(configuration, width, height, default(TPixel), metaData)
{
}
@@ -91,6 +91,35 @@ namespace SixLabors.ImageSharp
this.Clear(configuration.ParallelOptions, backgroundColor);
}
+ ///
+ /// Initializes a new instance of the class wrapping an existing buffer.
+ ///
+ internal ImageFrame(Configuration configuration, int width, int height, IBuffer consumedBuffer)
+ : this(configuration, width, height, consumedBuffer, new ImageFrameMetaData())
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class wrapping an existing buffer.
+ ///
+ internal ImageFrame(
+ Configuration configuration,
+ int width,
+ int height,
+ IBuffer consumedBuffer,
+ ImageFrameMetaData metaData)
+ {
+ Guard.NotNull(configuration, nameof(configuration));
+ Guard.MustBeGreaterThan(width, 0, nameof(width));
+ Guard.MustBeGreaterThan(height, 0, nameof(height));
+ Guard.NotNull(metaData, nameof(metaData));
+
+ this.configuration = configuration;
+ this.MemoryManager = configuration.MemoryManager;
+ this.PixelBuffer = new Buffer2D(consumedBuffer, width, height);
+ this.MetaData = metaData;
+ }
+
///
/// Initializes a new instance of the class.
///
diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs
index 324385601..2a95398e1 100644
--- a/src/ImageSharp/Image{TPixel}.cs
+++ b/src/ImageSharp/Image{TPixel}.cs
@@ -8,6 +8,7 @@ using System.Linq;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats;
+using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats;
@@ -78,7 +79,28 @@ namespace SixLabors.ImageSharp
this.configuration = configuration ?? Configuration.Default;
this.PixelType = new PixelTypeInfo(Unsafe.SizeOf() * 8);
this.MetaData = metadata ?? new ImageMetaData();
- this.frames = new ImageFrameCollection(this, width, height, default);
+ this.frames = new ImageFrameCollection(this, width, height, default(TPixel));
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// consuming an external buffer instance.
+ ///
+ internal Image(Configuration configuration, IBuffer consumedBuffer, int width, int height)
+ : this(configuration, consumedBuffer, width, height, new ImageMetaData())
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// consuming an external buffer instance.
+ ///
+ internal Image(Configuration configuration, IBuffer consumedBuffer, int width, int height, ImageMetaData metadata)
+ {
+ this.configuration = configuration;
+ this.PixelType = new PixelTypeInfo(Unsafe.SizeOf() * 8);
+ this.MetaData = metadata;
+ this.frames = new ImageFrameCollection(this, width, height, consumedBuffer);
}
///
diff --git a/src/ImageSharp/Memory/BasicArrayBuffer.cs b/src/ImageSharp/Memory/BasicArrayBuffer.cs
index 450399900..3b62f8a31 100644
--- a/src/ImageSharp/Memory/BasicArrayBuffer.cs
+++ b/src/ImageSharp/Memory/BasicArrayBuffer.cs
@@ -1,5 +1,7 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
using System;
-using System.Buffers;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Memory
diff --git a/src/ImageSharp/Memory/ConsumedBuffer.cs b/src/ImageSharp/Memory/ConsumedBuffer.cs
new file mode 100644
index 000000000..1f1bb76e4
--- /dev/null
+++ b/src/ImageSharp/Memory/ConsumedBuffer.cs
@@ -0,0 +1,32 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+
+namespace SixLabors.ImageSharp.Memory
+{
+ ///
+ /// A buffer implementation that consumes an existing instance.
+ /// The ownership of the memory remains external.
+ ///
+ /// The value type
+ internal sealed class ConsumedBuffer : IBuffer
+ where T : struct
+ {
+ public ConsumedBuffer(Memory memory)
+ {
+ this.Memory = memory;
+ }
+
+ public Memory Memory { get; }
+
+ public Span GetSpan()
+ {
+ return this.Memory.Span;
+ }
+
+ public void Dispose()
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs b/tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs
index 2825ddd77..3fe1380e4 100644
--- a/tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs
+++ b/tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs
@@ -9,7 +9,7 @@ using Xunit;
namespace SixLabors.ImageSharp.Tests.Advanced
{
-
+ using System.Buffers;
public class AdvancedImageExtensionsTests
{
@@ -39,6 +39,64 @@ namespace SixLabors.ImageSharp.Tests.Advanced
}
}
}
+
+ class TestMemoryManager : System.Buffers.MemoryManager
+ {
+ public TestMemoryManager(TPixel[] pixelArray)
+ {
+ this.PixelArray = pixelArray;
+ }
+
+ public TPixel[] PixelArray { get; }
+
+ protected override void Dispose(bool disposing)
+ {
+ }
+
+ public override Span GetSpan()
+ {
+ return this.PixelArray;
+ }
+
+ public override MemoryHandle Pin(int elementIndex = 0)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override void Unpin()
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ [Theory]
+ [WithSolidFilledImages(1, 1, "Red", PixelTypes.Rgba32 | PixelTypes.Bgr24)]
+ [WithTestPatternImages(131, 127, PixelTypes.Rgba32 | PixelTypes.Bgr24)]
+ public void WhenMemoryIsConsumed(TestImageProvider provider)
+ where TPixel : struct, IPixel
+ {
+ using (Image image0 = provider.GetImage())
+ {
+ var targetBuffer = new TPixel[image0.Width * image0.Height];
+ image0.GetPixelSpan().CopyTo(targetBuffer);
+
+ var managerOfExeternalMemory = new TestMemoryManager(targetBuffer);
+
+ Memory externalMemory = managerOfExeternalMemory.Memory;
+
+ using (Image image1 = Image.WrapMemory(externalMemory, image0.Width, image0.Height))
+ {
+ Memory internalMemory = image1.GetPixelMemory();
+ Assert.Equal(targetBuffer.Length, internalMemory.Length);
+ Assert.True(Unsafe.AreSame(ref targetBuffer[0], ref internalMemory.Span[0]));
+
+ image0.ComparePixelBufferTo(internalMemory.Span);
+ }
+
+ // Make sure externalMemory works after destruction:
+ image0.ComparePixelBufferTo(externalMemory.Span);
+ }
+ }
}
[Theory]
diff --git a/tests/ImageSharp.Tests/Image/ImageFramesCollectionTests.cs b/tests/ImageSharp.Tests/Image/ImageFramesCollectionTests.cs
index a26d88720..392397057 100644
--- a/tests/ImageSharp.Tests/Image/ImageFramesCollectionTests.cs
+++ b/tests/ImageSharp.Tests/Image/ImageFramesCollectionTests.cs
@@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Tests
System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("en-US");
this.image = new Image(10, 10);
- this.collection = new ImageFrameCollection(this.image, 10, 10, default);
+ this.collection = new ImageFrameCollection(this.image, 10, 10, default(Rgba32));
}
[Fact]