devotee_backend_softbuffer/
surface.rs

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