Browse Source

Additional webp constants, first attempt parsing VP8L header

pull/1552/head
Brian Popow 6 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>
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>
/// The header bytes identifying RIFF file.
/// </summary>
public static readonly byte[] FourCcBytes =
public static readonly byte[] RiffFourCc =
{
0x52, // R
0x49, // I

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

@ -69,6 +69,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
uint chunkSize = this.ReadImageHeader();
WebPImageInfo imageInfo = this.ReadVp8Info();
// TODO: there can be optional chunks after that, like EXIF.
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.
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))
{
vp8HeaderType = Vp8HeaderType.Vp8;
}
else if (this.buffer.AsSpan().SequenceEqual(WebPConstants.Vp8LHeader))
{
vp8HeaderType = Vp8HeaderType.Vp8L;
return this.ReadVp8Header();
}
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()
@ -175,10 +171,11 @@ namespace SixLabors.ImageSharp.Formats.WebP
Width = width,
Height = height,
IsLossLess = false, // note: this is maybe incorrect here
DataSize = chunkSize
};
}
private WebPImageInfo ReadVp8Header(Vp8HeaderType vp8HeaderType)
private WebPImageInfo ReadVp8Header()
{
// VP8 data size.
this.currentStream.Read(this.buffer, 0, 3);
@ -186,22 +183,64 @@ namespace SixLabors.ImageSharp.Formats.WebP
uint dataSize = BinaryPrimitives.ReadUInt32LittleEndian(this.buffer);
// https://tools.ietf.org/html/rfc6386#page-30
var imageInfo = new byte[11];
this.currentStream.Read(imageInfo, 0, imageInfo.Length);
int tmp = (imageInfo[2] << 16) | (imageInfo[1] << 8) | imageInfo[0];
// Frame tag that contains four fields:
// - A 1-bit frame type (0 for key frames, 1 for interframes).
// - 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 version = (tmp >> 1) & 0x7;
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
int width = BinaryPrimitives.ReadInt16LittleEndian(imageInfo.AsSpan(7)) & 0x3fff;
int height = BinaryPrimitives.ReadInt16LittleEndian(imageInfo.AsSpan(9)) & 0x3fff;
int width = BinaryPrimitives.ReadInt16LittleEndian(this.buffer) & 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()
{
Width = width,
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>
private bool IsRiffContainer(ReadOnlySpan<byte> header)
{
return header.Slice(0, 4).SequenceEqual(WebPConstants.FourCcBytes);
return header.Slice(0, 4).SequenceEqual(WebPConstants.RiffFourCc);
}
/// <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.
/// </summary>
public bool IsLossLess { get; set; }
public uint DataSize { get; set; }
}
}

Loading…
Cancel
Save