1mod detokenize;
48mod header;
49
50use std::rc::Rc;
51
52use crate::vp9::common::block::{
53 mi_cols as mi_cols_of, mi_rows as mi_rows_of, tile_offset, BlockSize, Partition,
54 PredictionMode, B_WIDTH_LOG2,
55};
56use crate::vp9::common::block::{TxMode, TxSize};
57use crate::vp9::common::bool_coder::BoolReader;
58use crate::vp9::common::frame_buffer::FrameBuffer;
59use crate::vp9::common::generated::{
60 DEFAULT_INTER_MODE_PROBS, DEFAULT_INTRA_INTER_PROBS, DEFAULT_PARTITION_PROBS,
61 DEFAULT_SINGLE_REF_PROBS, DEFAULT_SKIP_PROBS, KF_PARTITION_PROBS, KF_UV_MODE_PROBS,
62 KF_Y_MODE_PROBS,
63};
64use crate::vp9::common::idct::{idct4x4_add, idct8x8_add};
65use crate::vp9::common::inter_ctx::{intra_inter_context, single_ref_p1_context, InterNeighbor};
66use crate::vp9::common::inter_pred::{predict_inter_block, Plane as McPlane};
67use crate::vp9::common::intra_pred::build_intra_dc;
68use crate::vp9::common::mvref::{
69 find_mv_refs, Mv, MvRefGeom, MvRefInfo, INTRA_FRAME, LAST_FRAME, NONE_FRAME,
70};
71use crate::vp9::common::partition::{read_partition, PartitionContext};
72use crate::vp9::common::quant::{ac_quant, dc_quant};
73use crate::vp9::common::trees::{read_tree, INTER_MODE_TREE, INTRA_MODE_TREE};
74use detokenize::{decode_coefs, PlaneType, REF_TYPE_INTER, REF_TYPE_INTRA};
75use header::{parse_frame_header, FrameHeader, FrameType, REF_FRAMES};
76
77mod readmv;
78use readmv::read_mv;
79
80#[derive(Debug, Clone, PartialEq, Eq)]
82pub enum DecodeError {
83 Truncated,
85 Unsupported(&'static str),
87 Corrupt(&'static str),
89 TooLarge,
93}
94
95impl std::fmt::Display for DecodeError {
96 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97 match self {
98 DecodeError::Truncated => write!(f, "truncated VP9 bitstream"),
99 DecodeError::Unsupported(what) => write!(f, "unsupported VP9 feature: {what}"),
100 DecodeError::Corrupt(what) => write!(f, "corrupt VP9 bitstream: {what}"),
101 DecodeError::TooLarge => {
102 write!(f, "frame dimensions exceed the maximum of {MAX_DIM}")
103 }
104 }
105 }
106}
107
108pub const MAX_DIM: u32 = 8192;
111
112impl std::error::Error for DecodeError {}
113
114const DC_PRED: u8 = 0;
116
117#[derive(Clone, Copy)]
119enum Plane {
120 Y,
121 U,
122 V,
123}
124
125#[derive(Clone)]
130struct DecMi {
131 skip: bool,
133 is_inter: bool,
135 mode: PredictionMode,
138 ref_frame: [i8; 2],
141 mv: Mv,
143 ymode: u8,
145}
146
147impl Default for DecMi {
148 fn default() -> Self {
149 DecMi {
150 skip: false,
151 is_inter: false,
152 mode: PredictionMode::DcPred,
153 ref_frame: [INTRA_FRAME, NONE_FRAME],
154 mv: Mv::ZERO,
155 ymode: DC_PRED,
156 }
157 }
158}
159
160struct EntropyContext {
164 above_y: Vec<u8>,
165 above_u: Vec<u8>,
166 above_v: Vec<u8>,
167 left_y: [u8; 16],
168 left_u: [u8; 8],
169 left_v: [u8; 8],
170}
171
172impl EntropyContext {
173 fn new(mi_cols_aligned: usize) -> Self {
174 Self {
175 above_y: vec![0u8; 2 * mi_cols_aligned],
176 above_u: vec![0u8; mi_cols_aligned],
177 above_v: vec![0u8; mi_cols_aligned],
178 left_y: [0u8; 16],
179 left_u: [0u8; 8],
180 left_v: [0u8; 8],
181 }
182 }
183
184 fn reset_left(&mut self) {
185 self.left_y = [0u8; 16];
186 self.left_u = [0u8; 8];
187 self.left_v = [0u8; 8];
188 }
189}
190
191#[inline]
193fn ctx_combine(a: bool, b: bool) -> usize {
194 a as usize + b as usize
195}
196
197pub struct Vp9Decoder {
204 refs: [Option<Rc<FrameBuffer>>; REF_FRAMES],
208}
209
210impl Default for Vp9Decoder {
211 fn default() -> Self {
212 Self::new()
213 }
214}
215
216impl Vp9Decoder {
217 pub fn new() -> Self {
219 Self {
220 refs: std::array::from_fn(|_| None),
221 }
222 }
223
224 pub fn decode_frame(&mut self, bytes: &[u8]) -> Result<Rc<FrameBuffer>, DecodeError> {
232 let ref_sizes: [Option<(u32, u32)>; REF_FRAMES] =
235 std::array::from_fn(|i| self.refs[i].as_ref().map(|f| (f.crop_width, f.crop_height)));
236 let hdr = parse_frame_header(bytes, Some(&ref_sizes))?;
237
238 let reference: Option<Rc<FrameBuffer>> = match hdr.frame_type {
239 FrameType::Key => None,
240 FrameType::Inter => Some(
241 self.refs[hdr.ref_frame_idx[0] as usize]
242 .clone()
243 .ok_or(DecodeError::Corrupt("inter frame references an empty slot"))?,
244 ),
245 };
246
247 let mut recon = decode_frame_inner(&hdr, bytes, reference.as_deref())?;
248 recon.extend_borders();
252 let frame = Rc::new(recon);
253
254 for (i, slot) in self.refs.iter_mut().enumerate() {
256 if hdr.refresh_frame_flags & (1u8 << i) != 0 {
257 *slot = Some(Rc::clone(&frame));
258 }
259 }
260 Ok(frame)
261 }
262}
263
264pub fn decode_keyframe(bytes: &[u8]) -> Result<FrameBuffer, DecodeError> {
270 let hdr = parse_frame_header(bytes, None)?;
271 if hdr.frame_type != FrameType::Key {
272 return Err(DecodeError::Unsupported("non-keyframe (use Vp9Decoder)"));
273 }
274 decode_frame_inner(&hdr, bytes, None)
275}
276
277fn decode_frame_inner(
280 hdr: &FrameHeader,
281 bytes: &[u8],
282 reference: Option<&FrameBuffer>,
283) -> Result<FrameBuffer, DecodeError> {
284 if hdr.tx_mode != TxMode::Allow8X8 {
285 return Err(DecodeError::Unsupported("tx_mode != ALLOW_8X8"));
286 }
287 let is_keyframe = hdr.frame_type == FrameType::Key;
288 if !is_keyframe && reference.is_none() {
289 return Err(DecodeError::Corrupt("inter frame without a reference"));
290 }
291
292 let mi_rows = mi_rows_of(hdr.height);
293 let mi_cols = mi_cols_of(hdr.width);
294 let mi_cols_aligned = ((mi_cols + 7) & !7) as usize;
295 let q = hdr.base_qindex as i32;
296 let dequant = [dc_quant(q, 0), ac_quant(q, 0)];
297
298 let mut dec = FrameDecoder {
299 mi_rows,
300 mi_cols,
301 mi_cols_aligned,
302 dequant,
303 recon: FrameBuffer::new(hdr.width, hdr.height),
304 grid: vec![DecMi::default(); (mi_rows * mi_cols) as usize],
305 reference,
306 is_keyframe,
307 tile_col_start: 0,
308 tile_col_end: mi_cols,
309 };
310
311 let tiles = tile_geometry(mi_cols, hdr.log2_tile_cols);
313 let tile_data = &bytes[hdr.tile_data_offset()..];
314 let n = tiles.len();
315 let mut pos = 0usize;
316 for (i, &(col_start, col_end)) in tiles.iter().enumerate() {
317 let size = if i + 1 < n {
318 if pos + 4 > tile_data.len() {
319 return Err(DecodeError::Truncated);
320 }
321 let s = u32::from_be_bytes([
322 tile_data[pos],
323 tile_data[pos + 1],
324 tile_data[pos + 2],
325 tile_data[pos + 3],
326 ]) as usize;
327 pos += 4;
328 s
329 } else {
330 tile_data.len() - pos
331 };
332 if pos + size > tile_data.len() {
333 return Err(DecodeError::Truncated);
334 }
335 let tile_bytes = &tile_data[pos..pos + size];
336 pos += size;
337 dec.decode_tile(tile_bytes, col_start, col_end)?;
338 }
339
340 Ok(dec.recon)
341}
342
343fn tile_geometry(mi_cols: u32, log2: u32) -> Vec<(u32, u32)> {
345 (0..(1u32 << log2))
346 .map(|i| {
347 (
348 tile_offset(i, mi_cols, log2),
349 tile_offset(i + 1, mi_cols, log2),
350 )
351 })
352 .collect()
353}
354
355fn split_child(bsize: BlockSize) -> BlockSize {
357 match bsize {
358 BlockSize::B64X64 => BlockSize::B32X32,
359 BlockSize::B32X32 => BlockSize::B16X16,
360 BlockSize::B16X16 => BlockSize::B8X8,
361 _ => unreachable!("split_child only descends 64→32→16→8"),
362 }
363}
364
365struct FrameDecoder<'a> {
369 mi_rows: u32,
370 mi_cols: u32,
371 mi_cols_aligned: usize,
372 dequant: [i16; 2],
373 recon: FrameBuffer,
374 grid: Vec<DecMi>,
375 reference: Option<&'a FrameBuffer>,
378 is_keyframe: bool,
379 tile_col_start: u32,
383 tile_col_end: u32,
385}
386
387impl FrameDecoder<'_> {
388 #[inline]
389 fn idx(&self, mi_row: u32, mi_col: u32) -> usize {
390 (mi_row * self.mi_cols + mi_col) as usize
391 }
392
393 fn plane_ro(&self, p: Plane) -> (usize, usize, i32, i32) {
394 let (_d, o, s, w, h) = match p {
395 Plane::Y => self.recon.y(),
396 Plane::U => self.recon.u(),
397 Plane::V => self.recon.v(),
398 };
399 (o, s, w as i32, h as i32)
400 }
401
402 fn plane_mut(&mut self, p: Plane) -> &mut [u8] {
403 match p {
404 Plane::Y => self.recon.y_mut().0,
405 Plane::U => self.recon.u_mut().0,
406 Plane::V => self.recon.v_mut().0,
407 }
408 }
409
410 fn decode_tile(
412 &mut self,
413 bytes: &[u8],
414 col_start: u32,
415 col_end: u32,
416 ) -> Result<(), DecodeError> {
417 self.tile_col_start = col_start;
418 self.tile_col_end = col_end;
419
420 let mut r = BoolReader::new(bytes);
421 let mut pc = PartitionContext::new(self.mi_cols_aligned);
422 let mut ec = EntropyContext::new(self.mi_cols_aligned);
423
424 let mut mi_row = 0;
425 while mi_row < self.mi_rows {
426 pc.reset_left();
427 ec.reset_left();
428 let mut mi_col = col_start;
429 while mi_col < col_end {
430 self.decode_sb(&mut r, &mut pc, &mut ec, mi_row, mi_col, BlockSize::B64X64)?;
431 mi_col += 8;
432 }
433 mi_row += 8;
434 }
435 Ok(())
436 }
437
438 fn decode_sb(
441 &mut self,
442 r: &mut BoolReader,
443 pc: &mut PartitionContext,
444 ec: &mut EntropyContext,
445 mi_row: u32,
446 mi_col: u32,
447 bsize: BlockSize,
448 ) -> Result<(), DecodeError> {
449 if mi_row >= self.mi_rows || mi_col >= self.mi_cols {
450 return Ok(());
451 }
452 let bs = (1u32 << B_WIDTH_LOG2[bsize as usize]) / 4;
453 let partition_probs = if self.is_keyframe {
456 &KF_PARTITION_PROBS
457 } else {
458 &DEFAULT_PARTITION_PROBS
459 };
460 let partition = read_partition(
461 r,
462 pc,
463 bs,
464 mi_row,
465 mi_col,
466 bsize,
467 self.mi_rows,
468 self.mi_cols,
469 partition_probs,
470 );
471
472 match partition {
473 Partition::None => {
474 match bsize {
475 BlockSize::B8X8 => {
476 if self.is_keyframe {
477 self.decode_leaf_8x8(r, ec, mi_row, mi_col)?
478 } else {
479 self.decode_inter_leaf_8x8(r, ec, mi_row, mi_col)?
480 }
481 }
482 BlockSize::B16X16 => {
483 if self.is_keyframe {
484 self.decode_leaf16(r, ec, mi_row, mi_col)?
485 } else {
486 self.decode_inter_leaf16(r, ec, mi_row, mi_col)?
487 }
488 }
489 _ => return Err(DecodeError::Corrupt("unexpected leaf block size")),
491 }
492 pc.update(mi_row, mi_col, bsize, bsize);
494 }
495 Partition::Split => {
496 if bsize == BlockSize::B8X8 {
497 return Err(DecodeError::Corrupt("split of an 8x8 block"));
498 }
499 let sub = split_child(bsize);
500 self.decode_sb(r, pc, ec, mi_row, mi_col, sub)?;
501 self.decode_sb(r, pc, ec, mi_row, mi_col + bs, sub)?;
502 self.decode_sb(r, pc, ec, mi_row + bs, mi_col, sub)?;
503 self.decode_sb(r, pc, ec, mi_row + bs, mi_col + bs, sub)?;
504 }
505 Partition::Horz | Partition::Vert => {
507 return Err(DecodeError::Unsupported("HORZ/VERT partition (M2)"))
508 }
509 }
510 Ok(())
511 }
512
513 fn read_leaf_modes(
518 &mut self,
519 r: &mut BoolReader,
520 mi_row: u32,
521 mi_col: u32,
522 units: &[(u32, u32)],
523 ) -> Result<bool, DecodeError> {
524 let above_skip = mi_row > 0 && self.grid[self.idx(mi_row - 1, mi_col)].skip;
525 let left_skip =
526 mi_col > self.tile_col_start && self.grid[self.idx(mi_row, mi_col - 1)].skip;
527 let ctx = above_skip as usize + left_skip as usize;
528 let skip = r.read(DEFAULT_SKIP_PROBS[ctx]) != 0;
529
530 let a_mode = if mi_row > 0 {
531 self.grid[self.idx(mi_row - 1, mi_col)].ymode
532 } else {
533 DC_PRED
534 };
535 let l_mode = if mi_col > self.tile_col_start {
536 self.grid[self.idx(mi_row, mi_col - 1)].ymode
537 } else {
538 DC_PRED
539 };
540 let y_mode = read_tree(
541 r,
542 &INTRA_MODE_TREE,
543 &KF_Y_MODE_PROBS[a_mode as usize][l_mode as usize],
544 ) as u8;
545 let _uv_mode = read_tree(r, &INTRA_MODE_TREE, &KF_UV_MODE_PROBS[y_mode as usize]) as u8;
546 if y_mode != DC_PRED {
547 return Err(DecodeError::Unsupported("non-DC intra mode"));
548 }
549
550 let mi = DecMi {
551 skip,
552 ymode: y_mode,
553 ..DecMi::default()
554 };
555 for &(dr, dc) in units {
556 let i = self.idx(mi_row + dr, mi_col + dc);
557 self.grid[i] = mi.clone();
558 }
559 Ok(skip)
560 }
561
562 fn decode_leaf_8x8(
564 &mut self,
565 r: &mut BoolReader,
566 ec: &mut EntropyContext,
567 mi_row: u32,
568 mi_col: u32,
569 ) -> Result<(), DecodeError> {
570 let skip = self.read_leaf_modes(r, mi_row, mi_col, &[(0, 0)])?;
571 let up = mi_row > 0;
572 let left = mi_col > self.tile_col_start;
573
574 let ay = (mi_col * 2) as usize;
576 let ly = ((mi_row & 7) * 2) as usize;
577 let pt_y = ctx_combine(
578 ec.above_y[ay] != 0 || ec.above_y[ay + 1] != 0,
579 ec.left_y[ly] != 0 || ec.left_y[ly + 1] != 0,
580 );
581 let he_y = self.intra_recon_tx(
582 r,
583 Plane::Y,
584 TxSize::Tx8X8,
585 (mi_row * 8) as usize,
586 (mi_col * 8) as usize,
587 up,
588 left,
589 skip,
590 pt_y,
591 PlaneType::Y,
592 );
593 ec.above_y[ay] = he_y;
594 ec.above_y[ay + 1] = he_y;
595 ec.left_y[ly] = he_y;
596 ec.left_y[ly + 1] = he_y;
597
598 let au = mi_col as usize;
600 let lu = (mi_row & 7) as usize;
601 let cy = (mi_row * 4) as usize;
602 let cx = (mi_col * 4) as usize;
603
604 let pt_u = ctx_combine(ec.above_u[au] != 0, ec.left_u[lu] != 0);
605 let he_u = self.intra_recon_tx(
606 r,
607 Plane::U,
608 TxSize::Tx4X4,
609 cy,
610 cx,
611 up,
612 left,
613 skip,
614 pt_u,
615 PlaneType::Uv,
616 );
617 ec.above_u[au] = he_u;
618 ec.left_u[lu] = he_u;
619
620 let pt_v = ctx_combine(ec.above_v[au] != 0, ec.left_v[lu] != 0);
621 let he_v = self.intra_recon_tx(
622 r,
623 Plane::V,
624 TxSize::Tx4X4,
625 cy,
626 cx,
627 up,
628 left,
629 skip,
630 pt_v,
631 PlaneType::Uv,
632 );
633 ec.above_v[au] = he_v;
634 ec.left_v[lu] = he_v;
635
636 Ok(())
637 }
638
639 fn decode_leaf16(
642 &mut self,
643 r: &mut BoolReader,
644 ec: &mut EntropyContext,
645 mi_row: u32,
646 mi_col: u32,
647 ) -> Result<(), DecodeError> {
648 let units = [(0, 0), (0, 1), (1, 0), (1, 1)];
649 let skip = self.read_leaf_modes(r, mi_row, mi_col, &units)?;
650 let up_blk = mi_row > 0;
651 let left_blk = mi_col > self.tile_col_start;
652
653 let base_ay = (mi_col * 2) as usize;
655 let base_ly = ((mi_row & 7) * 2) as usize;
656 for sr in 0..2u32 {
657 for sc in 0..2u32 {
658 let ay = base_ay + (sc * 2) as usize;
659 let ly = base_ly + (sr * 2) as usize;
660 let pt = ctx_combine(
661 ec.above_y[ay] != 0 || ec.above_y[ay + 1] != 0,
662 ec.left_y[ly] != 0 || ec.left_y[ly + 1] != 0,
663 );
664 let he = self.intra_recon_tx(
665 r,
666 Plane::Y,
667 TxSize::Tx8X8,
668 (mi_row * 8 + sr * 8) as usize,
669 (mi_col * 8 + sc * 8) as usize,
670 sr > 0 || up_blk,
671 sc > 0 || left_blk,
672 skip,
673 pt,
674 PlaneType::Y,
675 );
676 ec.above_y[ay] = he;
677 ec.above_y[ay + 1] = he;
678 ec.left_y[ly] = he;
679 ec.left_y[ly + 1] = he;
680 }
681 }
682
683 self.decode_leaf16_chroma(
684 r,
685 ec,
686 mi_row,
687 mi_col,
688 up_blk,
689 left_blk,
690 skip,
691 REF_TYPE_INTRA,
692 );
693 Ok(())
694 }
695
696 #[allow(clippy::too_many_arguments)]
700 fn intra_recon_tx(
701 &mut self,
702 r: &mut BoolReader,
703 plane: Plane,
704 tx_size: TxSize,
705 y_px: usize,
706 x_px: usize,
707 up: bool,
708 left: bool,
709 skip: bool,
710 pt: usize,
711 ptype: PlaneType,
712 ) -> u8 {
713 let dequant = self.dequant;
714 let (org, stride, fw, fh) = self.plane_ro(plane);
715 let off = org + y_px * stride + x_px;
716 let rdata = self.plane_mut(plane);
717
718 build_intra_dc(
719 rdata,
720 off,
721 stride,
722 tx_size,
723 up,
724 left,
725 x_px as i32,
726 y_px as i32,
727 fw,
728 fh,
729 );
730
731 if skip {
732 return 0;
733 }
734
735 let (eob, dq) = decode_coefs(r, tx_size, ptype, REF_TYPE_INTRA, pt, dequant);
736 add_residual(tx_size, eob, &dq, &mut rdata[off..], stride);
737 (eob > 0) as u8
738 }
739
740 fn read_inter_mode_info(
753 &self,
754 r: &mut BoolReader,
755 mi_row: u32,
756 mi_col: u32,
757 bsize: BlockSize,
758 ) -> Result<(bool, PredictionMode, Mv), DecodeError> {
759 let above = self.inter_neighbor(mi_row.checked_sub(1), Some(mi_col));
760 let left = if mi_col > self.tile_col_start {
761 self.inter_neighbor(Some(mi_row), Some(mi_col - 1))
762 } else {
763 None
764 };
765
766 let above_skip = above.is_some() && self.grid[self.idx(mi_row - 1, mi_col)].skip;
768 let left_skip = left.is_some() && self.grid[self.idx(mi_row, mi_col - 1)].skip;
769 let skip = r.read(DEFAULT_SKIP_PROBS[above_skip as usize + left_skip as usize]) != 0;
770
771 let is_inter = r.read(DEFAULT_INTRA_INTER_PROBS[intra_inter_context(above, left)]) != 0;
773 if !is_inter {
774 return Err(DecodeError::Unsupported("intra block in inter frame"));
775 }
776
777 if r.read(DEFAULT_SINGLE_REF_PROBS[single_ref_p1_context(above, left)][0]) != 0 {
782 return Err(DecodeError::Unsupported("non-LAST reference"));
783 }
784
785 let (mode_context, refs) = self.mv_refs_at(mi_row, mi_col, bsize);
787 let inter_offset = read_tree(
788 r,
789 &INTER_MODE_TREE,
790 &DEFAULT_INTER_MODE_PROBS[mode_context as usize],
791 );
792 let mode = match inter_offset {
794 0 => PredictionMode::NearestMv,
795 1 => PredictionMode::NearMv,
796 2 => PredictionMode::ZeroMv,
797 _ => PredictionMode::NewMv,
798 };
799
800 let mv = match mode {
803 PredictionMode::ZeroMv => Mv::ZERO,
804 PredictionMode::NearestMv => refs[0],
805 PredictionMode::NearMv => refs[1],
806 _ => read_mv(r, refs[0]),
808 };
809
810 if mv.row % 16 != 0 || mv.col % 16 != 0 {
815 return Err(DecodeError::Corrupt("non-integer motion vector"));
816 }
817
818 Ok((skip, mode, mv))
819 }
820
821 fn inter_neighbor(&self, mi_row: Option<u32>, mi_col: Option<u32>) -> Option<InterNeighbor> {
824 let (r, c) = (mi_row?, mi_col?);
825 if r >= self.mi_rows || c >= self.mi_cols {
826 return None;
827 }
828 let mi = &self.grid[self.idx(r, c)];
829 Some(InterNeighbor {
830 is_inter: mi.is_inter,
831 ref0: mi.ref_frame[0],
832 ref1: mi.ref_frame[1],
833 })
834 }
835
836 fn mv_refs_at(&self, mi_row: u32, mi_col: u32, bsize: BlockSize) -> (u8, [Mv; 2]) {
840 let (mi_rows, mi_cols) = (self.mi_rows as i32, self.mi_cols as i32);
841 let cols = self.mi_cols;
842 let (tcs, tce) = (self.tile_col_start as i32, self.tile_col_end as i32);
843 let grid = &self.grid;
844 let geom = MvRefGeom {
845 mi_rows,
846 mi_cols,
847 ref_frame: LAST_FRAME,
848 ref_sign_bias: [0; 4],
849 allow_hp: false,
850 };
851 find_mv_refs(
852 |r, c| {
853 if r < 0 || c < tcs || r >= mi_rows || c >= tce {
854 None
855 } else {
856 let mi = &grid[(r as u32 * cols + c as u32) as usize];
857 Some(MvRefInfo {
858 mode: mi.mode as u8,
859 ref_frame: mi.ref_frame,
860 mv: [mi.mv, Mv::ZERO],
861 })
862 }
863 },
864 mi_row as i32,
865 mi_col as i32,
866 bsize,
867 &geom,
868 )
869 }
870
871 fn store_inter_mi(
875 &mut self,
876 mi_row: u32,
877 mi_col: u32,
878 units: &[(u32, u32)],
879 skip: bool,
880 mode: PredictionMode,
881 mv: Mv,
882 ) {
883 let mi = DecMi {
884 skip,
885 is_inter: true,
886 mode,
887 ref_frame: [LAST_FRAME, NONE_FRAME],
888 mv,
889 ymode: DC_PRED,
890 };
891 for &(dr, dc) in units {
892 let i = self.idx(mi_row + dr, mi_col + dc);
893 self.grid[i] = mi.clone();
894 }
895 }
896
897 fn motion_compensate(&mut self, mi_row: u32, mi_col: u32, mv: Mv, bsize_mi: i32) {
900 let reference = self
903 .reference
904 .expect("inter leaf requires a reference (validated at frame entry)");
905 let (mi_rows, mi_cols) = (self.mi_rows as i32, self.mi_cols as i32);
906 for plane in [McPlane::Y, McPlane::U, McPlane::V] {
907 predict_inter_block(
908 reference,
909 &mut self.recon,
910 plane,
911 mi_row as i32,
912 mi_col as i32,
913 mv,
914 bsize_mi,
915 mi_rows,
916 mi_cols,
917 );
918 }
919 }
920
921 fn decode_inter_leaf_8x8(
924 &mut self,
925 r: &mut BoolReader,
926 ec: &mut EntropyContext,
927 mi_row: u32,
928 mi_col: u32,
929 ) -> Result<(), DecodeError> {
930 let (skip, mode, mv) = self.read_inter_mode_info(r, mi_row, mi_col, BlockSize::B8X8)?;
931 self.store_inter_mi(mi_row, mi_col, &[(0, 0)], skip, mode, mv);
932 self.motion_compensate(mi_row, mi_col, mv, 1);
933
934 let ay = (mi_col * 2) as usize;
936 let ly = ((mi_row & 7) * 2) as usize;
937 let pt_y = ctx_combine(
938 ec.above_y[ay] != 0 || ec.above_y[ay + 1] != 0,
939 ec.left_y[ly] != 0 || ec.left_y[ly + 1] != 0,
940 );
941 let he_y = self.inter_recon_tx(
942 r,
943 Plane::Y,
944 TxSize::Tx8X8,
945 (mi_row * 8) as usize,
946 (mi_col * 8) as usize,
947 skip,
948 pt_y,
949 PlaneType::Y,
950 );
951 ec.above_y[ay] = he_y;
952 ec.above_y[ay + 1] = he_y;
953 ec.left_y[ly] = he_y;
954 ec.left_y[ly + 1] = he_y;
955
956 let au = mi_col as usize;
958 let lu = (mi_row & 7) as usize;
959 let cy = (mi_row * 4) as usize;
960 let cx = (mi_col * 4) as usize;
961
962 let pt_u = ctx_combine(ec.above_u[au] != 0, ec.left_u[lu] != 0);
963 let he_u = self.inter_recon_tx(
964 r,
965 Plane::U,
966 TxSize::Tx4X4,
967 cy,
968 cx,
969 skip,
970 pt_u,
971 PlaneType::Uv,
972 );
973 ec.above_u[au] = he_u;
974 ec.left_u[lu] = he_u;
975
976 let pt_v = ctx_combine(ec.above_v[au] != 0, ec.left_v[lu] != 0);
977 let he_v = self.inter_recon_tx(
978 r,
979 Plane::V,
980 TxSize::Tx4X4,
981 cy,
982 cx,
983 skip,
984 pt_v,
985 PlaneType::Uv,
986 );
987 ec.above_v[au] = he_v;
988 ec.left_v[lu] = he_v;
989
990 Ok(())
991 }
992
993 fn decode_inter_leaf16(
996 &mut self,
997 r: &mut BoolReader,
998 ec: &mut EntropyContext,
999 mi_row: u32,
1000 mi_col: u32,
1001 ) -> Result<(), DecodeError> {
1002 let (skip, mode, mv) = self.read_inter_mode_info(r, mi_row, mi_col, BlockSize::B16X16)?;
1003 let units = [(0, 0), (0, 1), (1, 0), (1, 1)];
1004 self.store_inter_mi(mi_row, mi_col, &units, skip, mode, mv);
1005 self.motion_compensate(mi_row, mi_col, mv, 2);
1006
1007 let base_ay = (mi_col * 2) as usize;
1009 let base_ly = ((mi_row & 7) * 2) as usize;
1010 for sr in 0..2u32 {
1011 for sc in 0..2u32 {
1012 let ay = base_ay + (sc * 2) as usize;
1013 let ly = base_ly + (sr * 2) as usize;
1014 let pt = ctx_combine(
1015 ec.above_y[ay] != 0 || ec.above_y[ay + 1] != 0,
1016 ec.left_y[ly] != 0 || ec.left_y[ly + 1] != 0,
1017 );
1018 let he = self.inter_recon_tx(
1019 r,
1020 Plane::Y,
1021 TxSize::Tx8X8,
1022 (mi_row * 8 + sr * 8) as usize,
1023 (mi_col * 8 + sc * 8) as usize,
1024 skip,
1025 pt,
1026 PlaneType::Y,
1027 );
1028 ec.above_y[ay] = he;
1029 ec.above_y[ay + 1] = he;
1030 ec.left_y[ly] = he;
1031 ec.left_y[ly + 1] = he;
1032 }
1033 }
1034
1035 self.decode_leaf16_chroma(r, ec, mi_row, mi_col, false, false, skip, REF_TYPE_INTER);
1036 Ok(())
1037 }
1038
1039 #[allow(clippy::too_many_arguments)]
1045 fn decode_leaf16_chroma(
1046 &mut self,
1047 r: &mut BoolReader,
1048 ec: &mut EntropyContext,
1049 mi_row: u32,
1050 mi_col: u32,
1051 up_blk: bool,
1052 left_blk: bool,
1053 skip: bool,
1054 ref_type: usize,
1055 ) {
1056 let au = mi_col as usize;
1057 let lu = (mi_row & 7) as usize;
1058 let cy = (mi_row * 4) as usize;
1059 let cx = (mi_col * 4) as usize;
1060
1061 for (plane, above, left) in [
1062 (Plane::U, &mut ec.above_u, &mut ec.left_u),
1063 (Plane::V, &mut ec.above_v, &mut ec.left_v),
1064 ] {
1065 let pt = ctx_combine(
1066 above[au] != 0 || above[au + 1] != 0,
1067 left[lu] != 0 || left[lu + 1] != 0,
1068 );
1069 let he = if ref_type == REF_TYPE_INTRA {
1070 self.intra_recon_tx(
1071 r,
1072 plane,
1073 TxSize::Tx8X8,
1074 cy,
1075 cx,
1076 up_blk,
1077 left_blk,
1078 skip,
1079 pt,
1080 PlaneType::Uv,
1081 )
1082 } else {
1083 self.inter_recon_tx(r, plane, TxSize::Tx8X8, cy, cx, skip, pt, PlaneType::Uv)
1084 };
1085 above[au] = he;
1086 above[au + 1] = he;
1087 left[lu] = he;
1088 left[lu + 1] = he;
1089 }
1090 }
1091
1092 #[allow(clippy::too_many_arguments)]
1095 fn inter_recon_tx(
1096 &mut self,
1097 r: &mut BoolReader,
1098 plane: Plane,
1099 tx_size: TxSize,
1100 y_px: usize,
1101 x_px: usize,
1102 skip: bool,
1103 pt: usize,
1104 ptype: PlaneType,
1105 ) -> u8 {
1106 if skip {
1107 return 0;
1108 }
1109 let dequant = self.dequant;
1110 let (org, stride, _, _) = self.plane_ro(plane);
1111 let off = org + y_px * stride + x_px;
1112 let (eob, dq) = decode_coefs(r, tx_size, ptype, REF_TYPE_INTER, pt, dequant);
1113 let rdata = self.plane_mut(plane);
1114 add_residual(tx_size, eob, &dq, &mut rdata[off..], stride);
1115 (eob > 0) as u8
1116 }
1117}
1118
1119fn add_residual(tx_size: TxSize, eob: usize, dq: &[i16; 64], dest: &mut [u8], stride: usize) {
1122 if eob == 0 {
1123 return;
1124 }
1125 match tx_size {
1126 TxSize::Tx4X4 => {
1127 let block: [i16; 16] = dq[..16].try_into().expect("4x4 block is 16 coeffs");
1128 idct4x4_add(&block, dest, stride);
1129 }
1130 TxSize::Tx8X8 => idct8x8_add(dq, dest, stride),
1131 _ => unreachable!("decoder only supports 4x4 and 8x8 transforms"),
1132 }
1133}
1134
1135#[cfg(all(test, feature = "test-utils"))]
1138mod tests;