Browse Source

Implement CodeResiduals() and Vp8Bitwriter

pull/1552/head
Brian Popow 6 years ago
parent
commit
7e23212ba1
  1. 64
      src/ImageSharp/Formats/WebP/BitWriter/BitWriterBase.cs
  2. 257
      src/ImageSharp/Formats/WebP/BitWriter/Vp8BitWriter.cs
  3. 46
      src/ImageSharp/Formats/WebP/BitWriter/Vp8LBitWriter.cs
  4. 119
      src/ImageSharp/Formats/WebP/Lossy/Vp8EncIterator.cs
  5. 66
      src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs
  6. 62
      src/ImageSharp/Formats/WebP/Lossy/Vp8Residual.cs
  7. 28
      src/ImageSharp/Formats/WebP/WebPLookupTables.cs

64
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
{
/// <summary>
/// Buffer to write to.
/// </summary>
private byte[] buffer;
/// <summary>
/// Initializes a new instance of the <see cref="BitWriterBase"/> class.
/// </summary>
/// <param name="expectedSize">The expected size in bytes.</param>
protected BitWriterBase(int expectedSize)
{
// TODO: use memory allocator here.
this.buffer = new byte[expectedSize];
}
/// <summary>
/// Initializes a new instance of the <see cref="BitWriterBase"/> class.
/// Used internally for cloning.
/// </summary>
private protected BitWriterBase(byte[] buffer) => this.buffer = buffer;
public byte[] Buffer
{
get { return this.buffer; }
}
/// <summary>
/// Resizes the buffer to write to.
/// </summary>
/// <param name="extraSize">The extra size in bytes needed.</param>
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;
}
}
}

257
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
{
/// <summary>
/// A bit writer for writing lossless webp streams.
/// A bit writer for writing lossy webp streams.
/// </summary>
internal class Vp8BitWriter
internal class Vp8BitWriter : BitWriterBase
{
/*private uint range;
private int range;
private uint value;
private int value;
/// <summary>
/// Number of outstanding bits.
@ -22,15 +24,18 @@ namespace SixLabors.ImageSharp.Formats.WebP.BitWriter
/// </summary>
private int nbBits;
private byte[] buffer;
private int pos;
private uint pos;
private int maxPos;
private bool error;
// private bool error;
/// <summary>
/// Initializes a new instance of the <see cref="Vp8BitWriter"/> class.
/// </summary>
/// <param name="expectedSize">The expected size in bytes.</param>
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.
}
}
/// <summary>
/// Resizes the buffer to write to.
/// </summary>
/// <param name="extraSize">The extra size in bytes needed.</param>
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);
}
}
}

46
src/ImageSharp/Formats/WebP/BitWriter/Vp8LBitWriter.cs

@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.BitWriter
/// <summary>
/// A bit writer for writing lossless webp streams.
/// </summary>
internal class Vp8LBitWriter
internal class Vp8LBitWriter : BitWriterBase
{
/// <summary>
/// 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
/// </summary>
private int used;
/// <summary>
/// Buffer to write to.
/// </summary>
private byte[] buffer;
/// <summary>
/// Current write position.
/// </summary>
@ -49,10 +44,9 @@ namespace SixLabors.ImageSharp.Formats.WebP.BitWriter
/// </summary>
/// <param name="expectedSize">The expected size in bytes.</param>
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;
}
/// <summary>
@ -60,8 +54,8 @@ namespace SixLabors.ImageSharp.Formats.WebP.BitWriter
/// Used internally for cloning.
/// </summary>
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
/// <param name="stream">The stream to write to.</param>
public void WriteToStream(Stream stream)
{
stream.Write(this.buffer.AsSpan(0, this.NumBytes()));
stream.Write(this.Buffer.AsSpan(0, this.NumBytes()));
}
/// <summary>
@ -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.
/// </summary>
/// <param name="extraSize">The extra size in bytes needed.</param>
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;
}
}
}

119
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
/// </summary>
public int[] LeftNz { get; }
/// <summary>
/// Gets or sets the macroblock bit-cost for luma.
/// </summary>
public long LumaBits { get; set; }
/// <summary>
/// Gets the bit counters for coded levels.
/// </summary>
public long[,] BitCount { get; }
/// <summary>
/// Gets or sets the macroblock bit-cost for chroma.
/// </summary>
public long UvBits { get; set; }
/// <summary>
/// Gets or sets the number of mb still to be processed.
/// </summary>
@ -641,6 +657,67 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
this.YuvOut2 = tmp;
}
public void NzToBytes()
{
Span<uint> nz = this.Nz.GetSpan();
uint tnz = nz[0];
uint lnz = nz[-1]; // TODO: -1?
Span<int> topNz = this.TopNz;
Span<int> 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<byte> input, Span<uint> 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<uint> nz = this.Nz.GetSpan();
uint tnz = nz[0];
uint lnz = nz[-1]; // TODO: -1?
Span<int> topNz = this.TopNz;
Span<int> 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)
{

66
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 };
/// <summary>
/// Initializes a new instance of the <see cref="Vp8Encoder"/> class.
/// </summary>
@ -67,8 +69,11 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
this.Nz = this.memoryAllocator.Allocate<uint>(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);
}
/// <summary>
@ -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<byte> yuvOut, int mode)

62
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
{
/// <summary>
/// On-the-fly info about the current set of residuals.
/// </summary>
internal class Vp8Residual
{
/// <summary>
/// Initializes a new instance of the <see cref="Vp8Residual"/> class.
/// </summary>
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;
}
}
}

28
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
};
/// <summary>
/// Lookup table for small values of log2(int).
/// </summary>

Loading…
Cancel
Save