Browse Source

Use memory allocator for alpha chunk and for the bitreader data

pull/1147/head
Brian Popow 6 years ago
parent
commit
3ddd8bf586
  1. 12
      src/ImageSharp/Formats/WebP/AlphaDecoder.cs
  2. 17
      src/ImageSharp/Formats/WebP/BitReaderBase.cs
  3. 10
      src/ImageSharp/Formats/WebP/Vp8BitReader.cs
  4. 14
      src/ImageSharp/Formats/WebP/Vp8LBitReader.cs
  5. 17
      src/ImageSharp/Formats/WebP/WebPDecoderCore.cs
  6. 13
      src/ImageSharp/Formats/WebP/WebPFeatures.cs
  7. 16
      src/ImageSharp/Formats/WebP/WebPImageInfo.cs
  8. 2
      src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs

12
src/ImageSharp/Formats/WebP/AlphaDecoder.cs

@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// <param name="alphaChunkHeader">The first byte of the alpha image stream contains information on ow to decode the stream.</param> /// <param name="alphaChunkHeader">The first byte of the alpha image stream contains information on ow to decode the stream.</param>
/// <param name="memoryAllocator">Used for allocating memory during decoding.</param> /// <param name="memoryAllocator">Used for allocating memory during decoding.</param>
/// <param name="configuration">The configuration.</param> /// <param name="configuration">The configuration.</param>
public AlphaDecoder(int width, int height, byte[] data, byte alphaChunkHeader, MemoryAllocator memoryAllocator, Configuration configuration) public AlphaDecoder(int width, int height, IMemoryOwner<byte> data, byte alphaChunkHeader, MemoryAllocator memoryAllocator, Configuration configuration)
{ {
this.Width = width; this.Width = width;
this.Height = height; this.Height = height;
@ -116,7 +116,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// <summary> /// <summary>
/// Gets the (maybe compressed) alpha data. /// Gets the (maybe compressed) alpha data.
/// </summary> /// </summary>
private byte[] Data { get; } private IMemoryOwner<byte> Data { get; }
/// <summary> /// <summary>
/// Gets the Vp8L decoder which is used to de compress the alpha channel, if needed. /// Gets the Vp8L decoder which is used to de compress the alpha channel, if needed.
@ -137,7 +137,8 @@ namespace SixLabors.ImageSharp.Formats.WebP
{ {
if (this.Compressed is false) if (this.Compressed is false)
{ {
if (this.Data.Length < (this.Width * this.Height)) Span<byte> dataSpan = this.Data.Memory.Span;
if (dataSpan.Length < (this.Width * this.Height))
{ {
WebPThrowHelper.ThrowImageFormatException("not enough data in the ALPH chunk"); WebPThrowHelper.ThrowImageFormatException("not enough data in the ALPH chunk");
} }
@ -145,11 +146,11 @@ namespace SixLabors.ImageSharp.Formats.WebP
Span<byte> alphaSpan = this.Alpha.Memory.Span; Span<byte> alphaSpan = this.Alpha.Memory.Span;
if (this.AlphaFilterType == WebPAlphaFilterType.None) if (this.AlphaFilterType == WebPAlphaFilterType.None)
{ {
this.Data.AsSpan(0, this.Width * this.Height).CopyTo(alphaSpan); dataSpan.Slice(0, this.Width * this.Height).CopyTo(alphaSpan);
return; return;
} }
Span<byte> deltas = this.Data.AsSpan(); Span<byte> deltas = dataSpan;
Span<byte> dst = alphaSpan; Span<byte> dst = alphaSpan;
Span<byte> prev = null; Span<byte> prev = null;
for (int y = 0; y < this.Height; ++y) for (int y = 0; y < this.Height; ++y)
@ -305,6 +306,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
public void Dispose() public void Dispose()
{ {
this.Vp8LDec?.Dispose(); this.Vp8LDec?.Dispose();
this.Data.Dispose();
this.Alpha?.Dispose(); this.Alpha?.Dispose();
} }
} }

17
src/ImageSharp/Formats/WebP/BitReaderBase.cs

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Buffers;
using System.IO; using System.IO;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
@ -11,12 +12,12 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// <summary> /// <summary>
/// Base class for VP8 and VP8L bitreader. /// Base class for VP8 and VP8L bitreader.
/// </summary> /// </summary>
internal abstract class BitReaderBase internal abstract class BitReaderBase : IDisposable
{ {
/// <summary> /// <summary>
/// Gets or sets the raw encoded image data. /// Gets or sets the raw encoded image data.
/// </summary> /// </summary>
public byte[] Data { get; set; } public IMemoryOwner<byte> Data { get; set; }
/// <summary> /// <summary>
/// Copies the raw encoded image data from the stream into a byte array. /// Copies the raw encoded image data from the stream into a byte array.
@ -26,24 +27,28 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// <param name="memoryAllocator">Used for allocating memory during reading data from the stream.</param> /// <param name="memoryAllocator">Used for allocating memory during reading data from the stream.</param>
protected void ReadImageDataFromStream(Stream input, int bytesToRead, MemoryAllocator memoryAllocator) protected void ReadImageDataFromStream(Stream input, int bytesToRead, MemoryAllocator memoryAllocator)
{ {
using (var ms = new MemoryStream()) this.Data = memoryAllocator.Allocate<byte>(bytesToRead);
Span<byte> dataSpan = this.Data.Memory.Span;
using (IManagedByteBuffer buffer = memoryAllocator.AllocateManagedByteBuffer(4096)) using (IManagedByteBuffer buffer = memoryAllocator.AllocateManagedByteBuffer(4096))
{ {
Span<byte> bufferSpan = buffer.GetSpan(); Span<byte> bufferSpan = buffer.GetSpan();
int read; int read;
while (bytesToRead > 0 && (read = input.Read(buffer.Array, 0, Math.Min(bufferSpan.Length, bytesToRead))) > 0) while (bytesToRead > 0 && (read = input.Read(buffer.Array, 0, Math.Min(bufferSpan.Length, bytesToRead))) > 0)
{ {
ms.Write(buffer.Array, 0, read); buffer.Array.AsSpan(0, read).CopyTo(dataSpan);
bytesToRead -= read; bytesToRead -= read;
dataSpan = dataSpan.Slice(read);
} }
if (bytesToRead > 0) if (bytesToRead > 0)
{ {
WebPThrowHelper.ThrowImageFormatException("image file has insufficient data"); WebPThrowHelper.ThrowImageFormatException("image file has insufficient data");
} }
this.Data = ms.ToArray();
} }
} }
/// <inheritdoc/>
public void Dispose() => this.Data?.Dispose();
} }
} }

10
src/ImageSharp/Formats/WebP/Vp8BitReader.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System.Buffers;
using System.Buffers.Binary; using System.Buffers.Binary;
using System.IO; using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -70,10 +70,10 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// <param name="imageData">The raw encoded image data.</param> /// <param name="imageData">The raw encoded image data.</param>
/// <param name="partitionLength">The partition length.</param> /// <param name="partitionLength">The partition length.</param>
/// <param name="startPos">Start index in the data array. Defaults to 0.</param> /// <param name="startPos">Start index in the data array. Defaults to 0.</param>
public Vp8BitReader(byte[] imageData, uint partitionLength, int startPos = 0) public Vp8BitReader(IMemoryOwner<byte> imageData, uint partitionLength, int startPos = 0)
{ {
this.Data = imageData; this.Data = imageData;
this.ImageDataSize = (uint)imageData.Length; this.ImageDataSize = (uint)imageData.Memory.Length;
this.PartitionLength = partitionLength; this.PartitionLength = partitionLength;
this.InitBitreader(partitionLength, startPos); this.InitBitreader(partitionLength, startPos);
} }
@ -184,7 +184,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
{ {
if (this.pos < this.bufferMax) if (this.pos < this.bufferMax)
{ {
ulong inBits = BinaryPrimitives.ReadUInt64LittleEndian(this.Data.AsSpan((int)this.pos, 8)); ulong inBits = BinaryPrimitives.ReadUInt64LittleEndian(this.Data.Memory.Span.Slice((int)this.pos, 8));
this.pos += BitsCount >> 3; this.pos += BitsCount >> 3;
ulong bits = this.ByteSwap64(inBits); ulong bits = this.ByteSwap64(inBits);
bits >>= 64 - BitsCount; bits >>= 64 - BitsCount;
@ -203,7 +203,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
if (this.pos < this.bufferEnd) if (this.pos < this.bufferEnd)
{ {
this.bits += 8; this.bits += 8;
this.value = this.Data[this.pos++] | (this.value << 8); this.value = this.Data.Memory.Span[(int)this.pos++] | (this.value << 8);
} }
else if (!this.eof) else if (!this.eof)
{ {

14
src/ImageSharp/Formats/WebP/Vp8LBitReader.cs

@ -1,6 +1,7 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.Buffers;
using System.IO; using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
@ -62,18 +63,19 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// Initializes a new instance of the <see cref="Vp8LBitReader"/> class. /// Initializes a new instance of the <see cref="Vp8LBitReader"/> class.
/// </summary> /// </summary>
/// <param name="data">Lossless compressed image data.</param> /// <param name="data">Lossless compressed image data.</param>
public Vp8LBitReader(byte[] data) public Vp8LBitReader(IMemoryOwner<byte> data)
{ {
this.Data = data; this.Data = data;
this.len = data.Length; this.len = data.Memory.Length;
this.value = 0; this.value = 0;
this.bitPos = 0; this.bitPos = 0;
this.Eos = false; this.Eos = false;
ulong currentValue = 0; ulong currentValue = 0;
System.Span<byte> dataSpan = this.Data.Memory.Span;
for (int i = 0; i < 8; ++i) for (int i = 0; i < 8; ++i)
{ {
currentValue |= (ulong)this.Data[i] << (8 * i); currentValue |= (ulong)dataSpan[i] << (8 * i);
} }
this.value = currentValue; this.value = currentValue;
@ -103,9 +105,10 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
ulong currentValue = 0; ulong currentValue = 0;
System.Span<byte> dataSpan = this.Data.Memory.Span;
for (int i = 0; i < length; ++i) for (int i = 0; i < length; ++i)
{ {
currentValue |= (ulong)this.Data[i] << (8 * i); currentValue |= (ulong)dataSpan[i] << (8 * i);
} }
this.value = currentValue; this.value = currentValue;
@ -200,10 +203,11 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// </summary> /// </summary>
private void ShiftBytes() private void ShiftBytes()
{ {
System.Span<byte> dataSpan = this.Data.Memory.Span;
while (this.bitPos >= 8 && this.pos < this.len) while (this.bitPos >= 8 && this.pos < this.len)
{ {
this.value >>= 8; this.value >>= 8;
this.value |= (ulong)this.Data[this.pos] << (Lbits - 8); this.value |= (ulong)dataSpan[(int)this.pos] << (Lbits - 8);
++this.pos; ++this.pos;
this.bitPos -= 8; this.bitPos -= 8;
} }

17
src/ImageSharp/Formats/WebP/WebPDecoderCore.cs

@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
this.currentStream = stream; this.currentStream = stream;
uint fileSize = this.ReadImageHeader(); uint fileSize = this.ReadImageHeader();
WebPImageInfo imageInfo = this.ReadVp8Info(); using var imageInfo = this.ReadVp8Info();
if (imageInfo.Features != null && imageInfo.Features.Animation) if (imageInfo.Features != null && imageInfo.Features.Animation)
{ {
WebPThrowHelper.ThrowNotSupportedException("Animations are not supported"); WebPThrowHelper.ThrowNotSupportedException("Animations are not supported");
@ -186,7 +186,8 @@ namespace SixLabors.ImageSharp.Formats.WebP
// The first two bit of it are reserved and should be 0. // The first two bit of it are reserved and should be 0.
if (imageFeatures >> 6 != 0) if (imageFeatures >> 6 != 0)
{ {
WebPThrowHelper.ThrowImageFormatException("first two bits of the VP8X header are expected to be zero"); WebPThrowHelper.ThrowImageFormatException(
"first two bits of the VP8X header are expected to be zero");
} }
// If bit 3 is set, a ICC Profile Chunk should be present. // If bit 3 is set, a ICC Profile Chunk should be present.
@ -232,12 +233,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
if (features.Animation) if (features.Animation)
{ {
// TODO: Animations are not yet supported. // TODO: Animations are not yet supported.
return new WebPImageInfo() return new WebPImageInfo() { Width = width, Height = height, Features = features };
{
Width = width,
Height = height,
Features = features
};
} }
// TODO: check if VP8 or VP8L info about the dimensions match VP8X info // TODO: check if VP8 or VP8L info about the dimensions match VP8X info
@ -444,8 +440,9 @@ namespace SixLabors.ImageSharp.Formats.WebP
case WebPChunkType.Alpha: case WebPChunkType.Alpha:
uint alphaChunkSize = this.ReadChunkSize(); uint alphaChunkSize = this.ReadChunkSize();
features.AlphaChunkHeader = (byte)this.currentStream.ReadByte(); features.AlphaChunkHeader = (byte)this.currentStream.ReadByte();
features.AlphaData = new byte[alphaChunkSize - 1]; var alphaDataSize = (int)(alphaChunkSize - 1);
this.currentStream.Read(features.AlphaData, 0, features.AlphaData.Length); features.AlphaData = this.memoryAllocator.Allocate<byte>(alphaDataSize);
this.currentStream.Read(features.AlphaData.Memory.Span, 0, alphaDataSize);
break; break;
} }
} }

13
src/ImageSharp/Formats/WebP/WebPFeatures.cs

@ -1,12 +1,15 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
namespace SixLabors.ImageSharp.Formats.WebP namespace SixLabors.ImageSharp.Formats.WebP
{ {
/// <summary> /// <summary>
/// Image features of a VP8X image. /// Image features of a VP8X image.
/// </summary> /// </summary>
internal class WebPFeatures internal class WebPFeatures : IDisposable
{ {
/// <summary> /// <summary>
/// Gets or sets a value indicating whether this image has a ICC Profile. /// Gets or sets a value indicating whether this image has a ICC Profile.
@ -21,7 +24,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// <summary> /// <summary>
/// Gets or sets the alpha data, if an ALPH chunk is present. /// Gets or sets the alpha data, if an ALPH chunk is present.
/// </summary> /// </summary>
public byte[] AlphaData { get; set; } public IMemoryOwner<byte> AlphaData { get; set; }
/// <summary> /// <summary>
/// Gets or sets the alpha chunk header. /// Gets or sets the alpha chunk header.
@ -42,5 +45,11 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// Gets or sets a value indicating whether this image is a animation. /// Gets or sets a value indicating whether this image is a animation.
/// </summary> /// </summary>
public bool Animation { get; set; } public bool Animation { get; set; }
/// <inheritdoc/>
public void Dispose()
{
this.AlphaData?.Dispose();
}
} }
} }

16
src/ImageSharp/Formats/WebP/WebPImageInfo.cs

@ -1,17 +1,19 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
namespace SixLabors.ImageSharp.Formats.WebP namespace SixLabors.ImageSharp.Formats.WebP
{ {
internal class WebPImageInfo internal class WebPImageInfo : IDisposable
{ {
/// <summary> /// <summary>
/// Gets or sets the bitmap width in pixels (signed integer). /// Gets or sets the bitmap width in pixels.
/// </summary> /// </summary>
public uint Width { get; set; } public uint Width { get; set; }
/// <summary> /// <summary>
/// Gets or sets the bitmap height in pixels (signed integer). /// Gets or sets the bitmap height in pixels.
/// </summary> /// </summary>
public uint Height { get; set; } public uint Height { get; set; }
@ -53,5 +55,13 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// Gets or sets the VP8 bitreader. Will be null, if its not a lossy image. /// Gets or sets the VP8 bitreader. Will be null, if its not a lossy image.
/// </summary> /// </summary>
public Vp8BitReader Vp8BitReader { get; set; } = null; public Vp8BitReader Vp8BitReader { get; set; } = null;
/// <inheritdoc/>
public void Dispose()
{
this.Vp8BitReader?.Dispose();
this.Vp8LBitReader?.Dispose();
this.Features?.AlphaData?.Dispose();
}
} }
} }

2
src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs

@ -1195,7 +1195,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
{ {
uint size = this.bitReader.Remaining - this.bitReader.PartitionLength; uint size = this.bitReader.Remaining - this.bitReader.PartitionLength;
int startIdx = (int)this.bitReader.PartitionLength; int startIdx = (int)this.bitReader.PartitionLength;
Span<byte> sz = this.bitReader.Data.AsSpan(startIdx); Span<byte> sz = this.bitReader.Data.Slice(startIdx);
int sizeLeft = (int)size; int sizeLeft = (int)size;
dec.NumPartsMinusOne = (1 << (int)this.bitReader.ReadValue(2)) - 1; dec.NumPartsMinusOne = (1 << (int)this.bitReader.ReadValue(2)) - 1;
int lastPart = dec.NumPartsMinusOne; int lastPart = dec.NumPartsMinusOne;

Loading…
Cancel
Save