Browse Source

Image.WrapMemory works

af/merge-core
Anton Firszov 8 years ago
parent
commit
5e428ed458
  1. 59
      src/ImageSharp/Image.WrapMemory.cs
  2. 11
      src/ImageSharp/ImageFrameCollection.cs
  3. 31
      src/ImageSharp/ImageFrame{TPixel}.cs
  4. 24
      src/ImageSharp/Image{TPixel}.cs
  5. 4
      src/ImageSharp/Memory/BasicArrayBuffer.cs
  6. 32
      src/ImageSharp/Memory/ConsumedBuffer.cs
  7. 60
      tests/ImageSharp.Tests/Advanced/AdvancedImageExtensionsTests.cs
  8. 2
      tests/ImageSharp.Tests/Image/ImageFramesCollectionTests.cs

59
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
{
/// <content>
/// Adds static methods allowing wrapping an existing memory area as an image.
/// </content>
public static partial class Image
{
// TODO: This is a WIP API, should be public when finished.
/// <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.
/// </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>
/// <param name="metaData">The <see cref="ImageMetaData"/></param>
/// <returns>An <see cref="Image{TPixel}"/> instance</returns>
internal static Image<TPixel> WrapMemory<TPixel>(
Configuration config,
Memory<TPixel> pixelMemory,
int width,
int height,
ImageMetaData metaData)
where TPixel : struct, IPixel<TPixel>
{
var buffer = new ConsumedBuffer<TPixel>(pixelMemory);
return new Image<TPixel>(config, buffer, 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.
/// </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>
/// <returns>An <see cref="Image{TPixel}"/> instance</returns>
internal static Image<TPixel> WrapMemory<TPixel>(
Memory<TPixel> pixelMemory,
int width,
int height)
where TPixel : struct, IPixel<TPixel>
{
return WrapMemory(Configuration.Default, pixelMemory, width, height, new ImageMetaData());
}
}
}

11
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<TPixel>(parent.GetConfiguration(), width, height, backgroundColor));
}
internal ImageFrameCollection(Image<TPixel> parent, int width, int height, IBuffer<TPixel> consumedBuffer)
{
Guard.NotNull(parent, nameof(parent));
this.parent = parent;
// Frames are already cloned within the caller
this.frames.Add(new ImageFrame<TPixel>(parent.GetConfiguration(), width, height, consumedBuffer));
}
internal ImageFrameCollection(Image<TPixel> parent, IEnumerable<ImageFrame<TPixel>> frames)
{
Guard.NotNull(parent, nameof(parent));

31
src/ImageSharp/ImageFrame{TPixel}.cs

@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp
/// <param name="height">The height of the image in pixels.</param>
/// <param name="metaData">The meta data.</param>
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);
}
/// <summary>
/// Initializes a new instance of the <see cref="ImageFrame{TPixel}" /> class wrapping an existing buffer.
/// </summary>
internal ImageFrame(Configuration configuration, int width, int height, IBuffer<TPixel> consumedBuffer)
: this(configuration, width, height, consumedBuffer, new ImageFrameMetaData())
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ImageFrame{TPixel}" /> class wrapping an existing buffer.
/// </summary>
internal ImageFrame(
Configuration configuration,
int width,
int height,
IBuffer<TPixel> 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<TPixel>(consumedBuffer, width, height);
this.MetaData = metaData;
}
/// <summary>
/// Initializes a new instance of the <see cref="ImageFrame{TPixel}" /> class.
/// </summary>

24
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<TPixel>() * 8);
this.MetaData = metadata ?? new ImageMetaData();
this.frames = new ImageFrameCollection<TPixel>(this, width, height, default);
this.frames = new ImageFrameCollection<TPixel>(this, width, height, default(TPixel));
}
/// <summary>
/// Initializes a new instance of the <see cref="Image{TPixel}"/> class
/// consuming an external buffer instance.
/// </summary>
internal Image(Configuration configuration, IBuffer<TPixel> consumedBuffer, int width, int height)
: this(configuration, consumedBuffer, width, height, new ImageMetaData())
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Image{TPixel}"/> class
/// consuming an external buffer instance.
/// </summary>
internal Image(Configuration configuration, IBuffer<TPixel> consumedBuffer, int width, int height, ImageMetaData metadata)
{
this.configuration = configuration;
this.PixelType = new PixelTypeInfo(Unsafe.SizeOf<TPixel>() * 8);
this.MetaData = metadata;
this.frames = new ImageFrameCollection<TPixel>(this, width, height, consumedBuffer);
}
/// <summary>

4
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

32
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
{
/// <summary>
/// A buffer implementation that consumes an existing <see cref="Memory{T}"/> instance.
/// The ownership of the memory remains external.
/// </summary>
/// <typeparam name="T">The value type</typeparam>
internal sealed class ConsumedBuffer<T> : IBuffer<T>
where T : struct
{
public ConsumedBuffer(Memory<T> memory)
{
this.Memory = memory;
}
public Memory<T> Memory { get; }
public Span<T> GetSpan()
{
return this.Memory.Span;
}
public void Dispose()
{
}
}
}

60
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<TPixel> : System.Buffers.MemoryManager<TPixel>
{
public TestMemoryManager(TPixel[] pixelArray)
{
this.PixelArray = pixelArray;
}
public TPixel[] PixelArray { get; }
protected override void Dispose(bool disposing)
{
}
public override Span<TPixel> 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<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> image0 = provider.GetImage())
{
var targetBuffer = new TPixel[image0.Width * image0.Height];
image0.GetPixelSpan().CopyTo(targetBuffer);
var managerOfExeternalMemory = new TestMemoryManager<TPixel>(targetBuffer);
Memory<TPixel> externalMemory = managerOfExeternalMemory.Memory;
using (Image<TPixel> image1 = Image.WrapMemory(externalMemory, image0.Width, image0.Height))
{
Memory<TPixel> 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]

2
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<Rgba32>(10, 10);
this.collection = new ImageFrameCollection<Rgba32>(this.image, 10, 10, default);
this.collection = new ImageFrameCollection<Rgba32>(this.image, 10, 10, default(Rgba32));
}
[Fact]

Loading…
Cancel
Save