devotee_backend_softbuffer/
surface.rs1use 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
55pub 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}