From 7f5a48137bce45de62881deaf32d1c13a3a7e1c0 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 27 Jan 2020 14:44:23 +0100 Subject: [PATCH] Move parsing VP8 infos into separate methods --- src/ImageSharp/Formats/WebP/Vp8BitReader.cs | 7 +- .../Formats/WebP/WebPDecoderCore.cs | 179 ++++++++++-------- 2 files changed, 102 insertions(+), 84 deletions(-) diff --git a/src/ImageSharp/Formats/WebP/Vp8BitReader.cs b/src/ImageSharp/Formats/WebP/Vp8BitReader.cs index 4fee28d39b..2fdd4959fd 100644 --- a/src/ImageSharp/Formats/WebP/Vp8BitReader.cs +++ b/src/ImageSharp/Formats/WebP/Vp8BitReader.cs @@ -46,8 +46,6 @@ namespace SixLabors.ImageSharp.Formats.WebP /// private long pos; - public int Pos { get { return (int)pos; } } - /// /// Initializes a new instance of the class. /// @@ -61,6 +59,11 @@ namespace SixLabors.ImageSharp.Formats.WebP this.InitBitreader(startPos); } + public int Pos + { + get { return (int)this.pos; } + } + public int GetBit(int prob) { Guard.MustBeGreaterThan(prob, 0, nameof(prob)); diff --git a/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs b/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs index 89f54dec6b..73fb3fed23 100644 --- a/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs +++ b/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs @@ -288,6 +288,9 @@ namespace SixLabors.ImageSharp.Formats.WebP this.currentStream.Read(this.buffer, 0, 4); uint dataSize = BinaryPrimitives.ReadUInt32LittleEndian(this.buffer); + // remaining counts the available image data payload. + uint remaining = dataSize; + // See paragraph 9.1 https://tools.ietf.org/html/rfc6386#page-30 // Frame tag that contains four fields: // - A 1-bit frame type (0 for key frames, 1 for interframes). @@ -296,6 +299,7 @@ namespace SixLabors.ImageSharp.Formats.WebP // - A 19-bit field containing the size of the first data partition in bytes. this.currentStream.Read(this.buffer, 0, 3); uint frameTag = (uint)(this.buffer[0] | (this.buffer[1] << 8) | (this.buffer[2] << 16)); + remaining -= 3; bool isKeyFrame = (frameTag & 0x1) is 0; if (!isKeyFrame) { @@ -334,14 +338,13 @@ namespace SixLabors.ImageSharp.Formats.WebP tmp = (uint)BinaryPrimitives.ReadInt16LittleEndian(this.buffer.AsSpan(2)); uint height = tmp & 0x3fff; sbyte yScale = (sbyte)(tmp >> 6); + remaining -= 7; if (width is 0 || height is 0) { 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) + if (partitionLength > remaining) { WebPThrowHelper.ThrowImageFormatException("bad partition length"); } @@ -355,10 +358,10 @@ namespace SixLabors.ImageSharp.Formats.WebP var bitReader = new Vp8BitReader( this.currentStream, - imageDataSize, + remaining, this.memoryAllocator); - // Paragraph 9.2: color space and clamp type follow + // Paragraph 9.2: color space and clamp type follow. sbyte colorSpace = (sbyte)bitReader.ReadValue(1); sbyte clampType = (sbyte)bitReader.ReadValue(1); var vp8PictureHeader = new Vp8PictureHeader() @@ -372,90 +375,18 @@ namespace SixLabors.ImageSharp.Formats.WebP }; // Paragraph 9.3: Parse the segment header. - var vp8SegmentHeader = new Vp8SegmentHeader(); - var proba = new Vp8Proba(); - vp8SegmentHeader.UseSegment = bitReader.ReadBool(); bool hasValue; - if (vp8SegmentHeader.UseSegment) - { - vp8SegmentHeader.UpdateMap = bitReader.ReadBool(); - bool updateData = bitReader.ReadBool(); - if (updateData) - { - vp8SegmentHeader.Delta = bitReader.ReadBool(); - for (int i = 0; i < vp8SegmentHeader.Quantizer.Length; i++) - { - hasValue = bitReader.ReadBool(); - uint quantizeValue = hasValue ? bitReader.ReadValue(7) : 0; - vp8SegmentHeader.Quantizer[i] = (byte)quantizeValue; - } - - for (int i = 0; i < vp8SegmentHeader.FilterStrength.Length; i++) - { - hasValue = bitReader.ReadBool(); - uint filterStrengthValue = hasValue ? bitReader.ReadValue(6) : 0; - vp8SegmentHeader.FilterStrength[i] = (byte)filterStrengthValue; - } - - if (vp8SegmentHeader.UpdateMap) - { - for (int s = 0; s < proba.Segments.Length; ++s) - { - hasValue = bitReader.ReadBool(); - proba.Segments[s] = hasValue ? bitReader.ReadValue(8) : 255; - } - } - } - } - else - { - vp8SegmentHeader.UpdateMap = false; - } + var proba = new Vp8Proba(); + Vp8SegmentHeader vp8SegmentHeader = ParseSegmentHeader(bitReader, proba); // Paragraph 9.4: Parse the filter specs. - var vp8FilterHeader = new Vp8FilterHeader(); - vp8FilterHeader.LoopFilter = bitReader.ReadBool() ? LoopFilter.Simple : LoopFilter.Complex; - vp8FilterHeader.Level = (int)bitReader.ReadValue(6); - vp8FilterHeader.Sharpness = (int)bitReader.ReadValue(3); - vp8FilterHeader.UseLfDelta = bitReader.ReadBool(); + Vp8FilterHeader vp8FilterHeader = ParseFilterHeader(bitReader); - // 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? - if (bitReader.ReadBool()) - { - for (int i = 0; i < vp8FilterHeader.RefLfDelta.Length; i++) - { - hasValue = bitReader.ReadBool(); - if (hasValue) - { - vp8FilterHeader.RefLfDelta[i] = bitReader.ReadSignedValue(6); - } - } - - for (int i = 0; i < vp8FilterHeader.ModeLfDelta.Length; i++) - { - hasValue = bitReader.ReadBool(); - if (hasValue) - { - vp8FilterHeader.ModeLfDelta[i] = bitReader.ReadSignedValue(6); - } - } - } - } - - // Paragraph 9.5: ParsePartitions. + // TODO: Review 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 int partStart = bitReader.Pos + (lastPart * 3); - for (int p = 0; p < lastPart; ++p) - { - // int psize = sz[0] | (sz[1] << 8) | (sz[2] << 16); - } // Paragraph 9.6: Dequantization Indices. int baseQ0 = (int)bitReader.ReadValue(7); @@ -516,7 +447,7 @@ namespace SixLabors.ImageSharp.Formats.WebP // Ignore the value of update_proba bitReader.ReadBool(); - // Parse probabilities. + // Paragraph 13.4: Parse probabilities. for (int t = 0; t < WebPConstants.NumTypes; ++t) { for (int b = 0; b < WebPConstants.NumBands; ++b) @@ -562,6 +493,90 @@ namespace SixLabors.ImageSharp.Formats.WebP }; } + private static Vp8FilterHeader ParseFilterHeader(Vp8BitReader bitReader) + { + var vp8FilterHeader = new Vp8FilterHeader(); + 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; + bool hasValue; + if (vp8FilterHeader.UseLfDelta) + { + // Update lf-delta? + if (bitReader.ReadBool()) + { + for (int i = 0; i < vp8FilterHeader.RefLfDelta.Length; i++) + { + hasValue = bitReader.ReadBool(); + if (hasValue) + { + vp8FilterHeader.RefLfDelta[i] = bitReader.ReadSignedValue(6); + } + } + + for (int i = 0; i < vp8FilterHeader.ModeLfDelta.Length; i++) + { + hasValue = bitReader.ReadBool(); + if (hasValue) + { + vp8FilterHeader.ModeLfDelta[i] = bitReader.ReadSignedValue(6); + } + } + } + } + + return vp8FilterHeader; + } + + private static Vp8SegmentHeader ParseSegmentHeader(Vp8BitReader bitReader, Vp8Proba proba) + { + var vp8SegmentHeader = new Vp8SegmentHeader(); + vp8SegmentHeader.UseSegment = bitReader.ReadBool(); + bool hasValue; + if (vp8SegmentHeader.UseSegment) + { + vp8SegmentHeader.UpdateMap = bitReader.ReadBool(); + bool updateData = bitReader.ReadBool(); + if (updateData) + { + vp8SegmentHeader.Delta = bitReader.ReadBool(); + for (int i = 0; i < vp8SegmentHeader.Quantizer.Length; i++) + { + hasValue = bitReader.ReadBool(); + uint quantizeValue = hasValue ? bitReader.ReadValue(7) : 0; + vp8SegmentHeader.Quantizer[i] = (byte)quantizeValue; + } + + for (int i = 0; i < vp8SegmentHeader.FilterStrength.Length; i++) + { + hasValue = bitReader.ReadBool(); + uint filterStrengthValue = hasValue ? bitReader.ReadValue(6) : 0; + vp8SegmentHeader.FilterStrength[i] = (byte)filterStrengthValue; + } + + if (vp8SegmentHeader.UpdateMap) + { + for (int s = 0; s < proba.Segments.Length; ++s) + { + hasValue = bitReader.ReadBool(); + proba.Segments[s] = hasValue ? bitReader.ReadValue(8) : 255; + } + } + } + } + else + { + vp8SegmentHeader.UpdateMap = false; + } + + return vp8SegmentHeader; + } + /// /// Reads the header of a lossless webp image. ///