Browse Source

Remove linq usage while getting band probability row, add additional guard checks.

pull/1552/head
Brian Popow 6 years ago
parent
commit
e84301c134
  1. 28
      src/ImageSharp/Formats/WebP/IntraPredictionMode.cs
  2. 3
      src/ImageSharp/Formats/WebP/Readme.md
  3. 12
      src/ImageSharp/Formats/WebP/ReconstructionFilter.cs
  4. 6
      src/ImageSharp/Formats/WebP/VP8BandProbas.cs
  5. 4
      src/ImageSharp/Formats/WebP/Vp8Decoder.cs
  6. 39
      src/ImageSharp/Formats/WebP/Vp8FilterHeader.cs
  7. 38
      src/ImageSharp/Formats/WebP/Vp8FilterInfo.cs
  8. 18
      src/ImageSharp/Formats/WebP/Vp8MacroBlockData.cs
  9. 12
      src/ImageSharp/Formats/WebP/Vp8Proba.cs
  10. 6
      src/ImageSharp/Formats/WebP/Vp8ProbaArray.cs
  11. 35
      src/ImageSharp/Formats/WebP/Vp8Profile.cs
  12. 2
      src/ImageSharp/Formats/WebP/Vp8QuantMatrix.cs
  13. 4
      src/ImageSharp/Formats/WebP/Vp8SegmentHeader.cs
  14. 7
      src/ImageSharp/Formats/WebP/WebPConstants.cs
  15. 1
      src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs
  16. 28
      src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs
  17. 8
      tests/ImageSharp.Tests/TestImages.cs
  18. 3
      tests/Images/Input/WebP/grid.png
  19. 4
      tests/Images/Input/WebP/lossless_with_iccp.webp
  20. 3
      tests/Images/Input/WebP/peak.png

28
src/ImageSharp/Formats/WebP/IntraPredictionMode.cs

@ -0,0 +1,28 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.WebP
{
internal enum IntraPredictionMode
{
/// <summary>
/// Predict DC using row above and column to the left.
/// </summary>
DcPrediction = 0,
/// <summary>
/// Propagate second differences a la "True Motion".
/// </summary>
TrueMotion = 1,
/// <summary>
/// Predict rows using row above.
/// </summary>
VPrediction = 2,
/// <summary>
/// Predict columns using column to the left.
/// </summary>
HPrediction = 3,
}
}

3
src/ImageSharp/Formats/WebP/Readme.md

@ -4,6 +4,7 @@ Reference implementation, specification and stuff like that:
- [google webp introduction](https://developers.google.com/speed/webp)
- [WebP Spec 1.0.3](https://chromium.googlesource.com/webm/libwebp/+/v1.0.3/doc/webp-container-spec.txt)
- [WebP VP8 chunk Spec](http://tools.ietf.org/html/rfc6386)
- [WebP VP8 Spec, Lossy](http://tools.ietf.org/html/rfc6386)
- [WebP VP8L Spec, Lossless](https://developers.google.com/speed/webp/docs/webp_lossless_bitstream_specification)
- [WebP filefront](https://wiki.fileformat.com/image/webp/)
- [WebP test data](https://github.com/webmproject/libwebp-test-data/)

12
src/ImageSharp/Formats/WebP/ReconstructionFilter.cs

@ -1,12 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.WebP
{
internal enum ReconstructionFilter
{
None,
Bicubic,
Bilinear
}
}

6
src/ImageSharp/Formats/WebP/VP8BandProbas.cs

@ -8,6 +8,9 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// </summary>
internal class Vp8BandProbas
{
/// <summary>
/// Initializes a new instance of the <see cref="Vp8BandProbas"/> class.
/// </summary>
public Vp8BandProbas()
{
this.Probabilities = new Vp8ProbaArray[WebPConstants.NumCtx];
@ -17,6 +20,9 @@ namespace SixLabors.ImageSharp.Formats.WebP
}
}
/// <summary>
/// Gets the Probabilities.
/// </summary>
public Vp8ProbaArray[] Probabilities { get; }
}
}

4
src/ImageSharp/Formats/WebP/Vp8Decoder.cs

@ -285,12 +285,12 @@ namespace SixLabors.ImageSharp.Formats.WebP
baseLevel = this.SegmentHeader.FilterStrength[s];
if (!this.SegmentHeader.Delta)
{
baseLevel += hdr.Level;
baseLevel += hdr.FilterLevel;
}
}
else
{
baseLevel = hdr.Level;
baseLevel = hdr.FilterLevel;
}
for (int i4x4 = 0; i4x4 <= 1; ++i4x4)

39
src/ImageSharp/Formats/WebP/Vp8FilterHeader.cs

@ -9,6 +9,13 @@ namespace SixLabors.ImageSharp.Formats.WebP
private const int NumModeLfDeltas = 4;
private int filterLevel;
private int sharpness;
/// <summary>
/// Initializes a new instance of the <see cref="Vp8FilterHeader"/> class.
/// </summary>
public Vp8FilterHeader()
{
this.RefLfDelta = new int[NumRefLfDeltas];
@ -20,16 +27,36 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// </summary>
public LoopFilter LoopFilter { get; set; }
// [0..63]
public int Level { get; set; }
/// <summary>
/// Gets or sets the filter level. Valid values are [0..63].
/// </summary>
public int FilterLevel
{
get => this.filterLevel;
set
{
Guard.MustBeBetweenOrEqualTo(value, 0, 63, nameof(this.FilterLevel));
this.filterLevel = value;
}
}
// [0..7]
public int Sharpness { get; set; }
/// <summary>
/// Gets or sets the filter sharpness. Valid values are [0..7].
/// </summary>
public int Sharpness
{
get => this.sharpness;
set
{
Guard.MustBeBetweenOrEqualTo(value, 0, 7, nameof(this.Sharpness));
this.sharpness = value;
}
}
public bool UseLfDelta { get; set; }
public int[] RefLfDelta { get; private set; }
public int[] RefLfDelta { get; }
public int[] ModeLfDelta { get; private set; }
public int[] ModeLfDelta { get; }
}
}

38
src/ImageSharp/Formats/WebP/Vp8FilterInfo.cs

@ -8,6 +8,12 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// </summary>
internal class Vp8FilterInfo : IDeepCloneable
{
private byte limit;
private byte innerLevel;
private byte highEdgeVarianceThreshold;
/// <summary>
/// Initializes a new instance of the <see cref="Vp8FilterInfo"/> class.
/// </summary>
@ -30,12 +36,28 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// <summary>
/// Gets or sets the filter limit in [3..189], or 0 if no filtering.
/// </summary>
public byte Limit { get; set; }
public byte Limit
{
get => this.limit;
set
{
Guard.MustBeBetweenOrEqualTo(value, (byte)0, (byte)189, nameof(this.Limit));
this.limit = value;
}
}
/// <summary>
/// Gets or sets the inner limit in [1..63].
/// Gets or sets the inner limit in [1..63], or 0 if no filtering.
/// </summary>
public byte InnerLevel { get; set; }
public byte InnerLevel
{
get => this.innerLevel;
set
{
Guard.MustBeBetweenOrEqualTo(value, (byte)0, (byte)63, nameof(this.InnerLevel));
this.innerLevel = value;
}
}
/// <summary>
/// Gets or sets a value indicating whether to do inner filtering.
@ -45,7 +67,15 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// <summary>
/// Gets or sets the high edge variance threshold in [0..2].
/// </summary>
public byte HighEdgeVarianceThreshold { get; set; }
public byte HighEdgeVarianceThreshold
{
get => this.highEdgeVarianceThreshold;
set
{
Guard.MustBeBetweenOrEqualTo(value, (byte)0, (byte)2, nameof(this.HighEdgeVarianceThreshold));
this.highEdgeVarianceThreshold = value;
}
}
/// <inheritdoc/>
public IDeepCloneable DeepClone() => new Vp8FilterInfo(this);

18
src/ImageSharp/Formats/WebP/Vp8MacroBlockData.cs

@ -37,8 +37,26 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// </summary>
public byte UvMode { get; set; }
/// <summary>
/// Gets or sets bit-wise info about the content of each sub-4x4 blocks (in decoding order).
/// Each of the 4x4 blocks for y/u/v is associated with a 2b code according to:
/// code=0 -> no coefficient
/// code=1 -> only DC
/// code=2 -> first three coefficients are non-zero
/// code=3 -> more than three coefficients are non-zero
/// This allows to call specialized transform functions.
/// </summary>
public uint NonZeroY { get; set; }
/// <summary>
/// Gets or sets bit-wise info about the content of each sub-4x4 blocks (in decoding order).
/// Each of the 4x4 blocks for y/u/v is associated with a 2b code according to:
/// code=0 -> no coefficient
/// code=1 -> only DC
/// code=2 -> first three coefficients are non-zero
/// code=3 -> more than three coefficients are non-zero
/// This allows to call specialized transform functions.
/// </summary>
public uint NonZeroUv { get; set; }
public bool Skip { get; set; }

12
src/ImageSharp/Formats/WebP/Vp8Proba.cs

@ -10,11 +10,14 @@ namespace SixLabors.ImageSharp.Formats.WebP
{
private const int MbFeatureTreeProbs = 3;
/// <summary>
/// Initializes a new instance of the <see cref="Vp8Proba"/> class.
/// </summary>
public Vp8Proba()
{
this.Segments = new uint[MbFeatureTreeProbs];
this.Bands = new Vp8BandProbas[WebPConstants.NumTypes, WebPConstants.NumBands];
this.BandsPtr = new Vp8BandProbas[WebPConstants.NumTypes, 16 + 1];
this.BandsPtr = new Vp8BandProbas[WebPConstants.NumTypes][];
for (int i = 0; i < WebPConstants.NumTypes; i++)
{
@ -26,10 +29,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
for (int i = 0; i < WebPConstants.NumTypes; i++)
{
for (int j = 0; j < 17; j++)
{
this.BandsPtr[i, j] = new Vp8BandProbas();
}
this.BandsPtr[i] = new Vp8BandProbas[16 + 1];
}
}
@ -37,6 +37,6 @@ namespace SixLabors.ImageSharp.Formats.WebP
public Vp8BandProbas[,] Bands { get; }
public Vp8BandProbas[,] BandsPtr { get; }
public Vp8BandProbas[][] BandsPtr { get; }
}
}

6
src/ImageSharp/Formats/WebP/Vp8ProbaArray.cs

@ -8,11 +8,17 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// </summary>
internal class Vp8ProbaArray
{
/// <summary>
/// Initializes a new instance of the <see cref="Vp8ProbaArray"/> class.
/// </summary>
public Vp8ProbaArray()
{
this.Probabilities = new byte[WebPConstants.NumProbas];
}
/// <summary>
/// Gets the probabilities.
/// </summary>
public byte[] Probabilities { get; }
}
}

35
src/ImageSharp/Formats/WebP/Vp8Profile.cs

@ -1,35 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.WebP
{
/// <summary>
/// The version number of the frame header enables or disables certain features in the bitstream.
/// +---------+-------------------------+-------------+
/// | Version | Reconstruction Filter | Loop Filter |
/// +---------+-------------------------+-------------+
/// | 0 | Bicubic | Normal |
/// | | | |
/// | 1 | Bilinear | Simple |
/// | | | |
/// | 2 | Bilinear | None |
/// | | | |
/// | 3 | None | None |
/// | | | |
/// | Other | Reserved for future use | |
/// +---------+-------------------------+-------------+
/// See paragraph 9, https://tools.ietf.org/html/rfc6386.
/// </summary>
internal class Vp8Profile
{
/// <summary>
/// Gets or sets the reconstruction filter.
/// </summary>
public ReconstructionFilter ReconstructionFilter { get; set; }
/// <summary>
/// Gets or sets the loop filter.
/// </summary>
public LoopFilter LoopFilter { get; set; }
}
}

2
src/ImageSharp/Formats/WebP/Vp8QuantMatrix.cs

@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
get => this.dither;
set
{
Guard.MustBeBetweenOrEqualTo(value, 0, 255, nameof(this.dither));
Guard.MustBeBetweenOrEqualTo(value, 0, 255, nameof(this.Dither));
this.dither = value;
}
}

4
src/ImageSharp/Formats/WebP/Vp8SegmentHeader.cs

@ -32,11 +32,11 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// <summary>
/// Gets quantization changes.
/// </summary>
public byte[] Quantizer { get; private set; }
public byte[] Quantizer { get; }
/// <summary>
/// Gets the filter strength for segments.
/// </summary>
public byte[] FilterStrength { get; private set; }
public byte[] FilterStrength { get; }
}
}

7
src/ImageSharp/Formats/WebP/WebPConstants.cs

@ -110,7 +110,6 @@ namespace SixLabors.ImageSharp.Formats.WebP
NumDistanceCodes
};
// VP8 constants from here on:
public const int NumMbSegments = 4;
public const int MaxNumPartitions = 8;
@ -126,12 +125,6 @@ namespace SixLabors.ImageSharp.Formats.WebP
// this is the common stride for enc/dec
public const int Bps = 32;
// intra prediction modes (TODO: maybe use an enum for this)
public const int DcPred = 0; // predict DC using row above and column to the left
public const int TmPred = 1; // propagate second differences a la "True Motion"
public const int VPred = 2; // predict rows using row above
public const int HPred = 3; // predict columns using column to the left
/// <summary>
/// How many extra lines are needed on the MB boundary for caching, given a filtering level.
/// Simple filter(1): up to 2 luma samples are read and 1 is written.

1
src/ImageSharp/Formats/WebP/WebPLosslessDecoder.cs

@ -602,7 +602,6 @@ namespace SixLabors.ImageSharp.Formats.WebP
int repeat = (int)(this.bitReader.ReadValue(extraBits) + repeatOffset);
if (symbol + repeat > numSymbols)
{
// TODO: not sure, if this should be treated as an error here
return;
}

28
src/ImageSharp/Formats/WebP/WebPLossyDecoder.cs

@ -3,7 +3,6 @@
using System;
using System.Buffers;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@ -207,8 +206,8 @@ namespace SixLabors.ImageSharp.Formats.WebP
{
// Hardcoded 16x16 intra-mode decision tree.
int yMode = this.bitReader.GetBit(156) != 0 ?
this.bitReader.GetBit(128) != 0 ? WebPConstants.TmPred : WebPConstants.HPred :
this.bitReader.GetBit(163) != 0 ? WebPConstants.VPred : WebPConstants.DcPred;
this.bitReader.GetBit(128) != 0 ? (int)IntraPredictionMode.TrueMotion : (int)IntraPredictionMode.HPrediction :
this.bitReader.GetBit(163) != 0 ? (int)IntraPredictionMode.VPrediction : (int)IntraPredictionMode.DcPrediction;
block.Modes[0] = (byte)yMode;
for (int i = 0; i < left.Length; i++)
{
@ -864,7 +863,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
int dstOffset = 0;
Vp8MacroBlockData block = dec.CurrentBlockData;
Vp8QuantMatrix q = dec.DeQuantMatrices[block.Segment];
Vp8BandProbas[,] bands = dec.Probabilities.BandsPtr;
Vp8BandProbas[][] bands = dec.Probabilities.BandsPtr;
Vp8BandProbas[] acProba;
Vp8MacroBlock leftMb = dec.LeftMacroBlock;
short[] dst = block.Coeffs;
@ -876,14 +875,14 @@ namespace SixLabors.ImageSharp.Formats.WebP
if (block.IsI4x4)
{
first = 0;
acProba = GetBandsRow(bands, 3);
acProba = bands[3];
}
else
{
// Parse DC
var dc = new short[16];
int ctx = (int)(mb.NoneZeroDcCoeffs + leftMb.NoneZeroDcCoeffs);
int nz = this.GetCoeffs(br, GetBandsRow(bands, 1), ctx, q.Y2Mat, 0, dc);
int nz = this.GetCoeffs(br, bands[1], ctx, q.Y2Mat, 0, dc);
mb.NoneZeroDcCoeffs = leftMb.NoneZeroDcCoeffs = (uint)(nz > 0 ? 1 : 0);
if (nz > 1)
{
@ -901,7 +900,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
}
first = 1;
acProba = GetBandsRow(bands, 0);
acProba = bands[0];
}
byte tnz = (byte)(mb.NoneZeroAcDcCoeffs & 0x0f);
@ -941,7 +940,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
for (int x = 0; x < 2; ++x)
{
int ctx = l + (tnz & 1);
int nz = this.GetCoeffs(br, GetBandsRow(bands, 2), ctx, q.UvMat, 0, dst.AsSpan(dstOffset));
int nz = this.GetCoeffs(br, bands[2], ctx, q.UvMat, 0, dst.AsSpan(dstOffset));
l = (nz > 0) ? 1 : 0;
tnz = (byte)((tnz >> 1) | (l << 3));
nzCoeffs = NzCodeBits(nzCoeffs, nz, dst[dstOffset] != 0 ? 1 : 0);
@ -1164,11 +1163,11 @@ namespace SixLabors.ImageSharp.Formats.WebP
{
Vp8FilterHeader vp8FilterHeader = dec.FilterHeader;
vp8FilterHeader.LoopFilter = this.bitReader.ReadBool() ? LoopFilter.Simple : LoopFilter.Complex;
vp8FilterHeader.Level = (int)this.bitReader.ReadValue(6);
vp8FilterHeader.FilterLevel = (int)this.bitReader.ReadValue(6);
vp8FilterHeader.Sharpness = (int)this.bitReader.ReadValue(3);
vp8FilterHeader.UseLfDelta = this.bitReader.ReadBool();
dec.Filter = (vp8FilterHeader.Level == 0) ? LoopFilter.None : vp8FilterHeader.LoopFilter;
dec.Filter = (vp8FilterHeader.FilterLevel == 0) ? LoopFilter.None : vp8FilterHeader.LoopFilter;
if (vp8FilterHeader.UseLfDelta)
{
// Update lf-delta?
@ -1314,7 +1313,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
for (int b = 0; b < 16 + 1; ++b)
{
proba.BandsPtr[t, b] = proba.Bands[t, WebPConstants.Bands[b]];
proba.BandsPtr[t][b] = proba.Bands[t, WebPConstants.Bands[b]];
}
}
@ -1391,13 +1390,6 @@ namespace SixLabors.ImageSharp.Formats.WebP
return nzCoeffs;
}
[MethodImpl(InliningOptions.ShortMethod)]
private static Vp8BandProbas[] GetBandsRow(Vp8BandProbas[,] bands, int rowIdx)
{
Vp8BandProbas[] bandsRow = Enumerable.Range(0, bands.GetLength(1)).Select(x => bands[rowIdx, x]).ToArray();
return bandsRow;
}
[MethodImpl(InliningOptions.ShortMethod)]
private static int CheckMode(int mbx, int mby, int mode)
{

8
tests/ImageSharp.Tests/TestImages.cs

@ -506,10 +506,10 @@ namespace SixLabors.ImageSharp.Tests
// Invalid / corrupted images
// Below images have errors according to webpinfo. The error message webpinfo gives is "Truncated data detected when parsing RIFF payload."
public const string LossLessCorruptImage1 = "Webp/lossless_big_random_alpha.webp"; // substract_green, predictor, cross_color.
public const string LossLessCorruptImage2 = "Webp/lossless_vec_2_7.webp"; // color_indexing, predictor.
public const string LossLessCorruptImage3 = "Webp/lossless_color_transform.webp"; // cross_color, predictor
public const string LossLessCorruptImage4 = "Webp/near_lossless_75.webp"; // predictor, cross_color.
public const string LossLessCorruptImage1 = "WebP/lossless_big_random_alpha.webp"; // substract_green, predictor, cross_color.
public const string LossLessCorruptImage2 = "WebP/lossless_vec_2_7.webp"; // color_indexing, predictor.
public const string LossLessCorruptImage3 = "WebP/lossless_color_transform.webp"; // cross_color, predictor
public const string LossLessCorruptImage4 = "WebP/near_lossless_75.webp"; // predictor, cross_color.
}
public static class Lossy

3
tests/Images/Input/WebP/grid.png

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5c53fb4527509058a8a4caf72e03ee8634f4704ab5369a8e5d194e62359d6ad0
size 117

4
tests/Images/Input/WebP/lossless_with_iccp.webp

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:312aea5ac9557bdfa78ec95bab5c3446a97c980317f46c96a20a4b7837d0ae37
size 68355
oid sha256:3cdb75584ac3db92d78c2c1ec828cb813d280540e5f1bb262ba0b2c352900f81
size 69076

3
tests/Images/Input/WebP/peak.png

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b9b56ed5c1278664222c77f9a452b824b4f9215c819502b3f6b0e0d44270e7e7
size 26456
Loading…
Cancel
Save