use std::path::PathBuf;
use std::sync::Arc;
use std::time::Duration;
use crate::{BlendMode, Crop, Grade};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Image {
pub width: u32,
pub height: u32,
pub data: Vec<u8>,
}
pub const STILL_SIDE: u32 = 16384;
pub const STILL_PIXELS: u64 = 1 << 28;
pub fn still_fits(w: u32, h: u32) -> bool {
w > 0 && h > 0 && w.max(h) <= STILL_SIDE && u64::from(w) * u64::from(h) <= STILL_PIXELS
}
#[derive(Debug, Clone, PartialEq)]
pub enum LayerSource {
Video {
input: PathBuf,
start: Duration,
map: Option<Arc<[i64]>>,
},
Image(Arc<Image>),
Still(PathBuf),
Scene(Arc<Scene>),
Filtered(Arc<crate::filter_node::Prep>),
}
#[derive(Debug, Clone, PartialEq)]
pub struct Scene {
pub size: crate::Size,
pub grade: Grade,
pub layers: Vec<Layer>,
pub overlays: Vec<crate::Overlay>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Layer {
pub source: LayerSource,
pub grade: Grade,
pub from: i64,
pub to: i64,
pub rects: Vec<Crop>,
pub rotation: Vec<f32>,
pub blend: BlendMode,
}
impl Layer {
pub fn rect_at(&self, frame: i64, width: u32, height: u32) -> Option<Crop> {
if frame < self.from || frame >= self.to {
return None;
}
let i = usize::try_from(frame - self.from).unwrap_or(0);
#[allow(clippy::cast_precision_loss)]
Some(
self.rects
.get(i)
.or(self.rects.last())
.copied()
.unwrap_or(Crop {
x: 0.0,
y: 0.0,
width: width as f32,
height: height as f32,
}),
)
}
pub fn rotation_at(&self, frame: i64) -> f32 {
if frame < self.from || frame >= self.to {
return 0.0;
}
let i = usize::try_from(frame - self.from).unwrap_or(0);
self.rotation
.get(i)
.or(self.rotation.last())
.copied()
.unwrap_or(0.0)
}
pub fn needs_composite(&self, frame: i64) -> bool {
self.blend != BlendMode::Normal || self.rotation_at(frame) % 360.0 != 0.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rect_holds_and_ranges() {
let r = |x| Crop {
x,
y: 0.0,
width: 10.0,
height: 10.0,
};
let l = Layer {
source: LayerSource::Image(Arc::new(Image {
width: 1,
height: 1,
data: vec![0; 4],
})),
grade: Grade::default(),
from: 5,
to: 10,
rects: vec![r(0.0), r(1.0)],
rotation: vec![0.0, 30.0],
blend: BlendMode::Normal,
};
assert_eq!(l.rect_at(4, 100, 50), None);
assert_eq!(l.rect_at(5, 100, 50), Some(r(0.0)));
assert_eq!(l.rect_at(9, 100, 50), Some(r(1.0)));
assert_eq!(l.rect_at(10, 100, 50), None);
let full = Layer {
rects: Vec::new(),
..l
};
assert_eq!(full.rect_at(5, 100, 50).map(|c| c.width), Some(100.0));
assert!(!full.needs_composite(5));
assert!(full.needs_composite(7));
assert!((full.rotation_at(9) - 30.0).abs() < f32::EPSILON);
assert!(full.rotation_at(10).abs() < f32::EPSILON);
let multiply = Layer {
rotation: Vec::new(),
blend: BlendMode::Multiply,
..full
};
assert!(multiply.needs_composite(5));
}
}