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 Fill for SoftSurface<'_> {
96    fn fill_from(&mut self, data: &[Self::Texel]) {
97        let start_x = self.render_window.0.x;
98        let start_y = self.render_window.0.y;
99
100        for y in 0..self.render_window.1.height {
101            for x in 0..self.render_window.1.width {
102                if let Some(pixel) = data.get((x + y * self.render_window.1.width) as usize) {
103                    for internal_y in 0..self.scale {
104                        let index = ((start_x + x * self.scale) as usize)
105                            + ((start_y + internal_y + (y * self.scale)) * self.surface_size.width)
106                                as usize;
107                        if let Some(buffer) =
108                            self.internal.get_mut(index..(index + self.scale as usize))
109                        {
110                            buffer.fill(*pixel);
111                        }
112                    }
113                }
114            }
115        }
116    }
117}
118
119impl Surface for SoftSurface<'_> {
120    type Texel = u32;
121
122    fn texel(&self, x: u32, y: u32) -> Option<u32> {
123        let (x, y) = filter_coords(x, y, self.render_window)?;
124        let window_start = self.render_window.0;
125        let surface_size = self.surface_size;
126        let scale = self.scale;
127        let buffer = self.internal.deref();
128
129        {
130            let (x, y) = (window_start.x + x * scale, window_start.y + y * scale);
131            if x >= surface_size.width || y >= surface_size.height {
132                return None;
133            }
134        }
135        let value = *buffer.get(
136            (window_start.x + x * scale + (y * scale + window_start.y) * surface_size.width)
137                as usize,
138        )?;
139        Some(value)
140    }
141
142    fn set_texel(&mut self, x: u32, y: u32, value: u32) {
143        if let Some((x, y)) = filter_coords(x, y, self.render_window) {
144            let window_start = self.render_window.0;
145            let surface_size = self.surface_size;
146            let scale = self.scale;
147            let buffer = self.internal.deref_mut();
148
149            {
150                let (x, y) = (window_start.x + x * scale, window_start.y + y * scale);
151                if x >= surface_size.width || y >= surface_size.height {
152                    return;
153                }
154            }
155
156            let start_x = window_start.x + x * scale;
157            let start_y = window_start.y + y * scale;
158
159            let end_x = (start_x + self.scale).min(self.surface_size.width);
160            for y in start_y..(start_y + self.scale) {
161                let slice_y = y * self.surface_size.width;
162                buffer[((start_x + slice_y) as usize)..((end_x + slice_y) as usize)].fill(value)
163            }
164        }
165    }
166
167    unsafe fn texel_unchecked(&self, x: u32, y: u32) -> u32 {
168        let window_start = self.render_window.0;
169        let scale = self.scale;
170        let surface_size = self.surface_size;
171        let buffer = self.internal.deref();
172        buffer[(window_start.x + x * scale + (y * scale + window_start.y) * surface_size.width)
173            as usize]
174    }
175
176    unsafe fn set_texel_unchecked(&mut self, x: u32, y: u32, value: u32) {
177        let window_start = self.render_window.0;
178        let scale = self.scale;
179        let buffer = self.internal.deref_mut();
180
181        let start_x = window_start.x + x * scale;
182        let start_y = window_start.y + y * scale;
183
184        let end_x = (start_x + self.scale).min(self.surface_size.width);
185        for y in start_y..(start_y + self.scale) {
186            let slice_y = y * self.surface_size.width;
187            buffer[((start_x + slice_y) as usize)..((end_x + slice_y) as usize)].fill(value)
188        }
189    }
190
191    fn clear(&mut self, value: Self::Texel) {
192        for y in self.render_window.0.y
193            ..(self.render_window.0.y + self.render_window.1.height * self.scale)
194        {
195            if let Some(slice) = self.internal.get_mut(
196                ((self.render_window.0.x + y * self.surface_size.width) as usize)
197                    ..((self.render_window.0.x
198                        + self.render_window.1.width * self.scale
199                        + y * self.surface_size.width) as usize),
200            ) {
201                slice.fill(value);
202            }
203        }
204    }
205
206    fn width(&self) -> u32 {
207        self.render_window.1.width
208    }
209
210    fn height(&self) -> u32 {
211        self.render_window.1.height
212    }
213}