diff --git a/src/ImageSharp/Formats/WebP/WebPChunkType.cs b/src/ImageSharp/Formats/WebP/WebPChunkType.cs
index ba7fb9fd6..85ee888bf 100644
--- a/src/ImageSharp/Formats/WebP/WebPChunkType.cs
+++ b/src/ImageSharp/Formats/WebP/WebPChunkType.cs
@@ -52,5 +52,10 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// For animated images, this chunk contains information about a single frame. If the Animation flag is not set, then this chunk SHOULD NOT be present.
///
Animation = 0x414E4D46,
+
+ ///
+ /// TODO: not sure what this is for yet.
+ ///
+ FRGM = 0x4652474D,
}
}
diff --git a/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs b/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs
index f3b70aaec..896034664 100644
--- a/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs
+++ b/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs
@@ -112,8 +112,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
// Read Chunk size.
// The size of the file in bytes starting at offset 8.
// The file size in the header is the total size of the chunks that follow plus 4 bytes for the ‘WEBP’ FourCC.
- this.currentStream.Read(this.buffer, 0, 4);
- uint chunkSize = BinaryPrimitives.ReadUInt32LittleEndian(this.buffer);
+ uint chunkSize = this.ReadChunkSize();
// Skip 'WEBP' from the header.
this.currentStream.Skip(4);
@@ -123,9 +122,9 @@ namespace SixLabors.ImageSharp.Formats.WebP
private WebPImageInfo ReadVp8Info(int vpxWidth = 0, int vpxHeight = 0)
{
- WebPChunkType type = this.ReadChunkType();
+ WebPChunkType chunkType = this.ReadChunkType();
- switch (type)
+ switch (chunkType)
{
case WebPChunkType.Vp8:
return this.ReadVp8Header(vpxWidth, vpxHeight);
@@ -142,8 +141,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
private WebPImageInfo ReadVp8XHeader()
{
- this.currentStream.Read(this.buffer, 0, 4);
- uint chunkSize = BinaryPrimitives.ReadUInt32LittleEndian(this.buffer);
+ uint chunkSize = this.ReadChunkSize();
// This byte contains information about the image features used.
// The first two bit should and the last bit should be 0.
@@ -163,7 +161,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
bool isXmpPresent = (imageFeatures & (1 << 2)) != 0;
// If bit 7 is set, animation should be present.
- bool isAnimationPresent = (imageFeatures & (1 << 7)) != 0;
+ bool isAnimationPresent = (imageFeatures & (1 << 1)) != 0;
// 3 reserved bytes should follow which are supposed to be zero.
this.currentStream.Read(this.buffer, 0, 3);
@@ -178,8 +176,37 @@ namespace SixLabors.ImageSharp.Formats.WebP
this.buffer[3] = 0;
int height = BinaryPrimitives.ReadInt32LittleEndian(this.buffer) + 1;
- // TODO: optional chunks ICCP and ANIM can follow here. Ignoring them for now.
- this.ParseOptionalChunks();
+ // Optional chunks ALPH, ICCP and ANIM can follow here. Ignoring them for now.
+ if (isIccPresent)
+ {
+ WebPChunkType chunkType = this.ReadChunkType();
+ uint iccpChunkSize = this.ReadChunkSize();
+ this.currentStream.Skip((int)iccpChunkSize);
+ }
+
+ if (isAnimationPresent)
+ {
+ // ANIM chunk will be followed by n ANMF chunks
+ WebPChunkType chunkType = this.ReadChunkType();
+ uint animationParameterChunkSize = this.ReadChunkSize();
+ this.currentStream.Skip((int)animationParameterChunkSize);
+ chunkType = this.ReadChunkType();
+ while (chunkType == WebPChunkType.Animation)
+ {
+ uint animationChunkSize = this.ReadChunkSize();
+ this.currentStream.Skip((int)animationChunkSize);
+ chunkType = this.ReadChunkType();
+ }
+
+ // TODO: there seems to follow something here after the last ANMF, im not sure yet how to parse.
+ }
+
+ if (isAlphaPresent)
+ {
+ WebPChunkType chunkType = this.ReadChunkType();
+ uint alphaChunkSize = this.ReadChunkSize();
+ this.currentStream.Skip((int)alphaChunkSize);
+ }
// A VP8 or VP8L chunk will follow here.
return this.ReadVp8Info(width, height);
@@ -232,8 +259,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
private WebPImageInfo ReadVp8LHeader(int vpxWidth = 0, int vpxHeight = 0)
{
// VP8 data size.
- this.currentStream.Read(this.buffer, 0, 4);
- uint dataSize = BinaryPrimitives.ReadUInt32LittleEndian(this.buffer);
+ uint dataSize = this.ReadChunkSize();
// One byte signature, should be 0x2f.
byte signature = (byte)this.currentStream.ReadByte();
@@ -322,7 +348,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
private void ParseOptionalChunks()
{
// Read VP8 chunk header.
- this.currentStream.Read(this.buffer, 0, 4);
+ // WebPChunkType chunkType = this.ReadChunkType();
}
private void ReadSimpleLossy(Buffer2D pixels, int width, int height, int chunkSize)
@@ -330,6 +356,8 @@ namespace SixLabors.ImageSharp.Formats.WebP
{
// TODO: implement decoding, for simulating the decoding, skipping the chunk size bytes.
this.currentStream.Skip(chunkSize);
+
+ this.ParseOptionalChunks();
}
private void ReadSimpleLossless(Buffer2D pixels, int width, int height, int chunkSize)
@@ -337,6 +365,8 @@ namespace SixLabors.ImageSharp.Formats.WebP
{
// TODO: implement decoding, for simulating the decoding, skipping the chunk size bytes.
this.currentStream.Skip(chunkSize);
+
+ this.ParseOptionalChunks();
}
private void ReadExtended(Buffer2D pixels, int width, int height)
@@ -357,5 +387,16 @@ namespace SixLabors.ImageSharp.Formats.WebP
? (WebPChunkType)BinaryPrimitives.ReadUInt32BigEndian(this.buffer)
: throw new ImageFormatException("Invalid WebP data.");
}
+
+ ///
+ /// Reads the chunk size.
+ ///
+ /// The chunk size in bytes.
+ private uint ReadChunkSize()
+ {
+ return this.currentStream.Read(this.buffer, 0, 4) == 4
+ ? BinaryPrimitives.ReadUInt32LittleEndian(this.buffer)
+ : throw new ImageFormatException("Invalid WebP data.");
+ }
}
}
diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebPDecoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebPDecoderTests.cs
index e61c3c3c6..d75d30d91 100644
--- a/tests/ImageSharp.Tests/Formats/WebP/WebPDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/WebP/WebPDecoderTests.cs
@@ -23,10 +23,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.WebP
};
[Theory]
- //[InlineData(Lossless.Lossless1, 1000, 307)]
- //[InlineData(Lossless.Lossless2, 1000, 307)]
- //[InlineData(Lossy.Alpha.LossyAlpha1, 1000, 307)]
- //[InlineData(Lossy.Alpha.LossyAlpha2, 1000, 307)]
+ [InlineData(Lossless.Lossless1, 1000, 307)]
+ [InlineData(Lossless.Lossless2, 1000, 307)]
+ [InlineData(Lossy.Alpha.LossyAlpha1, 1000, 307)]
+ [InlineData(Lossy.Alpha.LossyAlpha2, 1000, 307)]
[InlineData(Animated.Animated1, 400, 400)]
public void Identify_DetectsCorrectDimensions(string imagePath, int expectedWidth, int expectedHeight)
{