diff --git a/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs b/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs index fe0761a32d..2cc8f592cd 100644 --- a/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs +++ b/src/ImageSharp/Formats/WebP/WebPDecoderCore.cs @@ -42,6 +42,11 @@ namespace SixLabors.ImageSharp.Formats.WebP /// private Stream currentStream; + /// + /// The metadata. + /// + private ImageMetadata metadata; + /// /// Initializes a new instance of the class. /// @@ -61,17 +66,60 @@ namespace SixLabors.ImageSharp.Formats.WebP WebPMetadata webpMetadata = metadata.GetFormatMetadata(WebPFormat.Instance); this.currentStream = stream; + uint chunkSize = this.ReadImageHeader(); + WebPImageInfo imageInfo = this.ReadVp8Info(); + // TODO: there can be optional chunks after that, like EXIF. + + var image = new Image(this.configuration, imageInfo.Width, imageInfo.Height, this.metadata); + Buffer2D pixels = image.GetRootFramePixelBuffer(); + if (imageInfo.IsLooseLess) + { + ReadSimpleLossless(pixels, image.Width, image.Height); + } + else + { + ReadSimpleLossy(pixels, image.Width, image.Height); + } + + return image; + } + + /// + /// Reads the raw image information from the specified stream. + /// + /// The containing image data. + public IImageInfo Identify(Stream stream) + { + var metadata = new ImageMetadata(); + WebPMetadata webpMetadata = metadata.GetFormatMetadata(WebPFormat.Instance); + this.currentStream = stream; + + this.ReadImageHeader(); + WebPImageInfo imageInfo = this.ReadVp8Info(); + + // 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); + } + + private uint ReadImageHeader() + { // Skip FourCC header, we already know its a RIFF file at this point. this.currentStream.Skip(4); // Read Chunk size. this.currentStream.Read(this.buffer, 0, 4); - uint chunkSize = BinaryPrimitives.ReadUInt32LittleEndian(buffer); + uint chunkSize = BinaryPrimitives.ReadUInt32LittleEndian(this.buffer); // Skip 'WEBP' from the header. this.currentStream.Skip(4); - // Read VP8 header. + return chunkSize; + } + + private WebPImageInfo ReadVp8Info() + { + // Read VP8 chunk header. this.currentStream.Read(this.buffer, 0, 4); if (this.buffer.AsSpan().SequenceEqual(WebPConstants.AlphaHeader)) { @@ -83,7 +131,8 @@ namespace SixLabors.ImageSharp.Formats.WebP WebPThrowHelper.ThrowImageFormatException("Vp8X is not yet supported"); } - if (!(this.buffer.AsSpan().SequenceEqual(WebPConstants.Vp8Header) || this.buffer.AsSpan().SequenceEqual(WebPConstants.Vp8LHeader))) + if (!(this.buffer.AsSpan().SequenceEqual(WebPConstants.Vp8Header) + || this.buffer.AsSpan().SequenceEqual(WebPConstants.Vp8LHeader))) { WebPThrowHelper.ThrowImageFormatException("Unrecognized VP8 header"); } @@ -96,35 +145,25 @@ namespace SixLabors.ImageSharp.Formats.WebP uint dataSize = BinaryPrimitives.ReadUInt32LittleEndian(this.buffer); // https://tools.ietf.org/html/rfc6386#page-30 - byte[] c = new byte[11]; - this.currentStream.Read(c, 0, c.Length); - int tmp = (c[2] << 16) | (c[1] << 8) | c[0]; + var imageInfo = new byte[11]; + this.currentStream.Read(imageInfo, 0, imageInfo.Length); + int tmp = (imageInfo[2] << 16) | (imageInfo[1] << 8) | imageInfo[0]; int isKeyFrame = tmp & 0x1; int version = (tmp >> 1) & 0x7; int showFrame = (tmp >> 4) & 0x1; - // TODO: Get horizontal and vertical scale - int width = BinaryPrimitives.ReadInt16LittleEndian(c.AsSpan(7)) & 0x3fff; - int height = BinaryPrimitives.ReadInt16LittleEndian(c.AsSpan(9)) & 0x3fff; - - // TODO: DO something with the data - - // var image = new Image(this.configuration, this.infoHeader.Width, this.infoHeader.Height, this.metadata); - - // TODO: there can be optional chunks after that, like EXIF. - - throw new NotImplementedException(); - } - /// - /// Reads the raw image information from the specified stream. - /// - /// The containing image data. - public IImageInfo Identify(Stream stream) - { - throw new NotImplementedException(); + // TODO: Get horizontal and vertical scale + int width = BinaryPrimitives.ReadInt16LittleEndian(imageInfo.AsSpan(7)) & 0x3fff; + int height = BinaryPrimitives.ReadInt16LittleEndian(imageInfo.AsSpan(9)) & 0x3fff; - //this.ReadImageHeaders(stream, out _, out _); - //return new ImageInfo(new PixelTypeInfo(this.infoHeader.BitsPerPixel), this.infoHeader.Width, this.infoHeader.Height, this.metadata); + return new WebPImageInfo() + { + Width = width, + Height = height, + IsLooseLess = isLossLess, + Version = version, + DataSize = dataSize + }; } 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 new file mode 100644 index 0000000000..bfa778810a --- /dev/null +++ b/src/ImageSharp/Formats/WebP/WebPImageInfo.cs @@ -0,0 +1,27 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.WebP +{ + internal class WebPImageInfo + { + /// + /// Gets or sets the bitmap width in pixels (signed integer). + /// + public int Width { get; set; } + + /// + /// Gets or sets the bitmap height in pixels (signed integer). + /// + public int Height { get; set; } + + /// + /// Gets or sets whether this image uses a looseless compression. + /// + public bool IsLooseLess { get; set; } + + public int Version { get; set; } + + public uint DataSize { get; set; } + } +}