///|
/// One tile-local reconstructed plane and its coefficient neighbours. Pixels
/// include the AV1 MI padding; the visible frame rectangle is cropped only
/// after every block has been decoded.
priv struct Av1IntraPlane {
width : Int
height : Int
pixels : Array[Int]
tx_sizes : Array[Int]
above_level : Array[Int]
left_level : Array[Int]
above_dc : Array[Int]
left_dc : Array[Int]
}
///|
fn av1_intra_plane(
width : Int,
height : Int,
bit_depth : Int,
track_loop_filter? : Bool = false,
) -> Av1IntraPlane {
{
width,
height,
pixels: Array::make(width * height, 1 << (bit_depth - 1)),
tx_sizes: if track_loop_filter {
Array::make(width / 4 * (height / 4), 0)
} else {
[]
},
above_level: Array::make(width / 4, 0),
left_level: Array::make(height / 4, 0),
above_dc: Array::make(width / 4, 0),
left_dc: Array::make(height / 4, 0),
}
}
///|
/// Tile-local mode and residual state. Each mode CDF row persists across all
/// coding blocks, including palette mode, color-cache and index probabilities.
priv struct Av1IntraState {
cols : Int
rows : Int
skips : Array[Int]
cdef : Av1CdefConfig?
cdef_indices : Array[Int]
skip_cdfs : Array[Array[Int]]
y_modes : Array[Int]
uv_modes : Array[Int]
y_mode : Array[Array[Array[Int]]]
uv_cfl : Array[Array[Int]]
uv_no_cfl : Array[Array[Int]]
cfl : Av1CflState
angle_delta : Array[Array[Int]]
palette : Av1PaletteState
mut palette_y_map : Array[Int]
mut palette_uv_map : Array[Int]
filter_intra : Array[Array[Int]]
filter_intra_mode_cdf : Array[Int]
mut filter_intra_mode : Int
tables : Av1TxEntTables
y_coeff : Av1CoeffContext
uv_coeff : Av1CoeffContext
planes : Array[Av1IntraPlane]
base_q_idx : Int
/// Per-plane quantizer deltas; the dequant reads the plane's own index.
quant_deltas : Av1QuantDeltas
quant_matrix : Av1QuantMatrix?
intrabc : Av1IntrabcState?
/// Chroma subsampling shifts (0 or 1). Plane geometry, chroma block
/// positions and the coefficient contexts all derive from them.
sub_x : Int
sub_y : Int
bit_depth : Int
monochrome : Bool
reduced_tx_set : Bool
allow_screen_content_tools : Bool
enable_filter_intra : Bool
enable_intra_edge_filter : Bool
superblock_size : Int
mut prediction_block : Av1SbBlock?
mut prediction_partition : Av1SbPartition
mut y_angle_delta : Int
mut uv_angle_delta : Int
tx_mode_select : Bool
tx_depth : Av1TxDepthState
tx_neighbors : Av1TxTraversalState
/// Variable-transform split rows, used only when an inter block codes a
/// rectangular transform tree.
vartx : Av1VarTxCdfState
/// The per-block `segment_id` symbol's rows, one context per above/left
/// agreement level, adapted tile-wide like every other table.
seg_id : Array[Array[Int]]
/// The segment id each 4x4 unit of this tile took, in decode order. It is
/// what `get_cur_frame_segid` predicts the next block from, so it is written
/// over the block's full 4x4 footprint exactly as `skips` is.
seg_ids : Array[Int]
previous_seg_ids : Array[Int]
seg_pred_flags : Array[Int]
seg_pred_cdfs : Array[Array[Int]]
mut segment_id : Int
/// The parsed `segmentation_params` of the frame being decoded; empty when
/// the frame disabled segmentation.
segmentation : Av1SegmentationParams
/// This block's quantizer index: this superblock's index plus the segment's
/// ALT_Q feature (AV1 §6.10.9). The residual's CDF rows stay at the frame's
/// own q_category, as libdav1d's per-tile initialisation does; this index is
/// the one the dequantisation reads.
mut seg_q_idx : Int
/// Segment quantizer for syntax and LosslessArray: frame base plus ALT_Q,
/// deliberately ignoring this superblock's delta-Q (get_qindex(1, seg_id)).
mut seg_syntax_q_idx : Int
/// This superblock's quantizer index: the frame's base index accumulated with
/// the per-superblock `delta_q` symbols (AV1 §6.10.10), clipped as libdav1d's
/// `last_qidx` is. Block indices build on it.
mut sb_q_idx : Int
/// `delta_q_params` of the frame this tile belongs to: whether each
/// superblock codes its own quantizer delta, and the scale those deltas
/// carry.
delta_q_present : Bool
delta_q_res : Int
/// The per-superblock `delta_q` symbol's row, adapted tile-wide.
delta_q : Array[Int]
delta_lf_present : Bool
delta_lf_res : Int
delta_lf_multi : Bool
delta_lf_cdfs : Array[Array[Int]]
sb_lf_deltas : Array[Int]
lf_deltas : Array[Int]
/// This segment's lossless flag, independent of the superblock delta-Q.
mut seg_lossless : Bool
/// Inter-frame block state, or None for an intra frame whose leaf reads the
/// intra syntax directly.
inter : Av1InterInfo?
/// The inter block whose transforms are being reconstructed; a chroma
/// transform reads its origin to find the luma type it inherits.
mut current : Av1InterBlock?
restoration_units : Array[Av1RestorationUnitRecord]
}
///|
///|
/// DefaultSegmentIdCdf: three rows, one per `get_cur_frame_segid` context, over
/// `MAX_SEGMENTS - 1` symbols. libdav1d's `default_cdf.m.seg_id` in transcribed
/// form.
fn av1_seg_id_cdfs() -> Array[Array[Int]] {
[
[5622, 7893, 16093, 18233, 27809, 28373, 32533, 32768, 0],
[14274, 18230, 22557, 24935, 29980, 30851, 32344, 32768, 0],
[27527, 28487, 28723, 28890, 32397, 32647, 32679, 32768, 0],
]
}
///|
fn av1_intra_state(
width : Int,
height : Int,
base_q_idx : Int,
bit_depth : Int,
reduced_tx_set : Bool,
allow_screen_content_tools : Bool,
enable_filter_intra : Bool,
tx_mode_select : Bool,
cdef? : Av1CdefConfig? = None,
monochrome? : Bool = false,
enable_intra_edge_filter? : Bool = false,
superblock_size? : Int = 64,
track_loop_filter? : Bool = false,
quant_deltas? : Av1QuantDeltas = {
y_dc: 0,
u_dc: 0,
u_ac: 0,
v_dc: 0,
v_ac: 0,
},
sub_x? : Int = 1,
sub_y? : Int = 1,
inter? : Av1InterInfo? = None,
/// The frame's parsed `segmentation_params` (AV1 §6.10.9); empty when the
/// frame disabled segmentation.
segmentation? : Av1SegmentationParams? = None,
/// `delta_q_params` (AV1 §6.10.10): whether each superblock codes its own
/// quantizer delta and the log2 scale those deltas carry.
delta_q_present? : Bool = false,
delta_q_res? : Int = 0,
quant_matrix? : Av1QuantMatrix? = None,
allow_intrabc? : Bool = false,
frame_mi_cols? : Int = 0,
frame_mi_rows? : Int = 0,
tile_col? : Int = 0,
tile_row? : Int = 0,
previous_seg_ids? : Array[Int] = [],
delta_lf_present? : Bool = false,
delta_lf_res? : Int = 0,
delta_lf_multi? : Bool = false,
) -> Av1IntraState {
let filter_intra : Array[Array[Int]] = []
for
probability in [
4621, 6743, 5893, 7866, 12551, 9394, 12408, 14301, 12756, 22343, 16384, 16384,
16384, 16384, 16384, 16384, 12770, 10368, 20229, 18101, 16384, 16384,
] {
filter_intra.push([probability, 32768, 0])
}
let planes = [av1_intra_plane(width, height, bit_depth, track_loop_filter~)]
if !monochrome {
// Coded chroma extents follow ceil(width / 2^sub): the coded luma size is
// always a multiple of 8 here, so this only matters for odd visible sizes.
let chroma_w = (width + (1 << sub_x) - 1) >> sub_x
let chroma_h = (height + (1 << sub_y) - 1) >> sub_y
planes.push(
av1_intra_plane(chroma_w, chroma_h, bit_depth, track_loop_filter~),
)
planes.push(
av1_intra_plane(chroma_w, chroma_h, bit_depth, track_loop_filter~),
)
}
{
cols: width / 4,
rows: height / 4,
skips: Array::make(width / 4 * (height / 4), 0),
cdef,
cdef_indices: Array::make((width + 63) / 64 * ((height + 63) / 64), -1),
skip_cdfs: [[31671, 32768, 0], [16515, 32768, 0], [4576, 32768, 0]],
y_modes: Array::make(width / 4 * (height / 4), 0),
uv_modes: Array::make(width / 4 * (height / 4), 0),
y_mode: av1_intra_y_mode_cdfs(),
uv_cfl: av1_intra_uv_mode_cdfs(true),
uv_no_cfl: av1_intra_uv_mode_cdfs(false),
cfl: av1_cfl_state(sub_x~, sub_y~),
angle_delta: av1_intra_angle_cdfs(),
palette: av1_palette_state(width / 4, height / 4),
palette_y_map: [],
palette_uv_map: [],
filter_intra,
filter_intra_mode_cdf: [8949, 12776, 17211, 29558, 32768, 0],
filter_intra_mode: -1,
tables: av1_tx_ent_tables(base_q_idx),
y_coeff: av1_coeff_context(base_q_idx, 0, 0),
uv_coeff: av1_coeff_context(base_q_idx, 0, 1),
planes,
base_q_idx,
quant_deltas,
quant_matrix,
intrabc: if allow_intrabc {
Some(
av1_intrabc_state(
width,
height,
frame_mi_cols=if frame_mi_cols > 0 {
frame_mi_cols
} else {
width / 4
},
frame_mi_rows=if frame_mi_rows > 0 {
frame_mi_rows
} else {
height / 4
},
tile_col~,
tile_row~,
),
)
} else {
None
},
sub_x,
sub_y,
bit_depth,
monochrome,
reduced_tx_set,
allow_screen_content_tools,
enable_filter_intra,
enable_intra_edge_filter,
superblock_size,
prediction_block: None,
prediction_partition: Av1SbPartitionNone,
y_angle_delta: 0,
uv_angle_delta: 0,
tx_mode_select,
tx_depth: av1_tx_depth_state(),
tx_neighbors: av1_tx_traversal_state(width, height),
vartx: av1_vartx_cdf_state(),
seg_id: av1_seg_id_cdfs(),
seg_ids: Array::make(width / 4 * (height / 4), 0),
previous_seg_ids,
seg_pred_flags: Array::make(width / 4 * (height / 4), 0),
seg_pred_cdfs: Array::makei(3, _ => [16384, 32768, 0]),
segment_id: 0,
segmentation: match segmentation {
Some(params) => params
None => av1_segmentation_empty()
},
seg_q_idx: base_q_idx,
seg_syntax_q_idx: base_q_idx,
sb_q_idx: base_q_idx,
delta_q_present,
delta_q_res,
delta_q: av1_delta_q_cdf(),
delta_lf_present,
delta_lf_res,
delta_lf_multi,
delta_lf_cdfs: Array::makei(5, _ => av1_delta_q_cdf()),
sb_lf_deltas: [0, 0, 0, 0],
lf_deltas: if delta_lf_present {
Array::make(width / 4 * (height / 4) * 4, 0)
} else {
[]
},
seg_lossless: base_q_idx == 0 && av1_quant_deltas_lossless(quant_deltas),
inter,
current: None,
restoration_units: [],
}
}
///|
/// AV1 BLOCK_* indices for every legal coding-block geometry.
fn av1_intra_block_index(width : Int, height : Int) -> Int? {
if width == 4 && height == 4 {
Some(0)
} else if width == 4 && height == 8 {
Some(1)
} else if width == 8 && height == 4 {
Some(2)
} else if width == 8 && height == 8 {
Some(3)
} else if width == 8 && height == 16 {
Some(4)
} else if width == 16 && height == 8 {
Some(5)
} else if width == 16 && height == 16 {
Some(6)
} else if width == 16 && height == 32 {
Some(7)
} else if width == 32 && height == 16 {
Some(8)
} else if width == 32 && height == 32 {
Some(9)
} else if width == 32 && height == 64 {
Some(10)
} else if width == 64 && height == 32 {
Some(11)
} else if width == 64 && height == 64 {
Some(12)
} else if width == 64 && height == 128 {
Some(13)
} else if width == 128 && height == 64 {
Some(14)
} else if width == 128 && height == 128 {
Some(15)
} else if width == 4 && height == 16 {
Some(16)
} else if width == 16 && height == 4 {
Some(17)
} else if width == 8 && height == 32 {
Some(18)
} else if width == 32 && height == 8 {
Some(19)
} else if width == 16 && height == 64 {
Some(20)
} else if width == 64 && height == 16 {
Some(21)
} else {
None
}
}
///|
/// With 4:2:0 subsampling, blocks narrower than eight luma pixels share UV
/// samples with the preceding block. Only the odd MI coordinate on each
/// sub-eight-pixel axis reads and reconstructs the shared chroma block.
/// The chroma reference rule (AV1 section 6.4.3 is_chroma_reference): on a
/// subsampled axis a luma block narrower than eight pixels only carries chroma
/// when it is the second four-pixel half of its eight-pixel group. An axis
/// without subsampling never merges.
fn av1_intra_has_chroma(state : Av1IntraState, block : Av1SbBlock) -> Bool {
if state.monochrome {
return false
}
let x_owns = if state.sub_x == 1 {
block.width >= 8 || ((block.col / 4) & 1) != 0
} else {
true
}
let y_owns = if state.sub_y == 1 {
block.height >= 8 || ((block.row / 4) & 1) != 0
} else {
true
}
x_owns && y_owns
}
///|
/// DefaultDeltaQCdf (libdav1d `default_cdf.m.delta_q`, a CDF3 row): the
/// per-superblock quantizer delta's symbol, read at each superblock origin when
/// the frame signals `delta_q_present`.
fn av1_delta_q_cdf() -> Array[Int] {
[28160, 32120, 32677, 32768, 0]
}
///|
/// `read_delta_qindex` (AV1 §6.10.10, libaom `decodeframe.c`): the magnitude
/// symbol over the three-value row, extended through the hi_tok tail when it
/// saturates, then signed and scaled by `delta_q_res`. Accumulated into this
/// superblock's quantizer index exactly as libdav1d's `last_qidx` is: clipped
/// to 1..255, so a delta can never reach zero.
fn av1_read_superblock_delta_q(
decoder : Av1SymbolDecoder,
state : Av1IntraState,
) -> Unit {
if !state.delta_q_present {
return
}
let mut delta_q = decoder.symbol(state.delta_q)
if delta_q == 3 {
let n_bits = 1 + av1_msac_bools(decoder, 3)
delta_q = av1_msac_bools(decoder, n_bits) + 1 + (1 << n_bits)
}
if delta_q != 0 {
let negative = av1_msac_bool(decoder) == 1
if negative {
delta_q = -delta_q
}
delta_q = delta_q << state.delta_q_res
}
let accumulated = state.sb_q_idx + delta_q
state.sb_q_idx = if accumulated < 1 {
1
} else if accumulated > 255 {
255
} else {
accumulated
}
}
///|
/// Whether this block is the one libdav1d reads the superblock's delta from:
/// the first block of the superblock, which is a block the size of the whole
/// superblock only when the superblock is not split.
fn av1_at_superblock_origin(
state : Av1IntraState,
mi_row : Int,
mi_col : Int,
) -> Bool {
let sb_size = state.superblock_size
mi_col * 4 % sb_size == 0 && mi_row * 4 % sb_size == 0
}
///|
/// Whether the superblock's first block is a whole-superblock block, which
/// suppresses the delta symbol when it is skipped (libdav1d's
/// `bs != BS_64x64/128x128 || !b->skip`).
fn av1_superblock_block_is_whole(
width : Int,
height : Int,
superblock_size : Int,
) -> Bool {
width == superblock_size && height == superblock_size
}
///|
/// `get_cur_frame_segid` (AV1 §9.3.14) plus the `segment_id` symbol that
/// follows it. The predicted id comes from the neighbours already stored for
/// this tile, and the symbol codes the difference from it, de-interleaved
/// against the frame's last active segment id. Returns the id, or -1 when the
/// frame does not write the segment map at all.
///
/// `preskip` reads the symbol before the block's skip flag; the non-preskip
/// form reads it after skip and keeps the prediction for a skipped block, so a
/// skipped block codes no symbol.
fn av1_read_segment_id(
decoder : Av1SymbolDecoder,
state : Av1IntraState,
mi_row : Int,
mi_col : Int,
avail_u : Bool,
avail_l : Bool,
skip : Int,
preskip : Bool,
width4? : Int = 1,
height4? : Int = 1,
) -> Int {
let params = state.segmentation
if !params.enabled {
return -1
}
if state.inter is Some(_) && !params.update_map {
return av1_segment_previous_id(state, mi_row, mi_col, width4, height4)
}
if state.inter is Some(_) && params.temporal_update {
let context = (if avail_u && mi_row > 0 {
state.seg_pred_flags[(mi_row - 1) * state.cols + mi_col]
} else {
0
}) +
(if avail_l && mi_col > 0 {
state.seg_pred_flags[mi_row * state.cols + mi_col - 1]
} else {
0
})
let predicted = if !preskip && skip == 1 {
0
} else {
decoder.symbol(state.seg_pred_cdfs[context])
}
for y in mi_row..<(mi_row + height4) {
for x in mi_col..<(mi_col + width4) {
if y < state.rows && x < state.cols {
state.seg_pred_flags[y * state.cols + x] = predicted
}
}
}
if predicted == 1 {
return av1_segment_previous_id(state, mi_row, mi_col, width4, height4)
}
}
// The predicted id and the context come from the same three neighbours:
// above, left, and above-left of this block's top-left corner.
let above = if avail_u && mi_row > 0 {
state.seg_ids[(mi_row - 1) * state.cols + mi_col]
} else {
0
}
let left = if avail_l && mi_col > 0 {
state.seg_ids[mi_row * state.cols + mi_col - 1]
} else {
0
}
let topleft = if avail_u && avail_l && mi_row > 0 && mi_col > 0 {
state.seg_ids[(mi_row - 1) * state.cols + mi_col - 1]
} else {
0
}
let seg_ctx = if avail_u && avail_l {
if left == above && topleft == left {
2
} else if left == above || topleft == left || above == topleft {
1
} else {
0
}
} else {
0
}
let predicted = if avail_u && avail_l {
if above == topleft {
above
} else {
left
}
} else if avail_l {
left
} else if avail_u {
above
} else {
0
}
if !preskip && skip == 1 {
return predicted
}
let diff = decoder.symbol(state.seg_id[seg_ctx])
if diff < 0 {
return 0
}
let max = params.last_active_seg_id + 1
let value = av1_seg_neg_deinterleave(diff, predicted, max)
if value > params.last_active_seg_id || value >= av1_segments {
0
} else {
value
}
}
///|
/// `neg_deinterleave` (AV1 §5.11.16 / libdav1d `decode.c`): undo the encoder's
/// zig-zag folding of a segment id difference around the predicted id.
fn av1_seg_neg_deinterleave(diff : Int, ref : Int, max : Int) -> Int {
if ref == 0 {
diff
} else if ref >= max - 1 {
max - diff - 1
} else if 2 * ref < max {
if diff <= 2 * ref {
if (diff & 1) != 0 {
ref + ((diff + 1) >> 1)
} else {
ref - (diff >> 1)
}
} else {
diff
}
} else if diff <= 2 * (max - ref - 1) {
if (diff & 1) != 0 {
ref + ((diff + 1) >> 1)
} else {
ref - (diff >> 1)
}
} else {
max - (diff + 1)
}
}
///|
/// This block's dequantizer index: the superblock's index plus the segment's
/// ALT_Q feature, clamped to the 8-bit index range (AV1 §6.10.9).
fn av1_seg_block_qindex(state : Av1IntraState, seg_id : Int) -> Int {
av1_segment_qindex(state.sb_q_idx, state.segmentation, seg_id)
}
///|
/// Apply this block's segment: its quantizer index, its lossless flag and the
/// record later blocks predict their segment id from. Only the neighbour-map
/// write depends on update_map; quantization always follows the active segment.
fn av1_seg_apply_block(
state : Av1IntraState,
mi_row : Int,
mi_col : Int,
width4 : Int,
height4 : Int,
seg_id : Int,
) -> Unit {
state.segment_id = if seg_id < 0 { 0 } else { seg_id }
if state.delta_lf_present {
for row in mi_row..<(mi_row + height4) {
for col in mi_col..<(mi_col + width4) {
if row >= 0 && row < state.rows && col >= 0 && col < state.cols {
for k in 0..<4 {
state.lf_deltas[(row * state.cols + col) * 4 + k] = state.sb_lf_deltas[k]
}
}
}
}
}
if seg_id < 0 {
state.seg_q_idx = state.sb_q_idx
state.seg_syntax_q_idx = state.base_q_idx
state.seg_lossless = state.seg_syntax_q_idx == 0 &&
av1_quant_deltas_lossless(state.quant_deltas)
return
}
state.seg_q_idx = av1_seg_block_qindex(state, seg_id)
state.seg_syntax_q_idx = av1_segment_qindex(
state.base_q_idx,
state.segmentation,
seg_id,
)
state.seg_lossless = state.seg_syntax_q_idx == 0 &&
av1_quant_deltas_lossless(state.quant_deltas)
if state.segmentation.enabled {
for row in mi_row..<(mi_row + height4) {
if row < 0 || row >= state.rows {
continue
}
for col in mi_col..<(mi_col + width4) {
if col < 0 || col >= state.cols {
continue
}
state.seg_ids[row * state.cols + col] = seg_id
}
}
}
}
///|
/// Read the luma transform type after all_zero. The transform set uses the
/// square-up dimension for its DCT-only gate, but the square-down dimension
/// selects the CDF row (AV1 transform_type / get_tx_set).
fn av1_intra_tx_type(
decoder : Av1SymbolDecoder,
state : Av1IntraState,
width : Int,
height : Int,
mode : Int,
) -> Av1TxType? {
if state.seg_lossless || width >= 32 || height >= 32 {
return Some(Av1TxDctDct)
}
let direction = if state.filter_intra_mode >= 0 {
match av1_filter_intra_mode_to_intra_dir(state.filter_intra_mode) {
Some(value) => value
None => return None
}
} else {
mode
}
av1_decode_intra_tx_type(
decoder,
state.tables,
if width < height {
width
} else {
height
},
direction,
state.reduced_tx_set,
state.seg_syntax_q_idx,
)
}
///|
/// Complete one transform before the next one reads neighbour state. All
/// coefficient contexts are clipped to the coded MI bounds, including DC
/// sign sums for transforms that extend past a visible frame edge.
fn av1_intra_transform(
decoder : Av1SymbolDecoder,
state : Av1IntraState,
plane_index : Int,
mode : Int,
x : Int,
y : Int,
tx_width : Int,
tx_height : Int,
block_width : Int,
block_height : Int,
skip : Int,
inter? : Bool = false,
) -> Bool {
let plane = state.planes[plane_index]
if x >= plane.width || y >= plane.height {
return true
}
let x4 = x / 4
let y4 = y / 4
let w4 = tx_width / 4
let h4 = tx_height / 4
let cols = plane.width / 4
let rows = plane.height / 4
let mut above_level = 0
let mut left_level = 0
let mut above_nonzero = false
let mut left_nonzero = false
let mut dc_sign = 0
for i in 0.. above_level {
above_level = level
}
let dc = plane.above_dc[x4 + i]
if level != 0 || dc != 0 {
above_nonzero = true
}
if dc == 1 {
dc_sign = dc_sign - 1
} else if dc == 2 {
dc_sign = dc_sign + 1
}
}
}
for i in 0.. left_level {
left_level = level
}
let dc = plane.left_dc[y4 + i]
if level != 0 || dc != 0 {
left_nonzero = true
}
if dc == 1 {
dc_sign = dc_sign - 1
} else if dc == 2 {
dc_sign = dc_sign + 1
}
}
}
let whole = tx_width == block_width && tx_height == block_height
let skip_context = if plane_index == 0 {
av1_txb_skip_ctx_luma(above_level, left_level, whole)
} else {
av1_txb_skip_ctx_chroma(above_nonzero, left_nonzero, whole)
}
let tx_context = av1_tx_size_ctx_rect(tx_width, tx_height)
let all_zero = if skip == 1 {
1
} else {
decoder.symbol(state.tables.txb_skip[tx_context][skip_context])
}
let mut cul_level = 0
let mut dc_category = 0
let mut residual : Array[Int] = []
if all_zero == 0 {
let tx_type = if plane_index == 0 {
if inter {
match av1_block_motion_info(state) {
Some(info) =>
av1_inter_luma_tx_type(
decoder, state, info, tx_width, tx_height, x4, y4,
)
None => return false
}
} else {
match av1_intra_tx_type(decoder, state, tx_width, tx_height, mode) {
Some(value) => value
None => return false
}
}
} else if inter {
match state.current {
Some(current) => {
let info = match av1_block_motion_info(state) {
Some(value) => value
None => return false
}
av1_inter_chroma_tx_type(
state, info, current, tx_width, tx_height, x, y,
)
}
None => return false
}
} else if state.seg_lossless || tx_width >= 32 || tx_height >= 32 {
Av1TxDctDct
} else {
match
av1_chroma_tx_type(
if mode == AV1_INTRA_CFL {
AV1_INTRA_DC
} else {
mode
},
if tx_width < tx_height {
tx_width
} else {
tx_height
},
state.reduced_tx_set,
) {
Some(value) => value
None => return false
}
}
// U/V share probability rows, but the plane-level neighbour arrays above
// remain independent. The luma instance likewise survives all blocks.
let coeff_context = if plane_index == 0 {
state.y_coeff
} else {
state.uv_coeff
}
coeff_context.tx_ctx = tx_context
let coeffs = match
av1_decode_coeffs_leaf_with_decoder(
decoder,
state.seg_q_idx,
coeff_context,
tx_width,
bit_depth=state.bit_depth,
height=tx_height,
scan_class=av1_tx_scan_class(tx_type),
quant_matrix=state.quant_matrix,
plane=plane_index,
tx_type~,
lossless=state.seg_lossless,
dc_sign_ctx=if dc_sign < 0 { 1 } else if dc_sign > 0 { 2 } else { 0 },
dc_q_idx=av1_plane_qindex(
state.seg_q_idx,
state.quant_deltas,
plane_index,
true,
),
ac_q_idx=av1_plane_qindex(
state.seg_q_idx,
state.quant_deltas,
plane_index,
false,
),
) {
Some(value) => value
None => return false
}
cul_level = coeffs.cul_level
dc_category = coeffs.dc_category
let decoded = if state.seg_lossless {
av1_inverse_dct_2d(coeffs.coeffs, 4, 4, state.bit_depth, lossless=true)
} else {
av1_inverse_transform_2d(
tx_type,
coeffs.coeffs,
tx_width,
tx_height,
state.bit_depth,
)
}
residual = match decoded {
Some(value) => value
None => return false
}
} else if inter && plane_index == 0 {
// An all-zero luma transform still records DCT_DCT, because the block's
// chroma transforms inherit it (AV1 §5.11.39).
match av1_block_motion_info(state) {
Some(info) =>
av1_inter_record_tx_types(
info.field,
x4,
y4,
tx_width,
tx_height,
Av1TxDctDct,
)
None => ()
}
}
// AV1 §5.11.39 also writes `TxTypes[...] = DCT_DCT` for an all-zero luma
// transform of an *intra* block. That write is deliberately not mirrored here:
// this decoder has no intra-wide TxTypes grid, and nothing on the intra path
// would read it - a chroma transform of an intra block derives its type from
// `Mode_To_Txfm[UVMode]` (§5.11.47), and the loop filter and the transform-size
// state read `plane.tx_sizes`, which the tail below writes for every leaf,
// all-zero ones included.
let colors = if plane_index == 0 {
state.palette.y_colors
} else if plane_index == 1 {
state.palette.u_colors
} else {
state.palette.v_colors
}
let prediction = if inter {
// An inter block's prediction is already in the plane: motion compensation
// wrote it before the residual, and reconstruct only adds and clips.
Array::makei(tx_width * tx_height, i => {
let row = y + i / tx_width
let col = x + i % tx_width
plane.pixels[(if row < plane.height { row } else { plane.height - 1 }) *
plane.width +
(if col < plane.width { col } else { plane.width - 1 })]
})
} else if !colors.is_empty() {
let block = match state.prediction_block {
Some(value) => value
None => return false
}
let origin_x = if plane_index == 0 || state.sub_x == 0 {
block.col
} else {
block.col / 8 * 4
}
let origin_y = if plane_index == 0 || state.sub_y == 0 {
block.row
} else {
block.row / 8 * 4
}
let indices = if plane_index == 0 {
state.palette_y_map
} else {
state.palette_uv_map
}
if indices.length() != block_width * block_height ||
x < origin_x ||
y < origin_y ||
x - origin_x + tx_width > block_width ||
y - origin_y + tx_height > block_height {
return false
}
Array::makei(tx_width * tx_height, i => {
colors[indices[(y - origin_y + i / tx_width) * block_width +
x -
origin_x +
i % tx_width]]
})
} else if plane_index == 0 && state.filter_intra_mode >= 0 {
let n_top = if y == 0 {
0
} else if tx_width < plane.width - x {
tx_width
} else {
plane.width - x
}
let n_left = if x == 0 {
0
} else if tx_height < plane.height - y {
tx_height
} else {
plane.height - y
}
let above = Array::makei(n_top, i => {
plane.pixels[(y - 1) * plane.width + x + i]
})
let left = Array::makei(n_left, i => {
plane.pixels[(y + i) * plane.width + x - 1]
})
let corner = if x > 0 && y > 0 {
plane.pixels[(y - 1) * plane.width + x - 1]
} else {
1 << (state.bit_depth - 1)
}
match
av1_filter_intra_predict(
state.filter_intra_mode,
tx_width,
tx_height,
above,
left,
corner,
state.bit_depth,
n_top,
n_left,
) {
Some(value) => value
None => return false
}
} else if mode >= 1 && mode <= 8 {
let block = match state.prediction_block {
Some(value) => value
None => return false
}
let tile : Av1SbBlock = {
row: 0,
col: 0,
width: state.cols * 4,
height: state.rows * 4,
}
let counts = match
av1_intra_edge_counts(
tile,
tile.width,
tile.height,
block,
state.prediction_partition,
{ row: y, col: x, width: tx_width, height: tx_height, },
plane_index,
state.superblock_size,
sub_x=state.sub_x,
sub_y=state.sub_y,
) {
Some(value) => value
None => return false
}
let delta = if plane_index == 0 {
state.y_angle_delta
} else {
state.uv_angle_delta
}
let base_angles = [90, 180, 45, 135, 113, 157, 203, 67]
let angle = base_angles[mode - 1] + 3 * delta
let n_right = if angle < 90 { counts.n_topright } else { -1 }
let n_bottom = if angle > 180 { counts.n_bottomleft } else { -1 }
let above : Array[Int] = []
for i in 0..<(counts.n_top + (if n_right > 0 { n_right } else { 0 })) {
above.push(plane.pixels[(y - 1) * plane.width + x + i])
}
let left : Array[Int] = []
for i in 0..<(counts.n_left + (if n_bottom > 0 { n_bottom } else { 0 })) {
left.push(plane.pixels[(y + i) * plane.width + x - 1])
}
let corner = if counts.n_top > 0 && counts.n_left > 0 {
plane.pixels[(y - 1) * plane.width + x - 1]
} else {
1 << (state.bit_depth - 1)
}
let neighbor_smooth = match
av1_intra_edge_neighbor_smooth(
tile,
block,
plane_index,
state.y_modes,
state.uv_modes,
sub_x=state.sub_x,
sub_y=state.sub_y,
) {
Some(value) => value
None => return false
}
match
av1_directional_predict(
mode,
delta,
tx_width,
tx_height,
above,
left,
corner,
state.bit_depth,
counts.n_top,
counts.n_left,
n_right,
n_bottom,
state.enable_intra_edge_filter,
neighbor_smooth,
) {
Some(value) => value
None => return false
}
} else {
let above : Array[Int] = []
let left : Array[Int] = []
if y > 0 {
for i in 0.. 0 {
for i in 0.. 0 && y > 0 {
plane.pixels[(y - 1) * plane.width + x - 1]
} else {
1 << (state.bit_depth - 1)
}
match
av1_intra_predict(
if plane_index != 0 && mode == AV1_INTRA_CFL {
AV1_INTRA_DC
} else {
mode
},
tx_width,
tx_height,
above,
left,
corner,
state.bit_depth,
have_above=y > 0,
have_left=x > 0,
) {
Some(value) => value
None => return false
}
}
if plane_index != 0 && mode == AV1_INTRA_CFL {
if !av1_cfl_predict(
state.cfl,
prediction,
tx_width,
tx_height,
if plane_index == 1 {
state.cfl.alpha_u
} else {
state.cfl.alpha_v
},
state.bit_depth,
) {
return false
}
}
let store_luma = plane_index == 0 && state.cfl.store_y
if store_luma {
// CfL consumes the complete reconstructed TX, including samples that
// the visible/coded plane write below clips away at a frame edge.
for i in 0..= plane.height {
break
}
for col in 0..= plane.width {
break
}
let index = row * tx_width + col
let value = prediction[index] +
(if all_zero == 0 && !store_luma { residual[index] } else { 0 })
plane.pixels[(y + row) * plane.width + x + col] = av1_intra_clip(
value,
state.bit_depth,
)
}
}
for i in 0..=
/// BLOCK_8X8, including 4x16/16x4. Smaller indices and nondirectional modes
/// imply zero. Store the result for the selected plane; CfL alpha syntax is
/// decoded separately.
fn av1_intra_read_mode_angle(
decoder : Av1SymbolDecoder,
state : Av1IntraState,
mode : Int,
block_index : Int,
plane? : Int = 0,
) -> Bool {
if mode < 0 || mode > 12 {
return false
}
let delta = if mode >= 1 && mode <= 8 && block_index >= 3 {
decoder.symbol(state.angle_delta[mode - 1]) - 3
} else {
0
}
if delta < -3 || delta > 3 {
return false
}
if plane == 0 {
state.y_angle_delta = delta
} else {
state.uv_angle_delta = delta
}
true
}
///|
/// The first non-skipped coding block in a 64x64 CDEF region selects that
/// region's strength. A 128-pixel coding block shares one index across its
/// whole footprint; skipped blocks leave the region available for a later
/// non-skipped block without consuming entropy.
fn av1_intra_cdef_index(
decoder : Av1SymbolDecoder,
state : Av1IntraState,
block : Av1SbBlock,
skip : Int,
) -> Bool {
if skip == 1 {
return true
}
if skip != 0 {
return false
}
let config = match state.cdef {
Some(value) => value
None => return true
}
let width = state.cols * 4
let height = state.rows * 4
if block.col < 0 ||
block.row < 0 ||
block.col >= width ||
block.row >= height ||
block.width <= 0 ||
block.height <= 0 ||
block.width > 128 ||
block.height > 128 {
return false
}
let region_cols = (width + 63) / 64
let region_rows = (height + 63) / 64
let x0 = block.col / 64
let y0 = block.row / 64
let first = y0 * region_cols + x0
let mut index = state.cdef_indices[first]
if index < 0 {
index = 0
for _ in 0..= region_rows {
break
}
for col in x0..<(x1 + 1) {
if col >= region_cols {
break
}
state.cdef_indices[row * region_cols + col] = index
}
}
true
}
///|
/// Decode residuals in AV1 syntax order: 64x64 luma chunk, then plane, then
/// transform raster order within that chunk. Keep the full coding-plane
/// dimensions for txb_skip context; a chunk is not a new coding block.
fn av1_intra_residuals(
decoder : Av1SymbolDecoder,
state : Av1IntraState,
block : Av1SbBlock,
y_mode : Int,
uv_mode : Int,
luma_tx_width : Int,
luma_tx_height : Int,
skip : Int,
inter? : Bool = false,
) -> Bool {
state.prediction_block = Some(block)
let has_chroma = av1_intra_has_chroma(state, block)
let plane_count = if has_chroma { 3 } else { 1 }
av1_cfl_begin_block(
state.cfl,
block,
!state.monochrome && (!has_chroma || uv_mode == AV1_INTRA_CFL),
)
for chunk_y in 0..<((block.height + 63) / 64) {
for chunk_x in 0..<((block.width + 63) / 64) {
for plane in 0..> sub) per axis, and its origin rounds the luma
// position down to the chroma reference (even MI unit) on each
// subsampled axis (is_chroma_reference).
let plane_sub_x = if plane == 0 { 0 } else { state.sub_x }
let plane_sub_y = if plane == 0 { 0 } else { state.sub_y }
let divisor_x = 1 << plane_sub_x
let divisor_y = 1 << plane_sub_y
let width = if plane != 0 {
let scaled = block.width >> plane_sub_x
if scaled < 4 {
4
} else {
scaled
}
} else {
block.width
}
let height = if plane != 0 {
let scaled = block.height >> plane_sub_y
if scaled < 4 {
4
} else {
scaled
}
} else {
block.height
}
let origin_x = if plane_sub_x == 1 {
block.col / 8 * 4
} else {
block.col
}
let origin_y = if plane_sub_y == 1 {
block.row / 8 * 4
} else {
block.row
}
let unit_width = if block.width < 64 { width } else { 64 / divisor_x }
let unit_height = if block.height < 64 {
height
} else {
64 / divisor_y
}
let tx_width = if plane == 0 {
luma_tx_width
} else if state.seg_lossless {
4
} else if width > 32 {
32
} else {
width
}
let tx_height = if plane == 0 {
luma_tx_height
} else if state.seg_lossless {
4
} else if height > 32 {
32
} else {
height
}
let mut row = 0
while row < unit_height {
let mut col = 0
while col < unit_width {
if !av1_intra_transform(
decoder,
state,
plane,
if plane == 0 {
y_mode
} else {
uv_mode
},
origin_x + chunk_x * (64 / divisor_x) + col,
origin_y + chunk_y * (64 / divisor_y) + row,
tx_width,
tx_height,
width,
height,
skip,
inter~,
) {
return false
}
col = col + tx_width
}
row = row + tx_height
}
}
}
}
true
}
///|
/// Palette maps precede transform-size and coefficient syntax, even for
/// skipped transforms. Entropy covers the coded MI rectangle; map padding
/// extends the final decoded column/row without reading additional symbols.
fn av1_intra_palette_maps(
decoder : Av1SymbolDecoder,
state : Av1IntraState,
block : Av1SbBlock,
) -> Bool {
state.palette_y_map = []
state.palette_uv_map = []
let luma_cols = if block.width < state.cols * 4 - block.col {
block.width
} else {
state.cols * 4 - block.col
}
let luma_rows = if block.height < state.rows * 4 - block.row {
block.height
} else {
state.rows * 4 - block.row
}
for plane in 0..<2 {
let colors = if plane == 0 {
state.palette.y_colors
} else {
state.palette.u_colors
}
if colors.is_empty() {
continue
}
// Palette index maps are per plane pixel. On a subsampled axis a luma
// block narrower than eight pixels shares the chroma block of its paired
// neighbour, so the map grows to the full four-chroma-pixel unit
// (`extra`), matching the residual block above.
let plane_sub_x = if plane == 0 { 0 } else { state.sub_x }
let plane_sub_y = if plane == 0 { 0 } else { state.sub_y }
let extra_cols = if plane_sub_x == 1 && block.width < 8 { 2 } else { 0 }
let extra_rows = if plane_sub_y == 1 && block.height < 8 { 2 } else { 0 }
let map_width = (block.width >> plane_sub_x) + extra_cols
let map_height = (block.height >> plane_sub_y) + extra_rows
let indices = match
av1_palette_decode_index_map(
decoder,
colors.length(),
map_width,
map_height,
(luma_rows >> plane_sub_y) + extra_rows,
(luma_cols >> plane_sub_x) + extra_cols,
if plane == 0 {
state.palette.y_index[colors.length() - 2]
} else {
state.palette.uv_index[colors.length() - 2]
},
) {
Some(value) => value
None => return false
}
if plane == 0 {
state.palette_y_map = indices
} else {
state.palette_uv_map = indices
}
}
true
}
///|
fn av1_intra_block(
decoder : Av1SymbolDecoder,
state : Av1IntraState,
block : Av1SbBlock,
partition? : Av1SbPartition = Av1SbPartitionNone,
) -> Bool {
match state.inter {
Some(info) => av1_inter_tile_block(decoder, state, info, block, partition)
None => av1_intra_only_block(decoder, state, block, partition~)
}
}
///|
fn av1_intra_only_block(
decoder : Av1SymbolDecoder,
state : Av1IntraState,
block : Av1SbBlock,
partition? : Av1SbPartition = Av1SbPartitionNone,
) -> Bool {
state.prediction_partition = partition
state.y_angle_delta = 0
state.uv_angle_delta = 0
state.filter_intra_mode = -1
let block_index = match av1_intra_block_index(block.width, block.height) {
Some(value) => value
None => return false
}
let x4 = block.col / 4
let y4 = block.row / 4
let has_chroma = av1_intra_has_chroma(state, block)
let mut seg_id = if state.segmentation.seg_id_pre_skip {
av1_read_segment_id(
decoder,
state,
y4,
x4,
y4 > 0,
x4 > 0,
0,
true,
width4=block.width / 4,
height4=block.height / 4,
)
} else {
-1
}
let mut skip_context = 0
if y4 > 0 {
skip_context = skip_context + state.skips[(y4 - 1) * state.cols + x4]
}
if x4 > 0 {
skip_context = skip_context + state.skips[y4 * state.cols + x4 - 1]
}
let skip = if av1_segment_feature(state.segmentation, seg_id, 6) {
1
} else {
decoder.symbol(state.skip_cdfs[skip_context])
}
if skip != 0 && skip != 1 {
return false
}
// `read_segment_id` sits between skip and the CDEF index (AV1 §5.11.13), and
// the segment it names decides the block's quantizer index from here on.
if !state.segmentation.seg_id_pre_skip {
seg_id = av1_read_segment_id(
decoder,
state,
y4,
x4,
y4 > 0,
x4 > 0,
skip,
false,
width4=block.width / 4,
height4=block.height / 4,
)
}
if !av1_intra_cdef_index(decoder, state, block, skip) {
return false
}
// The superblock's quantizer delta follows the CDEF index (AV1 §6.10.10);
// a whole-superblock block that is skipped is the one case dav1d does not
// read it for.
if av1_at_superblock_origin(state, y4, x4) &&
!(skip == 1 &&
av1_superblock_block_is_whole(
block.width,
block.height,
state.superblock_size,
)) {
av1_read_superblock_delta_q(decoder, state)
av1_read_superblock_delta_lf(decoder, state)
}
// The segment's ALT_Q builds on this superblock's index, so the block's
// quantizer index is settled after the delta, not before it.
av1_seg_apply_block(state, y4, x4, block.width / 4, block.height / 4, seg_id)
match state.intrabc {
Some(ibc) =>
match decoder.symbol(ibc.use_cdf) {
0 => ()
1 =>
return av1_intrabc_block(
decoder, state, ibc, block, block_index, skip,
)
_ => return false
}
None => ()
}
let above_mode = if y4 > 0 {
state.y_modes[(y4 - 1) * state.cols + x4]
} else {
0
}
let left_mode = if x4 > 0 {
state.y_modes[y4 * state.cols + x4 - 1]
} else {
0
}
let y_mode = decoder.symbol(
state.y_mode[av1_intra_mode_context(above_mode)][av1_intra_mode_context(
left_mode,
)],
)
if !av1_intra_read_mode_angle(decoder, state, y_mode, block_index) {
return false
}
// Non-owning 4-axis blocks and monochrome streams have no UV syntax.
// Their placeholder mode is unused because residuals process only luma.
let uv_mode = if !has_chroma {
AV1_INTRA_DC
} else {
// is_cfl_allowed differs for lossless: only a 4x4 chroma residual block
// may use CfL, even when the luma coding block is below 32 pixels.
let cfl_allowed = if state.seg_lossless {
block.width <= 4 << state.sub_x && block.height <= 4 << state.sub_y
} else {
block.width <= 32 && block.height <= 32
}
let mode = decoder.symbol(
if cfl_allowed {
state.uv_cfl[y_mode]
} else {
state.uv_no_cfl[y_mode]
},
)
if mode == AV1_INTRA_CFL {
if !cfl_allowed || !av1_cfl_read_alphas(decoder, state.cfl) {
return false
}
} else if !av1_intra_read_mode_angle(
decoder,
state,
mode,
block_index,
plane=1,
) {
return false
}
mode
}
if !av1_palette_read_colors(
decoder,
state.palette,
block,
state.bit_depth,
y_mode,
uv_mode,
has_chroma,
state.allow_screen_content_tools,
) {
return false
}
if state.enable_filter_intra &&
y_mode == AV1_INTRA_DC &&
state.palette.y_colors.is_empty() &&
block.width <= 32 &&
block.height <= 32 {
match decoder.symbol(state.filter_intra[block_index]) {
0 => ()
1 => {
let mode = decoder.symbol(state.filter_intra_mode_cdf)
if mode < 0 || mode >= 5 {
return false
}
state.filter_intra_mode = mode
}
_ => return false
}
}
if !av1_intra_palette_maps(decoder, state, block) {
return false
}
let root : Av1TxNode = {
row: block.row,
col: block.col,
width: block.width,
height: block.height,
}
let selected = if state.seg_lossless {
(4, 4)
} else if state.tx_mode_select {
let depth = match
av1_decode_tx_depth_for_block(
decoder,
state.tx_depth,
block.width,
block.height,
av1_tx_depth_context_for_node(state.tx_neighbors, root),
) {
Some(value) => value
None => return false
}
match av1_tx_size_from_depth(block.width, block.height, depth) {
Some(value) => value
None => return false
}
} else {
match av1_tx_size_from_depth(block.width, block.height, 0) {
Some(value) => value
None => return false
}
}
let (luma_tx_width, luma_tx_height) = selected
av1_intra_record_modes(state, block, y_mode, uv_mode, has_chroma, skip)
if !av1_intra_residuals(
decoder, state, block, y_mode, uv_mode, luma_tx_width, luma_tx_height, skip,
) {
return false
}
av1_tx_traversal_record_size(
state.tx_neighbors,
root,
luma_tx_width,
luma_tx_height,
)
match state.intrabc {
Some(ibc) =>
av1_intrabc_record(
ibc, block, false, 0, 0, skip, luma_tx_width, luma_tx_height,
)
None => ()
}
true
}
///|
fn av1_intra_crop(
plane : Av1IntraPlane,
width : Int,
height : Int,
) -> Array[Int] {
let output : Array[Int] = Array::make(width * height, 0)
for row in 0.. Av1IntraState? {
if width <= 0 ||
height <= 0 ||
width > MAX_IMAGE_PIXELS / height ||
payload.length() == 0 ||
payload.length() > 268435455 ||
base_q_idx < 0 ||
base_q_idx > 255 ||
(bit_depth != 8 && bit_depth != 10 && bit_depth != 12) {
return None
}
match cdef {
Some(config) =>
if av1_frame_coded_lossless(
base_q_idx,
quant_deltas,
segmentation.unwrap_or(av1_segmentation_empty()),
) ||
!av1_cdef_config_valid(config, monochrome~) {
return None
}
None => ()
}
let coded_width = av1_partition_coded_extent(width)
let coded_height = av1_partition_coded_extent(height)
if coded_width > MAX_IMAGE_PIXELS / coded_height {
return None
}
let state = av1_intra_state(
coded_width,
coded_height,
base_q_idx,
bit_depth,
reduced_tx_set,
allow_screen_content_tools,
enable_filter_intra,
tx_mode_select,
cdef~,
monochrome~,
enable_intra_edge_filter~,
superblock_size=if use_128x128_superblock { 128 } else { 64 },
track_loop_filter~,
quant_deltas~,
sub_x~,
sub_y~,
inter~,
segmentation~,
delta_q_present~,
delta_q_res~,
quant_matrix~,
allow_intrabc~,
frame_mi_cols~,
frame_mi_rows~,
tile_col=tile_origin_col_mi,
tile_row=tile_origin_row_mi,
previous_seg_ids~,
delta_lf_present~,
delta_lf_res~,
delta_lf_multi~,
)
let neighbors = match
av1_partition_neighbor_state(
{ row: 0, col: 0, width: coded_width, height: coded_height, },
width,
height,
) {
Some(value) => value
None => return None
}
let decoder = av1_msac_new(payload, !disable_cdf_update)
// Every adaptive table the tile owns starts from the frame context, so a
// frame that inherits one through `primary_ref_frame` decodes with the
// reference's adapted probabilities instead of the defaults.
av1_intra_cdfs_of(state, frame_cdfs)
// The partition trees and the loop-restoration selectors are tile-owned like
// the block tables: each starts from the inherited frame context.
let cdfs = av1_partition_cdfs_of(frame_cdfs)
let sb_size = if use_128x128_superblock { 128 } else { 64 }
// Per-tile loop-restoration entropy state: the mode CDF rows adapt
// across every superblock of this tile and the coefficient references
// survive SB rows. Reset once per tile, never per SB.
let lr_tile_state = match restoration_layout {
Some(layout) =>
Some(av1_restoration_tile_state_new(layout.config.modes.length()))
None => None
}
match lr_tile_state {
Some(lr_state) =>
match frame_cdfs {
Some(snapshot) => av1_lr_cdfs_restore(lr_state, snapshot)
None => ()
}
None => ()
}
let mut row = 0
while row < coded_height {
let mut col = 0
while col < coded_width {
// Loop-restoration unit parameters precede this root SB's first
// partition symbol on the shared tile entropy decoder (AV1 spec
// §5.11.57). The SB's global coded MI rectangle, including its
// full nominal size at the frame edge, owns the units whose
// top-left corners fall inside it.
match lr_tile_state {
Some(lr_state) =>
match restoration_layout {
Some(layout) =>
av1_restoration_read_sb(
decoder,
lr_state,
layout,
tile_origin_col_mi + col / 4,
tile_origin_row_mi + row / 4,
sb_size,
subsampling_x=!monochrome && sub_x == 1,
subsampling_y=!monochrome && sub_y == 1,
)
None => ()
}
None => ()
}
if !av1_walk_partition_tree_context(
decoder,
cdfs,
neighbors,
av1_partition_node(row, col, sb_size, width, height),
fn(reader, block, partition) {
av1_intra_block(reader, state, block, partition~)
},
) {
return None
}
col = col + sb_size
}
row = row + sb_size
}
match lr_tile_state {
Some(lr_state) =>
for record in lr_state.units {
state.restoration_units.push(record)
}
None => ()
}
// The selected context_update_tile_id tile writes its final rows
// into the frame context the next frame may inherit (AV1 §8.3).
match cdfs_out {
Some(sink) =>
sink.push(
Some(
av1_frame_cdfs_capture(
state,
cdfs,
lr_tile_state,
inherited=frame_cdfs,
),
),
)
None => ()
}
Some(state)
}
///|
/// Keep full-depth reconstructed planes until the final color conversion.
fn av1_decode_intra_tile(
width : Int,
height : Int,
payload : Array[Byte],
base_q_idx : Int,
disable_cdf_update : Bool,
allow_screen_content_tools : Bool,
full_range : Bool,
matrix_coefficients : Int,
use_128x128_superblock : Bool,
bit_depth : Int,
reduced_tx_set : Bool,
enable_filter_intra : Bool,
tx_mode_select : Bool,
cdef? : Av1CdefConfig? = None,
enable_intra_edge_filter? : Bool = false,
sub_x? : Int = 1,
sub_y? : Int = 1,
) -> Image? {
let state = match
av1_decode_intra_tile_state(
width,
height,
payload,
base_q_idx,
disable_cdf_update,
allow_screen_content_tools,
use_128x128_superblock,
bit_depth,
reduced_tx_set,
enable_filter_intra,
tx_mode_select,
cdef~,
enable_intra_edge_filter~,
sub_x~,
sub_y~,
) {
Some(value) => value
None => return None
}
match cdef {
Some(config) => {
let filtered = match
av1_cdef_filter_frame(
state.planes[0].width,
state.planes[0].height,
[
state.planes[0].pixels,
state.planes[1].pixels,
state.planes[2].pixels,
],
state.skips,
state.cdef_indices,
config,
bit_depth,
sub_x~,
sub_y~,
) {
Some(value) => value
None => return None
}
for plane in 0..<3 {
for i in 0.. ()
}
let chroma_w = (width + (1 << sub_x) - 1) >> sub_x
let chroma_h = (height + (1 << sub_y) - 1) >> sub_y
av1_yuv_highbd_to_rgba(
width,
height,
av1_intra_crop(state.planes[0], width, height),
av1_intra_crop(state.planes[1], chroma_w, chroma_h),
av1_intra_crop(state.planes[2], chroma_w, chroma_h),
bit_depth,
subsampling_x=sub_x == 1,
subsampling_y=sub_y == 1,
full_range~,
matrix_coefficients~,
)
}