diff --git a/src/ImageSharp/Formats/WebP/BitWriter/Vp8BitWriter.cs b/src/ImageSharp/Formats/WebP/BitWriter/Vp8BitWriter.cs index 89cff21663..598a23e552 100644 --- a/src/ImageSharp/Formats/WebP/BitWriter/Vp8BitWriter.cs +++ b/src/ImageSharp/Formats/WebP/BitWriter/Vp8BitWriter.cs @@ -561,6 +561,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.BitWriter } } + // Writes the partition #0 modes (that is: all intra modes) private void CodeIntraModes(Vp8BitWriter bitWriter) { var it = new Vp8EncIterator(this.enc.YTop, this.enc.UvTop, this.enc.Nz, this.enc.MbInfo, this.enc.Preds, this.enc.Mbw, this.enc.Mbh); @@ -569,7 +570,8 @@ namespace SixLabors.ImageSharp.Formats.WebP.BitWriter do { Vp8MacroBlockInfo mb = it.CurrentMacroBlockInfo; - Span preds = it.Preds.AsSpan(it.PredIdx); + int predIdx = it.PredIdx; + Span preds = it.Preds.AsSpan(predIdx); if (this.enc.SegmentHeader.UpdateMap) { bitWriter.PutSegment(mb.Segment, this.enc.Proba.Segments); @@ -587,19 +589,18 @@ namespace SixLabors.ImageSharp.Formats.WebP.BitWriter } else { - Span topPred = it.Preds.AsSpan(it.PredIdx); - int x, y; - for (y = 0; y < 4; ++y) + Span topPred = it.Preds.AsSpan(predIdx - predsWidth); + for (int y = 0; y < 4; ++y) { - int left = preds[it.PredIdx - 1]; - for (x = 0; x < 4; ++x) + int left = it.Preds[predIdx - 1]; + for (int x = 0; x < 4; ++x) { byte[] probas = WebPLookupTables.ModesProba[topPred[x], left]; - left = bitWriter.PutI4Mode(preds[x], probas); + left = bitWriter.PutI4Mode(it.Preds[predIdx + x], probas); } - topPred = preds; - preds = preds.Slice(predsWidth); + topPred = it.Preds.AsSpan(predIdx); + predIdx += predsWidth; } } diff --git a/src/ImageSharp/Formats/WebP/Lossy/Vp8EncIterator.cs b/src/ImageSharp/Formats/WebP/Lossy/Vp8EncIterator.cs index c6fec2b44d..649b1705a0 100644 --- a/src/ImageSharp/Formats/WebP/Lossy/Vp8EncIterator.cs +++ b/src/ImageSharp/Formats/WebP/Lossy/Vp8EncIterator.cs @@ -2,10 +2,8 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Buffers; using System.Buffers.Binary; using SixLabors.ImageSharp.Formats.WebP.Lossless; -using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.WebP.Lossy { @@ -66,11 +64,11 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy private const int I4RD4 = I4DC4 + 16; - private const int I4VR4 = I4RD4 + 20; + private const int I4VR4 = I4DC4 + 20; - private const int I4LD4 = I4RD4 + 24; + private const int I4LD4 = I4DC4 + 24; - private const int I4VL4 = I4RD4 + 28; + private const int I4VL4 = I4DC4 + 28; private const int I4HD4 = (3 * 16 * WebPConstants.Bps) + (4 * WebPConstants.Bps); @@ -797,14 +795,14 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy // located at top[0..3], and top right is top[4..7] private void EncPredLuma4(Span dst, Span top, int topOffset) { - this.Dc4(dst, top, topOffset); + this.Dc4(dst.Slice(I4DC4), top, topOffset); this.Tm4(dst.Slice(I4TM4), top, topOffset); this.Ve4(dst.Slice(I4VE4), top, topOffset); this.He4(dst.Slice(I4HE4), top, topOffset); this.Rd4(dst.Slice(I4RD4), top, topOffset); this.Vr4(dst.Slice(I4VR4), top, topOffset); - this.Ld4(dst.Slice(I4LD4), top); - this.Vl4(dst.Slice(I4VL4), top); + this.Ld4(dst.Slice(I4LD4), top, topOffset); + this.Vl4(dst.Slice(I4VL4), top, topOffset); this.Hd4(dst.Slice(I4HD4), top, topOffset); this.Hu4(dst.Slice(I4HU4), top, topOffset); } @@ -989,7 +987,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy val = 0x01010101U * LossyUtils.Avg3(j, k, l); BinaryPrimitives.WriteUInt32BigEndian(dst.Slice(2 * WebPConstants.Bps), val); val = 0x01010101U * LossyUtils.Avg3(k, l, l); - BinaryPrimitives.WriteUInt32BigEndian(dst.Slice(1 * WebPConstants.Bps), val); + BinaryPrimitives.WriteUInt32BigEndian(dst.Slice(3 * WebPConstants.Bps), val); } private void Rd4(Span dst, Span top, int topOffset) @@ -1062,16 +1060,16 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy LossyUtils.Dst(dst, 3, 1, LossyUtils.Avg3(b, c, d)); } - private void Ld4(Span dst, Span top) + private void Ld4(Span dst, Span top, int topOffset) { - byte a = top[0]; - byte b = top[1]; - byte c = top[2]; - byte d = top[3]; - byte e = top[4]; - byte f = top[5]; - byte g = top[6]; - byte h = top[7]; + byte a = top[topOffset + 0]; + byte b = top[topOffset + 1]; + byte c = top[topOffset + 2]; + byte d = top[topOffset + 3]; + byte e = top[topOffset + 4]; + byte f = top[topOffset + 5]; + byte g = top[topOffset + 6]; + byte h = top[topOffset + 7]; LossyUtils.Dst(dst, 0, 0, LossyUtils.Avg3(a, b, c)); var bcd = LossyUtils.Avg3(b, c, d); @@ -1096,16 +1094,16 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy LossyUtils.Dst(dst, 3, 3, LossyUtils.Avg3(g, h, h)); } - private void Vl4(Span dst, Span top) + private void Vl4(Span dst, Span top, int topOffset) { - byte a = top[0]; - byte b = top[1]; - byte c = top[2]; - byte d = top[3]; - byte e = top[4]; - byte f = top[5]; - byte g = top[6]; - byte h = top[7]; + byte a = top[topOffset + 0]; + byte b = top[topOffset + 1]; + byte c = top[topOffset + 2]; + byte d = top[topOffset + 3]; + byte e = top[topOffset + 4]; + byte f = top[topOffset + 5]; + byte g = top[topOffset + 6]; + byte h = top[topOffset + 7]; LossyUtils.Dst(dst, 0, 0, LossyUtils.Avg2(a, b)); var bc = LossyUtils.Avg2(b, c); @@ -1294,6 +1292,11 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy this.YTop.AsSpan(0, topSize).Fill(127); this.UvTop.AsSpan().Fill(127); this.Nz.AsSpan().Fill(0); + + int predsW = (4 * this.mbw) + 1; + int predsH = (4 * this.mbh) + 1; + int predsSize = predsW * predsH; + this.Preds.AsSpan(predsSize + this.predsWidth, this.mbw).Fill(0); } 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 8972e270f2..e92b2fb0aa 100644 --- a/src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs +++ b/src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs @@ -4,6 +4,7 @@ using System; using System.Buffers; using System.IO; +using System.Linq; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Formats.WebP.BitWriter; @@ -22,11 +23,6 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy /// private readonly MemoryAllocator memoryAllocator; - /// - /// A bit writer for writing lossy webp streams. - /// - private readonly Vp8BitWriter bitWriter; - /// /// The quality, that will be used to encode the image. /// @@ -57,6 +53,11 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy /// private readonly int mbh; + /// + /// A bit writer for writing lossy webp streams. + /// + private Vp8BitWriter bitWriter; + /// /// The segment features. /// @@ -205,15 +206,9 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy this.Nz.AsSpan().Fill(3452816845); this.ResetBoundaryPredictions(); - - // Initialize the bitwriter. - this.BaseQuant = 36; // TODO: hardCoded for now. - int averageBytesPerMacroBlock = this.averageBytesPerMb[this.BaseQuant >> 4]; - int expectedSize = this.mbw * this.mbh * averageBytesPerMacroBlock; - this.bitWriter = new Vp8BitWriter(expectedSize, this); } - public int BaseQuant { get; } + public int BaseQuant { get; set; } /// /// Gets the probabilities. @@ -378,6 +373,11 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy this.AssignSegments(alphas); this.SetLoopParams(this.quality); + // Initialize the bitwriter. + int averageBytesPerMacroBlock = this.averageBytesPerMb[this.BaseQuant >> 4]; + int expectedSize = this.mbw * this.mbh * averageBytesPerMacroBlock; + this.bitWriter = new Vp8BitWriter(expectedSize, this); + // TODO: EncodeAlpha(); // Stats-collection loop. this.StatLoop(width, height, yStride, uvStride); @@ -589,6 +589,11 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy left[i * this.predsWidth] = (int)IntraPredictionMode.DcPrediction; } + int predsW = (4 * this.mbw) + 1; + int predsH = (4 * this.mbh) + 1; + int predsSize = predsW * predsH; + this.Preds.AsSpan(predsSize + this.predsWidth - 4, 4).Fill(0); + this.Nz[0] = 0; // constant } @@ -740,6 +745,9 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy dqm[i].Quant = Clip(q, 0, 127); } + // Purely indicative in the bitstream (except for the 1-segment case). + this.BaseQuant = dqm[0].Quant; + // uvAlpha is normally spread around ~60. The useful range is // typically ~30 (quite bad) to ~100 (ok to decimate UV more). // We map it to the safe maximal range of MAX/MIN_DQ_UV for dq_uv. @@ -1035,9 +1043,9 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy } else { - // Reconstruct partial block inside yuv_out2 buffer + // Reconstruct partial block inside YuvOut2 buffer Span tmpDst = it.YuvOut2.AsSpan(Vp8EncIterator.YOffEnc + WebPLookupTables.Vp8Scan[it.I4]); - nz |= this.ReconstructIntra4(it, dqm, rd.YAcLevels.AsSpan(it.I4, 16), src, tmpDst, bestI4Mode) << it.I4; + nz |= this.ReconstructIntra4(it, dqm, rd.YAcLevels.AsSpan(it.I4 * 16, 16), src, tmpDst, bestI4Mode) << it.I4; } } while (it.RotateI4(it.YuvOut2.AsSpan(Vp8EncIterator.YOffEnc))); @@ -1111,7 +1119,8 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy for (x = 0; x < 4; ++x) { int ctx = it.TopNz[x] + it.LeftNz[y]; - residual.SetCoeffs(rd.YAcLevels.AsSpan(16 * (x + (y * 4)), 16)); + Span coeffs = rd.YAcLevels.AsSpan(16 * (x + (y * 4)), 16); + residual.SetCoeffs(coeffs); int res = this.bitWriter.PutCoeffs(ctx, residual); it.TopNz[x] = it.LeftNz[y] = res; } @@ -1176,7 +1185,8 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy for (x = 0; x < 4; ++x) { int ctx = it.TopNz[x] + it.LeftNz[y]; - residual.SetCoeffs(rd.YAcLevels.AsSpan(16 * (x + (y * 4)), 16)); + Span coeffs = rd.YAcLevels.AsSpan(16 * (x + (y * 4)), 16); + residual.SetCoeffs(coeffs); var res = residual.RecordCoeffs(ctx); it.TopNz[x] = res; it.LeftNz[y] = res;