1use super::bits::BitReader;
32use super::cdef::CdefFilter;
33use super::cdf;
34use super::coder::{CoeffJob, Site, TileCoder};
35use super::coeff::{CoeffCdfs, TxTypeCtx};
36use super::deblock::Deblock;
37use super::direction::{
38 ANGLE_STEP, Edge, mode_base_angle, predict_directional, predict_filter_intra,
39};
40use super::frame::{Cdef, FrameHeader, LoopFilter, Segmentation, TileInfo, TxMode};
41use super::palette::{PALETTE_COLORS, color_context, palette_cache};
42use super::plane::Plane;
43use super::predict::{IntraMode, PredBlock, predict_intra_block};
44use super::restoration::{
45 LoopRestore, PlaneLr, RESTORE_NONE, RESTORE_SGRPROJ, RESTORE_SWITCHABLE, RESTORE_WIENER,
46 SGRPROJ_XQD_MID, WIENER_TAPS_MID, count_units_in_frame, read_sgrproj_unit, read_wiener_unit,
47};
48use super::seq::SequenceHeader;
49use super::superres::{SUPERRES_NUM, Superres};
50use super::symbol::SymbolDecoder;
51use super::transform::{
52 TxSize, TxType, ac_q, add_residual, dc_q, dequantize_with_matrix, inverse_transform_2d,
53 quantizer_matrix,
54};
55use super::transform_type::{IntraTxTypeCdfs, intra_dir, intra_tx_set};
56use super::tx_size::{BLOCK_4X4, TxDepthCdfs, TxSizeParams, code_tx_size, max_tx_size_rect};
57use otf_pixels_core::{PixelsError, Result};
58
59const MI_SIZE: usize = 4;
61pub const FRAME_LF_COUNT: usize = 4;
63const DELTA_SMALL: i32 = 3;
66const MAX_LOOP_FILTER: i32 = 63;
68pub const MAX_SEGMENTS: usize = 8;
70const SEG_LVL_ALT_Q: usize = 0;
72const SEG_LVL_ALT_LF_Y_V: usize = 1;
75const SEG_LVL_SKIP: usize = 6;
77const DC_PRED: usize = 0;
79const UV_CFL_PRED: usize = 13;
81const MAX_ANGLE_DELTA: i32 = 3;
83
84const INTRA_MODE_CONTEXT: [usize; 13] = [0, 1, 2, 3, 4, 4, 4, 4, 3, 0, 1, 2, 0];
87
88const PARTITION_NONE: usize = 0;
90const PARTITION_HORZ: usize = 1;
91const PARTITION_VERT: usize = 2;
92const PARTITION_SPLIT: usize = 3;
93const PARTITION_HORZ_A: usize = 4;
94const PARTITION_HORZ_B: usize = 5;
95const PARTITION_VERT_A: usize = 6;
96const PARTITION_VERT_B: usize = 7;
97const PARTITION_HORZ_4: usize = 8;
98const PARTITION_VERT_4: usize = 9;
99
100pub struct DecodedFrame {
104 pub planes: Vec<Plane>,
106}
107
108pub fn decode_still(
119 seq: &SequenceHeader,
120 frame: &FrameHeader,
121 tile_groups: &[&[u8]],
122) -> Result<DecodedFrame> {
123 if !frame.coded_lossless && !unimplemented_filters_off(frame) {
130 return Err(PixelsError::unsupported(
131 "avif: lossy frames are decoded only with film grain disabled; film \
132 grain synthesis is not implemented yet",
133 ));
134 }
135 if frame.allow_intrabc {
136 return Err(PixelsError::unsupported(
139 "avif: intra block copy is not implemented yet",
140 ));
141 }
142
143 let info = &frame.tile_info;
144 let sb_shift = if seq.use_128x128_superblock { 5 } else { 4 };
145 let mut state = TileState::new(seq, frame)?;
146 let mut next_tile = 0;
147 for group in tile_groups {
148 for tile in split_tile_group(group, info)? {
149 if tile.number != next_tile {
152 return Err(PixelsError::malformed(
153 "avif",
154 format!(
155 "tile {} arrived where tile {next_tile} was due",
156 tile.number
157 ),
158 ));
159 }
160 next_tile += 1;
161 let bounds = tile_bounds(info, sb_shift, state.mi_rows, state.mi_cols, tile.number);
162 state.decode_tile(tile.data, bounds)?;
163 }
164 }
165 if next_tile != info.count() {
166 return Err(PixelsError::malformed(
167 "avif",
168 format!("the frame codes {next_tile} of its {} tiles", info.count()),
169 ));
170 }
171 state.post_filter();
172 Ok(DecodedFrame {
173 planes: state.planes,
174 })
175}
176
177#[derive(Debug)]
179struct Tile<'a> {
180 number: u32,
181 data: &'a [u8],
182}
183
184fn split_tile_group<'a>(group: &'a [u8], info: &TileInfo) -> Result<Vec<Tile<'a>>> {
190 let count = info.count();
191 let mut reader = BitReader::new(group);
192 let (start, end) = if count > 1 && reader.flag()? {
193 let bits = info.cols_log2 + info.rows_log2;
194 (reader.f(bits)?, reader.f(bits)?)
195 } else {
196 (0, count.saturating_sub(1))
197 };
198 if start > end || end >= count {
199 return Err(PixelsError::malformed(
200 "avif",
201 format!("tile group spans tiles {start}..={end} of {count}"),
202 ));
203 }
204 reader.byte_alignment()?;
205 let mut rest = group.get(reader.byte_position()..).unwrap_or(&[]);
206
207 let mut tiles = Vec::new();
208 for number in start..=end {
209 let data = if number == end {
210 core::mem::take(&mut rest)
211 } else {
212 let width = info.tile_size_bytes as usize;
213 let size_bytes = rest.get(..width).ok_or_else(|| tile_overrun(number))?;
214 let size = size_bytes
215 .iter()
216 .rev()
217 .fold(0_usize, |acc, &b| (acc << 8) | usize::from(b))
218 + 1;
219 let body = rest.get(width..).ok_or_else(|| tile_overrun(number))?;
220 let (data, after) = (body.get(..size), body.get(size..));
221 rest = after.ok_or_else(|| tile_overrun(number))?;
222 data.ok_or_else(|| tile_overrun(number))?
223 };
224 tiles.push(Tile { number, data });
225 }
226 Ok(tiles)
227}
228
229fn tile_overrun(number: u32) -> PixelsError {
230 PixelsError::malformed(
231 "avif",
232 format!("tile {number} claims more bytes than its tile group holds"),
233 )
234}
235
236pub(crate) fn tile_bounds(
238 info: &TileInfo,
239 sb_shift: u32,
240 mi_rows: usize,
241 mi_cols: usize,
242 number: u32,
243) -> TileBounds {
244 let (row, col) = (number / info.cols.max(1), number % info.cols.max(1));
245 let start = |starts: &[u32], i: u32, limit: usize| {
246 starts
247 .get(i as usize)
248 .map_or(limit, |&sb| ((sb as usize) << sb_shift).min(limit))
249 };
250 TileBounds {
251 row_start: start(&info.row_starts_sb, row, mi_rows),
252 row_end: start(&info.row_starts_sb, row + 1, mi_rows),
253 col_start: start(&info.col_starts_sb, col, mi_cols),
254 col_end: start(&info.col_starts_sb, col + 1, mi_cols),
255 }
256}
257
258#[derive(Debug, Clone, Copy)]
261pub(crate) struct TileBounds {
262 pub(crate) row_start: usize,
263 pub(crate) row_end: usize,
264 pub(crate) col_start: usize,
265 pub(crate) col_end: usize,
266}
267
268fn unimplemented_filters_off(frame: &FrameHeader) -> bool {
273 !frame.film_grain.apply_grain
274}
275
276fn at_least_block_8x8(bw4: usize, bh4: usize) -> bool {
281 bw4 * bh4 >= 4
282}
283
284fn build_plane_lr(seq: &SequenceHeader, frame: &FrameHeader) -> Vec<PlaneLr> {
288 let num_planes = seq.color.num_planes as usize;
289 let lr = &frame.loop_restoration;
290 let upscaled_width = frame.upscaled_width as usize;
291 let frame_height = frame.frame_height as usize;
292 let round2 = |x: usize, n: usize| if n == 0 { x } else { (x + 1) >> 1 };
294 (0..num_planes)
295 .map(|plane| {
296 let frt = lr
297 .frame_restoration_type
298 .get(plane)
299 .copied()
300 .unwrap_or(RESTORE_NONE);
301 let unit_size = lr.unit_size.get(plane).copied().unwrap_or(256) as usize;
302 let (sub_x, sub_y) = if plane == 0 {
303 (0, 0)
304 } else {
305 (
306 seq.color.subsampling_x as usize,
307 seq.color.subsampling_y as usize,
308 )
309 };
310 let rows = count_units_in_frame(unit_size, round2(frame_height, sub_y));
311 let cols = count_units_in_frame(unit_size, round2(upscaled_width, sub_x));
312 PlaneLr::new(frt, unit_size, rows, cols)
313 })
314 .collect()
315}
316
317struct FrameCdfs {
320 partition_w8: [[u16; 5]; 4],
321 partition_w16: [[u16; 11]; 4],
322 partition_w32: [[u16; 11]; 4],
323 partition_w64: [[u16; 11]; 4],
324 partition_w128: [[u16; 9]; 4],
325 skip: [[u16; 3]; 3],
326 intra_frame_y_mode: [[[u16; 14]; 5]; 5],
327 uv_cfl_allowed: [[u16; 15]; 13],
328 uv_cfl_not_allowed: [[u16; 14]; 13],
329 angle_delta: [[u16; 8]; 8],
330 filter_intra: [[u16; 3]; 22],
331 filter_intra_mode: [u16; 6],
332 palette_y_mode: [[[u16; 3]; 3]; 7],
333 palette_uv_mode: [[u16; 3]; 2],
334 palette_y_size: [[u16; 8]; 7],
335 palette_uv_size: [[u16; 8]; 7],
336 palette_y_color: PaletteColorCdfs,
337 palette_uv_color: PaletteColorCdfs,
338 cfl_sign: [u16; 9],
339 cfl_alpha: [[u16; 17]; 6],
340 coeff: CoeffCdfs,
341 intra_tx_type: IntraTxTypeCdfs,
342 tx_depth: TxDepthCdfs,
343 restoration_type: [u16; 4],
344 use_wiener: [u16; 3],
345 use_sgrproj: [u16; 3],
346 delta_q: [u16; 5],
347 delta_lf: [u16; 5],
348 segment_id: [[u16; 9]; 3],
349 delta_lf_multi: [[u16; 5]; FRAME_LF_COUNT],
351}
352
353struct PaletteColorCdfs {
355 size2: [[u16; 3]; 5],
356 size3: [[u16; 4]; 5],
357 size4: [[u16; 5]; 5],
358 size5: [[u16; 6]; 5],
359 size6: [[u16; 7]; 5],
360 size7: [[u16; 8]; 5],
361 size8: [[u16; 9]; 5],
362}
363
364impl PaletteColorCdfs {
365 fn row(&mut self, palette_size: usize, ctx: usize) -> Result<&mut [u16]> {
367 Ok(match palette_size {
368 2 => get_mut(&mut self.size2, ctx)?,
369 3 => get_mut(&mut self.size3, ctx)?,
370 4 => get_mut(&mut self.size4, ctx)?,
371 5 => get_mut(&mut self.size5, ctx)?,
372 6 => get_mut(&mut self.size6, ctx)?,
373 7 => get_mut(&mut self.size7, ctx)?,
374 _ => get_mut(&mut self.size8, ctx)?,
375 })
376 }
377}
378
379impl FrameCdfs {
380 fn new(qctx: usize) -> Self {
381 Self {
382 partition_w8: cdf::DEFAULT_PARTITION_W8_CDF,
383 partition_w16: cdf::DEFAULT_PARTITION_W16_CDF,
384 partition_w32: cdf::DEFAULT_PARTITION_W32_CDF,
385 partition_w64: cdf::DEFAULT_PARTITION_W64_CDF,
386 partition_w128: cdf::DEFAULT_PARTITION_W128_CDF,
387 skip: cdf::DEFAULT_SKIP_CDF,
388 intra_frame_y_mode: cdf::DEFAULT_INTRA_FRAME_Y_MODE_CDF,
389 uv_cfl_allowed: cdf::DEFAULT_UV_MODE_CFL_ALLOWED_CDF,
390 uv_cfl_not_allowed: cdf::DEFAULT_UV_MODE_CFL_NOT_ALLOWED_CDF,
391 angle_delta: cdf::DEFAULT_ANGLE_DELTA_CDF,
392 filter_intra: cdf::DEFAULT_FILTER_INTRA_CDF,
393 filter_intra_mode: cdf::DEFAULT_FILTER_INTRA_MODE_CDF,
394 palette_y_mode: cdf::DEFAULT_PALETTE_Y_MODE_CDF,
395 palette_uv_mode: cdf::DEFAULT_PALETTE_UV_MODE_CDF,
396 palette_y_size: cdf::DEFAULT_PALETTE_Y_SIZE_CDF,
397 palette_uv_size: cdf::DEFAULT_PALETTE_UV_SIZE_CDF,
398 palette_y_color: PaletteColorCdfs {
399 size2: cdf::DEFAULT_PALETTE_SIZE_2_Y_COLOR_CDF,
400 size3: cdf::DEFAULT_PALETTE_SIZE_3_Y_COLOR_CDF,
401 size4: cdf::DEFAULT_PALETTE_SIZE_4_Y_COLOR_CDF,
402 size5: cdf::DEFAULT_PALETTE_SIZE_5_Y_COLOR_CDF,
403 size6: cdf::DEFAULT_PALETTE_SIZE_6_Y_COLOR_CDF,
404 size7: cdf::DEFAULT_PALETTE_SIZE_7_Y_COLOR_CDF,
405 size8: cdf::DEFAULT_PALETTE_SIZE_8_Y_COLOR_CDF,
406 },
407 palette_uv_color: PaletteColorCdfs {
408 size2: cdf::DEFAULT_PALETTE_SIZE_2_UV_COLOR_CDF,
409 size3: cdf::DEFAULT_PALETTE_SIZE_3_UV_COLOR_CDF,
410 size4: cdf::DEFAULT_PALETTE_SIZE_4_UV_COLOR_CDF,
411 size5: cdf::DEFAULT_PALETTE_SIZE_5_UV_COLOR_CDF,
412 size6: cdf::DEFAULT_PALETTE_SIZE_6_UV_COLOR_CDF,
413 size7: cdf::DEFAULT_PALETTE_SIZE_7_UV_COLOR_CDF,
414 size8: cdf::DEFAULT_PALETTE_SIZE_8_UV_COLOR_CDF,
415 },
416 cfl_sign: cdf::DEFAULT_CFL_SIGN_CDF,
417 cfl_alpha: cdf::DEFAULT_CFL_ALPHA_CDF,
418 coeff: CoeffCdfs::new(qctx),
419 intra_tx_type: IntraTxTypeCdfs::new(),
420 tx_depth: TxDepthCdfs::new(),
421 restoration_type: cdf::DEFAULT_RESTORATION_TYPE_CDF,
422 use_wiener: cdf::DEFAULT_USE_WIENER_CDF,
423 use_sgrproj: cdf::DEFAULT_USE_SGRPROJ_CDF,
424 delta_q: cdf::DEFAULT_DELTA_Q_CDF,
425 delta_lf: cdf::DEFAULT_DELTA_LF_CDF,
426 segment_id: cdf::DEFAULT_SEGMENT_ID_CDF,
427 delta_lf_multi: [cdf::DEFAULT_DELTA_LF_CDF; FRAME_LF_COUNT],
428 }
429 }
430}
431
432struct LevelContext {
435 above_level: Vec<u8>,
436 above_dc: Vec<u8>,
437 left_level: Vec<u8>,
438 left_dc: Vec<u8>,
439}
440
441pub(crate) struct TileState {
443 pub(crate) planes: Vec<Plane>,
444 cdfs: FrameCdfs,
445 bit_depth: u8,
446 num_planes: usize,
447 mi_cols: usize,
448 mi_rows: usize,
449 enable_filter_intra: bool,
450 enable_edge_filter: bool,
451 allow_screen_content: bool,
452 reduced_tx_set: bool,
454 coded_lossless: bool,
456 lossless: bool,
459 lossless_array: [bool; MAX_SEGMENTS],
461 segmentation: Segmentation,
463 seg_pre_skip: bool,
465 last_active_segment: usize,
467 segment_id: usize,
469 segment_ids: Vec<u8>,
472 using_qmatrix: bool,
474 qm: [u8; 3],
475 tx_mode: TxMode,
477 q_dc: [i32; 3],
480 q_ac: [i32; 3],
482 current_qindex: i32,
486 base_q: i32,
488 qctx: usize,
490 tile: TileBounds,
493 delta_q_present: bool,
495 delta_q_res: u32,
496 delta_lf_present: bool,
498 delta_lf_res: u32,
499 delta_lf_multi: bool,
500 read_deltas: bool,
502 delta_lf: [i8; FRAME_LF_COUNT],
504 delta_lfs: Vec<[i8; FRAME_LF_COUNT]>,
507 tx_sizes: Vec<u8>,
510 lf_tx_sizes: [Vec<u8>; 3],
514 frame_width: usize,
518 frame_height: usize,
519 upscaled_width: usize,
521 superres_denom: Option<usize>,
523 loop_filter: LoopFilter,
525 cdef: Cdef,
527 enable_cdef: bool,
530 cdef_idx: Vec<i16>,
533 subsampling_x: usize,
535 subsampling_y: usize,
536 uses_lr: bool,
538 lr: Vec<PlaneLr>,
541 ref_lr_wiener: [[[i32; 3]; 2]; 3],
544 ref_sgr_xqd: [[i32; 2]; 3],
546 sb_size4: usize,
547 block_decoded: Vec<Vec<u8>>,
550 y_modes: Vec<u8>,
552 uv_modes: Vec<u8>,
554 palette_sizes: [Vec<u8>; 2],
557 palette_colors: [Vec<[u16; PALETTE_COLORS]>; 2],
559 skips: Vec<u8>,
561 mi_wide_log2: Vec<u8>,
563 mi_high_log2: Vec<u8>,
565 ctx: Vec<LevelContext>,
567 max_luma_w: usize,
570 max_luma_h: usize,
571}
572
573impl TileState {
574 fn new(seq: &SequenceHeader, frame: &FrameHeader) -> Result<Self> {
575 let mi_cols = frame.mi_cols as usize;
576 let mi_rows = frame.mi_rows as usize;
577 let num_planes = seq.color.num_planes as usize;
578 let sb_size4 = if seq.use_128x128_superblock { 32 } else { 16 };
586 let padded_w = mi_cols.div_ceil(sb_size4) * sb_size4 * MI_SIZE;
587 let padded_h = mi_rows.div_ceil(sb_size4) * sb_size4 * MI_SIZE;
588 let (sub_x, sub_y) = (
589 seq.color.subsampling_x as usize,
590 seq.color.subsampling_y as usize,
591 );
592 let planes = (0..num_planes)
593 .map(|p| {
594 if p == 0 {
595 Plane::new(padded_w, padded_h)
596 } else {
597 Plane::new(padded_w >> sub_x, padded_h >> sub_y)
598 }
599 })
600 .collect();
601 let ctx = (0..num_planes)
602 .map(|_| LevelContext {
603 above_level: vec![0; mi_cols],
604 above_dc: vec![0; mi_cols],
605 left_level: vec![0; mi_rows],
606 left_dc: vec![0; mi_rows],
607 })
608 .collect();
609 let bd_stride = sb_size4 + 2;
610 let block_decoded = (0..num_planes)
611 .map(|_| vec![0; bd_stride * bd_stride])
612 .collect();
613 let base_q = i32::from(frame.quantization.base_q_idx);
616 let qctx = match base_q {
617 0..=20 => 0,
618 21..=60 => 1,
619 61..=120 => 2,
620 _ => 3,
621 };
622 let q = &frame.quantization;
623 Ok(Self {
624 planes,
625 cdfs: FrameCdfs::new(qctx),
626 bit_depth: seq.color.bit_depth,
627 num_planes,
628 mi_cols,
629 mi_rows,
630 enable_filter_intra: seq.enable_filter_intra,
631 enable_edge_filter: seq.enable_intra_edge_filter,
632 allow_screen_content: frame.allow_screen_content_tools,
633 reduced_tx_set: frame.reduced_tx_set,
634 coded_lossless: frame.coded_lossless,
635 lossless: frame.coded_lossless,
636 lossless_array: frame.lossless,
637 segmentation: frame.segmentation.clone(),
638 seg_pre_skip: frame.segmentation.pre_skip(),
639 last_active_segment: frame.segmentation.last_active_segment(),
640 segment_id: 0,
641 segment_ids: vec![0; mi_cols * mi_rows],
642 using_qmatrix: q.using_qmatrix,
643 qm: [q.qm_y, q.qm_u, q.qm_v],
644 tx_mode: frame.tx_mode,
645 q_dc: [q.delta_q_y_dc, q.delta_q_u_dc, q.delta_q_v_dc],
646 q_ac: [0, q.delta_q_u_ac, q.delta_q_v_ac],
647 current_qindex: base_q,
648 base_q,
649 qctx,
650 tile: TileBounds {
651 row_start: 0,
652 row_end: mi_rows,
653 col_start: 0,
654 col_end: mi_cols,
655 },
656 delta_q_present: frame.delta_q_present,
657 delta_q_res: frame.delta_q_res,
658 delta_lf_present: frame.delta_lf_present,
659 delta_lf_res: frame.delta_lf_res,
660 delta_lf_multi: frame.delta_lf_multi,
661 read_deltas: false,
662 delta_lf: [0; FRAME_LF_COUNT],
663 delta_lfs: vec![[0; FRAME_LF_COUNT]; mi_cols * mi_rows],
664 tx_sizes: vec![0; mi_cols * mi_rows],
665 lf_tx_sizes: [
666 vec![0; mi_cols * mi_rows],
667 vec![0; mi_cols * mi_rows],
668 vec![0; mi_cols * mi_rows],
669 ],
670 frame_width: frame.frame_width as usize,
671 frame_height: frame.frame_height as usize,
672 upscaled_width: frame.upscaled_width as usize,
673 superres_denom: (frame.superres_denom as usize != SUPERRES_NUM)
677 .then_some(frame.superres_denom as usize),
678 loop_filter: frame.loop_filter.clone(),
679 cdef: frame.cdef.clone(),
680 enable_cdef: seq.enable_cdef,
681 cdef_idx: vec![-1; mi_cols * mi_rows],
682 subsampling_x: seq.color.subsampling_x as usize,
683 subsampling_y: seq.color.subsampling_y as usize,
684 uses_lr: frame.loop_restoration.uses_lr,
685 lr: build_plane_lr(seq, frame),
686 ref_lr_wiener: [[WIENER_TAPS_MID; 2]; 3],
687 ref_sgr_xqd: [SGRPROJ_XQD_MID; 3],
688 sb_size4,
689 block_decoded,
690 y_modes: vec![0; mi_cols * mi_rows],
691 uv_modes: vec![0; mi_cols * mi_rows],
692 palette_sizes: [vec![0; mi_cols * mi_rows], vec![0; mi_cols * mi_rows]],
693 palette_colors: [
694 vec![[0; PALETTE_COLORS]; mi_cols * mi_rows],
695 vec![[0; PALETTE_COLORS]; mi_cols * mi_rows],
696 ],
697 skips: vec![0; mi_cols * mi_rows],
698 mi_wide_log2: vec![0; mi_cols * mi_rows],
699 mi_high_log2: vec![0; mi_cols * mi_rows],
700 ctx,
701 max_luma_w: 0,
702 max_luma_h: 0,
703 })
704 }
705
706 fn decode_tile(&mut self, tile_data: &[u8], tile: TileBounds) -> Result<()> {
709 let mut dec = SymbolDecoder::new(tile_data, false)?;
710 self.code_tile(&mut dec, tile)
711 }
712
713 pub(crate) fn code_tile(&mut self, dec: &mut impl TileCoder, tile: TileBounds) -> Result<()> {
716 self.tile = tile;
717 self.cdfs = FrameCdfs::new(self.qctx);
718 self.current_qindex = self.base_q;
719 self.delta_lf = [0; FRAME_LF_COUNT];
720 self.ref_lr_wiener = [[WIENER_TAPS_MID; 2]; 3];
721 self.ref_sgr_xqd = [SGRPROJ_XQD_MID; 3];
722 for c in &mut self.ctx {
723 c.above_level.fill(0);
724 c.above_dc.fill(0);
725 }
726 let sb_size4 = self.sb_size4;
727 let mut sb_row = tile.row_start;
730 while sb_row < tile.row_end {
731 self.reset_left_context();
732 let mut sb_col = tile.col_start;
733 while sb_col < tile.col_end {
734 self.read_deltas = self.delta_q_present;
735 self.clear_block_decoded(sb_row, sb_col);
736 self.read_lr(dec, sb_row, sb_col)?;
737 self.decode_partition(dec, sb_row, sb_col, sb_size4)?;
738 sb_col += sb_size4;
739 }
740 sb_row += sb_size4;
741 }
742 Ok(())
743 }
744
745 pub(crate) fn post_filter(&mut self) {
748 self.deblock();
749 let curr = self.uses_lr.then(|| self.planes.clone());
754 self.cdef();
755 if let Some(superres) = self.superres() {
756 self.planes = superres.upscale(&self.planes);
757 if let Some(curr) = curr {
758 let curr = superres.upscale(&curr);
759 self.loop_restore(&curr);
760 }
761 } else if let Some(curr) = curr {
762 self.loop_restore(&curr);
763 }
764 }
765
766 fn superres(&self) -> Option<Superres> {
768 self.superres_denom.map(|_| Superres {
769 frame_width: self.frame_width,
770 upscaled_width: self.upscaled_width,
771 frame_height: self.frame_height,
772 mi_cols: self.mi_cols,
773 subsampling_x: self.subsampling_x,
774 subsampling_y: self.subsampling_y,
775 bit_depth: self.bit_depth,
776 })
777 }
778
779 fn deblock(&mut self) {
783 Deblock {
784 planes: &mut self.planes,
785 loop_filter: &self.loop_filter,
786 bit_depth: self.bit_depth,
787 num_planes: self.num_planes,
788 subsampling_x: self.subsampling_x,
789 subsampling_y: self.subsampling_y,
790 mi_rows: self.mi_rows,
791 mi_cols: self.mi_cols,
792 frame_width: self.frame_width,
793 frame_height: self.frame_height,
794 lf_tx_sizes: &self.lf_tx_sizes,
795 delta_lfs: &self.delta_lfs,
796 delta_lf_multi: self.delta_lf_multi,
797 segment_ids: &self.segment_ids,
798 segment_lf: core::array::from_fn(|segment| {
799 core::array::from_fn(|i| {
800 self.segmentation
801 .feature_value(segment, SEG_LVL_ALT_LF_Y_V + i)
802 })
803 }),
804 }
805 .run();
806 }
807
808 fn cdef(&mut self) {
812 CdefFilter {
813 planes: &mut self.planes,
814 cdef: &self.cdef,
815 cdef_idx: &self.cdef_idx,
816 skips: &self.skips,
817 bit_depth: self.bit_depth,
818 num_planes: self.num_planes,
819 mi_rows: self.mi_rows,
820 mi_cols: self.mi_cols,
821 subsampling_x: self.subsampling_x,
822 subsampling_y: self.subsampling_y,
823 }
824 .run();
825 }
826
827 fn loop_restore(&mut self, curr: &[Plane]) {
832 let cdef = self.planes.clone();
833 LoopRestore {
834 planes: &mut self.planes,
835 curr,
836 cdef: &cdef,
837 lr: &self.lr,
838 bit_depth: self.bit_depth,
839 num_planes: self.num_planes,
840 subsampling_x: self.subsampling_x,
841 subsampling_y: self.subsampling_y,
842 upscaled_width: self.upscaled_width,
843 frame_height: self.frame_height,
844 }
845 .run();
846 }
847
848 fn clear_block_decoded(&mut self, r: usize, c: usize) {
851 let sb = self.sb_size4;
852 let stride = sb + 2;
853 for plane in 0..self.num_planes {
854 let (sub_x, sub_y) = self.plane_subsampling(plane);
855 let sb_width4 = ((self.tile.col_end - c) >> sub_x) as isize;
856 let sb_height4 = ((self.tile.row_end - r) >> sub_y) as isize;
857 let (sb_w, sb_h) = ((sb >> sub_x) as isize, (sb >> sub_y) as isize);
858 let Some(grid) = self.block_decoded.get_mut(plane) else {
859 continue;
860 };
861 for v in grid.iter_mut() {
862 *v = 0;
863 }
864 for x in -1_isize..=sb_w {
867 if x < sb_width4 {
868 if let Some(slot) = grid.get_mut(bd_index(stride, -1, x)) {
869 *slot = 1;
870 }
871 }
872 }
873 for y in -1_isize..=sb_h {
874 if y < sb_height4 {
875 if let Some(slot) = grid.get_mut(bd_index(stride, y, -1)) {
876 *slot = 1;
877 }
878 }
879 }
880 if let Some(slot) = grid.get_mut(bd_index(stride, sb_h, -1)) {
881 *slot = 0;
882 }
883 }
884 }
885
886 fn block_decoded_at(&self, plane: usize, sub_row: isize, sub_col: isize) -> bool {
887 let stride = self.sb_size4 + 2;
888 self.block_decoded
889 .get(plane)
890 .and_then(|g| g.get(bd_index(stride, sub_row, sub_col)))
891 .is_some_and(|&v| v != 0)
892 }
893
894 fn set_block_decoded(&mut self, plane: usize, sub_row: isize, sub_col: isize) {
895 let stride = self.sb_size4 + 2;
896 if let Some(slot) = self
897 .block_decoded
898 .get_mut(plane)
899 .and_then(|g| g.get_mut(bd_index(stride, sub_row, sub_col)))
900 {
901 *slot = 1;
902 }
903 }
904
905 const fn avail_u(&self, r: usize) -> bool {
907 r > self.tile.row_start
908 }
909
910 const fn avail_l(&self, c: usize) -> bool {
912 c > self.tile.col_start
913 }
914
915 fn reset_left_context(&mut self) {
916 for c in &mut self.ctx {
917 c.left_level.fill(0);
918 c.left_dc.fill(0);
919 }
920 }
921
922 fn decode_partition(
925 &mut self,
926 dec: &mut impl TileCoder,
927 r: usize,
928 c: usize,
929 bsize4: usize,
930 ) -> Result<()> {
931 if r >= self.mi_rows || c >= self.mi_cols {
932 return Ok(());
933 }
934 let avail_u = self.avail_u(r);
935 let avail_l = self.avail_l(c);
936 let half = bsize4 >> 1;
937 let has_rows = r + half < self.mi_rows;
938 let has_cols = c + half < self.mi_cols;
939
940 let partition = if bsize4 < 2 {
941 PARTITION_NONE
942 } else if has_rows && has_cols {
943 self.read_partition(dec, r, c, bsize4, avail_u, avail_l)?
944 } else if has_cols {
945 if self.read_split_or(dec, r, c, bsize4, avail_u, avail_l, true)? {
946 PARTITION_SPLIT
947 } else {
948 PARTITION_HORZ
949 }
950 } else if has_rows {
951 if self.read_split_or(dec, r, c, bsize4, avail_u, avail_l, false)? {
952 PARTITION_SPLIT
953 } else {
954 PARTITION_VERT
955 }
956 } else {
957 PARTITION_SPLIT
958 };
959
960 let quarter = bsize4 >> 2;
961 match partition {
962 PARTITION_NONE => self.decode_block(dec, r, c, bsize4, bsize4)?,
963 PARTITION_HORZ => {
964 self.decode_block(dec, r, c, bsize4, half)?;
965 if has_rows {
966 self.decode_block(dec, r + half, c, bsize4, half)?;
967 }
968 }
969 PARTITION_VERT => {
970 self.decode_block(dec, r, c, half, bsize4)?;
971 if has_cols {
972 self.decode_block(dec, r, c + half, half, bsize4)?;
973 }
974 }
975 PARTITION_SPLIT => {
976 self.decode_partition(dec, r, c, half)?;
977 self.decode_partition(dec, r, c + half, half)?;
978 self.decode_partition(dec, r + half, c, half)?;
979 self.decode_partition(dec, r + half, c + half, half)?;
980 }
981 PARTITION_HORZ_A => {
982 self.decode_block(dec, r, c, half, half)?;
983 self.decode_block(dec, r, c + half, half, half)?;
984 self.decode_block(dec, r + half, c, bsize4, half)?;
985 }
986 PARTITION_HORZ_B => {
987 self.decode_block(dec, r, c, bsize4, half)?;
988 self.decode_block(dec, r + half, c, half, half)?;
989 self.decode_block(dec, r + half, c + half, half, half)?;
990 }
991 PARTITION_VERT_A => {
992 self.decode_block(dec, r, c, half, half)?;
993 self.decode_block(dec, r + half, c, half, half)?;
994 self.decode_block(dec, r, c + half, half, bsize4)?;
995 }
996 PARTITION_VERT_B => {
997 self.decode_block(dec, r, c, half, bsize4)?;
998 self.decode_block(dec, r, c + half, half, half)?;
999 self.decode_block(dec, r + half, c + half, half, half)?;
1000 }
1001 PARTITION_HORZ_4 => {
1002 for k in 0..4 {
1003 let rr = r + quarter * k;
1004 if k == 3 && rr >= self.mi_rows {
1005 break;
1006 }
1007 self.decode_block(dec, rr, c, bsize4, quarter)?;
1008 }
1009 }
1010 PARTITION_VERT_4 => {
1011 for k in 0..4 {
1012 let cc = c + quarter * k;
1013 if k == 3 && cc >= self.mi_cols {
1014 break;
1015 }
1016 self.decode_block(dec, r, cc, quarter, bsize4)?;
1017 }
1018 }
1019 _ => {
1020 return Err(PixelsError::malformed("avif", "invalid partition type"));
1021 }
1022 }
1023 Ok(())
1024 }
1025
1026 fn read_partition(
1028 &mut self,
1029 dec: &mut impl TileCoder,
1030 r: usize,
1031 c: usize,
1032 bsize4: usize,
1033 avail_u: bool,
1034 avail_l: bool,
1035 ) -> Result<usize> {
1036 let ctx = self.partition_ctx(r, c, bsize4, avail_u, avail_l);
1037 let bsl = floor_log2_usize(bsize4);
1038 let cdf_row = self.partition_cdf(bsl, ctx)?;
1039 dec.symbol(cdf_row, Site::Partition { r, c, bsize4 })
1040 }
1041
1042 fn partition_ctx(
1044 &self,
1045 r: usize,
1046 c: usize,
1047 bsize4: usize,
1048 avail_u: bool,
1049 avail_l: bool,
1050 ) -> usize {
1051 let bsl = floor_log2_usize(bsize4) as u8;
1052 let above = avail_u
1053 && r.checked_sub(1)
1054 .and_then(|ru| self.mi_wide_log2.get(ru * self.mi_cols + c))
1055 .is_some_and(|&w| w < bsl);
1056 let left = avail_l
1057 && c.checked_sub(1)
1058 .and_then(|cl| self.mi_high_log2.get(r * self.mi_cols + cl))
1059 .is_some_and(|&h| h < bsl);
1060 usize::from(left) * 2 + usize::from(above)
1061 }
1062
1063 fn partition_cdf(&mut self, bsl: u32, ctx: usize) -> Result<&mut [u16]> {
1065 let row: &mut [u16] = match bsl {
1066 1 => get_mut(&mut self.cdfs.partition_w8, ctx)?,
1067 2 => get_mut(&mut self.cdfs.partition_w16, ctx)?,
1068 3 => get_mut(&mut self.cdfs.partition_w32, ctx)?,
1069 4 => get_mut(&mut self.cdfs.partition_w64, ctx)?,
1070 _ => get_mut(&mut self.cdfs.partition_w128, ctx)?,
1071 };
1072 Ok(row)
1073 }
1074
1075 #[allow(
1078 clippy::too_many_arguments,
1079 reason = "mirrors the split_or_* context inputs"
1080 )]
1081 fn read_split_or(
1082 &mut self,
1083 dec: &mut impl TileCoder,
1084 r: usize,
1085 c: usize,
1086 bsize4: usize,
1087 avail_u: bool,
1088 avail_l: bool,
1089 horz: bool,
1090 ) -> Result<bool> {
1091 let ctx = self.partition_ctx(r, c, bsize4, avail_u, avail_l);
1092 let bsl = floor_log2_usize(bsize4);
1093 let is_128 = bsize4 == 32;
1094 let src = self.partition_cdf(bsl, ctx)?;
1096 let partition_cdf: Vec<u16> = src.to_vec();
1097 let prob = |k: usize| -> i32 {
1098 let hi = partition_cdf.get(k).copied().unwrap_or(0);
1099 let lo = k
1100 .checked_sub(1)
1101 .and_then(|i| partition_cdf.get(i))
1102 .copied()
1103 .unwrap_or(0);
1104 i32::from(hi) - i32::from(lo)
1105 };
1106 let mut psum = if horz {
1109 prob(PARTITION_VERT) + prob(PARTITION_SPLIT) + prob(4) + prob(6) + prob(7)
1110 } else {
1111 prob(PARTITION_HORZ) + prob(PARTITION_SPLIT) + prob(4) + prob(5) + prob(6)
1112 };
1113 if !is_128 {
1114 psum += if horz { prob(9) } else { prob(8) };
1115 }
1116 let mut derived = [((1 << 15) - psum) as u16, 1 << 15, 0];
1117 Ok(dec.symbol(&mut derived, Site::SplitOr { r, c, bsize4 })? != 0)
1118 }
1119
1120 fn decode_block(
1122 &mut self,
1123 dec: &mut impl TileCoder,
1124 r: usize,
1125 c: usize,
1126 bw4: usize,
1127 bh4: usize,
1128 ) -> Result<()> {
1129 let avail_u = self.avail_u(r);
1130 let avail_l = self.avail_l(c);
1131 let (sub_x, sub_y) = (self.subsampling_x, self.subsampling_y);
1135 let shares_chroma =
1136 (bh4 == 1 && sub_y == 1 && r & 1 == 0) || (bw4 == 1 && sub_x == 1 && c & 1 == 0);
1137 let has_chroma = self.num_planes > 1 && !shares_chroma;
1138 let (avail_u_chroma, avail_l_chroma) = if has_chroma {
1141 (
1142 if sub_y == 1 && bh4 == 1 {
1143 r >= self.tile.row_start + 2
1144 } else {
1145 avail_u
1146 },
1147 if sub_x == 1 && bw4 == 1 {
1148 c >= self.tile.col_start + 2
1149 } else {
1150 avail_l
1151 },
1152 )
1153 } else {
1154 (false, false)
1155 };
1156
1157 dec.plan_block(self, r, c, bw4, bh4);
1161 self.segment_id = 0;
1162 if self.seg_pre_skip {
1163 self.read_segment_id(dec, r, c, avail_u, avail_l, false)?;
1164 }
1165 let skip = if self.seg_pre_skip
1166 && self
1167 .segmentation
1168 .feature_active(self.segment_id, SEG_LVL_SKIP)
1169 {
1170 true
1171 } else {
1172 self.read_skip(dec, r, c, avail_u, avail_l)?
1173 };
1174 if !self.seg_pre_skip {
1175 self.read_segment_id(dec, r, c, avail_u, avail_l, skip)?;
1176 }
1177 self.lossless = self
1178 .lossless_array
1179 .get(self.segment_id)
1180 .copied()
1181 .unwrap_or(false);
1182 let segment = self.segment_id as u8;
1183 for row in r..(r + bh4).min(self.mi_rows) {
1184 for col in c..(c + bw4).min(self.mi_cols) {
1185 if let Some(slot) = self.segment_ids.get_mut(row * self.mi_cols + col) {
1186 *slot = segment;
1187 }
1188 }
1189 }
1190
1191 self.read_cdef(dec, r, c, bw4, bh4, skip)?;
1194 self.read_delta_qindex(dec, bw4, bh4, skip)?;
1195 self.read_delta_lf(dec, bw4, bh4, skip)?;
1196 self.read_deltas = false;
1197 for row in r..(r + bh4).min(self.mi_rows) {
1198 for col in c..(c + bw4).min(self.mi_cols) {
1199 if let Some(slot) = self.delta_lfs.get_mut(row * self.mi_cols + col) {
1200 *slot = self.delta_lf;
1201 }
1202 }
1203 }
1204
1205 let y_mode = self.read_intra_frame_y_mode(dec, r, c, avail_u, avail_l)?;
1206 let y_delta = self.read_angle_delta(dec, y_mode, bw4, bh4, Site::AngleDeltaY)?;
1207
1208 let (uv_mode, uv_delta, cfl) = if has_chroma {
1209 let (uv, cfl) = self.read_uv_mode(dec, y_mode, bw4, bh4)?;
1210 let d = self.read_angle_delta(dec, uv, bw4, bh4, Site::AngleDeltaUv)?;
1211 (uv, d, cfl)
1212 } else {
1213 (DC_PRED, 0, None)
1214 };
1215
1216 let mut palette = Palette {
1219 block_w: bw4 * MI_SIZE,
1220 block_h: bh4 * MI_SIZE,
1221 ..Palette::default()
1222 };
1223 let palette_ok =
1224 self.allow_screen_content && at_least_block_8x8(bw4, bh4) && bw4 <= 16 && bh4 <= 16;
1225 if palette_ok {
1226 self.read_palette_mode_info(
1227 dec,
1228 r,
1229 c,
1230 bw4,
1231 bh4,
1232 y_mode,
1233 uv_mode,
1234 has_chroma,
1235 &mut palette,
1236 )?;
1237 }
1238
1239 let filter_intra = if palette.size_y > 0 {
1241 None
1242 } else {
1243 self.read_filter_intra(dec, y_mode, bw4, bh4)?
1244 };
1245
1246 self.record_block(r, c, bw4, bh4, y_mode, uv_mode, has_chroma, skip, &palette);
1248
1249 if palette.size_y > 0 || palette.size_uv > 0 {
1251 self.read_palette_tokens(dec, r, c, &mut palette)?;
1252 }
1253
1254 let luma_tx_size = self.read_block_tx_size(dec, r, c, bw4, bh4, skip)?;
1258
1259 if skip {
1260 self.reset_block_context(r, c, bw4, bh4, has_chroma);
1261 }
1262
1263 let modes = BlockModes {
1265 r,
1266 c,
1267 avail_u,
1268 avail_l,
1269 avail_u_chroma,
1270 avail_l_chroma,
1271 y_mode,
1272 uv_mode,
1273 y_delta,
1274 uv_delta,
1275 filter_intra,
1276 cfl,
1277 palette,
1278 luma_tx_size,
1279 };
1280 self.residual(dec, &modes, bw4, bh4, skip, has_chroma)?;
1281 Ok(())
1282 }
1283
1284 fn read_skip(
1285 &mut self,
1286 dec: &mut impl TileCoder,
1287 r: usize,
1288 c: usize,
1289 avail_u: bool,
1290 avail_l: bool,
1291 ) -> Result<bool> {
1292 let mut ctx = 0;
1293 if avail_u {
1294 ctx += usize::from(self.skip_at(r.wrapping_sub(1), c));
1295 }
1296 if avail_l {
1297 ctx += usize::from(self.skip_at(r, c.wrapping_sub(1)));
1298 }
1299 let cdf_row = get_mut(&mut self.cdfs.skip, ctx)?;
1300 Ok(dec.symbol(cdf_row, Site::Skip)? != 0)
1301 }
1302
1303 fn read_cdef(
1310 &mut self,
1311 dec: &mut impl TileCoder,
1312 r: usize,
1313 c: usize,
1314 bw4: usize,
1315 bh4: usize,
1316 skip: bool,
1317 ) -> Result<()> {
1318 if skip || self.coded_lossless || !self.enable_cdef {
1319 return Ok(());
1320 }
1321 let cdef_size4 = 16;
1323 let mask = !(cdef_size4 - 1);
1324 let base_r = r & mask;
1325 let base_c = c & mask;
1326 if self.cdef_idx.get(base_r * self.mi_cols + base_c).copied() != Some(-1) {
1327 return Ok(());
1328 }
1329 let value = dec.literal(self.cdef.bits, Site::CdefIdx)? as i16;
1330 let mut i = base_r;
1331 while i < base_r + bh4 {
1332 let mut j = base_c;
1333 while j < base_c + bw4 {
1334 if i < self.mi_rows && j < self.mi_cols {
1335 if let Some(slot) = self.cdef_idx.get_mut(i * self.mi_cols + j) {
1336 *slot = value;
1337 }
1338 }
1339 j += cdef_size4;
1340 }
1341 i += cdef_size4;
1342 }
1343 Ok(())
1344 }
1345
1346 fn read_segment_id(
1350 &mut self,
1351 dec: &mut impl TileCoder,
1352 r: usize,
1353 c: usize,
1354 avail_u: bool,
1355 avail_l: bool,
1356 skip: bool,
1357 ) -> Result<()> {
1358 if !self.segmentation.enabled {
1359 self.segment_id = 0;
1360 return Ok(());
1361 }
1362 let at = |row: usize, col: usize| -> i32 {
1363 self.segment_ids
1364 .get(row * self.mi_cols + col)
1365 .map_or(-1, |&s| i32::from(s))
1366 };
1367 let prev_ul = if avail_u && avail_l {
1368 at(r - 1, c - 1)
1369 } else {
1370 -1
1371 };
1372 let prev_u = if avail_u { at(r - 1, c) } else { -1 };
1373 let prev_l = if avail_l { at(r, c - 1) } else { -1 };
1374 let pred = if prev_u == -1 {
1375 prev_l.max(0)
1376 } else if prev_l == -1 || prev_ul == prev_u {
1377 prev_u
1378 } else {
1379 prev_l
1380 };
1381 let segment = if skip {
1382 pred
1383 } else {
1384 let ctx = if prev_ul < 0 {
1385 0
1386 } else if prev_ul == prev_u && prev_ul == prev_l {
1387 2
1388 } else if prev_ul == prev_u || prev_ul == prev_l || prev_u == prev_l {
1389 1
1390 } else {
1391 0
1392 };
1393 let diff = dec.symbol(get_mut(&mut self.cdfs.segment_id, ctx)?, Site::Other)? as i32;
1394 neg_deinterleave(diff, pred, self.last_active_segment as i32 + 1)
1395 };
1396 self.segment_id = usize::try_from(segment)
1398 .ok()
1399 .filter(|&s| s <= self.last_active_segment)
1400 .ok_or_else(|| {
1401 PixelsError::malformed(
1402 "avif",
1403 format!(
1404 "segment_id {segment} is outside 0..={}",
1405 self.last_active_segment
1406 ),
1407 )
1408 })?;
1409 Ok(())
1410 }
1411
1412 fn segment_qindex(&self, ignore_delta_q: bool) -> i32 {
1414 let base = if !ignore_delta_q && self.delta_q_present {
1415 self.current_qindex
1416 } else {
1417 self.base_q
1418 };
1419 if self
1420 .segmentation
1421 .feature_active(self.segment_id, SEG_LVL_ALT_Q)
1422 {
1423 (base
1424 + self
1425 .segmentation
1426 .feature_value(self.segment_id, SEG_LVL_ALT_Q))
1427 .clamp(0, 255)
1428 } else {
1429 base
1430 }
1431 }
1432
1433 fn read_delta_qindex(
1437 &mut self,
1438 dec: &mut impl TileCoder,
1439 bw4: usize,
1440 bh4: usize,
1441 skip: bool,
1442 ) -> Result<()> {
1443 if (bw4 == self.sb_size4 && bh4 == self.sb_size4 && skip) || !self.read_deltas {
1444 return Ok(());
1445 }
1446 let delta = read_delta(dec, &mut self.cdfs.delta_q)?;
1447 if delta != 0 {
1448 self.current_qindex = (self.current_qindex + (delta << self.delta_q_res)).clamp(1, 255);
1449 }
1450 Ok(())
1451 }
1452
1453 fn read_delta_lf(
1457 &mut self,
1458 dec: &mut impl TileCoder,
1459 bw4: usize,
1460 bh4: usize,
1461 skip: bool,
1462 ) -> Result<()> {
1463 if (bw4 == self.sb_size4 && bh4 == self.sb_size4 && skip)
1464 || !self.read_deltas
1465 || !self.delta_lf_present
1466 {
1467 return Ok(());
1468 }
1469 let count = if !self.delta_lf_multi {
1470 1
1471 } else if self.num_planes > 1 {
1472 FRAME_LF_COUNT
1473 } else {
1474 FRAME_LF_COUNT - 2
1475 };
1476 for i in 0..count {
1477 let cdf = if self.delta_lf_multi {
1478 get_mut(&mut self.cdfs.delta_lf_multi, i)?
1479 } else {
1480 &mut self.cdfs.delta_lf
1481 };
1482 let delta = read_delta(dec, cdf)?;
1483 if delta != 0 {
1484 let slot = get_mut(&mut self.delta_lf, i)?;
1485 let level = (i32::from(*slot) + (delta << self.delta_lf_res))
1486 .clamp(-MAX_LOOP_FILTER, MAX_LOOP_FILTER);
1487 *slot = level as i8;
1488 }
1489 }
1490 Ok(())
1491 }
1492
1493 fn read_lr(&mut self, dec: &mut impl TileCoder, r: usize, c: usize) -> Result<()> {
1499 if !self.uses_lr {
1500 return Ok(());
1501 }
1502 let (w4, h4) = (self.sb_size4, self.sb_size4);
1503 for plane in 0..self.num_planes {
1504 let Some(info) = self.lr.get(plane) else {
1505 continue;
1506 };
1507 if info.frame_restoration_type == RESTORE_NONE {
1508 continue;
1509 }
1510 let (sub_x, sub_y) = if plane == 0 {
1511 (0, 0)
1512 } else {
1513 (self.subsampling_x, self.subsampling_y)
1514 };
1515 let unit_size = info.unit_size;
1516 let unit_rows = info.unit_rows;
1517 let unit_cols = info.unit_cols;
1518 let unit_row_start = (r * (MI_SIZE >> sub_y)).div_ceil(unit_size);
1519 let unit_row_end = unit_rows.min(((r + h4) * (MI_SIZE >> sub_y)).div_ceil(unit_size));
1520 let (numerator, denominator) = match self.superres_denom {
1521 Some(denom) => ((MI_SIZE >> sub_x) * denom, unit_size * SUPERRES_NUM),
1522 None => (MI_SIZE >> sub_x, unit_size),
1523 };
1524 let unit_col_start = (c * numerator).div_ceil(denominator);
1525 let unit_col_end = unit_cols.min(((c + w4) * numerator).div_ceil(denominator));
1526 for unit_row in unit_row_start..unit_row_end {
1527 for unit_col in unit_col_start..unit_col_end {
1528 self.read_lr_unit(dec, plane, unit_row, unit_col)?;
1529 }
1530 }
1531 }
1532 Ok(())
1533 }
1534
1535 fn read_lr_unit(
1539 &mut self,
1540 dec: &mut impl TileCoder,
1541 plane: usize,
1542 unit_row: usize,
1543 unit_col: usize,
1544 ) -> Result<()> {
1545 let frame_type = self
1546 .lr
1547 .get(plane)
1548 .map_or(RESTORE_NONE, |p| p.frame_restoration_type);
1549 let restoration_type = match frame_type {
1550 RESTORE_WIENER => {
1551 if dec.symbol(&mut self.cdfs.use_wiener, Site::Other)? != 0 {
1552 RESTORE_WIENER
1553 } else {
1554 RESTORE_NONE
1555 }
1556 }
1557 RESTORE_SGRPROJ => {
1558 if dec.symbol(&mut self.cdfs.use_sgrproj, Site::Other)? != 0 {
1559 RESTORE_SGRPROJ
1560 } else {
1561 RESTORE_NONE
1562 }
1563 }
1564 RESTORE_SWITCHABLE => dec.symbol(&mut self.cdfs.restoration_type, Site::Other)? as u8,
1565 _ => RESTORE_NONE,
1566 };
1567
1568 let unit_cols = self.lr.get(plane).map_or(0, |p| p.unit_cols);
1569 let idx = unit_row * unit_cols + unit_col;
1570 if let Some(p) = self.lr.get_mut(plane) {
1571 if let Some(t) = p.lr_type.get_mut(idx) {
1572 *t = restoration_type;
1573 }
1574 }
1575
1576 if restoration_type == RESTORE_WIENER {
1577 let coeffs = match self.ref_lr_wiener.get_mut(plane) {
1578 Some(reference) => read_wiener_unit(dec, reference, plane != 0)?,
1579 None => return Ok(()),
1580 };
1581 if let Some(p) = self.lr.get_mut(plane) {
1582 if let Some(slot) = p.wiener.get_mut(idx) {
1583 *slot = coeffs;
1584 }
1585 }
1586 } else if restoration_type == RESTORE_SGRPROJ {
1587 let (set, xqd) = match self.ref_sgr_xqd.get_mut(plane) {
1588 Some(reference) => read_sgrproj_unit(dec, reference)?,
1589 None => return Ok(()),
1590 };
1591 if let Some(p) = self.lr.get_mut(plane) {
1592 if let Some(s) = p.sgr_set.get_mut(idx) {
1593 *s = set;
1594 }
1595 if let Some(x) = p.sgr_xqd.get_mut(idx) {
1596 *x = xqd;
1597 }
1598 }
1599 }
1600 Ok(())
1601 }
1602
1603 fn read_intra_frame_y_mode(
1604 &mut self,
1605 dec: &mut impl TileCoder,
1606 r: usize,
1607 c: usize,
1608 avail_u: bool,
1609 avail_l: bool,
1610 ) -> Result<usize> {
1611 let above = if avail_u {
1612 self.y_mode_at(r.wrapping_sub(1), c)
1613 } else {
1614 DC_PRED
1615 };
1616 let left = if avail_l {
1617 self.y_mode_at(r, c.wrapping_sub(1))
1618 } else {
1619 DC_PRED
1620 };
1621 let a = INTRA_MODE_CONTEXT.get(above).copied().unwrap_or(0);
1622 let l = INTRA_MODE_CONTEXT.get(left).copied().unwrap_or(0);
1623 let cdf_row = get_mut(get_mut(&mut self.cdfs.intra_frame_y_mode, a)?, l)?;
1624 dec.symbol(cdf_row, Site::YMode)
1625 }
1626
1627 fn read_uv_mode(
1630 &mut self,
1631 dec: &mut impl TileCoder,
1632 y_mode: usize,
1633 bw4: usize,
1634 bh4: usize,
1635 ) -> Result<(usize, Option<(i32, i32)>)> {
1636 let cfl_allowed = if self.lossless {
1643 let block = block_size_index(bw4, bh4);
1644 plane_residual_size(block, self.subsampling_x, self.subsampling_y) == BLOCK_4X4
1645 } else {
1646 bw4 <= 8 && bh4 <= 8
1647 };
1648 let uv = if cfl_allowed {
1649 let cdf_row = get_mut(&mut self.cdfs.uv_cfl_allowed, y_mode)?;
1650 dec.symbol(cdf_row, Site::UvMode)?
1651 } else {
1652 let cdf_row = get_mut(&mut self.cdfs.uv_cfl_not_allowed, y_mode)?;
1653 dec.symbol(cdf_row, Site::UvMode)?
1654 };
1655 let cfl = if uv == UV_CFL_PRED {
1656 Some(self.read_cfl_alphas(dec)?)
1657 } else {
1658 None
1659 };
1660 Ok((uv, cfl))
1661 }
1662
1663 fn read_cfl_alphas(&mut self, dec: &mut impl TileCoder) -> Result<(i32, i32)> {
1665 let signs = dec.symbol(&mut self.cdfs.cfl_sign, Site::CflSign)? as i32;
1666 let sign_u = (signs + 1) / 3;
1667 let sign_v = (signs + 1) % 3;
1668 let alpha_u = if sign_u != 0 {
1670 let ctx = ((sign_u - 1) * 3 + sign_v) as usize;
1671 let mag =
1672 dec.symbol(get_mut(&mut self.cdfs.cfl_alpha, ctx)?, Site::CflAlpha)? as i32 + 1;
1673 if sign_u == 1 { -mag } else { mag }
1674 } else {
1675 0
1676 };
1677 let alpha_v = if sign_v != 0 {
1678 let ctx = ((sign_v - 1) * 3 + sign_u) as usize;
1679 let mag =
1680 dec.symbol(get_mut(&mut self.cdfs.cfl_alpha, ctx)?, Site::CflAlpha)? as i32 + 1;
1681 if sign_v == 1 { -mag } else { mag }
1682 } else {
1683 0
1684 };
1685 Ok((alpha_u, alpha_v))
1686 }
1687
1688 #[allow(
1691 clippy::too_many_arguments,
1692 reason = "mirrors palette_mode_info inputs"
1693 )]
1694 fn read_palette_mode_info(
1695 &mut self,
1696 dec: &mut impl TileCoder,
1697 r: usize,
1698 c: usize,
1699 bw4: usize,
1700 bh4: usize,
1701 y_mode: usize,
1702 uv_mode: usize,
1703 has_chroma: bool,
1704 palette: &mut Palette,
1705 ) -> Result<()> {
1706 let bsize_ctx = (floor_log2_usize(bw4) + floor_log2_usize(bh4)).saturating_sub(2) as usize;
1710 let avail_u = self.avail_u(r);
1711 let avail_l = self.avail_l(c);
1712 if y_mode == DC_PRED {
1713 let ctx = usize::from(avail_u && self.palette_size_at(0, r.wrapping_sub(1), c) > 0)
1714 + usize::from(avail_l && self.palette_size_at(0, r, c.wrapping_sub(1)) > 0);
1715 let cdf_row = get_mut(get_mut(&mut self.cdfs.palette_y_mode, bsize_ctx)?, ctx)?;
1716 if dec.symbol(cdf_row, Site::Other)? != 0 {
1717 let size_cdf = get_mut(&mut self.cdfs.palette_y_size, bsize_ctx)?;
1718 let size = dec.symbol(size_cdf, Site::Other)? + 2;
1719 let cache = self.palette_cache_for(0, r, c);
1720 palette.size_y = size;
1721 palette.colors_y = self.read_palette_colors(dec, size, &cache, true)?;
1722 }
1723 }
1724 if has_chroma && uv_mode == DC_PRED {
1725 let ctx = usize::from(palette.size_y > 0);
1726 let cdf_row = get_mut(&mut self.cdfs.palette_uv_mode, ctx)?;
1727 if dec.symbol(cdf_row, Site::Other)? != 0 {
1728 let size_cdf = get_mut(&mut self.cdfs.palette_uv_size, bsize_ctx)?;
1729 let size = dec.symbol(size_cdf, Site::Other)? + 2;
1730 let cache = self.palette_cache_for(1, r, c);
1731 palette.size_uv = size;
1732 palette.colors_u = self.read_palette_colors(dec, size, &cache, false)?;
1733 palette.colors_v = self.read_palette_colors_v(dec, size)?;
1734 }
1735 }
1736 Ok(())
1737 }
1738
1739 fn palette_cache_for(&self, plane: usize, r: usize, c: usize) -> Vec<u16> {
1741 let above = if self.avail_u(r) && (r * MI_SIZE) % 64 != 0 {
1742 let n = self.palette_size_at(plane, r - 1, c) as usize;
1743 self.palette_colors_at(plane, r - 1, c, n)
1744 } else {
1745 Vec::new()
1746 };
1747 let left = if self.avail_l(c) {
1748 let n = self.palette_size_at(plane, r, c - 1) as usize;
1749 self.palette_colors_at(plane, r, c - 1, n)
1750 } else {
1751 Vec::new()
1752 };
1753 palette_cache(&above, &left)
1754 }
1755
1756 fn read_palette_colors(
1759 &self,
1760 dec: &mut impl TileCoder,
1761 size: usize,
1762 cache: &[u16],
1763 is_luma: bool,
1764 ) -> Result<[u16; PALETTE_COLORS]> {
1765 let bd = u32::from(self.bit_depth);
1766 let max = (1_i32 << bd) - 1;
1767 let clip1 = |v: i32| v.clamp(0, max) as u16;
1768 let mut colors = [0_u16; PALETTE_COLORS];
1769 let mut idx = 0;
1770 for &cached in cache.iter() {
1771 if idx >= size {
1772 break;
1773 }
1774 if dec.literal(1, Site::Other)? != 0 {
1775 set_at(&mut colors, idx, cached);
1776 idx += 1;
1777 }
1778 }
1779 if idx < size {
1780 set_at(&mut colors, idx, dec.literal(bd, Site::Other)? as u16);
1781 idx += 1;
1782 }
1783 if idx < size {
1784 let min_bits = bd.saturating_sub(3);
1785 let mut palette_bits = min_bits + dec.literal(2, Site::Other)?;
1786 while idx < size {
1787 let delta = dec.literal(palette_bits, Site::Other)? + u32::from(is_luma);
1791 let prev = i32::from(at(&colors, idx - 1));
1792 let color = clip1(prev + delta as i32);
1793 set_at(&mut colors, idx, color);
1794 let range = (1_i32 << bd) - i32::from(color) - i32::from(is_luma);
1795 palette_bits = palette_bits.min(ceil_log2(range.max(0) as u32));
1796 idx += 1;
1797 }
1798 }
1799 let slice = colors.get_mut(..size).unwrap_or(&mut []);
1800 slice.sort_unstable();
1801 Ok(colors)
1802 }
1803
1804 fn read_palette_colors_v(
1807 &self,
1808 dec: &mut impl TileCoder,
1809 size: usize,
1810 ) -> Result<[u16; PALETTE_COLORS]> {
1811 let bd = u32::from(self.bit_depth);
1812 let max = (1_i32 << bd) - 1;
1813 let max_val = 1_i32 << bd;
1814 let mut colors = [0_u16; PALETTE_COLORS];
1815 if dec.literal(1, Site::Other)? != 0 {
1816 let mut palette_bits = bd.saturating_sub(4) + dec.literal(2, Site::Other)?;
1817 set_at(&mut colors, 0, dec.literal(bd, Site::Other)? as u16);
1818 for idx in 1..size {
1819 let mut delta = dec.literal(palette_bits, Site::Other)? as i32;
1820 if delta != 0 && dec.literal(1, Site::Other)? != 0 {
1821 delta = -delta;
1822 }
1823 let mut val = i32::from(at(&colors, idx - 1)) + delta;
1824 if val < 0 {
1825 val += max_val;
1826 }
1827 if val >= max_val {
1828 val -= max_val;
1829 }
1830 set_at(&mut colors, idx, val.clamp(0, max) as u16);
1831 let _ = &mut palette_bits;
1832 }
1833 } else {
1834 for idx in 0..size {
1835 set_at(&mut colors, idx, dec.literal(bd, Site::Other)? as u16);
1836 }
1837 }
1838 Ok(colors)
1839 }
1840
1841 fn read_palette_tokens(
1845 &mut self,
1846 dec: &mut impl TileCoder,
1847 r: usize,
1848 c: usize,
1849 palette: &mut Palette,
1850 ) -> Result<()> {
1851 let (bw, bh) = (palette.block_w, palette.block_h);
1852 let onscreen_w = bw.min((self.mi_cols - c) * MI_SIZE);
1853 let onscreen_h = bh.min((self.mi_rows - r) * MI_SIZE);
1854 if palette.size_y > 0 {
1855 let dims = MapDims {
1856 w: bw,
1857 h: bh,
1858 onscreen_w,
1859 onscreen_h,
1860 };
1861 palette.map_y = self.read_color_map(dec, palette.size_y, dims, false)?;
1862 }
1863 if palette.size_uv > 0 {
1864 let (sub_x, sub_y) = (self.subsampling_x, self.subsampling_y);
1867 let mut dims = MapDims {
1868 w: bw >> sub_x,
1869 h: bh >> sub_y,
1870 onscreen_w: onscreen_w >> sub_x,
1871 onscreen_h: onscreen_h >> sub_y,
1872 };
1873 if dims.w < 4 {
1874 dims.w += 2;
1875 dims.onscreen_w += 2;
1876 }
1877 if dims.h < 4 {
1878 dims.h += 2;
1879 dims.onscreen_h += 2;
1880 }
1881 palette.uv_w = dims.w;
1882 palette.map_uv = self.read_color_map(dec, palette.size_uv, dims, true)?;
1883 }
1884 Ok(())
1885 }
1886
1887 fn read_color_map(
1889 &mut self,
1890 dec: &mut impl TileCoder,
1891 size: usize,
1892 dims: MapDims,
1893 chroma: bool,
1894 ) -> Result<Vec<u8>> {
1895 let (bw, bh) = (dims.onscreen_w, dims.onscreen_h);
1896 let stride = dims.w;
1897 let mut map = vec![0_u8; dims.w * dims.h];
1898 let first = dec.ns(size as u32)? as u8;
1899 if let Some(m) = map.first_mut() {
1900 *m = first;
1901 }
1902 let get =
1903 |map: &[u8], i: usize, j: usize| -> Option<u8> { map.get(i * stride + j).copied() };
1904 for i in 1..(bh + bw - 1) {
1905 let j_hi = i.min(bw - 1);
1906 let j_lo = i.saturating_sub(bh - 1);
1907 let mut j = j_hi as isize;
1908 while j >= j_lo as isize {
1909 let jj = j as usize;
1910 let row = i - jj;
1911 let left = if jj > 0 { get(&map, row, jj - 1) } else { None };
1912 let above_left = if row > 0 && jj > 0 {
1913 get(&map, row - 1, jj - 1)
1914 } else {
1915 None
1916 };
1917 let above = if row > 0 {
1918 get(&map, row - 1, jj)
1919 } else {
1920 None
1921 };
1922 let (order, ctx) = color_context(left, above_left, above, size);
1923 let cdf = if chroma {
1924 self.cdfs.palette_uv_color.row(size, ctx)?
1925 } else {
1926 self.cdfs.palette_y_color.row(size, ctx)?
1927 };
1928 let sym = dec.symbol(cdf, Site::Other)?;
1929 let color = order.get(sym).copied().unwrap_or(0);
1930 if let Some(slot) = map.get_mut(row * stride + jj) {
1931 *slot = color;
1932 }
1933 j -= 1;
1934 }
1935 }
1936 for i in 0..bh {
1939 let last = get(&map, i, bw - 1).unwrap_or(0);
1940 for j in bw..dims.w {
1941 if let Some(slot) = map.get_mut(i * stride + j) {
1942 *slot = last;
1943 }
1944 }
1945 }
1946 for i in bh..dims.h {
1947 for j in 0..dims.w {
1948 let v = get(&map, bh - 1, j).unwrap_or(0);
1949 if let Some(slot) = map.get_mut(i * stride + j) {
1950 *slot = v;
1951 }
1952 }
1953 }
1954 Ok(map)
1955 }
1956
1957 fn read_angle_delta(
1961 &mut self,
1962 dec: &mut impl TileCoder,
1963 mode: usize,
1964 bw4: usize,
1965 bh4: usize,
1966 site: Site,
1967 ) -> Result<i32> {
1968 let directional = (1..=8).contains(&mode);
1969 if directional && at_least_block_8x8(bw4, bh4) {
1970 let index = mode - 1;
1971 let cdf_row = get_mut(&mut self.cdfs.angle_delta, index)?;
1972 let symbol = dec.symbol(cdf_row, site)? as i32;
1973 return Ok(symbol - MAX_ANGLE_DELTA);
1974 }
1975 Ok(0)
1976 }
1977
1978 fn read_filter_intra(
1981 &mut self,
1982 dec: &mut impl TileCoder,
1983 y_mode: usize,
1984 bw4: usize,
1985 bh4: usize,
1986 ) -> Result<Option<usize>> {
1987 let max_dim = bw4.max(bh4) * MI_SIZE;
1988 if self.enable_filter_intra && y_mode == DC_PRED && max_dim <= 32 {
1989 let size = block_size_index(bw4, bh4);
1990 let cdf_row = get_mut(&mut self.cdfs.filter_intra, size)?;
1991 if dec.symbol(cdf_row, Site::Other)? != 0 {
1992 let mode = dec.symbol(&mut self.cdfs.filter_intra_mode, Site::Other)?;
1993 return Ok(Some(mode));
1994 }
1995 }
1996 Ok(None)
1997 }
1998
1999 fn read_block_tx_size(
2004 &mut self,
2005 dec: &mut impl TileCoder,
2006 r: usize,
2007 c: usize,
2008 bw4: usize,
2009 bh4: usize,
2010 _skip: bool,
2011 ) -> Result<TxSize> {
2012 let above_w = if self.avail_u(r) {
2013 self.tx_width_at(r - 1, c)
2014 } else {
2015 0
2016 };
2017 let left_h = if self.avail_l(c) {
2018 self.tx_height_at(r, c - 1)
2019 } else {
2020 0
2021 };
2022 let params = TxSizeParams {
2023 block: block_size_index(bw4, bh4),
2024 tx_mode_select: self.tx_mode == TxMode::Select,
2025 lossless: self.lossless,
2026 allow_select: true,
2027 above_w,
2028 left_h,
2029 };
2030 let tx = code_tx_size(dec, &mut self.cdfs.tx_depth, ¶ms)?;
2031 for y in r..(r + bh4).min(self.mi_rows) {
2032 for x in c..(c + bw4).min(self.mi_cols) {
2033 if let Some(v) = self.tx_sizes.get_mut(y * self.mi_cols + x) {
2034 *v = tx as u8;
2035 }
2036 }
2037 }
2038 Ok(tx)
2039 }
2040
2041 fn tx_width_at(&self, r: usize, c: usize) -> usize {
2043 self.tx_sizes
2044 .get(r * self.mi_cols + c)
2045 .map_or(0, |&i| TxSize::from_index(usize::from(i)).width())
2046 }
2047
2048 fn tx_height_at(&self, r: usize, c: usize) -> usize {
2050 self.tx_sizes
2051 .get(r * self.mi_cols + c)
2052 .map_or(0, |&i| TxSize::from_index(usize::from(i)).height())
2053 }
2054
2055 fn residual(
2060 &mut self,
2061 dec: &mut impl TileCoder,
2062 modes: &BlockModes,
2063 bw4: usize,
2064 bh4: usize,
2065 skip: bool,
2066 has_chroma: bool,
2067 ) -> Result<()> {
2068 let planes = if has_chroma { self.num_planes } else { 1 };
2069 let block = block_size_index(bw4, bh4);
2070 let width_chunks = (bw4 / 16).max(1);
2071 let height_chunks = (bh4 / 16).max(1);
2072 let chunk_size = if width_chunks > 1 || height_chunks > 1 {
2073 BLOCK_64X64
2074 } else {
2075 block
2076 };
2077 for chunk_y in 0..height_chunks {
2078 for chunk_x in 0..width_chunks {
2079 for plane in 0..planes {
2080 self.residual_plane(
2081 dec,
2082 modes,
2083 block,
2084 chunk_size,
2085 (chunk_x, chunk_y),
2086 plane,
2087 skip,
2088 )?;
2089 }
2090 }
2091 }
2092 Ok(())
2093 }
2094
2095 #[allow(clippy::too_many_arguments, reason = "mirrors the residual loop state")]
2097 fn residual_plane(
2098 &mut self,
2099 dec: &mut impl TileCoder,
2100 modes: &BlockModes,
2101 block: usize,
2102 chunk_size: usize,
2103 (chunk_x, chunk_y): (usize, usize),
2104 plane: usize,
2105 skip: bool,
2106 ) -> Result<()> {
2107 let (sub_x, sub_y) = self.plane_subsampling(plane);
2108 let cfl_alpha = match plane {
2110 1 => modes.cfl.map(|(u, _)| u),
2111 2 => modes.cfl.map(|(_, v)| v),
2112 _ => None,
2113 };
2114 let mode = if plane == 0 {
2115 modes.y_mode
2116 } else if modes.cfl.is_some() {
2117 DC_PRED
2118 } else {
2119 modes.uv_mode
2120 };
2121 let delta = if plane == 0 {
2122 modes.y_delta
2123 } else {
2124 modes.uv_delta
2125 };
2126 let intra = IntraMode::from_index(mode as u8)
2127 .ok_or_else(|| PixelsError::malformed("avif", "intra mode index out of range"))?;
2128 let filter_type = self.filter_type(modes, plane);
2129 let plane_block = plane_residual_size(block, sub_x, sub_y);
2132 if plane_block == BLOCK_INVALID {
2133 return Err(PixelsError::malformed(
2134 "avif",
2135 "a block shape the chroma subsampling cannot represent",
2136 ));
2137 }
2138 let (plane_bw4, plane_bh4) = block_4x4_dims(plane_block);
2139 let (num_w, num_h) = block_4x4_dims(plane_residual_size(chunk_size, sub_x, sub_y));
2140 let tx_size = if self.lossless {
2144 TxSize::Tx4x4
2145 } else if plane == 0 {
2146 modes.luma_tx_size
2147 } else {
2148 chroma_tx_size(plane_block)
2149 };
2150 let step_x = (tx_size.width() / MI_SIZE).max(1);
2151 let step_y = (tx_size.height() / MI_SIZE).max(1);
2152 let base_x = (modes.c >> sub_x) * MI_SIZE;
2154 let base_y = (modes.r >> sub_y) * MI_SIZE;
2155 let palette = self.palette_view_for(&modes.palette, plane, base_x, base_y);
2156 let (avail_l, avail_u) = if plane == 0 {
2157 (modes.avail_l, modes.avail_u)
2158 } else {
2159 (modes.avail_l_chroma, modes.avail_u_chroma)
2160 };
2161 let mut y = 0;
2162 while y < num_h {
2163 let mut x = 0;
2164 while x < num_w {
2165 let bx = x + ((chunk_x << 4) >> sub_x);
2168 let by = y + ((chunk_y << 4) >> sub_y);
2169 let tb = TxBlock {
2170 plane,
2171 x: base_x + bx * MI_SIZE,
2172 y: base_y + by * MI_SIZE,
2173 tx_size,
2174 mode: intra,
2175 mode_index: mode,
2176 angle_delta: delta,
2177 have_left: avail_l || bx > 0,
2178 have_above: avail_u || by > 0,
2179 filter_type,
2180 filter_intra: if plane == 0 { modes.filter_intra } else { None },
2182 cfl_alpha,
2183 palette,
2184 skip,
2185 plane_bw4,
2186 plane_bh4,
2187 };
2188 self.transform_block(dec, &tb)?;
2189 x += step_x;
2190 }
2191 y += step_y;
2192 }
2193 Ok(())
2194 }
2195
2196 fn frame_bounds(&self, plane: usize) -> (usize, usize) {
2200 let (sub_x, sub_y) = self.plane_subsampling(plane);
2201 (
2202 ((self.mi_cols * MI_SIZE) >> sub_x) - 1,
2203 ((self.mi_rows * MI_SIZE) >> sub_y) - 1,
2204 )
2205 }
2206
2207 fn plane_sb_mask_xy(&self, plane: usize) -> (isize, isize) {
2210 let (sub_x, sub_y) = self.plane_subsampling(plane);
2211 let mask = self.sb_size4 - 1;
2212 ((mask >> sub_x) as isize, (mask >> sub_y) as isize)
2213 }
2214
2215 fn plane_subsampling(&self, plane: usize) -> (usize, usize) {
2217 if plane == 0 {
2218 (0, 0)
2219 } else {
2220 (self.subsampling_x, self.subsampling_y)
2221 }
2222 }
2223
2224 fn palette_view_for<'a>(
2226 &self,
2227 palette: &'a Palette,
2228 plane: usize,
2229 base_x: usize,
2230 base_y: usize,
2231 ) -> Option<PaletteView<'a>> {
2232 if plane == 0 && palette.size_y > 0 {
2233 Some(PaletteView {
2234 map: &palette.map_y,
2235 colors: &palette.colors_y,
2236 block_w: palette.block_w,
2237 base_x,
2238 base_y,
2239 })
2240 } else if plane == 1 && palette.size_uv > 0 {
2241 Some(PaletteView {
2242 map: &palette.map_uv,
2243 colors: &palette.colors_u,
2244 block_w: palette.uv_w,
2245 base_x,
2246 base_y,
2247 })
2248 } else if plane == 2 && palette.size_uv > 0 {
2249 Some(PaletteView {
2250 map: &palette.map_uv,
2251 colors: &palette.colors_v,
2252 block_w: palette.uv_w,
2253 base_x,
2254 base_y,
2255 })
2256 } else {
2257 None
2258 }
2259 }
2260
2261 pub(crate) fn preview_prediction(
2270 &self,
2271 r: usize,
2272 c: usize,
2273 bw4: usize,
2274 bh4: usize,
2275 plane: usize,
2276 mode: usize,
2277 ) -> Option<Vec<u16>> {
2278 let (avail_u, avail_l) = (self.avail_u(r), self.avail_l(c));
2279 let modes = BlockModes {
2280 r,
2281 c,
2282 avail_u,
2283 avail_l,
2284 avail_u_chroma: avail_u,
2285 avail_l_chroma: avail_l,
2286 y_mode: mode,
2287 uv_mode: mode,
2288 y_delta: 0,
2289 uv_delta: 0,
2290 filter_intra: None,
2291 cfl: None,
2292 palette: Palette::default(),
2293 luma_tx_size: TxSize::Tx4x4,
2294 };
2295 let (sub_x, sub_y) = self.plane_subsampling(plane);
2296 let block = block_size_index(bw4, bh4);
2297 let plane_block = plane_residual_size(block, sub_x, sub_y);
2298 if plane_block == BLOCK_INVALID {
2299 return None;
2300 }
2301 let (plane_bw4, plane_bh4) = block_4x4_dims(plane_block);
2302 let tx_size = if plane == 0 {
2303 max_tx_size_rect(block)
2304 } else {
2305 chroma_tx_size(plane_block)
2306 };
2307 let tb = TxBlock {
2308 plane,
2309 x: (c >> sub_x) * MI_SIZE,
2310 y: (r >> sub_y) * MI_SIZE,
2311 tx_size,
2312 mode: IntraMode::from_index(mode as u8)?,
2313 mode_index: mode,
2314 angle_delta: 0,
2315 have_left: avail_l,
2316 have_above: avail_u,
2317 filter_type: self.filter_type(&modes, plane),
2318 filter_intra: None,
2319 cfl_alpha: None,
2320 palette: None,
2321 skip: false,
2322 plane_bw4,
2323 plane_bh4,
2324 };
2325 self.predict(&tb, tx_size.width(), tx_size.height()).ok()
2326 }
2327
2328 pub(crate) fn for_frame(seq: &SequenceHeader, frame: &FrameHeader) -> Result<Self> {
2330 Self::new(seq, frame)
2331 }
2332
2333 pub(crate) const fn mi_dims(&self) -> (usize, usize) {
2335 (self.mi_rows, self.mi_cols)
2336 }
2337
2338 fn filter_type(&self, modes: &BlockModes, plane: usize) -> bool {
2339 let is_smooth = |mode: usize| (9..=11).contains(&mode);
2340 let smooth_at = |r: usize, c: usize| {
2341 is_smooth(if plane == 0 {
2342 self.y_mode_at(r, c)
2343 } else {
2344 self.uv_mode_at(r, c)
2345 })
2346 };
2347 let (sub_x, sub_y) = self.plane_subsampling(plane);
2348 let (r, c) = (modes.r, modes.c);
2349 let (avail_u, avail_l) = if plane == 0 {
2350 (modes.avail_u, modes.avail_l)
2351 } else {
2352 (modes.avail_u_chroma, modes.avail_l_chroma)
2353 };
2354 let above = avail_u && {
2355 let row = r - 1 - usize::from(sub_y == 1 && r & 1 == 1);
2356 let col = c + usize::from(sub_x == 1 && c & 1 == 0);
2357 smooth_at(row, col)
2358 };
2359 let left = avail_l && {
2360 let row = r + usize::from(sub_y == 1 && r & 1 == 0);
2361 let col = c - 1 - usize::from(sub_x == 1 && c & 1 == 1);
2362 smooth_at(row, col)
2363 };
2364 above || left
2365 }
2366
2367 fn transform_block(&mut self, dec: &mut impl TileCoder, tb: &TxBlock) -> Result<()> {
2368 let (plane, x, y, tx_size, skip) = (tb.plane, tb.x, tb.y, tb.tx_size, tb.skip);
2369 let w = tx_size.width();
2370 let h = tx_size.height();
2371 let w4 = (w / MI_SIZE).max(1);
2372 let h4 = (h / MI_SIZE).max(1);
2373
2374 let (sub_x, sub_y) = self.plane_subsampling(plane);
2381 let max_x = (self.mi_cols * MI_SIZE) >> sub_x;
2382 let max_y = (self.mi_rows * MI_SIZE) >> sub_y;
2383 if x >= max_x || y >= max_y {
2384 return Ok(());
2385 }
2386
2387 let prediction = self.predict(tb, w, h)?;
2389 if plane == 0 {
2390 self.max_luma_w = x + w;
2393 self.max_luma_h = y + h;
2394 }
2395
2396 let x4 = x / MI_SIZE;
2397 let y4 = y / MI_SIZE;
2398 let final_block = if skip {
2399 prediction
2400 } else {
2401 let all_zero_ctx =
2402 self.all_zero_ctx(plane, x4, y4, w4, h4, tx_size, tb.plane_bw4, tb.plane_bh4);
2403 let dc_sign_ctx = self.dc_sign_ctx(plane, x4, y4, w4, h4);
2404 let ptype = usize::from(plane > 0);
2405 let tx_set = intra_tx_set(tx_size, self.reduced_tx_set);
2410 let dir = if plane == 0 {
2411 intra_dir(tb.mode_index, tb.filter_intra)
2412 } else {
2413 0
2414 };
2415 let qindex_positive = self.segment_qindex(true) > 0;
2418 let qindex = self.segment_qindex(false);
2421 let dc = dc_q(
2422 self.bit_depth,
2423 qindex + self.q_dc.get(plane).copied().unwrap_or(0),
2424 );
2425 let ac = ac_q(
2426 self.bit_depth,
2427 qindex + self.q_ac.get(plane).copied().unwrap_or(0),
2428 );
2429 let tx_ctx = TxTypeCtx {
2430 set: tx_set,
2431 intra_cdfs: &mut self.cdfs.intra_tx_type,
2432 intra_dir: dir,
2433 uv_mode: tb.mode_index,
2434 qindex_positive,
2435 lossless: self.lossless,
2436 };
2437 let job = CoeffJob {
2438 plane,
2439 x,
2440 y,
2441 prediction: &prediction,
2442 dc_q: dc,
2443 ac_q: ac,
2444 };
2445 let block = dec.coeffs(
2446 &mut self.cdfs.coeff,
2447 tx_size,
2448 tx_ctx,
2449 ptype,
2450 all_zero_ctx,
2451 dc_sign_ctx,
2452 &job,
2453 )?;
2454 self.update_level_context(plane, x4, y4, w4, h4, block.cul_level, block.dc_category);
2455 if block.eob > 0 {
2456 let level = if self.using_qmatrix && !self.lossless {
2460 self.qm.get(plane).copied().unwrap_or(15)
2461 } else {
2462 15
2463 };
2464 let matrix = ((block.tx_type as usize) < TxType::Idtx as usize)
2465 .then(|| quantizer_matrix(level, plane > 0, tx_size))
2466 .flatten();
2467 let dequant =
2468 dequantize_with_matrix(&block.quant, tx_size, dc, ac, matrix, self.bit_depth);
2469 let residual = inverse_transform_2d(
2470 &dequant,
2471 tx_size,
2472 block.tx_type,
2473 self.lossless,
2474 self.bit_depth,
2475 );
2476 add_residual(&prediction, &residual, block.tx_type, self.bit_depth)
2477 } else {
2478 prediction
2479 }
2480 };
2481
2482 if let Some(p) = self.planes.get_mut(plane) {
2483 for i in 0..h {
2484 for j in 0..w {
2485 let value = final_block.get(i * w + j).copied().unwrap_or(0);
2486 p.set(x + j, y + i, value);
2487 }
2488 }
2489 }
2490
2491 let (mask_x, mask_y) = self.plane_sb_mask_xy(plane);
2495 let tx_index = tx_size as u8;
2496 for dy in 0..h4 {
2497 for dx in 0..w4 {
2498 let sub_row = ((y4 + dy) as isize) & mask_y;
2499 let sub_col = ((x4 + dx) as isize) & mask_x;
2500 self.set_block_decoded(plane, sub_row, sub_col);
2501 if let Some(grid) = self.lf_tx_sizes.get_mut(plane) {
2502 if let Some(cell) = grid.get_mut((y4 + dy) * self.mi_cols + (x4 + dx)) {
2503 *cell = tx_index;
2504 }
2505 }
2506 }
2507 }
2508 Ok(())
2509 }
2510
2511 fn predict(&self, tb: &TxBlock, w: usize, h: usize) -> Result<Vec<u16>> {
2515 if let Some(pv) = tb.palette {
2516 let mut pred = vec![0_u16; w * h];
2519 for i in 0..h {
2520 for j in 0..w {
2521 let my = (tb.y + i).saturating_sub(pv.base_y);
2522 let mx = (tb.x + j).saturating_sub(pv.base_x);
2523 let idx = pv.map.get(my * pv.block_w + mx).copied().unwrap_or(0);
2524 if let Some(cell) = pred.get_mut(i * w + j) {
2525 *cell = pv.colors.get(usize::from(idx)).copied().unwrap_or(0);
2526 }
2527 }
2528 }
2529 return Ok(pred);
2530 }
2531 if let Some(filter_mode) = tb.filter_intra {
2532 let (above, left) = self.gather_edges(tb, w, h);
2533 return Ok(predict_filter_intra(
2534 filter_mode,
2535 &above,
2536 &left,
2537 w,
2538 h,
2539 self.bit_depth,
2540 ));
2541 }
2542 if let Some(base_angle) = mode_base_angle(tb.mode_index) {
2543 let p_angle = base_angle + tb.angle_delta * ANGLE_STEP;
2544 if p_angle != 90 && p_angle != 180 {
2545 return Ok(self.predict_directional(tb, p_angle, w, h));
2546 }
2547 }
2548 let (above, left, corner, have_above, have_left) =
2549 self.gather_neighbours(tb.plane, tb.x, tb.y, tb.have_left, tb.have_above, w, h);
2550 let block = PredBlock {
2551 above: &above,
2552 left: &left,
2553 corner,
2554 have_above,
2555 have_left,
2556 w,
2557 h,
2558 };
2559 let mut pred = predict_intra_block(tb.mode, &block, self.bit_depth)?;
2560 if let Some(alpha) = tb.cfl_alpha {
2561 self.apply_cfl(&mut pred, tb.x, tb.y, w, h, alpha);
2562 }
2563 Ok(pred)
2564 }
2565
2566 fn apply_cfl(&self, pred: &mut [u16], x: usize, y: usize, w: usize, h: usize, alpha: i32) {
2572 let max = (1_i32 << self.bit_depth) - 1;
2573 let luma = self.planes.first();
2574 let (sub_x, sub_y) = (self.subsampling_x, self.subsampling_y);
2575 let luma_at =
2576 |lx: usize, ly: usize| i32::from(luma.and_then(|p| p.get(lx, ly)).unwrap_or(0));
2577 let mut l = vec![0_i32; w * h];
2579 let mut sum = 0_i32;
2580 for i in 0..h {
2581 let luma_y = ((y + i) << sub_y).min(self.max_luma_h.saturating_sub(1 << sub_y));
2582 for j in 0..w {
2583 let luma_x = ((x + j) << sub_x).min(self.max_luma_w.saturating_sub(1 << sub_x));
2584 let mut t = 0;
2585 for dy in 0..=sub_y {
2586 for dx in 0..=sub_x {
2587 t += luma_at(luma_x + dx, luma_y + dy);
2588 }
2589 }
2590 let v = t << (3 - sub_x - sub_y);
2591 if let Some(cell) = l.get_mut(i * w + j) {
2592 *cell = v;
2593 }
2594 sum += v;
2595 }
2596 }
2597 let shift = w.trailing_zeros() + h.trailing_zeros();
2599 let luma_avg = (sum + (1 << (shift - 1))) >> shift;
2600 for i in 0..h {
2601 for j in 0..w {
2602 let ac = l.get(i * w + j).copied().unwrap_or(0) - luma_avg;
2603 let scaled = round2_signed(alpha * ac, 6);
2604 if let Some(cell) = pred.get_mut(i * w + j) {
2605 *cell = (i32::from(*cell) + scaled).clamp(0, max) as u16;
2606 }
2607 }
2608 }
2609 }
2610
2611 fn gather_edges(&self, tb: &TxBlock, w: usize, h: usize) -> (Edge, Edge) {
2616 let (plane, x, y) = (tb.plane, tb.x, tb.y);
2617 let mid = 1_i32 << (self.bit_depth - 1);
2618 let p = self.planes.get(plane);
2619 let at =
2620 |px: usize, py: usize| -> i32 { p.and_then(|pl| pl.get(px, py)).map_or(0, i32::from) };
2621 let (max_x, max_y) = self.frame_bounds(plane);
2622
2623 let x4 = x / MI_SIZE;
2624 let y4 = y / MI_SIZE;
2625 let w4 = (w / MI_SIZE).max(1);
2626 let h4 = (h / MI_SIZE).max(1);
2627 let (mask_x, mask_y) = self.plane_sb_mask_xy(plane);
2628 let sub_row = (y4 as isize) & mask_y;
2629 let sub_col = (x4 as isize) & mask_x;
2630 let have_above_right = self.block_decoded_at(plane, sub_row - 1, sub_col + w4 as isize);
2631 let have_below_left = self.block_decoded_at(plane, sub_row + h4 as isize, sub_col - 1);
2632
2633 let mut above = Edge::new();
2634 let mut left = Edge::new();
2635 let num = (w + h) as isize;
2636 if tb.have_above {
2638 let extent = if have_above_right { 2 * w } else { w };
2639 let above_limit = (x + extent - 1).min(max_x);
2640 for i in 0..num {
2641 above.set(i, at((x + i as usize).min(above_limit), y.wrapping_sub(1)));
2642 }
2643 } else if tb.have_left {
2644 let v = at(x.wrapping_sub(1), y);
2645 for i in 0..num {
2646 above.set(i, v);
2647 }
2648 } else {
2649 for i in 0..num {
2650 above.set(i, mid - 1);
2651 }
2652 }
2653 if tb.have_left {
2655 let extent = if have_below_left { 2 * h } else { h };
2656 let left_limit = (y + extent - 1).min(max_y);
2657 for i in 0..num {
2658 left.set(i, at(x.wrapping_sub(1), (y + i as usize).min(left_limit)));
2659 }
2660 } else if tb.have_above {
2661 let v = at(x, y.wrapping_sub(1));
2662 for i in 0..num {
2663 left.set(i, v);
2664 }
2665 } else {
2666 for i in 0..num {
2667 left.set(i, mid + 1);
2668 }
2669 }
2670 let corner = match (tb.have_above, tb.have_left) {
2671 (true, true) => at(x.wrapping_sub(1), y.wrapping_sub(1)),
2672 (true, false) => at(x, y.wrapping_sub(1)),
2673 (false, true) => at(x.wrapping_sub(1), y),
2674 (false, false) => mid,
2675 };
2676 above.set(-1, corner);
2677 left.set(-1, corner);
2678 (above, left)
2679 }
2680
2681 fn predict_directional(&self, tb: &TxBlock, p_angle: i32, w: usize, h: usize) -> Vec<u16> {
2684 let (x, y) = (tb.x, tb.y);
2685 let (mut above, mut left) = self.gather_edges(tb, w, h);
2686 let (max_x, max_y) = self.frame_bounds(tb.plane);
2687 let avail_above_px = (max_x as i32) - (x as i32) + 1;
2688 let avail_left_px = (max_y as i32) - (y as i32) + 1;
2689 predict_directional(
2690 p_angle,
2691 &mut above,
2692 &mut left,
2693 w,
2694 h,
2695 tb.have_left,
2696 tb.have_above,
2697 tb.filter_type,
2698 self.enable_edge_filter,
2699 avail_above_px,
2700 avail_left_px,
2701 self.bit_depth,
2702 )
2703 }
2704
2705 #[allow(clippy::too_many_arguments, reason = "mirrors the §7.11.2 edge inputs")]
2708 fn gather_neighbours(
2709 &self,
2710 plane: usize,
2711 x: usize,
2712 y: usize,
2713 have_left: bool,
2714 have_above: bool,
2715 w: usize,
2716 h: usize,
2717 ) -> (Vec<i32>, Vec<i32>, i32, bool, bool) {
2718 let tb = TxBlock {
2719 plane,
2720 x,
2721 y,
2722 tx_size: TxSize::Tx4x4,
2723 mode: IntraMode::Dc,
2724 mode_index: 0,
2725 angle_delta: 0,
2726 have_left,
2727 have_above,
2728 filter_type: false,
2729 filter_intra: None,
2730 cfl_alpha: None,
2731 palette: None,
2732 skip: false,
2733 plane_bw4: 0,
2734 plane_bh4: 0,
2735 };
2736 let (above_edge, left_edge) = self.gather_edges(&tb, w, h);
2737 let above: Vec<i32> = (0..w as isize).map(|j| above_edge.get(j)).collect();
2738 let left: Vec<i32> = (0..h as isize).map(|i| left_edge.get(i)).collect();
2739 (above, left, above_edge.get(-1), have_above, have_left)
2740 }
2741
2742 #[allow(clippy::too_many_arguments, reason = "mirrors the §8.3.2 ctx inputs")]
2748 fn all_zero_ctx(
2749 &self,
2750 plane: usize,
2751 x4: usize,
2752 y4: usize,
2753 w4: usize,
2754 h4: usize,
2755 tx_size: TxSize,
2756 bw4: usize,
2757 bh4: usize,
2758 ) -> usize {
2759 let Some(ctx) = self.ctx.get(plane) else {
2760 return 0;
2761 };
2762 let (sub_x, sub_y) = self.plane_subsampling(plane);
2764 let (max_x4, max_y4) = (self.mi_cols >> sub_x, self.mi_rows >> sub_y);
2765 let w = tx_size.width();
2766 let h = tx_size.height();
2767 if plane == 0 {
2768 let mut top = 0_u32;
2769 let mut left = 0_u32;
2770 for k in 0..w4 {
2771 if x4 + k < max_x4 {
2772 top = top.max(u32::from(ctx.above_level.get(x4 + k).copied().unwrap_or(0)));
2773 }
2774 }
2775 for k in 0..h4 {
2776 if y4 + k < max_y4 {
2777 left = left.max(u32::from(ctx.left_level.get(y4 + k).copied().unwrap_or(0)));
2778 }
2779 }
2780 let top = top.min(255);
2781 let left = left.min(255);
2782 if bw4 * MI_SIZE == w && bh4 * MI_SIZE == h {
2783 0
2784 } else if top == 0 && left == 0 {
2785 1
2786 } else if top == 0 || left == 0 {
2787 2 + usize::from(top.max(left) > 3)
2788 } else if top.max(left) <= 3 {
2789 4
2790 } else if top.min(left) <= 3 {
2791 5
2792 } else {
2793 6
2794 }
2795 } else {
2796 let mut above = 0_u8;
2797 let mut left = 0_u8;
2798 for i in 0..w4 {
2799 if x4 + i < max_x4 {
2800 above |= ctx.above_level.get(x4 + i).copied().unwrap_or(0);
2801 above |= ctx.above_dc.get(x4 + i).copied().unwrap_or(0);
2802 }
2803 }
2804 for i in 0..h4 {
2805 if y4 + i < max_y4 {
2806 left |= ctx.left_level.get(y4 + i).copied().unwrap_or(0);
2807 left |= ctx.left_dc.get(y4 + i).copied().unwrap_or(0);
2808 }
2809 }
2810 let mut c = 7 + usize::from(above != 0) + usize::from(left != 0);
2811 if bw4 * MI_SIZE * bh4 * MI_SIZE > w * h {
2812 c += 3;
2813 }
2814 c
2815 }
2816 }
2817
2818 fn dc_sign_ctx(&self, plane: usize, x4: usize, y4: usize, w4: usize, h4: usize) -> usize {
2820 let Some(ctx) = self.ctx.get(plane) else {
2821 return 0;
2822 };
2823 let (sub_x, sub_y) = self.plane_subsampling(plane);
2824 let (max_x4, max_y4) = (self.mi_cols >> sub_x, self.mi_rows >> sub_y);
2825 let mut dc_sign = 0_i32;
2826 for k in 0..w4 {
2827 if x4 + k < max_x4 {
2828 match ctx.above_dc.get(x4 + k).copied().unwrap_or(0) {
2829 1 => dc_sign -= 1,
2830 2 => dc_sign += 1,
2831 _ => {}
2832 }
2833 }
2834 }
2835 for k in 0..h4 {
2836 if y4 + k < max_y4 {
2837 match ctx.left_dc.get(y4 + k).copied().unwrap_or(0) {
2838 1 => dc_sign -= 1,
2839 2 => dc_sign += 1,
2840 _ => {}
2841 }
2842 }
2843 }
2844 if dc_sign < 0 {
2845 1
2846 } else if dc_sign > 0 {
2847 2
2848 } else {
2849 0
2850 }
2851 }
2852
2853 #[allow(clippy::too_many_arguments, reason = "one level entry per plane axis")]
2856 fn update_level_context(
2857 &mut self,
2858 plane: usize,
2859 x4: usize,
2860 y4: usize,
2861 w4: usize,
2862 h4: usize,
2863 cul: u8,
2864 dc: u8,
2865 ) {
2866 if let Some(ctx) = self.ctx.get_mut(plane) {
2867 for i in 0..w4 {
2868 if let Some(v) = ctx.above_level.get_mut(x4 + i) {
2869 *v = cul;
2870 }
2871 if let Some(v) = ctx.above_dc.get_mut(x4 + i) {
2872 *v = dc;
2873 }
2874 }
2875 for i in 0..h4 {
2876 if let Some(v) = ctx.left_level.get_mut(y4 + i) {
2877 *v = cul;
2878 }
2879 if let Some(v) = ctx.left_dc.get_mut(y4 + i) {
2880 *v = dc;
2881 }
2882 }
2883 }
2884 }
2885
2886 fn reset_block_context(
2889 &mut self,
2890 r: usize,
2891 c: usize,
2892 bw4: usize,
2893 bh4: usize,
2894 has_chroma: bool,
2895 ) {
2896 let planes = if has_chroma { self.num_planes } else { 1 };
2897 for plane in 0..planes {
2898 let (sub_x, sub_y) = self.plane_subsampling(plane);
2899 let Some(ctx) = self.ctx.get_mut(plane) else {
2900 continue;
2901 };
2902 for i in (c >> sub_x)..((c + bw4) >> sub_x) {
2903 if let Some(v) = ctx.above_level.get_mut(i) {
2904 *v = 0;
2905 }
2906 if let Some(v) = ctx.above_dc.get_mut(i) {
2907 *v = 0;
2908 }
2909 }
2910 for i in (r >> sub_y)..((r + bh4) >> sub_y) {
2911 if let Some(v) = ctx.left_level.get_mut(i) {
2912 *v = 0;
2913 }
2914 if let Some(v) = ctx.left_dc.get_mut(i) {
2915 *v = 0;
2916 }
2917 }
2918 }
2919 }
2920
2921 #[allow(clippy::too_many_arguments, reason = "records every per-block field")]
2922 fn record_block(
2923 &mut self,
2924 r: usize,
2925 c: usize,
2926 bw4: usize,
2927 bh4: usize,
2928 y_mode: usize,
2929 uv_mode: usize,
2930 has_chroma: bool,
2931 skip: bool,
2932 palette: &Palette,
2933 ) {
2934 let wide = floor_log2_usize(bw4) as u8;
2935 let high = floor_log2_usize(bh4) as u8;
2936 for y in r..(r + bh4).min(self.mi_rows) {
2937 for x in c..(c + bw4).min(self.mi_cols) {
2938 let idx = y * self.mi_cols + x;
2939 if let Some(v) = self.y_modes.get_mut(idx) {
2940 *v = y_mode as u8;
2941 }
2942 if has_chroma {
2945 if let Some(v) = self.uv_modes.get_mut(idx) {
2946 *v = uv_mode as u8;
2947 }
2948 }
2949 if let Some(v) = self.skips.get_mut(idx) {
2950 *v = u8::from(skip);
2951 }
2952 if let Some(v) = self.mi_wide_log2.get_mut(idx) {
2953 *v = wide;
2954 }
2955 if let Some(v) = self.mi_high_log2.get_mut(idx) {
2956 *v = high;
2957 }
2958 let [ps_y, ps_uv] = &mut self.palette_sizes;
2959 if let Some(v) = ps_y.get_mut(idx) {
2960 *v = palette.size_y as u8;
2961 }
2962 if let Some(v) = ps_uv.get_mut(idx) {
2963 *v = palette.size_uv as u8;
2964 }
2965 let [pc_y, pc_uv] = &mut self.palette_colors;
2966 if let Some(v) = pc_y.get_mut(idx) {
2967 *v = palette.colors_y;
2968 }
2969 if let Some(v) = pc_uv.get_mut(idx) {
2970 *v = palette.colors_u;
2971 }
2972 }
2973 }
2974 }
2975
2976 fn y_mode_at(&self, r: usize, c: usize) -> usize {
2977 self.y_modes
2978 .get(r * self.mi_cols + c)
2979 .map_or(DC_PRED, |&v| usize::from(v))
2980 }
2981
2982 fn uv_mode_at(&self, r: usize, c: usize) -> usize {
2983 self.uv_modes
2984 .get(r * self.mi_cols + c)
2985 .map_or(DC_PRED, |&v| usize::from(v))
2986 }
2987
2988 fn palette_size_at(&self, plane: usize, r: usize, c: usize) -> u8 {
2989 self.palette_sizes
2990 .get(plane)
2991 .and_then(|p| p.get(r * self.mi_cols + c))
2992 .copied()
2993 .unwrap_or(0)
2994 }
2995
2996 fn palette_colors_at(&self, plane: usize, r: usize, c: usize, n: usize) -> Vec<u16> {
2997 self.palette_colors
2998 .get(plane)
2999 .and_then(|p| p.get(r * self.mi_cols + c))
3000 .map(|colors| colors.iter().take(n).copied().collect())
3001 .unwrap_or_default()
3002 }
3003
3004 fn skip_at(&self, r: usize, c: usize) -> u8 {
3005 self.skips.get(r * self.mi_cols + c).copied().unwrap_or(0)
3006 }
3007}
3008
3009struct BlockModes {
3012 r: usize,
3013 c: usize,
3014 avail_u: bool,
3015 avail_l: bool,
3016 avail_u_chroma: bool,
3020 avail_l_chroma: bool,
3021 y_mode: usize,
3022 uv_mode: usize,
3023 y_delta: i32,
3024 uv_delta: i32,
3025 filter_intra: Option<usize>,
3027 cfl: Option<(i32, i32)>,
3029 palette: Palette,
3031 luma_tx_size: TxSize,
3033}
3034
3035#[derive(Default, Clone)]
3037struct Palette {
3038 size_y: usize,
3040 size_uv: usize,
3042 colors_y: [u16; PALETTE_COLORS],
3044 colors_u: [u16; PALETTE_COLORS],
3046 colors_v: [u16; PALETTE_COLORS],
3048 map_y: Vec<u8>,
3050 map_uv: Vec<u8>,
3052 block_w: usize,
3054 block_h: usize,
3055 uv_w: usize,
3057}
3058
3059#[derive(Clone, Copy)]
3061struct MapDims {
3062 w: usize,
3063 h: usize,
3064 onscreen_w: usize,
3065 onscreen_h: usize,
3066}
3067
3068struct TxBlock<'a> {
3070 plane: usize,
3071 x: usize,
3072 y: usize,
3073 tx_size: TxSize,
3075 mode: IntraMode,
3076 mode_index: usize,
3077 angle_delta: i32,
3078 have_left: bool,
3079 have_above: bool,
3080 filter_type: bool,
3081 filter_intra: Option<usize>,
3082 cfl_alpha: Option<i32>,
3084 palette: Option<PaletteView<'a>>,
3086 skip: bool,
3087 plane_bw4: usize,
3090 plane_bh4: usize,
3091}
3092
3093#[derive(Clone, Copy)]
3096struct PaletteView<'a> {
3097 map: &'a [u8],
3098 colors: &'a [u16; PALETTE_COLORS],
3099 block_w: usize,
3100 base_x: usize,
3101 base_y: usize,
3102}
3103
3104fn neg_deinterleave(diff: i32, reference: i32, max: i32) -> i32 {
3110 if reference == 0 {
3111 return diff;
3112 }
3113 if reference >= max - 1 {
3114 return max - diff - 1;
3115 }
3116 if 2 * reference < max {
3117 if diff <= 2 * reference {
3118 return if diff & 1 == 1 {
3119 reference + ((diff + 1) >> 1)
3120 } else {
3121 reference - (diff >> 1)
3122 };
3123 }
3124 return diff;
3125 }
3126 if diff <= 2 * (max - reference - 1) {
3127 if diff & 1 == 1 {
3128 reference + ((diff + 1) >> 1)
3129 } else {
3130 reference - (diff >> 1)
3131 }
3132 } else {
3133 max - (diff + 1)
3134 }
3135}
3136
3137fn read_delta(dec: &mut impl TileCoder, cdf: &mut [u16]) -> Result<i32> {
3141 let mut abs = dec.symbol(cdf, Site::Other)? as i32;
3142 if abs == DELTA_SMALL {
3143 let rem_bits = dec.literal(3, Site::Other)? + 1;
3144 let abs_bits = dec.literal(rem_bits, Site::Other)? as i32;
3145 abs = abs_bits + (1 << rem_bits) + 1;
3146 }
3147 if abs != 0 && dec.literal(1, Site::Other)? == 1 {
3148 abs = -abs;
3149 }
3150 Ok(abs)
3151}
3152
3153fn chroma_tx_size(block: usize) -> TxSize {
3154 match max_tx_size_rect(block) {
3155 TxSize::Tx64x64 | TxSize::Tx32x64 | TxSize::Tx64x32 => TxSize::Tx32x32,
3156 TxSize::Tx16x64 => TxSize::Tx16x32,
3157 TxSize::Tx64x16 => TxSize::Tx32x16,
3158 other => other,
3159 }
3160}
3161
3162fn bd_index(stride: usize, row: isize, col: isize) -> usize {
3165 let r = usize::try_from(row + 1).unwrap_or(0);
3166 let c = usize::try_from(col + 1).unwrap_or(0);
3167 r.saturating_mul(stride).saturating_add(c)
3168}
3169
3170fn floor_log2_usize(x: usize) -> u32 {
3172 (usize::BITS - 1) - x.max(1).leading_zeros()
3173}
3174
3175fn block_size_index(bw4: usize, bh4: usize) -> usize {
3177 match (bw4, bh4) {
3178 (1, 1) => 0,
3179 (1, 2) => 1,
3180 (2, 1) => 2,
3181 (2, 2) => 3,
3182 (2, 4) => 4,
3183 (4, 2) => 5,
3184 (4, 4) => 6,
3185 (4, 8) => 7,
3186 (8, 4) => 8,
3187 (8, 8) => 9,
3188 (8, 16) => 10,
3189 (16, 8) => 11,
3190 (16, 16) => 12,
3191 (16, 32) => 13,
3192 (32, 16) => 14,
3193 (32, 32) => 15,
3194 (1, 4) => 16,
3195 (4, 1) => 17,
3196 (2, 8) => 18,
3197 (8, 2) => 19,
3198 (4, 16) => 20,
3199 (16, 4) => 21,
3200 _ => 15,
3201 }
3202}
3203
3204const BLOCK_64X64: usize = 12;
3206
3207const BLOCK_INVALID: usize = usize::MAX;
3209
3210const BLOCK_4X4_DIMS: [(usize, usize); 22] = [
3214 (1, 1),
3215 (1, 2),
3216 (2, 1),
3217 (2, 2),
3218 (2, 4),
3219 (4, 2),
3220 (4, 4),
3221 (4, 8),
3222 (8, 4),
3223 (8, 8),
3224 (8, 16),
3225 (16, 8),
3226 (16, 16),
3227 (16, 32),
3228 (32, 16),
3229 (32, 32),
3230 (1, 4),
3231 (4, 1),
3232 (2, 8),
3233 (8, 2),
3234 (4, 16),
3235 (16, 4),
3236];
3237
3238const SUBSAMPLED_SIZE: [[[usize; 2]; 2]; 22] = {
3241 const I: usize = BLOCK_INVALID;
3242 [
3243 [[0, 0], [0, 0]],
3244 [[1, 0], [I, 0]],
3245 [[2, I], [0, 0]],
3246 [[3, 2], [1, 0]],
3247 [[4, 3], [I, 1]],
3248 [[5, I], [3, 2]],
3249 [[6, 5], [4, 3]],
3250 [[7, 6], [I, 4]],
3251 [[8, I], [6, 5]],
3252 [[9, 8], [7, 6]],
3253 [[10, 9], [I, 7]],
3254 [[11, I], [9, 8]],
3255 [[12, 11], [10, 9]],
3256 [[13, 12], [I, 10]],
3257 [[14, I], [12, 11]],
3258 [[15, 14], [13, 12]],
3259 [[16, 1], [I, 1]],
3260 [[17, I], [2, 2]],
3261 [[18, 4], [I, 16]],
3262 [[19, I], [5, 17]],
3263 [[20, 7], [I, 18]],
3264 [[21, I], [8, 19]],
3265 ]
3266};
3267
3268fn plane_residual_size(block: usize, sub_x: usize, sub_y: usize) -> usize {
3271 SUBSAMPLED_SIZE
3272 .get(block)
3273 .and_then(|row| row.get(sub_x))
3274 .and_then(|col| col.get(sub_y))
3275 .copied()
3276 .unwrap_or(BLOCK_INVALID)
3277}
3278
3279fn block_4x4_dims(block: usize) -> (usize, usize) {
3281 BLOCK_4X4_DIMS.get(block).copied().unwrap_or((0, 0))
3282}
3283
3284fn at(colors: &[u16; PALETTE_COLORS], i: usize) -> u16 {
3286 colors.get(i).copied().unwrap_or(0)
3287}
3288
3289fn set_at(colors: &mut [u16; PALETTE_COLORS], i: usize, value: u16) {
3291 if let Some(slot) = colors.get_mut(i) {
3292 *slot = value;
3293 }
3294}
3295
3296fn round2_signed(x: i32, n: u32) -> i32 {
3298 if x >= 0 {
3299 (x + (1 << (n - 1))) >> n
3300 } else {
3301 -((-x + (1 << (n - 1))) >> n)
3302 }
3303}
3304
3305fn ceil_log2(x: u32) -> u32 {
3307 if x < 2 {
3308 0
3309 } else {
3310 u32::BITS - (x - 1).leading_zeros()
3311 }
3312}
3313
3314fn get_mut<T>(slice: &mut [T], index: usize) -> Result<&mut T> {
3316 slice
3317 .get_mut(index)
3318 .ok_or_else(|| PixelsError::malformed("avif", "an AV1 tile CDF index ran out of range"))
3319}
3320
3321#[cfg(test)]
3322#[allow(
3323 clippy::unwrap_used,
3324 clippy::indexing_slicing,
3325 clippy::panic,
3326 reason = "tests operate on known-good values and assert shapes directly"
3327)]
3328mod tests {
3329 use super::*;
3330
3331 fn tiling(cols: u32, rows: u32, size_bytes: u32) -> TileInfo {
3332 TileInfo {
3333 cols_log2: cols.trailing_zeros(),
3334 rows_log2: rows.trailing_zeros(),
3335 cols,
3336 rows,
3337 col_starts_sb: (0..=cols).collect(),
3338 row_starts_sb: (0..=rows).collect(),
3339 context_update_tile_id: 0,
3340 tile_size_bytes: size_bytes,
3341 }
3342 }
3343
3344 fn numbers_and_data<'a>(tiles: &[Tile<'a>]) -> Vec<(u32, &'a [u8])> {
3345 tiles.iter().map(|t| (t.number, t.data)).collect()
3346 }
3347
3348 #[test]
3349 fn a_single_tile_group_has_no_header_and_no_sizes() {
3350 let group = [9, 8, 7];
3351 let tiles = split_tile_group(&group, &tiling(1, 1, 4)).unwrap();
3352 assert_eq!(numbers_and_data(&tiles), [(0, &group[..])]);
3353 }
3354
3355 #[test]
3356 fn tiles_are_split_by_their_little_endian_sizes() {
3357 let group = [0x00, 0x01, 0x00, 0xA, 0xB, 0x00, 0x00, 0xC, 0xD, 0xE];
3360 let tiles = split_tile_group(&group, &tiling(3, 1, 2)).unwrap();
3361 assert_eq!(
3362 numbers_and_data(&tiles),
3363 [(0, &[0xA, 0xB][..]), (1, &[0xC][..]), (2, &[0xD, 0xE][..])]
3364 );
3365 }
3366
3367 #[test]
3368 fn a_group_can_name_the_tiles_it_carries() {
3369 let group = [0b1101_1000, 0x00, 0xA, 0xB];
3372 let tiles = split_tile_group(&group, &tiling(2, 2, 1)).unwrap();
3373 assert_eq!(numbers_and_data(&tiles), [(2, &[0xA][..]), (3, &[0xB][..])]);
3374 }
3375
3376 #[test]
3377 fn broken_tile_groups_are_malformed_not_panics() {
3378 let info = tiling(2, 1, 4);
3379 let error = split_tile_group(&[0x00, 0xFF, 0, 0, 0, 1], &info).unwrap_err();
3381 assert_eq!(error.code(), otf_pixels_core::ErrorCode::Malformed);
3382 let error = split_tile_group(&[0x00, 0x01], &info).unwrap_err();
3384 assert_eq!(error.code(), otf_pixels_core::ErrorCode::Malformed);
3385 let error = split_tile_group(&[0b1110_0000], &tiling(2, 2, 1)).unwrap_err();
3387 assert_eq!(error.code(), otf_pixels_core::ErrorCode::Malformed);
3388 assert!(split_tile_group(&[], &info).is_err());
3390 }
3391
3392 #[test]
3393 fn neg_deinterleave_maps_each_difference_to_a_distinct_segment() {
3394 for max in 1..=8 {
3397 for reference in 0..max {
3398 let mut seen: Vec<i32> = (0..max)
3399 .map(|diff| neg_deinterleave(diff, reference, max))
3400 .collect();
3401 assert_eq!(seen[0], reference, "difference 0 is the prediction");
3402 seen.sort_unstable();
3403 assert_eq!(
3404 seen,
3405 (0..max).collect::<Vec<_>>(),
3406 "max {max} ref {reference}"
3407 );
3408 }
3409 }
3410 let order: Vec<i32> = (0..8).map(|d| neg_deinterleave(d, 3, 8)).collect();
3412 assert_eq!(order, [3, 4, 2, 5, 1, 6, 0, 7]);
3413 }
3414
3415 #[test]
3416 fn block_size_indices_match_the_spec_ordering() {
3417 assert_eq!(block_size_index(1, 1), 0);
3418 assert_eq!(block_size_index(2, 2), 3);
3419 assert_eq!(block_size_index(16, 16), 12);
3420 assert_eq!(block_size_index(16, 4), 21);
3421 }
3422
3423 #[test]
3424 fn at_least_block_8x8_follows_the_enum_order_not_the_dimensions() {
3425 let shapes = [
3430 (1, 1),
3431 (1, 2),
3432 (2, 1),
3433 (2, 2),
3434 (2, 4),
3435 (4, 2),
3436 (4, 4),
3437 (4, 8),
3438 (8, 4),
3439 (8, 8),
3440 (8, 16),
3441 (16, 8),
3442 (16, 16),
3443 (1, 4),
3444 (4, 1),
3445 (2, 8),
3446 (8, 2),
3447 (4, 16),
3448 (16, 4),
3449 ];
3450 for (bw4, bh4) in shapes {
3451 assert_eq!(
3452 at_least_block_8x8(bw4, bh4),
3453 block_size_index(bw4, bh4) >= 3,
3454 "{bw4}x{bh4}"
3455 );
3456 }
3457 assert!(at_least_block_8x8(1, 4));
3458 assert!(at_least_block_8x8(4, 1));
3459 assert!(!at_least_block_8x8(1, 2));
3460 }
3461
3462 #[test]
3463 fn subsampled_sizes_halve_each_subsampled_axis() {
3464 for block in 0..22 {
3468 let (bw4, bh4) = block_4x4_dims(block);
3469 assert_eq!(block_size_index(bw4, bh4), block);
3470 assert_eq!(plane_residual_size(block, 0, 0), block);
3471 for (sub_x, sub_y) in [(1, 0), (1, 1)] {
3472 let sub = plane_residual_size(block, sub_x, sub_y);
3473 if sub == BLOCK_INVALID {
3474 continue;
3475 }
3476 assert_eq!(
3477 block_4x4_dims(sub),
3478 ((bw4 >> sub_x).max(1), (bh4 >> sub_y).max(1)),
3479 "block {block} at {sub_x}x{sub_y}"
3480 );
3481 }
3482 }
3483 assert!((0..22).all(|b| plane_residual_size(b, 1, 1) != BLOCK_INVALID));
3486 assert_eq!(plane_residual_size(1, 1, 0), BLOCK_INVALID);
3487 }
3488
3489 #[test]
3490 fn floor_log2_of_block_units() {
3491 assert_eq!(floor_log2_usize(1), 0);
3492 assert_eq!(floor_log2_usize(2), 1);
3493 assert_eq!(floor_log2_usize(16), 4);
3494 }
3495}