Skip to main content

oximedia_codec/av1/
block.rs

1//! AV1 block-level data structures and parsing.
2//!
3//! This module contains the data structures for representing block-level
4//! information in AV1, including:
5//!
6//! - Block sizes from 4x4 to 128x128
7//! - Block partition types
8//! - Block mode information (intra/inter modes)
9//! - Per-plane block context
10//!
11//! # Block Sizes
12//!
13//! AV1 supports a wide range of block sizes from 4x4 to 128x128, with
14//! various rectangular shapes. The superblock size can be either 64x64
15//! or 128x128.
16//!
17//! # Partitions
18//!
19//! Blocks can be partitioned recursively into smaller blocks using
20//! 10 different partition types including splits, horizontal/vertical
21//! splits, and various T-shaped partitions.
22//!
23//! # Reference
24//!
25//! See AV1 Specification Section 5.9 for block syntax and Section 6.4
26//! for block semantics.
27
28#![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
37/// Const-compatible min function for u32.
38const fn const_min_u32(a: u32, b: u32) -> u32 {
39    if a < b {
40        a
41    } else {
42        b
43    }
44}
45
46// =============================================================================
47// Constants
48// =============================================================================
49
50/// Maximum number of planes (Y, U, V).
51pub const MAX_PLANES: usize = 3;
52
53/// Maximum number of segments.
54pub const MAX_SEGMENTS: usize = 8;
55
56/// Maximum superblock size (128x128).
57pub const MAX_SB_SIZE: usize = 128;
58
59/// Maximum superblock size in 4x4 units.
60pub const MAX_SB_SQUARE: usize = MAX_SB_SIZE / 4;
61
62/// Minimum block size (4x4).
63pub const MIN_BLOCK_SIZE: usize = 4;
64
65/// Number of block sizes.
66pub const BLOCK_SIZES: usize = 22;
67
68/// Number of partition types.
69pub const PARTITION_TYPES: usize = 10;
70
71/// Number of intra modes.
72pub const INTRA_MODES: usize = 13;
73
74/// Number of inter modes.
75pub const INTER_MODES: usize = 4;
76
77/// Number of reference frames.
78pub const REF_FRAMES: usize = 8;
79
80// =============================================================================
81// Block Size Enum
82// =============================================================================
83
84/// Block size enumeration.
85#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
86pub enum BlockSize {
87    /// 4x4 block.
88    #[default]
89    Block4x4 = 0,
90    /// 4x8 block.
91    Block4x8 = 1,
92    /// 8x4 block.
93    Block8x4 = 2,
94    /// 8x8 block.
95    Block8x8 = 3,
96    /// 8x16 block.
97    Block8x16 = 4,
98    /// 16x8 block.
99    Block16x8 = 5,
100    /// 16x16 block.
101    Block16x16 = 6,
102    /// 16x32 block.
103    Block16x32 = 7,
104    /// 32x16 block.
105    Block32x16 = 8,
106    /// 32x32 block.
107    Block32x32 = 9,
108    /// 32x64 block.
109    Block32x64 = 10,
110    /// 64x32 block.
111    Block64x32 = 11,
112    /// 64x64 block.
113    Block64x64 = 12,
114    /// 64x128 block.
115    Block64x128 = 13,
116    /// 128x64 block.
117    Block128x64 = 14,
118    /// 128x128 block.
119    Block128x128 = 15,
120    /// 4x16 block.
121    Block4x16 = 16,
122    /// 16x4 block.
123    Block16x4 = 17,
124    /// 8x32 block.
125    Block8x32 = 18,
126    /// 32x8 block.
127    Block32x8 = 19,
128    /// 16x64 block.
129    Block16x64 = 20,
130    /// 64x16 block.
131    Block64x16 = 21,
132}
133
134impl BlockSize {
135    /// Get width in samples.
136    #[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    /// Get height in samples.
153    #[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    /// Get width log2.
170    #[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    /// Get height log2.
184    #[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    /// Get width in 4x4 units.
198    #[must_use]
199    pub const fn width_mi(self) -> u32 {
200        self.width() / 4
201    }
202
203    /// Get height in 4x4 units.
204    #[must_use]
205    pub const fn height_mi(self) -> u32 {
206        self.height() / 4
207    }
208
209    /// Check if this is a square block.
210    #[must_use]
211    pub const fn is_square(self) -> bool {
212        self.width() == self.height()
213    }
214
215    /// Check if this block size is valid for a superblock.
216    #[must_use]
217    pub const fn is_superblock(self) -> bool {
218        matches!(self, Self::Block64x64 | Self::Block128x128)
219    }
220
221    /// Get the area in samples.
222    #[must_use]
223    pub const fn area(self) -> u32 {
224        self.width() * self.height()
225    }
226
227    /// Convert from integer value.
228    #[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    /// Get block size from dimensions.
258    #[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    /// Get maximum transform size for this block.
288    #[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    /// Get subsampled block size for chroma planes.
300    #[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// =============================================================================
313// Partition Type
314// =============================================================================
315
316/// Block partition type.
317#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
318pub enum PartitionType {
319    /// No partition (leaf block).
320    #[default]
321    None = 0,
322    /// Horizontal split into 2.
323    Horz = 1,
324    /// Vertical split into 2.
325    Vert = 2,
326    /// Split into 4 sub-blocks.
327    Split = 3,
328    /// Horizontal split, top A is smaller.
329    HorzA = 4,
330    /// Horizontal split, bottom B is smaller.
331    HorzB = 5,
332    /// Vertical split, left A is smaller.
333    VertA = 6,
334    /// Vertical split, right B is smaller.
335    VertB = 7,
336    /// Horizontal 4-way split.
337    Horz4 = 8,
338    /// Vertical 4-way split.
339    Vert4 = 9,
340}
341
342impl PartitionType {
343    /// Convert from integer value.
344    #[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    /// Get number of sub-blocks for this partition.
362    #[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    /// Check if this partition is a split.
374    #[must_use]
375    pub const fn is_split(self) -> bool {
376        matches!(self, Self::Split)
377    }
378
379    /// Check if this is a leaf partition (no further splits).
380    #[must_use]
381    pub const fn is_leaf(self) -> bool {
382        matches!(self, Self::None)
383    }
384}
385
386// =============================================================================
387// Intra Mode
388// =============================================================================
389
390/// Intra prediction mode.
391#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
392pub enum IntraMode {
393    /// DC prediction.
394    #[default]
395    DcPred = 0,
396    /// Vertical prediction.
397    VPred = 1,
398    /// Horizontal prediction.
399    HPred = 2,
400    /// Diagonal down-left prediction.
401    D45Pred = 3,
402    /// Diagonal down-right prediction.
403    D135Pred = 4,
404    /// Diagonal 113 degrees prediction.
405    D113Pred = 5,
406    /// Diagonal 157 degrees prediction.
407    D157Pred = 6,
408    /// Diagonal 203 degrees prediction.
409    D203Pred = 7,
410    /// Diagonal 67 degrees prediction.
411    D67Pred = 8,
412    /// Smooth prediction.
413    SmoothPred = 9,
414    /// Smooth vertical prediction.
415    SmoothVPred = 10,
416    /// Smooth horizontal prediction.
417    SmoothHPred = 11,
418    /// Paeth prediction.
419    PaethPred = 12,
420}
421
422impl IntraMode {
423    /// Convert from integer value.
424    #[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    /// Check if this is a directional mode.
445    #[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    /// Get angle delta allowed for this mode.
461    #[must_use]
462    pub const fn angle_delta_allowed(self) -> bool {
463        self.is_directional()
464    }
465}
466
467// =============================================================================
468// Inter Mode
469// =============================================================================
470
471/// Inter prediction mode.
472#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
473pub enum InterMode {
474    /// Nearest MV mode.
475    #[default]
476    NearestMv = 0,
477    /// Near MV mode.
478    NearMv = 1,
479    /// Global MV mode.
480    GlobalMv = 2,
481    /// New MV mode.
482    NewMv = 3,
483}
484
485impl InterMode {
486    /// Convert from integer value.
487    #[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    /// Check if this mode uses a new motion vector.
499    #[must_use]
500    pub const fn has_newmv(self) -> bool {
501        matches!(self, Self::NewMv)
502    }
503
504    /// Check if this mode uses global motion.
505    #[must_use]
506    pub const fn is_global(self) -> bool {
507        matches!(self, Self::GlobalMv)
508    }
509}
510
511// =============================================================================
512// Block Mode Info
513// =============================================================================
514
515/// Block mode information.
516#[derive(Clone, Debug, Default)]
517pub struct BlockModeInfo {
518    /// Block size.
519    pub block_size: BlockSize,
520    /// Segment ID.
521    pub segment_id: u8,
522    /// Skip residual flag.
523    pub skip: bool,
524    /// Skip mode (compound prediction skip).
525    pub skip_mode: bool,
526    /// Is inter block.
527    pub is_inter: bool,
528    /// Intra mode (for intra blocks).
529    pub intra_mode: IntraMode,
530    /// UV intra mode.
531    pub uv_mode: IntraMode,
532    /// Intra angle delta.
533    pub angle_delta: [i8; 2],
534    /// Inter mode (for inter blocks).
535    pub inter_mode: InterMode,
536    /// Reference frames (up to 2 for compound).
537    pub ref_frames: [i8; 2],
538    /// Motion vectors (up to 2 for compound).
539    pub mv: [[i16; 2]; 2],
540    /// Transform size.
541    pub tx_size: TxSize,
542    /// Use palette mode.
543    pub use_palette: bool,
544    /// Filter intra mode.
545    pub filter_intra_mode: u8,
546    /// Compound type.
547    pub compound_type: u8,
548    /// Interpolation filter.
549    pub interp_filter: [u8; 2],
550    /// Motion mode (simple, obmc, warp).
551    pub motion_mode: u8,
552}
553
554impl BlockModeInfo {
555    /// Create a new block mode info.
556    #[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    /// Check if this is an intra block.
580    #[must_use]
581    pub const fn is_intra(&self) -> bool {
582        !self.is_inter
583    }
584
585    /// Check if this block uses compound prediction.
586    #[must_use]
587    pub const fn is_compound(&self) -> bool {
588        self.ref_frames[1] >= 0
589    }
590
591    /// Get the number of reference frames used.
592    #[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// =============================================================================
605// Plane Block Context
606// =============================================================================
607
608/// Per-plane block context.
609#[derive(Clone, Debug, Default)]
610pub struct PlaneBlockContext {
611    /// Width in samples.
612    pub width: u32,
613    /// Height in samples.
614    pub height: u32,
615    /// X position in samples.
616    pub x: u32,
617    /// Y position in samples.
618    pub y: u32,
619    /// Transform size.
620    pub tx_size: TxSize,
621    /// Number of transform blocks in width.
622    pub tx_width: u32,
623    /// Number of transform blocks in height.
624    pub tx_height: u32,
625    /// Subsampling X.
626    pub subx: bool,
627    /// Subsampling Y.
628    pub suby: bool,
629}
630
631impl PlaneBlockContext {
632    /// Create a new plane block context.
633    #[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    /// Set position from block.
649    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        // Compute transform blocks
659        self.tx_width = self.width / self.tx_size.width();
660        self.tx_height = self.height / self.tx_size.height();
661    }
662
663    /// Get number of transform blocks.
664    #[must_use]
665    pub const fn num_tx_blocks(&self) -> u32 {
666        self.tx_width * self.tx_height
667    }
668}
669
670// =============================================================================
671// Block Context Manager
672// =============================================================================
673
674/// Manager for block-level context.
675#[derive(Clone, Debug)]
676pub struct BlockContextManager {
677    /// Plane contexts.
678    pub planes: [PlaneBlockContext; MAX_PLANES],
679    /// Current mode info.
680    pub mode_info: BlockModeInfo,
681    /// Row in 4x4 units.
682    pub mi_row: u32,
683    /// Column in 4x4 units.
684    pub mi_col: u32,
685    /// Above mode info references.
686    pub above_ctx: Vec<u8>,
687    /// Left mode info references.
688    pub left_ctx: Vec<u8>,
689}
690
691impl BlockContextManager {
692    /// Create a new block context manager.
693    #[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    /// Set current block position.
710    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    /// Get context for partition.
724    #[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        // Simple context based on neighbors
731        let ctx = u8::from(left < bs) + u8::from(above < bs);
732        ctx.min(3)
733    }
734
735    /// Get above context value.
736    #[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    /// Get left context value.
747    #[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    /// Update context after decoding a block.
758    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        // Update above context
764        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        // Update left context
772        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    /// Reset left context for new superblock row.
782    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// =============================================================================
794// Tests
795// =============================================================================
796
797#[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); // 8 / 2 for chroma
889        assert_eq!(ctx.height, 4);
890        assert_eq!(ctx.x, 8); // 16 / 2
891    }
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}