Browse Source

Correct AV1 inverse transform row traversal

pull/2633/head
James Jackson-South 2 months ago
parent
commit
9ef4059ee6
  1. 1
      HEIF_IMPLEMENTATION_PLAN.md
  2. 4
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1Inverse2dTransformer.cs
  3. 46
      tests/ImageSharp.Tests/Formats/Heif/Av1/Av1InverseTransformTests.cs

1
HEIF_IMPLEMENTATION_PLAN.md

@ -59,6 +59,7 @@ This snapshot pins or classifies the available references and failures; it does
| `Av1YuvConverter.ConvertToRgb`, `ConvertFromRgb`, scalar row conversion, and chroma reconstruction | H.273 formulas 20-31 and the identity, YCgCo, and non-constant-luminance matrix formulas; AV1 section 6.4.2 chroma sample positions | libavif `src/reformat.c` and `src/colr.c` at `092276ce89098ead06db80975173191e5fee1826`; libaom `aom/aom_image.h` at `03087864cf4bea6abb0d28f95cf7843511413d8f` | Scalar behavioral oracle for 8-bit full/limited-range conversion. Decode covers monochrome, YUV 4:2:0, 4:2:2, and 4:4:4 with AV1 chroma sample positioning; encode remains YUV 4:4:4 at this snapshot. Later high-bit-depth and SIMD paths must match it. |
| `Av1FrameBuffer` high-bit-depth sample layout and `Av1YuvConverter` 10/12-bit output conversion | AV1 section 6.4.1 bit depth and H.273 sample-range scaling | libaom `aom_scale/yv12config.h`, `av1/common/idct.c`, and `av1/common/reconintra.c` at `03087864cf4bea6abb0d28f95cf7843511413d8f`; libavif `src/avif.c` and `src/reformat.c` at `092276ce89098ead06db80975173191e5fee1826` | Establish two-byte native sample storage with sample-unit strides for 10/12-bit reconstruction and use the same scalar color model at every supported bit depth. |
| `Av1PredictionDecoder` and the scalar DC, directional, Paeth, smooth, and filter-intra predictors | AV1 sections 7.11.2 and 7.11.2.3 intra prediction | libaom `aom_dsp/intrapred.c` and `av1/common/reconintra.c` at `03087864cf4bea6abb0d28f95cf7843511413d8f` | Behavioral oracle for neighbor addressing, directional upsampling, Paeth selection, smooth normalization, filter-intra taps, and chroma-from-luma row strides. Existing managed scalar predictors remain the implementation base. The WIP rectangular smooth digest expectations encode width/height-swapped weights and must be replaced only from an independently generated oracle, not regenerated from this implementation. |
| `Av1Inverse2dTransformer` and `Av1InverseTransformerFactory` | AV1 section 7.11.2 inverse transform and reconstruction | libaom `av1/common/av1_inv_txfm1d.c`, `av1/common/av1_inv_txfm2d.c`, and `av1/common/idct.c` at `03087864cf4bea6abb0d28f95cf7843511413d8f` | Scalar transform oracle for coefficient-row traversal, intermediate layout, stage ranges, clipping, and high-bit-depth sample addition. The managed 16-bit overload is also used as a parity oracle for the byte overload. |
This table is intentionally incomplete. Add a row before each additional AV1 or HEVC algorithm is ported or materially reshaped.

4
src/ImageSharp/Formats/Heif/Av1/Transform/Av1Inverse2dTransformer.cs

@ -208,8 +208,8 @@ internal class Av1Inverse2dTransformer
}
Av1InverseTransformMath.RoundShiftArray(bufPtr, transformWidth, -shift[0]);
input.Slice(transformWidth);
bufPtr.Slice(transformWidth);
input = input[transformWidth..];
bufPtr = bufPtr[transformWidth..];
}
// Columns

46
tests/ImageSharp.Tests/Formats/Heif/Av1/Av1InverseTransformTests.cs

@ -221,6 +221,52 @@ public class Av1InverseTransformTests
Assert.True(CompareWithError<short>(expected, actual, 1));
}
[Theory]
[InlineData((int)Av1TransformType.DctDct)]
[InlineData((int)Av1TransformType.AdstAdst)]
[InlineData((int)Av1TransformType.Identity)]
public void EightBitTransformMatchesSixteenBitPipeline(int transformTypeIndex)
{
const int width = 4;
Av1TransformType transformType = (Av1TransformType)transformTypeIndex;
int[] coefficients =
[
48, -15, 7, 3,
11, 5, -9, 2,
-6, 8, 4, -3,
9, -2, 6, 1,
];
byte[] actual = new byte[width * width];
short[] expected = new short[width * width];
Array.Fill(actual, (byte)96);
Array.Fill(expected, (short)96);
int[] actualBuffer = new int[(width * width) + (2 * width)];
int[] expectedBuffer = new int[(width * width) + (2 * width)];
Av1Transform2dFlipConfiguration actualConfig = new(transformType, Av1TransformSize.Size4x4);
Av1Transform2dFlipConfiguration expectedConfig = new(transformType, Av1TransformSize.Size4x4);
Av1Inverse2dTransformer.Transform2dAdd(
coefficients,
actual,
width,
actual,
width,
actualConfig,
actualBuffer);
Av1Inverse2dTransformer.Transform2dAdd(
coefficients,
expected,
width,
expected,
width,
expectedConfig,
expectedBuffer,
8);
Assert.Equal(expected.Select(value => (byte)value), actual);
}
private static void AssertAccuracy1d(
Av1TransformType transformType,
Av1TransformSize transformSize,

Loading…
Cancel
Save