Browse Source

Change HuffmanTree and HuffmanTreeCode to struct

pull/1552/head
Brian Popow 6 years ago
parent
commit
a38015c90d
  1. 11
      src/ImageSharp/Formats/WebP/Lossless/HuffmanTree.cs
  2. 2
      src/ImageSharp/Formats/WebP/Lossless/HuffmanTreeCode.cs
  3. 49
      src/ImageSharp/Formats/WebP/Lossless/Vp8LEncoder.cs
  4. 27
      tests/ImageSharp.Tests/Formats/WebP/WebPEncoderTests.cs

11
src/ImageSharp/Formats/WebP/Lossless/HuffmanTree.cs

@ -9,17 +9,10 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
/// Represents the Huffman tree.
/// </summary>
[DebuggerDisplay("TotalCount = {TotalCount}, Value = {Value}, Left = {PoolIndexLeft}, Right = {PoolIndexRight}")]
internal class HuffmanTree : IDeepCloneable
internal struct HuffmanTree : IDeepCloneable
{
/// <summary>
/// Initializes a new instance of the <see cref="HuffmanTree"/> class.
/// </summary>
public HuffmanTree()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="HuffmanTree"/> class.
/// Initializes a new instance of the <see cref="HuffmanTree"/> struct.
/// </summary>
/// <param name="other">The HuffmanTree to create an instance from.</param>
private HuffmanTree(HuffmanTree other)

2
src/ImageSharp/Formats/WebP/Lossless/HuffmanTreeCode.cs

@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
/// <summary>
/// Represents the tree codes (depth and bits array).
/// </summary>
internal class HuffmanTreeCode
internal struct HuffmanTreeCode
{
/// <summary>
/// Gets or sets the number of symbols.

49
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
}
/// <summary>
/// Analyzes the image and decides what transforms should be used.
/// Analyzes the image and decides which transforms should be used.
/// </summary>
private CrunchConfig[] EncoderAnalyze<TPixel>(Image<TPixel> image, out bool redAndBlueAlwaysZero)
where TPixel : unmanaged, IPixel<TPixel>
@ -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<uint> redHisto = histo.Slice(256 * histoPairs[(int)minEntropyIx][0]);
Span<uint> blueHisto = histo.Slice(256 * histoPairs[(int)minEntropyIx][1]);
@ -1079,7 +1074,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
}
/// <summary>
/// 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.
/// </summary>
/// <returns>true, if a palette should be used.</returns>
@ -1167,7 +1162,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
}
/// <summary>
/// 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);
}
}

27
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<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
var encoder = new WebPEncoder()
{
Lossy = false
};
using (Image<TPixel> image = provider.GetImage())
{
image.VerifyEncoder(provider, "webp", "lossless", encoder);
}
}
}
}
Loading…
Cancel
Save