Convert indexed PNG images to Rust source code for WASM-4 engine.
https://gitlab.com/claudiomattera/png2wasm4src
[WASM-4] is an old-style fantasy game console implemented in WebAssembly.
Games can be developed in Rust (and in other languages), and the runtime has support for drawing sprites.
Sprites must either have a bit depth of one bit per pixel, or two bits per pixel, and must be properly encoded in variables, which can be done using the WASM-4 w4
command-line application.
This crate allows to perform the conversion from within Rust code, which allows to dynamically create variables from PNG images using a build.rs
[build script].
This crate can be used to automatically generate Rust variables from PNG images on cargo build
.
This way the Rust variables always reflect the current PNG image, and there is no risk of forgetting to update them.
Assume the following crate structure.
Directory assets
contains a subdirectory sprites
, which contains all the sprites.
Sprites are organized in subdirectories: sprite letters.png
is inside directory fonts
, and sprite tiles.png
is inside directory tiles
.
~~~~plain . ├── assets │ └── sprites │ ├── fonts │ │ └── letters.png │ └── tiles │ └── tiles.png ├── build.rs ├── Cargo.lock ├── Cargo.toml └── src └── lib.rs ~~~~
Now it is possible to generate the Rust code from the sprites PNG image inside a build.rs
build script.
~~~~rust use std::env::var; use std::fs::{File, OpenOptions}; use std::io::Write; use std::path::PathBuf;
use anyhow::Result;
use png2wasm4src::buildspritemodules_tree;
fn main() -> Result<()> { let module = buildspritemodules_tree("assets/sprites")?;
// Instruct cargo to re-run the build script if any source PNGs are changed
// cargo:rerun-if-changed=assets/sprites/player.png
// cargo:rerun-if-changed=assets/sprites/monsters/slime.png
// cargo:rerun-if-changed=assets/sprites/monsters/bandit.png
let mut cargo_instructions = String::default();
module.generate_cargo_build_instructions(&mut cargo_instructions)?;
println!("{}", cargo_instructions);
let mut output_file = open_output_file()?;
let module = module.parse()?;
writeln!(output_file, "{}", module)?;
Ok(())
}
fn openoutputfile() -> Result
The build script generates the following code, and writes it into the file ${OUT_DIR}/sprites.rs
.
~~~~rust pub mod sprites { pub mod fonts { pub const LETTERSWIDTH: u32 = 320; pub const LETTERSHEIGHT: u32 = 32; pub const LETTERSFLAGS: u32 = 1; // BLIT2BPP pub const LETTERS: [u8; 200] = [0x12, 0x34, 0x56...]; } pub mod tiles { pub const TILESWIDTH: u32 = 32; pub const TILESHEIGHT: u32 = 32; pub const TILESFLAGS: u32 = 0; // BLIT1BPP pub const TILES: [u8; 30] = [0x12, 0x34, 0x56...]; } } ~~~~
From any of the crate modules (for instance in lib.rs
) it is possible to include that file, and use all entities defined there.
~~~~rust use wasm::*;
// Include the generated file in the current module. // // Note: this is done at top level, not inside any function (but it could be // inside a module). include!(concat!(env!("OUT_DIR"), "/sprites.rs"));
fn drawsprite() { blit( sprites::tiles::TILES, 10, 10, sprites::tiles::TILESWIDTH, sprites::tiles::TILESHEIGHT, sprites::tiles::TILESFLAGS, ); blit( sprites::fonts::LETTERS, 10, 10, sprites::fonts::LETTERSWIDTH, sprites::fonts::LETTERSHEIGHT, sprites::fonts::LETTERS_FLAGS, ); } ~~~~
Copyright Claudio Mattera 2021
You are free to copy, modify, and distribute this application with attribution under the terms of the [MIT license]. See the License.txt
file for details.