From 3c419fcfdcfd15ffb9eb02a0b7a8e5ef1d1ef336 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 21 Oct 2019 21:33:12 +0200 Subject: [PATCH] Refactor determining chunk types --- src/ImageSharp/Formats/WebP/Vp8LBitReader.cs | 16 +-- src/ImageSharp/Formats/WebP/WebPChunkType.cs | 56 ++++++++++ src/ImageSharp/Formats/WebP/WebPConstants.cs | 101 ------------------ .../Formats/WebP/WebPDecoderCore.cs | 58 ++++++---- 4 files changed, 102 insertions(+), 129 deletions(-) create mode 100644 src/ImageSharp/Formats/WebP/WebPChunkType.cs diff --git a/src/ImageSharp/Formats/WebP/Vp8LBitReader.cs b/src/ImageSharp/Formats/WebP/Vp8LBitReader.cs index 39ee78cd6..036f441d0 100644 --- a/src/ImageSharp/Formats/WebP/Vp8LBitReader.cs +++ b/src/ImageSharp/Formats/WebP/Vp8LBitReader.cs @@ -15,11 +15,13 @@ namespace SixLabors.ImageSharp.Formats.WebP /// /// Initializes a new instance of the class. /// - /// The stream to read from. - public Vp8LBitReader(Stream stream) + /// The input stream to read from. + public Vp8LBitReader(Stream inputStream) { this.stream = new MemoryStream(); - stream.CopyTo(this.stream); + long inputStreamPos = inputStream.Position; + inputStream.CopyTo(this.stream); + inputStream.Position = inputStreamPos; this.Offset = 0; this.Bit = 0; } @@ -29,7 +31,7 @@ namespace SixLabors.ImageSharp.Formats.WebP private int Bit { get; set; } /// - /// Gets a value indicating whether the offset is inside the stream length. + /// Gets a value indicating whether the offset is inside the inputStream length. /// private bool ValidPosition { @@ -40,7 +42,7 @@ namespace SixLabors.ImageSharp.Formats.WebP } /// - /// Reads a unsigned short value from the stream. The bits of each byte are read in least-significant-bit-first order. + /// Reads a unsigned short value from the inputStream. The bits of each byte are read in least-significant-bit-first order. /// /// The number of bits to read (should not exceed 16). /// A ushort value. @@ -67,7 +69,7 @@ namespace SixLabors.ImageSharp.Formats.WebP { if (!this.ValidPosition) { - WebPThrowHelper.ThrowImageFormatException("The image stream does not contain enough data"); + WebPThrowHelper.ThrowImageFormatException("The image inputStream does not contain enough data"); } this.stream.Seek(this.Offset, SeekOrigin.Begin); @@ -79,7 +81,7 @@ namespace SixLabors.ImageSharp.Formats.WebP } /// - /// Advances the stream by one Bit. + /// Advances the inputStream by one Bit. /// public void AdvanceBit() { diff --git a/src/ImageSharp/Formats/WebP/WebPChunkType.cs b/src/ImageSharp/Formats/WebP/WebPChunkType.cs new file mode 100644 index 000000000..ba7fb9fd6 --- /dev/null +++ b/src/ImageSharp/Formats/WebP/WebPChunkType.cs @@ -0,0 +1,56 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.WebP +{ + /// + /// Contains a list of different webp chunk types. + /// + internal enum WebPChunkType : uint + { + /// + /// Header signaling the use of VP8 video format. + /// + Vp8 = 0x56503820U, + + /// + /// Header for a extended-VP8 chunk. + /// + Vp8L = 0x5650384CU, + + /// + /// Header for a extended-VP8 chunk. + /// + Vp8X = 0x56503858U, + + /// + /// Chunk contains information about the alpha channel. + /// + Alpha = 0x414C5048U, + + /// + /// Chunk which contains a color profile. + /// + Iccp = 0x49434350U, + + /// + /// Chunk which contains EXIF metadata about the image. + /// + Exif = 0x45584946U, + + /// + /// Chunk contains XMP metadata about the image. + /// + Xmp = 0x584D5020U, + + /// + /// For an animated image, this chunk contains the global parameters of the animation. + /// + AnimationParameter = 0x414E494D, + + /// + /// 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, + } +} diff --git a/src/ImageSharp/Formats/WebP/WebPConstants.cs b/src/ImageSharp/Formats/WebP/WebPConstants.cs index f07a79814..d1c451799 100644 --- a/src/ImageSharp/Formats/WebP/WebPConstants.cs +++ b/src/ImageSharp/Formats/WebP/WebPConstants.cs @@ -58,106 +58,5 @@ namespace SixLabors.ImageSharp.Formats.WebP 0x42, // B 0x50 // P }; - - /// - /// Header signaling the use of VP8 video format. - /// - public static readonly byte[] Vp8Header = - { - 0x56, // V - 0x50, // P - 0x38, // 8 - 0x20, // Space - }; - - /// - /// Header for a extended-VP8 chunk. - /// - public static readonly byte[] Vp8XHeader = - { - 0x56, // V - 0x50, // P - 0x38, // 8 - 0x58, // X - }; - - public static readonly byte LossLessFlag = 0x4C; // L - - /// - /// VP8 header, signaling the use of VP8L lossless format. - /// - public static readonly byte[] Vp8LHeader = - { - 0x56, // V - 0x50, // P - 0x38, // 8 - LossLessFlag // L - }; - - /// - /// Chunk contains information about the alpha channel. - /// - public static readonly byte[] AlphaHeader = - { - 0x41, // A - 0x4C, // L - 0x50, // P - 0x48, // H - }; - - /// - /// Chunk which contains a color profile. - /// - public static readonly byte[] IccpHeader = - { - 0x49, // I - 0x43, // C - 0x43, // C - 0x50, // P - }; - - /// - /// Chunk which contains EXIF metadata about the image. - /// - public static readonly byte[] ExifHeader = - { - 0x45, // E - 0x58, // X - 0x49, // I - 0x46, // F - }; - - /// - /// Chunk contains XMP metadata about the image. - /// - public static readonly byte[] XmpHeader = - { - 0x58, // X - 0x4D, // M - 0x50, // P - 0x20, // Space - }; - - /// - /// For an animated image, this chunk contains the global parameters of the animation. - /// - public static readonly byte[] AnimationParameterHeader = - { - 0x41, // A - 0x4E, // N - 0x49, // I - 0x4D, // M - }; - - /// - /// 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. - /// - public static readonly byte[] AnimationHeader = - { - 0x41, // A - 0x4E, // N - 0x4D, // M - 0x46, // F - }; } } diff --git a/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs b/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs index 4f574e7d7..f3b70aaec 100644 --- a/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs +++ b/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs @@ -4,7 +4,6 @@ using System; using System.Buffers.Binary; using System.IO; -using System.Linq; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; @@ -74,14 +73,15 @@ namespace SixLabors.ImageSharp.Formats.WebP Buffer2D pixels = image.GetRootFramePixelBuffer(); if (imageInfo.IsLossLess) { - ReadSimpleLossless(pixels, image.Width, image.Height); + ReadSimpleLossless(pixels, image.Width, image.Height, (int)imageInfo.DataSize); } else { - ReadSimpleLossy(pixels, image.Width, image.Height); + ReadSimpleLossy(pixels, image.Width, image.Height, (int)imageInfo.DataSize); } // TODO: there can be optional chunks after the image data, like EXIF, XMP etc. + this.ParseOptionalChunks(); return image; } @@ -123,22 +123,16 @@ namespace SixLabors.ImageSharp.Formats.WebP private WebPImageInfo ReadVp8Info(int vpxWidth = 0, int vpxHeight = 0) { - // Read VP8 chunk header. - this.currentStream.Read(this.buffer, 0, 4); - - if (this.buffer.AsSpan().SequenceEqual(WebPConstants.Vp8Header)) - { - return this.ReadVp8Header(vpxWidth, vpxHeight); - } + WebPChunkType type = this.ReadChunkType(); - if (this.buffer.AsSpan().SequenceEqual(WebPConstants.Vp8LHeader)) + switch (type) { - return this.ReadVp8LHeader(vpxWidth, vpxHeight); - } - - if (this.buffer.SequenceEqual(WebPConstants.Vp8XHeader)) - { - return this.ReadVp8XHeader(); + case WebPChunkType.Vp8: + return this.ReadVp8Header(vpxWidth, vpxHeight); + case WebPChunkType.Vp8L: + return this.ReadVp8LHeader(vpxWidth, vpxHeight); + case WebPChunkType.Vp8X: + return this.ReadVp8XHeader(); } WebPThrowHelper.ThrowImageFormatException("Unrecognized VP8 header"); @@ -185,6 +179,7 @@ namespace SixLabors.ImageSharp.Formats.WebP int height = BinaryPrimitives.ReadInt32LittleEndian(this.buffer) + 1; // TODO: optional chunks ICCP and ANIM can follow here. Ignoring them for now. + this.ParseOptionalChunks(); // A VP8 or VP8L chunk will follow here. return this.ReadVp8Info(width, height); @@ -324,16 +319,24 @@ namespace SixLabors.ImageSharp.Formats.WebP }; } - private void ReadSimpleLossy(Buffer2D pixels, int width, int height) + private void ParseOptionalChunks() + { + // Read VP8 chunk header. + this.currentStream.Read(this.buffer, 0, 4); + } + + private void ReadSimpleLossy(Buffer2D pixels, int width, int height, int chunkSize) where TPixel : struct, IPixel { - // TODO: implement decoding + // TODO: implement decoding, for simulating the decoding, skipping the chunk size bytes. + this.currentStream.Skip(chunkSize); } - private void ReadSimpleLossless(Buffer2D pixels, int width, int height) + private void ReadSimpleLossless(Buffer2D pixels, int width, int height, int chunkSize) where TPixel : struct, IPixel { - // TODO: implement decoding + // TODO: implement decoding, for simulating the decoding, skipping the chunk size bytes. + this.currentStream.Skip(chunkSize); } private void ReadExtended(Buffer2D pixels, int width, int height) @@ -341,5 +344,18 @@ namespace SixLabors.ImageSharp.Formats.WebP { // TODO: implement decoding } + + /// + /// Identifies the chunk type from the chunk. + /// + /// + /// Thrown if the input stream is not valid. + /// + private WebPChunkType ReadChunkType() + { + return this.currentStream.Read(this.buffer, 0, 4) == 4 + ? (WebPChunkType)BinaryPrimitives.ReadUInt32BigEndian(this.buffer) + : throw new ImageFormatException("Invalid WebP data."); + } } }