Browse Source

WrapMemory<T>(IMemoryOwner<T>) + additional System.Drawing test case

af/merge-core
Anton Firszov 8 years ago
parent
commit
f3d41f71a3
  1. 95
      src/ImageSharp/Image.WrapMemory.cs
  2. 1
      src/ImageSharp/Memory/MemorySource.cs
  3. 79
      tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs

95
src/ImageSharp/Image.WrapMemory.cs

@ -2,6 +2,8 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Buffers;
using SixLabors.ImageSharp.MetaData; using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory; using SixLabors.Memory;
@ -41,6 +43,27 @@ namespace SixLabors.ImageSharp
/// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance. /// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel type</typeparam> /// <typeparam name="TPixel">The pixel type</typeparam>
/// <param name="config">The <see cref="Configuration"/></param>
/// <param name="pixelMemory">The pixel memory</param>
/// <param name="width">The width of the memory image</param>
/// <param name="height">The height of the memory image</param>
/// <returns>An <see cref="Image{TPixel}"/> instance</returns>
public static Image<TPixel> WrapMemory<TPixel>(
Configuration config,
Memory<TPixel> pixelMemory,
int width,
int height)
where TPixel : struct, IPixel<TPixel>
{
return WrapMemory(config, pixelMemory, width, height, new ImageMetaData());
}
/// <summary>
/// Wraps an existing contigous memory area of 'width'x'height' pixels,
/// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance.
/// The memory is being observed, the caller remains responsible for managing it's lifecycle.
/// </summary>
/// <typeparam name="TPixel">The pixel type</typeparam>
/// <param name="pixelMemory">The pixel memory</param> /// <param name="pixelMemory">The pixel memory</param>
/// <param name="width">The width of the memory image</param> /// <param name="width">The width of the memory image</param>
/// <param name="height">The height of the memory image</param> /// <param name="height">The height of the memory image</param>
@ -51,7 +74,77 @@ namespace SixLabors.ImageSharp
int height) int height)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
return WrapMemory(Configuration.Default, pixelMemory, width, height, new ImageMetaData()); return WrapMemory(Configuration.Default, pixelMemory, width, height);
}
/// <summary>
/// Wraps an existing contigous memory area of 'width'x'height' pixels,
/// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance.
/// The ownership of the <paramref name="pixelMemoryOwner"/> is being transfered to the new <see cref="Image{TPixel}"/> instance,
/// meaning that the caller is not allowed to dispose <paramref name="pixelMemoryOwner"/>.
/// It will be disposed together with the result image.
/// </summary>
/// <typeparam name="TPixel">The pixel type</typeparam>
/// <param name="config">The <see cref="Configuration"/></param>
/// <param name="pixelMemoryOwner">The <see cref="IMemoryOwner{T}"/> that is being transfered to the image</param>
/// <param name="width">The width of the memory image</param>
/// <param name="height">The height of the memory image</param>
/// <param name="metaData">The <see cref="ImageMetaData"/></param>
/// <returns>An <see cref="Image{TPixel}"/> instance</returns>
public static Image<TPixel> WrapMemory<TPixel>(
Configuration config,
IMemoryOwner<TPixel> pixelMemoryOwner,
int width,
int height,
ImageMetaData metaData)
where TPixel : struct, IPixel<TPixel>
{
var memorySource = new MemorySource<TPixel>(pixelMemoryOwner, false);
return new Image<TPixel>(config, memorySource, width, height, metaData);
}
/// <summary>
/// Wraps an existing contigous memory area of 'width'x'height' pixels,
/// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance.
/// The ownership of the <paramref name="pixelMemoryOwner"/> is being transfered to the new <see cref="Image{TPixel}"/> instance,
/// meaning that the caller is not allowed to dispose <paramref name="pixelMemoryOwner"/>.
/// It will be disposed together with the result image.
/// </summary>
/// <typeparam name="TPixel">The pixel type</typeparam>
/// <param name="config">The <see cref="Configuration"/></param>
/// <param name="pixelMemoryOwner">The <see cref="IMemoryOwner{T}"/> that is being transfered to the image</param>
/// <param name="width">The width of the memory image</param>
/// <param name="height">The height of the memory image</param>
/// <returns>An <see cref="Image{TPixel}"/> instance</returns>
public static Image<TPixel> WrapMemory<TPixel>(
Configuration config,
IMemoryOwner<TPixel> pixelMemoryOwner,
int width,
int height)
where TPixel : struct, IPixel<TPixel>
{
return WrapMemory(config, pixelMemoryOwner, width, height, new ImageMetaData());
}
/// <summary>
/// Wraps an existing contigous memory area of 'width'x'height' pixels,
/// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance.
/// The ownership of the <paramref name="pixelMemoryOwner"/> is being transfered to the new <see cref="Image{TPixel}"/> instance,
/// meaning that the caller is not allowed to dispose <paramref name="pixelMemoryOwner"/>.
/// It will be disposed together with the result image.
/// </summary>
/// <typeparam name="TPixel">The pixel type</typeparam>
/// <param name="pixelMemoryOwner">The <see cref="IMemoryOwner{T}"/> that is being transfered to the image</param>
/// <param name="width">The width of the memory image</param>
/// <param name="height">The height of the memory image</param>
/// <returns>An <see cref="Image{TPixel}"/> instance</returns>
public static Image<TPixel> WrapMemory<TPixel>(
IMemoryOwner<TPixel> pixelMemoryOwner,
int width,
int height)
where TPixel : struct, IPixel<TPixel>
{
return WrapMemory(Configuration.Default, pixelMemoryOwner, width, height);
} }
} }
} }

1
src/ImageSharp/Memory/MemorySource.cs

@ -8,6 +8,7 @@ namespace SixLabors.Memory
{ {
/// <summary> /// <summary>
/// Holds a <see cref="System.Memory{T}"/> that is either OWNED or CONSUMED. /// Holds a <see cref="System.Memory{T}"/> that is either OWNED or CONSUMED.
/// When the memory is being owned, the <see cref="IMemoryOwner{T}"/> instance is also known.
/// Implements content transfer logic in <see cref="SwapOrCopyContent"/> that depends on the ownership status. /// Implements content transfer logic in <see cref="SwapOrCopyContent"/> that depends on the ownership status.
/// This is needed to transfer the contents of a temporary <see cref="Buffer2D{T}"/> /// This is needed to transfer the contents of a temporary <see cref="Buffer2D{T}"/>
/// to a persistent <see cref="SixLabors.ImageSharp.ImageFrame{T}.PixelBuffer"/> without copying the buffer. /// to a persistent <see cref="SixLabors.ImageSharp.ImageFrame{T}.PixelBuffer"/> without copying the buffer.

79
tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs

@ -5,9 +5,11 @@ using System;
using System.Buffers; using System.Buffers;
using System.Drawing; using System.Drawing;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
using SixLabors.Shapes; using SixLabors.Shapes;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
using Xunit; using Xunit;
@ -19,13 +21,17 @@ namespace SixLabors.ImageSharp.Tests
{ {
public class WrapMemory public class WrapMemory
{ {
/// <summary>
/// A <see cref="MemoryManager{T}"/> exposing the locked pixel memory of a <see cref="Bitmap"/> instance.
/// TODO: This should be an example in https://github.com/SixLabors/Samples
/// </summary>
class BitmapMemoryManager : MemoryManager<Bgra32> class BitmapMemoryManager : MemoryManager<Bgra32>
{ {
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) public BitmapMemoryManager(Bitmap bitmap)
{ {
@ -40,9 +46,21 @@ namespace SixLabors.ImageSharp.Tests
this.length = bitmap.Width * bitmap.Height; this.length = bitmap.Width * bitmap.Height;
} }
public bool IsDisposed { get; private set; }
protected override void Dispose(bool disposing) 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<Bgra32> GetSpan() public override unsafe Span<Bgra32> GetSpan()
@ -63,7 +81,26 @@ namespace SixLabors.ImageSharp.Tests
} }
[Fact] [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<Rgba32>(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)) 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)) 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))); image.Mutate(c => c.Fill(bg).Fill(fg, new RectangularPolygon(10, 10, 10, 10)));
} }
Assert.False(memoryManager.IsDisposed);
} }
string fn = System.IO.Path.Combine( string fn = System.IO.Path.Combine(
TestEnvironment.ActualOutputDirectoryFullPath, 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<Bgra32>.Red;
Bgra32 fg = NamedColors<Bgra32>.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); bmp.Save(fn, ImageFormat.Bmp);
} }

Loading…
Cancel
Save