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_camel_case_types)] |
11 | #![allow (dead_code)] |
12 | |
13 | use self::BlockSize::*; |
14 | use self::TxSize::*; |
15 | use crate::context::*; |
16 | use crate::frame::*; |
17 | use crate::predict::*; |
18 | use crate::recon_intra::*; |
19 | use crate::serialize::{Deserialize, Serialize}; |
20 | use crate::tiling::*; |
21 | use crate::transform::TxSize; |
22 | use crate::util::*; |
23 | use thiserror::Error; |
24 | |
25 | use std::mem::transmute; |
26 | use std::mem::MaybeUninit; |
27 | |
28 | // LAST_FRAME through ALTREF_FRAME correspond to slots 0-6. |
29 | #[derive (PartialEq, Eq, PartialOrd, Copy, Clone, Debug)] |
30 | pub enum RefType { |
31 | INTRA_FRAME = 0, |
32 | LAST_FRAME = 1, |
33 | LAST2_FRAME = 2, |
34 | LAST3_FRAME = 3, |
35 | GOLDEN_FRAME = 4, |
36 | BWDREF_FRAME = 5, |
37 | ALTREF2_FRAME = 6, |
38 | ALTREF_FRAME = 7, |
39 | NONE_FRAME = 8, |
40 | } |
41 | |
42 | impl RefType { |
43 | /// convert to a ref list index, 0-6 (`INTER_REFS_PER_FRAME`) |
44 | /// |
45 | /// # Panics |
46 | /// |
47 | /// - If the ref type is a None or Intra frame |
48 | #[inline ] |
49 | pub fn to_index(self) -> usize { |
50 | match self { |
51 | NONE_FRAME => { |
52 | panic!("Tried to get slot of NONE_FRAME" ); |
53 | } |
54 | INTRA_FRAME => { |
55 | panic!("Tried to get slot of INTRA_FRAME" ); |
56 | } |
57 | _ => (self as usize) - 1, |
58 | } |
59 | } |
60 | #[inline ] |
61 | pub const fn is_fwd_ref(self) -> bool { |
62 | (self as usize) < 5 |
63 | } |
64 | #[inline ] |
65 | pub const fn is_bwd_ref(self) -> bool { |
66 | (self as usize) >= 5 |
67 | } |
68 | } |
69 | |
70 | use self::RefType::*; |
71 | use std::fmt; |
72 | use std::fmt::Display; |
73 | |
74 | pub const ALL_INTER_REFS: [RefType; 7] = [ |
75 | LAST_FRAME, |
76 | LAST2_FRAME, |
77 | LAST3_FRAME, |
78 | GOLDEN_FRAME, |
79 | BWDREF_FRAME, |
80 | ALTREF2_FRAME, |
81 | ALTREF_FRAME, |
82 | ]; |
83 | |
84 | pub const LAST_LAST2_FRAMES: usize = 0; // { LAST_FRAME, LAST2_FRAME } |
85 | pub const LAST_LAST3_FRAMES: usize = 1; // { LAST_FRAME, LAST3_FRAME } |
86 | pub const LAST_GOLDEN_FRAMES: usize = 2; // { LAST_FRAME, GOLDEN_FRAME } |
87 | pub const BWDREF_ALTREF_FRAMES: usize = 3; // { BWDREF_FRAME, ALTREF_FRAME } |
88 | pub const LAST2_LAST3_FRAMES: usize = 4; // { LAST2_FRAME, LAST3_FRAME } |
89 | pub const LAST2_GOLDEN_FRAMES: usize = 5; // { LAST2_FRAME, GOLDEN_FRAME } |
90 | pub const LAST3_GOLDEN_FRAMES: usize = 6; // { LAST3_FRAME, GOLDEN_FRAME } |
91 | pub const BWDREF_ALTREF2_FRAMES: usize = 7; // { BWDREF_FRAME, ALTREF2_FRAME } |
92 | pub const ALTREF2_ALTREF_FRAMES: usize = 8; // { ALTREF2_FRAME, ALTREF_FRAME } |
93 | pub const TOTAL_UNIDIR_COMP_REFS: usize = 9; |
94 | |
95 | // NOTE: UNIDIR_COMP_REFS is the number of uni-directional reference pairs |
96 | // that are explicitly signaled. |
97 | pub const UNIDIR_COMP_REFS: usize = BWDREF_ALTREF_FRAMES + 1; |
98 | |
99 | pub const FWD_REFS: usize = 4; |
100 | pub const BWD_REFS: usize = 3; |
101 | pub const SINGLE_REFS: usize = 7; |
102 | pub const TOTAL_REFS_PER_FRAME: usize = 8; |
103 | pub const INTER_REFS_PER_FRAME: usize = 7; |
104 | pub const TOTAL_COMP_REFS: usize = |
105 | FWD_REFS * BWD_REFS + TOTAL_UNIDIR_COMP_REFS; |
106 | |
107 | pub const REF_FRAMES_LOG2: usize = 3; |
108 | pub const REF_FRAMES: usize = 1 << REF_FRAMES_LOG2; |
109 | |
110 | pub const REF_CONTEXTS: usize = 3; |
111 | pub const MVREF_ROW_COLS: usize = 3; |
112 | |
113 | #[derive (Copy, Clone, PartialEq, Eq, PartialOrd, Debug)] |
114 | pub enum PartitionType { |
115 | PARTITION_NONE, |
116 | PARTITION_HORZ, |
117 | PARTITION_VERT, |
118 | PARTITION_SPLIT, |
119 | PARTITION_HORZ_A, // HORZ split and the top partition is split again |
120 | PARTITION_HORZ_B, // HORZ split and the bottom partition is split again |
121 | PARTITION_VERT_A, // VERT split and the left partition is split again |
122 | PARTITION_VERT_B, // VERT split and the right partition is split again |
123 | PARTITION_HORZ_4, // 4:1 horizontal partition |
124 | PARTITION_VERT_4, // 4:1 vertical partition |
125 | PARTITION_INVALID, |
126 | } |
127 | |
128 | #[derive (Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] |
129 | pub enum BlockSize { |
130 | BLOCK_4X4, |
131 | BLOCK_4X8, |
132 | BLOCK_8X4, |
133 | BLOCK_8X8, |
134 | BLOCK_8X16, |
135 | BLOCK_16X8, |
136 | BLOCK_16X16, |
137 | BLOCK_16X32, |
138 | BLOCK_32X16, |
139 | BLOCK_32X32, |
140 | BLOCK_32X64, |
141 | BLOCK_64X32, |
142 | BLOCK_64X64, |
143 | BLOCK_64X128, |
144 | BLOCK_128X64, |
145 | BLOCK_128X128, |
146 | BLOCK_4X16, |
147 | BLOCK_16X4, |
148 | BLOCK_8X32, |
149 | BLOCK_32X8, |
150 | BLOCK_16X64, |
151 | BLOCK_64X16, |
152 | } |
153 | |
154 | #[derive (Debug, Error, Copy, Clone, Eq, PartialEq)] |
155 | pub struct InvalidBlockSize; |
156 | |
157 | impl Display for InvalidBlockSize { |
158 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
159 | f.write_str(data:"invalid block size" ) |
160 | } |
161 | } |
162 | |
163 | impl PartialOrd for BlockSize { |
164 | #[inline (always)] |
165 | fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { |
166 | use std::cmp::Ordering::{Equal, Greater, Less}; |
167 | match ( |
168 | self.width().cmp(&other.width()), |
169 | self.height().cmp(&other.height()), |
170 | ) { |
171 | (Greater, Less) | (Less, Greater) => None, |
172 | (Equal, Equal) => Some(Equal), |
173 | (Greater, _) | (_, Greater) => Some(Greater), |
174 | (Less, _) | (_, Less) => Some(Less), |
175 | } |
176 | } |
177 | } |
178 | |
179 | #[cfg (test)] |
180 | impl Default for BlockSize { |
181 | fn default() -> Self { |
182 | BlockSize::BLOCK_64X64 |
183 | } |
184 | } |
185 | |
186 | impl BlockSize { |
187 | pub const BLOCK_SIZES_ALL: usize = 22; |
188 | pub const BLOCK_SIZES: usize = BlockSize::BLOCK_SIZES_ALL - 6; // BLOCK_SIZES_ALL minus 4:1 non-squares, six of them |
189 | |
190 | #[inline ] |
191 | /// # Errors |
192 | /// |
193 | /// - Returns `InvalidBlockSize` if the given `w` and `h` do not produce |
194 | /// a valid block size. |
195 | pub fn from_width_and_height_opt( |
196 | w: usize, h: usize, |
197 | ) -> Result<BlockSize, InvalidBlockSize> { |
198 | match (w, h) { |
199 | (4, 4) => Ok(BLOCK_4X4), |
200 | (4, 8) => Ok(BLOCK_4X8), |
201 | (4, 16) => Ok(BLOCK_4X16), |
202 | (8, 4) => Ok(BLOCK_8X4), |
203 | (8, 8) => Ok(BLOCK_8X8), |
204 | (8, 16) => Ok(BLOCK_8X16), |
205 | (8, 32) => Ok(BLOCK_8X32), |
206 | (16, 4) => Ok(BLOCK_16X4), |
207 | (16, 8) => Ok(BLOCK_16X8), |
208 | (16, 16) => Ok(BLOCK_16X16), |
209 | (16, 32) => Ok(BLOCK_16X32), |
210 | (16, 64) => Ok(BLOCK_16X64), |
211 | (32, 8) => Ok(BLOCK_32X8), |
212 | (32, 16) => Ok(BLOCK_32X16), |
213 | (32, 32) => Ok(BLOCK_32X32), |
214 | (32, 64) => Ok(BLOCK_32X64), |
215 | (64, 16) => Ok(BLOCK_64X16), |
216 | (64, 32) => Ok(BLOCK_64X32), |
217 | (64, 64) => Ok(BLOCK_64X64), |
218 | (64, 128) => Ok(BLOCK_64X128), |
219 | (128, 64) => Ok(BLOCK_128X64), |
220 | (128, 128) => Ok(BLOCK_128X128), |
221 | _ => Err(InvalidBlockSize), |
222 | } |
223 | } |
224 | |
225 | /// # Panics |
226 | /// |
227 | /// - If the given `w` and `h` do not produce a valid block size. |
228 | pub fn from_width_and_height(w: usize, h: usize) -> BlockSize { |
229 | Self::from_width_and_height_opt(w, h).unwrap() |
230 | } |
231 | |
232 | #[inline ] |
233 | pub fn cfl_allowed(self) -> bool { |
234 | // TODO: fix me when enabling EXT_PARTITION_TYPES |
235 | self <= BlockSize::BLOCK_32X32 |
236 | } |
237 | |
238 | #[inline ] |
239 | pub const fn width(self) -> usize { |
240 | 1 << self.width_log2() |
241 | } |
242 | |
243 | /// width * height |
244 | #[inline ] |
245 | pub const fn area(self) -> usize { |
246 | self.width() * self.height() |
247 | } |
248 | |
249 | #[inline ] |
250 | pub const fn width_log2(self) -> usize { |
251 | match self { |
252 | BLOCK_4X4 | BLOCK_4X8 | BLOCK_4X16 => 2, |
253 | BLOCK_8X4 | BLOCK_8X8 | BLOCK_8X16 | BLOCK_8X32 => 3, |
254 | BLOCK_16X4 | BLOCK_16X8 | BLOCK_16X16 | BLOCK_16X32 | BLOCK_16X64 => 4, |
255 | BLOCK_32X8 | BLOCK_32X16 | BLOCK_32X32 | BLOCK_32X64 => 5, |
256 | BLOCK_64X16 | BLOCK_64X32 | BLOCK_64X64 | BLOCK_64X128 => 6, |
257 | BLOCK_128X64 | BLOCK_128X128 => 7, |
258 | } |
259 | } |
260 | |
261 | #[inline ] |
262 | pub const fn width_mi_log2(self) -> usize { |
263 | self.width_log2() - 2 |
264 | } |
265 | |
266 | #[inline ] |
267 | pub const fn width_mi(self) -> usize { |
268 | self.width() >> MI_SIZE_LOG2 |
269 | } |
270 | |
271 | #[inline ] |
272 | pub fn width_imp_b(self) -> usize { |
273 | (self.width() >> (IMPORTANCE_BLOCK_TO_BLOCK_SHIFT + BLOCK_TO_PLANE_SHIFT)) |
274 | .max(1) |
275 | } |
276 | |
277 | #[inline ] |
278 | pub const fn height(self) -> usize { |
279 | 1 << self.height_log2() |
280 | } |
281 | |
282 | #[inline ] |
283 | pub const fn height_log2(self) -> usize { |
284 | match self { |
285 | BLOCK_4X4 | BLOCK_8X4 | BLOCK_16X4 => 2, |
286 | BLOCK_4X8 | BLOCK_8X8 | BLOCK_16X8 | BLOCK_32X8 => 3, |
287 | BLOCK_4X16 | BLOCK_8X16 | BLOCK_16X16 | BLOCK_32X16 | BLOCK_64X16 => 4, |
288 | BLOCK_8X32 | BLOCK_16X32 | BLOCK_32X32 | BLOCK_64X32 => 5, |
289 | BLOCK_16X64 | BLOCK_32X64 | BLOCK_64X64 | BLOCK_128X64 => 6, |
290 | BLOCK_64X128 | BLOCK_128X128 => 7, |
291 | } |
292 | } |
293 | |
294 | #[inline ] |
295 | pub const fn height_mi_log2(self) -> usize { |
296 | self.height_log2() - 2 |
297 | } |
298 | |
299 | #[inline ] |
300 | pub const fn height_mi(self) -> usize { |
301 | self.height() >> MI_SIZE_LOG2 |
302 | } |
303 | |
304 | #[inline ] |
305 | pub fn height_imp_b(self) -> usize { |
306 | (self.height() >> (IMPORTANCE_BLOCK_TO_BLOCK_SHIFT + BLOCK_TO_PLANE_SHIFT)) |
307 | .max(1) |
308 | } |
309 | |
310 | #[inline ] |
311 | pub const fn tx_size(self) -> TxSize { |
312 | match self { |
313 | BLOCK_4X4 => TX_4X4, |
314 | BLOCK_4X8 => TX_4X8, |
315 | BLOCK_8X4 => TX_8X4, |
316 | BLOCK_8X8 => TX_8X8, |
317 | BLOCK_8X16 => TX_8X16, |
318 | BLOCK_16X8 => TX_16X8, |
319 | BLOCK_16X16 => TX_16X16, |
320 | BLOCK_16X32 => TX_16X32, |
321 | BLOCK_32X16 => TX_32X16, |
322 | BLOCK_32X32 => TX_32X32, |
323 | BLOCK_32X64 => TX_32X64, |
324 | BLOCK_64X32 => TX_64X32, |
325 | BLOCK_4X16 => TX_4X16, |
326 | BLOCK_16X4 => TX_16X4, |
327 | BLOCK_8X32 => TX_8X32, |
328 | BLOCK_32X8 => TX_32X8, |
329 | BLOCK_16X64 => TX_16X64, |
330 | BLOCK_64X16 => TX_64X16, |
331 | _ => TX_64X64, |
332 | } |
333 | } |
334 | |
335 | /// Source: `Subsampled_Size` (AV1 specification section 5.11.38) |
336 | /// |
337 | /// # Errors |
338 | /// |
339 | /// - Returns `InvalidBlockSize` if the given block size cannot |
340 | /// be subsampled in the requested way. |
341 | #[inline ] |
342 | pub const fn subsampled_size( |
343 | self, xdec: usize, ydec: usize, |
344 | ) -> Result<BlockSize, InvalidBlockSize> { |
345 | Ok(match (xdec, ydec) { |
346 | (0, 0) /* 4:4:4 */ => self, |
347 | (1, 0) /* 4:2:2 */ => match self { |
348 | BLOCK_4X4 | BLOCK_8X4 => BLOCK_4X4, |
349 | BLOCK_8X8 => BLOCK_4X8, |
350 | BLOCK_16X4 => BLOCK_8X4, |
351 | BLOCK_16X8 => BLOCK_8X8, |
352 | BLOCK_16X16 => BLOCK_8X16, |
353 | BLOCK_32X8 => BLOCK_16X8, |
354 | BLOCK_32X16 => BLOCK_16X16, |
355 | BLOCK_32X32 => BLOCK_16X32, |
356 | BLOCK_64X16 => BLOCK_32X16, |
357 | BLOCK_64X32 => BLOCK_32X32, |
358 | BLOCK_64X64 => BLOCK_32X64, |
359 | BLOCK_128X64 => BLOCK_64X64, |
360 | BLOCK_128X128 => BLOCK_64X128, |
361 | _ => return Err(InvalidBlockSize), |
362 | }, |
363 | (1, 1) /* 4:2:0 */ => match self { |
364 | BLOCK_4X4 | BLOCK_4X8 | BLOCK_8X4 | BLOCK_8X8 => BLOCK_4X4, |
365 | BLOCK_4X16 | BLOCK_8X16 => BLOCK_4X8, |
366 | BLOCK_8X32 => BLOCK_4X16, |
367 | BLOCK_16X4 | BLOCK_16X8 => BLOCK_8X4, |
368 | BLOCK_16X16 => BLOCK_8X8, |
369 | BLOCK_16X32 => BLOCK_8X16, |
370 | BLOCK_16X64 => BLOCK_8X32, |
371 | BLOCK_32X8 => BLOCK_16X4, |
372 | BLOCK_32X16 => BLOCK_16X8, |
373 | BLOCK_32X32 => BLOCK_16X16, |
374 | BLOCK_32X64 => BLOCK_16X32, |
375 | BLOCK_64X16 => BLOCK_32X8, |
376 | BLOCK_64X32 => BLOCK_32X16, |
377 | BLOCK_64X64 => BLOCK_32X32, |
378 | BLOCK_64X128 => BLOCK_32X64, |
379 | BLOCK_128X64 => BLOCK_64X32, |
380 | BLOCK_128X128 => BLOCK_64X64, |
381 | }, |
382 | _ => return Err(InvalidBlockSize), |
383 | }) |
384 | } |
385 | |
386 | /// # Panics |
387 | /// |
388 | /// Will panic if the subsampling is not possible |
389 | #[inline ] |
390 | pub fn largest_chroma_tx_size(self, xdec: usize, ydec: usize) -> TxSize { |
391 | let plane_bsize = self |
392 | .subsampled_size(xdec, ydec) |
393 | .expect("invalid block size for this subsampling mode" ); |
394 | |
395 | let chroma_tx_size = max_txsize_rect_lookup[plane_bsize as usize]; |
396 | |
397 | av1_get_coded_tx_size(chroma_tx_size) |
398 | } |
399 | |
400 | #[inline ] |
401 | pub const fn is_sqr(self) -> bool { |
402 | self.width_log2() == self.height_log2() |
403 | } |
404 | |
405 | #[inline ] |
406 | pub const fn is_sub8x8(self, xdec: usize, ydec: usize) -> bool { |
407 | xdec != 0 && self.width_log2() == 2 || ydec != 0 && self.height_log2() == 2 |
408 | } |
409 | |
410 | #[inline ] |
411 | pub const fn sub8x8_offset( |
412 | self, xdec: usize, ydec: usize, |
413 | ) -> (isize, isize) { |
414 | let offset_x = if xdec != 0 && self.width_log2() == 2 { -1 } else { 0 }; |
415 | let offset_y = if ydec != 0 && self.height_log2() == 2 { -1 } else { 0 }; |
416 | |
417 | (offset_x, offset_y) |
418 | } |
419 | |
420 | /// # Errors |
421 | /// |
422 | /// - Returns `InvalidBlockSize` if the block size cannot be split |
423 | /// in the requested way. |
424 | pub const fn subsize( |
425 | self, partition: PartitionType, |
426 | ) -> Result<BlockSize, InvalidBlockSize> { |
427 | use PartitionType::*; |
428 | |
429 | Ok(match partition { |
430 | PARTITION_NONE => self, |
431 | PARTITION_SPLIT => match self { |
432 | BLOCK_8X8 => BLOCK_4X4, |
433 | BLOCK_16X16 => BLOCK_8X8, |
434 | BLOCK_32X32 => BLOCK_16X16, |
435 | BLOCK_64X64 => BLOCK_32X32, |
436 | BLOCK_128X128 => BLOCK_64X64, |
437 | _ => return Err(InvalidBlockSize), |
438 | }, |
439 | PARTITION_HORZ | PARTITION_HORZ_A | PARTITION_HORZ_B => match self { |
440 | BLOCK_8X8 => BLOCK_8X4, |
441 | BLOCK_16X16 => BLOCK_16X8, |
442 | BLOCK_32X32 => BLOCK_32X16, |
443 | BLOCK_64X64 => BLOCK_64X32, |
444 | BLOCK_128X128 => BLOCK_128X64, |
445 | _ => return Err(InvalidBlockSize), |
446 | }, |
447 | PARTITION_VERT | PARTITION_VERT_A | PARTITION_VERT_B => match self { |
448 | BLOCK_8X8 => BLOCK_4X8, |
449 | BLOCK_16X16 => BLOCK_8X16, |
450 | BLOCK_32X32 => BLOCK_16X32, |
451 | BLOCK_64X64 => BLOCK_32X64, |
452 | BLOCK_128X128 => BLOCK_64X128, |
453 | _ => return Err(InvalidBlockSize), |
454 | }, |
455 | PARTITION_HORZ_4 => match self { |
456 | BLOCK_16X16 => BLOCK_16X4, |
457 | BLOCK_32X32 => BLOCK_32X8, |
458 | BLOCK_64X64 => BLOCK_64X16, |
459 | _ => return Err(InvalidBlockSize), |
460 | }, |
461 | PARTITION_VERT_4 => match self { |
462 | BLOCK_16X16 => BLOCK_4X16, |
463 | BLOCK_32X32 => BLOCK_8X32, |
464 | BLOCK_64X64 => BLOCK_16X64, |
465 | _ => return Err(InvalidBlockSize), |
466 | }, |
467 | _ => return Err(InvalidBlockSize), |
468 | }) |
469 | } |
470 | |
471 | pub const fn is_rect_tx_allowed(self) -> bool { |
472 | !matches!( |
473 | self, |
474 | BLOCK_4X4 |
475 | | BLOCK_8X8 |
476 | | BLOCK_16X16 |
477 | | BLOCK_32X32 |
478 | | BLOCK_64X64 |
479 | | BLOCK_64X128 |
480 | | BLOCK_128X64 |
481 | | BLOCK_128X128 |
482 | ) |
483 | } |
484 | } |
485 | |
486 | impl fmt::Display for BlockSize { |
487 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { |
488 | write!( |
489 | f, |
490 | " {}" , |
491 | match self { |
492 | BlockSize::BLOCK_4X4 => "4x4" , |
493 | BlockSize::BLOCK_4X8 => "4x8" , |
494 | BlockSize::BLOCK_8X4 => "8x4" , |
495 | BlockSize::BLOCK_8X8 => "8x8" , |
496 | BlockSize::BLOCK_8X16 => "8x16" , |
497 | BlockSize::BLOCK_16X8 => "16x8" , |
498 | BlockSize::BLOCK_16X16 => "16x16" , |
499 | BlockSize::BLOCK_16X32 => "16x32" , |
500 | BlockSize::BLOCK_32X16 => "32x16" , |
501 | BlockSize::BLOCK_32X32 => "32x32" , |
502 | BlockSize::BLOCK_32X64 => "32x64" , |
503 | BlockSize::BLOCK_64X32 => "64x32" , |
504 | BlockSize::BLOCK_64X64 => "64x64" , |
505 | BlockSize::BLOCK_64X128 => "64x128" , |
506 | BlockSize::BLOCK_128X64 => "128x64" , |
507 | BlockSize::BLOCK_128X128 => "128x128" , |
508 | BlockSize::BLOCK_4X16 => "4x16" , |
509 | BlockSize::BLOCK_16X4 => "16x4" , |
510 | BlockSize::BLOCK_8X32 => "8x32" , |
511 | BlockSize::BLOCK_32X8 => "32x8" , |
512 | BlockSize::BLOCK_16X64 => "16x64" , |
513 | BlockSize::BLOCK_64X16 => "64x16" , |
514 | } |
515 | ) |
516 | } |
517 | } |
518 | |
519 | pub const NEWMV_MODE_CONTEXTS: usize = 7; |
520 | pub const GLOBALMV_MODE_CONTEXTS: usize = 2; |
521 | pub const REFMV_MODE_CONTEXTS: usize = 6; |
522 | pub const INTER_COMPOUND_MODES: usize = 8; |
523 | |
524 | pub const REFMV_OFFSET: usize = 4; |
525 | pub const GLOBALMV_OFFSET: usize = 3; |
526 | pub const NEWMV_CTX_MASK: usize = (1 << GLOBALMV_OFFSET) - 1; |
527 | pub const GLOBALMV_CTX_MASK: usize = |
528 | (1 << (REFMV_OFFSET - GLOBALMV_OFFSET)) - 1; |
529 | pub const REFMV_CTX_MASK: usize = (1 << (8 - REFMV_OFFSET)) - 1; |
530 | |
531 | pub static RAV1E_PARTITION_TYPES: &[PartitionType] = &[ |
532 | PartitionType::PARTITION_NONE, |
533 | PartitionType::PARTITION_HORZ, |
534 | PartitionType::PARTITION_VERT, |
535 | PartitionType::PARTITION_SPLIT, |
536 | ]; |
537 | |
538 | #[derive (Copy, Clone, Debug, PartialEq, Eq, PartialOrd)] |
539 | pub enum GlobalMVMode { |
540 | IDENTITY = 0, // identity transformation, 0-parameter |
541 | TRANSLATION = 1, // translational motion 2-parameter |
542 | ROTZOOM = 2, // simplified affine with rotation + zoom only, 4-parameter |
543 | AFFINE = 3, // affine, 6-parameter |
544 | } |
545 | |
546 | #[derive (Copy, Clone, Debug, PartialEq, Eq, PartialOrd)] |
547 | pub enum MvSubpelPrecision { |
548 | MV_SUBPEL_NONE = -1, |
549 | MV_SUBPEL_LOW_PRECISION = 0, |
550 | MV_SUBPEL_HIGH_PRECISION, |
551 | } |
552 | |
553 | /* Symbols for coding which components are zero jointly */ |
554 | pub const MV_JOINTS: usize = 4; |
555 | |
556 | #[derive (Copy, Clone, Debug, PartialEq, Eq, PartialOrd)] |
557 | pub enum MvJointType { |
558 | MV_JOINT_ZERO = 0, /* Zero vector */ |
559 | MV_JOINT_HNZVZ = 1, /* Vert zero, hor nonzero */ |
560 | MV_JOINT_HZVNZ = 2, /* Hor zero, vert nonzero */ |
561 | MV_JOINT_HNZVNZ = 3, /* Both components nonzero */ |
562 | } |
563 | |
564 | fn supersample_chroma_bsize( |
565 | bsize: BlockSize, ss_x: usize, ss_y: usize, |
566 | ) -> BlockSize { |
567 | debug_assert!(ss_x < 2); |
568 | debug_assert!(ss_y < 2); |
569 | |
570 | match bsize { |
571 | BLOCK_4X4 => match (ss_x, ss_y) { |
572 | (1, 1) => BLOCK_8X8, |
573 | (1, 0) => BLOCK_8X4, |
574 | (0, 1) => BLOCK_4X8, |
575 | _ => bsize, |
576 | }, |
577 | BLOCK_4X8 => match (ss_x, ss_y) { |
578 | (1, 1) => BLOCK_8X8, |
579 | (1, 0) => BLOCK_8X8, |
580 | (0, 1) => BLOCK_4X8, |
581 | _ => bsize, |
582 | }, |
583 | BLOCK_8X4 => match (ss_x, ss_y) { |
584 | (1, 1) => BLOCK_8X8, |
585 | (1, 0) => BLOCK_8X4, |
586 | (0, 1) => BLOCK_8X8, |
587 | _ => bsize, |
588 | }, |
589 | BLOCK_4X16 => match (ss_x, ss_y) { |
590 | (1, 1) => BLOCK_8X16, |
591 | (1, 0) => BLOCK_8X16, |
592 | (0, 1) => BLOCK_4X16, |
593 | _ => bsize, |
594 | }, |
595 | BLOCK_16X4 => match (ss_x, ss_y) { |
596 | (1, 1) => BLOCK_16X8, |
597 | (1, 0) => BLOCK_16X4, |
598 | (0, 1) => BLOCK_16X8, |
599 | _ => bsize, |
600 | }, |
601 | _ => bsize, |
602 | } |
603 | } |
604 | |
605 | type IntraEdgeBuffer<T> = Aligned<[MaybeUninit<T>; 4 * MAX_TX_SIZE + 1]>; |
606 | |
607 | #[cfg (any(test, feature = "bench" ))] |
608 | type IntraEdgeMock<T> = Aligned<[T; 4 * MAX_TX_SIZE + 1]>; |
609 | |
610 | pub struct IntraEdge<'a, T: Pixel>(&'a [T], &'a [T], &'a [T]); |
611 | |
612 | impl<'a, T: Pixel> IntraEdge<'a, T> { |
613 | fn new( |
614 | edge_buf: &'a mut IntraEdgeBuffer<T>, init_left: usize, init_above: usize, |
615 | ) -> Self { |
616 | // SAFETY: Initialized in `get_intra_edges`. |
617 | let left = unsafe { |
618 | let begin_left = 2 * MAX_TX_SIZE - init_left; |
619 | let end_above = 2 * MAX_TX_SIZE + 1 + init_above; |
620 | slice_assume_init_mut(&mut edge_buf.data[begin_left..end_above]) |
621 | }; |
622 | let (left, top_left) = left.split_at(init_left); |
623 | let (top_left, above) = top_left.split_at(1); |
624 | Self(left, top_left, above) |
625 | } |
626 | |
627 | pub const fn as_slices(&self) -> (&'a [T], &'a [T], &'a [T]) { |
628 | (self.0, self.1, self.2) |
629 | } |
630 | |
631 | pub const fn top_left_ptr(&self) -> *const T { |
632 | self.1.as_ptr() |
633 | } |
634 | |
635 | #[cfg (any(test, feature = "bench" ))] |
636 | pub fn mock(edge_buf: &'a IntraEdgeMock<T>) -> Self { |
637 | let left = &edge_buf.data[..]; |
638 | let (left, top_left) = left.split_at(2 * MAX_TX_SIZE); |
639 | let (top_left, above) = top_left.split_at(1); |
640 | Self(left, top_left, above) |
641 | } |
642 | } |
643 | |
644 | pub fn get_intra_edges<'a, T: Pixel>( |
645 | edge_buf: &'a mut IntraEdgeBuffer<T>, |
646 | dst: &PlaneRegion<'_, T>, |
647 | partition_bo: TileBlockOffset, // partition bo, BlockOffset |
648 | bx: usize, |
649 | by: usize, |
650 | partition_size: BlockSize, // partition size, BlockSize |
651 | po: PlaneOffset, |
652 | tx_size: TxSize, |
653 | bit_depth: usize, |
654 | opt_mode: Option<PredictionMode>, |
655 | enable_intra_edge_filter: bool, |
656 | intra_param: IntraParam, |
657 | ) -> IntraEdge<'a, T> { |
658 | let mut init_left: usize = 0; |
659 | let mut init_above: usize = 0; |
660 | |
661 | let plane_cfg = &dst.plane_cfg; |
662 | |
663 | let base = 128u16 << (bit_depth - 8); |
664 | |
665 | { |
666 | // left pixels are ordered from bottom to top and right-aligned |
667 | let (left, not_left) = edge_buf.data.split_at_mut(2 * MAX_TX_SIZE); |
668 | let (top_left, above) = not_left.split_at_mut(1); |
669 | |
670 | let x = po.x as usize; |
671 | let y = po.y as usize; |
672 | |
673 | let mut needs_left = true; |
674 | let mut needs_topleft = true; |
675 | let mut needs_top = true; |
676 | let mut needs_topright = true; |
677 | let mut needs_bottomleft = true; |
678 | let mut needs_topleft_filter = false; |
679 | |
680 | if let Some(mut mode) = opt_mode { |
681 | mode = match mode { |
682 | PredictionMode::PAETH_PRED => match (x, y) { |
683 | (0, 0) => PredictionMode::DC_PRED, |
684 | (0, _) => PredictionMode::V_PRED, |
685 | (_, 0) => PredictionMode::H_PRED, |
686 | _ => PredictionMode::PAETH_PRED, |
687 | }, |
688 | _ => mode, |
689 | }; |
690 | |
691 | let p_angle = intra_mode_to_angle(mode) |
692 | + match intra_param { |
693 | IntraParam::AngleDelta(val) => (val * ANGLE_STEP) as isize, |
694 | _ => 0, |
695 | }; |
696 | |
697 | let dc_or_cfl = |
698 | mode == PredictionMode::DC_PRED || mode == PredictionMode::UV_CFL_PRED; |
699 | |
700 | needs_left = (!dc_or_cfl || x != 0) || (p_angle > 90 && p_angle != 180); |
701 | needs_topleft = mode == PredictionMode::PAETH_PRED |
702 | || (mode.is_directional() && p_angle != 90 && p_angle != 180); |
703 | needs_top = (!dc_or_cfl || y != 0) || (p_angle != 90 && p_angle < 180); |
704 | needs_topright = mode.is_directional() && p_angle < 90; |
705 | needs_bottomleft = mode.is_directional() && p_angle > 180; |
706 | needs_topleft_filter = |
707 | enable_intra_edge_filter && p_angle > 90 && p_angle < 180; |
708 | } |
709 | |
710 | let rect_w = |
711 | dst.rect().width.min(dst.plane_cfg.width - dst.rect().x as usize); |
712 | let rect_h = |
713 | dst.rect().height.min(dst.plane_cfg.height - dst.rect().y as usize); |
714 | |
715 | // Needs left |
716 | if needs_left { |
717 | let txh = if y + tx_size.height() > rect_h { |
718 | rect_h - y |
719 | } else { |
720 | tx_size.height() |
721 | }; |
722 | if x != 0 { |
723 | for i in 0..txh { |
724 | debug_assert!(y + i < rect_h); |
725 | left[2 * MAX_TX_SIZE - 1 - i].write(dst[y + i][x - 1]); |
726 | } |
727 | if txh < tx_size.height() { |
728 | let val = dst[y + txh - 1][x - 1]; |
729 | for i in txh..tx_size.height() { |
730 | left[2 * MAX_TX_SIZE - 1 - i].write(val); |
731 | } |
732 | } |
733 | } else { |
734 | let val = if y != 0 { dst[y - 1][0] } else { T::cast_from(base + 1) }; |
735 | for v in left[2 * MAX_TX_SIZE - tx_size.height()..].iter_mut() { |
736 | v.write(val); |
737 | } |
738 | } |
739 | init_left += tx_size.height(); |
740 | } |
741 | |
742 | // Needs top |
743 | if needs_top { |
744 | let txw = if x + tx_size.width() > rect_w { |
745 | rect_w - x |
746 | } else { |
747 | tx_size.width() |
748 | }; |
749 | if y != 0 { |
750 | above[..txw].copy_from_slice( |
751 | // SAFETY: &[T] and &[MaybeUninit<T>] have the same layout |
752 | unsafe { |
753 | transmute::<&[T], &[MaybeUninit<T>]>(&dst[y - 1][x..x + txw]) |
754 | }, |
755 | ); |
756 | if txw < tx_size.width() { |
757 | let val = dst[y - 1][x + txw - 1]; |
758 | for i in txw..tx_size.width() { |
759 | above[i].write(val); |
760 | } |
761 | } |
762 | } else { |
763 | let val = if x != 0 { dst[0][x - 1] } else { T::cast_from(base - 1) }; |
764 | for v in above[..tx_size.width()].iter_mut() { |
765 | v.write(val); |
766 | } |
767 | } |
768 | init_above += tx_size.width(); |
769 | } |
770 | |
771 | let bx4 = bx * (tx_size.width() >> MI_SIZE_LOG2); // bx,by are in tx block indices |
772 | let by4 = by * (tx_size.height() >> MI_SIZE_LOG2); |
773 | |
774 | let have_top = by4 != 0 |
775 | || if plane_cfg.ydec != 0 { |
776 | partition_bo.0.y > 1 |
777 | } else { |
778 | partition_bo.0.y > 0 |
779 | }; |
780 | let have_left = bx4 != 0 |
781 | || if plane_cfg.xdec != 0 { |
782 | partition_bo.0.x > 1 |
783 | } else { |
784 | partition_bo.0.x > 0 |
785 | }; |
786 | |
787 | let right_available = x + tx_size.width() < rect_w; |
788 | let bottom_available = y + tx_size.height() < rect_h; |
789 | |
790 | let scaled_partition_size = |
791 | supersample_chroma_bsize(partition_size, plane_cfg.xdec, plane_cfg.ydec); |
792 | |
793 | // Needs top right |
794 | if needs_topright { |
795 | debug_assert!(plane_cfg.xdec <= 1 && plane_cfg.ydec <= 1); |
796 | |
797 | let num_avail = if y != 0 |
798 | && has_top_right( |
799 | scaled_partition_size, |
800 | partition_bo, |
801 | have_top, |
802 | right_available, |
803 | tx_size, |
804 | by4, |
805 | bx4, |
806 | plane_cfg.xdec, |
807 | plane_cfg.ydec, |
808 | ) { |
809 | tx_size.width().min(rect_w - x - tx_size.width()) |
810 | } else { |
811 | 0 |
812 | }; |
813 | if num_avail > 0 { |
814 | above[tx_size.width()..][..num_avail].copy_from_slice( |
815 | // SAFETY: &[T] and &[MaybeUninit<T>] have the same layout |
816 | unsafe { |
817 | transmute::<&[T], &[MaybeUninit<T>]>( |
818 | &dst[y - 1][x + tx_size.width()..][..num_avail], |
819 | ) |
820 | }, |
821 | ); |
822 | } |
823 | if num_avail < tx_size.height() { |
824 | let val = above[tx_size.width() + num_avail - 1]; |
825 | for v in above |
826 | [tx_size.width() + num_avail..tx_size.width() + tx_size.height()] |
827 | .iter_mut() |
828 | { |
829 | *v = val; |
830 | } |
831 | } |
832 | init_above += tx_size.height(); |
833 | } |
834 | |
835 | // SAFETY: The blocks above have initialized the first `init_above` items. |
836 | let above = unsafe { slice_assume_init_mut(&mut above[..init_above]) }; |
837 | |
838 | // Needs bottom left |
839 | if needs_bottomleft { |
840 | debug_assert!(plane_cfg.xdec <= 1 && plane_cfg.ydec <= 1); |
841 | |
842 | let num_avail = if x != 0 |
843 | && has_bottom_left( |
844 | scaled_partition_size, |
845 | partition_bo, |
846 | bottom_available, |
847 | have_left, |
848 | tx_size, |
849 | by4, |
850 | bx4, |
851 | plane_cfg.xdec, |
852 | plane_cfg.ydec, |
853 | ) { |
854 | tx_size.height().min(rect_h - y - tx_size.height()) |
855 | } else { |
856 | 0 |
857 | }; |
858 | if num_avail > 0 { |
859 | for i in 0..num_avail { |
860 | left[2 * MAX_TX_SIZE - tx_size.height() - 1 - i] |
861 | .write(dst[y + tx_size.height() + i][x - 1]); |
862 | } |
863 | } |
864 | if num_avail < tx_size.width() { |
865 | let val = left[2 * MAX_TX_SIZE - tx_size.height() - num_avail]; |
866 | for v in left[(2 * MAX_TX_SIZE - tx_size.height() - tx_size.width()) |
867 | ..(2 * MAX_TX_SIZE - tx_size.height() - num_avail)] |
868 | .iter_mut() |
869 | { |
870 | *v = val; |
871 | } |
872 | } |
873 | init_left += tx_size.width(); |
874 | } |
875 | |
876 | // SAFETY: The blocks above have initialized last `init_left` items. |
877 | let left = unsafe { |
878 | slice_assume_init_mut(&mut left[2 * MAX_TX_SIZE - init_left..]) |
879 | }; |
880 | |
881 | // Needs top-left |
882 | if needs_topleft { |
883 | let top_left = top_left[0].write(match (x, y) { |
884 | (0, 0) => T::cast_from(base), |
885 | (_, 0) => dst[0][x - 1], |
886 | (0, _) => dst[y - 1][0], |
887 | _ => dst[y - 1][x - 1], |
888 | }); |
889 | |
890 | let (w, h) = (tx_size.width(), tx_size.height()); |
891 | if needs_topleft_filter && w + h >= 24 { |
892 | let (l, a, tl): (u32, u32, u32) = |
893 | (left[left.len() - 1].into(), above[0].into(), (*top_left).into()); |
894 | let s = l * 5 + tl * 6 + a * 5; |
895 | |
896 | *top_left = T::cast_from((s + (1 << 3)) >> 4); |
897 | } |
898 | } else { |
899 | top_left[0].write(T::cast_from(base)); |
900 | } |
901 | } |
902 | IntraEdge::new(edge_buf, init_left, init_above) |
903 | } |
904 | |
905 | pub fn has_tr(bo: TileBlockOffset, bsize: BlockSize) -> bool { |
906 | let sb_mi_size = BLOCK_64X64.width_mi(); /* Assume 64x64 for now */ |
907 | let mask_row = bo.0.y & LOCAL_BLOCK_MASK; |
908 | let mask_col = bo.0.x & LOCAL_BLOCK_MASK; |
909 | let target_n4_w = bsize.width_mi(); |
910 | let target_n4_h = bsize.height_mi(); |
911 | |
912 | let mut bs = target_n4_w.max(target_n4_h); |
913 | |
914 | if bs > BLOCK_64X64.width_mi() { |
915 | return false; |
916 | } |
917 | |
918 | let mut has_tr = !((mask_row & bs) != 0 && (mask_col & bs) != 0); |
919 | |
920 | /* TODO: assert its a power of two */ |
921 | |
922 | while bs < sb_mi_size { |
923 | if (mask_col & bs) != 0 { |
924 | if (mask_col & (2 * bs) != 0) && (mask_row & (2 * bs) != 0) { |
925 | has_tr = false; |
926 | break; |
927 | } |
928 | } else { |
929 | break; |
930 | } |
931 | bs <<= 1; |
932 | } |
933 | |
934 | /* The left hand of two vertical rectangles always has a top right (as the |
935 | * block above will have been decoded) */ |
936 | if (target_n4_w < target_n4_h) && (bo.0.x & target_n4_w) == 0 { |
937 | has_tr = true; |
938 | } |
939 | |
940 | /* The bottom of two horizontal rectangles never has a top right (as the block |
941 | * to the right won't have been decoded) */ |
942 | if (target_n4_w > target_n4_h) && (bo.0.y & target_n4_h) != 0 { |
943 | has_tr = false; |
944 | } |
945 | |
946 | /* The bottom left square of a Vertical A (in the old format) does |
947 | * not have a top right as it is decoded before the right hand |
948 | * rectangle of the partition */ |
949 | /* |
950 | if blk.partition == PartitionType::PARTITION_VERT_A { |
951 | if blk.n4_w == blk.n4_h { |
952 | if (mask_row & bs) != 0 { |
953 | has_tr = false; |
954 | } |
955 | } |
956 | } |
957 | */ |
958 | |
959 | has_tr |
960 | } |
961 | |
962 | pub fn has_bl(bo: TileBlockOffset, bsize: BlockSize) -> bool { |
963 | let sb_mi_size = BLOCK_64X64.width_mi(); /* Assume 64x64 for now */ |
964 | let mask_row = bo.0.y & LOCAL_BLOCK_MASK; |
965 | let mask_col = bo.0.x & LOCAL_BLOCK_MASK; |
966 | let target_n4_w = bsize.width_mi(); |
967 | let target_n4_h = bsize.height_mi(); |
968 | |
969 | let mut bs = target_n4_w.max(target_n4_h); |
970 | |
971 | if bs > BLOCK_64X64.width_mi() { |
972 | return false; |
973 | } |
974 | |
975 | let mut has_bl = |
976 | (mask_row & bs) == 0 && (mask_col & bs) == 0 && bs < sb_mi_size; |
977 | |
978 | /* TODO: assert its a power of two */ |
979 | |
980 | while 2 * bs < sb_mi_size { |
981 | if (mask_col & bs) == 0 { |
982 | if (mask_col & (2 * bs) == 0) && (mask_row & (2 * bs) == 0) { |
983 | has_bl = true; |
984 | break; |
985 | } |
986 | } else { |
987 | break; |
988 | } |
989 | bs <<= 1; |
990 | } |
991 | |
992 | /* The right hand of two vertical rectangles never has a bottom left (as the |
993 | * block below won't have been decoded) */ |
994 | if (target_n4_w < target_n4_h) && (bo.0.x & target_n4_w) != 0 { |
995 | has_bl = false; |
996 | } |
997 | |
998 | /* The top of two horizontal rectangles always has a bottom left (as the block |
999 | * to the left will have been decoded) */ |
1000 | if (target_n4_w > target_n4_h) && (bo.0.y & target_n4_h) == 0 { |
1001 | has_bl = true; |
1002 | } |
1003 | |
1004 | /* The bottom left square of a Vertical A (in the old format) does |
1005 | * not have a top right as it is decoded before the right hand |
1006 | * rectangle of the partition */ |
1007 | /* |
1008 | if blk.partition == PartitionType::PARTITION_VERT_A { |
1009 | if blk.n4_w == blk.n4_h { |
1010 | if (mask_row & bs) != 0 { |
1011 | has_tr = false; |
1012 | } |
1013 | } |
1014 | } |
1015 | */ |
1016 | |
1017 | has_bl |
1018 | } |
1019 | |
1020 | #[cfg (test)] |
1021 | mod tests { |
1022 | use crate::partition::BlockSize::*; |
1023 | use crate::partition::{BlockSize, InvalidBlockSize}; |
1024 | |
1025 | #[test ] |
1026 | fn from_wh_matches_naive() { |
1027 | fn from_wh_opt_naive( |
1028 | w: usize, h: usize, |
1029 | ) -> Result<BlockSize, InvalidBlockSize> { |
1030 | match (w, h) { |
1031 | (4, 4) => Ok(BLOCK_4X4), |
1032 | (4, 8) => Ok(BLOCK_4X8), |
1033 | (8, 4) => Ok(BLOCK_8X4), |
1034 | (8, 8) => Ok(BLOCK_8X8), |
1035 | (8, 16) => Ok(BLOCK_8X16), |
1036 | (16, 8) => Ok(BLOCK_16X8), |
1037 | (16, 16) => Ok(BLOCK_16X16), |
1038 | (16, 32) => Ok(BLOCK_16X32), |
1039 | (32, 16) => Ok(BLOCK_32X16), |
1040 | (32, 32) => Ok(BLOCK_32X32), |
1041 | (32, 64) => Ok(BLOCK_32X64), |
1042 | (64, 32) => Ok(BLOCK_64X32), |
1043 | (64, 64) => Ok(BLOCK_64X64), |
1044 | (64, 128) => Ok(BLOCK_64X128), |
1045 | (128, 64) => Ok(BLOCK_128X64), |
1046 | (128, 128) => Ok(BLOCK_128X128), |
1047 | (4, 16) => Ok(BLOCK_4X16), |
1048 | (16, 4) => Ok(BLOCK_16X4), |
1049 | (8, 32) => Ok(BLOCK_8X32), |
1050 | (32, 8) => Ok(BLOCK_32X8), |
1051 | (16, 64) => Ok(BLOCK_16X64), |
1052 | (64, 16) => Ok(BLOCK_64X16), |
1053 | _ => Err(InvalidBlockSize), |
1054 | } |
1055 | } |
1056 | |
1057 | for w in 0..256 { |
1058 | for h in 0..256 { |
1059 | let a = BlockSize::from_width_and_height_opt(w, h); |
1060 | let b = from_wh_opt_naive(w, h); |
1061 | |
1062 | assert_eq!(a, b); |
1063 | } |
1064 | } |
1065 | } |
1066 | } |
1067 | |