mirror of https://github.com/SixLabors/ImageSharp
8 changed files with 533 additions and 180 deletions
@ -1,157 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
using System.Runtime.CompilerServices; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|
||||
|
|
||||
internal class Av1DctDctInverseTransformer |
|
||||
{ |
|
||||
private const int UnitQuantizationShift = 2; |
|
||||
|
|
||||
internal static void InverseTransformAdd(ref int coefficients, Span<byte> readBuffer, int readStride, Span<byte> writeBuffer, int writeStride, Av1TransformFunctionParameters transformFunctionParameters) |
|
||||
{ |
|
||||
Guard.IsTrue(transformFunctionParameters.TransformType == Av1TransformType.DctDct, nameof(transformFunctionParameters.TransformType), "This class implements DCT-DCT transformations only."); |
|
||||
|
|
||||
switch (transformFunctionParameters.TransformSize) |
|
||||
{ |
|
||||
case Av1TransformSize.Size4x4: |
|
||||
InverseWhalshHadamard4x4(ref coefficients, ref readBuffer[0], readStride, ref writeBuffer[0], writeStride, transformFunctionParameters.EndOfBuffer, transformFunctionParameters.BitDepth); |
|
||||
break; |
|
||||
default: |
|
||||
throw new NotImplementedException("Only 4x4 transformation size supported for now"); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// SVT: highbd_iwht4x4_add
|
|
||||
/// </summary>
|
|
||||
private static void InverseWhalshHadamard4x4(ref int input, ref byte destinationForRead, int strideForRead, ref byte destinationForWrite, int strideForWrite, int endOfBuffer, int bitDepth) |
|
||||
{ |
|
||||
if (endOfBuffer > 1) |
|
||||
{ |
|
||||
InverseWhalshHadamard4x4Add16(ref input, ref destinationForRead, strideForRead, ref destinationForWrite, strideForWrite, bitDepth); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
InverseWhalshHadamard4x4Add1(ref input, ref destinationForRead, strideForRead, ref destinationForWrite, strideForWrite, bitDepth); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// SVT: svt_av1_highbd_iwht4x4_16_add_c
|
|
||||
/// </summary>
|
|
||||
private static void InverseWhalshHadamard4x4Add16(ref int input, ref byte destinationForRead, int strideForRead, ref byte destinationForWrite, int strideForWrite, int bitDepth) |
|
||||
{ |
|
||||
/* 4-point reversible, orthonormal inverse Walsh-Hadamard in 3.5 adds, |
|
||||
0.5 shifts per pixel. */ |
|
||||
int i; |
|
||||
Span<ushort> output = stackalloc ushort[16]; |
|
||||
ushort a1, b1, c1, d1, e1; |
|
||||
ref int ip = ref input; |
|
||||
ref ushort op = ref output[0]; |
|
||||
ref ushort opTmp = ref output[0]; |
|
||||
ref ushort destForRead = ref Unsafe.As<byte, ushort>(ref destinationForRead); |
|
||||
ref ushort destForWrite = ref Unsafe.As<byte, ushort>(ref destinationForWrite); |
|
||||
|
|
||||
for (i = 0; i < 4; i++) |
|
||||
{ |
|
||||
a1 = (ushort)(ip >> UnitQuantizationShift); |
|
||||
c1 = (ushort)(Unsafe.Add(ref ip, 1) >> UnitQuantizationShift); |
|
||||
d1 = (ushort)(Unsafe.Add(ref ip, 2) >> UnitQuantizationShift); |
|
||||
b1 = (ushort)(Unsafe.Add(ref ip, 3) >> UnitQuantizationShift); |
|
||||
a1 += c1; |
|
||||
d1 -= b1; |
|
||||
e1 = (ushort)((a1 - d1) >> 1); |
|
||||
b1 = (ushort)(e1 - b1); |
|
||||
c1 = (ushort)(e1 - c1); |
|
||||
a1 -= b1; |
|
||||
d1 += c1; |
|
||||
op = a1; |
|
||||
Unsafe.Add(ref op, 1) = b1; |
|
||||
Unsafe.Add(ref op, 2) = c1; |
|
||||
Unsafe.Add(ref op, 3) = d1; |
|
||||
ip = ref Unsafe.Add(ref ip, 4); |
|
||||
op = ref Unsafe.Add(ref op, 4); |
|
||||
} |
|
||||
|
|
||||
ip = opTmp; |
|
||||
for (i = 0; i < 4; i++) |
|
||||
{ |
|
||||
a1 = (ushort)ip; |
|
||||
c1 = (ushort)Unsafe.Add(ref ip, 4); |
|
||||
d1 = (ushort)Unsafe.Add(ref ip, 8); |
|
||||
b1 = (ushort)Unsafe.Add(ref ip, 12); |
|
||||
a1 += c1; |
|
||||
d1 -= b1; |
|
||||
e1 = (ushort)((a1 - d1) >> 1); |
|
||||
b1 = (ushort)(e1 - b1); |
|
||||
c1 = (ushort)(e1 - c1); |
|
||||
a1 -= b1; |
|
||||
d1 += c1; |
|
||||
/* Disabled in normal build |
|
||||
range_check_value(a1, (int8_t)(bd + 1)); |
|
||||
range_check_value(b1, (int8_t)(bd + 1)); |
|
||||
range_check_value(c1, (int8_t)(bd + 1)); |
|
||||
range_check_value(d1, (int8_t)(bd + 1)); |
|
||||
*/ |
|
||||
|
|
||||
destForWrite = ClipPixelAdd(destForRead, a1, bitDepth); |
|
||||
Unsafe.Add(ref destForWrite, strideForWrite) = ClipPixelAdd(Unsafe.Add(ref destForRead, strideForRead), b1, bitDepth); |
|
||||
Unsafe.Add(ref destForWrite, strideForWrite * 2) = ClipPixelAdd(Unsafe.Add(ref destForRead, strideForRead * 2), c1, bitDepth); |
|
||||
Unsafe.Add(ref destForWrite, strideForWrite * 3) = ClipPixelAdd(Unsafe.Add(ref destForRead, strideForRead * 3), d1, bitDepth); |
|
||||
|
|
||||
ip = ref Unsafe.Add(ref ip, 1); |
|
||||
destForRead = ref Unsafe.Add(ref destForRead, 1); |
|
||||
destForWrite = ref Unsafe.Add(ref destForWrite, 1); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// SVT: svt_av1_highbd_iwht4x4_1_add_c
|
|
||||
/// </summary>
|
|
||||
private static void InverseWhalshHadamard4x4Add1(ref int input, ref byte destinationForRead, int strideForRead, ref byte destinationForWrite, int strideForWrite, int bitDepth) |
|
||||
{ |
|
||||
int i; |
|
||||
ushort a1, e1; |
|
||||
Span<int> tmp = stackalloc int[4]; |
|
||||
ref int ip = ref input; |
|
||||
ref int ipTmp = ref tmp[0]; |
|
||||
ref int op = ref tmp[0]; |
|
||||
ref ushort destForRead = ref Unsafe.As<byte, ushort>(ref destinationForRead); |
|
||||
ref ushort destForWrite = ref Unsafe.As<byte, ushort>(ref destinationForWrite); |
|
||||
|
|
||||
a1 = (ushort)(ip >> UnitQuantizationShift); |
|
||||
e1 = (ushort)(a1 >> 1); |
|
||||
a1 -= e1; |
|
||||
op = a1; |
|
||||
Unsafe.Add(ref op, 1) = e1; |
|
||||
Unsafe.Add(ref op, 2) = e1; |
|
||||
Unsafe.Add(ref op, 3) = e1; |
|
||||
|
|
||||
ip = ipTmp; |
|
||||
for (i = 0; i < 4; i++) |
|
||||
{ |
|
||||
e1 = (ushort)(ip >> 1); |
|
||||
a1 = (ushort)(ip - e1); |
|
||||
destForWrite = ClipPixelAdd(destForRead, a1, bitDepth); |
|
||||
Unsafe.Add(ref destForWrite, strideForWrite) = ClipPixelAdd(Unsafe.Add(ref destForRead, strideForRead), a1, bitDepth); |
|
||||
Unsafe.Add(ref destForWrite, strideForWrite * 2) = ClipPixelAdd(Unsafe.Add(ref destForRead, strideForRead * 2), a1, bitDepth); |
|
||||
Unsafe.Add(ref destForWrite, strideForWrite * 3) = ClipPixelAdd(Unsafe.Add(ref destForRead, strideForRead * 3), a1, bitDepth); |
|
||||
ip = ref Unsafe.Add(ref ip, 1); |
|
||||
destForRead = ref Unsafe.Add(ref destForRead, 1); |
|
||||
destForWrite = ref Unsafe.Add(ref destForWrite, 1); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private static ushort ClipPixelAdd(ushort value, int trans, int bitDepth) |
|
||||
=> ClipPixel(value + trans, bitDepth); |
|
||||
|
|
||||
private static ushort ClipPixel(int value, int bitDepth) |
|
||||
=> bitDepth switch |
|
||||
{ |
|
||||
10 => (ushort)Av1Math.Clamp(value, 0, 1023), |
|
||||
12 => (ushort)Av1Math.Clamp(value, 0, 4095), |
|
||||
_ => (ushort)Av1Math.Clamp(value, 0, 255), |
|
||||
}; |
|
||||
} |
|
||||
@ -0,0 +1,363 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
||||
|
|
||||
|
internal class Av1Inverse2dTransformer |
||||
|
{ |
||||
|
private const int UnitQuantizationShift = 2; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// SVT: inv_txfm2d_add_c
|
||||
|
/// </summary>
|
||||
|
internal static void InverseTransform2dAdd( |
||||
|
Span<int> input, |
||||
|
Span<ushort> outputForRead, |
||||
|
int strideForRead, |
||||
|
Span<ushort> outputForWrite, |
||||
|
int strideForWrite, |
||||
|
Av1Transform2dFlipConfiguration config, |
||||
|
Span<int> transformFunctionBuffer, |
||||
|
int bitDepth) |
||||
|
{ |
||||
|
// Note when assigning txfm_size_col, we use the txfm_size from the
|
||||
|
// row configuration and vice versa. This is intentionally done to
|
||||
|
// accurately perform rectangular transforms. When the transform is
|
||||
|
// rectangular, the number of columns will be the same as the
|
||||
|
// txfm_size stored in the row cfg struct. It will make no difference
|
||||
|
// for square transforms.
|
||||
|
int transformWidth = config.TransformSize.GetWidth(); |
||||
|
int transformHeight = config.TransformSize.GetHeight(); |
||||
|
|
||||
|
// Take the shift from the larger dimension in the rectangular case.
|
||||
|
Span<int> shift = config.Shift; |
||||
|
int rectangleType = config.TransformSize.GetRectangleLogRatio(); |
||||
|
config.GenerateStageRange(bitDepth); |
||||
|
|
||||
|
int cosBitColumn = config.CosBitColumn; |
||||
|
int cosBitRow = config.CosBitRow; |
||||
|
IAv1Forward1dTransformer? functionColumn = Av1InverseTransformerFactory.GetTransformer(config.TransformFunctionTypeColumn); |
||||
|
IAv1Forward1dTransformer? functionRow = Av1InverseTransformerFactory.GetTransformer(config.TransformFunctionTypeRow); |
||||
|
Guard.NotNull(functionColumn); |
||||
|
Guard.NotNull(functionRow); |
||||
|
|
||||
|
// txfm_buf's length is txfm_size_row * txfm_size_col + 2 * MAX(txfm_size_row, txfm_size_col)
|
||||
|
// it is used for intermediate data buffering
|
||||
|
int bufferOffset = Math.Max(transformHeight, transformWidth); |
||||
|
Guard.MustBeSizedAtLeast(transformFunctionBuffer, (transformHeight * transformWidth) + (2 * bufferOffset), nameof(transformFunctionBuffer)); |
||||
|
Span<int> tempIn = transformFunctionBuffer; |
||||
|
Span<int> tempOut = tempIn.Slice(bufferOffset); |
||||
|
Span<int> buf = tempOut.Slice(bufferOffset); |
||||
|
Span<int> bufPtr = buf; |
||||
|
int c, r; |
||||
|
|
||||
|
// Rows
|
||||
|
for (r = 0; r < transformHeight; ++r) |
||||
|
{ |
||||
|
if (Math.Abs(rectangleType) == 1) |
||||
|
{ |
||||
|
for (c = 0; c < transformWidth; ++c) |
||||
|
{ |
||||
|
tempIn[c] = Av1Math.RoundShift((long)input[c] * Av1InverseTransformMath.NewInverseSqrt2, Av1InverseTransformMath.NewSqrt2BitCount); |
||||
|
} |
||||
|
|
||||
|
Av1InverseTransformMath.ClampBuffer(tempIn, transformWidth, (byte)(bitDepth + 8)); |
||||
|
functionRow.Transform(tempIn, bufPtr, cosBitRow, config.StageRangeRow); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
for (c = 0; c < transformWidth; ++c) |
||||
|
{ |
||||
|
tempIn[c] = input[c]; |
||||
|
} |
||||
|
|
||||
|
Av1InverseTransformMath.ClampBuffer(tempIn, transformWidth, (byte)(bitDepth + 8)); |
||||
|
functionRow.Transform(tempIn, bufPtr, cosBitRow, config.StageRangeRow); |
||||
|
} |
||||
|
|
||||
|
Av1InverseTransformMath.RoundShiftArray(bufPtr, transformWidth, -shift[0]); |
||||
|
input.Slice(transformWidth); |
||||
|
bufPtr.Slice(transformWidth); |
||||
|
} |
||||
|
|
||||
|
// Columns
|
||||
|
for (c = 0; c < transformWidth; ++c) |
||||
|
{ |
||||
|
if (!config.FlipLeftToRight) |
||||
|
{ |
||||
|
for (r = 0; r < transformHeight; ++r) |
||||
|
{ |
||||
|
tempIn[r] = buf[(r * transformWidth) + c]; |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
// flip left right
|
||||
|
for (r = 0; r < transformHeight; ++r) |
||||
|
{ |
||||
|
tempIn[r] = buf[(r * transformWidth) + (transformWidth - c - 1)]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
Av1InverseTransformMath.ClampBuffer(tempIn, transformHeight, (byte)Math.Max(bitDepth + 6, 16)); |
||||
|
functionColumn.Transform(tempIn, tempOut, cosBitColumn, config.StageRangeColumn); |
||||
|
Av1InverseTransformMath.RoundShiftArray(tempOut, transformHeight, -shift[1]); |
||||
|
if (!config.FlipUpsideDown) |
||||
|
{ |
||||
|
for (r = 0; r < transformHeight; ++r) |
||||
|
{ |
||||
|
outputForWrite[(r * strideForWrite) + c] = |
||||
|
Av1InverseTransformMath.ClipPixelAdd(outputForRead[(r * strideForRead) + c], tempOut[r], bitDepth); |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
// flip upside down
|
||||
|
for (r = 0; r < transformHeight; ++r) |
||||
|
{ |
||||
|
outputForWrite[(r * strideForWrite) + c] = Av1InverseTransformMath.ClipPixelAdd( |
||||
|
outputForRead[(r * strideForRead) + c], tempOut[transformHeight - r - 1], bitDepth); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// SVT: inv_txfm2d_add_c
|
||||
|
/// </summary>
|
||||
|
internal static void InverseTransform2dAdd( |
||||
|
Span<int> input, |
||||
|
Span<byte> outputForRead, |
||||
|
int strideForRead, |
||||
|
Span<byte> outputForWrite, |
||||
|
int strideForWrite, |
||||
|
Av1Transform2dFlipConfiguration config, |
||||
|
Span<int> transformFunctionBuffer) |
||||
|
{ |
||||
|
const int bitDepth = 8; |
||||
|
|
||||
|
// Note when assigning txfm_size_col, we use the txfm_size from the
|
||||
|
// row configuration and vice versa. This is intentionally done to
|
||||
|
// accurately perform rectangular transforms. When the transform is
|
||||
|
// rectangular, the number of columns will be the same as the
|
||||
|
// txfm_size stored in the row cfg struct. It will make no difference
|
||||
|
// for square transforms.
|
||||
|
int transformWidth = config.TransformSize.GetWidth(); |
||||
|
int transformHeight = config.TransformSize.GetHeight(); |
||||
|
|
||||
|
// Take the shift from the larger dimension in the rectangular case.
|
||||
|
Span<int> shift = config.Shift; |
||||
|
int rectangleType = config.TransformSize.GetRectangleLogRatio(); |
||||
|
config.GenerateStageRange(bitDepth); |
||||
|
|
||||
|
int cosBitColumn = config.CosBitColumn; |
||||
|
int cosBitRow = config.CosBitRow; |
||||
|
IAv1Forward1dTransformer? functionColumn = Av1InverseTransformerFactory.GetTransformer(config.TransformFunctionTypeColumn); |
||||
|
IAv1Forward1dTransformer? functionRow = Av1InverseTransformerFactory.GetTransformer(config.TransformFunctionTypeRow); |
||||
|
Guard.NotNull(functionColumn); |
||||
|
Guard.NotNull(functionRow); |
||||
|
|
||||
|
// txfm_buf's length is txfm_size_row * txfm_size_col + 2 * MAX(txfm_size_row, txfm_size_col)
|
||||
|
// it is used for intermediate data buffering
|
||||
|
int bufferOffset = Math.Max(transformHeight, transformWidth); |
||||
|
Guard.MustBeSizedAtLeast(transformFunctionBuffer, (transformHeight * transformWidth) + (2 * bufferOffset), nameof(transformFunctionBuffer)); |
||||
|
Span<int> tempIn = transformFunctionBuffer; |
||||
|
Span<int> tempOut = tempIn.Slice(bufferOffset); |
||||
|
Span<int> buf = tempOut.Slice(bufferOffset); |
||||
|
Span<int> bufPtr = buf; |
||||
|
int c, r; |
||||
|
|
||||
|
// Rows
|
||||
|
for (r = 0; r < transformHeight; ++r) |
||||
|
{ |
||||
|
if (Math.Abs(rectangleType) == 1) |
||||
|
{ |
||||
|
for (c = 0; c < transformWidth; ++c) |
||||
|
{ |
||||
|
tempIn[c] = Av1Math.RoundShift((long)input[c] * Av1InverseTransformMath.NewInverseSqrt2, Av1InverseTransformMath.NewSqrt2BitCount); |
||||
|
} |
||||
|
|
||||
|
Av1InverseTransformMath.ClampBuffer(tempIn, transformWidth, (byte)(bitDepth + 8)); |
||||
|
functionRow.Transform(tempIn, bufPtr, cosBitRow, config.StageRangeRow); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
for (c = 0; c < transformWidth; ++c) |
||||
|
{ |
||||
|
tempIn[c] = input[c]; |
||||
|
} |
||||
|
|
||||
|
Av1InverseTransformMath.ClampBuffer(tempIn, transformWidth, (byte)(bitDepth + 8)); |
||||
|
functionRow.Transform(tempIn, bufPtr, cosBitRow, config.StageRangeRow); |
||||
|
} |
||||
|
|
||||
|
Av1InverseTransformMath.RoundShiftArray(bufPtr, transformWidth, -shift[0]); |
||||
|
input.Slice(transformWidth); |
||||
|
bufPtr.Slice(transformWidth); |
||||
|
} |
||||
|
|
||||
|
// Columns
|
||||
|
for (c = 0; c < transformWidth; ++c) |
||||
|
{ |
||||
|
if (!config.FlipLeftToRight) |
||||
|
{ |
||||
|
for (r = 0; r < transformHeight; ++r) |
||||
|
{ |
||||
|
tempIn[r] = buf[(r * transformWidth) + c]; |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
// flip left right
|
||||
|
for (r = 0; r < transformHeight; ++r) |
||||
|
{ |
||||
|
tempIn[r] = buf[(r * transformWidth) + (transformWidth - c - 1)]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
Av1InverseTransformMath.ClampBuffer(tempIn, transformHeight, (byte)Math.Max(bitDepth + 6, 16)); |
||||
|
functionColumn.Transform(tempIn, tempOut, cosBitColumn, config.StageRangeColumn); |
||||
|
Av1InverseTransformMath.RoundShiftArray(tempOut, transformHeight, -shift[1]); |
||||
|
if (!config.FlipUpsideDown) |
||||
|
{ |
||||
|
for (r = 0; r < transformHeight; ++r) |
||||
|
{ |
||||
|
outputForWrite[(r * strideForWrite) + c] = |
||||
|
Av1InverseTransformMath.ClipPixelAdd(outputForRead[(r * strideForRead) + c], tempOut[r]); |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
// flip upside down
|
||||
|
for (r = 0; r < transformHeight; ++r) |
||||
|
{ |
||||
|
outputForWrite[(r * strideForWrite) + c] = Av1InverseTransformMath.ClipPixelAdd( |
||||
|
outputForRead[(r * strideForRead) + c], tempOut[transformHeight - r - 1]); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// SVT: highbd_iwht4x4_add
|
||||
|
/// </summary>
|
||||
|
private static void InverseWhalshHadamard4x4(ref int input, ref byte destinationForRead, int strideForRead, ref byte destinationForWrite, int strideForWrite, int endOfBuffer, int bitDepth) |
||||
|
{ |
||||
|
if (endOfBuffer > 1) |
||||
|
{ |
||||
|
InverseWhalshHadamard4x4Add16(ref input, ref destinationForRead, strideForRead, ref destinationForWrite, strideForWrite, bitDepth); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
InverseWhalshHadamard4x4Add1(ref input, ref destinationForRead, strideForRead, ref destinationForWrite, strideForWrite, bitDepth); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// SVT: svt_av1_highbd_iwht4x4_16_add_c
|
||||
|
/// </summary>
|
||||
|
private static void InverseWhalshHadamard4x4Add16(ref int input, ref byte destinationForRead, int strideForRead, ref byte destinationForWrite, int strideForWrite, int bitDepth) |
||||
|
{ |
||||
|
/* 4-point reversible, orthonormal inverse Walsh-Hadamard in 3.5 adds, |
||||
|
0.5 shifts per pixel. */ |
||||
|
int i; |
||||
|
Span<ushort> output = stackalloc ushort[16]; |
||||
|
ushort a1, b1, c1, d1, e1; |
||||
|
ref int ip = ref input; |
||||
|
ref ushort op = ref output[0]; |
||||
|
ref ushort opTmp = ref output[0]; |
||||
|
ref ushort destForRead = ref Unsafe.As<byte, ushort>(ref destinationForRead); |
||||
|
ref ushort destForWrite = ref Unsafe.As<byte, ushort>(ref destinationForWrite); |
||||
|
|
||||
|
for (i = 0; i < 4; i++) |
||||
|
{ |
||||
|
a1 = (ushort)(ip >> UnitQuantizationShift); |
||||
|
c1 = (ushort)(Unsafe.Add(ref ip, 1) >> UnitQuantizationShift); |
||||
|
d1 = (ushort)(Unsafe.Add(ref ip, 2) >> UnitQuantizationShift); |
||||
|
b1 = (ushort)(Unsafe.Add(ref ip, 3) >> UnitQuantizationShift); |
||||
|
a1 += c1; |
||||
|
d1 -= b1; |
||||
|
e1 = (ushort)((a1 - d1) >> 1); |
||||
|
b1 = (ushort)(e1 - b1); |
||||
|
c1 = (ushort)(e1 - c1); |
||||
|
a1 -= b1; |
||||
|
d1 += c1; |
||||
|
op = a1; |
||||
|
Unsafe.Add(ref op, 1) = b1; |
||||
|
Unsafe.Add(ref op, 2) = c1; |
||||
|
Unsafe.Add(ref op, 3) = d1; |
||||
|
ip = ref Unsafe.Add(ref ip, 4); |
||||
|
op = ref Unsafe.Add(ref op, 4); |
||||
|
} |
||||
|
|
||||
|
ip = opTmp; |
||||
|
for (i = 0; i < 4; i++) |
||||
|
{ |
||||
|
a1 = (ushort)ip; |
||||
|
c1 = (ushort)Unsafe.Add(ref ip, 4); |
||||
|
d1 = (ushort)Unsafe.Add(ref ip, 8); |
||||
|
b1 = (ushort)Unsafe.Add(ref ip, 12); |
||||
|
a1 += c1; |
||||
|
d1 -= b1; |
||||
|
e1 = (ushort)((a1 - d1) >> 1); |
||||
|
b1 = (ushort)(e1 - b1); |
||||
|
c1 = (ushort)(e1 - c1); |
||||
|
a1 -= b1; |
||||
|
d1 += c1; |
||||
|
/* Disabled in normal build |
||||
|
range_check_value(a1, (int8_t)(bd + 1)); |
||||
|
range_check_value(b1, (int8_t)(bd + 1)); |
||||
|
range_check_value(c1, (int8_t)(bd + 1)); |
||||
|
range_check_value(d1, (int8_t)(bd + 1)); |
||||
|
*/ |
||||
|
|
||||
|
destForWrite = Av1InverseTransformMath.ClipPixelAdd(destForRead, a1, bitDepth); |
||||
|
Unsafe.Add(ref destForWrite, strideForWrite) = Av1InverseTransformMath.ClipPixelAdd(Unsafe.Add(ref destForRead, strideForRead), b1, bitDepth); |
||||
|
Unsafe.Add(ref destForWrite, strideForWrite * 2) = Av1InverseTransformMath.ClipPixelAdd(Unsafe.Add(ref destForRead, strideForRead * 2), c1, bitDepth); |
||||
|
Unsafe.Add(ref destForWrite, strideForWrite * 3) = Av1InverseTransformMath.ClipPixelAdd(Unsafe.Add(ref destForRead, strideForRead * 3), d1, bitDepth); |
||||
|
|
||||
|
ip = ref Unsafe.Add(ref ip, 1); |
||||
|
destForRead = ref Unsafe.Add(ref destForRead, 1); |
||||
|
destForWrite = ref Unsafe.Add(ref destForWrite, 1); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// SVT: svt_av1_highbd_iwht4x4_1_add_c
|
||||
|
/// </summary>
|
||||
|
private static void InverseWhalshHadamard4x4Add1(ref int input, ref byte destinationForRead, int strideForRead, ref byte destinationForWrite, int strideForWrite, int bitDepth) |
||||
|
{ |
||||
|
int i; |
||||
|
ushort a1, e1; |
||||
|
Span<int> tmp = stackalloc int[4]; |
||||
|
ref int ip = ref input; |
||||
|
ref int ipTmp = ref tmp[0]; |
||||
|
ref int op = ref tmp[0]; |
||||
|
ref ushort destForRead = ref Unsafe.As<byte, ushort>(ref destinationForRead); |
||||
|
ref ushort destForWrite = ref Unsafe.As<byte, ushort>(ref destinationForWrite); |
||||
|
|
||||
|
a1 = (ushort)(ip >> UnitQuantizationShift); |
||||
|
e1 = (ushort)(a1 >> 1); |
||||
|
a1 -= e1; |
||||
|
op = a1; |
||||
|
Unsafe.Add(ref op, 1) = e1; |
||||
|
Unsafe.Add(ref op, 2) = e1; |
||||
|
Unsafe.Add(ref op, 3) = e1; |
||||
|
|
||||
|
ip = ipTmp; |
||||
|
for (i = 0; i < 4; i++) |
||||
|
{ |
||||
|
e1 = (ushort)(ip >> 1); |
||||
|
a1 = (ushort)(ip - e1); |
||||
|
destForWrite = Av1InverseTransformMath.ClipPixelAdd(destForRead, a1, bitDepth); |
||||
|
Unsafe.Add(ref destForWrite, strideForWrite) = Av1InverseTransformMath.ClipPixelAdd(Unsafe.Add(ref destForRead, strideForRead), a1, bitDepth); |
||||
|
Unsafe.Add(ref destForWrite, strideForWrite * 2) = Av1InverseTransformMath.ClipPixelAdd(Unsafe.Add(ref destForRead, strideForRead * 2), a1, bitDepth); |
||||
|
Unsafe.Add(ref destForWrite, strideForWrite * 3) = Av1InverseTransformMath.ClipPixelAdd(Unsafe.Add(ref destForRead, strideForRead * 3), a1, bitDepth); |
||||
|
ip = ref Unsafe.Add(ref ip, 1); |
||||
|
destForRead = ref Unsafe.Add(ref destForRead, 1); |
||||
|
destForWrite = ref Unsafe.Add(ref destForWrite, 1); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,38 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
||||
|
|
||||
|
internal static class Av1InverseTransformerFactory |
||||
|
{ |
||||
|
public static unsafe void InverseTransformAdd(Span<int> coefficients, Span<byte> readBuffer, int readStride, Span<byte> writeBuffer, int writeStride, Av1TransformFunctionParameters transformFunctionParameters) |
||||
|
{ |
||||
|
Guard.MustBeLessThanOrEqualTo(transformFunctionParameters.BitDepth, 8, nameof(transformFunctionParameters)); |
||||
|
Guard.IsFalse(transformFunctionParameters.Is16BitPipeline, nameof(transformFunctionParameters), "Calling 8-bit pipeline while 16-bit is requested."); |
||||
|
int width = transformFunctionParameters.TransformSize.GetWidth(); |
||||
|
int height = transformFunctionParameters.TransformSize.GetHeight(); |
||||
|
Span<int> buffer = new int[(width * height) + (2 * Math.Max(width, height))]; |
||||
|
Av1Transform2dFlipConfiguration config = new(transformFunctionParameters.TransformType, transformFunctionParameters.TransformSize); |
||||
|
Av1Inverse2dTransformer.InverseTransform2dAdd(coefficients, readBuffer, readStride, writeBuffer, writeStride, config, buffer); |
||||
|
} |
||||
|
|
||||
|
public static unsafe void InverseTransformAdd(Span<int> coefficients, Span<ushort> readBuffer, int readStride, Span<ushort> writeBuffer, int writeStride, Av1TransformFunctionParameters transformFunctionParameters) |
||||
|
{ |
||||
|
Guard.IsTrue(transformFunctionParameters.Is16BitPipeline, nameof(transformFunctionParameters), "Calling 16-bit pipeline while 8-bit is requested."); |
||||
|
int width = transformFunctionParameters.TransformSize.GetWidth(); |
||||
|
int height = transformFunctionParameters.TransformSize.GetHeight(); |
||||
|
Span<int> buffer = new int[(width * height) + (2 * Math.Max(width, height))]; |
||||
|
Av1Transform2dFlipConfiguration config = new(transformFunctionParameters.TransformType, transformFunctionParameters.TransformSize); |
||||
|
Av1Inverse2dTransformer.InverseTransform2dAdd(coefficients, readBuffer, readStride, writeBuffer, writeStride, config, buffer, transformFunctionParameters.BitDepth); |
||||
|
} |
||||
|
|
||||
|
internal static IAv1Forward1dTransformer? GetTransformer(Av1TransformFunctionType type) => type switch |
||||
|
{ |
||||
|
Av1TransformFunctionType.Dct4 => new Av1Dct4Inverse1dTransformer(), |
||||
|
Av1TransformFunctionType.Adst4 => new Av1Adst4Inverse1dTransformer(), |
||||
|
Av1TransformFunctionType.Identity4 => new Av1Identity4Inverse1dTransformer(), |
||||
|
_ => null |
||||
|
}; |
||||
|
} |
||||
@ -1,19 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Six Labors Split License.
|
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|
||||
|
|
||||
internal static class InverseTransformerFactory |
|
||||
{ |
|
||||
internal static unsafe void InverseTransformAdd(ref int coefficients, Span<byte> readBuffer, int readStride, Span<byte> writeBuffer, int writeStride, Av1TransformFunctionParameters transformFunctionParameters) |
|
||||
{ |
|
||||
switch (transformFunctionParameters.TransformType) |
|
||||
{ |
|
||||
case Av1TransformType.DctDct: |
|
||||
Av1DctDctInverseTransformer.InverseTransformAdd(ref coefficients, readBuffer, readStride, writeBuffer, writeStride, transformFunctionParameters); |
|
||||
break; |
|
||||
default: |
|
||||
throw new InvalidImageContentException("Unknown transform type: " + transformFunctionParameters.TransformType); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
Loading…
Reference in new issue