Browse Source

Move hashchain fill to Vp8LHashChain, use memory allocator

pull/1552/head
Brian Popow 6 years ago
parent
commit
75cdb2d1d5
  1. 276
      src/ImageSharp/Formats/WebP/Lossless/BackwardReferenceEncoder.cs
  2. 14
      src/ImageSharp/Formats/WebP/Lossless/CrunchConfig.cs
  3. 12
      src/ImageSharp/Formats/WebP/Lossless/CrunchSubConfig.cs
  4. 38
      src/ImageSharp/Formats/WebP/Lossless/LosslessUtils.cs
  5. 23
      src/ImageSharp/Formats/WebP/Lossless/Vp8LEncoder.cs
  6. 236
      src/ImageSharp/Formats/WebP/Lossless/Vp8LHashChain.cs
  7. 60
      src/ImageSharp/Formats/WebP/Lossy/LossyUtils.cs

276
src/ImageSharp/Formats/WebP/Lossless/BackwardReferenceEncoder.cs

@ -13,28 +13,10 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
/// </summary>
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;
/// <summary>
/// The number of bits for the window size.
/// </summary>
private const int WindowSizeBits = 20;
/// <summary>
/// 1M window (4M bytes) minus 120 special codes for short distances.
/// </summary>
private const int WindowSize = (1 << WindowSizeBits) - 120;
/// <summary>
/// We want the max value to be attainable and stored in MaxLengthBits bits.
/// </summary>
@ -46,183 +28,6 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
/// </summary>
private const int MinLength = 4;
// TODO: move to Hashchain?
public static void HashChainFill(Vp8LHashChain p, Span<uint> 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;
}
}
}
}
/// <summary>
/// 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()?
}
}
/// <summary>
@ -1033,75 +833,5 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
return dist + 120;
}
/// <summary>
/// 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.
/// </summary>
private static int FindMatchLength(Span<uint> array1, Span<uint> 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<uint> array1, Span<uint> array2, int length)
{
int matchLen = 0;
while (matchLen < length && array1[matchLen] == array2[matchLen])
{
matchLen++;
}
return matchLen;
}
/// <summary>
/// Calculates the hash for a pixel pair.
/// </summary>
/// <param name="bgra">An Span with two pixels.</param>
/// <returns>The hash.</returns>
private static uint GetPixPairHash64(Span<uint> bgra)
{
uint key = bgra[1] * HashMultiplierHi;
key += bgra[0] * HashMultiplierLo;
key = key >> (32 - HashBits);
return key;
}
/// <summary>
/// Returns the maximum number of hash chain lookups to do for a
/// given compression quality. Return value in range [8, 86].
/// </summary>
/// <param name="quality">The quality.</param>
/// <returns>Number of hash chain lookups.</returns>
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;
}
}
}

14
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<CrunchSubConfig> SubConfigs { get; } = new List<CrunchSubConfig>();
}
}

12
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; }
}
}

38
src/ImageSharp/Formats/WebP/Lossless/LosslessUtils.cs

@ -26,6 +26,44 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
private const double Log2Reciprocal = 1.44269504088896338700465094007086;
/// <summary>
/// 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.
/// </summary>
public static int FindMatchLength(Span<uint> array1, Span<uint> 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<uint> array1, Span<uint> 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)

23
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
/// <summary>
/// Encoder for lossless webp images.
/// </summary>
internal class Vp8LEncoder : IDisposable
internal partial class Vp8LEncoder : IDisposable
{
/// <summary>
/// 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<CrunchSubConfig> SubConfigs { get; } = new List<CrunchSubConfig>();
}
// TODO : Not a fan of private classes
private class CrunchSubConfig
{
public int Lz77 { get; set; }
public bool DoNotCache { get; set; }
}
}
}

236
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;
/// <summary>
/// The number of bits for the window size.
/// </summary>
private const int WindowSizeBits = 20;
/// <summary>
/// 1M window (4M bytes) minus 120 special codes for short distances.
/// </summary>
private const int WindowSize = (1 << WindowSizeBits) - 120;
/// <summary>
/// Initializes a new instance of the <see cref="Vp8LHashChain"/> class.
/// </summary>
@ -33,14 +54,229 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
/// </summary>
public int Size { get; }
public void Fill(MemoryAllocator memoryAllocator, Span<uint> bgra, int quality, int xSize, int ySize)
{
int size = xSize * ySize;
int iterMax = GetMaxItersForQuality(quality);
int windowSize = GetWindowSizeForHashChain(quality, xSize);
int pos;
using IMemoryOwner<int> hashToFirstIndexBuffer = memoryAllocator.Allocate<int>(HashSize);
Span<int> 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);
}
/// <summary>
/// Calculates the hash for a pixel pair.
/// </summary>
/// <param name="bgra">An Span with two pixels.</param>
/// <returns>The hash.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
private static uint GetPixPairHash64(Span<uint> bgra)
{
uint key = bgra[1] * HashMultiplierHi;
key += bgra[0] * HashMultiplierLo;
key = key >> (32 - HashBits);
return key;
}
/// <summary>
/// Returns the maximum number of hash chain lookups to do for a
/// given compression quality. Return value in range [8, 86].
/// </summary>
/// <param name="quality">The quality.</param>
/// <returns>Number of hash chain lookups.</returns>
[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;
}
}
}

60
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<byte> dst)
{
for (int j = 0; j < 16; ++j)
{
Memset(dst.Slice(j * WebPConstants.Bps), (byte)v, 0, 16);
}
}
public static void DC16(Span<byte> dst, Span<byte> yuv, int offset)
{
int offsetMinus1 = offset - 1;
@ -600,27 +591,6 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy
}
}
private static void TrueMotion(Span<byte> dst, Span<byte> 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<byte> 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<byte> 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<byte> dst)
{
for (int j = 0; j < 16; ++j)
{
Memset(dst.Slice(j * WebPConstants.Bps), (byte)v, 0, 16);
}
}
private static void TrueMotion(Span<byte> dst, Span<byte> 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<byte> 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<byte> p,

Loading…
Cancel
Save