diff --git a/src/ImageSharp/Formats/WebP/Lossless/BackwardReferenceEncoder.cs b/src/ImageSharp/Formats/WebP/Lossless/BackwardReferenceEncoder.cs
index 0be198c15d..f08b9f0b16 100644
--- a/src/ImageSharp/Formats/WebP/Lossless/BackwardReferenceEncoder.cs
+++ b/src/ImageSharp/Formats/WebP/Lossless/BackwardReferenceEncoder.cs
@@ -13,28 +13,10 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
///
public const int MaxLengthBits = 12;
- private const int HashBits = 18;
-
- private const int HashSize = 1 << HashBits;
-
- private const uint HashMultiplierHi = 0xc6a4a793u;
-
- private const uint HashMultiplierLo = 0x5bd1e996u;
-
private const float MaxEntropy = 1e30f;
private const int WindowOffsetsSizeMax = 32;
- ///
- /// The number of bits for the window size.
- ///
- private const int WindowSizeBits = 20;
-
- ///
- /// 1M window (4M bytes) minus 120 special codes for short distances.
- ///
- private const int WindowSize = (1 << WindowSizeBits) - 120;
-
///
/// We want the max value to be attainable and stored in MaxLengthBits bits.
///
@@ -46,183 +28,6 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
///
private const int MinLength = 4;
- // TODO: move to Hashchain?
- public static void HashChainFill(Vp8LHashChain p, Span bgra, int quality, int xSize, int ySize)
- {
- int size = xSize * ySize;
- int iterMax = GetMaxItersForQuality(quality);
- int windowSize = GetWindowSizeForHashChain(quality, xSize);
- int pos;
- var hashToFirstIndex = new int[HashSize]; // TODO: use memory allocator
-
- // Initialize hashToFirstIndex array to -1.
- hashToFirstIndex.AsSpan().Fill(-1);
-
- var chain = new int[size]; // TODO: use memory allocator.
-
- // Fill the chain linking pixels with the same hash.
- var bgraComp = bgra[0] == bgra[1];
- for (pos = 0; pos < size - 2;)
- {
- uint hashCode;
- bool bgraCompNext = bgra[pos + 1] == bgra[pos + 2];
- if (bgraComp && bgraCompNext)
- {
- // Consecutive pixels with the same color will share the same hash.
- // We therefore use a different hash: the color and its repetition length.
- var tmp = new uint[2];
- uint len = 1;
- tmp[0] = bgra[pos];
-
- // Figure out how far the pixels are the same. The last pixel has a different 64 bit hash,
- // as its next pixel does not have the same color, so we just need to get to
- // the last pixel equal to its follower.
- while (pos + (int)len + 2 < size && bgra[(int)(pos + len + 2)] == bgra[pos])
- {
- ++len;
- }
-
- if (len > MaxLength)
- {
- // Skip the pixels that match for distance=1 and length>MaxLength
- // because they are linked to their predecessor and we automatically
- // check that in the main for loop below. Skipping means setting no
- // predecessor in the chain, hence -1.
- pos += (int)(len - MaxLength);
- len = MaxLength;
- }
-
- // Process the rest of the hash chain.
- while (len > 0)
- {
- tmp[1] = len--;
- hashCode = GetPixPairHash64(tmp);
- chain[pos] = hashToFirstIndex[hashCode];
- hashToFirstIndex[hashCode] = pos++;
- }
-
- bgraComp = false;
- }
- else
- {
- // Just move one pixel forward.
- hashCode = GetPixPairHash64(bgra.Slice(pos));
- chain[pos] = hashToFirstIndex[hashCode];
- hashToFirstIndex[hashCode] = pos++;
- bgraComp = bgraCompNext;
- }
- }
-
- // Process the penultimate pixel.
- chain[pos] = hashToFirstIndex[GetPixPairHash64(bgra.Slice(pos))];
-
- // 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).
- p.OffsetLength[0] = p.OffsetLength[size - 1] = 0;
- for (int basePosition = size - 2; basePosition > 0;)
- {
- int maxLen = MaxFindCopyLength(size - 1 - basePosition);
- int bgraStart = basePosition;
- int iter = iterMax;
- int bestLength = 0;
- uint bestDistance = 0;
- uint bestBgra;
- int minPos = (basePosition > windowSize) ? basePosition - windowSize : 0;
- int lengthMax = (maxLen < 256) ? maxLen : 256;
- pos = chain[basePosition];
- int currLength;
-
- // Heuristic: use the comparison with the above line as an initialization.
- if (basePosition >= (uint)xSize)
- {
- currLength = FindMatchLength(bgra.Slice(bgraStart - xSize), bgra.Slice(bgraStart), bestLength, maxLen);
- if (currLength > bestLength)
- {
- bestLength = currLength;
- bestDistance = (uint)xSize;
- }
-
- iter--;
- }
-
- // Heuristic: compare to the previous pixel.
- currLength = FindMatchLength(bgra.Slice(bgraStart - 1), bgra.Slice(bgraStart), bestLength, maxLen);
- if (currLength > bestLength)
- {
- bestLength = currLength;
- bestDistance = 1;
- }
-
- iter--;
-
- if (bestLength == MaxLength)
- {
- pos = minPos - 1;
- }
-
- bestBgra = bgra.Slice(bgraStart)[bestLength];
-
- for (; pos >= minPos && (--iter > 0); pos = chain[pos])
- {
- if (bgra[pos + bestLength] != bestBgra)
- {
- continue;
- }
-
- currLength = VectorMismatch(bgra.Slice(pos), bgra.Slice(bgraStart), maxLen);
- if (bestLength < currLength)
- {
- bestLength = currLength;
- bestDistance = (uint)(basePosition - pos);
- bestBgra = bgra.Slice(bgraStart)[bestLength];
-
- // Stop if we have reached a good enough length.
- if (bestLength >= lengthMax)
- {
- break;
- }
- }
- }
-
- // We have the best match but in case the two intervals continue matching
- // to the left, we have the best matches for the left-extended pixels.
- var maxBasePosition = (uint)basePosition;
- while (true)
- {
- p.OffsetLength[basePosition] = (bestDistance << MaxLengthBits) | (uint)bestLength;
- --basePosition;
-
- // Stop if we don't have a match or if we are out of bounds.
- if (bestDistance == 0 || basePosition == 0)
- {
- break;
- }
-
- // Stop if we cannot extend the matching intervals to the left.
- if (basePosition < bestDistance || bgra[(int)(basePosition - bestDistance)] != bgra[basePosition])
- {
- break;
- }
-
- // Stop if we are matching at its limit because there could be a closer
- // matching interval with the same maximum length. Then again, if the
- // matching interval is as close as possible (best_distance == 1), we will
- // never find anything better so let's continue.
- if (bestLength == MaxLength && bestDistance != 1 && basePosition + MaxLength < maxBasePosition)
- {
- break;
- }
-
- if (bestLength < MaxLength)
- {
- bestLength++;
- maxBasePosition = (uint)basePosition;
- }
- }
- }
- }
-
///
/// Evaluates best possible backward references for specified quality. The input cacheBits to 'GetBackwardReferences'
/// sets the maximum cache bits to use (passing 0 implies disabling the local color cache).
@@ -901,9 +706,9 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
int i = 1;
while (i < pixelCount)
{
- int maxLen = MaxFindCopyLength(pixelCount - i);
- int rleLen = FindMatchLength(bgra.Slice(i), bgra.Slice(i - 1), 0, maxLen);
- int prevRowLen = (i < xSize) ? 0 : FindMatchLength(bgra.Slice(i), bgra.Slice(i - xSize), 0, maxLen);
+ int maxLen = LosslessUtils.MaxFindCopyLength(pixelCount - i);
+ int rleLen = LosslessUtils.FindMatchLength(bgra.Slice(i), bgra.Slice(i - 1), 0, maxLen);
+ int prevRowLen = (i < xSize) ? 0 : LosslessUtils.FindMatchLength(bgra.Slice(i), bgra.Slice(i - xSize), 0, maxLen);
if (rleLen >= prevRowLen && rleLen >= MinLength)
{
refs.Add(PixOrCopy.CreateCopy(1, (ushort)rleLen));
@@ -931,11 +736,6 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
i++;
}
}
-
- if (useColorCache)
- {
- // TODO: VP8LColorCacheClear()?
- }
}
///
@@ -1033,75 +833,5 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
return dist + 120;
}
-
- ///
- /// Returns the exact index where array1 and array2 are different. For an index
- /// inferior or equal to bestLenMatch, the return value just has to be strictly
- /// inferior to best_lenMatch. The current behavior is to return 0 if this index
- /// is bestLenMatch, and the index itself otherwise.
- /// If no two elements are the same, it returns maxLimit.
- ///
- private static int FindMatchLength(Span array1, Span array2, int bestLenMatch, int maxLimit)
- {
- // Before 'expensive' linear match, check if the two arrays match at the
- // current best length index.
- if (array1[bestLenMatch] != array2[bestLenMatch])
- {
- return 0;
- }
-
- return VectorMismatch(array1, array2, maxLimit);
- }
-
- private static int VectorMismatch(Span array1, Span array2, int length)
- {
- int matchLen = 0;
-
- while (matchLen < length && array1[matchLen] == array2[matchLen])
- {
- matchLen++;
- }
-
- return matchLen;
- }
-
- ///
- /// Calculates the hash for a pixel pair.
- ///
- /// An Span with two pixels.
- /// The hash.
- private static uint GetPixPairHash64(Span bgra)
- {
- uint key = bgra[1] * HashMultiplierHi;
- key += bgra[0] * HashMultiplierLo;
- key = key >> (32 - HashBits);
- return key;
- }
-
- ///
- /// Returns the maximum number of hash chain lookups to do for a
- /// given compression quality. Return value in range [8, 86].
- ///
- /// The quality.
- /// Number of hash chain lookups.
- private static int GetMaxItersForQuality(int quality)
- {
- return 8 + (quality * quality / 128);
- }
-
- private static int MaxFindCopyLength(int len)
- {
- return (len < MaxLength) ? len : MaxLength;
- }
-
- private static int GetWindowSizeForHashChain(int quality, int xSize)
- {
- int maxWindowSize = (quality > 75) ? WindowSize
- : (quality > 50) ? (xSize << 8)
- : (quality > 25) ? (xSize << 6)
- : (xSize << 4);
-
- return (maxWindowSize > WindowSize) ? WindowSize : maxWindowSize;
- }
}
}
diff --git a/src/ImageSharp/Formats/WebP/Lossless/CrunchConfig.cs b/src/ImageSharp/Formats/WebP/Lossless/CrunchConfig.cs
new file mode 100644
index 0000000000..02b0dcf713
--- /dev/null
+++ b/src/ImageSharp/Formats/WebP/Lossless/CrunchConfig.cs
@@ -0,0 +1,14 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using System.Collections.Generic;
+
+namespace SixLabors.ImageSharp.Formats.WebP.Lossless
+{
+ internal class CrunchConfig
+ {
+ public EntropyIx EntropyIdx { get; set; }
+
+ public List SubConfigs { get; } = new List();
+ }
+}
diff --git a/src/ImageSharp/Formats/WebP/Lossless/CrunchSubConfig.cs b/src/ImageSharp/Formats/WebP/Lossless/CrunchSubConfig.cs
new file mode 100644
index 0000000000..04e2d511bc
--- /dev/null
+++ b/src/ImageSharp/Formats/WebP/Lossless/CrunchSubConfig.cs
@@ -0,0 +1,12 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Formats.WebP.Lossless
+{
+ internal class CrunchSubConfig
+ {
+ public int Lz77 { get; set; }
+
+ public bool DoNotCache { get; set; }
+ }
+}
diff --git a/src/ImageSharp/Formats/WebP/Lossless/LosslessUtils.cs b/src/ImageSharp/Formats/WebP/Lossless/LosslessUtils.cs
index 16b5bce069..7c2cd46124 100644
--- a/src/ImageSharp/Formats/WebP/Lossless/LosslessUtils.cs
+++ b/src/ImageSharp/Formats/WebP/Lossless/LosslessUtils.cs
@@ -26,6 +26,44 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
private const double Log2Reciprocal = 1.44269504088896338700465094007086;
+ ///
+ /// Returns the exact index where array1 and array2 are different. For an index
+ /// inferior or equal to bestLenMatch, the return value just has to be strictly
+ /// inferior to best_lenMatch. The current behavior is to return 0 if this index
+ /// is bestLenMatch, and the index itself otherwise.
+ /// If no two elements are the same, it returns maxLimit.
+ ///
+ public static int FindMatchLength(Span array1, Span array2, int bestLenMatch, int maxLimit)
+ {
+ // Before 'expensive' linear match, check if the two arrays match at the
+ // current best length index.
+ if (array1[bestLenMatch] != array2[bestLenMatch])
+ {
+ return 0;
+ }
+
+ return VectorMismatch(array1, array2, maxLimit);
+ }
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public static int VectorMismatch(Span array1, Span array2, int length)
+ {
+ int matchLen = 0;
+
+ while (matchLen < length && array1[matchLen] == array2[matchLen])
+ {
+ matchLen++;
+ }
+
+ return matchLen;
+ }
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public static int MaxFindCopyLength(int len)
+ {
+ return (len < BackwardReferenceEncoder.MaxLength) ? len : BackwardReferenceEncoder.MaxLength;
+ }
+
public static int PrefixEncodeBits(int distance, ref int extraBits)
{
if (distance < PrefixLookupIdxMax)
diff --git a/src/ImageSharp/Formats/WebP/Lossless/Vp8LEncoder.cs b/src/ImageSharp/Formats/WebP/Lossless/Vp8LEncoder.cs
index 078710486f..a41d7295da 100644
--- a/src/ImageSharp/Formats/WebP/Lossless/Vp8LEncoder.cs
+++ b/src/ImageSharp/Formats/WebP/Lossless/Vp8LEncoder.cs
@@ -3,7 +3,6 @@
using System;
using System.Buffers;
-using System.Buffers.Binary;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -18,7 +17,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
///
/// Encoder for lossless webp images.
///
- internal class Vp8LEncoder : IDisposable
+ internal partial class Vp8LEncoder : IDisposable
{
///
/// Maximum number of reference blocks the image will be segmented into.
@@ -420,7 +419,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
}
// Calculate backward references from BGRA image.
- BackwardReferenceEncoder.HashChainFill(hashChain, bgra, this.quality, width, height);
+ hashChain.Fill(this.memoryAllocator, bgra, this.quality, width, height);
Vp8LBitWriter bitWriterBest = config.SubConfigs.Count > 1 ? this.bitWriter.Clone() : this.bitWriter;
Vp8LBitWriter bwInit = this.bitWriter;
@@ -618,7 +617,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
}
// Calculate backward references from the image pixels.
- BackwardReferenceEncoder.HashChainFill(hashChain, bgra, quality, width, height);
+ hashChain.Fill(this.memoryAllocator, bgra, quality, width, height);
Vp8LBackwardRefs refs = BackwardReferenceEncoder.GetBackwardReferences(
width,
@@ -1712,21 +1711,5 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
this.Palette.Dispose();
this.TransformData.Dispose();
}
-
- // TODO : Not a fan of private classes
- private class CrunchConfig
- {
- public EntropyIx EntropyIdx { get; set; }
-
- public List SubConfigs { get; } = new List();
- }
-
- // TODO : Not a fan of private classes
- private class CrunchSubConfig
- {
- public int Lz77 { get; set; }
-
- public bool DoNotCache { get; set; }
- }
}
}
diff --git a/src/ImageSharp/Formats/WebP/Lossless/Vp8LHashChain.cs b/src/ImageSharp/Formats/WebP/Lossless/Vp8LHashChain.cs
index 0262ac332f..54a711c384 100644
--- a/src/ImageSharp/Formats/WebP/Lossless/Vp8LHashChain.cs
+++ b/src/ImageSharp/Formats/WebP/Lossless/Vp8LHashChain.cs
@@ -2,11 +2,32 @@
// Licensed under the Apache License, Version 2.0.
using System;
+using System.Buffers;
+using System.Runtime.CompilerServices;
+using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.WebP.Lossless
{
internal class Vp8LHashChain
{
+ private const uint HashMultiplierHi = 0xc6a4a793u;
+
+ private const uint HashMultiplierLo = 0x5bd1e996u;
+
+ private const int HashBits = 18;
+
+ private const int HashSize = 1 << HashBits;
+
+ ///
+ /// The number of bits for the window size.
+ ///
+ private const int WindowSizeBits = 20;
+
+ ///
+ /// 1M window (4M bytes) minus 120 special codes for short distances.
+ ///
+ private const int WindowSize = (1 << WindowSizeBits) - 120;
+
///
/// Initializes a new instance of the class.
///
@@ -33,14 +54,229 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
///
public int Size { get; }
+ public void Fill(MemoryAllocator memoryAllocator, Span bgra, int quality, int xSize, int ySize)
+ {
+ int size = xSize * ySize;
+ int iterMax = GetMaxItersForQuality(quality);
+ int windowSize = GetWindowSizeForHashChain(quality, xSize);
+ int pos;
+ using IMemoryOwner hashToFirstIndexBuffer = memoryAllocator.Allocate(HashSize);
+ Span hashToFirstIndex = hashToFirstIndexBuffer.GetSpan();
+
+ // Initialize hashToFirstIndex array to -1.
+ hashToFirstIndex.Fill(-1);
+
+ var chain = new int[size];
+
+ // Fill the chain linking pixels with the same hash.
+ var bgraComp = bgra[0] == bgra[1];
+ for (pos = 0; pos < size - 2;)
+ {
+ uint hashCode;
+ bool bgraCompNext = bgra[pos + 1] == bgra[pos + 2];
+ if (bgraComp && bgraCompNext)
+ {
+ // Consecutive pixels with the same color will share the same hash.
+ // We therefore use a different hash: the color and its repetition length.
+ var tmp = new uint[2];
+ uint len = 1;
+ tmp[0] = bgra[pos];
+
+ // Figure out how far the pixels are the same. The last pixel has a different 64 bit hash,
+ // as its next pixel does not have the same color, so we just need to get to
+ // the last pixel equal to its follower.
+ while (pos + (int)len + 2 < size && bgra[(int)(pos + len + 2)] == bgra[pos])
+ {
+ ++len;
+ }
+
+ if (len > BackwardReferenceEncoder.MaxLength)
+ {
+ // Skip the pixels that match for distance=1 and length>MaxLength
+ // because they are linked to their predecessor and we automatically
+ // check that in the main for loop below. Skipping means setting no
+ // predecessor in the chain, hence -1.
+ pos += (int)(len - BackwardReferenceEncoder.MaxLength);
+ len = BackwardReferenceEncoder.MaxLength;
+ }
+
+ // Process the rest of the hash chain.
+ while (len > 0)
+ {
+ tmp[1] = len--;
+ hashCode = GetPixPairHash64(tmp);
+ chain[pos] = hashToFirstIndex[(int)hashCode];
+ hashToFirstIndex[(int)hashCode] = pos++;
+ }
+
+ bgraComp = false;
+ }
+ else
+ {
+ // Just move one pixel forward.
+ hashCode = GetPixPairHash64(bgra.Slice(pos));
+ chain[pos] = hashToFirstIndex[(int)hashCode];
+ hashToFirstIndex[(int)hashCode] = pos++;
+ bgraComp = bgraCompNext;
+ }
+ }
+
+ // Process the penultimate pixel.
+ chain[pos] = hashToFirstIndex[(int)GetPixPairHash64(bgra.Slice(pos))];
+
+ // 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;
+ for (int basePosition = size - 2; basePosition > 0;)
+ {
+ int maxLen = LosslessUtils.MaxFindCopyLength(size - 1 - basePosition);
+ int bgraStart = basePosition;
+ int iter = iterMax;
+ int bestLength = 0;
+ uint bestDistance = 0;
+ int minPos = (basePosition > windowSize) ? basePosition - windowSize : 0;
+ int lengthMax = (maxLen < 256) ? maxLen : 256;
+ pos = chain[basePosition];
+ int currLength;
+
+ // Heuristic: use the comparison with the above line as an initialization.
+ if (basePosition >= (uint)xSize)
+ {
+ currLength = LosslessUtils.FindMatchLength(bgra.Slice(bgraStart - xSize), bgra.Slice(bgraStart), bestLength, maxLen);
+ if (currLength > bestLength)
+ {
+ bestLength = currLength;
+ bestDistance = (uint)xSize;
+ }
+
+ iter--;
+ }
+
+ // Heuristic: compare to the previous pixel.
+ currLength = LosslessUtils.FindMatchLength(bgra.Slice(bgraStart - 1), bgra.Slice(bgraStart), bestLength, maxLen);
+ if (currLength > bestLength)
+ {
+ bestLength = currLength;
+ bestDistance = 1;
+ }
+
+ iter--;
+
+ if (bestLength == BackwardReferenceEncoder.MaxLength)
+ {
+ pos = minPos - 1;
+ }
+
+ var bestBgra = bgra.Slice(bgraStart)[bestLength];
+
+ for (; pos >= minPos && (--iter > 0); pos = chain[pos])
+ {
+ if (bgra[pos + bestLength] != bestBgra)
+ {
+ continue;
+ }
+
+ currLength = LosslessUtils.VectorMismatch(bgra.Slice(pos), bgra.Slice(bgraStart), maxLen);
+ if (bestLength < currLength)
+ {
+ bestLength = currLength;
+ bestDistance = (uint)(basePosition - pos);
+ bestBgra = bgra.Slice(bgraStart)[bestLength];
+
+ // Stop if we have reached a good enough length.
+ if (bestLength >= lengthMax)
+ {
+ break;
+ }
+ }
+ }
+
+ // We have the best match but in case the two intervals continue matching
+ // to the left, we have the best matches for the left-extended pixels.
+ var maxBasePosition = (uint)basePosition;
+ while (true)
+ {
+ this.OffsetLength[basePosition] = (bestDistance << BackwardReferenceEncoder.MaxLengthBits) | (uint)bestLength;
+ --basePosition;
+
+ // Stop if we don't have a match or if we are out of bounds.
+ if (bestDistance == 0 || basePosition == 0)
+ {
+ break;
+ }
+
+ // Stop if we cannot extend the matching intervals to the left.
+ if (basePosition < bestDistance || bgra[(int)(basePosition - bestDistance)] != bgra[basePosition])
+ {
+ break;
+ }
+
+ // Stop if we are matching at its limit because there could be a closer
+ // matching interval with the same maximum length. Then again, if the
+ // matching interval is as close as possible (best_distance == 1), we will
+ // never find anything better so let's continue.
+ if (bestLength == BackwardReferenceEncoder.MaxLength && bestDistance != 1 && basePosition + BackwardReferenceEncoder.MaxLength < maxBasePosition)
+ {
+ break;
+ }
+
+ if (bestLength < BackwardReferenceEncoder.MaxLength)
+ {
+ bestLength++;
+ maxBasePosition = (uint)basePosition;
+ }
+ }
+ }
+ }
+
+ [MethodImpl(InliningOptions.ShortMethod)]
public int FindLength(int basePosition)
{
return (int)(this.OffsetLength[basePosition] & ((1U << BackwardReferenceEncoder.MaxLengthBits) - 1));
}
+ [MethodImpl(InliningOptions.ShortMethod)]
public int FindOffset(int basePosition)
{
return (int)(this.OffsetLength[basePosition] >> BackwardReferenceEncoder.MaxLengthBits);
}
+
+ ///
+ /// Calculates the hash for a pixel pair.
+ ///
+ /// An Span with two pixels.
+ /// The hash.
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static uint GetPixPairHash64(Span bgra)
+ {
+ uint key = bgra[1] * HashMultiplierHi;
+ key += bgra[0] * HashMultiplierLo;
+ key = key >> (32 - HashBits);
+ return key;
+ }
+
+ ///
+ /// Returns the maximum number of hash chain lookups to do for a
+ /// given compression quality. Return value in range [8, 86].
+ ///
+ /// The quality.
+ /// Number of hash chain lookups.
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static int GetMaxItersForQuality(int quality)
+ {
+ return 8 + (quality * quality / 128);
+ }
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static int GetWindowSizeForHashChain(int quality, int xSize)
+ {
+ int maxWindowSize = (quality > 75) ? WindowSize
+ : (quality > 50) ? (xSize << 8)
+ : (quality > 25) ? (xSize << 6)
+ : (xSize << 4);
+
+ return (maxWindowSize > WindowSize) ? WindowSize : maxWindowSize;
+ }
}
}
diff --git a/src/ImageSharp/Formats/WebP/Lossy/LossyUtils.cs b/src/ImageSharp/Formats/WebP/Lossy/LossyUtils.cs
index 53360a981d..c06d933586 100644
--- a/src/ImageSharp/Formats/WebP/Lossy/LossyUtils.cs
+++ b/src/ImageSharp/Formats/WebP/Lossy/LossyUtils.cs
@@ -10,15 +10,6 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
{
internal static class LossyUtils
{
- [MethodImpl(InliningOptions.ShortMethod)]
- private static void Put16(int v, Span dst)
- {
- for (int j = 0; j < 16; ++j)
- {
- Memset(dst.Slice(j * WebPConstants.Bps), (byte)v, 0, 16);
- }
- }
-
public static void DC16(Span dst, Span yuv, int offset)
{
int offsetMinus1 = offset - 1;
@@ -600,27 +591,6 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
}
}
- private static void TrueMotion(Span dst, Span yuv, int offset, int size)
- {
- // For information about how true motion works, see rfc6386, page 52. ff and section 20.14.
- int topOffset = offset - WebPConstants.Bps;
- Span top = yuv.Slice(topOffset);
- byte p = yuv[topOffset - 1];
- int leftOffset = offset - 1;
- byte left = yuv[leftOffset];
- for (int y = 0; y < size; ++y)
- {
- for (int x = 0; x < size; ++x)
- {
- dst[x] = (byte)Clamp255(left + top[x] - p);
- }
-
- leftOffset += WebPConstants.Bps;
- left = yuv[leftOffset];
- dst = dst.Slice(WebPConstants.Bps);
- }
- }
-
// Simple In-loop filtering (Paragraph 15.2)
public static void SimpleVFilter16(Span p, int offset, int stride, int thresh)
{
@@ -790,6 +760,36 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
return bit == 0 ? WebPLookupTables.Vp8EntropyCost[proba] : WebPLookupTables.Vp8EntropyCost[255 - proba];
}
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void Put16(int v, Span dst)
+ {
+ for (int j = 0; j < 16; ++j)
+ {
+ Memset(dst.Slice(j * WebPConstants.Bps), (byte)v, 0, 16);
+ }
+ }
+
+ private static void TrueMotion(Span dst, Span yuv, int offset, int size)
+ {
+ // For information about how true motion works, see rfc6386, page 52. ff and section 20.14.
+ int topOffset = offset - WebPConstants.Bps;
+ Span top = yuv.Slice(topOffset);
+ byte p = yuv[topOffset - 1];
+ int leftOffset = offset - 1;
+ byte left = yuv[leftOffset];
+ for (int y = 0; y < size; ++y)
+ {
+ for (int x = 0; x < size; ++x)
+ {
+ dst[x] = (byte)Clamp255(left + top[x] - p);
+ }
+
+ leftOffset += WebPConstants.Bps;
+ left = yuv[leftOffset];
+ dst = dst.Slice(WebPConstants.Bps);
+ }
+ }
+
// Complex In-loop filtering (Paragraph 15.3)
private static void FilterLoop24(
Span p,