From c7a92ecfea0426b7d2dea1dbb003143765879b71 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 24 Aug 2026 20:50:59 +1000 Subject: [PATCH] Document HEIF frame pipeline --- .../Heif/Av1/Pipeline/Av1FrameDecoder.cs | 79 +++++++++++++++++-- .../Heif/Av1/Pipeline/Av1FrameEncoder.cs | 66 ++++------------ .../Heif/Av1/Pipeline/IAv1FrameDecoder.cs | 10 +-- 3 files changed, 92 insertions(+), 63 deletions(-) diff --git a/src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1FrameDecoder.cs b/src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1FrameDecoder.cs index ef3590ef4..6f9821cf6 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1FrameDecoder.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1FrameDecoder.cs @@ -8,16 +8,53 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.Transform; namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline; +/// +/// Reconstructs the coded blocks of one AV1 still-image frame into planar sample buffers. +/// internal class Av1FrameDecoder : IAv1FrameDecoder { + /// + /// The sequence-level superblock and color configuration. + /// private readonly ObuSequenceHeader sequenceHeader; + + /// + /// The frame-level tile, quantization, and reconstruction configuration. + /// private readonly ObuFrameHeader frameHeader; + + /// + /// The parsed superblock and block-mode information for the frame. + /// private readonly Av1FrameInfo frameInfo; + + /// + /// The destination planar sample buffers for reconstructed pixels. + /// private readonly Av1FrameBuffer frameBuffer; + + /// + /// The coefficient inverse-quantization stage shared across superblocks. + /// private readonly Av1InverseQuantizer inverseQuantizer; + + /// + /// The frame's base per-segment and per-plane dequantization values. + /// private readonly Av1DeQuantizationContext deQuants; + + /// + /// The block reconstruction stage that applies prediction and inverse transforms. + /// private readonly Av1BlockDecoder blockDecoder; + /// + /// Initializes a new instance of the class. + /// + /// The parsed AV1 sequence header. + /// The parsed AV1 frame header. + /// The parsed superblock and block-mode information. + /// The destination planar sample buffers. public Av1FrameDecoder(ObuSequenceHeader sequenceHeader, ObuFrameHeader frameHeader, Av1FrameInfo frameInfo, Av1FrameBuffer frameBuffer) { this.sequenceHeader = sequenceHeader; @@ -29,8 +66,12 @@ internal class Av1FrameDecoder : IAv1FrameDecoder this.blockDecoder = new(this.sequenceHeader, this.frameHeader, this.frameBuffer); } + /// + /// Reconstructs every coded tile of the frame; in-loop post-processing stages remain disabled until implemented. + /// public void DecodeFrame() { + // Tile columns are the outer traversal because each call walks that column's tile rows and their superblocks. for (int column = 0; column < this.frameHeader.TilesInfo.TileColumnCount; column++) { this.DecodeFrameTiles(column); @@ -39,6 +80,9 @@ internal class Av1FrameDecoder : IAv1FrameDecoder bool doLoopFilterFlag = false; bool doLoopRestoration = false; bool doUpscale = false; + + // These flags remain false until the corresponding normative stages have complete scalar implementations + // and independent still-image vectors; silently running partial filters would corrupt reconstructed pixels. if (doLoopFilterFlag) { this.DecodeLoopFilterForFrame(); @@ -61,14 +105,18 @@ internal class Av1FrameDecoder : IAv1FrameDecoder } /// - /// SVT: decode_tile + /// Reconstructs every tile row in one tile column. /// + /// The zero-based tile-column index. + /// SVT-AV1: decode_tile. private void DecodeFrameTiles(int tileColumn) { int tileRowCount = this.frameHeader.TilesInfo.TileRowCount; int tileCount = tileRowCount * this.frameHeader.TilesInfo.TileColumnCount; for (int row = 0; row < tileRowCount; row++) { + // Tile row starts are signaled in 4x4 mode-info units. Convert to pixels and then to superblock rows + // so the frame-level superblock store and the tile-local syntax address the same region. int superblockRowTileStart = this.frameHeader.TilesInfo.TileRowStartModeInfo[row] << Av1Constants.ModeInfoSizeLog2 >> this.sequenceHeader.SuperblockSizeLog2; int superblockRow = row + superblockRowTileStart; @@ -82,8 +130,13 @@ internal class Av1FrameDecoder : IAv1FrameDecoder } /// - /// SVT: decode_tile_row + /// Reconstructs the superblocks in one tile row from left to right. /// + /// The zero-based tile-row index. + /// The zero-based tile-column index. + /// The frame-relative row in 4x4 mode-info units. + /// The frame-relative superblock row. + /// SVT-AV1: decode_tile_row. private void DecodeTileRow(int tileRow, int tileColumn, int modeInfoRow, int superblockRow) { int superblockModeInfoSizeLog2 = this.sequenceHeader.SuperblockSizeLog2 - Av1Constants.ModeInfoSizeLog2; @@ -96,6 +149,7 @@ internal class Av1FrameDecoder : IAv1FrameDecoder for (int modeInfoColumn = tileInfo.TileColumnStartModeInfo[tileColumn]; modeInfoColumn < tileInfo.TileColumnStartModeInfo[tileColumn + 1]; modeInfoColumn += this.sequenceHeader.SuperblockModeInfoSize) { + // Convert the signaled 4x4 mode-info column to the frame-level superblock index used by Av1FrameInfo. int superblockColumn = modeInfoColumn << Av1Constants.ModeInfoSizeLog2 >> this.sequenceHeader.SuperblockSizeLog2; Av1SuperblockInfo superblockInfo = this.frameInfo.GetSuperblock(new Point(superblockColumn, superblockRow)); @@ -106,8 +160,12 @@ internal class Av1FrameDecoder : IAv1FrameDecoder } /// - /// SVT: svt_aom_decode_super_block + /// Reconstructs one superblock after applying its block state and delta-Q context. /// + /// The superblock's top-left position in 4x4 mode-info units. + /// The decoded syntax and block modes for the superblock. + /// The tile that contains the superblock. + /// SVT-AV1: svt_aom_decode_super_block. public void DecodeSuperblock(Point modeInfoPosition, Av1SuperblockInfo superblockInfo, Av1TileInfo tileInfo) { this.blockDecoder.UpdateSuperblock(superblockInfo); @@ -116,8 +174,12 @@ internal class Av1FrameDecoder : IAv1FrameDecoder } /// - /// SVT: decode_partition + /// Reconstructs each decoded block in a superblock partition. /// + /// The superblock's frame-relative origin in 4x4 mode-info units. + /// The superblock whose block modes are traversed. + /// The tile boundary information used by intra prediction. + /// SVT-AV1: decode_partition. private void DecodePartition(Point modeInfoPosition, Av1SuperblockInfo superblockInfo, Av1TileInfo tileInfo) { foreach (Av1BlockModeInfo modeInfo in superblockInfo.GetModeInfos()) @@ -125,18 +187,23 @@ internal class Av1FrameDecoder : IAv1FrameDecoder Point subPosition = modeInfo.PositionInSuperblock; Av1BlockSize subSize = modeInfo.BlockSize; Point globalPosition = new(modeInfoPosition.X, modeInfoPosition.Y); + + // Block positions are stored relative to the superblock; prediction and reconstruction require frame-relative mode-info coordinates. globalPosition.Offset(subPosition); this.blockDecoder.DecodeBlock(modeInfo, globalPosition, subSize, superblockInfo, tileInfo); } } + /// + /// Traverses frame superblocks in the order required by the not-yet-implemented deblocking stage. + /// private void DecodeLoopFilterForFrame() { int superblockSizeLog2 = this.sequenceHeader.SuperblockSizeLog2; int pictureWidthInSuperblocks = Av1Math.DivideLog2Ceiling(this.frameHeader.FrameSize.FrameWidth, this.sequenceHeader.SuperblockSizeLog2); int pictureHeightInSuperblocks = Av1Math.DivideLog2Ceiling(this.frameHeader.FrameSize.FrameHeight, this.sequenceHeader.SuperblockSizeLog2); - // Loop over a frame : tregger dec_loop_filter_sb for each SB + // Deblocking uses raster traversal so each block can consume already reconstructed top and left edges. for (int superblockIndexY = 0; superblockIndexY < pictureHeightInSuperblocks; ++superblockIndexY) { for (int superblockIndexX = 0; superblockIndexX < pictureWidthInSuperblocks; ++superblockIndexX) @@ -148,7 +215,7 @@ internal class Av1FrameDecoder : IAv1FrameDecoder Point superblockPoint = new(superblockOriginX, superblockOriginY); Av1SuperblockInfo superblockInfo = this.frameInfo.GetSuperblock(superblockPoint); - // LF function for a SB + // Superblock filtering remains disabled until its complete plane and edge-strength implementation is available. /* DecodeLoopFilterForSuperblock( superblockInfo, diff --git a/src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1FrameEncoder.cs b/src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1FrameEncoder.cs index 6bdd55ac5..b5a847d40 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1FrameEncoder.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1FrameEncoder.cs @@ -5,70 +5,32 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.Tiling; namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline; +/// +/// Defines the work-in-progress AV1 still-image frame-encoding boundary. +/// internal class Av1FrameEncoder { + /// + /// The source plane samples supplied for frame encoding. + /// private readonly Av1FrameBuffer frameBuffer; + /// + /// Initializes a new instance of the class. + /// + /// The source frame samples to encode. public Av1FrameEncoder(Av1FrameBuffer frameBuffer) { this.frameBuffer = frameBuffer; } /// - /// SVT: svt_av1_enc_init + /// Represents the not-yet-implemented entry point for encoding one AV1 still-image frame. /// + /// SVT-AV1: svt_av1_enc_init. public static void Encode() { - /************************************ - * Thread Handles - ************************************/ - - // Resource Coordination - // Single thread calling svt_aom_resource_coordination_kernel with context enc_handle_ptr->resource_coordination_context_ptr - // Multiple threads calling svt_aom_picture_analysis_kernel, with context enc_handle_ptr->picture_analysis_context_ptr_array - - // Picture Decision - // Single thread calling svt_aom_picture_decision_kernel with context enc_handle_ptr->picture_decision_context_ptr - - // Motion Estimation - // Multiple threads calling svt_aom_motion_estimation_kernel with context enc_handle_ptr->motion_estimation_context_ptr_array - - // Initial Rate Control - // Single thread calling svt_aom_initial_rate_control_kernel with context enc_handle_ptr->initial_rate_control_context_ptr - - // Source Based Oprations - // source_based_operations_context_ptr_array - - // TPL dispenser - // Multiple threads calling svt_aom_tpl_disp_kernel with context enc_handle_ptr->tpl_disp_context_ptr_array - - // Picture Manager - // Single thread calling svt_aom_picture_manager_kernel with context enc_handle_ptr->picture_manager_context_ptr - - // Rate Control - // Single thread calling svt_aom_rate_control_kernel with context enc_handle_ptr->rate_control_context_ptr - - // Mode Decision Configuration Process - // Multiple threads calling svt_aom_mode_decision_configuration_kernel with context enc_handle_ptr->mode_decision_configuration_context_ptr_array - - // EncDec Process - // Multiple threads calling svt_aom_mode_decision_kernel enc_handle_ptr->enc_dec_context_ptr_array - - // Dlf Process - // Multiple threads calling svt_aom_dlf_kernel with context enc_handle_ptr->dlf_context_ptr_array - - // Cdef Process - // Multiple threads calling svt_aom_cdef_kernel enc_handle_ptr->cdef_context_ptr_array - - // Rest Process - // Multiple threads calling svt_aom_rest_kernel enc_handle_ptr->rest_context_ptr_array - - // Entropy Coding Process - // Multiple threads calling svt_aom_entropy_coding_kernel enc_handle_ptr->entropy_coding_context_ptr_array - - // Packetization - // Single thread calling svt_aom_packetization_kernel with context enc_handle_ptr->packetization_context_ptr - - // svt_print_memory_usage(); + // Still-image encoding needs the normative analysis, transform, quantization, entropy, and packetization stages, + // but it does not require SVT-AV1's application-level worker graph or video-sequence process orchestration. } } diff --git a/src/ImageSharp/Formats/Heif/Av1/Pipeline/IAv1FrameDecoder.cs b/src/ImageSharp/Formats/Heif/Av1/Pipeline/IAv1FrameDecoder.cs index 1204e8bc1..ce1763015 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Pipeline/IAv1FrameDecoder.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Pipeline/IAv1FrameDecoder.cs @@ -6,15 +6,15 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.Tiling; namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline; /// -/// Interface for decoder of a single frame. +/// Defines reconstruction of decoded AV1 superblocks within a single still-image frame. /// internal interface IAv1FrameDecoder { /// - /// Decode a single superblock. + /// Reconstructs one decoded superblock into the current frame buffer. /// - /// The top left position of the superblock, in mode info units. - /// The superblock to decode - /// The tile in whcih the superblock is positioned. + /// The superblock's top-left position in 4x4 mode-info units. + /// The decoded syntax and block modes for the superblock. + /// The tile that contains the superblock. void DecodeSuperblock(Point modeInfoPosition, Av1SuperblockInfo superblockInfo, Av1TileInfo tileInfo); }