Skip to content

Commit 2cf379e

Browse files
Merge pull request #3066 from SixLabors/js/expose-buffer-helpers
Add row-stride overloads for memory APIs
2 parents 10f8b0f + d557bb2 commit 2cf379e

26 files changed

Lines changed: 1436 additions & 530 deletions

src/ImageSharp/Image.LoadPixelData.cs

Lines changed: 109 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// Licensed under the Six Labors Split License.
33

44
using System.Runtime.InteropServices;
5-
using SixLabors.ImageSharp.Memory;
65
using SixLabors.ImageSharp.PixelFormats;
76

87
namespace SixLabors.ImageSharp;
@@ -25,6 +24,27 @@ public static Image<TPixel> LoadPixelData<TPixel>(ReadOnlySpan<TPixel> data, int
2524
where TPixel : unmanaged, IPixel<TPixel>
2625
=> LoadPixelData(Configuration.Default, data, width, height);
2726

27+
/// <summary>
28+
/// Create a new instance of the <see cref="Image{TPixel}"/> class from raw <typeparamref name="TPixel"/> data
29+
/// using <paramref name="rowStride"/> pixels between source row starts.
30+
/// </summary>
31+
/// <param name="data">The readonly span containing image data.</param>
32+
/// <param name="width">The width of the final image.</param>
33+
/// <param name="height">The height of the final image.</param>
34+
/// <param name="rowStride">The number of pixels between row starts in <paramref name="data"/>.</param>
35+
/// <typeparam name="TPixel">The pixel format.</typeparam>
36+
/// <exception cref="ArgumentOutOfRangeException">
37+
/// <paramref name="width"/> or <paramref name="height"/> is not positive,
38+
/// or <paramref name="rowStride"/> is less than <paramref name="width"/>.
39+
/// </exception>
40+
/// <exception cref="ArgumentException">
41+
/// <paramref name="data"/> is smaller than <c>((height - 1) * rowStride) + width</c>.
42+
/// </exception>
43+
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
44+
public static Image<TPixel> LoadPixelData<TPixel>(ReadOnlySpan<TPixel> data, int width, int height, int rowStride)
45+
where TPixel : unmanaged, IPixel<TPixel>
46+
=> LoadPixelData(Configuration.Default, data, width, height, rowStride);
47+
2848
/// <summary>
2949
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given readonly span of bytes in <typeparamref name="TPixel"/> format.
3050
/// </summary>
@@ -38,6 +58,28 @@ public static Image<TPixel> LoadPixelData<TPixel>(ReadOnlySpan<byte> data, int w
3858
where TPixel : unmanaged, IPixel<TPixel>
3959
=> LoadPixelData<TPixel>(Configuration.Default, data, width, height);
4060

61+
/// <summary>
62+
/// Create a new instance of the <see cref="Image{TPixel}"/> class from a readonly span of bytes in
63+
/// <typeparamref name="TPixel"/> format using <paramref name="rowStrideInBytes"/> bytes between source row starts.
64+
/// </summary>
65+
/// <param name="data">The readonly span containing image data.</param>
66+
/// <param name="width">The width of the final image.</param>
67+
/// <param name="height">The height of the final image.</param>
68+
/// <param name="rowStrideInBytes">The number of bytes between row starts in <paramref name="data"/>.</param>
69+
/// <typeparam name="TPixel">The pixel format.</typeparam>
70+
/// <exception cref="ArgumentOutOfRangeException">
71+
/// <paramref name="width"/> or <paramref name="height"/> is not positive,
72+
/// or <paramref name="rowStrideInBytes"/> resolves to fewer than <paramref name="width"/> pixels.
73+
/// </exception>
74+
/// <exception cref="ArgumentException">
75+
/// <paramref name="rowStrideInBytes"/> is not divisible by the pixel size,
76+
/// or <paramref name="data"/> is smaller than the required strided image length.
77+
/// </exception>
78+
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
79+
public static Image<TPixel> LoadPixelData<TPixel>(ReadOnlySpan<byte> data, int width, int height, int rowStrideInBytes)
80+
where TPixel : unmanaged, IPixel<TPixel>
81+
=> LoadPixelData<TPixel>(Configuration.Default, data, width, height, rowStrideInBytes);
82+
4183
/// <summary>
4284
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the given readonly span of bytes in <typeparamref name="TPixel"/> format.
4385
/// </summary>
@@ -53,6 +95,40 @@ public static Image<TPixel> LoadPixelData<TPixel>(Configuration configuration, R
5395
where TPixel : unmanaged, IPixel<TPixel>
5496
=> LoadPixelData(configuration, MemoryMarshal.Cast<byte, TPixel>(data), width, height);
5597

98+
/// <summary>
99+
/// Create a new instance of the <see cref="Image{TPixel}"/> class from a readonly span of bytes in
100+
/// <typeparamref name="TPixel"/> format using <paramref name="rowStrideInBytes"/> bytes between source row starts.
101+
/// </summary>
102+
/// <param name="configuration">The configuration for the decoder.</param>
103+
/// <param name="data">The readonly span containing image data.</param>
104+
/// <param name="width">The width of the final image.</param>
105+
/// <param name="height">The height of the final image.</param>
106+
/// <param name="rowStrideInBytes">The number of bytes between row starts in <paramref name="data"/>.</param>
107+
/// <typeparam name="TPixel">The pixel format.</typeparam>
108+
/// <exception cref="ArgumentNullException">The configuration is null.</exception>
109+
/// <exception cref="ArgumentOutOfRangeException">
110+
/// <paramref name="width"/> or <paramref name="height"/> is not positive,
111+
/// or <paramref name="rowStrideInBytes"/> resolves to fewer than <paramref name="width"/> pixels.
112+
/// </exception>
113+
/// <exception cref="ArgumentException">
114+
/// <paramref name="rowStrideInBytes"/> is not divisible by the pixel size,
115+
/// or <paramref name="data"/> is smaller than the required strided image length.
116+
/// </exception>
117+
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
118+
public static Image<TPixel> LoadPixelData<TPixel>(
119+
Configuration configuration,
120+
ReadOnlySpan<byte> data,
121+
int width,
122+
int height,
123+
int rowStrideInBytes)
124+
where TPixel : unmanaged, IPixel<TPixel>
125+
{
126+
Guard.NotNull(configuration, nameof(configuration));
127+
128+
int rowStride = GetPixelRowStrideFromByteStride<TPixel>(width, rowStrideInBytes, nameof(rowStrideInBytes));
129+
return LoadPixelData(configuration, MemoryMarshal.Cast<byte, TPixel>(data), width, height, rowStride);
130+
}
131+
56132
/// <summary>
57133
/// Create a new instance of the <see cref="Image{TPixel}"/> class from the raw <typeparamref name="TPixel"/> data.
58134
/// </summary>
@@ -66,20 +142,42 @@ public static Image<TPixel> LoadPixelData<TPixel>(Configuration configuration, R
66142
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
67143
public static Image<TPixel> LoadPixelData<TPixel>(Configuration configuration, ReadOnlySpan<TPixel> data, int width, int height)
68144
where TPixel : unmanaged, IPixel<TPixel>
145+
=> LoadPixelData(configuration, data, width, height, width);
146+
147+
/// <summary>
148+
/// Create a new instance of the <see cref="Image{TPixel}"/> class from raw <typeparamref name="TPixel"/> data
149+
/// using <paramref name="rowStride"/> pixels between source row starts.
150+
/// </summary>
151+
/// <param name="configuration">The configuration for the decoder.</param>
152+
/// <param name="data">The readonly span containing the image pixel data.</param>
153+
/// <param name="width">The width of the final image.</param>
154+
/// <param name="height">The height of the final image.</param>
155+
/// <param name="rowStride">The number of pixels between row starts in <paramref name="data"/>.</param>
156+
/// <exception cref="ArgumentNullException">The configuration is null.</exception>
157+
/// <exception cref="ArgumentOutOfRangeException">
158+
/// <paramref name="width"/> or <paramref name="height"/> is not positive,
159+
/// or <paramref name="rowStride"/> is less than <paramref name="width"/>.
160+
/// </exception>
161+
/// <exception cref="ArgumentException">
162+
/// <paramref name="data"/> is smaller than <c>((height - 1) * rowStride) + width</c>.
163+
/// </exception>
164+
/// <typeparam name="TPixel">The pixel format.</typeparam>
165+
/// <returns>A new <see cref="Image{TPixel}"/>.</returns>
166+
public static Image<TPixel> LoadPixelData<TPixel>(
167+
Configuration configuration,
168+
ReadOnlySpan<TPixel> data,
169+
int width,
170+
int height,
171+
int rowStride)
172+
where TPixel : unmanaged, IPixel<TPixel>
69173
{
70174
Guard.NotNull(configuration, nameof(configuration));
71-
72-
if (data.IsEmpty)
73-
{
74-
throw new ArgumentException("Pixel data cannot be empty.", nameof(data));
75-
}
76-
77-
int count = width * height;
78-
Guard.MustBeGreaterThanOrEqualTo(data.Length, count, nameof(data));
175+
ValidateWrapMemoryStride(width, height, rowStride, nameof(rowStride));
176+
long requiredLength = GetRequiredLength(width, height, rowStride);
177+
Guard.MustBeGreaterThanOrEqualTo(data.Length, requiredLength, nameof(data));
79178

80179
Image<TPixel> image = new(configuration, width, height);
81-
data = data[..count];
82-
data.CopyTo(image.Frames.RootFrame.PixelBuffer.FastMemoryGroup);
180+
image.Frames.RootFrame.PixelBuffer.CopyFrom(data, rowStride);
83181

84182
return image;
85183
}

0 commit comments

Comments
 (0)