use crate::geometry::Fit;
use crate::output_layer::OutputLayer;
#[derive(Clone, Debug)]
pub struct Output {
pub width: u32,
pub height: u32,
pub background_color: [f32; 4],
pub background_image: Option<String>,
pub background_fit: Fit,
pub layers: Vec<OutputLayer>,
}
impl Output {
pub fn new(width: u32, height: u32) -> Self {
Output {
width,
height,
background_color: [0.0, 0.0, 0.0, 1.0],
background_image: None,
background_fit: Fit::Cover,
layers: Vec::new(),
}
}
pub fn with_background_color(mut self, color: [f32; 4]) -> Self {
self.background_color = [
color[0].clamp(0.0, 1.0),
color[1].clamp(0.0, 1.0),
color[2].clamp(0.0, 1.0),
color[3].clamp(0.0, 1.0),
];
self
}
pub fn with_background_image(mut self, path: impl Into<String>) -> Self {
self.background_image = Some(path.into());
self
}
pub fn with_background_fit(mut self, fit: Fit) -> Self {
self.background_fit = fit;
self
}
pub fn add_layer(&mut self, layer: OutputLayer) {
self.layers.push(layer);
self.sort_layers();
}
pub fn add_layers(&mut self, layers: impl IntoIterator<Item = OutputLayer>) {
self.layers.extend(layers);
self.sort_layers();
}
pub fn remove_layer(&mut self, video_source_id: &str) -> bool {
let before_len = self.layers.len();
self.layers.retain(|layer| layer.video_source_id != video_source_id);
self.layers.len() < before_len
}
pub fn get_layer_mut(&mut self, video_source_id: &str) -> Option<&mut OutputLayer> {
self.layers.iter_mut().find(|layer| layer.video_source_id == video_source_id)
}
fn sort_layers(&mut self) {
self.layers.sort_by_key(|layer| layer.z_index);
}
pub fn visible_layers(&self) -> impl Iterator<Item = &OutputLayer> {
self.layers.iter().filter(|layer| layer.visible)
}
pub fn get_layer(&self, video_source_id: &str) -> Option<&OutputLayer> {
self.layers.iter().find(|layer| layer.video_source_id == video_source_id)
}
pub fn has_layer(&self, video_source_id: &str) -> bool {
self.layers.iter().any(|layer| layer.video_source_id == video_source_id)
}
pub fn visible_layer_count(&self) -> usize {
self.layers.iter().filter(|layer| layer.visible).count()
}
pub fn layer_count(&self) -> usize {
self.layers.len()
}
pub fn clear_layers(&mut self) {
self.layers.clear();
}
pub fn with_background_rgb(mut self, r: f32, g: f32, b: f32) -> Self {
self.background_color = [
r.clamp(0.0, 1.0),
g.clamp(0.0, 1.0),
b.clamp(0.0, 1.0),
1.0,
];
self
}
pub fn with_transparent_background(mut self) -> Self {
self.background_color = [0.0, 0.0, 0.0, 0.0];
self
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::geometry::Rect;
#[test]
fn output_creation() {
let output = Output::new(1920, 1080);
assert_eq!(output.width, 1920);
assert_eq!(output.height, 1080);
assert_eq!(output.background_color, [0.0, 0.0, 0.0, 1.0]);
assert!(output.background_image.is_none());
assert!(output.layers.is_empty());
}
#[test]
fn output_with_custom_background() {
let output = Output::new(1280, 720)
.with_background_color([0.2, 0.4, 0.6, 1.0])
.with_background_image("background.png");
assert_eq!(output.background_color, [0.2, 0.4, 0.6, 1.0]);
assert_eq!(output.background_image, Some("background.png".to_string()));
}
#[test]
fn layers_are_sorted_by_z_index() {
let mut output = Output::new(640, 480);
let layer_a = OutputLayer::new("video_a", Rect::new(0.0, 0.0, 200.0, 200.0))
.with_z_index(2);
let layer_b = OutputLayer::new("video_b", Rect::new(100.0, 100.0, 200.0, 200.0))
.with_z_index(0);
let layer_c = OutputLayer::new("video_c", Rect::new(200.0, 200.0, 200.0, 200.0))
.with_z_index(1);
output.add_layers(vec![layer_a, layer_b, layer_c]);
assert_eq!(output.layers[0].z_index, 0);
assert_eq!(output.layers[1].z_index, 1);
assert_eq!(output.layers[2].z_index, 2);
}
#[test]
fn remove_layer_by_source_id() {
let mut output = Output::new(640, 480);
output.add_layer(OutputLayer::new("video_a", Rect::new(0.0, 0.0, 100.0, 100.0)));
output.add_layer(OutputLayer::new("video_b", Rect::new(100.0, 100.0, 100.0, 100.0)));
assert_eq!(output.layers.len(), 2);
assert!(output.remove_layer("video_a"));
assert_eq!(output.layers.len(), 1);
assert_eq!(output.layers[0].video_source_id, "video_b");
}
#[test]
fn visible_layers_filter() {
let mut output = Output::new(640, 480);
let mut layer_a = OutputLayer::new("video_a", Rect::new(0.0, 0.0, 100.0, 100.0));
let mut layer_b = OutputLayer::new("video_b", Rect::new(100.0, 100.0, 100.0, 100.0));
layer_a.visible = true;
layer_b.visible = false;
output.add_layers(vec![layer_a, layer_b]);
let visible: Vec<_> = output.visible_layers().collect();
assert_eq!(visible.len(), 1);
assert_eq!(visible[0].video_source_id, "video_a");
}
}