|
|
@ -13,19 +13,69 @@ using System.Runtime.Intrinsics.X86; |
|
|
// ReSharper disable InconsistentNaming
|
|
|
// ReSharper disable InconsistentNaming
|
|
|
namespace SixLabors.ImageSharp.Formats.Webp.Lossy |
|
|
namespace SixLabors.ImageSharp.Formats.Webp.Lossy |
|
|
{ |
|
|
{ |
|
|
internal static unsafe class LossyUtils |
|
|
internal static class LossyUtils |
|
|
{ |
|
|
{ |
|
|
|
|
|
#if SUPPORTS_RUNTIME_INTRINSICS
|
|
|
|
|
|
private static readonly Vector128<byte> Mean16x4Mask = Vector128.Create((short)0x00ff).AsByte(); |
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
// Note: method name in libwebp reference implementation is called VP8SSE16x16.
|
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
public static int Vp8Sse16X16(Span<byte> a, Span<byte> b) => GetSse(a, b, 16, 16); |
|
|
public static int Vp8_Sse16X16(Span<byte> a, Span<byte> b) => Vp8_SseNxN(a, b, 16, 16); |
|
|
|
|
|
|
|
|
|
|
|
// Note: method name in libwebp reference implementation is called VP8SSE16x8.
|
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
public static int Vp8Sse16X8(Span<byte> a, Span<byte> b) => GetSse(a, b, 16, 8); |
|
|
public static int Vp8_Sse16X8(Span<byte> a, Span<byte> b) => Vp8_SseNxN(a, b, 16, 8); |
|
|
|
|
|
|
|
|
|
|
|
// Note: method name in libwebp reference implementation is called VP8SSE4x4.
|
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
public static int Vp8Sse4X4(Span<byte> a, Span<byte> b) => GetSse(a, b, 4, 4); |
|
|
public static int Vp8_Sse4X4(Span<byte> a, Span<byte> b) |
|
|
|
|
|
{ |
|
|
|
|
|
#if SUPPORTS_RUNTIME_INTRINSICS
|
|
|
|
|
|
if (Sse2.IsSupported) |
|
|
|
|
|
{ |
|
|
|
|
|
// Load values.
|
|
|
|
|
|
ref byte aRef = ref MemoryMarshal.GetReference(a); |
|
|
|
|
|
Vector128<byte> a0 = Unsafe.As<byte, Vector128<byte>>(ref aRef); |
|
|
|
|
|
Vector128<byte> a1 = Unsafe.As<byte, Vector128<byte>>(ref Unsafe.Add(ref aRef, WebpConstants.Bps)); |
|
|
|
|
|
Vector128<byte> a2 = Unsafe.As<byte, Vector128<byte>>(ref Unsafe.Add(ref aRef, WebpConstants.Bps * 2)); |
|
|
|
|
|
Vector128<byte> a3 = Unsafe.As<byte, Vector128<byte>>(ref Unsafe.Add(ref aRef, WebpConstants.Bps * 3)); |
|
|
|
|
|
ref byte bRef = ref MemoryMarshal.GetReference(b); |
|
|
|
|
|
Vector128<byte> b0 = Unsafe.As<byte, Vector128<byte>>(ref bRef); |
|
|
|
|
|
Vector128<byte> b1 = Unsafe.As<byte, Vector128<byte>>(ref Unsafe.Add(ref bRef, WebpConstants.Bps)); |
|
|
|
|
|
Vector128<byte> b2 = Unsafe.As<byte, Vector128<byte>>(ref Unsafe.Add(ref bRef, WebpConstants.Bps * 2)); |
|
|
|
|
|
Vector128<byte> b3 = Unsafe.As<byte, Vector128<byte>>(ref Unsafe.Add(ref bRef, WebpConstants.Bps * 3)); |
|
|
|
|
|
|
|
|
|
|
|
// Combine pair of lines.
|
|
|
|
|
|
Vector128<int> a01 = Sse2.UnpackLow(a0.AsInt32(), a1.AsInt32()); |
|
|
|
|
|
Vector128<int> a23 = Sse2.UnpackLow(a2.AsInt32(), a3.AsInt32()); |
|
|
|
|
|
Vector128<int> b01 = Sse2.UnpackLow(b0.AsInt32(), b1.AsInt32()); |
|
|
|
|
|
Vector128<int> b23 = Sse2.UnpackLow(b2.AsInt32(), b3.AsInt32()); |
|
|
|
|
|
|
|
|
|
|
|
// Convert to 16b.
|
|
|
|
|
|
Vector128<byte> a01s = Sse2.UnpackLow(a01.AsByte(), Vector128<byte>.Zero); |
|
|
|
|
|
Vector128<byte> a23s = Sse2.UnpackLow(a23.AsByte(), Vector128<byte>.Zero); |
|
|
|
|
|
Vector128<byte> b01s = Sse2.UnpackLow(b01.AsByte(), Vector128<byte>.Zero); |
|
|
|
|
|
Vector128<byte> b23s = Sse2.UnpackLow(b23.AsByte(), Vector128<byte>.Zero); |
|
|
|
|
|
|
|
|
|
|
|
// subtract, square and accumulate.
|
|
|
|
|
|
Vector128<byte> d0 = Sse2.SubtractSaturate(a01s, b01s); |
|
|
|
|
|
Vector128<byte> d1 = Sse2.SubtractSaturate(a23s, b23s); |
|
|
|
|
|
Vector128<int> e0 = Sse2.MultiplyAddAdjacent(d0.AsInt16(), d0.AsInt16()); |
|
|
|
|
|
Vector128<int> e1 = Sse2.MultiplyAddAdjacent(d1.AsInt16(), d1.AsInt16()); |
|
|
|
|
|
Vector128<int> sum = Sse2.Add(e0, e1); |
|
|
|
|
|
|
|
|
|
|
|
return Numerics.ReduceSum(sum); |
|
|
|
|
|
} |
|
|
|
|
|
else |
|
|
|
|
|
#endif
|
|
|
|
|
|
{ |
|
|
|
|
|
return Vp8_SseNxN(a, b, 4, 4); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
public static int GetSse(Span<byte> a, Span<byte> b, int w, int h) |
|
|
public static int Vp8_SseNxN(Span<byte> a, Span<byte> b, int w, int h) |
|
|
{ |
|
|
{ |
|
|
int count = 0; |
|
|
int count = 0; |
|
|
int aOffset = 0; |
|
|
int aOffset = 0; |
|
|
@ -84,7 +134,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy |
|
|
#if SUPPORTS_RUNTIME_INTRINSICS
|
|
|
#if SUPPORTS_RUNTIME_INTRINSICS
|
|
|
if (Sse41.IsSupported) |
|
|
if (Sse41.IsSupported) |
|
|
{ |
|
|
{ |
|
|
int diffSum = TTransformSse41(a, b, w, scratch); |
|
|
int diffSum = TTransformSse41(a, b, w); |
|
|
return Math.Abs(diffSum) >> 5; |
|
|
return Math.Abs(diffSum) >> 5; |
|
|
} |
|
|
} |
|
|
else |
|
|
else |
|
|
@ -611,11 +661,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy |
|
|
/// Returns the weighted sum of the absolute value of transformed coefficients.
|
|
|
/// Returns the weighted sum of the absolute value of transformed coefficients.
|
|
|
/// w[] contains a row-major 4 by 4 symmetric matrix.
|
|
|
/// w[] contains a row-major 4 by 4 symmetric matrix.
|
|
|
/// </summary>
|
|
|
/// </summary>
|
|
|
public static int TTransformSse41(Span<byte> inputA, Span<byte> inputB, Span<ushort> w, Span<int> scratch) |
|
|
public static int TTransformSse41(Span<byte> inputA, Span<byte> inputB, Span<ushort> w) |
|
|
{ |
|
|
{ |
|
|
Span<int> sum = scratch.Slice(0, 4); |
|
|
|
|
|
sum.Clear(); |
|
|
|
|
|
|
|
|
|
|
|
// Load and combine inputs.
|
|
|
// Load and combine inputs.
|
|
|
Vector128<byte> ina0 = Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(inputA)); |
|
|
Vector128<byte> ina0 = Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(inputA)); |
|
|
Vector128<byte> ina1 = Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(inputA.Slice(WebpConstants.Bps, 16))); |
|
|
Vector128<byte> ina1 = Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(inputA.Slice(WebpConstants.Bps, 16))); |
|
|
@ -720,9 +767,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy |
|
|
// difference of weighted sums.
|
|
|
// difference of weighted sums.
|
|
|
Vector128<int> result = Sse2.Subtract(ab0ab2Sum.AsInt32(), b0w0bb2w8Sum.AsInt32()); |
|
|
Vector128<int> result = Sse2.Subtract(ab0ab2Sum.AsInt32(), b0w0bb2w8Sum.AsInt32()); |
|
|
|
|
|
|
|
|
ref int outputRef = ref MemoryMarshal.GetReference(sum); |
|
|
return Numerics.ReduceSum(result); |
|
|
Unsafe.As<int, Vector128<int>>(ref outputRef) = result.AsInt32(); |
|
|
|
|
|
return sum[3] + sum[2] + sum[1] + sum[0]; |
|
|
|
|
|
} |
|
|
} |
|
|
#endif
|
|
|
#endif
|
|
|
|
|
|
|
|
|
@ -735,7 +780,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy |
|
|
public static void TransformOne(Span<short> src, Span<byte> dst, Span<int> scratch) |
|
|
public static void TransformOne(Span<short> src, Span<byte> dst, Span<int> scratch) |
|
|
{ |
|
|
{ |
|
|
Span<int> tmp = scratch.Slice(0, 16); |
|
|
Span<int> tmp = scratch.Slice(0, 16); |
|
|
tmp.Clear(); |
|
|
|
|
|
int tmpOffset = 0; |
|
|
int tmpOffset = 0; |
|
|
for (int srcOffset = 0; srcOffset < 4; srcOffset++) |
|
|
for (int srcOffset = 0; srcOffset < 4; srcOffset++) |
|
|
{ |
|
|
{ |
|
|
@ -938,26 +982,55 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy |
|
|
FilterLoop24(v, offsetPlus4, 1, stride, 8, thresh, ithresh, hevThresh); |
|
|
FilterLoop24(v, offsetPlus4, 1, stride, 8, thresh, ithresh, hevThresh); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
public static void Mean16x4(Span<byte> input, Span<uint> dc) |
|
|
public static uint LoadUv(byte u, byte v) => |
|
|
|
|
|
(uint)(u | (v << 16)); // We process u and v together stashed into 32bit(16bit each).
|
|
|
|
|
|
|
|
|
|
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
|
|
|
public static void YuvToBgr(int y, int u, int v, Span<byte> bgr) |
|
|
|
|
|
{ |
|
|
{ |
|
|
bgr[0] = (byte)YuvToB(y, u); |
|
|
#if SUPPORTS_RUNTIME_INTRINSICS
|
|
|
bgr[1] = (byte)YuvToG(y, u, v); |
|
|
if (Ssse3.IsSupported) |
|
|
bgr[2] = (byte)YuvToR(y, v); |
|
|
{ |
|
|
} |
|
|
Vector128<byte> a0 = Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(input)); |
|
|
|
|
|
Vector128<byte> a1 = Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(input.Slice(WebpConstants.Bps, 16))); |
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
Vector128<byte> a2 = Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(input.Slice(WebpConstants.Bps * 2, 16))); |
|
|
public static int YuvToB(int y, int u) => Clip8(MultHi(y, 19077) + MultHi(u, 33050) - 17685); |
|
|
Vector128<byte> a3 = Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(input.Slice(WebpConstants.Bps * 3, 16))); |
|
|
|
|
|
Vector128<short> b0 = Sse2.ShiftRightLogical(a0.AsInt16(), 8); // hi byte
|
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
Vector128<short> b1 = Sse2.ShiftRightLogical(a1.AsInt16(), 8); |
|
|
public static int YuvToG(int y, int u, int v) => Clip8(MultHi(y, 19077) - MultHi(u, 6419) - MultHi(v, 13320) + 8708); |
|
|
Vector128<short> b2 = Sse2.ShiftRightLogical(a2.AsInt16(), 8); |
|
|
|
|
|
Vector128<short> b3 = Sse2.ShiftRightLogical(a3.AsInt16(), 8); |
|
|
|
|
|
Vector128<byte> c0 = Sse2.And(a0, Mean16x4Mask); // lo byte
|
|
|
|
|
|
Vector128<byte> c1 = Sse2.And(a1, Mean16x4Mask); |
|
|
|
|
|
Vector128<byte> c2 = Sse2.And(a2, Mean16x4Mask); |
|
|
|
|
|
Vector128<byte> c3 = Sse2.And(a3, Mean16x4Mask); |
|
|
|
|
|
Vector128<int> d0 = Sse2.Add(b0.AsInt32(), c0.AsInt32()); |
|
|
|
|
|
Vector128<int> d1 = Sse2.Add(b1.AsInt32(), c1.AsInt32()); |
|
|
|
|
|
Vector128<int> d2 = Sse2.Add(b2.AsInt32(), c2.AsInt32()); |
|
|
|
|
|
Vector128<int> d3 = Sse2.Add(b3.AsInt32(), c3.AsInt32()); |
|
|
|
|
|
Vector128<int> e0 = Sse2.Add(d0, d1); |
|
|
|
|
|
Vector128<int> e1 = Sse2.Add(d2, d3); |
|
|
|
|
|
Vector128<int> f0 = Sse2.Add(e0, e1); |
|
|
|
|
|
Vector128<short> hadd = Ssse3.HorizontalAdd(f0.AsInt16(), f0.AsInt16()); |
|
|
|
|
|
Vector128<uint> wide = Sse2.UnpackLow(hadd, Vector128<short>.Zero).AsUInt32(); |
|
|
|
|
|
|
|
|
|
|
|
ref uint outputRef = ref MemoryMarshal.GetReference(dc); |
|
|
|
|
|
Unsafe.As<uint, Vector128<uint>>(ref outputRef) = wide; |
|
|
|
|
|
} |
|
|
|
|
|
else |
|
|
|
|
|
#endif
|
|
|
|
|
|
{ |
|
|
|
|
|
for (int k = 0; k < 4; k++) |
|
|
|
|
|
{ |
|
|
|
|
|
uint avg = 0; |
|
|
|
|
|
for (int y = 0; y < 4; y++) |
|
|
|
|
|
{ |
|
|
|
|
|
for (int x = 0; x < 4; x++) |
|
|
|
|
|
{ |
|
|
|
|
|
avg += input[x + (y * WebpConstants.Bps)]; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
dc[k] = avg; |
|
|
public static int YuvToR(int y, int v) => Clip8(MultHi(y, 19077) + MultHi(v, 26149) - 14234); |
|
|
input = input.Slice(4); // go to next 4x4 block.
|
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
public static byte Avg2(byte a, byte b) => (byte)((a + b + 1) >> 1); |
|
|
public static byte Avg2(byte a, byte b) => (byte)((a + b + 1) >> 1); |
|
|
@ -1163,9 +1236,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy |
|
|
return WebpLookupTables.Abs0(p1 - p0) > thresh || WebpLookupTables.Abs0(q1 - q0) > thresh; |
|
|
return WebpLookupTables.Abs0(p1 - p0) > thresh || WebpLookupTables.Abs0(q1 - q0) > thresh; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
|
|
|
private static int MultHi(int v, int coeff) => (v * coeff) >> 8; |
|
|
|
|
|
|
|
|
|
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
private static void Store(Span<byte> dst, int x, int y, int v) |
|
|
private static void Store(Span<byte> dst, int x, int y, int v) |
|
|
{ |
|
|
{ |
|
|
@ -1188,13 +1258,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy |
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
private static int Mul2(int a) => (a * 35468) >> 16; |
|
|
private static int Mul2(int a) => (a * 35468) >> 16; |
|
|
|
|
|
|
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
|
|
|
private static byte Clip8(int v) |
|
|
|
|
|
{ |
|
|
|
|
|
int yuvMask = (256 << 6) - 1; |
|
|
|
|
|
return (byte)((v & ~yuvMask) == 0 ? v >> 6 : v < 0 ? 0 : 255); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
private static void Put8x8uv(byte value, Span<byte> dst) |
|
|
private static void Put8x8uv(byte value, Span<byte> dst) |
|
|
{ |
|
|
{ |
|
|
|