1#![allow(dead_code)]
29#![allow(clippy::cast_possible_truncation)]
30#![allow(clippy::similar_names)]
31#![allow(clippy::struct_excessive_bools)]
32#![allow(clippy::bool_to_int_with_if)]
33#![allow(clippy::match_same_arms)]
34
35use super::transform::TxSize;
36
37const fn const_min_u32(a: u32, b: u32) -> u32 {
39 if a < b {
40 a
41 } else {
42 b
43 }
44}
45
46pub const MAX_PLANES: usize = 3;
52
53pub const MAX_SEGMENTS: usize = 8;
55
56pub const MAX_SB_SIZE: usize = 128;
58
59pub const MAX_SB_SQUARE: usize = MAX_SB_SIZE / 4;
61
62pub const MIN_BLOCK_SIZE: usize = 4;
64
65pub const BLOCK_SIZES: usize = 22;
67
68pub const PARTITION_TYPES: usize = 10;
70
71pub const INTRA_MODES: usize = 13;
73
74pub const INTER_MODES: usize = 4;
76
77pub const REF_FRAMES: usize = 8;
79
80#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
86pub enum BlockSize {
87 #[default]
89 Block4x4 = 0,
90 Block4x8 = 1,
92 Block8x4 = 2,
94 Block8x8 = 3,
96 Block8x16 = 4,
98 Block16x8 = 5,
100 Block16x16 = 6,
102 Block16x32 = 7,
104 Block32x16 = 8,
106 Block32x32 = 9,
108 Block32x64 = 10,
110 Block64x32 = 11,
112 Block64x64 = 12,
114 Block64x128 = 13,
116 Block128x64 = 14,
118 Block128x128 = 15,
120 Block4x16 = 16,
122 Block16x4 = 17,
124 Block8x32 = 18,
126 Block32x8 = 19,
128 Block16x64 = 20,
130 Block64x16 = 21,
132}
133
134impl BlockSize {
135 #[must_use]
137 pub const fn width(self) -> u32 {
138 match self {
139 Self::Block4x4 | Self::Block4x8 | Self::Block4x16 => 4,
140 Self::Block8x4 | Self::Block8x8 | Self::Block8x16 | Self::Block8x32 => 8,
141 Self::Block16x4
142 | Self::Block16x8
143 | Self::Block16x16
144 | Self::Block16x32
145 | Self::Block16x64 => 16,
146 Self::Block32x8 | Self::Block32x16 | Self::Block32x32 | Self::Block32x64 => 32,
147 Self::Block64x16 | Self::Block64x32 | Self::Block64x64 | Self::Block64x128 => 64,
148 Self::Block128x64 | Self::Block128x128 => 128,
149 }
150 }
151
152 #[must_use]
154 pub const fn height(self) -> u32 {
155 match self {
156 Self::Block4x4 | Self::Block8x4 | Self::Block16x4 => 4,
157 Self::Block4x8 | Self::Block8x8 | Self::Block16x8 | Self::Block32x8 => 8,
158 Self::Block4x16
159 | Self::Block8x16
160 | Self::Block16x16
161 | Self::Block32x16
162 | Self::Block64x16 => 16,
163 Self::Block8x32 | Self::Block16x32 | Self::Block32x32 | Self::Block64x32 => 32,
164 Self::Block16x64 | Self::Block32x64 | Self::Block64x64 | Self::Block128x64 => 64,
165 Self::Block64x128 | Self::Block128x128 => 128,
166 }
167 }
168
169 #[must_use]
171 pub const fn width_log2(self) -> u8 {
172 match self.width() {
173 4 => 2,
174 8 => 3,
175 16 => 4,
176 32 => 5,
177 64 => 6,
178 128 => 7,
179 _ => 0,
180 }
181 }
182
183 #[must_use]
185 pub const fn height_log2(self) -> u8 {
186 match self.height() {
187 4 => 2,
188 8 => 3,
189 16 => 4,
190 32 => 5,
191 64 => 6,
192 128 => 7,
193 _ => 0,
194 }
195 }
196
197 #[must_use]
199 pub const fn width_mi(self) -> u32 {
200 self.width() / 4
201 }
202
203 #[must_use]
205 pub const fn height_mi(self) -> u32 {
206 self.height() / 4
207 }
208
209 #[must_use]
211 pub const fn is_square(self) -> bool {
212 self.width() == self.height()
213 }
214
215 #[must_use]
217 pub const fn is_superblock(self) -> bool {
218 matches!(self, Self::Block64x64 | Self::Block128x128)
219 }
220
221 #[must_use]
223 pub const fn area(self) -> u32 {
224 self.width() * self.height()
225 }
226
227 #[must_use]
229 pub const fn from_u8(val: u8) -> Option<Self> {
230 match val {
231 0 => Some(Self::Block4x4),
232 1 => Some(Self::Block4x8),
233 2 => Some(Self::Block8x4),
234 3 => Some(Self::Block8x8),
235 4 => Some(Self::Block8x16),
236 5 => Some(Self::Block16x8),
237 6 => Some(Self::Block16x16),
238 7 => Some(Self::Block16x32),
239 8 => Some(Self::Block32x16),
240 9 => Some(Self::Block32x32),
241 10 => Some(Self::Block32x64),
242 11 => Some(Self::Block64x32),
243 12 => Some(Self::Block64x64),
244 13 => Some(Self::Block64x128),
245 14 => Some(Self::Block128x64),
246 15 => Some(Self::Block128x128),
247 16 => Some(Self::Block4x16),
248 17 => Some(Self::Block16x4),
249 18 => Some(Self::Block8x32),
250 19 => Some(Self::Block32x8),
251 20 => Some(Self::Block16x64),
252 21 => Some(Self::Block64x16),
253 _ => None,
254 }
255 }
256
257 #[must_use]
259 pub const fn from_dimensions(width: u32, height: u32) -> Option<Self> {
260 match (width, height) {
261 (4, 4) => Some(Self::Block4x4),
262 (4, 8) => Some(Self::Block4x8),
263 (8, 4) => Some(Self::Block8x4),
264 (8, 8) => Some(Self::Block8x8),
265 (8, 16) => Some(Self::Block8x16),
266 (16, 8) => Some(Self::Block16x8),
267 (16, 16) => Some(Self::Block16x16),
268 (16, 32) => Some(Self::Block16x32),
269 (32, 16) => Some(Self::Block32x16),
270 (32, 32) => Some(Self::Block32x32),
271 (32, 64) => Some(Self::Block32x64),
272 (64, 32) => Some(Self::Block64x32),
273 (64, 64) => Some(Self::Block64x64),
274 (64, 128) => Some(Self::Block64x128),
275 (128, 64) => Some(Self::Block128x64),
276 (128, 128) => Some(Self::Block128x128),
277 (4, 16) => Some(Self::Block4x16),
278 (16, 4) => Some(Self::Block16x4),
279 (8, 32) => Some(Self::Block8x32),
280 (32, 8) => Some(Self::Block32x8),
281 (16, 64) => Some(Self::Block16x64),
282 (64, 16) => Some(Self::Block64x16),
283 _ => None,
284 }
285 }
286
287 #[must_use]
289 pub const fn max_tx_size(self) -> TxSize {
290 match const_min_u32(self.width(), self.height()) {
291 4 => TxSize::Tx4x4,
292 8 => TxSize::Tx8x8,
293 16 => TxSize::Tx16x16,
294 32 => TxSize::Tx32x32,
295 _ => TxSize::Tx64x64,
296 }
297 }
298
299 #[must_use]
301 pub const fn subsampled(self, subx: bool, suby: bool) -> Option<Self> {
302 let w = if subx { self.width() / 2 } else { self.width() };
303 let h = if suby {
304 self.height() / 2
305 } else {
306 self.height()
307 };
308 Self::from_dimensions(w, h)
309 }
310}
311
312#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
318pub enum PartitionType {
319 #[default]
321 None = 0,
322 Horz = 1,
324 Vert = 2,
326 Split = 3,
328 HorzA = 4,
330 HorzB = 5,
332 VertA = 6,
334 VertB = 7,
336 Horz4 = 8,
338 Vert4 = 9,
340}
341
342impl PartitionType {
343 #[must_use]
345 pub const fn from_u8(val: u8) -> Option<Self> {
346 match val {
347 0 => Some(Self::None),
348 1 => Some(Self::Horz),
349 2 => Some(Self::Vert),
350 3 => Some(Self::Split),
351 4 => Some(Self::HorzA),
352 5 => Some(Self::HorzB),
353 6 => Some(Self::VertA),
354 7 => Some(Self::VertB),
355 8 => Some(Self::Horz4),
356 9 => Some(Self::Vert4),
357 _ => None,
358 }
359 }
360
361 #[must_use]
363 pub const fn num_sub_blocks(self) -> u8 {
364 match self {
365 Self::None => 1,
366 Self::Horz | Self::Vert => 2,
367 Self::Split => 4,
368 Self::HorzA | Self::HorzB | Self::VertA | Self::VertB => 3,
369 Self::Horz4 | Self::Vert4 => 4,
370 }
371 }
372
373 #[must_use]
375 pub const fn is_split(self) -> bool {
376 matches!(self, Self::Split)
377 }
378
379 #[must_use]
381 pub const fn is_leaf(self) -> bool {
382 matches!(self, Self::None)
383 }
384}
385
386#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
392pub enum IntraMode {
393 #[default]
395 DcPred = 0,
396 VPred = 1,
398 HPred = 2,
400 D45Pred = 3,
402 D135Pred = 4,
404 D113Pred = 5,
406 D157Pred = 6,
408 D203Pred = 7,
410 D67Pred = 8,
412 SmoothPred = 9,
414 SmoothVPred = 10,
416 SmoothHPred = 11,
418 PaethPred = 12,
420}
421
422impl IntraMode {
423 #[must_use]
425 pub const fn from_u8(val: u8) -> Option<Self> {
426 match val {
427 0 => Some(Self::DcPred),
428 1 => Some(Self::VPred),
429 2 => Some(Self::HPred),
430 3 => Some(Self::D45Pred),
431 4 => Some(Self::D135Pred),
432 5 => Some(Self::D113Pred),
433 6 => Some(Self::D157Pred),
434 7 => Some(Self::D203Pred),
435 8 => Some(Self::D67Pred),
436 9 => Some(Self::SmoothPred),
437 10 => Some(Self::SmoothVPred),
438 11 => Some(Self::SmoothHPred),
439 12 => Some(Self::PaethPred),
440 _ => None,
441 }
442 }
443
444 #[must_use]
446 pub const fn is_directional(self) -> bool {
447 matches!(
448 self,
449 Self::VPred
450 | Self::HPred
451 | Self::D45Pred
452 | Self::D135Pred
453 | Self::D113Pred
454 | Self::D157Pred
455 | Self::D203Pred
456 | Self::D67Pred
457 )
458 }
459
460 #[must_use]
462 pub const fn angle_delta_allowed(self) -> bool {
463 self.is_directional()
464 }
465}
466
467#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
473pub enum InterMode {
474 #[default]
476 NearestMv = 0,
477 NearMv = 1,
479 GlobalMv = 2,
481 NewMv = 3,
483}
484
485impl InterMode {
486 #[must_use]
488 pub const fn from_u8(val: u8) -> Option<Self> {
489 match val {
490 0 => Some(Self::NearestMv),
491 1 => Some(Self::NearMv),
492 2 => Some(Self::GlobalMv),
493 3 => Some(Self::NewMv),
494 _ => None,
495 }
496 }
497
498 #[must_use]
500 pub const fn has_newmv(self) -> bool {
501 matches!(self, Self::NewMv)
502 }
503
504 #[must_use]
506 pub const fn is_global(self) -> bool {
507 matches!(self, Self::GlobalMv)
508 }
509}
510
511#[derive(Clone, Debug, Default)]
517pub struct BlockModeInfo {
518 pub block_size: BlockSize,
520 pub segment_id: u8,
522 pub skip: bool,
524 pub skip_mode: bool,
526 pub is_inter: bool,
528 pub intra_mode: IntraMode,
530 pub uv_mode: IntraMode,
532 pub angle_delta: [i8; 2],
534 pub inter_mode: InterMode,
536 pub ref_frames: [i8; 2],
538 pub mv: [[i16; 2]; 2],
540 pub tx_size: TxSize,
542 pub use_palette: bool,
544 pub filter_intra_mode: u8,
546 pub compound_type: u8,
548 pub interp_filter: [u8; 2],
550 pub motion_mode: u8,
552}
553
554impl BlockModeInfo {
555 #[must_use]
557 pub const fn new() -> Self {
558 Self {
559 block_size: BlockSize::Block4x4,
560 segment_id: 0,
561 skip: false,
562 skip_mode: false,
563 is_inter: false,
564 intra_mode: IntraMode::DcPred,
565 uv_mode: IntraMode::DcPred,
566 angle_delta: [0, 0],
567 inter_mode: InterMode::NearestMv,
568 ref_frames: [-1, -1],
569 mv: [[0, 0], [0, 0]],
570 tx_size: TxSize::Tx4x4,
571 use_palette: false,
572 filter_intra_mode: 0,
573 compound_type: 0,
574 interp_filter: [0, 0],
575 motion_mode: 0,
576 }
577 }
578
579 #[must_use]
581 pub const fn is_intra(&self) -> bool {
582 !self.is_inter
583 }
584
585 #[must_use]
587 pub const fn is_compound(&self) -> bool {
588 self.ref_frames[1] >= 0
589 }
590
591 #[must_use]
593 pub const fn num_refs(&self) -> u8 {
594 if self.ref_frames[1] >= 0 {
595 2
596 } else if self.ref_frames[0] >= 0 {
597 1
598 } else {
599 0
600 }
601 }
602}
603
604#[derive(Clone, Debug, Default)]
610pub struct PlaneBlockContext {
611 pub width: u32,
613 pub height: u32,
615 pub x: u32,
617 pub y: u32,
619 pub tx_size: TxSize,
621 pub tx_width: u32,
623 pub tx_height: u32,
625 pub subx: bool,
627 pub suby: bool,
629}
630
631impl PlaneBlockContext {
632 #[must_use]
634 pub const fn new(plane: usize, subx: bool, suby: bool) -> Self {
635 Self {
636 width: 4,
637 height: 4,
638 x: 0,
639 y: 0,
640 tx_size: TxSize::Tx4x4,
641 tx_width: 1,
642 tx_height: 1,
643 subx: plane > 0 && subx,
644 suby: plane > 0 && suby,
645 }
646 }
647
648 pub fn set_from_block(&mut self, bx: u32, by: u32, bsize: BlockSize) {
650 let scale_x = if self.subx { 2 } else { 1 };
651 let scale_y = if self.suby { 2 } else { 1 };
652
653 self.width = bsize.width() / scale_x;
654 self.height = bsize.height() / scale_y;
655 self.x = bx / scale_x;
656 self.y = by / scale_y;
657
658 self.tx_width = self.width / self.tx_size.width();
660 self.tx_height = self.height / self.tx_size.height();
661 }
662
663 #[must_use]
665 pub const fn num_tx_blocks(&self) -> u32 {
666 self.tx_width * self.tx_height
667 }
668}
669
670#[derive(Clone, Debug)]
676pub struct BlockContextManager {
677 pub planes: [PlaneBlockContext; MAX_PLANES],
679 pub mode_info: BlockModeInfo,
681 pub mi_row: u32,
683 pub mi_col: u32,
685 pub above_ctx: Vec<u8>,
687 pub left_ctx: Vec<u8>,
689}
690
691impl BlockContextManager {
692 #[must_use]
694 pub fn new(width_mi: u32, subx: bool, suby: bool) -> Self {
695 Self {
696 planes: [
697 PlaneBlockContext::new(0, subx, suby),
698 PlaneBlockContext::new(1, subx, suby),
699 PlaneBlockContext::new(2, subx, suby),
700 ],
701 mode_info: BlockModeInfo::new(),
702 mi_row: 0,
703 mi_col: 0,
704 above_ctx: vec![0; width_mi as usize],
705 left_ctx: vec![0; MAX_SB_SQUARE],
706 }
707 }
708
709 pub fn set_position(&mut self, mi_row: u32, mi_col: u32, bsize: BlockSize) {
711 self.mi_row = mi_row;
712 self.mi_col = mi_col;
713 self.mode_info.block_size = bsize;
714
715 let bx = mi_col * 4;
716 let by = mi_row * 4;
717
718 for plane in &mut self.planes {
719 plane.set_from_block(bx, by, bsize);
720 }
721 }
722
723 #[must_use]
725 pub fn get_partition_context(&self, bsize: BlockSize) -> u8 {
726 let bs = bsize.width_log2();
727 let above = self.get_above_ctx(0);
728 let left = self.get_left_ctx(0);
729
730 let ctx = u8::from(left < bs) + u8::from(above < bs);
732 ctx.min(3)
733 }
734
735 #[must_use]
737 pub fn get_above_ctx(&self, offset: u32) -> u8 {
738 let col = self.mi_col as usize + offset as usize;
739 if col < self.above_ctx.len() {
740 self.above_ctx[col]
741 } else {
742 0
743 }
744 }
745
746 #[must_use]
748 pub fn get_left_ctx(&self, offset: u32) -> u8 {
749 let row = (self.mi_row as usize + offset as usize) % MAX_SB_SQUARE;
750 if row < self.left_ctx.len() {
751 self.left_ctx[row]
752 } else {
753 0
754 }
755 }
756
757 pub fn update_context(&mut self, bsize: BlockSize) {
759 let w = bsize.width_mi() as usize;
760 let h = bsize.height_mi() as usize;
761 let ctx_val = if self.mode_info.is_inter { 1 } else { 0 };
762
763 let col = self.mi_col as usize;
765 for i in 0..w {
766 if col + i < self.above_ctx.len() {
767 self.above_ctx[col + i] = ctx_val;
768 }
769 }
770
771 let row = self.mi_row as usize;
773 for i in 0..h {
774 let r = (row + i) % MAX_SB_SQUARE;
775 if r < self.left_ctx.len() {
776 self.left_ctx[r] = ctx_val;
777 }
778 }
779 }
780
781 pub fn reset_left_context(&mut self) {
783 self.left_ctx.fill(0);
784 }
785}
786
787impl Default for BlockContextManager {
788 fn default() -> Self {
789 Self::new(1920 / 4, true, true)
790 }
791}
792
793#[cfg(test)]
798mod tests {
799 use super::*;
800
801 #[test]
802 fn test_block_size_dimensions() {
803 assert_eq!(BlockSize::Block4x4.width(), 4);
804 assert_eq!(BlockSize::Block4x4.height(), 4);
805 assert_eq!(BlockSize::Block128x128.width(), 128);
806 assert_eq!(BlockSize::Block128x128.height(), 128);
807 assert_eq!(BlockSize::Block4x8.width(), 4);
808 assert_eq!(BlockSize::Block4x8.height(), 8);
809 }
810
811 #[test]
812 fn test_block_size_log2() {
813 assert_eq!(BlockSize::Block4x4.width_log2(), 2);
814 assert_eq!(BlockSize::Block8x8.width_log2(), 3);
815 assert_eq!(BlockSize::Block16x16.width_log2(), 4);
816 assert_eq!(BlockSize::Block128x128.width_log2(), 7);
817 }
818
819 #[test]
820 fn test_block_size_mi() {
821 assert_eq!(BlockSize::Block4x4.width_mi(), 1);
822 assert_eq!(BlockSize::Block8x8.width_mi(), 2);
823 assert_eq!(BlockSize::Block128x128.width_mi(), 32);
824 }
825
826 #[test]
827 fn test_block_size_is_square() {
828 assert!(BlockSize::Block4x4.is_square());
829 assert!(BlockSize::Block8x8.is_square());
830 assert!(!BlockSize::Block4x8.is_square());
831 assert!(!BlockSize::Block8x4.is_square());
832 }
833
834 #[test]
835 fn test_block_size_from_u8() {
836 assert_eq!(BlockSize::from_u8(0), Some(BlockSize::Block4x4));
837 assert_eq!(BlockSize::from_u8(15), Some(BlockSize::Block128x128));
838 assert_eq!(BlockSize::from_u8(100), None);
839 }
840
841 #[test]
842 fn test_block_size_from_dimensions() {
843 assert_eq!(BlockSize::from_dimensions(4, 4), Some(BlockSize::Block4x4));
844 assert_eq!(
845 BlockSize::from_dimensions(128, 128),
846 Some(BlockSize::Block128x128)
847 );
848 assert_eq!(BlockSize::from_dimensions(3, 3), None);
849 }
850
851 #[test]
852 fn test_partition_type() {
853 assert_eq!(PartitionType::None.num_sub_blocks(), 1);
854 assert_eq!(PartitionType::Horz.num_sub_blocks(), 2);
855 assert_eq!(PartitionType::Split.num_sub_blocks(), 4);
856 assert!(PartitionType::Split.is_split());
857 assert!(PartitionType::None.is_leaf());
858 }
859
860 #[test]
861 fn test_intra_mode() {
862 assert!(IntraMode::VPred.is_directional());
863 assert!(!IntraMode::DcPred.is_directional());
864 assert!(IntraMode::D45Pred.angle_delta_allowed());
865 }
866
867 #[test]
868 fn test_inter_mode() {
869 assert!(InterMode::NewMv.has_newmv());
870 assert!(InterMode::GlobalMv.is_global());
871 assert!(!InterMode::NearestMv.has_newmv());
872 }
873
874 #[test]
875 fn test_block_mode_info() {
876 let info = BlockModeInfo::new();
877 assert!(!info.is_inter);
878 assert!(info.is_intra());
879 assert!(!info.is_compound());
880 assert_eq!(info.num_refs(), 0);
881 }
882
883 #[test]
884 fn test_plane_block_context() {
885 let mut ctx = PlaneBlockContext::new(1, true, true);
886 ctx.set_from_block(16, 16, BlockSize::Block8x8);
887
888 assert_eq!(ctx.width, 4); assert_eq!(ctx.height, 4);
890 assert_eq!(ctx.x, 8); }
892
893 #[test]
894 fn test_block_context_manager() {
895 let mut mgr = BlockContextManager::new(480, true, true);
896 mgr.set_position(4, 8, BlockSize::Block16x16);
897
898 assert_eq!(mgr.mi_row, 4);
899 assert_eq!(mgr.mi_col, 8);
900 }
901
902 #[test]
903 fn test_block_size_subsampled() {
904 let size = BlockSize::Block16x16;
905 let subsampled = size.subsampled(true, true);
906
907 assert_eq!(subsampled, Some(BlockSize::Block8x8));
908 }
909
910 #[test]
911 fn test_block_size_max_tx_size() {
912 assert_eq!(BlockSize::Block4x4.max_tx_size(), TxSize::Tx4x4);
913 assert_eq!(BlockSize::Block8x8.max_tx_size(), TxSize::Tx8x8);
914 assert_eq!(BlockSize::Block16x16.max_tx_size(), TxSize::Tx16x16);
915 }
916
917 #[test]
918 fn test_constants() {
919 assert_eq!(MAX_PLANES, 3);
920 assert_eq!(MAX_SEGMENTS, 8);
921 assert_eq!(MAX_SB_SIZE, 128);
922 assert_eq!(BLOCK_SIZES, 22);
923 assert_eq!(PARTITION_TYPES, 10);
924 assert_eq!(INTRA_MODES, 13);
925 }
926
927 #[test]
928 fn test_block_context_manager_update() {
929 let mut mgr = BlockContextManager::new(480, true, true);
930 mgr.set_position(0, 0, BlockSize::Block8x8);
931 mgr.mode_info.is_inter = true;
932 mgr.update_context(BlockSize::Block8x8);
933
934 assert_eq!(mgr.above_ctx[0], 1);
935 assert_eq!(mgr.above_ctx[1], 1);
936 }
937}