1use 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#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
41pub struct BlockOffset {
42 pub x: usize,
43 pub y: usize,
44}
45
46#[derive(Clone, Copy, Debug, PartialEq, Eq)]
49pub struct PlaneBlockOffset(pub BlockOffset);
50
51#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
54pub struct TileBlockOffset(pub BlockOffset);
55
56impl BlockOffset {
57 #[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 #[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 #[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 #[inline]
103 pub const fn sb_offset(self) -> PlaneSuperBlockOffset {
104 PlaneSuperBlockOffset(self.0.sb_offset())
105 }
106
107 #[inline]
109 pub const fn plane_offset(self, plane: &PlaneConfig) -> PlaneOffset {
110 self.0.plane_offset(plane)
111 }
112
113 #[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 #[inline]
135 pub const fn sb_offset(self) -> TileSuperBlockOffset {
136 TileSuperBlockOffset(self.0.sb_offset())
137 }
138
139 #[inline]
141 pub const fn plane_offset(self, plane: &PlaneConfig) -> PlaneOffset {
142 self.0.plane_offset(plane)
143 }
144
145 #[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 pub neighbors_ref_counts: [u8; INTER_REFS_PER_FRAME],
173 pub cdef_index: u8,
174 pub bsize: BlockSize,
175 pub n4_w: u8, pub n4_h: u8, pub txsize: TxSize,
178 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_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 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 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 if plane == 0 {
478 if plane_bsize == tx_size.block_size() {
479 txb_ctx.txb_skip_ctx = 0;
480 } else {
481 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 padding: [u16; 5],
544}
545
546#[derive(Clone, Copy)]
547#[repr(C)]
548pub struct NMVContext {
549 pub joints_cdf: [u16; MV_JOINTS],
550 padding: [u16; 12],
552 pub comps: [NMVComponent; 2],
553}
554
555pub 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
662impl 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 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!(); }
778
779 let (ctx_luma, ctx_chroma) = (0, 0); 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 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 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 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 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 let mut far_newmv_count: usize = 0; 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 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 mv_stack.sort_by(|a, b| b.weight.cmp(&a.weight));
1291
1292 if mv_stack.len() < 2 {
1293 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 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 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 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 }
1433
1434 if ref_frames[0] != INTRA_FRAME {
1435 } else {
1437 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 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 pub fn write_mv<W: Writer>(
1726 &mut self, w: &mut W, mv: MotionVector, ref_mv: MotionVector,
1727 mv_precision: MvSubpelPrecision,
1728 ) {
1729 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 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 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 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 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 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 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}