use std::sync::Arc;
use crate::animation::Animation;
use crate::duration::Lifetime;
use crate::id::ObjectId;
pub use oxideav_core::PixelFormat;
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Canvas {
Raster {
width: u32,
height: u32,
pixel_format: PixelFormat,
},
Vector {
width: f32,
height: f32,
unit: LengthUnit,
},
}
impl Canvas {
pub const fn raster(width: u32, height: u32) -> Self {
Canvas::Raster {
width,
height,
pixel_format: PixelFormat::Yuv420P,
}
}
pub fn raster_size(&self) -> Option<(u32, u32)> {
match self {
Canvas::Raster { width, height, .. } => Some((*width, *height)),
Canvas::Vector { .. } => None,
}
}
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum LengthUnit {
#[default]
Point,
Millimetre,
Inch,
CssPixel,
DevicePixel,
}
#[derive(Clone, Debug)]
pub struct SceneObject {
pub id: ObjectId,
pub kind: ObjectKind,
pub transform: Transform,
pub lifetime: Lifetime,
pub animations: Vec<Animation>,
pub z_order: i32,
pub opacity: f32,
pub blend_mode: BlendMode,
pub effects: Vec<Effect>,
pub clip: Option<ClipRect>,
}
impl Default for SceneObject {
fn default() -> Self {
SceneObject {
id: ObjectId::default(),
kind: ObjectKind::Shape(Shape::rect(0.0, 0.0)),
transform: Transform::identity(),
lifetime: Lifetime::default(),
animations: Vec::new(),
z_order: 0,
opacity: 1.0,
blend_mode: BlendMode::default(),
effects: Vec::new(),
clip: None,
}
}
}
#[non_exhaustive]
#[derive(Clone, Debug)]
pub enum ObjectKind {
Image(ImageSource),
Video(VideoSource),
Text(TextRun),
Shape(Shape),
Group(Vec<ObjectId>),
Live(LiveStreamHandle),
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Transform {
pub position: (f32, f32),
pub scale: (f32, f32),
pub rotation: f32,
pub anchor: (f32, f32),
pub skew: (f32, f32),
}
impl Transform {
pub const fn identity() -> Self {
Transform {
position: (0.0, 0.0),
scale: (1.0, 1.0),
rotation: 0.0,
anchor: (0.5, 0.5),
skew: (0.0, 0.0),
}
}
}
impl Default for Transform {
fn default() -> Self {
Transform::identity()
}
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum BlendMode {
#[default]
Normal,
Multiply,
Screen,
Overlay,
Add,
Subtract,
Copy,
}
#[derive(Clone, Debug)]
pub struct Effect {
pub name: String,
pub params: Vec<(String, f32)>,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ClipRect {
pub x: f32,
pub y: f32,
pub width: f32,
pub height: f32,
}
#[non_exhaustive]
#[derive(Clone, Debug)]
pub enum ImageSource {
Decoded(Arc<oxideav_core::VideoFrame>),
Path(String),
EncodedBytes(Arc<[u8]>),
}
#[non_exhaustive]
#[derive(Clone, Debug)]
pub enum VideoSource {
Path(String),
EncodedBytes(Arc<[u8]>),
}
#[derive(Clone, Debug, Default)]
pub struct TextRun {
pub text: String,
pub font_family: String,
pub font_weight: u16,
pub font_size: f32,
pub color: u32,
pub advances: Option<Vec<f32>>,
pub italic: bool,
pub underline: bool,
}
#[non_exhaustive]
#[derive(Clone, Debug)]
pub enum Shape {
Rect {
width: f32,
height: f32,
fill: u32,
stroke: Option<Stroke>,
corner_radius: f32,
},
Polygon {
points: Vec<(f32, f32)>,
fill: u32,
stroke: Option<Stroke>,
},
Path {
data: String,
fill: u32,
stroke: Option<Stroke>,
},
}
impl Shape {
pub const fn rect(width: f32, height: f32) -> Self {
Shape::Rect {
width,
height,
fill: 0,
stroke: None,
corner_radius: 0.0,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Stroke {
pub color: u32,
pub width: f32,
}
#[derive(Clone, Debug)]
pub struct LiveStreamHandle {
pub uri: String,
pub hint_size: Option<(u32, u32)>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn raster_canvas_size() {
let c = Canvas::raster(640, 480);
assert_eq!(c.raster_size(), Some((640, 480)));
}
#[test]
fn vector_canvas_no_raster_size() {
let c = Canvas::Vector {
width: 595.0,
height: 842.0,
unit: LengthUnit::Point,
};
assert!(c.raster_size().is_none());
}
#[test]
fn transform_identity_roundtrip() {
let t = Transform::identity();
assert_eq!(t.position, (0.0, 0.0));
assert_eq!(t.scale, (1.0, 1.0));
assert_eq!(t.anchor, (0.5, 0.5));
}
#[test]
fn scene_object_default_is_neutral() {
let o = SceneObject::default();
assert_eq!(o.opacity, 1.0);
assert_eq!(o.blend_mode, BlendMode::Normal);
assert!(o.animations.is_empty());
}
}