1#![forbid(unsafe_code)]
8#![allow(clippy::unreadable_literal)]
9#![allow(clippy::items_after_statements)]
10#![allow(clippy::unnecessary_wraps)]
11#![allow(clippy::struct_excessive_bools)]
12#![allow(clippy::identity_op)]
13#![allow(clippy::range_plus_one)]
14#![allow(clippy::needless_range_loop)]
15#![allow(clippy::useless_conversion)]
16#![allow(clippy::redundant_closure_for_method_calls)]
17#![allow(clippy::single_match_else)]
18#![allow(dead_code)]
19#![allow(clippy::doc_markdown)]
20#![allow(clippy::unused_self)]
21#![allow(clippy::trivially_copy_pass_by_ref)]
22#![allow(clippy::cast_possible_truncation)]
23#![allow(clippy::cast_sign_loss)]
24#![allow(clippy::missing_errors_doc)]
25#![allow(clippy::similar_names)]
26#![allow(clippy::cast_precision_loss)]
27#![allow(clippy::cast_lossless)]
28#![allow(clippy::manual_div_ceil)]
29
30use super::{
31 ChromaSubsampling, FrameBuffer, PlaneBuffer, PlaneType, ReconstructResult, ReconstructionError,
32};
33
34pub const MAX_TX_SIZE: usize = 64;
40
41pub const MIN_TX_SIZE: usize = 4;
43
44pub const MAX_COEFF_VALUE: i32 = 32767;
46
47pub const MIN_COEFF_VALUE: i32 = -32768;
49
50#[derive(Clone, Debug)]
56pub struct TransformBlock {
57 coeffs: Vec<i32>,
59 width: usize,
61 height: usize,
63 eob: usize,
65 tx_type: TransformType,
67}
68
69impl TransformBlock {
70 #[must_use]
72 pub fn new(width: usize, height: usize) -> Self {
73 let size = width * height;
74 Self {
75 coeffs: vec![0; size],
76 width,
77 height,
78 eob: 0,
79 tx_type: TransformType::Dct,
80 }
81 }
82
83 #[must_use]
85 pub const fn width(&self) -> usize {
86 self.width
87 }
88
89 #[must_use]
91 pub const fn height(&self) -> usize {
92 self.height
93 }
94
95 #[must_use]
97 pub const fn eob(&self) -> usize {
98 self.eob
99 }
100
101 pub fn set_eob(&mut self, eob: usize) {
103 self.eob = eob;
104 }
105
106 #[must_use]
108 pub const fn tx_type(&self) -> TransformType {
109 self.tx_type
110 }
111
112 pub fn set_tx_type(&mut self, tx_type: TransformType) {
114 self.tx_type = tx_type;
115 }
116
117 #[must_use]
119 pub fn get(&self, row: usize, col: usize) -> i32 {
120 if row < self.height && col < self.width {
121 self.coeffs[row * self.width + col]
122 } else {
123 0
124 }
125 }
126
127 pub fn set(&mut self, row: usize, col: usize, value: i32) {
129 if row < self.height && col < self.width {
130 self.coeffs[row * self.width + col] = value;
131 }
132 }
133
134 pub fn set_clamped(&mut self, row: usize, col: usize, value: i32) {
136 let clamped = value.clamp(MIN_COEFF_VALUE, MAX_COEFF_VALUE);
137 self.set(row, col, clamped);
138 }
139
140 #[must_use]
142 pub fn coeffs(&self) -> &[i32] {
143 &self.coeffs
144 }
145
146 pub fn coeffs_mut(&mut self) -> &mut [i32] {
148 &mut self.coeffs
149 }
150
151 pub fn clear(&mut self) {
153 self.coeffs.fill(0);
154 self.eob = 0;
155 }
156
157 #[must_use]
159 pub const fn is_zero(&self) -> bool {
160 self.eob == 0
161 }
162
163 #[must_use]
165 pub fn count_nonzero(&self) -> usize {
166 self.coeffs.iter().filter(|&&c| c != 0).count()
167 }
168
169 #[must_use]
171 pub fn get_scan(&self, scan_pos: usize) -> i32 {
172 if scan_pos < self.coeffs.len() {
173 self.coeffs[scan_pos]
174 } else {
175 0
176 }
177 }
178
179 pub fn set_scan(&mut self, scan_pos: usize, value: i32) {
181 if scan_pos < self.coeffs.len() {
182 self.coeffs[scan_pos] = value;
183 }
184 }
185}
186
187#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
189pub enum TransformType {
190 #[default]
192 Dct,
193 Adst,
195 Identity,
197 FlipDct,
199 FlipAdst,
201}
202
203impl TransformType {
204 #[must_use]
206 pub const fn horizontal(self) -> Self {
207 self
208 }
209
210 #[must_use]
212 pub const fn vertical(self) -> Self {
213 self
214 }
215
216 #[must_use]
218 pub const fn is_identity(self) -> bool {
219 matches!(self, Self::Identity)
220 }
221}
222
223#[derive(Clone, Debug)]
229pub struct ResidualPlane {
230 data: Vec<i32>,
232 width: u32,
234 height: u32,
236 stride: usize,
238 plane_type: PlaneType,
240}
241
242impl ResidualPlane {
243 #[must_use]
245 pub fn new(width: u32, height: u32, plane_type: PlaneType) -> Self {
246 let stride = ((width as usize + 15) / 16) * 16;
248 let size = stride * height as usize;
249
250 Self {
251 data: vec![0; size],
252 width,
253 height,
254 stride,
255 plane_type,
256 }
257 }
258
259 #[must_use]
261 pub const fn width(&self) -> u32 {
262 self.width
263 }
264
265 #[must_use]
267 pub const fn height(&self) -> u32 {
268 self.height
269 }
270
271 #[must_use]
273 pub const fn stride(&self) -> usize {
274 self.stride
275 }
276
277 #[must_use]
279 pub const fn plane_type(&self) -> PlaneType {
280 self.plane_type
281 }
282
283 #[must_use]
285 pub fn get(&self, x: u32, y: u32) -> i32 {
286 if x >= self.width || y >= self.height {
287 return 0;
288 }
289 let idx = y as usize * self.stride + x as usize;
290 self.data.get(idx).copied().unwrap_or(0)
291 }
292
293 pub fn set(&mut self, x: u32, y: u32, value: i32) {
295 if x < self.width && y < self.height {
296 let idx = y as usize * self.stride + x as usize;
297 if idx < self.data.len() {
298 self.data[idx] = value;
299 }
300 }
301 }
302
303 #[must_use]
305 pub fn row(&self, y: u32) -> &[i32] {
306 if y >= self.height {
307 return &[];
308 }
309 let start = y as usize * self.stride;
310 let end = start + self.width as usize;
311 if end <= self.data.len() {
312 &self.data[start..end]
313 } else {
314 &[]
315 }
316 }
317
318 pub fn row_mut(&mut self, y: u32) -> &mut [i32] {
320 if y >= self.height {
321 return &mut [];
322 }
323 let start = y as usize * self.stride;
324 let end = start + self.width as usize;
325 if end <= self.data.len() {
326 &mut self.data[start..end]
327 } else {
328 &mut []
329 }
330 }
331
332 #[must_use]
334 pub fn data(&self) -> &[i32] {
335 &self.data
336 }
337
338 pub fn data_mut(&mut self) -> &mut [i32] {
340 &mut self.data
341 }
342
343 pub fn clear(&mut self) {
345 self.data.fill(0);
346 }
347
348 pub fn clear_block(&mut self, x: u32, y: u32, width: u32, height: u32) {
350 for dy in 0..height {
351 for dx in 0..width {
352 self.set(x + dx, y + dy, 0);
353 }
354 }
355 }
356
357 pub fn write_block(&mut self, x: u32, y: u32, block: &TransformBlock) {
359 for row in 0..block.height() {
360 for col in 0..block.width() {
361 let value = block.get(row, col);
362 self.set(x + col as u32, y + row as u32, value);
363 }
364 }
365 }
366
367 #[must_use]
369 pub fn read_block(&self, x: u32, y: u32, width: usize, height: usize) -> TransformBlock {
370 let mut block = TransformBlock::new(width, height);
371 for row in 0..height {
372 for col in 0..width {
373 let value = self.get(x + col as u32, y + row as u32);
374 block.set(row, col, value);
375 }
376 }
377 block
378 }
379}
380
381#[derive(Clone, Debug)]
387pub struct ResidualBuffer {
388 y_plane: ResidualPlane,
390 u_plane: Option<ResidualPlane>,
392 v_plane: Option<ResidualPlane>,
394 width: u32,
396 height: u32,
398 subsampling: ChromaSubsampling,
400}
401
402impl ResidualBuffer {
403 #[must_use]
405 pub fn new(width: u32, height: u32, subsampling: ChromaSubsampling) -> Self {
406 let y_plane = ResidualPlane::new(width, height, PlaneType::Y);
407
408 let (u_plane, v_plane) = match subsampling {
409 ChromaSubsampling::Mono => (None, None),
410 _ => {
411 let (cw, ch) = subsampling.chroma_size(width, height);
412 (
413 Some(ResidualPlane::new(cw, ch, PlaneType::U)),
414 Some(ResidualPlane::new(cw, ch, PlaneType::V)),
415 )
416 }
417 };
418
419 Self {
420 y_plane,
421 u_plane,
422 v_plane,
423 width,
424 height,
425 subsampling,
426 }
427 }
428
429 #[must_use]
431 pub const fn width(&self) -> u32 {
432 self.width
433 }
434
435 #[must_use]
437 pub const fn height(&self) -> u32 {
438 self.height
439 }
440
441 #[must_use]
443 pub fn y_plane(&self) -> &ResidualPlane {
444 &self.y_plane
445 }
446
447 pub fn y_plane_mut(&mut self) -> &mut ResidualPlane {
449 &mut self.y_plane
450 }
451
452 #[must_use]
454 pub fn u_plane(&self) -> Option<&ResidualPlane> {
455 self.u_plane.as_ref()
456 }
457
458 pub fn u_plane_mut(&mut self) -> Option<&mut ResidualPlane> {
460 self.u_plane.as_mut()
461 }
462
463 #[must_use]
465 pub fn v_plane(&self) -> Option<&ResidualPlane> {
466 self.v_plane.as_ref()
467 }
468
469 pub fn v_plane_mut(&mut self) -> Option<&mut ResidualPlane> {
471 self.v_plane.as_mut()
472 }
473
474 #[must_use]
476 pub fn plane(&self, plane_type: PlaneType) -> Option<&ResidualPlane> {
477 match plane_type {
478 PlaneType::Y => Some(&self.y_plane),
479 PlaneType::U => self.u_plane.as_ref(),
480 PlaneType::V => self.v_plane.as_ref(),
481 }
482 }
483
484 pub fn plane_mut(&mut self, plane_type: PlaneType) -> Option<&mut ResidualPlane> {
486 match plane_type {
487 PlaneType::Y => Some(&mut self.y_plane),
488 PlaneType::U => self.u_plane.as_mut(),
489 PlaneType::V => self.v_plane.as_mut(),
490 }
491 }
492
493 pub fn clear(&mut self) {
495 self.y_plane.clear();
496 if let Some(ref mut u) = self.u_plane {
497 u.clear();
498 }
499 if let Some(ref mut v) = self.v_plane {
500 v.clear();
501 }
502 }
503
504 pub fn add_to_frame(&self, frame: &mut FrameBuffer) -> ReconstructResult<()> {
510 if frame.width() != self.width || frame.height() != self.height {
511 return Err(ReconstructionError::InvalidDimensions {
512 width: self.width,
513 height: self.height,
514 });
515 }
516
517 add_residual_plane(&self.y_plane, frame.y_plane_mut());
519
520 if let (Some(ref resid), Some(ref mut plane)) = (&self.u_plane, frame.u_plane_mut()) {
522 add_residual_plane(resid, plane);
523 }
524
525 if let (Some(ref resid), Some(ref mut plane)) = (&self.v_plane, frame.v_plane_mut()) {
527 add_residual_plane(resid, plane);
528 }
529
530 Ok(())
531 }
532
533 pub fn add_block_to_frame(
535 &self,
536 frame: &mut FrameBuffer,
537 plane: PlaneType,
538 x: u32,
539 y: u32,
540 width: u32,
541 height: u32,
542 ) -> ReconstructResult<()> {
543 let resid_plane = self
544 .plane(plane)
545 .ok_or_else(|| ReconstructionError::InvalidInput("Plane not available".to_string()))?;
546 let frame_plane = frame
547 .plane_mut(plane)
548 .ok_or_else(|| ReconstructionError::InvalidInput("Plane not available".to_string()))?;
549
550 let max_val = frame_plane.max_value();
551
552 for dy in 0..height {
553 for dx in 0..width {
554 let px = x + dx;
555 let py = y + dy;
556
557 let pixel = i32::from(frame_plane.get(px, py));
558 let residual = resid_plane.get(px, py);
559 let result = (pixel + residual).clamp(0, i32::from(max_val));
560
561 frame_plane.set(px, py, result as i16);
562 }
563 }
564
565 Ok(())
566 }
567}
568
569fn add_residual_plane(residual: &ResidualPlane, pixels: &mut PlaneBuffer) {
571 let max_val = pixels.max_value();
572 let width = residual.width().min(pixels.width());
573 let height = residual.height().min(pixels.height());
574
575 for y in 0..height {
576 let resid_row = residual.row(y);
577 let pixel_row = pixels.row_mut(y);
578
579 for x in 0..width as usize {
580 let pixel = i32::from(pixel_row[x]);
581 let resid = resid_row[x];
582 let result = (pixel + resid).clamp(0, i32::from(max_val));
583 pixel_row[x] = result as i16;
584 }
585 }
586}
587
588pub fn add_residual(
602 prediction: &mut [i16],
603 residual: &[i32],
604 width: usize,
605 height: usize,
606 bit_depth: u8,
607) {
608 let max_val = (1i32 << bit_depth) - 1;
609 let size = width * height;
610
611 for i in 0..size.min(prediction.len()).min(residual.len()) {
612 let pred = i32::from(prediction[i]);
613 let resid = residual[i];
614 let result = (pred + resid).clamp(0, max_val);
615 prediction[i] = result as i16;
616 }
617}
618
619pub fn add_residual_stride(
621 prediction: &mut [i16],
622 pred_stride: usize,
623 residual: &[i32],
624 resid_stride: usize,
625 width: usize,
626 height: usize,
627 bit_depth: u8,
628) {
629 let max_val = (1i32 << bit_depth) - 1;
630
631 for y in 0..height {
632 let pred_row = &mut prediction[y * pred_stride..];
633 let resid_row = &residual[y * resid_stride..];
634
635 for x in 0..width {
636 let pred = i32::from(pred_row[x]);
637 let resid = resid_row[x];
638 let result = (pred + resid).clamp(0, max_val);
639 pred_row[x] = result as i16;
640 }
641 }
642}
643
644pub fn clip_residuals(residuals: &mut [i32], bit_depth: u8) {
646 let max = (1i32 << bit_depth) - 1;
648 let min = -(1i32 << bit_depth);
649
650 for r in residuals.iter_mut() {
651 *r = (*r).clamp(min, max);
652 }
653}
654
655#[cfg(test)]
660mod tests {
661 use super::*;
662
663 #[test]
664 fn test_transform_block_new() {
665 let block = TransformBlock::new(8, 8);
666 assert_eq!(block.width(), 8);
667 assert_eq!(block.height(), 8);
668 assert_eq!(block.eob(), 0);
669 assert!(block.is_zero());
670 }
671
672 #[test]
673 fn test_transform_block_get_set() {
674 let mut block = TransformBlock::new(8, 8);
675 block.set(2, 3, 100);
676 block.set_eob(25);
677
678 assert_eq!(block.get(2, 3), 100);
679 assert_eq!(block.eob(), 25);
680 assert!(!block.is_zero());
681 }
682
683 #[test]
684 fn test_transform_block_count_nonzero() {
685 let mut block = TransformBlock::new(4, 4);
686 block.set(0, 0, 10);
687 block.set(1, 1, 20);
688 block.set(2, 2, 30);
689
690 assert_eq!(block.count_nonzero(), 3);
691 }
692
693 #[test]
694 fn test_transform_type() {
695 assert!(!TransformType::Dct.is_identity());
696 assert!(TransformType::Identity.is_identity());
697 }
698
699 #[test]
700 fn test_residual_plane_new() {
701 let plane = ResidualPlane::new(64, 48, PlaneType::Y);
702 assert_eq!(plane.width(), 64);
703 assert_eq!(plane.height(), 48);
704 assert_eq!(plane.plane_type(), PlaneType::Y);
705 }
706
707 #[test]
708 fn test_residual_plane_get_set() {
709 let mut plane = ResidualPlane::new(64, 48, PlaneType::Y);
710 plane.set(10, 20, 500);
711 assert_eq!(plane.get(10, 20), 500);
712 assert_eq!(plane.get(0, 0), 0);
713 }
714
715 #[test]
716 fn test_residual_plane_write_read_block() {
717 let mut plane = ResidualPlane::new(64, 48, PlaneType::Y);
718
719 let mut block = TransformBlock::new(4, 4);
720 block.set(0, 0, 100);
721 block.set(1, 1, 200);
722 block.set(3, 3, 300);
723
724 plane.write_block(8, 8, &block);
725
726 let read_block = plane.read_block(8, 8, 4, 4);
727 assert_eq!(read_block.get(0, 0), 100);
728 assert_eq!(read_block.get(1, 1), 200);
729 assert_eq!(read_block.get(3, 3), 300);
730 }
731
732 #[test]
733 fn test_residual_buffer_new() {
734 let buffer = ResidualBuffer::new(1920, 1080, ChromaSubsampling::Cs420);
735 assert_eq!(buffer.width(), 1920);
736 assert_eq!(buffer.height(), 1080);
737
738 assert!(buffer.u_plane().is_some());
739 assert!(buffer.v_plane().is_some());
740 }
741
742 #[test]
743 fn test_residual_buffer_mono() {
744 let buffer = ResidualBuffer::new(1920, 1080, ChromaSubsampling::Mono);
745 assert!(buffer.u_plane().is_none());
746 assert!(buffer.v_plane().is_none());
747 }
748
749 #[test]
750 fn test_residual_buffer_clear() {
751 let mut buffer = ResidualBuffer::new(64, 48, ChromaSubsampling::Cs420);
752 buffer.y_plane_mut().set(10, 10, 1000);
753
754 buffer.clear();
755 assert_eq!(buffer.y_plane().get(10, 10), 0);
756 }
757
758 #[test]
759 fn test_add_residual_to_frame() {
760 let mut frame = FrameBuffer::new(64, 48, 8, ChromaSubsampling::Cs420);
761 let mut residual = ResidualBuffer::new(64, 48, ChromaSubsampling::Cs420);
762
763 frame.y_plane_mut().set(10, 10, 100);
765
766 residual.y_plane_mut().set(10, 10, 50);
768
769 residual.add_to_frame(&mut frame).expect("should succeed");
771
772 assert_eq!(frame.y_plane().get(10, 10), 150);
774 }
775
776 #[test]
777 fn test_add_residual_clamping() {
778 let mut frame = FrameBuffer::new(64, 48, 8, ChromaSubsampling::Cs420);
779 let mut residual = ResidualBuffer::new(64, 48, ChromaSubsampling::Cs420);
780
781 frame.y_plane_mut().set(10, 10, 250);
783
784 residual.y_plane_mut().set(10, 10, 100);
786
787 residual.add_to_frame(&mut frame).expect("should succeed");
789
790 assert_eq!(frame.y_plane().get(10, 10), 255);
791 }
792
793 #[test]
794 fn test_add_residual_function() {
795 let mut prediction = vec![100i16, 150, 200, 50];
796 let residual = vec![20i32, -30, 100, -100];
797
798 add_residual(&mut prediction, &residual, 2, 2, 8);
799
800 assert_eq!(prediction[0], 120); assert_eq!(prediction[1], 120); assert_eq!(prediction[2], 255); assert_eq!(prediction[3], 0); }
805
806 #[test]
807 fn test_clip_residuals() {
808 let mut residuals = vec![100, -100, 500, -500];
809 clip_residuals(&mut residuals, 8);
810
811 assert_eq!(residuals[0], 100);
812 assert_eq!(residuals[1], -100);
813 assert_eq!(residuals[2], 255); assert_eq!(residuals[3], -256); }
816
817 #[test]
818 fn test_constants() {
819 assert_eq!(MAX_TX_SIZE, 64);
820 assert_eq!(MIN_TX_SIZE, 4);
821 assert_eq!(MAX_COEFF_VALUE, 32767);
822 assert_eq!(MIN_COEFF_VALUE, -32768);
823 }
824}