Skip to main content

rav1e/context/
block_unit.rs

1// Copyright (c) 2017-2022, The rav1e contributors. All rights reserved
2//
3// This source code is subject to the terms of the BSD 2 Clause License and
4// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
5// was not distributed with this source code in the LICENSE file, you can
6// obtain it at www.aomedia.org/license/software. If the Alliance for Open
7// Media Patent License 1.0 was not distributed with this source code in the
8// PATENTS file, you can obtain it at www.aomedia.org/license/patent.
9
10use std::mem::MaybeUninit;
11
12use super::*;
13
14use crate::predict::PredictionMode;
15
16pub const MAX_PLANES: usize = 3;
17
18pub const BLOCK_SIZE_GROUPS: usize = 4;
19pub const MAX_ANGLE_DELTA: usize = 3;
20pub const DIRECTIONAL_MODES: usize = 8;
21pub const KF_MODE_CONTEXTS: usize = 5;
22
23pub const INTRA_INTER_CONTEXTS: usize = 4;
24pub const INTER_MODE_CONTEXTS: usize = 8;
25pub const DRL_MODE_CONTEXTS: usize = 3;
26pub const COMP_INTER_CONTEXTS: usize = 5;
27pub const COMP_REF_TYPE_CONTEXTS: usize = 5;
28pub const UNI_COMP_REF_CONTEXTS: usize = 3;
29
30pub const PLANE_TYPES: usize = 2;
31const REF_TYPES: usize = 2;
32
33pub const COMP_INDEX_CONTEXTS: usize = 6;
34pub const COMP_GROUP_IDX_CONTEXTS: usize = 6;
35
36pub const COEFF_CONTEXT_MAX_WIDTH: usize = MAX_TILE_WIDTH / MI_SIZE;
37
38/// Absolute offset in blocks, where a block is defined
39/// to be an `N*N` square where `N == (1 << BLOCK_TO_PLANE_SHIFT)`.
40#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
41pub struct BlockOffset {
42  pub x: usize,
43  pub y: usize,
44}
45
46/// Absolute offset in blocks inside a plane, where a block is defined
47/// to be an `N*N` square where `N == (1 << BLOCK_TO_PLANE_SHIFT)`.
48#[derive(Clone, Copy, Debug, PartialEq, Eq)]
49pub struct PlaneBlockOffset(pub BlockOffset);
50
51/// Absolute offset in blocks inside a tile, where a block is defined
52/// to be an `N*N` square where `N == (1 << BLOCK_TO_PLANE_SHIFT)`.
53#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
54pub struct TileBlockOffset(pub BlockOffset);
55
56impl BlockOffset {
57  /// Offset of the superblock in which this block is located.
58  #[inline]
59  const fn sb_offset(self) -> SuperBlockOffset {
60    SuperBlockOffset {
61      x: self.x >> SUPERBLOCK_TO_BLOCK_SHIFT,
62      y: self.y >> SUPERBLOCK_TO_BLOCK_SHIFT,
63    }
64  }
65
66  /// Offset of the top-left pixel of this block.
67  #[inline]
68  const fn plane_offset(self, plane: &PlaneConfig) -> PlaneOffset {
69    PlaneOffset {
70      x: (self.x >> plane.xdec << BLOCK_TO_PLANE_SHIFT) as isize,
71      y: (self.y >> plane.ydec << BLOCK_TO_PLANE_SHIFT) as isize,
72    }
73  }
74
75  /// Convert to plane offset without decimation.
76  #[inline]
77  const fn to_luma_plane_offset(self) -> PlaneOffset {
78    PlaneOffset {
79      x: (self.x as isize) << BLOCK_TO_PLANE_SHIFT,
80      y: (self.y as isize) << BLOCK_TO_PLANE_SHIFT,
81    }
82  }
83
84  #[inline]
85  const fn y_in_sb(self) -> usize {
86    self.y % MIB_SIZE
87  }
88
89  #[inline]
90  fn with_offset(self, col_offset: isize, row_offset: isize) -> BlockOffset {
91    let x = self.x as isize + col_offset;
92    let y = self.y as isize + row_offset;
93    debug_assert!(x >= 0);
94    debug_assert!(y >= 0);
95
96    BlockOffset { x: x as usize, y: y as usize }
97  }
98}
99
100impl PlaneBlockOffset {
101  /// Offset of the superblock in which this block is located.
102  #[inline]
103  pub const fn sb_offset(self) -> PlaneSuperBlockOffset {
104    PlaneSuperBlockOffset(self.0.sb_offset())
105  }
106
107  /// Offset of the top-left pixel of this block.
108  #[inline]
109  pub const fn plane_offset(self, plane: &PlaneConfig) -> PlaneOffset {
110    self.0.plane_offset(plane)
111  }
112
113  /// Convert to plane offset without decimation.
114  #[inline]
115  pub const fn to_luma_plane_offset(self) -> PlaneOffset {
116    self.0.to_luma_plane_offset()
117  }
118
119  #[inline]
120  pub const fn y_in_sb(self) -> usize {
121    self.0.y_in_sb()
122  }
123
124  #[inline]
125  pub fn with_offset(
126    self, col_offset: isize, row_offset: isize,
127  ) -> PlaneBlockOffset {
128    Self(self.0.with_offset(col_offset, row_offset))
129  }
130}
131
132impl TileBlockOffset {
133  /// Offset of the superblock in which this block is located.
134  #[inline]
135  pub const fn sb_offset(self) -> TileSuperBlockOffset {
136    TileSuperBlockOffset(self.0.sb_offset())
137  }
138
139  /// Offset of the top-left pixel of this block.
140  #[inline]
141  pub const fn plane_offset(self, plane: &PlaneConfig) -> PlaneOffset {
142    self.0.plane_offset(plane)
143  }
144
145  /// Convert to plane offset without decimation.
146  #[inline]
147  pub const fn to_luma_plane_offset(self) -> PlaneOffset {
148    self.0.to_luma_plane_offset()
149  }
150
151  #[inline]
152  pub const fn y_in_sb(self) -> usize {
153    self.0.y_in_sb()
154  }
155
156  #[inline]
157  pub fn with_offset(
158    self, col_offset: isize, row_offset: isize,
159  ) -> TileBlockOffset {
160    Self(self.0.with_offset(col_offset, row_offset))
161  }
162}
163
164#[derive(Copy, Clone)]
165pub struct Block {
166  pub mode: PredictionMode,
167  pub partition: PartitionType,
168  pub skip: bool,
169  pub ref_frames: [RefType; 2],
170  pub mv: [MotionVector; 2],
171  // note: indexes are reflist index, NOT the same as libaom
172  pub neighbors_ref_counts: [u8; INTER_REFS_PER_FRAME],
173  pub cdef_index: u8,
174  pub bsize: BlockSize,
175  pub n4_w: u8, /* block width in the unit of mode_info */
176  pub n4_h: u8, /* block height in the unit of mode_info */
177  pub txsize: TxSize,
178  // The block-level deblock_deltas are left-shifted by
179  // fi.deblock.block_delta_shift and added to the frame-configured
180  // deltas
181  pub deblock_deltas: [i8; FRAME_LF_COUNT],
182  pub segmentation_idx: u8,
183}
184
185impl Block {
186  pub fn is_inter(&self) -> bool {
187    self.mode >= PredictionMode::NEARESTMV
188  }
189  pub fn has_second_ref(&self) -> bool {
190    self.ref_frames[1] != INTRA_FRAME && self.ref_frames[1] != NONE_FRAME
191  }
192}
193
194impl Default for Block {
195  fn default() -> Block {
196    Block {
197      mode: PredictionMode::DC_PRED,
198      partition: PartitionType::PARTITION_NONE,
199      skip: false,
200      ref_frames: [INTRA_FRAME; 2],
201      mv: [MotionVector::default(); 2],
202      neighbors_ref_counts: [0; INTER_REFS_PER_FRAME],
203      cdef_index: 0,
204      bsize: BLOCK_64X64,
205      n4_w: BLOCK_64X64.width_mi() as u8,
206      n4_h: BLOCK_64X64.height_mi() as u8,
207      txsize: TX_64X64,
208      deblock_deltas: [0, 0, 0, 0],
209      segmentation_idx: 0,
210    }
211  }
212}
213
214#[derive(Clone)]
215pub struct BlockContextCheckpoint {
216  x: usize,
217  chroma_sampling: ChromaSampling,
218  cdef_coded: bool,
219  above_partition_context: [u8; MIB_SIZE >> 1],
220  // left context is also at 8x8 granularity
221  left_partition_context: [u8; MIB_SIZE >> 1],
222  above_tx_context: [u8; MIB_SIZE],
223  left_tx_context: [u8; MIB_SIZE],
224  above_coeff_context: [[u8; MIB_SIZE]; MAX_PLANES],
225  left_coeff_context: [[u8; MIB_SIZE]; MAX_PLANES],
226}
227
228pub struct BlockContext<'a> {
229  pub cdef_coded: bool,
230  pub code_deltas: bool,
231  pub update_seg: bool,
232  pub preskip_segid: bool,
233  pub above_partition_context: [u8; PARTITION_CONTEXT_MAX_WIDTH],
234  pub left_partition_context: [u8; MIB_SIZE >> 1],
235  pub above_tx_context: [u8; COEFF_CONTEXT_MAX_WIDTH],
236  pub left_tx_context: [u8; MIB_SIZE],
237  pub above_coeff_context: [[u8; COEFF_CONTEXT_MAX_WIDTH]; MAX_PLANES],
238  pub left_coeff_context: [[u8; MIB_SIZE]; MAX_PLANES],
239  pub blocks: &'a mut TileBlocksMut<'a>,
240}
241
242impl<'a> BlockContext<'a> {
243  pub fn new(blocks: &'a mut TileBlocksMut<'a>) -> Self {
244    BlockContext {
245      cdef_coded: false,
246      code_deltas: false,
247      update_seg: false,
248      preskip_segid: false,
249      above_partition_context: [0; PARTITION_CONTEXT_MAX_WIDTH],
250      left_partition_context: [0; MIB_SIZE >> 1],
251      above_tx_context: [0; COEFF_CONTEXT_MAX_WIDTH],
252      left_tx_context: [0; MIB_SIZE],
253      above_coeff_context: [
254        [0; COEFF_CONTEXT_MAX_WIDTH],
255        [0; COEFF_CONTEXT_MAX_WIDTH],
256        [0; COEFF_CONTEXT_MAX_WIDTH],
257      ],
258      left_coeff_context: [[0; MIB_SIZE]; MAX_PLANES],
259      blocks,
260    }
261  }
262
263  pub fn checkpoint(
264    &self, tile_bo: &TileBlockOffset, chroma_sampling: ChromaSampling,
265  ) -> BlockContextCheckpoint {
266    let x = tile_bo.0.x & (COEFF_CONTEXT_MAX_WIDTH - MIB_SIZE);
267    let mut checkpoint = BlockContextCheckpoint {
268      x,
269      chroma_sampling,
270      cdef_coded: self.cdef_coded,
271      above_partition_context: [0; MIB_SIZE >> 1],
272      left_partition_context: self.left_partition_context,
273      above_tx_context: [0; MIB_SIZE],
274      left_tx_context: self.left_tx_context,
275      above_coeff_context: [[0; MIB_SIZE]; MAX_PLANES],
276      left_coeff_context: self.left_coeff_context,
277    };
278    checkpoint.above_partition_context.copy_from_slice(
279      &self.above_partition_context[(x >> 1)..][..(MIB_SIZE >> 1)],
280    );
281    checkpoint
282      .above_tx_context
283      .copy_from_slice(&self.above_tx_context[x..][..MIB_SIZE]);
284    let num_planes =
285      if chroma_sampling == ChromaSampling::Cs400 { 1 } else { 3 };
286    for (p, (dst, src)) in checkpoint
287      .above_coeff_context
288      .iter_mut()
289      .zip(self.above_coeff_context.iter())
290      .enumerate()
291      .take(num_planes)
292    {
293      let xdec = (p > 0 && chroma_sampling != ChromaSampling::Cs444) as usize;
294      dst.copy_from_slice(&src[(x >> xdec)..][..MIB_SIZE]);
295    }
296    checkpoint
297  }
298
299  pub fn rollback(&mut self, checkpoint: &BlockContextCheckpoint) {
300    let x = checkpoint.x & (COEFF_CONTEXT_MAX_WIDTH - MIB_SIZE);
301    self.cdef_coded = checkpoint.cdef_coded;
302    self.above_partition_context[(x >> 1)..][..(MIB_SIZE >> 1)]
303      .copy_from_slice(&checkpoint.above_partition_context);
304    self.left_partition_context = checkpoint.left_partition_context;
305    self.above_tx_context[x..][..MIB_SIZE]
306      .copy_from_slice(&checkpoint.above_tx_context);
307    self.left_tx_context = checkpoint.left_tx_context;
308    let num_planes =
309      if checkpoint.chroma_sampling == ChromaSampling::Cs400 { 1 } else { 3 };
310    for (p, (dst, src)) in self
311      .above_coeff_context
312      .iter_mut()
313      .zip(checkpoint.above_coeff_context.iter())
314      .enumerate()
315      .take(num_planes)
316    {
317      let xdec = (p > 0 && checkpoint.chroma_sampling != ChromaSampling::Cs444)
318        as usize;
319      dst[(x >> xdec)..][..MIB_SIZE].copy_from_slice(src);
320    }
321    self.left_coeff_context = checkpoint.left_coeff_context;
322  }
323
324  #[inline]
325  pub fn set_dc_sign(cul_level: &mut u32, dc_val: i32) {
326    if dc_val < 0 {
327      *cul_level |= 1 << COEFF_CONTEXT_BITS;
328    } else if dc_val > 0 {
329      *cul_level += 2 << COEFF_CONTEXT_BITS;
330    }
331  }
332
333  pub fn set_coeff_context(
334    &mut self, plane: usize, bo: TileBlockOffset, tx_size: TxSize,
335    xdec: usize, ydec: usize, value: u8,
336  ) {
337    for above in &mut self.above_coeff_context[plane][(bo.0.x >> xdec)..]
338      [..tx_size.width_mi()]
339    {
340      *above = value;
341    }
342    let bo_y = bo.y_in_sb();
343    for left in &mut self.left_coeff_context[plane][(bo_y >> ydec)..]
344      [..tx_size.height_mi()]
345    {
346      *left = value;
347    }
348  }
349
350  fn reset_left_coeff_context(&mut self, plane: usize) {
351    for c in &mut self.left_coeff_context[plane] {
352      *c = 0;
353    }
354  }
355
356  fn reset_left_partition_context(&mut self) {
357    for c in &mut self.left_partition_context {
358      *c = 0;
359    }
360  }
361
362  pub fn update_tx_size_context(
363    &mut self, bo: TileBlockOffset, bsize: BlockSize, tx_size: TxSize,
364    skip: bool,
365  ) {
366    let n4_w = bsize.width_mi();
367    let n4_h = bsize.height_mi();
368
369    let (tx_w, tx_h) = if skip {
370      ((n4_w * MI_SIZE) as u8, (n4_h * MI_SIZE) as u8)
371    } else {
372      (tx_size.width() as u8, tx_size.height() as u8)
373    };
374
375    let above_ctx = &mut self.above_tx_context[bo.0.x..bo.0.x + n4_w];
376    let left_ctx =
377      &mut self.left_tx_context[bo.y_in_sb()..bo.y_in_sb() + n4_h];
378
379    for v in above_ctx[0..n4_w].iter_mut() {
380      *v = tx_w;
381    }
382
383    for v in left_ctx[0..n4_h].iter_mut() {
384      *v = tx_h;
385    }
386  }
387
388  fn reset_left_tx_context(&mut self) {
389    for c in &mut self.left_tx_context {
390      *c = 0;
391    }
392  }
393
394  pub fn reset_left_contexts(&mut self, planes: usize) {
395    for p in 0..planes {
396      BlockContext::reset_left_coeff_context(self, p);
397    }
398    BlockContext::reset_left_partition_context(self);
399
400    BlockContext::reset_left_tx_context(self);
401  }
402
403  // The mode info data structure has a one element border above and to the
404  // left of the entries corresponding to real macroblocks.
405  // The prediction flags in these dummy entries are initialized to 0.
406  // 0 - inter/inter, inter/--, --/inter, --/--
407  // 1 - intra/inter, inter/intra
408  // 2 - intra/--, --/intra
409  // 3 - intra/intra
410  pub fn intra_inter_context(&self, bo: TileBlockOffset) -> usize {
411    let has_above = bo.0.y > 0;
412    let has_left = bo.0.x > 0;
413
414    match (has_above, has_left) {
415      (true, true) => {
416        let above_intra = !self.blocks.above_of(bo).is_inter();
417        let left_intra = !self.blocks.left_of(bo).is_inter();
418        if above_intra && left_intra {
419          3
420        } else {
421          (above_intra || left_intra) as usize
422        }
423      }
424      (true, false) => {
425        if self.blocks.above_of(bo).is_inter() {
426          0
427        } else {
428          2
429        }
430      }
431      (false, true) => {
432        if self.blocks.left_of(bo).is_inter() {
433          0
434        } else {
435          2
436        }
437      }
438      _ => 0,
439    }
440  }
441
442  pub fn get_txb_ctx(
443    &self, plane_bsize: BlockSize, tx_size: TxSize, plane: usize,
444    bo: TileBlockOffset, xdec: usize, ydec: usize, frame_clipped_txw: usize,
445    frame_clipped_txh: usize,
446  ) -> TXB_CTX {
447    let mut txb_ctx = TXB_CTX { txb_skip_ctx: 0, dc_sign_ctx: 0 };
448    const MAX_TX_SIZE_UNIT: usize = 16;
449    const signs: [i8; 3] = [0, -1, 1];
450    const dc_sign_contexts: [usize; 4 * MAX_TX_SIZE_UNIT + 1] = [
451      1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
452      1, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
453      2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
454    ];
455    let mut dc_sign: i16 = 0;
456
457    let above_ctxs = &self.above_coeff_context[plane][(bo.0.x >> xdec)..]
458      [..frame_clipped_txw >> 2];
459    let left_ctxs = &self.left_coeff_context[plane][(bo.y_in_sb() >> ydec)..]
460      [..frame_clipped_txh >> 2];
461
462    // Decide txb_ctx.dc_sign_ctx
463    for &ctx in above_ctxs {
464      let sign = ctx >> COEFF_CONTEXT_BITS;
465      dc_sign += signs[sign as usize] as i16;
466    }
467
468    for &ctx in left_ctxs {
469      let sign = ctx >> COEFF_CONTEXT_BITS;
470      dc_sign += signs[sign as usize] as i16;
471    }
472
473    txb_ctx.dc_sign_ctx =
474      dc_sign_contexts[(dc_sign + 2 * MAX_TX_SIZE_UNIT as i16) as usize];
475
476    // Decide txb_ctx.txb_skip_ctx
477    if plane == 0 {
478      if plane_bsize == tx_size.block_size() {
479        txb_ctx.txb_skip_ctx = 0;
480      } else {
481        // This is the algorithm to generate table skip_contexts[min][max].
482        //    if (!max)
483        //      txb_skip_ctx = 1;
484        //    else if (!min)
485        //      txb_skip_ctx = 2 + (max > 3);
486        //    else if (max <= 3)
487        //      txb_skip_ctx = 4;
488        //    else if (min <= 3)
489        //      txb_skip_ctx = 5;
490        //    else
491        //      txb_skip_ctx = 6;
492        const skip_contexts: [[u8; 5]; 5] = [
493          [1, 2, 2, 2, 3],
494          [1, 4, 4, 4, 5],
495          [1, 4, 4, 4, 5],
496          [1, 4, 4, 4, 5],
497          [1, 4, 4, 4, 6],
498        ];
499
500        let top: u8 = above_ctxs.iter().fold(0, |acc, ctx| acc | *ctx)
501          & COEFF_CONTEXT_MASK as u8;
502
503        let left: u8 = left_ctxs.iter().fold(0, |acc, ctx| acc | *ctx)
504          & COEFF_CONTEXT_MASK as u8;
505
506        let max = cmp::min(top | left, 4);
507        let min = cmp::min(cmp::min(top, left), 4);
508        txb_ctx.txb_skip_ctx =
509          skip_contexts[min as usize][max as usize] as usize;
510      }
511    } else {
512      let top: u8 = above_ctxs.iter().fold(0, |acc, ctx| acc | *ctx);
513      let left: u8 = left_ctxs.iter().fold(0, |acc, ctx| acc | *ctx);
514      let ctx_base = (top != 0) as usize + (left != 0) as usize;
515      let ctx_offset = if num_pels_log2_lookup[plane_bsize as usize]
516        > num_pels_log2_lookup[tx_size.block_size() as usize]
517      {
518        10
519      } else {
520        7
521      };
522      txb_ctx.txb_skip_ctx = ctx_base + ctx_offset;
523    }
524
525    txb_ctx
526  }
527}
528
529#[derive(Clone, Copy)]
530#[repr(C)]
531pub struct NMVComponent {
532  pub sign_cdf: [u16; 2],
533  pub class0_hp_cdf: [u16; 2],
534  pub hp_cdf: [u16; 2],
535  pub class0_cdf: [u16; CLASS0_SIZE],
536  pub bits_cdf: [[u16; 2]; MV_OFFSET_BITS],
537
538  pub class0_fp_cdf: [[u16; MV_FP_SIZE]; CLASS0_SIZE],
539  pub fp_cdf: [u16; MV_FP_SIZE],
540
541  pub classes_cdf: [u16; MV_CLASSES],
542  // MV_CLASSES + 5 == 16; pad the last CDF for rollback.
543  padding: [u16; 5],
544}
545
546#[derive(Clone, Copy)]
547#[repr(C)]
548pub struct NMVContext {
549  pub joints_cdf: [u16; MV_JOINTS],
550  // MV_JOINTS + 12 == 16; pad the last CDF for rollback.
551  padding: [u16; 12],
552  pub comps: [NMVComponent; 2],
553}
554
555// lv_map
556pub static default_nmv_context: NMVContext = {
557  NMVContext {
558    joints_cdf: cdf([4096, 11264, 19328]),
559    padding: [0; 12],
560    comps: [
561      NMVComponent {
562        classes_cdf: cdf([
563          28672, 30976, 31858, 32320, 32551, 32656, 32740, 32757, 32762, 32767,
564        ]),
565        class0_fp_cdf: cdf_2d([[16384, 24576, 26624], [12288, 21248, 24128]]),
566        fp_cdf: cdf([8192, 17408, 21248]),
567        sign_cdf: cdf([128 * 128]),
568        class0_hp_cdf: cdf([160 * 128]),
569        hp_cdf: cdf([128 * 128]),
570        class0_cdf: cdf([216 * 128]),
571        bits_cdf: cdf_2d([
572          [128 * 136],
573          [128 * 140],
574          [128 * 148],
575          [128 * 160],
576          [128 * 176],
577          [128 * 192],
578          [128 * 224],
579          [128 * 234],
580          [128 * 234],
581          [128 * 240],
582        ]),
583        padding: [0; 5],
584      },
585      NMVComponent {
586        classes_cdf: cdf([
587          28672, 30976, 31858, 32320, 32551, 32656, 32740, 32757, 32762, 32767,
588        ]),
589        class0_fp_cdf: cdf_2d([[16384, 24576, 26624], [12288, 21248, 24128]]),
590        fp_cdf: cdf([8192, 17408, 21248]),
591        sign_cdf: cdf([128 * 128]),
592        class0_hp_cdf: cdf([160 * 128]),
593        hp_cdf: cdf([128 * 128]),
594        class0_cdf: cdf([216 * 128]),
595        bits_cdf: cdf_2d([
596          [128 * 136],
597          [128 * 140],
598          [128 * 148],
599          [128 * 160],
600          [128 * 176],
601          [128 * 192],
602          [128 * 224],
603          [128 * 234],
604          [128 * 234],
605          [128 * 240],
606        ]),
607        padding: [0; 5],
608      },
609    ],
610  }
611};
612
613#[derive(Clone)]
614pub struct CandidateMV {
615  pub this_mv: MotionVector,
616  pub comp_mv: MotionVector,
617  pub weight: u32,
618}
619
620#[derive(Clone)]
621pub struct FrameBlocks {
622  blocks: Box<[Block]>,
623  pub cols: usize,
624  pub rows: usize,
625}
626
627impl FrameBlocks {
628  pub fn new(cols: usize, rows: usize) -> Self {
629    Self {
630      blocks: vec![Block::default(); cols * rows].into_boxed_slice(),
631      cols,
632      rows,
633    }
634  }
635
636  #[inline(always)]
637  pub fn as_tile_blocks(&self) -> TileBlocks<'_> {
638    TileBlocks::new(self, 0, 0, self.cols, self.rows)
639  }
640
641  #[inline(always)]
642  pub fn as_tile_blocks_mut(&mut self) -> TileBlocksMut<'_> {
643    TileBlocksMut::new(self, 0, 0, self.cols, self.rows)
644  }
645}
646
647impl Index<usize> for FrameBlocks {
648  type Output = [Block];
649  #[inline]
650  fn index(&self, index: usize) -> &Self::Output {
651    &self.blocks[index * self.cols..(index + 1) * self.cols]
652  }
653}
654
655impl IndexMut<usize> for FrameBlocks {
656  #[inline]
657  fn index_mut(&mut self, index: usize) -> &mut Self::Output {
658    &mut self.blocks[index * self.cols..(index + 1) * self.cols]
659  }
660}
661
662// for convenience, also index by BlockOffset
663
664impl Index<PlaneBlockOffset> for FrameBlocks {
665  type Output = Block;
666  #[inline]
667  fn index(&self, bo: PlaneBlockOffset) -> &Self::Output {
668    &self[bo.0.y][bo.0.x]
669  }
670}
671
672impl IndexMut<PlaneBlockOffset> for FrameBlocks {
673  #[inline]
674  fn index_mut(&mut self, bo: PlaneBlockOffset) -> &mut Self::Output {
675    &mut self[bo.0.y][bo.0.x]
676  }
677}
678
679impl ContextWriter<'_> {
680  pub fn get_cdf_intra_mode_kf(
681    &self, bo: TileBlockOffset,
682  ) -> &[u16; INTRA_MODES] {
683    static intra_mode_context: [usize; INTRA_MODES] =
684      [0, 1, 2, 3, 4, 4, 4, 4, 3, 0, 1, 2, 0];
685    let above_mode = if bo.0.y > 0 {
686      self.bc.blocks.above_of(bo).mode
687    } else {
688      PredictionMode::DC_PRED
689    };
690    let left_mode = if bo.0.x > 0 {
691      self.bc.blocks.left_of(bo).mode
692    } else {
693      PredictionMode::DC_PRED
694    };
695    let above_ctx = intra_mode_context[above_mode as usize];
696    let left_ctx = intra_mode_context[left_mode as usize];
697    &self.fc.kf_y_cdf[above_ctx][left_ctx]
698  }
699
700  pub fn write_intra_mode_kf<W: Writer>(
701    &mut self, w: &mut W, bo: TileBlockOffset, mode: PredictionMode,
702  ) {
703    static intra_mode_context: [usize; INTRA_MODES] =
704      [0, 1, 2, 3, 4, 4, 4, 4, 3, 0, 1, 2, 0];
705    let above_mode = if bo.0.y > 0 {
706      self.bc.blocks.above_of(bo).mode
707    } else {
708      PredictionMode::DC_PRED
709    };
710    let left_mode = if bo.0.x > 0 {
711      self.bc.blocks.left_of(bo).mode
712    } else {
713      PredictionMode::DC_PRED
714    };
715    let above_ctx = intra_mode_context[above_mode as usize];
716    let left_ctx = intra_mode_context[left_mode as usize];
717    let cdf = &self.fc.kf_y_cdf[above_ctx][left_ctx];
718    symbol_with_update!(self, w, mode as u32, cdf);
719  }
720
721  pub fn get_cdf_intra_mode(&self, bsize: BlockSize) -> &[u16; INTRA_MODES] {
722    &self.fc.y_mode_cdf[size_group_lookup[bsize as usize] as usize]
723  }
724
725  #[inline]
726  pub fn write_intra_mode<W: Writer>(
727    &mut self, w: &mut W, bsize: BlockSize, mode: PredictionMode,
728  ) {
729    let cdf = &self.fc.y_mode_cdf[size_group_lookup[bsize as usize] as usize];
730    symbol_with_update!(self, w, mode as u32, cdf);
731  }
732
733  #[inline]
734  pub fn write_intra_uv_mode<W: Writer>(
735    &mut self, w: &mut W, uv_mode: PredictionMode, y_mode: PredictionMode,
736    bs: BlockSize,
737  ) {
738    if bs.cfl_allowed() {
739      let cdf = &self.fc.uv_mode_cfl_cdf[y_mode as usize];
740      symbol_with_update!(self, w, uv_mode as u32, cdf);
741    } else {
742      let cdf = &self.fc.uv_mode_cdf[y_mode as usize];
743      symbol_with_update!(self, w, uv_mode as u32, cdf);
744    }
745  }
746
747  #[inline]
748  pub fn write_angle_delta<W: Writer>(
749    &mut self, w: &mut W, angle: i8, mode: PredictionMode,
750  ) {
751    symbol_with_update!(
752      self,
753      w,
754      (angle + MAX_ANGLE_DELTA as i8) as u32,
755      &self.fc.angle_delta_cdf
756        [mode as usize - PredictionMode::V_PRED as usize]
757    );
758  }
759
760  pub fn write_use_filter_intra<W: Writer>(
761    &mut self, w: &mut W, enable: bool, block_size: BlockSize,
762  ) {
763    let cdf = &self.fc.filter_intra_cdfs[block_size as usize];
764    symbol_with_update!(self, w, enable as u32, cdf);
765  }
766
767  /// # Panics
768  ///
769  /// - If called with `enable: true` (not yet implemented
770  pub fn write_use_palette_mode<W: Writer>(
771    &mut self, w: &mut W, enable: bool, bsize: BlockSize, bo: TileBlockOffset,
772    luma_mode: PredictionMode, chroma_mode: PredictionMode, xdec: usize,
773    ydec: usize, cs: ChromaSampling,
774  ) {
775    if enable {
776      unimplemented!(); // TODO
777    }
778
779    let (ctx_luma, ctx_chroma) = (0, 0); // TODO: increase based on surrounding block info
780
781    if luma_mode == PredictionMode::DC_PRED {
782      let bsize_ctx = bsize.width_mi_log2() + bsize.height_mi_log2() - 2;
783      let cdf = &self.fc.palette_y_mode_cdfs[bsize_ctx][ctx_luma];
784      symbol_with_update!(self, w, enable as u32, cdf);
785    }
786
787    if has_chroma(bo, bsize, xdec, ydec, cs)
788      && chroma_mode == PredictionMode::DC_PRED
789    {
790      let cdf = &self.fc.palette_uv_mode_cdfs[ctx_chroma];
791      symbol_with_update!(self, w, enable as u32, cdf);
792    }
793  }
794
795  fn find_valid_row_offs(
796    row_offset: isize, mi_row: usize, mi_rows: usize,
797  ) -> isize {
798    cmp::min(
799      cmp::max(row_offset, -(mi_row as isize)),
800      (mi_rows - mi_row - 1) as isize,
801    )
802  }
803
804  fn find_valid_col_offs(
805    col_offset: isize, mi_col: usize, mi_cols: usize,
806  ) -> isize {
807    cmp::min(
808      cmp::max(col_offset, -(mi_col as isize)),
809      (mi_cols - mi_col - 1) as isize,
810    )
811  }
812
813  fn find_matching_mv(
814    mv: MotionVector, mv_stack: &mut ArrayVec<CandidateMV, 9>,
815  ) -> bool {
816    for mv_cand in mv_stack {
817      if mv.row == mv_cand.this_mv.row && mv.col == mv_cand.this_mv.col {
818        return true;
819      }
820    }
821    false
822  }
823
824  fn find_matching_mv_and_update_weight(
825    mv: MotionVector, mv_stack: &mut ArrayVec<CandidateMV, 9>, weight: u32,
826  ) -> bool {
827    for mv_cand in mv_stack {
828      if mv.row == mv_cand.this_mv.row && mv.col == mv_cand.this_mv.col {
829        mv_cand.weight += weight;
830        return true;
831      }
832    }
833    false
834  }
835
836  fn find_matching_comp_mv_and_update_weight(
837    mvs: [MotionVector; 2], mv_stack: &mut ArrayVec<CandidateMV, 9>,
838    weight: u32,
839  ) -> bool {
840    for mv_cand in mv_stack {
841      if mvs[0].row == mv_cand.this_mv.row
842        && mvs[0].col == mv_cand.this_mv.col
843        && mvs[1].row == mv_cand.comp_mv.row
844        && mvs[1].col == mv_cand.comp_mv.col
845      {
846        mv_cand.weight += weight;
847        return true;
848      }
849    }
850    false
851  }
852
853  fn add_ref_mv_candidate(
854    ref_frames: [RefType; 2], blk: &Block,
855    mv_stack: &mut ArrayVec<CandidateMV, 9>, weight: u32,
856    newmv_count: &mut usize, is_compound: bool,
857  ) -> bool {
858    if !blk.is_inter() {
859      /* For intrabc */
860      false
861    } else if is_compound {
862      if blk.ref_frames[0] == ref_frames[0]
863        && blk.ref_frames[1] == ref_frames[1]
864      {
865        let found_match = Self::find_matching_comp_mv_and_update_weight(
866          blk.mv, mv_stack, weight,
867        );
868
869        if !found_match && mv_stack.len() < MAX_REF_MV_STACK_SIZE {
870          let mv_cand =
871            CandidateMV { this_mv: blk.mv[0], comp_mv: blk.mv[1], weight };
872
873          mv_stack.push(mv_cand);
874        }
875
876        if blk.mode.has_newmv() {
877          *newmv_count += 1;
878        }
879
880        true
881      } else {
882        false
883      }
884    } else {
885      let mut found = false;
886      for i in 0..2 {
887        if blk.ref_frames[i] == ref_frames[0] {
888          let found_match = Self::find_matching_mv_and_update_weight(
889            blk.mv[i], mv_stack, weight,
890          );
891
892          if !found_match && mv_stack.len() < MAX_REF_MV_STACK_SIZE {
893            let mv_cand = CandidateMV {
894              this_mv: blk.mv[i],
895              comp_mv: MotionVector::default(),
896              weight,
897            };
898
899            mv_stack.push(mv_cand);
900          }
901
902          if blk.mode.has_newmv() {
903            *newmv_count += 1;
904          }
905
906          found = true;
907        }
908      }
909      found
910    }
911  }
912
913  fn add_extra_mv_candidate<T: Pixel>(
914    blk: &Block, ref_frames: [RefType; 2],
915    mv_stack: &mut ArrayVec<CandidateMV, 9>, fi: &FrameInvariants<T>,
916    is_compound: bool, ref_id_count: &mut [usize; 2],
917    ref_id_mvs: &mut [[MotionVector; 2]; 2], ref_diff_count: &mut [usize; 2],
918    ref_diff_mvs: &mut [[MotionVector; 2]; 2],
919  ) {
920    if is_compound {
921      for cand_list in 0..2 {
922        let cand_ref = blk.ref_frames[cand_list];
923        if cand_ref != INTRA_FRAME && cand_ref != NONE_FRAME {
924          for list in 0..2 {
925            let mut cand_mv = blk.mv[cand_list];
926            if cand_ref == ref_frames[list] && ref_id_count[list] < 2 {
927              ref_id_mvs[list][ref_id_count[list]] = cand_mv;
928              ref_id_count[list] += 1;
929            } else if ref_diff_count[list] < 2 {
930              if fi.ref_frame_sign_bias[cand_ref.to_index()]
931                != fi.ref_frame_sign_bias[ref_frames[list].to_index()]
932              {
933                cand_mv.row = -cand_mv.row;
934                cand_mv.col = -cand_mv.col;
935              }
936              ref_diff_mvs[list][ref_diff_count[list]] = cand_mv;
937              ref_diff_count[list] += 1;
938            }
939          }
940        }
941      }
942    } else {
943      for cand_list in 0..2 {
944        let cand_ref = blk.ref_frames[cand_list];
945        if cand_ref != INTRA_FRAME && cand_ref != NONE_FRAME {
946          let mut mv = blk.mv[cand_list];
947          if fi.ref_frame_sign_bias[cand_ref.to_index()]
948            != fi.ref_frame_sign_bias[ref_frames[0].to_index()]
949          {
950            mv.row = -mv.row;
951            mv.col = -mv.col;
952          }
953
954          if !Self::find_matching_mv(mv, mv_stack) {
955            let mv_cand = CandidateMV {
956              this_mv: mv,
957              comp_mv: MotionVector::default(),
958              weight: 2,
959            };
960            mv_stack.push(mv_cand);
961          }
962        }
963      }
964    }
965  }
966
967  fn scan_row_mbmi(
968    &self, bo: TileBlockOffset, row_offset: isize, max_row_offs: isize,
969    processed_rows: &mut isize, ref_frames: [RefType; 2],
970    mv_stack: &mut ArrayVec<CandidateMV, 9>, newmv_count: &mut usize,
971    bsize: BlockSize, is_compound: bool,
972  ) -> bool {
973    let bc = &self.bc;
974    let target_n4_w = bsize.width_mi();
975
976    let end_mi = cmp::min(
977      cmp::min(target_n4_w, bc.blocks.cols() - bo.0.x),
978      BLOCK_64X64.width_mi(),
979    );
980    let n4_w_8 = BLOCK_8X8.width_mi();
981    let n4_w_16 = BLOCK_16X16.width_mi();
982    let mut col_offset = 0;
983
984    if row_offset.abs() > 1 {
985      col_offset = 1;
986      if ((bo.0.x & 0x01) != 0) && (target_n4_w < n4_w_8) {
987        col_offset -= 1;
988      }
989    }
990
991    let use_step_16 = target_n4_w >= 16;
992
993    let mut found_match = false;
994
995    let mut i = 0;
996    while i < end_mi {
997      let cand =
998        &bc.blocks[bo.with_offset(col_offset + i as isize, row_offset)];
999
1000      let n4_w = cand.n4_w as usize;
1001      let mut len = cmp::min(target_n4_w, n4_w);
1002      if use_step_16 {
1003        len = cmp::max(n4_w_16, len);
1004      } else if row_offset.abs() > 1 {
1005        len = cmp::max(len, n4_w_8);
1006      }
1007
1008      let mut weight: u32 = 2;
1009      if target_n4_w >= n4_w_8 && target_n4_w <= n4_w {
1010        let inc = cmp::min(-max_row_offs + row_offset + 1, cand.n4_h as isize);
1011        assert!(inc >= 0);
1012        weight = cmp::max(weight, inc as u32);
1013        *processed_rows = inc - row_offset - 1;
1014      }
1015
1016      if Self::add_ref_mv_candidate(
1017        ref_frames,
1018        cand,
1019        mv_stack,
1020        len as u32 * weight,
1021        newmv_count,
1022        is_compound,
1023      ) {
1024        found_match = true;
1025      }
1026
1027      i += len;
1028    }
1029
1030    found_match
1031  }
1032
1033  fn scan_col_mbmi(
1034    &self, bo: TileBlockOffset, col_offset: isize, max_col_offs: isize,
1035    processed_cols: &mut isize, ref_frames: [RefType; 2],
1036    mv_stack: &mut ArrayVec<CandidateMV, 9>, newmv_count: &mut usize,
1037    bsize: BlockSize, is_compound: bool,
1038  ) -> bool {
1039    let bc = &self.bc;
1040
1041    let target_n4_h = bsize.height_mi();
1042
1043    let end_mi = cmp::min(
1044      cmp::min(target_n4_h, bc.blocks.rows() - bo.0.y),
1045      BLOCK_64X64.height_mi(),
1046    );
1047    let n4_h_8 = BLOCK_8X8.height_mi();
1048    let n4_h_16 = BLOCK_16X16.height_mi();
1049    let mut row_offset = 0;
1050
1051    if col_offset.abs() > 1 {
1052      row_offset = 1;
1053      if ((bo.0.y & 0x01) != 0) && (target_n4_h < n4_h_8) {
1054        row_offset -= 1;
1055      }
1056    }
1057
1058    let use_step_16 = target_n4_h >= 16;
1059
1060    let mut found_match = false;
1061
1062    let mut i = 0;
1063    while i < end_mi {
1064      let cand =
1065        &bc.blocks[bo.with_offset(col_offset, row_offset + i as isize)];
1066      let n4_h = cand.n4_h as usize;
1067      let mut len = cmp::min(target_n4_h, n4_h);
1068      if use_step_16 {
1069        len = cmp::max(n4_h_16, len);
1070      } else if col_offset.abs() > 1 {
1071        len = cmp::max(len, n4_h_8);
1072      }
1073
1074      let mut weight: u32 = 2;
1075      if target_n4_h >= n4_h_8 && target_n4_h <= n4_h {
1076        let inc = cmp::min(-max_col_offs + col_offset + 1, cand.n4_w as isize);
1077        assert!(inc >= 0);
1078        weight = cmp::max(weight, inc as u32);
1079        *processed_cols = inc - col_offset - 1;
1080      }
1081
1082      if Self::add_ref_mv_candidate(
1083        ref_frames,
1084        cand,
1085        mv_stack,
1086        len as u32 * weight,
1087        newmv_count,
1088        is_compound,
1089      ) {
1090        found_match = true;
1091      }
1092
1093      i += len;
1094    }
1095
1096    found_match
1097  }
1098
1099  fn scan_blk_mbmi(
1100    &self, bo: TileBlockOffset, ref_frames: [RefType; 2],
1101    mv_stack: &mut ArrayVec<CandidateMV, 9>, newmv_count: &mut usize,
1102    is_compound: bool,
1103  ) -> bool {
1104    if bo.0.x >= self.bc.blocks.cols() || bo.0.y >= self.bc.blocks.rows() {
1105      return false;
1106    }
1107
1108    let weight = 2 * BLOCK_8X8.width_mi() as u32;
1109    /* Always assume its within a tile, probably wrong */
1110    Self::add_ref_mv_candidate(
1111      ref_frames,
1112      &self.bc.blocks[bo],
1113      mv_stack,
1114      weight,
1115      newmv_count,
1116      is_compound,
1117    )
1118  }
1119
1120  fn add_offset(mv_stack: &mut ArrayVec<CandidateMV, 9>) {
1121    for cand_mv in mv_stack {
1122      cand_mv.weight += REF_CAT_LEVEL;
1123    }
1124  }
1125
1126  #[profiling::function]
1127  fn setup_mvref_list<T: Pixel>(
1128    &self, bo: TileBlockOffset, ref_frames: [RefType; 2],
1129    mv_stack: &mut ArrayVec<CandidateMV, 9>, bsize: BlockSize,
1130    fi: &FrameInvariants<T>, is_compound: bool,
1131  ) -> usize {
1132    let (_rf, _rf_num) = (INTRA_FRAME, 1);
1133
1134    let target_n4_h = bsize.height_mi();
1135    let target_n4_w = bsize.width_mi();
1136
1137    let mut max_row_offs: isize = 0;
1138    let row_adj =
1139      (target_n4_h < BLOCK_8X8.height_mi()) && (bo.0.y & 0x01) != 0x0;
1140
1141    let mut max_col_offs: isize = 0;
1142    let col_adj =
1143      (target_n4_w < BLOCK_8X8.width_mi()) && (bo.0.x & 0x01) != 0x0;
1144
1145    let mut processed_rows: isize = 0;
1146    let mut processed_cols: isize = 0;
1147
1148    let up_avail = bo.0.y > 0;
1149    let left_avail = bo.0.x > 0;
1150
1151    if up_avail {
1152      max_row_offs = -2 * MVREF_ROW_COLS as isize + row_adj as isize;
1153
1154      // limit max offset for small blocks
1155      if target_n4_h < BLOCK_8X8.height_mi() {
1156        max_row_offs = -2 * 2 + row_adj as isize;
1157      }
1158
1159      let rows = self.bc.blocks.rows();
1160      max_row_offs = Self::find_valid_row_offs(max_row_offs, bo.0.y, rows);
1161    }
1162
1163    if left_avail {
1164      max_col_offs = -2 * MVREF_ROW_COLS as isize + col_adj as isize;
1165
1166      // limit max offset for small blocks
1167      if target_n4_w < BLOCK_8X8.width_mi() {
1168        max_col_offs = -2 * 2 + col_adj as isize;
1169      }
1170
1171      let cols = self.bc.blocks.cols();
1172      max_col_offs = Self::find_valid_col_offs(max_col_offs, bo.0.x, cols);
1173    }
1174
1175    let mut row_match = false;
1176    let mut col_match = false;
1177    let mut newmv_count: usize = 0;
1178
1179    if max_row_offs.abs() >= 1 {
1180      let found_match = self.scan_row_mbmi(
1181        bo,
1182        -1,
1183        max_row_offs,
1184        &mut processed_rows,
1185        ref_frames,
1186        mv_stack,
1187        &mut newmv_count,
1188        bsize,
1189        is_compound,
1190      );
1191      row_match |= found_match;
1192    }
1193    if max_col_offs.abs() >= 1 {
1194      let found_match = self.scan_col_mbmi(
1195        bo,
1196        -1,
1197        max_col_offs,
1198        &mut processed_cols,
1199        ref_frames,
1200        mv_stack,
1201        &mut newmv_count,
1202        bsize,
1203        is_compound,
1204      );
1205      col_match |= found_match;
1206    }
1207    if has_tr(bo, bsize) && bo.0.y > 0 {
1208      let found_match = self.scan_blk_mbmi(
1209        bo.with_offset(target_n4_w as isize, -1),
1210        ref_frames,
1211        mv_stack,
1212        &mut newmv_count,
1213        is_compound,
1214      );
1215      row_match |= found_match;
1216    }
1217
1218    let nearest_match = usize::from(row_match) + usize::from(col_match);
1219
1220    Self::add_offset(mv_stack);
1221
1222    /* Scan the second outer area. */
1223    let mut far_newmv_count: usize = 0; // won't be used
1224
1225    let found_match = bo.0.x > 0
1226      && bo.0.y > 0
1227      && self.scan_blk_mbmi(
1228        bo.with_offset(-1, -1),
1229        ref_frames,
1230        mv_stack,
1231        &mut far_newmv_count,
1232        is_compound,
1233      );
1234    row_match |= found_match;
1235
1236    for idx in 2..=MVREF_ROW_COLS {
1237      let row_offset = -2 * idx as isize + 1 + row_adj as isize;
1238      let col_offset = -2 * idx as isize + 1 + col_adj as isize;
1239
1240      if row_offset.abs() <= max_row_offs.abs()
1241        && row_offset.abs() > processed_rows
1242      {
1243        let found_match = self.scan_row_mbmi(
1244          bo,
1245          row_offset,
1246          max_row_offs,
1247          &mut processed_rows,
1248          ref_frames,
1249          mv_stack,
1250          &mut far_newmv_count,
1251          bsize,
1252          is_compound,
1253        );
1254        row_match |= found_match;
1255      }
1256
1257      if col_offset.abs() <= max_col_offs.abs()
1258        && col_offset.abs() > processed_cols
1259      {
1260        let found_match = self.scan_col_mbmi(
1261          bo,
1262          col_offset,
1263          max_col_offs,
1264          &mut processed_cols,
1265          ref_frames,
1266          mv_stack,
1267          &mut far_newmv_count,
1268          bsize,
1269          is_compound,
1270        );
1271        col_match |= found_match;
1272      }
1273    }
1274
1275    let total_match = usize::from(row_match) + usize::from(col_match);
1276
1277    assert!(total_match >= nearest_match);
1278
1279    // mode_context contains both newmv_context and refmv_context, where newmv_context
1280    // lies in the REF_MVOFFSET least significant bits
1281    let mode_context = match nearest_match {
1282      0 => cmp::min(total_match, 1) + (total_match << REFMV_OFFSET),
1283      1 => 3 - cmp::min(newmv_count, 1) + ((2 + total_match) << REFMV_OFFSET),
1284      _ => 5 - cmp::min(newmv_count, 1) + (5 << REFMV_OFFSET),
1285    };
1286
1287    /* TODO: Find nearest match and assign nearest and near mvs */
1288
1289    // 7.10.2.11 Sort MV stack according to weight
1290    mv_stack.sort_by(|a, b| b.weight.cmp(&a.weight));
1291
1292    if mv_stack.len() < 2 {
1293      // 7.10.2.12 Extra search process
1294
1295      let w4 = bsize.width_mi().min(16).min(self.bc.blocks.cols() - bo.0.x);
1296      let h4 = bsize.height_mi().min(16).min(self.bc.blocks.rows() - bo.0.y);
1297      let num4x4 = w4.min(h4);
1298
1299      let passes = i32::from(!up_avail)..=i32::from(left_avail);
1300
1301      let mut ref_id_count: [usize; 2] = [0; 2];
1302      let mut ref_diff_count: [usize; 2] = [0; 2];
1303      let mut ref_id_mvs = [[MotionVector::default(); 2]; 2];
1304      let mut ref_diff_mvs = [[MotionVector::default(); 2]; 2];
1305
1306      for pass in passes {
1307        let mut idx = 0;
1308        while idx < num4x4 && mv_stack.len() < 2 {
1309          let rbo = if pass == 0 {
1310            bo.with_offset(idx as isize, -1)
1311          } else {
1312            bo.with_offset(-1, idx as isize)
1313          };
1314
1315          let blk = &self.bc.blocks[rbo];
1316          Self::add_extra_mv_candidate(
1317            blk,
1318            ref_frames,
1319            mv_stack,
1320            fi,
1321            is_compound,
1322            &mut ref_id_count,
1323            &mut ref_id_mvs,
1324            &mut ref_diff_count,
1325            &mut ref_diff_mvs,
1326          );
1327
1328          idx += if pass == 0 { blk.n4_w } else { blk.n4_h } as usize;
1329        }
1330      }
1331
1332      if is_compound {
1333        let mut combined_mvs = [[MotionVector::default(); 2]; 2];
1334
1335        for list in 0..2 {
1336          let mut comp_count = 0;
1337          for idx in 0..ref_id_count[list] {
1338            combined_mvs[comp_count][list] = ref_id_mvs[list][idx];
1339            comp_count += 1;
1340          }
1341          for idx in 0..ref_diff_count[list] {
1342            if comp_count < 2 {
1343              combined_mvs[comp_count][list] = ref_diff_mvs[list][idx];
1344              comp_count += 1;
1345            }
1346          }
1347        }
1348
1349        if mv_stack.len() == 1 {
1350          let mv_cand = if combined_mvs[0][0].row == mv_stack[0].this_mv.row
1351            && combined_mvs[0][0].col == mv_stack[0].this_mv.col
1352            && combined_mvs[0][1].row == mv_stack[0].comp_mv.row
1353            && combined_mvs[0][1].col == mv_stack[0].comp_mv.col
1354          {
1355            CandidateMV {
1356              this_mv: combined_mvs[1][0],
1357              comp_mv: combined_mvs[1][1],
1358              weight: 2,
1359            }
1360          } else {
1361            CandidateMV {
1362              this_mv: combined_mvs[0][0],
1363              comp_mv: combined_mvs[0][1],
1364              weight: 2,
1365            }
1366          };
1367          mv_stack.push(mv_cand);
1368        } else {
1369          for idx in 0..2 {
1370            let mv_cand = CandidateMV {
1371              this_mv: combined_mvs[idx][0],
1372              comp_mv: combined_mvs[idx][1],
1373              weight: 2,
1374            };
1375            mv_stack.push(mv_cand);
1376          }
1377        }
1378
1379        assert!(mv_stack.len() == 2);
1380      }
1381    }
1382
1383    /* TODO: Handle single reference frame extension */
1384
1385    let frame_bo = PlaneBlockOffset(BlockOffset {
1386      x: self.bc.blocks.x() + bo.0.x,
1387      y: self.bc.blocks.y() + bo.0.y,
1388    });
1389    // clamp mvs
1390    for mv in mv_stack {
1391      let blk_w = bsize.width();
1392      let blk_h = bsize.height();
1393      let border_w = 128 + blk_w as isize * 8;
1394      let border_h = 128 + blk_h as isize * 8;
1395      let mvx_min =
1396        -(frame_bo.0.x as isize) * (8 * MI_SIZE) as isize - border_w;
1397      let mvx_max = ((self.bc.blocks.frame_cols() - frame_bo.0.x) as isize
1398        - (blk_w / MI_SIZE) as isize)
1399        * (8 * MI_SIZE) as isize
1400        + border_w;
1401      let mvy_min =
1402        -(frame_bo.0.y as isize) * (8 * MI_SIZE) as isize - border_h;
1403      let mvy_max = ((self.bc.blocks.frame_rows() - frame_bo.0.y) as isize
1404        - (blk_h / MI_SIZE) as isize)
1405        * (8 * MI_SIZE) as isize
1406        + border_h;
1407      mv.this_mv.row =
1408        (mv.this_mv.row as isize).clamp(mvy_min, mvy_max) as i16;
1409      mv.this_mv.col =
1410        (mv.this_mv.col as isize).clamp(mvx_min, mvx_max) as i16;
1411      mv.comp_mv.row =
1412        (mv.comp_mv.row as isize).clamp(mvy_min, mvy_max) as i16;
1413      mv.comp_mv.col =
1414        (mv.comp_mv.col as isize).clamp(mvx_min, mvx_max) as i16;
1415    }
1416
1417    mode_context
1418  }
1419
1420  /// # Panics
1421  ///
1422  /// - If the first ref frame is not set (`NONE_FRAME`)
1423  pub fn find_mvrefs<T: Pixel>(
1424    &self, bo: TileBlockOffset, ref_frames: [RefType; 2],
1425    mv_stack: &mut ArrayVec<CandidateMV, 9>, bsize: BlockSize,
1426    fi: &FrameInvariants<T>, is_compound: bool,
1427  ) -> usize {
1428    assert!(ref_frames[0] != NONE_FRAME);
1429    if ref_frames[0] != NONE_FRAME {
1430      // TODO: If ref_frames[0] != INTRA_FRAME, convert global mv to an mv;
1431      // otherwise, set the global mv ref to invalid.
1432    }
1433
1434    if ref_frames[0] != INTRA_FRAME {
1435      /* TODO: Set zeromv ref to the converted global motion vector */
1436    } else {
1437      /* TODO: Set the zeromv ref to 0 */
1438      return 0;
1439    }
1440
1441    self.setup_mvref_list(bo, ref_frames, mv_stack, bsize, fi, is_compound)
1442  }
1443
1444  pub fn fill_neighbours_ref_counts(&mut self, bo: TileBlockOffset) {
1445    let mut ref_counts = [0; INTER_REFS_PER_FRAME];
1446
1447    if bo.0.y > 0 {
1448      let above_b = self.bc.blocks.above_of(bo);
1449      if above_b.is_inter() {
1450        ref_counts[above_b.ref_frames[0].to_index()] += 1;
1451        if above_b.has_second_ref() {
1452          ref_counts[above_b.ref_frames[1].to_index()] += 1;
1453        }
1454      }
1455    }
1456
1457    if bo.0.x > 0 {
1458      let left_b = self.bc.blocks.left_of(bo);
1459      if left_b.is_inter() {
1460        ref_counts[left_b.ref_frames[0].to_index()] += 1;
1461        if left_b.has_second_ref() {
1462          ref_counts[left_b.ref_frames[1].to_index()] += 1;
1463        }
1464      }
1465    }
1466    self.bc.blocks[bo].neighbors_ref_counts = ref_counts;
1467  }
1468
1469  #[inline]
1470  pub const fn ref_count_ctx(counts0: u8, counts1: u8) -> usize {
1471    if counts0 < counts1 {
1472      0
1473    } else if counts0 == counts1 {
1474      1
1475    } else {
1476      2
1477    }
1478  }
1479
1480  #[inline]
1481  pub fn get_pred_ctx_brfarf2_or_arf(&self, bo: TileBlockOffset) -> usize {
1482    let ref_counts = self.bc.blocks[bo].neighbors_ref_counts;
1483
1484    let brfarf2_count = ref_counts[BWDREF_FRAME.to_index()]
1485      + ref_counts[ALTREF2_FRAME.to_index()];
1486    let arf_count = ref_counts[ALTREF_FRAME.to_index()];
1487
1488    ContextWriter::ref_count_ctx(brfarf2_count, arf_count)
1489  }
1490
1491  #[inline]
1492  pub fn get_pred_ctx_ll2_or_l3gld(&self, bo: TileBlockOffset) -> usize {
1493    let ref_counts = self.bc.blocks[bo].neighbors_ref_counts;
1494
1495    let l_l2_count =
1496      ref_counts[LAST_FRAME.to_index()] + ref_counts[LAST2_FRAME.to_index()];
1497    let l3_gold_count =
1498      ref_counts[LAST3_FRAME.to_index()] + ref_counts[GOLDEN_FRAME.to_index()];
1499
1500    ContextWriter::ref_count_ctx(l_l2_count, l3_gold_count)
1501  }
1502
1503  #[inline]
1504  pub fn get_pred_ctx_last_or_last2(&self, bo: TileBlockOffset) -> usize {
1505    let ref_counts = self.bc.blocks[bo].neighbors_ref_counts;
1506
1507    let l_count = ref_counts[LAST_FRAME.to_index()];
1508    let l2_count = ref_counts[LAST2_FRAME.to_index()];
1509
1510    ContextWriter::ref_count_ctx(l_count, l2_count)
1511  }
1512
1513  #[inline]
1514  pub fn get_pred_ctx_last3_or_gold(&self, bo: TileBlockOffset) -> usize {
1515    let ref_counts = self.bc.blocks[bo].neighbors_ref_counts;
1516
1517    let l3_count = ref_counts[LAST3_FRAME.to_index()];
1518    let gold_count = ref_counts[GOLDEN_FRAME.to_index()];
1519
1520    ContextWriter::ref_count_ctx(l3_count, gold_count)
1521  }
1522
1523  #[inline]
1524  pub fn get_pred_ctx_brf_or_arf2(&self, bo: TileBlockOffset) -> usize {
1525    let ref_counts = self.bc.blocks[bo].neighbors_ref_counts;
1526
1527    let brf_count = ref_counts[BWDREF_FRAME.to_index()];
1528    let arf2_count = ref_counts[ALTREF2_FRAME.to_index()];
1529
1530    ContextWriter::ref_count_ctx(brf_count, arf2_count)
1531  }
1532
1533  pub fn get_comp_mode_ctx(&self, bo: TileBlockOffset) -> usize {
1534    let avail_left = bo.0.x > 0;
1535    let avail_up = bo.0.y > 0;
1536    let (left0, left1) = if avail_left {
1537      let bo_left = bo.with_offset(-1, 0);
1538      let ref_frames = &self.bc.blocks[bo_left].ref_frames;
1539      (ref_frames[0], ref_frames[1])
1540    } else {
1541      (INTRA_FRAME, NONE_FRAME)
1542    };
1543    let (above0, above1) = if avail_up {
1544      let bo_up = bo.with_offset(0, -1);
1545      let ref_frames = &self.bc.blocks[bo_up].ref_frames;
1546      (ref_frames[0], ref_frames[1])
1547    } else {
1548      (INTRA_FRAME, NONE_FRAME)
1549    };
1550    let left_single = left1 == NONE_FRAME;
1551    let above_single = above1 == NONE_FRAME;
1552    let left_intra = left0 == INTRA_FRAME;
1553    let above_intra = above0 == INTRA_FRAME;
1554    let left_backward = left0.is_bwd_ref();
1555    let above_backward = above0.is_bwd_ref();
1556
1557    if avail_left && avail_up {
1558      if above_single && left_single {
1559        (above_backward ^ left_backward) as usize
1560      } else if above_single {
1561        2 + (above_backward || above_intra) as usize
1562      } else if left_single {
1563        2 + (left_backward || left_intra) as usize
1564      } else {
1565        4
1566      }
1567    } else if avail_up {
1568      if above_single {
1569        above_backward as usize
1570      } else {
1571        3
1572      }
1573    } else if avail_left {
1574      if left_single {
1575        left_backward as usize
1576      } else {
1577        3
1578      }
1579    } else {
1580      1
1581    }
1582  }
1583
1584  pub fn get_comp_ref_type_ctx(&self, bo: TileBlockOffset) -> usize {
1585    fn is_samedir_ref_pair(ref0: RefType, ref1: RefType) -> bool {
1586      (ref0.is_bwd_ref() && ref0 != NONE_FRAME)
1587        == (ref1.is_bwd_ref() && ref1 != NONE_FRAME)
1588    }
1589
1590    let avail_left = bo.0.x > 0;
1591    let avail_up = bo.0.y > 0;
1592    let (left0, left1) = if avail_left {
1593      let bo_left = bo.with_offset(-1, 0);
1594      let ref_frames = &self.bc.blocks[bo_left].ref_frames;
1595      (ref_frames[0], ref_frames[1])
1596    } else {
1597      (INTRA_FRAME, NONE_FRAME)
1598    };
1599    let (above0, above1) = if avail_up {
1600      let bo_up = bo.with_offset(0, -1);
1601      let ref_frames = &self.bc.blocks[bo_up].ref_frames;
1602      (ref_frames[0], ref_frames[1])
1603    } else {
1604      (INTRA_FRAME, NONE_FRAME)
1605    };
1606    let left_single = left1 == NONE_FRAME;
1607    let above_single = above1 == NONE_FRAME;
1608    let left_intra = left0 == INTRA_FRAME;
1609    let above_intra = above0 == INTRA_FRAME;
1610    let above_comp_inter = avail_up && !above_intra && !above_single;
1611    let left_comp_inter = avail_left && !left_intra && !left_single;
1612    let above_uni_comp =
1613      above_comp_inter && is_samedir_ref_pair(above0, above1);
1614    let left_uni_comp = left_comp_inter && is_samedir_ref_pair(left0, left1);
1615
1616    if avail_up && !above_intra && avail_left && !left_intra {
1617      let samedir = is_samedir_ref_pair(above0, left0) as usize;
1618
1619      if !above_comp_inter && !left_comp_inter {
1620        1 + 2 * samedir
1621      } else if !above_comp_inter {
1622        if !left_uni_comp {
1623          1
1624        } else {
1625          3 + samedir
1626        }
1627      } else if !left_comp_inter {
1628        if !above_uni_comp {
1629          1
1630        } else {
1631          3 + samedir
1632        }
1633      } else if !above_uni_comp && !left_uni_comp {
1634        0
1635      } else if !above_uni_comp || !left_uni_comp {
1636        2
1637      } else {
1638        3 + ((above0 == BWDREF_FRAME) == (left0 == BWDREF_FRAME)) as usize
1639      }
1640    } else if avail_up && avail_left {
1641      if above_comp_inter {
1642        1 + 2 * above_uni_comp as usize
1643      } else if left_comp_inter {
1644        1 + 2 * left_uni_comp as usize
1645      } else {
1646        2
1647      }
1648    } else if above_comp_inter {
1649      4 * above_uni_comp as usize
1650    } else if left_comp_inter {
1651      4 * left_uni_comp as usize
1652    } else {
1653      2
1654    }
1655  }
1656
1657  /// # Panics
1658  ///
1659  /// - If `mode` is not an inter mode
1660  pub fn write_compound_mode<W: Writer>(
1661    &mut self, w: &mut W, mode: PredictionMode, ctx: usize,
1662  ) {
1663    let newmv_ctx = ctx & NEWMV_CTX_MASK;
1664    let refmv_ctx = (ctx >> REFMV_OFFSET) & REFMV_CTX_MASK;
1665
1666    let ctx = if refmv_ctx < 2 {
1667      newmv_ctx.min(1)
1668    } else if refmv_ctx < 4 {
1669      (newmv_ctx + 1).min(4)
1670    } else {
1671      (newmv_ctx.max(1) + 3).min(7)
1672    };
1673
1674    assert!(mode >= PredictionMode::NEAREST_NEARESTMV);
1675    let val = match mode {
1676      PredictionMode::NEAREST_NEARESTMV => 0,
1677      PredictionMode::NEAR_NEAR0MV
1678      | PredictionMode::NEAR_NEAR1MV
1679      | PredictionMode::NEAR_NEAR2MV => 1,
1680      PredictionMode::NEAREST_NEWMV => 2,
1681      PredictionMode::NEW_NEARESTMV => 3,
1682      PredictionMode::NEAR_NEW0MV
1683      | PredictionMode::NEAR_NEW1MV
1684      | PredictionMode::NEAR_NEW2MV => 4,
1685      PredictionMode::NEW_NEAR0MV
1686      | PredictionMode::NEW_NEAR1MV
1687      | PredictionMode::NEW_NEAR2MV => 5,
1688      PredictionMode::GLOBAL_GLOBALMV => 6,
1689      PredictionMode::NEW_NEWMV => 7,
1690      _ => unreachable!(),
1691    };
1692    symbol_with_update!(self, w, val, &self.fc.compound_mode_cdf[ctx]);
1693  }
1694
1695  pub fn write_inter_mode<W: Writer>(
1696    &mut self, w: &mut W, mode: PredictionMode, ctx: usize,
1697  ) {
1698    use PredictionMode::{GLOBALMV, NEARESTMV, NEWMV};
1699    let newmv_ctx = ctx & NEWMV_CTX_MASK;
1700    let cdf = &self.fc.newmv_cdf[newmv_ctx];
1701    symbol_with_update!(self, w, (mode != NEWMV) as u32, cdf);
1702    if mode != NEWMV {
1703      let zeromv_ctx = (ctx >> GLOBALMV_OFFSET) & GLOBALMV_CTX_MASK;
1704      let cdf = &self.fc.zeromv_cdf[zeromv_ctx];
1705      symbol_with_update!(self, w, (mode != GLOBALMV) as u32, cdf);
1706      if mode != GLOBALMV {
1707        let refmv_ctx = (ctx >> REFMV_OFFSET) & REFMV_CTX_MASK;
1708        let cdf = &self.fc.refmv_cdf[refmv_ctx];
1709        symbol_with_update!(self, w, (mode != NEARESTMV) as u32, cdf);
1710      }
1711    }
1712  }
1713
1714  #[inline]
1715  pub fn write_drl_mode<W: Writer>(
1716    &mut self, w: &mut W, drl_mode: bool, ctx: usize,
1717  ) {
1718    let cdf = &self.fc.drl_cdfs[ctx];
1719    symbol_with_update!(self, w, drl_mode as u32, cdf);
1720  }
1721
1722  /// # Panics
1723  ///
1724  /// - If the MV is invalid
1725  pub fn write_mv<W: Writer>(
1726    &mut self, w: &mut W, mv: MotionVector, ref_mv: MotionVector,
1727    mv_precision: MvSubpelPrecision,
1728  ) {
1729    // <https://aomediacodec.github.io/av1-spec/#assign-mv-semantics>
1730    assert!(mv.is_valid());
1731
1732    let diff =
1733      MotionVector { row: mv.row - ref_mv.row, col: mv.col - ref_mv.col };
1734    let j: MvJointType = av1_get_mv_joint(diff);
1735
1736    let cdf = &self.fc.nmv_context.joints_cdf;
1737    symbol_with_update!(self, w, j as u32, cdf);
1738
1739    if mv_joint_vertical(j) {
1740      self.encode_mv_component(w, diff.row as i32, 0, mv_precision);
1741    }
1742    if mv_joint_horizontal(j) {
1743      self.encode_mv_component(w, diff.col as i32, 1, mv_precision);
1744    }
1745  }
1746
1747  pub fn write_block_deblock_deltas<W: Writer>(
1748    &mut self, w: &mut W, bo: TileBlockOffset, multi: bool, planes: usize,
1749  ) {
1750    let block = &self.bc.blocks[bo];
1751    let deltas_count = if multi { FRAME_LF_COUNT + planes - 3 } else { 1 };
1752    let deltas = &block.deblock_deltas[..deltas_count];
1753
1754    for (i, &delta) in deltas.iter().enumerate() {
1755      let abs = delta.unsigned_abs() as u32;
1756      let cdf = if multi {
1757        &self.fc.deblock_delta_multi_cdf[i]
1758      } else {
1759        &self.fc.deblock_delta_cdf
1760      };
1761
1762      symbol_with_update!(self, w, cmp::min(abs, DELTA_LF_SMALL), cdf);
1763
1764      if abs >= DELTA_LF_SMALL {
1765        let bits = msb(abs as i32 - 1) as u32;
1766        w.literal(3, bits - 1);
1767        w.literal(bits as u8, abs - (1 << bits) - 1);
1768      }
1769      if abs > 0 {
1770        w.bool(delta < 0, 16384);
1771      }
1772    }
1773  }
1774
1775  pub fn write_is_inter<W: Writer>(
1776    &mut self, w: &mut W, bo: TileBlockOffset, is_inter: bool,
1777  ) {
1778    let ctx = self.bc.intra_inter_context(bo);
1779    let cdf = &self.fc.intra_inter_cdfs[ctx];
1780    symbol_with_update!(self, w, is_inter as u32, cdf);
1781  }
1782
1783  pub fn write_coeffs_lv_map<T: Coefficient, W: Writer>(
1784    &mut self, w: &mut W, plane: usize, bo: TileBlockOffset, coeffs_in: &[T],
1785    eob: u16, pred_mode: PredictionMode, tx_size: TxSize, tx_type: TxType,
1786    plane_bsize: BlockSize, xdec: usize, ydec: usize,
1787    use_reduced_tx_set: bool, frame_clipped_txw: usize,
1788    frame_clipped_txh: usize,
1789  ) -> bool {
1790    debug_assert!(frame_clipped_txw != 0);
1791    debug_assert!(frame_clipped_txh != 0);
1792
1793    let is_inter = pred_mode >= PredictionMode::NEARESTMV;
1794
1795    // Note: Both intra and inter mode uses inter scan order. Surprised?
1796    let scan: &[u16] = &av1_scan_orders[tx_size as usize][tx_type as usize]
1797      .scan[..usize::from(eob)];
1798    let height = av1_get_coded_tx_size(tx_size).height();
1799
1800    // Create a slice with coeffs in scan order
1801    let mut coeffs_storage: Aligned<ArrayVec<T, { 32 * 32 }>> =
1802      Aligned::new(ArrayVec::new());
1803    let coeffs = &mut coeffs_storage.data;
1804    coeffs.extend(scan.iter().map(|&scan_idx| coeffs_in[scan_idx as usize]));
1805
1806    let cul_level: u32 = coeffs.iter().map(|c| u32::cast_from(c.abs())).sum();
1807
1808    let txs_ctx = Self::get_txsize_entropy_ctx(tx_size);
1809    let txb_ctx = self.bc.get_txb_ctx(
1810      plane_bsize,
1811      tx_size,
1812      plane,
1813      bo,
1814      xdec,
1815      ydec,
1816      frame_clipped_txw,
1817      frame_clipped_txh,
1818    );
1819
1820    {
1821      let cdf = &self.fc.txb_skip_cdf[txs_ctx][txb_ctx.txb_skip_ctx];
1822      symbol_with_update!(self, w, (eob == 0) as u32, cdf);
1823    }
1824
1825    if eob == 0 {
1826      self.bc.set_coeff_context(plane, bo, tx_size, xdec, ydec, 0);
1827      return false;
1828    }
1829
1830    let mut levels_buf = [0u8; TX_PAD_2D];
1831    let levels: &mut [u8] =
1832      &mut levels_buf[TX_PAD_TOP * (height + TX_PAD_HOR)..];
1833
1834    self.txb_init_levels(coeffs_in, height, levels, height + TX_PAD_HOR);
1835
1836    let tx_class = tx_type_to_class[tx_type as usize];
1837    let plane_type = usize::from(plane != 0);
1838
1839    // Signal tx_type for luma plane only
1840    if plane == 0 {
1841      self.write_tx_type(
1842        w,
1843        tx_size,
1844        tx_type,
1845        pred_mode,
1846        is_inter,
1847        use_reduced_tx_set,
1848      );
1849    }
1850
1851    self.encode_eob(eob, tx_size, tx_class, txs_ctx, plane_type, w);
1852    self.encode_coeffs(
1853      coeffs, levels, scan, eob, tx_size, tx_class, txs_ctx, plane_type, w,
1854    );
1855    let cul_level =
1856      self.encode_coeff_signs(coeffs, w, plane_type, txb_ctx, cul_level);
1857    self.bc.set_coeff_context(plane, bo, tx_size, xdec, ydec, cul_level as u8);
1858    true
1859  }
1860
1861  fn encode_eob<W: Writer>(
1862    &mut self, eob: u16, tx_size: TxSize, tx_class: TxClass, txs_ctx: usize,
1863    plane_type: usize, w: &mut W,
1864  ) {
1865    let (eob_pt, eob_extra) = Self::get_eob_pos_token(eob);
1866    let eob_multi_size: usize = tx_size.area_log2() - 4;
1867    let eob_multi_ctx: usize = usize::from(tx_class != TX_CLASS_2D);
1868
1869    match eob_multi_size {
1870      0 => {
1871        let cdf = &self.fc.eob_flag_cdf16[plane_type][eob_multi_ctx];
1872        symbol_with_update!(self, w, eob_pt - 1, cdf);
1873      }
1874      1 => {
1875        let cdf = &self.fc.eob_flag_cdf32[plane_type][eob_multi_ctx];
1876        symbol_with_update!(self, w, eob_pt - 1, cdf);
1877      }
1878      2 => {
1879        let cdf = &self.fc.eob_flag_cdf64[plane_type][eob_multi_ctx];
1880        symbol_with_update!(self, w, eob_pt - 1, cdf);
1881      }
1882      3 => {
1883        let cdf = &self.fc.eob_flag_cdf128[plane_type][eob_multi_ctx];
1884        symbol_with_update!(self, w, eob_pt - 1, cdf);
1885      }
1886      4 => {
1887        let cdf = &self.fc.eob_flag_cdf256[plane_type][eob_multi_ctx];
1888        symbol_with_update!(self, w, eob_pt - 1, cdf);
1889      }
1890      5 => {
1891        let cdf = &self.fc.eob_flag_cdf512[plane_type][eob_multi_ctx];
1892        symbol_with_update!(self, w, eob_pt - 1, cdf);
1893      }
1894      _ => {
1895        let cdf = &self.fc.eob_flag_cdf1024[plane_type][eob_multi_ctx];
1896        symbol_with_update!(self, w, eob_pt - 1, cdf);
1897      }
1898    }
1899
1900    let eob_offset_bits = k_eob_offset_bits[eob_pt as usize];
1901
1902    if eob_offset_bits > 0 {
1903      let mut eob_shift = eob_offset_bits - 1;
1904      let mut bit: u32 = u32::from((eob_extra & (1 << eob_shift)) != 0);
1905      let cdf =
1906        &self.fc.eob_extra_cdf[txs_ctx][plane_type][(eob_pt - 3) as usize];
1907      symbol_with_update!(self, w, bit, cdf);
1908      for i in 1..eob_offset_bits {
1909        eob_shift = eob_offset_bits - 1 - i;
1910        bit = u32::from((eob_extra & (1 << eob_shift)) != 0);
1911        w.bit(bit as u16);
1912      }
1913    }
1914  }
1915
1916  fn encode_coeffs<T: Coefficient, W: Writer>(
1917    &mut self, coeffs: &[T], levels: &mut [u8], scan: &[u16], eob: u16,
1918    tx_size: TxSize, tx_class: TxClass, txs_ctx: usize, plane_type: usize,
1919    w: &mut W,
1920  ) {
1921    let mut coeff_contexts =
1922      Aligned::<[MaybeUninit<i8>; MAX_CODED_TX_SQUARE]>::uninit_array();
1923
1924    // get_nz_map_contexts sets coeff_contexts contiguously as a parallel array for scan, not in scan order
1925    let coeff_contexts = self.get_nz_map_contexts(
1926      levels,
1927      scan,
1928      eob,
1929      tx_size,
1930      tx_class,
1931      &mut coeff_contexts.data,
1932    );
1933
1934    let bhl = Self::get_txb_bhl(tx_size);
1935
1936    let scan_with_ctx =
1937      scan.iter().copied().zip(coeff_contexts.iter().copied());
1938    for (c, ((pos, coeff_ctx), v)) in
1939      scan_with_ctx.zip(coeffs.iter().copied()).enumerate().rev()
1940    {
1941      let pos = pos as usize;
1942      let coeff_ctx = coeff_ctx as usize;
1943      let level = v.abs();
1944
1945      if c == usize::from(eob) - 1 {
1946        symbol_with_update!(
1947          self,
1948          w,
1949          cmp::min(u32::cast_from(level), 3) - 1,
1950          &self.fc.coeff_base_eob_cdf[txs_ctx][plane_type][coeff_ctx]
1951        );
1952      } else {
1953        symbol_with_update!(
1954          self,
1955          w,
1956          cmp::min(u32::cast_from(level), 3),
1957          &self.fc.coeff_base_cdf[txs_ctx][plane_type][coeff_ctx]
1958        );
1959      }
1960
1961      if level > T::cast_from(NUM_BASE_LEVELS) {
1962        let base_range = level - T::cast_from(1 + NUM_BASE_LEVELS);
1963        let br_ctx = Self::get_br_ctx(levels, pos, bhl, tx_class);
1964        let mut idx: T = T::cast_from(0);
1965
1966        loop {
1967          if idx >= T::cast_from(COEFF_BASE_RANGE) {
1968            break;
1969          }
1970          let k = cmp::min(base_range - idx, T::cast_from(BR_CDF_SIZE - 1));
1971          let cdf = &self.fc.coeff_br_cdf
1972            [txs_ctx.min(TxSize::TX_32X32 as usize)][plane_type][br_ctx];
1973          symbol_with_update!(self, w, u32::cast_from(k), cdf);
1974          if k < T::cast_from(BR_CDF_SIZE - 1) {
1975            break;
1976          }
1977          idx += T::cast_from(BR_CDF_SIZE - 1);
1978        }
1979      }
1980    }
1981  }
1982
1983  fn encode_coeff_signs<T: Coefficient, W: Writer>(
1984    &mut self, coeffs: &[T], w: &mut W, plane_type: usize, txb_ctx: TXB_CTX,
1985    orig_cul_level: u32,
1986  ) -> u32 {
1987    // Loop to code all signs in the transform block,
1988    // starting with the sign of DC (if applicable)
1989    for (c, &v) in coeffs.iter().enumerate() {
1990      if v == T::cast_from(0) {
1991        continue;
1992      }
1993
1994      let level = v.abs();
1995      let sign = u32::from(v < T::cast_from(0));
1996      if c == 0 {
1997        let cdf = &self.fc.dc_sign_cdf[plane_type][txb_ctx.dc_sign_ctx];
1998        symbol_with_update!(self, w, sign, cdf);
1999      } else {
2000        w.bit(sign as u16);
2001      }
2002      // save extra golomb codes for separate loop
2003      if level > T::cast_from(COEFF_BASE_RANGE + NUM_BASE_LEVELS) {
2004        w.write_golomb(u32::cast_from(
2005          level - T::cast_from(COEFF_BASE_RANGE + NUM_BASE_LEVELS + 1),
2006        ));
2007      }
2008    }
2009
2010    let mut new_cul_level =
2011      cmp::min(COEFF_CONTEXT_MASK as u32, orig_cul_level);
2012
2013    BlockContext::set_dc_sign(&mut new_cul_level, i32::cast_from(coeffs[0]));
2014
2015    new_cul_level
2016  }
2017}