oxygengine_composite_renderer/
composite_renderer.rs

1use crate::math::{Color, Rect, Vec2};
2use core::{
3    assets::{asset::AssetId, database::AssetsDatabase},
4    error::*,
5    Ignite, Scalar,
6};
7use serde::{Deserialize, Serialize};
8use std::{borrow::Cow, ops::Range};
9
10#[derive(Ignite, Debug, Default, Clone, Serialize, Deserialize)]
11pub struct Rectangle {
12    pub color: Color,
13    pub rect: Rect,
14}
15
16impl Rectangle {
17    pub fn align(mut self, factor: Vec2) -> Self {
18        self.rect = self.rect.align(factor);
19        self
20    }
21}
22
23#[derive(Ignite, Debug, Copy, Clone, Serialize, Deserialize)]
24pub enum TextAlign {
25    Left,
26    Center,
27    Right,
28}
29
30impl Default for TextAlign {
31    fn default() -> Self {
32        TextAlign::Left
33    }
34}
35
36#[derive(Ignite, Debug, Copy, Clone, Serialize, Deserialize)]
37pub enum TextBaseLine {
38    Top,
39    Middle,
40    Bottom,
41    Alphabetic,
42    Hanging,
43}
44
45impl Default for TextBaseLine {
46    fn default() -> Self {
47        TextBaseLine::Top
48    }
49}
50
51#[derive(Ignite, Debug, Default, Clone, Serialize, Deserialize)]
52pub struct Text<'a> {
53    #[serde(default)]
54    pub color: Color,
55    #[serde(default)]
56    pub font: Cow<'a, str>,
57    #[serde(default)]
58    pub align: TextAlign,
59    #[serde(default)]
60    pub baseline: TextBaseLine,
61    #[serde(default)]
62    pub text: Cow<'a, str>,
63    #[serde(default)]
64    pub position: Vec2,
65    #[serde(default = "Text::default_size")]
66    pub size: Scalar,
67    #[serde(default)]
68    pub max_width: Option<Scalar>,
69}
70
71impl<'a> Text<'a> {
72    fn default_size() -> Scalar {
73        32.0
74    }
75
76    pub fn new(font: &'a str, text: &'a str) -> Self {
77        Self {
78            color: Default::default(),
79            font: font.into(),
80            align: Default::default(),
81            baseline: Default::default(),
82            text: text.into(),
83            position: 0.0.into(),
84            size: 32.0,
85            max_width: None,
86        }
87    }
88
89    pub fn new_owned(font: String, text: String) -> Self {
90        Self {
91            color: Default::default(),
92            font: font.into(),
93            align: Default::default(),
94            baseline: Default::default(),
95            text: text.into(),
96            position: 0.0.into(),
97            size: 32.0,
98            max_width: None,
99        }
100    }
101
102    pub fn color(mut self, color: Color) -> Self {
103        self.color = color;
104        self
105    }
106
107    pub fn align(mut self, align: TextAlign) -> Self {
108        self.align = align;
109        self
110    }
111
112    pub fn baseline(mut self, baseline: TextBaseLine) -> Self {
113        self.baseline = baseline;
114        self
115    }
116
117    pub fn position(mut self, position: Vec2) -> Self {
118        self.position = position;
119        self
120    }
121
122    pub fn size(mut self, size: Scalar) -> Self {
123        self.size = size;
124        self
125    }
126}
127
128#[derive(Ignite, Debug, Clone, Serialize, Deserialize)]
129pub enum PathElement {
130    MoveTo(Vec2),
131    LineTo(Vec2),
132    /// (control point A, control point B, point)
133    BezierCurveTo(Vec2, Vec2, Vec2),
134    /// (control point, point)
135    QuadraticCurveTo(Vec2, Vec2),
136    /// (point, radius, angles range)
137    Arc(Vec2, Scalar, Range<Scalar>),
138    /// (point, radius, rotation, angles range)
139    Ellipse(Vec2, Vec2, Scalar, Range<Scalar>),
140    Rectangle(Rect),
141}
142
143#[derive(Ignite, Debug, Default, Clone, Serialize, Deserialize)]
144pub struct Path {
145    #[serde(default)]
146    pub color: Color,
147    #[serde(default)]
148    pub elements: Vec<PathElement>,
149}
150
151#[derive(Ignite, Debug, Default, Clone, Serialize, Deserialize)]
152pub struct Mask {
153    #[serde(default)]
154    pub elements: Vec<PathElement>,
155}
156
157#[derive(Ignite, Debug, Default, Copy, Clone, Serialize, Deserialize)]
158pub struct TriangleFace {
159    #[serde(default)]
160    pub a: usize,
161    #[serde(default)]
162    pub b: usize,
163    #[serde(default)]
164    pub c: usize,
165}
166
167impl TriangleFace {
168    pub fn new(a: usize, b: usize, c: usize) -> Self {
169        Self { a, b, c }
170    }
171}
172
173#[derive(Ignite, Debug, Default, Clone, Serialize, Deserialize)]
174pub struct Triangles<'a> {
175    #[serde(default)]
176    pub image: Cow<'a, str>,
177    #[serde(default)]
178    pub color: Color,
179    /// [(position, tex coord)]
180    #[serde(default)]
181    pub vertices: Vec<(Vec2, Vec2)>,
182    #[serde(default)]
183    pub faces: Vec<TriangleFace>,
184}
185
186#[derive(Ignite, Debug, Default, Clone, Serialize, Deserialize)]
187pub struct Image<'a> {
188    #[serde(default)]
189    pub image: Cow<'a, str>,
190    #[serde(default)]
191    pub source: Option<Rect>,
192    #[serde(default)]
193    pub destination: Option<Rect>,
194    #[serde(default)]
195    pub alignment: Vec2,
196}
197
198impl<'a> Image<'a> {
199    pub fn new(image: &'a str) -> Self {
200        Self {
201            image: image.into(),
202            source: None,
203            destination: None,
204            alignment: 0.0.into(),
205        }
206    }
207
208    pub fn new_owned(image: String) -> Self {
209        Self {
210            image: image.into(),
211            source: None,
212            destination: None,
213            alignment: 0.0.into(),
214        }
215    }
216
217    pub fn source(mut self, rect: Option<Rect>) -> Self {
218        self.source = rect;
219        self
220    }
221
222    pub fn destination(mut self, rect: Option<Rect>) -> Self {
223        self.destination = rect;
224        self
225    }
226
227    pub fn align(mut self, factor: Vec2) -> Self {
228        self.alignment = factor;
229        self
230    }
231}
232
233#[derive(Ignite, Debug, Clone, Serialize, Deserialize)]
234pub enum Renderable<'a> {
235    None,
236    Rectangle(Rectangle),
237    FullscreenRectangle(Color),
238    Text(Text<'a>),
239    Path(Path),
240    Mask(Mask),
241    Image(Image<'a>),
242    Triangles(Triangles<'a>),
243    Commands(Vec<Command<'a>>),
244}
245
246impl<'a> Renderable<'a> {
247    pub fn is_none(&self) -> bool {
248        matches!(self, Self::None)
249    }
250}
251
252impl<'a> From<()> for Renderable<'a> {
253    fn from(_: ()) -> Self {
254        Renderable::None
255    }
256}
257
258impl<'a> From<Rectangle> for Renderable<'a> {
259    fn from(rect: Rectangle) -> Self {
260        Renderable::Rectangle(rect)
261    }
262}
263
264impl<'a> From<Text<'a>> for Renderable<'a> {
265    fn from(text: Text<'a>) -> Self {
266        Renderable::Text(text)
267    }
268}
269
270impl<'a> From<Path> for Renderable<'a> {
271    fn from(path: Path) -> Self {
272        Renderable::Path(path)
273    }
274}
275
276impl<'a> From<Mask> for Renderable<'a> {
277    fn from(mask: Mask) -> Self {
278        Renderable::Mask(mask)
279    }
280}
281
282impl<'a> From<Image<'a>> for Renderable<'a> {
283    fn from(image: Image<'a>) -> Self {
284        Renderable::Image(image)
285    }
286}
287
288impl<'a> From<Triangles<'a>> for Renderable<'a> {
289    fn from(triangles: Triangles<'a>) -> Self {
290        Renderable::Triangles(triangles)
291    }
292}
293
294#[derive(Ignite, Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
295pub enum Effect {
296    SourceOver,
297    SourceIn,
298    SourceOut,
299    SourceAtop,
300    DestinationOver,
301    DestinationIn,
302    DestinationOut,
303    DestinationAtop,
304    Lighter,
305    Copy,
306    Xor,
307    Multiply,
308    Screen,
309    Overlay,
310    Darken,
311    Lighten,
312    ColorDodge,
313    ColorBurn,
314    HardLight,
315    SoftLight,
316    Difference,
317    Exclusion,
318    Hue,
319    Saturation,
320    Color,
321    Luminosity,
322}
323
324impl Default for Effect {
325    fn default() -> Self {
326        Effect::SourceOver
327    }
328}
329
330impl ToString for Effect {
331    fn to_string(&self) -> String {
332        use Effect::*;
333        match self {
334            SourceOver => "source-over",
335            SourceIn => "source-in",
336            SourceOut => "source-out",
337            SourceAtop => "source-atop",
338            DestinationOver => "destination-over",
339            DestinationIn => "destination-in",
340            DestinationOut => "destination-out",
341            DestinationAtop => "destination-atop",
342            Lighter => "lighter",
343            Copy => "copy",
344            Xor => "xor",
345            Multiply => "multiply",
346            Screen => "screen",
347            Overlay => "overlay",
348            Darken => "darken",
349            Lighten => "lighten",
350            ColorDodge => "color-dodge",
351            ColorBurn => "color-burn",
352            HardLight => "hard-light",
353            SoftLight => "soft-light",
354            Difference => "difference",
355            Exclusion => "exclusion",
356            Hue => "hue",
357            Saturation => "saturation",
358            Color => "color",
359            Luminosity => "luminosity",
360        }
361        .to_owned()
362    }
363}
364
365#[derive(Ignite, Debug, Clone, Serialize, Deserialize)]
366pub enum Command<'a> {
367    None,
368    Draw(Renderable<'a>),
369    /// (line width, renderable)
370    Stroke(Scalar, Renderable<'a>),
371    /// (a, b, c, d, e, f)
372    Transform(Scalar, Scalar, Scalar, Scalar, Scalar, Scalar),
373    Effect(Effect),
374    Alpha(Scalar),
375    Filter(Cow<'a, str>),
376    Smoothing(bool),
377    Store,
378    Restore,
379}
380
381#[derive(Debug, Default, Clone, Serialize, Deserialize)]
382pub struct Stats {
383    pub view_size: Vec2,
384    pub render_ops: usize,
385    pub renderables: usize,
386    pub fps: Scalar,
387    pub delta_time: Scalar,
388    pub images_count: usize,
389    pub fontfaces_count: usize,
390    pub surfaces_count: usize,
391}
392
393#[derive(Debug, Clone, Serialize, Deserialize)]
394pub struct RenderState {
395    pub clear_color: Option<Color>,
396    pub image_smoothing: bool,
397    pub image_source_inner_margin: Scalar,
398    pub triangles_outer_margin: Scalar,
399    stats: Stats,
400}
401
402impl Default for RenderState {
403    fn default() -> Self {
404        Self {
405            clear_color: Some(Color::black()),
406            image_smoothing: true,
407            image_source_inner_margin: 0.0,
408            triangles_outer_margin: 0.0,
409            stats: Stats::default(),
410        }
411    }
412}
413
414impl RenderState {
415    pub fn new(clear_color: Option<Color>) -> Self {
416        Self {
417            clear_color,
418            image_smoothing: true,
419            image_source_inner_margin: 0.0,
420            triangles_outer_margin: 0.0,
421            stats: Stats::default(),
422        }
423    }
424
425    pub fn clear_color(mut self, clear_color: Option<Color>) -> Self {
426        self.clear_color = clear_color;
427        self
428    }
429
430    pub fn image_smoothing(mut self, image_smoothing: bool) -> Self {
431        self.image_smoothing = image_smoothing;
432        self
433    }
434
435    pub fn image_source_inner_margin(mut self, image_source_inner_margin: Scalar) -> Self {
436        self.image_source_inner_margin = image_source_inner_margin;
437        self
438    }
439
440    pub fn triangles_outer_margin(mut self, triangles_outer_margin: Scalar) -> Self {
441        self.triangles_outer_margin = triangles_outer_margin;
442        self
443    }
444
445    pub fn stats(&self) -> &Stats {
446        &self.stats
447    }
448
449    pub fn set_stats(&mut self, stats: Stats) {
450        self.stats = stats;
451    }
452}
453
454pub trait CompositeRenderer: Send + Sync {
455    // -> (render ops, renderables)
456    fn execute<'a, I>(&mut self, commands: I) -> Result<(usize, usize)>
457    where
458        I: IntoIterator<Item = Command<'a>>;
459
460    fn images_count(&self) -> usize {
461        0
462    }
463
464    fn fontfaces_count(&self) -> usize {
465        0
466    }
467
468    fn surfaces_count(&self) -> usize {
469        0
470    }
471
472    fn state(&self) -> &RenderState;
473
474    fn state_mut(&mut self) -> &mut RenderState;
475
476    fn view_size(&self) -> Vec2;
477
478    fn update_state(&mut self) {}
479
480    fn update_cache(&mut self, _assets: &AssetsDatabase) {}
481
482    fn create_surface(&mut self, name: &str, width: usize, height: usize) -> bool;
483
484    fn destroy_surface(&mut self, name: &str) -> bool;
485
486    fn has_surface(&mut self, name: &str) -> bool;
487
488    fn get_surface_size(&self, name: &str) -> Option<(usize, usize)>;
489
490    fn update_surface<'a, I>(&mut self, name: &str, commands: I) -> Result<(usize, usize)>
491    where
492        I: IntoIterator<Item = Command<'a>>;
493}
494
495pub trait CompositeRendererResources<T> {
496    fn add_resource(&mut self, id: String, resource: T) -> Result<AssetId>;
497
498    fn remove_resource(&mut self, id: AssetId) -> Result<T>;
499}