Browse Source

Initial processor implementation, code base for tests

pull/2076/head
Dmitry Pentin 4 years ago
parent
commit
8192ff2a17
  1. 157
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor2.cs
  2. 127
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor4.cs
  3. 16
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor8.cs
  4. 20
      src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs
  5. 159
      tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs
  6. 19
      tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.FastFloatingPointDCT.cs
  7. 21
      tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs

157
src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor2.cs

@ -0,0 +1,157 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
internal sealed class DownScalingComponentProcessor2 : ComponentProcessor
{
private readonly IRawJpegData rawJpeg;
public DownScalingComponentProcessor2(MemoryAllocator memoryAllocator, JpegFrame frame, IRawJpegData rawJpeg, Size postProcessorBufferSize, IJpegComponent component)
: base(memoryAllocator, frame, postProcessorBufferSize, component, 4)
=> this.rawJpeg = rawJpeg;
public override void CopyBlocksToColorBuffer(int spectralStep)
{
Buffer2D<Block8x8> spectralBuffer = this.Component.SpectralBlocks;
float maximumValue = this.Frame.MaxColorChannelValue;
float normalizationValue = MathF.Ceiling(maximumValue / 2);
int destAreaStride = this.ColorBuffer.Width;
int blocksRowsPerStep = this.Component.SamplingFactors.Height;
Size subSamplingDivisors = this.Component.SubSamplingDivisors;
Block8x8F dequantTable = this.rawJpeg.QuantizationTables[this.Component.QuantizationTableIndex];
Block8x8F workspaceBlock = default;
int yBlockStart = spectralStep * blocksRowsPerStep;
for (int y = 0; y < blocksRowsPerStep; y++)
{
int yBuffer = y * this.BlockAreaSize.Height;
Span<float> colorBufferRow = this.ColorBuffer.DangerousGetRowSpan(yBuffer);
Span<Block8x8> blockRow = spectralBuffer.DangerousGetRowSpan(yBlockStart + y);
for (int xBlock = 0; xBlock < spectralBuffer.Width; xBlock++)
{
// Integer to float
workspaceBlock.LoadFrom(ref blockRow[xBlock]);
// IDCT/Normalization/Range
TransformIDCT_4x4(ref workspaceBlock, ref dequantTable, normalizationValue, maximumValue);
// Save to the intermediate buffer
int xColorBufferStart = xBlock * this.BlockAreaSize.Width;
ScaledCopyTo(
ref workspaceBlock,
ref colorBufferRow[xColorBufferStart],
destAreaStride,
subSamplingDivisors.Width,
subSamplingDivisors.Height);
}
}
}
public static void TransformIDCT_4x4(ref Block8x8F block, ref Block8x8F dequantTable, float normalizationValue, float maxValue)
{
const int DCTSIZE = 8;
const float FIX_0_541196100 = 0.541196100f;
const float FIX_0_765366865 = 0.765366865f;
const float FIX_1_847759065 = 1.847759065f;
// input block is transposed so term indices must be tranposed too
float tmp0, tmp2, tmp10, tmp12;
float z1, z2, z3;
for (int ctr = 0; ctr < 4; ctr++)
{
// Even part
tmp0 = block[ctr * DCTSIZE] * dequantTable[ctr * DCTSIZE];
tmp2 = block[(ctr * DCTSIZE) + 2] * dequantTable[(ctr * DCTSIZE) + 2];
tmp10 = tmp0 + tmp2;
tmp12 = tmp0 - tmp2;
// Odd part
z2 = block[(ctr * DCTSIZE) + 1] * dequantTable[(ctr * DCTSIZE) + 1];
z3 = block[(ctr * DCTSIZE) + 3] * dequantTable[(ctr * DCTSIZE) + 3];
z1 = (z2 + z3) * FIX_0_541196100;
tmp0 = z1 + (z2 * FIX_0_765366865);
tmp2 = z1 - (z3 * FIX_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;
}
for (int ctr = 0; ctr < 4; ctr++)
{
// Even part
tmp0 = block[(ctr * 8) + 0 + 4];
tmp2 = block[(ctr * 8) + 2 + 4];
tmp10 = tmp0 + tmp2;
tmp12 = tmp0 - tmp2;
// Odd part
z2 = block[(ctr * 8) + 1 + 4];
z3 = block[(ctr * 8) + 3 + 4];
z1 = (z2 + z3) * FIX_0_541196100;
tmp0 = z1 + (z2 * FIX_0_765366865);
tmp2 = z1 - (z3 * FIX_1_847759065);
/* Final output stage */
block[(ctr * 8) + 0] = (float)Math.Round(Numerics.Clamp(tmp10 + tmp0 + normalizationValue, 0, maxValue));
block[(ctr * 8) + 3] = (float)Math.Round(Numerics.Clamp(tmp10 - tmp0 + normalizationValue, 0, maxValue));
block[(ctr * 8) + 1] = (float)Math.Round(Numerics.Clamp(tmp12 + tmp2 + normalizationValue, 0, maxValue));
block[(ctr * 8) + 2] = (float)Math.Round(Numerics.Clamp(tmp12 - tmp2 + normalizationValue, 0, maxValue));
}
}
[MethodImpl(InliningOptions.ShortMethod)]
public static void ScaledCopyTo(ref Block8x8F block, ref float destRef, int destStrideWidth, int horizontalScale, int verticalScale)
{
// TODO: Optimize: implement all cases with scale-specific, loopless code!
CopyArbitraryScale(ref block, ref destRef, destStrideWidth, horizontalScale, verticalScale);
[MethodImpl(InliningOptions.ColdPath)]
static void CopyArbitraryScale(ref Block8x8F block, ref float areaOrigin, int areaStride, int horizontalScale, int verticalScale)
{
for (int y = 0; y < 4; y++)
{
int yy = y * verticalScale;
int y8 = y * 8;
for (int x = 0; x < 4; x++)
{
int xx = x * horizontalScale;
float value = block[y8 + x];
for (int i = 0; i < verticalScale; i++)
{
int baseIdx = ((yy + i) * areaStride) + xx;
for (int j = 0; j < horizontalScale; j++)
{
// area[xx + j, yy + i] = value;
Unsafe.Add(ref areaOrigin, baseIdx + j) = value;
}
}
}
}
}
}
}
}

127
src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor4.cs

@ -0,0 +1,127 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
internal sealed class DownScalingComponentProcessor4 : ComponentProcessor
{
private readonly IRawJpegData rawJpeg;
public DownScalingComponentProcessor4(MemoryAllocator memoryAllocator, JpegFrame frame, IRawJpegData rawJpeg, Size postProcessorBufferSize, IJpegComponent component)
: base(memoryAllocator, frame, postProcessorBufferSize, component, 2)
=> this.rawJpeg = rawJpeg;
public override void CopyBlocksToColorBuffer(int spectralStep)
{
Buffer2D<Block8x8> spectralBuffer = this.Component.SpectralBlocks;
float maximumValue = this.Frame.MaxColorChannelValue;
float normalizationValue = MathF.Ceiling(maximumValue / 2);
int destAreaStride = this.ColorBuffer.Width;
int blocksRowsPerStep = this.Component.SamplingFactors.Height;
Size subSamplingDivisors = this.Component.SubSamplingDivisors;
Block8x8F dequantTable = this.rawJpeg.QuantizationTables[this.Component.QuantizationTableIndex];
Block8x8F workspaceBlock = default;
int yBlockStart = spectralStep * blocksRowsPerStep;
for (int y = 0; y < blocksRowsPerStep; y++)
{
int yBuffer = y * this.BlockAreaSize.Height;
Span<float> colorBufferRow = this.ColorBuffer.DangerousGetRowSpan(yBuffer);
Span<Block8x8> blockRow = spectralBuffer.DangerousGetRowSpan(yBlockStart + y);
for (int xBlock = 0; xBlock < spectralBuffer.Width; xBlock++)
{
// Integer to float
workspaceBlock.LoadFrom(ref blockRow[xBlock]);
// IDCT/Normalization/Range
TransformIDCT_2x2(ref workspaceBlock, ref dequantTable, normalizationValue, maximumValue);
// Save to the intermediate buffer
int xColorBufferStart = xBlock * this.BlockAreaSize.Width;
ScaledCopyTo(
ref workspaceBlock,
ref colorBufferRow[xColorBufferStart],
destAreaStride,
subSamplingDivisors.Width,
subSamplingDivisors.Height);
}
}
}
public static void TransformIDCT_2x2(ref Block8x8F block, ref Block8x8F dequantTable, float normalizationValue, float maxValue)
{
// input block is transposed so term indices must be tranposed too
float tmp0, tmp1, tmp2, tmp3, tmp4, tmp5;
// 0
// => 0 1
// 8
tmp4 = block[0] * dequantTable[0];
tmp5 = block[1] * dequantTable[1];
tmp0 = tmp4 + tmp5;
tmp2 = tmp4 - tmp5;
// 1
// => 8 9
// 9
tmp4 = block[8] * dequantTable[8];
tmp5 = block[9] * dequantTable[9];
tmp1 = tmp4 + tmp5;
tmp3 = tmp4 - tmp5;
// Row 0
block[0] = (float)Math.Round(Numerics.Clamp(tmp0 + tmp1 + normalizationValue, 0, maxValue));
block[1] = (float)Math.Round(Numerics.Clamp(tmp0 - tmp1 + normalizationValue, 0, maxValue));
// Row 1
block[8] = (float)Math.Round(Numerics.Clamp(tmp2 + tmp3 + normalizationValue, 0, maxValue));
block[9] = (float)Math.Round(Numerics.Clamp(tmp2 - tmp3 + normalizationValue, 0, maxValue));
}
[MethodImpl(InliningOptions.ShortMethod)]
public static void ScaledCopyTo(ref Block8x8F block, ref float destRef, int destStrideWidth, int horizontalScale, int verticalScale)
{
// TODO: Optimize: implement all cases with scale-specific, loopless code!
CopyArbitraryScale(ref block, ref destRef, destStrideWidth, horizontalScale, verticalScale);
[MethodImpl(InliningOptions.ColdPath)]
static void CopyArbitraryScale(ref Block8x8F block, ref float areaOrigin, int areaStride, int horizontalScale, int verticalScale)
{
for (int y = 0; y < 2; y++)
{
int yy = y * verticalScale;
int y8 = y * 8;
for (int x = 0; x < 2; x++)
{
int xx = x * horizontalScale;
float value = block[y8 + x];
for (int i = 0; i < verticalScale; i++)
{
int baseIdx = ((yy + i) * areaStride) + xx;
for (int j = 0; j < horizontalScale; j++)
{
// area[xx + j, yy + i] = value;
Unsafe.Add(ref areaOrigin, baseIdx + j) = value;
}
}
}
}
}
}
}
}

16
src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor8.cs

@ -38,14 +38,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
for (int xBlock = 0; xBlock < spectralBuffer.Width; xBlock++)
{
// get direct current term - averaged 8x8 pixel value
float dc = blockRow[xBlock][0];
// dequantization
dc *= this.dcDequantizer;
// Normalize & round
dc = (float)Math.Round(Numerics.Clamp(dc + normalizationValue, 0, maximumValue));
float dc = TransformIDCT_1x1(blockRow[xBlock][0], this.dcDequantizer, normalizationValue, maximumValue);
// Save to the intermediate buffer
int xColorBufferStart = xBlock * this.BlockAreaSize.Width;
@ -59,6 +52,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
}
}
[MethodImpl(InliningOptions.ShortMethod)]
public static float TransformIDCT_1x1(float dc, float dequantizer, float normalizationValue, float maxValue)
{
dc *= dequantizer;
return (float)Math.Round(Numerics.Clamp(dc + normalizationValue, 0, maxValue));
}
[MethodImpl(InliningOptions.ShortMethod)]
public static void ScaledCopyTo(float value, ref float destRef, int destStrideWidth, int horizontalScale, int verticalScale)
{

20
src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs

@ -33,6 +33,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
// 8 => 8, no scaling
8,
// 8 => 4, 1/2 of the original size
4,
// 8 => 2, 1/4 of the original size
2,
// 8 => 1, 1/8 of the original size
1,
};
@ -256,6 +262,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
this.componentProcessors[i] = new DirectComponentProcessor(allocator, frame, jpegData, postProcessorBufferSize, frame.Components[i]);
}
break;
case 4:
for (int i = 0; i < this.componentProcessors.Length; i++)
{
this.componentProcessors[i] = new DownScalingComponentProcessor2(allocator, frame, jpegData, postProcessorBufferSize, frame.Components[i]);
}
break;
case 2:
for (int i = 0; i < this.componentProcessors.Length; i++)
{
this.componentProcessors[i] = new DownScalingComponentProcessor4(allocator, frame, jpegData, postProcessorBufferSize, frame.Components[i]);
}
break;
case 1:
for (int i = 0; i < this.componentProcessors.Length; i++)

159
tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs

@ -3,6 +3,7 @@
using System;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils;
using SixLabors.ImageSharp.Tests.TestUtilities;
using Xunit;
@ -14,8 +15,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[Trait("Format", "Jpg")]
public static class DCTTests
{
private const int MaxAllowedValue = short.MaxValue;
private const int MinAllowedValue = short.MinValue;
// size of input values is 10 bit max
private const float MaxInputValue = 1023;
private const float MinInputValue = -1024;
// output value range is 12 bit max
private const float MaxOutputValue = 4096;
private const float NormalizationValue = MaxOutputValue / 2;
internal static Block8x8F CreateBlockFromScalar(float value)
{
@ -41,7 +47,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[InlineData(3)]
public void LLM_TransformIDCT_CompareToNonOptimized(int seed)
{
float[] sourceArray = Create8x8RoundedRandomFloatData(MinAllowedValue, MaxAllowedValue, seed);
float[] sourceArray = Create8x8RandomFloatData(MinInputValue, MaxInputValue, seed);
var srcBlock = Block8x8F.Load(sourceArray);
@ -74,7 +80,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[InlineData(3)]
public void LLM_TransformIDCT_CompareToAccurate(int seed)
{
float[] sourceArray = Create8x8RoundedRandomFloatData(MinAllowedValue, MaxAllowedValue, seed);
float[] sourceArray = Create8x8RandomFloatData(MinInputValue, MaxInputValue, seed);
var srcBlock = Block8x8F.Load(sourceArray);
@ -113,7 +119,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
{
int seed = FeatureTestRunner.Deserialize<int>(serialized);
Span<float> src = Create8x8RoundedRandomFloatData(MinAllowedValue, MaxAllowedValue, seed);
Span<float> src = Create8x8RandomFloatData(MinInputValue, MaxInputValue, seed);
var srcBlock = default(Block8x8F);
srcBlock.LoadFrom(src);
@ -156,6 +162,147 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
HwIntrinsics.AllowAll | HwIntrinsics.DisableFMA | HwIntrinsics.DisableAVX | HwIntrinsics.DisableSIMD);
}
//[Theory]
//[InlineData(1)]
//[InlineData(2)]
//public void TranformIDCT_4x4(int seed)
//{
// Span<float> src = Create8x8RandomFloatData(MinInputValue, MaxInputValue, seed, 4, 4);
// var srcBlock = default(Block8x8F);
// srcBlock.LoadFrom(src);
// float[] expectedDest = new float[64];
// float[] temp = new float[64];
// // reference
// ReferenceImplementations.LLM_FloatingPoint_DCT.IDCT2D_llm(src, expectedDest, temp);
// // testee
// // Part of the IDCT calculations is fused into the quantization step
// // We must multiply input block with adjusted no-quantization matrix
// // before applying IDCT
// Block8x8F dequantMatrix = CreateBlockFromScalar(1);
// // Dequantization using unit matrix - no values are upscaled
// // as quant matrix is all 1's
// // This step is needed to apply adjusting multipliers to the input block
// FastFloatingPointDCT.AdjustToIDCT(ref dequantMatrix);
// // testee
// // IDCT implementation tranforms blocks after transposition
// srcBlock.TransposeInplace();
// DownScalingComponentProcessor2.TransformIDCT_4x4(ref srcBlock, ref dequantMatrix, NormalizationValue, MaxOutputValue);
// var comparer = new ApproximateFloatComparer(1f);
// Span<float> expectedSpan = expectedDest.AsSpan();
// Span<float> actualSpan = srcBlock.ToArray().AsSpan();
// AssertEquality(expectedSpan, actualSpan, comparer);
// AssertEquality(expectedSpan.Slice(8), actualSpan.Slice(8), comparer);
// AssertEquality(expectedSpan.Slice(16), actualSpan.Slice(16), comparer);
// AssertEquality(expectedSpan.Slice(24), actualSpan.Slice(24), comparer);
// static void AssertEquality(Span<float> expected, Span<float> actual, ApproximateFloatComparer comparer)
// {
// for (int x = 0; x < 4; x++)
// {
// float expectedValue = (float)Math.Round(Numerics.Clamp(expected[x] + NormalizationValue, 0, MaxOutputValue));
// Assert.Equal(expectedValue, actual[x], comparer);
// }
// }
//}
[Theory]
[InlineData(1)]
[InlineData(2)]
public void TranformIDCT_2x2(int seed)
{
Span<float> src = Create8x8RandomFloatData(MinInputValue, MaxInputValue, seed, 2, 2);
var srcBlock = default(Block8x8F);
srcBlock.LoadFrom(src);
float[] expectedDest = new float[64];
float[] temp = new float[64];
// reference
ReferenceImplementations.LLM_FloatingPoint_DCT.IDCT2D_llm(src, expectedDest, temp);
// testee
// Part of the IDCT calculations is fused into the quantization step
// We must multiply input block with adjusted no-quantization matrix
// before applying IDCT
Block8x8F dequantMatrix = CreateBlockFromScalar(1);
// Dequantization using unit matrix - no values are upscaled
// as quant matrix is all 1's
// This step is needed to apply adjusting multipliers to the input block
FastFloatingPointDCT.AdjustToIDCT(ref dequantMatrix);
// testee
// IDCT implementation tranforms blocks after transposition
srcBlock.TransposeInplace();
DownScalingComponentProcessor4.TransformIDCT_2x2(ref srcBlock, ref dequantMatrix, NormalizationValue, MaxOutputValue);
var comparer = new ApproximateFloatComparer(0.1f);
// top-left
float topLeftExpected = (float)Math.Round(Numerics.Clamp(expectedDest[0] + NormalizationValue, 0, MaxOutputValue));
Assert.Equal(topLeftExpected, srcBlock[0], comparer);
// top-right
float topRightExpected = (float)Math.Round(Numerics.Clamp(expectedDest[7] + NormalizationValue, 0, MaxOutputValue));
Assert.Equal(topRightExpected, srcBlock[1], comparer);
// bot-left
float botLeftExpected = (float)Math.Round(Numerics.Clamp(expectedDest[56] + NormalizationValue, 0, MaxOutputValue));
Assert.Equal(botLeftExpected, srcBlock[8], comparer);
// bot-right
float botRightExpected = (float)Math.Round(Numerics.Clamp(expectedDest[63] + NormalizationValue, 0, MaxOutputValue));
Assert.Equal(botRightExpected, srcBlock[9], comparer);
}
[Theory]
[InlineData(1)]
[InlineData(2)]
public void TranformIDCT_1x1(int seed)
{
Span<float> src = Create8x8RandomFloatData(MinInputValue, MaxInputValue, seed, 1, 1);
var srcBlock = default(Block8x8F);
srcBlock.LoadFrom(src);
float[] expectedDest = new float[64];
float[] temp = new float[64];
// reference
ReferenceImplementations.LLM_FloatingPoint_DCT.IDCT2D_llm(src, expectedDest, temp);
// testee
// Part of the IDCT calculations is fused into the quantization step
// We must multiply input block with adjusted no-quantization matrix
// before applying IDCT
Block8x8F dequantMatrix = CreateBlockFromScalar(1);
// Dequantization using unit matrix - no values are upscaled
// as quant matrix is all 1's
// This step is needed to apply adjusting multipliers to the input block
FastFloatingPointDCT.AdjustToIDCT(ref dequantMatrix);
// testee
// IDCT implementation tranforms blocks after transposition
srcBlock.TransposeInplace();
float actual = DownScalingComponentProcessor8.TransformIDCT_1x1(
srcBlock[0],
dequantMatrix[0],
NormalizationValue,
MaxOutputValue);
float expected = (float)Math.Round(Numerics.Clamp(expectedDest[0] + NormalizationValue, 0, MaxOutputValue));
Assert.Equal(actual, expected, new ApproximateFloatComparer(0.1f));
}
// Forward transform
// This test covers entire FDCT conversion chain
// This test checks all hardware implementations
@ -168,7 +315,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
{
int seed = FeatureTestRunner.Deserialize<int>(serialized);
Span<float> src = Create8x8RoundedRandomFloatData(MinAllowedValue, MaxAllowedValue, seed);
Span<float> src = Create8x8RandomFloatData(MinInputValue, MaxInputValue, seed);
var block = default(Block8x8F);
block.LoadFrom(src);

19
tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.FastFloatingPointDCT.cs

@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[InlineData(2, 200)]
public void LLM_IDCT_IsEquivalentTo_AccurateImplementation(int seed, int range)
{
float[] sourceArray = Create8x8RoundedRandomFloatData(-range, range, seed);
float[] sourceArray = Create8x8RandomFloatData(-range, range, seed);
var source = Block8x8F.Load(sourceArray);
@ -64,23 +64,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
this.CompareBlocks(expected, actual, 0.1f);
}
[Theory]
[InlineData(42, 1000)]
[InlineData(1, 1000)]
[InlineData(2, 1000)]
public void LLM_IDCT_CompareToIntegerRoundedAccurateImplementation(int seed, int range)
{
Block8x8F fSource = CreateRoundedRandomFloatBlock(-range, range, seed);
Block8x8 iSource = fSource.RoundAsInt16Block();
Block8x8 iExpected = ReferenceImplementations.AccurateDCT.TransformIDCT(ref iSource);
Block8x8F fExpected = iExpected.AsFloatBlock();
Block8x8F fActual = ReferenceImplementations.LLM_FloatingPoint_DCT.TransformIDCT(ref fSource);
this.CompareBlocks(fExpected, fActual, 2);
}
[Theory]
[InlineData(42)]
[InlineData(1)]

21
tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegFixture.cs

@ -89,33 +89,28 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
return result;
}
internal static float[] Create8x8RoundedRandomFloatData(int minValue, int maxValue, int seed = 42)
=> Create8x8RandomIntData(minValue, maxValue, seed).ConvertAllToFloat();
public static float[] Create8x8RandomFloatData(float minValue, float maxValue, int seed = 42)
public static float[] Create8x8RandomFloatData(float minValue, float maxValue, int seed = 42, int xBorder = 8, int yBorder = 8)
{
var rnd = new Random(seed);
var result = new float[64];
for (int i = 0; i < 8; i++)
float[] result = new float[64];
for (int y = 0; y < yBorder; y++)
{
for (int j = 0; j < 8; j++)
int y8 = y * 8;
for (int x = 0; x < xBorder; x++)
{
double val = rnd.NextDouble();
val *= maxValue - minValue;
val += minValue;
result[(i * 8) + j] = (float)val;
result[y8 + x] = (float)val;
}
}
return result;
}
internal static Block8x8F CreateRandomFloatBlock(float minValue, float maxValue, int seed = 42) =>
Block8x8F.Load(Create8x8RandomFloatData(minValue, maxValue, seed));
internal static Block8x8F CreateRoundedRandomFloatBlock(int minValue, int maxValue, int seed = 42) =>
Block8x8F.Load(Create8x8RoundedRandomFloatData(minValue, maxValue, seed));
internal static Block8x8F CreateRandomFloatBlock(float minValue, float maxValue, int seed = 42, int xBorder = 8, int yBorder = 8) =>
Block8x8F.Load(Create8x8RandomFloatData(minValue, maxValue, seed, xBorder, yBorder));
internal void Print8x8Data<T>(T[] data) => this.Print8x8Data(new Span<T>(data));

Loading…
Cancel
Save