diff --git a/HEIF_IMPLEMENTATION_PLAN.md b/HEIF_IMPLEMENTATION_PLAN.md index 4d665c3e9c..6595b85e60 100644 --- a/HEIF_IMPLEMENTATION_PLAN.md +++ b/HEIF_IMPLEMENTATION_PLAN.md @@ -846,6 +846,7 @@ Encoder verification contract: - [x] The expanded checkpoint exposed a pre-existing transform-block test that asserted uninitialized pooled padding was zero. The test now initializes the complete physical luma plane with a sentinel and proves the block operation leaves both adjacent padding samples unchanged. The exact net11 Release rebuild remains at 1,005 baseline warnings and zero errors, the focused allocator-order set passes 30 of 30 cases, and the complete HEIF/AV1 namespace passes 8,859 of 8,859 direct VSTest cases with zero failures or skips. - [x] Combined-frame OBU output now counts the byte-aligned frame and tile-group headers, non-final tile-size fields, and owned tile payloads before emitting the OBU size. It retains only the small allocator-owned header scratch and writes each entropy-coded tile span directly from its detached owner, removing the second file-sized allocator rent and complete-payload copy. A 64 KiB regression proves exactly one sub-payload-sized byte rent with a balanced return and verifies the exact streamed tile tail; the existing two-tile round trip proves size-prefix and ordering parity. The focused writer and production-frame set passes 32 of 32 direct net11 VSTest cases, current-main `aomdec` accepts all 29 generated native-format payloads, and the complete HEIF/AV1 namespace passes 8,860 of 8,860 cases with zero failures or skips. - [x] Finalized fixed-block decisions now set the block-level transform-skip flag only when every retained luma and coded chroma transform has zero EOB, matching current libaom's conjunction of per-plane skip state. The previous always-false flag produced legal but redundant non-skip and zero-coefficient syntax. Monochrome and 4:2:0 regressions prove both branches from actual coefficient state; the focused decision and production-frame set passes 32 of 32 direct net11 VSTest cases. Current-main `aomdec` accepts all 29 regenerated payloads, the recorded decoded-frame MD5s are unchanged, and affected 16x16 constant 8-bit and 10-bit payloads are one byte smaller. The complete HEIF/AV1 namespace passes 8,862 of 8,862 cases with zero failures or skips. +- [x] Operation-wide allocation tracking now exercises a real 64x64 12-bit 4:4:4 frame through packed-pixel conversion, both native frame owners, picture and coefficient state, reusable block workspaces, entropy coding, OBU framing, and a non-seekable destination. It proves exactly one 60 KiB tile-output reservation from current libaom's all-intra 2.5x rule and balanced exactly-once returns for every tracked allocation before the operation completes. The focused ownership case passes 1 of 1 and the complete HEIF/AV1 namespace passes 8,863 of 8,863 direct net11 VSTest cases with zero failures or skips. ### 7. Write complete AVIF output diff --git a/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1EncoderFrameTests.cs b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1EncoderFrameTests.cs index 7261b8cc19..309a32664d 100644 --- a/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1EncoderFrameTests.cs +++ b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1EncoderFrameTests.cs @@ -288,6 +288,53 @@ public class Av1EncoderFrameTests Assert.Equal(allocation.AllocationId, returned.AllocationId); } + [Fact] + public void EncodeReturnsEveryOperationAllocationAndUsesOneLibaomSizedTileReservation() + { + const int Width = 64; + const int Height = 64; + const int ExpectedTileOutputLength = 60 * 1024; + + using Image source = new(Width, Height); + for (int y = 0; y < Height; y++) + { + Span row = source.Frames.RootFrame.PixelBuffer.DangerousGetRowSpan(y); + for (int x = 0; x < Width; x++) + { + row[x] = new Rgba32( + (byte)((x * 3) + y), + (byte)(x + (y * 5)), + (byte)((x * 7) + (y * 11))); + } + } + + TestMemoryAllocator allocator = new(); + allocator.EnableNonThreadSafeLogging(); + Configuration configuration = Configuration.Default.Clone(); + configuration.MemoryAllocator = allocator; + using MemoryStream storage = new(); + using NonSeekableStream destination = new(storage); + + _ = Av1FrameEncoder.Encode( + configuration, + source.Frames.RootFrame, + destination, + CreateColorConfig(Av1BitDepth.TwelveBit, Av1ColorFormat.Yuv444), + qIndex: 37); + + Assert.False(destination.CanSeek); + Assert.NotEqual(0, storage.Length); + TestMemoryAllocator.AllocationRequest tileOutput = Assert.Single( + allocator.AllocationLog, + allocation => allocation.ElementType == typeof(byte) && allocation.Length == ExpectedTileOutputLength); + + Assert.Equal(ExpectedTileOutputLength, tileOutput.Length); + Assert.Equal(allocator.AllocationLog.Count, allocator.ReturnLog.Count); + Assert.Equal( + allocator.AllocationLog.Select(allocation => allocation.AllocationId).Order(), + allocator.ReturnLog.Select(returned => returned.AllocationId).Order()); + } + private static ObuColorConfig CreateColorConfig( Av1BitDepth bitDepth, Av1ColorFormat colorFormat = Av1ColorFormat.Yuv400)