From 98fb8af07d258f194c7e0d580f51decd06c51aac Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Thu, 5 Nov 2020 20:41:10 +0100 Subject: [PATCH] Fix some VP8 encoding mistakes --- .../Formats/WebP/Lossy/Vp8EncIterator.cs | 81 ++++++++++--------- .../Formats/WebP/Lossy/Vp8Encoder.cs | 65 +++++++-------- .../Formats/WebP/Lossy/Vp8Residual.cs | 48 +++++------ 3 files changed, 101 insertions(+), 93 deletions(-) diff --git a/src/ImageSharp/Formats/WebP/Lossy/Vp8EncIterator.cs b/src/ImageSharp/Formats/WebP/Lossy/Vp8EncIterator.cs index 4607bf1c0e..c6fec2b44d 100644 --- a/src/ImageSharp/Formats/WebP/Lossy/Vp8EncIterator.cs +++ b/src/ImageSharp/Formats/WebP/Lossy/Vp8EncIterator.cs @@ -99,21 +99,21 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy private int uvTopIdx; - public Vp8EncIterator(IMemoryOwner yTop, IMemoryOwner uvTop, IMemoryOwner preds, IMemoryOwner 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 /// /// Gets the top luma samples at position 'X'. /// - public IMemoryOwner YTop { get; } + public byte[] YTop { get; } /// /// Gets the top u/v samples at position 'X', packed as 16 bytes. /// - public IMemoryOwner UvTop { get; } + public byte[] UvTop { get; } /// /// Gets the intra mode predictors (4x4 blocks). /// - public IMemoryOwner Preds { get; } + public byte[] Preds { get; } /// /// Gets the current start index of the intra mode predictors. @@ -212,7 +211,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy /// /// Gets the non-zero pattern. /// - public IMemoryOwner Nz { get; } + public uint[] Nz { get; } /// /// 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 yTop = this.YTop.GetSpan(); + Span 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 y, Span u, Span v, int yStride, int uvStride, int width, int height) + public void Import(Span y, Span u, Span 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 yTop = this.YTop.Slice(this.yTopIdx, 16); + Span 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 preds = this.Preds.Slice(this.predIdx); + Span 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 left = this.X != 0 ? this.YLeft.AsSpan() : null; - Span top = this.Y != 0 ? this.YTop.Slice(this.yTopIdx) : null; + Span top = this.Y != 0 ? this.YTop.AsSpan(this.yTopIdx) : null; this.EncPredLuma16(this.YuvP, left, top); } public void MakeChroma8Preds() { Span left = this.X != 0 ? this.UvLeft.AsSpan() : null; - Span top = this.Y != 0 ? this.UvTop.Slice(this.uvTopIdx) : null; + Span 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 nz = this.Nz.GetSpan(); + Span 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 topNz = this.TopNz; Span 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 input, Span 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; } /// diff --git a/src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs b/src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs index ef227e53f3..6747ff6dbc 100644 --- a/src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs +++ b/src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs @@ -149,9 +149,9 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy this.Y = this.memoryAllocator.Allocate(pixelCount); this.U = this.memoryAllocator.Allocate(uvSize); this.V = this.memoryAllocator.Allocate(uvSize); - this.YTop = this.memoryAllocator.Allocate(this.mbw * 16); - this.UvTop = this.memoryAllocator.Allocate(this.mbw * 16 * 2); - this.Nz = this.memoryAllocator.Allocate(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(predSize); - this.Preds = this.memoryAllocator.Allocate(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 /// /// Gets the top luma samples. /// - private IMemoryOwner YTop { get; } + private byte[] YTop { get; } /// /// Gets the top u/v samples. U and V are packed into 16 bytes (8 U + 8 V). /// - private IMemoryOwner UvTop { get; } + private byte[] UvTop { get; } /// - /// Gets the prediction modes: (4*mbw+1) * (4*mbh+1). + /// Gets the non-zero pattern. /// - private IMemoryOwner Preds { get; } + private uint[] Nz { get; } /// - /// Gets the non-zero pattern. + /// Gets the prediction modes: (4*mbw+1) * (4*mbh+1). /// - private IMemoryOwner Nz { get; } + private byte[] Preds { get; } /// /// 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(); } /// @@ -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 y = this.Y.GetSpan(); Span u = this.U.GetSpan(); Span 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 top = this.Preds.GetSpan(); - Span left = this.Preds.Slice(this.predsWidth - 1); + Span top = this.Preds.AsSpan(); // original source top starts at: enc->preds_ - enc->preds_w_ + Span 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 dst, Span reference, int x, int y, byte v) + private static void Store(Span dst, Span reference, int x, int y, int v) { dst[x + (y * WebPConstants.Bps)] = LossyUtils.Clip8B(reference[x + (y * WebPConstants.Bps)] + (v >> 3)); } diff --git a/src/ImageSharp/Formats/WebP/Lossy/Vp8Residual.cs b/src/ImageSharp/Formats/WebP/Lossy/Vp8Residual.cs index f79e8f91d2..7b61997487 100644 --- a/src/ImageSharp/Formats/WebP/Lossy/Vp8Residual.cs +++ b/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;