Skip to main content

siglus_scene_vm/assets/
mod.rs

1pub mod g00;
2
3use anyhow::{bail, Context, Result};
4use std::path::Path;
5
6/// A decoded RGBA image.
7#[derive(Clone, Debug)]
8pub struct RgbaImage {
9    pub width: u32,
10    pub height: u32,
11    /// Image-space center metadata from formats that carry it (notably G00 cuts).
12    /// Siglus OBJECT/PCT rendering applies this in the object render path only;
13    /// helper textures keep this at zero and remain top-left positioned.
14    pub center_x: i32,
15    pub center_y: i32,
16    /// length = width * height * 4
17    pub rgba: Vec<u8>,
18}
19
20/// Load an image from disk.
21///
22/// Supported:
23/// - .g00 (decoded by our g00 decoder)
24/// - .png/.jpg/.bmp (decoded by `image` crate)
25///
26/// DDS is detected but not decoded in this stage.
27pub fn load_image_any(path: &Path, g00_frame_index: usize) -> Result<RgbaImage> {
28    let ext = path
29        .extension()
30        .and_then(|s| s.to_str())
31        .unwrap_or("")
32        .to_ascii_lowercase();
33
34    match ext.as_str() {
35        "g00" => {
36            let bytes = crate::resource::read_file_bytes(path).with_context(|| format!("read {:?}", path))?;
37            let decoded =
38                g00::decode_g00(&bytes).with_context(|| format!("decode g00 {:?}", path))?;
39            if decoded.frames.is_empty() {
40                bail!("g00 has no frames: {:?}", path);
41            }
42            if g00_frame_index >= decoded.frames.len() {
43                bail!(
44                    "g00 frame index out of range: {:?} index={} count={}",
45                    path,
46                    g00_frame_index,
47                    decoded.frames.len()
48                );
49            }
50            Ok(decoded.frames[g00_frame_index].clone())
51        }
52        "png" | "jpg" | "jpeg" | "bmp" | "dds" => {
53            #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
54            let img = {
55                let bytes = crate::resource::read_file_bytes(path)
56                    .with_context(|| format!("read image {:?}", path))?;
57                image::load_from_memory(&bytes).with_context(|| format!("decode image {:?}", path))?
58            };
59            #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
60            let img = image::open(path).with_context(|| format!("decode image {:?}", path))?;
61            let rgba = img.to_rgba8();
62            let (w, h) = rgba.dimensions();
63            Ok(RgbaImage {
64                width: w,
65                height: h,
66                center_x: 0,
67                center_y: 0,
68                rgba: rgba.into_raw(),
69            })
70        }
71        _ => {
72            bail!("unsupported image extension: {:?}", path);
73        }
74    }
75}