1#![forbid(unsafe_code)]
25#![allow(dead_code)]
26#![allow(clippy::doc_markdown)]
27#![allow(clippy::unused_self)]
28#![allow(clippy::cast_possible_truncation)]
29#![allow(clippy::trivially_copy_pass_by_ref)]
30#![allow(clippy::match_same_arms)]
31#![allow(clippy::manual_div_ceil)]
32#![allow(clippy::missing_errors_doc)]
33
34use super::frame_header::FrameSize;
35use super::sequence::SequenceHeader;
36use crate::error::{CodecError, CodecResult};
37use oximedia_io::BitReader;
38
39pub const MAX_TILE_COLS: usize = 64;
45
46pub const MAX_TILE_ROWS: usize = 64;
48
49pub const MAX_TILE_COUNT: usize = MAX_TILE_COLS * MAX_TILE_ROWS;
51
52pub const MAX_TILE_AREA_SB: usize = 4096;
54
55pub const MIN_TILE_WIDTH_SB: usize = 1;
57
58pub const MAX_TILE_WIDTH_SB: usize = 64;
60
61pub const MIN_TILE_HEIGHT_SB: usize = 1;
63
64pub const MAX_TILE_HEIGHT_SB: usize = 64;
66
67pub const TILE_SIZE_BYTES_MINUS_1_BITS: u8 = 2;
69
70#[derive(Clone, Debug, Default)]
76pub struct TileInfo {
77 pub tile_cols: u32,
79 pub tile_rows: u32,
81 pub tile_col_starts: Vec<u32>,
83 pub tile_row_starts: Vec<u32>,
85 pub context_update_tile_id: u32,
87 pub tile_size_bytes: u8,
89 pub uniform_tile_spacing: bool,
91 pub tile_cols_log2: u8,
93 pub tile_rows_log2: u8,
95 pub min_tile_cols_log2: u8,
97 pub max_tile_cols_log2: u8,
99 pub min_tile_rows_log2: u8,
101 pub max_tile_rows_log2: u8,
103 pub sb_cols: u32,
105 pub sb_rows: u32,
107 pub sb_size: u32,
109}
110
111impl TileInfo {
112 #[must_use]
114 pub fn new() -> Self {
115 Self::default()
116 }
117
118 #[allow(clippy::too_many_lines, clippy::cast_possible_truncation)]
124 pub fn parse(
125 reader: &mut BitReader<'_>,
126 _seq: &SequenceHeader,
127 frame_size: &FrameSize,
128 ) -> CodecResult<Self> {
129 let mut tile_info = Self::new();
130
131 tile_info.sb_size = 64; tile_info.sb_cols = frame_size.sb_cols(tile_info.sb_size);
136 tile_info.sb_rows = frame_size.sb_rows(tile_info.sb_size);
137
138 let max_tile_width_sb = MAX_TILE_WIDTH_SB.min(tile_info.sb_cols as usize);
140 let max_tile_area_sb = MAX_TILE_AREA_SB;
141
142 tile_info.min_tile_cols_log2 = Self::tile_log2(max_tile_width_sb as u32, tile_info.sb_cols);
143 tile_info.max_tile_cols_log2 =
144 Self::tile_log2(1, tile_info.sb_cols.min(MAX_TILE_COLS as u32));
145 tile_info.max_tile_rows_log2 =
146 Self::tile_log2(1, tile_info.sb_rows.min(MAX_TILE_ROWS as u32));
147
148 let min_log2_tiles = Self::tile_log2(
150 max_tile_area_sb as u32,
151 tile_info.sb_cols * tile_info.sb_rows,
152 );
153 tile_info.min_tile_rows_log2 = min_log2_tiles.saturating_sub(tile_info.max_tile_cols_log2);
154
155 tile_info.uniform_tile_spacing = reader.read_bit().map_err(CodecError::Core)? != 0;
157
158 if tile_info.uniform_tile_spacing {
159 tile_info.tile_cols_log2 = tile_info.min_tile_cols_log2;
161 while tile_info.tile_cols_log2 < tile_info.max_tile_cols_log2 {
162 let increment = reader.read_bit().map_err(CodecError::Core)?;
163 if increment != 0 {
164 tile_info.tile_cols_log2 += 1;
165 } else {
166 break;
167 }
168 }
169
170 let tile_width_sb = (tile_info.sb_cols + (1 << tile_info.tile_cols_log2) - 1)
172 >> tile_info.tile_cols_log2;
173 let mut start_sb = 0u32;
174 tile_info.tile_col_starts.clear();
175 while start_sb < tile_info.sb_cols {
176 tile_info.tile_col_starts.push(start_sb);
177 start_sb += tile_width_sb;
178 }
179 tile_info.tile_col_starts.push(tile_info.sb_cols);
180 tile_info.tile_cols = (tile_info.tile_col_starts.len() - 1) as u32;
181
182 tile_info.tile_rows_log2 = tile_info.min_tile_rows_log2;
184 while tile_info.tile_rows_log2 < tile_info.max_tile_rows_log2 {
185 let increment = reader.read_bit().map_err(CodecError::Core)?;
186 if increment != 0 {
187 tile_info.tile_rows_log2 += 1;
188 } else {
189 break;
190 }
191 }
192
193 let tile_height_sb = (tile_info.sb_rows + (1 << tile_info.tile_rows_log2) - 1)
195 >> tile_info.tile_rows_log2;
196 let mut start_sb = 0u32;
197 tile_info.tile_row_starts.clear();
198 while start_sb < tile_info.sb_rows {
199 tile_info.tile_row_starts.push(start_sb);
200 start_sb += tile_height_sb;
201 }
202 tile_info.tile_row_starts.push(tile_info.sb_rows);
203 tile_info.tile_rows = (tile_info.tile_row_starts.len() - 1) as u32;
204 } else {
205 let mut widest_tile_sb = 0u32;
207 let mut start_sb = 0u32;
208 tile_info.tile_col_starts.clear();
209
210 while start_sb < tile_info.sb_cols {
211 tile_info.tile_col_starts.push(start_sb);
212 let max_width = tile_info.sb_cols - start_sb;
213 let width_in_sbs_minus_1 = Self::ns(reader, max_width)?;
214 let size_sb = width_in_sbs_minus_1 + 1;
215 widest_tile_sb = widest_tile_sb.max(size_sb);
216 start_sb += size_sb;
217 }
218 tile_info.tile_col_starts.push(tile_info.sb_cols);
219 tile_info.tile_cols = (tile_info.tile_col_starts.len() - 1) as u32;
220 tile_info.tile_cols_log2 = Self::tile_log2(1, tile_info.tile_cols);
221
222 let mut start_sb = 0u32;
224 tile_info.tile_row_starts.clear();
225
226 while start_sb < tile_info.sb_rows {
227 tile_info.tile_row_starts.push(start_sb);
228 let max_height = tile_info.sb_rows - start_sb;
229 let height_in_sbs_minus_1 = Self::ns(reader, max_height)?;
230 let size_sb = height_in_sbs_minus_1 + 1;
231 start_sb += size_sb;
232 }
233 tile_info.tile_row_starts.push(tile_info.sb_rows);
234 tile_info.tile_rows = (tile_info.tile_row_starts.len() - 1) as u32;
235 tile_info.tile_rows_log2 = Self::tile_log2(1, tile_info.tile_rows);
236 }
237
238 if tile_info.tile_cols_log2 > 0 || tile_info.tile_rows_log2 > 0 {
240 let tile_bits = tile_info.tile_cols_log2 + tile_info.tile_rows_log2;
241 tile_info.context_update_tile_id =
242 reader.read_bits(tile_bits).map_err(CodecError::Core)? as u32;
243 tile_info.tile_size_bytes = reader
244 .read_bits(TILE_SIZE_BYTES_MINUS_1_BITS)
245 .map_err(CodecError::Core)? as u8
246 + 1;
247 } else {
248 tile_info.context_update_tile_id = 0;
249 tile_info.tile_size_bytes = 1;
250 }
251
252 Ok(tile_info)
253 }
254
255 #[must_use]
257 fn tile_log2(blk_size: u32, target: u32) -> u8 {
258 let mut k = 0u8;
259 while (blk_size << k) < target {
260 k += 1;
261 }
262 k
263 }
264
265 #[allow(clippy::cast_possible_truncation)]
267 fn ns(reader: &mut BitReader<'_>, n: u32) -> CodecResult<u32> {
268 if n <= 1 {
269 return Ok(0);
270 }
271
272 let w = 32 - (n - 1).leading_zeros();
273 let m = (1u32 << w) - n;
274
275 let v = reader.read_bits(w as u8 - 1).map_err(CodecError::Core)? as u32;
276 if v < m {
277 Ok(v)
278 } else {
279 let extra_bit = u32::from(reader.read_bit().map_err(CodecError::Core)?);
280 Ok((v << 1) - m + extra_bit)
281 }
282 }
283
284 #[must_use]
286 pub const fn tile_count(&self) -> u32 {
287 self.tile_cols * self.tile_rows
288 }
289
290 #[must_use]
292 pub fn tile_size_sb(&self, tile_col: u32, tile_row: u32) -> (u32, u32) {
293 let col_start = self
294 .tile_col_starts
295 .get(tile_col as usize)
296 .copied()
297 .unwrap_or(0);
298 let col_end = self
299 .tile_col_starts
300 .get(tile_col as usize + 1)
301 .copied()
302 .unwrap_or(col_start);
303 let row_start = self
304 .tile_row_starts
305 .get(tile_row as usize)
306 .copied()
307 .unwrap_or(0);
308 let row_end = self
309 .tile_row_starts
310 .get(tile_row as usize + 1)
311 .copied()
312 .unwrap_or(row_start);
313
314 (col_end - col_start, row_end - row_start)
315 }
316
317 #[must_use]
319 pub fn tile_size_pixels(&self, tile_col: u32, tile_row: u32) -> (u32, u32) {
320 let (width_sb, height_sb) = self.tile_size_sb(tile_col, tile_row);
321 (width_sb * self.sb_size, height_sb * self.sb_size)
322 }
323
324 #[must_use]
326 pub const fn tile_index(&self, tile_col: u32, tile_row: u32) -> u32 {
327 tile_row * self.tile_cols + tile_col
328 }
329
330 #[must_use]
332 pub const fn tile_position(&self, tile_idx: u32) -> (u32, u32) {
333 (tile_idx % self.tile_cols, tile_idx / self.tile_cols)
334 }
335
336 #[must_use]
338 pub fn tile_col_start_sb(&self, tile_col: u32) -> u32 {
339 self.tile_col_starts
340 .get(tile_col as usize)
341 .copied()
342 .unwrap_or(0)
343 }
344
345 #[must_use]
347 pub fn tile_row_start_sb(&self, tile_row: u32) -> u32 {
348 self.tile_row_starts
349 .get(tile_row as usize)
350 .copied()
351 .unwrap_or(0)
352 }
353
354 #[must_use]
356 pub fn tile_start_pixels(&self, tile_col: u32, tile_row: u32) -> (u32, u32) {
357 (
358 self.tile_col_start_sb(tile_col) * self.sb_size,
359 self.tile_row_start_sb(tile_row) * self.sb_size,
360 )
361 }
362
363 #[must_use]
365 pub const fn is_single_tile(&self) -> bool {
366 self.tile_cols == 1 && self.tile_rows == 1
367 }
368
369 #[must_use]
371 pub const fn is_left_edge(&self, tile_col: u32) -> bool {
372 tile_col == 0
373 }
374
375 #[must_use]
377 pub fn is_right_edge(&self, tile_col: u32) -> bool {
378 tile_col == self.tile_cols - 1
379 }
380
381 #[must_use]
383 pub const fn is_top_edge(&self, tile_row: u32) -> bool {
384 tile_row == 0
385 }
386
387 #[must_use]
389 pub fn is_bottom_edge(&self, tile_row: u32) -> bool {
390 tile_row == self.tile_rows - 1
391 }
392}
393
394pub type TileConfig = TileInfo;
396
397#[derive(Clone, Debug)]
399pub struct TileData {
400 pub tile_col: u32,
402 pub tile_row: u32,
404 pub offset: usize,
406 pub size: usize,
408 pub tile_idx: u32,
410}
411
412impl TileData {
413 #[must_use]
415 pub fn new(tile_col: u32, tile_row: u32, offset: usize, size: usize, tile_cols: u32) -> Self {
416 Self {
417 tile_col,
418 tile_row,
419 offset,
420 size,
421 tile_idx: tile_row * tile_cols + tile_col,
422 }
423 }
424
425 #[must_use]
427 pub const fn is_valid(&self) -> bool {
428 self.size > 0
429 }
430}
431
432#[derive(Clone, Debug)]
434pub struct TileGroup {
435 pub tile_start: u32,
437 pub tile_end: u32,
439 pub tiles: Vec<TileData>,
441 pub num_tiles: u32,
443}
444
445impl TileGroup {
446 #[must_use]
448 pub fn new(tile_start: u32, tile_end: u32) -> Self {
449 Self {
450 tile_start,
451 tile_end,
452 tiles: Vec::new(),
453 num_tiles: tile_end - tile_start + 1,
454 }
455 }
456
457 #[must_use]
459 pub fn tile_count(&self) -> u32 {
460 self.tile_end - self.tile_start + 1
461 }
462
463 pub fn add_tile(&mut self, tile: TileData) {
465 self.tiles.push(tile);
466 }
467
468 #[must_use]
470 pub fn get_tile(&self, idx: usize) -> Option<&TileData> {
471 self.tiles.get(idx)
472 }
473
474 #[must_use]
476 pub const fn contains_tile(&self, tile_idx: u32) -> bool {
477 tile_idx >= self.tile_start && tile_idx <= self.tile_end
478 }
479
480 #[must_use]
482 pub const fn is_single_tile(&self) -> bool {
483 self.tile_start == self.tile_end
484 }
485}
486
487#[derive(Clone, Debug)]
489pub struct TileGroupObu {
490 pub tile_info: TileInfo,
492 pub groups: Vec<TileGroup>,
494}
495
496impl TileGroupObu {
497 #[must_use]
499 pub fn new(tile_info: TileInfo) -> Self {
500 Self {
501 tile_info,
502 groups: Vec::new(),
503 }
504 }
505
506 #[allow(clippy::cast_possible_truncation)]
512 pub fn parse(&mut self, data: &[u8]) -> CodecResult<()> {
513 let mut reader = BitReader::new(data);
514 let num_tiles = self.tile_info.tile_count();
515
516 let (tile_start, tile_end) = if num_tiles > 1 {
518 let tile_bits = self.tile_info.tile_cols_log2 + self.tile_info.tile_rows_log2;
519 let tile_start = reader.read_bits(tile_bits).map_err(CodecError::Core)? as u32;
520 let tile_end = reader.read_bits(tile_bits).map_err(CodecError::Core)? as u32;
521 (tile_start, tile_end)
522 } else {
523 (0, 0)
524 };
525
526 let mut group = TileGroup::new(tile_start, tile_end);
527
528 reader.byte_align();
530
531 let header_bytes = reader.bits_read().div_ceil(8);
533 let mut offset = header_bytes;
534
535 for tile_idx in tile_start..=tile_end {
536 let tile_size = if tile_idx < tile_end {
537 let size_bytes = self.tile_info.tile_size_bytes as usize;
539 let mut size = 0u32;
540 for i in 0..size_bytes {
541 if offset + i >= data.len() {
542 return Err(CodecError::InvalidBitstream(
543 "Tile data truncated".to_string(),
544 ));
545 }
546 size |= u32::from(data[offset + i]) << (8 * i);
547 }
548 offset += size_bytes;
549 (size + 1) as usize
550 } else {
551 data.len() - offset
553 };
554
555 let (tile_col, tile_row) = self.tile_info.tile_position(tile_idx);
556 let tile_data = TileData::new(
557 tile_col,
558 tile_row,
559 offset,
560 tile_size,
561 self.tile_info.tile_cols,
562 );
563
564 group.add_tile(tile_data);
565 offset += tile_size;
566 }
567
568 self.groups.push(group);
569 Ok(())
570 }
571
572 #[must_use]
574 pub fn total_tiles(&self) -> usize {
575 self.groups.iter().map(|g| g.tiles.len()).sum()
576 }
577
578 #[must_use]
580 pub fn get_tile(&self, tile_idx: u32) -> Option<&TileData> {
581 for group in &self.groups {
582 if group.contains_tile(tile_idx) {
583 let local_idx = (tile_idx - group.tile_start) as usize;
584 return group.get_tile(local_idx);
585 }
586 }
587 None
588 }
589
590 #[must_use]
592 pub fn is_complete(&self) -> bool {
593 let total_expected = self.tile_info.tile_count() as usize;
594 self.total_tiles() == total_expected
595 }
596}
597
598#[derive(Clone, Debug, Default)]
600pub struct TileDecoderState {
601 pub tile_col: u32,
603 pub tile_row: u32,
605 pub sb_col: u32,
607 pub sb_row: u32,
609 pub tile_width_sb: u32,
611 pub tile_height_sb: u32,
613 pub completed: bool,
615}
616
617impl TileDecoderState {
618 #[must_use]
620 pub fn new(tile_info: &TileInfo, tile_col: u32, tile_row: u32) -> Self {
621 let (width, height) = tile_info.tile_size_sb(tile_col, tile_row);
622 Self {
623 tile_col,
624 tile_row,
625 sb_col: 0,
626 sb_row: 0,
627 tile_width_sb: width,
628 tile_height_sb: height,
629 completed: false,
630 }
631 }
632
633 pub fn advance(&mut self) {
635 self.sb_col += 1;
636 if self.sb_col >= self.tile_width_sb {
637 self.sb_col = 0;
638 self.sb_row += 1;
639 if self.sb_row >= self.tile_height_sb {
640 self.completed = true;
641 }
642 }
643 }
644
645 #[must_use]
647 pub const fn is_complete(&self) -> bool {
648 self.completed
649 }
650
651 #[must_use]
653 pub const fn position(&self) -> (u32, u32) {
654 (self.sb_col, self.sb_row)
655 }
656
657 #[must_use]
659 pub const fn total_superblocks(&self) -> u32 {
660 self.tile_width_sb * self.tile_height_sb
661 }
662
663 #[must_use]
665 pub fn decoded_superblocks(&self) -> u32 {
666 self.sb_row * self.tile_width_sb + self.sb_col
667 }
668}
669
670#[derive(Clone, Debug)]
672pub struct TileDecoder {
673 pub tile_info: TileInfo,
675 pub tile_states: Vec<TileDecoderState>,
677}
678
679impl TileDecoder {
680 #[must_use]
682 pub fn new(tile_info: TileInfo) -> Self {
683 let mut tile_states = Vec::with_capacity(tile_info.tile_count() as usize);
684 for row in 0..tile_info.tile_rows {
685 for col in 0..tile_info.tile_cols {
686 tile_states.push(TileDecoderState::new(&tile_info, col, row));
687 }
688 }
689 Self {
690 tile_info,
691 tile_states,
692 }
693 }
694
695 #[must_use]
697 pub fn get_state(&self, idx: usize) -> Option<&TileDecoderState> {
698 self.tile_states.get(idx)
699 }
700
701 pub fn get_state_mut(&mut self, idx: usize) -> Option<&mut TileDecoderState> {
703 self.tile_states.get_mut(idx)
704 }
705
706 #[must_use]
708 pub fn all_complete(&self) -> bool {
709 self.tile_states.iter().all(TileDecoderState::is_complete)
710 }
711
712 #[must_use]
714 pub fn complete_count(&self) -> usize {
715 self.tile_states.iter().filter(|s| s.is_complete()).count()
716 }
717
718 pub fn reset(&mut self) {
720 for state in &mut self.tile_states {
721 state.sb_col = 0;
722 state.sb_row = 0;
723 state.completed = false;
724 }
725 }
726}
727
728#[cfg(test)]
733mod tests {
734 use super::*;
735
736 fn create_test_tile_info() -> TileInfo {
737 TileInfo {
738 tile_cols: 2,
739 tile_rows: 2,
740 tile_col_starts: vec![0, 15, 30],
741 tile_row_starts: vec![0, 8, 17],
742 context_update_tile_id: 0,
743 tile_size_bytes: 4,
744 uniform_tile_spacing: true,
745 tile_cols_log2: 1,
746 tile_rows_log2: 1,
747 min_tile_cols_log2: 0,
748 max_tile_cols_log2: 2,
749 min_tile_rows_log2: 0,
750 max_tile_rows_log2: 2,
751 sb_cols: 30,
752 sb_rows: 17,
753 sb_size: 64,
754 }
755 }
756
757 #[test]
758 fn test_tile_info_default() {
759 let tile_info = TileInfo::default();
760 assert_eq!(tile_info.tile_cols, 0);
761 assert_eq!(tile_info.tile_rows, 0);
762 }
763
764 #[test]
765 fn test_tile_count() {
766 let tile_info = create_test_tile_info();
767 assert_eq!(tile_info.tile_count(), 4);
768 }
769
770 #[test]
771 fn test_tile_size_sb() {
772 let tile_info = create_test_tile_info();
773 assert_eq!(tile_info.tile_size_sb(0, 0), (15, 8));
774 assert_eq!(tile_info.tile_size_sb(1, 0), (15, 8));
775 assert_eq!(tile_info.tile_size_sb(0, 1), (15, 9));
776 assert_eq!(tile_info.tile_size_sb(1, 1), (15, 9));
777 }
778
779 #[test]
780 fn test_tile_size_pixels() {
781 let tile_info = create_test_tile_info();
782 let (width, height) = tile_info.tile_size_pixels(0, 0);
783 assert_eq!(width, 15 * 64);
784 assert_eq!(height, 8 * 64);
785 }
786
787 #[test]
788 fn test_tile_index() {
789 let tile_info = create_test_tile_info();
790 assert_eq!(tile_info.tile_index(0, 0), 0);
791 assert_eq!(tile_info.tile_index(1, 0), 1);
792 assert_eq!(tile_info.tile_index(0, 1), 2);
793 assert_eq!(tile_info.tile_index(1, 1), 3);
794 }
795
796 #[test]
797 fn test_tile_position() {
798 let tile_info = create_test_tile_info();
799 assert_eq!(tile_info.tile_position(0), (0, 0));
800 assert_eq!(tile_info.tile_position(1), (1, 0));
801 assert_eq!(tile_info.tile_position(2), (0, 1));
802 assert_eq!(tile_info.tile_position(3), (1, 1));
803 }
804
805 #[test]
806 fn test_tile_start_sb() {
807 let tile_info = create_test_tile_info();
808 assert_eq!(tile_info.tile_col_start_sb(0), 0);
809 assert_eq!(tile_info.tile_col_start_sb(1), 15);
810 assert_eq!(tile_info.tile_row_start_sb(0), 0);
811 assert_eq!(tile_info.tile_row_start_sb(1), 8);
812 }
813
814 #[test]
815 fn test_tile_start_pixels() {
816 let tile_info = create_test_tile_info();
817 assert_eq!(tile_info.tile_start_pixels(0, 0), (0, 0));
818 assert_eq!(tile_info.tile_start_pixels(1, 0), (15 * 64, 0));
819 assert_eq!(tile_info.tile_start_pixels(0, 1), (0, 8 * 64));
820 }
821
822 #[test]
823 fn test_is_single_tile() {
824 let mut tile_info = TileInfo::default();
825 tile_info.tile_cols = 1;
826 tile_info.tile_rows = 1;
827 assert!(tile_info.is_single_tile());
828
829 tile_info.tile_cols = 2;
830 assert!(!tile_info.is_single_tile());
831 }
832
833 #[test]
834 fn test_tile_edges() {
835 let tile_info = create_test_tile_info();
836
837 assert!(tile_info.is_left_edge(0));
838 assert!(!tile_info.is_left_edge(1));
839 assert!(!tile_info.is_right_edge(0));
840 assert!(tile_info.is_right_edge(1));
841 assert!(tile_info.is_top_edge(0));
842 assert!(!tile_info.is_top_edge(1));
843 assert!(!tile_info.is_bottom_edge(0));
844 assert!(tile_info.is_bottom_edge(1));
845 }
846
847 #[test]
848 fn test_tile_log2() {
849 assert_eq!(TileInfo::tile_log2(1, 1), 0);
850 assert_eq!(TileInfo::tile_log2(1, 2), 1);
851 assert_eq!(TileInfo::tile_log2(1, 4), 2);
852 assert_eq!(TileInfo::tile_log2(2, 4), 1);
853 }
854
855 #[test]
856 fn test_tile_data() {
857 let tile_data = TileData::new(1, 2, 100, 500, 4);
858 assert_eq!(tile_data.tile_col, 1);
859 assert_eq!(tile_data.tile_row, 2);
860 assert_eq!(tile_data.offset, 100);
861 assert_eq!(tile_data.size, 500);
862 assert_eq!(tile_data.tile_idx, 2 * 4 + 1);
863 assert!(tile_data.is_valid());
864
865 let empty_tile = TileData::new(0, 0, 0, 0, 1);
866 assert!(!empty_tile.is_valid());
867 }
868
869 #[test]
870 fn test_tile_group() {
871 let mut group = TileGroup::new(0, 3);
872 assert_eq!(group.tile_count(), 4);
873 assert!(!group.is_single_tile());
874
875 assert!(group.contains_tile(0));
876 assert!(group.contains_tile(3));
877 assert!(!group.contains_tile(4));
878
879 group.add_tile(TileData::new(0, 0, 0, 100, 2));
880 assert_eq!(group.tiles.len(), 1);
881 assert!(group.get_tile(0).is_some());
882 }
883
884 #[test]
885 fn test_tile_group_single() {
886 let group = TileGroup::new(5, 5);
887 assert_eq!(group.tile_count(), 1);
888 assert!(group.is_single_tile());
889 }
890
891 #[test]
892 fn test_tile_group_obu() {
893 let tile_info = create_test_tile_info();
894 let obu = TileGroupObu::new(tile_info);
895
896 assert_eq!(obu.total_tiles(), 0);
897 assert!(!obu.is_complete());
898 }
899
900 #[test]
901 fn test_tile_decoder_state() {
902 let tile_info = create_test_tile_info();
903 let mut state = TileDecoderState::new(&tile_info, 0, 0);
904
905 assert_eq!(state.tile_col, 0);
906 assert_eq!(state.tile_row, 0);
907 assert_eq!(state.position(), (0, 0));
908 assert!(!state.is_complete());
909 assert_eq!(state.tile_width_sb, 15);
910 assert_eq!(state.tile_height_sb, 8);
911 assert_eq!(state.total_superblocks(), 120);
912
913 state.advance();
914 assert_eq!(state.position(), (1, 0));
915 assert_eq!(state.decoded_superblocks(), 1);
916 }
917
918 #[test]
919 fn test_tile_decoder_state_wrap() {
920 let tile_info = TileInfo {
921 tile_cols: 1,
922 tile_rows: 1,
923 tile_col_starts: vec![0, 2],
924 tile_row_starts: vec![0, 2],
925 sb_cols: 2,
926 sb_rows: 2,
927 sb_size: 64,
928 ..Default::default()
929 };
930
931 let mut state = TileDecoderState::new(&tile_info, 0, 0);
932 assert_eq!(state.tile_width_sb, 2);
933 assert_eq!(state.tile_height_sb, 2);
934
935 state.advance();
936 assert_eq!(state.position(), (1, 0));
937
938 state.advance();
939 assert_eq!(state.position(), (0, 1));
940
941 state.advance();
942 assert_eq!(state.position(), (1, 1));
943
944 state.advance();
945 assert!(state.is_complete());
946 }
947
948 #[test]
949 fn test_tile_decoder() {
950 let tile_info = create_test_tile_info();
951 let decoder = TileDecoder::new(tile_info);
952
953 assert_eq!(decoder.tile_states.len(), 4);
954 assert!(!decoder.all_complete());
955 assert_eq!(decoder.complete_count(), 0);
956
957 assert!(decoder.get_state(0).is_some());
958 assert!(decoder.get_state(3).is_some());
959 assert!(decoder.get_state(4).is_none());
960 }
961
962 #[test]
963 fn test_tile_decoder_reset() {
964 let tile_info = create_test_tile_info();
965 let mut decoder = TileDecoder::new(tile_info);
966
967 if let Some(state) = decoder.get_state_mut(0) {
969 state.advance();
970 state.advance();
971 }
972
973 decoder.reset();
974
975 for state in &decoder.tile_states {
976 assert_eq!(state.position(), (0, 0));
977 assert!(!state.is_complete());
978 }
979 }
980
981 #[test]
982 fn test_constants() {
983 assert_eq!(MAX_TILE_COLS, 64);
984 assert_eq!(MAX_TILE_ROWS, 64);
985 assert_eq!(MAX_TILE_COUNT, 4096);
986 assert_eq!(MAX_TILE_AREA_SB, 4096);
987 }
988}