Browse Source

Implement Parse Partitions

pull/1552/head
Brian Popow 6 years ago
parent
commit
9838e2e512
  1. 4
      src/ImageSharp/Formats/WebP/BitReaderBase.cs
  2. 4
      src/ImageSharp/Formats/WebP/VP8BandProbas.cs
  3. 44
      src/ImageSharp/Formats/WebP/Vp8BitReader.cs
  4. 60
      src/ImageSharp/Formats/WebP/Vp8Decoder.cs
  5. 3
      src/ImageSharp/Formats/WebP/Vp8MacroBlockData.cs
  6. 16
      src/ImageSharp/Formats/WebP/Vp8Proba.cs
  7. 6
      src/ImageSharp/Formats/WebP/Vp8QuantMatrix.cs
  8. 14
      src/ImageSharp/Formats/WebP/Vp8TopSamples.cs
  9. 3
      src/ImageSharp/Formats/WebP/WebPConstants.cs
  10. 22
      src/ImageSharp/Formats/WebP/WebPDecoderCore.cs
  11. 9
      src/ImageSharp/Formats/WebP/WebPImageInfo.cs
  12. 227
      src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs

4
src/ImageSharp/Formats/WebP/BitReaderBase.cs

@ -15,9 +15,9 @@ namespace SixLabors.ImageSharp.Formats.WebP
internal abstract class BitReaderBase internal abstract class BitReaderBase
{ {
/// <summary> /// <summary>
/// Gets raw encoded image data. /// Gets or sets the raw encoded image data.
/// </summary> /// </summary>
protected byte[] Data { get; private set; } public byte[] Data { get; set; }
/// <summary> /// <summary>
/// Copies the raw encoded image data from the stream into a byte array. /// Copies the raw encoded image data from the stream into a byte array.

4
src/ImageSharp/Formats/WebP/VP8BandProbas.cs

@ -11,6 +11,10 @@ namespace SixLabors.ImageSharp.Formats.WebP
public Vp8BandProbas() public Vp8BandProbas()
{ {
this.Probabilities = new Vp8ProbaArray[WebPConstants.NumCtx]; this.Probabilities = new Vp8ProbaArray[WebPConstants.NumCtx];
for (int i = 0; i < WebPConstants.NumCtx; i++)
{
this.Probabilities[i] = new Vp8ProbaArray();
}
} }
public Vp8ProbaArray[] Probabilities { get; } public Vp8ProbaArray[] Probabilities { get; }

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

@ -32,9 +32,11 @@ namespace SixLabors.ImageSharp.Formats.WebP
private int bits; private int bits;
/// <summary> /// <summary>
/// The next byte to be read. /// Max packed-read position on buffer.
/// </summary> /// </summary>
private byte buf; private uint bufferMax;
private uint bufferEnd;
/// <summary> /// <summary>
/// True if input is exhausted. /// True if input is exhausted.
@ -53,10 +55,20 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// <param name="imageDataSize">The raw image data size in bytes.</param> /// <param name="imageDataSize">The raw image data size in bytes.</param>
/// <param name="memoryAllocator">Used for allocating memory during reading data from the stream.</param> /// <param name="memoryAllocator">Used for allocating memory during reading data from the stream.</param>
/// <param name="startPos">Start index in the data array. Defaults to 0.</param> /// <param name="startPos">Start index in the data array. Defaults to 0.</param>
public Vp8BitReader(Stream inputStream, uint imageDataSize, MemoryAllocator memoryAllocator, int startPos = 0) public Vp8BitReader(Stream inputStream, uint imageDataSize, MemoryAllocator memoryAllocator, uint partitionLength, int startPos = 0)
{ {
this.ImageDataSize = imageDataSize;
this.PartitionLength = partitionLength;
this.ReadImageDataFromStream(inputStream, (int)imageDataSize, memoryAllocator); this.ReadImageDataFromStream(inputStream, (int)imageDataSize, memoryAllocator);
this.InitBitreader(startPos); this.InitBitreader(partitionLength, startPos);
}
public Vp8BitReader(byte[] imageData, uint partitionLength, int startPos = 0)
{
this.Data = imageData;
this.ImageDataSize = (uint)imageData.Length;
this.PartitionLength = partitionLength;
this.InitBitreader(partitionLength, startPos);
} }
public int Pos public int Pos
@ -64,6 +76,12 @@ namespace SixLabors.ImageSharp.Formats.WebP
get { return (int)this.pos; } get { return (int)this.pos; }
} }
public uint ImageDataSize { get; }
public uint PartitionLength { get; }
public uint Remaining { get; set; }
public int GetBit(int prob) public int GetBit(int prob)
{ {
Guard.MustBeGreaterThan(prob, 0, nameof(prob)); Guard.MustBeGreaterThan(prob, 0, nameof(prob));
@ -123,26 +141,26 @@ namespace SixLabors.ImageSharp.Formats.WebP
return this.ReadValue(1) != 0 ? -value : value; return this.ReadValue(1) != 0 ? -value : value;
} }
private void InitBitreader(int pos = 0) private void InitBitreader(uint size, int pos = 0)
{ {
this.range = 255 - 1; this.range = 255 - 1;
this.value = 0; this.value = 0;
this.bits = -8; // to load the very first 8bits. this.bits = -8; // to load the very first 8 bits.
this.eof = false; this.eof = false;
this.pos = 0; this.pos = pos;
this.bufferEnd = (uint)(pos + size);
this.bufferMax = (uint)(size > 8 ? pos + size - 8 + 1 : pos);
this.LoadNewBytes(); this.LoadNewBytes();
} }
private void LoadNewBytes() private void LoadNewBytes()
{ {
if (this.pos < this.Data.Length) if (this.pos < this.bufferMax)
{ {
ulong bits; ulong inBits = BinaryPrimitives.ReadUInt64LittleEndian(this.Data.AsSpan((int)this.pos, 8));
ulong inBits = BinaryPrimitives.ReadUInt64LittleEndian(this.Data.AsSpan().Slice((int)this.pos, 8));
this.pos += BitsCount >> 3; this.pos += BitsCount >> 3;
this.buf = this.Data[this.pos]; ulong bits = this.ByteSwap64(inBits);
bits = this.ByteSwap64(inBits);
bits >>= 64 - BitsCount; bits >>= 64 - BitsCount;
this.value = bits | (this.value << BitsCount); this.value = bits | (this.value << BitsCount);
this.bits += BitsCount; this.bits += BitsCount;
@ -156,7 +174,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
private void LoadFinalBytes() private void LoadFinalBytes()
{ {
// Only read 8bits at a time. // Only read 8bits at a time.
if (this.pos < this.Data.Length) if (this.pos < this.bufferEnd)
{ {
this.bits += 8; this.bits += 8;
this.value = this.Data[this.pos++] | (this.value << 8); this.value = this.Data[this.pos++] | (this.value << 8);

60
src/ImageSharp/Formats/WebP/Vp8Decoder.cs

@ -15,10 +15,32 @@ namespace SixLabors.ImageSharp.Formats.WebP
this.FilterHeader = filterHeader; this.FilterHeader = filterHeader;
this.SegmentHeader = segmentHeader; this.SegmentHeader = segmentHeader;
this.Probabilities = probabilities; this.Probabilities = probabilities;
this.IntraL = new byte[4];
this.YuvBuffer = new byte[(WebPConstants.Bps * 17) + (WebPConstants.Bps * 9)];
this.MbWidth = (int)((this.PictureHeader.Width + 15) >> 4);
this.MbHeight = (int)((this.PictureHeader.Height + 15) >> 4);
this.MacroBlockInfo = new Vp8MacroBlock[this.MbWidth];
this.MacroBlockData = new Vp8MacroBlockData[this.MbWidth];
this.YuvTopSamples = new Vp8TopSamples[this.MbWidth];
for (int i = 0; i < this.MbWidth; i++)
{
this.MacroBlockInfo[i] = new Vp8MacroBlock();
this.MacroBlockData[i] = new Vp8MacroBlockData();
this.YuvTopSamples[i] = new Vp8TopSamples();
}
this.DeQuantMatrices = new Vp8QuantMatrix[WebPConstants.NumMbSegments]; this.DeQuantMatrices = new Vp8QuantMatrix[WebPConstants.NumMbSegments];
this.FilterStrength = new Vp8FilterInfo[WebPConstants.NumMbSegments, 2]; this.FilterStrength = new Vp8FilterInfo[WebPConstants.NumMbSegments, 2];
this.IntraL = new byte[4]; for (int i = 0; i < WebPConstants.NumMbSegments; i++)
{
this.DeQuantMatrices[i] = new Vp8QuantMatrix();
for (int j = 0; j < 2; j++)
{
this.FilterStrength[i, j] = new Vp8FilterInfo();
}
}
this.Vp8BitReaders = new Vp8BitReader[WebPConstants.MaxNumPartitions];
this.Init(io); this.Init(io);
} }
@ -30,14 +52,20 @@ namespace SixLabors.ImageSharp.Formats.WebP
public Vp8SegmentHeader SegmentHeader { get; } public Vp8SegmentHeader SegmentHeader { get; }
// number of partitions minus one.
public uint NumPartsMinusOne { get; }
// per-partition boolean decoders.
public Vp8BitReader[] Vp8BitReaders { get; }
public bool Dither { get; set; } public bool Dither { get; set; }
/// <summary> /// <summary>
/// Gets or sets dequantization matrices (one set of DC/AC dequant factor per segment). /// Gets or sets dequantization matrices (one set of DC/AC dequant factor per segment).
/// </summary> /// </summary>
public Vp8QuantMatrix[] DeQuantMatrices { get; private set; } public Vp8QuantMatrix[] DeQuantMatrices { get; }
public bool UseSkipProba { get; set; } public bool UseSkipProbability { get; set; }
public byte SkipProbability { get; set; } public byte SkipProbability { get; set; }
@ -52,12 +80,12 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// <summary> /// <summary>
/// Gets or sets the width in macroblock units. /// Gets or sets the width in macroblock units.
/// </summary> /// </summary>
public int MbWidth { get; set; } public int MbWidth { get; }
/// <summary> /// <summary>
/// Gets or sets the height in macroblock units. /// Gets or sets the height in macroblock units.
/// </summary> /// </summary>
public int MbHeight { get; set; } public int MbHeight { get; }
/// <summary> /// <summary>
/// Gets or sets the top-left x index of the macroblock that must be in-loop filtered. /// Gets or sets the top-left x index of the macroblock that must be in-loop filtered.
@ -72,7 +100,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// <summary> /// <summary>
/// Gets or sets the last bottom-right x index of the macroblock that must be decoded. /// Gets or sets the last bottom-right x index of the macroblock that must be decoded.
/// </summary> /// </summary>
public int BotomRightMbX { get; set; } public int BottomRightMbX { get; set; }
/// <summary> /// <summary>
/// Gets or sets the last bottom-right y index of the macroblock that must be decoded. /// Gets or sets the last bottom-right y index of the macroblock that must be decoded.
@ -90,14 +118,14 @@ namespace SixLabors.ImageSharp.Formats.WebP
public int MbY { get; set; } public int MbY { get; set; }
/// <summary> /// <summary>
/// Gets or sets the parsed reconstruction data. /// Gets the parsed reconstruction data.
/// </summary> /// </summary>
public Vp8MacroBlockData[] MacroBlockData { get; set; } public Vp8MacroBlockData[] MacroBlockData { get; }
/// <summary> /// <summary>
/// Gets or sets contextual macroblock infos. /// Gets contextual macroblock infos.
/// </summary> /// </summary>
public Vp8MacroBlock[] MacroBlockInfo { get; set; } public Vp8MacroBlock[] MacroBlockInfo { get; }
public int MacroBlockIdx { get; set; } public int MacroBlockIdx { get; set; }
@ -105,6 +133,10 @@ namespace SixLabors.ImageSharp.Formats.WebP
public Vp8FilterInfo[,] FilterStrength { get; } public Vp8FilterInfo[,] FilterStrength { get; }
public byte[] YuvBuffer { get; }
public Vp8TopSamples[] YuvTopSamples { get; }
/// <summary> /// <summary>
/// Gets or sets filter strength info. /// Gets or sets filter strength info.
/// </summary> /// </summary>
@ -112,8 +144,6 @@ namespace SixLabors.ImageSharp.Formats.WebP
public void Init(Vp8Io io) public void Init(Vp8Io io)
{ {
this.MbWidth = (int)((this.PictureHeader.Width + 15) >> 4);
this.MbHeight = (int)((this.PictureHeader.Height + 15) >> 4);
int intraPredModeSize = 4 * this.MbWidth; int intraPredModeSize = 4 * this.MbWidth;
this.IntraT = new byte[intraPredModeSize]; this.IntraT = new byte[intraPredModeSize];
@ -157,10 +187,10 @@ namespace SixLabors.ImageSharp.Formats.WebP
// We need some 'extra' pixels on the right/bottom. // We need some 'extra' pixels on the right/bottom.
this.BottomRightMbY = (io.CropBottom + 15 + extraPixels) >> 4; this.BottomRightMbY = (io.CropBottom + 15 + extraPixels) >> 4;
this.BotomRightMbX = (io.CropRight + 15 + extraPixels) >> 4; this.BottomRightMbX = (io.CropRight + 15 + extraPixels) >> 4;
if (this.BotomRightMbX > this.MbWidth) if (this.BottomRightMbX > this.MbWidth)
{ {
this.BotomRightMbX = this.MbWidth; this.BottomRightMbX = this.MbWidth;
} }
if (this.BottomRightMbY > this.MbHeight) if (this.BottomRightMbY > this.MbHeight)

3
src/ImageSharp/Formats/WebP/Vp8MacroBlockData.cs

@ -11,12 +11,13 @@ namespace SixLabors.ImageSharp.Formats.WebP
public Vp8MacroBlockData() public Vp8MacroBlockData()
{ {
this.Modes = new byte[16]; this.Modes = new byte[16];
this.Coeffs = new short[384];
} }
/// <summary> /// <summary>
/// Gets or sets the coefficient. 384 coeffs = (16+4+4) * 4*4. /// Gets or sets the coefficient. 384 coeffs = (16+4+4) * 4*4.
/// </summary> /// </summary>
public short Coeffs { get; set; } public short[] Coeffs { get; set; }
/// <summary> /// <summary>
/// Gets or sets a value indicating whether its intra4x4. /// Gets or sets a value indicating whether its intra4x4.

16
src/ImageSharp/Formats/WebP/Vp8Proba.cs

@ -15,6 +15,22 @@ namespace SixLabors.ImageSharp.Formats.WebP
this.Segments = new uint[MbFeatureTreeProbs]; this.Segments = new uint[MbFeatureTreeProbs];
this.Bands = new Vp8BandProbas[WebPConstants.NumTypes, WebPConstants.NumBands]; this.Bands = new Vp8BandProbas[WebPConstants.NumTypes, WebPConstants.NumBands];
this.BandsPtr = new Vp8BandProbas[WebPConstants.NumTypes, 16 + 1]; this.BandsPtr = new Vp8BandProbas[WebPConstants.NumTypes, 16 + 1];
for (int i = 0; i < WebPConstants.NumTypes; i++)
{
for (int j = 0; j < WebPConstants.NumBands; j++)
{
this.Bands[i, j] = new Vp8BandProbas();
}
}
for (int i = 0; i < WebPConstants.NumTypes; i++)
{
for (int j = 0; j < 17; j++)
{
this.BandsPtr[i, j] = new Vp8BandProbas();
}
}
} }
public uint[] Segments { get; } public uint[] Segments { get; }

6
src/ImageSharp/Formats/WebP/Vp8QuantMatrix.cs

@ -5,11 +5,11 @@ namespace SixLabors.ImageSharp.Formats.WebP
{ {
internal class Vp8QuantMatrix internal class Vp8QuantMatrix
{ {
public int[] Y1Mat { get; set; } public int[] Y1Mat { get; } = new int[2];
public int[] Y2Mat { get; set; } public int[] Y2Mat { get; } = new int[2];
public int[] UvMat { get; set; } public int[] UvMat { get; } = new int[2];
/// <summary> /// <summary>
/// Gets or sets the U/V quantizer value. /// Gets or sets the U/V quantizer value.

14
src/ImageSharp/Formats/WebP/Vp8TopSamples.cs

@ -0,0 +1,14 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.WebP
{
internal class Vp8TopSamples
{
public byte[] Y { get; } = new byte[16];
public byte[] U { get; } = new byte[8];
public byte[] V { get; } = new byte[8];
}
}

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

@ -121,6 +121,9 @@ namespace SixLabors.ImageSharp.Formats.WebP
public const int NumCtx = 3; public const int NumCtx = 3;
// this is the common stride for enc/dec
public const int Bps = 32;
// intra prediction modes (TODO: maybe use an enum for this) // intra prediction modes (TODO: maybe use an enum for this)
public const int DcPred = 0; public const int DcPred = 0;
public const int TmPred = 1; public const int TmPred = 1;

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

@ -291,7 +291,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
// remaining counts the available image data payload. // remaining counts the available image data payload.
uint remaining = dataSize; uint remaining = dataSize;
// See paragraph 9.1 https://tools.ietf.org/html/rfc6386#page-30 // 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).
// - A 3-bit version number. // - A 3-bit version number.
@ -359,31 +359,21 @@ namespace SixLabors.ImageSharp.Formats.WebP
var bitReader = new Vp8BitReader( var bitReader = new Vp8BitReader(
this.currentStream, this.currentStream,
remaining, remaining,
this.memoryAllocator); this.memoryAllocator,
partitionLength);
// Paragraph 9.2: color space and clamp type follow. bitReader.Remaining = remaining;
sbyte colorSpace = (sbyte)bitReader.ReadValue(1);
sbyte clampType = (sbyte)bitReader.ReadValue(1);
var vp8PictureHeader = new Vp8PictureHeader()
{
Width = (uint)width,
Height = (uint)height,
XScale = xScale,
YScale = yScale,
ColorSpace = colorSpace,
ClampType = clampType
};
return new WebPImageInfo() return new WebPImageInfo()
{ {
Width = width, Width = width,
Height = height, Height = height,
XScale = xScale,
YScale = yScale,
BitsPerPixel = features?.Alpha is true ? WebPBitsPerPixel.Pixel32 : WebPBitsPerPixel.Pixel24, BitsPerPixel = features?.Alpha is true ? WebPBitsPerPixel.Pixel32 : WebPBitsPerPixel.Pixel24,
IsLossLess = false, IsLossLess = false,
Features = features, Features = features,
Vp8Profile = (sbyte)version, Vp8Profile = (sbyte)version,
Vp8FrameHeader = vp8FrameHeader, Vp8FrameHeader = vp8FrameHeader,
Vp8PictureHeader = vp8PictureHeader,
Vp8BitReader = bitReader Vp8BitReader = bitReader
}; };
} }

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

@ -15,6 +15,10 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// </summary> /// </summary>
public uint Height { get; set; } public uint Height { get; set; }
public sbyte XScale { get; set; }
public sbyte YScale { get; set; }
/// <summary> /// <summary>
/// Gets or sets the bits per pixel. /// Gets or sets the bits per pixel.
/// </summary> /// </summary>
@ -40,11 +44,6 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// </summary> /// </summary>
public Vp8FrameHeader Vp8FrameHeader { get; set; } public Vp8FrameHeader Vp8FrameHeader { get; set; }
/// <summary>
/// Gets or sets the VP8 picture header.
/// </summary>
public Vp8PictureHeader Vp8PictureHeader { get; set; }
/// <summary> /// <summary>
/// Gets or sets the VP8L bitreader. Will be null, if its not lossless image. /// Gets or sets the VP8L bitreader. Will be null, if its not lossless image.
/// </summary> /// </summary>

227
src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs

@ -36,6 +36,19 @@ namespace SixLabors.ImageSharp.Formats.WebP
// TODO residue signal from DCT: 4x4 blocks of DCT transforms, 16Y, 4U, 4V // TODO residue signal from DCT: 4x4 blocks of DCT transforms, 16Y, 4U, 4V
Vp8Profile vp8Profile = this.DecodeProfile(info.Vp8Profile); Vp8Profile vp8Profile = this.DecodeProfile(info.Vp8Profile);
// Paragraph 9.2: color space and clamp type follow.
sbyte colorSpace = (sbyte)this.bitReader.ReadValue(1);
sbyte clampType = (sbyte)this.bitReader.ReadValue(1);
var vp8PictureHeader = new Vp8PictureHeader()
{
Width = (uint)width,
Height = (uint)height,
XScale = info.XScale,
YScale = info.YScale,
ColorSpace = colorSpace,
ClampType = clampType
};
// Paragraph 9.3: Parse the segment header. // Paragraph 9.3: Parse the segment header.
var proba = new Vp8Proba(); var proba = new Vp8Proba();
Vp8SegmentHeader vp8SegmentHeader = this.ParseSegmentHeader(proba); Vp8SegmentHeader vp8SegmentHeader = this.ParseSegmentHeader(proba);
@ -43,23 +56,21 @@ namespace SixLabors.ImageSharp.Formats.WebP
// Paragraph 9.4: Parse the filter specs. // Paragraph 9.4: Parse the filter specs.
Vp8FilterHeader vp8FilterHeader = this.ParseFilterHeader(); Vp8FilterHeader vp8FilterHeader = this.ParseFilterHeader();
// TODO: Review Paragraph 9.5: ParsePartitions. var vp8Io = default(Vp8Io);
int numPartsMinusOne = (1 << (int)this.bitReader.ReadValue(2)) - 1; var decoder = new Vp8Decoder(info.Vp8FrameHeader, vp8PictureHeader, vp8FilterHeader, vp8SegmentHeader, proba, vp8Io);
int lastPart = numPartsMinusOne;
// TODO: check if we have enough data available here, throw exception if not // Paragraph 9.5: Parse partitions.
int partStart = this.bitReader.Pos + (lastPart * 3); this.ParsePartitions(decoder);
// Paragraph 9.6: Dequantization Indices. // Paragraph 9.6: Dequantization Indices.
this.ParseDequantizationIndices(vp8SegmentHeader); this.ParseDequantizationIndices(decoder.SegmentHeader);
// Ignore the value of update_proba // Ignore the value of update_proba
this.bitReader.ReadBool(); this.bitReader.ReadBool();
// Paragraph 13.4: Parse probabilities. // Paragraph 13.4: Parse probabilities.
this.ParseProbabilities(proba); this.ParseProbabilities(decoder, decoder.Probabilities);
var vp8Io = default(Vp8Io);
var decoder = new Vp8Decoder(info.Vp8FrameHeader, info.Vp8PictureHeader, vp8FilterHeader, vp8SegmentHeader, proba, vp8Io);
this.ParseFrame(decoder, vp8Io); this.ParseFrame(decoder, vp8Io);
} }
@ -81,21 +92,9 @@ namespace SixLabors.ImageSharp.Formats.WebP
// Prepare for next scanline. // Prepare for next scanline.
this.InitScanline(dec); this.InitScanline(dec);
// TODO: Reconstruct, filter and emit the row. // Reconstruct, filter and emit the row.
} this.ProcessRow(dec);
}
private void InitScanline(Vp8Decoder dec)
{
Vp8MacroBlock left = dec.MacroBlockInfo[dec.MacroBlockIdx - 1];
left.NoneZeroAcDcCoeffs = 0;
left.NoneZeroDcCoeffs = 0;
for (int i = 0; i < dec.IntraL.Length; i++)
{
dec.IntraL[i] = 0;
} }
dec.MbX = 0;
} }
private void ParseIntraMode(Vp8Decoder dec, int mbX) private void ParseIntraMode(Vp8Decoder dec, int mbX)
@ -117,7 +116,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
block.Segment = 0; block.Segment = 0;
} }
if (dec.UseSkipProba) if (dec.UseSkipProbability)
{ {
block.Skip = (byte)this.bitReader.GetBit(dec.SkipProbability); block.Skip = (byte)this.bitReader.GetBit(dec.SkipProbability);
} }
@ -165,7 +164,144 @@ namespace SixLabors.ImageSharp.Formats.WebP
block.UvMode = (byte)(this.bitReader.GetBit(142) is 0 ? 0 : block.UvMode = (byte)(this.bitReader.GetBit(142) is 0 ? 0 :
this.bitReader.GetBit(114) is 0 ? 2 : this.bitReader.GetBit(114) is 0 ? 2 :
this.bitReader.GetBit(183) > 0 ? 1 : 3); this.bitReader.GetBit(183) > 0 ? 1 : 3);
}
private void InitScanline(Vp8Decoder dec)
{
Vp8MacroBlock left = dec.MacroBlockInfo[dec.MacroBlockIdx - 1];
left.NoneZeroAcDcCoeffs = 0;
left.NoneZeroDcCoeffs = 0;
for (int i = 0; i < dec.IntraL.Length; i++)
{
dec.IntraL[i] = 0;
}
dec.MbX = 0;
}
private void ProcessRow(Vp8Decoder dec)
{
bool filterRow = (dec.Filter != LoopFilter.None) &&
(dec.MbY >= dec.TopLeftMbY) && (dec.MbY <= dec.BottomRightMbY);
this.ReconstructRow(dec, filterRow);
}
private void ReconstructRow(Vp8Decoder dec, bool filterRow)
{
int mby = dec.MbY;
int yOff = (WebPConstants.Bps * 1) + 8;
int uOff = yOff + (WebPConstants.Bps * 16) + WebPConstants.Bps;
int vOff = uOff + 16;
Span<byte> yDst = dec.YuvBuffer.AsSpan(yOff);
Span<byte> uDst = dec.YuvBuffer.AsSpan(uOff);
Span<byte> vDst = dec.YuvBuffer.AsSpan(vOff);
// Initialize left-most block.
for (int i = 0; i < 16; ++i)
{
yDst[(i * WebPConstants.Bps) - 1] = 129;
}
for (int i = 0; i < 8; ++i)
{
uDst[(i * WebPConstants.Bps) - 1] = 129;
vDst[(i * WebPConstants.Bps) - 1] = 129;
}
// Init top-left sample on left column too.
if (mby > 0)
{
yDst[-1 - WebPConstants.Bps] = uDst[-1 - WebPConstants.Bps] = vDst[-1 - WebPConstants.Bps] = 129;
}
else
{
// We only need to do this init once at block (0,0).
// Afterward, it remains valid for the whole topmost row.
Span<byte> tmp = dec.YuvBuffer.AsSpan(yOff - WebPConstants.Bps - 1, 16 + 4 + 1);
for (int i = 0; i < tmp.Length; ++i)
{
tmp[i] = 127;
}
tmp = dec.YuvBuffer.AsSpan(uOff - WebPConstants.Bps - 1, 8 + 1);
for (int i = 0; i < tmp.Length; ++i)
{
tmp[i] = 127;
}
tmp = dec.YuvBuffer.AsSpan(vOff - WebPConstants.Bps - 1, 8 + 1);
for (int i = 0; i < tmp.Length; ++i)
{
tmp[i] = 127;
}
}
// Reconstruct one row.
for (int mbx = 0; mbx < dec.MbWidth; ++mbx)
{
Vp8MacroBlockData block = dec.MacroBlockData[mbx];
// Rotate in the left samples from previously decoded block. We move four
// pixels at a time for alignment reason, and because of in-loop filter.
if (mbx > 0)
{
for (int i = -1; i < 16; ++i)
{
// Copy32b(&y_dst[j * BPS - 4], &y_dst[j * BPS + 12]);
}
for (int i = -1; i < 8; ++i)
{
// Copy32b(&u_dst[j * BPS - 4], &u_dst[j * BPS + 4]);
// Copy32b(&v_dst[j * BPS - 4], &v_dst[j * BPS + 4]);
}
// Bring top samples into the cache.
Vp8TopSamples topSamples = dec.YuvTopSamples[mbx];
short[] coeffs = block.Coeffs;
uint bits = block.NonZeroY;
if (mby > 0)
{
//memcpy(y_dst - BPS, top_yuv[0].y, 16);
//memcpy(u_dst - BPS, top_yuv[0].u, 8);
//memcpy(v_dst - BPS, top_yuv[0].v, 8);
}
// Predict and add residuals.
if (block.IsI4x4)
{
if (mby > 0)
{
if (mbx >= dec.MbWidth - 1)
{
// On rightmost border.
//memset(top_right, top_yuv[0].y[15], sizeof(*top_right));
}
else
{
// memcpy(top_right, top_yuv[1].y, sizeof(*top_right));
}
}
// Replicate the top-right pixels below.
// Predict and add residuals for all 4x4 blocks in turn.
for (int n = 0; n < 16; ++n, bits <<= 2)
{
}
}
else
{
// 16x16
}
}
}
} }
private Vp8Profile DecodeProfile(int version) private Vp8Profile DecodeProfile(int version)
@ -193,7 +329,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
Vp8MacroBlock left = dec.MacroBlockInfo[dec.MacroBlockIdx - 1]; // TODO: not sure if this - 1 is correct here Vp8MacroBlock left = dec.MacroBlockInfo[dec.MacroBlockIdx - 1]; // TODO: not sure if this - 1 is correct here
Vp8MacroBlock macroBlock = dec.MacroBlockInfo[dec.MacroBlockIdx + dec.MbX]; Vp8MacroBlock macroBlock = dec.MacroBlockInfo[dec.MacroBlockIdx + dec.MbX];
Vp8MacroBlockData blockData = dec.MacroBlockData[dec.MacroBlockIdx + dec.MbX]; Vp8MacroBlockData blockData = dec.MacroBlockData[dec.MacroBlockIdx + dec.MbX];
int skip = dec.UseSkipProba ? blockData.Skip : 0; int skip = dec.UseSkipProbability ? blockData.Skip : 0;
if (skip is 0) if (skip is 0)
{ {
@ -555,6 +691,34 @@ namespace SixLabors.ImageSharp.Formats.WebP
return vp8FilterHeader; return vp8FilterHeader;
} }
private void ParsePartitions(Vp8Decoder dec)
{
uint size = this.bitReader.Remaining - this.bitReader.PartitionLength;
int startIdx = (int)this.bitReader.PartitionLength;
Span<byte> sz = this.bitReader.Data.AsSpan(startIdx);
int sizeLeft = (int)size;
int numPartsMinusOne = (1 << (int)this.bitReader.ReadValue(2)) - 1;
int lastPart = numPartsMinusOne;
int partStart = startIdx + (lastPart * 3);
sizeLeft -= lastPart * 3;
for (int p = 0; p < lastPart; ++p)
{
int pSize = sz[0] | (sz[1] << 8) | (sz[2] << 16);
if (pSize > sizeLeft)
{
pSize = sizeLeft;
}
dec.Vp8BitReaders[p] = new Vp8BitReader(this.bitReader.Data, (uint)pSize, partStart);
partStart += pSize;
sizeLeft -= pSize;
sz = sz.Slice(3);
}
dec.Vp8BitReaders[lastPart] = new Vp8BitReader(this.bitReader.Data, (uint)sizeLeft, partStart);
}
private void ParseDequantizationIndices(Vp8SegmentHeader vp8SegmentHeader) private void ParseDequantizationIndices(Vp8SegmentHeader vp8SegmentHeader)
{ {
int baseQ0 = (int)this.bitReader.ReadValue(7); int baseQ0 = (int)this.bitReader.ReadValue(7);
@ -613,7 +777,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
} }
private void ParseProbabilities(Vp8Proba proba) private void ParseProbabilities(Vp8Decoder dec, Vp8Proba proba)
{ {
for (int t = 0; t < WebPConstants.NumTypes; ++t) for (int t = 0; t < WebPConstants.NumTypes; ++t)
{ {
@ -623,8 +787,8 @@ namespace SixLabors.ImageSharp.Formats.WebP
{ {
for (int p = 0; p < WebPConstants.NumProbas; ++p) for (int p = 0; p < WebPConstants.NumProbas; ++p)
{ {
var prob = WebPConstants.CoeffsUpdateProba[t, b, c, p]; byte prob = WebPConstants.CoeffsUpdateProba[t, b, c, p];
int v = this.bitReader.GetBit(prob) == 0 int v = this.bitReader.GetBit(prob) > 0
? (int)this.bitReader.ReadValue(8) ? (int)this.bitReader.ReadValue(8)
: WebPConstants.DefaultCoeffsProba[t, b, c, p]; : WebPConstants.DefaultCoeffsProba[t, b, c, p];
proba.Bands[t, b].Probabilities[c].Probabilities[p] = (byte)v; proba.Bands[t, b].Probabilities[c].Probabilities[p] = (byte)v;
@ -638,11 +802,10 @@ namespace SixLabors.ImageSharp.Formats.WebP
} }
} }
// TODO: those values needs to be stored somewhere dec.UseSkipProbability = this.bitReader.ReadBool();
bool useSkipProba = this.bitReader.ReadBool(); if (dec.UseSkipProbability)
if (useSkipProba)
{ {
uint skipP = this.bitReader.ReadValue(8); dec.SkipProbability = (byte)this.bitReader.ReadValue(8);
} }
} }

Loading…
Cancel
Save