diff --git a/HEIF_IMPLEMENTATION_PLAN.md b/HEIF_IMPLEMENTATION_PLAN.md
index 08e7cd5831..f843b3e832 100644
--- a/HEIF_IMPLEMENTATION_PLAN.md
+++ b/HEIF_IMPLEMENTATION_PLAN.md
@@ -29,7 +29,7 @@ Checkboxes may be marked complete only when the implementation and the verificat
## Delivery dashboard
-Last reconciled with the source tree on 2026-08-27 against the worktree based on commit `cf0c81eb3`, including the completed AV1 transform, OBU-framing, and independently verified intra-block-copy checkpoints. This dashboard is the authoritative delivery order. The detailed phase checklists below provide subsystem evidence; they do not override the current-stage marker or permit work to skip ahead.
+Last reconciled with the source tree on 2026-08-27 against the worktree based on commit `07cc0291e`, including the completed AV1 transform, OBU-framing, intra-block-copy, and 12-profile reconstruction checkpoints. This dashboard is the authoritative delivery order. The detailed phase checklists below provide subsystem evidence; they do not override the current-stage marker or permit work to skip ahead.
Status meanings:
@@ -40,13 +40,13 @@ Status meanings:
Current development stage: **Stage 3 — complete AV1 still-image decoding.** The transform checkpoint is closed: forward transforms use one libaom-shaped SIMD-first operator architecture across `Vector512`, `Vector256`, and `Vector128`, with scalar fallback; inverse production traversal uses the verified `Vector256` and `Vector128` tiers with scalar fallback; and implementation-mechanic type and file suffixes have been removed. Neither AV1 nor HEVC production encoding is implemented.
-Immediate checkpoint: **remove every remaining valid AV1 still-image unsupported branch and prove the complete decode matrix.** Each syntax tool must be implemented through the established SIMD-first architecture with scalar fallback and verified with independent AVIF/libaom evidence across supported bit depths, chroma layouts, filters, grain, and color signaling.
+Immediate checkpoint: **inventory and remove every remaining valid AV1 still-image unsupported branch, one independently verified syntax tool at a time.** The base AV1 profile matrix is now exact across 8/10/12-bit monochrome, 4:2:0, 4:2:2, and 4:4:4 reconstruction and presentation under every available dispatch tier. That matrix is the regression gate for the remaining compression-tool fixtures; it does not by itself prove every normative still-image tool.
| Order | Delivery stage | State | Delivered state | Gate that remains open |
| --- | --- | --- | --- | --- |
| 1 | Baseline, provenance, documentation, and public contract | In progress | Pinned codec references, a bounded image-only scope, encoder options, typed bit depth, decoder-option propagation, and extensive HEIF documentation exist. | Complete the all-file documentation audit, record a fresh Release baseline, finish distinct public HEIC/AVIF save boundaries, and close API review. |
| 2 | Bounded HEIF item and image-sequence container | In progress | Still-item parsing, grids, auxiliary alpha, metadata properties, bounded image-sequence tracks, Identify, and all-sync AV1 sequence presentation are connected. | Complete adversarial boundary coverage, remaining item/property behavior, reference-dependent sequence reconstruction, and the bounded sequence writer. |
-| 3 | Still-image AV1 and HEVC decoding | **Current** | HEVC reconstruction reaches exact HM/libheif fixtures across the recorded 8/10/12-bit and chroma cases. AV1 includes bounded OBU framing, reconstruction, filters, grain, color, transforms, and independently verified intra-block-copy syntax and prediction through SIMD-first static-generic operators. | Remove every other valid AV1 still-image unsupported branch with independent vectors, then complete the remaining HEVC profile and Range Extensions matrix. |
+| 3 | Still-image AV1 and HEVC decoding | **Current** | HEVC reconstruction reaches exact HM/libheif fixtures across the recorded 8/10/12-bit and chroma cases. AV1 includes bounded OBU framing, reconstruction, filters, grain, color, transforms, intra-block copy, and an exact independent 12-profile bit-depth/chroma matrix through every dispatch tier. | Remove every remaining valid AV1 still-image unsupported branch with independent compression-tool vectors, then complete the remaining HEVC profile and Range Extensions matrix. |
| 4 | Complete decoded presentation and animation | In progress | Shared SIMD-first AV1/HEVC color conversion, ICC application, grids, transforms, direct planar alpha composition, frame metadata, repetition, and independently decodable AV1 sequence samples exist. | Close the full color/ICC cross-product, HEVC sequence decoding, AV1/HEVC reference-dependent samples, frame-local metadata/alpha behavior, and independent animated decode vectors. |
| 5 | AV1/AVIF encoding | Not started | RGB-to-planar conversion, forward transforms, OBU writer foundations, options, and container-writing infrastructure exist. | `HeifEncoderCore` still rejects AV1. Implement a real independently decodable lossy/lossless AV1 payload and the complete AVIF item/metadata matrix. |
| 6 | HEVC/HEIC encoding | Not started | Shared input color conversion, options, and HEIF writer infrastructure exist. | `HeifEncoderCore` still rejects HEVC. Implement a real independently decodable lossy/lossless HEVC payload and the complete HEIC item/metadata matrix. |
@@ -56,7 +56,8 @@ Immediate checkpoint: **remove every remaining valid AV1 still-image unsupported
## Immediate execution queue
- [x] Finish the libaom-shaped AV1 forward-transform architecture, measured production dispatch, inverse-tier correction, suffix cleanup, `FeatureTestRunner` matrix, and focused Release verification recorded below.
-- [ ] **Current:** complete AV1 still-image decoding for every valid still syntax path and independently verify the full bit-depth, chroma, compression-tool, filter, grain, and color matrix.
+- [x] Close the base AV1 profile matrix with exact native-plane and presented-image comparisons for 8/10/12-bit monochrome, 4:2:0, 4:2:2, and 4:4:4 fixtures under normal, AVX2, 128-bit, and scalar dispatch.
+- [ ] **Current:** inventory and remove every remaining valid AV1 still-image unsupported branch, adding exact independent compression-tool fixtures to the profile-matrix regression gate.
- [ ] Complete the remaining HEVC still-image profile and Range Extensions matrix with exact independent native-plane and presentation evidence.
- [ ] Close shared decoded presentation, ICC, alpha, grid, transform, metadata, and animated AV1/HEVC decode gates.
- [ ] Implement and independently verify real AV1/AVIF still encoding.
@@ -500,6 +501,8 @@ Implement and verify in dependency order:
- [ ] One coherent decoder lifecycle that retains parsed frame and tile state and disposes all buffers deterministically.
- [ ] Tile partitioning, mode information, segmentation, delta quantization, transform-size selection, coefficient token decode, inverse quantization, and inverse transforms.
- [x] Match libaom's depth-first traversal and frame-edge behavior for all ten AV1 partition types. Independent 8/10/12-bit streams collectively select every terminal partition shape and contain nested block geometry that requires recursive `Split` traversal; their complete native planes remain byte-exact under normal hardware dispatch and the scalar fallback.
+ - [x] Verify the complete 8/10/12-bit monochrome, 4:2:0, 4:2:2, and 4:4:4 base profile matrix against pinned libaom native planes and pinned libavif presentation output. All 12 fixtures match exactly with normal dispatch, AVX-512 disabled, AVX disabled, and all hardware intrinsics disabled through `FeatureTestRunner`.
+ - [x] Correct monochrome plane classification, maximum-superblock-row loop-filter traversal, and identity/one-dimensional inverse-quantization-matrix selection exposed by the profile matrix. The quantization rule matches libaom's `tx_type < IDTX` boundary and is verified by the exact native-plane oracle rather than a tolerance.
- [ ] Intra prediction, including every directional, smooth, Paeth, CFL, filter-intra, and palette case permitted by AV1.
- [x] Implement SIMD-first chroma-from-luma storage, 4:4:4/4:2:2/4:2:0 subsampling, rounded mean subtraction, and 8/10/12-bit prediction with exact scalar fallback and `FeatureTestRunner` parity.
- [x] Implement allocation-free SIMD-first palette reconstruction for palette sizes 2-8, transform widths 4-64, and 8/10/12-bit samples with exact scalar fallback and `FeatureTestRunner` parity.
diff --git a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuColorConfig.cs b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuColorConfig.cs
index 62783cbde4..c3de94a038 100644
--- a/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuColorConfig.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/OpenBitstreamUnit/ObuColorConfig.cs
@@ -90,6 +90,13 @@ internal class ObuColorConfig
/// The corresponding AV1 color format.
public Av1ColorFormat GetColorFormat()
{
+ if (this.IsMonochrome)
+ {
+ // AV1 sets both subsampling flags for monochrome sequences even though no chroma planes exist. The
+ // mono_chrome syntax therefore owns the plane layout and must take precedence over those derived flags.
+ return Av1ColorFormat.Yuv400;
+ }
+
Av1ColorFormat format = Av1ColorFormat.Yuv400;
if (this.SubSamplingX && this.SubSamplingY)
{
diff --git a/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopFilter/Av1LoopFilterDecoder.cs b/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopFilter/Av1LoopFilterDecoder.cs
index 7a4e2ef946..9c8ec96a3e 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopFilter/Av1LoopFilterDecoder.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopFilter/Av1LoopFilterDecoder.cs
@@ -61,7 +61,7 @@ internal class Av1LoopFilterDecoder
}
///
- /// Filters every enabled plane, processing all vertical boundaries before horizontal boundaries.
+ /// Filters every enabled plane in maximum-superblock row bands.
///
public void DecodeFrame()
{
@@ -72,45 +72,57 @@ internal class Av1LoopFilterDecoder
}
ObuColorConfig colorConfig = this.sequenceHeader.ColorConfig;
- for (int planeIndex = 0; planeIndex < colorConfig.PlaneCount; planeIndex++)
+ int modeInfoRowsPerBand = 1 << (Av1Constants.MaxSuperBlockSizeLog2 - Av1Constants.ModeInfoSizeLog2);
+
+ // libaom's loop_filter_rows processes one MAX_MIB_SIZE band at a time so that a completed band can be
+ // presented before the remainder of the frame. The ordering is observable because the vertical and
+ // horizontal passes modify intersecting sample neighborhoods in place.
+ for (int rowStart = 0; rowStart < this.frameHeader.ModeInfoRowCount; rowStart += modeInfoRowsPerBand)
{
- Av1Plane plane = (Av1Plane)planeIndex;
- int planeFilterLevel = plane switch
- {
- Av1Plane.U => filterParameters.FilterLevelU,
- Av1Plane.V => filterParameters.FilterLevelV,
- _ => Math.Max(filterParameters.FilterLevel[0], filterParameters.FilterLevel[1])
- };
+ int rowEnd = Math.Min(rowStart + modeInfoRowsPerBand, this.frameHeader.ModeInfoRowCount);
- if (planeFilterLevel == 0)
+ for (int planeIndex = 0; planeIndex < colorConfig.PlaneCount; planeIndex++)
{
- continue;
- }
+ Av1Plane plane = (Av1Plane)planeIndex;
+ int planeFilterLevel = plane switch
+ {
+ Av1Plane.U => filterParameters.FilterLevelU,
+ Av1Plane.V => filterParameters.FilterLevelV,
+ _ => Math.Max(filterParameters.FilterLevel[0], filterParameters.FilterLevel[1])
+ };
- int subX = plane != Av1Plane.Y && colorConfig.SubSamplingX ? 1 : 0;
- int subY = plane != Av1Plane.Y && colorConfig.SubSamplingY ? 1 : 0;
- Span lowBitDepthSamples = default;
- Span highBitDepthSamples = default;
- int stride;
+ if (planeFilterLevel == 0)
+ {
+ continue;
+ }
- if (this.frameBuffer.BytesPerSample == 2)
- {
- Span signedSamples = this.frameBuffer.DeriveBlockPointer16(plane, Point.Empty, subX, subY, out stride);
- highBitDepthSamples = MemoryMarshal.Cast(signedSamples);
- }
- else
- {
- lowBitDepthSamples = this.frameBuffer.DeriveBlockPointer(plane, Point.Empty, subX, subY, out stride);
- }
+ int subX = plane != Av1Plane.Y && colorConfig.SubSamplingX ? 1 : 0;
+ int subY = plane != Av1Plane.Y && colorConfig.SubSamplingY ? 1 : 0;
+ Span lowBitDepthSamples = default;
+ Span highBitDepthSamples = default;
+ int stride;
- this.FilterPlane(plane, subX, subY, stride, lowBitDepthSamples, highBitDepthSamples);
+ if (this.frameBuffer.BytesPerSample == 2)
+ {
+ Span signedSamples = this.frameBuffer.DeriveBlockPointer16(plane, Point.Empty, subX, subY, out stride);
+ highBitDepthSamples = MemoryMarshal.Cast(signedSamples);
+ }
+ else
+ {
+ lowBitDepthSamples = this.frameBuffer.DeriveBlockPointer(plane, Point.Empty, subX, subY, out stride);
+ }
+
+ this.FilterPlane(plane, rowStart, rowEnd, subX, subY, stride, lowBitDepthSamples, highBitDepthSamples);
+ }
}
}
///
- /// Filters one plane in the AV1 vertical-then-horizontal boundary order.
+ /// Filters one plane band in the AV1 vertical-then-horizontal boundary order.
///
/// The color plane to filter.
+ /// The inclusive band origin in luma 4x4 units.
+ /// The exclusive band limit in luma 4x4 units.
/// The horizontal chroma subsampling shift.
/// The vertical chroma subsampling shift.
/// The plane stride in logical samples.
@@ -118,6 +130,8 @@ internal class Av1LoopFilterDecoder
/// The high-bit-depth plane storage, when active.
private void FilterPlane(
Av1Plane plane,
+ int rowStart,
+ int rowEnd,
int subX,
int subY,
int stride,
@@ -127,25 +141,41 @@ internal class Av1LoopFilterDecoder
int rowStep = 1 << subY;
int columnStep = 1 << subX;
- // The AV1 result is independent of ordering within a pass, but vertical filtering must finish before any
- // horizontal filtering begins because the two directions modify intersecting sample neighborhoods.
- for (int pass = 0; pass < 2; pass++)
+ // The vertical pass advances across each row because successive vertical edges do not share modified samples.
+ // Chroma rows retain luma-grid coordinates and therefore advance by two mode-info units when subsampled.
+ for (int row = rowStart; row < rowEnd; row += rowStep)
{
- for (int row = 0; row < this.frameHeader.ModeInfoRowCount; row += rowStep)
+ for (int column = 0; column < this.frameHeader.ModeInfoColumnCount; column += columnStep)
{
- for (int column = 0; column < this.frameHeader.ModeInfoColumnCount; column += columnStep)
- {
- this.FilterEdge(
- plane,
- pass,
- row,
- column,
- subX,
- subY,
- stride,
- lowBitDepthSamples,
- highBitDepthSamples);
- }
+ this.FilterEdge(
+ plane,
+ 0,
+ row,
+ column,
+ subX,
+ subY,
+ stride,
+ lowBitDepthSamples,
+ highBitDepthSamples);
+ }
+ }
+
+ // Horizontal edges are visited down each column. This ordering is observable because adjacent horizontal
+ // filters can modify samples that a later edge reads, so it must match libaom's av1_filter_block_plane_horz.
+ for (int column = 0; column < this.frameHeader.ModeInfoColumnCount; column += columnStep)
+ {
+ for (int row = rowStart; row < rowEnd; row += rowStep)
+ {
+ this.FilterEdge(
+ plane,
+ 1,
+ row,
+ column,
+ subX,
+ subY,
+ stride,
+ lowBitDepthSamples,
+ highBitDepthSamples);
}
}
}
@@ -247,6 +277,7 @@ internal class Av1LoopFilterDecoder
int boundaryLimit = (2 * (filterLevel + 2)) + limit;
int highEdgeVarianceThreshold = filterLevel >> 4;
int q0Offset = stride + (planeY * stride) + planeX;
+
if (this.frameBuffer.BytesPerSample == 2)
{
if (verticalBoundary)
diff --git a/src/ImageSharp/Formats/Heif/Av1/Pipeline/Quantizers/Av1InverseQuantizer.cs b/src/ImageSharp/Formats/Heif/Av1/Pipeline/Quantizers/Av1InverseQuantizer.cs
index d7a6957a24..233117e071 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Pipeline/Quantizers/Av1InverseQuantizer.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Pipeline/Quantizers/Av1InverseQuantizer.cs
@@ -95,13 +95,14 @@ internal class Av1InverseQuantizer
short dequantDc = this.deQuantsDeltaQ.GetDc(mode.SegmentId, plane);
short dequantAc = this.deQuantsDeltaQ.GetAc(mode.SegmentId, plane);
- // The final matrix level is flat. Lossless blocks, frames without matrices, and one-dimensional transforms
- // must use it so coefficient frequency does not change the signaled dequantization value.
+ // The final matrix level is flat. Lossless blocks and frames without matrices select it globally. AV1 also
+ // requires identity and one-dimensional transform types, which occupy the enum range from Identity onward,
+ // to bypass frequency weighting even when the frame signals quantization matrices.
int qmLevel = lossless || !usingQuantizationMatrix
? Av1ScanOrderConstants.QuantizationMatrixLevelCount - 1
: this.frameHeader.SegmentationParameters.QMLevel[(int)plane][mode.SegmentId];
- ReadOnlySpan iqMatrix = (transformType.ToClass() == Av1TransformClass.Class2D)
+ ReadOnlySpan iqMatrix = transformType < Av1TransformType.Identity
? Av1InverseQuantizationLookup.GetQuantizationMatrix(qmLevel, plane, transformSize)
: Av1InverseQuantizationLookup.GetQuantizationMatrix(Av1Constants.QuantificationMatrixLevelCount - 1, Av1Plane.Y, transformSize);
diff --git a/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1ReconstructionConformanceTests.cs b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1ReconstructionConformanceTests.cs
index a918d1126e..8241a5fa06 100644
--- a/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1ReconstructionConformanceTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1ReconstructionConformanceTests.cs
@@ -2,12 +2,15 @@
// Licensed under the Six Labors Split License.
using System.Buffers.Binary;
+using System.Text;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Heif;
using SixLabors.ImageSharp.Formats.Heif.Av1;
using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
using SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
+using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
using SixLabors.ImageSharp.Memory;
+using SixLabors.ImageSharp.Metadata.Profiles.Cicp;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests.TestUtilities;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
@@ -20,6 +23,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Heif.Av1;
[Trait("Format", "Avif")]
public class Av1ReconstructionConformanceTests
{
+ ///
+ /// The width and height of one CDEF unit in 4x4 luma mode-information units.
+ ///
+ private const int CdefUnitModeInfoSize = 16;
+
///
/// The hardware configurations covering normal SIMD dispatch and the scalar fallback.
///
@@ -37,6 +45,12 @@ public class Av1ReconstructionConformanceTests
private const HwIntrinsics IntraBlockCopyConfigurations =
HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic;
+ ///
+ /// The hardware configurations covering the narrower vector widths and scalar fallback for the profile matrix.
+ ///
+ private const HwIntrinsics ProfileFallbackConfigurations =
+ HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic;
+
///
/// The hardware configurations covering the 128-bit and scalar lossless inverse-transform paths.
///
@@ -88,6 +102,16 @@ public class Av1ReconstructionConformanceTests
///
private const int LosslessFixtureHeight = 60;
+ ///
+ /// The displayed width shared by the independent AV1 profile fixtures.
+ ///
+ private const int ProfileFixtureWidth = 512;
+
+ ///
+ /// The displayed height shared by the independent AV1 profile fixtures.
+ ///
+ private const int ProfileFixtureHeight = 256;
+
///
/// The hardware configurations covering the available vector widths and the scalar color-conversion fallback.
///
@@ -150,6 +174,38 @@ public class Av1ReconstructionConformanceTests
public void DecodeWithActiveCdefMatchesPinnedLibavifPresentation()
=> FeatureTestRunner.RunWithHwIntrinsicsFeature(ValidatePresentedFixtures, PresentationConfigurations);
+ ///
+ /// Verifies exact native reconstruction for every valid AV1 profile, bit-depth, and chroma-format combination
+ /// supported by AVIF across every available vector width and the scalar fallback.
+ ///
+ [Fact]
+ public void DecodeProfileMatrixMatchesPinnedLibaomReference()
+ => ValidateProfileNativeFixtures();
+
+ ///
+ /// Verifies exact native reconstruction for every valid AV1 profile, bit-depth, and chroma-format combination
+ /// under each narrower vector width and the scalar fallback.
+ ///
+ [Fact]
+ public void DecodeProfileMatrixFallbacksMatchPinnedLibaomReference()
+ => FeatureTestRunner.RunWithHwIntrinsicsFeature(ValidateProfileNativeFixtures, ProfileFallbackConfigurations);
+
+ ///
+ /// Verifies exact presented pixels, public bit-depth metadata, and CICP signaling for every valid AV1 profile,
+ /// bit-depth, and chroma-format combination supported by AVIF.
+ ///
+ [Fact]
+ public void DecodeProfileMatrixMatchesPinnedLibavifPresentation()
+ => ValidateProfilePresentedFixtures();
+
+ ///
+ /// Verifies exact presented pixels, public bit-depth metadata, and CICP signaling under each narrower vector width
+ /// and the scalar fallback.
+ ///
+ [Fact]
+ public void DecodeProfileMatrixFallbacksMatchPinnedLibavifPresentation()
+ => FeatureTestRunner.RunWithHwIntrinsicsFeature(ValidateProfilePresentedFixtures, ProfileFallbackConfigurations);
+
///
/// Verifies decoded luma and chroma palette syntax and exact native samples against scalar libaom for an
/// independently encoded AV1 still-picture stream.
@@ -292,6 +348,275 @@ public class Av1ReconstructionConformanceTests
Assert.Equal(RequiredPartitionCoverage, coverage & RequiredPartitionCoverage);
}
+ ///
+ /// Validates every native profile fixture under the hardware configuration selected by
+ /// .
+ ///
+ private static void ValidateProfileNativeFixtures()
+ {
+ ValidateProfileNativeFixture(
+ TestImages.Heif.Av1Profile8BitMonochromeAvif,
+ TestImages.Heif.Av1Profile8BitMonochromeReference,
+ Av1BitDepth.EightBit,
+ Av1ColorFormat.Yuv400,
+ ObuSequenceProfile.Main);
+
+ ValidateProfileNativeFixture(
+ TestImages.Heif.Av1Profile8Bit420Avif,
+ TestImages.Heif.Av1Profile8Bit420Reference,
+ Av1BitDepth.EightBit,
+ Av1ColorFormat.Yuv420,
+ ObuSequenceProfile.Main);
+
+ ValidateProfileNativeFixture(
+ TestImages.Heif.Av1Profile8Bit422Avif,
+ TestImages.Heif.Av1Profile8Bit422Reference,
+ Av1BitDepth.EightBit,
+ Av1ColorFormat.Yuv422,
+ ObuSequenceProfile.Professional);
+
+ ValidateProfileNativeFixture(
+ TestImages.Heif.Av1Profile8Bit444Avif,
+ TestImages.Heif.Av1Profile8Bit444Reference,
+ Av1BitDepth.EightBit,
+ Av1ColorFormat.Yuv444,
+ ObuSequenceProfile.High);
+
+ ValidateProfileNativeFixture(
+ TestImages.Heif.Av1Profile10BitMonochromeAvif,
+ TestImages.Heif.Av1Profile10BitMonochromeReference,
+ Av1BitDepth.TenBit,
+ Av1ColorFormat.Yuv400,
+ ObuSequenceProfile.Main);
+
+ ValidateProfileNativeFixture(
+ TestImages.Heif.Av1Profile10Bit420Avif,
+ TestImages.Heif.Av1Profile10Bit420Reference,
+ Av1BitDepth.TenBit,
+ Av1ColorFormat.Yuv420,
+ ObuSequenceProfile.Main);
+
+ ValidateProfileNativeFixture(
+ TestImages.Heif.Av1Profile10Bit422Avif,
+ TestImages.Heif.Av1Profile10Bit422Reference,
+ Av1BitDepth.TenBit,
+ Av1ColorFormat.Yuv422,
+ ObuSequenceProfile.Professional);
+
+ ValidateProfileNativeFixture(
+ TestImages.Heif.Av1Profile10Bit444Avif,
+ TestImages.Heif.Av1Profile10Bit444Reference,
+ Av1BitDepth.TenBit,
+ Av1ColorFormat.Yuv444,
+ ObuSequenceProfile.High);
+
+ ValidateProfileNativeFixture(
+ TestImages.Heif.Av1Profile12BitMonochromeAvif,
+ TestImages.Heif.Av1Profile12BitMonochromeReference,
+ Av1BitDepth.TwelveBit,
+ Av1ColorFormat.Yuv400,
+ ObuSequenceProfile.Professional);
+
+ ValidateProfileNativeFixture(
+ TestImages.Heif.Av1Profile12Bit420Avif,
+ TestImages.Heif.Av1Profile12Bit420Reference,
+ Av1BitDepth.TwelveBit,
+ Av1ColorFormat.Yuv420,
+ ObuSequenceProfile.Professional);
+
+ ValidateProfileNativeFixture(
+ TestImages.Heif.Av1Profile12Bit422Avif,
+ TestImages.Heif.Av1Profile12Bit422Reference,
+ Av1BitDepth.TwelveBit,
+ Av1ColorFormat.Yuv422,
+ ObuSequenceProfile.Professional);
+
+ ValidateProfileNativeFixture(
+ TestImages.Heif.Av1Profile12Bit444Avif,
+ TestImages.Heif.Av1Profile12Bit444Reference,
+ Av1BitDepth.TwelveBit,
+ Av1ColorFormat.Yuv444,
+ ObuSequenceProfile.Professional);
+ }
+
+ ///
+ /// Validates every presented profile fixture under the hardware configuration selected by
+ /// .
+ ///
+ private static void ValidateProfilePresentedFixtures()
+ {
+ ValidateProfilePresentedFixture(
+ TestImages.Heif.Av1Profile8BitMonochromeAvif,
+ TestImages.Heif.Av1Profile8BitMonochromePresentationReference,
+ HeifBitDepth.Bit8);
+
+ ValidateProfilePresentedFixture(
+ TestImages.Heif.Av1Profile8Bit420Avif,
+ TestImages.Heif.Av1Profile8Bit420PresentationReference,
+ HeifBitDepth.Bit8);
+
+ ValidateProfilePresentedFixture(
+ TestImages.Heif.Av1Profile8Bit422Avif,
+ TestImages.Heif.Av1Profile8Bit422PresentationReference,
+ HeifBitDepth.Bit8);
+
+ ValidateProfilePresentedFixture(
+ TestImages.Heif.Av1Profile8Bit444Avif,
+ TestImages.Heif.Av1Profile8Bit444PresentationReference,
+ HeifBitDepth.Bit8);
+
+ ValidateProfilePresentedFixture(
+ TestImages.Heif.Av1Profile10BitMonochromeAvif,
+ TestImages.Heif.Av1Profile10BitMonochromePresentationReference,
+ HeifBitDepth.Bit10);
+
+ ValidateProfilePresentedFixture(
+ TestImages.Heif.Av1Profile10Bit420Avif,
+ TestImages.Heif.Av1Profile10Bit420PresentationReference,
+ HeifBitDepth.Bit10);
+
+ ValidateProfilePresentedFixture(
+ TestImages.Heif.Av1Profile10Bit422Avif,
+ TestImages.Heif.Av1Profile10Bit422PresentationReference,
+ HeifBitDepth.Bit10);
+
+ ValidateProfilePresentedFixture(
+ TestImages.Heif.Av1Profile10Bit444Avif,
+ TestImages.Heif.Av1Profile10Bit444PresentationReference,
+ HeifBitDepth.Bit10);
+
+ ValidateProfilePresentedFixture(
+ TestImages.Heif.Av1Profile12BitMonochromeAvif,
+ TestImages.Heif.Av1Profile12BitMonochromePresentationReference,
+ HeifBitDepth.Bit12);
+
+ ValidateProfilePresentedFixture(
+ TestImages.Heif.Av1Profile12Bit420Avif,
+ TestImages.Heif.Av1Profile12Bit420PresentationReference,
+ HeifBitDepth.Bit12);
+
+ ValidateProfilePresentedFixture(
+ TestImages.Heif.Av1Profile12Bit422Avif,
+ TestImages.Heif.Av1Profile12Bit422PresentationReference,
+ HeifBitDepth.Bit12);
+
+ ValidateProfilePresentedFixture(
+ TestImages.Heif.Av1Profile12Bit444Avif,
+ TestImages.Heif.Av1Profile12Bit444PresentationReference,
+ HeifBitDepth.Bit12);
+ }
+
+ ///
+ /// Validates one independently encoded AVIF against its native Y4M reference and signaled sequence profile.
+ ///
+ /// The complete AVIF container.
+ /// The native Y4M output produced by the pinned scalar libaom-backed decoder.
+ /// The expected AV1 sample precision.
+ /// The expected native chroma-sampling layout.
+ /// The AV1 profile required by the bit-depth and chroma-format combination.
+ private static void ValidateProfileNativeFixture(
+ string imagePath,
+ string referencePath,
+ Av1BitDepth bitDepth,
+ Av1ColorFormat colorFormat,
+ ObuSequenceProfile sequenceProfile)
+ {
+ byte[] imageBytes = TestFile.Create(imagePath).Bytes;
+ byte[] referenceBytes = TestFile.Create(referencePath).Bytes;
+ (string chromaTag, string extendedChromaTag) = GetY4mColorSpace(bitDepth, colorFormat);
+ string expectedHeader =
+ $"YUV4MPEG2 W{ProfileFixtureWidth} H{ProfileFixtureHeight} F25:1 Ip A0:0 C{chromaTag} XYSCSS={extendedChromaTag} XCOLORRANGE=FULL\n";
+
+ int headerTerminator = referenceBytes.AsSpan().IndexOf((byte)'\n');
+ Assert.NotEqual(-1, headerTerminator);
+ int fileHeaderLength = headerTerminator + 1;
+ Assert.Equal(expectedHeader, Encoding.ASCII.GetString(referenceBytes, 0, fileHeaderLength));
+
+ ReadOnlySpan nativeReference = referenceBytes.AsSpan(fileHeaderLength);
+ ReadOnlySpan frameHeader = "FRAME\n"u8;
+ Assert.True(nativeReference.StartsWith(frameHeader));
+ nativeReference = nativeReference[frameHeader.Length..];
+
+ Span payload = GetSoleAv1ItemPayload(imageBytes);
+ using Av1Decoder decoder = new(Configuration.Default);
+ using Av1FrameBuffer frameBuffer = decoder.DecodeFrameBuffer(payload, null, null, out _);
+
+ Assert.Equal(ProfileFixtureWidth, frameBuffer.Width);
+ Assert.Equal(ProfileFixtureHeight, frameBuffer.Height);
+ Assert.Equal(bitDepth, frameBuffer.BitDepth);
+ Assert.Equal(colorFormat, frameBuffer.ColorFormat);
+
+ ObuSequenceHeader sequenceHeader = Assert.IsType(decoder.SequenceHeader);
+ ObuColorConfig colorConfig = sequenceHeader.ColorConfig;
+ Assert.Equal(sequenceProfile, sequenceHeader.SequenceProfile);
+ Assert.Equal(bitDepth, colorConfig.BitDepth);
+ Assert.Equal(colorFormat, colorConfig.GetColorFormat());
+ Assert.Equal(colorFormat == Av1ColorFormat.Yuv400, colorConfig.IsMonochrome);
+ Assert.True(colorConfig.IsColorDescriptionPresent);
+ Assert.Equal(ObuColorPrimaries.Bt709, colorConfig.ColorPrimaries);
+ Assert.Equal(ObuTransferCharacteristics.Srgb, colorConfig.TransferCharacteristics);
+ Assert.Equal(ObuMatrixCoefficients.Bt601, colorConfig.MatrixCoefficients);
+ Assert.True(colorConfig.ColorRange);
+ AssertNativePlanesEqual(decoder, frameBuffer, nativeReference);
+ }
+
+ ///
+ /// Validates the exact public presentation and metadata of one independently encoded AVIF profile fixture.
+ ///
+ /// The complete AVIF container.
+ /// The eight-bit RGBA output produced by the pinned scalar libavif decoder.
+ /// The expected public HEIF sample precision.
+ private static void ValidateProfilePresentedFixture(string imagePath, string referencePath, HeifBitDepth metadataBitDepth)
+ {
+ DecoderOptions options = new() { MaxFrames = 1 };
+ byte[] imageBytes = TestFile.Create(imagePath).Bytes;
+ byte[] referenceBytes = TestFile.Create(referencePath).Bytes;
+ using Image image = Image.Load(options, imageBytes);
+ using Image reference = Image.Load(referenceBytes);
+
+ Assert.Equal(ProfileFixtureWidth, image.Width);
+ Assert.Equal(ProfileFixtureHeight, image.Height);
+ Assert.Single(image.Frames);
+
+ HeifMetadata metadata = image.Metadata.GetHeifMetadata();
+ Assert.Equal(HeifCompressionMethod.Av1, metadata.CompressionMethod);
+ Assert.Equal(metadataBitDepth, metadata.BitDepth);
+
+ CicpProfile colorProfile = Assert.IsType(image.Metadata.CicpProfile);
+ Assert.Equal(CicpColorPrimaries.ItuRBt709_6, colorProfile.ColorPrimaries);
+ Assert.Equal(CicpTransferCharacteristics.Iec61966_2_1, colorProfile.TransferCharacteristics);
+ Assert.Equal(CicpMatrixCoefficients.ItuRBt601_7_525, colorProfile.MatrixCoefficients);
+ Assert.True(colorProfile.FullRange);
+ ImageComparer.Exact.VerifySimilarity(reference, image);
+ }
+
+ ///
+ /// Gets the Y4M chroma tags that encode one AV1 bit-depth and sampling-layout combination.
+ ///
+ /// The encoded AV1 sample precision.
+ /// The encoded AV1 chroma-sampling layout.
+ /// The Y4M C tag and extended XYSCSS tag.
+ private static (string ChromaTag, string ExtendedChromaTag) GetY4mColorSpace(Av1BitDepth bitDepth, Av1ColorFormat colorFormat)
+ {
+ // Y4M uses a legacy 420jpeg name at eight bits, lowercase p in high-depth C tags, and uppercase P in the
+ // corresponding XYSCSS tags. Keeping the exact spellings detects a reference generated with different layout.
+ return (bitDepth, colorFormat) switch
+ {
+ (Av1BitDepth.EightBit, Av1ColorFormat.Yuv400) => ("mono", "400"),
+ (Av1BitDepth.EightBit, Av1ColorFormat.Yuv420) => ("420jpeg", "420JPEG"),
+ (Av1BitDepth.EightBit, Av1ColorFormat.Yuv422) => ("422", "422"),
+ (Av1BitDepth.EightBit, Av1ColorFormat.Yuv444) => ("444", "444"),
+ (Av1BitDepth.TenBit, Av1ColorFormat.Yuv400) => ("mono10", "400"),
+ (Av1BitDepth.TenBit, Av1ColorFormat.Yuv420) => ("420p10", "420P10"),
+ (Av1BitDepth.TenBit, Av1ColorFormat.Yuv422) => ("422p10", "422P10"),
+ (Av1BitDepth.TenBit, Av1ColorFormat.Yuv444) => ("444p10", "444P10"),
+ (Av1BitDepth.TwelveBit, Av1ColorFormat.Yuv400) => ("mono12", "400"),
+ (Av1BitDepth.TwelveBit, Av1ColorFormat.Yuv420) => ("420p12", "420P12"),
+ (Av1BitDepth.TwelveBit, Av1ColorFormat.Yuv422) => ("422p12", "422P12"),
+ _ => ("444p12", "444P12")
+ };
+ }
+
///
/// Validates every active-CDEF fixture under the hardware configuration selected by .
///
@@ -439,7 +764,7 @@ public class Av1ReconstructionConformanceTests
Assert.NotNull(decoder.FrameHeader);
Assert.True(decoder.FrameHeader.AllowIntraBlockCopy);
Assert.NotEqual(0, GetIntraBlockCopyBlockCount(decoder));
- AssertNativePlanesEqual(frameBuffer, nativeReference);
+ AssertNativePlanesEqual(decoder, frameBuffer, nativeReference);
}
///
@@ -912,7 +1237,7 @@ public class Av1ReconstructionConformanceTests
Assert.Equal(RequiredPaletteCoverage, GetPaletteCoverage(decoder));
}
- AssertNativePlanesEqual(frameBuffer, reference);
+ AssertNativePlanesEqual(decoder, frameBuffer, reference);
return restorationCoverage;
}
@@ -985,7 +1310,7 @@ public class Av1ReconstructionConformanceTests
Assert.True(hasCodedResidual);
- AssertNativePlanesEqual(frameBuffer, nativeReference);
+ AssertNativePlanesEqual(decoder, frameBuffer, nativeReference);
}
///
@@ -1398,9 +1723,10 @@ public class Av1ReconstructionConformanceTests
///
/// Compares every visible native component sample with the independent planar reference.
///
+ /// The decoder state used to identify the coded block containing a mismatch.
/// The reconstructed AV1 component planes.
/// The planar Y, U, and V samples produced by the pinned libaom decoder.
- private static void AssertNativePlanesEqual(Av1FrameBuffer frameBuffer, ReadOnlySpan reference)
+ private static void AssertNativePlanesEqual(Av1Decoder decoder, Av1FrameBuffer frameBuffer, ReadOnlySpan reference)
{
(int chromaSubsamplingX, int chromaSubsamplingY) = frameBuffer.ColorFormat switch
{
@@ -1413,6 +1739,13 @@ public class Av1ReconstructionConformanceTests
ReadOnlySpan planes = frameBuffer.ColorFormat == Av1ColorFormat.Yuv400
? [Av1Plane.Y]
: [Av1Plane.Y, Av1Plane.U, Av1Plane.V];
+ int mismatchCount = 0;
+ Av1Plane largestMismatchPlane = default;
+ int largestMismatchX = 0;
+ int largestMismatchY = 0;
+ ushort largestExpected = 0;
+ ushort largestActual = 0;
+ StringBuilder mismatchDescription = null;
foreach (Av1Plane plane in planes)
{
@@ -1430,7 +1763,25 @@ public class Av1ReconstructionConformanceTests
ReadOnlySpan expectedRow = reference.Slice(referenceOffset, planeWidth);
for (int x = 0; x < planeWidth; x++)
{
- AssertSampleEqual(plane, x, y, expectedRow[x], actualRow[x]);
+ if (expectedRow[x] != actualRow[x])
+ {
+ if (mismatchCount < 16)
+ {
+ mismatchDescription ??= new StringBuilder();
+ mismatchDescription.Append($" {plane}({x},{y})={expectedRow[x]}/{actualRow[x]}");
+ }
+
+ if (mismatchCount == 0 || Math.Abs(expectedRow[x] - actualRow[x]) > Math.Abs(largestExpected - largestActual))
+ {
+ largestMismatchPlane = plane;
+ largestMismatchX = x;
+ largestMismatchY = y;
+ largestExpected = expectedRow[x];
+ largestActual = actualRow[x];
+ }
+
+ mismatchCount++;
+ }
}
referenceOffset += planeWidth;
@@ -1445,7 +1796,26 @@ public class Av1ReconstructionConformanceTests
for (int x = 0; x < planeWidth; x++)
{
ushort expected = BinaryPrimitives.ReadUInt16LittleEndian(reference.Slice(referenceOffset, sizeof(ushort)));
- AssertSampleEqual(plane, x, y, expected, actualRow[x]);
+ if (expected != actualRow[x])
+ {
+ if (mismatchCount < 16)
+ {
+ mismatchDescription ??= new StringBuilder();
+ mismatchDescription.Append($" {plane}({x},{y})={expected}/{actualRow[x]}");
+ }
+
+ if (mismatchCount == 0 || Math.Abs(expected - actualRow[x]) > Math.Abs(largestExpected - largestActual))
+ {
+ largestMismatchPlane = plane;
+ largestMismatchX = x;
+ largestMismatchY = y;
+ largestExpected = expected;
+ largestActual = actualRow[x];
+ }
+
+ mismatchCount++;
+ }
+
referenceOffset += sizeof(ushort);
}
}
@@ -1453,6 +1823,15 @@ public class Av1ReconstructionConformanceTests
}
Assert.Equal(reference.Length, referenceOffset);
+ AssertSampleEqual(
+ decoder,
+ largestMismatchPlane,
+ largestMismatchX,
+ largestMismatchY,
+ largestExpected,
+ largestActual,
+ mismatchCount,
+ mismatchDescription?.ToString() ?? string.Empty);
}
///
@@ -1467,16 +1846,126 @@ public class Av1ReconstructionConformanceTests
///
/// Reports the exact component coordinate when independently decoded samples differ.
///
+ /// The decoder state used to identify the coded block containing the sample.
/// The compared component plane.
/// The sample X coordinate.
/// The sample Y coordinate.
/// The reference sample.
/// The reconstructed sample.
- private static void AssertSampleEqual(Av1Plane plane, int x, int y, ushort expected, ushort actual)
+ /// The total number of unequal native samples.
+ /// The first unequal samples in plane traversal order.
+ private static void AssertSampleEqual(
+ Av1Decoder decoder,
+ Av1Plane plane,
+ int x,
+ int y,
+ ushort expected,
+ ushort actual,
+ int mismatchCount,
+ string mismatchDescription)
{
if (expected != actual)
{
- Assert.Fail($"Plane {plane} differs at ({x}, {y}): expected {expected}, actual {actual}.");
+ Av1FrameInfo frameInfo = Assert.IsType(decoder.FrameInfo);
+ int modeInfoColumn = x >> Av1Constants.ModeInfoSizeLog2;
+ int modeInfoRow = y >> Av1Constants.ModeInfoSizeLog2;
+ Av1BlockModeInfo modeInfo = frameInfo.GetModeInfoAt(new Point(modeInfoColumn, modeInfoRow));
+ int blockColumn = modeInfoColumn;
+ while (blockColumn > 0 && ReferenceEquals(frameInfo.GetModeInfoAt(new Point(blockColumn - 1, modeInfoRow)), modeInfo))
+ {
+ blockColumn--;
+ }
+
+ int blockRow = modeInfoRow;
+ while (blockRow > 0 && ReferenceEquals(frameInfo.GetModeInfoAt(new Point(modeInfoColumn, blockRow - 1)), modeInfo))
+ {
+ blockRow--;
+ }
+
+ int superblockSize = frameInfo.SuperblockModeInfoSize;
+ Av1SuperblockInfo superblock = frameInfo.GetSuperblock(new Point(blockColumn / superblockSize, blockRow / superblockSize));
+ Span transforms = superblock.GetTransformInfoY().Slice(
+ modeInfo.GetFirstTransformLocation(Av1Plane.Y),
+ modeInfo.GetTransformUnitCount(Av1Plane.Y));
+
+ Av1TransformInfo containingTransform = transforms[0];
+ int containingTransformIndex = 0;
+ int transformColumn = modeInfoColumn - blockColumn;
+ int transformRow = modeInfoRow - blockRow;
+ for (int transformIndex = 0; transformIndex < transforms.Length; transformIndex++)
+ {
+ Av1TransformInfo transform = transforms[transformIndex];
+ if (transformColumn >= transform.OffsetX && transformColumn < transform.OffsetX + transform.Size.Get4x4WideCount()
+ && transformRow >= transform.OffsetY && transformRow < transform.OffsetY + transform.Size.Get4x4HighCount())
+ {
+ containingTransform = transform;
+ containingTransformIndex = transformIndex;
+ break;
+ }
+ }
+
+ int superblockTransformIndex = modeInfo.GetFirstTransformLocation(Av1Plane.Y) + containingTransformIndex;
+ Span superblockTransforms = superblock.GetTransformInfoY();
+ Span superblockCoefficients = superblock.CoefficientsY;
+ int coefficientOffset = 0;
+ for (int transformIndex = 0; transformIndex < superblockTransformIndex; transformIndex++)
+ {
+ if (superblockTransforms[transformIndex].CodeBlockFlag)
+ {
+ coefficientOffset += superblockCoefficients[coefficientOffset] + 1;
+ }
+ }
+
+ StringBuilder coefficientDescription = new();
+ if (containingTransform.CodeBlockFlag)
+ {
+ int coefficientCount = superblockCoefficients[coefficientOffset];
+ coefficientDescription.Append($", quantized-coefficients={coefficientCount}:[");
+ for (int coefficientIndex = 0; coefficientIndex < coefficientCount; coefficientIndex++)
+ {
+ if (coefficientIndex != 0)
+ {
+ coefficientDescription.Append(',');
+ }
+
+ coefficientDescription.Append(superblockCoefficients[coefficientOffset + coefficientIndex + 1]);
+ }
+
+ coefficientDescription.Append(']');
+ }
+
+ ObuFrameHeader frameHeader = Assert.IsType(decoder.FrameHeader);
+ int cdefUnitColumn = (modeInfoColumn % superblockSize) / CdefUnitModeInfoSize;
+ int cdefUnitRow = (modeInfoRow % superblockSize) / CdefUnitModeInfoSize;
+ int cdefStrengthIndex = frameInfo.GetCdefStrength(superblock.Position)[cdefUnitColumn + (cdefUnitRow << 1)];
+ int cdefStrength = cdefStrengthIndex < 0 ? -1 : frameHeader.CdefParameters.YStrength[cdefStrengthIndex];
+ int nextModeInfoRow = Math.Min(modeInfoRow + 1, frameHeader.ModeInfoRowCount - 1);
+ Av1BlockModeInfo nextRowModeInfo = frameInfo.GetModeInfoAt(new Point(modeInfoColumn, nextModeInfoRow));
+ Av1BlockModeInfo aboveModeInfo = frameInfo.GetModeInfoAt(new Point(modeInfoColumn, Math.Max(blockRow - 1, 0)));
+ Av1BlockModeInfo leftModeInfo = frameInfo.GetModeInfoAt(new Point(Math.Max(blockColumn - 1, 0), modeInfoRow));
+
+ // Exact conformance failures need the owning syntax state. A coordinate alone does not distinguish
+ // prediction, residual reconstruction, and in-loop filtering failures inside a large coded frame.
+ Assert.Fail(
+ $"Plane {plane} differs at ({x}, {y}): expected {expected}, actual {actual}. "
+ + $"Total unequal samples={mismatchCount}:{mismatchDescription}. "
+ + $"Block={modeInfo.BlockSize}, mode={modeInfo.YMode}, partition={modeInfo.PartitionType}, skip={modeInfo.Skip}, "
+ + $"filter-intra={modeInfo.UseFilterIntra}/{modeInfo.FilterIntraMode}, angle-delta={modeInfo.GetAngleDelta(plane)}, "
+ + $"palette-size={modeInfo.GetPaletteSize(plane)}, transforms={modeInfo.GetTransformUnitCount(plane)}, "
+ + $"transform={containingTransform.Size}/{containingTransform.Type}/coded={containingTransform.CodeBlockFlag} "
+ + $"at ({containingTransform.OffsetX}, {containingTransform.OffsetY}), block-origin=({blockColumn}, {blockRow}). "
+ + $"Loop-filter={frameHeader.LoopFilterParameters.FilterLevel[0]}/{frameHeader.LoopFilterParameters.FilterLevel[1]}, "
+ + $"sharpness={frameHeader.LoopFilterParameters.SharpnessLevel}, delta-q={frameHeader.DeltaQParameters.IsPresent}, "
+ + $"superblock-q={superblock.SuperblockQuantizerIndex}{coefficientDescription}, "
+ + $"delta-lf={frameHeader.DeltaLoopFilterParameters.IsPresent}/{frameHeader.DeltaLoopFilterParameters.IsMulti}, "
+ + $"CDEF={cdefStrengthIndex}/{cdefStrength}, restoration={frameHeader.LoopRestorationParameters.Items[0].Type}, "
+ + $"film-grain={frameHeader.FilmGrainParameters.ApplyGrain}, "
+ + $"tiles={frameHeader.TilesInfo.TileColumnCount}x{frameHeader.TilesInfo.TileRowCount}, "
+ + $"first-tile-end=({frameHeader.TilesInfo.TileColumnStartModeInfo[1]}, {frameHeader.TilesInfo.TileRowStartModeInfo[1]}). "
+ + $"Neighbors: above={aboveModeInfo.BlockSize}/{aboveModeInfo.YMode}/skip={aboveModeInfo.Skip}, "
+ + $"left={leftModeInfo.BlockSize}/{leftModeInfo.YMode}/skip={leftModeInfo.Skip}, "
+ + $"next-row={nextRowModeInfo.BlockSize}/{nextRowModeInfo.YMode}/skip={nextRowModeInfo.Skip}/"
+ + $"angle-delta={nextRowModeInfo.GetAngleDelta(plane)}/transforms={nextRowModeInfo.GetTransformUnitCount(plane)}.");
}
}
}
diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs
index 1895b7ef15..45cdb3bc43 100644
--- a/tests/ImageSharp.Tests/TestImages.cs
+++ b/tests/ImageSharp.Tests/TestImages.cs
@@ -1365,6 +1365,42 @@ public static class TestImages
public const string Av1Cdef12BitReference = "Heif/Av1/Conformance/libaom-cdef-cosmos-12b-libaom.yuv";
public const string Av1Cdef12BitAvif = "Heif/Av1/Conformance/libavif-cdef-cosmos-12b.avif";
public const string Av1Cdef12BitPresentationReference = "Heif/Av1/Conformance/libavif-cdef-cosmos-12b.png";
+ public const string Av1Profile8BitMonochromeAvif = "Heif/Av1/Conformance/libavif-profile-8b-400.avif";
+ public const string Av1Profile8BitMonochromeReference = "Heif/Av1/Conformance/libavif-profile-8b-400-libaom-y4m.yuv";
+ public const string Av1Profile8BitMonochromePresentationReference = "Heif/Av1/Conformance/libavif-profile-8b-400.png";
+ public const string Av1Profile8Bit420Avif = "Heif/Av1/Conformance/libavif-profile-8b-420.avif";
+ public const string Av1Profile8Bit420Reference = "Heif/Av1/Conformance/libavif-profile-8b-420-libaom-y4m.yuv";
+ public const string Av1Profile8Bit420PresentationReference = "Heif/Av1/Conformance/libavif-profile-8b-420.png";
+ public const string Av1Profile8Bit422Avif = "Heif/Av1/Conformance/libavif-profile-8b-422.avif";
+ public const string Av1Profile8Bit422Reference = "Heif/Av1/Conformance/libavif-profile-8b-422-libaom-y4m.yuv";
+ public const string Av1Profile8Bit422PresentationReference = "Heif/Av1/Conformance/libavif-profile-8b-422.png";
+ public const string Av1Profile8Bit444Avif = "Heif/Av1/Conformance/libavif-profile-8b-444.avif";
+ public const string Av1Profile8Bit444Reference = "Heif/Av1/Conformance/libavif-profile-8b-444-libaom-y4m.yuv";
+ public const string Av1Profile8Bit444PresentationReference = "Heif/Av1/Conformance/libavif-profile-8b-444.png";
+ public const string Av1Profile10BitMonochromeAvif = "Heif/Av1/Conformance/libavif-profile-10b-400.avif";
+ public const string Av1Profile10BitMonochromeReference = "Heif/Av1/Conformance/libavif-profile-10b-400-libaom-y4m.yuv";
+ public const string Av1Profile10BitMonochromePresentationReference = "Heif/Av1/Conformance/libavif-profile-10b-400.png";
+ public const string Av1Profile10Bit420Avif = "Heif/Av1/Conformance/libavif-profile-10b-420.avif";
+ public const string Av1Profile10Bit420Reference = "Heif/Av1/Conformance/libavif-profile-10b-420-libaom-y4m.yuv";
+ public const string Av1Profile10Bit420PresentationReference = "Heif/Av1/Conformance/libavif-profile-10b-420.png";
+ public const string Av1Profile10Bit422Avif = "Heif/Av1/Conformance/libavif-profile-10b-422.avif";
+ public const string Av1Profile10Bit422Reference = "Heif/Av1/Conformance/libavif-profile-10b-422-libaom-y4m.yuv";
+ public const string Av1Profile10Bit422PresentationReference = "Heif/Av1/Conformance/libavif-profile-10b-422.png";
+ public const string Av1Profile10Bit444Avif = "Heif/Av1/Conformance/libavif-profile-10b-444.avif";
+ public const string Av1Profile10Bit444Reference = "Heif/Av1/Conformance/libavif-profile-10b-444-libaom-y4m.yuv";
+ public const string Av1Profile10Bit444PresentationReference = "Heif/Av1/Conformance/libavif-profile-10b-444.png";
+ public const string Av1Profile12BitMonochromeAvif = "Heif/Av1/Conformance/libavif-profile-12b-400.avif";
+ public const string Av1Profile12BitMonochromeReference = "Heif/Av1/Conformance/libavif-profile-12b-400-libaom-y4m.yuv";
+ public const string Av1Profile12BitMonochromePresentationReference = "Heif/Av1/Conformance/libavif-profile-12b-400.png";
+ public const string Av1Profile12Bit420Avif = "Heif/Av1/Conformance/libavif-profile-12b-420.avif";
+ public const string Av1Profile12Bit420Reference = "Heif/Av1/Conformance/libavif-profile-12b-420-libaom-y4m.yuv";
+ public const string Av1Profile12Bit420PresentationReference = "Heif/Av1/Conformance/libavif-profile-12b-420.png";
+ public const string Av1Profile12Bit422Avif = "Heif/Av1/Conformance/libavif-profile-12b-422.avif";
+ public const string Av1Profile12Bit422Reference = "Heif/Av1/Conformance/libavif-profile-12b-422-libaom-y4m.yuv";
+ public const string Av1Profile12Bit422PresentationReference = "Heif/Av1/Conformance/libavif-profile-12b-422.png";
+ public const string Av1Profile12Bit444Avif = "Heif/Av1/Conformance/libavif-profile-12b-444.avif";
+ public const string Av1Profile12Bit444Reference = "Heif/Av1/Conformance/libavif-profile-12b-444-libaom-y4m.yuv";
+ public const string Av1Profile12Bit444PresentationReference = "Heif/Av1/Conformance/libavif-profile-12b-444.png";
public const string Av1Palette8BitPayload = "Heif/Av1/Conformance/libaom-palette-draw-points-8b-444.bit";
public const string Av1Palette8BitReference = "Heif/Av1/Conformance/libaom-palette-draw-points-8b-444-libaom.yuv";
public const string Av1Palette8BitAvif = "Heif/Av1/Conformance/libavif-palette-draw-points-8b.avif";
diff --git a/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-10b-400-libaom-y4m.yuv b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-10b-400-libaom-y4m.yuv
new file mode 100644
index 0000000000..d1c20f8290
--- /dev/null
+++ b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-10b-400-libaom-y4m.yuv
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1c71bca8da6ee62c42e1ebdcfcba89e2829f8fd9569d3bf8107c8321ed085b06
+size 262220
diff --git a/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-10b-400.avif b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-10b-400.avif
new file mode 100644
index 0000000000..428648e41c
--- /dev/null
+++ b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-10b-400.avif
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:78d25037c2c41f387e74a88ed844fe9888f64e88f8155af31ccf994f5566c23d
+size 1631
diff --git a/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-10b-400.png b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-10b-400.png
new file mode 100644
index 0000000000..25a8163e49
--- /dev/null
+++ b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-10b-400.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5ed0fd0f21b2c5620b7b6289cf3ebdd6001e8498ce7b0daec17d5657051f9cea
+size 5224
diff --git a/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-10b-420-libaom-y4m.yuv b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-10b-420-libaom-y4m.yuv
new file mode 100644
index 0000000000..aee84e102f
--- /dev/null
+++ b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-10b-420-libaom-y4m.yuv
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:39b2dca5c922a09a085b798fdf9cad0ce218418eb0fd33a08388ff05e6dd3114
+size 393295
diff --git a/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-10b-420.avif b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-10b-420.avif
new file mode 100644
index 0000000000..4aadeb766b
--- /dev/null
+++ b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-10b-420.avif
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5df732dc3c72684f35dfa9c69781133e6383ae8406e66e57d8cc3fa5e0dd852d
+size 1682
diff --git a/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-10b-420.png b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-10b-420.png
new file mode 100644
index 0000000000..30a54877bb
--- /dev/null
+++ b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-10b-420.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:649c9fef1d87858cc6b49b7fcf51ad691ea22f0a513cd69ab61a3bf1943dd1bf
+size 9635
diff --git a/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-10b-422-libaom-y4m.yuv b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-10b-422-libaom-y4m.yuv
new file mode 100644
index 0000000000..51aa38edc4
--- /dev/null
+++ b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-10b-422-libaom-y4m.yuv
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:79c6e4560af81fe427bc38f1fa64ee43622d1ec02a7876a533ccb6be72955a9e
+size 524367
diff --git a/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-10b-422.avif b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-10b-422.avif
new file mode 100644
index 0000000000..1dd877072f
--- /dev/null
+++ b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-10b-422.avif
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3420ea3f3fb2430ffa502a85190e3aab9aafdeb24880748d023b3a5091943c6d
+size 1685
diff --git a/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-10b-422.png b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-10b-422.png
new file mode 100644
index 0000000000..90ecec2d0d
--- /dev/null
+++ b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-10b-422.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4c1a70c907c1040fcb7b2a6618fb0f9c84a4ddfdf441238138f9f3349fb79696
+size 9573
diff --git a/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-10b-444-libaom-y4m.yuv b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-10b-444-libaom-y4m.yuv
new file mode 100644
index 0000000000..3cd001634e
--- /dev/null
+++ b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-10b-444-libaom-y4m.yuv
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ad9f1b3318f9795f325a2f8d4e6dd5e5ea2c4ea51713f6aa9dab302a970b25bb
+size 786511
diff --git a/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-10b-444.avif b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-10b-444.avif
new file mode 100644
index 0000000000..e7f3f99313
--- /dev/null
+++ b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-10b-444.avif
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:354e339eaf0e6e2889659c2a889d65b9bdf6022543fb9f9522d11301251e0075
+size 1682
diff --git a/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-10b-444.png b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-10b-444.png
new file mode 100644
index 0000000000..30a54877bb
--- /dev/null
+++ b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-10b-444.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:649c9fef1d87858cc6b49b7fcf51ad691ea22f0a513cd69ab61a3bf1943dd1bf
+size 9635
diff --git a/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-12b-400-libaom-y4m.yuv b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-12b-400-libaom-y4m.yuv
new file mode 100644
index 0000000000..70b3404a26
--- /dev/null
+++ b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-12b-400-libaom-y4m.yuv
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a020ff72013124975caae5823eaaa1abd3ed66e19cf6ccb31ff96fcd0b63a084
+size 262220
diff --git a/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-12b-400.avif b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-12b-400.avif
new file mode 100644
index 0000000000..d7d93a454c
--- /dev/null
+++ b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-12b-400.avif
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:72af196f86395af9b5939598be97c45cd065ad4b62c276a0486bf76a6568020b
+size 1643
diff --git a/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-12b-400.png b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-12b-400.png
new file mode 100644
index 0000000000..2c50dec739
--- /dev/null
+++ b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-12b-400.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:53c0c2e538c85323fceee54d30b7ba29d2af5fd9770a617faceadb9f2a502b2c
+size 5459
diff --git a/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-12b-420-libaom-y4m.yuv b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-12b-420-libaom-y4m.yuv
new file mode 100644
index 0000000000..c40c8ac495
--- /dev/null
+++ b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-12b-420-libaom-y4m.yuv
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0b3b67cc8fff91e5302f02f4718967b9a2871b321109defdafb85dd46b558876
+size 393295
diff --git a/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-12b-420.avif b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-12b-420.avif
new file mode 100644
index 0000000000..f0a41f80c0
--- /dev/null
+++ b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-12b-420.avif
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:063d1980df27ae7ff996c437d962ca51d4615d6f745eca6cc55a5c57a05c761d
+size 1688
diff --git a/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-12b-420.png b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-12b-420.png
new file mode 100644
index 0000000000..b3b82c26c1
--- /dev/null
+++ b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-12b-420.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:589a61003f368ce59defd0e2334b5d50bfa24119e488bea19c56542bcfc4e930
+size 10122
diff --git a/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-12b-422-libaom-y4m.yuv b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-12b-422-libaom-y4m.yuv
new file mode 100644
index 0000000000..79d63b266f
--- /dev/null
+++ b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-12b-422-libaom-y4m.yuv
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:efbb14ce47121971afca860b811b34da9414fb961a73e2f16fd403d835cf89d1
+size 524367
diff --git a/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-12b-422.avif b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-12b-422.avif
new file mode 100644
index 0000000000..f87357b03a
--- /dev/null
+++ b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-12b-422.avif
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:80d606728693f5f890fc1ffb7a2bcff1b2128b783143445fdbed89da30b1353f
+size 1686
diff --git a/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-12b-422.png b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-12b-422.png
new file mode 100644
index 0000000000..d06bbba677
--- /dev/null
+++ b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-12b-422.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4c86a0e37069641c3e077428f2a0632081ef3b8fe997e10721347377ad98d14f
+size 10142
diff --git a/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-12b-444-libaom-y4m.yuv b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-12b-444-libaom-y4m.yuv
new file mode 100644
index 0000000000..b802feb305
--- /dev/null
+++ b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-12b-444-libaom-y4m.yuv
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:43907add76f881850cfdc4ed23655dc71443c8d40ef16093b51745650dc66307
+size 786511
diff --git a/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-12b-444.avif b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-12b-444.avif
new file mode 100644
index 0000000000..ae98187a54
--- /dev/null
+++ b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-12b-444.avif
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f35443d16382c2d184adc3e518230ce4df497a429d101c92b96bb592b50c1a36
+size 1683
diff --git a/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-12b-444.png b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-12b-444.png
new file mode 100644
index 0000000000..5658d5ae11
--- /dev/null
+++ b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-12b-444.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:13b3e95816e3b98016fc7b8c11e161a3204e0dcf9a38008254d1468ec7e73769
+size 9942
diff --git a/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-8b-400-libaom-y4m.yuv b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-8b-400-libaom-y4m.yuv
new file mode 100644
index 0000000000..33c5a69c78
--- /dev/null
+++ b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-8b-400-libaom-y4m.yuv
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:da90f960e2297224b621ff866353d89ddb0dd9ed0ffcb201d4b5e49c250c5c07
+size 131146
diff --git a/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-8b-400.avif b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-8b-400.avif
new file mode 100644
index 0000000000..6452bda077
--- /dev/null
+++ b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-8b-400.avif
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1b33f3d2f20a4d1d828ee3ed44e94e0ac5a13caa3971e0279ba740581437c194
+size 1665
diff --git a/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-8b-400.png b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-8b-400.png
new file mode 100644
index 0000000000..e55b20c57f
--- /dev/null
+++ b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-8b-400.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:260a860d1f01e56c0154a31a6f4802fd27b5f507f724d55084f5278b388003c2
+size 5294
diff --git a/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-8b-420-libaom-y4m.yuv b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-8b-420-libaom-y4m.yuv
new file mode 100644
index 0000000000..1257f0c682
--- /dev/null
+++ b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-8b-420-libaom-y4m.yuv
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:98342115d81519b685c3c3d4077e74896b5c3ebc6eb44ccfe11dd20f43fc06df
+size 196689
diff --git a/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-8b-420.avif b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-8b-420.avif
new file mode 100644
index 0000000000..d6934fac62
--- /dev/null
+++ b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-8b-420.avif
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:16353df2ed0b1e5f3b5111e6f514e8ba56b81be8b20aae5b4d07ccf71f203538
+size 1718
diff --git a/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-8b-420.png b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-8b-420.png
new file mode 100644
index 0000000000..fae45e031a
--- /dev/null
+++ b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-8b-420.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2b39c31c012ad7e194870fcdd074a83f225dc0dfc40731f0c72fa8847821e0fd
+size 9631
diff --git a/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-8b-422-libaom-y4m.yuv b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-8b-422-libaom-y4m.yuv
new file mode 100644
index 0000000000..c398ab5335
--- /dev/null
+++ b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-8b-422-libaom-y4m.yuv
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:dd25de31e7c3175b8854cdf63ce233d2657b07ba038158286763c19ab4ca60cc
+size 262217
diff --git a/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-8b-422.avif b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-8b-422.avif
new file mode 100644
index 0000000000..40f000b051
--- /dev/null
+++ b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-8b-422.avif
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b1f8d6ae7131d2596f43ac9a67c0afce6f4cb4eacd323383a22073bcb8afc05a
+size 1736
diff --git a/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-8b-422.png b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-8b-422.png
new file mode 100644
index 0000000000..abdbafc004
--- /dev/null
+++ b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-8b-422.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:253291bc0bcebde6cc0ef6729c52ec025cba4a7ed046a3231297da7c63df3f4a
+size 9748
diff --git a/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-8b-444-libaom-y4m.yuv b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-8b-444-libaom-y4m.yuv
new file mode 100644
index 0000000000..418816b6d0
--- /dev/null
+++ b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-8b-444-libaom-y4m.yuv
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:dae8878737e0afcc7084a252f5963bc8915e0febbf7a92284bad14bb8723e826
+size 393289
diff --git a/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-8b-444.avif b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-8b-444.avif
new file mode 100644
index 0000000000..573da92774
--- /dev/null
+++ b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-8b-444.avif
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ef6c09e4c7b3ecfcc7fef52abe3eab6afe422ec6bf9fd0fd3af00db445384a6f
+size 1722
diff --git a/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-8b-444.png b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-8b-444.png
new file mode 100644
index 0000000000..3e0b2f65d6
--- /dev/null
+++ b/tests/Images/Input/Heif/Av1/Conformance/libavif-profile-8b-444.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:96423c89e68f6d4e6a349e0693b1c0b106055654681d51c822ab43fd12b86223
+size 9703