Browse Source

Merge remote-tracking branch 'origin/js/webp-hack' into webp

pull/1552/head
Brian Popow 6 years ago
parent
commit
d9fc4f46ee
  1. 32
      src/ImageSharp/Formats/WebP/Lossless/BackwardReferenceEncoder.cs
  2. 476
      src/ImageSharp/Formats/WebP/Lossless/LosslessUtils.cs
  3. 410
      src/ImageSharp/Formats/WebP/Lossless/PredictorEncoder.cs
  4. 3
      src/ImageSharp/Formats/WebP/Lossless/Vp8LEncoder.cs

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

@ -340,6 +340,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
colorCache[i].Init(i); colorCache[i].Init(i);
} }
// TODO: Don't use the enumerator here.
// Find the cache_bits giving the lowest entropy. // Find the cache_bits giving the lowest entropy.
using List<PixOrCopy>.Enumerator c = refs.Refs.GetEnumerator(); using List<PixOrCopy>.Enumerator c = refs.Refs.GetEnumerator();
while (c.MoveNext()) while (c.MoveNext())
@ -363,7 +364,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
++histos[0].Alpha[a]; ++histos[0].Alpha[a];
// Deal with cacheBits > 0. // Deal with cacheBits > 0.
for (int i = cacheBitsMax; i >= 1; i--, key >>= 1) for (int i = cacheBitsMax; i >= 1; --i, key >>= 1)
{ {
if (colorCache[i].Lookup(key) == pix) if (colorCache[i].Lookup(key) == pix)
{ {
@ -388,6 +389,12 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
int len = v.Len; int len = v.Len;
uint bgraPrev = bgra[pos] ^ 0xffffffffu; uint bgraPrev = bgra[pos] ^ 0xffffffffu;
// TODO: Original has this loop?
// VP8LPrefixEncode(len, &code, &extra_bits, &extra_bits_value);
// for (i = 0; i <= cache_bits_max; ++i)
// {
// ++histos[i]->literal_[NUM_LITERAL_CODES + code];
// }
// Update the color caches. // Update the color caches.
do do
{ {
@ -409,7 +416,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
} }
} }
for (int i = 0; i <= cacheBitsMax; i++) for (int i = 0; i <= cacheBitsMax; ++i)
{ {
double entropy = histos[i].EstimateBits(); double entropy = histos[i].EstimateBits();
if (i == 0 || entropy < entropyMin) if (i == 0 || entropy < entropyMin)
@ -461,7 +468,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
// Add first pixel as literal. // Add first pixel as literal.
AddSingleLiteralWithCostModel(bgra, colorCache, costModel, 0, useColorCache, 0.0f, costManager.Costs, distArray); AddSingleLiteralWithCostModel(bgra, colorCache, costModel, 0, useColorCache, 0.0f, costManager.Costs, distArray);
for (int i = 1; i < pixCount; i++) for (int i = 1; i < pixCount; ++i)
{ {
float prevCost = costManager.Costs[i - 1]; float prevCost = costManager.Costs[i - 1];
int offset = hashChain.FindOffset(i); int offset = hashChain.FindOffset(i);
@ -660,7 +667,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
// [i,i+len) + [i+len, length of best match at i+len) // [i,i+len) + [i+len, length of best match at i+len)
// while we check if we can use: // while we check if we can use:
// [i,j) (where j<=i+len) + [j, length of best match at j) // [i,j) (where j<=i+len) + [j, length of best match at j)
for (j = iLastCheck + 1; j <= jMax; j++) for (j = iLastCheck + 1; j <= jMax; ++j)
{ {
int lenJ = hashChain.FindLength(j); int lenJ = hashChain.FindLength(j);
int reach = j + (lenJ >= MinLength ? lenJ : 1); // 1 for single literal. int reach = j + (lenJ >= MinLength ? lenJ : 1); // 1 for single literal.
@ -720,7 +727,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
int i = pixelCount - 2; int i = pixelCount - 2;
int countsPos = i; int countsPos = i;
counts[countsPos + 1] = 1; counts[countsPos + 1] = 1;
for (; i >= 0; i--, countsPos--) for (; i >= 0; --i, --countsPos)
{ {
if (bgra[i] == bgra[i + 1]) if (bgra[i] == bgra[i + 1])
{ {
@ -739,9 +746,9 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
// Figure out the window offsets around a pixel. They are stored in a // Figure out the window offsets around a pixel. They are stored in a
// spiraling order around the pixel as defined by DistanceToPlaneCode. // spiraling order around the pixel as defined by DistanceToPlaneCode.
for (int y = 0; y <= 6; y++) for (int y = 0; y <= 6; ++y)
{ {
for (int x = -6; x <= 6; x++) for (int x = -6; x <= 6; ++x)
{ {
int offset = (y * xSize) + x; int offset = (y * xSize) + x;
@ -762,7 +769,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
} }
// For narrow images, not all plane codes are reached, so remove those. // For narrow images, not all plane codes are reached, so remove those.
for (i = 0; i < WindowOffsetsSizeMax; i++) for (i = 0; i < WindowOffsetsSizeMax; ++i)
{ {
if (windowOffsets[i] == 0) if (windowOffsets[i] == 0)
{ {
@ -774,7 +781,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
// Given a pixel P, find the offsets that reach pixels unreachable from P-1 // Given a pixel P, find the offsets that reach pixels unreachable from P-1
// with any of the offsets in windowOffsets[]. // with any of the offsets in windowOffsets[].
for (i = 0; i < windowOffsetsSize; i++) for (i = 0; i < windowOffsetsSize; ++i)
{ {
bool isReachable = false; bool isReachable = false;
for (int j = 0; j < windowOffsetsSize && !isReachable; ++j) for (int j = 0; j < windowOffsetsSize && !isReachable; ++j)
@ -785,7 +792,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
if (!isReachable) if (!isReachable)
{ {
windowOffsetsNew[windowOffsetsNewSize] = windowOffsets[i]; windowOffsetsNew[windowOffsetsNewSize] = windowOffsets[i];
windowOffsetsNewSize++; ++windowOffsetsNewSize;
} }
} }
@ -917,7 +924,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
refs.Add(PixOrCopy.CreateCopy((uint)xSize, (ushort)prevRowLen)); refs.Add(PixOrCopy.CreateCopy((uint)xSize, (ushort)prevRowLen));
if (useColorCache) if (useColorCache)
{ {
for (int k = 0; k < prevRowLen; k++) for (int k = 0; k < prevRowLen; ++k)
{ {
colorCache.Insert(bgra[i + k]); colorCache.Insert(bgra[i + k]);
} }
@ -943,6 +950,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
/// </summary> /// </summary>
private static void BackwardRefsWithLocalCache(Span<uint> bgra, int cacheBits, Vp8LBackwardRefs refs) private static void BackwardRefsWithLocalCache(Span<uint> bgra, int cacheBits, Vp8LBackwardRefs refs)
{ {
// TODO: Don't use enumerator.
int pixelIndex = 0; int pixelIndex = 0;
using List<PixOrCopy>.Enumerator c = refs.Refs.GetEnumerator(); using List<PixOrCopy>.Enumerator c = refs.Refs.GetEnumerator();
var colorCache = new ColorCache(); var colorCache = new ColorCache();
@ -969,7 +977,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
else else
{ {
// refs was created without local cache, so it can not have cache indexes. // refs was created without local cache, so it can not have cache indexes.
for (int k = 0; k < v.Len; k++) for (int k = 0; k < v.Len; ++k)
{ {
colorCache.Insert(bgra[pixelIndex++]); colorCache.Insert(bgra[pixelIndex++]);
} }

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

@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
/// <summary> /// <summary>
/// Utility functions for the lossless decoder. /// Utility functions for the lossless decoder.
/// </summary> /// </summary>
internal static class LosslessUtils internal static unsafe class LosslessUtils
{ {
private const uint Predictor0 = WebPConstants.ArgbBlack; private const uint Predictor0 = WebPConstants.ArgbBlack;
@ -244,104 +244,116 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
/// </summary> /// </summary>
/// <param name="transform">The transform data.</param> /// <param name="transform">The transform data.</param>
/// <param name="pixelData">The pixel data to apply the inverse transform.</param> /// <param name="pixelData">The pixel data to apply the inverse transform.</param>
/// <param name="output">The resulting pixel data with the reversed transformation data.</param> /// <param name="outputSpan">The resulting pixel data with the reversed transformation data.</param>
public static void PredictorInverseTransform(Vp8LTransform transform, Span<uint> pixelData, Span<uint> output) public static void PredictorInverseTransform(
{ Vp8LTransform transform,
int processedPixels = 0; Span<uint> pixelData,
int width = transform.XSize; Span<uint> outputSpan)
Span<uint> transformData = transform.Data.GetSpan(); {
fixed (uint* inputFixed = pixelData)
// First Row follows the L (mode=1) mode. fixed (uint* outputFixed = outputSpan)
PredictorAdd0(pixelData, processedPixels, 1, output);
PredictorAdd1(pixelData, 1, width - 1, output);
processedPixels += width;
int y = 1;
int yEnd = transform.YSize;
int tileWidth = 1 << transform.Bits;
int mask = tileWidth - 1;
int tilesPerRow = SubSampleSize(width, transform.Bits);
int predictorModeIdxBase = (y >> transform.Bits) * tilesPerRow;
while (y < yEnd)
{ {
int predictorModeIdx = predictorModeIdxBase; uint* input = inputFixed;
int x = 1; uint* output = outputFixed;
int width = transform.XSize;
Span<uint> transformData = transform.Data.GetSpan();
// First Row follows the L (mode=1) mode.
PredictorAdd0(input, null, 1, output);
PredictorAdd1(input + 1, null, width - 1, output + 1);
input += width;
output += width;
int y = 1;
int yEnd = transform.YSize;
int tileWidth = 1 << transform.Bits;
int mask = tileWidth - 1;
int tilesPerRow = SubSampleSize(width, transform.Bits);
int predictorModeIdxBase = (y >> transform.Bits) * tilesPerRow;
while (y < yEnd)
{
int predictorModeIdx = predictorModeIdxBase;
int x = 1;
// First pixel follows the T (mode=2) mode. // First pixel follows the T (mode=2) mode.
PredictorAdd2(pixelData, processedPixels, 1, width, output); PredictorAdd2(input, output - width, 1, output);
while (x < width) // .. the rest:
{ while (x < width)
uint predictorMode = (transformData[predictorModeIdx++] >> 8) & 0xf;
int xEnd = (x & ~mask) + tileWidth;
if (xEnd > width)
{ {
xEnd = width; uint predictorMode = (transformData[predictorModeIdx++] >> 8) & 0xf;
} int xEnd = (x & ~mask) + tileWidth;
if (xEnd > width)
{
xEnd = width;
}
// There are 14 different prediction modes. // There are 14 different prediction modes.
// In each prediction mode, the current pixel value is predicted from one or more neighboring pixels whose values are already known. // In each prediction mode, the current pixel value is predicted from one
int startIdx = processedPixels + x; // or more neighboring pixels whose values are already known.
int numberOfPixels = xEnd - x; switch (predictorMode)
switch (predictorMode) {
{ case 0:
case 0: PredictorAdd0(input + x, output + x - width, xEnd - x, output + x);
PredictorAdd0(pixelData, startIdx, numberOfPixels, output); break;
break; case 1:
case 1: PredictorAdd1(input + x, output + x - width, xEnd - x, output + x);
PredictorAdd1(pixelData, startIdx, numberOfPixels, output); break;
break; case 2:
case 2: PredictorAdd2(input + x, output + x - width, xEnd - x, output + x);
PredictorAdd2(pixelData, startIdx, numberOfPixels, width, output); break;
break; case 3:
case 3: PredictorAdd3(input + x, output + x - width, xEnd - x, output + x);
PredictorAdd3(pixelData, startIdx, numberOfPixels, width, output); break;
break; case 4:
case 4: PredictorAdd4(input + x, output + x - width, xEnd - x, output + x);
PredictorAdd4(pixelData, startIdx, numberOfPixels, width, output); break;
break; case 5:
case 5: PredictorAdd5(input + x, output + x - width, xEnd - x, output + x);
PredictorAdd5(pixelData, startIdx, numberOfPixels, width, output); break;
break; case 6:
case 6: PredictorAdd6(input + x, output + x - width, xEnd - x, output + x);
PredictorAdd6(pixelData, startIdx, numberOfPixels, width, output); break;
break; case 7:
case 7: PredictorAdd7(input + x, output + x - width, xEnd - x, output + x);
PredictorAdd7(pixelData, startIdx, numberOfPixels, width, output); break;
break; case 8:
case 8: PredictorAdd8(input + x, output + x - width, xEnd - x, output + x);
PredictorAdd8(pixelData, startIdx, numberOfPixels, width, output); break;
break; case 9:
case 9: PredictorAdd9(input + x, output + x - width, xEnd - x, output + x);
PredictorAdd9(pixelData, startIdx, numberOfPixels, width, output); break;
break; case 10:
case 10: PredictorAdd10(input + x, output + x - width, xEnd - x, output + x);
PredictorAdd10(pixelData, startIdx, numberOfPixels, width, output); break;
break; case 11:
case 11: PredictorAdd11(input + x, output + x - width, xEnd - x, output + x);
PredictorAdd11(pixelData, startIdx, numberOfPixels, width, output); break;
break; case 12:
case 12: PredictorAdd12(input + x, output + x - width, xEnd - x, output + x);
PredictorAdd12(pixelData, startIdx, numberOfPixels, width, output); break;
break; case 13:
case 13: PredictorAdd13(input + x, output + x - width, xEnd - x, output + x);
PredictorAdd13(pixelData, startIdx, numberOfPixels, width, output); break;
break; }
x = xEnd;
} }
x = xEnd; input += width;
} output += width;
++y;
processedPixels += width; if ((y & mask) == 0)
++y; {
if ((y & mask) == 0) // Use the same mask, since tiles are squares.
{ predictorModeIdxBase += tilesPerRow;
// Use the same mask, since tiles are squares. }
predictorModeIdxBase += tilesPerRow;
} }
} }
output.CopyTo(pixelData); outputSpan.CopyTo(pixelData);
} }
public static void ExpandColorMap(int numColors, Span<uint> transformData, Span<uint> newColorMap) public static void ExpandColorMap(int numColors, Span<uint> transformData, Span<uint> newColorMap)
@ -582,374 +594,352 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
return code; return code;
} }
private static void PredictorAdd0(Span<uint> input, int startIdx, int numberOfPixels, Span<uint> output) [MethodImpl(InliningOptions.ShortMethod)]
private static void PredictorAdd0(uint* input, uint* upper, int numberOfPixels, uint* output)
{ {
int endIdx = startIdx + numberOfPixels; for (int x = 0; x < numberOfPixels; ++x)
uint pred = Predictor0;
for (int x = startIdx; x < endIdx; ++x)
{ {
output[x] = AddPixels(input[x], pred); output[x] = AddPixels(input[x], WebPConstants.ArgbBlack);
} }
} }
private static void PredictorAdd1(Span<uint> input, int startIdx, int numberOfPixels, Span<uint> output) [MethodImpl(InliningOptions.ShortMethod)]
private static void PredictorAdd1(uint* input, uint* upper, int numberOfPixels, uint* output)
{ {
int endIdx = startIdx + numberOfPixels; uint left = output[-1];
uint left = output[startIdx - 1]; for (int x = 0; x < numberOfPixels; ++x)
for (int x = startIdx; x < endIdx; ++x)
{ {
output[x] = left = AddPixels(input[x], left); output[x] = left = AddPixels(input[x], left);
} }
} }
private static void PredictorAdd2(Span<uint> input, int startIdx, int numberOfPixels, int width, Span<uint> output) [MethodImpl(InliningOptions.ShortMethod)]
private static void PredictorAdd2(uint* input, uint* upper, int numberOfPixels, uint* output)
{ {
int endIdx = startIdx + numberOfPixels; for (int x = 0; x < numberOfPixels; ++x)
int offset = startIdx - width;
for (int x = startIdx; x < endIdx; ++x)
{ {
uint pred = Predictor2(output, offset++); uint pred = Predictor2(output[x - 1], upper + x);
output[x] = AddPixels(input[x], pred); output[x] = AddPixels(input[x], pred);
} }
} }
private static void PredictorAdd3(Span<uint> input, int startIdx, int numberOfPixels, int width, Span<uint> output) [MethodImpl(InliningOptions.ShortMethod)]
private static void PredictorAdd3(uint* input, uint* upper, int numberOfPixels, uint* output)
{ {
int endIdx = startIdx + numberOfPixels; for (int x = 0; x < numberOfPixels; ++x)
int offset = startIdx - width;
for (int x = startIdx; x < endIdx; ++x)
{ {
uint pred = Predictor3(output, offset++); uint pred = Predictor3(output[x - 1], upper + x);
output[x] = AddPixels(input[x], pred); output[x] = AddPixels(input[x], pred);
} }
} }
private static void PredictorAdd4(Span<uint> input, int startIdx, int numberOfPixels, int width, Span<uint> output) [MethodImpl(InliningOptions.ShortMethod)]
private static void PredictorAdd4(uint* input, uint* upper, int numberOfPixels, uint* output)
{ {
int endIdx = startIdx + numberOfPixels; for (int x = 0; x < numberOfPixels; ++x)
int offset = startIdx - width;
for (int x = startIdx; x < endIdx; ++x)
{ {
uint pred = Predictor4(output, offset++); uint pred = Predictor4(output[x - 1], upper + x);
output[x] = AddPixels(input[x], pred); output[x] = AddPixels(input[x], pred);
} }
} }
private static void PredictorAdd5(Span<uint> input, int startIdx, int numberOfPixels, int width, Span<uint> output) [MethodImpl(InliningOptions.ShortMethod)]
private static void PredictorAdd5(uint* input, uint* upper, int numberOfPixels, uint* output)
{ {
int endIdx = startIdx + numberOfPixels; for (int x = 0; x < numberOfPixels; ++x)
int offset = startIdx - width;
for (int x = startIdx; x < endIdx; ++x)
{ {
uint pred = Predictor5(output[x - 1], output, offset++); uint pred = Predictor5(output[x - 1], upper + x);
output[x] = AddPixels(input[x], pred); output[x] = AddPixels(input[x], pred);
} }
} }
private static void PredictorAdd6(Span<uint> input, int startIdx, int numberOfPixels, int width, Span<uint> output) [MethodImpl(InliningOptions.ShortMethod)]
private static void PredictorAdd6(uint* input, uint* upper, int numberOfPixels, uint* output)
{ {
int endIdx = startIdx + numberOfPixels; for (int x = 0; x < numberOfPixels; ++x)
int offset = startIdx - width;
for (int x = startIdx; x < endIdx; ++x)
{ {
uint pred = Predictor6(output[x - 1], output, offset++); uint pred = Predictor6(output[x - 1], upper + x);
output[x] = AddPixels(input[x], pred); output[x] = AddPixels(input[x], pred);
} }
} }
private static void PredictorAdd7(Span<uint> input, int startIdx, int numberOfPixels, int width, Span<uint> output) [MethodImpl(InliningOptions.ShortMethod)]
private static void PredictorAdd7(uint* input, uint* upper, int numberOfPixels, uint* output)
{ {
int endIdx = startIdx + numberOfPixels; for (int x = 0; x < numberOfPixels; ++x)
int offset = startIdx - width;
for (int x = startIdx; x < endIdx; ++x)
{ {
uint pred = Predictor7(output[x - 1], output, offset++); uint pred = Predictor7(output[x - 1], upper + x);
output[x] = AddPixels(input[x], pred); output[x] = AddPixels(input[x], pred);
} }
} }
private static void PredictorAdd8(Span<uint> input, int startIdx, int numberOfPixels, int width, Span<uint> output) [MethodImpl(InliningOptions.ShortMethod)]
private static void PredictorAdd8(uint* input, uint* upper, int numberOfPixels, uint* output)
{ {
int endIdx = startIdx + numberOfPixels; for (int x = 0; x < numberOfPixels; ++x)
int offset = startIdx - width;
for (int x = startIdx; x < endIdx; ++x)
{ {
uint pred = Predictor8(output, offset++); uint pred = Predictor8(output[x - 1], upper + x);
output[x] = AddPixels(input[x], pred); output[x] = AddPixels(input[x], pred);
} }
} }
private static void PredictorAdd9(Span<uint> input, int startIdx, int numberOfPixels, int width, Span<uint> output) [MethodImpl(InliningOptions.ShortMethod)]
private static void PredictorAdd9(uint* input, uint* upper, int numberOfPixels, uint* output)
{ {
int endIdx = startIdx + numberOfPixels; for (int x = 0; x < numberOfPixels; ++x)
int offset = startIdx - width;
for (int x = startIdx; x < endIdx; ++x)
{ {
uint pred = Predictor9(output, offset++); uint pred = Predictor9(output[x - 1], upper + x);
output[x] = AddPixels(input[x], pred); output[x] = AddPixels(input[x], pred);
} }
} }
private static void PredictorAdd10(Span<uint> input, int startIdx, int numberOfPixels, int width, Span<uint> output) [MethodImpl(InliningOptions.ShortMethod)]
private static void PredictorAdd10(uint* input, uint* upper, int numberOfPixels, uint* output)
{ {
int endIdx = startIdx + numberOfPixels; for (int x = 0; x < numberOfPixels; ++x)
int offset = startIdx - width;
for (int x = startIdx; x < endIdx; ++x)
{ {
uint pred = Predictor10(output[x - 1], output, offset++); uint pred = Predictor10(output[x - 1], upper + x);
output[x] = AddPixels(input[x], pred); output[x] = AddPixels(input[x], pred);
} }
} }
private static void PredictorAdd11(Span<uint> input, int startIdx, int numberOfPixels, int width, Span<uint> output) [MethodImpl(InliningOptions.ShortMethod)]
private static void PredictorAdd11(uint* input, uint* upper, int numberOfPixels, uint* output)
{ {
int endIdx = startIdx + numberOfPixels; for (int x = 0; x < numberOfPixels; ++x)
int offset = startIdx - width;
for (int x = startIdx; x < endIdx; ++x)
{ {
uint pred = Predictor11(output[x - 1], output, offset++); uint pred = Predictor11(output[x - 1], upper + x);
output[x] = AddPixels(input[x], pred); output[x] = AddPixels(input[x], pred);
} }
} }
private static void PredictorAdd12(Span<uint> input, int startIdx, int numberOfPixels, int width, Span<uint> output) [MethodImpl(InliningOptions.ShortMethod)]
private static void PredictorAdd12(uint* input, uint* upper, int numberOfPixels, uint* output)
{ {
int endIdx = startIdx + numberOfPixels; for (int x = 0; x < numberOfPixels; ++x)
int offset = startIdx - width;
for (int x = startIdx; x < endIdx; ++x)
{ {
uint pred = Predictor12(output[x - 1], output, offset++); uint pred = Predictor12(output[x - 1], upper + x);
output[x] = AddPixels(input[x], pred); output[x] = AddPixels(input[x], pred);
} }
} }
private static void PredictorAdd13(Span<uint> input, int startIdx, int numberOfPixels, int width, Span<uint> output) [MethodImpl(InliningOptions.ShortMethod)]
private static void PredictorAdd13(uint* input, uint* upper, int numberOfPixels, uint* output)
{ {
int endIdx = startIdx + numberOfPixels; for (int x = 0; x < numberOfPixels; ++x)
int offset = startIdx - width;
for (int x = startIdx; x < endIdx; ++x)
{ {
uint pred = Predictor13(output[x - 1], output, offset++); uint pred = Predictor13(output[x - 1], upper + x);
output[x] = AddPixels(input[x], pred); output[x] = AddPixels(input[x], pred);
} }
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static uint Predictor2(Span<uint> top, int idx) public static uint Predictor2(uint left, uint* top)
{ {
return top[idx]; return top[0];
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static uint Predictor3(Span<uint> top, int idx) public static uint Predictor3(uint left, uint* top)
{ {
return top[idx + 1]; return top[1];
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static uint Predictor4(Span<uint> top, int idx) public static uint Predictor4(uint left, uint* top)
{ {
return top[idx - 1]; return top[-1];
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static uint Predictor5(uint left, Span<uint> top, int idx) public static uint Predictor5(uint left, uint* top)
{ {
uint pred = Average3(left, top[idx], top[idx + 1]); return Average3(left, top[0], top[1]);
return pred;
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static uint Predictor6(uint left, Span<uint> top, int idx) public static uint Predictor6(uint left, uint* top)
{ {
uint pred = Average2(left, top[idx - 1]); return Average2(left, top[-1]);
return pred;
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static uint Predictor7(uint left, Span<uint> top, int idx) public static uint Predictor7(uint left, uint* top)
{ {
uint pred = Average2(left, top[idx]); return Average2(left, top[0]);
return pred;
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static uint Predictor8(Span<uint> top, int idx) public static uint Predictor8(uint left, uint* top)
{ {
uint pred = Average2(top[idx - 1], top[idx]); return Average2(top[-1], top[0]);
return pred;
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static uint Predictor9(Span<uint> top, int idx) public static uint Predictor9(uint left, uint* top)
{ {
uint pred = Average2(top[idx], top[idx + 1]); return Average2(top[0], top[1]);
return pred;
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static uint Predictor10(uint left, Span<uint> top, int idx) public static uint Predictor10(uint left, uint* top)
{ {
uint pred = Average4(left, top[idx - 1], top[idx], top[idx + 1]); return Average4(left, top[-1], top[0], top[1]);
return pred;
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static uint Predictor11(uint left, Span<uint> top, int idx) public static uint Predictor11(uint left, uint* top)
{ {
uint pred = Select(top[idx], left, top[idx - 1]); return Select(top[0], left, top[-1]);
return pred;
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static uint Predictor12(uint left, Span<uint> top, int idx) public static uint Predictor12(uint left, uint* top)
{ {
uint pred = ClampedAddSubtractFull(left, top[idx], top[idx - 1]); return ClampedAddSubtractFull(left, top[0], top[-1]);
return pred;
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static uint Predictor13(uint left, Span<uint> top, int idx) public static uint Predictor13(uint left, uint* top)
{ {
uint pred = ClampedAddSubtractHalf(left, top[idx], top[idx - 1]); return ClampedAddSubtractHalf(left, top[0], top[-1]);
return pred;
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static void PredictorSub0(Span<uint> input, int numPixels, Span<uint> output) public static void PredictorSub0(uint* input, int numPixels, uint* output)
{ {
for (int i = 0; i < numPixels; i++) for (int i = 0; i < numPixels; ++i)
{ {
output[i] = SubPixels(input[i], WebPConstants.ArgbBlack); output[i] = SubPixels(input[i], WebPConstants.ArgbBlack);
} }
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static void PredictorSub1(Span<uint> input, int idx, int numPixels, Span<uint> output) public static void PredictorSub1(uint* input, int numPixels, uint* output)
{ {
for (int i = 0; i < numPixels; i++) for (int i = 0; i < numPixels; ++i)
{ {
output[i] = SubPixels(input[idx + i], input[idx + i - 1]); output[i] = SubPixels(input[i], input[i - 1]);
} }
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static void PredictorSub2(Span<uint> input, int idx, Span<uint> upper, int numPixels, Span<uint> output) public static void PredictorSub2(uint* input, uint* upper, int numPixels, uint* output)
{ {
for (int x = 0; x < numPixels; x++) for (int x = 0; x < numPixels; ++x)
{ {
uint pred = Predictor2(upper, x); uint pred = Predictor2(input[x - 1], upper + x);
output[x] = SubPixels(input[idx + x], pred); output[x] = SubPixels(input[x], pred);
} }
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static void PredictorSub3(Span<uint> input, int idx, Span<uint> upper, int numPixels, Span<uint> output) public static void PredictorSub3(uint* input, uint* upper, int numPixels, uint* output)
{ {
for (int x = 0; x < numPixels; x++) for (int x = 0; x < numPixels; ++x)
{ {
uint pred = Predictor3(upper, x); uint pred = Predictor3(input[x - 1], upper + x);
output[x] = SubPixels(input[idx + x], pred); output[x] = SubPixels(input[x], pred);
} }
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static void PredictorSub4(Span<uint> input, int idx, Span<uint> upper, int numPixels, Span<uint> output) public static void PredictorSub4(uint* input, uint* upper, int numPixels, uint* output)
{ {
for (int x = 0; x < numPixels; x++) for (int x = 0; x < numPixels; ++x)
{ {
uint pred = Predictor4(upper, x); uint pred = Predictor4(input[x - 1], upper + x);
output[x] = SubPixels(input[idx + x], pred); output[x] = SubPixels(input[x], pred);
} }
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static void PredictorSub5(Span<uint> input, int idx, Span<uint> upper, int numPixels, Span<uint> output) public static void PredictorSub5(uint* input, uint* upper, int numPixels, uint* output)
{ {
for (int x = 0; x < numPixels; x++) for (int x = 0; x < numPixels; ++x)
{ {
uint pred = Predictor5(input[idx - 1], upper, x); uint pred = Predictor5(input[x - 1], upper + x);
output[x] = SubPixels(input[idx + x], pred); output[x] = SubPixels(input[x], pred);
} }
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static void PredictorSub6(Span<uint> input, int idx, Span<uint> upper, int numPixels, Span<uint> output) public static void PredictorSub6(uint* input, uint* upper, int numPixels, uint* output)
{ {
for (int x = 0; x < numPixels; x++) for (int x = 0; x < numPixels; ++x)
{ {
uint pred = Predictor6(input[idx - 1], upper, x); uint pred = Predictor6(input[x - 1], upper + x);
output[x] = SubPixels(input[idx + x], pred); output[x] = SubPixels(input[x], pred);
} }
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static void PredictorSub7(Span<uint> input, int idx, Span<uint> upper, int numPixels, Span<uint> output) public static void PredictorSub7(uint* input, uint* upper, int numPixels, uint* output)
{ {
for (int x = 0; x < numPixels; x++) for (int x = 0; x < numPixels; ++x)
{ {
uint pred = Predictor7(input[idx - 1], upper, x); uint pred = Predictor7(input[x - 1], upper + x);
output[x] = SubPixels(input[idx + x], pred); output[x] = SubPixels(input[x], pred);
} }
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static void PredictorSub8(Span<uint> input, int idx, Span<uint> upper, int numPixels, Span<uint> output) public static void PredictorSub8(uint* input, uint* upper, int numPixels, uint* output)
{ {
for (int x = 0; x < numPixels; x++) for (int x = 0; x < numPixels; ++x)
{ {
uint pred = Predictor8(upper, x); uint pred = Predictor8(input[x - 1], upper + x);
output[x] = SubPixels(input[idx + x], pred); output[x] = SubPixels(input[x], pred);
} }
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static void PredictorSub9(Span<uint> input, int idx, Span<uint> upper, int numPixels, Span<uint> output) public static void PredictorSub9(uint* input, uint* upper, int numPixels, uint* output)
{ {
for (int x = 0; x < numPixels; x++) for (int x = 0; x < numPixels; ++x)
{ {
uint pred = Predictor9(upper, x); uint pred = Predictor9(input[x - 1], upper + x);
output[x] = SubPixels(input[idx + x], pred); output[x] = SubPixels(input[x], pred);
} }
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static void PredictorSub10(Span<uint> input, int idx, Span<uint> upper, int numPixels, Span<uint> output) public static void PredictorSub10(uint* input, uint* upper, int numPixels, uint* output)
{ {
for (int x = 0; x < numPixels; x++) for (int x = 0; x < numPixels; ++x)
{ {
uint pred = Predictor10(input[idx - 1], upper, x); uint pred = Predictor10(input[x - 1], upper + x);
output[x] = SubPixels(input[idx + x], pred); output[x] = SubPixels(input[x], pred);
} }
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static void PredictorSub11(Span<uint> input, int idx, Span<uint> upper, int numPixels, Span<uint> output) public static void PredictorSub11(uint* input, uint* upper, int numPixels, uint* output)
{ {
for (int x = 0; x < numPixels; x++) for (int x = 0; x < numPixels; ++x)
{ {
uint pred = Predictor11(input[idx - 1], upper, x); uint pred = Predictor11(input[x - 1], upper + x);
output[x] = SubPixels(input[idx + x], pred); output[x] = SubPixels(input[x], pred);
} }
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static void PredictorSub12(Span<uint> input, int idx, Span<uint> upper, int numPixels, Span<uint> output) public static void PredictorSub12(uint* input, uint* upper, int numPixels, uint* output)
{ {
for (int x = 0; x < numPixels; x++) for (int x = 0; x < numPixels; ++x)
{ {
uint pred = Predictor12(input[idx - 1], upper, x); uint pred = Predictor12(input[x - 1], upper + x);
output[x] = SubPixels(input[idx + x], pred); output[x] = SubPixels(input[x], pred);
} }
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public static void PredictorSub13(Span<uint> input, int idx, Span<uint> upper, int numPixels, Span<uint> output) public static void PredictorSub13(uint* input, uint* upper, int numPixels, uint* output)
{ {
for (int x = 0; x < numPixels; x++) for (int x = 0; x < numPixels; ++x)
{ {
uint pred = Predictor13(input[idx - 1], upper, x); uint pred = Predictor13(input[x - 1], upper + x);
output[x] = SubPixels(input[idx + x], pred); output[x] = SubPixels(input[x], pred);
} }
} }

410
src/ImageSharp/Formats/WebP/Lossless/PredictorEncoder.cs

@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
/// <summary> /// <summary>
/// Image transform methods for the lossless webp encoder. /// Image transform methods for the lossless webp encoder.
/// </summary> /// </summary>
internal static class PredictorEncoder internal static unsafe class PredictorEncoder
{ {
private const int GreenRedToBlueNumAxis = 8; private const int GreenRedToBlueNumAxis = 8;
@ -27,27 +27,61 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
/// with respect to predictions. If nearLosslessQuality &lt; 100, applies /// with respect to predictions. If nearLosslessQuality &lt; 100, applies
/// near lossless processing, shaving off more bits of residuals for lower qualities. /// near lossless processing, shaving off more bits of residuals for lower qualities.
/// </summary> /// </summary>
public static void ResidualImage(int width, int height, int bits, Span<uint> argb, Span<uint> argbScratch, Span<uint> image, int nearLosslessQuality, bool exact, bool usedSubtractGreen) public static void ResidualImage(
int width,
int height,
int bits,
Span<uint> argb,
Span<uint> argbScratch,
Span<uint> image,
int nearLosslessQuality,
bool exact,
bool usedSubtractGreen)
{ {
int tilesPerRow = LosslessUtils.SubSampleSize(width, bits); int tilesPerRow = LosslessUtils.SubSampleSize(width, bits);
int tilesPerCol = LosslessUtils.SubSampleSize(height, bits); int tilesPerCol = LosslessUtils.SubSampleSize(height, bits);
int maxQuantization = 1 << LosslessUtils.NearLosslessBits(nearLosslessQuality); int maxQuantization = 1 << LosslessUtils.NearLosslessBits(nearLosslessQuality);
// TODO: Can we optimize this?
var histo = new int[4][]; var histo = new int[4][];
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
{ {
histo[i] = new int[256]; histo[i] = new int[256];
} }
for (int tileY = 0; tileY < tilesPerCol; tileY++) // TODO: Low Effort
for (int tileY = 0; tileY < tilesPerCol; ++tileY)
{ {
for (int tileX = 0; tileX < tilesPerRow; tileX++) for (int tileX = 0; tileX < tilesPerRow; ++tileX)
{ {
int pred = GetBestPredictorForTile(width, height, tileX, tileY, bits, histo, argbScratch, argb, maxQuantization, exact, usedSubtractGreen, image); int pred = GetBestPredictorForTile(
width,
height,
tileX,
tileY,
bits,
histo,
argbScratch,
argb,
maxQuantization,
exact,
usedSubtractGreen,
image);
image[(tileY * tilesPerRow) + tileX] = (uint)(WebPConstants.ArgbBlack | (pred << 8)); image[(tileY * tilesPerRow) + tileX] = (uint)(WebPConstants.ArgbBlack | (pred << 8));
} }
} }
CopyImageWithPrediction(width, height, bits, image, argbScratch, argb, maxQuantization, exact, usedSubtractGreen); CopyImageWithPrediction(
width,
height,
bits,
image,
argbScratch,
argb,
maxQuantization,
exact,
usedSubtractGreen);
} }
public static void ColorSpaceTransform(int width, int height, int bits, int quality, Span<uint> argb, Span<uint> image) public static void ColorSpaceTransform(int width, int height, int bits, int quality, Span<uint> argb, Span<uint> image)
@ -55,8 +89,8 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
int maxTileSize = 1 << bits; int maxTileSize = 1 << bits;
int tileXSize = LosslessUtils.SubSampleSize(width, bits); int tileXSize = LosslessUtils.SubSampleSize(width, bits);
int tileYSize = LosslessUtils.SubSampleSize(height, bits); int tileYSize = LosslessUtils.SubSampleSize(height, bits);
int[] accumulatedRedHisto = new int[256]; var accumulatedRedHisto = new int[256];
int[] accumulatedBlueHisto = new int[256]; var accumulatedBlueHisto = new int[256];
var prevX = default(Vp8LMultipliers); var prevX = default(Vp8LMultipliers);
var prevY = default(Vp8LMultipliers); var prevY = default(Vp8LMultipliers);
for (int tileY = 0; tileY < tileYSize; tileY++) for (int tileY = 0; tileY < tileYSize; tileY++)
@ -165,9 +199,9 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
Span<byte> maxDiffs = MemoryMarshal.Cast<uint, byte>(currentRow.Slice(width + 1)); Span<byte> maxDiffs = MemoryMarshal.Cast<uint, byte>(currentRow.Slice(width + 1));
float bestDiff = MaxDiffCost; float bestDiff = MaxDiffCost;
int bestMode = 0; int bestMode = 0;
uint[] residuals = new uint[1 << WebPConstants.MaxTransformBits]; var residuals = new uint[1 << WebPConstants.MaxTransformBits];
int[][] histoArgb = new int[4][]; var histoArgb = new int[4][];
int[][] bestHisto = new int[4][]; var bestHisto = new int[4][];
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
{ {
histoArgb[i] = new int[256]; histoArgb[i] = new int[256];
@ -207,13 +241,16 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
Span<uint> src = argb.Slice((y * width) + contextStartX, maxX + haveLeft + ((y + 1) < height ? 1 : 0)); Span<uint> src = argb.Slice((y * width) + contextStartX, maxX + haveLeft + ((y + 1) < height ? 1 : 0));
Span<uint> dst = currentRow.Slice(contextStartX); Span<uint> dst = currentRow.Slice(contextStartX);
src.CopyTo(dst); src.CopyTo(dst);
// TODO: Source wraps this in conditional
// WEBP_NEAR_LOSSLESS == 1
if (maxQuantization > 1 && y >= 1 && y + 1 < height) if (maxQuantization > 1 && y >= 1 && y + 1 < height)
{ {
MaxDiffsForRow(contextWidth, width, argb.Slice((y * width) + contextStartX), maxDiffs.Slice(contextStartX), usedSubtractGreen); MaxDiffsForRow(contextWidth, width, argb.Slice((y * width) + contextStartX), maxDiffs.Slice(contextStartX), usedSubtractGreen);
} }
GetResidual(width, height, upperRow, currentRow, maxDiffs, mode, startX, startX + maxX, y, maxQuantization, exact, usedSubtractGreen, residuals); GetResidual(width, height, upperRow, currentRow, maxDiffs, mode, startX, startX + maxX, y, maxQuantization, exact, usedSubtractGreen, residuals);
for (int relativeX = 0; relativeX < maxX; relativeX++) for (int relativeX = 0; relativeX < maxX; ++relativeX)
{ {
UpdateHisto(histoArgb, residuals[relativeX]); UpdateHisto(histoArgb, residuals[relativeX]);
} }
@ -234,6 +271,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
if (curDiff < bestDiff) if (curDiff < bestDiff)
{ {
// TODO: Consider swapping references
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
{ {
histoArgb[i].AsSpan().CopyTo(bestHisto[i]); histoArgb[i].AsSpan().CopyTo(bestHisto[i]);
@ -261,113 +299,130 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
/// the deviation further to pixels which depend on the current pixel for their /// the deviation further to pixels which depend on the current pixel for their
/// predictions. /// predictions.
/// </summary> /// </summary>
private static void GetResidual(int width, int height, Span<uint> upperRow, Span<uint> currentRow, Span<byte> maxDiffs, int mode, int xStart, int xEnd, int y, int maxQuantization, bool exact, bool usedSubtractGreen, Span<uint> output) private static void GetResidual(
int width,
int height,
Span<uint> upperRowSpan,
Span<uint> currentRowSpan,
Span<byte> maxDiffs,
int mode,
int xStart,
int xEnd,
int y,
int maxQuantization,
bool exact,
bool usedSubtractGreen,
Span<uint> output)
{ {
if (exact) if (exact)
{ {
PredictBatch(mode, xStart, y, xEnd - xStart, currentRow, upperRow, output); PredictBatch(mode, xStart, y, xEnd - xStart, currentRowSpan, upperRowSpan, output);
} }
else else
{ {
for (int x = xStart; x < xEnd; x++) fixed (uint* currentRow = currentRowSpan)
fixed (uint* upperRow = upperRowSpan)
{ {
uint predict = 0; for (int x = xStart; x < xEnd; ++x)
uint residual;
if (y == 0)
{
predict = (x == 0) ? WebPConstants.ArgbBlack : currentRow[x - 1]; // Left.
}
else if (x == 0)
{
predict = upperRow[x]; // Top.
}
else
{ {
switch (mode) uint predict = 0;
uint residual;
if (y == 0)
{ {
case 0: predict = (x == 0) ? WebPConstants.ArgbBlack : currentRow[x - 1]; // Left.
predict = WebPConstants.ArgbBlack; }
break; else if (x == 0)
case 1: {
predict = currentRow[x - 1]; predict = upperRow[x]; // Top.
break; }
case 2: else
predict = LosslessUtils.Predictor2(upperRow, x); {
break; switch (mode)
case 3: {
predict = LosslessUtils.Predictor3(upperRow, x); case 0:
break; predict = WebPConstants.ArgbBlack;
case 4: break;
predict = LosslessUtils.Predictor4(upperRow, x); case 1:
break; predict = currentRow[x - 1];
case 5: break;
predict = LosslessUtils.Predictor5(currentRow[x - 1], upperRow, x); case 2:
break; predict = LosslessUtils.Predictor2(currentRow[x - 1], upperRow + x);
case 6: break;
predict = LosslessUtils.Predictor6(currentRow[x - 1], upperRow, x); case 3:
break; predict = LosslessUtils.Predictor3(currentRow[x - 1], upperRow + x);
case 7: break;
predict = LosslessUtils.Predictor7(currentRow[x - 1], upperRow, x); case 4:
break; predict = LosslessUtils.Predictor4(currentRow[x - 1], upperRow + x);
case 8: break;
predict = LosslessUtils.Predictor8(upperRow, x); case 5:
break; predict = LosslessUtils.Predictor5(currentRow[x - 1], upperRow + x);
case 9: break;
predict = LosslessUtils.Predictor9(upperRow, x); case 6:
break; predict = LosslessUtils.Predictor6(currentRow[x - 1], upperRow + x);
case 10: break;
predict = LosslessUtils.Predictor10(currentRow[x - 1], upperRow, x); case 7:
break; predict = LosslessUtils.Predictor7(currentRow[x - 1], upperRow + x);
case 11: break;
predict = LosslessUtils.Predictor11(currentRow[x - 1], upperRow, x); case 8:
break; predict = LosslessUtils.Predictor8(currentRow[x - 1], upperRow + x);
case 12: break;
predict = LosslessUtils.Predictor12(currentRow[x - 1], upperRow, x); case 9:
break; predict = LosslessUtils.Predictor9(currentRow[x - 1], upperRow + x);
case 13: break;
predict = LosslessUtils.Predictor13(currentRow[x - 1], upperRow, x); case 10:
break; predict = LosslessUtils.Predictor10(currentRow[x - 1], upperRow + x);
break;
case 11:
predict = LosslessUtils.Predictor11(currentRow[x - 1], upperRow + x);
break;
case 12:
predict = LosslessUtils.Predictor12(currentRow[x - 1], upperRow + x);
break;
case 13:
predict = LosslessUtils.Predictor13(currentRow[x - 1], upperRow + x);
break;
}
} }
}
if (maxQuantization == 1 || mode == 0 || y == 0 || y == height - 1 || x == 0 || x == width - 1) if (maxQuantization == 1 || mode == 0 || y == 0 || y == height - 1 || x == 0 || x == width - 1)
{ {
residual = LosslessUtils.SubPixels(currentRow[x], predict); residual = LosslessUtils.SubPixels(currentRow[x], predict);
} }
else else
{ {
residual = NearLossless(currentRow[x], predict, maxQuantization, maxDiffs[x], usedSubtractGreen); residual = NearLossless(currentRow[x], predict, maxQuantization, maxDiffs[x], usedSubtractGreen);
// Update the source image. // Update the source image.
currentRow[x] = LosslessUtils.AddPixels(predict, residual); currentRow[x] = LosslessUtils.AddPixels(predict, residual);
// x is never 0 here so we do not need to update upperRow like below. // x is never 0 here so we do not need to update upperRow like below.
} }
if ((currentRow[x] & MaskAlpha) == 0) if ((currentRow[x] & MaskAlpha) == 0)
{
// If alpha is 0, cleanup RGB. We can choose the RGB values of the
// residual for best compression. The prediction of alpha itself can be
// non-zero and must be kept though. We choose RGB of the residual to be
// 0.
residual &= MaskAlpha;
// Update the source image.
currentRow[x] = predict & ~MaskAlpha;
// The prediction for the rightmost pixel in a row uses the leftmost
// pixel
// in that row as its top-right context pixel. Hence if we change the
// leftmost pixel of current_row, the corresponding change must be
// applied
// to upperRow as well where top-right context is being read from.
if (x == 0 && y != 0)
{ {
upperRow[width] = currentRow[0]; // If alpha is 0, cleanup RGB. We can choose the RGB values of the
// residual for best compression. The prediction of alpha itself can be
// non-zero and must be kept though. We choose RGB of the residual to be
// 0.
residual &= MaskAlpha;
// Update the source image.
currentRow[x] = predict & ~MaskAlpha;
// The prediction for the rightmost pixel in a row uses the leftmost
// pixel
// in that row as its top-right context pixel. Hence if we change the
// leftmost pixel of current_row, the corresponding change must be
// applied
// to upperRow as well where top-right context is being read from.
if (x == 0 && y != 0)
{
upperRow[width] = currentRow[0];
}
} }
}
output[x - xStart] = residual; output[x - xStart] = residual;
}
} }
} }
} }
@ -486,14 +541,18 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
Span<uint> upperRow = argbScratch; Span<uint> upperRow = argbScratch;
Span<uint> currentRow = upperRow.Slice(width + 1); Span<uint> currentRow = upperRow.Slice(width + 1);
Span<byte> currentMaxDiffs = MemoryMarshal.Cast<uint, byte>(currentRow.Slice(width + 1)); Span<byte> currentMaxDiffs = MemoryMarshal.Cast<uint, byte>(currentRow.Slice(width + 1));
// TODO: This should be wrapped in a condition?
Span<byte> lowerMaxDiffs = currentMaxDiffs.Slice(width); Span<byte> lowerMaxDiffs = currentMaxDiffs.Slice(width);
for (int y = 0; y < height; y++) for (int y = 0; y < height; ++y)
{ {
Span<uint> tmp32 = upperRow; Span<uint> tmp32 = upperRow;
upperRow = currentRow; upperRow = currentRow;
currentRow = tmp32; currentRow = tmp32;
Span<uint> src = argb.Slice(y * width, width + ((y + 1) < height ? 1 : 0)); Span<uint> src = argb.Slice(y * width, width + ((y + 1) < height ? 1 : 0));
src.CopyTo(currentRow); src.CopyTo(currentRow);
// TODO: Near lossless conditional?
if (maxQuantization > 1) if (maxQuantization > 1)
{ {
// Compute max_diffs for the lower row now, because that needs the // Compute max_diffs for the lower row now, because that needs the
@ -537,77 +596,90 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
} }
} }
private static void PredictBatch(int mode, int xStart, int y, int numPixels, Span<uint> current, Span<uint> upper, Span<uint> output) private static void PredictBatch(
int mode,
int xStart,
int y,
int numPixels,
Span<uint> currentSpan,
Span<uint> upperSpan,
Span<uint> outputSpan)
{ {
if (xStart == 0) fixed (uint* current = currentSpan)
fixed (uint* upper = upperSpan)
fixed (uint* outputFixed = outputSpan)
{ {
if (y == 0) uint* output = outputFixed;
if (xStart == 0)
{ {
// ARGB_BLACK. if (y == 0)
LosslessUtils.PredictorSub0(current, 1, output); {
// ARGB_BLACK.
LosslessUtils.PredictorSub0(current, 1, output);
}
else
{
// Top one.
LosslessUtils.PredictorSub2(current, upper, 1, output);
}
++xStart;
++output;
--numPixels;
} }
else
if (y == 0)
{ {
// Top one. // Left one.
LosslessUtils.PredictorSub2(current, 0, upper, 1, output); LosslessUtils.PredictorSub1(current + xStart, numPixels, output);
} }
else
xStart++;
output = output.Slice(1);
numPixels--;
}
if (y == 0)
{
// Left one.
LosslessUtils.PredictorSub1(current, xStart, numPixels, output);
}
else
{
switch (mode)
{ {
case 0: switch (mode)
LosslessUtils.PredictorSub0(current, numPixels, output); {
break; case 0:
case 1: LosslessUtils.PredictorSub0(current, numPixels, output);
LosslessUtils.PredictorSub1(current, xStart, numPixels, output); break;
break; case 1:
case 2: LosslessUtils.PredictorSub1(current + xStart, numPixels, output);
LosslessUtils.PredictorSub2(current, xStart, upper.Slice(xStart), numPixels, output); break;
break; case 2:
case 3: LosslessUtils.PredictorSub2(current + xStart, upper + xStart, numPixels, output);
LosslessUtils.PredictorSub3(current, xStart, upper.Slice(xStart), numPixels, output); break;
break; case 3:
case 4: LosslessUtils.PredictorSub3(current + xStart, upper + xStart, numPixels, output);
LosslessUtils.PredictorSub4(current, xStart, upper.Slice(xStart), numPixels, output); break;
break; case 4:
case 5: LosslessUtils.PredictorSub4(current + xStart, upper + xStart, numPixels, output);
LosslessUtils.PredictorSub5(current, xStart, upper.Slice(xStart), numPixels, output); break;
break; case 5:
case 6: LosslessUtils.PredictorSub5(current + xStart, upper + xStart, numPixels, output);
LosslessUtils.PredictorSub6(current, xStart, upper.Slice(xStart), numPixels, output); break;
break; case 6:
case 7: LosslessUtils.PredictorSub6(current + xStart, upper + xStart, numPixels, output);
LosslessUtils.PredictorSub7(current, xStart, upper.Slice(xStart), numPixels, output); break;
break; case 7:
case 8: LosslessUtils.PredictorSub7(current + xStart, upper + xStart, numPixels, output);
LosslessUtils.PredictorSub8(current, xStart, upper.Slice(xStart), numPixels, output); break;
break; case 8:
case 9: LosslessUtils.PredictorSub8(current + xStart, upper + xStart, numPixels, output);
LosslessUtils.PredictorSub9(current, xStart, upper.Slice(xStart), numPixels, output); break;
break; case 9:
case 10: LosslessUtils.PredictorSub9(current + xStart, upper + xStart, numPixels, output);
LosslessUtils.PredictorSub10(current, xStart, upper.Slice(xStart), numPixels, output); break;
break; case 10:
case 11: LosslessUtils.PredictorSub10(current + xStart, upper + xStart, numPixels, output);
LosslessUtils.PredictorSub11(current, xStart, upper.Slice(xStart), numPixels, output); break;
break; case 11:
case 12: LosslessUtils.PredictorSub11(current + xStart, upper + xStart, numPixels, output);
LosslessUtils.PredictorSub12(current, xStart, upper.Slice(xStart), numPixels, output); break;
break; case 12:
case 13: LosslessUtils.PredictorSub12(current + xStart, upper + xStart, numPixels, output);
LosslessUtils.PredictorSub13(current, xStart, upper.Slice(xStart), numPixels, output); break;
break; case 13:
LosslessUtils.PredictorSub13(current + xStart, upper + xStart, numPixels, output);
break;
}
} }
} }
} }
@ -627,7 +699,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
right = AddGreenToBlueAndRed(right); right = AddGreenToBlueAndRed(right);
} }
for (int x = 1; x < width - 1; x++) for (int x = 1; x < width - 1; ++x)
{ {
uint up = argb[-stride + x]; uint up = argb[-stride + x];
uint down = argb[stride + x]; uint down = argb[stride + x];
@ -884,7 +956,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
private static float PredictionCostSpatialHistogram(int[][] accumulated, int[][] tile) private static float PredictionCostSpatialHistogram(int[][] accumulated, int[][] tile)
{ {
double retVal = 0.0d; double retVal = 0.0d;
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; ++i)
{ {
double kExpValue = 0.94; double kExpValue = 0.94;
retVal += PredictionCostSpatial(tile[i], 1, kExpValue); retVal += PredictionCostSpatial(tile[i], 1, kExpValue);

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

@ -607,12 +607,15 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
{ {
int cacheBits = 0; int cacheBits = 0;
var histogramSymbols = new short[1]; // Only one tree, one symbol. var histogramSymbols = new short[1]; // Only one tree, one symbol.
// TODO: Can HuffmanTreeCode be struct
var huffmanCodes = new HuffmanTreeCode[5]; var huffmanCodes = new HuffmanTreeCode[5];
for (int i = 0; i < huffmanCodes.Length; i++) for (int i = 0; i < huffmanCodes.Length; i++)
{ {
huffmanCodes[i] = new HuffmanTreeCode(); huffmanCodes[i] = new HuffmanTreeCode();
} }
// TODO: Can HuffmanTree be struct
var huffTree = new HuffmanTree[3UL * WebPConstants.CodeLengthCodes]; var huffTree = new HuffmanTree[3UL * WebPConstants.CodeLengthCodes];
for (int i = 0; i < huffTree.Length; i++) for (int i = 0; i < huffTree.Length; i++)
{ {

Loading…
Cancel
Save