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 Vector(oxideav_core::VectorFrame),
123}
124
125#[derive(Clone, Copy, Debug, PartialEq)]
128pub struct Transform {
129 pub position: (f32, f32),
130 pub scale: (f32, f32),
131 pub rotation: f32,
133 pub anchor: (f32, f32),
136 pub skew: (f32, f32),
138}
139
140impl Transform {
141 pub const fn identity() -> Self {
142 Transform {
143 position: (0.0, 0.0),
144 scale: (1.0, 1.0),
145 rotation: 0.0,
146 anchor: (0.5, 0.5),
147 skew: (0.0, 0.0),
148 }
149 }
150}
151
152impl Default for Transform {
153 fn default() -> Self {
154 Transform::identity()
155 }
156}
157
158#[non_exhaustive]
160#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
161pub enum BlendMode {
162 #[default]
163 Normal,
164 Multiply,
165 Screen,
166 Overlay,
167 Add,
168 Subtract,
170 Copy,
173}
174
175#[derive(Clone, Debug)]
179pub struct Effect {
180 pub name: String,
181 pub params: Vec<(String, f32)>,
182}
183
184#[derive(Clone, Copy, Debug, PartialEq)]
186pub struct ClipRect {
187 pub x: f32,
188 pub y: f32,
189 pub width: f32,
190 pub height: f32,
191}
192
193#[non_exhaustive]
196#[derive(Clone, Debug)]
197pub enum ImageSource {
198 Decoded(Arc<oxideav_core::VideoFrame>),
200 Path(String),
202 EncodedBytes(Arc<[u8]>),
204}
205
206#[non_exhaustive]
209#[derive(Clone, Debug)]
210pub enum VideoSource {
211 Path(String),
212 EncodedBytes(Arc<[u8]>),
213}
214
215#[derive(Clone, Debug, Default)]
219pub struct TextRun {
220 pub text: String,
221 pub font_family: String,
222 pub font_weight: u16,
223 pub font_size: f32,
224 pub color: u32,
226 pub advances: Option<Vec<f32>>,
229 pub italic: bool,
230 pub underline: bool,
231}
232
233#[non_exhaustive]
235#[derive(Clone, Debug)]
236pub enum Shape {
237 Rect {
238 width: f32,
239 height: f32,
240 fill: u32,
241 stroke: Option<Stroke>,
242 corner_radius: f32,
243 },
244 Polygon {
245 points: Vec<(f32, f32)>,
246 fill: u32,
247 stroke: Option<Stroke>,
248 },
249 Path {
250 data: String,
252 fill: u32,
253 stroke: Option<Stroke>,
254 },
255}
256
257impl Shape {
258 pub const fn rect(width: f32, height: f32) -> Self {
261 Shape::Rect {
262 width,
263 height,
264 fill: 0,
265 stroke: None,
266 corner_radius: 0.0,
267 }
268 }
269}
270
271#[derive(Clone, Copy, Debug, PartialEq)]
272pub struct Stroke {
273 pub color: u32,
274 pub width: f32,
275}
276
277#[derive(Clone, Debug)]
280pub struct LiveStreamHandle {
281 pub uri: String,
285 pub hint_size: Option<(u32, u32)>,
288}
289
290#[cfg(test)]
291mod tests {
292 use super::*;
293
294 #[test]
295 fn raster_canvas_size() {
296 let c = Canvas::raster(640, 480);
297 assert_eq!(c.raster_size(), Some((640, 480)));
298 }
299
300 #[test]
301 fn vector_canvas_no_raster_size() {
302 let c = Canvas::Vector {
303 width: 595.0,
304 height: 842.0,
305 unit: LengthUnit::Point,
306 };
307 assert!(c.raster_size().is_none());
308 }
309
310 #[test]
311 fn transform_identity_roundtrip() {
312 let t = Transform::identity();
313 assert_eq!(t.position, (0.0, 0.0));
314 assert_eq!(t.scale, (1.0, 1.0));
315 assert_eq!(t.anchor, (0.5, 0.5));
316 }
317
318 #[test]
319 fn scene_object_default_is_neutral() {
320 let o = SceneObject::default();
321 assert_eq!(o.opacity, 1.0);
322 assert_eq!(o.blend_mode, BlendMode::Normal);
323 assert!(o.animations.is_empty());
324 }
325}