core/reorder: simplify I/P/B reorderer
[nihav.git] / nihav-core / src / scale / mod.rs
CommitLineData
03accf76
KS
1use crate::frame::*;
2
3mod kernel;
4
5mod colorcvt;
6mod repack;
7mod scale;
8
9#[derive(Clone,Copy,PartialEq)]
10pub struct ScaleInfo {
11 pub fmt: NAPixelFormaton,
12 pub width: usize,
13 pub height: usize,
14}
15
16impl std::fmt::Display for ScaleInfo {
17 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
18 write!(f, "({}x{}, {})", self.width, self.height, self.fmt)
19 }
20}
21
22#[derive(Debug,Clone,Copy,PartialEq)]
23#[allow(dead_code)]
24pub enum ScaleError {
25 NoFrame,
26 AllocError,
27 InvalidArgument,
28 NotImplemented,
29 Bug,
30}
31
32pub type ScaleResult<T> = Result<T, ScaleError>;
33
34/*trait Kernel {
35 fn init(&mut self, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo) -> ScaleResult<NABufferType>;
36 fn process(&mut self, pic_in: &NABufferType, pic_out: &mut NABufferType);
37}*/
38
39struct KernelDesc {
40 name: &'static str,
6011e201 41 create: fn () -> Box<dyn kernel::Kernel>,
03accf76
KS
42}
43
44impl KernelDesc {
6011e201 45 fn find(name: &str) -> ScaleResult<Box<dyn kernel::Kernel>> {
03accf76
KS
46 for kern in KERNELS.iter() {
47 if kern.name == name {
48 return Ok((kern.create)());
49 }
50 }
51 Err(ScaleError::InvalidArgument)
52 }
53}
54
55const KERNELS: &[KernelDesc] = &[
56 KernelDesc { name: "pack", create: repack::create_pack },
57 KernelDesc { name: "unpack", create: repack::create_unpack },
58 KernelDesc { name: "depal", create: repack::create_depal },
59 KernelDesc { name: "scale", create: scale::create_scale },
60 KernelDesc { name: "rgb_to_yuv", create: colorcvt::create_rgb2yuv },
61 KernelDesc { name: "yuv_to_rgb", create: colorcvt::create_yuv2rgb },
62];
63
64struct Stage {
65 fmt_out: ScaleInfo,
66 tmp_pic: NABufferType,
67 next: Option<Box<Stage>>,
6011e201 68 worker: Box<dyn kernel::Kernel>,
03accf76
KS
69}
70
71pub fn get_scale_fmt_from_pic(pic: &NABufferType) -> ScaleInfo {
72 let info = pic.get_video_info().unwrap();
73 ScaleInfo { fmt: info.get_format(), width: info.get_width(), height: info.get_height() }
74}
75
76impl Stage {
77 fn new(name: &str, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo) -> ScaleResult<Self> {
78 let mut worker = KernelDesc::find(name)?;
79 let tmp_pic = worker.init(in_fmt, dest_fmt)?;
80 let fmt_out = get_scale_fmt_from_pic(&tmp_pic);
81 Ok(Self { fmt_out, tmp_pic, next: None, worker })
82 }
83 fn add(&mut self, new: Stage) {
84 if let Some(ref mut next) = self.next {
85 next.add(new);
86 } else {
87 self.next = Some(Box::new(new));
88 }
89 }
90 fn process(&mut self, pic_in: &NABufferType, pic_out: &mut NABufferType) -> ScaleResult<()> {
91 if let Some(ref mut nextstage) = self.next {
92 self.worker.process(pic_in, &mut self.tmp_pic);
93 nextstage.process(&self.tmp_pic, pic_out)?;
94 } else {
95 self.worker.process(pic_in, pic_out);
96 }
97 Ok(())
98 }
99 fn drop_last_tmp(&mut self) {
100 if let Some(ref mut nextstage) = self.next {
101 nextstage.drop_last_tmp();
102 } else {
103 self.tmp_pic = NABufferType::None;
104 }
105 }
106}
107
108pub struct NAScale {
109 fmt_in: ScaleInfo,
110 fmt_out: ScaleInfo,
111 just_convert: bool,
112 pipeline: Option<Stage>,
113}
114
115fn check_format(in_fmt: NAVideoInfo, ref_fmt: &ScaleInfo, just_convert: bool) -> ScaleResult<()> {
116 if in_fmt.get_format() != ref_fmt.fmt { return Err(ScaleError::InvalidArgument); }
117 if !just_convert && (in_fmt.get_width() != ref_fmt.width || in_fmt.get_height() != ref_fmt.height) {
118 return Err(ScaleError::InvalidArgument);
119 }
120 Ok(())
121}
122
123fn copy(pic_in: &NABufferType, pic_out: &mut NABufferType)
124{
125 if let (Some(ref sbuf), Some(ref mut dbuf)) = (pic_in.get_vbuf(), pic_out.get_vbuf()) {
126 let sdata = sbuf.get_data();
127 let ddata = dbuf.get_data_mut().unwrap();
128 ddata.copy_from_slice(&sdata[0..]);
129 } else {
130 unimplemented!();
131 }
132}
133
134macro_rules! add_stage {
135 ($head:expr, $new:expr) => {
136 if let Some(ref mut h) = $head {
137 h.add($new);
138 } else {
139 $head = Some($new);
140 }
141 };
142}
143fn is_better_fmt(a: &ScaleInfo, b: &ScaleInfo) -> bool {
144 if (a.width >= b.width) && (a.height >= b.height) {
145 return true;
146 }
147 if a.fmt.get_max_depth() > b.fmt.get_max_depth() {
148 return true;
149 }
150 if a.fmt.get_max_subsampling() < b.fmt.get_max_subsampling() {
151 return true;
152 }
153 false
154}
155fn build_pipeline(ifmt: &ScaleInfo, ofmt: &ScaleInfo, just_convert: bool) -> ScaleResult<Option<Stage>> {
156 let inname = ifmt.fmt.get_model().get_short_name();
157 let outname = ofmt.fmt.get_model().get_short_name();
158
159println!("convert {} -> {}", ifmt, ofmt);
e243ceb4 160 let needs_scale = if (ofmt.fmt.get_max_subsampling() > 0) &&
03accf76 161 (ofmt.fmt.get_max_subsampling() != ifmt.fmt.get_max_subsampling()) {
e243ceb4
KS
162 true
163 } else {
164 !just_convert
165 };
03accf76
KS
166 let needs_unpack = needs_scale || !ifmt.fmt.is_unpacked();
167 let needs_pack = !ofmt.fmt.is_unpacked();
e243ceb4 168 let needs_convert = inname != outname;
03accf76
KS
169 let scale_before_cvt = is_better_fmt(&ifmt, &ofmt) && needs_convert
170 && (ofmt.fmt.get_max_subsampling() == 0);
171//todo stages for model and gamma conversion
172
173 let mut stages: Option<Stage> = None;
174 let mut cur_fmt = *ifmt;
175
176 if needs_unpack {
177println!("[adding unpack]");
e243ceb4
KS
178 let new_stage = if !cur_fmt.fmt.is_paletted() {
179 Stage::new("unpack", &cur_fmt, &ofmt)?
180 } else {
181 Stage::new("depal", &cur_fmt, &ofmt)?
182 };
03accf76
KS
183 cur_fmt = new_stage.fmt_out;
184 add_stage!(stages, new_stage);
185 }
186 if needs_scale && scale_before_cvt {
187println!("[adding scale]");
188 let new_stage = Stage::new("scale", &cur_fmt, &ofmt)?;
189 cur_fmt = new_stage.fmt_out;
190 add_stage!(stages, new_stage);
191 }
192 if needs_convert {
193println!("[adding convert]");
194 let cvtname = format!("{}_to_{}", inname, outname);
195println!("[{}]", cvtname);
196 let new_stage = Stage::new(&cvtname, &cur_fmt, &ofmt)?;
197//todo if fails try converting via RGB or YUV
198 cur_fmt = new_stage.fmt_out;
199 add_stage!(stages, new_stage);
200//todo alpha plane copy/add
201 }
202 if needs_scale && !scale_before_cvt {
203println!("[adding scale]");
204 let new_stage = Stage::new("scale", &cur_fmt, &ofmt)?;
205 cur_fmt = new_stage.fmt_out;
206 add_stage!(stages, new_stage);
207 }
208//todo flip if needed
209 if needs_pack {
210println!("[adding pack]");
211 let new_stage = Stage::new("pack", &cur_fmt, &ofmt)?;
212 //cur_fmt = new_stage.fmt_out;
213 add_stage!(stages, new_stage);
214 }
215
216 if let Some(ref mut head) = stages {
217 head.drop_last_tmp();
218 }
219
220 Ok(stages)
221}
222
223impl NAScale {
224 pub fn new(fmt_in: ScaleInfo, fmt_out: ScaleInfo) -> ScaleResult<Self> {
225 let pipeline;
226 let just_convert = (fmt_in.width == fmt_out.width) && (fmt_in.height == fmt_out.height);
227 if fmt_in != fmt_out {
228 pipeline = build_pipeline(&fmt_in, &fmt_out, just_convert)?;
229 } else {
230 pipeline = None;
231 }
232 Ok(Self { fmt_in, fmt_out, just_convert, pipeline })
233 }
234 pub fn needs_processing(&self) -> bool { self.pipeline.is_some() }
235 pub fn get_in_fmt(&self) -> ScaleInfo { self.fmt_in }
236 pub fn get_out_fmt(&self) -> ScaleInfo { self.fmt_out }
237 pub fn convert(&mut self, pic_in: &NABufferType, pic_out: &mut NABufferType) -> ScaleResult<()> {
238 let in_info = pic_in.get_video_info();
239 let out_info = pic_out.get_video_info();
240 if in_info.is_none() || out_info.is_none() { return Err(ScaleError::InvalidArgument); }
241 let in_info = in_info.unwrap();
242 let out_info = out_info.unwrap();
243 if self.just_convert &&
244 (in_info.get_width() != out_info.get_width() || in_info.get_height() != out_info.get_height()) {
245 return Err(ScaleError::InvalidArgument);
246 }
247 check_format(in_info, &self.fmt_in, self.just_convert)?;
248 check_format(out_info, &self.fmt_out, self.just_convert)?;
249 if let Some(ref mut pipe) = self.pipeline {
250 pipe.process(pic_in, pic_out)
251 } else {
252 copy(pic_in, pic_out);
253 Ok(())
254 }
255 }
256}
257
258#[cfg(test)]
259mod test {
260 use super::*;
261
262 fn fill_pic(pic: &mut NABufferType, val: u8) {
263 if let Some(ref mut buf) = pic.get_vbuf() {
264 let data = buf.get_data_mut().unwrap();
265 for el in data.iter_mut() { *el = val; }
266 } else if let Some(ref mut buf) = pic.get_vbuf16() {
267 let data = buf.get_data_mut().unwrap();
268 for el in data.iter_mut() { *el = val as u16; }
269 } else if let Some(ref mut buf) = pic.get_vbuf32() {
270 let data = buf.get_data_mut().unwrap();
271 for el in data.iter_mut() { *el = (val as u32) * 0x01010101; }
272 }
273 }
274 #[test]
275 fn test_convert() {
276 let mut in_pic = alloc_video_buffer(NAVideoInfo::new(1, 1, false, RGB565_FORMAT), 3).unwrap();
277 fill_pic(&mut in_pic, 42);
278 let mut out_pic = alloc_video_buffer(NAVideoInfo::new(1, 1, false, RGB24_FORMAT), 3).unwrap();
279 fill_pic(&mut out_pic, 0);
280 let ifmt = get_scale_fmt_from_pic(&in_pic);
281 let ofmt = get_scale_fmt_from_pic(&out_pic);
282 let mut scaler = NAScale::new(ifmt, ofmt).unwrap();
283 scaler.convert(&in_pic, &mut out_pic).unwrap();
284 let obuf = out_pic.get_vbuf().unwrap();
285 let odata = obuf.get_data();
286 assert_eq!(odata[0], 0x0);
287 assert_eq!(odata[1], 0x4);
288 assert_eq!(odata[2], 0x52);
289
290 let mut in_pic = alloc_video_buffer(NAVideoInfo::new(4, 4, false, RGB24_FORMAT), 3).unwrap();
291 fill_pic(&mut in_pic, 42);
292 let mut out_pic = alloc_video_buffer(NAVideoInfo::new(4, 4, false, YUV420_FORMAT), 3).unwrap();
293 fill_pic(&mut out_pic, 0);
294 let ifmt = get_scale_fmt_from_pic(&in_pic);
295 let ofmt = get_scale_fmt_from_pic(&out_pic);
296 let mut scaler = NAScale::new(ifmt, ofmt).unwrap();
297 scaler.convert(&in_pic, &mut out_pic).unwrap();
298 let obuf = out_pic.get_vbuf().unwrap();
299 let yoff = obuf.get_offset(0);
300 let uoff = obuf.get_offset(1);
301 let voff = obuf.get_offset(2);
302 let odata = obuf.get_data();
303 assert_eq!(odata[yoff], 42);
304 assert!(((odata[uoff] ^ 0x80) as i8).abs() <= 1);
305 assert!(((odata[voff] ^ 0x80) as i8).abs() <= 1);
306 let mut scaler = NAScale::new(ofmt, ifmt).unwrap();
307 scaler.convert(&out_pic, &mut in_pic).unwrap();
308 let obuf = in_pic.get_vbuf().unwrap();
309 let odata = obuf.get_data();
310 assert_eq!(odata[0], 42);
311 }
312 #[test]
313 fn test_scale() {
314 let mut in_pic = alloc_video_buffer(NAVideoInfo::new(2, 2, false, RGB565_FORMAT), 3).unwrap();
315 fill_pic(&mut in_pic, 42);
316 let mut out_pic = alloc_video_buffer(NAVideoInfo::new(3, 3, false, RGB565_FORMAT), 3).unwrap();
317 fill_pic(&mut out_pic, 0);
318 let ifmt = get_scale_fmt_from_pic(&in_pic);
319 let ofmt = get_scale_fmt_from_pic(&out_pic);
320 let mut scaler = NAScale::new(ifmt, ofmt).unwrap();
321 scaler.convert(&in_pic, &mut out_pic).unwrap();
322 let obuf = out_pic.get_vbuf16().unwrap();
323 let odata = obuf.get_data();
324 assert_eq!(odata[0], 42);
325 }
326 #[test]
327 fn test_scale_and_convert() {
328 let mut in_pic = alloc_video_buffer(NAVideoInfo::new(7, 3, false, RGB565_FORMAT), 3).unwrap();
329 fill_pic(&mut in_pic, 42);
330 let mut out_pic = alloc_video_buffer(NAVideoInfo::new(4, 4, false, YUV420_FORMAT), 3).unwrap();
331 fill_pic(&mut out_pic, 0);
332 let ifmt = get_scale_fmt_from_pic(&in_pic);
333 let ofmt = get_scale_fmt_from_pic(&out_pic);
334 let mut scaler = NAScale::new(ifmt, ofmt).unwrap();
335 scaler.convert(&in_pic, &mut out_pic).unwrap();
336 let obuf = out_pic.get_vbuf().unwrap();
337 let yoff = obuf.get_offset(0);
338 let uoff = obuf.get_offset(1);
339 let voff = obuf.get_offset(2);
340 let odata = obuf.get_data();
341 assert_eq!(odata[yoff], 28);
342 assert_eq!(odata[uoff], 154);
343 assert_eq!(odata[voff], 103);
344 }
345}