Skip to content

Commit a075501

Browse files
authored
Do not allocate intermediate byte[] array when encoding to Stream with config (#21)
* Do not allocate intermediate byte[] array when encoding to Stream * Remove unnecessary allocation in WebPEncoder.ManagedWriter for NETCOREAPP * Update dependencies * Readonly EncodeOutput.Stream
1 parent 462cd4a commit a075501

3 files changed

Lines changed: 28 additions & 26 deletions

File tree

src/Imazen.Test.Webp/Imazen.Test.Webp.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
</PropertyGroup>
88

99
<ItemGroup>
10-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.*" />
10+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.*" />
1111
<PackageReference Include="xunit" Version="2.*" />
12-
<PackageReference Include="xunit.runner.visualstudio" Version="2.*">
12+
<PackageReference Include="xunit.runner.visualstudio" Version="3.*">
1313
<PrivateAssets>all</PrivateAssets>
1414
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1515
</PackageReference>
@@ -25,7 +25,7 @@
2525

2626
<!-- System.Drawing.Common for .NET Core tests on Windows -->
2727
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0' Or '$(TargetFramework)' == 'net10.0'">
28-
<PackageReference Include="System.Drawing.Common" Version="8.*" />
28+
<PackageReference Include="System.Drawing.Common" Version="10.*" />
2929
</ItemGroup>
3030

3131
<ItemGroup>

src/Imazen.WebP/Imazen.WebP.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
<!-- System.Drawing.Common for netstandard2.0 and net8.0 -->
2020
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0' Or '$(TargetFramework)' == 'net8.0'">
21-
<PackageReference Include="System.Drawing.Common" Version="8.*" />
21+
<PackageReference Include="System.Drawing.Common" Version="10.*" />
2222
</ItemGroup>
2323

2424
<!-- System.Runtime.InteropServices.RuntimeInformation for net472/net48 -->

src/Imazen.WebP/WebPEncoder.cs

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,28 @@ namespace Imazen.WebP
1111
public static class WebPEncoder
1212
{
1313
// Prevent delegate from being GC'd during encoding
14-
[ThreadStatic] private static WebPWriterFunction? _writerDelegate;
14+
private static readonly WebPWriterFunction _writerDelegate = ManagedWriter;
1515

16-
private class EncodeOutput
16+
private sealed class EncodeOutput(Stream stream)
1717
{
18-
public MemoryStream Stream = new MemoryStream();
18+
public readonly Stream Stream = stream;
1919
}
2020

2121
private static int ManagedWriter(IntPtr data, UIntPtr dataSize, ref WebPPicture picture)
2222
{
2323
int size = (int)(uint)dataSize;
2424
if (size <= 0) return 1;
25-
byte[] buffer = new byte[size];
26-
Marshal.Copy(data, buffer, 0, size);
2725
var handle = GCHandle.FromIntPtr(picture.custom_ptr);
2826
var ctx = (EncodeOutput)handle.Target!;
27+
#if NETCOREAPP
28+
unsafe {
29+
ctx.Stream.Write(new ReadOnlySpan<byte>((void*)data, size));
30+
}
31+
#else
32+
byte[] buffer = new byte[size];
33+
Marshal.Copy(data, buffer, 0, size);
2934
ctx.Stream.Write(buffer, 0, size);
35+
#endif
3036
return 1;
3137
}
3238

@@ -169,11 +175,23 @@ private static UIntPtr EncodeLossless(IntPtr data, int width, int height, int st
169175
/// <returns>Encoded WebP data</returns>
170176
public static byte[] Encode(byte[] pixels, int width, int height, int stride,
171177
WebPPixelFormat format, WebPEncoderConfig config)
178+
{
179+
var outputStream = new MemoryStream();
180+
Encode(pixels, width, height, stride, format, config, outputStream);
181+
return outputStream.ToArray();
182+
}
183+
184+
/// <summary>
185+
/// Encodes raw pixel data using advanced WebPEncoderConfig settings and writes to a stream.
186+
/// </summary>
187+
public static void Encode(byte[] pixels, int width, int height, int stride,
188+
WebPPixelFormat format, WebPEncoderConfig config, Stream outputStream)
172189
{
173190
if (pixels == null) throw new ArgumentNullException(nameof(pixels));
174191
if (config == null) throw new ArgumentNullException(nameof(config));
175192
if (width <= 0) throw new ArgumentOutOfRangeException(nameof(width));
176193
if (height <= 0) throw new ArgumentOutOfRangeException(nameof(height));
194+
if (outputStream == null) throw new ArgumentNullException(nameof(outputStream));
177195

178196
if (!config.Validate())
179197
throw new ArgumentException("Invalid WebP encoder configuration", nameof(config));
@@ -193,10 +211,8 @@ public static byte[] Encode(byte[] pixels, int width, int height, int stride,
193211
picture.use_argb = 1;
194212

195213
var pixelHandle = GCHandle.Alloc(pixels, GCHandleType.Pinned);
196-
var output = new EncodeOutput();
214+
var output = new EncodeOutput(outputStream);
197215
var outputHandle = GCHandle.Alloc(output);
198-
// Keep delegate alive during encoding
199-
_writerDelegate = ManagedWriter;
200216
try
201217
{
202218
IntPtr pixelPtr = pixelHandle.AddrOfPinnedObject();
@@ -233,8 +249,6 @@ public static byte[] Encode(byte[] pixels, int width, int height, int stride,
233249

234250
if (encodeResult == 0)
235251
throw new Exception($"WebP encode failed with error: {picture.error_code}");
236-
237-
return output.Stream.ToArray();
238252
}
239253
finally
240254
{
@@ -245,19 +259,7 @@ public static byte[] Encode(byte[] pixels, int width, int height, int stride,
245259
});
246260
pixelHandle.Free();
247261
outputHandle.Free();
248-
_writerDelegate = null;
249262
}
250263
}
251-
252-
/// <summary>
253-
/// Encodes raw pixel data using advanced WebPEncoderConfig settings and writes to a stream.
254-
/// </summary>
255-
public static void Encode(byte[] pixels, int width, int height, int stride,
256-
WebPPixelFormat format, WebPEncoderConfig config, Stream outputStream)
257-
{
258-
if (outputStream == null) throw new ArgumentNullException(nameof(outputStream));
259-
byte[] encoded = Encode(pixels, width, height, stride, format, config);
260-
outputStream.Write(encoded, 0, encoded.Length);
261-
}
262264
}
263265
}

0 commit comments

Comments
 (0)