devotee_backend_pixels/
lib.rs

1#![deny(missing_docs)]
2
3//! [Pixels](https://crates.io/crates/pixels)-based backend for the [devotee](https://crates.io/crates/devotee) project.
4
5use std::num::NonZeroU32;
6
7use devotee_backend::winit::dpi::PhysicalPosition;
8use devotee_backend::winit::window::Window;
9use devotee_backend::{Backend, BackendImage, Converter};
10use pixels::wgpu::{Color, TextureFormat};
11use pixels::{Pixels, PixelsBuilder, SurfaceTexture};
12
13/// [Pixels](https://crates.io/crates/pixels)-based backend.
14pub struct PixelsBackend {
15    pixels: Pixels,
16}
17
18impl Backend for PixelsBackend {
19    fn new(window: &Window, resolution: (u32, u32), _scale: u32) -> Option<Self> {
20        let pixels = {
21            let window_size = window.inner_size();
22            let surface_texture =
23                SurfaceTexture::new(window_size.width, window_size.height, &window);
24            let builder = PixelsBuilder::new(resolution.0, resolution.1, surface_texture)
25                .texture_format(TextureFormat::Rgba8Unorm)
26                .surface_texture_format(TextureFormat::Bgra8Unorm);
27
28            #[cfg(target_arch = "wasm32")]
29            {
30                pollster::block_on(builder.build_async())
31            }
32            #[cfg(not(target_arch = "wasm32"))]
33            {
34                builder.build()
35            }
36        }
37        .ok()?;
38
39        Some(PixelsBackend { pixels })
40    }
41
42    fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Option<()> {
43        self.pixels.resize_surface(width.into(), height.into()).ok()
44    }
45
46    fn draw_image<'a, P: 'a, I>(
47        &mut self,
48        image: &'a dyn BackendImage<'a, P, Iterator = I>,
49        converter: &dyn Converter<Palette = P>,
50        _window: &Window,
51        background: u32,
52    ) -> Option<()>
53    where
54        I: Iterator<Item = &'a P>,
55    {
56        let r = (background & 0x00ff0000) >> 16;
57        let g = (background & 0x0000ff00) >> 8;
58        let b = background & 0x000000ff;
59
60        self.pixels.clear_color(Color {
61            r: r as f64 / 255.0,
62            g: g as f64 / 255.0,
63            b: b as f64 / 255.0,
64            a: 1.0,
65        });
66
67        for (chunk, pixel) in self
68            .pixels
69            .frame_mut()
70            .chunks_exact_mut(4)
71            .zip(image.pixels())
72        {
73            let argb = converter.convert(pixel);
74            let r = ((argb & 0x00ff0000) >> 16) as u8;
75            let g = ((argb & 0x0000ff00) >> 8) as u8;
76            let b = (argb & 0x000000ff) as u8;
77            chunk[0] = r;
78            chunk[1] = g;
79            chunk[2] = b;
80            chunk[3] = 0xff;
81        }
82
83        self.pixels.render().ok()
84    }
85
86    fn window_pos_to_inner(
87        &self,
88        position: PhysicalPosition<f64>,
89        _window: &Window,
90        _resolution: (u32, u32),
91    ) -> Result<(i32, i32), (i32, i32)> {
92        self.pixels
93            .window_pos_to_pixel((position.x as f32, position.y as f32))
94            .map(|(a, b)| (a as i32, b as i32))
95            .map_err(|(a, b)| (a as i32, b as i32))
96    }
97}