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 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}