1#![forbid(unsafe_code)]
7#![allow(dead_code)]
8#![allow(clippy::cast_possible_wrap)]
9#![allow(clippy::match_same_arms)]
10#![allow(clippy::doc_markdown)]
11#![allow(clippy::cast_lossless)]
12#![allow(clippy::cast_possible_truncation)]
13
14use super::{BlockDimensions, IntraPredContext};
15
16pub trait IntraPredictor {
21 fn predict(
29 &self,
30 ctx: &IntraPredContext,
31 output: &mut [u16],
32 stride: usize,
33 dims: BlockDimensions,
34 );
35}
36
37#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
39#[repr(u8)]
40pub enum IntraMode {
41 #[default]
43 Dc = 0,
44 Vertical = 1,
46 Horizontal = 2,
48 D45 = 3,
50 D135 = 4,
52 D113 = 5,
54 D157 = 6,
56 D203 = 7,
58 D67 = 8,
60 Smooth = 9,
62 SmoothV = 10,
64 SmoothH = 11,
66 Paeth = 12,
68 FilterIntra = 13,
70}
71
72impl IntraMode {
73 pub const COUNT: usize = 14;
75
76 #[must_use]
78 pub const fn is_directional(self) -> bool {
79 matches!(
80 self,
81 Self::Vertical
82 | Self::Horizontal
83 | Self::D45
84 | Self::D135
85 | Self::D113
86 | Self::D157
87 | Self::D203
88 | Self::D67
89 )
90 }
91
92 #[must_use]
94 pub const fn is_smooth(self) -> bool {
95 matches!(self, Self::Smooth | Self::SmoothV | Self::SmoothH)
96 }
97
98 #[must_use]
101 pub const fn nominal_angle(self) -> Option<u16> {
102 match self {
103 Self::Vertical => Some(90),
104 Self::Horizontal => Some(180),
105 Self::D45 => Some(45),
106 Self::D135 => Some(135),
107 Self::D113 => Some(113),
108 Self::D157 => Some(157),
109 Self::D203 => Some(203),
110 Self::D67 => Some(67),
111 _ => None,
112 }
113 }
114
115 #[must_use]
117 pub const fn from_u8(value: u8) -> Option<Self> {
118 match value {
119 0 => Some(Self::Dc),
120 1 => Some(Self::Vertical),
121 2 => Some(Self::Horizontal),
122 3 => Some(Self::D45),
123 4 => Some(Self::D135),
124 5 => Some(Self::D113),
125 6 => Some(Self::D157),
126 7 => Some(Self::D203),
127 8 => Some(Self::D67),
128 9 => Some(Self::Smooth),
129 10 => Some(Self::SmoothV),
130 11 => Some(Self::SmoothH),
131 12 => Some(Self::Paeth),
132 13 => Some(Self::FilterIntra),
133 _ => None,
134 }
135 }
136}
137
138#[derive(Clone, Copy, Debug, PartialEq, Eq)]
140pub struct DirectionalMode {
141 pub angle: u16,
143 pub delta: AngleDelta,
145}
146
147impl DirectionalMode {
148 #[must_use]
150 pub const fn new(angle: u16) -> Self {
151 Self {
152 angle,
153 delta: AngleDelta::Zero,
154 }
155 }
156
157 #[must_use]
159 pub const fn with_delta(angle: u16, delta: AngleDelta) -> Self {
160 Self { angle, delta }
161 }
162
163 #[must_use]
165 pub const fn effective_angle(self) -> i16 {
166 self.angle as i16 + self.delta.degrees()
167 }
168
169 #[must_use]
171 pub const fn is_vertical_ish(self) -> bool {
172 let angle = self.effective_angle();
173 angle > 45 && angle < 135
174 }
175
176 #[must_use]
178 pub const fn is_horizontal_ish(self) -> bool {
179 let angle = self.effective_angle();
180 angle > 135 && angle < 225
181 }
182}
183
184impl Default for DirectionalMode {
185 fn default() -> Self {
186 Self::new(90) }
188}
189
190#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
195#[repr(i8)]
196pub enum AngleDelta {
197 Minus3 = -3,
199 Minus2 = -2,
201 Minus1 = -1,
203 #[default]
205 Zero = 0,
206 Plus1 = 1,
208 Plus2 = 2,
210 Plus3 = 3,
212}
213
214impl AngleDelta {
215 pub const STEP_DEGREES: i16 = 3;
217
218 #[must_use]
220 pub const fn degrees(self) -> i16 {
221 (self as i8 as i16) * Self::STEP_DEGREES
222 }
223
224 #[must_use]
226 pub const fn from_steps(steps: i8) -> Option<Self> {
227 match steps {
228 -3 => Some(Self::Minus3),
229 -2 => Some(Self::Minus2),
230 -1 => Some(Self::Minus1),
231 0 => Some(Self::Zero),
232 1 => Some(Self::Plus1),
233 2 => Some(Self::Plus2),
234 3 => Some(Self::Plus3),
235 _ => None,
236 }
237 }
238
239 #[must_use]
241 pub const fn steps(self) -> i8 {
242 self as i8
243 }
244}
245
246#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
248pub enum NonDirectionalMode {
249 #[default]
251 Dc,
252 Smooth,
254 SmoothV,
256 SmoothH,
258 Paeth,
260}
261
262impl NonDirectionalMode {
263 #[must_use]
265 pub const fn from_intra_mode(mode: IntraMode) -> Option<Self> {
266 match mode {
267 IntraMode::Dc => Some(Self::Dc),
268 IntraMode::Smooth => Some(Self::Smooth),
269 IntraMode::SmoothV => Some(Self::SmoothV),
270 IntraMode::SmoothH => Some(Self::SmoothH),
271 IntraMode::Paeth => Some(Self::Paeth),
272 _ => None,
273 }
274 }
275}
276
277pub const MODE_ANGLE_TABLE: [u16; 8] = [
281 90, 180, 45, 135, 113, 157, 203, 67, ];
290
291#[derive(Clone, Copy, Debug, PartialEq, Eq)]
293pub enum Vp9DirectionalMode {
294 Vertical = 0,
296 Horizontal = 1,
298 D45 = 2,
300 D135 = 3,
302 D117 = 4,
304 D153 = 5,
306 D207 = 6,
308 D63 = 7,
310}
311
312impl Vp9DirectionalMode {
313 #[must_use]
315 pub const fn angle(self) -> u16 {
316 match self {
317 Self::Vertical => 90,
318 Self::Horizontal => 180,
319 Self::D45 => 45,
320 Self::D135 => 135,
321 Self::D117 => 117,
322 Self::D153 => 153,
323 Self::D207 => 207,
324 Self::D63 => 63,
325 }
326 }
327
328 #[must_use]
330 pub const fn to_directional(self) -> DirectionalMode {
331 DirectionalMode::new(self.angle())
332 }
333}
334
335pub const EXTENDED_ANGLES: [i16; 7] = [
339 -9, -6, -3, 0, 3, 6, 9, ];
347
348#[must_use]
350pub const fn calculate_effective_angle(base_angle: u16, delta: AngleDelta) -> i16 {
351 base_angle as i16 + delta.degrees()
352}
353
354#[must_use]
359pub fn get_direction_delta(angle: i16) -> (i32, i32) {
360 let radians = (angle as f64) * std::f64::consts::PI / 180.0;
367
368 let dx = (radians.sin() * 256.0).round() as i32;
371 let dy = (radians.cos() * 256.0).round() as i32;
372
373 (dx, dy)
374}
375
376pub const DX_TABLE: [i32; 91] = {
379 let mut table = [0i32; 91];
380 let mut i = 0;
381 while i < 91 {
382 table[i] = match i {
385 0 => 0,
386 15 => 66,
387 30 => 128,
388 45 => 181,
389 60 => 222,
390 75 => 247,
391 90 => 256,
392 _ => {
393 let base = (i / 15) * 15;
395 let next = base + 15;
396 if next > 90 {
397 256
398 } else {
399 let base_val = match base {
400 0 => 0,
401 15 => 66,
402 30 => 128,
403 45 => 181,
404 60 => 222,
405 75 => 247,
406 _ => 0,
407 };
408 let next_val = match next {
409 15 => 66,
410 30 => 128,
411 45 => 181,
412 60 => 222,
413 75 => 247,
414 90 => 256,
415 _ => 256,
416 };
417 base_val + ((next_val - base_val) * ((i - base) as i32)) / 15
418 }
419 }
420 };
421 i += 1;
422 }
423 table
424};
425
426pub const DY_TABLE: [i32; 91] = {
428 let mut table = [0i32; 91];
429 let mut i = 0;
430 while i < 91 {
431 table[i] = match i {
433 0 => 256,
434 15 => 247,
435 30 => 222,
436 45 => 181,
437 60 => 128,
438 75 => 66,
439 90 => 0,
440 _ => {
441 let base = (i / 15) * 15;
442 let next = base + 15;
443 if next > 90 {
444 0
445 } else {
446 let base_val = match base {
447 0 => 256,
448 15 => 247,
449 30 => 222,
450 45 => 181,
451 60 => 128,
452 75 => 66,
453 _ => 0,
454 };
455 let next_val = match next {
456 15 => 247,
457 30 => 222,
458 45 => 181,
459 60 => 128,
460 75 => 66,
461 90 => 0,
462 _ => 0,
463 };
464 base_val + ((next_val - base_val) * ((i - base) as i32)) / 15
465 }
466 }
467 };
468 i += 1;
469 }
470 table
471};
472
473#[cfg(test)]
474mod tests {
475 use super::*;
476
477 #[test]
478 fn test_intra_mode_directional() {
479 assert!(IntraMode::Vertical.is_directional());
480 assert!(IntraMode::Horizontal.is_directional());
481 assert!(IntraMode::D45.is_directional());
482 assert!(IntraMode::D135.is_directional());
483 assert!(!IntraMode::Dc.is_directional());
484 assert!(!IntraMode::Smooth.is_directional());
485 assert!(!IntraMode::Paeth.is_directional());
486 }
487
488 #[test]
489 fn test_intra_mode_smooth() {
490 assert!(IntraMode::Smooth.is_smooth());
491 assert!(IntraMode::SmoothV.is_smooth());
492 assert!(IntraMode::SmoothH.is_smooth());
493 assert!(!IntraMode::Dc.is_smooth());
494 assert!(!IntraMode::Vertical.is_smooth());
495 }
496
497 #[test]
498 fn test_angle_delta() {
499 assert_eq!(AngleDelta::Zero.degrees(), 0);
500 assert_eq!(AngleDelta::Plus1.degrees(), 3);
501 assert_eq!(AngleDelta::Plus3.degrees(), 9);
502 assert_eq!(AngleDelta::Minus1.degrees(), -3);
503 assert_eq!(AngleDelta::Minus3.degrees(), -9);
504 }
505
506 #[test]
507 fn test_directional_mode() {
508 let mode = DirectionalMode::new(90);
509 assert_eq!(mode.effective_angle(), 90);
510 assert!(mode.is_vertical_ish());
511 assert!(!mode.is_horizontal_ish());
512
513 let mode_with_delta = DirectionalMode::with_delta(90, AngleDelta::Plus2);
514 assert_eq!(mode_with_delta.effective_angle(), 96);
515 }
516
517 #[test]
518 fn test_vp9_directional_mode() {
519 assert_eq!(Vp9DirectionalMode::Vertical.angle(), 90);
520 assert_eq!(Vp9DirectionalMode::Horizontal.angle(), 180);
521 assert_eq!(Vp9DirectionalMode::D45.angle(), 45);
522 assert_eq!(Vp9DirectionalMode::D117.angle(), 117);
523 }
524
525 #[test]
526 fn test_intra_mode_from_u8() {
527 assert_eq!(IntraMode::from_u8(0), Some(IntraMode::Dc));
528 assert_eq!(IntraMode::from_u8(1), Some(IntraMode::Vertical));
529 assert_eq!(IntraMode::from_u8(12), Some(IntraMode::Paeth));
530 assert_eq!(IntraMode::from_u8(100), None);
531 }
532
533 #[test]
534 fn test_nominal_angles() {
535 assert_eq!(IntraMode::Vertical.nominal_angle(), Some(90));
536 assert_eq!(IntraMode::Horizontal.nominal_angle(), Some(180));
537 assert_eq!(IntraMode::D45.nominal_angle(), Some(45));
538 assert_eq!(IntraMode::Dc.nominal_angle(), None);
539 }
540
541 #[test]
542 fn test_angle_delta_from_steps() {
543 assert_eq!(AngleDelta::from_steps(0), Some(AngleDelta::Zero));
544 assert_eq!(AngleDelta::from_steps(3), Some(AngleDelta::Plus3));
545 assert_eq!(AngleDelta::from_steps(-3), Some(AngleDelta::Minus3));
546 assert_eq!(AngleDelta::from_steps(4), None);
547 assert_eq!(AngleDelta::from_steps(-4), None);
548 }
549}