diff --git a/src/ImageSharp/Formats/WebP/Lossless/HuffmanTree.cs b/src/ImageSharp/Formats/WebP/Lossless/HuffmanTree.cs
index d7179ad121..a4578912ef 100644
--- a/src/ImageSharp/Formats/WebP/Lossless/HuffmanTree.cs
+++ b/src/ImageSharp/Formats/WebP/Lossless/HuffmanTree.cs
@@ -9,17 +9,10 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
/// Represents the Huffman tree.
///
[DebuggerDisplay("TotalCount = {TotalCount}, Value = {Value}, Left = {PoolIndexLeft}, Right = {PoolIndexRight}")]
- internal class HuffmanTree : IDeepCloneable
+ internal struct HuffmanTree : IDeepCloneable
{
///
- /// Initializes a new instance of the class.
- ///
- public HuffmanTree()
- {
- }
-
- ///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the struct.
///
/// The HuffmanTree to create an instance from.
private HuffmanTree(HuffmanTree other)
diff --git a/src/ImageSharp/Formats/WebP/Lossless/HuffmanTreeCode.cs b/src/ImageSharp/Formats/WebP/Lossless/HuffmanTreeCode.cs
index 6b6e234d2d..ef88aae849 100644
--- a/src/ImageSharp/Formats/WebP/Lossless/HuffmanTreeCode.cs
+++ b/src/ImageSharp/Formats/WebP/Lossless/HuffmanTreeCode.cs
@@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
///
/// Represents the tree codes (depth and bits array).
///
- internal class HuffmanTreeCode
+ internal struct HuffmanTreeCode
{
///
/// Gets or sets the number of symbols.
diff --git a/src/ImageSharp/Formats/WebP/Lossless/Vp8LEncoder.cs b/src/ImageSharp/Formats/WebP/Lossless/Vp8LEncoder.cs
index e5b53c6f85..8194c54b83 100644
--- a/src/ImageSharp/Formats/WebP/Lossless/Vp8LEncoder.cs
+++ b/src/ImageSharp/Formats/WebP/Lossless/Vp8LEncoder.cs
@@ -321,12 +321,10 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
this.HistoBits,
bytePosition);
}
-
- // TODO: Comparison and picking of best (smallest) encoding
}
///
- /// Analyzes the image and decides what transforms should be used.
+ /// Analyzes the image and decides which transforms should be used.
///
private CrunchConfig[] EncoderAnalyze(Image image, out bool redAndBlueAlwaysZero)
where TPixel : unmanaged, IPixel
@@ -462,7 +460,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
var huffmanCodes = new HuffmanTreeCode[bitArraySize];
for (int i = 0; i < huffmanCodes.Length; i++)
{
- huffmanCodes[i] = new HuffmanTreeCode();
+ huffmanCodes[i] = default;
}
GetHuffBitLengthsAndCodes(histogramImage, huffmanCodes);
@@ -526,10 +524,9 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
}
// Store actual literals.
- var hdrSizeTmp = (int)(this.bitWriter.NumBytes() - initBytePosition);
this.StoreImageToBitMask(width, histogramBits, refsBest, histogramSymbols, huffmanCodes);
- // TODO: Keep track of the smallest image so far.
+ // Keep track of the smallest image so far.
if (bitWriterBest != null && this.bitWriter.NumBytes() < bitWriterBest.NumBytes())
{
// TODO: This was done in the reference by swapping references, this will be slower
@@ -609,18 +606,16 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
int cacheBits = 0;
var histogramSymbols = new ushort[1]; // Only one tree, one symbol.
- // TODO: Can HuffmanTreeCode be struct
var huffmanCodes = new HuffmanTreeCode[5];
for (int i = 0; i < huffmanCodes.Length; i++)
{
- huffmanCodes[i] = new HuffmanTreeCode();
+ huffmanCodes[i] = default;
}
- // TODO: Can HuffmanTree be struct
var huffTree = new HuffmanTree[3UL * WebPConstants.CodeLengthCodes];
for (int i = 0; i < huffTree.Length; i++)
{
- huffTree[i] = new HuffmanTree();
+ huffTree[i] = default;
}
// Calculate backward references from the image pixels.
@@ -790,14 +785,14 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
{
if (trimmedLength == 2)
{
- this.bitWriter.PutBits(0, 3 + 2); // nbitpairs=1, trimmed_length=2
+ this.bitWriter.PutBits(0, 3 + 2); // nbitpairs=1, trimmedLength=2
}
else
{
- int nbits = WebPCommonUtils.BitsLog2Floor((uint)trimmedLength - 2);
- int nbitpairs = (nbits / 2) + 1;
- this.bitWriter.PutBits((uint)nbitpairs - 1, 3);
- this.bitWriter.PutBits((uint)trimmedLength - 2, nbitpairs * 2);
+ int nBits = WebPCommonUtils.BitsLog2Floor((uint)trimmedLength - 2);
+ int nBitPairs = (nBits / 2) + 1;
+ this.bitWriter.PutBits((uint)nBitPairs - 1, 3);
+ this.bitWriter.PutBits((uint)trimmedLength - 2, nBitPairs * 2);
}
}
@@ -1056,13 +1051,13 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
// Let's check if the histogram of the chosen entropy mode has
// non-zero red and blue values. If all are zero, we can later skip
// the cross color optimization.
- var histoPairs = new byte[][]
+ byte[][] histoPairs =
{
- new byte[] { (byte)HistoIx.HistoRed, (byte)HistoIx.HistoBlue },
- new byte[] { (byte)HistoIx.HistoRedPred, (byte)HistoIx.HistoBluePred },
- new byte[] { (byte)HistoIx.HistoRedSubGreen, (byte)HistoIx.HistoBlueSubGreen },
- new byte[] { (byte)HistoIx.HistoRedPredSubGreen, (byte)HistoIx.HistoBluePredSubGreen },
- new byte[] { (byte)HistoIx.HistoRed, (byte)HistoIx.HistoBlue }
+ new[] { (byte)HistoIx.HistoRed, (byte)HistoIx.HistoBlue },
+ new[] { (byte)HistoIx.HistoRedPred, (byte)HistoIx.HistoBluePred },
+ new[] { (byte)HistoIx.HistoRedSubGreen, (byte)HistoIx.HistoBlueSubGreen },
+ new[] { (byte)HistoIx.HistoRedPredSubGreen, (byte)HistoIx.HistoBluePredSubGreen },
+ new[] { (byte)HistoIx.HistoRed, (byte)HistoIx.HistoBlue }
};
Span redHisto = histo.Slice(256 * histoPairs[(int)minEntropyIx][0]);
Span blueHisto = histo.Slice(256 * histoPairs[(int)minEntropyIx][1]);
@@ -1079,7 +1074,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
}
///
- /// If number of colors in the image is less than or equal to MAX_PALETTE_SIZE,
+ /// If number of colors in the image is less than or equal to MaxPaletteSize,
/// creates a palette and returns true, else returns false.
///
/// true, if a palette should be used.
@@ -1167,7 +1162,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
}
///
- /// Remap argb values in src[] to packed palettes entries in dst[]
+ /// Remap bgra values in src[] to packed palettes entries in dst[]
/// using 'row' as a temporary buffer of size 'width'.
/// We assume that all src[] values have a corresponding entry in the palette.
/// Note: src[] can be the same as dst[]
@@ -1293,8 +1288,8 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
LosslessUtils.BundleColorMap(tmpRow, width, xBits, dst);
- src = src.Slice((int)srcStride);
- dst = dst.Slice((int)dstStride);
+ src = src.Slice(srcStride);
+ dst = dst.Slice(dstStride);
}
}
@@ -1318,8 +1313,8 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
LosslessUtils.BundleColorMap(tmpRow, width, xBits, dst);
- src = src.Slice((int)srcStride);
- dst = dst.Slice((int)dstStride);
+ src = src.Slice(srcStride);
+ dst = dst.Slice(dstStride);
}
}
diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebPEncoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebPEncoderTests.cs
new file mode 100644
index 0000000000..2cc2c88fe7
--- /dev/null
+++ b/tests/ImageSharp.Tests/Formats/WebP/WebPEncoderTests.cs
@@ -0,0 +1,27 @@
+using SixLabors.ImageSharp.Formats.WebP;
+using SixLabors.ImageSharp.PixelFormats;
+using Xunit;
+
+namespace SixLabors.ImageSharp.Tests.Formats.WebP
+{
+ using static TestImages.WebP;
+
+ public class WebPEncoderTests
+ {
+ [Theory]
+ [WithFile(TestImages.Bmp.Car, PixelTypes.Rgba32)]
+ public void Encode_Lossless_Works(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel
+ {
+ var encoder = new WebPEncoder()
+ {
+ Lossy = false
+ };
+
+ using (Image image = provider.GetImage())
+ {
+ image.VerifyEncoder(provider, "webp", "lossless", encoder);
+ }
+ }
+ }
+}