Browse Source

Use separate buffer for encoded image data and input bgra data

pull/1552/head
Brian Popow 5 years ago
parent
commit
1fe943a9fa
  1. 62
      src/ImageSharp/Formats/WebP/Lossless/Vp8LEncoder.cs

62
src/ImageSharp/Formats/WebP/Lossless/Vp8LEncoder.cs

@ -80,6 +80,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
this.method = Numerics.Clamp(method, 0, 6);
this.bitWriter = new Vp8LBitWriter(initialSize);
this.Bgra = memoryAllocator.Allocate<uint>(pixelCount);
this.EncodedData = memoryAllocator.Allocate<uint>(pixelCount);
this.Palette = memoryAllocator.Allocate<uint>(WebpConstants.MaxPaletteSize);
this.Refs = new Vp8LBackwardRefs[3];
this.HashChain = new Vp8LHashChain(pixelCount);
@ -96,10 +97,15 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
}
/// <summary>
/// Gets the memory for the encoded output image data.
/// Gets the memory for the image data as packed bgra values.
/// </summary>
public IMemoryOwner<uint> Bgra { get; }
/// <summary>
/// Gets the memory for the encoded output image data.
/// </summary>
public IMemoryOwner<uint> EncodedData { get; }
/// <summary>
/// Gets or sets the scratch memory for bgra rows used for predictions.
/// </summary>
@ -237,7 +243,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
// Convert image pixels to bgra array.
this.ConvertPixelsToBgra(image, width, height);
Span<uint> bgra = this.Bgra.GetSpan();
ReadOnlySpan<uint> bgra = this.Bgra.GetSpan();
Span<uint> encodedData = this.EncodedData.GetSpan();
// Analyze image (entropy, numPalettes etc).
CrunchConfig[] crunchConfigs = this.EncoderAnalyze(bgra, width, height, out bool redAndBlueAlwaysZero);
@ -248,6 +255,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
bool isFirstConfig = true;
foreach (CrunchConfig crunchConfig in crunchConfigs)
{
bgra.CopyTo(encodedData);
bool useCache = true;
this.UsePalette = crunchConfig.EntropyIdx == EntropyIx.Palette ||
crunchConfig.EntropyIdx == EntropyIx.PaletteAndSpatial;
@ -297,15 +305,11 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
// Encode and write the transformed image.
this.EncodeImage(
bgra,
this.HashChain,
this.Refs,
this.CurrentWidth,
height,
useCache,
crunchConfig,
this.CacheBits,
this.HistoBits);
this.CacheBits);
// If we are better than what we already have.
if (isFirstConfig || this.bitWriter.NumBytes() < bestSize)
@ -421,9 +425,11 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
return crunchConfigs.ToArray();
}
private void EncodeImage(ReadOnlySpan<uint> bgra, Vp8LHashChain hashChain, Vp8LBackwardRefs[] refsArray, int width, int height, bool useCache, CrunchConfig config, int cacheBits, int histogramBits)
private void EncodeImage(int width, int height, bool useCache, CrunchConfig config, int cacheBits)
{
int histogramImageXySize = LosslessUtils.SubSampleSize(width, histogramBits) * LosslessUtils.SubSampleSize(height, histogramBits);
// bgra data with transformations applied.
Span<uint> bgra = this.EncodedData.GetSpan();
int histogramImageXySize = LosslessUtils.SubSampleSize(width, this.HistoBits) * LosslessUtils.SubSampleSize(height, this.HistoBits);
ushort[] histogramSymbols = new ushort[histogramImageXySize];
var huffTree = new HuffmanTree[3 * WebpConstants.CodeLengthCodes];
for (int i = 0; i < huffTree.Length; i++)
@ -444,7 +450,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
}
// Calculate backward references from BGRA image.
hashChain.Fill(this.memoryAllocator, bgra, this.quality, width, height);
this.HashChain.Fill(this.memoryAllocator, bgra, this.quality, width, height);
Vp8LBitWriter bitWriterBest = config.SubConfigs.Count > 1 ? this.bitWriter.Clone() : this.bitWriter;
Vp8LBitWriter bwInit = this.bitWriter;
@ -458,13 +464,13 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
this.quality,
subConfig.Lz77,
ref cacheBits,
hashChain,
refsArray[0],
refsArray[1]); // TODO : Pass do not cache
this.HashChain,
this.Refs[0],
this.Refs[1]);
// Keep the best references aside and use the other element from the first
// two as a temporary for later usage.
Vp8LBackwardRefs refsTmp = refsArray[refsBest.Equals(refsArray[0]) ? 1 : 0];
Vp8LBackwardRefs refsTmp = this.Refs[refsBest.Equals(this.Refs[0]) ? 1 : 0];
this.bitWriter.Reset(bwInit);
var tmpHisto = new Vp8LHistogram(cacheBits);
@ -475,7 +481,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
}
// Build histogram image and symbols from backward references.
HistogramEncoder.GetHistoImageSymbols(width, height, refsBest, this.quality, histogramBits, cacheBits, histogramImage, tmpHisto, histogramSymbols);
HistogramEncoder.GetHistoImageSymbols(width, height, refsBest, this.quality, this.HistoBits, cacheBits, histogramImage, tmpHisto, histogramSymbols);
// Create Huffman bit lengths and codes for each histogram image.
int histogramImageSize = histogramImage.Count;
@ -517,8 +523,15 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
}
}
this.bitWriter.PutBits((uint)(histogramBits - 2), 3);
this.EncodeImageNoHuffman(histogramBgra, hashChain, refsTmp, refsArray[2], LosslessUtils.SubSampleSize(width, histogramBits), LosslessUtils.SubSampleSize(height, histogramBits), this.quality);
this.bitWriter.PutBits((uint)(this.HistoBits - 2), 3);
this.EncodeImageNoHuffman(
histogramBgra,
this.HashChain,
refsTmp,
this.Refs[2],
LosslessUtils.SubSampleSize(width, this.HistoBits),
LosslessUtils.SubSampleSize(height, this.HistoBits),
this.quality);
}
// Store Huffman codes.
@ -547,12 +560,12 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
}
// Store actual literals.
this.StoreImageToBitMask(width, histogramBits, refsBest, histogramSymbols, huffmanCodes);
this.StoreImageToBitMask(width, this.HistoBits, refsBest, histogramSymbols, huffmanCodes);
// Keep track of the smallest image so far.
if (isFirstIteration || (bitWriterBest != null && this.bitWriter.NumBytes() < bitWriterBest.NumBytes()))
{
// TODO: This was done in the reference by swapping references, this will be slower
// TODO: This was done in the reference by swapping references, this will be slower.
bitWriterBest = this.bitWriter.Clone();
}
@ -589,7 +602,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
{
this.bitWriter.PutBits(WebpConstants.TransformPresent, 1);
this.bitWriter.PutBits((uint)Vp8LTransformType.SubtractGreen, 2);
LosslessUtils.SubtractGreenFromBlueAndRed(this.Bgra.GetSpan());
LosslessUtils.SubtractGreenFromBlueAndRed(this.EncodedData.GetSpan());
}
private void ApplyPredictFilter(int width, int height, bool usedSubtractGreen)
@ -600,7 +613,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
int transformWidth = LosslessUtils.SubSampleSize(width, predBits);
int transformHeight = LosslessUtils.SubSampleSize(height, predBits);
PredictorEncoder.ResidualImage(width, height, predBits, this.Bgra.GetSpan(), this.BgraScratch.GetSpan(), this.TransformData.GetSpan(), nearLosslessStrength, exact, usedSubtractGreen);
PredictorEncoder.ResidualImage(width, height, predBits, this.EncodedData.GetSpan(), this.BgraScratch.GetSpan(), this.TransformData.GetSpan(), nearLosslessStrength, exact, usedSubtractGreen);
this.bitWriter.PutBits(WebpConstants.TransformPresent, 1);
this.bitWriter.PutBits((uint)Vp8LTransformType.PredictorTransform, 2);
@ -615,7 +628,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
int transformWidth = LosslessUtils.SubSampleSize(width, colorTransformBits);
int transformHeight = LosslessUtils.SubSampleSize(height, colorTransformBits);
PredictorEncoder.ColorSpaceTransform(width, height, colorTransformBits, this.quality, this.Bgra.GetSpan(), this.TransformData.GetSpan());
PredictorEncoder.ColorSpaceTransform(width, height, colorTransformBits, this.quality, this.EncodedData.GetSpan(), this.TransformData.GetSpan());
this.bitWriter.PutBits(WebpConstants.TransformPresent, 1);
this.bitWriter.PutBits((uint)Vp8LTransformType.CrossColorTransform, 2);
@ -1161,9 +1174,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
private void MapImageFromPalette(int width, int height)
{
Span<uint> src = this.Bgra.GetSpan();
Span<uint> src = this.EncodedData.GetSpan();
int srcStride = this.CurrentWidth;
Span<uint> dst = this.Bgra.GetSpan(); // Applying the palette will be done in place.
Span<uint> dst = this.EncodedData.GetSpan(); // Applying the palette will be done in place.
Span<uint> palette = this.Palette.GetSpan();
int paletteSize = this.PaletteSize;
int xBits;
@ -1698,6 +1711,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
public void Dispose()
{
this.Bgra.Dispose();
this.EncodedData.Dispose();
this.BgraScratch.Dispose();
this.Palette.Dispose();
this.TransformData.Dispose();

Loading…
Cancel
Save