1#![forbid(unsafe_code)]
29#![allow(dead_code)]
30#![allow(clippy::doc_markdown)]
31#![allow(clippy::too_many_arguments)]
32#![allow(clippy::cast_possible_truncation)]
33#![allow(clippy::cast_sign_loss)]
34#![allow(clippy::cast_possible_wrap)]
35#![allow(clippy::similar_names)]
36#![allow(clippy::module_name_repetitions)]
37
38use super::coefficients::{
39 dequantize_block, get_dequant_shift, CoeffBuffer, CoeffContext, CoeffStats, EobContext, EobPt,
40 LevelContext, ScanOrderCache,
41};
42use super::entropy::SymbolReader;
43use super::entropy_tables::CdfContext;
44use super::quantization::QuantizationParams;
45use super::transform::{TxSize, TxType};
46use crate::error::CodecResult;
47
48pub const COEFF_BASE_MAX: u32 = 3;
54
55pub const BR_CDF_SIZE: usize = 4;
57
58pub const MAX_BR_PARAM: u8 = 5;
60
61pub const EOB_OFFSET_BITS: [u8; 12] = [0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
63
64pub const COEFF_SKIP_THRESHOLD: u16 = 256;
66
67#[derive(Debug)]
73pub struct CoeffDecoder {
74 reader: SymbolReader,
76 cdf_context: CdfContext,
78 scan_cache: ScanOrderCache,
80 quant_params: QuantizationParams,
82 bit_depth: u8,
84}
85
86impl CoeffDecoder {
87 pub fn new(data: Vec<u8>, quant_params: QuantizationParams, bit_depth: u8) -> Self {
89 Self {
90 reader: SymbolReader::new(data),
91 cdf_context: CdfContext::new(),
92 scan_cache: ScanOrderCache::new(),
93 quant_params,
94 bit_depth,
95 }
96 }
97
98 pub fn decode_coefficients(
100 &mut self,
101 tx_size: TxSize,
102 tx_type: TxType,
103 plane: u8,
104 skip: bool,
105 ) -> CodecResult<CoeffBuffer> {
106 let mut ctx = CoeffContext::new(tx_size, tx_type, plane);
107
108 if skip {
109 return Ok(CoeffBuffer::from_tx_size(tx_size));
111 }
112
113 ctx.eob = self.decode_eob(tx_size, plane)?;
115
116 if ctx.eob == 0 {
117 return Ok(CoeffBuffer::from_tx_size(tx_size));
119 }
120
121 let scan = self.scan_cache.get(tx_size, ctx.tx_class()).to_vec();
123
124 self.decode_coeff_levels(&mut ctx, &scan)?;
126
127 self.decode_signs(&mut ctx, &scan)?;
129
130 self.dequantize_coefficients(&mut ctx, plane)?;
132
133 let mut buffer = CoeffBuffer::from_tx_size(tx_size);
135 self.reorder_coefficients(&ctx, &scan, &mut buffer)?;
136
137 Ok(buffer)
138 }
139
140 fn decode_eob(&mut self, tx_size: TxSize, plane: u8) -> CodecResult<u16> {
148 let _eob_ctx = EobContext::new(tx_size);
149
150 let ctx = (tx_size as usize * 3) + (plane as usize);
152 let eob_multi_cdf = self.cdf_context.get_eob_multi_cdf_mut(ctx);
153 let eob_multi_symbol = self.reader.read_symbol(eob_multi_cdf);
154
155 self.finalize_eob_from_symbol(eob_multi_symbol)
156 }
157
158 fn finalize_eob_from_symbol(&mut self, symbol: usize) -> CodecResult<u16> {
172 if symbol == 0 {
173 return Ok(0); }
175
176 let eob_pt = EobPt::from_symbol(symbol)?;
181 let extra_bits = eob_pt.extra_bits();
182
183 let eob_extra = if extra_bits > 0 {
184 self.reader.read_literal(extra_bits)
185 } else {
186 0
187 };
188
189 let eob_multi = symbol as u8;
190 let eob = EobContext::compute_eob(eob_multi, eob_extra as u16);
191 Ok(eob)
192 }
193
194 fn decode_coeff_levels(&mut self, ctx: &mut CoeffContext, scan: &[u16]) -> CodecResult<()> {
196 let eob = ctx.eob as usize;
197
198 for scan_idx in (0..eob).rev() {
200 let pos = scan[scan_idx] as usize;
201 let level = self.decode_coeff_level(ctx, pos, scan_idx == eob - 1)?;
202 ctx.levels[pos] = level as i32;
203 }
204
205 Ok(())
206 }
207
208 fn decode_coeff_level(
210 &mut self,
211 ctx: &CoeffContext,
212 pos: usize,
213 is_eob: bool,
214 ) -> CodecResult<u32> {
215 let level_ctx = ctx.compute_level_context(pos);
216
217 let base_level = if is_eob {
219 1 + self.decode_coeff_base_eob(&level_ctx, ctx.plane)?
221 } else {
222 self.decode_coeff_base(&level_ctx, ctx.plane)?
223 };
224
225 if base_level >= COEFF_BASE_MAX {
226 let range = self.decode_coeff_base_range(&level_ctx, ctx.plane)?;
228 Ok(base_level + range)
229 } else {
230 Ok(base_level)
231 }
232 }
233
234 fn decode_coeff_base(&mut self, level_ctx: &LevelContext, _plane: u8) -> CodecResult<u32> {
236 let context = level_ctx.context() as usize;
237 let cdf = self.cdf_context.get_coeff_base_cdf_mut(context);
238 Ok(self.reader.read_symbol(cdf) as u32)
239 }
240
241 fn decode_coeff_base_eob(&mut self, level_ctx: &LevelContext, _plane: u8) -> CodecResult<u32> {
243 let context = level_ctx.context() as usize;
244 let cdf = self.cdf_context.get_coeff_base_eob_cdf_mut(context);
245 Ok(self.reader.read_symbol(cdf) as u32)
246 }
247
248 fn decode_coeff_base_range(
250 &mut self,
251 level_ctx: &LevelContext,
252 _plane: u8,
253 ) -> CodecResult<u32> {
254 let context = level_ctx.mag_context() as usize;
255 let mut total_range = 0u32;
256
257 for _level in 0..5 {
259 let br_cdf = self.cdf_context.get_coeff_br_cdf_mut(context);
260 let br_symbol = self.reader.read_symbol(br_cdf);
261
262 if br_symbol < BR_CDF_SIZE - 1 {
263 total_range += br_symbol as u32;
264 break;
265 } else {
266 total_range += (BR_CDF_SIZE - 1) as u32;
267 }
268 }
269
270 Ok(total_range)
271 }
272
273 fn decode_signs(&mut self, ctx: &mut CoeffContext, scan: &[u16]) -> CodecResult<()> {
275 let eob = ctx.eob as usize;
276
277 if ctx.levels[0] != 0 {
279 let dc_sign_ctx = ctx.dc_sign_context();
280 let cdf_slice = self.cdf_context.get_dc_sign_cdf_mut(dc_sign_ctx as usize);
281 if cdf_slice.len() >= 3 {
284 let mut cdf_array = [cdf_slice[0], cdf_slice[1], cdf_slice[2]];
285 let sign = self.reader.read_bool(&mut cdf_array);
286 cdf_slice[0] = cdf_array[0];
288 cdf_slice[1] = cdf_array[1];
289 cdf_slice[2] = cdf_array[2];
290 ctx.signs[0] = sign;
291 if sign {
292 ctx.levels[0] = -ctx.levels[0];
293 }
294 }
295 }
296
297 for scan_idx in 1..eob {
299 let pos = scan[scan_idx] as usize;
300 if ctx.levels[pos] != 0 {
301 let sign = self.reader.read_bool_eq();
303 ctx.signs[pos] = sign;
304 if sign {
305 ctx.levels[pos] = -ctx.levels[pos];
306 }
307 }
308 }
309
310 Ok(())
311 }
312
313 fn dequantize_coefficients(&mut self, ctx: &mut CoeffContext, plane: u8) -> CodecResult<()> {
315 let dc_dequant = self
317 .quant_params
318 .get_dc_quant(plane as usize, self.bit_depth) as i16;
319 let ac_dequant = self
320 .quant_params
321 .get_ac_quant(plane as usize, self.bit_depth) as i16;
322 let shift = get_dequant_shift(self.bit_depth);
323
324 dequantize_block(&mut ctx.levels, dc_dequant, ac_dequant, shift);
325
326 Ok(())
327 }
328
329 fn reorder_coefficients(
331 &self,
332 ctx: &CoeffContext,
333 scan: &[u16],
334 buffer: &mut CoeffBuffer,
335 ) -> CodecResult<()> {
336 let eob = ctx.eob as usize;
337
338 for scan_idx in 0..eob {
339 let pos = scan[scan_idx] as usize;
340 if pos < ctx.levels.len() {
341 let level = ctx.levels[pos];
342 let (row, col) = ctx.get_scan_position(pos);
343 buffer.set(row as usize, col as usize, level);
344 }
345 }
346
347 Ok(())
348 }
349
350 pub fn has_more_data(&self) -> bool {
352 self.reader.has_more_data()
353 }
354
355 pub fn position(&self) -> usize {
357 self.reader.position()
358 }
359}
360
361#[derive(Debug)]
367pub struct CoeffEncoder {
368 writer: super::entropy::SymbolWriter,
370 cdf_context: CdfContext,
372 scan_cache: ScanOrderCache,
374}
375
376impl CoeffEncoder {
377 #[must_use]
379 pub fn new() -> Self {
380 Self {
381 writer: super::entropy::SymbolWriter::new(),
382 cdf_context: CdfContext::new(),
383 scan_cache: ScanOrderCache::new(),
384 }
385 }
386
387 pub fn encode_coefficients(
389 &mut self,
390 buffer: &CoeffBuffer,
391 tx_size: TxSize,
392 tx_type: TxType,
393 plane: u8,
394 ) -> CodecResult<()> {
395 let tx_class = tx_type.tx_class();
396 let scan = self.scan_cache.get(tx_size, tx_class).to_vec();
397
398 let eob = self.find_eob(buffer, &scan);
400
401 self.encode_eob(eob, tx_size, plane)?;
403
404 if eob == 0 {
405 return Ok(());
406 }
407
408 let mut levels = vec![0i32; eob as usize];
410 buffer.copy_to_scan(&mut levels, &scan[..eob as usize]);
411
412 self.encode_levels(&levels, &scan, plane)?;
414
415 self.encode_signs(&levels, &scan)?;
417
418 Ok(())
419 }
420
421 fn find_eob(&self, buffer: &CoeffBuffer, scan: &[u16]) -> u16 {
423 for (i, &pos) in scan.iter().enumerate().rev() {
424 let (row, col) = self.pos_to_rowcol(pos as usize, buffer);
425 if buffer.get(row, col) != 0 {
426 return (i + 1) as u16;
427 }
428 }
429 0
430 }
431
432 fn pos_to_rowcol(&self, pos: usize, buffer: &CoeffBuffer) -> (usize, usize) {
434 let width = buffer.width();
435 (pos / width, pos % width)
436 }
437
438 fn encode_eob(&mut self, eob: u16, tx_size: TxSize, plane: u8) -> CodecResult<()> {
440 let ctx = (tx_size as usize * 3) + (plane as usize);
441
442 if eob == 0 {
443 let cdf = self.cdf_context.get_eob_multi_cdf_mut(ctx);
444 self.writer.write_symbol(0, cdf);
445 return Ok(());
446 }
447
448 let eob_pt = EobPt::from_eob(eob);
449 let cdf = self.cdf_context.get_eob_multi_cdf_mut(ctx);
450 self.writer.write_symbol(eob_pt as usize, cdf);
451
452 let extra_bits = eob_pt.extra_bits();
454 if extra_bits > 0 {
455 let offset = eob - eob_pt.base_eob();
456 self.writer.write_literal(offset as u32, extra_bits);
457 }
458
459 Ok(())
460 }
461
462 fn encode_levels(&mut self, levels: &[i32], scan: &[u16], plane: u8) -> CodecResult<()> {
464 let mut ctx = LevelContext::new();
465
466 for (scan_idx, &_pos) in scan.iter().enumerate().rev() {
467 let level = levels[scan_idx].unsigned_abs();
468
469 let base_level = level.min(COEFF_BASE_MAX);
470 let is_eob = scan_idx == levels.len() - 1;
471
472 if is_eob {
473 let cdf = self
474 .cdf_context
475 .get_coeff_base_eob_cdf_mut(ctx.context() as usize);
476 self.writer.write_symbol((base_level - 1) as usize, cdf);
477 } else {
478 let cdf = self
479 .cdf_context
480 .get_coeff_base_cdf_mut(ctx.context() as usize);
481 self.writer.write_symbol(base_level as usize, cdf);
482 }
483
484 if level >= COEFF_BASE_MAX {
485 self.encode_base_range(level - COEFF_BASE_MAX, &ctx, plane)?;
486 }
487
488 ctx.mag += level;
490 if level > 0 {
491 ctx.count += 1;
492 }
493 }
494
495 Ok(())
496 }
497
498 fn encode_base_range(&mut self, range: u32, ctx: &LevelContext, _plane: u8) -> CodecResult<()> {
500 let mut remaining = range;
501 let mag_ctx = ctx.mag_context() as usize;
502
503 for _level in 0..5 {
504 if remaining == 0 {
505 break;
506 }
507
508 let symbol = remaining.min((BR_CDF_SIZE - 1) as u32) as usize;
509 let cdf = self.cdf_context.get_coeff_br_cdf_mut(mag_ctx);
510 self.writer.write_symbol(symbol, cdf);
511
512 if symbol < BR_CDF_SIZE - 1 {
513 break;
514 }
515
516 remaining -= (BR_CDF_SIZE - 1) as u32;
517 }
518
519 Ok(())
520 }
521
522 fn encode_signs(&mut self, levels: &[i32], scan: &[u16]) -> CodecResult<()> {
524 if !levels.is_empty() && levels[0] != 0 {
526 let dc_ctx = 1; let cdf_slice = self.cdf_context.get_dc_sign_cdf_mut(dc_ctx);
528 if cdf_slice.len() >= 3 {
529 let mut cdf = [cdf_slice[0], cdf_slice[1], cdf_slice[2]];
530 self.writer.write_bool(levels[0] < 0, &mut cdf);
531 cdf_slice[0] = cdf[0];
533 cdf_slice[1] = cdf[1];
534 cdf_slice[2] = cdf[2];
535 }
536 }
537
538 for (idx, &_pos) in scan.iter().enumerate().skip(1) {
540 if idx < levels.len() && levels[idx] != 0 {
541 let mut cdf = [16384u16, 32768, 0];
543 self.writer.write_bool(levels[idx] < 0, &mut cdf);
544 }
545 }
546
547 Ok(())
548 }
549
550 #[must_use]
552 pub fn finish(self) -> Vec<u8> {
553 self.writer.finish()
554 }
555}
556
557impl Default for CoeffEncoder {
558 fn default() -> Self {
559 Self::new()
560 }
561}
562
563pub struct BatchedCoeffDecoder {
569 decoder: CoeffDecoder,
571 blocks: Vec<CoeffBuffer>,
573}
574
575impl BatchedCoeffDecoder {
576 pub fn new(data: Vec<u8>, quant_params: QuantizationParams, bit_depth: u8) -> Self {
578 Self {
579 decoder: CoeffDecoder::new(data, quant_params, bit_depth),
580 blocks: Vec::new(),
581 }
582 }
583
584 pub fn decode_blocks(
586 &mut self,
587 specs: &[(TxSize, TxType, u8, bool)],
588 ) -> CodecResult<Vec<CoeffBuffer>> {
589 self.blocks.clear();
590
591 for &(tx_size, tx_type, plane, skip) in specs {
592 let buffer = self
593 .decoder
594 .decode_coefficients(tx_size, tx_type, plane, skip)?;
595 self.blocks.push(buffer);
596 }
597
598 Ok(std::mem::take(&mut self.blocks))
599 }
600
601 #[must_use]
603 pub fn get_statistics(&self) -> Vec<CoeffStats> {
604 self.blocks
605 .iter()
606 .map(|b| CoeffStats::from_coeffs(b.as_slice()))
607 .collect()
608 }
609}
610
611#[derive(Clone, Debug, Default)]
617pub struct CoeffAnalysis {
618 pub total_coeffs: u64,
620 pub zero_count: u64,
622 pub nonzero_count: u64,
624 pub dc_sum: i64,
626 pub ac_sum: i64,
628 pub max_abs: u32,
630}
631
632impl CoeffAnalysis {
633 #[must_use]
635 pub const fn new() -> Self {
636 Self {
637 total_coeffs: 0,
638 zero_count: 0,
639 nonzero_count: 0,
640 dc_sum: 0,
641 ac_sum: 0,
642 max_abs: 0,
643 }
644 }
645
646 pub fn analyze(&mut self, buffer: &CoeffBuffer) {
648 let coeffs = buffer.as_slice();
649 self.total_coeffs += coeffs.len() as u64;
650
651 if !coeffs.is_empty() {
652 self.dc_sum += i64::from(coeffs[0]);
653 }
654
655 for (i, &coeff) in coeffs.iter().enumerate() {
656 let abs_val = coeff.unsigned_abs();
657
658 if coeff == 0 {
659 self.zero_count += 1;
660 } else {
661 self.nonzero_count += 1;
662 self.max_abs = self.max_abs.max(abs_val);
663
664 if i > 0 {
665 self.ac_sum += i64::from(coeff);
666 }
667 }
668 }
669 }
670
671 #[must_use]
673 pub fn sparsity(&self) -> f64 {
674 if self.total_coeffs > 0 {
675 (self.zero_count as f64 / self.total_coeffs as f64) * 100.0
676 } else {
677 0.0
678 }
679 }
680
681 #[must_use]
683 pub fn avg_dc(&self) -> f64 {
684 if self.total_coeffs > 0 {
685 self.dc_sum as f64 / self.total_coeffs as f64
686 } else {
687 0.0
688 }
689 }
690}
691
692#[cfg(test)]
697mod tests {
698 use super::*;
699
700 fn create_test_quant_params() -> QuantizationParams {
701 QuantizationParams {
702 base_q_idx: 100,
703 delta_q_y_dc: 0,
704 delta_q_u_dc: 0,
705 delta_q_v_dc: 0,
706 delta_q_u_ac: 0,
707 delta_q_v_ac: 0,
708 using_qmatrix: false,
709 qm_y: 15,
710 qm_u: 15,
711 qm_v: 15,
712 delta_q_present: false,
713 delta_q_res: 0,
714 }
715 }
716
717 #[test]
718 fn test_coeff_decoder_creation() {
719 let data = vec![0u8; 128];
720 let quant = create_test_quant_params();
721 let decoder = CoeffDecoder::new(data, quant, 8);
722 assert!(decoder.has_more_data());
723 }
724
725 #[test]
726 fn test_coeff_encoder_creation() {
727 let encoder = CoeffEncoder::new();
728 let output = encoder.finish();
729 assert!(!output.is_empty() || output.is_empty());
730 }
731
732 #[test]
733 fn test_batched_decoder() {
734 let data = vec![0u8; 256];
735 let quant = create_test_quant_params();
736 let mut decoder = BatchedCoeffDecoder::new(data, quant, 8);
737
738 let specs = vec![
739 (TxSize::Tx4x4, TxType::DctDct, 0, false),
740 (TxSize::Tx8x8, TxType::DctDct, 0, false),
741 ];
742
743 let _ = decoder.decode_blocks(&specs);
745 }
746
747 #[test]
748 fn test_coeff_analysis() {
749 let mut analysis = CoeffAnalysis::new();
750 let mut buffer = CoeffBuffer::new(4, 4);
751
752 buffer.set(0, 0, 100); buffer.set(1, 1, 50); buffer.set(2, 2, -30); analysis.analyze(&buffer);
757
758 assert_eq!(analysis.total_coeffs, 16);
759 assert_eq!(analysis.nonzero_count, 3);
760 assert_eq!(analysis.zero_count, 13);
761 assert_eq!(analysis.max_abs, 100);
762 }
763
764 #[test]
765 fn test_coeff_analysis_sparsity() {
766 let mut analysis = CoeffAnalysis::new();
767 let buffer = CoeffBuffer::new(8, 8); analysis.analyze(&buffer);
770
771 assert_eq!(analysis.sparsity(), 100.0);
772 }
773
774 #[test]
775 fn test_constants() {
776 assert_eq!(COEFF_BASE_MAX, 3);
777 assert_eq!(BR_CDF_SIZE, 4);
778 assert_eq!(MAX_BR_PARAM, 5);
779 }
780
781 #[test]
782 fn test_eob_offset_bits() {
783 assert_eq!(EOB_OFFSET_BITS[0], 0);
784 assert_eq!(EOB_OFFSET_BITS[3], 1);
785 assert_eq!(EOB_OFFSET_BITS[11], 9);
786 }
787
788 #[test]
789 fn test_coeff_analysis_avg_dc() {
790 let mut analysis = CoeffAnalysis::new();
791 let mut buffer = CoeffBuffer::new(4, 4);
792 buffer.set(0, 0, 200);
793
794 analysis.analyze(&buffer);
795
796 assert!((analysis.avg_dc() - 12.5).abs() < 0.1);
798 }
799
800 #[test]
801 fn test_coeff_decoder_position() {
802 let data = vec![0u8; 128];
803 let quant = create_test_quant_params();
804 let decoder = CoeffDecoder::new(data, quant, 8);
805 assert!(decoder.position() <= 128);
806 }
807
808 #[test]
828 fn test_finalize_eob_from_symbol_reads_correct_extra_bits() {
829 use super::super::coefficients::{EobPt, EOB_GROUP_START};
830
831 for symbol in 1..=11usize {
841 let data = vec![0xFFu8; 64];
843 let quant = create_test_quant_params();
844 let mut decoder = CoeffDecoder::new(data, quant, 8);
845
846 let eob = decoder
847 .finalize_eob_from_symbol(symbol)
848 .expect("legal symbol must succeed");
849
850 let fixed_extra_bits = EobPt::from_symbol(symbol)
851 .expect("legal symbol")
852 .extra_bits();
853 let expected_extra: u16 = if fixed_extra_bits == 0 {
854 0
855 } else {
856 (1u16 << fixed_extra_bits) - 1
857 };
858 let expected_eob = EOB_GROUP_START[symbol] + expected_extra;
859 assert_eq!(
860 eob, expected_eob,
861 "symbol {symbol}: fixed decoder must read {fixed_extra_bits} \
862 extra bits (all 1s → {expected_extra}) and produce \
863 EOB_GROUP_START[{symbol}]+{expected_extra}={expected_eob}",
864 );
865
866 let buggy_extra_bits = EobPt::from_eob(symbol as u16).extra_bits();
868 if symbol >= 4 {
869 let buggy_extra: u16 = if buggy_extra_bits == 0 {
872 0
873 } else {
874 (1u16 << buggy_extra_bits) - 1
875 };
876 let buggy_eob = EOB_GROUP_START[symbol] + buggy_extra;
877 assert!(
878 eob > buggy_eob,
879 "symbol {symbol}: fixed eob {eob} must exceed buggy eob \
880 {buggy_eob} (proves the regression test discriminates)",
881 );
882 } else {
883 assert_eq!(
885 fixed_extra_bits, buggy_extra_bits,
886 "symbol {symbol} (<=3): fixed and buggy extra_bits must \
887 coincide",
888 );
889 }
890 }
891 }
892
893 #[test]
896 fn test_finalize_eob_from_symbol_zero_returns_zero() {
897 let data = vec![0xFFu8; 16];
898 let quant = create_test_quant_params();
899 let mut decoder = CoeffDecoder::new(data, quant, 8);
900
901 let eob = decoder
902 .finalize_eob_from_symbol(0)
903 .expect("symbol 0 always succeeds");
904 assert_eq!(eob, 0, "symbol 0 must short-circuit to EOB = 0");
905 }
906
907 #[test]
911 fn test_finalize_eob_from_symbol_rejects_out_of_range() {
912 let data = vec![0xFFu8; 16];
913 let quant = create_test_quant_params();
914 let mut decoder = CoeffDecoder::new(data, quant, 8);
915
916 for bad_symbol in 12..=15usize {
917 let err = decoder
918 .finalize_eob_from_symbol(bad_symbol)
919 .expect_err("symbols >=12 must be rejected");
920 let msg = format!("{err}");
921 assert!(
922 msg.contains("EOB-multi"),
923 "error message should mention EOB-multi: {msg}",
924 );
925 }
926 }
927
928 #[test]
940 fn test_pos_to_rowcol_tx4x8() {
941 let encoder = CoeffEncoder::new();
944 let buf = CoeffBuffer::from_tx_size(TxSize::Tx4x8);
945 assert_eq!(buf.width(), 4);
946 assert_eq!(buf.height(), 8);
947
948 assert_eq!(encoder.pos_to_rowcol(0, &buf), (0, 0), "pos 0 -> (0,0)");
949 assert_eq!(encoder.pos_to_rowcol(3, &buf), (0, 3), "pos 3 -> (0,3)");
950 assert_eq!(encoder.pos_to_rowcol(4, &buf), (1, 0), "pos 4 -> (1,0)");
951 assert_eq!(encoder.pos_to_rowcol(7, &buf), (1, 3), "pos 7 -> (1,3)");
952 }
953
954 #[test]
955 fn test_pos_to_rowcol_tx8x4() {
956 let encoder = CoeffEncoder::new();
959 let buf = CoeffBuffer::from_tx_size(TxSize::Tx8x4);
960 assert_eq!(buf.width(), 8);
961 assert_eq!(buf.height(), 4);
962
963 assert_eq!(encoder.pos_to_rowcol(9, &buf), (1, 1), "pos 9 -> (1,1)");
964 assert_eq!(encoder.pos_to_rowcol(0, &buf), (0, 0), "pos 0 -> (0,0)");
965 assert_eq!(encoder.pos_to_rowcol(7, &buf), (0, 7), "pos 7 -> (0,7)");
966 assert_eq!(encoder.pos_to_rowcol(8, &buf), (1, 0), "pos 8 -> (1,0)");
967 }
968
969 #[test]
970 fn test_pos_to_rowcol_tx4x16() {
971 let encoder = CoeffEncoder::new();
974 let buf = CoeffBuffer::from_tx_size(TxSize::Tx4x16);
975 assert_eq!(buf.width(), 4);
976 assert_eq!(buf.height(), 16);
977
978 assert_eq!(encoder.pos_to_rowcol(17, &buf), (4, 1), "pos 17 -> (4,1)");
979 assert_eq!(encoder.pos_to_rowcol(0, &buf), (0, 0), "pos 0 -> (0,0)");
980 assert_eq!(encoder.pos_to_rowcol(3, &buf), (0, 3), "pos 3 -> (0,3)");
981 assert_eq!(encoder.pos_to_rowcol(4, &buf), (1, 0), "pos 4 -> (1,0)");
982 }
983
984 #[test]
985 fn test_pos_to_rowcol_square_unchanged() {
986 let encoder = CoeffEncoder::new();
989 let buf = CoeffBuffer::from_tx_size(TxSize::Tx4x4);
990 assert_eq!(buf.width(), 4);
991 assert_eq!(buf.height(), 4);
992
993 for pos in 0..16usize {
994 let (row, col) = encoder.pos_to_rowcol(pos, &buf);
995 assert_eq!(row, pos / 4, "Tx4x4 pos {pos}: row mismatch");
996 assert_eq!(col, pos % 4, "Tx4x4 pos {pos}: col mismatch");
997 }
998 }
999}