fix clippy warnings
[nihav.git] / nihav-duck / src / codecs / vpenc / motion_est.rs
CommitLineData
19cfcd2f
KS
1use nihav_codec_support::codecs::{MV, ZERO_MV};
2
3use std::str::FromStr;
4
e6aaad5c 5#[derive(Debug,Clone,Copy,PartialEq,Default)]
c5d5793c 6#[allow(dead_code)]
19cfcd2f
KS
7pub enum MVSearchMode {
8 Full,
c5d5793c 9 SEA,
19cfcd2f 10 Diamond,
e6aaad5c 11 #[default]
19cfcd2f 12 Hexagon,
c5d5793c 13 EPZS,
19cfcd2f
KS
14}
15
19cfcd2f
KS
16pub struct ParseError{}
17
18impl FromStr for MVSearchMode {
19 type Err = ParseError;
20
21 #[allow(clippy::single_match)]
22 fn from_str(s: &str) -> Result<Self, Self::Err> {
23 match s {
24 "full" => Ok(MVSearchMode::Full),
c5d5793c 25 "sea" => Ok(MVSearchMode::SEA),
19cfcd2f
KS
26 "dia" => Ok(MVSearchMode::Diamond),
27 "hex" => Ok(MVSearchMode::Hexagon),
c5d5793c 28 "epzs" => Ok(MVSearchMode::EPZS),
19cfcd2f
KS
29 _ => Err(ParseError{}),
30 }
31 }
32}
33
34impl std::fmt::Display for MVSearchMode {
35 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
36 match *self {
37 MVSearchMode::Full => write!(f, "full"),
c5d5793c 38 MVSearchMode::SEA => write!(f, "sea"),
19cfcd2f
KS
39 MVSearchMode::Diamond => write!(f, "dia"),
40 MVSearchMode::Hexagon => write!(f, "hex"),
c5d5793c 41 MVSearchMode::EPZS => write!(f, "epzs"),
19cfcd2f
KS
42 }
43 }
44}
45
46trait FromPixels {
47 fn from_pixels(self) -> Self;
48}
49
50impl FromPixels for MV {
51 fn from_pixels(self) -> MV {
52 MV { x: self.x * 4, y: self.y * 4 }
53 }
54}
55
56pub const DIA_PATTERN: [MV; 9] = [
57 ZERO_MV,
58 MV {x: -2, y: 0},
59 MV {x: -1, y: 1},
60 MV {x: 0, y: 2},
61 MV {x: 1, y: 1},
62 MV {x: 2, y: 0},
63 MV {x: 1, y: -1},
64 MV {x: 0, y: -2},
65 MV {x: -1, y: -1}
66];
67
68pub const HEX_PATTERN: [MV; 7] = [
69 ZERO_MV,
70 MV {x: -2, y: 0},
71 MV {x: -1, y: 2},
72 MV {x: 1, y: 2},
73 MV {x: 2, y: 0},
74 MV {x: 1, y: -2},
75 MV {x: -1, y: -2}
76];
77
78pub const REFINEMENT: [MV; 4] = [
79 MV {x: -1, y: 0},
80 MV {x: 0, y: 1},
81 MV {x: 1, y: 0},
82 MV {x: 0, y: -1}
83];
84
85#[macro_export]
86macro_rules! search_template {
c5d5793c
KS
87 ($self: expr, $mv_est: expr, $cur_blk: expr, $mb_x: expr, $mb_y: expr, $sad_func: ident, $threshold: expr) => ({
88 search_template!($self, $mv_est, $cur_blk, $mb_x, $mb_y, $sad_func, $threshold, ZERO_MV, MAX_DIST, true)
89 });
90 ($self: expr, $mv_est: expr, $cur_blk: expr, $mb_x: expr, $mb_y: expr, $sad_func: ident, $threshold: expr, $start_mv: expr, $best_dist: expr, $fullpel_stage: expr) => ({
91 let mut best_dist = $best_dist;
92 let mut best_mv = $start_mv;
19cfcd2f
KS
93
94 let mut min_dist;
95 let mut min_idx;
96
c5d5793c
KS
97 if $fullpel_stage {
98 $self.reset();
99 loop {
100 let mut cur_best_dist = best_dist;
101 for (dist, &point) in $self.dist.iter_mut().zip($self.point.iter()) {
102 if *dist == MAX_DIST {
103 *dist = $mv_est.$sad_func($cur_blk, $mb_x, $mb_y, point.from_pixels(), cur_best_dist);
104 cur_best_dist = cur_best_dist.min(*dist);
105 if *dist <= $threshold {
106 break;
107 }
19cfcd2f
KS
108 }
109 }
c5d5793c
KS
110 min_dist = $self.dist[0];
111 min_idx = 0;
112 for (i, &dist) in $self.dist.iter().enumerate().skip(1) {
113 if dist < min_dist {
114 min_dist = dist;
115 min_idx = i;
116 if dist <= $threshold {
117 break;
118 }
19cfcd2f
KS
119 }
120 }
c5d5793c
KS
121 if min_dist <= $threshold || min_idx == 0 || best_dist == min_dist || $self.point[min_idx].x.abs() >= $mv_est.mv_range || $self.point[min_idx].y.abs() >= $mv_est.mv_range {
122 break;
123 }
124 best_dist = min_dist;
125 $self.update($self.steps[min_idx]);
19cfcd2f
KS
126 }
127 best_dist = min_dist;
c5d5793c
KS
128 best_mv = $self.point[min_idx];
129 if best_dist <= $threshold {
130 return (best_mv.from_pixels(), best_dist);
131 }
132 for &step in REFINEMENT.iter() {
133 let mv = best_mv + step;
134 let dist = $mv_est.$sad_func($cur_blk, $mb_x, $mb_y, mv.from_pixels(), MAX_DIST);
135 if best_dist > dist {
136 best_dist = dist;
137 best_mv = mv;
138 }
139 }
140 best_mv = best_mv.from_pixels();
141 if best_dist <= $threshold {
142 return (best_mv, best_dist);
19cfcd2f 143 }
19cfcd2f
KS
144 }
145
146 // subpel refinement
147 $self.set_new_point(best_mv, best_dist);
148 loop {
149 let mut cur_best_dist = best_dist;
150 for (dist, &point) in $self.dist.iter_mut().zip($self.point.iter()) {
151 if *dist == MAX_DIST {
152 *dist = $mv_est.$sad_func($cur_blk, $mb_x, $mb_y, point, cur_best_dist);
153 cur_best_dist = cur_best_dist.min(*dist);
c5d5793c 154 if *dist <= $threshold {
19cfcd2f
KS
155 break;
156 }
157 }
158 }
159 min_dist = $self.dist[0];
160 min_idx = 0;
161 for (i, &dist) in $self.dist.iter().enumerate().skip(1) {
162 if dist < min_dist {
163 min_dist = dist;
164 min_idx = i;
c5d5793c 165 if dist <= $threshold {
19cfcd2f
KS
166 break;
167 }
168 }
169 }
c5d5793c 170 if min_dist <= $threshold || min_idx == 0 || best_dist == min_dist || $self.point[min_idx].x.abs() >= $mv_est.mv_range * 8 || $self.point[min_idx].y.abs() >= $mv_est.mv_range * 8 {
19cfcd2f
KS
171 break;
172 }
173 best_dist = min_dist;
174 $self.update($self.steps[min_idx]);
175 }
176 best_dist = min_dist;
177 best_mv = $self.point[min_idx];
c5d5793c 178 if best_dist <= $threshold {
19cfcd2f
KS
179 return (best_mv, best_dist);
180 }
181 for &step in REFINEMENT.iter() {
182 let mv = best_mv + step;
183 let dist = $mv_est.$sad_func($cur_blk, $mb_x, $mb_y, mv, MAX_DIST);
184 if best_dist > dist {
185 best_dist = dist;
186 best_mv = mv;
187 }
188 }
189 (best_mv, best_dist)
c5d5793c 190 });
19cfcd2f 191}