diff --git a/src/ImageSharp/Formats/WebP/Vp8LBitReader.cs b/src/ImageSharp/Formats/WebP/Vp8LBitReader.cs
index 180f3cb5b..d3a13035d 100644
--- a/src/ImageSharp/Formats/WebP/Vp8LBitReader.cs
+++ b/src/ImageSharp/Formats/WebP/Vp8LBitReader.cs
@@ -1,8 +1,12 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
+using System;
using System.IO;
+using SixLabors.ImageSharp.Memory;
+using SixLabors.Memory;
+
namespace SixLabors.ImageSharp.Formats.WebP
{
///
@@ -67,13 +71,15 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// Initializes a new instance of the class.
///
/// The input stream to read from.
- public Vp8LBitReader(Stream inputStream)
+ /// The image data size in bytes.
+ /// Used for allocating memory during reading data from the stream.
+ public Vp8LBitReader(Stream inputStream, uint imageDataSize, MemoryAllocator memoryAllocator)
{
- long length = inputStream.Length - inputStream.Position;
+ long length = imageDataSize;
using (var ms = new MemoryStream())
{
- inputStream.CopyTo(ms);
+ CopyStream(inputStream, ms, (int)imageDataSize, memoryAllocator);
this.data = ms.ToArray();
}
@@ -114,13 +120,15 @@ namespace SixLabors.ImageSharp.Formats.WebP
this.ShiftBytes();
return (uint)val;
}
- else
- {
- this.SetEndOfStream();
- return 0;
- }
+
+ this.SetEndOfStream();
+ return 0;
}
+ ///
+ /// Reads a single bit from the stream.
+ ///
+ /// True if the bit read was 1, false otherwise.
public bool ReadBit()
{
uint bit = this.ReadBits(1);
@@ -155,6 +163,9 @@ namespace SixLabors.ImageSharp.Formats.WebP
return this.eos || ((this.pos == this.len) && (this.bitPos > Vp8LLbits));
}
+ ///
+ /// If not at EOS, reload up to Vp8LLbits byte-by-byte.
+ ///
private void ShiftBytes()
{
while (this.bitPos >= 8 && this.pos < this.len)
@@ -176,5 +187,24 @@ namespace SixLabors.ImageSharp.Formats.WebP
this.eos = true;
this.bitPos = 0; // To avoid undefined behaviour with shifts.
}
+
+ private static void CopyStream(Stream input, Stream output, int bytesToRead, MemoryAllocator memoryAllocator)
+ {
+ using (IManagedByteBuffer buffer = memoryAllocator.AllocateManagedByteBuffer(4096))
+ {
+ Span bufferSpan = buffer.GetSpan();
+ int read;
+ while (bytesToRead > 0 && (read = input.Read(buffer.Array, 0, Math.Min(bufferSpan.Length, bytesToRead))) > 0)
+ {
+ output.Write(buffer.Array, 0, read);
+ bytesToRead -= read;
+ }
+
+ if (bytesToRead > 0)
+ {
+ WebPThrowHelper.ThrowImageFormatException("image file has insufficient data");
+ }
+ }
+ }
}
}
diff --git a/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs b/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs
index 39d7ecdfc..104138c96 100644
--- a/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs
+++ b/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs
@@ -7,6 +7,7 @@ using System.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
+using SixLabors.ImageSharp.Metadata.Profiles.Exif;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
@@ -32,21 +33,11 @@ namespace SixLabors.ImageSharp.Formats.WebP
///
private readonly MemoryAllocator memoryAllocator;
- ///
- /// The bitmap decoder options.
- ///
- private readonly IWebPDecoderOptions options;
-
///
/// The stream to decode from.
///
private Stream currentStream;
- ///
- /// The metadata.
- ///
- private ImageMetadata metadata;
-
///
/// The webp specific metadata.
///
@@ -61,9 +52,19 @@ namespace SixLabors.ImageSharp.Formats.WebP
{
this.configuration = configuration;
this.memoryAllocator = configuration.MemoryAllocator;
- this.options = options;
+ this.IgnoreMetadata = options.IgnoreMetadata;
}
+ ///
+ /// Gets a value indicating whether the metadata should be ignored when the image is being decoded.
+ ///
+ public bool IgnoreMetadata { get; }
+
+ ///
+ /// Gets the decoded by this decoder instance.
+ ///
+ public ImageMetadata Metadata { get; private set; }
+
///
/// Decodes the image from the specified and sets the data to the image.
///
@@ -73,6 +74,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
public Image Decode(Stream stream)
where TPixel : struct, IPixel
{
+ this.Metadata = new ImageMetadata();
this.currentStream = stream;
uint fileSize = this.ReadImageHeader();
@@ -82,7 +84,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
WebPThrowHelper.ThrowNotSupportedException("Animations are not supported");
}
- var image = new Image(this.configuration, imageInfo.Width, imageInfo.Height, this.metadata);
+ var image = new Image(this.configuration, imageInfo.Width, imageInfo.Height, this.Metadata);
Buffer2D pixels = image.GetRootFramePixelBuffer();
if (imageInfo.IsLossLess)
{
@@ -95,7 +97,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
lossyDecoder.Decode(pixels, image.Width, image.Height, (int)imageInfo.ImageDataSize);
}
- // There can be optional chunks after the image data, like EXIF, XMP etc.
+ // There can be optional chunks after the image data, like EXIF and XMP.
if (imageInfo.Features != null)
{
this.ParseOptionalChunks(imageInfo.Features);
@@ -117,7 +119,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
// TODO: not sure yet where to get this info. Assuming 24 bits for now.
int bitsPerPixel = 24;
- return new ImageInfo(new PixelTypeInfo(bitsPerPixel), imageInfo.Width, imageInfo.Height, this.metadata);
+ return new ImageInfo(new PixelTypeInfo(bitsPerPixel), imageInfo.Width, imageInfo.Height, this.Metadata);
}
///
@@ -142,8 +144,8 @@ namespace SixLabors.ImageSharp.Formats.WebP
private WebPImageInfo ReadVp8Info()
{
- this.metadata = new ImageMetadata();
- this.webpMetadata = this.metadata.GetFormatMetadata(WebPFormat.Instance);
+ this.Metadata = new ImageMetadata();
+ this.webpMetadata = this.Metadata.GetFormatMetadata(WebPFormat.Instance);
WebPChunkType chunkType = this.ReadChunkType();
@@ -322,10 +324,11 @@ namespace SixLabors.ImageSharp.Formats.WebP
this.webpMetadata.Format = WebPFormatType.Lossless;
// VP8 data size.
- uint dataSize = this.ReadChunkSize();
+ uint imageDataSize = this.ReadChunkSize();
+
+ var bitReader = new Vp8LBitReader(this.currentStream, imageDataSize, this.memoryAllocator);
// One byte signature, should be 0x2f.
- var bitReader = new Vp8LBitReader(this.currentStream);
uint signature = bitReader.ReadBits(8);
if (signature != WebPConstants.Vp8LMagicByte)
{
@@ -349,7 +352,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
Width = (int)width,
Height = (int)height,
IsLossLess = true,
- ImageDataSize = dataSize,
+ ImageDataSize = imageDataSize,
Features = features,
Vp9LBitReader = bitReader
};
@@ -363,7 +366,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// The webp features.
private void ParseOptionalChunks(WebPFeatures features)
{
- if (features.ExifProfile is false && features.XmpMetaData is false)
+ if (this.IgnoreMetadata || (features.ExifProfile is false && features.XmpMetaData is false))
{
return;
}
@@ -374,8 +377,17 @@ namespace SixLabors.ImageSharp.Formats.WebP
WebPChunkType chunkType = this.ReadChunkType();
uint chunkLength = this.ReadChunkSize();
- // Skip chunk data for now.
- this.currentStream.Skip((int)chunkLength);
+ if (chunkType is WebPChunkType.Exif)
+ {
+ var exifData = new byte[chunkLength];
+ this.currentStream.Read(exifData, 0, (int)chunkLength);
+ this.Metadata.ExifProfile = new ExifProfile(exifData);
+ }
+ else
+ {
+ // Skip XMP chunk data for now.
+ this.currentStream.Skip((int)chunkLength);
+ }
}
}
diff --git a/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs b/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs
index 906f11efd..484f5071f 100644
--- a/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs
+++ b/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs
@@ -46,7 +46,8 @@ namespace SixLabors.ImageSharp.Formats.WebP
// TODO weiter bei S.11
// bit STREAM: See https://tools.ietf.org/html/rfc6386#page-29 ("Frame Header")
- Vp8LBitReader bitReader = new Vp8LBitReader(this.currentStream);
+ // TODO: Vp8BitReader should be used here instead
+ /*Vp8LBitReader bitReader = new Vp8LBitReader(this.currentStream);
bool isInterframe = bitReader.ReadBit();
if (isInterframe)
{
@@ -58,7 +59,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
bool isShowFrame = bitReader.ReadBit();
- uint firstPartitionSize = (bitReader.ReadBits(16) << 3) | bitReader.ReadBits(3);
+ uint firstPartitionSize = (bitReader.ReadBits(16) << 3) | bitReader.ReadBits(3);*/
}
private (ReconstructionFilter, LoopFilter) DecodeVersion(byte version)
diff --git a/tests/Images/Input/WebP/exif.webp b/tests/Images/Input/WebP/exif.webp
new file mode 100644
index 000000000..440250dd8
Binary files /dev/null and b/tests/Images/Input/WebP/exif.webp differ
diff --git a/tests/Images/Input/WebP/exif_lossless.webp b/tests/Images/Input/WebP/exif_lossless.webp
new file mode 100644
index 000000000..18ef4153f
Binary files /dev/null and b/tests/Images/Input/WebP/exif_lossless.webp differ