diff --git a/src/ImageSharp/Formats/Webp/AlphaEncoder.cs b/src/ImageSharp/Formats/Webp/AlphaEncoder.cs
index 571da5bb2..38497281f 100644
--- a/src/ImageSharp/Formats/Webp/AlphaEncoder.cs
+++ b/src/ImageSharp/Formats/Webp/AlphaEncoder.cs
@@ -24,13 +24,15 @@ namespace SixLabors.ImageSharp.Formats.Webp
/// The global configuration.
/// The memory manager.
/// Indicates, if the data should be compressed with the lossless webp compression.
- /// The alpha data.
- public static byte[] EncodeAlpha(Image image, Configuration configuration, MemoryAllocator memoryAllocator, bool compress)
+ /// The size in bytes of the alpha data.
+ /// The encoded alpha data.
+ public static IMemoryOwner EncodeAlpha(Image image, Configuration configuration, MemoryAllocator memoryAllocator, bool compress, out int size)
where TPixel : unmanaged, IPixel
{
- byte[] alphaData = ExtractAlphaChannel(image, configuration, memoryAllocator);
int width = image.Width;
int height = image.Height;
+ IMemoryOwner alphaData = ExtractAlphaChannel(image, configuration, memoryAllocator);
+
if (compress)
{
WebpEncodingMethod effort = WebpEncodingMethod.Default;
@@ -49,11 +51,14 @@ namespace SixLabors.ImageSharp.Formats.Webp
// The transparency information will be stored in the green channel of the ARGB quadruplet.
// The green channel is allowed extra transformation steps in the specification -- unlike the other channels,
// that can improve compression.
- using Image alphaAsImage = DispatchAlphaToGreen(image, alphaData);
+ using Image alphaAsImage = DispatchAlphaToGreen(image, alphaData.GetSpan());
+
+ size = lossLessEncoder.EncodeAlphaImageData(alphaAsImage, alphaData);
- return lossLessEncoder.EncodeAlphaImageData(alphaAsImage);
+ return alphaData;
}
+ size = width * height;
return alphaData;
}
@@ -64,7 +69,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
/// The to encode from.
/// A byte sequence of length width * height, containing all the 8-bit transparency values in scan order.
/// The transparency image.
- private static Image DispatchAlphaToGreen(Image image, byte[] alphaData)
+ private static Image DispatchAlphaToGreen(Image image, Span alphaData)
where TPixel : unmanaged, IPixel
{
int width = image.Width;
@@ -75,7 +80,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
{
Memory rowBuffer = alphaAsImage.DangerousGetPixelRowMemory(y);
Span pixelRow = rowBuffer.Span;
- Span alphaRow = alphaData.AsSpan(y * width, width);
+ Span alphaRow = alphaData.Slice(y * width, width);
for (int x = 0; x < width; x++)
{
// Leave A/R/B channels zero'd.
@@ -94,13 +99,14 @@ namespace SixLabors.ImageSharp.Formats.Webp
/// The global configuration.
/// The memory manager.
/// A byte sequence of length width * height, containing all the 8-bit transparency values in scan order.
- private static byte[] ExtractAlphaChannel(Image image, Configuration configuration, MemoryAllocator memoryAllocator)
+ private static IMemoryOwner ExtractAlphaChannel(Image image, Configuration configuration, MemoryAllocator memoryAllocator)
where TPixel : unmanaged, IPixel
{
Buffer2D imageBuffer = image.Frames.RootFrame.PixelBuffer;
int height = image.Height;
int width = image.Width;
- byte[] alphaData = new byte[width * height];
+ IMemoryOwner alphaDataBuffer = memoryAllocator.Allocate(width * height);
+ Span alphaData = alphaDataBuffer.GetSpan();
using IMemoryOwner rowBuffer = memoryAllocator.Allocate(width);
Span rgbaRow = rowBuffer.GetSpan();
@@ -116,7 +122,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
}
}
- return alphaData;
+ return alphaDataBuffer;
}
}
}
diff --git a/src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs b/src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs
index 84c9d3f13..fc1accfde 100644
--- a/src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs
+++ b/src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs
@@ -47,6 +47,12 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
/// The stream to write to.
public void WriteToStream(Stream stream) => stream.Write(this.Buffer.AsSpan(0, this.NumBytes()));
+ ///
+ /// Writes the encoded bytes of the image to the given buffer. Call Finish() before this.
+ ///
+ /// The destination buffer.
+ public void WriteToBuffer(Span dest) => this.Buffer.AsSpan(0, this.NumBytes()).CopyTo(dest);
+
///
/// Resizes the buffer to write to.
///
@@ -108,7 +114,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
///
/// The alpha chunk bytes.
/// The alpha data chunk size in bytes.
- protected uint AlphaChunkSize(byte[] alphaBytes)
+ protected uint AlphaChunkSize(Span alphaBytes)
{
uint alphaSize = (uint)alphaBytes.Length + 1;
uint alphaChunkSize = WebpConstants.ChunkHeaderSize + alphaSize + (alphaSize & 1);
@@ -147,10 +153,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
/// The stream to write to.
/// The alpha channel data bytes.
/// Indicates, if the alpha channel data is compressed.
- protected void WriteAlphaChunk(Stream stream, byte[] dataBytes, bool alphaDataIsCompressed)
+ protected void WriteAlphaChunk(Stream stream, Span dataBytes, bool alphaDataIsCompressed)
{
- DebugGuard.NotNull(dataBytes, nameof(dataBytes));
-
uint size = (uint)dataBytes.Length + 1;
Span buf = this.scratchBuffer.AsSpan(0, 4);
BinaryPrimitives.WriteUInt32BigEndian(buf, (uint)WebpChunkType.Alpha);
diff --git a/src/ImageSharp/Formats/Webp/BitWriter/Vp8BitWriter.cs b/src/ImageSharp/Formats/Webp/BitWriter/Vp8BitWriter.cs
index 577a87e6a..fa6e09d87 100644
--- a/src/ImageSharp/Formats/Webp/BitWriter/Vp8BitWriter.cs
+++ b/src/ImageSharp/Formats/Webp/BitWriter/Vp8BitWriter.cs
@@ -411,7 +411,15 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
/// Flag indicating, if a alpha channel is present.
/// The alpha channel data.
/// Indicates, if the alpha data is compressed.
- public void WriteEncodedImageToStream(Stream stream, ExifProfile exifProfile, XmpProfile xmpProfile, uint width, uint height, bool hasAlpha, byte[] alphaData, bool alphaDataIsCompressed)
+ public void WriteEncodedImageToStream(
+ Stream stream,
+ ExifProfile exifProfile,
+ XmpProfile xmpProfile,
+ uint width,
+ uint height,
+ bool hasAlpha,
+ Span alphaData,
+ bool alphaDataIsCompressed)
{
bool isVp8X = false;
byte[] exifBytes = null;
@@ -661,7 +669,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
ExifProfile exifProfile,
XmpProfile xmpProfile,
bool hasAlpha,
- byte[] alphaData,
+ Span alphaData,
bool alphaDataIsCompressed)
{
this.WriteRiffHeader(stream, riffSize);
diff --git a/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs b/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs
index 797d0794f..ece9aefd0 100644
--- a/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs
+++ b/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs
@@ -263,8 +263,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
///
/// The type of the pixel.
/// The to encode from.
- /// The encoded alpha stream.
- public byte[] EncodeAlphaImageData(Image image)
+ /// The destination buffer to write the encoded alpha data to.
+ /// The size of the data in bytes.
+ public int EncodeAlphaImageData(Image image, IMemoryOwner alphaData)
where TPixel : unmanaged, IPixel
{
int width = image.Width;
@@ -273,12 +274,12 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
// Convert image pixels to bgra array.
this.ConvertPixelsToBgra(image, width, height);
- // The image-stream does NOT contain any headers describing the image dimension, the dimension is already known.
+ // The image-stream will NOT contain any headers describing the image dimension, the dimension is already known.
this.EncodeStream(image);
this.bitWriter.Finish();
- using var ms = new MemoryStream();
- this.bitWriter.WriteToStream(ms);
- return ms.ToArray();
+ int size = this.bitWriter.NumBytes();
+ this.bitWriter.WriteToBuffer(alphaData.GetSpan());
+ return size;
}
///
diff --git a/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs b/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs
index d8bd8f759..4b7f3f5c8 100644
--- a/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs
+++ b/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs
@@ -241,6 +241,11 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
public int DqUvDc { get; private set; }
+ ///
+ /// Gets or sets the alpha data.
+ ///
+ private IMemoryOwner AlphaData { get; set; }
+
///
/// Gets the luma component.
///
@@ -322,12 +327,12 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
int expectedSize = this.Mbw * this.Mbh * averageBytesPerMacroBlock;
this.bitWriter = new Vp8BitWriter(expectedSize, this);
- // Extract and encode alpha data, if present.
- byte[] alphaData = null;
+ // Extract and encode alpha channel data, if present.
+ int alphaDataSize = 0;
if (hasAlpha)
{
// TODO: This can potentially run in an separate task.
- alphaData = AlphaEncoder.EncodeAlpha(image, this.configuration, this.memoryAllocator, this.alphaCompression);
+ this.AlphaData = AlphaEncoder.EncodeAlpha(image, this.configuration, this.memoryAllocator, this.alphaCompression, out alphaDataSize);
}
// Stats-collection loop.
@@ -363,7 +368,15 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// Write bytes from the bitwriter buffer to the stream.
ImageMetadata metadata = image.Metadata;
metadata.SyncProfiles();
- this.bitWriter.WriteEncodedImageToStream(stream, metadata.ExifProfile, metadata.XmpProfile, (uint)width, (uint)height, hasAlpha, alphaData, this.alphaCompression);
+ this.bitWriter.WriteEncodedImageToStream(
+ stream,
+ metadata.ExifProfile,
+ metadata.XmpProfile,
+ (uint)width,
+ (uint)height,
+ hasAlpha,
+ hasAlpha ? this.AlphaData.GetSpan().Slice(0, alphaDataSize) : Span.Empty,
+ this.alphaCompression);
}
///
@@ -372,6 +385,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
this.Y.Dispose();
this.U.Dispose();
this.V.Dispose();
+ this.AlphaData?.Dispose();
}
///