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). ///