core/scale: add output buffer flipping if needed
[nihav.git] / nihav-core / src / scale / mod.rs
index d878c4c0041ef4643b90666ec79973000ac05ede..ad96da95c4b1a85c15a8f47c6da45016c249ec89 100644 (file)
@@ -123,9 +123,38 @@ fn check_format(in_fmt: NAVideoInfo, ref_fmt: &ScaleInfo, just_convert: bool) ->
 fn copy(pic_in: &NABufferType, pic_out: &mut NABufferType)
 {
     if let (Some(ref sbuf), Some(ref mut dbuf)) = (pic_in.get_vbuf(), pic_out.get_vbuf()) {
-        let sdata = sbuf.get_data();
-        let ddata = dbuf.get_data_mut().unwrap();
-        ddata.copy_from_slice(&sdata[0..]);
+        let mut same = true;
+        let num_components = sbuf.get_info().get_format().get_num_comp();
+        for i in 0..num_components {
+            if sbuf.get_stride(i) != dbuf.get_stride(i) {
+                same = false;
+                break;
+            }
+            if sbuf.get_offset(i) != dbuf.get_offset(i) {
+                same = false;
+                break;
+            }
+        }
+        if same {
+            let sdata = sbuf.get_data();
+            let ddata = dbuf.get_data_mut().unwrap();
+            ddata.copy_from_slice(&sdata[0..]);
+        } else {
+            let sdata = sbuf.get_data();
+            for comp in 0..num_components {
+                let (_, h) = sbuf.get_dimensions(comp);
+                let src = &sdata[sbuf.get_offset(comp)..];
+                let sstride = sbuf.get_stride(comp);
+                let doff = dbuf.get_offset(comp);
+                let dstride = dbuf.get_stride(comp);
+                let ddata = dbuf.get_data_mut().unwrap();
+                let dst = &mut ddata[doff..];
+                let copy_size = sstride.min(dstride);
+                for (dline, sline) in dst.chunks_exact_mut(dstride).take(h).zip(src.chunks_exact(sstride)) {
+                    (&mut dline[..copy_size]).copy_from_slice(&sline[..copy_size]);
+                }
+            }
+        }
     } else {
         unimplemented!();
     }
@@ -205,7 +234,6 @@ println!("[adding scale]");
         cur_fmt = new_stage.fmt_out;
         add_stage!(stages, new_stage);
     }
-//todo flip if needed
     if needs_pack {
 println!("[adding pack]");
         let new_stage = Stage::new("pack", &cur_fmt, &ofmt)?;
@@ -220,6 +248,74 @@ println!("[adding pack]");
     Ok(stages)
 }
 
+fn swap_plane<T:Copy>(data: &mut [T], stride: usize, h: usize, line0: &mut [T], line1: &mut [T]) {
+    let mut doff0 = 0;
+    let mut doff1 = stride * (h - 1);
+    for _ in 0..h/2 {
+        line0.copy_from_slice(&data[doff0..][..stride]);
+        line1.copy_from_slice(&data[doff1..][..stride]);
+        (&mut data[doff1..][..stride]).copy_from_slice(line0);
+        (&mut data[doff0..][..stride]).copy_from_slice(line1);
+        doff0 += stride;
+        doff1 -= stride;
+    }
+}
+
+pub fn flip_picture(pic: &mut NABufferType) -> ScaleResult<()> {
+    match pic {
+        NABufferType::Video(ref mut vb) => {
+            let ncomp = vb.get_num_components();
+            for comp in 0..ncomp {
+                let off    = vb.get_offset(comp);
+                let stride = vb.get_stride(comp);
+                let (_, h) = vb.get_dimensions(comp);
+                let data = vb.get_data_mut().unwrap();
+                let mut line0 = vec![0; stride];
+                let mut line1 = vec![0; stride];
+                swap_plane(&mut data[off..], stride, h, line0.as_mut_slice(), line1.as_mut_slice());
+            }
+        },
+        NABufferType::Video16(ref mut vb) => {
+            let ncomp = vb.get_num_components();
+            for comp in 0..ncomp {
+                let off    = vb.get_offset(comp);
+                let stride = vb.get_stride(comp);
+                let (_, h) = vb.get_dimensions(comp);
+                let data = vb.get_data_mut().unwrap();
+                let mut line0 = vec![0; stride];
+                let mut line1 = vec![0; stride];
+                swap_plane(&mut data[off..], stride, h, line0.as_mut_slice(), line1.as_mut_slice());
+            }
+        },
+        NABufferType::Video32(ref mut vb) => {
+            let ncomp = vb.get_num_components();
+            for comp in 0..ncomp {
+                let off    = vb.get_offset(comp);
+                let stride = vb.get_stride(comp);
+                let (_, h) = vb.get_dimensions(comp);
+                let data = vb.get_data_mut().unwrap();
+                let mut line0 = vec![0; stride];
+                let mut line1 = vec![0; stride];
+                swap_plane(&mut data[off..], stride, h, line0.as_mut_slice(), line1.as_mut_slice());
+            }
+        },
+        NABufferType::VideoPacked(ref mut vb) => {
+            let ncomp = vb.get_num_components();
+            for comp in 0..ncomp {
+                let off    = vb.get_offset(comp);
+                let stride = vb.get_stride(comp);
+                let (_, h) = vb.get_dimensions(comp);
+                let data = vb.get_data_mut().unwrap();
+                let mut line0 = vec![0; stride];
+                let mut line1 = vec![0; stride];
+                swap_plane(&mut data[off..], stride, h, line0.as_mut_slice(), line1.as_mut_slice());
+            }
+        },
+        _ => { return Err(ScaleError::InvalidArgument); },
+    };
+    Ok(())
+}
+
 impl NAScale {
     pub fn new(fmt_in: ScaleInfo, fmt_out: ScaleInfo) -> ScaleResult<Self> {
         let pipeline;
@@ -244,14 +340,19 @@ impl NAScale {
                 (in_info.get_width() != out_info.get_width() || in_info.get_height() != out_info.get_height()) {
             return Err(ScaleError::InvalidArgument);
         }
+        let needs_flip = in_info.is_flipped() ^ out_info.is_flipped();
         check_format(in_info,  &self.fmt_in,  self.just_convert)?;
         check_format(out_info, &self.fmt_out, self.just_convert)?;
-        if let Some(ref mut pipe) = self.pipeline {
-            pipe.process(pic_in, pic_out)
-        } else {
-            copy(pic_in, pic_out);
-            Ok(())
+        let ret = if let Some(ref mut pipe) = self.pipeline {
+                pipe.process(pic_in, pic_out)
+            } else {
+                copy(pic_in, pic_out);
+                Ok(())
+            };
+        if ret.is_ok() && needs_flip {
+            flip_picture(pic_out)?;
         }
+        ret
     }
 }