diff --git a/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs b/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs index fd16b3d268..73246778c1 100644 --- a/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs +++ b/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs @@ -192,7 +192,7 @@ namespace SixLabors.ImageSharp.Formats.Webp this.RestoreToBackground(imageFrame, backgroundColor); } - using Image decodedImage = this.DecodeImageData(frameData, webpInfo); + using Buffer2D decodedImage = this.DecodeImageData(frameData, webpInfo); this.DrawDecodedImageOnCanvas(decodedImage, imageFrame, frameX, frameY, frameWidth, frameHeight); if (previousFrame != null && frameData.BlendingMethod is AnimationBlendingMethod.AlphaBlending) @@ -244,7 +244,7 @@ namespace SixLabors.ImageSharp.Formats.Webp /// The frame data. /// The webp information. /// A decoded image. - private Image DecodeImageData(AnimationFrameData frameData, WebpImageInfo webpInfo) + private Buffer2D DecodeImageData(AnimationFrameData frameData, WebpImageInfo webpInfo) where TPixel : unmanaged, IPixel { var decodedImage = new Image((int)frameData.Width, (int)frameData.Height); @@ -262,6 +262,8 @@ namespace SixLabors.ImageSharp.Formats.Webp var lossyDecoder = new WebpLossyDecoder(webpInfo.Vp8BitReader, this.memoryAllocator, this.configuration); lossyDecoder.Decode(pixelBufferDecoded, (int)webpInfo.Width, (int)webpInfo.Height, webpInfo, this.AlphaData); } + + return pixelBufferDecoded; } catch { @@ -272,8 +274,6 @@ namespace SixLabors.ImageSharp.Formats.Webp { webpInfo.Dispose(); } - - return decodedImage; } /// @@ -286,16 +286,15 @@ namespace SixLabors.ImageSharp.Formats.Webp /// The frame y coordinate. /// The width of the frame. /// The height of the frame. - private void DrawDecodedImageOnCanvas(Image decodedImage, ImageFrame imageFrame, int frameX, int frameY, int frameWidth, int frameHeight) + private void DrawDecodedImageOnCanvas(Buffer2D decodedImage, ImageFrame imageFrame, int frameX, int frameY, int frameWidth, int frameHeight) where TPixel : unmanaged, IPixel { - Buffer2D decodedImagePixels = decodedImage.Frames.RootFrame.PixelBuffer; Buffer2D imageFramePixels = imageFrame.PixelBuffer; int decodedRowIdx = 0; for (int y = frameY; y < frameY + frameHeight; y++) { Span framePixelRow = imageFramePixels.DangerousGetRowSpan(y); - Span decodedPixelRow = decodedImagePixels.DangerousGetRowSpan(decodedRowIdx++).Slice(0, frameWidth); + Span decodedPixelRow = decodedImage.DangerousGetRowSpan(decodedRowIdx++).Slice(0, frameWidth); decodedPixelRow.TryCopyTo(framePixelRow.Slice(frameX)); } } diff --git a/src/ImageSharp/Formats/Webp/WebpChunkParsingUtils.cs b/src/ImageSharp/Formats/Webp/WebpChunkParsingUtils.cs index 5b8e1857c7..26d82a8929 100644 --- a/src/ImageSharp/Formats/Webp/WebpChunkParsingUtils.cs +++ b/src/ImageSharp/Formats/Webp/WebpChunkParsingUtils.cs @@ -3,9 +3,9 @@ using System; using System.Buffers.Binary; -using System.IO; using SixLabors.ImageSharp.Formats.Webp.BitReader; using SixLabors.ImageSharp.Formats.Webp.Lossy; +using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; @@ -19,10 +19,15 @@ namespace SixLabors.ImageSharp.Formats.Webp /// Reads the header of a lossy webp image. /// /// Information about this webp image. - public static WebpImageInfo ReadVp8Header(MemoryAllocator memoryAllocator, Stream stream, byte[] buffer, WebpFeatures features) + public static WebpImageInfo ReadVp8Header(MemoryAllocator memoryAllocator, BufferedReadStream stream, byte[] buffer, WebpFeatures features) { // VP8 data size (not including this 4 bytes). - stream.Read(buffer, 0, 4); + int bytesRead = stream.Read(buffer, 0, 4); + if (bytesRead != 4) + { + WebpThrowHelper.ThrowInvalidImageContentException("Not enough data to read the VP8 header"); + } + uint dataSize = BinaryPrimitives.ReadUInt32LittleEndian(buffer); // Remaining counts the available image data payload. @@ -34,7 +39,12 @@ namespace SixLabors.ImageSharp.Formats.Webp // - A 3-bit version number. // - A 1-bit show_frame flag. // - A 19-bit field containing the size of the first data partition in bytes. - stream.Read(buffer, 0, 3); + bytesRead = stream.Read(buffer, 0, 3); + if (bytesRead != 3) + { + WebpThrowHelper.ThrowInvalidImageContentException("Not enough data to read the VP8 header"); + } + uint frameTag = (uint)(buffer[0] | (buffer[1] << 8) | (buffer[2] << 16)); remaining -= 3; bool isNoKeyFrame = (frameTag & 0x1) == 1; @@ -62,17 +72,27 @@ namespace SixLabors.ImageSharp.Formats.Webp } // Check for VP8 magic bytes. - stream.Read(buffer, 0, 3); + bytesRead = stream.Read(buffer, 0, 3); + if (bytesRead != 3) + { + WebpThrowHelper.ThrowInvalidImageContentException("Not enough data to read the VP8 magic bytes"); + } + if (!buffer.AsSpan(0, 3).SequenceEqual(WebpConstants.Vp8HeaderMagicBytes)) { WebpThrowHelper.ThrowImageFormatException("VP8 magic bytes not found"); } - stream.Read(buffer, 0, 4); - uint tmp = (uint)BinaryPrimitives.ReadInt16LittleEndian(buffer); + bytesRead = stream.Read(buffer, 0, 4); + if (bytesRead != 4) + { + WebpThrowHelper.ThrowInvalidImageContentException("Not enough data to read the VP8 header, could not read width and height"); + } + + uint tmp = BinaryPrimitives.ReadUInt16LittleEndian(buffer); uint width = tmp & 0x3fff; sbyte xScale = (sbyte)(tmp >> 6); - tmp = (uint)BinaryPrimitives.ReadInt16LittleEndian(buffer.AsSpan(2)); + tmp = BinaryPrimitives.ReadUInt16LittleEndian(buffer.AsSpan(2)); uint height = tmp & 0x3fff; sbyte yScale = (sbyte)(tmp >> 6); remaining -= 7; @@ -121,7 +141,7 @@ namespace SixLabors.ImageSharp.Formats.Webp /// Reads the header of a lossless webp image. /// /// Information about this image. - public static WebpImageInfo ReadVp8LHeader(MemoryAllocator memoryAllocator, Stream stream, byte[] buffer, WebpFeatures features) + public static WebpImageInfo ReadVp8LHeader(MemoryAllocator memoryAllocator, BufferedReadStream stream, byte[] buffer, WebpFeatures features) { // VP8 data size. uint imageDataSize = ReadChunkSize(stream, buffer); @@ -176,7 +196,7 @@ namespace SixLabors.ImageSharp.Formats.Webp /// After the image header, image data will follow. After that optional image metadata chunks (EXIF and XMP) can follow. /// /// Information about this webp image. - public static WebpImageInfo ReadVp8XHeader(Stream stream, byte[] buffer, WebpFeatures features) + public static WebpImageInfo ReadVp8XHeader(BufferedReadStream stream, byte[] buffer, WebpFeatures features) { uint fileSize = ReadChunkSize(stream, buffer); @@ -234,11 +254,15 @@ namespace SixLabors.ImageSharp.Formats.Webp /// The stream to read from. /// The buffer to store the read data into. /// A unsigned 24 bit integer. - public static uint ReadUnsignedInt24Bit(Stream stream, byte[] buffer) + public static uint ReadUnsignedInt24Bit(BufferedReadStream stream, byte[] buffer) { - stream.Read(buffer, 0, 3); - buffer[3] = 0; - return (uint)BinaryPrimitives.ReadInt32LittleEndian(buffer); + if (stream.Read(buffer, 0, 3) == 3) + { + buffer[3] = 0; + return BinaryPrimitives.ReadUInt32LittleEndian(buffer); + } + + throw new ImageFormatException("Invalid Webp data, could not read unsigned integer."); } /// @@ -248,7 +272,7 @@ namespace SixLabors.ImageSharp.Formats.Webp /// The stream to read the data from. /// Buffer to store the data read from the stream. /// The chunk size in bytes. - public static uint ReadChunkSize(Stream stream, byte[] buffer) + public static uint ReadChunkSize(BufferedReadStream stream, byte[] buffer) { if (stream.Read(buffer, 0, 4) == 4) { @@ -267,7 +291,7 @@ namespace SixLabors.ImageSharp.Formats.Webp /// /// Thrown if the input stream is not valid. /// - public static WebpChunkType ReadChunkType(Stream stream, byte[] buffer) + public static WebpChunkType ReadChunkType(BufferedReadStream stream, byte[] buffer) { if (stream.Read(buffer, 0, 4) == 4) { @@ -283,7 +307,7 @@ namespace SixLabors.ImageSharp.Formats.Webp /// If there are more such chunks, readers MAY ignore all except the first one. /// Also, a file may possibly contain both 'EXIF' and 'XMP ' chunks. /// - public static void ParseOptionalChunks(Stream stream, WebpChunkType chunkType, ImageMetadata metadata, bool ignoreMetaData, byte[] buffer) + public static void ParseOptionalChunks(BufferedReadStream stream, WebpChunkType chunkType, ImageMetadata metadata, bool ignoreMetaData, byte[] buffer) { long streamLength = stream.Length; while (stream.Position < streamLength) diff --git a/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs index 84ee5d0a2c..979ac55825 100644 --- a/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs +++ b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs @@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Formats.Webp /// /// The stream to decode from. /// - private Stream currentStream; + private BufferedReadStream currentStream; /// /// The webp specific metadata.