]> git.nihav.org Git - nihav.git/blame_incremental - nihav-core/src/scale/mod.rs
nihav_misc/avix: remove unneeded mut qualifier
[nihav.git] / nihav-core / src / scale / mod.rs
... / ...
CommitLineData
1//! Image conversion functionality.
2
3//! # Examples
4//!
5//! Convert input image into YUV one and scale down two times.
6//! ```no_run
7//! use nihav_core::scale::*;
8//! use nihav_core::formats::{RGB24_FORMAT, YUV420_FORMAT};
9//! use nihav_core::frame::{alloc_video_buffer, NAVideoInfo};
10//!
11//! let mut in_pic = alloc_video_buffer(NAVideoInfo::new(640, 480, false, RGB24_FORMAT), 4).unwrap();
12//! let mut out_pic = alloc_video_buffer(NAVideoInfo::new(320, 240, false, YUV420_FORMAT), 4).unwrap();
13//! let in_fmt = get_scale_fmt_from_pic(&in_pic);
14//! let out_fmt = get_scale_fmt_from_pic(&out_pic);
15//! let mut scaler = NAScale::new(in_fmt, out_fmt).unwrap();
16//! scaler.convert(&in_pic, &mut out_pic).unwrap();
17//! ```
18use crate::frame::*;
19
20mod kernel;
21
22mod colourcvt;
23mod depth;
24mod fill;
25mod repack;
26#[allow(clippy::module_inception)]
27mod scale;
28
29mod palette;
30
31pub use crate::scale::palette::{palettise_frame, QuantisationMode, PaletteSearchMode};
32
33/// Image format information used by the converter.
34#[derive(Clone,Copy,PartialEq)]
35pub struct ScaleInfo {
36 /// Pixel format description.
37 pub fmt: NAPixelFormaton,
38 /// Image width.
39 pub width: usize,
40 /// Image height.
41 pub height: usize,
42}
43
44impl std::fmt::Display for ScaleInfo {
45 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
46 write!(f, "({}x{}, {})", self.width, self.height, self.fmt)
47 }
48}
49
50/// A list specifying general image conversion errors.
51#[derive(Debug,Clone,Copy,PartialEq)]
52#[allow(dead_code)]
53pub enum ScaleError {
54 /// Input or output buffer contains no image data.
55 NoFrame,
56 /// Allocation failed.
57 AllocError,
58 /// Invalid argument.
59 InvalidArgument,
60 /// Feature is not implemented.
61 NotImplemented,
62 /// Internal implementation bug.
63 Bug,
64}
65
66/// A specialised `Result` type for image conversion operations.
67pub type ScaleResult<T> = Result<T, ScaleError>;
68
69/*trait Kernel {
70 fn init(&mut self, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo) -> ScaleResult<NABufferType>;
71 fn process(&mut self, pic_in: &NABufferType, pic_out: &mut NABufferType);
72}*/
73
74struct KernelDesc {
75 name: &'static str,
76 create: fn () -> Box<dyn kernel::Kernel>,
77}
78
79impl KernelDesc {
80 fn find(name: &str) -> ScaleResult<Box<dyn kernel::Kernel>> {
81 for kern in KERNELS.iter() {
82 if kern.name == name {
83 return Ok((kern.create)());
84 }
85 }
86 Err(ScaleError::InvalidArgument)
87 }
88}
89
90const KERNELS: &[KernelDesc] = &[
91 KernelDesc { name: "pack", create: repack::create_pack },
92 KernelDesc { name: "unpack", create: repack::create_unpack },
93 KernelDesc { name: "depal", create: repack::create_depal },
94 KernelDesc { name: "palette", create: palette::create_palettise },
95 KernelDesc { name: "scale", create: scale::create_scale },
96 KernelDesc { name: "shallow", create: depth::create_shallow },
97 KernelDesc { name: "fill", create: fill::create_fill },
98 KernelDesc { name: "rgb_to_yuv", create: colourcvt::create_rgb2yuv },
99 KernelDesc { name: "yuv_to_rgb", create: colourcvt::create_yuv2rgb },
100];
101
102struct Stage {
103 fmt_out: ScaleInfo,
104 tmp_pic: NABufferType,
105 next: Option<Box<Stage>>,
106 worker: Box<dyn kernel::Kernel>,
107}
108
109/// Converts input picture information into format used by scaler.
110pub fn get_scale_fmt_from_pic(pic: &NABufferType) -> ScaleInfo {
111 let info = pic.get_video_info().unwrap();
112 ScaleInfo { fmt: info.get_format(), width: info.get_width(), height: info.get_height() }
113}
114
115impl Stage {
116 fn new(name: &str, in_fmt: &ScaleInfo, dest_fmt: &ScaleInfo, options: &[(String, String)]) -> ScaleResult<Self> {
117 let mut worker = KernelDesc::find(name)?;
118 let tmp_pic = worker.init(in_fmt, dest_fmt, options)?;
119 let fmt_out = get_scale_fmt_from_pic(&tmp_pic);
120 Ok(Self { fmt_out, tmp_pic, next: None, worker })
121 }
122 fn add(&mut self, new: Stage) {
123 if let Some(ref mut next) = self.next {
124 next.add(new);
125 } else {
126 self.next = Some(Box::new(new));
127 }
128 }
129 fn process(&mut self, pic_in: &NABufferType, pic_out: &mut NABufferType) -> ScaleResult<()> {
130 if let Some(ref mut nextstage) = self.next {
131 self.worker.process(pic_in, &mut self.tmp_pic);
132 nextstage.process(&self.tmp_pic, pic_out)?;
133 } else {
134 self.worker.process(pic_in, pic_out);
135 }
136 Ok(())
137 }
138 fn drop_last_tmp(&mut self) {
139 if let Some(ref mut nextstage) = self.next {
140 nextstage.drop_last_tmp();
141 } else {
142 self.tmp_pic = NABufferType::None;
143 }
144 }
145}
146
147/// Image format converter.
148pub struct NAScale {
149 fmt_in: ScaleInfo,
150 fmt_out: ScaleInfo,
151 just_convert: bool,
152 pipeline: Option<Stage>,
153}
154
155fn check_format(in_fmt: NAVideoInfo, ref_fmt: &ScaleInfo, just_convert: bool) -> ScaleResult<()> {
156 if in_fmt.get_format() != ref_fmt.fmt { return Err(ScaleError::InvalidArgument); }
157 if !just_convert && (in_fmt.get_width() != ref_fmt.width || in_fmt.get_height() != ref_fmt.height) {
158 return Err(ScaleError::InvalidArgument);
159 }
160 Ok(())
161}
162
163fn copy(pic_in: &NABufferType, pic_out: &mut NABufferType)
164{
165 if let (Some(ref sbuf), Some(ref mut dbuf)) = (pic_in.get_vbuf(), pic_out.get_vbuf()) {
166 if sbuf.get_info().get_format().is_paletted() {
167 let same = sbuf.get_stride(0) == dbuf.get_stride(0) && sbuf.get_offset(1) == dbuf.get_offset(1);
168 if same {
169 let src = sbuf.get_data();
170 let dst = dbuf.get_data_mut().unwrap();
171 dst.copy_from_slice(src);
172 } else {
173 let (_, h) = sbuf.get_dimensions(0);
174 let soff = sbuf.get_offset(0);
175 let spoff = sbuf.get_offset(1);
176 let sstride = sbuf.get_stride(0);
177 let src = sbuf.get_data();
178 let doff = dbuf.get_offset(0);
179 let dpoff = dbuf.get_offset(1);
180 let dstride = dbuf.get_stride(0);
181 let dst = dbuf.get_data_mut().unwrap();
182 let copy_size = sstride.min(dstride);
183 for (dline, sline) in dst[doff..].chunks_exact_mut(dstride).take(h).zip(src[soff..].chunks_exact(sstride)) {
184 dline[..copy_size].copy_from_slice(&sline[..copy_size]);
185 }
186 dst[dpoff..].copy_from_slice(&src[spoff..]);
187 }
188 return;
189 }
190 let mut same = true;
191 let src_components = sbuf.get_info().get_format().get_num_comp();
192 let dst_components = dbuf.get_info().get_format().get_num_comp();
193 for i in 0..src_components.max(dst_components) {
194 if sbuf.get_stride(i) != dbuf.get_stride(i) {
195 same = false;
196 break;
197 }
198 if sbuf.get_offset(i) != dbuf.get_offset(i) {
199 same = false;
200 break;
201 }
202 }
203 if same {
204 let sdata = sbuf.get_data();
205 let ddata = dbuf.get_data_mut().unwrap();
206 let copy_len = sdata.len().min(ddata.len());
207 ddata[..copy_len].copy_from_slice(&sdata[..copy_len]);
208 } else {
209 let sdata = sbuf.get_data();
210 for comp in 0..src_components.min(dst_components) {
211 let (_, h) = sbuf.get_dimensions(comp);
212 let src = &sdata[sbuf.get_offset(comp)..];
213 let sstride = sbuf.get_stride(comp);
214 let doff = dbuf.get_offset(comp);
215 let dstride = dbuf.get_stride(comp);
216 let ddata = dbuf.get_data_mut().unwrap();
217 let dst = &mut ddata[doff..];
218 let copy_size = sstride.min(dstride);
219 if sstride == 0 && dstride == 0 {
220 continue;
221 }
222 for (dline, sline) in dst.chunks_exact_mut(dstride).take(h).zip(src.chunks_exact(sstride)) {
223 dline[..copy_size].copy_from_slice(&sline[..copy_size]);
224 }
225 }
226 }
227 } else if let (Some(ref sbuf), Some(ref mut dbuf)) = (pic_in.get_vbuf16(), pic_out.get_vbuf16()) {
228 let mut same = true;
229 let src_components = sbuf.get_info().get_format().get_num_comp();
230 let dst_components = dbuf.get_info().get_format().get_num_comp();
231 for i in 0..src_components.max(dst_components) {
232 if sbuf.get_stride(i) != dbuf.get_stride(i) {
233 same = false;
234 break;
235 }
236 if sbuf.get_offset(i) != dbuf.get_offset(i) {
237 same = false;
238 break;
239 }
240 }
241 if same {
242 let sdata = sbuf.get_data();
243 let ddata = dbuf.get_data_mut().unwrap();
244 let copy_len = sdata.len().min(ddata.len());
245 ddata[..copy_len].copy_from_slice(&sdata[..copy_len]);
246 } else {
247 let sdata = sbuf.get_data();
248 for comp in 0..src_components.min(dst_components) {
249 let (_, h) = sbuf.get_dimensions(comp);
250 let src = &sdata[sbuf.get_offset(comp)..];
251 let sstride = sbuf.get_stride(comp);
252 let doff = dbuf.get_offset(comp);
253 let dstride = dbuf.get_stride(comp);
254 let ddata = dbuf.get_data_mut().unwrap();
255 let dst = &mut ddata[doff..];
256 let copy_size = sstride.min(dstride);
257 for (dline, sline) in dst.chunks_exact_mut(dstride).take(h).zip(src.chunks_exact(sstride)) {
258 dline[..copy_size].copy_from_slice(&sline[..copy_size]);
259 }
260 }
261 }
262 } else {
263 unimplemented!();
264 }
265}
266
267macro_rules! add_stage {
268 ($head:expr, $new:expr) => {
269 if let Some(ref mut h) = $head {
270 h.add($new);
271 } else {
272 $head = Some($new);
273 }
274 };
275}
276fn is_better_fmt(a: &ScaleInfo, b: &ScaleInfo) -> bool {
277 if (a.width >= b.width) && (a.height >= b.height) {
278 return true;
279 }
280 if a.fmt.get_max_depth() > b.fmt.get_max_depth() {
281 return true;
282 }
283 if a.fmt.get_max_subsampling() < b.fmt.get_max_subsampling() {
284 return true;
285 }
286 false
287}
288fn fmt_needs_scale(ifmt: &NAPixelFormaton, ofmt: &NAPixelFormaton) -> bool {
289 for (ichr, ochr) in ifmt.comp_info.iter().zip(ofmt.comp_info.iter()) {
290 if let (Some(ic), Some(oc)) = (ichr, ochr) {
291 if ic.h_ss != oc.h_ss || ic.v_ss != oc.v_ss {
292 return true;
293 }
294 }
295 }
296 false
297}
298fn build_pipeline(ifmt: &ScaleInfo, ofmt: &ScaleInfo, just_convert: bool, options: &[(String, String)]) -> ScaleResult<Option<Stage>> {
299 let mut debug = false;
300 for (name, value) in options.iter() {
301 if name == "debug" && (value.is_empty() || value == "true") {
302 debug = true;
303 break;
304 }
305 }
306
307 let inname = ifmt.fmt.get_model().get_short_name();
308 let outname = ofmt.fmt.get_model().get_short_name();
309
310 if debug {
311 println!("convert {} -> {}", ifmt, ofmt);
312 }
313 let needs_scale = if fmt_needs_scale(&ifmt.fmt, &ofmt.fmt) {
314 true
315 } else {
316 !just_convert
317 };
318 let needs_unpack = !ifmt.fmt.is_unpacked();
319 let needs_pack = !ofmt.fmt.is_unpacked();
320 let needs_convert = inname != outname;
321 let scale_before_cvt = is_better_fmt(ifmt, ofmt) && needs_convert
322 && (ofmt.fmt.get_max_subsampling() == 0);
323 let needs_palettise = ofmt.fmt.palette;
324//todo stages for model and gamma conversion
325
326 let mut stages: Option<Stage> = None;
327 let mut cur_fmt = *ifmt;
328
329 if needs_unpack {
330 if debug {
331 println!("[adding unpack]");
332 }
333 let new_stage = if !cur_fmt.fmt.is_paletted() {
334 Stage::new("unpack", &cur_fmt, ofmt, options)?
335 } else {
336 Stage::new("depal", &cur_fmt, ofmt, options)?
337 };
338 cur_fmt = new_stage.fmt_out;
339 add_stage!(stages, new_stage);
340 }
341 if needs_scale && scale_before_cvt {
342 if debug {
343 println!("[adding scale]");
344 }
345 let new_stage = Stage::new("scale", &cur_fmt, ofmt, options)?;
346 cur_fmt = new_stage.fmt_out;
347 add_stage!(stages, new_stage);
348 }
349 if needs_convert {
350 if debug {
351 println!("[adding convert]");
352 }
353 let cvtname = format!("{}_to_{}", inname, outname);
354 if debug {
355 println!("[{}]", cvtname);
356 }
357 let new_stage = Stage::new(&cvtname, &cur_fmt, ofmt, options)?;
358//todo if fails try converting via RGB or YUV
359 cur_fmt = new_stage.fmt_out;
360 add_stage!(stages, new_stage);
361//todo alpha plane copy/add
362 }
363 if needs_scale && !scale_before_cvt {
364 if debug {
365 println!("[adding scale]");
366 }
367 let new_stage = Stage::new("scale", &cur_fmt, ofmt, options)?;
368 cur_fmt = new_stage.fmt_out;
369 add_stage!(stages, new_stage);
370 }
371 let is_in_high_bd = cur_fmt.fmt.get_max_depth() > 8;
372 let is_out_high_bd = ofmt.fmt.get_max_depth() > 8;
373 if is_in_high_bd && !is_out_high_bd {
374 if debug {
375 println!("[adding shallow]");
376 }
377 let new_stage = Stage::new("shallow", &cur_fmt, ofmt, options)?;
378 cur_fmt = new_stage.fmt_out;
379 add_stage!(stages, new_stage);
380 }
381 let icomponents = cur_fmt.fmt.components - if cur_fmt.fmt.alpha { 1 } else { 0 };
382 let ocomponents = ofmt.fmt.components - if ofmt.fmt.alpha { 1 } else { 0 };
383 if !needs_palettise && ((!cur_fmt.fmt.alpha && ofmt.fmt.alpha) || (icomponents < ocomponents)) {
384 if debug {
385 println!("[adding fill]");
386 }
387 let new_stage = Stage::new("fill", &cur_fmt, ofmt, options)?;
388 cur_fmt = new_stage.fmt_out;
389 add_stage!(stages, new_stage);
390 }
391 if needs_pack && !needs_palettise {
392 if debug {
393 println!("[adding pack]");
394 }
395 let new_stage = Stage::new("pack", &cur_fmt, ofmt, options)?;
396 //cur_fmt = new_stage.fmt_out;
397 add_stage!(stages, new_stage);
398 }
399 if needs_palettise {
400 if debug {
401 println!("[adding palettise]");
402 }
403 let new_stage = Stage::new("palette", &cur_fmt, ofmt, options)?;
404 //cur_fmt = new_stage.fmt_out;
405 add_stage!(stages, new_stage);
406 }
407
408 if let Some(ref mut head) = stages {
409 head.drop_last_tmp();
410 }
411
412 Ok(stages)
413}
414
415fn swap_plane<T:Copy>(data: &mut [T], stride: usize, h: usize, line0: &mut [T], line1: &mut [T]) {
416 let mut doff0 = 0;
417 let mut doff1 = stride * (h - 1);
418 for _ in 0..h/2 {
419 line0.copy_from_slice(&data[doff0..][..stride]);
420 line1.copy_from_slice(&data[doff1..][..stride]);
421 data[doff1..][..stride].copy_from_slice(line0);
422 data[doff0..][..stride].copy_from_slice(line1);
423 doff0 += stride;
424 doff1 -= stride;
425 }
426}
427
428/// Flips the picture contents.
429pub fn flip_picture(pic: &mut NABufferType) -> ScaleResult<()> {
430 match pic {
431 NABufferType::Video(ref mut vb) => {
432 let ncomp = vb.get_num_components();
433 for comp in 0..ncomp {
434 let off = vb.get_offset(comp);
435 let stride = vb.get_stride(comp);
436 let (_, h) = vb.get_dimensions(comp);
437 let data = vb.get_data_mut().unwrap();
438 let mut line0 = vec![0; stride];
439 let mut line1 = vec![0; stride];
440 swap_plane(&mut data[off..], stride, h, line0.as_mut_slice(), line1.as_mut_slice());
441 }
442 },
443 NABufferType::Video16(ref mut vb) => {
444 let ncomp = vb.get_num_components().max(1);
445 for comp in 0..ncomp {
446 let off = vb.get_offset(comp);
447 let stride = vb.get_stride(comp);
448 let (_, h) = vb.get_dimensions(comp);
449 let data = vb.get_data_mut().unwrap();
450 let mut line0 = vec![0; stride];
451 let mut line1 = vec![0; stride];
452 swap_plane(&mut data[off..], stride, h, line0.as_mut_slice(), line1.as_mut_slice());
453 }
454 },
455 NABufferType::Video32(ref mut vb) => {
456 let ncomp = vb.get_num_components().max(1);
457 for comp in 0..ncomp {
458 let off = vb.get_offset(comp);
459 let stride = vb.get_stride(comp);
460 let (_, h) = vb.get_dimensions(comp);
461 let data = vb.get_data_mut().unwrap();
462 let mut line0 = vec![0; stride];
463 let mut line1 = vec![0; stride];
464 swap_plane(&mut data[off..], stride, h, line0.as_mut_slice(), line1.as_mut_slice());
465 }
466 },
467 NABufferType::VideoPacked(ref mut vb) => {
468 let ncomp = vb.get_num_components();
469 for comp in 0..ncomp {
470 let off = vb.get_offset(comp);
471 let stride = vb.get_stride(comp);
472 let (_, h) = vb.get_dimensions(comp);
473 let data = vb.get_data_mut().unwrap();
474 let mut line0 = vec![0; stride];
475 let mut line1 = vec![0; stride];
476 swap_plane(&mut data[off..], stride, h, line0.as_mut_slice(), line1.as_mut_slice());
477 }
478 if ncomp == 0 && vb.get_stride(0) != 0 {
479 let off = vb.get_offset(0);
480 let stride = vb.get_stride(0);
481 let (_, h) = vb.get_dimensions(0);
482 let data = vb.get_data_mut().unwrap();
483 let mut line0 = vec![0; stride];
484 let mut line1 = vec![0; stride];
485 swap_plane(&mut data[off..], stride, h, line0.as_mut_slice(), line1.as_mut_slice());
486 }
487 },
488 _ => { return Err(ScaleError::InvalidArgument); },
489 };
490 Ok(())
491}
492
493impl NAScale {
494 /// Constructs a new `NAScale` instance.
495 pub fn new(fmt_in: ScaleInfo, fmt_out: ScaleInfo) -> ScaleResult<Self> {
496 let just_convert = (fmt_in.width == fmt_out.width) && (fmt_in.height == fmt_out.height);
497 let pipeline = if fmt_in != fmt_out {
498 build_pipeline(&fmt_in, &fmt_out, just_convert, &[])?
499 } else {
500 None
501 };
502 Ok(Self { fmt_in, fmt_out, just_convert, pipeline })
503 }
504 /// Constructs a new `NAScale` instance taking into account provided options.
505 pub fn new_with_options(fmt_in: ScaleInfo, fmt_out: ScaleInfo, options: &[(String, String)]) -> ScaleResult<Self> {
506 let just_convert = (fmt_in.width == fmt_out.width) && (fmt_in.height == fmt_out.height);
507 let pipeline = if fmt_in != fmt_out {
508 build_pipeline(&fmt_in, &fmt_out, just_convert, options)?
509 } else {
510 None
511 };
512 Ok(Self { fmt_in, fmt_out, just_convert, pipeline })
513 }
514 /// Checks whether requested conversion operation is needed at all.
515 pub fn needs_processing(&self) -> bool { self.pipeline.is_some() }
516 /// Returns the input image format.
517 pub fn get_in_fmt(&self) -> ScaleInfo { self.fmt_in }
518 /// Returns the output image format.
519 pub fn get_out_fmt(&self) -> ScaleInfo { self.fmt_out }
520 /// Performs the image format conversion.
521 pub fn convert(&mut self, pic_in: &NABufferType, pic_out: &mut NABufferType) -> ScaleResult<()> {
522 let in_info = pic_in.get_video_info();
523 let out_info = pic_out.get_video_info();
524 if in_info.is_none() || out_info.is_none() { return Err(ScaleError::InvalidArgument); }
525 let in_info = in_info.unwrap();
526 let out_info = out_info.unwrap();
527 if self.just_convert &&
528 (in_info.get_width() != out_info.get_width() || in_info.get_height() != out_info.get_height()) {
529 return Err(ScaleError::InvalidArgument);
530 }
531 let needs_flip = in_info.is_flipped() ^ out_info.is_flipped();
532 check_format(in_info, &self.fmt_in, self.just_convert)?;
533 check_format(out_info, &self.fmt_out, self.just_convert)?;
534 let ret = if let Some(ref mut pipe) = self.pipeline {
535 pipe.process(pic_in, pic_out)
536 } else {
537 copy(pic_in, pic_out);
538 Ok(())
539 };
540 if ret.is_ok() && needs_flip {
541 flip_picture(pic_out)?;
542 }
543 ret
544 }
545}
546
547#[cfg(test)]
548mod test {
549 use super::*;
550
551 fn fill_pic(pic: &mut NABufferType, val: u8) {
552 if let Some(ref mut buf) = pic.get_vbuf() {
553 let data = buf.get_data_mut().unwrap();
554 for el in data.iter_mut() { *el = val; }
555 } else if let Some(ref mut buf) = pic.get_vbuf16() {
556 let data = buf.get_data_mut().unwrap();
557 for el in data.iter_mut() { *el = val as u16; }
558 } else if let Some(ref mut buf) = pic.get_vbuf32() {
559 let data = buf.get_data_mut().unwrap();
560 for el in data.iter_mut() { *el = (val as u32) * 0x01010101; }
561 }
562 }
563 #[test]
564 fn test_convert() {
565 let mut in_pic = alloc_video_buffer(NAVideoInfo::new(1, 1, false, RGB565_FORMAT), 3).unwrap();
566 fill_pic(&mut in_pic, 42);
567 let mut out_pic = alloc_video_buffer(NAVideoInfo::new(1, 1, false, RGB24_FORMAT), 3).unwrap();
568 fill_pic(&mut out_pic, 0);
569 let ifmt = get_scale_fmt_from_pic(&in_pic);
570 let ofmt = get_scale_fmt_from_pic(&out_pic);
571 let mut scaler = NAScale::new(ifmt, ofmt).unwrap();
572 scaler.convert(&in_pic, &mut out_pic).unwrap();
573 let obuf = out_pic.get_vbuf().unwrap();
574 let odata = obuf.get_data();
575 assert_eq!(odata[0], 0x0);
576 assert_eq!(odata[1], 0x4);
577 assert_eq!(odata[2], 0x52);
578
579 let mut in_pic = alloc_video_buffer(NAVideoInfo::new(4, 4, false, RGB24_FORMAT), 3).unwrap();
580 fill_pic(&mut in_pic, 42);
581 let mut out_pic = alloc_video_buffer(NAVideoInfo::new(4, 4, false, YUV420_FORMAT), 3).unwrap();
582 fill_pic(&mut out_pic, 0);
583 let ifmt = get_scale_fmt_from_pic(&in_pic);
584 let ofmt = get_scale_fmt_from_pic(&out_pic);
585 let mut scaler = NAScale::new(ifmt, ofmt).unwrap();
586 scaler.convert(&in_pic, &mut out_pic).unwrap();
587 let obuf = out_pic.get_vbuf().unwrap();
588 let yoff = obuf.get_offset(0);
589 let uoff = obuf.get_offset(1);
590 let voff = obuf.get_offset(2);
591 let odata = obuf.get_data();
592 assert_eq!(odata[yoff], 42);
593 assert!(((odata[uoff] ^ 0x80) as i8).abs() <= 1);
594 assert!(((odata[voff] ^ 0x80) as i8).abs() <= 1);
595 let mut scaler = NAScale::new(ofmt, ifmt).unwrap();
596 scaler.convert(&out_pic, &mut in_pic).unwrap();
597 let obuf = in_pic.get_vbuf().unwrap();
598 let odata = obuf.get_data();
599 assert_eq!(odata[0], 42);
600 }
601 #[test]
602 fn test_scale() {
603 let mut in_pic = alloc_video_buffer(NAVideoInfo::new(2, 2, false, RGB565_FORMAT), 3).unwrap();
604 fill_pic(&mut in_pic, 42);
605 let mut out_pic = alloc_video_buffer(NAVideoInfo::new(3, 3, false, RGB565_FORMAT), 3).unwrap();
606 fill_pic(&mut out_pic, 0);
607 let ifmt = get_scale_fmt_from_pic(&in_pic);
608 let ofmt = get_scale_fmt_from_pic(&out_pic);
609 let mut scaler = NAScale::new(ifmt, ofmt).unwrap();
610 scaler.convert(&in_pic, &mut out_pic).unwrap();
611 let obuf = out_pic.get_vbuf16().unwrap();
612 let odata = obuf.get_data();
613 assert_eq!(odata[0], 42);
614 }
615 #[test]
616 fn test_scale_and_convert() {
617 let mut in_pic = alloc_video_buffer(NAVideoInfo::new(7, 3, false, RGB565_FORMAT), 3).unwrap();
618 fill_pic(&mut in_pic, 42);
619 let mut out_pic = alloc_video_buffer(NAVideoInfo::new(4, 4, false, YUV420_FORMAT), 3).unwrap();
620 fill_pic(&mut out_pic, 0);
621 let ifmt = get_scale_fmt_from_pic(&in_pic);
622 let ofmt = get_scale_fmt_from_pic(&out_pic);
623 let mut scaler = NAScale::new(ifmt, ofmt).unwrap();
624 scaler.convert(&in_pic, &mut out_pic).unwrap();
625 let obuf = out_pic.get_vbuf().unwrap();
626 let yoff = obuf.get_offset(0);
627 let uoff = obuf.get_offset(1);
628 let voff = obuf.get_offset(2);
629 let odata = obuf.get_data();
630 assert_eq!(odata[yoff], 11);
631 assert_eq!(odata[uoff], 162);
632 assert_eq!(odata[voff], 118);
633 }
634 #[test]
635 fn test_scale_and_convert_to_pal() {
636 let mut in_pic = alloc_video_buffer(NAVideoInfo::new(7, 3, false, YUV420_FORMAT), 3).unwrap();
637 fill_pic(&mut in_pic, 142);
638 let mut out_pic = alloc_video_buffer(NAVideoInfo::new(4, 4, false, PAL8_FORMAT), 0).unwrap();
639 fill_pic(&mut out_pic, 0);
640 let ifmt = get_scale_fmt_from_pic(&in_pic);
641 let ofmt = get_scale_fmt_from_pic(&out_pic);
642 let mut scaler = NAScale::new(ifmt, ofmt).unwrap();
643 scaler.convert(&in_pic, &mut out_pic).unwrap();
644 let obuf = out_pic.get_vbuf().unwrap();
645 let dataoff = obuf.get_offset(0);
646 let paloff = obuf.get_offset(1);
647 let odata = obuf.get_data();
648 assert_eq!(odata[dataoff], 0);
649 assert_eq!(odata[paloff], 157);
650 assert_eq!(odata[paloff + 1], 129);
651 assert_eq!(odata[paloff + 2], 170);
652 }
653 #[test]
654 fn test_scale_modes() {
655 const IN_DATA: [[u8; 6]; 2] = [
656 [0xFF, 0xC0, 0x40, 0x00, 0x40, 0xC0],
657 [0x00, 0x40, 0xC0, 0xFF, 0xC0, 0x40]
658 ];
659 const TEST_DATA: &[(&str, [[u8; 9]; 3])] = &[
660 ("nn",
661 [[0xFF, 0xC0, 0x40, 0xFF, 0xC0, 0x40, 0x00, 0x40, 0xC0],
662 [0xFF, 0xC0, 0x40, 0xFF, 0xC0, 0x40, 0x00, 0x40, 0xC0],
663 [0x00, 0x40, 0xC0, 0x00, 0x40, 0xC0, 0xFF, 0xC0, 0x40]]),
664 ("bilin",
665 [[0xFF, 0xC0, 0x40, 0x55, 0x6A, 0x95, 0x00, 0x40, 0xC0],
666 [0x55, 0x6A, 0x95, 0x8D, 0x86, 0x78, 0xAA, 0x95, 0x6A],
667 [0x00, 0x40, 0xC0, 0xAA, 0x95, 0x6A, 0xFF, 0xC0, 0x40]]),
668 ("bicubic",
669 [[0xFF, 0xC0, 0x40, 0x4B, 0x65, 0x9A, 0x00, 0x36, 0xC9],
670 [0x4B, 0x65, 0x9A, 0x94, 0x8A, 0x74, 0xB3, 0x9D, 0x61],
671 [0x00, 0x36, 0xC9, 0xBA, 0x9D, 0x61, 0xFF, 0xD3, 0x2B]]),
672 ("lanczos",
673 [[0xFF, 0xC0, 0x40, 0x4C, 0x66, 0x98, 0x00, 0x31, 0xCD],
674 [0x4C, 0x66, 0x98, 0x91, 0x88, 0x74, 0xB1, 0x9D, 0x5F],
675 [0x00, 0x31, 0xCD, 0xBB, 0x9D, 0x5F, 0xFF, 0xDD, 0x1E]]),
676 ("lanczos2",
677 [[0xFF, 0xC0, 0x40, 0x4F, 0x68, 0x9B, 0x00, 0x35, 0xCD],
678 [0x4F, 0x68, 0x9B, 0x96, 0x8D, 0x79, 0xB3, 0xA0, 0x64],
679 [0x00, 0x35, 0xCD, 0xBE, 0xA1, 0x65, 0xFF, 0xDC, 0x28]]),
680 ];
681
682 let in_pic = alloc_video_buffer(NAVideoInfo::new(2, 2, false, RGB24_FORMAT), 3).unwrap();
683 if let Some(ref mut vbuf) = in_pic.get_vbuf() {
684 let stride = vbuf.get_stride(0);
685 let data = vbuf.get_data_mut().unwrap();
686 for (dline, rline) in data.chunks_mut(stride).zip(IN_DATA.iter()) {
687 dline[..6].copy_from_slice(rline);
688 }
689 } else {
690 panic!("wrong format");
691 }
692 let mut out_pic = alloc_video_buffer(NAVideoInfo::new(3, 3, false, RGB24_FORMAT), 3).unwrap();
693 let ifmt = get_scale_fmt_from_pic(&in_pic);
694 let ofmt = get_scale_fmt_from_pic(&out_pic);
695 for (method, ref_data) in TEST_DATA.iter() {
696 fill_pic(&mut out_pic, 0);
697 let mut scaler = NAScale::new_with_options(ifmt, ofmt, &[("scaler".to_string(), method.to_string())]).unwrap();
698 scaler.convert(&in_pic, &mut out_pic).unwrap();
699 let obuf = out_pic.get_vbuf().unwrap();
700 let ostride = obuf.get_stride(0);
701 let odata = obuf.get_data();
702 for (oline, rline) in odata.chunks(ostride).zip(ref_data.iter()) {
703 for (&a, &b) in oline.iter().zip(rline.iter()) {
704 assert_eq!(a, b);
705 }
706 }
707 }
708 }
709}