|
|
|
@ -4,6 +4,7 @@ |
|
|
|
using System; |
|
|
|
using System.Runtime.CompilerServices; |
|
|
|
|
|
|
|
#pragma warning disable IDE0078
|
|
|
|
namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
|
|
|
{ |
|
|
|
/// <summary>
|
|
|
|
@ -14,10 +15,22 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
|
|
|
/// </remarks>
|
|
|
|
internal static class ScaledFloatingPointDCT |
|
|
|
{ |
|
|
|
#pragma warning disable SA1310 // naming rules violation warnings
|
|
|
|
private const float F32_0_541196100 = 0.541196100f; |
|
|
|
private const float F32_0_765366865 = 0.765366865f; |
|
|
|
private const float F32_1_847759065 = 1.847759065f; |
|
|
|
#pragma warning disable SA1310
|
|
|
|
private const float FP32_0_541196100 = 0.541196100f; |
|
|
|
private const float FP32_0_765366865 = 0.765366865f; |
|
|
|
private const float FP32_1_847759065 = 1.847759065f; |
|
|
|
private const float FP32_0_211164243 = 0.211164243f; |
|
|
|
private const float FP32_1_451774981 = 1.451774981f; |
|
|
|
private const float FP32_2_172734803 = 2.172734803f; |
|
|
|
private const float FP32_1_061594337 = 1.061594337f; |
|
|
|
private const float FP32_0_509795579 = 0.509795579f; |
|
|
|
private const float FP32_0_601344887 = 0.601344887f; |
|
|
|
private const float FP32_0_899976223 = 0.899976223f; |
|
|
|
private const float FP32_2_562915447 = 2.562915447f; |
|
|
|
private const float FP32_0_720959822 = 0.720959822f; |
|
|
|
private const float FP32_0_850430095 = 0.850430095f; |
|
|
|
private const float FP32_1_272758580 = 1.272758580f; |
|
|
|
private const float FP32_3_624509785 = 3.624509785f; |
|
|
|
#pragma warning restore SA1310
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
@ -39,76 +52,169 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components |
|
|
|
quantTable.TransposeInplace(); |
|
|
|
} |
|
|
|
|
|
|
|
public static float TransformIDCT_1x1(float dc, float dequantizer, float normalizationValue, float maxValue) |
|
|
|
=> MathF.Round(Numerics.Clamp((dc * dequantizer) + normalizationValue, 0, maxValue)); |
|
|
|
|
|
|
|
public static void TransformIDCT_2x2(ref Block8x8F block, ref Block8x8F dequantTable, float normalizationValue, float maxValue) |
|
|
|
{ |
|
|
|
float tmp4 = block[0] * dequantTable[0]; |
|
|
|
float tmp5 = block[1] * dequantTable[1]; |
|
|
|
float tmp0 = tmp4 + tmp5; |
|
|
|
float tmp2 = tmp4 - tmp5; |
|
|
|
|
|
|
|
tmp4 = block[8] * dequantTable[8]; |
|
|
|
tmp5 = block[9] * dequantTable[9]; |
|
|
|
float tmp1 = tmp4 + tmp5; |
|
|
|
float tmp3 = tmp4 - tmp5; |
|
|
|
|
|
|
|
block[0] = MathF.Round(Numerics.Clamp(tmp0 + tmp1 + normalizationValue, 0, maxValue)); |
|
|
|
block[1] = MathF.Round(Numerics.Clamp(tmp0 - tmp1 + normalizationValue, 0, maxValue)); |
|
|
|
block[8] = MathF.Round(Numerics.Clamp(tmp2 + tmp3 + normalizationValue, 0, maxValue)); |
|
|
|
block[9] = MathF.Round(Numerics.Clamp(tmp2 - tmp3 + normalizationValue, 0, maxValue)); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Apply 2D floating point 'donwscaling' IDCT inplace producing
|
|
|
|
/// 8x8 -> 4x4 result.
|
|
|
|
/// </summary>
|
|
|
|
/// <remarks>
|
|
|
|
/// Resulting matrix is stored in the top left 4x4 part of the
|
|
|
|
/// <paramref name="block"/>.
|
|
|
|
/// </remarks>
|
|
|
|
/// <param name="block">Input block.</param>
|
|
|
|
/// <param name="dequantTable">Dequantization table adjusted by <see cref="AdjustToIDCT(ref Block8x8F)"/>.</param>
|
|
|
|
/// <param name="normalizationValue">Output range normalization value, 1/2 of the <paramref name="maxValue"/>.</param>
|
|
|
|
/// <param name="maxValue">Maximum value of the output range.</param>
|
|
|
|
public static void TransformIDCT_4x4(ref Block8x8F block, ref Block8x8F dequantTable, float normalizationValue, float maxValue) |
|
|
|
{ |
|
|
|
for (int ctr = 0; ctr < 4; ctr++) |
|
|
|
for (int ctr = 0; ctr < 8; ctr++) |
|
|
|
{ |
|
|
|
// Don't process row 4, second pass doesn't use it
|
|
|
|
if (ctr == 4) |
|
|
|
{ |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
// Even part
|
|
|
|
float tmp0 = block[ctr * 8] * dequantTable[ctr * 8]; |
|
|
|
float tmp2 = block[(ctr * 8) + 2] * dequantTable[(ctr * 8) + 2]; |
|
|
|
float tmp0 = block[(ctr * 8) + 0] * dequantTable[(ctr * 8) + 0] * 2; |
|
|
|
|
|
|
|
float z2 = block[(ctr * 8) + 2] * dequantTable[(ctr * 8) + 2]; |
|
|
|
float z3 = block[(ctr * 8) + 6] * dequantTable[(ctr * 8) + 6]; |
|
|
|
|
|
|
|
float tmp2 = (z2 * FP32_1_847759065) + (z3 * -FP32_0_765366865); |
|
|
|
|
|
|
|
float tmp10 = tmp0 + tmp2; |
|
|
|
float tmp12 = tmp0 - tmp2; |
|
|
|
|
|
|
|
// Odd part
|
|
|
|
float z2 = block[(ctr * 8) + 1] * dequantTable[(ctr * 8) + 1]; |
|
|
|
float z3 = block[(ctr * 8) + 3] * dequantTable[(ctr * 8) + 3]; |
|
|
|
|
|
|
|
float z1 = (z2 + z3) * F32_0_541196100; |
|
|
|
tmp0 = z1 + (z2 * F32_0_765366865); |
|
|
|
tmp2 = z1 - (z3 * F32_1_847759065); |
|
|
|
|
|
|
|
/* Final output stage */ |
|
|
|
block[ctr + 4] = tmp10 + tmp0; |
|
|
|
block[ctr + 28] = tmp10 - tmp0; |
|
|
|
block[ctr + 12] = tmp12 + tmp2; |
|
|
|
block[ctr + 20] = tmp12 - tmp2; |
|
|
|
float z1 = block[(ctr * 8) + 7] * dequantTable[(ctr * 8) + 7]; |
|
|
|
z2 = block[(ctr * 8) + 5] * dequantTable[(ctr * 8) + 5]; |
|
|
|
z3 = block[(ctr * 8) + 3] * dequantTable[(ctr * 8) + 3]; |
|
|
|
float z4 = block[(ctr * 8) + 1] * dequantTable[(ctr * 8) + 1]; |
|
|
|
|
|
|
|
tmp0 = (z1 * -FP32_0_211164243) + |
|
|
|
(z2 * FP32_1_451774981) + |
|
|
|
(z3 * -FP32_2_172734803) + |
|
|
|
(z4 * FP32_1_061594337); |
|
|
|
|
|
|
|
tmp2 = (z1 * -FP32_0_509795579) + |
|
|
|
(z2 * -FP32_0_601344887) + |
|
|
|
(z3 * FP32_0_899976223) + |
|
|
|
(z4 * FP32_2_562915447); |
|
|
|
|
|
|
|
// temporal result is saved to +4 shifted indices
|
|
|
|
// because result is saved into the top left 2x2 region of the
|
|
|
|
// input block
|
|
|
|
block[(ctr * 8) + 0 + 4] = (tmp10 + tmp2) / 2; |
|
|
|
block[(ctr * 8) + 3 + 4] = (tmp10 - tmp2) / 2; |
|
|
|
block[(ctr * 8) + 1 + 4] = (tmp12 + tmp0) / 2; |
|
|
|
block[(ctr * 8) + 2 + 4] = (tmp12 - tmp0) / 2; |
|
|
|
} |
|
|
|
|
|
|
|
for (int ctr = 0; ctr < 4; ctr++) |
|
|
|
{ |
|
|
|
// Even part
|
|
|
|
float tmp0 = block[(ctr * 8) + 0 + 4]; |
|
|
|
float tmp2 = block[(ctr * 8) + 2 + 4]; |
|
|
|
float tmp0 = block[ctr + (8 * 0) + 4] * 2; |
|
|
|
|
|
|
|
float tmp2 = (block[ctr + (8 * 2) + 4] * FP32_1_847759065) + (block[ctr + (8 * 6) + 4] * -FP32_0_765366865); |
|
|
|
|
|
|
|
float tmp10 = tmp0 + tmp2; |
|
|
|
float tmp12 = tmp0 - tmp2; |
|
|
|
|
|
|
|
// Odd part
|
|
|
|
float z2 = block[(ctr * 8) + 1 + 4]; |
|
|
|
float z3 = block[(ctr * 8) + 3 + 4]; |
|
|
|
|
|
|
|
float z1 = (z2 + z3) * F32_0_541196100; |
|
|
|
tmp0 = z1 + (z2 * F32_0_765366865); |
|
|
|
tmp2 = z1 - (z3 * F32_1_847759065); |
|
|
|
|
|
|
|
/* Final output stage */ |
|
|
|
block[(ctr * 8) + 0] = MathF.Round(Numerics.Clamp(tmp10 + tmp0 + normalizationValue, 0, maxValue)); |
|
|
|
block[(ctr * 8) + 3] = MathF.Round(Numerics.Clamp(tmp10 - tmp0 + normalizationValue, 0, maxValue)); |
|
|
|
block[(ctr * 8) + 1] = MathF.Round(Numerics.Clamp(tmp12 + tmp2 + normalizationValue, 0, maxValue)); |
|
|
|
block[(ctr * 8) + 2] = MathF.Round(Numerics.Clamp(tmp12 - tmp2 + normalizationValue, 0, maxValue)); |
|
|
|
float z1 = block[ctr + (8 * 7) + 4]; |
|
|
|
float z2 = block[ctr + (8 * 5) + 4]; |
|
|
|
float z3 = block[ctr + (8 * 3) + 4]; |
|
|
|
float z4 = block[ctr + (8 * 1) + 4]; |
|
|
|
|
|
|
|
tmp0 = (z1 * -FP32_0_211164243) + |
|
|
|
(z2 * FP32_1_451774981) + |
|
|
|
(z3 * -FP32_2_172734803) + |
|
|
|
(z4 * FP32_1_061594337); |
|
|
|
|
|
|
|
tmp2 = (z1 * -FP32_0_509795579) + |
|
|
|
(z2 * -FP32_0_601344887) + |
|
|
|
(z3 * FP32_0_899976223) + |
|
|
|
(z4 * FP32_2_562915447); |
|
|
|
|
|
|
|
// Save results to the top left 4x4 subregion
|
|
|
|
block[(ctr * 8) + 0] = MathF.Round(Numerics.Clamp(((tmp10 + tmp2) / 2) + normalizationValue, 0, maxValue)); |
|
|
|
block[(ctr * 8) + 3] = MathF.Round(Numerics.Clamp(((tmp10 - tmp2) / 2) + normalizationValue, 0, maxValue)); |
|
|
|
block[(ctr * 8) + 1] = MathF.Round(Numerics.Clamp(((tmp12 + tmp0) / 2) + normalizationValue, 0, maxValue)); |
|
|
|
block[(ctr * 8) + 2] = MathF.Round(Numerics.Clamp(((tmp12 - tmp0) / 2) + normalizationValue, 0, maxValue)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Apply 2D floating point 'donwscaling' IDCT inplace producing
|
|
|
|
/// 8x8 -> 2x2 result.
|
|
|
|
/// </summary>
|
|
|
|
/// <remarks>
|
|
|
|
/// Resulting matrix is stored in the top left 2x2 part of the
|
|
|
|
/// <paramref name="block"/>.
|
|
|
|
/// </remarks>
|
|
|
|
/// <param name="block">Input block.</param>
|
|
|
|
/// <param name="dequantTable">Dequantization table adjusted by <see cref="AdjustToIDCT(ref Block8x8F)"/>.</param>
|
|
|
|
/// <param name="normalizationValue">Output range normalization value, 1/2 of the <paramref name="maxValue"/>.</param>
|
|
|
|
/// <param name="maxValue">Maximum value of the output range.</param>
|
|
|
|
public static void TransformIDCT_2x2(ref Block8x8F block, ref Block8x8F dequantTable, float normalizationValue, float maxValue) |
|
|
|
{ |
|
|
|
for (int ctr = 0; ctr < 8; ctr++) |
|
|
|
{ |
|
|
|
// Don't process rows 2/4/6, second pass doesn't use it
|
|
|
|
if (ctr == 2 || ctr == 4 || ctr == 6) |
|
|
|
{ |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
// Even part
|
|
|
|
float tmp0; |
|
|
|
float z1 = block[(ctr * 8) + 0] * dequantTable[(ctr * 8) + 0]; |
|
|
|
float tmp10 = z1 * 4; |
|
|
|
|
|
|
|
// Odd part
|
|
|
|
z1 = block[(ctr * 8) + 7] * dequantTable[(ctr * 8) + 7]; |
|
|
|
tmp0 = z1 * -FP32_0_720959822; |
|
|
|
z1 = block[(ctr * 8) + 5] * dequantTable[(ctr * 8) + 5]; |
|
|
|
tmp0 += z1 * FP32_0_850430095; |
|
|
|
z1 = block[(ctr * 8) + 3] * dequantTable[(ctr * 8) + 3]; |
|
|
|
tmp0 += z1 * -FP32_1_272758580; |
|
|
|
z1 = block[(ctr * 8) + 1] * dequantTable[(ctr * 8) + 1]; |
|
|
|
tmp0 += z1 * FP32_3_624509785; |
|
|
|
|
|
|
|
// temporal result is saved to +2 shifted indices
|
|
|
|
// because result is saved into the top left 2x2 region of the
|
|
|
|
// input block
|
|
|
|
block[(ctr * 8) + 2] = (tmp10 + tmp0) / 4; |
|
|
|
block[(ctr * 8) + 3] = (tmp10 - tmp0) / 4; |
|
|
|
} |
|
|
|
|
|
|
|
for (int ctr = 0; ctr < 2; ctr++) |
|
|
|
{ |
|
|
|
// Even part
|
|
|
|
float tmp10 = block[ctr + (8 * 0) + 2] * 4; |
|
|
|
|
|
|
|
// Odd part
|
|
|
|
float tmp0 = (block[ctr + (8 * 7) + 2] * -FP32_0_720959822) + |
|
|
|
(block[ctr + (8 * 5) + 2] * FP32_0_850430095) + |
|
|
|
(block[ctr + (8 * 3) + 2] * -FP32_1_272758580) + |
|
|
|
(block[ctr + (8 * 1) + 2] * FP32_3_624509785); |
|
|
|
|
|
|
|
// Save results to the top left 2x2 subregion
|
|
|
|
block[(ctr * 8) + 0] = MathF.Round(Numerics.Clamp(((tmp10 + tmp0) / 4) + normalizationValue, 0, maxValue)); |
|
|
|
block[(ctr * 8) + 1] = MathF.Round(Numerics.Clamp(((tmp10 - tmp0) / 4) + normalizationValue, 0, maxValue)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Apply 2D floating point 'donwscaling' IDCT inplace producing
|
|
|
|
/// 8x8 -> 1x1 result.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="dc">Direct current term value from input block.</param>
|
|
|
|
/// <param name="dequantizer">Dequantization value.</param>
|
|
|
|
/// <param name="normalizationValue">Output range normalization value, 1/2 of the <paramref name="maxValue"/>.</param>
|
|
|
|
/// <param name="maxValue">Maximum value of the output range.</param>
|
|
|
|
public static float TransformIDCT_1x1(float dc, float dequantizer, float normalizationValue, float maxValue) |
|
|
|
=> MathF.Round(Numerics.Clamp((dc * dequantizer) + normalizationValue, 0, maxValue)); |
|
|
|
} |
|
|
|
} |
|
|
|
#pragma warning restore IDE0078
|
|
|
|
|