/// The warped-motion least-squares fit and the shear/affine conversion, plus
/// the 1/64-pel warp filter table, transcribed from libdav1d (`warpmv.c` and
/// `tables.c`) and AV1 7.11.3.5 / 7.11.3.8.
///
/// A block that selects LOCALWARP estimates a warp model from the motion
/// vectors of the already-decoded neighbours above and to the left of it, and
/// predicts each sample through that model rather than through a single
/// motion vector. Both halves live here: the fit (`av1_warp_find_affine_int`,
/// `av1_warp_shear_params`) and the model the prediction half consumes
/// (`matrix` with its four `abcd` terms).
///|
/// `div_lut` from libdav1d: a precomputed reciprocal per unit of the divisor's
/// remainder, so the 64x64 least-squares solve needs no division.
let av1_warp_div_lut : Array[Int] = [
16384, 16320, 16257, 16194, 16132, 16070, 16009, 15948, 15888, 15828, 15768, 15709,
15650, 15592, 15534, 15477, 15420, 15364, 15308, 15252, 15197, 15142, 15087, 15033,
14980, 14926, 14873, 14821, 14769, 14717, 14665, 14614, 14564, 14513, 14463, 14413,
14364, 14315, 14266, 14218, 14170, 14122, 14075, 14028, 13981, 13935, 13888, 13843,
13797, 13752, 13707, 13662, 13618, 13574, 13530, 13487, 13443, 13400, 13358, 13315,
13273, 13231, 13190, 13148, 13107, 13066, 13026, 12985, 12945, 12906, 12866, 12827,
12788, 12749, 12710, 12672, 12633, 12596, 12558, 12520, 12483, 12446, 12409, 12373,
12336, 12300, 12264, 12228, 12193, 12157, 12122, 12087, 12053, 12018, 11984, 11950,
11916, 11882, 11848, 11815, 11782, 11749, 11716, 11683, 11651, 11619, 11586, 11555,
11523, 11491, 11460, 11429, 11398, 11367, 11336, 11305, 11275, 11245, 11215, 11185,
11155, 11125, 11096, 11067, 11038, 11009, 10980, 10951, 10923, 10894, 10866, 10838,
10810, 10782, 10755, 10727, 10700, 10673, 10645, 10618, 10592, 10565, 10538, 10512,
10486, 10460, 10434, 10408, 10382, 10356, 10331, 10305, 10280, 10255, 10230, 10205,
10180, 10156, 10131, 10107, 10082, 10058, 10034, 10010, 9986, 9963, 9939, 9916,
9892, 9869, 9846, 9823, 9800, 9777, 9754, 9732, 9709, 9687, 9664, 9642, 9620, 9598,
9576, 9554, 9533, 9511, 9489, 9468, 9447, 9425, 9404, 9383, 9362, 9341, 9321, 9300,
9279, 9259, 9239, 9218, 9198, 9178, 9158, 9138, 9118, 9098, 9079, 9059, 9039, 9020,
9001, 8981, 8962, 8943, 8924, 8905, 8886, 8867, 8849, 8830, 8812, 8793, 8775, 8756,
8738, 8720, 8702, 8684, 8666, 8648, 8630, 8613, 8595, 8577, 8560, 8542, 8525, 8508,
8490, 8473, 8456, 8439, 8422, 8405, 8389, 8372, 8355, 8339, 8322, 8306, 8289, 8273,
8257, 8240, 8224, 8208, 8192,
]
///|
/// `dav1d_mc_warp_filter`: eight taps per 1/64-pel phase, for the range
/// [-1, 1.5). Only the [0, 1) rows are reachable from a clamped phase.
let av1_warp_filters : Array[Array[Int]] = [
[0, 0, 127, 1, 0, 0, 0, 0],
[0, -1, 127, 2, 0, 0, 0, 0],
[1, -3, 127, 4, -1, 0, 0, 0],
[1, -4, 126, 6, -2, 1, 0, 0],
[1, -5, 126, 8, -3, 1, 0, 0],
[1, -6, 125, 11, -4, 1, 0, 0],
[1, -7, 124, 13, -4, 1, 0, 0],
[2, -8, 123, 15, -5, 1, 0, 0],
[2, -9, 122, 18, -6, 1, 0, 0],
[2, -10, 121, 20, -6, 1, 0, 0],
[2, -11, 120, 22, -7, 2, 0, 0],
[2, -12, 119, 25, -8, 2, 0, 0],
[3, -13, 117, 27, -8, 2, 0, 0],
[3, -13, 116, 29, -9, 2, 0, 0],
[3, -14, 114, 32, -10, 3, 0, 0],
[3, -15, 113, 35, -10, 2, 0, 0],
[3, -15, 111, 37, -11, 3, 0, 0],
[3, -16, 109, 40, -11, 3, 0, 0],
[3, -16, 108, 42, -12, 3, 0, 0],
[4, -17, 106, 45, -13, 3, 0, 0],
[4, -17, 104, 47, -13, 3, 0, 0],
[4, -17, 102, 50, -14, 3, 0, 0],
[4, -17, 100, 52, -14, 3, 0, 0],
[4, -18, 98, 55, -15, 4, 0, 0],
[4, -18, 96, 58, -15, 3, 0, 0],
[4, -18, 94, 60, -16, 4, 0, 0],
[4, -18, 91, 63, -16, 4, 0, 0],
[4, -18, 89, 65, -16, 4, 0, 0],
[4, -18, 87, 68, -17, 4, 0, 0],
[4, -18, 85, 70, -17, 4, 0, 0],
[4, -18, 82, 73, -17, 4, 0, 0],
[4, -18, 80, 75, -17, 4, 0, 0],
[4, -18, 78, 78, -18, 4, 0, 0],
[4, -17, 75, 80, -18, 4, 0, 0],
[4, -17, 73, 82, -18, 4, 0, 0],
[4, -17, 70, 85, -18, 4, 0, 0],
[4, -17, 68, 87, -18, 4, 0, 0],
[4, -16, 65, 89, -18, 4, 0, 0],
[4, -16, 63, 91, -18, 4, 0, 0],
[4, -16, 60, 94, -18, 4, 0, 0],
[3, -15, 58, 96, -18, 4, 0, 0],
[4, -15, 55, 98, -18, 4, 0, 0],
[3, -14, 52, 100, -17, 4, 0, 0],
[3, -14, 50, 102, -17, 4, 0, 0],
[3, -13, 47, 104, -17, 4, 0, 0],
[3, -13, 45, 106, -17, 4, 0, 0],
[3, -12, 42, 108, -16, 3, 0, 0],
[3, -11, 40, 109, -16, 3, 0, 0],
[3, -11, 37, 111, -15, 3, 0, 0],
[2, -10, 35, 113, -15, 3, 0, 0],
[3, -10, 32, 114, -14, 3, 0, 0],
[2, -9, 29, 116, -13, 3, 0, 0],
[2, -8, 27, 117, -13, 3, 0, 0],
[2, -8, 25, 119, -12, 2, 0, 0],
[2, -7, 22, 120, -11, 2, 0, 0],
[1, -6, 20, 121, -10, 2, 0, 0],
[1, -6, 18, 122, -9, 2, 0, 0],
[1, -5, 15, 123, -8, 2, 0, 0],
[1, -4, 13, 124, -7, 1, 0, 0],
[1, -4, 11, 125, -6, 1, 0, 0],
[1, -3, 8, 126, -5, 1, 0, 0],
[1, -2, 6, 126, -4, 1, 0, 0],
[0, -1, 4, 127, -3, 1, 0, 0],
[0, 0, 2, 127, -1, 0, 0, 0],
[0, 0, 0, 127, 1, 0, 0, 0],
[0, 0, -1, 127, 2, 0, 0, 0],
[0, 1, -3, 127, 4, -2, 1, 0],
[0, 1, -5, 127, 6, -2, 1, 0],
[0, 2, -6, 126, 8, -3, 1, 0],
[-1, 2, -7, 126, 11, -4, 2, -1],
[-1, 3, -8, 125, 13, -5, 2, -1],
[-1, 3, -10, 124, 16, -6, 3, -1],
[-1, 4, -11, 123, 18, -7, 3, -1],
[-1, 4, -12, 122, 20, -7, 3, -1],
[-1, 4, -13, 121, 23, -8, 3, -1],
[-2, 5, -14, 120, 25, -9, 4, -1],
[-1, 5, -15, 119, 27, -10, 4, -1],
[-1, 5, -16, 118, 30, -11, 4, -1],
[-2, 6, -17, 116, 33, -12, 5, -1],
[-2, 6, -17, 114, 35, -12, 5, -1],
[-2, 6, -18, 113, 38, -13, 5, -1],
[-2, 7, -19, 111, 41, -14, 6, -2],
[-2, 7, -19, 110, 43, -15, 6, -2],
[-2, 7, -20, 108, 46, -15, 6, -2],
[-2, 7, -20, 106, 49, -16, 6, -2],
[-2, 7, -21, 104, 51, -16, 7, -2],
[-2, 7, -21, 102, 54, -17, 7, -2],
[-2, 8, -21, 100, 56, -18, 7, -2],
[-2, 8, -22, 98, 59, -18, 7, -2],
[-2, 8, -22, 96, 62, -19, 7, -2],
[-2, 8, -22, 94, 64, -19, 7, -2],
[-2, 8, -22, 91, 67, -20, 8, -2],
[-2, 8, -22, 89, 69, -20, 8, -2],
[-2, 8, -22, 87, 72, -21, 8, -2],
[-2, 8, -21, 84, 74, -21, 8, -2],
[-2, 8, -22, 82, 77, -21, 8, -2],
[-2, 8, -21, 79, 79, -21, 8, -2],
[-2, 8, -21, 77, 82, -22, 8, -2],
[-2, 8, -21, 74, 84, -21, 8, -2],
[-2, 8, -21, 72, 87, -22, 8, -2],
[-2, 8, -20, 69, 89, -22, 8, -2],
[-2, 8, -20, 67, 91, -22, 8, -2],
[-2, 7, -19, 64, 94, -22, 8, -2],
[-2, 7, -19, 62, 96, -22, 8, -2],
[-2, 7, -18, 59, 98, -22, 8, -2],
[-2, 7, -18, 56, 100, -21, 8, -2],
[-2, 7, -17, 54, 102, -21, 7, -2],
[-2, 7, -16, 51, 104, -21, 7, -2],
[-2, 6, -16, 49, 106, -20, 7, -2],
[-2, 6, -15, 46, 108, -20, 7, -2],
[-2, 6, -15, 43, 110, -19, 7, -2],
[-2, 6, -14, 41, 111, -19, 7, -2],
[-1, 5, -13, 38, 113, -18, 6, -2],
[-1, 5, -12, 35, 114, -17, 6, -2],
[-1, 5, -12, 33, 116, -17, 6, -2],
[-1, 4, -11, 30, 118, -16, 5, -1],
[-1, 4, -10, 27, 119, -15, 5, -1],
[-1, 4, -9, 25, 120, -14, 5, -2],
[-1, 3, -8, 23, 121, -13, 4, -1],
[-1, 3, -7, 20, 122, -12, 4, -1],
[-1, 3, -7, 18, 123, -11, 4, -1],
[-1, 3, -6, 16, 124, -10, 3, -1],
[-1, 2, -5, 13, 125, -8, 3, -1],
[-1, 2, -4, 11, 126, -7, 2, -1],
[0, 1, -3, 8, 126, -6, 2, 0],
[0, 1, -2, 6, 127, -5, 1, 0],
[0, 1, -2, 4, 127, -3, 1, 0],
[0, 0, 0, 2, 127, -1, 0, 0],
[0, 0, 0, 1, 127, 0, 0, 0],
[0, 0, 0, -1, 127, 2, 0, 0],
[0, 0, 1, -3, 127, 4, -1, 0],
[0, 0, 1, -4, 126, 6, -2, 1],
[0, 0, 1, -5, 126, 8, -3, 1],
[0, 0, 1, -6, 125, 11, -4, 1],
[0, 0, 1, -7, 124, 13, -4, 1],
[0, 0, 2, -8, 123, 15, -5, 1],
[0, 0, 2, -9, 122, 18, -6, 1],
[0, 0, 2, -10, 121, 20, -6, 1],
[0, 0, 2, -11, 120, 22, -7, 2],
[0, 0, 2, -12, 119, 25, -8, 2],
[0, 0, 3, -13, 117, 27, -8, 2],
[0, 0, 3, -13, 116, 29, -9, 2],
[0, 0, 3, -14, 114, 32, -10, 3],
[0, 0, 3, -15, 113, 35, -10, 2],
[0, 0, 3, -15, 111, 37, -11, 3],
[0, 0, 3, -16, 109, 40, -11, 3],
[0, 0, 3, -16, 108, 42, -12, 3],
[0, 0, 4, -17, 106, 45, -13, 3],
[0, 0, 4, -17, 104, 47, -13, 3],
[0, 0, 4, -17, 102, 50, -14, 3],
[0, 0, 4, -17, 100, 52, -14, 3],
[0, 0, 4, -18, 98, 55, -15, 4],
[0, 0, 4, -18, 96, 58, -15, 3],
[0, 0, 4, -18, 94, 60, -16, 4],
[0, 0, 4, -18, 91, 63, -16, 4],
[0, 0, 4, -18, 89, 65, -16, 4],
[0, 0, 4, -18, 87, 68, -17, 4],
[0, 0, 4, -18, 85, 70, -17, 4],
[0, 0, 4, -18, 82, 73, -17, 4],
[0, 0, 4, -18, 80, 75, -17, 4],
[0, 0, 4, -18, 78, 78, -18, 4],
[0, 0, 4, -17, 75, 80, -18, 4],
[0, 0, 4, -17, 73, 82, -18, 4],
[0, 0, 4, -17, 70, 85, -18, 4],
[0, 0, 4, -17, 68, 87, -18, 4],
[0, 0, 4, -16, 65, 89, -18, 4],
[0, 0, 4, -16, 63, 91, -18, 4],
[0, 0, 4, -16, 60, 94, -18, 4],
[0, 0, 3, -15, 58, 96, -18, 4],
[0, 0, 4, -15, 55, 98, -18, 4],
[0, 0, 3, -14, 52, 100, -17, 4],
[0, 0, 3, -14, 50, 102, -17, 4],
[0, 0, 3, -13, 47, 104, -17, 4],
[0, 0, 3, -13, 45, 106, -17, 4],
[0, 0, 3, -12, 42, 108, -16, 3],
[0, 0, 3, -11, 40, 109, -16, 3],
[0, 0, 3, -11, 37, 111, -15, 3],
[0, 0, 2, -10, 35, 113, -15, 3],
[0, 0, 3, -10, 32, 114, -14, 3],
[0, 0, 2, -9, 29, 116, -13, 3],
[0, 0, 2, -8, 27, 117, -13, 3],
[0, 0, 2, -8, 25, 119, -12, 2],
[0, 0, 2, -7, 22, 120, -11, 2],
[0, 0, 1, -6, 20, 121, -10, 2],
[0, 0, 1, -6, 18, 122, -9, 2],
[0, 0, 1, -5, 15, 123, -8, 2],
[0, 0, 1, -4, 13, 124, -7, 1],
[0, 0, 1, -4, 11, 125, -6, 1],
[0, 0, 1, -3, 8, 126, -5, 1],
[0, 0, 1, -2, 6, 126, -4, 1],
[0, 0, 0, -1, 4, 127, -3, 1],
[0, 0, 0, 0, 2, 127, -1, 0],
[0, 0, 0, 0, 2, 127, -1, 0],
]
///|
/// `apply_sign` from libdav1d: the magnitude with the sign of `value`.
fn av1_warp_apply_sign(value : Int, sign : Int) -> Int {
if sign < 0 {
-value
} else {
value
}
}
///|
/// `iclip(v, INT16_MIN, INT16_MAX)` then a sign-preserving rounding to the
/// model's 6-bit coefficient grid.
fn av1_warp_iclip_wmp(v : Int) -> Int {
let clipped = if v < -32768 { -32768 } else if v > 32767 { 32767 } else { v }
let magnitude = if clipped < 0 { -clipped } else { clipped }
av1_warp_apply_sign((magnitude + 32) >> 6, clipped) * (1 << 6)
}
///|
/// `resolve_divisor_32`: the reciprocal multiplier and shift for a 32-bit
/// divisor that is not a power of two.
fn av1_warp_resolve_divisor_32(d : Int) -> (Int, Int) {
let mut shift = 0
let mut x = d
while x > 1 {
shift = shift + 1
x = x >> 1
}
let e = d - (1 << shift)
let f = if shift > 8 {
(e + (1 << (shift - 9))) >> (shift - 8)
} else {
e << (8 - shift)
}
(av1_warp_div_lut[f], shift + 14)
}
///|
/// `resolve_divisor_64`: the same for the 64-bit determinant. The determinant
/// of the normal equations exceeds 32 bits for an eight-sample fit, so the
/// divisor arrives as an Int64 and the products stay in Int64.
fn av1_warp_resolve_divisor_64(d : Int64) -> (Int, Int) {
let mut shift = 0
let mut x = d
while x > 1L {
shift = shift + 1
x = x >> 1
}
let e = d - (1L << shift)
let f = if shift > 8 {
((e + (1L << (shift - 9))) >> (shift - 8)).to_int()
} else {
(e << (8 - shift)).to_int()
}
(av1_warp_div_lut[f], shift + 14)
}
///|
/// `dav1d_get_shear_params`: fill the four prediction coefficients from a
/// fitted matrix, and report whether the model is a pure shear that the
/// prediction cannot represent. A shear is decoded as identity.
fn av1_warp_shear_params(
matrix : Array[Int],
coefficients : Array[Int],
) -> Bool {
if matrix[2] <= 0 {
return true
}
coefficients[0] = av1_warp_iclip_wmp(matrix[2] - 0x10000)
coefficients[1] = av1_warp_iclip_wmp(matrix[3])
let (y, shift) = av1_warp_resolve_divisor_32(
if matrix[2] < 0 {
-matrix[2]
} else {
matrix[2]
},
)
let y = av1_warp_apply_sign(y, matrix[2])
let rnd = 1 << shift >> 1
// `mat[4] * 0x10000 * y` overflows 32 bits for a large gamma, and so does
// `mat[3] * mat[4] * y`, so both products run in Int64.
let v1 = matrix[4].to_int64() * 0x10000L * y.to_int64()
let magnitude = if v1 < 0L { -v1 } else { v1 }
coefficients[2] = av1_warp_iclip_wmp(
av1_warp_apply_sign(
((magnitude + rnd.to_int64()) >> shift).to_int(),
if v1 < 0L {
-1
} else {
1
},
),
)
let v2 = matrix[3].to_int64() * matrix[4].to_int64() * y.to_int64()
let magnitude2 = if v2 < 0L { -v2 } else { v2 }
coefficients[3] = av1_warp_iclip_wmp(
matrix[5] -
av1_warp_apply_sign(
((magnitude2 + rnd.to_int64()) >> shift).to_int(),
if v2 < 0L {
-1
} else {
1
},
) -
0x10000,
)
let a0 = if coefficients[0] < 0 { -coefficients[0] } else { coefficients[0] }
let a1 = if coefficients[1] < 0 { -coefficients[1] } else { coefficients[1] }
let a2 = if coefficients[2] < 0 { -coefficients[2] } else { coefficients[2] }
let a3 = if coefficients[3] < 0 { -coefficients[3] } else { coefficients[3] }
4 * a0 + 7 * a1 >= 0x10000 || 4 * a2 + 4 * a3 >= 0x10000
}
///|
/// `dav1d_set_affine_mv2d`: pin the model's translation so the block's centre
/// predicts exactly the block's own motion vector.
fn av1_warp_affine_mv2d(
matrix : Array[Int],
bw4 : Int,
bh4 : Int,
mv_row : Int,
mv_col : Int,
bx4 : Int,
by4 : Int,
) -> Unit {
let rsuy = 2 * bh4 - 1
let rsux = 2 * bw4 - 1
let isuy = by4 * 4 + rsuy
let isux = bx4 * 4 + rsux
let v0 = mv_col * 0x2000 - (isux * (matrix[2] - 0x10000) + isuy * matrix[3])
let v1 = mv_row * 0x2000 - (isux * matrix[4] + isuy * (matrix[5] - 0x10000))
matrix[0] = if v0 < -0x800000 {
-0x800000
} else if v0 > 0x7fffff {
0x7fffff
} else {
v0
}
matrix[1] = if v1 < -0x800000 {
-0x800000
} else if v1 > 0x7fffff {
0x7fffff
} else {
v1
}
}
///|
/// `get_mult_shift_ndiag` / `get_mult_shift_diag`: one solved coefficient.
/// The product of a normal-equation entry and the reciprocal exceeds 32 bits,
/// so `px` arrives as an Int64.
fn av1_warp_mult_shift_ndiag(px : Int64, idet : Int, shift : Int) -> Int {
let v1 = px * idet.to_int64()
let magnitude = if v1 < 0L { -v1 } else { v1 }
let v2 = av1_warp_apply_sign(
((magnitude + (1L << shift >> 1)) >> shift).to_int(),
if v1 < 0L {
-1
} else {
1
},
)
if v2 < -0x1fff {
-0x1fff
} else if v2 > 0x1fff {
0x1fff
} else {
v2
}
}
///|
fn av1_warp_mult_shift_diag(px : Int64, idet : Int, shift : Int) -> Int {
let v1 = px * idet.to_int64()
let magnitude = if v1 < 0L { -v1 } else { v1 }
let v2 = av1_warp_apply_sign(
((magnitude + (1L << shift >> 1)) >> shift).to_int(),
if v1 < 0L {
-1
} else {
1
},
)
if v2 < 0xe001 {
0xe001
} else if v2 > 0x11fff {
0x11fff
} else {
v2
}
}
///|
/// `dav1d_find_affine_int`: the weighted least-squares fit of the two affine
/// rows from the sample pairs, or true when the system is singular.
/// `pts` is flat, four integers per sample in the order
/// (in-x, in-y, out-x, out-y), and the model is left in `matrix`.
fn av1_warp_find_affine_int(
pts : Array[Int],
count : Int,
bw4 : Int,
bh4 : Int,
mv_row : Int,
mv_col : Int,
matrix : Array[Int],
bx4 : Int,
by4 : Int,
) -> Bool {
let mut sxx = 0
let mut sxy = 0
let mut syy = 0
let mut bxx = 0
let mut bxy = 0
let mut cxx = 0
let mut cyy = 0
let rsuy = 2 * bh4 - 1
let rsux = 2 * bw4 - 1
let suy = rsuy * 8
let sux = rsux * 8
let duy = suy + mv_row
let dux = sux + mv_col
for i in 0..> 2) + sx * 2 + 8
sxy = sxy + ((sx * sy) >> 2) + sx + sy + 4
syy = syy + ((sy * sy) >> 2) + sy * 2 + 8
bxx = bxx + ((sx * dx) >> 2) + sx + dx + 8
bxy = bxy + ((sy * dx) >> 2) + sy + dx + 4
cxx = cxx + ((sx * dy) >> 2) + sx + dy + 4
cyy = cyy + ((sy * dy) >> 2) + sy + dy + 8
}
}
// The determinant of the normal equations, and the reciprocal that solves
// them without a division. Both products exceed 32 bits for a large sample
// set, so the whole solve runs in Int64 exactly as libdav1d's does.
let det = sxx.to_int64() * syy.to_int64() - sxy.to_int64() * sxy.to_int64()
if det == 0L {
return true
}
let abs_det = if det < 0L { -det } else { det }
let (idet_raw, shift_raw) = av1_warp_resolve_divisor_64(abs_det)
let idet = av1_warp_apply_sign(idet_raw, if det < 0L { -1 } else { 1 })
let mut shift = shift_raw - 16
let mut idet = idet
if shift < 0 {
idet = idet << -shift
shift = 0
}
matrix[2] = av1_warp_mult_shift_diag(
syy.to_int64() * bxx.to_int64() - sxy.to_int64() * bxy.to_int64(),
idet,
shift,
)
matrix[3] = av1_warp_mult_shift_ndiag(
sxx.to_int64() * bxy.to_int64() - sxy.to_int64() * bxx.to_int64(),
idet,
shift,
)
matrix[4] = av1_warp_mult_shift_ndiag(
syy.to_int64() * cxx.to_int64() - sxy.to_int64() * cyy.to_int64(),
idet,
shift,
)
matrix[5] = av1_warp_mult_shift_diag(
sxx.to_int64() * cyy.to_int64() - sxy.to_int64() * cxx.to_int64(),
idet,
shift,
)
av1_warp_affine_mv2d(matrix, bw4, bh4, mv_row, mv_col, bx4, by4)
false
}
///|
/// A derived warp model: the fitted matrix plus the four coefficients the
/// prediction consumes. A model this decoder cannot represent (a shear, or a
/// singular fit) is not built at all - the block then decodes with its own
/// motion vector, which is what AV1 specifies for an identity model.
pub(all) struct Av1WarpModel {
matrix : Array[Int]
coefficients : Array[Int]
}
///|
/// One neighbour's 4x4 extent, read from the motion field's own record of the
/// block that covers that unit.
fn av1_warp_block_extent(
field : Av1MotionField,
row : Int,
col : Int,
) -> (Int, Int) {
if row < 0 || col < 0 || row >= field.rows || col >= field.cols {
(0, 0)
} else {
let unit = row * field.cols + col
(field.width4[unit], field.height4[unit])
}
}
///|
/// Whether the neighbour at that unit names exactly this list.
fn av1_warp_matches(
field : Av1MotionField,
row : Int,
col : Int,
ref : Int,
) -> Bool {
if row < 0 || col < 0 || row >= field.rows || col >= field.cols {
false
} else {
let unit = row * field.cols + col
// The second reference is NONE for a single-reference neighbour.
field.refs[unit * 2] == ref && field.refs[unit * 2 + 1] == -1
}
}
///|
/// `find_matching_ref` (AV1 7.10.4.3): the mask of 4x4 positions along this
/// block's top and left edges whose neighbour predicts from the same reference,
/// which is what the warp estimator draws its samples from. Bit 32 of each mask
/// marks the top-left and top-right corners.
///
/// `mi_row`/`mi_col` are the block's 4x4 position in the frame, `bw4`/`bh4` its
/// extent and `w4`/`h4` the extents the masks may not step past (the block's own
/// for luma, the plane's for chroma).
fn av1_warp_matching_masks(
field : Av1MotionField,
mi_row : Int,
mi_col : Int,
bw4 : Int,
bh4 : Int,
w4 : Int,
h4 : Int,
have_left : Bool,
have_top : Bool,
ref_name : Int,
have_top_right? : Bool = true,
) -> (Int64, Int64) {
// The coded block may extend beyond the frame. Keep its nominal bw4/bh4
// for neighbour geometry, but clip the scan extents as in dav1d decode_b.
// An out-of-frame neighbour has extent zero and cannot advance the scan.
let available_w4 = field.cols - mi_col
let available_h4 = field.rows - mi_row
let w4 = if w4 < available_w4 { w4 } else { available_w4 }
let h4 = if h4 < available_h4 { h4 } else { available_h4 }
// The corner markers live at bit 32, so both masks are Int64: a 32-bit Int
// would shift them away entirely.
let mut top_mask = 0L
let mut left_mask = 0L
let mut count = 0
let mut have_topleft = have_top && have_left
let mut have_top_right = have_top_right && have_top && bw4 < 32 && bh4 < 32
if have_top {
if av1_warp_matches(field, mi_row - 1, mi_col, ref_name) {
top_mask = top_mask | 1L
count = 1
}
let (aw4_start, _) = av1_warp_block_extent(field, mi_row - 1, mi_col)
let mut aw4 = aw4_start
if aw4 >= bw4 {
// The neighbour is at least as wide as this block. When it is wider, its
// own offset inside that neighbour decides whether the top-left corner
// is really this block's, and whether it reaches past the right edge.
let off = mi_col & (aw4 - 1)
if off != 0 {
have_topleft = false
}
if aw4 - off > bw4 {
// A neighbour extending past the right edge already covers the corner.
have_top_right = false
}
} else {
let mut mask = 1 << aw4
let mut x = aw4
while x < w4 {
if av1_warp_matches(field, mi_row - 1, mi_col + x, ref_name) {
top_mask = top_mask | mask.to_int64()
count = count + 1
if count >= 8 {
return (top_mask, left_mask)
}
}
let (next, _) = av1_warp_block_extent(field, mi_row - 1, mi_col + x)
aw4 = next
mask = mask << aw4
x = x + aw4
}
}
}
if have_left {
if av1_warp_matches(field, mi_row, mi_col - 1, ref_name) {
left_mask = left_mask | 1L
count = count + 1
if count >= 8 {
return (top_mask, left_mask)
}
}
let (_, lh4_start) = av1_warp_block_extent(field, mi_row, mi_col - 1)
let mut lh4 = lh4_start
if lh4 >= bh4 {
if (mi_row & (lh4 - 1)) != 0 {
have_topleft = false
}
} else {
let mut mask = 1 << lh4
let mut y = lh4
while y < h4 {
if av1_warp_matches(field, mi_row + y, mi_col - 1, ref_name) {
left_mask = left_mask | mask.to_int64()
count = count + 1
if count >= 8 {
return (top_mask, left_mask)
}
}
let (_, next) = av1_warp_block_extent(field, mi_row + y, mi_col - 1)
lh4 = next
mask = mask << lh4
y = y + lh4
}
}
}
if have_topleft && av1_warp_matches(field, mi_row - 1, mi_col - 1, ref_name) {
left_mask = left_mask | (1L << 32)
count = count + 1
if count >= 8 {
return (top_mask, left_mask)
}
}
if have_top_right &&
av1_warp_matches(field, mi_row - 1, mi_col + bw4, ref_name) {
top_mask = top_mask | (1L << 32)
}
(top_mask, left_mask)
}
///|
/// `derive_warpmv` (AV1 7.11.3.5): collect the sample pairs the masks point at,
/// drop the ones whose motion differs from this block's own by more than the
/// size-scaled threshold, fit the affine rows and convert the result into the
/// prediction's coefficients. Returns None when the model the fit produces is
/// one the prediction cannot represent, which the caller decodes as identity
/// and therefore predicts with the block's own motion vector.
fn av1_warp_derive(
field : Av1MotionField,
mi_row : Int,
mi_col : Int,
bw4 : Int,
bh4 : Int,
w4 : Int,
h4 : Int,
have_left : Bool,
have_top : Bool,
ref_name : Int,
mv_row : Int,
mv_col : Int,
have_top_right? : Bool = true,
) -> Av1WarpModel? {
let (top_mask, left_mask) = av1_warp_matching_masks(
field,
mi_row,
mi_col,
bw4,
bh4,
w4,
h4,
have_left,
have_top,
ref_name,
have_top_right~,
)
let pts : Array[Int] = Array::make(8 * 4, 0)
let mut count = 0
// The order matches libdav1d's `add_sample(dx, dy, sx, sy, rp)`: the column
// offset and step first, then the row ones, then the neighbour's 4x4 unit.
let add = fn(
delta_col : Int,
delta_row : Int,
step_col : Int,
step_row : Int,
row : Int,
col : Int,
) -> Unit {
if count >= 8 {
return
}
let unit = if row < 0 || col < 0 || row >= field.rows || col >= field.cols {
-1
} else {
row * field.cols + col
}
if unit < 0 {
return
}
let (bw, bh) = (field.width4[unit], field.height4[unit])
let in_x = 16 * (2 * delta_col + step_col * bw) - 8
let in_y = 16 * (2 * delta_row + step_row * bh) - 8
pts[count * 4] = in_x
pts[count * 4 + 1] = in_y
pts[count * 4 + 2] = in_x + field.mv_cols[unit * 2]
pts[count * 4 + 3] = in_y + field.mv_rows[unit * 2]
count = count + 1
}
if (top_mask & 0xFFFFFFFFL) == 1L && left_mask >> 32 == 0L {
let (aw4, _) = av1_warp_block_extent(field, mi_row - 1, mi_col)
let off = mi_col & (aw4 - 1)
add(-off, 0, 1, -1, mi_row - 1, mi_col)
} else {
let mut off = 0
let mut xmask = top_mask & 0xFFFFFFFFL
while xmask != 0 {
let tz = av1_warp_lowest_bit(xmask)
off = off + tz
xmask = xmask >> tz
add(off, 0, 1, -1, mi_row - 1, mi_col + off)
xmask = xmask & -2L
}
}
if left_mask == 1L {
let (_, lh4) = av1_warp_block_extent(field, mi_row, mi_col - 1)
let off = mi_row & (lh4 - 1)
add(0, -off, -1, 1, mi_row - off, mi_col - 1)
} else {
let mut off = 0
let mut ymask = left_mask & 0xFFFFFFFFL
while ymask != 0 {
let tz = av1_warp_lowest_bit(ymask)
off = off + tz
ymask = ymask >> tz
add(0, off, -1, 1, mi_row + off, mi_col - 1)
ymask = ymask & -2L
}
}
if left_mask >> 32 != 0L {
add(0, 0, -1, -1, mi_row - 1, mi_col - 1)
}
if top_mask >> 32 != 0L {
add(bw4, 0, 1, -1, mi_row - 1, mi_col + bw4)
}
if count == 0 {
return None
}
// The threshold scales with the larger block axis, and a sample whose
// displacement differs from this block's own by more is replaced by the
// last sample that stays inside it (AV1 7.11.3.5).
let larger = if bw4 > bh4 { bw4 } else { bh4 }
let clipped = if larger < 4 { 4 } else if larger > 28 { 28 } else { larger }
let thresh = 4 * clipped
let mvd : Array[Int] = Array::make(count, 0)
let mut kept = 0
for i in 0.. thresh {
mvd[i] = -1
} else {
mvd[i] = a + b
kept = kept + 1
}
}
let mut kept = kept
if kept == 0 {
kept = 1
mvd[0] = 0
} else {
// Compact the kept samples to the front by swapping from the tail.
let mut i = 0
let mut j = count - 1
let mut k = 0
while k < count - kept {
while i < count && mvd[i] != -1 {
i = i + 1
}
while j > i && mvd[j] == -1 {
j = j - 1
}
if i >= j {
break
}
mvd[i] = mvd[j]
for t in 0..<4 {
let value = pts[j * 4 + t]
pts[j * 4 + t] = pts[i * 4 + t]
pts[i * 4 + t] = value
}
i = i + 1
j = j - 1
k = k + 1
}
}
let matrix : Array[Int] = [0, 0, 0, 0, 0, 0]
if av1_warp_find_affine_int(
pts, kept, bw4, bh4, mv_row, mv_col, matrix, mi_col, mi_row,
) {
return None
}
let coefficients : Array[Int] = [0, 0, 0, 0]
if av1_warp_shear_params(matrix, coefficients) {
return None
}
Some({ matrix, coefficients, })
}
///|
/// The index of the lowest set bit, i.e. the count of trailing zeros.
fn av1_warp_lowest_bit(value : Int64) -> Int {
if value == 0L {
0
} else {
let mut out = 0
let mut x = value
while (x & 1L) == 0L {
out = out + 1
x = x >> 1
}
out
}
}
///|
/// The intermediate rounding shift of the two-pass warp filter: four for 8 and
/// 10-bit references, two for 12-bit ones (AV1 7.11.3.8).
fn av1_warp_intermediate_bits(bit_depth : Int) -> Int {
if bit_depth >= 12 {
2
} else {
4
}
}
///|
/// The warp filter row for one 1/64-pel phase. `phase` is the subpel offset in
/// 1/65536 units, and the low bits below 1/64-pel are dropped, which is what
/// libdav1d's `dav1d_mc_warp_filter[64 + ((x + 512) >> 10)]` does.
fn av1_warp_filter(phase : Int) -> Array[Int] {
let index = 64 + ((phase + 512) >> 10)
if index < 0 {
av1_warp_filters[0]
} else if index >= av1_warp_filters.length() {
av1_warp_filters[av1_warp_filters.length() - 1]
} else {
av1_warp_filters[index]
}
}
///|
/// `warp_affine_8x8`: one 8x8 (or narrower) chunk of a warped prediction.
/// The horizontal pass filters fifteen rows into a scratch buffer with the
/// model's per-row phase, and the vertical pass reads it with the per-column
/// phase, clipping once at the end (AV1 7.11.3.8). Samples past the frame are
/// clamped, which is the same replication libdav1d's `emu_edge` produces.
/// Compound output retains the intermediate fractional bits without clipping;
/// unlike dav1d's signed 16-bit buffer it carries no PREP_BIAS offset.
fn av1_warp_affine_chunk(
samples : Array[Int],
stride : Int,
origin_x : Int,
origin_y : Int,
last_x : Int,
last_y : Int,
limit : Int,
coefficients : Array[Int],
mx : Int,
my : Int,
width : Int,
bit_depth : Int,
compound? : Bool = false,
) -> Array[Int] {
let intermediate_bits = av1_warp_intermediate_bits(bit_depth)
let mut round_shift = 7 - intermediate_bits
if round_shift < 0 {
round_shift = 0
}
let bias = 1 << round_shift >> 1
let mid : Array[Int] = Array::make(15 * 8, 0)
let mut row_mx = mx
for row in 0..<15 {
let source_y = if origin_y + row - 3 < 0 {
0
} else if origin_y + row - 3 > last_y {
last_y
} else {
origin_y + row - 3
}
let mut tmx = row_mx
for column in 0.. last_x {
last_x
} else {
at
}
sum = sum + filter[tap] * samples[source_y * stride + source_x]
}
mid[row * 8 + column] = sum >> round_shift
tmx = tmx + coefficients[0]
}
row_mx = row_mx + coefficients[1]
}
let final_shift = if compound { 7 } else { 7 + intermediate_bits }
let bias2 = 1 << final_shift >> 1
let out : Array[Int] = Array::make(8 * width, 0)
let mut row_my = my
for row in 0..<8 {
let mut tmy = row_my
for column in 0..> final_shift
out[row * width + column] = if compound {
value
} else if value < 0 {
0
} else if value > limit {
limit
} else {
value
}
tmy = tmy + coefficients[2]
}
row_my = row_my + coefficients[3]
}
out
}
///|
/// `warp_predict` (AV1 7.11.3.8) for one plane of one block. The block is walked
/// in 8x8 chunks; each chunk's phase comes from the model evaluated at the
/// chunk's centre in luma coordinates and shifted down by the plane's
/// subsampling, exactly as libdav1d's `mc_warp` does. The result is row-major
/// and clipped for single prediction; compound callers receive the unclipped
/// post-round precision to blend with another warp or ordinary MC prediction.
fn av1_warp_predict(
reference : Av1RefFrame,
model : Av1WarpModel,
plane : Int,
mi_col : Int,
mi_row : Int,
plane_x : Int,
plane_y : Int,
plane_width : Int,
plane_height : Int,
compound? : Bool = false,
) -> Array[Int] {
let matrix = model.matrix
let coefficients = model.coefficients
let (sub_x, sub_y) = if plane == 0 {
(0, 0)
} else {
(reference.sub_x, reference.sub_y)
}
let stride = if plane == 0 {
reference.coded_width
} else {
(reference.coded_width + reference.sub_x) >> reference.sub_x
}
// Clamp against the visible reference extent, never the padded allocation.
let last_x = ((reference.width + sub_x) >> sub_x) - 1
let last_y = ((reference.height + sub_y) >> sub_y) - 1
let limit = (1 << reference.bit_depth) - 1
let samples = reference.planes[plane]
let out : Array[Int] = Array::make(plane_width * plane_height, 0)
let mut y = 0
while y < plane_height {
let rows = if plane_height - y < 8 { plane_height - y } else { 8 }
// The chunk centre in luma coordinates, then the model's two rows at it.
let src_y = mi_row * 4 + ((y + 4) << sub_y)
let mat3_y = matrix[3].to_int64() * src_y.to_int64() + matrix[0].to_int64()
let mat5_y = matrix[5].to_int64() * src_y.to_int64() + matrix[1].to_int64()
let mut x = 0
while x < plane_width {
let width = if plane_width - x < 8 { plane_width - x } else { 8 }
let src_x = mi_col * 4 + ((x + 4) << sub_x)
let mvx = (matrix[2].to_int64() * src_x.to_int64() + mat3_y) >> sub_x
let mvy = (matrix[4].to_int64() * src_x.to_int64() + mat5_y) >> sub_y
let dx = (mvx >> 16).to_int() - 4
let mx = (
(mvx & 0xFFFFL).to_int() - coefficients[0] * 4 - coefficients[1] * 7
) &
-64
let dy = (mvy >> 16).to_int() - 4
let my = (
(mvy & 0xFFFFL).to_int() - coefficients[2] * 4 - coefficients[3] * 4
) &
-64
let chunk = av1_warp_affine_chunk(
samples,
stride,
dx,
dy,
last_x,
last_y,
limit,
coefficients,
mx,
my,
width,
reference.bit_depth,
compound~,
)
for row in 0.. Av1WarpModel? {
if matrix.length() < 6 {
return None
}
let coefficients : Array[Int] = [0, 0, 0, 0]
if av1_warp_shear_params(matrix, coefficients) {
None
} else {
Some({ matrix, coefficients, })
}
}