From 1dc01205e9d41d0893ffceff9eb1630f5416d8ac Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 28 Feb 2020 20:23:55 +0100 Subject: [PATCH] Enable macroblock filtering --- src/ImageSharp/Formats/WebP/Vp8Decoder.cs | 4 ++ .../Formats/WebP/WebPLossyDecoder.cs | 58 ++++++++----------- .../Formats/WebP/WebPDecoderTests.cs | 1 + tests/Images/Input/WebP/lossless_vec_list.txt | 42 -------------- 4 files changed, 30 insertions(+), 75 deletions(-) delete mode 100644 tests/Images/Input/WebP/lossless_vec_list.txt diff --git a/src/ImageSharp/Formats/WebP/Vp8Decoder.cs b/src/ImageSharp/Formats/WebP/Vp8Decoder.cs index 236765a96..e4705f7e9 100644 --- a/src/ImageSharp/Formats/WebP/Vp8Decoder.cs +++ b/src/ImageSharp/Formats/WebP/Vp8Decoder.cs @@ -170,6 +170,10 @@ namespace SixLabors.ImageSharp.Formats.WebP public byte[] CacheV { get; } + public int CacheYOffset { get; set; } + + public int CacheUvOffset { get; set; } + public int CacheYStride { get; } public int CacheUvStride { get; } diff --git a/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs b/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs index 03b8e11ad..740b97eee 100644 --- a/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs +++ b/src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs @@ -201,15 +201,7 @@ namespace SixLabors.ImageSharp.Formats.WebP private void ProcessRow(Vp8Decoder dec, Vp8Io io) { - bool filterRow = (dec.Filter != LoopFilter.None) && - (dec.MbY >= dec.TopLeftMbY) && (dec.MbY <= dec.BottomRightMbY); - this.ReconstructRow(dec); - if (filterRow) - { - this.FilterRow(dec); - } - this.FinishRow(dec, io); } @@ -455,12 +447,9 @@ namespace SixLabors.ImageSharp.Formats.WebP } // Transfer reconstructed samples from yuv_buffer cache to final destination. - int cacheId = 0; // TODO: what should be cacheId, always 0? - int yOffset = cacheId * 16 * dec.CacheYStride; - int uvOffset = cacheId * 8 * dec.CacheUvStride; - Span yOut = dec.CacheY.AsSpan((mbx * 16) + yOffset); - Span uOut = dec.CacheU.AsSpan((mbx * 8) + uvOffset); - Span vOut = dec.CacheV.AsSpan((mbx * 8) + uvOffset); + Span yOut = dec.CacheY.AsSpan(dec.CacheYOffset + (mbx * 16)); + Span uOut = dec.CacheU.AsSpan(dec.CacheUvOffset + (mbx * 8)); + Span vOut = dec.CacheV.AsSpan(dec.CacheUvOffset + (mbx * 8)); for (int j = 0; j < 16; ++j) { yDst.Slice(j * WebPConstants.Bps, 16).CopyTo(yOut.Slice(j * dec.CacheYStride)); @@ -479,7 +468,7 @@ namespace SixLabors.ImageSharp.Formats.WebP int mby = dec.MbY; for (int mbx = dec.TopLeftMbX; mbx < dec.BottomRightMbX; ++mbx) { - //this.DoFilter(dec, mbx, mby); + this.DoFilter(dec, mbx, mby); } } @@ -497,7 +486,7 @@ namespace SixLabors.ImageSharp.Formats.WebP if (dec.Filter is LoopFilter.Simple) { - int offset = mbx * 16; + int offset = dec.CacheYOffset + (mbx * 16); if (mbx > 0) { LossyUtils.SimpleHFilter16(dec.CacheY, offset, yBps, limit + 4); @@ -521,8 +510,8 @@ namespace SixLabors.ImageSharp.Formats.WebP else if (dec.Filter is LoopFilter.Complex) { int uvBps = dec.CacheUvStride; - int yOffset = mbx * 16; - int uvOffset = mbx * 8; + int yOffset = dec.CacheYOffset + (mbx * 16); + int uvOffset = dec.CacheUvOffset + (mbx * 8); int hevThresh = filterInfo.HighEdgeVarianceThreshold; if (mbx > 0) { @@ -552,22 +541,22 @@ namespace SixLabors.ImageSharp.Formats.WebP private void FinishRow(Vp8Decoder dec, Vp8Io io) { - int cacheId = 0; - int yBps = dec.CacheYStride; int extraYRows = WebPConstants.FilterExtraRows[(int)dec.Filter]; int ySize = extraYRows * dec.CacheYStride; int uvSize = (extraYRows / 2) * dec.CacheUvStride; - int yOffset = cacheId * 16 * dec.CacheYStride; - int uvOffset = cacheId * 8 * dec.CacheUvStride; Span yDst = dec.CacheY.AsSpan(); Span uDst = dec.CacheU.AsSpan(); Span vDst = dec.CacheV.AsSpan(); int mby = dec.MbY; bool isFirstRow = mby is 0; bool isLastRow = mby >= dec.BottomRightMbY - 1; + bool filterRow = (dec.Filter != LoopFilter.None) && + (dec.MbY >= dec.TopLeftMbY) && (dec.MbY <= dec.BottomRightMbY); - // TODO: Filter row - //FilterRow(dec); + if (filterRow) + { + this.FilterRow(dec); + } int yStart = mby * 16; int yEnd = (mby + 1) * 16; @@ -580,9 +569,9 @@ namespace SixLabors.ImageSharp.Formats.WebP } else { - io.Y = dec.CacheY.AsSpan(yOffset); - io.U = dec.CacheU.AsSpan(uvOffset); - io.V = dec.CacheV.AsSpan(uvOffset); + io.Y = dec.CacheY.AsSpan(dec.CacheYOffset); + io.U = dec.CacheU.AsSpan(dec.CacheUvOffset); + io.V = dec.CacheV.AsSpan(dec.CacheUvOffset); } if (!isLastRow) @@ -605,10 +594,9 @@ namespace SixLabors.ImageSharp.Formats.WebP // Rotate top samples if needed. if (!isLastRow) { - // TODO: double check this. Cache needs extra rows for filtering! - //yDst.Slice(16 * dec.CacheYStride, ySize).CopyTo(dec.CacheY); - //uDst.Slice(8 * dec.CacheUvStride, uvSize).CopyTo(dec.CacheU); - //vDst.Slice(8 * dec.CacheUvStride, uvSize).CopyTo(dec.CacheV); + yDst.Slice(16 * dec.CacheYStride, ySize).CopyTo(dec.CacheY.AsSpan()); + uDst.Slice(8 * dec.CacheUvStride, uvSize).CopyTo(dec.CacheU.AsSpan()); + vDst.Slice(8 * dec.CacheUvStride, uvSize).CopyTo(dec.CacheV.AsSpan()); } } @@ -1125,7 +1113,7 @@ namespace SixLabors.ImageSharp.Formats.WebP return vp8SegmentHeader; } - private Vp8FilterHeader ParseFilterHeader(Vp8Decoder dec) + private void ParseFilterHeader(Vp8Decoder dec) { Vp8FilterHeader vp8FilterHeader = dec.FilterHeader; vp8FilterHeader.LoopFilter = this.bitReader.ReadBool() ? LoopFilter.Simple : LoopFilter.Complex; @@ -1160,7 +1148,11 @@ namespace SixLabors.ImageSharp.Formats.WebP } } - return vp8FilterHeader; + int extraRows = WebPConstants.FilterExtraRows[(int)dec.Filter]; + int extraY = extraRows * dec.CacheYStride; + int extraUv = (extraRows / 2) * dec.CacheUvStride; + dec.CacheYOffset = extraY; + dec.CacheUvOffset = extraUv; } private void ParsePartitions(Vp8Decoder dec) diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebPDecoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebPDecoderTests.cs index 8538071a0..24324ffe3 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/WebPDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/WebPDecoderTests.cs @@ -81,6 +81,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.WebP [WithFile(Lossy.VeryShort, PixelTypes.Rgba32)] [WithFile(Lossy.BikeComplexFilter, PixelTypes.Rgba32)] [WithFile(Lossy.ComplexFilter01, PixelTypes.Rgba32)] + [WithFile(Lossy.ComplexFilter02, PixelTypes.Rgba32)] public void WebpDecoder_CanDecode_Lossy_WithComplexFilter(TestImageProvider provider) where TPixel : struct, IPixel { diff --git a/tests/Images/Input/WebP/lossless_vec_list.txt b/tests/Images/Input/WebP/lossless_vec_list.txt deleted file mode 100644 index d72bb0c71..000000000 --- a/tests/Images/Input/WebP/lossless_vec_list.txt +++ /dev/null @@ -1,42 +0,0 @@ -List of features used in each test vector. -All the 'lossless_vec_1_*.webp' WebP files should decode to an image comparable to equivalently 'grid.png'. -This synthetic picture is made of 16x16 grid-alternating pixels with RGBA values equal to -blue B=(0,0,255,255) and half-transparent red R=(255,0,0,128), according to -the pattern: -BRBRBRBRBRBRBRBR -RBRBRBRBRBRBRBRB -BRBRBRBRBRBRBRBR -RBRBRBRBRBRBRBRB -BRBRBRBRBRBRBRBR -RBRBRBRBRBRBRBRB -BRBRBRBRBRBRBRBR -RBRBRBRBRBRBRBRB -BRBRBRBRBRBRBRBR -RBRBRBRBRBRBRBRB -BRBRBRBRBRBRBRBR -RBRBRBRBRBRBRBRB -BRBRBRBRBRBRBRBR -RBRBRBRBRBRBRBRB -BRBRBRBRBRBRBRBR -RBRBRBRBRBRBRBRB - -The 'lossless_vec_2_*.webp' WebP files should decode to an image comparable -to equivalently 'peak.png'. Their alpha channel is fully opaque. - -Feature list: -lossless_vec_?_0.webp: none -lossless_vec_?_1.webp: PALETTE -lossless_vec_?_2.webp: PREDICTION -lossless_vec_?_3.webp: PREDICTION PALETTE -lossless_vec_?_4.webp: SUBTRACT-GREEN -lossless_vec_?_5.webp: SUBTRACT-GREEN PALETTE -lossless_vec_?_6.webp: PREDICTION SUBTRACT-GREEN -lossless_vec_?_7.webp: PREDICTION SUBTRACT-GREEN PALETTE -lossless_vec_?_8.webp: CROSS-COLOR-TRANSFORM -lossless_vec_?_9.webp: CROSS-COLOR-TRANSFORM PALETTE -lossless_vec_?_10.webp: PREDICTION CROSS-COLOR-TRANSFORM -lossless_vec_?_11.webp: PREDICTION CROSS-COLOR-TRANSFORM PALETTE -lossless_vec_?_12.webp: CROSS-COLOR-TRANSFORM SUBTRACT-GREEN -lossless_vec_?_13.webp: CROSS-COLOR-TRANSFORM SUBTRACT-GREEN PALETTE -lossless_vec_?_14_.webp: PREDICTION CROSS-COLOR-TRANSFORM SUBTRACT-GREEN -lossless_vec_?_15.webp: PREDICTION CROSS-COLOR-TRANSFORM SUBTRACT-GREEN PALETTE