Skip to main content

combs_mesh/engine/
sprites.rs

1//! Sprite atlas helpers: frame geometry and frame extraction. Used by the
2//! renderers; kept renderer-agnostic so a future GPU renderer reuses them.
3
4use crate::blocks::SpriteAtlas;
5use crate::error::{MeshError, Result};
6
7/// Returns `(x, y, width, height)` of frame `frame_index` in the atlas.
8/// Frames are laid out row-major: `cols = atlas.width / frame_width`.
9pub fn frame_rect(atlas: &SpriteAtlas, frame_index: u32) -> Result<(u32, u32, u32, u32)> {
10    atlas.validate()?;
11    if frame_index >= atlas.frame_count {
12        return Err(MeshError::InvalidBlock(format!(
13            "frame index {frame_index} out of range ({} frames)",
14            atlas.frame_count
15        )));
16    }
17    let cols = atlas.width / atlas.frame_width;
18    let x = (frame_index % cols) * atlas.frame_width;
19    let y = (frame_index / cols) * atlas.frame_height;
20    Ok((x, y, atlas.frame_width, atlas.frame_height))
21}
22
23/// Extracts frame `frame_index` as a tightly packed
24/// `frame_width * frame_height * 4` RGBA8 buffer.
25pub fn extract_frame(atlas: &SpriteAtlas, frame_index: u32) -> Result<Vec<u8>> {
26    let (x, y, w, h) = frame_rect(atlas, frame_index)?;
27    let stride = atlas.width as usize * 4;
28    let row_bytes = w as usize * 4;
29    let mut out = Vec::with_capacity(row_bytes * h as usize);
30    for row in 0..h as usize {
31        let start = (y as usize + row) * stride + x as usize * 4;
32        out.extend_from_slice(&atlas.rgba[start..start + row_bytes]);
33    }
34    Ok(out)
35}