Skip to main content

kas_soft/
lib.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License in the LICENSE-APACHE file or at:
4//     https://www.apache.org/licenses/LICENSE-2.0
5
6//! KAS graphics backend over [softbuffer]
7//!
8//! This crate implements a KAS's drawing APIs over [softbuffer].
9//!
10//! This crate supports themes via the [`kas::theme`].
11//!
12//! [softbuffer]: https://github.com/rust-windowing/softbuffer
13
14mod atlas;
15mod basic;
16mod draw;
17
18use std::num::NonZeroU32;
19use std::sync::Arc;
20use std::time::Instant;
21
22pub use draw::{Draw, Shared};
23use kas::cast::Cast;
24use kas::draw::{SharedState, WindowCommon, color};
25use kas::geom::Size;
26use kas::runner::raw_window_handle::{HasDisplayHandle, HasWindowHandle};
27use kas::runner::{
28    GraphicsFeatures, GraphicsInstance, HasDisplayAndWindowHandle, RunError, WindowSurface,
29};
30
31/// Graphics context
32pub struct Instance {}
33
34impl Instance {
35    /// Construct a new `Instance`
36    #[allow(clippy::new_without_default)]
37    pub fn new() -> Self {
38        Instance {}
39    }
40}
41
42pub struct Surface {
43    size: Size,
44    surface: softbuffer::Surface<Arc<dyn HasDisplayHandle>, Arc<dyn HasWindowHandle>>,
45    draw: Draw,
46}
47
48/// Convert to softbuffer's color representation
49///
50/// sRGB is assumed.
51fn color_to_u32(c: impl Into<color::Rgba8Srgb>) -> u32 {
52    u32::from_be_bytes(c.into().0) >> 8
53}
54
55impl WindowSurface for Surface {
56    type Shared = Shared;
57
58    fn size(&self) -> Size {
59        self.size
60    }
61
62    fn configure(&mut self, _: &mut Shared, size: Size) -> bool {
63        if size == self.size() {
64            return false;
65        }
66
67        self.size = size;
68        self.draw.resize(size);
69
70        let width = NonZeroU32::new(size.0.cast()).expect("zero-sized surface");
71        let height = NonZeroU32::new(size.1.cast()).expect("zero-sized surface");
72        self.surface
73            .resize(width, height)
74            .expect("surface resize failed");
75        true
76    }
77
78    fn draw_iface<'iface>(
79        &'iface mut self,
80        shared: &'iface mut SharedState<Shared>,
81    ) -> kas::draw::DrawIface<'iface, Shared> {
82        kas::draw::DrawIface::new(&mut self.draw, shared)
83    }
84
85    fn common_mut(&mut self) -> &mut WindowCommon {
86        &mut self.draw.common
87    }
88
89    fn present(&mut self, shared: &mut Shared, clear_color: color::Rgba) -> Instant {
90        let mut buffer = self
91            .surface
92            .buffer_mut()
93            .expect("failed to access surface buffer");
94        let width: usize = self.size.0.cast();
95        let height: usize = self.size.1.cast();
96        debug_assert_eq!(width * height, buffer.len());
97
98        let c = color_to_u32(clear_color);
99        buffer.fill(c);
100
101        self.draw.render(shared, &mut buffer, (width, height));
102
103        let pre_present = Instant::now();
104        buffer.present().expect("failed to present buffer");
105        pre_present
106    }
107}
108
109impl GraphicsInstance for Instance {
110    type Shared = Shared;
111
112    type Surface = Surface;
113
114    fn new_shared(&mut self, _: Option<&Surface>, _: GraphicsFeatures) -> Result<Shared, RunError> {
115        Ok(Shared::default())
116    }
117
118    fn new_surface(
119        &mut self,
120        window: std::sync::Arc<dyn HasDisplayAndWindowHandle + Send + Sync>,
121        _: bool,
122    ) -> std::result::Result<Self::Surface, RunError>
123    where
124        Self: Sized,
125    {
126        let display = window.clone() as Arc<dyn HasDisplayHandle>;
127        let window = window as Arc<dyn HasWindowHandle>;
128        let context =
129            softbuffer::Context::new(display).map_err(|err| RunError::Graphics(Box::new(err)))?;
130        let surface = softbuffer::Surface::new(&context, window)
131            .map_err(|err| RunError::Graphics(Box::new(err)))?;
132
133        Ok(Surface {
134            size: Size::ZERO,
135            surface,
136            draw: Draw::default(),
137        })
138    }
139}