devotee_backend_softbuffer/
lib.rs1#![deny(missing_docs)]
2
3use std::num::NonZeroU32;
6
7use devotee_backend::winit::dpi::PhysicalPosition;
8use devotee_backend::winit::window::Window;
9use devotee_backend::{Backend, BackendImage, Converter};
10use softbuffer::{Context, Surface};
11
12pub struct SoftbufferBackend {
14 surface: Surface,
15}
16
17impl Backend for SoftbufferBackend {
18 fn new(window: &Window, resolution: (u32, u32), scale: u32) -> Option<Self> {
19 let context = unsafe { Context::new(&window) }.ok()?;
21 let mut surface = unsafe { Surface::new(&context, &window) }.ok()?;
24 unsafe {
26 surface.resize(
27 NonZeroU32::new_unchecked(resolution.0 * scale),
28 NonZeroU32::new_unchecked(resolution.1 * scale),
29 )
30 }
31 .ok()?;
32
33 Some(SoftbufferBackend { surface })
34 }
35
36 fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Option<()> {
37 self.surface.resize(width, height).ok()
38 }
39
40 fn draw_image<'a, P: 'a, I>(
41 &mut self,
42 image: &'a dyn BackendImage<'a, P, Iterator = I>,
43 converter: &dyn Converter<Palette = P>,
44 window: &Window,
45 background: u32,
46 ) -> Option<()>
47 where
48 I: Iterator<Item = &'a P>,
49 {
50 let mut buffer = self.surface.buffer_mut().ok()?;
51
52 let surface_size = window.inner_size();
53
54 if buffer.len() != (surface_size.width * surface_size.height) as usize {
55 return Some(());
56 }
57
58 buffer.fill(background);
59
60 let scale_x = surface_size.width / image.width();
61 let scale_y = surface_size.height / image.height();
62
63 let minimal_scale = scale_x.min(scale_y);
64
65 if minimal_scale < 1 {
66 } else {
67 let start_x = (surface_size.width - image.width() * minimal_scale) as usize / 2;
68 let start_y = (surface_size.height - image.height() * minimal_scale) as usize / 2;
69
70 for y in 0..image.height() {
71 for x in 0..image.width() {
72 let pixel = unsafe { image.pixel_unsafe(x, y) };
74 let rgb = converter.convert(pixel);
75
76 for iy in 0..minimal_scale {
77 let index = (start_x + (x * minimal_scale) as usize)
78 + (iy as usize + start_y + (y * minimal_scale) as usize)
79 * surface_size.width as usize;
80
81 buffer[index..index + minimal_scale as usize].fill(rgb);
82 }
83 }
84 }
85 }
86
87 buffer.present().ok()
88 }
89
90 fn window_pos_to_inner(
91 &self,
92 position: PhysicalPosition<f64>,
93 window: &Window,
94 resolution: (u32, u32),
95 ) -> Result<(i32, i32), (i32, i32)> {
96 let size = window.inner_size();
97 let scale_x = size.width / resolution.0;
98 let scale_y = size.height / resolution.1;
99
100 let minimal_scale = scale_x.min(scale_y);
101
102 if minimal_scale < 1 {
103 Err((0, 0))
104 } else {
105 let position = (position.x as i32, position.y as i32);
106 let start_x = ((size.width - resolution.0 * minimal_scale) / 2) as i32;
107 let start_y = ((size.height - resolution.1 * minimal_scale) / 2) as i32;
108
109 let position = (
110 (position.0 - start_x) / minimal_scale as i32,
111 (position.1 - start_y) / minimal_scale as i32,
112 );
113
114 if position.0 < 0
115 || position.0 >= resolution.0 as i32
116 || position.1 < 0
117 || position.1 >= resolution.1 as i32
118 {
119 Err(position)
120 } else {
121 Ok(position)
122 }
123 }
124 }
125}