Browse Source

Use memory allocator for alpha data

pull/1971/head
Brian Popow 4 years ago
parent
commit
cf672b99ae
  1. 26
      src/ImageSharp/Formats/Webp/AlphaEncoder.cs
  2. 12
      src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs
  3. 12
      src/ImageSharp/Formats/Webp/BitWriter/Vp8BitWriter.cs
  4. 13
      src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs
  5. 22
      src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs

26
src/ImageSharp/Formats/Webp/AlphaEncoder.cs

@ -24,13 +24,15 @@ namespace SixLabors.ImageSharp.Formats.Webp
/// <param name="configuration">The global configuration.</param>
/// <param name="memoryAllocator">The memory manager.</param>
/// <param name="compress">Indicates, if the data should be compressed with the lossless webp compression.</param>
/// <returns>The alpha data.</returns>
public static byte[] EncodeAlpha<TPixel>(Image<TPixel> image, Configuration configuration, MemoryAllocator memoryAllocator, bool compress)
/// <param name="size">The size in bytes of the alpha data.</param>
/// <returns>The encoded alpha data.</returns>
public static IMemoryOwner<byte> EncodeAlpha<TPixel>(Image<TPixel> image, Configuration configuration, MemoryAllocator memoryAllocator, bool compress, out int size)
where TPixel : unmanaged, IPixel<TPixel>
{
byte[] alphaData = ExtractAlphaChannel(image, configuration, memoryAllocator);
int width = image.Width;
int height = image.Height;
IMemoryOwner<byte> 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<Rgba32> alphaAsImage = DispatchAlphaToGreen(image, alphaData);
using Image<Rgba32> 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
/// <param name="image">The <see cref="ImageFrame{TPixel}"/> to encode from.</param>
/// <param name="alphaData">A byte sequence of length width * height, containing all the 8-bit transparency values in scan order.</param>
/// <returns>The transparency image.</returns>
private static Image<Rgba32> DispatchAlphaToGreen<TPixel>(Image<TPixel> image, byte[] alphaData)
private static Image<Rgba32> DispatchAlphaToGreen<TPixel>(Image<TPixel> image, Span<byte> alphaData)
where TPixel : unmanaged, IPixel<TPixel>
{
int width = image.Width;
@ -75,7 +80,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
{
Memory<Rgba32> rowBuffer = alphaAsImage.DangerousGetPixelRowMemory(y);
Span<Rgba32> pixelRow = rowBuffer.Span;
Span<byte> alphaRow = alphaData.AsSpan(y * width, width);
Span<byte> 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
/// <param name="configuration">The global configuration.</param>
/// <param name="memoryAllocator">The memory manager.</param>
/// <returns>A byte sequence of length width * height, containing all the 8-bit transparency values in scan order.</returns>
private static byte[] ExtractAlphaChannel<TPixel>(Image<TPixel> image, Configuration configuration, MemoryAllocator memoryAllocator)
private static IMemoryOwner<byte> ExtractAlphaChannel<TPixel>(Image<TPixel> image, Configuration configuration, MemoryAllocator memoryAllocator)
where TPixel : unmanaged, IPixel<TPixel>
{
Buffer2D<TPixel> imageBuffer = image.Frames.RootFrame.PixelBuffer;
int height = image.Height;
int width = image.Width;
byte[] alphaData = new byte[width * height];
IMemoryOwner<byte> alphaDataBuffer = memoryAllocator.Allocate<byte>(width * height);
Span<byte> alphaData = alphaDataBuffer.GetSpan();
using IMemoryOwner<Rgba32> rowBuffer = memoryAllocator.Allocate<Rgba32>(width);
Span<Rgba32> rgbaRow = rowBuffer.GetSpan();
@ -116,7 +122,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
}
}
return alphaData;
return alphaDataBuffer;
}
}
}

12
src/ImageSharp/Formats/Webp/BitWriter/BitWriterBase.cs

@ -47,6 +47,12 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
/// <param name="stream">The stream to write to.</param>
public void WriteToStream(Stream stream) => stream.Write(this.Buffer.AsSpan(0, this.NumBytes()));
/// <summary>
/// Writes the encoded bytes of the image to the given buffer. Call Finish() before this.
/// </summary>
/// <param name="dest">The destination buffer.</param>
public void WriteToBuffer(Span<byte> dest) => this.Buffer.AsSpan(0, this.NumBytes()).CopyTo(dest);
/// <summary>
/// Resizes the buffer to write to.
/// </summary>
@ -108,7 +114,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
/// </summary>
/// <param name="alphaBytes">The alpha chunk bytes.</param>
/// <returns>The alpha data chunk size in bytes.</returns>
protected uint AlphaChunkSize(byte[] alphaBytes)
protected uint AlphaChunkSize(Span<byte> alphaBytes)
{
uint alphaSize = (uint)alphaBytes.Length + 1;
uint alphaChunkSize = WebpConstants.ChunkHeaderSize + alphaSize + (alphaSize & 1);
@ -147,10 +153,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
/// <param name="stream">The stream to write to.</param>
/// <param name="dataBytes">The alpha channel data bytes.</param>
/// <param name="alphaDataIsCompressed">Indicates, if the alpha channel data is compressed.</param>
protected void WriteAlphaChunk(Stream stream, byte[] dataBytes, bool alphaDataIsCompressed)
protected void WriteAlphaChunk(Stream stream, Span<byte> dataBytes, bool alphaDataIsCompressed)
{
DebugGuard.NotNull(dataBytes, nameof(dataBytes));
uint size = (uint)dataBytes.Length + 1;
Span<byte> buf = this.scratchBuffer.AsSpan(0, 4);
BinaryPrimitives.WriteUInt32BigEndian(buf, (uint)WebpChunkType.Alpha);

12
src/ImageSharp/Formats/Webp/BitWriter/Vp8BitWriter.cs

@ -411,7 +411,15 @@ namespace SixLabors.ImageSharp.Formats.Webp.BitWriter
/// <param name="hasAlpha">Flag indicating, if a alpha channel is present.</param>
/// <param name="alphaData">The alpha channel data.</param>
/// <param name="alphaDataIsCompressed">Indicates, if the alpha data is compressed.</param>
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<byte> 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<byte> alphaData,
bool alphaDataIsCompressed)
{
this.WriteRiffHeader(stream, riffSize);

13
src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs

@ -263,8 +263,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="image">The <see cref="Image{TPixel}"/> to encode from.</param>
/// <returns>The encoded alpha stream.</returns>
public byte[] EncodeAlphaImageData<TPixel>(Image<TPixel> image)
/// <param name="alphaData">The destination buffer to write the encoded alpha data to.</param>
/// <returns>The size of the data in bytes.</returns>
public int EncodeAlphaImageData<TPixel>(Image<TPixel> image, IMemoryOwner<byte> alphaData)
where TPixel : unmanaged, IPixel<TPixel>
{
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;
}
/// <summary>

22
src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs

@ -241,6 +241,11 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
public int DqUvDc { get; private set; }
/// <summary>
/// Gets or sets the alpha data.
/// </summary>
private IMemoryOwner<byte> AlphaData { get; set; }
/// <summary>
/// Gets the luma component.
/// </summary>
@ -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<byte>.Empty,
this.alphaCompression);
}
/// <inheritdoc/>
@ -372,6 +385,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
this.Y.Dispose();
this.U.Dispose();
this.V.Dispose();
this.AlphaData?.Dispose();
}
/// <summary>

Loading…
Cancel
Save