/// Masked-compound wedge masks (AV1 §7.11.3.11-14).
///
/// A wedge mask is a `64 x 64` blend-weight table cut down to the block's own
/// size along one of six directions, optionally mirrored. The table is built
/// from three one-dimensional master columns: the oblique-63 column is sampled
/// with a per-row shift, and the other directions are its transposes and
/// complements (go-av1 `decode/wedge.go`, `initWedgeMasks`).
///|
/// Direction indices in the wedge codebook.
const AV1_WEDGE_HORIZONTAL : Int = 0
///|
const AV1_WEDGE_VERTICAL : Int = 1
///|
const AV1_WEDGE_OBLIQUE27 : Int = 2
///|
const AV1_WEDGE_OBLIQUE63 : Int = 3
///|
const AV1_WEDGE_OBLIQUE117 : Int = 4
///|
const AV1_WEDGE_OBLIQUE153 : Int = 5
///|
/// `Wedge_Master_Oblique_Odd` (go-av1 `decode/wedge.go`).
let av1_wedge_master_oblique_odd : Array[Int] = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 2, 6, 18, 37, 53, 60, 63, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
]
///|
/// `Wedge_Master_Oblique_Even`.
let av1_wedge_master_oblique_even : Array[Int] = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 4, 11, 27, 46, 58, 62, 63, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
]
///|
/// `Wedge_Master_Vertical`.
let av1_wedge_master_vertical : Array[Int] = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 2, 7, 21, 43, 57, 62, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
]
///|
/// `Wedge_Codebook[3][16][3]` = { direction, xoff, yoff } per block shape
/// (0 tall, 1 wide, 2 square) and wedge index.
let av1_wedge_codebook : Array[Array[Array[Int]]] = [
[
[AV1_WEDGE_OBLIQUE27, 4, 4],
[AV1_WEDGE_OBLIQUE63, 4, 4],
[AV1_WEDGE_OBLIQUE117, 4, 4],
[AV1_WEDGE_OBLIQUE153, 4, 4],
[AV1_WEDGE_HORIZONTAL, 4, 2],
[AV1_WEDGE_HORIZONTAL, 4, 4],
[AV1_WEDGE_HORIZONTAL, 4, 6],
[AV1_WEDGE_VERTICAL, 4, 4],
[AV1_WEDGE_OBLIQUE27, 4, 2],
[AV1_WEDGE_OBLIQUE27, 4, 6],
[AV1_WEDGE_OBLIQUE153, 4, 2],
[AV1_WEDGE_OBLIQUE153, 4, 6],
[AV1_WEDGE_OBLIQUE63, 2, 4],
[AV1_WEDGE_OBLIQUE63, 6, 4],
[AV1_WEDGE_OBLIQUE117, 2, 4],
[AV1_WEDGE_OBLIQUE117, 6, 4],
],
[
[AV1_WEDGE_OBLIQUE27, 4, 4],
[AV1_WEDGE_OBLIQUE63, 4, 4],
[AV1_WEDGE_OBLIQUE117, 4, 4],
[AV1_WEDGE_OBLIQUE153, 4, 4],
[AV1_WEDGE_VERTICAL, 2, 4],
[AV1_WEDGE_VERTICAL, 4, 4],
[AV1_WEDGE_VERTICAL, 6, 4],
[AV1_WEDGE_HORIZONTAL, 4, 4],
[AV1_WEDGE_OBLIQUE27, 4, 2],
[AV1_WEDGE_OBLIQUE27, 4, 6],
[AV1_WEDGE_OBLIQUE153, 4, 2],
[AV1_WEDGE_OBLIQUE153, 4, 6],
[AV1_WEDGE_OBLIQUE63, 2, 4],
[AV1_WEDGE_OBLIQUE63, 6, 4],
[AV1_WEDGE_OBLIQUE117, 2, 4],
[AV1_WEDGE_OBLIQUE117, 6, 4],
],
[
[AV1_WEDGE_OBLIQUE27, 4, 4],
[AV1_WEDGE_OBLIQUE63, 4, 4],
[AV1_WEDGE_OBLIQUE117, 4, 4],
[AV1_WEDGE_OBLIQUE153, 4, 4],
[AV1_WEDGE_HORIZONTAL, 4, 2],
[AV1_WEDGE_HORIZONTAL, 4, 6],
[AV1_WEDGE_VERTICAL, 2, 4],
[AV1_WEDGE_VERTICAL, 6, 4],
[AV1_WEDGE_OBLIQUE27, 4, 2],
[AV1_WEDGE_OBLIQUE27, 4, 6],
[AV1_WEDGE_OBLIQUE153, 4, 2],
[AV1_WEDGE_OBLIQUE153, 4, 6],
[AV1_WEDGE_OBLIQUE63, 2, 4],
[AV1_WEDGE_OBLIQUE63, 6, 4],
[AV1_WEDGE_OBLIQUE117, 2, 4],
[AV1_WEDGE_OBLIQUE117, 6, 4],
],
]
///|
/// `Wedge_Bits[BLOCK_SIZES]`: the number of wedge index bits per block size
/// (zero means this size cannot code a wedge at all). The nine sizes that can
/// are the ones dav1d fills: 8x8..32x32 in the pyramid, plus 8x32 and 32x8.
let av1_wedge_bits : Array[Int] = [
0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 0,
]
///|
/// Whether a block size can code a wedge.
pub fn av1_wedge_shape_ok(width : Int, height : Int) -> Bool {
match av1_intra_block_index(width, height) {
Some(index) => index < av1_wedge_bits.length() && av1_wedge_bits[index] != 0
None => false
}
}
///|
/// The six `64 x 64` master tables, built per call (the construction is a few
/// tens of thousands of writes, and a frame has only a handful of wedge
/// blocks).
fn av1_wedge_master() -> Array[Array[Array[Int]]] {
let master : Array[Array[Array[Int]]] = Array::make(6, [])
for d in 0..<6 {
master[d] = Array::makei(64, _ => Array::make(64, 0))
}
for j in 0..<64 {
let mut shift = 16
let mut i = 0
while i < 64 {
let even_index = if j - shift < 0 {
0
} else if j - shift > 63 {
63
} else {
j - shift
}
master[AV1_WEDGE_OBLIQUE63][i][j] = av1_wedge_master_oblique_even[even_index]
shift = shift - 1
let odd_index = if j - shift < 0 {
0
} else if j - shift > 63 {
63
} else {
j - shift
}
master[AV1_WEDGE_OBLIQUE63][i + 1][j] = av1_wedge_master_oblique_odd[odd_index]
master[AV1_WEDGE_VERTICAL][i][j] = av1_wedge_master_vertical[j]
master[AV1_WEDGE_VERTICAL][i + 1][j] = av1_wedge_master_vertical[j]
i = i + 2
}
}
for i in 0..<64 {
for j in 0..<64 {
let mask = master[AV1_WEDGE_OBLIQUE63][i][j]
master[AV1_WEDGE_OBLIQUE27][j][i] = mask
master[AV1_WEDGE_OBLIQUE117][i][63 - j] = 64 - mask
master[AV1_WEDGE_OBLIQUE153][63 - j][i] = 64 - mask
master[AV1_WEDGE_HORIZONTAL][j][i] = master[AV1_WEDGE_VERTICAL][i][j]
}
}
master
}
///|
/// The `width x height` wedge mask for one block, indexed `[y][x]`, or None
/// when the block size cannot code a wedge. The sign chooses each of the two
/// mirrored variants: the mask whose average is at least 32, or its
/// complement (AV1 §7.11.3.11). Valid indices are 0 through 15; invalid
/// indices also return None.
pub fn av1_wedge_mask(
width : Int,
height : Int,
index : Int,
sign : Int,
) -> Array[Int]? {
if !av1_wedge_shape_ok(width, height) {
return None
}
if index < 0 || index >= 16 {
return None
}
let master = av1_wedge_master()
let shape = if height > width { 0 } else if height < width { 1 } else { 2 }
let entry = av1_wedge_codebook[shape][index]
let direction = entry[0]
let xoff = 32 - ((entry[1] * width) >> 3)
let yoff = 32 - ((entry[2] * height) >> 3)
if xoff < 0 || yoff < 0 || xoff + width > 64 || yoff + height > 64 {
return None
}
let mut sum = 0
for i in 0.. Array[Int]? {
let luma = match av1_wedge_mask(width, height, index, sign) {
Some(value) => value
None => return None
}
Some(av1_reduce_plane_mask(luma, width, height, sub_x, sub_y))
}
///|
/// The difference-weight mask of AV1 §7.11.3.12: one weight per luma sample
/// derived from how far apart the two predictions are there, `38 + diff/16`
/// clipped to 64, complemented when `mask_type` selects the mirrored variant.
/// The chroma planes take the luma mask averaged down to their sample grid by
/// §7.11.3.14, so this is always built at luma size and reduced here - the two
/// predictions handed in are this plane's, but only luma's reach the weight.
fn av1_diffwtd_mask(
first : Array[Int],
second : Array[Int],
width : Int,
height : Int,
sub_x : Int,
sub_y : Int,
mask_type : Int,
bit_depth : Int,
) -> Array[Int] {
// The two predictions arrive as filter intermediates: for an 8-bit stream
// that is the sample scaled by 16 (`interPostRound` is 4), for a 12-bit one
// by 4. The mask's own rounding is the reference decoder's
// `38 + ((|diff| + rnd) >> sh)` over those intermediates, which on the
// sample scale is `38 + diff / 16` at 8 bits.
let shift = bit_depth + (if bit_depth == 12 { 2 } else { 4 }) - 4
let rnd = 1 << (shift - 5)
let luma = Array::make(width * height, 0)
for k in 0.. second[k] {
first[k] - second[k]
} else {
second[k] - first[k]
}
let m = 38 + ((diff + rnd) >> shift)
let clipped = if m > 64 { 64 } else { m }
luma[k] = if mask_type == 0 { clipped } else { 64 - clipped }
}
if sub_x != 1 {
return luma
}
av1_reduce_plane_mask(luma, width, height, sub_x, sub_y)
}
///|
///|
/// Average a luma-sized mask down to one plane's sample grid, the reduction
/// §7.11.3.14 applies to every mask the luma plane built: two columns for 4:2:2,
/// a 2x2 group for 4:2:0, each rounded the way the mask blend process rounds.
fn av1_reduce_plane_mask(
luma : Array[Int],
width : Int,
height : Int,
sub_x : Int,
sub_y : Int,
) -> Array[Int] {
if sub_x != 1 {
return luma
}
let out_w = width >> sub_x
let out_h = height >> sub_y
let out = Array::make(out_w * out_h, 0)
for i in 0..> 2
} else {
(sum + 1) >> 1
}
}
}
out
}