/rust/registry/src/index.crates.io-1949cf8c6b5b557f/rav1e-0.8.1/src/predict.rs
Line | Count | Source |
1 | | // Copyright (c) 2017-2022, The rav1e contributors. All rights reserved |
2 | | // |
3 | | // This source code is subject to the terms of the BSD 2 Clause License and |
4 | | // the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License |
5 | | // was not distributed with this source code in the LICENSE file, you can |
6 | | // obtain it at www.aomedia.org/license/software. If the Alliance for Open |
7 | | // Media Patent License 1.0 was not distributed with this source code in the |
8 | | // PATENTS file, you can obtain it at www.aomedia.org/license/patent. |
9 | | |
10 | | #![allow(non_upper_case_globals)] |
11 | | #![allow(non_camel_case_types)] |
12 | | #![allow(dead_code)] |
13 | | |
14 | | use std::mem::MaybeUninit; |
15 | | |
16 | | cfg_if::cfg_if! { |
17 | | if #[cfg(nasm_x86_64)] { |
18 | | pub use crate::asm::x86::predict::*; |
19 | | } else if #[cfg(asm_neon)] { |
20 | | pub use crate::asm::aarch64::predict::*; |
21 | | } else { |
22 | | pub use self::rust::*; |
23 | | } |
24 | | } |
25 | | |
26 | | use aligned_vec::{avec, ABox}; |
27 | | |
28 | | use crate::context::{TileBlockOffset, MAX_SB_SIZE_LOG2, MAX_TX_SIZE}; |
29 | | use crate::cpu_features::CpuFeatureLevel; |
30 | | use crate::encoder::FrameInvariants; |
31 | | use crate::frame::*; |
32 | | use crate::mc::*; |
33 | | use crate::partition::*; |
34 | | use crate::tiling::*; |
35 | | use crate::transform::*; |
36 | | use crate::util::*; |
37 | | |
38 | | pub const ANGLE_STEP: i8 = 3; |
39 | | |
40 | | // TODO: Review the order of this list. |
41 | | // The order impacts compression efficiency. |
42 | | pub static RAV1E_INTRA_MODES: &[PredictionMode] = &[ |
43 | | PredictionMode::DC_PRED, |
44 | | PredictionMode::H_PRED, |
45 | | PredictionMode::V_PRED, |
46 | | PredictionMode::SMOOTH_PRED, |
47 | | PredictionMode::SMOOTH_H_PRED, |
48 | | PredictionMode::SMOOTH_V_PRED, |
49 | | PredictionMode::PAETH_PRED, |
50 | | PredictionMode::D45_PRED, |
51 | | PredictionMode::D135_PRED, |
52 | | PredictionMode::D113_PRED, |
53 | | PredictionMode::D157_PRED, |
54 | | PredictionMode::D203_PRED, |
55 | | PredictionMode::D67_PRED, |
56 | | ]; |
57 | | |
58 | | pub static RAV1E_INTER_MODES_MINIMAL: &[PredictionMode] = |
59 | | &[PredictionMode::NEARESTMV]; |
60 | | |
61 | | pub static RAV1E_INTER_COMPOUND_MODES: &[PredictionMode] = &[ |
62 | | PredictionMode::GLOBAL_GLOBALMV, |
63 | | PredictionMode::NEAREST_NEARESTMV, |
64 | | PredictionMode::NEW_NEWMV, |
65 | | PredictionMode::NEAREST_NEWMV, |
66 | | PredictionMode::NEW_NEARESTMV, |
67 | | PredictionMode::NEAR_NEAR0MV, |
68 | | PredictionMode::NEAR_NEAR1MV, |
69 | | PredictionMode::NEAR_NEAR2MV, |
70 | | ]; |
71 | | |
72 | | // There are more modes than in the spec because every allowed |
73 | | // drl index for NEAR modes is considered its own mode. |
74 | | #[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Default)] |
75 | | pub enum PredictionMode { |
76 | | #[default] |
77 | | DC_PRED, // Average of above and left pixels |
78 | | V_PRED, // Vertical |
79 | | H_PRED, // Horizontal |
80 | | D45_PRED, // Directional 45 degree |
81 | | D135_PRED, // Directional 135 degree |
82 | | D113_PRED, // Directional 113 degree |
83 | | D157_PRED, // Directional 157 degree |
84 | | D203_PRED, // Directional 203 degree |
85 | | D67_PRED, // Directional 67 degree |
86 | | SMOOTH_PRED, // Combination of horizontal and vertical interpolation |
87 | | SMOOTH_V_PRED, |
88 | | SMOOTH_H_PRED, |
89 | | PAETH_PRED, |
90 | | UV_CFL_PRED, |
91 | | NEARESTMV, |
92 | | NEAR0MV, |
93 | | NEAR1MV, |
94 | | NEAR2MV, |
95 | | GLOBALMV, |
96 | | NEWMV, |
97 | | // Compound ref compound modes |
98 | | NEAREST_NEARESTMV, |
99 | | NEAR_NEAR0MV, |
100 | | NEAR_NEAR1MV, |
101 | | NEAR_NEAR2MV, |
102 | | NEAREST_NEWMV, |
103 | | NEW_NEARESTMV, |
104 | | NEAR_NEW0MV, |
105 | | NEAR_NEW1MV, |
106 | | NEAR_NEW2MV, |
107 | | NEW_NEAR0MV, |
108 | | NEW_NEAR1MV, |
109 | | NEW_NEAR2MV, |
110 | | GLOBAL_GLOBALMV, |
111 | | NEW_NEWMV, |
112 | | } |
113 | | |
114 | | // This is a higher number than in the spec and cannot be used |
115 | | // for bitstream writing purposes. |
116 | | pub const PREDICTION_MODES: usize = 34; |
117 | | |
118 | | #[derive(Copy, Clone, Debug)] |
119 | | pub enum PredictionVariant { |
120 | | NONE, |
121 | | LEFT, |
122 | | TOP, |
123 | | BOTH, |
124 | | } |
125 | | |
126 | | impl PredictionVariant { |
127 | | #[inline] |
128 | 0 | const fn new(x: usize, y: usize) -> Self { |
129 | 0 | match (x, y) { |
130 | 0 | (0, 0) => PredictionVariant::NONE, |
131 | 0 | (_, 0) => PredictionVariant::LEFT, |
132 | 0 | (0, _) => PredictionVariant::TOP, |
133 | 0 | _ => PredictionVariant::BOTH, |
134 | | } |
135 | 0 | } Unexecuted instantiation: <rav1e::predict::PredictionVariant>::new Unexecuted instantiation: <rav1e::predict::PredictionVariant>::new |
136 | | } |
137 | | |
138 | 0 | pub const fn intra_mode_to_angle(mode: PredictionMode) -> isize { |
139 | 0 | match mode { |
140 | 0 | PredictionMode::V_PRED => 90, |
141 | 0 | PredictionMode::H_PRED => 180, |
142 | 0 | PredictionMode::D45_PRED => 45, |
143 | 0 | PredictionMode::D135_PRED => 135, |
144 | 0 | PredictionMode::D113_PRED => 113, |
145 | 0 | PredictionMode::D157_PRED => 157, |
146 | 0 | PredictionMode::D203_PRED => 203, |
147 | 0 | PredictionMode::D67_PRED => 67, |
148 | 0 | _ => 0, |
149 | | } |
150 | 0 | } |
151 | | |
152 | | impl PredictionMode { |
153 | | #[inline] |
154 | 0 | pub fn is_compound(self) -> bool { |
155 | 0 | self >= PredictionMode::NEAREST_NEARESTMV |
156 | 0 | } Unexecuted instantiation: <rav1e::predict::PredictionMode>::is_compound Unexecuted instantiation: <rav1e::predict::PredictionMode>::is_compound |
157 | | #[inline] |
158 | 0 | pub fn has_nearmv(self) -> bool { |
159 | 0 | self == PredictionMode::NEAR0MV |
160 | 0 | || self == PredictionMode::NEAR1MV |
161 | 0 | || self == PredictionMode::NEAR2MV |
162 | 0 | || self == PredictionMode::NEAR_NEAR0MV |
163 | 0 | || self == PredictionMode::NEAR_NEAR1MV |
164 | 0 | || self == PredictionMode::NEAR_NEAR2MV |
165 | 0 | || self == PredictionMode::NEAR_NEW0MV |
166 | 0 | || self == PredictionMode::NEAR_NEW1MV |
167 | 0 | || self == PredictionMode::NEAR_NEW2MV |
168 | 0 | || self == PredictionMode::NEW_NEAR0MV |
169 | 0 | || self == PredictionMode::NEW_NEAR1MV |
170 | 0 | || self == PredictionMode::NEW_NEAR2MV |
171 | 0 | } Unexecuted instantiation: <rav1e::predict::PredictionMode>::has_nearmv Unexecuted instantiation: <rav1e::predict::PredictionMode>::has_nearmv |
172 | | #[inline] |
173 | 0 | pub fn has_newmv(self) -> bool { |
174 | 0 | self == PredictionMode::NEWMV |
175 | 0 | || self == PredictionMode::NEW_NEWMV |
176 | 0 | || self == PredictionMode::NEAREST_NEWMV |
177 | 0 | || self == PredictionMode::NEW_NEARESTMV |
178 | 0 | || self == PredictionMode::NEAR_NEW0MV |
179 | 0 | || self == PredictionMode::NEAR_NEW1MV |
180 | 0 | || self == PredictionMode::NEAR_NEW2MV |
181 | 0 | || self == PredictionMode::NEW_NEAR0MV |
182 | 0 | || self == PredictionMode::NEW_NEAR1MV |
183 | 0 | || self == PredictionMode::NEW_NEAR2MV |
184 | 0 | } |
185 | | #[inline] |
186 | 0 | pub fn ref_mv_idx(self) -> usize { |
187 | 0 | if self == PredictionMode::NEAR0MV |
188 | 0 | || self == PredictionMode::NEAR1MV |
189 | 0 | || self == PredictionMode::NEAR2MV |
190 | | { |
191 | 0 | self as usize - PredictionMode::NEAR0MV as usize + 1 |
192 | 0 | } else if self == PredictionMode::NEAR_NEAR0MV |
193 | 0 | || self == PredictionMode::NEAR_NEAR1MV |
194 | 0 | || self == PredictionMode::NEAR_NEAR2MV |
195 | | { |
196 | 0 | self as usize - PredictionMode::NEAR_NEAR0MV as usize + 1 |
197 | | } else { |
198 | 0 | 1 |
199 | | } |
200 | 0 | } Unexecuted instantiation: <rav1e::predict::PredictionMode>::ref_mv_idx Unexecuted instantiation: <rav1e::predict::PredictionMode>::ref_mv_idx |
201 | | |
202 | | /// # Panics |
203 | | /// |
204 | | /// - If called on an inter `PredictionMode` |
205 | 0 | pub fn predict_intra<T: Pixel>( |
206 | 0 | self, tile_rect: TileRect, dst: &mut PlaneRegionMut<'_, T>, |
207 | 0 | tx_size: TxSize, bit_depth: usize, ac: &[i16], intra_param: IntraParam, |
208 | 0 | ief_params: Option<IntraEdgeFilterParameters>, edge_buf: &IntraEdge<T>, |
209 | 0 | cpu: CpuFeatureLevel, |
210 | 0 | ) { |
211 | 0 | assert!(self.is_intra()); |
212 | 0 | let &Rect { x: frame_x, y: frame_y, .. } = dst.rect(); |
213 | 0 | debug_assert!(frame_x >= 0 && frame_y >= 0); |
214 | | // x and y are expressed relative to the tile |
215 | 0 | let x = frame_x as usize - tile_rect.x; |
216 | 0 | let y = frame_y as usize - tile_rect.y; |
217 | | |
218 | 0 | let variant = PredictionVariant::new(x, y); |
219 | | |
220 | 0 | let alpha = match intra_param { |
221 | 0 | IntraParam::Alpha(val) => val, |
222 | 0 | _ => 0, |
223 | | }; |
224 | 0 | let angle_delta = match intra_param { |
225 | 0 | IntraParam::AngleDelta(val) => val, |
226 | 0 | _ => 0, |
227 | | }; |
228 | | |
229 | 0 | let mode = match self { |
230 | 0 | PredictionMode::PAETH_PRED => match variant { |
231 | 0 | PredictionVariant::NONE => PredictionMode::DC_PRED, |
232 | 0 | PredictionVariant::TOP => PredictionMode::V_PRED, |
233 | 0 | PredictionVariant::LEFT => PredictionMode::H_PRED, |
234 | 0 | PredictionVariant::BOTH => PredictionMode::PAETH_PRED, |
235 | | }, |
236 | 0 | PredictionMode::UV_CFL_PRED if alpha == 0 => PredictionMode::DC_PRED, |
237 | 0 | _ => self, |
238 | | }; |
239 | | |
240 | 0 | let angle = match mode { |
241 | 0 | PredictionMode::UV_CFL_PRED => alpha as isize, |
242 | 0 | _ => intra_mode_to_angle(mode) + (angle_delta * ANGLE_STEP) as isize, |
243 | | }; |
244 | | |
245 | 0 | dispatch_predict_intra::<T>( |
246 | 0 | mode, variant, dst, tx_size, bit_depth, ac, angle, ief_params, edge_buf, |
247 | 0 | cpu, |
248 | | ); |
249 | 0 | } Unexecuted instantiation: <rav1e::predict::PredictionMode>::predict_intra::<u16> Unexecuted instantiation: <rav1e::predict::PredictionMode>::predict_intra::<u8> |
250 | | |
251 | | #[inline] |
252 | 0 | pub fn is_intra(self) -> bool { |
253 | 0 | self < PredictionMode::NEARESTMV |
254 | 0 | } Unexecuted instantiation: <rav1e::predict::PredictionMode>::is_intra Unexecuted instantiation: <rav1e::predict::PredictionMode>::is_intra |
255 | | |
256 | | #[inline] |
257 | 0 | pub fn is_cfl(self) -> bool { |
258 | 0 | self == PredictionMode::UV_CFL_PRED |
259 | 0 | } Unexecuted instantiation: <rav1e::predict::PredictionMode>::is_cfl Unexecuted instantiation: <rav1e::predict::PredictionMode>::is_cfl |
260 | | |
261 | | #[inline] |
262 | 0 | pub fn is_directional(self) -> bool { |
263 | 0 | self >= PredictionMode::V_PRED && self <= PredictionMode::D67_PRED |
264 | 0 | } Unexecuted instantiation: <rav1e::predict::PredictionMode>::is_directional Unexecuted instantiation: <rav1e::predict::PredictionMode>::is_directional |
265 | | |
266 | | #[inline(always)] |
267 | 0 | pub const fn angle_delta_count(self) -> i8 { |
268 | 0 | match self { |
269 | | PredictionMode::V_PRED |
270 | | | PredictionMode::H_PRED |
271 | | | PredictionMode::D45_PRED |
272 | | | PredictionMode::D135_PRED |
273 | | | PredictionMode::D113_PRED |
274 | | | PredictionMode::D157_PRED |
275 | | | PredictionMode::D203_PRED |
276 | 0 | | PredictionMode::D67_PRED => 7, |
277 | 0 | _ => 1, |
278 | | } |
279 | 0 | } |
280 | | |
281 | | // Used by inter prediction to extract the fractional component of a mv and |
282 | | // obtain the correct PlaneSlice to operate on. |
283 | | #[inline] |
284 | 0 | fn get_mv_params<T: Pixel>( |
285 | 0 | rec_plane: &Plane<T>, po: PlaneOffset, mv: MotionVector, |
286 | 0 | ) -> (i32, i32, PlaneSlice<T>) { |
287 | 0 | let &PlaneConfig { xdec, ydec, .. } = &rec_plane.cfg; |
288 | 0 | let row_offset = mv.row as i32 >> (3 + ydec); |
289 | 0 | let col_offset = mv.col as i32 >> (3 + xdec); |
290 | 0 | let row_frac = ((mv.row as i32) << (1 - ydec)) & 0xf; |
291 | 0 | let col_frac = ((mv.col as i32) << (1 - xdec)) & 0xf; |
292 | 0 | let qo = PlaneOffset { |
293 | 0 | x: po.x + col_offset as isize - 3, |
294 | 0 | y: po.y + row_offset as isize - 3, |
295 | 0 | }; |
296 | 0 | (row_frac, col_frac, rec_plane.slice(qo).clamp().subslice(3, 3)) |
297 | 0 | } Unexecuted instantiation: <rav1e::predict::PredictionMode>::get_mv_params::<u16> Unexecuted instantiation: <rav1e::predict::PredictionMode>::get_mv_params::<u8> |
298 | | |
299 | | /// Inter prediction with a single reference (i.e. not compound mode) |
300 | | /// |
301 | | /// # Panics |
302 | | /// |
303 | | /// - If called on an intra `PredictionMode` |
304 | 0 | pub fn predict_inter_single<T: Pixel>( |
305 | 0 | self, fi: &FrameInvariants<T>, tile_rect: TileRect, p: usize, |
306 | 0 | po: PlaneOffset, dst: &mut PlaneRegionMut<'_, T>, width: usize, |
307 | 0 | height: usize, ref_frame: RefType, mv: MotionVector, |
308 | 0 | ) { |
309 | 0 | assert!(!self.is_intra()); |
310 | 0 | let frame_po = tile_rect.to_frame_plane_offset(po); |
311 | | |
312 | 0 | let mode = fi.default_filter; |
313 | | |
314 | 0 | if let Some(ref rec) = |
315 | 0 | fi.rec_buffer.frames[fi.ref_frames[ref_frame.to_index()] as usize] |
316 | 0 | { |
317 | 0 | let (row_frac, col_frac, src) = |
318 | 0 | PredictionMode::get_mv_params(&rec.frame.planes[p], frame_po, mv); |
319 | 0 | put_8tap( |
320 | 0 | dst, |
321 | 0 | src, |
322 | 0 | width, |
323 | 0 | height, |
324 | 0 | col_frac, |
325 | 0 | row_frac, |
326 | 0 | mode, |
327 | 0 | mode, |
328 | 0 | fi.sequence.bit_depth, |
329 | 0 | fi.cpu_feature_level, |
330 | 0 | ); |
331 | 0 | } |
332 | 0 | } Unexecuted instantiation: <rav1e::predict::PredictionMode>::predict_inter_single::<u16> Unexecuted instantiation: <rav1e::predict::PredictionMode>::predict_inter_single::<u8> |
333 | | |
334 | | /// Inter prediction with two references. |
335 | | /// |
336 | | /// # Panics |
337 | | /// |
338 | | /// - If called on an intra `PredictionMode` |
339 | 0 | pub fn predict_inter_compound<T: Pixel>( |
340 | 0 | self, fi: &FrameInvariants<T>, tile_rect: TileRect, p: usize, |
341 | 0 | po: PlaneOffset, dst: &mut PlaneRegionMut<'_, T>, width: usize, |
342 | 0 | height: usize, ref_frames: [RefType; 2], mvs: [MotionVector; 2], |
343 | 0 | buffer: &mut InterCompoundBuffers, |
344 | 0 | ) { |
345 | 0 | assert!(!self.is_intra()); |
346 | 0 | let frame_po = tile_rect.to_frame_plane_offset(po); |
347 | | |
348 | 0 | let mode = fi.default_filter; |
349 | | |
350 | 0 | for i in 0..2 { |
351 | 0 | if let Some(ref rec) = |
352 | 0 | fi.rec_buffer.frames[fi.ref_frames[ref_frames[i].to_index()] as usize] |
353 | 0 | { |
354 | 0 | let (row_frac, col_frac, src) = PredictionMode::get_mv_params( |
355 | 0 | &rec.frame.planes[p], |
356 | 0 | frame_po, |
357 | 0 | mvs[i], |
358 | 0 | ); |
359 | 0 | prep_8tap( |
360 | 0 | buffer.get_buffer_mut(i), |
361 | 0 | src, |
362 | 0 | width, |
363 | 0 | height, |
364 | 0 | col_frac, |
365 | 0 | row_frac, |
366 | 0 | mode, |
367 | 0 | mode, |
368 | 0 | fi.sequence.bit_depth, |
369 | 0 | fi.cpu_feature_level, |
370 | 0 | ); |
371 | 0 | } |
372 | | } |
373 | 0 | mc_avg( |
374 | 0 | dst, |
375 | 0 | buffer.get_buffer(0), |
376 | 0 | buffer.get_buffer(1), |
377 | 0 | width, |
378 | 0 | height, |
379 | 0 | fi.sequence.bit_depth, |
380 | 0 | fi.cpu_feature_level, |
381 | | ); |
382 | 0 | } Unexecuted instantiation: <rav1e::predict::PredictionMode>::predict_inter_compound::<u16> Unexecuted instantiation: <rav1e::predict::PredictionMode>::predict_inter_compound::<u8> |
383 | | |
384 | | /// Inter prediction that determines whether compound mode is being used based |
385 | | /// on the second [`RefType`] in [`ref_frames`]. |
386 | 0 | pub fn predict_inter<T: Pixel>( |
387 | 0 | self, fi: &FrameInvariants<T>, tile_rect: TileRect, p: usize, |
388 | 0 | po: PlaneOffset, dst: &mut PlaneRegionMut<'_, T>, width: usize, |
389 | 0 | height: usize, ref_frames: [RefType; 2], mvs: [MotionVector; 2], |
390 | 0 | compound_buffer: &mut InterCompoundBuffers, |
391 | 0 | ) { |
392 | 0 | let is_compound = ref_frames[1] != RefType::INTRA_FRAME |
393 | 0 | && ref_frames[1] != RefType::NONE_FRAME; |
394 | | |
395 | 0 | if !is_compound { |
396 | 0 | self.predict_inter_single( |
397 | 0 | fi, |
398 | 0 | tile_rect, |
399 | 0 | p, |
400 | 0 | po, |
401 | 0 | dst, |
402 | 0 | width, |
403 | 0 | height, |
404 | 0 | ref_frames[0], |
405 | 0 | mvs[0], |
406 | | ) |
407 | 0 | } else { |
408 | 0 | self.predict_inter_compound( |
409 | 0 | fi, |
410 | 0 | tile_rect, |
411 | 0 | p, |
412 | 0 | po, |
413 | 0 | dst, |
414 | 0 | width, |
415 | 0 | height, |
416 | 0 | ref_frames, |
417 | 0 | mvs, |
418 | 0 | compound_buffer, |
419 | 0 | ); |
420 | 0 | } |
421 | 0 | } Unexecuted instantiation: <rav1e::predict::PredictionMode>::predict_inter::<u16> Unexecuted instantiation: <rav1e::predict::PredictionMode>::predict_inter::<u8> |
422 | | } |
423 | | |
424 | | /// A pair of buffers holding the interpolation of two references. Use for |
425 | | /// compound inter prediction. |
426 | | #[derive(Debug)] |
427 | | pub struct InterCompoundBuffers { |
428 | | data: ABox<[i16]>, |
429 | | } |
430 | | |
431 | | impl InterCompoundBuffers { |
432 | | // Size of one of the two buffers used. |
433 | | const BUFFER_SIZE: usize = 1 << (2 * MAX_SB_SIZE_LOG2); |
434 | | |
435 | | /// Get the buffer for eith |
436 | | #[inline] |
437 | 0 | fn get_buffer_mut(&mut self, i: usize) -> &mut [i16] { |
438 | 0 | match i { |
439 | 0 | 0 => &mut self.data[0..Self::BUFFER_SIZE], |
440 | 0 | 1 => &mut self.data[Self::BUFFER_SIZE..2 * Self::BUFFER_SIZE], |
441 | 0 | _ => panic!(), |
442 | | } |
443 | 0 | } Unexecuted instantiation: <rav1e::predict::InterCompoundBuffers>::get_buffer_mut Unexecuted instantiation: <rav1e::predict::InterCompoundBuffers>::get_buffer_mut |
444 | | |
445 | | #[inline] |
446 | 0 | fn get_buffer(&self, i: usize) -> &[i16] { |
447 | 0 | match i { |
448 | 0 | 0 => &self.data[0..Self::BUFFER_SIZE], |
449 | 0 | 1 => &self.data[Self::BUFFER_SIZE..2 * Self::BUFFER_SIZE], |
450 | 0 | _ => panic!(), |
451 | | } |
452 | 0 | } Unexecuted instantiation: <rav1e::predict::InterCompoundBuffers>::get_buffer Unexecuted instantiation: <rav1e::predict::InterCompoundBuffers>::get_buffer |
453 | | } |
454 | | |
455 | | impl Default for InterCompoundBuffers { |
456 | 0 | fn default() -> Self { |
457 | 0 | Self { data: avec![0; 2 * Self::BUFFER_SIZE].into_boxed_slice() } |
458 | 0 | } |
459 | | } |
460 | | |
461 | | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd)] |
462 | | pub enum InterIntraMode { |
463 | | II_DC_PRED, |
464 | | II_V_PRED, |
465 | | II_H_PRED, |
466 | | II_SMOOTH_PRED, |
467 | | INTERINTRA_MODES, |
468 | | } |
469 | | |
470 | | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd)] |
471 | | pub enum CompoundType { |
472 | | COMPOUND_AVERAGE, |
473 | | COMPOUND_WEDGE, |
474 | | COMPOUND_DIFFWTD, |
475 | | COMPOUND_TYPES, |
476 | | } |
477 | | |
478 | | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd)] |
479 | | pub enum MotionMode { |
480 | | SIMPLE_TRANSLATION, |
481 | | OBMC_CAUSAL, // 2-sided OBMC |
482 | | WARPED_CAUSAL, // 2-sided WARPED |
483 | | MOTION_MODES, |
484 | | } |
485 | | |
486 | | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd)] |
487 | | pub enum PaletteSize { |
488 | | TWO_COLORS, |
489 | | THREE_COLORS, |
490 | | FOUR_COLORS, |
491 | | FIVE_COLORS, |
492 | | SIX_COLORS, |
493 | | SEVEN_COLORS, |
494 | | EIGHT_COLORS, |
495 | | PALETTE_SIZES, |
496 | | } |
497 | | |
498 | | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd)] |
499 | | pub enum PaletteColor { |
500 | | PALETTE_COLOR_ONE, |
501 | | PALETTE_COLOR_TWO, |
502 | | PALETTE_COLOR_THREE, |
503 | | PALETTE_COLOR_FOUR, |
504 | | PALETTE_COLOR_FIVE, |
505 | | PALETTE_COLOR_SIX, |
506 | | PALETTE_COLOR_SEVEN, |
507 | | PALETTE_COLOR_EIGHT, |
508 | | PALETTE_COLORS, |
509 | | } |
510 | | |
511 | | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd)] |
512 | | pub enum FilterIntraMode { |
513 | | FILTER_DC_PRED, |
514 | | FILTER_V_PRED, |
515 | | FILTER_H_PRED, |
516 | | FILTER_D157_PRED, |
517 | | FILTER_PAETH_PRED, |
518 | | FILTER_INTRA_MODES, |
519 | | } |
520 | | |
521 | | #[derive(Copy, Clone, Debug)] |
522 | | pub enum IntraParam { |
523 | | AngleDelta(i8), |
524 | | Alpha(i16), |
525 | | None, |
526 | | } |
527 | | |
528 | | #[derive(Debug, Clone, Copy, Default)] |
529 | | pub struct AngleDelta { |
530 | | pub y: i8, |
531 | | pub uv: i8, |
532 | | } |
533 | | |
534 | | #[derive(Copy, Clone, Default)] |
535 | | pub struct IntraEdgeFilterParameters { |
536 | | pub plane: usize, |
537 | | pub above_ref_frame_types: Option<[RefType; 2]>, |
538 | | pub left_ref_frame_types: Option<[RefType; 2]>, |
539 | | pub above_mode: Option<PredictionMode>, |
540 | | pub left_mode: Option<PredictionMode>, |
541 | | } |
542 | | |
543 | | impl IntraEdgeFilterParameters { |
544 | 0 | pub fn new( |
545 | 0 | plane: usize, above_ctx: Option<CodedBlockInfo>, |
546 | 0 | left_ctx: Option<CodedBlockInfo>, |
547 | 0 | ) -> Self { |
548 | | IntraEdgeFilterParameters { |
549 | 0 | plane, |
550 | 0 | above_mode: match above_ctx { |
551 | 0 | Some(bi) => match plane { |
552 | 0 | 0 => bi.luma_mode, |
553 | 0 | _ => bi.chroma_mode, |
554 | | } |
555 | 0 | .into(), |
556 | 0 | None => None, |
557 | | }, |
558 | 0 | left_mode: match left_ctx { |
559 | 0 | Some(bi) => match plane { |
560 | 0 | 0 => bi.luma_mode, |
561 | 0 | _ => bi.chroma_mode, |
562 | | } |
563 | 0 | .into(), |
564 | 0 | None => None, |
565 | | }, |
566 | 0 | above_ref_frame_types: above_ctx.map(|bi| bi.reference_types), |
567 | 0 | left_ref_frame_types: left_ctx.map(|bi| bi.reference_types), |
568 | | } |
569 | 0 | } |
570 | | |
571 | | /// # Panics |
572 | | /// |
573 | | /// - If the appropriate ref frame types are not set on `self` |
574 | 0 | pub fn use_smooth_filter(self) -> bool { |
575 | 0 | let above_smooth = match self.above_mode { |
576 | | Some(PredictionMode::SMOOTH_PRED) |
577 | | | Some(PredictionMode::SMOOTH_V_PRED) |
578 | | | Some(PredictionMode::SMOOTH_H_PRED) => { |
579 | 0 | self.plane == 0 |
580 | 0 | || self.above_ref_frame_types.unwrap()[0] == RefType::INTRA_FRAME |
581 | | } |
582 | 0 | _ => false, |
583 | | }; |
584 | | |
585 | 0 | let left_smooth = match self.left_mode { |
586 | | Some(PredictionMode::SMOOTH_PRED) |
587 | | | Some(PredictionMode::SMOOTH_V_PRED) |
588 | | | Some(PredictionMode::SMOOTH_H_PRED) => { |
589 | 0 | self.plane == 0 |
590 | 0 | || self.left_ref_frame_types.unwrap()[0] == RefType::INTRA_FRAME |
591 | | } |
592 | 0 | _ => false, |
593 | | }; |
594 | | |
595 | 0 | above_smooth || left_smooth |
596 | 0 | } |
597 | | } |
598 | | |
599 | | // Weights are quadratic from '1' to '1 / block_size', scaled by 2^sm_weight_log2_scale. |
600 | | const sm_weight_log2_scale: u8 = 8; |
601 | | |
602 | | // Smooth predictor weights |
603 | | #[rustfmt::skip] |
604 | | static sm_weight_arrays: [u8; 2 * MAX_TX_SIZE] = [ |
605 | | // Unused, because we always offset by bs, which is at least 2. |
606 | | 0, 0, |
607 | | // bs = 2 |
608 | | 255, 128, |
609 | | // bs = 4 |
610 | | 255, 149, 85, 64, |
611 | | // bs = 8 |
612 | | 255, 197, 146, 105, 73, 50, 37, 32, |
613 | | // bs = 16 |
614 | | 255, 225, 196, 170, 145, 123, 102, 84, 68, 54, 43, 33, 26, 20, 17, 16, |
615 | | // bs = 32 |
616 | | 255, 240, 225, 210, 196, 182, 169, 157, 145, 133, 122, 111, 101, 92, 83, 74, |
617 | | 66, 59, 52, 45, 39, 34, 29, 25, 21, 17, 14, 12, 10, 9, 8, 8, |
618 | | // bs = 64 |
619 | | 255, 248, 240, 233, 225, 218, 210, 203, 196, 189, 182, 176, 169, 163, 156, |
620 | | 150, 144, 138, 133, 127, 121, 116, 111, 106, 101, 96, 91, 86, 82, 77, 73, 69, |
621 | | 65, 61, 57, 54, 50, 47, 44, 41, 38, 35, 32, 29, 27, 25, 22, 20, 18, 16, 15, |
622 | | 13, 12, 10, 9, 8, 7, 6, 6, 5, 5, 4, 4, 4, |
623 | | ]; |
624 | | |
625 | | #[inline(always)] |
626 | 0 | const fn get_scaled_luma_q0(alpha_q3: i16, ac_pred_q3: i16) -> i32 { |
627 | 0 | let scaled_luma_q6 = (alpha_q3 as i32) * (ac_pred_q3 as i32); |
628 | 0 | let abs_scaled_luma_q0 = (scaled_luma_q6.abs() + 32) >> 6; |
629 | 0 | if scaled_luma_q6 < 0 { |
630 | 0 | -abs_scaled_luma_q0 |
631 | | } else { |
632 | 0 | abs_scaled_luma_q0 |
633 | | } |
634 | 0 | } |
635 | | |
636 | | /// # Returns |
637 | | /// |
638 | | /// Initialized luma AC coefficients |
639 | | /// |
640 | | /// # Panics |
641 | | /// |
642 | | /// - If the block size is invalid for subsampling |
643 | | /// |
644 | 0 | pub fn luma_ac<'ac, T: Pixel>( |
645 | 0 | ac: &'ac mut [MaybeUninit<i16>], ts: &mut TileStateMut<'_, T>, |
646 | 0 | tile_bo: TileBlockOffset, bsize: BlockSize, tx_size: TxSize, |
647 | 0 | fi: &FrameInvariants<T>, |
648 | 0 | ) -> &'ac mut [i16] { |
649 | | use crate::context::MI_SIZE_LOG2; |
650 | | |
651 | 0 | let PlaneConfig { xdec, ydec, .. } = ts.input.planes[1].cfg; |
652 | 0 | let plane_bsize = bsize.subsampled_size(xdec, ydec).unwrap(); |
653 | | |
654 | | // ensure ac has the right length, so there aren't any uninitialized elements at the end |
655 | 0 | let ac = &mut ac[..plane_bsize.area()]; |
656 | | |
657 | 0 | let bo = if bsize.is_sub8x8(xdec, ydec) { |
658 | 0 | let offset = bsize.sub8x8_offset(xdec, ydec); |
659 | 0 | tile_bo.with_offset(offset.0, offset.1) |
660 | | } else { |
661 | 0 | tile_bo |
662 | | }; |
663 | 0 | let rec = &ts.rec.planes[0]; |
664 | 0 | let luma = &rec.subregion(Area::BlockStartingAt { bo: bo.0 }); |
665 | 0 | let frame_bo = ts.to_frame_block_offset(bo); |
666 | | |
667 | 0 | let frame_clipped_bw: usize = |
668 | 0 | ((fi.w_in_b - frame_bo.0.x) << MI_SIZE_LOG2).min(bsize.width()); |
669 | 0 | let frame_clipped_bh: usize = |
670 | 0 | ((fi.h_in_b - frame_bo.0.y) << MI_SIZE_LOG2).min(bsize.height()); |
671 | | |
672 | | // Similar to 'MaxLumaW' and 'MaxLumaH' stated in https://aomediacodec.github.io/av1-spec/#transform-block-semantics |
673 | 0 | let max_luma_w = if bsize.width() > BlockSize::BLOCK_8X8.width() { |
674 | 0 | let txw_log2 = tx_size.width_log2(); |
675 | 0 | ((frame_clipped_bw + (1 << txw_log2) - 1) >> txw_log2) << txw_log2 |
676 | | } else { |
677 | 0 | bsize.width() |
678 | | }; |
679 | 0 | let max_luma_h = if bsize.height() > BlockSize::BLOCK_8X8.height() { |
680 | 0 | let txh_log2 = tx_size.height_log2(); |
681 | 0 | ((frame_clipped_bh + (1 << txh_log2) - 1) >> txh_log2) << txh_log2 |
682 | | } else { |
683 | 0 | bsize.height() |
684 | | }; |
685 | | |
686 | 0 | let w_pad = (bsize.width() - max_luma_w) >> (2 + xdec); |
687 | 0 | let h_pad = (bsize.height() - max_luma_h) >> (2 + ydec); |
688 | 0 | let cpu = fi.cpu_feature_level; |
689 | | |
690 | 0 | (match (xdec, ydec) { |
691 | 0 | (0, 0) => pred_cfl_ac::<T, 0, 0>, |
692 | 0 | (1, 0) => pred_cfl_ac::<T, 1, 0>, |
693 | 0 | (_, _) => pred_cfl_ac::<T, 1, 1>, |
694 | 0 | })(ac, luma, plane_bsize, w_pad, h_pad, cpu); |
695 | | |
696 | | // SAFETY: it relies on individual pred_cfl_ac implementations to initialize the ac |
697 | 0 | unsafe { slice_assume_init_mut(ac) } |
698 | 0 | } Unexecuted instantiation: rav1e::predict::luma_ac::<u16> Unexecuted instantiation: rav1e::predict::luma_ac::<u8> |
699 | | |
700 | | pub(crate) mod rust { |
701 | | use super::*; |
702 | | use std::mem::size_of; |
703 | | |
704 | | #[inline(always)] |
705 | 0 | pub fn dispatch_predict_intra<T: Pixel>( |
706 | 0 | mode: PredictionMode, variant: PredictionVariant, |
707 | 0 | dst: &mut PlaneRegionMut<'_, T>, tx_size: TxSize, bit_depth: usize, |
708 | 0 | ac: &[i16], angle: isize, ief_params: Option<IntraEdgeFilterParameters>, |
709 | 0 | edge_buf: &IntraEdge<T>, _cpu: CpuFeatureLevel, |
710 | 0 | ) { |
711 | 0 | let width = tx_size.width(); |
712 | 0 | let height = tx_size.height(); |
713 | | |
714 | | // left pixels are ordered from bottom to top and right-aligned |
715 | 0 | let (left, top_left, above) = edge_buf.as_slices(); |
716 | | |
717 | 0 | let above_slice = above; |
718 | 0 | let left_slice = &left[left.len().saturating_sub(height)..]; |
719 | 0 | let left_and_left_below_slice = |
720 | 0 | &left[left.len().saturating_sub(width + height)..]; |
721 | | |
722 | 0 | match mode { |
723 | | PredictionMode::DC_PRED => { |
724 | 0 | (match variant { |
725 | 0 | PredictionVariant::NONE => pred_dc_128, |
726 | 0 | PredictionVariant::LEFT => pred_dc_left, |
727 | 0 | PredictionVariant::TOP => pred_dc_top, |
728 | 0 | PredictionVariant::BOTH => pred_dc, |
729 | 0 | })(dst, above_slice, left_slice, width, height, bit_depth) |
730 | | } |
731 | 0 | PredictionMode::V_PRED if angle == 90 => { |
732 | 0 | pred_v(dst, above_slice, width, height) |
733 | | } |
734 | 0 | PredictionMode::H_PRED if angle == 180 => { |
735 | 0 | pred_h(dst, left_slice, width, height) |
736 | | } |
737 | | PredictionMode::H_PRED |
738 | | | PredictionMode::V_PRED |
739 | | | PredictionMode::D45_PRED |
740 | | | PredictionMode::D135_PRED |
741 | | | PredictionMode::D113_PRED |
742 | | | PredictionMode::D157_PRED |
743 | | | PredictionMode::D203_PRED |
744 | 0 | | PredictionMode::D67_PRED => pred_directional( |
745 | 0 | dst, |
746 | 0 | above_slice, |
747 | 0 | left_and_left_below_slice, |
748 | 0 | top_left, |
749 | 0 | angle as usize, |
750 | 0 | width, |
751 | 0 | height, |
752 | 0 | bit_depth, |
753 | 0 | ief_params, |
754 | | ), |
755 | | PredictionMode::SMOOTH_PRED => { |
756 | 0 | pred_smooth(dst, above_slice, left_slice, width, height) |
757 | | } |
758 | | PredictionMode::SMOOTH_V_PRED => { |
759 | 0 | pred_smooth_v(dst, above_slice, left_slice, width, height) |
760 | | } |
761 | | PredictionMode::SMOOTH_H_PRED => { |
762 | 0 | pred_smooth_h(dst, above_slice, left_slice, width, height) |
763 | | } |
764 | | PredictionMode::PAETH_PRED => { |
765 | 0 | pred_paeth(dst, above_slice, left_slice, top_left[0], width, height) |
766 | | } |
767 | 0 | PredictionMode::UV_CFL_PRED => (match variant { |
768 | 0 | PredictionVariant::NONE => pred_cfl_128, |
769 | 0 | PredictionVariant::LEFT => pred_cfl_left, |
770 | 0 | PredictionVariant::TOP => pred_cfl_top, |
771 | 0 | PredictionVariant::BOTH => pred_cfl, |
772 | | })( |
773 | 0 | dst, |
774 | 0 | ac, |
775 | 0 | angle as i16, |
776 | 0 | above_slice, |
777 | 0 | left_slice, |
778 | 0 | width, |
779 | 0 | height, |
780 | 0 | bit_depth, |
781 | | ), |
782 | 0 | _ => unimplemented!(), |
783 | | } |
784 | 0 | } Unexecuted instantiation: rav1e::predict::rust::dispatch_predict_intra::<u16> Unexecuted instantiation: rav1e::predict::rust::dispatch_predict_intra::<u8> |
785 | | |
786 | 0 | pub(crate) fn pred_dc<T: Pixel>( |
787 | 0 | output: &mut PlaneRegionMut<'_, T>, above: &[T], left: &[T], width: usize, |
788 | 0 | height: usize, _bit_depth: usize, |
789 | 0 | ) { |
790 | 0 | let edges = left[..height].iter().chain(above[..width].iter()); |
791 | 0 | let len = (width + height) as u32; |
792 | 0 | let avg = (edges.fold(0u32, |acc, &v| { |
793 | 0 | let v: u32 = v.into(); |
794 | 0 | v + acc |
795 | 0 | }) + (len >> 1)) Unexecuted instantiation: rav1e::predict::rust::pred_dc::<u16>::{closure#0}Unexecuted instantiation: rav1e::predict::rust::pred_dc::<u8>::{closure#0} |
796 | 0 | / len; |
797 | 0 | let avg = T::cast_from(avg); |
798 | | |
799 | 0 | for line in output.rows_iter_mut().take(height) { |
800 | 0 | line[..width].fill(avg); |
801 | 0 | } |
802 | 0 | } Unexecuted instantiation: rav1e::predict::rust::pred_dc::<u16> Unexecuted instantiation: rav1e::predict::rust::pred_dc::<u8> |
803 | | |
804 | 0 | pub(crate) fn pred_dc_128<T: Pixel>( |
805 | 0 | output: &mut PlaneRegionMut<'_, T>, _above: &[T], _left: &[T], |
806 | 0 | width: usize, height: usize, bit_depth: usize, |
807 | 0 | ) { |
808 | 0 | let v = T::cast_from(128u32 << (bit_depth - 8)); |
809 | 0 | for line in output.rows_iter_mut().take(height) { |
810 | 0 | line[..width].fill(v); |
811 | 0 | } |
812 | 0 | } Unexecuted instantiation: rav1e::predict::rust::pred_dc_128::<u16> Unexecuted instantiation: rav1e::predict::rust::pred_dc_128::<u8> |
813 | | |
814 | 0 | pub(crate) fn pred_dc_left<T: Pixel>( |
815 | 0 | output: &mut PlaneRegionMut<'_, T>, _above: &[T], left: &[T], |
816 | 0 | width: usize, height: usize, _bit_depth: usize, |
817 | 0 | ) { |
818 | 0 | let sum = left[..].iter().fold(0u32, |acc, &v| { |
819 | 0 | let v: u32 = v.into(); |
820 | 0 | v + acc |
821 | 0 | }); Unexecuted instantiation: rav1e::predict::rust::pred_dc_left::<u16>::{closure#0}Unexecuted instantiation: rav1e::predict::rust::pred_dc_left::<u8>::{closure#0} |
822 | 0 | let avg = T::cast_from((sum + (height >> 1) as u32) / height as u32); |
823 | 0 | for line in output.rows_iter_mut().take(height) { |
824 | 0 | line[..width].fill(avg); |
825 | 0 | } |
826 | 0 | } Unexecuted instantiation: rav1e::predict::rust::pred_dc_left::<u16> Unexecuted instantiation: rav1e::predict::rust::pred_dc_left::<u8> |
827 | | |
828 | 0 | pub(crate) fn pred_dc_top<T: Pixel>( |
829 | 0 | output: &mut PlaneRegionMut<'_, T>, above: &[T], _left: &[T], |
830 | 0 | width: usize, height: usize, _bit_depth: usize, |
831 | 0 | ) { |
832 | 0 | let sum = above[..width].iter().fold(0u32, |acc, &v| { |
833 | 0 | let v: u32 = v.into(); |
834 | 0 | v + acc |
835 | 0 | }); Unexecuted instantiation: rav1e::predict::rust::pred_dc_top::<u16>::{closure#0}Unexecuted instantiation: rav1e::predict::rust::pred_dc_top::<u8>::{closure#0} |
836 | 0 | let avg = T::cast_from((sum + (width >> 1) as u32) / width as u32); |
837 | 0 | for line in output.rows_iter_mut().take(height) { |
838 | 0 | line[..width].fill(avg); |
839 | 0 | } |
840 | 0 | } Unexecuted instantiation: rav1e::predict::rust::pred_dc_top::<u16> Unexecuted instantiation: rav1e::predict::rust::pred_dc_top::<u8> |
841 | | |
842 | 0 | pub(crate) fn pred_h<T: Pixel>( |
843 | 0 | output: &mut PlaneRegionMut<'_, T>, left: &[T], width: usize, |
844 | 0 | height: usize, |
845 | 0 | ) { |
846 | 0 | for (line, l) in output.rows_iter_mut().zip(left[..height].iter().rev()) { |
847 | 0 | line[..width].fill(*l); |
848 | 0 | } |
849 | 0 | } Unexecuted instantiation: rav1e::predict::rust::pred_h::<u16> Unexecuted instantiation: rav1e::predict::rust::pred_h::<u8> |
850 | | |
851 | 0 | pub(crate) fn pred_v<T: Pixel>( |
852 | 0 | output: &mut PlaneRegionMut<'_, T>, above: &[T], width: usize, |
853 | 0 | height: usize, |
854 | 0 | ) { |
855 | 0 | for line in output.rows_iter_mut().take(height) { |
856 | 0 | line[..width].copy_from_slice(&above[..width]) |
857 | | } |
858 | 0 | } Unexecuted instantiation: rav1e::predict::rust::pred_v::<u16> Unexecuted instantiation: rav1e::predict::rust::pred_v::<u8> |
859 | | |
860 | 0 | pub(crate) fn pred_paeth<T: Pixel>( |
861 | 0 | output: &mut PlaneRegionMut<'_, T>, above: &[T], left: &[T], |
862 | 0 | above_left: T, width: usize, height: usize, |
863 | 0 | ) { |
864 | 0 | for r in 0..height { |
865 | 0 | let row = &mut output[r]; |
866 | 0 | for c in 0..width { |
867 | | // Top-left pixel is fixed in libaom |
868 | 0 | let raw_top_left: i32 = above_left.into(); |
869 | 0 | let raw_left: i32 = left[height - 1 - r].into(); |
870 | 0 | let raw_top: i32 = above[c].into(); |
871 | | |
872 | 0 | let p_base = raw_top + raw_left - raw_top_left; |
873 | 0 | let p_left = (p_base - raw_left).abs(); |
874 | 0 | let p_top = (p_base - raw_top).abs(); |
875 | 0 | let p_top_left = (p_base - raw_top_left).abs(); |
876 | | |
877 | | // Return nearest to base of left, top and top_left |
878 | 0 | if p_left <= p_top && p_left <= p_top_left { |
879 | 0 | row[c] = T::cast_from(raw_left); |
880 | 0 | } else if p_top <= p_top_left { |
881 | 0 | row[c] = T::cast_from(raw_top); |
882 | 0 | } else { |
883 | 0 | row[c] = T::cast_from(raw_top_left); |
884 | 0 | } |
885 | | } |
886 | | } |
887 | 0 | } Unexecuted instantiation: rav1e::predict::rust::pred_paeth::<u16> Unexecuted instantiation: rav1e::predict::rust::pred_paeth::<u8> |
888 | | |
889 | 0 | pub(crate) fn pred_smooth<T: Pixel>( |
890 | 0 | output: &mut PlaneRegionMut<'_, T>, above: &[T], left: &[T], width: usize, |
891 | 0 | height: usize, |
892 | 0 | ) { |
893 | 0 | let below_pred = left[0]; // estimated by bottom-left pixel |
894 | 0 | let right_pred = above[width - 1]; // estimated by top-right pixel |
895 | 0 | let sm_weights_w = &sm_weight_arrays[width..]; |
896 | 0 | let sm_weights_h = &sm_weight_arrays[height..]; |
897 | | |
898 | 0 | let log2_scale = 1 + sm_weight_log2_scale; |
899 | 0 | let scale = 1_u16 << sm_weight_log2_scale; |
900 | | |
901 | | // Weights sanity checks |
902 | 0 | assert!((sm_weights_w[0] as u16) < scale); |
903 | 0 | assert!((sm_weights_h[0] as u16) < scale); |
904 | 0 | assert!((scale - sm_weights_w[width - 1] as u16) < scale); |
905 | 0 | assert!((scale - sm_weights_h[height - 1] as u16) < scale); |
906 | | // ensures no overflow when calculating predictor |
907 | 0 | assert!(log2_scale as usize + size_of::<T>() < 31); |
908 | | |
909 | 0 | for r in 0..height { |
910 | 0 | let row = &mut output[r]; |
911 | 0 | for c in 0..width { |
912 | 0 | let pixels = [above[c], below_pred, left[height - 1 - r], right_pred]; |
913 | | |
914 | 0 | let weights = [ |
915 | 0 | sm_weights_h[r] as u16, |
916 | 0 | scale - sm_weights_h[r] as u16, |
917 | 0 | sm_weights_w[c] as u16, |
918 | 0 | scale - sm_weights_w[c] as u16, |
919 | 0 | ]; |
920 | | |
921 | 0 | assert!( |
922 | 0 | scale >= (sm_weights_h[r] as u16) |
923 | 0 | && scale >= (sm_weights_w[c] as u16) |
924 | | ); |
925 | | |
926 | | // Sum up weighted pixels |
927 | 0 | let mut this_pred: u32 = weights |
928 | 0 | .iter() |
929 | 0 | .zip(pixels.iter()) |
930 | 0 | .map(|(w, p)| { |
931 | 0 | let p: u32 = (*p).into(); |
932 | 0 | (*w as u32) * p |
933 | 0 | }) Unexecuted instantiation: rav1e::predict::rust::pred_smooth::<u16>::{closure#0}Unexecuted instantiation: rav1e::predict::rust::pred_smooth::<u8>::{closure#0} |
934 | 0 | .sum(); |
935 | 0 | this_pred = (this_pred + (1 << (log2_scale - 1))) >> log2_scale; |
936 | | |
937 | 0 | row[c] = T::cast_from(this_pred); |
938 | | } |
939 | | } |
940 | 0 | } Unexecuted instantiation: rav1e::predict::rust::pred_smooth::<u16> Unexecuted instantiation: rav1e::predict::rust::pred_smooth::<u8> |
941 | | |
942 | 0 | pub(crate) fn pred_smooth_h<T: Pixel>( |
943 | 0 | output: &mut PlaneRegionMut<'_, T>, above: &[T], left: &[T], width: usize, |
944 | 0 | height: usize, |
945 | 0 | ) { |
946 | 0 | let right_pred = above[width - 1]; // estimated by top-right pixel |
947 | 0 | let sm_weights = &sm_weight_arrays[width..]; |
948 | | |
949 | 0 | let log2_scale = sm_weight_log2_scale; |
950 | 0 | let scale = 1_u16 << sm_weight_log2_scale; |
951 | | |
952 | | // Weights sanity checks |
953 | 0 | assert!((sm_weights[0] as u16) < scale); |
954 | 0 | assert!((scale - sm_weights[width - 1] as u16) < scale); |
955 | | // ensures no overflow when calculating predictor |
956 | 0 | assert!(log2_scale as usize + size_of::<T>() < 31); |
957 | | |
958 | 0 | for r in 0..height { |
959 | 0 | let row = &mut output[r]; |
960 | 0 | for c in 0..width { |
961 | 0 | let pixels = [left[height - 1 - r], right_pred]; |
962 | 0 | let weights = [sm_weights[c] as u16, scale - sm_weights[c] as u16]; |
963 | | |
964 | 0 | assert!(scale >= sm_weights[c] as u16); |
965 | | |
966 | 0 | let mut this_pred: u32 = weights |
967 | 0 | .iter() |
968 | 0 | .zip(pixels.iter()) |
969 | 0 | .map(|(w, p)| { |
970 | 0 | let p: u32 = (*p).into(); |
971 | 0 | (*w as u32) * p |
972 | 0 | }) Unexecuted instantiation: rav1e::predict::rust::pred_smooth_h::<u16>::{closure#0}Unexecuted instantiation: rav1e::predict::rust::pred_smooth_h::<u8>::{closure#0} |
973 | 0 | .sum(); |
974 | 0 | this_pred = (this_pred + (1 << (log2_scale - 1))) >> log2_scale; |
975 | | |
976 | 0 | row[c] = T::cast_from(this_pred); |
977 | | } |
978 | | } |
979 | 0 | } Unexecuted instantiation: rav1e::predict::rust::pred_smooth_h::<u16> Unexecuted instantiation: rav1e::predict::rust::pred_smooth_h::<u8> |
980 | | |
981 | 0 | pub(crate) fn pred_smooth_v<T: Pixel>( |
982 | 0 | output: &mut PlaneRegionMut<'_, T>, above: &[T], left: &[T], width: usize, |
983 | 0 | height: usize, |
984 | 0 | ) { |
985 | 0 | let below_pred = left[0]; // estimated by bottom-left pixel |
986 | 0 | let sm_weights = &sm_weight_arrays[height..]; |
987 | | |
988 | 0 | let log2_scale = sm_weight_log2_scale; |
989 | 0 | let scale = 1_u16 << sm_weight_log2_scale; |
990 | | |
991 | | // Weights sanity checks |
992 | 0 | assert!((sm_weights[0] as u16) < scale); |
993 | 0 | assert!((scale - sm_weights[height - 1] as u16) < scale); |
994 | | // ensures no overflow when calculating predictor |
995 | 0 | assert!(log2_scale as usize + size_of::<T>() < 31); |
996 | | |
997 | 0 | for r in 0..height { |
998 | 0 | let row = &mut output[r]; |
999 | 0 | for c in 0..width { |
1000 | 0 | let pixels = [above[c], below_pred]; |
1001 | 0 | let weights = [sm_weights[r] as u16, scale - sm_weights[r] as u16]; |
1002 | | |
1003 | 0 | assert!(scale >= sm_weights[r] as u16); |
1004 | | |
1005 | 0 | let mut this_pred: u32 = weights |
1006 | 0 | .iter() |
1007 | 0 | .zip(pixels.iter()) |
1008 | 0 | .map(|(w, p)| { |
1009 | 0 | let p: u32 = (*p).into(); |
1010 | 0 | (*w as u32) * p |
1011 | 0 | }) Unexecuted instantiation: rav1e::predict::rust::pred_smooth_v::<u16>::{closure#0}Unexecuted instantiation: rav1e::predict::rust::pred_smooth_v::<u8>::{closure#0} |
1012 | 0 | .sum(); |
1013 | 0 | this_pred = (this_pred + (1 << (log2_scale - 1))) >> log2_scale; |
1014 | | |
1015 | 0 | row[c] = T::cast_from(this_pred); |
1016 | | } |
1017 | | } |
1018 | 0 | } Unexecuted instantiation: rav1e::predict::rust::pred_smooth_v::<u16> Unexecuted instantiation: rav1e::predict::rust::pred_smooth_v::<u8> |
1019 | | |
1020 | 0 | pub(crate) fn pred_cfl_ac<T: Pixel, const XDEC: usize, const YDEC: usize>( |
1021 | 0 | ac: &mut [MaybeUninit<i16>], luma: &PlaneRegion<'_, T>, |
1022 | 0 | plane_bsize: BlockSize, w_pad: usize, h_pad: usize, _cpu: CpuFeatureLevel, |
1023 | 0 | ) { |
1024 | 0 | let max_luma_w = (plane_bsize.width() - w_pad * 4) << XDEC; |
1025 | 0 | let max_luma_h = (plane_bsize.height() - h_pad * 4) << YDEC; |
1026 | 0 | let max_luma_x: usize = max_luma_w.max(8) - (1 << XDEC); |
1027 | 0 | let max_luma_y: usize = max_luma_h.max(8) - (1 << YDEC); |
1028 | 0 | let mut sum: i32 = 0; |
1029 | | |
1030 | 0 | let ac = &mut ac[..plane_bsize.area()]; |
1031 | | |
1032 | 0 | for (sub_y, ac_rows) in |
1033 | 0 | ac.chunks_exact_mut(plane_bsize.width()).enumerate() |
1034 | | { |
1035 | 0 | for (sub_x, ac_item) in ac_rows.iter_mut().enumerate() { |
1036 | | // Refer to https://aomediacodec.github.io/av1-spec/#predict-chroma-from-luma-process |
1037 | 0 | let luma_y = sub_y << YDEC; |
1038 | 0 | let luma_x = sub_x << XDEC; |
1039 | 0 | let y = luma_y.min(max_luma_y); |
1040 | 0 | let x = luma_x.min(max_luma_x); |
1041 | 0 | let mut sample: i16 = i16::cast_from(luma[y][x]); |
1042 | 0 | if XDEC != 0 { |
1043 | 0 | sample += i16::cast_from(luma[y][x + 1]); |
1044 | 0 | } |
1045 | 0 | if YDEC != 0 { |
1046 | 0 | debug_assert!(XDEC != 0); |
1047 | 0 | sample += i16::cast_from(luma[y + 1][x]) |
1048 | 0 | + i16::cast_from(luma[y + 1][x + 1]); |
1049 | 0 | } |
1050 | 0 | sample <<= 3 - XDEC - YDEC; |
1051 | 0 | ac_item.write(sample); |
1052 | 0 | sum += sample as i32; |
1053 | | } |
1054 | | } |
1055 | | // SAFETY: the loop above has initialized all items |
1056 | 0 | let ac = unsafe { slice_assume_init_mut(ac) }; |
1057 | 0 | let shift = plane_bsize.width_log2() + plane_bsize.height_log2(); |
1058 | 0 | let average = ((sum + (1 << (shift - 1))) >> shift) as i16; |
1059 | | |
1060 | 0 | for val in ac { |
1061 | 0 | *val -= average; |
1062 | 0 | } |
1063 | 0 | } Unexecuted instantiation: rav1e::predict::rust::pred_cfl_ac::<u16, 0, 0> Unexecuted instantiation: rav1e::predict::rust::pred_cfl_ac::<u16, 1, 1> Unexecuted instantiation: rav1e::predict::rust::pred_cfl_ac::<u16, 1, 0> Unexecuted instantiation: rav1e::predict::rust::pred_cfl_ac::<u8, 0, 0> Unexecuted instantiation: rav1e::predict::rust::pred_cfl_ac::<u8, 1, 1> Unexecuted instantiation: rav1e::predict::rust::pred_cfl_ac::<u8, 1, 0> |
1064 | | |
1065 | 0 | pub(crate) fn pred_cfl_inner<T: Pixel>( |
1066 | 0 | output: &mut PlaneRegionMut<'_, T>, ac: &[i16], alpha: i16, width: usize, |
1067 | 0 | height: usize, bit_depth: usize, |
1068 | 0 | ) { |
1069 | 0 | if alpha == 0 { |
1070 | 0 | return; |
1071 | 0 | } |
1072 | 0 | debug_assert!(ac.len() >= width * height); |
1073 | 0 | assert!(output.plane_cfg.stride >= width); |
1074 | 0 | assert!(output.rows_iter().len() >= height); |
1075 | | |
1076 | 0 | let sample_max = (1 << bit_depth) - 1; |
1077 | 0 | let avg: i32 = output[0][0].into(); |
1078 | | |
1079 | 0 | for (line, luma) in |
1080 | 0 | output.rows_iter_mut().zip(ac.chunks_exact(width)).take(height) |
1081 | | { |
1082 | 0 | for (v, &l) in line[..width].iter_mut().zip(luma[..width].iter()) { |
1083 | 0 | *v = T::cast_from( |
1084 | 0 | (avg + get_scaled_luma_q0(alpha, l)).clamp(0, sample_max), |
1085 | 0 | ); |
1086 | 0 | } |
1087 | | } |
1088 | 0 | } Unexecuted instantiation: rav1e::predict::rust::pred_cfl_inner::<u16> Unexecuted instantiation: rav1e::predict::rust::pred_cfl_inner::<u8> |
1089 | | |
1090 | 0 | pub(crate) fn pred_cfl<T: Pixel>( |
1091 | 0 | output: &mut PlaneRegionMut<'_, T>, ac: &[i16], alpha: i16, above: &[T], |
1092 | 0 | left: &[T], width: usize, height: usize, bit_depth: usize, |
1093 | 0 | ) { |
1094 | 0 | pred_dc(output, above, left, width, height, bit_depth); |
1095 | 0 | pred_cfl_inner(output, ac, alpha, width, height, bit_depth); |
1096 | 0 | } Unexecuted instantiation: rav1e::predict::rust::pred_cfl::<u16> Unexecuted instantiation: rav1e::predict::rust::pred_cfl::<u8> |
1097 | | |
1098 | 0 | pub(crate) fn pred_cfl_128<T: Pixel>( |
1099 | 0 | output: &mut PlaneRegionMut<'_, T>, ac: &[i16], alpha: i16, above: &[T], |
1100 | 0 | left: &[T], width: usize, height: usize, bit_depth: usize, |
1101 | 0 | ) { |
1102 | 0 | pred_dc_128(output, above, left, width, height, bit_depth); |
1103 | 0 | pred_cfl_inner(output, ac, alpha, width, height, bit_depth); |
1104 | 0 | } Unexecuted instantiation: rav1e::predict::rust::pred_cfl_128::<u16> Unexecuted instantiation: rav1e::predict::rust::pred_cfl_128::<u8> |
1105 | | |
1106 | 0 | pub(crate) fn pred_cfl_left<T: Pixel>( |
1107 | 0 | output: &mut PlaneRegionMut<'_, T>, ac: &[i16], alpha: i16, above: &[T], |
1108 | 0 | left: &[T], width: usize, height: usize, bit_depth: usize, |
1109 | 0 | ) { |
1110 | 0 | pred_dc_left(output, above, left, width, height, bit_depth); |
1111 | 0 | pred_cfl_inner(output, ac, alpha, width, height, bit_depth); |
1112 | 0 | } Unexecuted instantiation: rav1e::predict::rust::pred_cfl_left::<u16> Unexecuted instantiation: rav1e::predict::rust::pred_cfl_left::<u8> |
1113 | | |
1114 | 0 | pub(crate) fn pred_cfl_top<T: Pixel>( |
1115 | 0 | output: &mut PlaneRegionMut<'_, T>, ac: &[i16], alpha: i16, above: &[T], |
1116 | 0 | left: &[T], width: usize, height: usize, bit_depth: usize, |
1117 | 0 | ) { |
1118 | 0 | pred_dc_top(output, above, left, width, height, bit_depth); |
1119 | 0 | pred_cfl_inner(output, ac, alpha, width, height, bit_depth); |
1120 | 0 | } Unexecuted instantiation: rav1e::predict::rust::pred_cfl_top::<u16> Unexecuted instantiation: rav1e::predict::rust::pred_cfl_top::<u8> |
1121 | | |
1122 | | #[allow(clippy::collapsible_if)] |
1123 | | #[allow(clippy::collapsible_else_if)] |
1124 | | #[allow(clippy::needless_return)] |
1125 | 0 | pub(crate) const fn select_ief_strength( |
1126 | 0 | width: usize, height: usize, smooth_filter: bool, angle_delta: isize, |
1127 | 0 | ) -> u8 { |
1128 | 0 | let block_wh = width + height; |
1129 | 0 | let abs_delta = angle_delta.unsigned_abs(); |
1130 | | |
1131 | 0 | if smooth_filter { |
1132 | 0 | if block_wh <= 8 { |
1133 | 0 | if abs_delta >= 64 { |
1134 | 0 | return 2; |
1135 | 0 | } |
1136 | 0 | if abs_delta >= 40 { |
1137 | 0 | return 1; |
1138 | 0 | } |
1139 | 0 | } else if block_wh <= 16 { |
1140 | 0 | if abs_delta >= 48 { |
1141 | 0 | return 2; |
1142 | 0 | } |
1143 | 0 | if abs_delta >= 20 { |
1144 | 0 | return 1; |
1145 | 0 | } |
1146 | 0 | } else if block_wh <= 24 { |
1147 | 0 | if abs_delta >= 4 { |
1148 | 0 | return 3; |
1149 | 0 | } |
1150 | | } else { |
1151 | 0 | return 3; |
1152 | | } |
1153 | | } else { |
1154 | 0 | if block_wh <= 8 { |
1155 | 0 | if abs_delta >= 56 { |
1156 | 0 | return 1; |
1157 | 0 | } |
1158 | 0 | } else if block_wh <= 16 { |
1159 | 0 | if abs_delta >= 40 { |
1160 | 0 | return 1; |
1161 | 0 | } |
1162 | 0 | } else if block_wh <= 24 { |
1163 | 0 | if abs_delta >= 32 { |
1164 | 0 | return 3; |
1165 | 0 | } |
1166 | 0 | if abs_delta >= 16 { |
1167 | 0 | return 2; |
1168 | 0 | } |
1169 | 0 | if abs_delta >= 8 { |
1170 | 0 | return 1; |
1171 | 0 | } |
1172 | 0 | } else if block_wh <= 32 { |
1173 | 0 | if abs_delta >= 32 { |
1174 | 0 | return 3; |
1175 | 0 | } |
1176 | 0 | if abs_delta >= 4 { |
1177 | 0 | return 2; |
1178 | 0 | } |
1179 | 0 | return 1; |
1180 | | } else { |
1181 | 0 | return 3; |
1182 | | } |
1183 | | } |
1184 | | |
1185 | 0 | return 0; |
1186 | 0 | } |
1187 | | |
1188 | 0 | pub(crate) const fn select_ief_upsample( |
1189 | 0 | width: usize, height: usize, smooth_filter: bool, angle_delta: isize, |
1190 | 0 | ) -> bool { |
1191 | 0 | let block_wh = width + height; |
1192 | 0 | let abs_delta = angle_delta.unsigned_abs(); |
1193 | | |
1194 | 0 | if abs_delta == 0 || abs_delta >= 40 { |
1195 | 0 | false |
1196 | 0 | } else if smooth_filter { |
1197 | 0 | block_wh <= 8 |
1198 | | } else { |
1199 | 0 | block_wh <= 16 |
1200 | | } |
1201 | 0 | } |
1202 | | |
1203 | 0 | pub(crate) fn filter_edge<T: Pixel>( |
1204 | 0 | size: usize, strength: u8, edge: &mut [T], |
1205 | 0 | ) { |
1206 | | const INTRA_EDGE_KERNEL: [[u32; 5]; 3] = |
1207 | | [[0, 4, 8, 4, 0], [0, 5, 6, 5, 0], [2, 4, 4, 4, 2]]; |
1208 | | |
1209 | 0 | if strength == 0 { |
1210 | 0 | return; |
1211 | 0 | } |
1212 | | |
1213 | | // Copy the edge buffer to avoid predicting from |
1214 | | // just-filtered samples. |
1215 | 0 | let mut edge_filtered = [MaybeUninit::<T>::uninit(); MAX_TX_SIZE * 4 + 1]; |
1216 | 0 | let edge_filtered = |
1217 | 0 | init_slice_repeat_mut(&mut edge_filtered[..edge.len()], T::zero()); |
1218 | 0 | edge_filtered.copy_from_slice(&edge[..edge.len()]); |
1219 | | |
1220 | 0 | for i in 1..size { |
1221 | 0 | let mut s = 0; |
1222 | | |
1223 | 0 | for j in 0..INTRA_EDGE_KERNEL[0].len() { |
1224 | 0 | let k = (i + j).saturating_sub(2).min(size - 1); |
1225 | 0 | s += INTRA_EDGE_KERNEL[(strength - 1) as usize][j] |
1226 | 0 | * edge[k].to_u32().unwrap(); |
1227 | 0 | } |
1228 | | |
1229 | 0 | edge_filtered[i] = T::cast_from((s + 8) >> 4); |
1230 | | } |
1231 | 0 | edge.copy_from_slice(edge_filtered); |
1232 | 0 | } Unexecuted instantiation: rav1e::predict::rust::filter_edge::<u16> Unexecuted instantiation: rav1e::predict::rust::filter_edge::<u8> |
1233 | | |
1234 | 0 | pub(crate) fn upsample_edge<T: Pixel>( |
1235 | 0 | size: usize, edge: &mut [T], bit_depth: usize, |
1236 | 0 | ) { |
1237 | | // The input edge should be valid in the -1..size range, |
1238 | | // where the -1 index is the top-left edge pixel. Since |
1239 | | // negative indices are unsafe in Rust, the caller is |
1240 | | // expected to globally offset it by 1, which makes the |
1241 | | // input range 0..=size. |
1242 | 0 | let mut dup = [MaybeUninit::<T>::uninit(); MAX_TX_SIZE]; |
1243 | 0 | let dup = init_slice_repeat_mut(&mut dup[..size + 3], T::zero()); |
1244 | 0 | dup[0] = edge[0]; |
1245 | 0 | dup[1..=size + 1].copy_from_slice(&edge[0..=size]); |
1246 | 0 | dup[size + 2] = edge[size]; |
1247 | | |
1248 | | // Past here the edge is being filtered, and its |
1249 | | // effective range is shifted from -1..size to |
1250 | | // -2..2*size-1. Again, because this is safe Rust, |
1251 | | // we cannot use negative indices, and the actual range |
1252 | | // will be 0..=2*size. The caller is expected to adjust |
1253 | | // its indices on receipt of the filtered edge. |
1254 | 0 | edge[0] = dup[0]; |
1255 | | |
1256 | 0 | for i in 0..size { |
1257 | 0 | let mut s = -dup[i].to_i32().unwrap() |
1258 | 0 | + (9 * dup[i + 1].to_i32().unwrap()) |
1259 | 0 | + (9 * dup[i + 2].to_i32().unwrap()) |
1260 | 0 | - dup[i + 3].to_i32().unwrap(); |
1261 | 0 | s = ((s + 8) / 16).clamp(0, (1 << bit_depth) - 1); |
1262 | 0 |
|
1263 | 0 | edge[2 * i + 1] = T::cast_from(s); |
1264 | 0 | edge[2 * i + 2] = dup[i + 2]; |
1265 | 0 | } |
1266 | 0 | } Unexecuted instantiation: rav1e::predict::rust::upsample_edge::<u16> Unexecuted instantiation: rav1e::predict::rust::upsample_edge::<u8> |
1267 | | |
1268 | 0 | pub(crate) const fn dr_intra_derivative(p_angle: usize) -> usize { |
1269 | 0 | match p_angle { |
1270 | 0 | 3 => 1023, |
1271 | 0 | 6 => 547, |
1272 | 0 | 9 => 372, |
1273 | 0 | 14 => 273, |
1274 | 0 | 17 => 215, |
1275 | 0 | 20 => 178, |
1276 | 0 | 23 => 151, |
1277 | 0 | 26 => 132, |
1278 | 0 | 29 => 116, |
1279 | 0 | 32 => 102, |
1280 | 0 | 36 => 90, |
1281 | 0 | 39 => 80, |
1282 | 0 | 42 => 71, |
1283 | 0 | 45 => 64, |
1284 | 0 | 48 => 57, |
1285 | 0 | 51 => 51, |
1286 | 0 | 54 => 45, |
1287 | 0 | 58 => 40, |
1288 | 0 | 61 => 35, |
1289 | 0 | 64 => 31, |
1290 | 0 | 67 => 27, |
1291 | 0 | 70 => 23, |
1292 | 0 | 73 => 19, |
1293 | 0 | 76 => 15, |
1294 | 0 | 81 => 11, |
1295 | 0 | 84 => 7, |
1296 | 0 | 87 => 3, |
1297 | 0 | _ => 0, |
1298 | | } |
1299 | 0 | } |
1300 | | |
1301 | 0 | pub(crate) fn pred_directional<T: Pixel>( |
1302 | 0 | output: &mut PlaneRegionMut<'_, T>, above: &[T], left: &[T], |
1303 | 0 | top_left: &[T], p_angle: usize, width: usize, height: usize, |
1304 | 0 | bit_depth: usize, ief_params: Option<IntraEdgeFilterParameters>, |
1305 | 0 | ) { |
1306 | 0 | let sample_max = (1 << bit_depth) - 1; |
1307 | | |
1308 | 0 | let max_x = output.plane_cfg.width as isize - 1; |
1309 | 0 | let max_y = output.plane_cfg.height as isize - 1; |
1310 | | |
1311 | 0 | let mut upsample_above = false; |
1312 | 0 | let mut upsample_left = false; |
1313 | | |
1314 | 0 | let mut above_edge: &[T] = above; |
1315 | 0 | let mut left_edge: &[T] = left; |
1316 | 0 | let top_left_edge: T = top_left[0]; |
1317 | | |
1318 | 0 | let enable_edge_filter = ief_params.is_some(); |
1319 | | |
1320 | | // Initialize above and left edge buffers of the largest possible needed size if upsampled |
1321 | | // The first value is the top left pixel, also mutable and indexed at -1 in the spec |
1322 | 0 | let mut above_filtered = [MaybeUninit::<T>::uninit(); MAX_TX_SIZE * 4 + 1]; |
1323 | 0 | let above_filtered = init_slice_repeat_mut( |
1324 | 0 | &mut above_filtered[..=(width + height) * 2], |
1325 | 0 | T::zero(), |
1326 | | ); |
1327 | 0 | let mut left_filtered = [MaybeUninit::<T>::uninit(); MAX_TX_SIZE * 4 + 1]; |
1328 | 0 | let left_filtered = init_slice_repeat_mut( |
1329 | 0 | &mut left_filtered[..=(width + height) * 2], |
1330 | 0 | T::zero(), |
1331 | | ); |
1332 | | |
1333 | 0 | if enable_edge_filter { |
1334 | 0 | let above_len = above.len().min(above_filtered.len() - 1); |
1335 | 0 | let left_len = left.len().min(left_filtered.len() - 1); |
1336 | 0 | above_filtered[1..=above_len].clone_from_slice(&above[..above_len]); |
1337 | 0 | for i in 1..=left_len { |
1338 | 0 | left_filtered[i] = left[left.len() - i]; |
1339 | 0 | } |
1340 | | |
1341 | 0 | let smooth_filter = ief_params.unwrap().use_smooth_filter(); |
1342 | | |
1343 | 0 | if p_angle != 90 && p_angle != 180 { |
1344 | 0 | above_filtered[0] = top_left_edge; |
1345 | 0 | left_filtered[0] = top_left_edge; |
1346 | | |
1347 | 0 | let num_px = ( |
1348 | 0 | width.min((max_x - output.rect().x + 1).try_into().unwrap()) |
1349 | 0 | + if p_angle < 90 { height } else { 0 } |
1350 | | + 1, // above |
1351 | 0 | height.min((max_y - output.rect().y + 1).try_into().unwrap()) |
1352 | 0 | + if p_angle > 180 { width } else { 0 } |
1353 | | + 1, // left |
1354 | | ); |
1355 | | |
1356 | 0 | let filter_strength = select_ief_strength( |
1357 | 0 | width, |
1358 | 0 | height, |
1359 | 0 | smooth_filter, |
1360 | 0 | p_angle as isize - 90, |
1361 | | ); |
1362 | 0 | filter_edge(num_px.0, filter_strength, above_filtered); |
1363 | 0 | let filter_strength = select_ief_strength( |
1364 | 0 | width, |
1365 | 0 | height, |
1366 | 0 | smooth_filter, |
1367 | 0 | p_angle as isize - 180, |
1368 | | ); |
1369 | 0 | filter_edge(num_px.1, filter_strength, left_filtered); |
1370 | 0 | } |
1371 | | |
1372 | 0 | let num_px = ( |
1373 | 0 | width + if p_angle < 90 { height } else { 0 }, // above |
1374 | 0 | height + if p_angle > 180 { width } else { 0 }, // left |
1375 | | ); |
1376 | | |
1377 | 0 | upsample_above = select_ief_upsample( |
1378 | 0 | width, |
1379 | 0 | height, |
1380 | 0 | smooth_filter, |
1381 | 0 | p_angle as isize - 90, |
1382 | 0 | ); |
1383 | 0 | if upsample_above { |
1384 | 0 | upsample_edge(num_px.0, above_filtered, bit_depth); |
1385 | 0 | } |
1386 | 0 | upsample_left = select_ief_upsample( |
1387 | 0 | width, |
1388 | 0 | height, |
1389 | 0 | smooth_filter, |
1390 | 0 | p_angle as isize - 180, |
1391 | 0 | ); |
1392 | 0 | if upsample_left { |
1393 | 0 | upsample_edge(num_px.1, left_filtered, bit_depth); |
1394 | 0 | } |
1395 | | |
1396 | 0 | left_filtered.reverse(); |
1397 | 0 | above_edge = above_filtered; |
1398 | 0 | left_edge = left_filtered; |
1399 | 0 | } |
1400 | | |
1401 | 0 | let dx = if p_angle < 90 { |
1402 | 0 | dr_intra_derivative(p_angle) |
1403 | 0 | } else if p_angle > 90 && p_angle < 180 { |
1404 | 0 | dr_intra_derivative(180 - p_angle) |
1405 | | } else { |
1406 | 0 | 0 // undefined |
1407 | | }; |
1408 | | |
1409 | 0 | let dy = if p_angle > 90 && p_angle < 180 { |
1410 | 0 | dr_intra_derivative(p_angle - 90) |
1411 | 0 | } else if p_angle > 180 { |
1412 | 0 | dr_intra_derivative(270 - p_angle) |
1413 | | } else { |
1414 | 0 | 0 // undefined |
1415 | | }; |
1416 | | |
1417 | | // edge buffer index offsets applied due to the fact |
1418 | | // that we cannot safely use negative indices in Rust |
1419 | 0 | let upsample_above = upsample_above as usize; |
1420 | 0 | let upsample_left = upsample_left as usize; |
1421 | 0 | let offset_above = (enable_edge_filter as usize) << upsample_above; |
1422 | 0 | let offset_left = (enable_edge_filter as usize) << upsample_left; |
1423 | | |
1424 | 0 | if p_angle < 90 { |
1425 | 0 | for i in 0..height { |
1426 | 0 | let row = &mut output[i]; |
1427 | 0 | for j in 0..width { |
1428 | 0 | let idx = (i + 1) * dx; |
1429 | 0 | let base = (idx >> (6 - upsample_above)) + (j << upsample_above); |
1430 | 0 | let shift = (((idx << upsample_above) >> 1) & 31) as i32; |
1431 | 0 | let max_base_x = (height + width - 1) << upsample_above; |
1432 | 0 | let v = (if base < max_base_x { |
1433 | 0 | let a: i32 = above_edge[base + offset_above].into(); |
1434 | 0 | let b: i32 = above_edge[base + 1 + offset_above].into(); |
1435 | 0 | round_shift(a * (32 - shift) + b * shift, 5) |
1436 | | } else { |
1437 | 0 | let c: i32 = above_edge[max_base_x + offset_above].into(); |
1438 | 0 | c |
1439 | | }) |
1440 | 0 | .clamp(0, sample_max); |
1441 | 0 | row[j] = T::cast_from(v); |
1442 | | } |
1443 | | } |
1444 | 0 | } else if p_angle > 90 && p_angle < 180 { |
1445 | 0 | for i in 0..height { |
1446 | 0 | let row = &mut output[i]; |
1447 | 0 | for j in 0..width { |
1448 | 0 | let idx = (j << 6) as isize - ((i + 1) * dx) as isize; |
1449 | 0 | let base = idx >> (6 - upsample_above); |
1450 | 0 | if base >= -(1 << upsample_above) { |
1451 | 0 | let shift = (((idx << upsample_above) >> 1) & 31) as i32; |
1452 | 0 | let a: i32 = if !enable_edge_filter && base < 0 { |
1453 | 0 | top_left_edge |
1454 | | } else { |
1455 | 0 | above_edge[(base + offset_above as isize) as usize] |
1456 | | } |
1457 | 0 | .into(); |
1458 | 0 | let b: i32 = |
1459 | 0 | above_edge[(base + 1 + offset_above as isize) as usize].into(); |
1460 | 0 | let v = round_shift(a * (32 - shift) + b * shift, 5) |
1461 | 0 | .clamp(0, sample_max); |
1462 | 0 | row[j] = T::cast_from(v); |
1463 | | } else { |
1464 | 0 | let idx = (i << 6) as isize - ((j + 1) * dy) as isize; |
1465 | 0 | let base = idx >> (6 - upsample_left); |
1466 | 0 | let shift = (((idx << upsample_left) >> 1) & 31) as i32; |
1467 | 0 | let l = left_edge.len() - 1; |
1468 | 0 | let a: i32 = if !enable_edge_filter && base < 0 { |
1469 | 0 | top_left_edge |
1470 | 0 | } else if (base + offset_left as isize) == -2 { |
1471 | 0 | left_edge[0] |
1472 | | } else { |
1473 | 0 | left_edge[l - (base + offset_left as isize) as usize] |
1474 | | } |
1475 | 0 | .into(); |
1476 | 0 | let b: i32 = if (base + offset_left as isize) == -2 { |
1477 | 0 | left_edge[1] |
1478 | | } else { |
1479 | 0 | left_edge[l - (base + offset_left as isize + 1) as usize] |
1480 | | } |
1481 | 0 | .into(); |
1482 | 0 | let v = round_shift(a * (32 - shift) + b * shift, 5) |
1483 | 0 | .clamp(0, sample_max); |
1484 | 0 | row[j] = T::cast_from(v); |
1485 | | } |
1486 | | } |
1487 | | } |
1488 | 0 | } else if p_angle > 180 { |
1489 | 0 | for i in 0..height { |
1490 | 0 | let row = &mut output[i]; |
1491 | 0 | for j in 0..width { |
1492 | 0 | let idx = (j + 1) * dy; |
1493 | 0 | let base = (idx >> (6 - upsample_left)) + (i << upsample_left); |
1494 | 0 | let shift = (((idx << upsample_left) >> 1) & 31) as i32; |
1495 | 0 | let l = left_edge.len() - 1; |
1496 | 0 | let a: i32 = left_edge[l.saturating_sub(base + offset_left)].into(); |
1497 | 0 | let b: i32 = |
1498 | 0 | left_edge[l.saturating_sub(base + offset_left + 1)].into(); |
1499 | 0 | let v = |
1500 | 0 | round_shift(a * (32 - shift) + b * shift, 5).clamp(0, sample_max); |
1501 | 0 | row[j] = T::cast_from(v); |
1502 | 0 | } |
1503 | | } |
1504 | 0 | } |
1505 | 0 | } Unexecuted instantiation: rav1e::predict::rust::pred_directional::<u16> Unexecuted instantiation: rav1e::predict::rust::pred_directional::<u8> |
1506 | | } |
1507 | | |
1508 | | #[cfg(test)] |
1509 | | mod test { |
1510 | | use super::*; |
1511 | | use crate::predict::rust::*; |
1512 | | use num_traits::*; |
1513 | | |
1514 | | #[test] |
1515 | | fn pred_matches_u8() { |
1516 | | let edge_buf = |
1517 | | Aligned::from_fn(|i| (i + 32).saturating_sub(MAX_TX_SIZE * 2).as_()); |
1518 | | let (all_left, top_left, above) = IntraEdge::mock(&edge_buf).as_slices(); |
1519 | | let left = &all_left[all_left.len() - 4..]; |
1520 | | |
1521 | | let mut output = Plane::from_slice(&[0u8; 4 * 4], 4); |
1522 | | |
1523 | | pred_dc(&mut output.as_region_mut(), above, left, 4, 4, 8); |
1524 | | assert_eq!(&output.data[..], [32u8; 16]); |
1525 | | |
1526 | | pred_dc_top(&mut output.as_region_mut(), above, left, 4, 4, 8); |
1527 | | assert_eq!(&output.data[..], [35u8; 16]); |
1528 | | |
1529 | | pred_dc_left(&mut output.as_region_mut(), above, left, 4, 4, 8); |
1530 | | assert_eq!(&output.data[..], [30u8; 16]); |
1531 | | |
1532 | | pred_dc_128(&mut output.as_region_mut(), above, left, 4, 4, 8); |
1533 | | assert_eq!(&output.data[..], [128u8; 16]); |
1534 | | |
1535 | | pred_v(&mut output.as_region_mut(), above, 4, 4); |
1536 | | assert_eq!( |
1537 | | &output.data[..], |
1538 | | [33, 34, 35, 36, 33, 34, 35, 36, 33, 34, 35, 36, 33, 34, 35, 36] |
1539 | | ); |
1540 | | |
1541 | | pred_h(&mut output.as_region_mut(), left, 4, 4); |
1542 | | assert_eq!( |
1543 | | &output.data[..], |
1544 | | [31, 31, 31, 31, 30, 30, 30, 30, 29, 29, 29, 29, 28, 28, 28, 28] |
1545 | | ); |
1546 | | |
1547 | | pred_paeth(&mut output.as_region_mut(), above, left, top_left[0], 4, 4); |
1548 | | assert_eq!( |
1549 | | &output.data[..], |
1550 | | [32, 34, 35, 36, 30, 32, 32, 36, 29, 32, 32, 32, 28, 28, 32, 32] |
1551 | | ); |
1552 | | |
1553 | | pred_smooth(&mut output.as_region_mut(), above, left, 4, 4); |
1554 | | assert_eq!( |
1555 | | &output.data[..], |
1556 | | [32, 34, 35, 35, 30, 32, 33, 34, 29, 31, 32, 32, 29, 30, 32, 32] |
1557 | | ); |
1558 | | |
1559 | | pred_smooth_h(&mut output.as_region_mut(), above, left, 4, 4); |
1560 | | assert_eq!( |
1561 | | &output.data[..], |
1562 | | [31, 33, 34, 35, 30, 33, 34, 35, 29, 32, 34, 34, 28, 31, 33, 34] |
1563 | | ); |
1564 | | |
1565 | | pred_smooth_v(&mut output.as_region_mut(), above, left, 4, 4); |
1566 | | assert_eq!( |
1567 | | &output.data[..], |
1568 | | [33, 34, 35, 36, 31, 31, 32, 33, 30, 30, 30, 31, 29, 30, 30, 30] |
1569 | | ); |
1570 | | |
1571 | | let left = &all_left[all_left.len() - 8..]; |
1572 | | let angles = [ |
1573 | | 3, 6, 9, 14, 17, 20, 23, 26, 29, 32, 36, 39, 42, 45, 48, 51, 54, 58, 61, |
1574 | | 64, 67, 70, 73, 76, 81, 84, 87, |
1575 | | ]; |
1576 | | let expected = [ |
1577 | | [40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40], |
1578 | | [40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40], |
1579 | | [39, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40], |
1580 | | [37, 38, 39, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40], |
1581 | | [36, 37, 38, 39, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40], |
1582 | | [36, 37, 38, 39, 39, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40], |
1583 | | [35, 36, 37, 38, 38, 39, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40], |
1584 | | [35, 36, 37, 38, 37, 38, 39, 40, 39, 40, 40, 40, 40, 40, 40, 40], |
1585 | | [35, 36, 37, 38, 37, 38, 39, 40, 38, 39, 40, 40, 40, 40, 40, 40], |
1586 | | [35, 36, 37, 38, 36, 37, 38, 39, 38, 39, 40, 40, 39, 40, 40, 40], |
1587 | | [34, 35, 36, 37, 36, 37, 38, 39, 37, 38, 39, 40, 39, 40, 40, 40], |
1588 | | [34, 35, 36, 37, 36, 37, 38, 39, 37, 38, 39, 40, 38, 39, 40, 40], |
1589 | | [34, 35, 36, 37, 35, 36, 37, 38, 36, 37, 38, 39, 37, 38, 39, 40], |
1590 | | [34, 35, 36, 37, 35, 36, 37, 38, 36, 37, 38, 39, 37, 38, 39, 40], |
1591 | | [34, 35, 36, 37, 35, 36, 37, 38, 36, 37, 38, 39, 37, 38, 39, 40], |
1592 | | [34, 35, 36, 37, 35, 36, 37, 38, 35, 36, 37, 38, 36, 37, 38, 39], |
1593 | | [34, 35, 36, 37, 34, 35, 36, 37, 35, 36, 37, 38, 36, 37, 38, 39], |
1594 | | [34, 35, 36, 37, 34, 35, 36, 37, 35, 36, 37, 38, 36, 37, 38, 39], |
1595 | | [34, 35, 36, 37, 34, 35, 36, 37, 35, 36, 37, 38, 35, 36, 37, 38], |
1596 | | [33, 34, 35, 36, 34, 35, 36, 37, 34, 35, 36, 37, 35, 36, 37, 38], |
1597 | | [33, 34, 35, 36, 34, 35, 36, 37, 34, 35, 36, 37, 35, 36, 37, 38], |
1598 | | [33, 34, 35, 36, 34, 35, 36, 37, 34, 35, 36, 37, 34, 35, 36, 37], |
1599 | | [33, 34, 35, 36, 34, 35, 36, 37, 34, 35, 36, 37, 34, 35, 36, 37], |
1600 | | [33, 34, 35, 36, 33, 34, 35, 36, 34, 35, 36, 37, 34, 35, 36, 37], |
1601 | | [33, 34, 35, 36, 33, 34, 35, 36, 34, 35, 36, 37, 34, 35, 36, 37], |
1602 | | [33, 34, 35, 36, 33, 34, 35, 36, 33, 34, 35, 36, 33, 34, 35, 36], |
1603 | | [33, 34, 35, 36, 33, 34, 35, 36, 33, 34, 35, 36, 33, 34, 35, 36], |
1604 | | ]; |
1605 | | for (&angle, expected) in angles.iter().zip(expected.iter()) { |
1606 | | pred_directional( |
1607 | | &mut output.as_region_mut(), |
1608 | | above, |
1609 | | left, |
1610 | | top_left, |
1611 | | angle, |
1612 | | 4, |
1613 | | 4, |
1614 | | 8, |
1615 | | None, |
1616 | | ); |
1617 | | assert_eq!(&output.data[..], expected); |
1618 | | } |
1619 | | } |
1620 | | |
1621 | | #[test] |
1622 | | fn pred_max() { |
1623 | | let max12bit = 4096 - 1; |
1624 | | let above = [max12bit; 32]; |
1625 | | let left = [max12bit; 32]; |
1626 | | |
1627 | | let mut o = Plane::from_slice(&vec![0u16; 32 * 32], 32); |
1628 | | |
1629 | | pred_dc(&mut o.as_region_mut(), &above[..4], &left[..4], 4, 4, 16); |
1630 | | |
1631 | | for l in o.data.chunks(32).take(4) { |
1632 | | for v in l[..4].iter() { |
1633 | | assert_eq!(*v, max12bit); |
1634 | | } |
1635 | | } |
1636 | | |
1637 | | pred_h(&mut o.as_region_mut(), &left[..4], 4, 4); |
1638 | | |
1639 | | for l in o.data.chunks(32).take(4) { |
1640 | | for v in l[..4].iter() { |
1641 | | assert_eq!(*v, max12bit); |
1642 | | } |
1643 | | } |
1644 | | |
1645 | | pred_v(&mut o.as_region_mut(), &above[..4], 4, 4); |
1646 | | |
1647 | | for l in o.data.chunks(32).take(4) { |
1648 | | for v in l[..4].iter() { |
1649 | | assert_eq!(*v, max12bit); |
1650 | | } |
1651 | | } |
1652 | | |
1653 | | let above_left = max12bit; |
1654 | | |
1655 | | pred_paeth( |
1656 | | &mut o.as_region_mut(), |
1657 | | &above[..4], |
1658 | | &left[..4], |
1659 | | above_left, |
1660 | | 4, |
1661 | | 4, |
1662 | | ); |
1663 | | |
1664 | | for l in o.data.chunks(32).take(4) { |
1665 | | for v in l[..4].iter() { |
1666 | | assert_eq!(*v, max12bit); |
1667 | | } |
1668 | | } |
1669 | | |
1670 | | pred_smooth(&mut o.as_region_mut(), &above[..4], &left[..4], 4, 4); |
1671 | | |
1672 | | for l in o.data.chunks(32).take(4) { |
1673 | | for v in l[..4].iter() { |
1674 | | assert_eq!(*v, max12bit); |
1675 | | } |
1676 | | } |
1677 | | |
1678 | | pred_smooth_h(&mut o.as_region_mut(), &above[..4], &left[..4], 4, 4); |
1679 | | |
1680 | | for l in o.data.chunks(32).take(4) { |
1681 | | for v in l[..4].iter() { |
1682 | | assert_eq!(*v, max12bit); |
1683 | | } |
1684 | | } |
1685 | | |
1686 | | pred_smooth_v(&mut o.as_region_mut(), &above[..4], &left[..4], 4, 4); |
1687 | | |
1688 | | for l in o.data.chunks(32).take(4) { |
1689 | | for v in l[..4].iter() { |
1690 | | assert_eq!(*v, max12bit); |
1691 | | } |
1692 | | } |
1693 | | } |
1694 | | } |