Browse Source

Merge pull request #1610 from ptasev/pt/mem-manager-byte

Added overloads to `Image.WrapMemory` for `IMemoryOwner<byte>`.
pull/1614/head
James Jackson-South 5 years ago
committed by GitHub
parent
commit
c5c4dd209b
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 76
      src/ImageSharp/Image.WrapMemory.cs
  2. 54
      src/ImageSharp/Memory/ByteMemoryOwner{T}.cs
  3. 69
      tests/ImageSharp.Tests/Image/ImageTests.WrapMemory.cs

76
src/ImageSharp/Image.WrapMemory.cs

@ -303,6 +303,82 @@ namespace SixLabors.ImageSharp
where TPixel : unmanaged, IPixel<TPixel>
=> WrapMemory<TPixel>(Configuration.Default, byteMemory, width, height);
/// <summary>
/// Wraps an existing contiguous memory area of at least 'width' x 'height' pixels,
/// allowing to view/manipulate it as an <see cref="Image{TPixel}"/> instance.
/// The ownership of the <paramref name="byteMemoryOwner"/> is being transferred to the new <see cref="Image{TPixel}"/> instance,
/// meaning that the caller is not allowed to dispose <paramref name="byteMemoryOwner"/>.
/// It will be disposed together with the result image.
/// </summary>
/// <typeparam name="TPixel">The pixel type</typeparam>
/// <param name="configuration">The <see cref="Configuration"/></param>
/// <param name="byteMemoryOwner">The <see cref="IMemoryOwner{T}"/> that is being transferred 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>
/// <exception cref="ArgumentNullException">The configuration is null.</exception>
/// <exception cref="ArgumentNullException">The metadata is null.</exception>
/// <returns>An <see cref="Image{TPixel}"/> instance</returns>
public static Image<TPixel> WrapMemory<TPixel>(
Configuration configuration,
IMemoryOwner<byte> byteMemoryOwner,
int width,
int height,
ImageMetadata metadata)
where TPixel : unmanaged, IPixel<TPixel>
{
Guard.NotNull(configuration, nameof(configuration));
Guard.NotNull(metadata, nameof(metadata));
var pixelMemoryOwner = new ByteMemoryOwner<TPixel>(byteMemoryOwner);
Guard.IsTrue(pixelMemoryOwner.Memory.Length >= (long)width * height, nameof(pixelMemoryOwner), "The length of the input memory is less than the specified image size");
var memorySource = MemoryGroup<TPixel>.Wrap(pixelMemoryOwner);
return new Image<TPixel>(configuration, memorySource, width, height, metadata);
}
/// <summary>
/// Wraps an existing contiguous memory area of at least 'width' x 'height' pixels,
/// allowing to view/manipulate it as an <see cref="Image{TPixel}"/> instance.
/// The ownership of the <paramref name="byteMemoryOwner"/> is being transferred to the new <see cref="Image{TPixel}"/> instance,
/// meaning that the caller is not allowed to dispose <paramref name="byteMemoryOwner"/>.
/// It will be disposed together with the result image.
/// </summary>
/// <typeparam name="TPixel">The pixel type.</typeparam>
/// <param name="configuration">The <see cref="Configuration"/></param>
/// <param name="byteMemoryOwner">The <see cref="IMemoryOwner{T}"/> that is being transferred to the image.</param>
/// <param name="width">The width of the memory image.</param>
/// <param name="height">The height of the memory image.</param>
/// <exception cref="ArgumentNullException">The configuration is null.</exception>
/// <returns>An <see cref="Image{TPixel}"/> instance</returns>
public static Image<TPixel> WrapMemory<TPixel>(
Configuration configuration,
IMemoryOwner<byte> byteMemoryOwner,
int width,
int height)
where TPixel : unmanaged, IPixel<TPixel>
=> WrapMemory<TPixel>(configuration, byteMemoryOwner, width, height, new ImageMetadata());
/// <summary>
/// Wraps an existing contiguous memory area of at least 'width' x 'height' pixels,
/// allowing to view/manipulate it as an <see cref="Image{TPixel}"/> instance.
/// The ownership of the <paramref name="byteMemoryOwner"/> is being transferred to the new <see cref="Image{TPixel}"/> instance,
/// meaning that the caller is not allowed to dispose <paramref name="byteMemoryOwner"/>.
/// It will be disposed together with the result image.
/// </summary>
/// <typeparam name="TPixel">The pixel type</typeparam>
/// <param name="byteMemoryOwner">The <see cref="IMemoryOwner{T}"/> that is being transferred 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<byte> byteMemoryOwner,
int width,
int height)
where TPixel : unmanaged, IPixel<TPixel>
=> WrapMemory<TPixel>(Configuration.Default, byteMemoryOwner, width, height);
/// <summary>
/// <para>
/// Wraps an existing contiguous memory area of at least 'width' x 'height' pixels allowing viewing/manipulation as

54
src/ImageSharp/Memory/ByteMemoryOwner{T}.cs

@ -0,0 +1,54 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
namespace SixLabors.ImageSharp.Memory
{
/// <summary>
/// A custom <see cref="IMemoryOwner{T}"/> that can wrap <see cref="IMemoryOwner{T}"/> of <see cref="byte"/> instances
/// and cast them to be <see cref="IMemoryOwner{T}"/> for any arbitrary unmanaged <typeparamref name="T"/> value type.
/// </summary>
/// <typeparam name="T">The value type to use when casting the wrapped <see cref="IMemoryOwner{T}"/> instance.</typeparam>
internal sealed class ByteMemoryOwner<T> : IMemoryOwner<T>
where T : unmanaged
{
private readonly IMemoryOwner<byte> memoryOwner;
private readonly ByteMemoryManager<T> memoryManager;
private bool disposedValue;
/// <summary>
/// Initializes a new instance of the <see cref="ByteMemoryOwner{T}"/> class.
/// </summary>
/// <param name="memoryOwner">The <see cref="IMemoryOwner{T}"/> of <see cref="byte"/> instance to wrap.</param>
public ByteMemoryOwner(IMemoryOwner<byte> memoryOwner)
{
this.memoryOwner = memoryOwner;
this.memoryManager = new ByteMemoryManager<T>(memoryOwner.Memory);
}
/// <inheritdoc/>
public Memory<T> Memory => this.memoryManager.Memory;
private void Dispose(bool disposing)
{
if (!this.disposedValue)
{
if (disposing)
{
this.memoryOwner.Dispose();
}
this.disposedValue = true;
}
}
/// <inheritdoc/>
public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
this.Dispose(disposing: true);
}
}
}

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

@ -380,11 +380,11 @@ namespace SixLabors.ImageSharp.Tests
private class TestMemoryOwner<T> : IMemoryOwner<T>
{
public bool Disposed { get; private set; }
public Memory<T> Memory { get; set; }
public void Dispose()
{
}
public void Dispose() => this.Disposed = true;
}
[Theory]
@ -410,7 +410,68 @@ namespace SixLabors.ImageSharp.Tests
var array = new Rgba32[size];
var memory = new TestMemoryOwner<Rgba32> { Memory = array };
Image.WrapMemory(memory, height, width);
using (var img = Image.WrapMemory<Rgba32>(memory, width, height))
{
Assert.Equal(width, img.Width);
Assert.Equal(height, img.Height);
for (int i = 0; i < height; ++i)
{
var arrayIndex = width * i;
Span<Rgba32> rowSpan = img.GetPixelRowSpan(i);
ref Rgba32 r0 = ref rowSpan[0];
ref Rgba32 r1 = ref array[arrayIndex];
Assert.True(Unsafe.AreSame(ref r0, ref r1));
}
}
Assert.True(memory.Disposed);
}
[Theory]
[InlineData(0, 5, 5)]
[InlineData(20, 5, 5)]
[InlineData(1023, 32, 32)]
public void WrapMemory_IMemoryOwnerOfByte_InvalidSize(int size, int height, int width)
{
var array = new byte[size * Unsafe.SizeOf<Rgba32>()];
var memory = new TestMemoryOwner<byte> { Memory = array };
Assert.Throws<ArgumentException>(() => Image.WrapMemory<Rgba32>(memory, height, width));
}
[Theory]
[InlineData(25, 5, 5)]
[InlineData(26, 5, 5)]
[InlineData(2, 1, 1)]
[InlineData(1024, 32, 32)]
[InlineData(2048, 32, 32)]
public void WrapMemory_IMemoryOwnerOfByte_ValidSize(int size, int height, int width)
{
var pixelSize = Unsafe.SizeOf<Rgba32>();
var array = new byte[size * pixelSize];
var memory = new TestMemoryOwner<byte> { Memory = array };
using (var img = Image.WrapMemory<Rgba32>(memory, width, height))
{
Assert.Equal(width, img.Width);
Assert.Equal(height, img.Height);
for (int i = 0; i < height; ++i)
{
var arrayIndex = pixelSize * width * i;
Span<Rgba32> rowSpan = img.GetPixelRowSpan(i);
ref Rgba32 r0 = ref rowSpan[0];
ref Rgba32 r1 = ref Unsafe.As<byte, Rgba32>(ref array[arrayIndex]);
Assert.True(Unsafe.AreSame(ref r0, ref r1));
}
}
Assert.True(memory.Disposed);
}
[Theory]

Loading…
Cancel
Save