diff --git a/src/ImageSharp/Formats/WebP/AlphaDecoder.cs b/src/ImageSharp/Formats/WebP/AlphaDecoder.cs index 3a5f0ca4fc..6737c4d098 100644 --- a/src/ImageSharp/Formats/WebP/AlphaDecoder.cs +++ b/src/ImageSharp/Formats/WebP/AlphaDecoder.cs @@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Formats.WebP /// The first byte of the alpha image stream contains information on ow to decode the stream. /// Used for allocating memory during decoding. /// The configuration. - public AlphaDecoder(int width, int height, byte[] data, byte alphaChunkHeader, MemoryAllocator memoryAllocator, Configuration configuration) + public AlphaDecoder(int width, int height, IMemoryOwner data, byte alphaChunkHeader, MemoryAllocator memoryAllocator, Configuration configuration) { this.Width = width; this.Height = height; @@ -116,7 +116,7 @@ namespace SixLabors.ImageSharp.Formats.WebP /// /// Gets the (maybe compressed) alpha data. /// - private byte[] Data { get; } + private IMemoryOwner Data { get; } /// /// 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.Data.Length < (this.Width * this.Height)) + Span dataSpan = this.Data.Memory.Span; + if (dataSpan.Length < (this.Width * this.Height)) { WebPThrowHelper.ThrowImageFormatException("not enough data in the ALPH chunk"); } @@ -145,11 +146,11 @@ namespace SixLabors.ImageSharp.Formats.WebP Span alphaSpan = this.Alpha.Memory.Span; 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; } - Span deltas = this.Data.AsSpan(); + Span deltas = dataSpan; Span dst = alphaSpan; Span prev = null; for (int y = 0; y < this.Height; ++y) @@ -305,6 +306,7 @@ namespace SixLabors.ImageSharp.Formats.WebP public void Dispose() { this.Vp8LDec?.Dispose(); + this.Data.Dispose(); this.Alpha?.Dispose(); } } diff --git a/src/ImageSharp/Formats/WebP/BitReaderBase.cs b/src/ImageSharp/Formats/WebP/BitReaderBase.cs index 3a5bf4f4ae..f35368c15a 100644 --- a/src/ImageSharp/Formats/WebP/BitReaderBase.cs +++ b/src/ImageSharp/Formats/WebP/BitReaderBase.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.IO; using SixLabors.ImageSharp.Memory; @@ -11,12 +12,12 @@ namespace SixLabors.ImageSharp.Formats.WebP /// /// Base class for VP8 and VP8L bitreader. /// - internal abstract class BitReaderBase + internal abstract class BitReaderBase : IDisposable { /// /// Gets or sets the raw encoded image data. /// - public byte[] Data { get; set; } + public IMemoryOwner Data { get; set; } /// /// Copies the raw encoded image data from the stream into a byte array. @@ -26,24 +27,28 @@ namespace SixLabors.ImageSharp.Formats.WebP /// Used for allocating memory during reading data from the stream. protected void ReadImageDataFromStream(Stream input, int bytesToRead, MemoryAllocator memoryAllocator) { - using (var ms = new MemoryStream()) + this.Data = memoryAllocator.Allocate(bytesToRead); + Span dataSpan = this.Data.Memory.Span; + using (IManagedByteBuffer buffer = memoryAllocator.AllocateManagedByteBuffer(4096)) { Span bufferSpan = buffer.GetSpan(); int read; 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; + dataSpan = dataSpan.Slice(read); } if (bytesToRead > 0) { WebPThrowHelper.ThrowImageFormatException("image file has insufficient data"); } - - this.Data = ms.ToArray(); } } + + /// + public void Dispose() => this.Data?.Dispose(); } } diff --git a/src/ImageSharp/Formats/WebP/Vp8BitReader.cs b/src/ImageSharp/Formats/WebP/Vp8BitReader.cs index 742125963c..66681b89de 100644 --- a/src/ImageSharp/Formats/WebP/Vp8BitReader.cs +++ b/src/ImageSharp/Formats/WebP/Vp8BitReader.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; +using System.Buffers; using System.Buffers.Binary; using System.IO; using System.Runtime.CompilerServices; @@ -70,10 +70,10 @@ namespace SixLabors.ImageSharp.Formats.WebP /// The raw encoded image data. /// The partition length. /// Start index in the data array. Defaults to 0. - public Vp8BitReader(byte[] imageData, uint partitionLength, int startPos = 0) + public Vp8BitReader(IMemoryOwner imageData, uint partitionLength, int startPos = 0) { this.Data = imageData; - this.ImageDataSize = (uint)imageData.Length; + this.ImageDataSize = (uint)imageData.Memory.Length; this.PartitionLength = partitionLength; this.InitBitreader(partitionLength, startPos); } @@ -184,7 +184,7 @@ namespace SixLabors.ImageSharp.Formats.WebP { 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; ulong bits = this.ByteSwap64(inBits); bits >>= 64 - BitsCount; @@ -203,7 +203,7 @@ namespace SixLabors.ImageSharp.Formats.WebP if (this.pos < this.bufferEnd) { 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) { diff --git a/src/ImageSharp/Formats/WebP/Vp8LBitReader.cs b/src/ImageSharp/Formats/WebP/Vp8LBitReader.cs index 8990de769c..f90023b89c 100644 --- a/src/ImageSharp/Formats/WebP/Vp8LBitReader.cs +++ b/src/ImageSharp/Formats/WebP/Vp8LBitReader.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System.Buffers; using System.IO; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Memory; @@ -62,18 +63,19 @@ namespace SixLabors.ImageSharp.Formats.WebP /// Initializes a new instance of the class. /// /// Lossless compressed image data. - public Vp8LBitReader(byte[] data) + public Vp8LBitReader(IMemoryOwner data) { this.Data = data; - this.len = data.Length; + this.len = data.Memory.Length; this.value = 0; this.bitPos = 0; this.Eos = false; ulong currentValue = 0; + System.Span dataSpan = this.Data.Memory.Span; for (int i = 0; i < 8; ++i) { - currentValue |= (ulong)this.Data[i] << (8 * i); + currentValue |= (ulong)dataSpan[i] << (8 * i); } this.value = currentValue; @@ -103,9 +105,10 @@ namespace SixLabors.ImageSharp.Formats.WebP } ulong currentValue = 0; + System.Span dataSpan = this.Data.Memory.Span; for (int i = 0; i < length; ++i) { - currentValue |= (ulong)this.Data[i] << (8 * i); + currentValue |= (ulong)dataSpan[i] << (8 * i); } this.value = currentValue; @@ -200,10 +203,11 @@ namespace SixLabors.ImageSharp.Formats.WebP /// private void ShiftBytes() { + System.Span dataSpan = this.Data.Memory.Span; while (this.bitPos >= 8 && this.pos < this.len) { this.value >>= 8; - this.value |= (ulong)this.Data[this.pos] << (Lbits - 8); + this.value |= (ulong)dataSpan[(int)this.pos] << (Lbits - 8); ++this.pos; this.bitPos -= 8; } diff --git a/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs b/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs index e6f5dfe3e4..f0060edaf5 100644 --- a/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs +++ b/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs @@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp.Formats.WebP this.currentStream = stream; uint fileSize = this.ReadImageHeader(); - WebPImageInfo imageInfo = this.ReadVp8Info(); + using var imageInfo = this.ReadVp8Info(); if (imageInfo.Features != null && imageInfo.Features.Animation) { 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. 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. @@ -232,12 +233,7 @@ namespace SixLabors.ImageSharp.Formats.WebP if (features.Animation) { // TODO: Animations are not yet supported. - return new WebPImageInfo() - { - Width = width, - Height = height, - Features = features - }; + return new WebPImageInfo() { Width = width, Height = height, Features = features }; } // 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: uint alphaChunkSize = this.ReadChunkSize(); features.AlphaChunkHeader = (byte)this.currentStream.ReadByte(); - features.AlphaData = new byte[alphaChunkSize - 1]; - this.currentStream.Read(features.AlphaData, 0, features.AlphaData.Length); + var alphaDataSize = (int)(alphaChunkSize - 1); + features.AlphaData = this.memoryAllocator.Allocate(alphaDataSize); + this.currentStream.Read(features.AlphaData.Memory.Span, 0, alphaDataSize); break; } } diff --git a/src/ImageSharp/Formats/WebP/WebPFeatures.cs b/src/ImageSharp/Formats/WebP/WebPFeatures.cs index 3fd0350766..cf1053057c 100644 --- a/src/ImageSharp/Formats/WebP/WebPFeatures.cs +++ b/src/ImageSharp/Formats/WebP/WebPFeatures.cs @@ -1,12 +1,15 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; +using System.Buffers; + namespace SixLabors.ImageSharp.Formats.WebP { /// /// Image features of a VP8X image. /// - internal class WebPFeatures + internal class WebPFeatures : IDisposable { /// /// Gets or sets a value indicating whether this image has a ICC Profile. @@ -21,7 +24,7 @@ namespace SixLabors.ImageSharp.Formats.WebP /// /// Gets or sets the alpha data, if an ALPH chunk is present. /// - public byte[] AlphaData { get; set; } + public IMemoryOwner AlphaData { get; set; } /// /// 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. /// public bool Animation { get; set; } + + /// + public void Dispose() + { + this.AlphaData?.Dispose(); + } } } diff --git a/src/ImageSharp/Formats/WebP/WebPImageInfo.cs b/src/ImageSharp/Formats/WebP/WebPImageInfo.cs index 19a72c0c28..b7aa4b36a1 100644 --- a/src/ImageSharp/Formats/WebP/WebPImageInfo.cs +++ b/src/ImageSharp/Formats/WebP/WebPImageInfo.cs @@ -1,17 +1,19 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; + namespace SixLabors.ImageSharp.Formats.WebP { - internal class WebPImageInfo + internal class WebPImageInfo : IDisposable { /// - /// Gets or sets the bitmap width in pixels (signed integer). + /// Gets or sets the bitmap width in pixels. /// public uint Width { get; set; } /// - /// Gets or sets the bitmap height in pixels (signed integer). + /// Gets or sets the bitmap height in pixels. /// 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. /// public Vp8BitReader Vp8BitReader { get; set; } = null; + + /// + public void Dispose() + { + this.Vp8BitReader?.Dispose(); + this.Vp8LBitReader?.Dispose(); + this.Features?.AlphaData?.Dispose(); + } } } diff --git a/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs b/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs index 0b26727d59..a741ad42d3 100644 --- a/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs +++ b/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs @@ -1195,7 +1195,7 @@ namespace SixLabors.ImageSharp.Formats.WebP { uint size = this.bitReader.Remaining - this.bitReader.PartitionLength; int startIdx = (int)this.bitReader.PartitionLength; - Span sz = this.bitReader.Data.AsSpan(startIdx); + Span sz = this.bitReader.Data.Slice(startIdx); int sizeLeft = (int)size; dec.NumPartsMinusOne = (1 << (int)this.bitReader.ReadValue(2)) - 1; int lastPart = dec.NumPartsMinusOne;