From 6a1b61819f6f44f8d16bc3442f37770b33149787 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Thu, 13 Feb 2020 16:20:27 +0100 Subject: [PATCH] Fix some parsing mistakes --- src/ImageSharp/Formats/WebP/LossyUtils.cs | 52 ++++++++- src/ImageSharp/Formats/WebP/Vp8BitReader.cs | 20 ++++ src/ImageSharp/Formats/WebP/Vp8Decoder.cs | 18 +++ .../Formats/WebP/WebPLossyDecoder.cs | 104 ++++++++++++------ 4 files changed, 162 insertions(+), 32 deletions(-) diff --git a/src/ImageSharp/Formats/WebP/LossyUtils.cs b/src/ImageSharp/Formats/WebP/LossyUtils.cs index a4c488b61b..6d824793e0 100644 --- a/src/ImageSharp/Formats/WebP/LossyUtils.cs +++ b/src/ImageSharp/Formats/WebP/LossyUtils.cs @@ -177,6 +177,56 @@ namespace SixLabors.ImageSharp.Formats.WebP Put8x8uv((byte)(dc0 >> 3), dst); } + public static void DC4_C(Span dst) + { + + } + + public static void TM4_C(Span dst) + { + + } + + public static void VE4_C(Span dst) + { + + } + + public static void HE4_C(Span dst) + { + + } + + public static void RD4_C(Span dst) + { + + } + + public static void VR4_C(Span dst) + { + + } + + public static void LD4_C(Span dst) + { + + } + + public static void VL4_C(Span dst) + { + + } + + public static void HD4_C(Span dst) + { + + } + + public static void HU4_C(Span dst) + { + + } + public static void Transform(Span src, Span dst, bool doTwo) { TransformOne(src, dst); @@ -311,7 +361,7 @@ namespace SixLabors.ImageSharp.Formats.WebP private static byte Clip8B(int v) { - return (byte)((v & ~0xff) > 0 ? v : (v < 0) ? 0 : 255); + return (byte)((v & ~0xff) is 0 ? v : (v < 0) ? 0 : 255); } private static void Put8x8uv(byte value, Span dst) diff --git a/src/ImageSharp/Formats/WebP/Vp8BitReader.cs b/src/ImageSharp/Formats/WebP/Vp8BitReader.cs index bec618ce8b..1dabc349c1 100644 --- a/src/ImageSharp/Formats/WebP/Vp8BitReader.cs +++ b/src/ImageSharp/Formats/WebP/Vp8BitReader.cs @@ -115,6 +115,26 @@ namespace SixLabors.ImageSharp.Formats.WebP return bit ? 1 : 0; } + // simplified version of VP8GetBit() for prob=0x80 (note shift is always 1 here) + public int GetSigned(int v) + { + if (this.bits < 0) + { + this.LoadNewBytes(); + } + + int pos = this.bits; + uint split = this.range >> 1; + ulong value = this.value >> pos; + ulong mask = (split - value) >> 31; // -1 or 0 + this.bits -= 1; + this.range += (uint)mask; + this.range |= 1; + this.value -= ((split + 1) & mask) << pos; + + return (v ^ (int)mask) - (int)mask; + } + public bool ReadBool() { return this.ReadValue(1) is 1; diff --git a/src/ImageSharp/Formats/WebP/Vp8Decoder.cs b/src/ImageSharp/Formats/WebP/Vp8Decoder.cs index 872ddf36e9..469eba3e9c 100644 --- a/src/ImageSharp/Formats/WebP/Vp8Decoder.cs +++ b/src/ImageSharp/Formats/WebP/Vp8Decoder.cs @@ -44,6 +44,14 @@ namespace SixLabors.ImageSharp.Formats.WebP } } + uint width = pictureHeader.Width; + uint height = pictureHeader.Height; + + // TODO: use memory allocator + this.Y = new byte[width * height]; + this.U = new byte[width * height]; + this.V = new byte[width * height]; + this.Vp8BitReaders = new Vp8BitReader[WebPConstants.MaxNumPartitions]; this.Init(io); } @@ -139,6 +147,16 @@ namespace SixLabors.ImageSharp.Formats.WebP public Vp8TopSamples[] YuvTopSamples { get; } + public byte[] Y { get; } + + public byte[] U { get; } + + public byte[] V { get; } + + public int YStride { get; } + + public int UvStride { get; } + /// /// Gets or sets filter strength info. /// diff --git a/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs b/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs index 725b5857a1..d5b4d711af 100644 --- a/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs +++ b/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs @@ -63,14 +63,15 @@ namespace SixLabors.ImageSharp.Formats.WebP this.ParsePartitions(decoder); // Paragraph 9.6: Dequantization Indices. - this.ParseDequantizationIndices(decoder.SegmentHeader); + this.ParseDequantizationIndices(decoder); // Ignore the value of update_proba this.bitReader.ReadBool(); // Paragraph 13.4: Parse probabilities. - this.ParseProbabilities(decoder, decoder.Probabilities); + this.ParseProbabilities(decoder); + // Decode image data. this.ParseFrame(decoder, vp8Io); } @@ -219,8 +220,7 @@ namespace SixLabors.ImageSharp.Formats.WebP // Init top-left sample on left column too. if (mby > 0) { - // TODO: - // yDst[-1 - WebPConstants.Bps] = uDst[-1 - WebPConstants.Bps] = vDst[-1 - WebPConstants.Bps] = 129; + yuv[yOff - 1 - WebPConstants.Bps] = yuv[uOff - 1 - WebPConstants.Bps] = yuv[vOff - 1 - WebPConstants.Bps] = 129; } else { @@ -286,12 +286,13 @@ namespace SixLabors.ImageSharp.Formats.WebP // Predict and add residuals. if (block.IsI4x4) { + // uint32_t* const top_right = (uint32_t*)(y_dst - BPS + 16); if (mby > 0) { if (mbx >= dec.MbWidth - 1) { // On rightmost border. - //memset(top_right, top_yuv[0].y[15], sizeof(*top_right)); + // memset(top_right, top_yuv[0].y[15], sizeof(*top_right)); } else { @@ -300,38 +301,49 @@ namespace SixLabors.ImageSharp.Formats.WebP } // Replicate the top-right pixels below. - + // top_right[BPS] = top_right[2 * BPS] = top_right[3 * BPS] = top_right[0]; // Predict and add residuals for all 4x4 blocks in turn. for (int n = 0; n < 16; ++n, bits <<= 2) { // uint8_t * const dst = y_dst + kScan[n]; + Span dst = yDst.Slice(WebPConstants.KScan[n]); byte lumaMode = block.Modes[n]; switch (lumaMode) { case 0: + LossyUtils.DC4_C(dst); break; case 1: + LossyUtils.TM4_C(dst); break; case 2: + LossyUtils.VE4_C(dst); break; case 3: + LossyUtils.HE4_C(dst); break; case 4: + LossyUtils.RD4_C(dst); break; case 5: + LossyUtils.VR4_C(dst); break; case 6: + LossyUtils.LD4_C(dst); break; case 7: + LossyUtils.VL4_C(dst); break; case 8: + LossyUtils.HD4_C(dst); break; case 9: + LossyUtils.HU4_C(dst); break; } - //DoTransform(bits, coeffs + n * 16, dst); + this.DoTransform(bits, coeffs.AsSpan(n * 16), dst); } } else @@ -409,6 +421,32 @@ namespace SixLabors.ImageSharp.Formats.WebP this.DoUVTransform(bitsUv >> 0, coeffs.AsSpan(16 * 16), uDst); this.DoUVTransform(bitsUv >> 8, coeffs.AsSpan(20 * 16), vDst); + + // Stash away top samples for next block. + if (mby < dec.MbHeight - 1) + { + yDst.Slice(15 * WebPConstants.Bps, 16).CopyTo(topYuv.Y); + uDst.Slice(7 * WebPConstants.Bps, 8).CopyTo(topYuv.U); + vDst.Slice(7 * WebPConstants.Bps, 8).CopyTo(topYuv.V); + } + + // Transfer reconstructed samples from yuv_b_ cache to final destination. + int cacheId = 0; // TODO: what should be cacheId? + int yOffset = cacheId * 16 * dec.YStride; + int uvOffset = cacheId * 8 * dec.UvStride; + Span yOut = dec.Y.AsSpan((mbx * 16) + yOffset); + Span uOut = dec.U.AsSpan((mbx * 8) + uvOffset); + Span vOut = dec.V.AsSpan((mbx * 8) + uvOffset); + for (int j = 0; j < 16; ++j) + { + yDst.Slice(j * WebPConstants.Bps, 16).CopyTo(yOut.Slice(j * dec.YStride)); + } + + for (int j = 0; j < 8; ++j) + { + uDst.Slice(j * WebPConstants.Bps, 8).CopyTo(uOut); + vDst.Slice(j * WebPConstants.Bps, 8).CopyTo(vOut); + } } } @@ -504,13 +542,13 @@ namespace SixLabors.ImageSharp.Formats.WebP uint nonZeroY = 0; uint nonZeroUv = 0; int first; - var dst = new short[384]; int dstOffset = 0; Vp8MacroBlockData block = dec.MacroBlockData[dec.MbX]; Vp8QuantMatrix q = dec.DeQuantMatrices[block.Segment]; Vp8BandProbas[,] bands = dec.Probabilities.BandsPtr; Vp8BandProbas[] acProba; Vp8MacroBlock leftMb = dec.MacroBlockInfo[0]; + short[] dst = block.Coeffs; if (!block.IsI4x4) { @@ -519,7 +557,7 @@ namespace SixLabors.ImageSharp.Formats.WebP int ctx = (int)(mb.NoneZeroDcCoeffs + leftMb.NoneZeroDcCoeffs); int nz = this.GetCoeffs(br, GetBandsRow(bands, 1), ctx, q.Y2Mat, 0, dc); mb.NoneZeroDcCoeffs = leftMb.NoneZeroDcCoeffs = (uint)(nz > 0 ? 1 : 0); - if (nz > 0) + if (nz > 1) { // More than just the DC -> perform the full transform. this.TransformWht(dc, dst); @@ -534,7 +572,7 @@ namespace SixLabors.ImageSharp.Formats.WebP } first = 1; - acProba = GetBandsRow(bands, 1); + acProba = GetBandsRow(bands, 0); } else { @@ -615,14 +653,14 @@ namespace SixLabors.ImageSharp.Formats.WebP Vp8ProbaArray p = prob[n].Probabilities[ctx]; for (; n < 16; ++n) { - if (this.bitReader.GetBit((int)p.Probabilities[0]) is 0) + if (br.GetBit(p.Probabilities[0]) is 0) { // Previous coeff was last non - zero coeff. return n; } // Sequence of zero coeffs. - while (br.GetBit((int)p.Probabilities[1]) is 0) + while (br.GetBit(p.Probabilities[1]) is 0) { p = prob[++n].Probabilities[0]; if (n is 16) @@ -633,57 +671,57 @@ namespace SixLabors.ImageSharp.Formats.WebP // Non zero coeffs. int v; - if (br.GetBit((int)p.Probabilities[2]) is 0) + if (br.GetBit(p.Probabilities[2]) is 0) { v = 1; p = prob[n + 1].Probabilities[1]; } else { - v = this.GetLargeValue(p.Probabilities); + v = this.GetLargeValue(br, p.Probabilities); p = prob[n + 1].Probabilities[2]; } int idx = n > 0 ? 1 : 0; - coeffs[WebPConstants.Zigzag[n]] = (short)(br.ReadSignedValue(v) * dq[idx]); + coeffs[WebPConstants.Zigzag[n]] = (short)(br.GetSigned(v) * dq[idx]); } return 16; } - private int GetLargeValue(byte[] p) + private int GetLargeValue(Vp8BitReader br, byte[] p) { // See section 13 - 2: http://tools.ietf.org/html/rfc6386#section-13.2 int v; - if (this.bitReader.GetBit(p[3]) is 0) + if (br.GetBit(p[3]) is 0) { - if (this.bitReader.GetBit(p[4]) is 0) + if (br.GetBit(p[4]) is 0) { v = 2; } else { - v = 3 + this.bitReader.GetBit(p[5]); + v = 3 + br.GetBit(p[5]); } } else { - if (this.bitReader.GetBit(p[6]) is 0) + if (br.GetBit(p[6]) is 0) { - if (this.bitReader.GetBit(p[7]) is 0) + if (br.GetBit(p[7]) is 0) { - v = 5 + this.bitReader.GetBit(159); + v = 5 + br.GetBit(159); } else { - v = 7 + (2 * this.bitReader.GetBit(165)); - v += this.bitReader.GetBit(145); + v = 7 + (2 * br.GetBit(165)); + v += br.GetBit(145); } } else { - int bit1 = this.bitReader.GetBit(p[8]); - int bit0 = this.bitReader.GetBit(p[9] + bit1); + int bit1 = br.GetBit(p[8]); + int bit0 = br.GetBit(p[9] + bit1); int cat = (2 * bit1) + bit0; v = 0; byte[] tab = null; @@ -708,7 +746,7 @@ namespace SixLabors.ImageSharp.Formats.WebP for (int i = 0; i < tab.Length; i++) { - v += v + this.bitReader.GetBit(tab[i]); + v += v + br.GetBit(tab[i]); } v += 3 + (8 << cat); @@ -866,8 +904,10 @@ namespace SixLabors.ImageSharp.Formats.WebP dec.Vp8BitReaders[lastPart] = new Vp8BitReader(this.bitReader.Data, (uint)sizeLeft, partStart); } - private void ParseDequantizationIndices(Vp8SegmentHeader vp8SegmentHeader) + private void ParseDequantizationIndices(Vp8Decoder decoder) { + Vp8SegmentHeader vp8SegmentHeader = decoder.SegmentHeader; + int baseQ0 = (int)this.bitReader.ReadValue(7); bool hasValue = this.bitReader.ReadBool(); int dqy1Dc = hasValue ? this.bitReader.ReadSignedValue(4) : 0; @@ -894,7 +934,7 @@ namespace SixLabors.ImageSharp.Formats.WebP { if (i > 0) { - // dec->dqm_[i] = dec->dqm_[0]; + decoder.DeQuantMatrices[i] = decoder.DeQuantMatrices[0]; continue; } else @@ -903,7 +943,7 @@ namespace SixLabors.ImageSharp.Formats.WebP } } - var m = new Vp8QuantMatrix(); + Vp8QuantMatrix m = decoder.DeQuantMatrices[i]; m.Y1Mat[0] = WebPConstants.DcTable[Clip(q + dqy1Dc, 127)]; m.Y1Mat[1] = WebPConstants.AcTable[Clip(q + 0, 127)]; m.Y2Mat[0] = WebPConstants.DcTable[Clip(q + dqy2Dc, 127)] * 2; @@ -924,8 +964,10 @@ namespace SixLabors.ImageSharp.Formats.WebP } } - private void ParseProbabilities(Vp8Decoder dec, Vp8Proba proba) + private void ParseProbabilities(Vp8Decoder dec) { + Vp8Proba proba = dec.Probabilities; + for (int t = 0; t < WebPConstants.NumTypes; ++t) { for (int b = 0; b < WebPConstants.NumBands; ++b)