1#![forbid(unsafe_code)]
24#![allow(dead_code)]
25#![allow(clippy::too_many_arguments)]
26#![allow(clippy::similar_names)]
27#![allow(clippy::cast_possible_truncation)]
28#![allow(clippy::cast_sign_loss)]
29#![allow(clippy::cast_possible_wrap)]
30#![allow(clippy::needless_pass_by_value)]
31#![allow(clippy::unused_self)]
32#![allow(clippy::trivially_copy_pass_by_ref)]
33#![allow(clippy::needless_range_loop)]
34#![allow(clippy::manual_memcpy)]
35#![allow(clippy::if_same_then_else)]
36#![allow(clippy::comparison_chain)]
37#![allow(clippy::manual_rem_euclid)]
38#![allow(clippy::cast_lossless)]
39
40use super::{
41 AngleDelta, BitDepth, BlockDimensions, DirectionalMode, IntraPredContext, IntraPredictor,
42};
43
44#[derive(Clone, Copy, Debug)]
46pub struct DirectionalPredictor {
47 angle: u16,
49 delta: AngleDelta,
51 bit_depth: BitDepth,
53}
54
55impl DirectionalPredictor {
56 #[must_use]
58 pub const fn new(angle: u16, bit_depth: BitDepth) -> Self {
59 Self {
60 angle,
61 delta: AngleDelta::Zero,
62 bit_depth,
63 }
64 }
65
66 #[must_use]
68 pub const fn with_delta(angle: u16, delta: AngleDelta, bit_depth: BitDepth) -> Self {
69 Self {
70 angle,
71 delta,
72 bit_depth,
73 }
74 }
75
76 #[must_use]
78 pub const fn effective_angle(&self) -> i16 {
79 self.angle as i16 + self.delta.degrees()
80 }
81
82 pub fn predict_angle(
84 &self,
85 ctx: &IntraPredContext,
86 output: &mut [u16],
87 stride: usize,
88 dims: BlockDimensions,
89 ) {
90 let angle = self.effective_angle();
91
92 match angle {
94 90 => self.predict_vertical(ctx, output, stride, dims),
95 180 => self.predict_horizontal(ctx, output, stride, dims),
96 45 => self.predict_d45(ctx, output, stride, dims),
97 135 => self.predict_d135(ctx, output, stride, dims),
98 _ => self.predict_generic(ctx, output, stride, dims, angle),
99 }
100 }
101
102 fn predict_vertical(
104 &self,
105 ctx: &IntraPredContext,
106 output: &mut [u16],
107 stride: usize,
108 dims: BlockDimensions,
109 ) {
110 let top = ctx.top_samples();
111
112 for y in 0..dims.height {
113 let row_start = y * stride;
114 for x in 0..dims.width {
115 output[row_start + x] = top[x];
116 }
117 }
118 }
119
120 fn predict_horizontal(
122 &self,
123 ctx: &IntraPredContext,
124 output: &mut [u16],
125 stride: usize,
126 dims: BlockDimensions,
127 ) {
128 let left = ctx.left_samples();
129
130 for y in 0..dims.height {
131 let row_start = y * stride;
132 let left_val = left[y];
133 for x in 0..dims.width {
134 output[row_start + x] = left_val;
135 }
136 }
137 }
138
139 fn predict_d45(
141 &self,
142 ctx: &IntraPredContext,
143 output: &mut [u16],
144 stride: usize,
145 dims: BlockDimensions,
146 ) {
147 let top = ctx.top_samples();
148
149 for y in 0..dims.height {
150 let row_start = y * stride;
151 for x in 0..dims.width {
152 let idx = x + y + 1;
154 let sample = if idx < ctx.top_samples().len() {
155 top[idx]
156 } else {
157 top[ctx.top_samples().len().saturating_sub(1)]
159 };
160 output[row_start + x] = sample;
161 }
162 }
163 }
164
165 fn predict_d135(
167 &self,
168 ctx: &IntraPredContext,
169 output: &mut [u16],
170 stride: usize,
171 dims: BlockDimensions,
172 ) {
173 let top = ctx.top_samples();
174 let left = ctx.left_samples();
175 let top_left = ctx.top_left_sample();
176
177 for y in 0..dims.height {
178 let row_start = y * stride;
179 for x in 0..dims.width {
180 let sample = if y > x {
181 left[y - x - 1]
183 } else if y < x {
184 top[x - y - 1]
186 } else {
187 top_left
189 };
190 output[row_start + x] = sample;
191 }
192 }
193 }
194
195 fn predict_generic(
197 &self,
198 ctx: &IntraPredContext,
199 output: &mut [u16],
200 stride: usize,
201 dims: BlockDimensions,
202 angle: i16,
203 ) {
204 let (dx, dy) = get_direction_deltas(angle);
206
207 if angle < 90 {
208 self.predict_from_top(ctx, output, stride, dims, dx, dy);
210 } else if angle < 180 {
211 self.predict_from_top_left(ctx, output, stride, dims, dx, dy, angle);
213 } else if angle < 270 {
214 self.predict_from_left(ctx, output, stride, dims, dx, dy);
216 } else {
217 self.predict_from_left_up(ctx, output, stride, dims, dx, dy);
219 }
220 }
221
222 fn predict_from_top(
224 &self,
225 ctx: &IntraPredContext,
226 output: &mut [u16],
227 stride: usize,
228 dims: BlockDimensions,
229 dx: i32,
230 _dy: i32,
231 ) {
232 let top = ctx.top_samples();
233 let max_idx = top.len().saturating_sub(1);
234
235 for y in 0..dims.height {
236 let row_start = y * stride;
237 for x in 0..dims.width {
238 let src_x = ((x as i32) * 256 + (y as i32 + 1) * dx) / 256;
240 let frac = (((x as i32) * 256 + (y as i32 + 1) * dx) % 256) as u16;
241
242 let idx = src_x.clamp(0, max_idx as i32) as usize;
243 let idx_next = (idx + 1).min(max_idx);
244
245 let sample = interpolate(top[idx], top[idx_next], frac);
247 output[row_start + x] = sample;
248 }
249 }
250 }
251
252 fn predict_from_top_left(
254 &self,
255 ctx: &IntraPredContext,
256 output: &mut [u16],
257 stride: usize,
258 dims: BlockDimensions,
259 dx: i32,
260 dy: i32,
261 angle: i16,
262 ) {
263 let top = ctx.top_samples();
264 let left = ctx.left_samples();
265 let top_left = ctx.top_left_sample();
266
267 for y in 0..dims.height {
268 let row_start = y * stride;
269 for x in 0..dims.width {
270 let sample = if angle <= 90 {
271 let src_x = (x as i32) + ((y as i32 + 1) * dx) / 256;
273 let frac = ((y as i32 + 1) * dx) % 256;
274 get_sample_from_neighbors(
275 top,
276 left,
277 top_left,
278 src_x,
279 -1, frac.unsigned_abs() as u16,
281 true,
282 )
283 } else {
284 let src_y = (y as i32) + ((x as i32 + 1) * dy) / 256;
286 let frac = ((x as i32 + 1) * dy) % 256;
287 get_sample_from_neighbors(
288 top,
289 left,
290 top_left,
291 -1, src_y,
293 frac.unsigned_abs() as u16,
294 false,
295 )
296 };
297 output[row_start + x] = sample;
298 }
299 }
300 }
301
302 fn predict_from_left(
304 &self,
305 ctx: &IntraPredContext,
306 output: &mut [u16],
307 stride: usize,
308 dims: BlockDimensions,
309 _dx: i32,
310 dy: i32,
311 ) {
312 let left = ctx.left_samples();
313 let max_idx = left.len().saturating_sub(1);
314
315 for y in 0..dims.height {
316 let row_start = y * stride;
317 for x in 0..dims.width {
318 let src_y = ((y as i32) * 256 + (x as i32 + 1) * dy) / 256;
320 let frac = (((y as i32) * 256 + (x as i32 + 1) * dy) % 256) as u16;
321
322 let idx = src_y.clamp(0, max_idx as i32) as usize;
323 let idx_next = (idx + 1).min(max_idx);
324
325 let sample = interpolate(left[idx], left[idx_next], frac);
327 output[row_start + x] = sample;
328 }
329 }
330 }
331
332 fn predict_from_left_up(
334 &self,
335 ctx: &IntraPredContext,
336 output: &mut [u16],
337 stride: usize,
338 dims: BlockDimensions,
339 dx: i32,
340 _dy: i32,
341 ) {
342 let left = ctx.left_samples();
343 let top = ctx.top_samples();
344 let top_left = ctx.top_left_sample();
345
346 for y in 0..dims.height {
347 let row_start = y * stride;
348 for x in 0..dims.width {
349 let src_idx = (y as i32) - ((x as i32 + 1) * dx.abs()) / 256;
350 let frac = (((x as i32 + 1) * dx.abs()) % 256) as u16;
351
352 let sample = if src_idx >= 0 {
353 let idx = src_idx as usize;
354 let idx_next = (idx + 1).min(left.len().saturating_sub(1));
355 interpolate(left[idx], left[idx_next], frac)
356 } else {
357 let top_idx = (-(src_idx + 1)) as usize;
359 if top_idx == 0 {
360 top_left
361 } else if top_idx <= top.len() {
362 top[top_idx - 1]
363 } else {
364 top[top.len().saturating_sub(1)]
365 }
366 };
367 output[row_start + x] = sample;
368 }
369 }
370 }
371}
372
373impl IntraPredictor for DirectionalPredictor {
374 fn predict(
375 &self,
376 ctx: &IntraPredContext,
377 output: &mut [u16],
378 stride: usize,
379 dims: BlockDimensions,
380 ) {
381 self.predict_angle(ctx, output, stride, dims);
382 }
383}
384
385#[derive(Clone, Copy, Debug, Default)]
387pub struct VerticalPredictor;
388
389impl VerticalPredictor {
390 #[must_use]
392 pub const fn new() -> Self {
393 Self
394 }
395}
396
397impl IntraPredictor for VerticalPredictor {
398 fn predict(
399 &self,
400 ctx: &IntraPredContext,
401 output: &mut [u16],
402 stride: usize,
403 dims: BlockDimensions,
404 ) {
405 let top = ctx.top_samples();
406
407 for y in 0..dims.height {
408 let row_start = y * stride;
409 for x in 0..dims.width {
410 output[row_start + x] = top[x];
411 }
412 }
413 }
414}
415
416#[derive(Clone, Copy, Debug, Default)]
418pub struct HorizontalPredictor;
419
420impl HorizontalPredictor {
421 #[must_use]
423 pub const fn new() -> Self {
424 Self
425 }
426}
427
428impl IntraPredictor for HorizontalPredictor {
429 fn predict(
430 &self,
431 ctx: &IntraPredContext,
432 output: &mut [u16],
433 stride: usize,
434 dims: BlockDimensions,
435 ) {
436 let left = ctx.left_samples();
437
438 for y in 0..dims.height {
439 let row_start = y * stride;
440 let left_val = left[y];
441 for x in 0..dims.width {
442 output[row_start + x] = left_val;
443 }
444 }
445 }
446}
447
448#[derive(Clone, Copy, Debug, Default)]
450pub struct D45Predictor;
451
452impl D45Predictor {
453 #[must_use]
455 pub const fn new() -> Self {
456 Self
457 }
458}
459
460impl IntraPredictor for D45Predictor {
461 fn predict(
462 &self,
463 ctx: &IntraPredContext,
464 output: &mut [u16],
465 stride: usize,
466 dims: BlockDimensions,
467 ) {
468 let predictor = DirectionalPredictor::new(45, BitDepth::Bits8);
469 predictor.predict_d45(ctx, output, stride, dims);
470 }
471}
472
473#[derive(Clone, Copy, Debug, Default)]
475pub struct D63Predictor;
476
477impl D63Predictor {
478 #[must_use]
480 pub const fn new() -> Self {
481 Self
482 }
483}
484
485impl IntraPredictor for D63Predictor {
486 fn predict(
487 &self,
488 ctx: &IntraPredContext,
489 output: &mut [u16],
490 stride: usize,
491 dims: BlockDimensions,
492 ) {
493 let predictor = DirectionalPredictor::new(63, BitDepth::Bits8);
494 predictor.predict_angle(ctx, output, stride, dims);
495 }
496}
497
498#[derive(Clone, Copy, Debug, Default)]
500pub struct D67Predictor;
501
502impl D67Predictor {
503 #[must_use]
505 pub const fn new() -> Self {
506 Self
507 }
508}
509
510impl IntraPredictor for D67Predictor {
511 fn predict(
512 &self,
513 ctx: &IntraPredContext,
514 output: &mut [u16],
515 stride: usize,
516 dims: BlockDimensions,
517 ) {
518 let predictor = DirectionalPredictor::new(67, BitDepth::Bits8);
519 predictor.predict_angle(ctx, output, stride, dims);
520 }
521}
522
523#[derive(Clone, Copy, Debug, Default)]
525pub struct D113Predictor;
526
527impl D113Predictor {
528 #[must_use]
530 pub const fn new() -> Self {
531 Self
532 }
533}
534
535impl IntraPredictor for D113Predictor {
536 fn predict(
537 &self,
538 ctx: &IntraPredContext,
539 output: &mut [u16],
540 stride: usize,
541 dims: BlockDimensions,
542 ) {
543 let predictor = DirectionalPredictor::new(113, BitDepth::Bits8);
544 predictor.predict_angle(ctx, output, stride, dims);
545 }
546}
547
548#[derive(Clone, Copy, Debug, Default)]
550pub struct D117Predictor;
551
552impl D117Predictor {
553 #[must_use]
555 pub const fn new() -> Self {
556 Self
557 }
558}
559
560impl IntraPredictor for D117Predictor {
561 fn predict(
562 &self,
563 ctx: &IntraPredContext,
564 output: &mut [u16],
565 stride: usize,
566 dims: BlockDimensions,
567 ) {
568 let predictor = DirectionalPredictor::new(117, BitDepth::Bits8);
569 predictor.predict_angle(ctx, output, stride, dims);
570 }
571}
572
573#[derive(Clone, Copy, Debug, Default)]
575pub struct D135Predictor;
576
577impl D135Predictor {
578 #[must_use]
580 pub const fn new() -> Self {
581 Self
582 }
583}
584
585impl IntraPredictor for D135Predictor {
586 fn predict(
587 &self,
588 ctx: &IntraPredContext,
589 output: &mut [u16],
590 stride: usize,
591 dims: BlockDimensions,
592 ) {
593 let predictor = DirectionalPredictor::new(135, BitDepth::Bits8);
594 predictor.predict_d135(ctx, output, stride, dims);
595 }
596}
597
598#[derive(Clone, Copy, Debug, Default)]
600pub struct D153Predictor;
601
602impl D153Predictor {
603 #[must_use]
605 pub const fn new() -> Self {
606 Self
607 }
608}
609
610impl IntraPredictor for D153Predictor {
611 fn predict(
612 &self,
613 ctx: &IntraPredContext,
614 output: &mut [u16],
615 stride: usize,
616 dims: BlockDimensions,
617 ) {
618 let predictor = DirectionalPredictor::new(153, BitDepth::Bits8);
619 predictor.predict_angle(ctx, output, stride, dims);
620 }
621}
622
623#[derive(Clone, Copy, Debug, Default)]
625pub struct D157Predictor;
626
627impl D157Predictor {
628 #[must_use]
630 pub const fn new() -> Self {
631 Self
632 }
633}
634
635impl IntraPredictor for D157Predictor {
636 fn predict(
637 &self,
638 ctx: &IntraPredContext,
639 output: &mut [u16],
640 stride: usize,
641 dims: BlockDimensions,
642 ) {
643 let predictor = DirectionalPredictor::new(157, BitDepth::Bits8);
644 predictor.predict_angle(ctx, output, stride, dims);
645 }
646}
647
648#[derive(Clone, Copy, Debug, Default)]
650pub struct D203Predictor;
651
652impl D203Predictor {
653 #[must_use]
655 pub const fn new() -> Self {
656 Self
657 }
658}
659
660impl IntraPredictor for D203Predictor {
661 fn predict(
662 &self,
663 ctx: &IntraPredContext,
664 output: &mut [u16],
665 stride: usize,
666 dims: BlockDimensions,
667 ) {
668 let predictor = DirectionalPredictor::new(203, BitDepth::Bits8);
669 predictor.predict_angle(ctx, output, stride, dims);
670 }
671}
672
673#[derive(Clone, Copy, Debug, Default)]
675pub struct D207Predictor;
676
677impl D207Predictor {
678 #[must_use]
680 pub const fn new() -> Self {
681 Self
682 }
683}
684
685impl IntraPredictor for D207Predictor {
686 fn predict(
687 &self,
688 ctx: &IntraPredContext,
689 output: &mut [u16],
690 stride: usize,
691 dims: BlockDimensions,
692 ) {
693 let predictor = DirectionalPredictor::new(207, BitDepth::Bits8);
694 predictor.predict_angle(ctx, output, stride, dims);
695 }
696}
697
698#[must_use]
701fn get_direction_deltas(angle: i16) -> (i32, i32) {
702 let angle = ((angle % 360) + 360) % 360;
704
705 match angle {
708 0 => (0, 256),
709 45 => (181, 181),
710 90 => (256, 0),
711 135 => (181, -181),
712 180 => (0, -256),
713 225 => (-181, -181),
714 270 => (-256, 0),
715 315 => (-181, 181),
716 _ => {
717 let radians = (angle as f64) * std::f64::consts::PI / 180.0;
719 let dx = (radians.sin() * 256.0).round() as i32;
720 let dy = (radians.cos() * 256.0).round() as i32;
721 (dx, dy)
722 }
723 }
724}
725
726#[inline]
728fn interpolate(a: u16, b: u16, frac: u16) -> u16 {
729 let a32 = u32::from(a);
731 let b32 = u32::from(b);
732 let frac32 = u32::from(frac);
733
734 let result = (a32 * (256 - frac32) + b32 * frac32 + 128) / 256;
735 result as u16
736}
737
738fn get_sample_from_neighbors(
740 top: &[u16],
741 left: &[u16],
742 top_left: u16,
743 x: i32,
744 y: i32,
745 frac: u16,
746 use_top: bool,
747) -> u16 {
748 if use_top {
749 let idx = x.clamp(0, (top.len() - 1) as i32) as usize;
751 let idx_next = (idx + 1).min(top.len() - 1);
752 interpolate(top[idx], top[idx_next], frac)
753 } else {
754 if y < 0 {
756 top_left
757 } else {
758 let idx = y.clamp(0, (left.len() - 1) as i32) as usize;
759 let idx_next = (idx + 1).min(left.len() - 1);
760 interpolate(left[idx], left[idx_next], frac)
761 }
762 }
763}
764
765pub fn get_direction_samples(
767 ctx: &IntraPredContext,
768 angle: i16,
769 width: usize,
770 height: usize,
771) -> Vec<u16> {
772 let mode = DirectionalMode::new(angle as u16);
773 let mut samples = Vec::with_capacity(width * height);
774
775 let top = ctx.top_samples();
776 let left = ctx.left_samples();
777
778 if mode.is_vertical_ish() {
779 for y in 0..height {
781 for x in 0..width {
782 let idx = (x + y).min(top.len() - 1);
783 samples.push(top[idx]);
784 }
785 }
786 } else {
787 for y in 0..height {
789 for x in 0..width {
790 let idx = (x + y).min(left.len() - 1);
791 samples.push(left[idx]);
792 }
793 }
794 }
795
796 samples
797}
798
799#[cfg(test)]
800mod tests {
801 use super::*;
802 use crate::intra::context::IntraPredContext;
803
804 fn create_test_context() -> IntraPredContext {
805 let mut ctx = IntraPredContext::new(8, 8, BitDepth::Bits8);
806
807 for i in 0..16 {
809 ctx.set_top_sample(i, ((i + 1) * 10) as u16);
810 }
811
812 for i in 0..16 {
814 ctx.set_left_sample(i, (15 + i * 10) as u16);
815 }
816
817 ctx.set_top_left_sample(5);
818 ctx.set_availability(true, true);
819
820 ctx
821 }
822
823 #[test]
824 fn test_vertical_prediction() {
825 let ctx = create_test_context();
826 let predictor = VerticalPredictor::new();
827 let dims = BlockDimensions::new(4, 4);
828 let mut output = vec![0u16; 16];
829
830 predictor.predict(&ctx, &mut output, 4, dims);
831
832 assert_eq!(output[0], 10); assert_eq!(output[1], 20); assert_eq!(output[2], 30); assert_eq!(output[3], 40); assert_eq!(output[4], 10);
840 assert_eq!(output[5], 20);
841 }
842
843 #[test]
844 fn test_horizontal_prediction() {
845 let ctx = create_test_context();
846 let predictor = HorizontalPredictor::new();
847 let dims = BlockDimensions::new(4, 4);
848 let mut output = vec![0u16; 16];
849
850 predictor.predict(&ctx, &mut output, 4, dims);
851
852 assert_eq!(output[0], 15);
855 assert_eq!(output[1], 15);
856 assert_eq!(output[2], 15);
857 assert_eq!(output[3], 15);
858
859 assert_eq!(output[4], 25);
861 assert_eq!(output[5], 25);
862 }
863
864 #[test]
865 fn test_d45_prediction() {
866 let ctx = create_test_context();
867 let predictor = D45Predictor::new();
868 let dims = BlockDimensions::new(4, 4);
869 let mut output = vec![0u16; 16];
870
871 predictor.predict(&ctx, &mut output, 4, dims);
872
873 assert_eq!(output[0], 20);
878 assert_eq!(output[1], 30);
879 assert_eq!(output[4], 30); }
881
882 #[test]
883 fn test_d135_prediction() {
884 let ctx = create_test_context();
885 let predictor = D135Predictor::new();
886 let dims = BlockDimensions::new(4, 4);
887 let mut output = vec![0u16; 16];
888
889 predictor.predict(&ctx, &mut output, 4, dims);
890
891 assert_eq!(output[0], 5);
896 assert_eq!(output[1], 10);
897 assert_eq!(output[4], 15); }
899
900 #[test]
901 fn test_interpolation() {
902 assert_eq!(interpolate(100, 200, 0), 100);
904
905 let result = interpolate(100, 200, 255);
907 assert!(result >= 199 && result <= 200);
908
909 let result = interpolate(100, 200, 128);
911 assert!(result >= 149 && result <= 151);
912 }
913
914 #[test]
915 fn test_direction_deltas() {
916 let (dx, dy) = get_direction_deltas(0);
917 assert_eq!((dx, dy), (0, 256));
918
919 let (dx, dy) = get_direction_deltas(90);
920 assert_eq!((dx, dy), (256, 0));
921
922 let (dx, dy) = get_direction_deltas(180);
923 assert_eq!((dx, dy), (0, -256));
924
925 let (dx, dy) = get_direction_deltas(45);
926 assert_eq!((dx, dy), (181, 181));
927 }
928
929 #[test]
930 fn test_directional_predictor() {
931 let ctx = create_test_context();
932 let predictor = DirectionalPredictor::new(90, BitDepth::Bits8);
933 let dims = BlockDimensions::new(4, 4);
934 let mut output = vec![0u16; 16];
935
936 predictor.predict(&ctx, &mut output, 4, dims);
937
938 assert_eq!(output[0], 10);
940 assert_eq!(output[1], 20);
941 }
942
943 #[test]
944 fn test_directional_with_delta() {
945 let predictor = DirectionalPredictor::with_delta(90, AngleDelta::Plus3, BitDepth::Bits8);
946 assert_eq!(predictor.effective_angle(), 99);
947 }
948}