diff --git a/src/ImageSharp/Formats/WebP/BitWriter/BitWriterBase.cs b/src/ImageSharp/Formats/WebP/BitWriter/BitWriterBase.cs
new file mode 100644
index 0000000000..fd3cd44d9d
--- /dev/null
+++ b/src/ImageSharp/Formats/WebP/BitWriter/BitWriterBase.cs
@@ -0,0 +1,64 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+
+namespace SixLabors.ImageSharp.Formats.WebP.BitWriter
+{
+ internal abstract class BitWriterBase
+ {
+ ///
+ /// Buffer to write to.
+ ///
+ private byte[] buffer;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The expected size in bytes.
+ protected BitWriterBase(int expectedSize)
+ {
+ // TODO: use memory allocator here.
+ this.buffer = new byte[expectedSize];
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ /// Used internally for cloning.
+ ///
+ private protected BitWriterBase(byte[] buffer) => this.buffer = buffer;
+
+ public byte[] Buffer
+ {
+ get { return this.buffer; }
+ }
+
+ ///
+ /// Resizes the buffer to write to.
+ ///
+ /// The extra size in bytes needed.
+ public abstract void BitWriterResize(int extraSize);
+
+ protected bool ResizeBuffer(int maxBytes, int sizeRequired)
+ {
+ if (maxBytes > 0 && sizeRequired < maxBytes)
+ {
+ return true;
+ }
+
+ int newSize = (3 * maxBytes) >> 1;
+ if (newSize < sizeRequired)
+ {
+ newSize = sizeRequired;
+ }
+
+ // Make new size multiple of 1k.
+ newSize = ((newSize >> 10) + 1) << 10;
+
+ // TODO: use memory allocator.
+ Array.Resize(ref this.buffer, newSize);
+
+ return false;
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/WebP/BitWriter/Vp8BitWriter.cs b/src/ImageSharp/Formats/WebP/BitWriter/Vp8BitWriter.cs
index a0dd881135..d55716159e 100644
--- a/src/ImageSharp/Formats/WebP/BitWriter/Vp8BitWriter.cs
+++ b/src/ImageSharp/Formats/WebP/BitWriter/Vp8BitWriter.cs
@@ -1,16 +1,18 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
+using SixLabors.ImageSharp.Formats.WebP.Lossy;
+
namespace SixLabors.ImageSharp.Formats.WebP.BitWriter
{
///
- /// A bit writer for writing lossless webp streams.
+ /// A bit writer for writing lossy webp streams.
///
- internal class Vp8BitWriter
+ internal class Vp8BitWriter : BitWriterBase
{
- /*private uint range;
+ private int range;
- private uint value;
+ private int value;
///
/// Number of outstanding bits.
@@ -22,15 +24,18 @@ namespace SixLabors.ImageSharp.Formats.WebP.BitWriter
///
private int nbBits;
- private byte[] buffer;
-
- private int pos;
+ private uint pos;
private int maxPos;
- private bool error;
+ // private bool error;
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The expected size in bytes.
public Vp8BitWriter(int expectedSize)
+ : base(expectedSize)
{
this.range = 255 - 1;
this.value = 0;
@@ -38,9 +43,239 @@ namespace SixLabors.ImageSharp.Formats.WebP.BitWriter
this.nbBits = -8;
this.pos = 0;
this.maxPos = 0;
- this.error = false;
- // BitWriterResize(expected_size);
- }*/
+ // this.error = false;
+ }
+
+ public uint Pos
+ {
+ get { return this.pos; }
+ }
+
+ public int PutCoeffs(int ctx, Vp8Residual residual)
+ {
+ int tabIdx = 0;
+ int n = residual.First;
+ Vp8ProbaArray p = residual.Prob[n][ctx];
+ if (!this.PutBit(residual.Last >= 0, p.Probabilities[0]))
+ {
+ return 0;
+ }
+
+ while (n < 16)
+ {
+ int c = residual.Coeffs[n++];
+ bool sign = c < 0;
+ int v = sign ? -c : c;
+ if (!this.PutBit(v != 0, p.Probabilities[1]))
+ {
+ p = residual.Prob[WebPConstants.Bands[n]][0];
+ continue;
+ }
+
+ if (!this.PutBit(v > 1, p.Probabilities[2]))
+ {
+ p = residual.Prob[WebPConstants.Bands[n]][1];
+ }
+ else
+ {
+ if (!this.PutBit(v > 4, p.Probabilities[3]))
+ {
+ if (this.PutBit(v != 2, p.Probabilities[4]))
+ {
+ this.PutBit(v == 4, p.Probabilities[5]);
+ }
+ }
+ else if (!this.PutBit(v > 10, p.Probabilities[6]))
+ {
+ if (!this.PutBit(v > 6, p.Probabilities[7]))
+ {
+ this.PutBit(v == 6, 159);
+ }
+ else
+ {
+ this.PutBit(v >= 9, 165);
+ this.PutBit(!((v & 1) != 0), 145);
+ }
+ }
+ else
+ {
+ int mask;
+ byte[] tab;
+ if (v < 3 + (8 << 1))
+ {
+ // VP8Cat3 (3b)
+ this.PutBit(0, p.Probabilities[8]);
+ this.PutBit(0, p.Probabilities[9]);
+ v -= 3 + (8 << 0);
+ mask = 1 << 2;
+ tab = WebPConstants.Cat3;
+ tabIdx = 0;
+ }
+ else if (v < 3 + (8 << 2))
+ {
+ // VP8Cat4 (4b)
+ this.PutBit(0, p.Probabilities[8]);
+ this.PutBit(1, p.Probabilities[9]);
+ v -= 3 + (8 << 1);
+ mask = 1 << 3;
+ tab = WebPConstants.Cat4;
+ tabIdx = 0;
+ }
+ else if (v < 3 + (8 << 3))
+ {
+ // VP8Cat5 (5b)
+ this.PutBit(1, p.Probabilities[8]);
+ this.PutBit(0, p.Probabilities[10]);
+ v -= 3 + (8 << 2);
+ mask = 1 << 4;
+ tab = WebPConstants.Cat5;
+ tabIdx = 0;
+ }
+ else
+ {
+ // VP8Cat6 (11b)
+ this.PutBit(1, p.Probabilities[8]);
+ this.PutBit(1, p.Probabilities[10]);
+ v -= 3 + (8 << 3);
+ mask = 1 << 10;
+ tab = WebPConstants.Cat6;
+ tabIdx = 0;
+ }
+
+ while (mask != 0)
+ {
+ this.PutBit(v & mask, tab[tabIdx++]);
+ mask >>= 1;
+ }
+ }
+
+ p = residual.Prob[WebPConstants.Bands[n]][2];
+ }
+
+ this.PutBitUniform(sign ? 1 : 0);
+ if (n == 16 || !this.PutBit(n <= residual.Last, p.Probabilities[0]))
+ {
+ return 1; // EOB
+ }
+ }
+
+ return 1;
+ }
+
+ private bool PutBit(bool bit, int prob)
+ {
+ return this.PutBit(bit ? 1 : 0, prob);
+ }
+
+ private bool PutBit(int bit, int prob)
+ {
+ int split = (this.range * prob) >> 8;
+ if (bit != 0)
+ {
+ this.value += split + 1;
+ this.range -= split + 1;
+ }
+ else
+ {
+ this.range = split;
+ }
+
+ if (this.range < 127)
+ {
+ // emit 'shift' bits out and renormalize.
+ int shift = WebPLookupTables.Norm[this.range];
+ this.range = WebPLookupTables.NewRange[this.range];
+ this.value <<= shift;
+ this.nbBits += shift;
+ if (this.nbBits > 0)
+ {
+ this.Flush();
+ }
+ }
+
+ return bit != 0;
+ }
+
+ private int PutBitUniform(int bit)
+ {
+ int split = this.range >> 1;
+ if (bit != 0)
+ {
+ this.value += split + 1;
+ this.range -= split + 1;
+ }
+ else
+ {
+ this.range = split;
+ }
+
+ if (this.range < 127)
+ {
+ this.range = WebPLookupTables.NewRange[this.range];
+ this.value <<= 1;
+ this.nbBits += 1;
+ if (this.nbBits > 0)
+ {
+ this.Flush();
+ }
+ }
+
+ return bit;
+ }
+
+ private void Flush()
+ {
+ int s = 8 + this.nbBits;
+ int bits = this.value >> s;
+ this.value -= bits << s;
+ this.nbBits -= 8;
+ if ((bits & 0xff) != 0xff)
+ {
+ var pos = this.pos;
+ this.BitWriterResize(this.run + 1);
+
+ if ((bits & 0x100) != 0)
+ {
+ // overflow -> propagate carry over pending 0xff's
+ if (pos > 0)
+ {
+ this.Buffer[pos - 1]++;
+ }
+ }
+
+ if (this.run > 0)
+ {
+ int value = (bits & 0x100) != 0 ? 0x00 : 0xff;
+ for (; this.run > 0; --this.run)
+ {
+ this.Buffer[pos++] = (byte)value;
+ }
+ }
+
+ this.Buffer[pos++] = (byte)(bits & 0xff);
+ this.pos = pos;
+ }
+ else
+ {
+ this.run++; // Delay writing of bytes 0xff, pending eventual carry.
+ }
+ }
+
+ ///
+ /// Resizes the buffer to write to.
+ ///
+ /// The extra size in bytes needed.
+ public override void BitWriterResize(int extraSize)
+ {
+ // TODO: review again if this works as intended. Probably needs a unit test ...
+ var neededSize = this.pos + extraSize;
+ if (neededSize <= this.maxPos)
+ {
+ return;
+ }
+
+ this.ResizeBuffer(this.maxPos, (int)neededSize);
+ }
}
}
diff --git a/src/ImageSharp/Formats/WebP/BitWriter/Vp8LBitWriter.cs b/src/ImageSharp/Formats/WebP/BitWriter/Vp8LBitWriter.cs
index 5a931259ff..99c819f173 100644
--- a/src/ImageSharp/Formats/WebP/BitWriter/Vp8LBitWriter.cs
+++ b/src/ImageSharp/Formats/WebP/BitWriter/Vp8LBitWriter.cs
@@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.BitWriter
///
/// A bit writer for writing lossless webp streams.
///
- internal class Vp8LBitWriter
+ internal class Vp8LBitWriter : BitWriterBase
{
///
/// This is the minimum amount of size the memory buffer is guaranteed to grow when extra space is needed.
@@ -32,11 +32,6 @@ namespace SixLabors.ImageSharp.Formats.WebP.BitWriter
///
private int used;
- ///
- /// Buffer to write to.
- ///
- private byte[] buffer;
-
///
/// Current write position.
///
@@ -49,10 +44,9 @@ namespace SixLabors.ImageSharp.Formats.WebP.BitWriter
///
/// The expected size in bytes.
public Vp8LBitWriter(int expectedSize)
+ : base(expectedSize)
{
- // TODO: maybe use memory allocator here.
- this.buffer = new byte[expectedSize];
- this.end = this.buffer.Length;
+ this.end = this.Buffer.Length;
}
///
@@ -60,8 +54,8 @@ namespace SixLabors.ImageSharp.Formats.WebP.BitWriter
/// Used internally for cloning.
///
private Vp8LBitWriter(byte[] buffer, ulong bits, int used, int cur)
+ : base(buffer)
{
- this.buffer = buffer;
this.bits = bits;
this.used = used;
this.cur = cur;
@@ -113,8 +107,8 @@ namespace SixLabors.ImageSharp.Formats.WebP.BitWriter
public Vp8LBitWriter Clone()
{
- var clonedBuffer = new byte[this.buffer.Length];
- Buffer.BlockCopy(this.buffer, 0, clonedBuffer, 0, this.cur);
+ var clonedBuffer = new byte[this.Buffer.Length];
+ System.Buffer.BlockCopy(this.Buffer, 0, clonedBuffer, 0, this.cur);
return new Vp8LBitWriter(clonedBuffer, this.bits, this.used, this.cur);
}
@@ -124,7 +118,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.BitWriter
/// The stream to write to.
public void WriteToStream(Stream stream)
{
- stream.Write(this.buffer.AsSpan(0, this.NumBytes()));
+ stream.Write(this.Buffer.AsSpan(0, this.NumBytes()));
}
///
@@ -135,7 +129,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.BitWriter
this.BitWriterResize((this.used + 7) >> 3);
while (this.used > 0)
{
- this.buffer[this.cur++] = (byte)this.bits;
+ this.Buffer[this.cur++] = (byte)this.bits;
this.bits >>= 8;
this.used -= 8;
}
@@ -155,7 +149,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.BitWriter
this.BitWriterResize(extraSize);
}
- BinaryPrimitives.WriteUInt64LittleEndian(this.buffer.AsSpan(this.cur), this.bits);
+ BinaryPrimitives.WriteUInt64LittleEndian(this.Buffer.AsSpan(this.cur), this.bits);
this.cur += WriterBytes;
this.bits >>= WriterBits;
this.used -= WriterBits;
@@ -165,30 +159,18 @@ namespace SixLabors.ImageSharp.Formats.WebP.BitWriter
/// Resizes the buffer to write to.
///
/// The extra size in bytes needed.
- private void BitWriterResize(int extraSize)
+ public override void BitWriterResize(int extraSize)
{
- int maxBytes = this.end + this.buffer.Length;
+ // TODO: review again if this works as intended. Probably needs a unit test ...
+ int maxBytes = this.end + this.Buffer.Length;
int sizeRequired = this.cur + extraSize;
- if (maxBytes > 0 && sizeRequired < maxBytes)
+ if (this.ResizeBuffer(maxBytes, sizeRequired))
{
return;
}
- int newSize = (3 * maxBytes) >> 1;
- if (newSize < sizeRequired)
- {
- newSize = sizeRequired;
- }
-
- // make new size multiple of 1k
- newSize = ((newSize >> 10) + 1) << 10;
- if (this.cur > 0)
- {
- Array.Resize(ref this.buffer, newSize);
- }
-
- this.end = this.buffer.Length;
+ this.end = this.Buffer.Length;
}
}
}
diff --git a/src/ImageSharp/Formats/WebP/Lossy/Vp8EncIterator.cs b/src/ImageSharp/Formats/WebP/Lossy/Vp8EncIterator.cs
index b9fed7f52c..818b8ece76 100644
--- a/src/ImageSharp/Formats/WebP/Lossy/Vp8EncIterator.cs
+++ b/src/ImageSharp/Formats/WebP/Lossy/Vp8EncIterator.cs
@@ -123,6 +123,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
this.TopNz = new int[9];
this.LeftNz = new int[9];
this.I4Boundary = new byte[37];
+ this.BitCount = new long[4, 3];
// To match the C++ initial values of the reference implementation, initialize all with 204.
byte defaultInitVal = 204;
@@ -226,6 +227,21 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
///
public int[] LeftNz { get; }
+ ///
+ /// Gets or sets the macroblock bit-cost for luma.
+ ///
+ public long LumaBits { get; set; }
+
+ ///
+ /// Gets the bit counters for coded levels.
+ ///
+ public long[,] BitCount { get; }
+
+ ///
+ /// Gets or sets the macroblock bit-cost for chroma.
+ ///
+ public long UvBits { get; set; }
+
///
/// Gets or sets the number of mb still to be processed.
///
@@ -641,6 +657,67 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
this.YuvOut2 = tmp;
}
+ public void NzToBytes()
+ {
+ Span nz = this.Nz.GetSpan();
+ uint tnz = nz[0];
+ uint lnz = nz[-1]; // TODO: -1?
+ Span topNz = this.TopNz;
+ Span leftNz = this.LeftNz;
+
+ // Top-Y
+ topNz[0] = this.Bit(tnz, 12);
+ topNz[1] = this.Bit(tnz, 13);
+ topNz[2] = this.Bit(tnz, 14);
+ topNz[3] = this.Bit(tnz, 15);
+
+ // Top-U
+ topNz[4] = this.Bit(tnz, 18);
+ topNz[5] = this.Bit(tnz, 19);
+
+ // Top-V
+ topNz[6] = this.Bit(tnz, 22);
+ topNz[7] = this.Bit(tnz, 23);
+
+ // DC
+ topNz[8] = this.Bit(tnz, 24);
+
+ // left-Y
+ leftNz[0] = this.Bit(lnz, 3);
+ leftNz[1] = this.Bit(lnz, 7);
+ leftNz[2] = this.Bit(lnz, 11);
+ leftNz[3] = this.Bit(lnz, 15);
+
+ // left-U
+ leftNz[4] = this.Bit(lnz, 17);
+ leftNz[5] = this.Bit(lnz, 19);
+
+ // left-V
+ leftNz[6] = this.Bit(lnz, 21);
+ leftNz[7] = this.Bit(lnz, 23);
+
+ // left-DC is special, iterated separately.
+ }
+
+ public void BytesToNz()
+ {
+ uint nz = 0;
+ int[] topNz = this.TopNz;
+ int[] leftNz = this.LeftNz;
+
+ // top
+ nz |= (uint)((topNz[0] << 12) | (topNz[1] << 13));
+ nz |= (uint)((topNz[2] << 14) | (topNz[3] << 15));
+ nz |= (uint)((topNz[4] << 18) | (topNz[5] << 19));
+ nz |= (uint)((topNz[6] << 22) | (topNz[7] << 23));
+ nz |= (uint)(topNz[8] << 24); // we propagate the top bit, esp. for intra4
+
+ // left
+ nz |= (uint)((leftNz[0] << 3) | (leftNz[1] << 7));
+ nz |= (uint)(leftNz[2] << 11);
+ nz |= (uint)((leftNz[4] << 17) | (leftNz[6] << 21));
+ }
+
private void Mean16x4(Span input, Span dc)
{
for (int k = 0; k < 4; ++k)
@@ -1197,48 +1274,6 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
this.Nz.GetSpan().Fill(0);
}
- private void NzToBytes()
- {
- Span nz = this.Nz.GetSpan();
- uint tnz = nz[0];
- uint lnz = nz[-1]; // TODO: -1?
- Span topNz = this.TopNz;
- Span leftNz = this.LeftNz;
-
- // Top-Y
- topNz[0] = this.Bit(tnz, 12);
- topNz[1] = this.Bit(tnz, 13);
- topNz[2] = this.Bit(tnz, 14);
- topNz[3] = this.Bit(tnz, 15);
-
- // Top-U
- topNz[4] = this.Bit(tnz, 18);
- topNz[5] = this.Bit(tnz, 19);
-
- // Top-V
- topNz[6] = this.Bit(tnz, 22);
- topNz[7] = this.Bit(tnz, 23);
-
- // DC
- topNz[8] = this.Bit(tnz, 24);
-
- // left-Y
- leftNz[0] = this.Bit(lnz, 3);
- leftNz[1] = this.Bit(lnz, 7);
- leftNz[2] = this.Bit(lnz, 11);
- leftNz[3] = this.Bit(lnz, 15);
-
- // left-U
- leftNz[4] = this.Bit(lnz, 17);
- leftNz[5] = this.Bit(lnz, 19);
-
- // left-V
- leftNz[6] = this.Bit(lnz, 21);
- leftNz[7] = this.Bit(lnz, 23);
-
- // left-DC is special, iterated separately.
- }
-
// Convert packed context to byte array.
private int Bit(uint nz, int n)
{
diff --git a/src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs b/src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs
index 61661733aa..16eb7e2985 100644
--- a/src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs
+++ b/src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs
@@ -44,6 +44,8 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
private readonly byte[] zigzag = new byte[] { 0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15 };
+ private readonly byte[] averageBytesPerMb = { 50, 24, 16, 9, 7, 5, 3, 2 };
+
///
/// Initializes a new instance of the class.
///
@@ -67,8 +69,11 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
this.Nz = this.memoryAllocator.Allocate(mbw + 1);
this.MbHeaderLimit = 256 * 510 * 8 * 1024 / (mbw * mbh);
- // TODO: properly initialize the bitwriter
- this.bitWriter = new Vp8BitWriter();
+ // Initialize the bitwriter.
+ var baseQuant = 36; // TODO: hardCoded for now.
+ int averageBytesPerMacroBlock = this.averageBytesPerMb[baseQuant >> 4];
+ int expectedSize = mbw * mbh * averageBytesPerMacroBlock;
+ this.bitWriter = new Vp8BitWriter(expectedSize);
}
///
@@ -401,6 +406,63 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
private void CodeResiduals(Vp8EncIterator it, Vp8ModeScore rd)
{
+ int x, y, ch;
+ var residual = new Vp8Residual();
+ bool i16 = it.CurrentMacroBlockInfo.MacroBlockType == Vp8MacroBlockType.I16X16;
+ int segment = it.CurrentMacroBlockInfo.Segment;
+
+ it.NzToBytes();
+
+ var pos1 = this.bitWriter.Pos;
+ if (i16)
+ {
+ residual.Init(0, 1);
+ residual.SetCoeffs(rd.YDcLevels);
+ int res = this.bitWriter.PutCoeffs(it.TopNz[8] + it.LeftNz[8], residual);
+ it.TopNz[8] = it.LeftNz[8] = res;
+ residual.Init(1, 0);
+ }
+ else
+ {
+ residual.Init(0, 3);
+ }
+
+ // luma-AC
+ for (y = 0; y < 4; ++y)
+ {
+ for (x = 0; x < 4; ++x)
+ {
+ int ctx = it.TopNz[x] + it.LeftNz[y];
+ residual.SetCoeffs(rd.YAcLevels[x + (y * 4)]);
+ int res = this.bitWriter.PutCoeffs(ctx, residual);
+ it.TopNz[x] = it.LeftNz[y] = res;
+ }
+ }
+
+ var pos2 = this.bitWriter.Pos;
+
+ // U/V
+ residual.Init(0, 2);
+ for (ch = 0; ch <= 2; ch += 2)
+ {
+ for (y = 0; y < 2; ++y)
+ {
+ for (x = 0; x < 2; ++x)
+ {
+ int ctx = it.TopNz[4 + ch + x] + it.LeftNz[4 + ch + y];
+ residual.SetCoeffs(rd.UvLevels[(ch * 2) + x + (y * 2)]);
+ var res = this.bitWriter.PutCoeffs(ctx, residual);
+ it.TopNz[4 + ch + x] = it.LeftNz[4 + ch + y] = res;
+ }
+ }
+ }
+
+ var pos3 = this.bitWriter.Pos;
+ it.LumaBits = pos2 - pos1;
+ it.UvBits = pos3 - pos2;
+ it.BitCount[segment, i16 ? 1 : 0] += it.LumaBits;
+ it.BitCount[segment, 2] += it.UvBits;
+ it.BytesToNz();
}
private int ReconstructIntra16(Vp8EncIterator it, Vp8SegmentInfo dqm, Vp8ModeScore rd, Span yuvOut, int mode)
diff --git a/src/ImageSharp/Formats/WebP/Lossy/Vp8Residual.cs b/src/ImageSharp/Formats/WebP/Lossy/Vp8Residual.cs
new file mode 100644
index 0000000000..58e60a76c3
--- /dev/null
+++ b/src/ImageSharp/Formats/WebP/Lossy/Vp8Residual.cs
@@ -0,0 +1,62 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+
+namespace SixLabors.ImageSharp.Formats.WebP.Lossy
+{
+ ///
+ /// On-the-fly info about the current set of residuals.
+ ///
+ internal class Vp8Residual
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public Vp8Residual()
+ {
+ this.Prob = new Vp8ProbaArray[3][];
+ for (int i = 0; i < 3; i++)
+ {
+ this.Prob[i] = new Vp8ProbaArray[11];
+ }
+ }
+
+ public int First { get; set; }
+
+ public int Last { get; set; }
+
+ public int CoeffType { get; set; }
+
+ public short[] Coeffs { get; set; }
+
+ public Vp8ProbaArray[][] Prob { get; }
+
+ public void Init(int first, int coeffType)
+ {
+ this.First = first;
+ this.CoeffType = coeffType;
+
+ // TODO:
+ // res->prob = enc->proba_.coeffs_[coeff_type];
+ // res->stats = enc->proba_.stats_[coeff_type];
+ // res->costs = enc->proba_.remapped_costs_[coeff_type];
+ }
+
+ public void SetCoeffs(short[] coeffs)
+ {
+ int n;
+ this.Last = -1;
+ for (n = 15; n >= 0; --n)
+ {
+ if (coeffs[n] != 0)
+ {
+ this.Last = n;
+ break;
+ }
+ }
+
+ this.Coeffs = coeffs;
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/WebP/WebPLookupTables.cs b/src/ImageSharp/Formats/WebP/WebPLookupTables.cs
index a283acd26a..b23cdd3236 100644
--- a/src/ImageSharp/Formats/WebP/WebPLookupTables.cs
+++ b/src/ImageSharp/Formats/WebP/WebPLookupTables.cs
@@ -53,6 +53,34 @@ namespace SixLabors.ImageSharp.Formats.WebP
public static readonly short[,][] Vp8FixedCostsI4 = new short[10, 10][];
+ public static readonly byte[] Norm =
+ {
+ // renorm_sizes[i] = 8 - log2(i)
+ 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0
+ };
+
+ public static readonly byte[] NewRange =
+ {
+ // range = ((range + 1) << kVP8Log2Range[range]) - 1
+ 127, 127, 191, 127, 159, 191, 223, 127, 143, 159, 175, 191, 207, 223, 239,
+ 127, 135, 143, 151, 159, 167, 175, 183, 191, 199, 207, 215, 223, 231, 239,
+ 247, 127, 131, 135, 139, 143, 147, 151, 155, 159, 163, 167, 171, 175, 179,
+ 183, 187, 191, 195, 199, 203, 207, 211, 215, 219, 223, 227, 231, 235, 239,
+ 243, 247, 251, 127, 129, 131, 133, 135, 137, 139, 141, 143, 145, 147, 149,
+ 151, 153, 155, 157, 159, 161, 163, 165, 167, 169, 171, 173, 175, 177, 179,
+ 181, 183, 185, 187, 189, 191, 193, 195, 197, 199, 201, 203, 205, 207, 209,
+ 211, 213, 215, 217, 219, 221, 223, 225, 227, 229, 231, 233, 235, 237, 239,
+ 241, 243, 245, 247, 249, 251, 253, 127
+ };
+
///
/// Lookup table for small values of log2(int).
///