diff --git a/src/ImageSharp/Formats/WebP/Lossless/LosslessUtils.cs b/src/ImageSharp/Formats/WebP/Lossless/LosslessUtils.cs
index eac88609c3..ac69906652 100644
--- a/src/ImageSharp/Formats/WebP/Lossless/LosslessUtils.cs
+++ b/src/ImageSharp/Formats/WebP/Lossless/LosslessUtils.cs
@@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
///
/// Utility functions for the lossless decoder.
///
- internal static class LosslessUtils
+ internal static unsafe class LosslessUtils
{
private const uint Predictor0 = WebPConstants.ArgbBlack;
@@ -244,104 +244,117 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
///
/// The transform data.
/// The pixel data to apply the inverse transform.
- /// The resulting pixel data with the reversed transformation data.
- public static void PredictorInverseTransform(Vp8LTransform transform, Span pixelData, Span output)
- {
- int processedPixels = 0;
- int width = transform.XSize;
- Span transformData = transform.Data.GetSpan();
-
- // First Row follows the L (mode=1) mode.
- 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)
+ /// The resulting pixel data with the reversed transformation data.
+ public static void PredictorInverseTransform(
+ Vp8LTransform transform,
+ Span pixelData,
+ Span outputSpan)
+ {
+ fixed (uint* inputFixed = pixelData)
+ fixed (uint* outputFixed = outputSpan)
{
- int predictorModeIdx = predictorModeIdxBase;
- int x = 1;
+ uint* input = inputFixed;
+ uint* output = outputFixed;
+
+ int width = transform.XSize;
+ Span transformData = transform.Data.GetSpan();
+
+ // First Row follows the L (mode=1) mode.
+ PredictorAdd0(input, null, 1, output);
+ PredictorAdd1(input + 1, null, width - 1, output);
+ 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.
- PredictorAdd2(pixelData, processedPixels, 1, width, output);
+ // First pixel follows the T (mode=2) mode.
+ PredictorAdd2(input, output - width, 1, output);
- while (x < width)
- {
- uint predictorMode = (transformData[predictorModeIdx++] >> 8) & 0xf;
- int xEnd = (x & ~mask) + tileWidth;
- if (xEnd > width)
+ // .. the rest:
+ while (x < 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.
- // In each prediction mode, the current pixel value is predicted from one or more neighboring pixels whose values are already known.
- int startIdx = processedPixels + x;
- int numberOfPixels = xEnd - x;
- switch (predictorMode)
- {
- case 0:
- PredictorAdd0(pixelData, startIdx, numberOfPixels, output);
- break;
- case 1:
- PredictorAdd1(pixelData, startIdx, numberOfPixels, output);
- break;
- case 2:
- PredictorAdd2(pixelData, startIdx, numberOfPixels, width, output);
- break;
- case 3:
- PredictorAdd3(pixelData, startIdx, numberOfPixels, width, output);
- break;
- case 4:
- PredictorAdd4(pixelData, startIdx, numberOfPixels, width, output);
- break;
- case 5:
- PredictorAdd5(pixelData, startIdx, numberOfPixels, width, output);
- break;
- case 6:
- PredictorAdd6(pixelData, startIdx, numberOfPixels, width, output);
- break;
- case 7:
- PredictorAdd7(pixelData, startIdx, numberOfPixels, width, output);
- break;
- case 8:
- PredictorAdd8(pixelData, startIdx, numberOfPixels, width, output);
- break;
- case 9:
- PredictorAdd9(pixelData, startIdx, numberOfPixels, width, output);
- break;
- case 10:
- PredictorAdd10(pixelData, startIdx, numberOfPixels, width, output);
- break;
- case 11:
- PredictorAdd11(pixelData, startIdx, numberOfPixels, width, output);
- break;
- case 12:
- PredictorAdd12(pixelData, startIdx, numberOfPixels, width, output);
- break;
- case 13:
- PredictorAdd13(pixelData, startIdx, numberOfPixels, width, output);
- break;
+ // 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.
+ switch (predictorMode)
+ {
+ case 0:
+ PredictorAdd0(input + x, output + x - width, xEnd - x, output + x);
+ break;
+ case 1:
+ PredictorAdd1(input + x, output + x - width, xEnd - x, output + x);
+ break;
+ case 2:
+ PredictorAdd2(input + x, output + x - width, xEnd - x, output + x);
+ break;
+ case 3:
+ PredictorAdd3(input + x, output + x - width, xEnd - x, output + x);
+ break;
+ case 4:
+ PredictorAdd4(input + x, output + x - width, xEnd - x, output + x);
+ break;
+ case 5:
+ PredictorAdd5(input + x, output + x - width, xEnd - x, output + x);
+ break;
+ case 6:
+ PredictorAdd6(input + x, output + x - width, xEnd - x, output + x);
+ break;
+ case 7:
+ PredictorAdd7(input + x, output + x - width, xEnd - x, output + x);
+ break;
+ case 8:
+ PredictorAdd8(input + x, output + x - width, xEnd - x, output + x);
+ break;
+ case 9:
+ PredictorAdd9(input + x, output + x - width, xEnd - x, output + x);
+ break;
+ case 10:
+ PredictorAdd10(input + x, output + x - width, xEnd - x, output + x);
+ break;
+ case 11:
+ PredictorAdd11(input + x, output + x - width, xEnd - x, output + x);
+ break;
+ case 12:
+ PredictorAdd12(input + x, output + x - width, xEnd - x, output + x);
+ break;
+ case 13:
+ PredictorAdd13(input + x, output + x - width, xEnd - x, output + x);
+ break;
+ }
+
+ x = xEnd;
}
- x = xEnd;
- }
+ input += width;
+ output += width;
+ ++y;
- processedPixels += width;
- ++y;
- if ((y & mask) == 0)
- {
- // Use the same mask, since tiles are squares.
- predictorModeIdxBase += tilesPerRow;
+ if ((y & mask) == 0)
+ {
+ // Use the same mask, since tiles are squares.
+ predictorModeIdxBase += tilesPerRow;
+ }
}
}
- output.CopyTo(pixelData);
+ // TODO: I can't see the equivalent code in the source?
+ outputSpan.CopyTo(pixelData);
}
public static void ExpandColorMap(int numColors, Span transformData, Span newColorMap)
@@ -582,374 +595,352 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
return code;
}
- private static void PredictorAdd0(Span input, int startIdx, int numberOfPixels, Span output)
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void PredictorAdd0(uint* input, uint* upper, int numberOfPixels, uint* output)
{
- int endIdx = startIdx + numberOfPixels;
- uint pred = Predictor0;
- for (int x = startIdx; x < endIdx; ++x)
+ for (int x = 0; x < numberOfPixels; ++x)
{
- output[x] = AddPixels(input[x], pred);
+ output[x] = AddPixels(input[x], WebPConstants.ArgbBlack);
}
}
- private static void PredictorAdd1(Span input, int startIdx, int numberOfPixels, Span output)
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void PredictorAdd1(uint* input, uint* upper, int numberOfPixels, uint* output)
{
- int endIdx = startIdx + numberOfPixels;
- uint left = output[startIdx - 1];
- for (int x = startIdx; x < endIdx; ++x)
+ uint left = output[-1];
+ for (int x = 0; x < numberOfPixels; ++x)
{
output[x] = left = AddPixels(input[x], left);
}
}
- private static void PredictorAdd2(Span input, int startIdx, int numberOfPixels, int width, Span output)
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void PredictorAdd2(uint* input, uint* upper, int numberOfPixels, uint* output)
{
- int endIdx = startIdx + numberOfPixels;
- int offset = startIdx - width;
- for (int x = startIdx; x < endIdx; ++x)
+ for (int x = 0; x < numberOfPixels; ++x)
{
- uint pred = Predictor2(output, offset++);
+ uint pred = Predictor2(output[x - 1], upper + x);
output[x] = AddPixels(input[x], pred);
}
}
- private static void PredictorAdd3(Span input, int startIdx, int numberOfPixels, int width, Span output)
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void PredictorAdd3(uint* input, uint* upper, int numberOfPixels, uint* output)
{
- int endIdx = startIdx + numberOfPixels;
- int offset = startIdx - width;
- for (int x = startIdx; x < endIdx; ++x)
+ for (int x = 0; x < numberOfPixels; ++x)
{
- uint pred = Predictor3(output, offset++);
+ uint pred = Predictor3(output[x - 1], upper + x);
output[x] = AddPixels(input[x], pred);
}
}
- private static void PredictorAdd4(Span input, int startIdx, int numberOfPixels, int width, Span output)
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void PredictorAdd4(uint* input, uint* upper, int numberOfPixels, uint* output)
{
- int endIdx = startIdx + numberOfPixels;
- int offset = startIdx - width;
- for (int x = startIdx; x < endIdx; ++x)
+ for (int x = 0; x < numberOfPixels; ++x)
{
- uint pred = Predictor4(output, offset++);
+ uint pred = Predictor4(output[x - 1], upper + x);
output[x] = AddPixels(input[x], pred);
}
}
- private static void PredictorAdd5(Span input, int startIdx, int numberOfPixels, int width, Span output)
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void PredictorAdd5(uint* input, uint* upper, int numberOfPixels, uint* output)
{
- int endIdx = startIdx + numberOfPixels;
- int offset = startIdx - width;
- for (int x = startIdx; x < endIdx; ++x)
+ for (int x = 0; x < numberOfPixels; ++x)
{
- uint pred = Predictor5(output[x - 1], output, offset++);
+ uint pred = Predictor5(output[x - 1], upper + x);
output[x] = AddPixels(input[x], pred);
}
}
- private static void PredictorAdd6(Span input, int startIdx, int numberOfPixels, int width, Span output)
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void PredictorAdd6(uint* input, uint* upper, int numberOfPixels, uint* output)
{
- int endIdx = startIdx + numberOfPixels;
- int offset = startIdx - width;
- for (int x = startIdx; x < endIdx; ++x)
+ for (int x = 0; x < numberOfPixels; ++x)
{
- uint pred = Predictor6(output[x - 1], output, offset++);
+ uint pred = Predictor6(output[x - 1], upper + x);
output[x] = AddPixels(input[x], pred);
}
}
- private static void PredictorAdd7(Span input, int startIdx, int numberOfPixels, int width, Span output)
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void PredictorAdd7(uint* input, uint* upper, int numberOfPixels, uint* output)
{
- int endIdx = startIdx + numberOfPixels;
- int offset = startIdx - width;
- for (int x = startIdx; x < endIdx; ++x)
+ for (int x = 0; x < numberOfPixels; ++x)
{
- uint pred = Predictor7(output[x - 1], output, offset++);
+ uint pred = Predictor7(output[x - 1], upper + x);
output[x] = AddPixels(input[x], pred);
}
}
- private static void PredictorAdd8(Span input, int startIdx, int numberOfPixels, int width, Span output)
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void PredictorAdd8(uint* input, uint* upper, int numberOfPixels, uint* output)
{
- int endIdx = startIdx + numberOfPixels;
- int offset = startIdx - width;
- for (int x = startIdx; x < endIdx; ++x)
+ for (int x = 0; x < numberOfPixels; ++x)
{
- uint pred = Predictor8(output, offset++);
+ uint pred = Predictor8(output[x - 1], upper + x);
output[x] = AddPixels(input[x], pred);
}
}
- private static void PredictorAdd9(Span input, int startIdx, int numberOfPixels, int width, Span output)
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void PredictorAdd9(uint* input, uint* upper, int numberOfPixels, uint* output)
{
- int endIdx = startIdx + numberOfPixels;
- int offset = startIdx - width;
- for (int x = startIdx; x < endIdx; ++x)
+ for (int x = 0; x < numberOfPixels; ++x)
{
- uint pred = Predictor9(output, offset++);
+ uint pred = Predictor9(output[x - 1], upper + x);
output[x] = AddPixels(input[x], pred);
}
}
- private static void PredictorAdd10(Span input, int startIdx, int numberOfPixels, int width, Span output)
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void PredictorAdd10(uint* input, uint* upper, int numberOfPixels, uint* output)
{
- int endIdx = startIdx + numberOfPixels;
- int offset = startIdx - width;
- for (int x = startIdx; x < endIdx; ++x)
+ for (int x = 0; x < numberOfPixels; ++x)
{
- uint pred = Predictor10(output[x - 1], output, offset++);
+ uint pred = Predictor10(output[x - 1], upper + x);
output[x] = AddPixels(input[x], pred);
}
}
- private static void PredictorAdd11(Span input, int startIdx, int numberOfPixels, int width, Span output)
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void PredictorAdd11(uint* input, uint* upper, int numberOfPixels, uint* output)
{
- int endIdx = startIdx + numberOfPixels;
- int offset = startIdx - width;
- for (int x = startIdx; x < endIdx; ++x)
+ for (int x = 0; x < numberOfPixels; ++x)
{
- uint pred = Predictor11(output[x - 1], output, offset++);
+ uint pred = Predictor11(output[x - 1], upper + x);
output[x] = AddPixels(input[x], pred);
}
}
- private static void PredictorAdd12(Span input, int startIdx, int numberOfPixels, int width, Span output)
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void PredictorAdd12(uint* input, uint* upper, int numberOfPixels, uint* output)
{
- int endIdx = startIdx + numberOfPixels;
- int offset = startIdx - width;
- for (int x = startIdx; x < endIdx; ++x)
+ for (int x = 0; x < numberOfPixels; ++x)
{
- uint pred = Predictor12(output[x - 1], output, offset++);
+ uint pred = Predictor12(output[x - 1], upper + x);
output[x] = AddPixels(input[x], pred);
}
}
- private static void PredictorAdd13(Span input, int startIdx, int numberOfPixels, int width, Span output)
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void PredictorAdd13(uint* input, uint* upper, int numberOfPixels, uint* output)
{
- int endIdx = startIdx + numberOfPixels;
- int offset = startIdx - width;
- for (int x = startIdx; x < endIdx; ++x)
+ for (int x = 0; x < numberOfPixels; ++x)
{
- uint pred = Predictor13(output[x - 1], output, offset++);
+ uint pred = Predictor13(output[x - 1], upper + x);
output[x] = AddPixels(input[x], pred);
}
}
[MethodImpl(InliningOptions.ShortMethod)]
- public static uint Predictor2(Span top, int idx)
+ public static uint Predictor2(uint left, uint* top)
{
- return top[idx];
+ return top[0];
}
[MethodImpl(InliningOptions.ShortMethod)]
- public static uint Predictor3(Span top, int idx)
+ public static uint Predictor3(uint left, uint* top)
{
- return top[idx + 1];
+ return top[1];
}
[MethodImpl(InliningOptions.ShortMethod)]
- public static uint Predictor4(Span top, int idx)
+ public static uint Predictor4(uint left, uint* top)
{
- return top[idx - 1];
+ return top[-1];
}
[MethodImpl(InliningOptions.ShortMethod)]
- public static uint Predictor5(uint left, Span top, int idx)
+ public static uint Predictor5(uint left, uint* top)
{
- uint pred = Average3(left, top[idx], top[idx + 1]);
- return pred;
+ return Average3(left, top[0], top[1]);
}
[MethodImpl(InliningOptions.ShortMethod)]
- public static uint Predictor6(uint left, Span top, int idx)
+ public static uint Predictor6(uint left, uint* top)
{
- uint pred = Average2(left, top[idx - 1]);
- return pred;
+ return Average2(left, top[-1]);
}
[MethodImpl(InliningOptions.ShortMethod)]
- public static uint Predictor7(uint left, Span top, int idx)
+ public static uint Predictor7(uint left, uint* top)
{
- uint pred = Average2(left, top[idx]);
- return pred;
+ return Average2(left, top[0]);
}
[MethodImpl(InliningOptions.ShortMethod)]
- public static uint Predictor8(Span top, int idx)
+ public static uint Predictor8(uint left, uint* top)
{
- uint pred = Average2(top[idx - 1], top[idx]);
- return pred;
+ return Average2(top[-1], top[0]);
}
[MethodImpl(InliningOptions.ShortMethod)]
- public static uint Predictor9(Span top, int idx)
+ public static uint Predictor9(uint left, uint* top)
{
- uint pred = Average2(top[idx], top[idx + 1]);
- return pred;
+ return Average2(top[0], top[1]);
}
[MethodImpl(InliningOptions.ShortMethod)]
- public static uint Predictor10(uint left, Span top, int idx)
+ public static uint Predictor10(uint left, uint* top)
{
- uint pred = Average4(left, top[idx - 1], top[idx], top[idx + 1]);
- return pred;
+ return Average4(left, top[-1], top[0], top[1]);
}
[MethodImpl(InliningOptions.ShortMethod)]
- public static uint Predictor11(uint left, Span top, int idx)
+ public static uint Predictor11(uint left, uint* top)
{
- uint pred = Select(top[idx], left, top[idx - 1]);
- return pred;
+ return Select(top[0], left, top[-1]);
}
[MethodImpl(InliningOptions.ShortMethod)]
- public static uint Predictor12(uint left, Span top, int idx)
+ public static uint Predictor12(uint left, uint* top)
{
- uint pred = ClampedAddSubtractFull(left, top[idx], top[idx - 1]);
- return pred;
+ return ClampedAddSubtractFull(left, top[0], top[-1]);
}
[MethodImpl(InliningOptions.ShortMethod)]
- public static uint Predictor13(uint left, Span top, int idx)
+ public static uint Predictor13(uint left, uint* top)
{
- uint pred = ClampedAddSubtractHalf(left, top[idx], top[idx - 1]);
- return pred;
+ return ClampedAddSubtractHalf(left, top[0], top[-1]);
}
[MethodImpl(InliningOptions.ShortMethod)]
- public static void PredictorSub0(Span input, int numPixels, Span 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);
}
}
[MethodImpl(InliningOptions.ShortMethod)]
- public static void PredictorSub1(Span input, int idx, int numPixels, Span 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)]
- public static void PredictorSub2(Span input, int idx, Span upper, int numPixels, Span 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);
- output[x] = SubPixels(input[idx + x], pred);
+ uint pred = Predictor2(input[x - 1], upper + x);
+ output[x] = SubPixels(input[x], pred);
}
}
[MethodImpl(InliningOptions.ShortMethod)]
- public static void PredictorSub3(Span input, int idx, Span upper, int numPixels, Span 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);
- output[x] = SubPixels(input[idx + x], pred);
+ uint pred = Predictor3(input[x - 1], upper + x);
+ output[x] = SubPixels(input[x], pred);
}
}
[MethodImpl(InliningOptions.ShortMethod)]
- public static void PredictorSub4(Span input, int idx, Span upper, int numPixels, Span 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);
- output[x] = SubPixels(input[idx + x], pred);
+ uint pred = Predictor4(input[x - 1], upper + x);
+ output[x] = SubPixels(input[x], pred);
}
}
[MethodImpl(InliningOptions.ShortMethod)]
- public static void PredictorSub5(Span input, int idx, Span upper, int numPixels, Span 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);
- output[x] = SubPixels(input[idx + x], pred);
+ uint pred = Predictor5(input[x - 1], upper + x);
+ output[x] = SubPixels(input[x], pred);
}
}
[MethodImpl(InliningOptions.ShortMethod)]
- public static void PredictorSub6(Span input, int idx, Span upper, int numPixels, Span 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);
- output[x] = SubPixels(input[idx + x], pred);
+ uint pred = Predictor6(input[x - 1], upper + x);
+ output[x] = SubPixels(input[x], pred);
}
}
[MethodImpl(InliningOptions.ShortMethod)]
- public static void PredictorSub7(Span input, int idx, Span upper, int numPixels, Span 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);
- output[x] = SubPixels(input[idx + x], pred);
+ uint pred = Predictor7(input[x - 1], upper + x);
+ output[x] = SubPixels(input[x], pred);
}
}
[MethodImpl(InliningOptions.ShortMethod)]
- public static void PredictorSub8(Span input, int idx, Span upper, int numPixels, Span 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);
- output[x] = SubPixels(input[idx + x], pred);
+ uint pred = Predictor8(input[x - 1], upper + x);
+ output[x] = SubPixels(input[x], pred);
}
}
[MethodImpl(InliningOptions.ShortMethod)]
- public static void PredictorSub9(Span input, int idx, Span upper, int numPixels, Span 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);
- output[x] = SubPixels(input[idx + x], pred);
+ uint pred = Predictor9(input[x - 1], upper + x);
+ output[x] = SubPixels(input[x], pred);
}
}
[MethodImpl(InliningOptions.ShortMethod)]
- public static void PredictorSub10(Span input, int idx, Span upper, int numPixels, Span 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);
- output[x] = SubPixels(input[idx + x], pred);
+ uint pred = Predictor10(input[x - 1], upper + x);
+ output[x] = SubPixels(input[x], pred);
}
}
[MethodImpl(InliningOptions.ShortMethod)]
- public static void PredictorSub11(Span input, int idx, Span upper, int numPixels, Span 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);
- output[x] = SubPixels(input[idx + x], pred);
+ uint pred = Predictor11(input[x - 1], upper + x);
+ output[x] = SubPixels(input[x], pred);
}
}
[MethodImpl(InliningOptions.ShortMethod)]
- public static void PredictorSub12(Span input, int idx, Span upper, int numPixels, Span 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);
- output[x] = SubPixels(input[idx + x], pred);
+ uint pred = Predictor12(input[x - 1], upper + x);
+ output[x] = SubPixels(input[x], pred);
}
}
[MethodImpl(InliningOptions.ShortMethod)]
- public static void PredictorSub13(Span input, int idx, Span upper, int numPixels, Span 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);
- output[x] = SubPixels(input[idx + x], pred);
+ uint pred = Predictor13(input[x - 1], upper + x);
+ output[x] = SubPixels(input[x], pred);
}
}
diff --git a/src/ImageSharp/Formats/WebP/Lossless/PredictorEncoder.cs b/src/ImageSharp/Formats/WebP/Lossless/PredictorEncoder.cs
index bc2c7b5946..c7783de40a 100644
--- a/src/ImageSharp/Formats/WebP/Lossless/PredictorEncoder.cs
+++ b/src/ImageSharp/Formats/WebP/Lossless/PredictorEncoder.cs
@@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
///
/// Image transform methods for the lossless webp encoder.
///
- internal static class PredictorEncoder
+ internal static unsafe class PredictorEncoder
{
private const int GreenRedToBlueNumAxis = 8;
@@ -27,27 +27,61 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
/// with respect to predictions. If nearLosslessQuality < 100, applies
/// near lossless processing, shaving off more bits of residuals for lower qualities.
///
- public static void ResidualImage(int width, int height, int bits, Span argb, Span argbScratch, Span image, int nearLosslessQuality, bool exact, bool usedSubtractGreen)
+ public static void ResidualImage(
+ int width,
+ int height,
+ int bits,
+ Span argb,
+ Span argbScratch,
+ Span image,
+ int nearLosslessQuality,
+ bool exact,
+ bool usedSubtractGreen)
{
int tilesPerRow = LosslessUtils.SubSampleSize(width, bits);
int tilesPerCol = LosslessUtils.SubSampleSize(height, bits);
int maxQuantization = 1 << LosslessUtils.NearLosslessBits(nearLosslessQuality);
+
+ // TODO: Can we optimize this?
var histo = new int[4][];
for (int i = 0; i < 4; i++)
{
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));
}
}
- 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 argb, Span image)
@@ -261,113 +295,130 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
/// the deviation further to pixels which depend on the current pixel for their
/// predictions.
///
- private static void GetResidual(int width, int height, Span upperRow, Span currentRow, Span maxDiffs, int mode, int xStart, int xEnd, int y, int maxQuantization, bool exact, bool usedSubtractGreen, Span output)
+ private static void GetResidual(
+ int width,
+ int height,
+ Span upperRowSpan,
+ Span currentRowSpan,
+ Span maxDiffs,
+ int mode,
+ int xStart,
+ int xEnd,
+ int y,
+ int maxQuantization,
+ bool exact,
+ bool usedSubtractGreen,
+ Span output)
{
if (exact)
{
- PredictBatch(mode, xStart, y, xEnd - xStart, currentRow, upperRow, output);
+ PredictBatch(mode, xStart, y, xEnd - xStart, currentRowSpan, upperRowSpan, output);
}
else
{
- for (int x = xStart; x < xEnd; x++)
+ fixed (uint* currentRow = currentRowSpan)
+ fixed (uint* upperRow = upperRowSpan)
{
- uint predict = 0;
- uint residual;
- if (y == 0)
- {
- predict = (x == 0) ? WebPConstants.ArgbBlack : currentRow[x - 1]; // Left.
- }
- else if (x == 0)
- {
- predict = upperRow[x]; // Top.
- }
- else
+ for (int x = xStart; x < xEnd; ++x)
{
- switch (mode)
+ uint predict = 0;
+ uint residual;
+ if (y == 0)
{
- case 0:
- predict = WebPConstants.ArgbBlack;
- break;
- case 1:
- predict = currentRow[x - 1];
- break;
- case 2:
- predict = LosslessUtils.Predictor2(upperRow, x);
- break;
- case 3:
- predict = LosslessUtils.Predictor3(upperRow, x);
- break;
- case 4:
- predict = LosslessUtils.Predictor4(upperRow, x);
- break;
- case 5:
- predict = LosslessUtils.Predictor5(currentRow[x - 1], upperRow, x);
- break;
- case 6:
- predict = LosslessUtils.Predictor6(currentRow[x - 1], upperRow, x);
- break;
- case 7:
- predict = LosslessUtils.Predictor7(currentRow[x - 1], upperRow, x);
- break;
- case 8:
- predict = LosslessUtils.Predictor8(upperRow, x);
- break;
- case 9:
- predict = LosslessUtils.Predictor9(upperRow, x);
- break;
- case 10:
- 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;
+ predict = (x == 0) ? WebPConstants.ArgbBlack : currentRow[x - 1]; // Left.
+ }
+ else if (x == 0)
+ {
+ predict = upperRow[x]; // Top.
+ }
+ else
+ {
+ switch (mode)
+ {
+ case 0:
+ predict = WebPConstants.ArgbBlack;
+ break;
+ case 1:
+ predict = currentRow[x - 1];
+ break;
+ case 2:
+ predict = LosslessUtils.Predictor2(currentRow[x - 1], upperRow + x);
+ break;
+ case 3:
+ predict = LosslessUtils.Predictor3(currentRow[x - 1], upperRow + x);
+ break;
+ case 4:
+ predict = LosslessUtils.Predictor4(currentRow[x - 1], upperRow + x);
+ break;
+ case 5:
+ predict = LosslessUtils.Predictor5(currentRow[x - 1], upperRow + x);
+ break;
+ case 6:
+ predict = LosslessUtils.Predictor6(currentRow[x - 1], upperRow + x);
+ break;
+ case 7:
+ predict = LosslessUtils.Predictor7(currentRow[x - 1], upperRow + x);
+ break;
+ case 8:
+ predict = LosslessUtils.Predictor8(currentRow[x - 1], upperRow + x);
+ break;
+ case 9:
+ predict = LosslessUtils.Predictor9(currentRow[x - 1], upperRow + x);
+ break;
+ case 10:
+ 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)
- {
- residual = LosslessUtils.SubPixels(currentRow[x], predict);
- }
- else
- {
- residual = NearLossless(currentRow[x], predict, maxQuantization, maxDiffs[x], usedSubtractGreen);
+ if (maxQuantization == 1 || mode == 0 || y == 0 || y == height - 1 || x == 0 || x == width - 1)
+ {
+ residual = LosslessUtils.SubPixels(currentRow[x], predict);
+ }
+ else
+ {
+ residual = NearLossless(currentRow[x], predict, maxQuantization, maxDiffs[x], usedSubtractGreen);
- // Update the source image.
- currentRow[x] = LosslessUtils.AddPixels(predict, residual);
+ // Update the source image.
+ 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 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)
+ if ((currentRow[x] & MaskAlpha) == 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 +537,18 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
Span upperRow = argbScratch;
Span currentRow = upperRow.Slice(width + 1);
Span currentMaxDiffs = MemoryMarshal.Cast(currentRow.Slice(width + 1));
+
+ // TODO: This should be wrapped in a condition.
Span lowerMaxDiffs = currentMaxDiffs.Slice(width);
- for (int y = 0; y < height; y++)
+ for (int y = 0; y < height; ++y)
{
Span tmp32 = upperRow;
upperRow = currentRow;
currentRow = tmp32;
Span src = argb.Slice(y * width, width + ((y + 1) < height ? 1 : 0));
src.CopyTo(currentRow);
+
+ // TODO: Near lossless conditional.
if (maxQuantization > 1)
{
// Compute max_diffs for the lower row now, because that needs the
@@ -537,77 +592,90 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
}
}
- private static void PredictBatch(int mode, int xStart, int y, int numPixels, Span current, Span upper, Span output)
+ private static void PredictBatch(
+ int mode,
+ int xStart,
+ int y,
+ int numPixels,
+ Span currentSpan,
+ Span upperSpan,
+ Span 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.
- LosslessUtils.PredictorSub0(current, 1, output);
+ if (y == 0)
+ {
+ // 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.
- LosslessUtils.PredictorSub2(current, 0, upper, 1, output);
+ // Left one.
+ LosslessUtils.PredictorSub1(current + xStart, numPixels, output);
}
-
- xStart++;
- output = output.Slice(1);
- numPixels--;
- }
-
- if (y == 0)
- {
- // Left one.
- LosslessUtils.PredictorSub1(current, xStart, numPixels, output);
- }
- else
- {
- switch (mode)
+ else
{
- case 0:
- LosslessUtils.PredictorSub0(current, numPixels, output);
- break;
- case 1:
- LosslessUtils.PredictorSub1(current, xStart, numPixels, output);
- break;
- case 2:
- LosslessUtils.PredictorSub2(current, xStart, upper.Slice(xStart), numPixels, output);
- break;
- case 3:
- LosslessUtils.PredictorSub3(current, xStart, upper.Slice(xStart), numPixels, output);
- break;
- case 4:
- LosslessUtils.PredictorSub4(current, xStart, upper.Slice(xStart), numPixels, output);
- break;
- case 5:
- LosslessUtils.PredictorSub5(current, xStart, upper.Slice(xStart), numPixels, output);
- break;
- case 6:
- LosslessUtils.PredictorSub6(current, xStart, upper.Slice(xStart), numPixels, output);
- break;
- case 7:
- LosslessUtils.PredictorSub7(current, xStart, upper.Slice(xStart), numPixels, output);
- break;
- case 8:
- LosslessUtils.PredictorSub8(current, xStart, upper.Slice(xStart), numPixels, output);
- break;
- case 9:
- LosslessUtils.PredictorSub9(current, xStart, upper.Slice(xStart), numPixels, output);
- break;
- case 10:
- LosslessUtils.PredictorSub10(current, xStart, upper.Slice(xStart), numPixels, output);
- break;
- case 11:
- LosslessUtils.PredictorSub11(current, xStart, upper.Slice(xStart), numPixels, output);
- break;
- case 12:
- LosslessUtils.PredictorSub12(current, xStart, upper.Slice(xStart), numPixels, output);
- break;
- case 13:
- LosslessUtils.PredictorSub13(current, xStart, upper.Slice(xStart), numPixels, output);
- break;
+ switch (mode)
+ {
+ case 0:
+ LosslessUtils.PredictorSub0(current, numPixels, output);
+ break;
+ case 1:
+ LosslessUtils.PredictorSub1(current + xStart, numPixels, output);
+ break;
+ case 2:
+ LosslessUtils.PredictorSub2(current + xStart, upper + xStart, numPixels, output);
+ break;
+ case 3:
+ LosslessUtils.PredictorSub3(current + xStart, upper + xStart, numPixels, output);
+ break;
+ case 4:
+ LosslessUtils.PredictorSub4(current + xStart, upper + xStart, numPixels, output);
+ break;
+ case 5:
+ LosslessUtils.PredictorSub5(current + xStart, upper + xStart, numPixels, output);
+ break;
+ case 6:
+ LosslessUtils.PredictorSub6(current + xStart, upper + xStart, numPixels, output);
+ break;
+ case 7:
+ LosslessUtils.PredictorSub7(current + xStart, upper + xStart, numPixels, output);
+ break;
+ case 8:
+ LosslessUtils.PredictorSub8(current + xStart, upper + xStart, numPixels, output);
+ break;
+ case 9:
+ LosslessUtils.PredictorSub9(current + xStart, upper + xStart, numPixels, output);
+ break;
+ case 10:
+ LosslessUtils.PredictorSub10(current + xStart, upper + xStart, numPixels, output);
+ break;
+ case 11:
+ LosslessUtils.PredictorSub11(current + xStart, upper + xStart, numPixels, output);
+ break;
+ case 12:
+ LosslessUtils.PredictorSub12(current + xStart, upper + xStart, numPixels, output);
+ break;
+ case 13:
+ LosslessUtils.PredictorSub13(current + xStart, upper + xStart, numPixels, output);
+ break;
+ }
}
}
}
@@ -627,7 +695,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
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 down = argb[stride + x];