Browse Source

Fix some VP8 encoding mistakes

pull/1552/head
Brian Popow 6 years ago
parent
commit
98fb8af07d
  1. 81
      src/ImageSharp/Formats/WebP/Lossy/Vp8EncIterator.cs
  2. 65
      src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs
  3. 48
      src/ImageSharp/Formats/WebP/Lossy/Vp8Residual.cs

81
src/ImageSharp/Formats/WebP/Lossy/Vp8EncIterator.cs

@ -99,21 +99,21 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
private int uvTopIdx;
public Vp8EncIterator(IMemoryOwner<byte> yTop, IMemoryOwner<byte> uvTop, IMemoryOwner<byte> preds, IMemoryOwner<uint> nz, Vp8MacroBlockInfo[] mb, int mbw, int mbh)
public Vp8EncIterator(byte[] yTop, byte[] uvTop, uint[] nz, Vp8MacroBlockInfo[] mb, byte[] preds, int mbw, int mbh)
{
this.mbw = mbw;
this.mbh = mbh;
this.Mb = mb;
this.currentMbIdx = 0;
this.nzIdx = 0;
this.predIdx = 0;
this.nzIdx = 1;
this.yTopIdx = 0;
this.uvTopIdx = 0;
this.YTop = yTop;
this.UvTop = uvTop;
this.Preds = preds;
this.Nz = nz;
this.Preds = preds;
this.predsWidth = (4 * mbw) + 1;
this.predIdx = this.predsWidth;
this.YuvIn = new byte[WebPConstants.Bps * 16];
this.YuvOut = new byte[WebPConstants.Bps * 16];
this.YuvOut2 = new byte[WebPConstants.Bps * 16];
@ -125,7 +125,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
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.
// To match the C initial values of the reference implementation, initialize all with 204.
byte defaultInitVal = 204;
this.YuvIn.AsSpan().Fill(defaultInitVal);
this.YuvOut.AsSpan().Fill(defaultInitVal);
@ -133,7 +133,6 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
this.YuvP.AsSpan().Fill(defaultInitVal);
this.YLeft.AsSpan().Fill(defaultInitVal);
this.UvLeft.AsSpan().Fill(defaultInitVal);
this.Preds.GetSpan().Fill(defaultInitVal);
for (int i = -255; i <= 255 + 255; ++i)
{
@ -186,17 +185,17 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
/// <summary>
/// Gets the top luma samples at position 'X'.
/// </summary>
public IMemoryOwner<byte> YTop { get; }
public byte[] YTop { get; }
/// <summary>
/// Gets the top u/v samples at position 'X', packed as 16 bytes.
/// </summary>
public IMemoryOwner<byte> UvTop { get; }
public byte[] UvTop { get; }
/// <summary>
/// Gets the intra mode predictors (4x4 blocks).
/// </summary>
public IMemoryOwner<byte> Preds { get; }
public byte[] Preds { get; }
/// <summary>
/// Gets the current start index of the intra mode predictors.
@ -212,7 +211,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
/// <summary>
/// Gets the non-zero pattern.
/// </summary>
public IMemoryOwner<uint> Nz { get; }
public uint[] Nz { get; }
/// <summary>
/// Gets 32+5 boundary samples needed by intra4x4.
@ -292,7 +291,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
this.I4Boundary[i] = this.YLeft[15 - i + 1];
}
Span<byte> yTop = this.YTop.GetSpan();
Span<byte> yTop = this.YTop.AsSpan(this.yTopIdx);
for (i = 0; i < 16; ++i)
{
// top
@ -320,7 +319,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
}
// Import uncompressed samples from source.
public void Import(Span<byte> y, Span<byte> u, Span<byte> v, int yStride, int uvStride, int width, int height)
public void Import(Span<byte> y, Span<byte> u, Span<byte> v, int yStride, int uvStride, int width, int height, bool importBoundarySamples)
{
int yStartIdx = ((this.Y * yStride) + this.X) * 16;
int uvStartIdx = ((this.Y * uvStride) + this.X) * 8;
@ -339,6 +338,11 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
this.ImportBlock(uSrc, uvStride, uIn, uvw, uvh, 8);
this.ImportBlock(vSrc, uvStride, vIn, uvw, uvh, 8);
if (!importBoundarySamples)
{
return;
}
// Import source (uncompressed) samples into boundary.
if (this.X == 0)
{
@ -367,17 +371,17 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
this.ImportLine(v.Slice(uvStartIdx - 1), uvStride, vLeft.Slice(1), uvh, 8);
}
Span<byte> yTop = this.YTop.Slice(this.yTopIdx, 16);
Span<byte> yTop = this.YTop.AsSpan(this.yTopIdx, 16);
if (this.Y == 0)
{
yTop.Fill(127);
this.UvTop.Slice(this.uvTopIdx, 16).Fill(127);
this.UvTop.AsSpan(this.uvTopIdx, 16).Fill(127);
}
else
{
this.ImportLine(y.Slice(yStartIdx - yStride), 1, yTop, w, 16);
this.ImportLine(u.Slice(uvStartIdx - uvStride), 1, this.UvTop.Slice(this.uvTopIdx, 8), uvw, 8);
this.ImportLine(v.Slice(uvStartIdx - uvStride), 1, this.UvTop.Slice(this.uvTopIdx + 8, 8), uvw, 8);
this.ImportLine(u.Slice(uvStartIdx - uvStride), 1, this.UvTop.AsSpan(this.uvTopIdx, 8), uvw, 8);
this.ImportLine(v.Slice(uvStartIdx - uvStride), 1, this.UvTop.AsSpan(this.uvTopIdx + 8, 8), uvw, 8);
}
}
@ -472,7 +476,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
public void SetIntra16Mode(int mode)
{
Span<byte> preds = this.Preds.Slice(this.predIdx);
Span<byte> preds = this.Preds.AsSpan(this.predIdx);
for (int y = 0; y < 4; ++y)
{
preds.Slice(0, 4).Fill((byte)mode);
@ -488,7 +492,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
int predIdx = this.predIdx;
for (int y = 4; y > 0; --y)
{
modes.AsSpan(modesIdx, 4).CopyTo(this.Preds.Slice(predIdx));
modes.AsSpan(modesIdx, 4).CopyTo(this.Preds.AsSpan(predIdx));
predIdx += this.predsWidth;
modesIdx += 4;
}
@ -502,8 +506,8 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
int predIdx = this.predIdx;
int x = this.I4 & 3;
int y = this.I4 >> 2;
int left = (x == 0) ? this.Preds.Slice(predIdx + (y * predsWidth) - 1)[0] : modes[this.I4 - 1];
int top = (y == 0) ? this.Preds.Slice(predIdx - predsWidth + x)[0] : modes[this.I4 - 4];
int left = (x == 0) ? this.Preds[predIdx + (y * predsWidth) - 1] : modes[this.I4 - 1];
int top = (y == 0) ? this.Preds[predIdx - predsWidth + x] : modes[this.I4 - 4];
return WebPLookupTables.Vp8FixedCostsI4[top, left];
}
@ -574,16 +578,16 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
}
// top-left (before 'top'!)
this.YLeft[0] = this.YTop.GetSpan()[15];
this.UvLeft[0] = this.UvTop.GetSpan()[0 + 7];
this.UvLeft[16] = this.UvTop.GetSpan()[8 + 7];
this.YLeft[0] = this.YTop[this.yTopIdx + 15];
this.UvLeft[0] = this.UvTop[this.uvTopIdx + 0 + 7];
this.UvLeft[16] = this.UvTop[this.uvTopIdx + 8 + 7];
}
if (y < this.mbh - 1)
{
// top
ySrc.Slice(15 * WebPConstants.Bps, 16).CopyTo(this.YTop.GetSpan());
uvSrc.Slice(7 * WebPConstants.Bps, 8 + 8).CopyTo(this.UvTop.GetSpan());
ySrc.Slice(15 * WebPConstants.Bps, 16).CopyTo(this.YTop.AsSpan(this.yTopIdx));
uvSrc.Slice(7 * WebPConstants.Bps, 8 + 8).CopyTo(this.UvTop.AsSpan(this.uvTopIdx));
}
}
@ -636,26 +640,26 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
if (this.CurrentMacroBlockInfo.MacroBlockType == Vp8MacroBlockType.I16X16)
{
// Reset all predictors.
this.Nz.GetSpan()[0] = 0;
this.Nz[this.nzIdx] = 0;
this.LeftNz[8] = 0;
}
else
{
this.Nz.GetSpan()[0] &= 1 << 24; // Preserve the dc_nz bit.
this.Nz[this.nzIdx] &= 1 << 24; // Preserve the dc_nz bit.
}
}
public void MakeLuma16Preds()
{
Span<byte> left = this.X != 0 ? this.YLeft.AsSpan() : null;
Span<byte> top = this.Y != 0 ? this.YTop.Slice(this.yTopIdx) : null;
Span<byte> top = this.Y != 0 ? this.YTop.AsSpan(this.yTopIdx) : null;
this.EncPredLuma16(this.YuvP, left, top);
}
public void MakeChroma8Preds()
{
Span<byte> left = this.X != 0 ? this.UvLeft.AsSpan() : null;
Span<byte> top = this.Y != 0 ? this.UvTop.Slice(this.uvTopIdx) : null;
Span<byte> top = this.Y != 0 ? this.UvTop.AsSpan(this.uvTopIdx) : null;
this.EncPredChroma8(this.YuvP, left, top);
}
@ -673,10 +677,10 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
public void NzToBytes()
{
Span<uint> nz = this.Nz.GetSpan();
Span<uint> nz = this.Nz.AsSpan();
uint lnz = 0; // TODO: -1?
uint tnz = nz[0];
uint lnz = nz[this.nzIdx - 1];
uint tnz = nz[this.nzIdx];
Span<int> topNz = this.TopNz;
Span<int> leftNz = this.LeftNz;
@ -731,6 +735,8 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
nz |= (uint)((leftNz[0] << 3) | (leftNz[1] << 7));
nz |= (uint)(leftNz[2] << 11);
nz |= (uint)((leftNz[4] << 17) | (leftNz[6] << 21));
this.Nz[this.nzIdx] = nz;
}
private void Mean16x4(Span<byte> input, Span<uint> dc)
@ -1257,7 +1263,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
this.X = 0;
this.Y = y;
this.currentMbIdx = y * this.mbw;
this.nzIdx = 0;
this.nzIdx = 1; // note: in reference source nz starts at -1.
this.yTopIdx = 0;
this.uvTopIdx = 0;
this.predIdx = this.predsWidth + (y * 4 * this.predsWidth);
@ -1285,15 +1291,14 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
private void InitTop()
{
int topSize = this.mbw * 16;
this.YTop.Slice(0, topSize).Fill(127);
this.UvTop.GetSpan().Fill(127);
this.Nz.GetSpan().Fill(0);
this.YTop.AsSpan(0, topSize).Fill(127);
this.UvTop.AsSpan().Fill(127);
this.Nz.AsSpan().Fill(0);
}
// Convert packed context to byte array.
private int Bit(uint nz, int n)
{
return (int)(nz & (1 << n));
return (nz & (1 << n)) != 0 ? 1 : 0;
}
/// <summary>

65
src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs

@ -149,9 +149,9 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
this.Y = this.memoryAllocator.Allocate<byte>(pixelCount);
this.U = this.memoryAllocator.Allocate<byte>(uvSize);
this.V = this.memoryAllocator.Allocate<byte>(uvSize);
this.YTop = this.memoryAllocator.Allocate<byte>(this.mbw * 16);
this.UvTop = this.memoryAllocator.Allocate<byte>(this.mbw * 16 * 2);
this.Nz = this.memoryAllocator.Allocate<uint>(this.mbw + 1);
this.YTop = new byte[this.mbw * 16];
this.UvTop = new byte[this.mbw * 16 * 2];
this.Nz = new uint[this.mbw + 1];
this.MbHeaderLimit = 256 * 510 * 8 * 1024 / (this.mbw * this.mbh);
int predSize = (((4 * this.mbw) + 1) * ((4 * this.mbh) + 1)) + this.predsWidth + 1;
@ -169,10 +169,14 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
this.proba = new Vp8EncProba();
// this.Preds = this.memoryAllocator.Allocate<byte>(predSize);
this.Preds = this.memoryAllocator.Allocate<byte>(predSize * 2); // TODO: figure out how much mem we need here. This is too much.
this.Preds = new byte[predSize * 2]; // TODO: figure out how much mem we need here. This is too much.
this.predsWidth = (4 * this.mbw) + 1;
// Initialize with default values, which the reference c implementation uses,
// to be able to compare to the original and spot differences.
this.Preds.AsSpan().Fill(205);
this.Nz.AsSpan().Fill(3452816845);
this.ResetBoundaryPredictions();
// Initialize the bitwriter.
@ -205,22 +209,22 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
/// <summary>
/// Gets the top luma samples.
/// </summary>
private IMemoryOwner<byte> YTop { get; }
private byte[] YTop { get; }
/// <summary>
/// Gets the top u/v samples. U and V are packed into 16 bytes (8 U + 8 V).
/// </summary>
private IMemoryOwner<byte> UvTop { get; }
private byte[] UvTop { get; }
/// <summary>
/// Gets the prediction modes: (4*mbw+1) * (4*mbh+1).
/// Gets the non-zero pattern.
/// </summary>
private IMemoryOwner<byte> Preds { get; }
private uint[] Nz { get; }
/// <summary>
/// Gets the non-zero pattern.
/// Gets the prediction modes: (4*mbw+1) * (4*mbh+1).
/// </summary>
private IMemoryOwner<uint> Nz { get; }
private byte[] Preds { get; }
/// <summary>
/// Gets a rough limit for header bits per MB.
@ -251,7 +255,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
segmentInfos[i] = new Vp8SegmentInfo();
}
var it = new Vp8EncIterator(this.YTop, this.UvTop, this.Preds, this.Nz, this.mbInfo, this.mbw, this.mbh);
var it = new Vp8EncIterator(this.YTop, this.UvTop, this.Nz, this.mbInfo, this.Preds, this.mbw, this.mbh);
var alphas = new int[WebPConstants.MaxAlpha + 1];
this.alpha = this.MacroBlockAnalysis(width, height, it, y, u, v, yStride, uvStride, alphas, out this.uvAlpha);
@ -268,7 +272,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
do
{
var info = new Vp8ModeScore();
it.Import(y, u, v, yStride, uvStride, width, height);
it.Import(y, u, v, yStride, uvStride, width, height, false);
if (!this.Decimate(it, segmentInfos, info, this.rdOptLevel))
{
this.CodeResiduals(it, info);
@ -292,9 +296,6 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
this.Y.Dispose();
this.U.Dispose();
this.V.Dispose();
this.YTop.Dispose();
this.UvTop.Dispose();
this.Preds.Dispose();
}
/// <summary>
@ -305,7 +306,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
private void StatLoop(int width, int height, int yStride, int uvStride, Vp8SegmentInfo[] segmentInfos)
{
int targetSize = 0; // TODO: target size is hardcoded.
float targetPsnr = 0.0f; // TDOO: targetPsnr is hardcoded.
float targetPsnr = 0.0f; // TODO: targetPsnr is hardcoded.
int method = this.method;
bool doSearch = false; // TODO: doSearch hardcoded for now.
bool fastProbe = (method == 0 || method == 3) && !doSearch;
@ -377,7 +378,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
Span<byte> y = this.Y.GetSpan();
Span<byte> u = this.U.GetSpan();
Span<byte> v = this.V.GetSpan();
var it = new Vp8EncIterator(this.YTop, this.UvTop, this.Preds, this.Nz, this.mbInfo, this.mbw, this.mbh);
var it = new Vp8EncIterator(this.YTop, this.UvTop, this.Nz, this.mbInfo, this.Preds, this.mbw, this.mbh);
long size = 0;
long sizeP0 = 0;
long distortion = 0;
@ -387,7 +388,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
do
{
var info = new Vp8ModeScore();
it.Import(y, u, v, yStride, uvStride, width, height);
it.Import(y, u, v, yStride, uvStride, width, height, false);
if (this.Decimate(it, segmentInfos, info, rdOpt))
{
// Just record the number of skips and act like skipProba is not used.
@ -432,8 +433,8 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
private void ResetBoundaryPredictions()
{
Span<byte> top = this.Preds.GetSpan();
Span<byte> left = this.Preds.Slice(this.predsWidth - 1);
Span<byte> top = this.Preds.AsSpan(); // original source top starts at: enc->preds_ - enc->preds_w_
Span<byte> left = this.Preds.AsSpan(this.predsWidth - 1);
for (int i = 0; i < 4 * this.mbw; ++i)
{
top[i] = (int)IntraPredictionMode.DcPrediction;
@ -444,7 +445,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
left[i * this.predsWidth] = (int)IntraPredictionMode.DcPrediction;
}
// TODO: enc->nz_[-1] = 0; // constant
this.Nz[0] = 0; // constant
}
// Simplified k-Means, to assign Nb segments based on alpha-histogram.
@ -704,7 +705,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
{
do
{
it.Import(y, u, v, yStride, uvStride, width, height);
it.Import(y, u, v, yStride, uvStride, width, height, true);
int bestAlpha = this.MbAnalyze(it, alphas, out var bestUvAlpha);
// Accumulate for later complexity analysis.
@ -875,7 +876,8 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
}
else
{
nz = this.ReconstructIntra16(it, dqm, rd, it.YuvOut.AsSpan(Vp8EncIterator.YOffEnc), it.Preds.Slice(it.PredIdx)[0]);
int intra16Mode = it.Preds[it.PredIdx];
nz = this.ReconstructIntra16(it, dqm, rd, it.YuvOut.AsSpan(Vp8EncIterator.YOffEnc), intra16Mode);
}
// ... and UV!
@ -974,7 +976,6 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
int x, y, ch;
var residual = new Vp8Residual();
bool i16 = it.CurrentMacroBlockInfo.MacroBlockType == Vp8MacroBlockType.I16X16;
int segment = it.CurrentMacroBlockInfo.Segment;
it.NzToBytes();
@ -1041,7 +1042,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
this.FTransform2(src.Slice(WebPLookupTables.Vp8Scan[n]), reference.Slice(WebPLookupTables.Vp8Scan[n]), tmpSpan.Slice(n * 16, 16), tmpSpan.Slice((n + 1) * 16, 16));
}
this.FTransformWht(tmp.AsSpan(0), dcTmp);
this.FTransformWht(tmp, dcTmp);
nz |= this.QuantizeBlock(dcTmp, rd.YDcLevels, dqm.Y2) << 24;
for (n = 0; n < 16; n += 2)
@ -1049,7 +1050,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
// Zero-out the first coeff, so that: a) nz is correct below, and
// b) finding 'last' non-zero coeffs in SetResidualCoeffs() is simplified.
tmp[n * 16] = tmp[(n + 1) * 16] = 0;
nz |= this.Quantize2Blocks(tmpSpan.Slice(n * 16), rd.YAcLevels.AsSpan(n * 16, 32), dqm.Y1) << n;
nz |= this.Quantize2Blocks(tmpSpan.Slice(n * 16, 32), rd.YAcLevels.AsSpan(n * 16, 32), dqm.Y1) << n;
}
// Transform back.
@ -1279,10 +1280,10 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
int b = dc - tmp[8];
int c = Mul(tmp[4], KC2) - Mul(tmp[12], KC1);
int d = Mul(tmp[4], KC1) + Mul(tmp[12], KC2);
Store(dst, reference, 0, i, (byte)(a + d));
Store(dst, reference, 1, i, (byte)(b + c));
Store(dst, reference, 2, i, (byte)(b - c));
Store(dst, reference, 3, i, (byte)(a - d));
Store(dst, reference, 0, i, (a + d));
Store(dst, reference, 1, i, (b + c));
Store(dst, reference, 2, i, (b - c));
Store(dst, reference, 3, i, (a - d));
tmp = tmp.Slice(1);
}
}
@ -1675,7 +1676,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
}
[MethodImpl(InliningOptions.ShortMethod)]
private static void Store(Span<byte> dst, Span<byte> reference, int x, int y, byte v)
private static void Store(Span<byte> dst, Span<byte> reference, int x, int y, int v)
{
dst[x + (y * WebPConstants.Bps)] = LossyUtils.Clip8B(reference[x + (y * WebPConstants.Bps)] + (v >> 3));
}

48
src/ImageSharp/Formats/WebP/Lossy/Vp8Residual.cs

@ -71,34 +71,36 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
{
this.RecordStats(0, s, 1);
s = this.Stats[WebPConstants.Vp8EncBands[n]].Stats[0];
this.RecordStats(1, s, 1);
if (this.RecordStats((v + 1) > 2u ? 1 : 0, s, 2) == 0)
}
this.RecordStats(1, s, 1);
var bit = 2u < (uint)(v + 1);
if (this.RecordStats(bit ? 1 : 0, s, 2) == 0)
{
// v = -1 or 1
s = this.Stats[WebPConstants.Vp8EncBands[n]].Stats[1];
}
else
{
v = Math.Abs(v);
if (v > MaxVariableLevel)
{
// v = -1 or 1
s = this.Stats[WebPConstants.Vp8EncBands[n]].Stats[1];
v = MaxVariableLevel;
}
else
{
v = Math.Abs(v);
if (v > MaxVariableLevel)
{
v = MaxVariableLevel;
}
int bits = WebPLookupTables.Vp8LevelCodes[v - 1][1];
int pattern = WebPLookupTables.Vp8LevelCodes[v - 1][0];
int i;
for (i = 0; (pattern >>= 1) != 0; ++i)
int bits = WebPLookupTables.Vp8LevelCodes[v - 1][1];
int pattern = WebPLookupTables.Vp8LevelCodes[v - 1][0];
int i;
for (i = 0; (pattern >>= 1) != 0; ++i)
{
int mask = 2 << i;
if ((pattern & 1) != 0)
{
int mask = 2 << i;
if ((pattern & 1) != 0)
{
this.RecordStats(bits & mask, s, 3 + i);
}
this.RecordStats((bits & mask) != 0 ? 1 : 0, s, 3 + i);
}
s = this.Stats[WebPConstants.Vp8EncBands[n]].Stats[2];
}
s = this.Stats[WebPConstants.Vp8EncBands[n]].Stats[2];
}
}
@ -119,7 +121,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
statsArr.Stats[idx] = ((statsArr.Stats[idx] + 1u) >> 1) & 0x7fff7fffu; // -> divide the stats by 2.
}
// record bit count (lower 16 bits) and increment total count (upper 16 bits).
// Record bit count (lower 16 bits) and increment total count (upper 16 bits).
statsArr.Stats[idx] += 0x00010000u + (uint)bit;
return bit;

Loading…
Cancel
Save