diff --git a/src/ImageSharp/Formats/Webp/Lossy/Vp8BandProbas.cs b/src/ImageSharp/Formats/Webp/Lossy/Vp8BandProbas.cs
index 90506efb8..86304d4bd 100644
--- a/src/ImageSharp/Formats/Webp/Lossy/Vp8BandProbas.cs
+++ b/src/ImageSharp/Formats/Webp/Lossy/Vp8BandProbas.cs
@@ -1,6 +1,8 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
+using System.Text.Json.Serialization;
+
namespace SixLabors.ImageSharp.Formats.Webp.Lossy;
///
@@ -20,6 +22,13 @@ internal class Vp8BandProbas
}
}
+ ///
+ /// Initializes a new instance of the class.
+ /// Only used for unit tests.
+ ///
+ [JsonConstructor]
+ public Vp8BandProbas(Vp8ProbaArray[] probabilities) => this.Probabilities = probabilities;
+
///
/// Gets the Probabilities.
///
diff --git a/src/ImageSharp/Formats/Webp/Lossy/Vp8CostArray.cs b/src/ImageSharp/Formats/Webp/Lossy/Vp8CostArray.cs
index 2c8d723d7..8268e1c02 100644
--- a/src/ImageSharp/Formats/Webp/Lossy/Vp8CostArray.cs
+++ b/src/ImageSharp/Formats/Webp/Lossy/Vp8CostArray.cs
@@ -1,6 +1,8 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
+using System.Text.Json.Serialization;
+
namespace SixLabors.ImageSharp.Formats.Webp.Lossy;
internal class Vp8CostArray
@@ -10,5 +12,12 @@ internal class Vp8CostArray
///
public Vp8CostArray() => this.Costs = new ushort[67 + 1];
+ ///
+ /// Initializes a new instance of the class.
+ /// Only used for unit tests.
+ ///
+ [JsonConstructor]
+ public Vp8CostArray(ushort[] costs) => this.Costs = costs;
+
public ushort[] Costs { get; }
}
diff --git a/src/ImageSharp/Formats/Webp/Lossy/Vp8Costs.cs b/src/ImageSharp/Formats/Webp/Lossy/Vp8Costs.cs
index eee22159e..2441436fb 100644
--- a/src/ImageSharp/Formats/Webp/Lossy/Vp8Costs.cs
+++ b/src/ImageSharp/Formats/Webp/Lossy/Vp8Costs.cs
@@ -1,6 +1,8 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
+using System.Text.Json.Serialization;
+
namespace SixLabors.ImageSharp.Formats.Webp.Lossy;
internal class Vp8Costs
@@ -17,6 +19,13 @@ internal class Vp8Costs
}
}
+ ///
+ /// Initializes a new instance of the class.
+ /// Only used for unit tests.
+ ///
+ [JsonConstructor]
+ public Vp8Costs(Vp8CostArray[] costs) => this.Costs = costs;
+
///
/// Gets the Costs.
///
diff --git a/src/ImageSharp/Formats/Webp/Lossy/Vp8ProbaArray.cs b/src/ImageSharp/Formats/Webp/Lossy/Vp8ProbaArray.cs
index 337527542..a19f9742f 100644
--- a/src/ImageSharp/Formats/Webp/Lossy/Vp8ProbaArray.cs
+++ b/src/ImageSharp/Formats/Webp/Lossy/Vp8ProbaArray.cs
@@ -1,6 +1,8 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
+using System.Text.Json.Serialization;
+
namespace SixLabors.ImageSharp.Formats.Webp.Lossy;
///
@@ -13,6 +15,13 @@ internal class Vp8ProbaArray
///
public Vp8ProbaArray() => this.Probabilities = new byte[WebpConstants.NumProbas];
+ ///
+ /// Initializes a new instance of the class.
+ /// Only used for unit tests.
+ ///
+ [JsonConstructor]
+ public Vp8ProbaArray(byte[] probabilities) => this.Probabilities = probabilities;
+
///
/// Gets the probabilities.
///
diff --git a/src/ImageSharp/Formats/Webp/Lossy/Vp8Residual.cs b/src/ImageSharp/Formats/Webp/Lossy/Vp8Residual.cs
index 68bf09f94..c00d7dbdd 100644
--- a/src/ImageSharp/Formats/Webp/Lossy/Vp8Residual.cs
+++ b/src/ImageSharp/Formats/Webp/Lossy/Vp8Residual.cs
@@ -7,6 +7,7 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
+using System.Text.Json.Serialization;
namespace SixLabors.ImageSharp.Formats.Webp.Lossy;
@@ -15,6 +16,22 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy;
///
internal class Vp8Residual
{
+ public Vp8Residual()
+ {
+ }
+
+ [JsonConstructor]
+ public Vp8Residual(int first, int last, int coeffType, short[] coeffs, Vp8BandProbas[] prob, Vp8Stats[] stats, Vp8Costs[] costs)
+ {
+ this.First = first;
+ this.Last = last;
+ this.CoeffType = coeffType;
+ this.Coeffs = coeffs;
+ this.Prob = prob;
+ this.Stats = stats;
+ this.Costs = costs;
+ }
+
public int First { get; set; }
public int Last { get; set; }
@@ -156,6 +173,55 @@ internal class Vp8Residual
return LossyUtils.Vp8BitCost(0, (byte)p0);
}
+ if (false)
+ {
+ Span scratch = stackalloc byte[32];
+ Span ctxs = scratch.Slice(0, 16);
+ Span levels = scratch.Slice(16);
+ Span absLevels = stackalloc ushort[16];
+
+ // Precompute clamped levels and contexts, packed to 8b.
+ ref short outputRef = ref MemoryMarshal.GetReference(this.Coeffs);
+ Vector256 c0 = Unsafe.As>(ref outputRef).AsInt16();
+ Vector256 d0 = Avx2.Subtract(Vector256.Zero, c0);
+ Vector256 e0 = Avx2.Max(c0, d0); // abs(v), 16b
+ Vector256 f = Avx2.PackSignedSaturate(e0, e0);
+ Vector256 g = Avx2.Min(f.AsByte(), Vector256.Create((byte)2));
+ Vector256 h = Avx2.Min(f.AsByte(), Vector256.Create((byte)67)); // clampLevel in [0..67]
+
+ ref byte ctxsRef = ref MemoryMarshal.GetReference(ctxs);
+ ref byte levelsRef = ref MemoryMarshal.GetReference(levels);
+ ref ushort absLevelsRef = ref MemoryMarshal.GetReference(absLevels);
+ Unsafe.As>(ref ctxsRef) = g.GetLower();
+ Unsafe.As>(ref levelsRef) = h.GetLower();
+ Unsafe.As>(ref absLevelsRef) = e0.AsUInt16();
+
+ int level;
+ int flevel;
+ for (; n < this.Last; ++n)
+ {
+ int ctx = ctxs[n];
+ level = levels[n];
+ flevel = absLevels[n];
+ cost += WebpLookupTables.Vp8LevelFixedCosts[flevel] + t.Costs[level];
+ t = costs[n + 1].Costs[ctx];
+ }
+
+ // Last coefficient is always non-zero.
+ level = levels[n];
+ flevel = absLevels[n];
+ cost += WebpLookupTables.Vp8LevelFixedCosts[flevel] + t.Costs[level];
+ if (n < 15)
+ {
+ int b = WebpConstants.Vp8EncBands[n + 1];
+ int ctx = ctxs[n];
+ int lastP0 = this.Prob[b].Probabilities[ctx].Probabilities[0];
+ cost += LossyUtils.Vp8BitCost(0, (byte)lastP0);
+ }
+
+ return cost;
+ }
+
if (Sse2.IsSupported)
{
Span scratch = stackalloc byte[32];
diff --git a/src/ImageSharp/Formats/Webp/Lossy/Vp8Stats.cs b/src/ImageSharp/Formats/Webp/Lossy/Vp8Stats.cs
index dda921a7c..b6c05f2d4 100644
--- a/src/ImageSharp/Formats/Webp/Lossy/Vp8Stats.cs
+++ b/src/ImageSharp/Formats/Webp/Lossy/Vp8Stats.cs
@@ -1,6 +1,8 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
+using System.Text.Json.Serialization;
+
namespace SixLabors.ImageSharp.Formats.Webp.Lossy;
internal class Vp8Stats
@@ -17,5 +19,12 @@ internal class Vp8Stats
}
}
+ ///
+ /// Initializes a new instance of the class.
+ /// Only used for unit tests.
+ ///
+ [JsonConstructor]
+ public Vp8Stats(Vp8StatsArray[] stats) => this.Stats = stats;
+
public Vp8StatsArray[] Stats { get; }
}
diff --git a/src/ImageSharp/Formats/Webp/Lossy/Vp8StatsArray.cs b/src/ImageSharp/Formats/Webp/Lossy/Vp8StatsArray.cs
index 2fbba6996..864248dd8 100644
--- a/src/ImageSharp/Formats/Webp/Lossy/Vp8StatsArray.cs
+++ b/src/ImageSharp/Formats/Webp/Lossy/Vp8StatsArray.cs
@@ -1,6 +1,8 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
+using System.Text.Json.Serialization;
+
namespace SixLabors.ImageSharp.Formats.Webp.Lossy;
internal class Vp8StatsArray
@@ -10,5 +12,12 @@ internal class Vp8StatsArray
///
public Vp8StatsArray() => this.Stats = new uint[WebpConstants.NumProbas];
+ ///
+ /// Initializes a new instance of the class.
+ /// Only used for unit tests.
+ ///
+ [JsonConstructor]
+ public Vp8StatsArray(uint[] stats) => this.Stats = stats;
+
public uint[] Stats { get; }
}