From 2acb3df0eaeccd73a136377787ac466fd5a83760 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 24 Jan 2020 17:00:11 +0100 Subject: [PATCH] Fix some issues in Vp8BitReader --- src/ImageSharp/Formats/WebP/LoopFilter.cs | 2 +- src/ImageSharp/Formats/WebP/Vp8BitReader.cs | 38 ++++++++++--------- .../Formats/WebP/WebPDecoderCore.cs | 27 +++++++++++-- .../Formats/WebP/WebPLossyDecoder.cs | 2 +- 4 files changed, 45 insertions(+), 24 deletions(-) diff --git a/src/ImageSharp/Formats/WebP/LoopFilter.cs b/src/ImageSharp/Formats/WebP/LoopFilter.cs index 80a6b95ed..acf7a1114 100644 --- a/src/ImageSharp/Formats/WebP/LoopFilter.cs +++ b/src/ImageSharp/Formats/WebP/LoopFilter.cs @@ -5,7 +5,7 @@ namespace SixLabors.ImageSharp.Formats.WebP { internal enum LoopFilter { - Normal, + Complex, Simple, None } diff --git a/src/ImageSharp/Formats/WebP/Vp8BitReader.cs b/src/ImageSharp/Formats/WebP/Vp8BitReader.cs index a3996b08d..5264f5bc1 100644 --- a/src/ImageSharp/Formats/WebP/Vp8BitReader.cs +++ b/src/ImageSharp/Formats/WebP/Vp8BitReader.cs @@ -36,16 +36,6 @@ namespace SixLabors.ImageSharp.Formats.WebP /// private byte buf; - /// - /// End of read buffer. - /// - // private byte bufEnd; - - /// - /// Max packed-read position on buffer. - /// - // private byte bufMax; - /// /// True if input is exhausted. /// @@ -68,7 +58,7 @@ namespace SixLabors.ImageSharp.Formats.WebP this.range = 255 - 1; this.value = 0; - this.bits = -8; // to load the very first 8bits; + this.bits = -8; // to load the very first 8bits. this.eof = false; this.pos = 0; @@ -87,7 +77,8 @@ namespace SixLabors.ImageSharp.Formats.WebP int pos = this.bits; uint split = (uint)((range * prob) >> 8); - bool bit = this.value > split; + ulong value = this.value >> pos; + bool bit = value > split; if (bit) { range -= split; @@ -117,9 +108,9 @@ namespace SixLabors.ImageSharp.Formats.WebP Guard.MustBeGreaterThan(nBits, 0, nameof(nBits)); uint v = 0; - while (this.bits-- > 0) + while (nBits-- > 0) { - v |= (uint)this.GetBit(0x80) << this.bits; + v |= (uint)this.GetBit(0x80) << nBits; } return v; @@ -140,7 +131,7 @@ namespace SixLabors.ImageSharp.Formats.WebP ulong bits; ulong inBits = BinaryPrimitives.ReadUInt64LittleEndian(this.Data.AsSpan().Slice((int)this.pos, 8)); this.pos += BitsCount >> 3; - this.buf = this.Data[BitsCount >> 3]; + this.buf = this.Data[this.pos]; bits = this.ByteSwap64(inBits); bits >>= 64 - BitsCount; this.value = bits | (this.value << BitsCount); @@ -182,9 +173,20 @@ namespace SixLabors.ImageSharp.Formats.WebP private int BitsLog2Floor(uint n) { - long firstSetBit; - throw new NotImplementedException(); - // BitScanReverse(firstSetBit, n); + // Search the mask data from most significant bit (MSB) to least significant bit (LSB) for a set bit (1). + // https://docs.microsoft.com/en-us/cpp/intrinsics/bitscanreverse-bitscanreverse64?view=vs-2019 + uint mask = 1; + for (int y = 0; y < 32; y++) + { + if ((mask & n) == mask) + { + return y; + } + + mask <<= 1; + } + + return 0; } } } diff --git a/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs b/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs index f0b0233cf..f5e1af2cb 100644 --- a/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs +++ b/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs @@ -284,7 +284,7 @@ namespace SixLabors.ImageSharp.Formats.WebP { this.webpMetadata.Format = WebPFormatType.Lossy; - // VP8 data size. + // VP8 data size (not including this 4 bytes). this.currentStream.Read(this.buffer, 0, 4); uint dataSize = BinaryPrimitives.ReadUInt32LittleEndian(this.buffer); @@ -339,6 +339,13 @@ namespace SixLabors.ImageSharp.Formats.WebP WebPThrowHelper.ThrowImageFormatException("width or height can not be zero"); } + // The size of the encoded image data payload. + uint imageDataSize = dataSize - 10; // we have read 10 bytes already. + if (partitionLength > imageDataSize) + { + WebPThrowHelper.ThrowImageFormatException("bad partition length"); + } + var vp8FrameHeader = new Vp8FrameHeader() { KeyFrame = true, @@ -346,7 +353,10 @@ namespace SixLabors.ImageSharp.Formats.WebP PartitionLength = partitionLength }; - var bitReader = new Vp8BitReader(this.currentStream, dataSize - 10, this.memoryAllocator); + var bitReader = new Vp8BitReader( + this.currentStream, + imageDataSize, + this.memoryAllocator); // Paragraph 9.2: color space and clamp type follow sbyte colorSpace = (sbyte)bitReader.ReadValue(1); @@ -394,10 +404,14 @@ namespace SixLabors.ImageSharp.Formats.WebP // Paragraph 9.4: Parse the filter specs. var vp8FilterHeader = new Vp8FilterHeader(); - vp8FilterHeader.LoopFilter = bitReader.ReadBool() ? LoopFilter.Simple : LoopFilter.Normal; + vp8FilterHeader.LoopFilter = bitReader.ReadBool() ? LoopFilter.Simple : LoopFilter.Complex; vp8FilterHeader.Level = (int)bitReader.ReadValue(6); vp8FilterHeader.Sharpness = (int)bitReader.ReadValue(3); vp8FilterHeader.UseLfDelta = bitReader.ReadBool(); + + // TODO: use enum here? + // 0 = 0ff, 1 = simple, 2 = complex + int filterType = (vp8FilterHeader.Level is 0) ? 0 : vp8FilterHeader.LoopFilter is LoopFilter.Simple ? 1 : 2; if (vp8FilterHeader.UseLfDelta) { // Update lf-delta? @@ -423,7 +437,12 @@ namespace SixLabors.ImageSharp.Formats.WebP } } - // TODO: ParsePartitions + // Paragraph 9.5: ParsePartitions. + int numPartsMinusOne = (1 << (int)bitReader.ReadValue(2)) - 1; + int lastPart = numPartsMinusOne; + // TODO: check if we have enough data available here, throw exception if not + + return new WebPImageInfo() { diff --git a/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs b/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs index 89f58f633..86fb099fd 100644 --- a/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs +++ b/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs @@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp.Formats.WebP switch (version) { case 0: - return new Vp8Profile { ReconstructionFilter = ReconstructionFilter.Bicubic, LoopFilter = LoopFilter.Normal }; + return new Vp8Profile { ReconstructionFilter = ReconstructionFilter.Bicubic, LoopFilter = LoopFilter.Complex }; case 1: return new Vp8Profile { ReconstructionFilter = ReconstructionFilter.Bilinear, LoopFilter = LoopFilter.Simple }; case 2: