From 0392d0ea87c30419068851f8c7637c95c32f727b Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 16 Oct 2019 21:16:23 +0200 Subject: [PATCH] Add reading of VP8X header --- src/ImageSharp/Formats/WebP/Readme.md | 2 +- src/ImageSharp/Formats/WebP/Vp8HeaderType.cs | 31 +++++++++ src/ImageSharp/Formats/WebP/WebPConstants.cs | 2 +- .../Formats/WebP/WebPDecoderCore.cs | 65 +++++++++++++++---- src/ImageSharp/Formats/WebP/WebPImageInfo.cs | 4 -- 5 files changed, 85 insertions(+), 19 deletions(-) create mode 100644 src/ImageSharp/Formats/WebP/Vp8HeaderType.cs diff --git a/src/ImageSharp/Formats/WebP/Readme.md b/src/ImageSharp/Formats/WebP/Readme.md index 41409f1369..664df5043e 100644 --- a/src/ImageSharp/Formats/WebP/Readme.md +++ b/src/ImageSharp/Formats/WebP/Readme.md @@ -3,6 +3,6 @@ Reference implementation, specification and stuff like that: - [google webp introduction](https://developers.google.com/speed/webp) -- [WebP Spec 0.2](https://chromium.googlesource.com/webm/libwebp/+/v0.2.0/doc/webp-container-spec.txt) +- [WebP Spec 1.0.3](https://chromium.googlesource.com/webm/libwebp/+/v1.0.3/doc/webp-container-spec.txt) - [WebP VP8 chunk Spec](http://tools.ietf.org/html/rfc6386) - [WebP filefront](https://wiki.fileformat.com/image/webp/) diff --git a/src/ImageSharp/Formats/WebP/Vp8HeaderType.cs b/src/ImageSharp/Formats/WebP/Vp8HeaderType.cs new file mode 100644 index 0000000000..18e311da3b --- /dev/null +++ b/src/ImageSharp/Formats/WebP/Vp8HeaderType.cs @@ -0,0 +1,31 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.WebP +{ + /// + /// Enum for the different VP8 chunk header types. + /// + public enum Vp8HeaderType + { + /// + /// Invalid VP8 header. + /// + Invalid = 0, + + /// + /// A VP8 header. + /// + Vp8 = 1, + + /// + /// VP8 header, signaling the use of VP8L lossless format. + /// + Vp8L = 2, + + /// + /// Header for a extended-VP8 chunk. + /// + Vp8X = 3, + } +} diff --git a/src/ImageSharp/Formats/WebP/WebPConstants.cs b/src/ImageSharp/Formats/WebP/WebPConstants.cs index e1f0fd23d3..bdc026937b 100644 --- a/src/ImageSharp/Formats/WebP/WebPConstants.cs +++ b/src/ImageSharp/Formats/WebP/WebPConstants.cs @@ -58,7 +58,7 @@ namespace SixLabors.ImageSharp.Formats.WebP 0x56, // V 0x50, // P 0x38, // 8 - 0x88, // X + 0x58, // X }; public static readonly byte LossLessFlag = 0x4C; // L diff --git a/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs b/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs index d4d7503c59..5c5c5d3aca 100644 --- a/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs +++ b/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs @@ -4,6 +4,7 @@ using System; using System.Buffers.Binary; using System.IO; +using System.Linq; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; @@ -108,6 +109,8 @@ namespace SixLabors.ImageSharp.Formats.WebP this.currentStream.Skip(4); // 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); @@ -126,19 +129,57 @@ namespace SixLabors.ImageSharp.Formats.WebP WebPThrowHelper.ThrowImageFormatException("Alpha channel is not yet supported"); } - if (this.buffer.AsSpan().SequenceEqual(WebPConstants.Vp8XHeader)) + var vp8HeaderType = Vp8HeaderType.Invalid; + if (this.buffer.AsSpan().SequenceEqual(WebPConstants.Vp8Header)) { - WebPThrowHelper.ThrowImageFormatException("Vp8X is not yet supported"); + vp8HeaderType = Vp8HeaderType.Vp8; } - - if (!(this.buffer.AsSpan().SequenceEqual(WebPConstants.Vp8Header) - || this.buffer.AsSpan().SequenceEqual(WebPConstants.Vp8LHeader))) + else if (this.buffer.AsSpan().SequenceEqual(WebPConstants.Vp8LHeader)) + { + vp8HeaderType = Vp8HeaderType.Vp8L; + } + else if (this.buffer.SequenceEqual(WebPConstants.Vp8XHeader)) + { + vp8HeaderType = Vp8HeaderType.Vp8X; + } + else { WebPThrowHelper.ThrowImageFormatException("Unrecognized VP8 header"); } - bool isLossLess = this.buffer[3] == WebPConstants.LossLessFlag; + return vp8HeaderType == Vp8HeaderType.Vp8X ? this.ReadVp8XHeader() : this.ReadVp8Header(vp8HeaderType); + } + private WebPImageInfo ReadVp8XHeader() + { + this.currentStream.Read(this.buffer, 0, 4); + uint chunkSize = BinaryPrimitives.ReadUInt32LittleEndian(this.buffer); + + byte imageFeatures = (byte)this.currentStream.ReadByte(); + + // 3 reserved bytes should follow which are supposed to be zero. + this.currentStream.Read(this.buffer, 0, 3); + + // 3 bytes for the width. + this.currentStream.Read(this.buffer, 0, 3); + this.buffer[3] = 0; + int width = BinaryPrimitives.ReadInt32LittleEndian(this.buffer) + 1; + + // 3 bytes for the height. + this.currentStream.Read(this.buffer, 0, 3); + this.buffer[3] = 0; + int height = BinaryPrimitives.ReadInt32LittleEndian(this.buffer) + 1; + + return new WebPImageInfo() + { + Width = width, + Height = height, + IsLossLess = false, // note: this is maybe incorrect here + }; + } + + private WebPImageInfo ReadVp8Header(Vp8HeaderType vp8HeaderType) + { // VP8 data size. this.currentStream.Read(this.buffer, 0, 3); this.buffer[3] = 0; @@ -157,13 +198,11 @@ namespace SixLabors.ImageSharp.Formats.WebP int height = BinaryPrimitives.ReadInt16LittleEndian(imageInfo.AsSpan(9)) & 0x3fff; return new WebPImageInfo() - { - Width = width, - Height = height, - IsLossLess = isLossLess, - Version = version, - DataSize = dataSize - }; + { + Width = width, + Height = height, + IsLossLess = vp8HeaderType == Vp8HeaderType.Vp8L, + }; } private void ReadSimpleLossy(Buffer2D pixels, int width, int height) diff --git a/src/ImageSharp/Formats/WebP/WebPImageInfo.cs b/src/ImageSharp/Formats/WebP/WebPImageInfo.cs index 1cf98865c0..065e5dd7e3 100644 --- a/src/ImageSharp/Formats/WebP/WebPImageInfo.cs +++ b/src/ImageSharp/Formats/WebP/WebPImageInfo.cs @@ -19,9 +19,5 @@ namespace SixLabors.ImageSharp.Formats.WebP /// Gets or sets whether this image uses a lossless compression. /// public bool IsLossLess { get; set; } - - public int Version { get; set; } - - public uint DataSize { get; set; } } }