dotzuki_renderer/
walk_sprite.rs1use dotzuki_engine::render::{FrameBuffer, Rgba};
11
12pub struct WalkSprite {
14 pixels: Vec<Rgba>,
16 sheet_w: u32,
17 pub frame_w: u32,
18 pub frame_h: u32,
19 pub rows: u32,
20 pub cols: u32,
21}
22
23impl WalkSprite {
24 pub fn from_rgba(
26 pixels: Vec<Rgba>,
27 sheet_w: u32,
28 sheet_h: u32,
29 frame_w: u32,
30 frame_h: u32,
31 ) -> Result<Self, String> {
32 if frame_w == 0
33 || frame_h == 0
34 || sheet_w < frame_w
35 || sheet_h < frame_h
36 || pixels.len() != (sheet_w * sheet_h) as usize
37 {
38 return Err(format!(
39 "bad sheet {sheet_w}x{sheet_h} / frame {frame_w}x{frame_h} / {} px",
40 pixels.len()
41 ));
42 }
43 Ok(Self {
44 pixels,
45 sheet_w,
46 frame_w,
47 frame_h,
48 rows: sheet_h / frame_h,
49 cols: sheet_w / frame_w,
50 })
51 }
52
53 #[cfg(feature = "image-assets")]
55 pub fn load(path: &std::path::Path, frame_w: u32, frame_h: u32) -> Result<Self, String> {
56 let bytes = std::fs::read(path).map_err(|e| format!("read {}: {e}", path.display()))?;
57 let img = image::load_from_memory(&bytes)
58 .map_err(|e| format!("decode {}: {e}", path.display()))?
59 .to_rgba8();
60 let (w, h) = img.dimensions();
61 let pixels = img
62 .pixels()
63 .map(|p| Rgba::new(p.0[0], p.0[1], p.0[2], p.0[3]))
64 .collect();
65 Self::from_rgba(pixels, w, h, frame_w, frame_h)
66 }
67
68 #[inline]
70 pub fn pixel(&self, row: u32, col: u32, fx: u32, fy: u32) -> Rgba {
71 if row >= self.rows || col >= self.cols || fx >= self.frame_w || fy >= self.frame_h {
72 return Rgba::TRANSPARENT;
73 }
74 let x = col * self.frame_w + fx;
75 let y = row * self.frame_h + fy;
76 self.pixels
77 .get((y * self.sheet_w + x) as usize)
78 .copied()
79 .unwrap_or(Rgba::TRANSPARENT)
80 }
81
82 pub fn draw_on_tile(
86 &self,
87 fb: &mut FrameBuffer,
88 row: u32,
89 col: u32,
90 foot_x: i32,
91 foot_y: i32,
92 tile: i32,
93 ) {
94 let sx = foot_x - (self.frame_w as i32 - tile) / 2;
95 let sy = foot_y - (self.frame_h as i32 - tile);
96 let (w, h) = (fb.width() as i32, fb.height() as i32);
97 for fy in 0..self.frame_h {
98 let py = sy + fy as i32;
99 if py < 0 || py >= h {
100 continue;
101 }
102 for fx in 0..self.frame_w {
103 let c = self.pixel(row, col, fx, fy);
104 if c.a == 0 {
105 continue;
106 }
107 let px = sx + fx as i32;
108 if px < 0 || px >= w {
109 continue;
110 }
111 fb.set_pixel(px as u32, py as u32, c);
112 }
113 }
114 }
115}