Browse Source

Change hashchain to use the memoryAllocator

pull/1846/head
Brian Popow 5 years ago
parent
commit
3de317f6d5
  1. 15
      src/ImageSharp/Formats/Webp/Lossless/BackwardReferenceEncoder.cs
  2. 9
      src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs
  3. 59
      src/ImageSharp/Formats/Webp/Lossless/Vp8LHashChain.cs

15
src/ImageSharp/Formats/Webp/Lossless/BackwardReferenceEncoder.cs

@ -3,10 +3,11 @@
using System;
using System.Collections.Generic;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Webp.Lossless
{
internal class BackwardReferenceEncoder
internal static class BackwardReferenceEncoder
{
/// <summary>
/// Maximum bit length.
@ -41,6 +42,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
int quality,
int lz77TypesToTry,
ref int cacheBits,
MemoryAllocator memoryAllocator,
Vp8LHashChain hashChain,
Vp8LBackwardRefs best,
Vp8LBackwardRefs worst)
@ -69,7 +71,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
BackwardReferencesLz77(width, height, bgra, 0, hashChain, worst);
break;
case Vp8LLz77Type.Lz77Box:
hashChainBox = new Vp8LHashChain(width * height);
hashChainBox = new Vp8LHashChain(memoryAllocator, width * height);
BackwardReferencesLz77Box(width, height, bgra, 0, hashChain, hashChainBox, worst);
break;
}
@ -617,7 +619,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
}
}
hashChain.OffsetLength[0] = 0;
Span<uint> hashChainOffsetLength = hashChain.OffsetLength.GetSpan();
hashChainOffsetLength[0] = 0;
for (i = 1; i < pixelCount; i++)
{
int ind;
@ -695,19 +698,19 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
if (bestLength <= MinLength)
{
hashChain.OffsetLength[i] = 0;
hashChainOffsetLength[i] = 0;
bestOffsetPrev = 0;
bestLengthPrev = 0;
}
else
{
hashChain.OffsetLength[i] = (uint)((bestOffset << MaxLengthBits) | bestLength);
hashChainOffsetLength[i] = (uint)((bestOffset << MaxLengthBits) | bestLength);
bestOffsetPrev = bestOffset;
bestLengthPrev = bestLength;
}
}
hashChain.OffsetLength[0] = 0;
hashChainOffsetLength[0] = 0;
BackwardReferencesLz77(xSize, ySize, bgra, cacheBits, hashChain, refs);
}

9
src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs

@ -124,7 +124,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
this.EncodedData = memoryAllocator.Allocate<uint>(pixelCount);
this.Palette = memoryAllocator.Allocate<uint>(WebpConstants.MaxPaletteSize);
this.Refs = new Vp8LBackwardRefs[3];
this.HashChain = new Vp8LHashChain(pixelCount);
this.HashChain = new Vp8LHashChain(memoryAllocator, pixelCount);
// We round the block size up, so we're guaranteed to have at most MaxRefsBlockPerImage blocks used:
int refsBlockSize = ((pixelCount - 1) / MaxRefsBlockPerImage) + 1;
@ -515,7 +515,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
}
// Calculate backward references from BGRA image.
this.HashChain.Fill(this.memoryAllocator, bgra, this.quality, width, height, lowEffort);
this.HashChain.Fill(bgra, this.quality, width, height, lowEffort);
Vp8LBitWriter bitWriterBest = config.SubConfigs.Count > 1 ? this.bitWriter.Clone() : this.bitWriter;
Vp8LBitWriter bwInit = this.bitWriter;
@ -529,6 +529,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
this.quality,
subConfig.Lz77,
ref cacheBits,
this.memoryAllocator,
this.HashChain,
this.Refs[0],
this.Refs[1]);
@ -735,7 +736,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
}
// Calculate backward references from the image pixels.
hashChain.Fill(this.memoryAllocator, bgra, quality, width, height, lowEffort);
hashChain.Fill(bgra, quality, width, height, lowEffort);
Vp8LBackwardRefs refs = BackwardReferenceEncoder.GetBackwardReferences(
width,
@ -744,6 +745,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
quality,
(int)Vp8LLz77Type.Lz77Standard | (int)Vp8LLz77Type.Lz77Rle,
ref cacheBits,
this.memoryAllocator,
hashChain,
refsTmp1,
refsTmp2);
@ -1802,6 +1804,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
this.BgraScratch.Dispose();
this.Palette.Dispose();
this.TransformData.Dispose();
this.HashChain.Dispose();
}
}
}

59
src/ImageSharp/Formats/Webp/Lossless/Vp8LHashChain.cs

@ -8,7 +8,7 @@ using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Webp.Lossless
{
internal class Vp8LHashChain
internal class Vp8LHashChain : IDisposable
{
private const uint HashMultiplierHi = 0xc6a4a793u;
@ -28,14 +28,19 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
/// </summary>
private const int WindowSize = (1 << WindowSizeBits) - 120;
private readonly MemoryAllocator memoryAllocator;
private bool disposed;
/// <summary>
/// Initializes a new instance of the <see cref="Vp8LHashChain"/> class.
/// </summary>
/// <param name="memoryAllocator">The memory allocator.</param>
/// <param name="size">The size off the chain.</param>
public Vp8LHashChain(int size)
public Vp8LHashChain(MemoryAllocator memoryAllocator, int size)
{
this.OffsetLength = new uint[size];
this.OffsetLength.AsSpan().Fill(0xcdcdcdcd);
this.memoryAllocator = memoryAllocator;
this.OffsetLength = this.memoryAllocator.Allocate<uint>(size, AllocationOptions.Clean);
this.Size = size;
}
@ -45,16 +50,16 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
/// These 20 bits are the limit defined by GetWindowSizeForHashChain (through WindowSize = 1 &lt;&lt; 20).
/// The lower 12 bits contain the length of the match.
/// </summary>
public uint[] OffsetLength { get; }
public IMemoryOwner<uint> OffsetLength { get; }
/// <summary>
/// Gets the size of the hash chain.
/// This is the maximum size of the hash_chain that can be constructed.
/// This is the maximum size of the hashchain that can be constructed.
/// Typically this is the pixel count (width x height) for a given image.
/// </summary>
public int Size { get; }
public void Fill(MemoryAllocator memoryAllocator, ReadOnlySpan<uint> bgra, int quality, int xSize, int ySize, bool lowEffort)
public void Fill(ReadOnlySpan<uint> bgra, int quality, int xSize, int ySize, bool lowEffort)
{
int size = xSize * ySize;
int iterMax = GetMaxItersForQuality(quality);
@ -63,20 +68,21 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
if (size <= 2)
{
this.OffsetLength[0] = 0;
this.OffsetLength.GetSpan()[0] = 0;
return;
}
using IMemoryOwner<int> hashToFirstIndexBuffer = memoryAllocator.Allocate<int>(HashSize);
using IMemoryOwner<int> hashToFirstIndexBuffer = this.memoryAllocator.Allocate<int>(HashSize);
using IMemoryOwner<int> chainBuffer = this.memoryAllocator.Allocate<int>(size, AllocationOptions.Clean);
Span<int> hashToFirstIndex = hashToFirstIndexBuffer.GetSpan();
Span<int> chain = chainBuffer.GetSpan();
// Initialize hashToFirstIndex array to -1.
hashToFirstIndex.Fill(-1);
int[] chain = new int[size];
// Fill the chain linking pixels with the same hash.
bool bgraComp = bgra.Length > 1 && bgra[0] == bgra[1];
Span<uint> tmp = stackalloc uint[2];
for (pos = 0; pos < size - 2;)
{
uint hashCode;
@ -85,7 +91,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
{
// Consecutive pixels with the same color will share the same hash.
// We therefore use a different hash: the color and its repetition length.
uint[] tmp = new uint[2];
tmp.Clear();
uint len = 1;
tmp[0] = bgra[pos];
@ -134,7 +140,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
// Find the best match interval at each pixel, defined by an offset to the
// pixel and a length. The right-most pixel cannot match anything to the right
// (hence a best length of 0) and the left-most pixel nothing to the left (hence an offset of 0).
this.OffsetLength[0] = this.OffsetLength[size - 1] = 0;
Span<uint> offsetLength = this.OffsetLength.GetSpan();
offsetLength[0] = offsetLength[size - 1] = 0;
for (int basePosition = size - 2; basePosition > 0;)
{
int maxLen = LosslessUtils.MaxFindCopyLength(size - 1 - basePosition);
@ -208,7 +215,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
uint maxBasePosition = (uint)basePosition;
while (true)
{
this.OffsetLength[basePosition] = (bestDistance << BackwardReferenceEncoder.MaxLengthBits) | (uint)bestLength;
offsetLength[basePosition] = (bestDistance << BackwardReferenceEncoder.MaxLengthBits) | (uint)bestLength;
--basePosition;
// Stop if we don't have a match or if we are out of bounds.
@ -242,10 +249,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
}
[MethodImpl(InliningOptions.ShortMethod)]
public int FindLength(int basePosition) => (int)(this.OffsetLength[basePosition] & ((1U << BackwardReferenceEncoder.MaxLengthBits) - 1));
public int FindLength(int basePosition) => (int)(this.OffsetLength.GetSpan()[basePosition] & ((1U << BackwardReferenceEncoder.MaxLengthBits) - 1));
[MethodImpl(InliningOptions.ShortMethod)]
public int FindOffset(int basePosition) => (int)(this.OffsetLength[basePosition] >> BackwardReferenceEncoder.MaxLengthBits);
public int FindOffset(int basePosition) => (int)(this.OffsetLength.GetSpan()[basePosition] >> BackwardReferenceEncoder.MaxLengthBits);
/// <summary>
/// Calculates the hash for a pixel pair.
@ -280,5 +287,25 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
return maxWindowSize > WindowSize ? WindowSize : maxWindowSize;
}
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
this.OffsetLength.Dispose();
}
this.disposed = true;
}
}
/// <inheritdoc />
public void Dispose()
{
this.Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
}

Loading…
Cancel
Save