Skip to content

Commit ade5ba5

Browse files
Merge pull request #1314 from SixLabors/sp/byte-to-tpixel-wrapping
Image.WrapMemory<TPixel> APIs wrapping Memory<byte>
2 parents c4dabd9 + 26b948d commit ade5ba5

File tree

5 files changed

+322
-13
lines changed

5 files changed

+322
-13
lines changed

src/ImageSharp/Image.WrapMemory.cs

Lines changed: 76 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public abstract partial class Image
1717
{
1818
/// <summary>
1919
/// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
20-
/// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance.
20+
/// allowing to view/manipulate it as an <see cref="Image{TPixel}"/> instance.
2121
/// </summary>
2222
/// <typeparam name="TPixel">The pixel type</typeparam>
2323
/// <param name="configuration">The <see cref="Configuration"/></param>
@@ -38,14 +38,15 @@ public static Image<TPixel> WrapMemory<TPixel>(
3838
{
3939
Guard.NotNull(configuration, nameof(configuration));
4040
Guard.NotNull(metadata, nameof(metadata));
41+
Guard.IsTrue(pixelMemory.Length == width * height, nameof(pixelMemory), "The length of the input memory doesn't match the specified image size");
4142

4243
var memorySource = MemoryGroup<TPixel>.Wrap(pixelMemory);
4344
return new Image<TPixel>(configuration, memorySource, width, height, metadata);
4445
}
4546

4647
/// <summary>
4748
/// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
48-
/// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance.
49+
/// allowing to view/manipulate it as an <see cref="Image{TPixel}"/> instance.
4950
/// </summary>
5051
/// <typeparam name="TPixel">The pixel type</typeparam>
5152
/// <param name="configuration">The <see cref="Configuration"/></param>
@@ -64,7 +65,7 @@ public static Image<TPixel> WrapMemory<TPixel>(
6465

6566
/// <summary>
6667
/// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
67-
/// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance.
68+
/// allowing to view/manipulate it as an <see cref="Image{TPixel}"/> instance.
6869
/// The memory is being observed, the caller remains responsible for managing it's lifecycle.
6970
/// </summary>
7071
/// <typeparam name="TPixel">The pixel type.</typeparam>
@@ -81,7 +82,7 @@ public static Image<TPixel> WrapMemory<TPixel>(
8182

8283
/// <summary>
8384
/// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
84-
/// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance.
85+
/// allowing to view/manipulate it as an <see cref="Image{TPixel}"/> instance.
8586
/// The ownership of the <paramref name="pixelMemoryOwner"/> is being transferred to the new <see cref="Image{TPixel}"/> instance,
8687
/// meaning that the caller is not allowed to dispose <paramref name="pixelMemoryOwner"/>.
8788
/// It will be disposed together with the result image.
@@ -105,14 +106,15 @@ public static Image<TPixel> WrapMemory<TPixel>(
105106
{
106107
Guard.NotNull(configuration, nameof(configuration));
107108
Guard.NotNull(metadata, nameof(metadata));
109+
Guard.IsTrue(pixelMemoryOwner.Memory.Length == width * height, nameof(pixelMemoryOwner), "The length of the input memory doesn't match the specified image size");
108110

109111
var memorySource = MemoryGroup<TPixel>.Wrap(pixelMemoryOwner);
110112
return new Image<TPixel>(configuration, memorySource, width, height, metadata);
111113
}
112114

113115
/// <summary>
114116
/// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
115-
/// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance.
117+
/// allowing to view/manipulate it as an <see cref="Image{TPixel}"/> instance.
116118
/// The ownership of the <paramref name="pixelMemoryOwner"/> is being transferred to the new <see cref="Image{TPixel}"/> instance,
117119
/// meaning that the caller is not allowed to dispose <paramref name="pixelMemoryOwner"/>.
118120
/// It will be disposed together with the result image.
@@ -134,7 +136,7 @@ public static Image<TPixel> WrapMemory<TPixel>(
134136

135137
/// <summary>
136138
/// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
137-
/// allowing to view/manipulate it as an ImageSharp <see cref="Image{TPixel}"/> instance.
139+
/// allowing to view/manipulate it as an <see cref="Image{TPixel}"/> instance.
138140
/// The ownership of the <paramref name="pixelMemoryOwner"/> is being transferred to the new <see cref="Image{TPixel}"/> instance,
139141
/// meaning that the caller is not allowed to dispose <paramref name="pixelMemoryOwner"/>.
140142
/// It will be disposed together with the result image.
@@ -150,5 +152,73 @@ public static Image<TPixel> WrapMemory<TPixel>(
150152
int height)
151153
where TPixel : unmanaged, IPixel<TPixel>
152154
=> WrapMemory(Configuration.Default, pixelMemoryOwner, width, height);
155+
156+
/// <summary>
157+
/// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
158+
/// allowing to view/manipulate it as an <see cref="Image{TPixel}"/> instance.
159+
/// </summary>
160+
/// <typeparam name="TPixel">The pixel type</typeparam>
161+
/// <param name="configuration">The <see cref="Configuration"/></param>
162+
/// <param name="byteMemory">The byte memory representing the pixel data.</param>
163+
/// <param name="width">The width of the memory image.</param>
164+
/// <param name="height">The height of the memory image.</param>
165+
/// <param name="metadata">The <see cref="ImageMetadata"/>.</param>
166+
/// <exception cref="ArgumentNullException">The configuration is null.</exception>
167+
/// <exception cref="ArgumentNullException">The metadata is null.</exception>
168+
/// <returns>An <see cref="Image{TPixel}"/> instance</returns>
169+
public static Image<TPixel> WrapMemory<TPixel>(
170+
Configuration configuration,
171+
Memory<byte> byteMemory,
172+
int width,
173+
int height,
174+
ImageMetadata metadata)
175+
where TPixel : unmanaged, IPixel<TPixel>
176+
{
177+
Guard.NotNull(configuration, nameof(configuration));
178+
Guard.NotNull(metadata, nameof(metadata));
179+
180+
var memoryManager = new ByteMemoryManager<TPixel>(byteMemory);
181+
182+
Guard.IsTrue(memoryManager.Memory.Length == width * height, nameof(byteMemory), "The length of the input memory doesn't match the specified image size");
183+
184+
var memorySource = MemoryGroup<TPixel>.Wrap(memoryManager.Memory);
185+
return new Image<TPixel>(configuration, memorySource, width, height, metadata);
186+
}
187+
188+
/// <summary>
189+
/// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
190+
/// allowing to view/manipulate it as an <see cref="Image{TPixel}"/> instance.
191+
/// </summary>
192+
/// <typeparam name="TPixel">The pixel type</typeparam>
193+
/// <param name="configuration">The <see cref="Configuration"/></param>
194+
/// <param name="byteMemory">The byte memory representing the pixel data.</param>
195+
/// <param name="width">The width of the memory image.</param>
196+
/// <param name="height">The height of the memory image.</param>
197+
/// <exception cref="ArgumentNullException">The configuration is null.</exception>
198+
/// <returns>An <see cref="Image{TPixel}"/> instance.</returns>
199+
public static Image<TPixel> WrapMemory<TPixel>(
200+
Configuration configuration,
201+
Memory<byte> byteMemory,
202+
int width,
203+
int height)
204+
where TPixel : unmanaged, IPixel<TPixel>
205+
=> WrapMemory<TPixel>(configuration, byteMemory, width, height, new ImageMetadata());
206+
207+
/// <summary>
208+
/// Wraps an existing contiguous memory area of 'width' x 'height' pixels,
209+
/// allowing to view/manipulate it as an <see cref="Image{TPixel}"/> instance.
210+
/// The memory is being observed, the caller remains responsible for managing it's lifecycle.
211+
/// </summary>
212+
/// <typeparam name="TPixel">The pixel type.</typeparam>
213+
/// <param name="byteMemory">The byte memory representing the pixel data.</param>
214+
/// <param name="width">The width of the memory image.</param>
215+
/// <param name="height">The height of the memory image.</param>
216+
/// <returns>An <see cref="Image{TPixel}"/> instance.</returns>
217+
public static Image<TPixel> WrapMemory<TPixel>(
218+
Memory<byte> byteMemory,
219+
int width,
220+
int height)
221+
where TPixel : unmanaged, IPixel<TPixel>
222+
=> WrapMemory<TPixel>(Configuration.Default, byteMemory, width, height);
153223
}
154224
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Apache License, Version 2.0.
3+
using System;
4+
using System.Buffers;
5+
using System.Runtime.CompilerServices;
6+
using System.Runtime.InteropServices;
7+
8+
namespace SixLabors.ImageSharp.Memory
9+
{
10+
/// <summary>
11+
/// A custom <see cref="MemoryManager{T}"/> that can wrap <see cref="Memory{T}"/> of <see cref="byte"/> instances
12+
/// and cast them to be <see cref="Memory{T}"/> for any arbitrary unmanaged <typeparamref name="T"/> value type.
13+
/// </summary>
14+
/// <typeparam name="T">The value type to use when casting the wrapped <see cref="Memory{T}"/> instance.</typeparam>
15+
internal sealed class ByteMemoryManager<T> : MemoryManager<T>
16+
where T : unmanaged
17+
{
18+
/// <summary>
19+
/// The wrapped <see cref="Memory{T}"/> of <see cref="byte"/> instance.
20+
/// </summary>
21+
private readonly Memory<byte> memory;
22+
23+
/// <summary>
24+
/// Initializes a new instance of the <see cref="ByteMemoryManager{T}"/> class.
25+
/// </summary>
26+
/// <param name="memory">The <see cref="Memory{T}"/> of <see cref="byte"/> instance to wrap.</param>
27+
public ByteMemoryManager(Memory<byte> memory)
28+
{
29+
this.memory = memory;
30+
}
31+
32+
/// <inheritdoc/>
33+
protected override void Dispose(bool disposing)
34+
{
35+
}
36+
37+
/// <inheritdoc/>
38+
public override Span<T> GetSpan()
39+
{
40+
return MemoryMarshal.Cast<byte, T>(this.memory.Span);
41+
}
42+
43+
/// <inheritdoc/>
44+
public override MemoryHandle Pin(int elementIndex = 0)
45+
{
46+
// We need to adjust the offset into the wrapped byte segment,
47+
// as the input index refers to the target-cast memory of T.
48+
// We just have to shift this index by the byte size of T.
49+
return this.memory.Slice(elementIndex * Unsafe.SizeOf<T>()).Pin();
50+
}
51+
52+
/// <inheritdoc/>
53+
public override void Unpin()
54+
{
55+
}
56+
}
57+
}

src/ImageSharp/Memory/MemoryOwnerExtensions.cs

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,27 @@ namespace SixLabors.ImageSharp.Memory
1313
/// </summary>
1414
internal static class MemoryOwnerExtensions
1515
{
16+
/// <summary>
17+
/// Gets a <see cref="Span{T}"/> from an <see cref="IMemoryOwner{T}"/> instance.
18+
/// </summary>
19+
/// <param name="buffer">The buffer</param>
20+
/// <returns>The <see cref="Span{T}"/></returns>
1621
[MethodImpl(MethodImplOptions.AggressiveInlining)]
1722
public static Span<T> GetSpan<T>(this IMemoryOwner<T> buffer)
18-
=> buffer.Memory.Span;
23+
{
24+
return buffer.Memory.Span;
25+
}
1926

27+
/// <summary>
28+
/// Gets the length of an <see cref="IMemoryOwner{T}"/> internal buffer.
29+
/// </summary>
30+
/// <param name="buffer">The buffer</param>
31+
/// <returns>The length of the buffer</returns>
2032
[MethodImpl(MethodImplOptions.AggressiveInlining)]
2133
public static int Length<T>(this IMemoryOwner<T> buffer)
22-
=> buffer.GetSpan().Length;
34+
{
35+
return buffer.Memory.Length;
36+
}
2337

2438
/// <summary>
2539
/// Gets a <see cref="Span{T}"/> to an offsetted position inside the buffer.
@@ -56,8 +70,16 @@ public static void Clear<T>(this IMemoryOwner<T> buffer)
5670
buffer.GetSpan().Clear();
5771
}
5872

73+
/// <summary>
74+
/// Gets a reference to the first item in the internal buffer for an <see cref="IMemoryOwner{T}"/> instance.
75+
/// </summary>
76+
/// <param name="buffer">The buffer</param>
77+
/// <returns>A reference to the first item within the memory wrapped by <paramref name="buffer"/></returns>
78+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
5979
public static ref T GetReference<T>(this IMemoryOwner<T> buffer)
60-
where T : struct =>
61-
ref MemoryMarshal.GetReference(buffer.GetSpan());
80+
where T : struct
81+
{
82+
return ref MemoryMarshal.GetReference(buffer.GetSpan());
83+
}
6284
}
6385
}

src/ImageSharp/Processing/Processors/Dithering/OrderedDither.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ public PaletteDitherRowOperation(
262262
this.source = source;
263263
this.bounds = bounds;
264264
this.scale = processor.DitherScale;
265-
this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(processor.Palette.Span.Length);
265+
this.bitDepth = ImageMaths.GetBitsNeededForColorDepth(processor.Palette.Length);
266266
}
267267

268268
[MethodImpl(InliningOptions.ShortMethod)]

0 commit comments

Comments
 (0)