Browse Source

Move parsing VP8 infos into separate methods

pull/1552/head
Brian Popow 6 years ago
parent
commit
7f5a48137b
  1. 7
      src/ImageSharp/Formats/WebP/Vp8BitReader.cs
  2. 179
      src/ImageSharp/Formats/WebP/WebPDecoderCore.cs

7
src/ImageSharp/Formats/WebP/Vp8BitReader.cs

@ -46,8 +46,6 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// </summary> /// </summary>
private long pos; private long pos;
public int Pos { get { return (int)pos; } }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Vp8BitReader"/> class. /// Initializes a new instance of the <see cref="Vp8BitReader"/> class.
/// </summary> /// </summary>
@ -61,6 +59,11 @@ namespace SixLabors.ImageSharp.Formats.WebP
this.InitBitreader(startPos); this.InitBitreader(startPos);
} }
public int Pos
{
get { return (int)this.pos; }
}
public int GetBit(int prob) public int GetBit(int prob)
{ {
Guard.MustBeGreaterThan(prob, 0, nameof(prob)); Guard.MustBeGreaterThan(prob, 0, nameof(prob));

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

@ -288,6 +288,9 @@ namespace SixLabors.ImageSharp.Formats.WebP
this.currentStream.Read(this.buffer, 0, 4); this.currentStream.Read(this.buffer, 0, 4);
uint dataSize = BinaryPrimitives.ReadUInt32LittleEndian(this.buffer); uint dataSize = BinaryPrimitives.ReadUInt32LittleEndian(this.buffer);
// remaining counts the available image data payload.
uint remaining = dataSize;
// See paragraph 9.1 https://tools.ietf.org/html/rfc6386#page-30 // See paragraph 9.1 https://tools.ietf.org/html/rfc6386#page-30
// Frame tag that contains four fields: // Frame tag that contains four fields:
// - A 1-bit frame type (0 for key frames, 1 for interframes). // - A 1-bit frame type (0 for key frames, 1 for interframes).
@ -296,6 +299,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
// - A 19-bit field containing the size of the first data partition in bytes. // - A 19-bit field containing the size of the first data partition in bytes.
this.currentStream.Read(this.buffer, 0, 3); this.currentStream.Read(this.buffer, 0, 3);
uint frameTag = (uint)(this.buffer[0] | (this.buffer[1] << 8) | (this.buffer[2] << 16)); uint frameTag = (uint)(this.buffer[0] | (this.buffer[1] << 8) | (this.buffer[2] << 16));
remaining -= 3;
bool isKeyFrame = (frameTag & 0x1) is 0; bool isKeyFrame = (frameTag & 0x1) is 0;
if (!isKeyFrame) if (!isKeyFrame)
{ {
@ -334,14 +338,13 @@ namespace SixLabors.ImageSharp.Formats.WebP
tmp = (uint)BinaryPrimitives.ReadInt16LittleEndian(this.buffer.AsSpan(2)); tmp = (uint)BinaryPrimitives.ReadInt16LittleEndian(this.buffer.AsSpan(2));
uint height = tmp & 0x3fff; uint height = tmp & 0x3fff;
sbyte yScale = (sbyte)(tmp >> 6); sbyte yScale = (sbyte)(tmp >> 6);
remaining -= 7;
if (width is 0 || height is 0) if (width is 0 || height is 0)
{ {
WebPThrowHelper.ThrowImageFormatException("width or height can not be zero"); WebPThrowHelper.ThrowImageFormatException("width or height can not be zero");
} }
// The size of the encoded image data payload. if (partitionLength > remaining)
uint imageDataSize = dataSize - 10; // we have read 10 bytes already.
if (partitionLength > imageDataSize)
{ {
WebPThrowHelper.ThrowImageFormatException("bad partition length"); WebPThrowHelper.ThrowImageFormatException("bad partition length");
} }
@ -355,10 +358,10 @@ namespace SixLabors.ImageSharp.Formats.WebP
var bitReader = new Vp8BitReader( var bitReader = new Vp8BitReader(
this.currentStream, this.currentStream,
imageDataSize, remaining,
this.memoryAllocator); this.memoryAllocator);
// Paragraph 9.2: color space and clamp type follow // Paragraph 9.2: color space and clamp type follow.
sbyte colorSpace = (sbyte)bitReader.ReadValue(1); sbyte colorSpace = (sbyte)bitReader.ReadValue(1);
sbyte clampType = (sbyte)bitReader.ReadValue(1); sbyte clampType = (sbyte)bitReader.ReadValue(1);
var vp8PictureHeader = new Vp8PictureHeader() var vp8PictureHeader = new Vp8PictureHeader()
@ -372,90 +375,18 @@ namespace SixLabors.ImageSharp.Formats.WebP
}; };
// Paragraph 9.3: Parse the segment header. // Paragraph 9.3: Parse the segment header.
var vp8SegmentHeader = new Vp8SegmentHeader();
var proba = new Vp8Proba();
vp8SegmentHeader.UseSegment = bitReader.ReadBool();
bool hasValue; bool hasValue;
if (vp8SegmentHeader.UseSegment) var proba = new Vp8Proba();
{ Vp8SegmentHeader vp8SegmentHeader = ParseSegmentHeader(bitReader, proba);
vp8SegmentHeader.UpdateMap = bitReader.ReadBool();
bool updateData = bitReader.ReadBool();
if (updateData)
{
vp8SegmentHeader.Delta = bitReader.ReadBool();
for (int i = 0; i < vp8SegmentHeader.Quantizer.Length; i++)
{
hasValue = bitReader.ReadBool();
uint quantizeValue = hasValue ? bitReader.ReadValue(7) : 0;
vp8SegmentHeader.Quantizer[i] = (byte)quantizeValue;
}
for (int i = 0; i < vp8SegmentHeader.FilterStrength.Length; i++)
{
hasValue = bitReader.ReadBool();
uint filterStrengthValue = hasValue ? bitReader.ReadValue(6) : 0;
vp8SegmentHeader.FilterStrength[i] = (byte)filterStrengthValue;
}
if (vp8SegmentHeader.UpdateMap)
{
for (int s = 0; s < proba.Segments.Length; ++s)
{
hasValue = bitReader.ReadBool();
proba.Segments[s] = hasValue ? bitReader.ReadValue(8) : 255;
}
}
}
}
else
{
vp8SegmentHeader.UpdateMap = false;
}
// Paragraph 9.4: Parse the filter specs. // Paragraph 9.4: Parse the filter specs.
var vp8FilterHeader = new Vp8FilterHeader(); Vp8FilterHeader vp8FilterHeader = ParseFilterHeader(bitReader);
vp8FilterHeader.LoopFilter = bitReader.ReadBool() ? LoopFilter.Simple : LoopFilter.Complex;
vp8FilterHeader.Level = (int)bitReader.ReadValue(6);
vp8FilterHeader.Sharpness = (int)bitReader.ReadValue(3);
vp8FilterHeader.UseLfDelta = bitReader.ReadBool();
// TODO: use enum here? // TODO: Review Paragraph 9.5: ParsePartitions.
// 0 = 0ff, 1 = simple, 2 = complex
int filterType = (vp8FilterHeader.Level is 0) ? 0 : vp8FilterHeader.LoopFilter is LoopFilter.Simple ? 1 : 2;
if (vp8FilterHeader.UseLfDelta)
{
// Update lf-delta?
if (bitReader.ReadBool())
{
for (int i = 0; i < vp8FilterHeader.RefLfDelta.Length; i++)
{
hasValue = bitReader.ReadBool();
if (hasValue)
{
vp8FilterHeader.RefLfDelta[i] = bitReader.ReadSignedValue(6);
}
}
for (int i = 0; i < vp8FilterHeader.ModeLfDelta.Length; i++)
{
hasValue = bitReader.ReadBool();
if (hasValue)
{
vp8FilterHeader.ModeLfDelta[i] = bitReader.ReadSignedValue(6);
}
}
}
}
// Paragraph 9.5: ParsePartitions.
int numPartsMinusOne = (1 << (int)bitReader.ReadValue(2)) - 1; int numPartsMinusOne = (1 << (int)bitReader.ReadValue(2)) - 1;
int lastPart = numPartsMinusOne; int lastPart = numPartsMinusOne;
// TODO: check if we have enough data available here, throw exception if not // TODO: check if we have enough data available here, throw exception if not
int partStart = bitReader.Pos + (lastPart * 3); int partStart = bitReader.Pos + (lastPart * 3);
for (int p = 0; p < lastPart; ++p)
{
// int psize = sz[0] | (sz[1] << 8) | (sz[2] << 16);
}
// Paragraph 9.6: Dequantization Indices. // Paragraph 9.6: Dequantization Indices.
int baseQ0 = (int)bitReader.ReadValue(7); int baseQ0 = (int)bitReader.ReadValue(7);
@ -516,7 +447,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
// Ignore the value of update_proba // Ignore the value of update_proba
bitReader.ReadBool(); bitReader.ReadBool();
// Parse probabilities. // Paragraph 13.4: Parse probabilities.
for (int t = 0; t < WebPConstants.NumTypes; ++t) for (int t = 0; t < WebPConstants.NumTypes; ++t)
{ {
for (int b = 0; b < WebPConstants.NumBands; ++b) for (int b = 0; b < WebPConstants.NumBands; ++b)
@ -562,6 +493,90 @@ namespace SixLabors.ImageSharp.Formats.WebP
}; };
} }
private static Vp8FilterHeader ParseFilterHeader(Vp8BitReader bitReader)
{
var vp8FilterHeader = new Vp8FilterHeader();
vp8FilterHeader.LoopFilter = bitReader.ReadBool() ? LoopFilter.Simple : LoopFilter.Complex;
vp8FilterHeader.Level = (int)bitReader.ReadValue(6);
vp8FilterHeader.Sharpness = (int)bitReader.ReadValue(3);
vp8FilterHeader.UseLfDelta = bitReader.ReadBool();
// TODO: use enum here?
// 0 = 0ff, 1 = simple, 2 = complex
int filterType = (vp8FilterHeader.Level is 0) ? 0 : vp8FilterHeader.LoopFilter is LoopFilter.Simple ? 1 : 2;
bool hasValue;
if (vp8FilterHeader.UseLfDelta)
{
// Update lf-delta?
if (bitReader.ReadBool())
{
for (int i = 0; i < vp8FilterHeader.RefLfDelta.Length; i++)
{
hasValue = bitReader.ReadBool();
if (hasValue)
{
vp8FilterHeader.RefLfDelta[i] = bitReader.ReadSignedValue(6);
}
}
for (int i = 0; i < vp8FilterHeader.ModeLfDelta.Length; i++)
{
hasValue = bitReader.ReadBool();
if (hasValue)
{
vp8FilterHeader.ModeLfDelta[i] = bitReader.ReadSignedValue(6);
}
}
}
}
return vp8FilterHeader;
}
private static Vp8SegmentHeader ParseSegmentHeader(Vp8BitReader bitReader, Vp8Proba proba)
{
var vp8SegmentHeader = new Vp8SegmentHeader();
vp8SegmentHeader.UseSegment = bitReader.ReadBool();
bool hasValue;
if (vp8SegmentHeader.UseSegment)
{
vp8SegmentHeader.UpdateMap = bitReader.ReadBool();
bool updateData = bitReader.ReadBool();
if (updateData)
{
vp8SegmentHeader.Delta = bitReader.ReadBool();
for (int i = 0; i < vp8SegmentHeader.Quantizer.Length; i++)
{
hasValue = bitReader.ReadBool();
uint quantizeValue = hasValue ? bitReader.ReadValue(7) : 0;
vp8SegmentHeader.Quantizer[i] = (byte)quantizeValue;
}
for (int i = 0; i < vp8SegmentHeader.FilterStrength.Length; i++)
{
hasValue = bitReader.ReadBool();
uint filterStrengthValue = hasValue ? bitReader.ReadValue(6) : 0;
vp8SegmentHeader.FilterStrength[i] = (byte)filterStrengthValue;
}
if (vp8SegmentHeader.UpdateMap)
{
for (int s = 0; s < proba.Segments.Length; ++s)
{
hasValue = bitReader.ReadBool();
proba.Segments[s] = hasValue ? bitReader.ReadValue(8) : 255;
}
}
}
}
else
{
vp8SegmentHeader.UpdateMap = false;
}
return vp8SegmentHeader;
}
/// <summary> /// <summary>
/// Reads the header of a lossless webp image. /// Reads the header of a lossless webp image.
/// </summary> /// </summary>

Loading…
Cancel
Save