Repository navigation
Expand file tree
/
Copy pathinter.rs
More file actions
182 lines (169 loc) · 6.78 KB
/
Copy pathinter.rs
File metadata and controls
182 lines (169 loc) · 6.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
//! Fractional sample interpolation (8.5.3.3.3) and weighted sample prediction (8.5.3.3.4).
use crate::picture::Frame;
use crate::spec_tables::{CHROMA_FILTER, LUMA_FILTER};
/// Max prediction block size + filter margin.
const WIN: usize = 64 + 7;
/// Reference windows and first-pass rows, reused between blocks (no per-block zeroing).
pub struct McScratch {
win: Vec<i16>,
tmp: Vec<i16>,
}
impl Default for McScratch {
fn default() -> Self {
McScratch { win: vec![0; WIN * WIN], tmp: vec![0; WIN * WIN] }
}
}
/// `out[j] = (sum_k f[k] * src[j + k * step]) >> shift` for `W` outputs: taps outer, outputs
/// inner, so each tap is one vector multiply-add across the row (exact integer sums).
#[inline(always)]
fn filt_w<const T: usize, const W: usize>(src: &[i16], step: usize, f: &[i8; T], shift: u32, out: &mut [i16]) {
let mut acc = [0i32; W];
for k in 0..T {
let c = f[k] as i32;
let Some(p) = src.get(k * step..).and_then(|s| s.first_chunk::<W>()) else {
return;
};
for j in 0..W {
acc[j] += c * p[j] as i32;
}
}
let Some(o) = out.first_chunk_mut::<W>() else {
return;
};
for j in 0..W {
o[j] = (acc[j] >> shift) as i16;
}
}
/// [`filt_w`] for a run of `w` outputs (prediction block widths are fixed sizes).
#[inline(always)]
fn filt<const T: usize>(src: &[i16], step: usize, f: &[i8; T], shift: u32, out: &mut [i16], w: usize) {
match w {
2 => filt_w::<T, 2>(src, step, f, shift, out),
4 => filt_w::<T, 4>(src, step, f, shift, out),
6 => filt_w::<T, 6>(src, step, f, shift, out),
8 => filt_w::<T, 8>(src, step, f, shift, out),
12 => filt_w::<T, 12>(src, step, f, shift, out),
16 => filt_w::<T, 16>(src, step, f, shift, out),
24 => filt_w::<T, 24>(src, step, f, shift, out),
32 => filt_w::<T, 32>(src, step, f, shift, out),
48 => filt_w::<T, 48>(src, step, f, shift, out),
64 => filt_w::<T, 64>(src, step, f, shift, out),
_ => {
for (j, o) in out[..w].iter_mut().enumerate() {
let s: i32 = (0..T).map(|k| f[k] as i32 * src[j + k * step] as i32).sum();
*o = (s >> shift) as i16;
}
}
}
}
/// Separable sub-sample interpolation of a w x h block from `win` (stride WIN, the block's
/// top-left sample at (m, m) for a T-tap filter with m = T / 2 - 1).
#[allow(clippy::too_many_arguments)]
fn interpolate<const T: usize>(
win: &[i16],
tmp: &mut [i16],
fx: usize,
fy: usize,
hf: &[i8; T],
vf: &[i8; T],
w: usize,
h: usize,
shift1: u32,
shift3: u32,
out: &mut [i16],
) {
let m = T / 2 - 1;
if fx == 0 && fy == 0 {
for r in 0..h {
for (o, &s) in out[r * w..r * w + w].iter_mut().zip(&win[(r + m) * WIN + m..]) {
*o = s << shift3;
}
}
} else if fy == 0 {
for r in 0..h {
filt(&win[(r + m) * WIN..], 1, hf, shift1, &mut out[r * w..], w);
}
} else if fx == 0 {
for r in 0..h {
filt(&win[r * WIN + m..], WIN, vf, shift1, &mut out[r * w..], w);
}
} else {
for r in 0..h + T - 1 {
filt(&win[r * WIN..], 1, hf, shift1, &mut tmp[r * WIN..], w);
}
for r in 0..h {
filt(&tmp[r * WIN..], WIN, vf, 6, &mut out[r * w..], w);
}
}
}
/// Luma prediction samples (14-bit intermediate precision) of a w x h block at (x, y) displaced by the
/// quarter-sample motion vector `mv`, into `out` (stride w).
#[allow(clippy::too_many_arguments)]
pub fn mc_luma(f: &Frame, x: i32, y: i32, mv: [i16; 2], w: usize, h: usize, out: &mut [i16], sc: &mut McScratch) {
let bd = f.bit_depth;
let (fx, fy) = ((mv[0] & 3) as usize, (mv[1] & 3) as usize);
let xi = x + (mv[0] as i32 >> 2);
let yi = y + (mv[1] as i32 >> 2);
let shift1 = bd.min(12) - 8;
let shift3 = 14 - bd;
f.luma_window(xi - 3, yi - 3, w + 7, h + 7, &mut sc.win, WIN);
interpolate::<8>(&sc.win, &mut sc.tmp, fx, fy, &LUMA_FILTER[fx], &LUMA_FILTER[fy], w, h, shift1, shift3, out);
}
/// Chroma prediction samples of a w x h chroma block at chroma position (x, y) with a 1/8-sample
/// vector (4:2:0).
#[allow(clippy::too_many_arguments)]
pub fn mc_chroma(f: &Frame, c: usize, x: i32, y: i32, mv: [i16; 2], w: usize, h: usize, out: &mut [i16], sc: &mut McScratch) {
let bd = f.bit_depth_c;
let (fx, fy) = ((mv[0] & 7) as usize, (mv[1] & 7) as usize);
let xi = x + (mv[0] as i32 >> 3);
let yi = y + (mv[1] as i32 >> 3);
let shift1 = bd.min(12) - 8;
let shift3 = 14 - bd;
f.chroma_window(c, xi - 1, yi - 1, w + 3, h + 3, &mut sc.win, WIN);
interpolate::<4>(&sc.win, &mut sc.tmp, fx, fy, &CHROMA_FILTER[fx], &CHROMA_FILTER[fy], w, h, shift1, shift3, out);
}
/// Default weighted prediction, one list (8-262).
pub fn put_uni(p: &[i16], w: usize, h: usize, bd: u32, dst: &mut [u16], ds: usize) {
let shift = 14 - bd;
let off = if shift > 0 { 1 << (shift - 1) } else { 0 };
let max = (1i32 << bd) - 1;
// Rows as slices and min / max clipping (`clamp` asserts its bounds) so the loop vectorises.
for (d, s) in dst.chunks_mut(ds).zip(p[..w * h].chunks(w)) {
for (d, &s) in d[..w].iter_mut().zip(s) {
*d = ((s as i32 + off) >> shift).max(0).min(max) as u16;
}
}
}
/// Default weighted prediction, bi-prediction (8-263).
pub fn put_bi(p0: &[i16], p1: &[i16], w: usize, h: usize, bd: u32, dst: &mut [u16], ds: usize) {
let shift = 15 - bd;
let off = 1 << (shift - 1);
let max = (1i32 << bd) - 1;
for ((d, a), b) in dst.chunks_mut(ds).zip(p0[..w * h].chunks(w)).zip(p1[..w * h].chunks(w)) {
for ((d, &a), &b) in d[..w].iter_mut().zip(a).zip(b) {
*d = ((a as i32 + b as i32 + off) >> shift).max(0).min(max) as u16;<
4982
/div>
}
}
}
/// Explicit weighted prediction, one list (8-265). `o` is the offset already scaled to the bit depth.
pub fn put_weighted_uni(p: &[i16], w: usize, h: usize, bd: u32, log2wd: u32, wt: i32, o: i32, dst: &mut [u16], ds: usize) {
let max = (1i32 << bd) - 1;
for r in 0..h {
for c in 0..w {
let v = p[r * w + c] as i32 * wt;
let v = if log2wd >= 1 { ((v + (1 << (log2wd - 1))) >> log2wd) + o } else { v + o };
dst[r * ds + c] = v.max(0).min(max) as u16;
}
}
}
/// Explicit weighted bi-prediction (8-266).
pub fn put_weighted_bi(p0: &[i16], p1: &[i16], w: usize, h: usize, bd: u32, log2wd: u32, w0: i32, w1: i32, o0: i32, o1: i32, dst: &mut [u16], ds: usize) {
let max = (1i32 << bd) - 1;
for r in 0..h {
for c in 0..w {
let i = r * w + c;
let v = (p0[i] as i32 * w0 + p1[i] as i32 * w1 + ((o0 + o1 + 1) << log2wd)) >> (log2wd + 1);
dst[r * ds + c] = v.max(0).min(max) as u16;
}
}
}