1use crate::blocks::SpriteAtlas;
5use crate::engine::sprites;
6use crate::error::{MeshError, Result};
7use crate::render::Renderer;
8
9#[derive(Debug, Default, Clone, Copy)]
11pub struct CpuRenderer;
12
13impl CpuRenderer {
14 #[must_use]
16 pub fn new() -> Self {
17 CpuRenderer
18 }
19}
20
21impl Renderer for CpuRenderer {
22 fn render_frame(&self, atlas: &SpriteAtlas, frame_index: u32) -> Result<Vec<u8>> {
23 sprites::extract_frame(atlas, frame_index)
24 }
25
26 fn compose(
27 &self,
28 layers: &[(&SpriteAtlas, u32, i32, i32)],
29 width: u32,
30 height: u32,
31 ) -> Result<Vec<u8>> {
32 if width == 0 || height == 0 {
33 return Err(MeshError::InvalidBlock("canvas must be non-empty".into()));
34 }
35 let mut canvas = vec![0u8; width as usize * height as usize * 4];
36 for &(atlas, frame_index, x, y) in layers {
37 let frame = sprites::extract_frame(atlas, frame_index)?;
38 blit_src_over(
39 &mut canvas,
40 width,
41 height,
42 &frame,
43 atlas.frame_width,
44 atlas.frame_height,
45 x,
46 y,
47 );
48 }
49 Ok(canvas)
50 }
51}
52
53#[allow(clippy::too_many_arguments)]
55fn blit_src_over(
56 dst: &mut [u8],
57 dst_w: u32,
58 dst_h: u32,
59 src: &[u8],
60 src_w: u32,
61 src_h: u32,
62 ox: i32,
63 oy: i32,
64) {
65 for sy in 0..src_h as i32 {
66 let dy = oy + sy;
67 if dy < 0 || dy >= dst_h as i32 {
68 continue;
69 }
70 for sx in 0..src_w as i32 {
71 let dx = ox + sx;
72 if dx < 0 || dx >= dst_w as i32 {
73 continue;
74 }
75 let si = (sy as u32 * src_w + sx as u32) as usize * 4;
76 let di = (dy as u32 * dst_w + dx as u32) as usize * 4;
77 blend_pixel(&mut dst[di..di + 4], &src[si..si + 4]);
78 }
79 }
80}
81
82fn blend_pixel(dst: &mut [u8], src: &[u8]) {
85 let sa = src[3] as u16;
86 if sa == 0 {
87 return;
88 }
89 if sa == 255 {
90 dst.copy_from_slice(src);
91 return;
92 }
93 let da = dst[3] as u32;
96 let sa32 = sa as u32;
97 let inv = 255 - sa32;
98 let out_a = sa32 + (da * inv + 127) / 255;
99 if out_a == 0 {
100 dst.iter_mut().for_each(|b| *b = 0);
101 return;
102 }
103 for c in 0..3 {
104 let sc = src[c] as u32;
105 let dc = dst[c] as u32;
106 let premul = sc * sa32 * 255 + dc * da * inv;
107 dst[c] = ((premul / out_a + 127) / 255) as u8;
108 }
109 dst[3] = out_a as u8;
110}