/// AV1 CfL follows libaom cfl.c/cfl.h and the normative probability defaults
/// at revision 8e7b6a567df174d795479b92b4ac766d271add73.
/// Copyright (c) 2016, Alliance for Open Media. All rights reserved.
/// BSD 2 Clause License and Alliance for Open Media Patent License 1.0:
/// https://www.aomedia.org/license/software and https://www.aomedia.org/license/patent
///|
const AV1_INTRA_CFL : Int = 13
///|
/// The alpha probabilities are tile-local. U and V intentionally share the
/// same six adaptive rows. Reconstructed luma stays in Q3 until the complete
/// chroma prediction footprint has been padded and its rounded mean removed.
priv struct Av1CflState {
sign : Array[Int]
alpha : Array[Array[Int]]
mut alpha_u : Int
mut alpha_v : Int
mut store_y : Bool
mut base_x : Int
mut base_y : Int
recon_q3 : Array[Int]
ac_q3 : Array[Int]
mut buf_width : Int
mut buf_height : Int
mut ac_width : Int
mut ac_height : Int
mut ac_ready : Bool
/// Chroma subsampling shifts and the resulting chroma buffer stride.
sub_x : Int
sub_y : Int
stride : Int
}
///|
fn av1_cfl_state(sub_x? : Int = 1, sub_y? : Int = 1) -> Av1CflState {
let stride = 64 >> sub_x
{
sign: [1418, 2123, 13340, 18405, 26972, 28343, 32294, 32768, 0],
alpha: [
[
7637, 20719, 31401, 32481, 32657, 32688, 32692, 32696, 32700, 32704, 32708,
32712, 32716, 32720, 32724, 32768, 0,
],
[
14365, 23603, 28135, 31168, 32167, 32395, 32487, 32573, 32620, 32647, 32668,
32672, 32676, 32680, 32684, 32768, 0,
],
[
11532, 22380, 28445, 31360, 32349, 32523, 32584, 32649, 32673, 32677, 32681,
32685, 32689, 32693, 32697, 32768, 0,
],
[
26990, 31402, 32282, 32571, 32692, 32696, 32700, 32704, 32708, 32712, 32716,
32720, 32724, 32728, 32732, 32768, 0,
],
[
17248, 26058, 28904, 30608, 31305, 31877, 32126, 32321, 32394, 32464, 32516,
32560, 32576, 32593, 32622, 32768, 0,
],
[
14738, 21678, 25779, 27901, 29024, 30302, 30980, 31843, 32144, 32413, 32520,
32594, 32622, 32656, 32660, 32768, 0,
],
],
alpha_u: 0,
alpha_v: 0,
store_y: false,
base_x: 0,
base_y: 0,
recon_q3: Array::make(stride * (64 >> sub_y), 0),
ac_q3: Array::make(stride * (64 >> sub_y), 0),
buf_width: 0,
buf_height: 0,
ac_width: 0,
ac_height: 0,
ac_ready: false,
sub_x,
sub_y,
stride,
}
}
///|
/// joint_sign enumerates every U/V zero/negative/positive pair except (0,0).
/// Read U before V: for joint signs 3 and 7 both magnitudes use one CDF row.
fn av1_cfl_read_alphas(decoder : Av1SymbolDecoder, cfl : Av1CflState) -> Bool {
let joint = decoder.symbol(cfl.sign)
if joint < 0 || joint > 7 {
return false
}
let sign_u = (joint + 1) / 3
let sign_v = (joint + 1) % 3
cfl.alpha_u = 0
cfl.alpha_v = 0
if sign_u != 0 {
let magnitude = decoder.symbol(cfl.alpha[joint - 2]) + 1
cfl.alpha_u = if sign_u == 1 { -magnitude } else { magnitude }
}
if sign_v != 0 {
let magnitude = decoder.symbol(cfl.alpha[3 * sign_v + sign_u - 3]) + 1
cfl.alpha_v = if sign_v == 1 { -magnitude } else { magnitude }
}
true
}
///|
/// Sub-eight-pixel coding blocks share a buffer rooted at their enclosing
/// 8-pixel luma group. Do not clear it here: preceding non-owner blocks have
/// already stored the luma that the eventual chroma owner needs.
fn av1_cfl_begin_block(
cfl : Av1CflState,
block : Av1SbBlock,
store_y : Bool,
) -> Unit {
cfl.store_y = store_y
// A subsampled axis merges sub-eight-pixel luma blocks into their eight-pixel
// group, so the shared buffer is rooted there; an unsampled axis is 1:1.
cfl.base_x = if cfl.sub_x == 1 && block.width < 8 {
block.col / 8 * 8
} else {
block.col
}
cfl.base_y = if cfl.sub_y == 1 && block.height < 8 {
block.row / 8 * 8
} else {
block.row
}
}
///|
/// Store the complete clipped reconstruction of one luma TX, including its
/// samples beyond the frame MI rectangle. The downsampled luma keeps three
/// fractional bits (Q3): the subsampling window sum is scaled by 8 / n, so
/// 4:2:0 stores 2 * (Y00 + Y01 + Y10 + Y11), 4:2:2 and 4:4:0 store four
/// times the pair sum, and 4:4:4 stores eight times the single sample.
fn av1_cfl_store_tx(
cfl : Av1CflState,
x : Int,
y : Int,
width : Int,
height : Int,
reconstruction : Array[Int],
) -> Bool {
let col = (x - cfl.base_x) >> cfl.sub_x
let row = (y - cfl.base_y) >> cfl.sub_y
let store_width = width >> cfl.sub_x
let store_height = height >> cfl.sub_y
if x < cfl.base_x ||
y < cfl.base_y ||
width < 4 ||
height < 4 ||
width > 32 ||
height > 32 ||
width % 2 != 0 ||
height % 2 != 0 ||
reconstruction.length() != width * height ||
col + store_width > cfl.stride ||
row + store_height > 64 >> cfl.sub_y {
return false
}
if col == 0 && row == 0 {
cfl.buf_width = store_width
cfl.buf_height = store_height
} else {
if col + store_width > cfl.buf_width {
cfl.buf_width = col + store_width
}
if row + store_height > cfl.buf_height {
cfl.buf_height = row + store_height
}
}
cfl.ac_ready = false
// 8 / (2^sub_x * 2^sub_y) keeps the mean in Q3 for every subsampling.
let precision = 8 >> (cfl.sub_x + cfl.sub_y)
for j in 0.. Bool {
if width < 4 ||
height < 4 ||
width > 64 >> cfl.sub_x ||
height > 64 >> cfl.sub_y ||
(width & (width - 1)) != 0 ||
(height & (height - 1)) != 0 ||
cfl.buf_width <= 0 ||
cfl.buf_height <= 0 {
return false
}
if cfl.ac_ready && cfl.ac_width == width && cfl.ac_height == height {
return true
}
if cfl.buf_width < width {
for row in 0.. Int {
let product = alpha_q3 * ac_q3
if product < 0 {
-((-product + 32) >> 6)
} else {
(product + 32) >> 6
}
}
///|
/// CfL adjusts an ordinary DC prediction. Clip this prediction before the
/// caller adds its chroma residual, which has a separate reconstruction clip.
fn av1_cfl_predict(
cfl : Av1CflState,
prediction : Array[Int],
width : Int,
height : Int,
alpha_q3 : Int,
bit_depth : Int,
) -> Bool {
if prediction.length() != width * height ||
alpha_q3 < -16 ||
alpha_q3 > 16 ||
(bit_depth != 8 && bit_depth != 10 && bit_depth != 12) ||
!av1_cfl_prepare(cfl, width, height) {
return false
}
for row in 0..