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.
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 <see cref="Image{TPixel}"/> instance.
/// </summary>
/// <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="width">The width of the memory image</param>
/// <param name="height">The height of the memory image</param>
@ -51,7 +74,77 @@ namespace SixLabors.ImageSharp
int height)
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>
/// 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.
/// 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.

79
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
{
/// <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>
{
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<Bgra32> 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<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))
{
@ -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<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);
}

Loading…
Cancel
Save