devotee_backend_softbuffer/
surface.rs

1use std::num::NonZeroU32;
2use std::ops::{Deref, DerefMut};
3use std::rc::Rc;
4
5use devotee_backend::middling::{Fill, Surface};
6use winit::dpi::{PhysicalPosition, PhysicalSize};
7use winit::window::Window;
8
9pub(crate) fn estimate_render_window_position_scale(
10    surface_size: PhysicalSize<u32>,
11    scale: ScaleMode,
12    render_window_size: PhysicalSize<u32>,
13) -> (PhysicalPosition<u32>, u32) {
14    let scale = match scale {
15        ScaleMode::Fixed(scale) => scale.get(),
16        ScaleMode::Auto => (surface_size.width / render_window_size.width)
17            .min(surface_size.height / render_window_size.height)
18            .max(1),
19    };
20
21    (
22        PhysicalPosition::new(
23            surface_size
24                .width
25                .saturating_sub(render_window_size.width * scale)
26                / 2,
27            surface_size
28                .height
29                .saturating_sub(render_window_size.height * scale)
30                / 2,
31        ),
32        scale,
33    )
34}
35
36fn filter_coords(
37    x: u32,
38    y: u32,
39    window: (PhysicalPosition<u32>, PhysicalSize<u32>),
40) -> Option<(u32, u32)> {
41    if x >= window.1.width || y >= window.1.height {
42        None
43    } else {
44        Some((x, y))
45    }
46}
47
48#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Default)]
49pub(super) enum ScaleMode {
50    #[default]
51    Auto,
52    Fixed(NonZeroU32),
53}
54
55/// Render surface implementation.
56pub struct SoftSurface<'a> {
57    internal: softbuffer::Buffer<'a, Rc<Window>, Rc<Window>>,
58    surface_size: PhysicalSize<u32>,
59    scale: u32,
60    render_window: (PhysicalPosition<u32>, PhysicalSize<u32>),
61}
62
63impl<'a> SoftSurface<'a> {
64    pub(super) fn new(
65        internal: softbuffer::Buffer<'a, Rc<Window>, Rc<Window>>,
66        surface_size: PhysicalSize<u32>,
67        scale: ScaleMode,
68        render_window_size: PhysicalSize<u32>,
69    ) -> Self {
70        let (render_window_position, scale) =
71            estimate_render_window_position_scale(surface_size, scale, render_window_size);
72        let render_window_size = PhysicalSize::new(
73            render_window_size.width.min(surface_size.width),
74            render_window_size.height.min(surface_size.height),
75        );
76        let render_window = (render_window_position, render_window_size);
77        Self {
78            internal,
79            surface_size,
80            scale,
81            render_window,
82        }
83    }
84
85    pub(super) fn clear(&mut self, color: u32) -> Result<(), softbuffer::SoftBufferError> {
86        self.internal.fill(color);
87        Ok(())
88    }
89
90    pub(super) fn present(self) -> Result<(), softbuffer::SoftBufferError> {
91        self.internal.present()
92    }
93}
94
95impl<I> Fill<I> for SoftSurface<'_>
96where
97    I: Iterator<Item = Self::Texel>,
98{
99    fn fill_from(&mut self, data: I) {
100        let mut data = data;
101        let start_x = self.render_window.0.x;
102        let start_y = self.render_window.0.y;
103
104        for y in 0..self.render_window.1.height {
105            for x in 0..self.render_window.1.width {
106                if let Some(pixel) = data.next() {
107                    for internal_y in 0..self.scale {
108                        let index = ((start_x + x * self.scale) as usize)
109                            + ((start_y + internal_y + (y * self.scale)) * self.surface_size.width)
110                                as usize;
111                        if let Some(buffer) =
112                            self.internal.get_mut(index..(index + self.scale as usize))
113                        {
114                            buffer.fill(pixel);
115                        }
116                    }
117                }
118            }
119        }
120    }
121}
122
123impl Surface for SoftSurface<'_> {
124    type Texel = u32;
125
126    fn texel(&self, x: u32, y: u32) -> Option<u32> {
127        let (x, y) = filter_coords(x, y, self.render_window)?;
128        let window_start = self.render_window.0;
129        let surface_size = self.surface_size;
130        let scale = self.scale;
131        let buffer = self.internal.deref();
132
133        {
134            let (x, y) = (window_start.x + x * scale, window_start.y + y * scale);
135            if x >= surface_size.width || y >= surface_size.height {
136                return None;
137            }
138        }
139        let value = *buffer.get(
140            (window_start.x + x * scale + (y * scale + window_start.y) * surface_size.width)
141                as usize,
142        )?;
143        Some(value)
144    }
145
146    fn set_texel(&mut self, x: u32, y: u32, value: u32) {
147        if let Some((x, y)) = filter_coords(x, y, self.render_window) {
148            let window_start = self.render_window.0;
149            let surface_size = self.surface_size;
150            let scale = self.scale;
151            let buffer = self.internal.deref_mut();
152
153            {
154                let (x, y) = (window_start.x + x * scale, window_start.y + y * scale);
155                if x >= surface_size.width || y >= surface_size.height {
156                    return;
157                }
158            }
159
160            let start_x = window_start.x + x * scale;
161            let start_y = window_start.y + y * scale;
162
163            let end_x = (start_x + self.scale).min(self.surface_size.width);
164            for y in start_y..(start_y + self.scale) {
165                let slice_y = y * self.surface_size.width;
166                buffer[((start_x + slice_y) as usize)..((end_x + slice_y) as usize)].fill(value)
167            }
168        }
169    }
170
171    unsafe fn texel_unchecked(&self, x: u32, y: u32) -> u32 {
172        let window_start = self.render_window.0;
173        let scale = self.scale;
174        let surface_size = self.surface_size;
175        let buffer = self.internal.deref();
176        buffer[(window_start.x + x * scale + (y * scale + window_start.y) * surface_size.width)
177            as usize]
178    }
179
180    unsafe fn set_texel_unchecked(&mut self, x: u32, y: u32, value: u32) {
181        let window_start = self.render_window.0;
182        let scale = self.scale;
183        let buffer = self.internal.deref_mut();
184
185        let start_x = window_start.x + x * scale;
186        let start_y = window_start.y + y * scale;
187
188        let end_x = (start_x + self.scale).min(self.surface_size.width);
189        for y in start_y..(start_y + self.scale) {
190            let slice_y = y * self.surface_size.width;
191            buffer[((start_x + slice_y) as usize)..((end_x + slice_y) as usize)].fill(value)
192        }
193    }
194
195    fn clear(&mut self, value: Self::Texel) {
196        for y in self.render_window.0.y
197            ..(self.render_window.0.y + self.render_window.1.height * self.scale)
198        {
199            if let Some(slice) = self.internal.get_mut(
200                ((self.render_window.0.x + y * self.surface_size.width) as usize)
201                    ..((self.render_window.0.x
202                        + self.render_window.1.width * self.scale
203                        + y * self.surface_size.width) as usize),
204            ) {
205                slice.fill(value);
206            }
207        }
208    }
209
210    fn width(&self) -> u32 {
211        self.render_window.1.width
212    }
213
214    fn height(&self) -> u32 {
215        self.render_window.1.height
216    }
217}