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: