Browse Source

Additional webp constants, first attempt parsing VP8L header

pull/1552/head
Brian Popow 7 years ago
parent
commit
b198ddb453
  1. 22
      src/ImageSharp/Formats/WebP/WebPConstants.cs
  2. 83
      src/ImageSharp/Formats/WebP/WebPDecoderCore.cs
  3. 2
      src/ImageSharp/Formats/WebP/WebPImageFormatDetector.cs
  4. 2
      src/ImageSharp/Formats/WebP/WebPImageInfo.cs

22
src/ImageSharp/Formats/WebP/WebPConstants.cs

@ -17,10 +17,30 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// </summary> /// </summary>
public static readonly IEnumerable<string> MimeTypes = new[] { "image/webp", }; public static readonly IEnumerable<string> MimeTypes = new[] { "image/webp", };
/// <summary>
/// Signature which identifies a VP8 header.
/// </summary>
public static readonly byte[] Vp8MagicBytes =
{
0x9D,
0x01,
0x2A
};
/// <summary>
/// Signature byte which identifies a VP8L header.
/// </summary>
public static byte Vp8LMagicByte = 0x2F;
/// <summary>
/// Bits for width and height infos of a VPL8 image.
/// </summary>
public static int Vp8LImageSizeBits = 14;
/// <summary> /// <summary>
/// The header bytes identifying RIFF file. /// The header bytes identifying RIFF file.
/// </summary> /// </summary>
public static readonly byte[] FourCcBytes = public static readonly byte[] RiffFourCc =
{ {
0x52, // R 0x52, // R
0x49, // I 0x49, // I

83
src/ImageSharp/Formats/WebP/WebPDecoderCore.cs

@ -69,6 +69,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
uint chunkSize = this.ReadImageHeader(); uint chunkSize = this.ReadImageHeader();
WebPImageInfo imageInfo = this.ReadVp8Info(); WebPImageInfo imageInfo = this.ReadVp8Info();
// TODO: there can be optional chunks after that, like EXIF. // TODO: there can be optional chunks after that, like EXIF.
var image = new Image<TPixel>(this.configuration, imageInfo.Width, imageInfo.Height, this.metadata); var image = new Image<TPixel>(this.configuration, imageInfo.Width, imageInfo.Height, this.metadata);
@ -124,30 +125,25 @@ namespace SixLabors.ImageSharp.Formats.WebP
{ {
// Read VP8 chunk header. // Read VP8 chunk header.
this.currentStream.Read(this.buffer, 0, 4); this.currentStream.Read(this.buffer, 0, 4);
if (this.buffer.AsSpan().SequenceEqual(WebPConstants.AlphaHeader))
{
WebPThrowHelper.ThrowImageFormatException("Alpha channel is not yet supported");
}
var vp8HeaderType = Vp8HeaderType.Invalid;
if (this.buffer.AsSpan().SequenceEqual(WebPConstants.Vp8Header)) if (this.buffer.AsSpan().SequenceEqual(WebPConstants.Vp8Header))
{ {
vp8HeaderType = Vp8HeaderType.Vp8; return this.ReadVp8Header();
}
else if (this.buffer.AsSpan().SequenceEqual(WebPConstants.Vp8LHeader))
{
vp8HeaderType = Vp8HeaderType.Vp8L;
} }
else if (this.buffer.SequenceEqual(WebPConstants.Vp8XHeader))
if (this.buffer.AsSpan().SequenceEqual(WebPConstants.Vp8LHeader))
{ {
vp8HeaderType = Vp8HeaderType.Vp8X; return this.ReadVp8LHeader();
} }
else
if (this.buffer.SequenceEqual(WebPConstants.Vp8XHeader))
{ {
WebPThrowHelper.ThrowImageFormatException("Unrecognized VP8 header"); return this.ReadVp8XHeader();
} }
return vp8HeaderType == Vp8HeaderType.Vp8X ? this.ReadVp8XHeader() : this.ReadVp8Header(vp8HeaderType); WebPThrowHelper.ThrowImageFormatException("Unrecognized VP8 header");
return new WebPImageInfo();
} }
private WebPImageInfo ReadVp8XHeader() private WebPImageInfo ReadVp8XHeader()
@ -175,10 +171,11 @@ namespace SixLabors.ImageSharp.Formats.WebP
Width = width, Width = width,
Height = height, Height = height,
IsLossLess = false, // note: this is maybe incorrect here IsLossLess = false, // note: this is maybe incorrect here
DataSize = chunkSize
}; };
} }
private WebPImageInfo ReadVp8Header(Vp8HeaderType vp8HeaderType) private WebPImageInfo ReadVp8Header()
{ {
// VP8 data size. // VP8 data size.
this.currentStream.Read(this.buffer, 0, 3); this.currentStream.Read(this.buffer, 0, 3);
@ -186,22 +183,64 @@ namespace SixLabors.ImageSharp.Formats.WebP
uint dataSize = BinaryPrimitives.ReadUInt32LittleEndian(this.buffer); uint dataSize = BinaryPrimitives.ReadUInt32LittleEndian(this.buffer);
// https://tools.ietf.org/html/rfc6386#page-30 // https://tools.ietf.org/html/rfc6386#page-30
var imageInfo = new byte[11]; // Frame tag that contains four fields:
this.currentStream.Read(imageInfo, 0, imageInfo.Length); // - A 1-bit frame type (0 for key frames, 1 for interframes).
int tmp = (imageInfo[2] << 16) | (imageInfo[1] << 8) | imageInfo[0]; // - A 3-bit version number.
// - A 1-bit show_frame flag.
// - A 19-bit field containing the size of the first data partition in bytes.
this.currentStream.Read(this.buffer, 0, 3);
int tmp = (this.buffer[2] << 16) | (this.buffer[1] << 8) | this.buffer[0];
int isKeyFrame = tmp & 0x1; int isKeyFrame = tmp & 0x1;
int version = (tmp >> 1) & 0x7; int version = (tmp >> 1) & 0x7;
int showFrame = (tmp >> 4) & 0x1; int showFrame = (tmp >> 4) & 0x1;
// Check for VP8 magic bytes.
this.currentStream.Read(this.buffer, 0, 4);
if (!this.buffer.AsSpan(1).SequenceEqual(WebPConstants.Vp8MagicBytes))
{
WebPThrowHelper.ThrowImageFormatException("VP8 magic bytes not found");
}
this.currentStream.Read(this.buffer, 0, 4);
// TODO: Get horizontal and vertical scale // TODO: Get horizontal and vertical scale
int width = BinaryPrimitives.ReadInt16LittleEndian(imageInfo.AsSpan(7)) & 0x3fff; int width = BinaryPrimitives.ReadInt16LittleEndian(this.buffer) & 0x3fff;
int height = BinaryPrimitives.ReadInt16LittleEndian(imageInfo.AsSpan(9)) & 0x3fff; int height = BinaryPrimitives.ReadInt16LittleEndian(this.buffer.AsSpan(2)) & 0x3fff;
return new WebPImageInfo()
{
Width = width,
Height = height,
IsLossLess = false,
DataSize = dataSize
};
}
private WebPImageInfo ReadVp8LHeader()
{
// VP8 data size.
this.currentStream.Read(this.buffer, 0, 4);
uint dataSize = BinaryPrimitives.ReadUInt32LittleEndian(this.buffer);
// One byte signature, should be 0x2f.
byte signature = (byte)this.currentStream.ReadByte();
if (signature != WebPConstants.Vp8LMagicByte)
{
WebPThrowHelper.ThrowImageFormatException("Invalid VP8L signature");
}
// The first 28 bits of the bitstream specify the width and height of the image.
this.currentStream.Read(this.buffer, 0, 4);
// TODO: A bitreader should be used from here on which reads least-significant-bit-first
int height = 0;
int width = 0;
return new WebPImageInfo() return new WebPImageInfo()
{ {
Width = width, Width = width,
Height = height, Height = height,
IsLossLess = vp8HeaderType == Vp8HeaderType.Vp8L, IsLossLess = true,
DataSize = dataSize
}; };
} }

2
src/ImageSharp/Formats/WebP/WebPImageFormatDetector.cs

@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// <returns>True, if its a valid RIFF FourCC.</returns> /// <returns>True, if its a valid RIFF FourCC.</returns>
private bool IsRiffContainer(ReadOnlySpan<byte> header) private bool IsRiffContainer(ReadOnlySpan<byte> header)
{ {
return header.Slice(0, 4).SequenceEqual(WebPConstants.FourCcBytes); return header.Slice(0, 4).SequenceEqual(WebPConstants.RiffFourCc);
} }
/// <summary> /// <summary>

2
src/ImageSharp/Formats/WebP/WebPImageInfo.cs

@ -19,5 +19,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// Gets or sets whether this image uses a lossless compression. /// Gets or sets whether this image uses a lossless compression.
/// </summary> /// </summary>
public bool IsLossLess { get; set; } public bool IsLossLess { get; set; }
public uint DataSize { get; set; }
} }
} }

Loading…
Cancel
Save