Browse Source

Enable macroblock filtering

pull/1552/head
Brian Popow 6 years ago
parent
commit
1dc01205e9
  1. 4
      src/ImageSharp/Formats/WebP/Vp8Decoder.cs
  2. 58
      src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs
  3. 1
      tests/ImageSharp.Tests/Formats/WebP/WebPDecoderTests.cs
  4. 42
      tests/Images/Input/WebP/lossless_vec_list.txt

4
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; }

58
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<byte> yOut = dec.CacheY.AsSpan((mbx * 16) + yOffset);
Span<byte> uOut = dec.CacheU.AsSpan((mbx * 8) + uvOffset);
Span<byte> vOut = dec.CacheV.AsSpan((mbx * 8) + uvOffset);
Span<byte> yOut = dec.CacheY.AsSpan(dec.CacheYOffset + (mbx * 16));
Span<byte> uOut = dec.CacheU.AsSpan(dec.CacheUvOffset + (mbx * 8));
Span<byte> 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<byte> yDst = dec.CacheY.AsSpan();
Span<byte> uDst = dec.CacheU.AsSpan();
Span<byte> 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)

1
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<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{

42
tests/Images/Input/WebP/lossless_vec_list.txt

@ -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
Loading…
Cancel
Save