1use std::sync::Arc;
4
5use crate::animation::Animation;
6use crate::duration::Lifetime;
7use crate::id::ObjectId;
8
9pub use oxideav_core::PixelFormat;
13
14#[non_exhaustive]
17#[derive(Clone, Copy, Debug, PartialEq)]
18pub enum Canvas {
19 Raster {
22 width: u32,
23 height: u32,
24 pixel_format: PixelFormat,
25 },
26 Vector {
30 width: f32,
31 height: f32,
32 unit: LengthUnit,
33 },
34}
35
36impl Canvas {
37 pub const fn raster(width: u32, height: u32) -> Self {
39 Canvas::Raster {
40 width,
41 height,
42 pixel_format: PixelFormat::Yuv420P,
43 }
44 }
45
46 pub fn raster_size(&self) -> Option<(u32, u32)> {
48 match self {
49 Canvas::Raster { width, height, .. } => Some((*width, *height)),
50 Canvas::Vector { .. } => None,
51 }
52 }
53}
54
55#[non_exhaustive]
57#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
58pub enum LengthUnit {
59 #[default]
61 Point,
62 Millimetre,
64 Inch,
66 CssPixel,
68 DevicePixel,
70}
71
72#[derive(Clone, Debug)]
74pub struct SceneObject {
75 pub id: ObjectId,
76 pub kind: ObjectKind,
77 pub transform: Transform,
78 pub lifetime: Lifetime,
79 pub animations: Vec<Animation>,
80 pub z_order: i32,
81 pub opacity: f32,
82 pub blend_mode: BlendMode,
83 pub effects: Vec<Effect>,
84 pub clip: Option<ClipRect>,
85}
86
87impl Default for SceneObject {
88 fn default() -> Self {
89 SceneObject {
90 id: ObjectId::default(),
91 kind: ObjectKind::Shape(Shape::rect(0.0, 0.0)),
92 transform: Transform::identity(),
93 lifetime: Lifetime::default(),
94 animations: Vec::new(),
95 z_order: 0,
96 opacity: 1.0,
97 blend_mode: BlendMode::default(),
98 effects: Vec::new(),
99 clip: None,
100 }
101 }
102}
103
104#[non_exhaustive]
106#[derive(Clone, Debug)]
107pub enum ObjectKind {
108 Image(ImageSource),
109 Video(VideoSource),
110 Text(TextRun),
111 Shape(Shape),
112 Group(Vec<ObjectId>),
113 Live(LiveStreamHandle),
114}
115
116#[derive(Clone, Copy, Debug, PartialEq)]
119pub struct Transform {
120 pub position: (f32, f32),
121 pub scale: (f32, f32),
122 pub rotation: f32,
124 pub anchor: (f32, f32),
127 pub skew: (f32, f32),
129}
130
131impl Transform {
132 pub const fn identity() -> Self {
133 Transform {
134 position: (0.0, 0.0),
135 scale: (1.0, 1.0),
136 rotation: 0.0,
137 anchor: (0.5, 0.5),
138 skew: (0.0, 0.0),
139 }
140 }
141}
142
143impl Default for Transform {
144 fn default() -> Self {
145 Transform::identity()
146 }
147}
148
149#[non_exhaustive]
151#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
152pub enum BlendMode {
153 #[default]
154 Normal,
155 Multiply,
156 Screen,
157 Overlay,
158 Add,
159 Subtract,
161 Copy,
164}
165
166#[derive(Clone, Debug)]
170pub struct Effect {
171 pub name: String,
172 pub params: Vec<(String, f32)>,
173}
174
175#[derive(Clone, Copy, Debug, PartialEq)]
177pub struct ClipRect {
178 pub x: f32,
179 pub y: f32,
180 pub width: f32,
181 pub height: f32,
182}
183
184#[non_exhaustive]
187#[derive(Clone, Debug)]
188pub enum ImageSource {
189 Decoded(Arc<oxideav_core::VideoFrame>),
191 Path(String),
193 EncodedBytes(Arc<[u8]>),
195}
196
197#[non_exhaustive]
200#[derive(Clone, Debug)]
201pub enum VideoSource {
202 Path(String),
203 EncodedBytes(Arc<[u8]>),
204}
205
206#[derive(Clone, Debug, Default)]
210pub struct TextRun {
211 pub text: String,
212 pub font_family: String,
213 pub font_weight: u16,
214 pub font_size: f32,
215 pub color: u32,
217 pub advances: Option<Vec<f32>>,
220 pub italic: bool,
221 pub underline: bool,
222}
223
224#[non_exhaustive]
226#[derive(Clone, Debug)]
227pub enum Shape {
228 Rect {
229 width: f32,
230 height: f32,
231 fill: u32,
232 stroke: Option<Stroke>,
233 corner_radius: f32,
234 },
235 Polygon {
236 points: Vec<(f32, f32)>,
237 fill: u32,
238 stroke: Option<Stroke>,
239 },
240 Path {
241 data: String,
243 fill: u32,
244 stroke: Option<Stroke>,
245 },
246}
247
248impl Shape {
249 pub const fn rect(width: f32, height: f32) -> Self {
252 Shape::Rect {
253 width,
254 height,
255 fill: 0,
256 stroke: None,
257 corner_radius: 0.0,
258 }
259 }
260}
261
262#[derive(Clone, Copy, Debug, PartialEq)]
263pub struct Stroke {
264 pub color: u32,
265 pub width: f32,
266}
267
268#[derive(Clone, Debug)]
271pub struct LiveStreamHandle {
272 pub uri: String,
276 pub hint_size: Option<(u32, u32)>,
279}
280
281#[cfg(test)]
282mod tests {
283 use super::*;
284
285 #[test]
286 fn raster_canvas_size() {
287 let c = Canvas::raster(640, 480);
288 assert_eq!(c.raster_size(), Some((640, 480)));
289 }
290
291 #[test]
292 fn vector_canvas_no_raster_size() {
293 let c = Canvas::Vector {
294 width: 595.0,
295 height: 842.0,
296 unit: LengthUnit::Point,
297 };
298 assert!(c.raster_size().is_none());
299 }
300
301 #[test]
302 fn transform_identity_roundtrip() {
303 let t = Transform::identity();
304 assert_eq!(t.position, (0.0, 0.0));
305 assert_eq!(t.scale, (1.0, 1.0));
306 assert_eq!(t.anchor, (0.5, 0.5));
307 }
308
309 #[test]
310 fn scene_object_default_is_neutral() {
311 let o = SceneObject::default();
312 assert_eq!(o.opacity, 1.0);
313 assert_eq!(o.blend_mode, BlendMode::Normal);
314 assert!(o.animations.is_empty());
315 }
316}