From 4a9f98e09c6a33936b57b8170733cee539694c98 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Wed, 21 Jul 2021 20:26:38 +0100 Subject: [PATCH 01/16] Allow agb-image-converter from the path --- agb/Cargo.lock | 2 -- agb/Cargo.toml | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/agb/Cargo.lock b/agb/Cargo.lock index d3189c30..0efcd827 100644 --- a/agb/Cargo.lock +++ b/agb/Cargo.lock @@ -19,8 +19,6 @@ dependencies = [ [[package]] name = "agb_image_converter" version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63c48ea2b7f5bb3c4605234f6355218401a840055ce703602b8266076a1e6f30" dependencies = [ "image", "typed-builder", diff --git a/agb/Cargo.toml b/agb/Cargo.toml index d1f2eb9b..08db3bd7 100644 --- a/agb/Cargo.toml +++ b/agb/Cargo.toml @@ -20,7 +20,7 @@ lto = true bitflags = "1.2" [build-dependencies] -agb_image_converter = "0.4.0" +agb_image_converter = { version = "0.4.0", path = "../agb-image-converter" } [package.metadata.docs.rs] default-target = "thumbv6m-none-eabi" From 383c5454eab01fe0b006481db03200f4cdaf6d2e Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Wed, 21 Jul 2021 20:27:04 +0100 Subject: [PATCH 02/16] Start work for new proc macro by generating a TileData --- agb-image-converter/src/rust_generator.rs | 11 ++++++++--- agb/examples/test_logo.rs | 6 +++--- agb/src/display/example_logo.rs | 6 +++--- agb/src/display/mod.rs | 2 ++ agb/src/display/tile_data.rs | 17 +++++++++++++++++ 5 files changed, 33 insertions(+), 9 deletions(-) create mode 100644 agb/src/display/tile_data.rs diff --git a/agb-image-converter/src/rust_generator.rs b/agb-image-converter/src/rust_generator.rs index 27c49021..ae288631 100644 --- a/agb-image-converter/src/rust_generator.rs +++ b/agb-image-converter/src/rust_generator.rs @@ -12,9 +12,12 @@ pub(crate) fn generate_code( tile_size: TileSize, crate_prefix: String, ) -> io::Result<()> { + writeln!(output, "#[allow(non_upper_case_globals)]")?; + writeln!(output, "pub const {}: {}::display::tile_data::TileData = {{", "test", crate_prefix)?; + writeln!( output, - "pub const PALETTE_DATA: &[{}::display::palette16::Palette16] = &[", + "const PALETTE_DATA: &[{}::display::palette16::Palette16] = &[", crate_prefix, )?; @@ -39,7 +42,7 @@ pub(crate) fn generate_code( writeln!(output, "];")?; writeln!(output)?; - writeln!(output, "pub const TILE_DATA: &[u32] = &[",)?; + writeln!(output, "const TILE_DATA: &[u32] = &[",)?; let tile_size = tile_size.to_size(); @@ -82,7 +85,7 @@ pub(crate) fn generate_code( writeln!(output, "];")?; writeln!(output)?; - write!(output, "pub const PALETTE_ASSIGNMENT: &[u8] = &[")?; + write!(output, "const PALETTE_ASSIGNMENT: &[u8] = &[")?; for (i, assignment) in results.assignments.iter().enumerate() { if i % 16 == 0 { @@ -93,5 +96,7 @@ pub(crate) fn generate_code( writeln!(output, "\n];")?; + writeln!(output, "{}::display::tile_data::TileData::new(PALETTE_DATA, TILE_DATA, PALETTE_ASSIGNMENT)\n}};", crate_prefix)?; + Ok(()) } diff --git a/agb/examples/test_logo.rs b/agb/examples/test_logo.rs index 888e04d5..29f49884 100644 --- a/agb/examples/test_logo.rs +++ b/agb/examples/test_logo.rs @@ -10,14 +10,14 @@ pub fn main() -> ! { let mut gba = agb::Gba::new(); let mut gfx = gba.display.video.tiled0(); - gfx.set_background_palettes(example_logo::PALETTE_DATA); - gfx.set_background_tilemap(0, example_logo::TILE_DATA); + gfx.set_background_palettes(example_logo::test.palettes); + gfx.set_background_tilemap(0, example_logo::test.tiles); let mut back = gfx.get_background().unwrap(); let mut entries: [u16; 30 * 20] = [0; 30 * 20]; for tile_id in 0..(30 * 20) { - let palette_entry = example_logo::PALETTE_ASSIGNMENT[tile_id as usize] as u16; + let palette_entry = example_logo::test.palette_assignments[tile_id as usize] as u16; entries[tile_id as usize] = tile_id | (palette_entry << 12); } diff --git a/agb/src/display/example_logo.rs b/agb/src/display/example_logo.rs index 9adc2b3d..2d5379f0 100644 --- a/agb/src/display/example_logo.rs +++ b/agb/src/display/example_logo.rs @@ -4,14 +4,14 @@ include!(concat!(env!("OUT_DIR"), "/test_logo.rs")); fn logo_display(gba: &mut crate::Gba) { let mut gfx = gba.display.video.tiled0(); - gfx.set_background_palettes(PALETTE_DATA); - gfx.set_background_tilemap(0, TILE_DATA); + gfx.set_background_palettes(test.palettes); + gfx.set_background_tilemap(0, test.tiles); let mut back = gfx.get_background().unwrap(); let mut entries: [u16; 30 * 20] = [0; 30 * 20]; for tile_id in 0..(30 * 20) { - let palette_entry = PALETTE_ASSIGNMENT[tile_id as usize] as u16; + let palette_entry = test.palette_assignments[tile_id as usize] as u16; entries[tile_id as usize] = tile_id | (palette_entry << 12); } diff --git a/agb/src/display/mod.rs b/agb/src/display/mod.rs index afd81570..9b00c4e2 100644 --- a/agb/src/display/mod.rs +++ b/agb/src/display/mod.rs @@ -22,6 +22,8 @@ pub mod tiled0; pub mod vblank; /// Giving out graphics mode. pub mod video; +/// Data produced by agb-image-converter +pub mod tile_data; const DISPLAY_CONTROL: MemoryMapped = unsafe { MemoryMapped::new(0x0400_0000) }; const DISPLAY_STATUS: MemoryMapped = unsafe { MemoryMapped::new(0x0400_0004) }; diff --git a/agb/src/display/tile_data.rs b/agb/src/display/tile_data.rs new file mode 100644 index 00000000..aa6630ef --- /dev/null +++ b/agb/src/display/tile_data.rs @@ -0,0 +1,17 @@ +use crate::display::palette16::Palette16; + +pub struct TileData { + pub palettes: &'static [Palette16], + pub tiles: &'static [u32], + pub palette_assignments: &'static [u8], +} + +impl TileData { + pub const fn new(palettes: &'static [Palette16], tiles: &'static [u32], palette_assignments: &'static [u8]) -> Self { + TileData { + palettes, + tiles, + palette_assignments, + } + } +} \ No newline at end of file From 53952d8e349f09c11f3943e13a8d70864390eb80 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Wed, 21 Jul 2021 20:59:25 +0100 Subject: [PATCH 03/16] Add support for reading config data from a toml file --- agb-image-converter/Cargo.lock | 31 ++++++++++ agb-image-converter/Cargo.toml | 4 +- agb-image-converter/src/config.rs | 96 +++++++++++++++++++++++++++++++ agb-image-converter/src/lib.rs | 1 + agb/Cargo.lock | 31 ++++++++++ 5 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 agb-image-converter/src/config.rs diff --git a/agb-image-converter/Cargo.lock b/agb-image-converter/Cargo.lock index 80b05ad7..4eeae28a 100644 --- a/agb-image-converter/Cargo.lock +++ b/agb-image-converter/Cargo.lock @@ -13,6 +13,8 @@ name = "agb_image_converter" version = "0.4.0" dependencies = [ "image", + "serde", + "toml", "typed-builder", ] @@ -166,6 +168,26 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "serde" +version = "1.0.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "syn" version = "1.0.73" @@ -177,6 +199,15 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + [[package]] name = "typed-builder" version = "0.9.0" diff --git a/agb-image-converter/Cargo.toml b/agb-image-converter/Cargo.toml index 8e6889c4..cf9ed0b3 100644 --- a/agb-image-converter/Cargo.toml +++ b/agb-image-converter/Cargo.toml @@ -8,4 +8,6 @@ description = "Library for converting graphics for use on the Game Boy Advance" [dependencies] image = { version = "0.23.14", default-features = false, features = [ "png", "bmp" ] } -typed-builder = "0.9.0" \ No newline at end of file +typed-builder = "0.9.0" +toml = "0.5.8" +serde = { version = "1.0", features = ["derive"] } \ No newline at end of file diff --git a/agb-image-converter/src/config.rs b/agb-image-converter/src/config.rs new file mode 100644 index 00000000..56017ace --- /dev/null +++ b/agb-image-converter/src/config.rs @@ -0,0 +1,96 @@ +use serde::Deserialize; +use std::collections::HashMap; +use std::fs; + +use crate::{TileSize, Colour}; + +pub fn parse(filename: &str) -> Box { + let config_toml = fs::read_to_string(filename).expect("Failed to read file"); + + let config: ConfigV1 = toml::from_str(&config_toml).expect("Failed to parse file"); + + if config.version != "1.0" { + panic!("Expected version of {} to be 1.0, got {}", filename, config.version); + } + + Box::new(config) +} + +pub trait Config { + fn crate_prefix(&self) -> String; + fn images(&self) -> HashMap; +} + +pub trait Image { + fn filename(&self) -> String; + fn transparent_colour(&self) -> Option; + fn tilesize(&self) -> TileSize; +} + +#[derive(Deserialize)] +pub struct ConfigV1 { + version: String, + crate_prefix: Option, + + image: HashMap, +} + +impl Config for ConfigV1 { + fn crate_prefix(&self) -> String { + self.crate_prefix.clone().unwrap_or("agb".to_owned()) + } + + fn images(&self) -> HashMap { + self.image.iter() + .map(|(filename, image)| ( + filename.clone(), image as &dyn Image + )).collect() + } +} + +#[derive(Deserialize)] +pub struct ImageV1 { + filename: String, + transparent_colour: Option, + tile_size: TileSizeV1, +} + +impl Image for ImageV1 { + fn filename(&self) -> String { + self.filename.clone() + } + + fn transparent_colour(&self) -> Option { + if let Some(colour) = &self.transparent_colour { + if colour.len() != 6 { + panic!("Expected colour to be 6 characters, got {}", colour); + } + + let r = u8::from_str_radix(&colour[0..2], 16).unwrap(); + let g = u8::from_str_radix(&colour[2..4], 16).unwrap(); + let b = u8::from_str_radix(&colour[4..6], 16).unwrap(); + + return Some(Colour::from_rgb(r, g, b)); + } + + None + } + + fn tilesize(&self) -> TileSize { + self.tile_size.into() + } +} + +#[derive(Deserialize, Clone, Copy)] +pub enum TileSizeV1 { + #[serde(rename = "8x8")] + Tile8, +} + +impl Into for TileSizeV1 { + fn into(self) -> TileSize { + match self { + TileSizeV1::Tile8 => TileSize::Tile8, + } + } +} \ No newline at end of file diff --git a/agb-image-converter/src/lib.rs b/agb-image-converter/src/lib.rs index e13ce861..94bb4eef 100644 --- a/agb-image-converter/src/lib.rs +++ b/agb-image-converter/src/lib.rs @@ -8,6 +8,7 @@ mod colour; mod image_loader; mod palette16; mod rust_generator; +mod config; use image_loader::Image; diff --git a/agb/Cargo.lock b/agb/Cargo.lock index 0efcd827..30880c62 100644 --- a/agb/Cargo.lock +++ b/agb/Cargo.lock @@ -21,6 +21,8 @@ name = "agb_image_converter" version = "0.4.0" dependencies = [ "image", + "serde", + "toml", "typed-builder", ] @@ -174,6 +176,26 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "serde" +version = "1.0.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "syn" version = "1.0.73" @@ -185,6 +207,15 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + [[package]] name = "typed-builder" version = "0.9.0" From 47c5c0f86e47e301f12a449af0d0a12df878fde7 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Wed, 21 Jul 2021 21:01:24 +0100 Subject: [PATCH 04/16] Extract variable for output_variable_name --- agb-image-converter/src/lib.rs | 1 + agb-image-converter/src/rust_generator.rs | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/agb-image-converter/src/lib.rs b/agb-image-converter/src/lib.rs index 94bb4eef..f93a091e 100644 --- a/agb-image-converter/src/lib.rs +++ b/agb-image-converter/src/lib.rs @@ -57,6 +57,7 @@ pub fn convert_image(settings: ImageConverterConfig) { rust_generator::generate_code( &mut writer, + "test", &optimisation_results, &image, settings.tile_size, diff --git a/agb-image-converter/src/rust_generator.rs b/agb-image-converter/src/rust_generator.rs index ae288631..1fe9e5bd 100644 --- a/agb-image-converter/src/rust_generator.rs +++ b/agb-image-converter/src/rust_generator.rs @@ -7,13 +7,14 @@ use crate::TileSize; pub(crate) fn generate_code( output: &mut dyn Write, + output_variable_name: &str, results: &Palette16OptimisationResults, image: &Image, tile_size: TileSize, crate_prefix: String, ) -> io::Result<()> { writeln!(output, "#[allow(non_upper_case_globals)]")?; - writeln!(output, "pub const {}: {}::display::tile_data::TileData = {{", "test", crate_prefix)?; + writeln!(output, "pub const {}: {}::display::tile_data::TileData = {{", output_variable_name, crate_prefix)?; writeln!( output, From 8713f514be51a922d3bdd8f2faead9c32e5e0ca8 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Wed, 21 Jul 2021 22:07:09 +0100 Subject: [PATCH 05/16] Switch to a proc macro for generating the image data --- agb-image-converter/Cargo.lock | 10 +++ agb-image-converter/Cargo.toml | 6 +- agb-image-converter/src/bin/convert.rs | 17 ------ agb-image-converter/src/config.rs | 11 ++-- agb-image-converter/src/lib.rs | 74 +++++++++++++++-------- agb-image-converter/src/rust_generator.rs | 56 ++++++++--------- agb/Cargo.lock | 10 +++ agb/Cargo.toml | 2 - agb/build.rs | 12 ---- agb/examples/test_logo.rs | 14 +---- agb/gfx/agb_logo.toml | 9 +++ agb/src/display/example_logo.rs | 21 ++++--- 12 files changed, 132 insertions(+), 110 deletions(-) delete mode 100644 agb-image-converter/src/bin/convert.rs create mode 100644 agb/gfx/agb_logo.toml diff --git a/agb-image-converter/Cargo.lock b/agb-image-converter/Cargo.lock index 4eeae28a..e2070f5c 100644 --- a/agb-image-converter/Cargo.lock +++ b/agb-image-converter/Cargo.lock @@ -13,6 +13,7 @@ name = "agb_image_converter" version = "0.4.0" dependencies = [ "image", + "litrs", "serde", "toml", "typed-builder", @@ -88,6 +89,15 @@ dependencies = [ "png", ] +[[package]] +name = "litrs" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9275e0933cf8bb20f008924c0cb07a0692fe54d8064996520bf998de9eb79aa" +dependencies = [ + "proc-macro2", +] + [[package]] name = "miniz_oxide" version = "0.3.7" diff --git a/agb-image-converter/Cargo.toml b/agb-image-converter/Cargo.toml index cf9ed0b3..982334d8 100644 --- a/agb-image-converter/Cargo.toml +++ b/agb-image-converter/Cargo.toml @@ -6,8 +6,12 @@ edition = "2018" license = "MPL-2.0" description = "Library for converting graphics for use on the Game Boy Advance" +[lib] +proc-macro = true + [dependencies] image = { version = "0.23.14", default-features = false, features = [ "png", "bmp" ] } typed-builder = "0.9.0" toml = "0.5.8" -serde = { version = "1.0", features = ["derive"] } \ No newline at end of file +serde = { version = "1.0", features = ["derive"] } +litrs = "0.2.3" \ No newline at end of file diff --git a/agb-image-converter/src/bin/convert.rs b/agb-image-converter/src/bin/convert.rs deleted file mode 100644 index 366fd492..00000000 --- a/agb-image-converter/src/bin/convert.rs +++ /dev/null @@ -1,17 +0,0 @@ -use std::env; - -use agb_image_converter::{convert_image, ImageConverterConfig, TileSize}; - -fn main() { - let args: Vec<_> = env::args().collect(); - - let file_path = &args[1]; - let output_path = &args[2]; - convert_image( - ImageConverterConfig::builder() - .tile_size(TileSize::Tile8) - .input_image(file_path.into()) - .output_file(output_path.into()) - .build(), - ); -} diff --git a/agb-image-converter/src/config.rs b/agb-image-converter/src/config.rs index 56017ace..c2570f39 100644 --- a/agb-image-converter/src/config.rs +++ b/agb-image-converter/src/config.rs @@ -4,8 +4,8 @@ use std::fs; use crate::{TileSize, Colour}; -pub fn parse(filename: &str) -> Box { - let config_toml = fs::read_to_string(filename).expect("Failed to read file"); +pub(crate) fn parse(filename: &str) -> Box { + let config_toml = fs::read_to_string(filename).expect(&format!("Failed to read file {}", filename)); let config: ConfigV1 = toml::from_str(&config_toml).expect("Failed to parse file"); @@ -16,12 +16,12 @@ pub fn parse(filename: &str) -> Box { Box::new(config) } -pub trait Config { +pub(crate) trait Config { fn crate_prefix(&self) -> String; fn images(&self) -> HashMap; } -pub trait Image { +pub(crate) trait Image { fn filename(&self) -> String; fn transparent_colour(&self) -> Option; fn tilesize(&self) -> TileSize; @@ -85,12 +85,15 @@ impl Image for ImageV1 { pub enum TileSizeV1 { #[serde(rename = "8x8")] Tile8, + #[serde(rename = "16x16")] + Tile16, } impl Into for TileSizeV1 { fn into(self) -> TileSize { match self { TileSizeV1::Tile8 => TileSize::Tile8, + TileSizeV1::Tile16 => TileSize::Tile16, } } } \ No newline at end of file diff --git a/agb-image-converter/src/lib.rs b/agb-image-converter/src/lib.rs index f93a091e..97e74990 100644 --- a/agb-image-converter/src/lib.rs +++ b/agb-image-converter/src/lib.rs @@ -1,8 +1,9 @@ -use std::fs::File; -use std::io::BufWriter; -use std::path::PathBuf; +use proc_macro::TokenStream; +use litrs::StringLit; -use typed_builder::TypedBuilder; +use std::path::Path; +use std::convert::TryFrom; +use std::fmt::Write; mod colour; mod image_loader; @@ -12,10 +13,10 @@ mod config; use image_loader::Image; -pub use colour::Colour; +use colour::Colour; #[derive(Debug, Clone, Copy)] -pub enum TileSize { +pub(crate) enum TileSize { Tile8, Tile16, } @@ -29,41 +30,62 @@ impl TileSize { } } -#[derive(TypedBuilder)] -pub struct ImageConverterConfig { - #[builder(default, setter(strip_option))] - transparent_colour: Option, - tile_size: TileSize, - input_image: PathBuf, - output_file: PathBuf, +#[proc_macro] +pub fn include_gfx(input: TokenStream) -> TokenStream { + let first_token = input.into_iter().next().expect("no input"); - #[builder(default, setter(strip_option))] - crate_prefix: Option, + let filename = match StringLit::try_from(first_token) { + Err(e) => return e.to_compile_error(), + Ok(filename) => filename.into_value(), + }; + + let root = std::env::var("CARGO_MANIFEST_DIR").expect("Failed to get cargo manifest dir"); + let path = Path::new(&root).join(&*filename); + let parent = path.parent().expect("Expected a parent directory for the path"); + + let config = config::parse(&path.to_string_lossy()); + + let module_name = path.file_stem().expect("Expected a file stem"); + + let mut output = String::new(); + + writeln!(&mut output, "mod {} {{", module_name.to_string_lossy()).unwrap(); + writeln!(&mut output, "const _: &[u8] = include_bytes!(\"{}\");", path.to_string_lossy()).unwrap(); + + for (image_name, image) in config.images() { + writeln!(&mut output, "{}", convert_image(image, parent, &image_name, &config.crate_prefix())).unwrap(); + } + + writeln!(&mut output, "}}").unwrap(); + + output.parse().expect("Failed to generate valid rust code") } -pub fn convert_image(settings: ImageConverterConfig) { - let image = Image::load_from_file(&settings.input_image); +fn convert_image(settings: &dyn config::Image, parent: &Path, variable_name: &str, crate_prefix: &str) -> String { + let image_filename = &parent.join(&settings.filename()); + let image = Image::load_from_file(image_filename); - let tile_size = settings.tile_size.to_size(); + let tile_size = settings.tilesize().to_size(); if image.width % tile_size != 0 || image.height % tile_size != 0 { panic!("Image size not a multiple of tile size"); } let optimiser = optimiser_for_image(&image, tile_size); - let optimisation_results = optimiser.optimise_palettes(settings.transparent_colour); + let optimisation_results = optimiser.optimise_palettes(settings.transparent_colour()); - let output_file = File::create(&settings.output_file).expect("Failed to create file"); - let mut writer = BufWriter::new(output_file); + let mut writer = String::new(); rust_generator::generate_code( &mut writer, - "test", + variable_name, &optimisation_results, &image, - settings.tile_size, - settings.crate_prefix.unwrap_or("agb".to_owned()), - ) - .expect("Failed to write data"); + &image_filename.to_string_lossy(), + settings.tilesize(), + crate_prefix.to_owned(), + ); + + writer } fn optimiser_for_image(image: &Image, tile_size: usize) -> palette16::Palette16Optimiser { diff --git a/agb-image-converter/src/rust_generator.rs b/agb-image-converter/src/rust_generator.rs index 1fe9e5bd..34ab8f1b 100644 --- a/agb-image-converter/src/rust_generator.rs +++ b/agb-image-converter/src/rust_generator.rs @@ -1,5 +1,4 @@ -use std::io; -use std::io::Write; +use std::fmt::Write; use crate::image_loader::Image; use crate::palette16::Palette16OptimisationResults; @@ -10,40 +9,43 @@ pub(crate) fn generate_code( output_variable_name: &str, results: &Palette16OptimisationResults, image: &Image, + image_filename: &str, tile_size: TileSize, crate_prefix: String, -) -> io::Result<()> { - writeln!(output, "#[allow(non_upper_case_globals)]")?; - writeln!(output, "pub const {}: {}::display::tile_data::TileData = {{", output_variable_name, crate_prefix)?; +) { + writeln!(output, "#[allow(non_upper_case_globals)]").unwrap(); + writeln!(output, "pub const {}: {}::display::tile_data::TileData = {{", output_variable_name, crate_prefix).unwrap(); + + writeln!(output, "const _: &[u8] = include_bytes!(\"{}\");", image_filename).unwrap(); writeln!( output, "const PALETTE_DATA: &[{}::display::palette16::Palette16] = &[", crate_prefix, - )?; + ).unwrap(); for palette in &results.optimised_palettes { write!( output, " {}::display::palette16::Palette16::new([", crate_prefix - )?; + ).unwrap(); for colour in palette.clone() { - write!(output, "0x{:08x}, ", colour.to_rgb15())?; + write!(output, "0x{:08x}, ", colour.to_rgb15()).unwrap(); } for _ in palette.clone().into_iter().len()..16 { - write!(output, "0x00000000, ")?; + write!(output, "0x00000000, ").unwrap(); } - writeln!(output, "]),")?; + writeln!(output, "]),").unwrap(); } - writeln!(output, "];")?; - writeln!(output)?; + writeln!(output, "];").unwrap(); + writeln!(output).unwrap(); - writeln!(output, "const TILE_DATA: &[u32] = &[",)?; + writeln!(output, "const TILE_DATA: &[u32] = &[",).unwrap(); let tile_size = tile_size.to_size(); @@ -58,46 +60,44 @@ pub(crate) fn generate_code( output, " /* {}, {} (palette index {}) */", x, y, palette_index - )?; + ).unwrap(); for inner_y in 0..tile_size / 8 { - write!(output, " ")?; + write!(output, " ").unwrap(); for inner_x in 0..tile_size / 8 { for j in inner_y * 8..inner_y * 8 + 8 { - write!(output, "0x")?; + write!(output, "0x").unwrap(); for i in (inner_x * 8..inner_x * 8 + 8).rev() { let colour = image.colour(x * tile_size + i, y * tile_size + j); let colour_index = palette.colour_index(colour); - write!(output, "{:x}", colour_index)?; + write!(output, "{:x}", colour_index).unwrap(); } - write!(output, ", ")?; + write!(output, ", ").unwrap(); } } } - writeln!(output)?; + writeln!(output).unwrap(); } } - writeln!(output, "];")?; - writeln!(output)?; + writeln!(output, "];").unwrap(); + writeln!(output).unwrap(); - write!(output, "const PALETTE_ASSIGNMENT: &[u8] = &[")?; + write!(output, "const PALETTE_ASSIGNMENT: &[u8] = &[").unwrap(); for (i, assignment) in results.assignments.iter().enumerate() { if i % 16 == 0 { - write!(output, "\n ")?; + write!(output, "\n ").unwrap(); } - write!(output, "{}, ", assignment)?; + write!(output, "{}, ", assignment).unwrap(); } - writeln!(output, "\n];")?; + writeln!(output, "\n];").unwrap(); - writeln!(output, "{}::display::tile_data::TileData::new(PALETTE_DATA, TILE_DATA, PALETTE_ASSIGNMENT)\n}};", crate_prefix)?; - - Ok(()) + writeln!(output, "{}::display::tile_data::TileData::new(PALETTE_DATA, TILE_DATA, PALETTE_ASSIGNMENT)\n}};", crate_prefix).unwrap(); } diff --git a/agb/Cargo.lock b/agb/Cargo.lock index 30880c62..a7002fe4 100644 --- a/agb/Cargo.lock +++ b/agb/Cargo.lock @@ -21,6 +21,7 @@ name = "agb_image_converter" version = "0.4.0" dependencies = [ "image", + "litrs", "serde", "toml", "typed-builder", @@ -96,6 +97,15 @@ dependencies = [ "png", ] +[[package]] +name = "litrs" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9275e0933cf8bb20f008924c0cb07a0692fe54d8064996520bf998de9eb79aa" +dependencies = [ + "proc-macro2", +] + [[package]] name = "miniz_oxide" version = "0.3.7" diff --git a/agb/Cargo.toml b/agb/Cargo.toml index 08db3bd7..f99665aa 100644 --- a/agb/Cargo.toml +++ b/agb/Cargo.toml @@ -18,8 +18,6 @@ lto = true [dependencies] bitflags = "1.2" - -[build-dependencies] agb_image_converter = { version = "0.4.0", path = "../agb-image-converter" } [package.metadata.docs.rs] diff --git a/agb/build.rs b/agb/build.rs index e0b833cc..217ba164 100644 --- a/agb/build.rs +++ b/agb/build.rs @@ -1,5 +1,3 @@ -use agb_image_converter::{convert_image, Colour, ImageConverterConfig, TileSize}; - fn main() { println!("cargo:rerun-if-changed=crt0.s"); println!("cargo:rerun-if-changed=gba_mb.ld"); @@ -24,14 +22,4 @@ fn main() { } println!("cargo:rustc-link-search={}", out_dir); - - convert_image( - ImageConverterConfig::builder() - .transparent_colour(Colour::from_rgb(1, 1, 1)) - .tile_size(TileSize::Tile8) - .input_image("gfx/test_logo.png".into()) - .output_file(format!("{}/test_logo.rs", out_dir).into()) - .crate_prefix("crate".to_owned()) - .build(), - ); } diff --git a/agb/examples/test_logo.rs b/agb/examples/test_logo.rs index 29f49884..6eddc02b 100644 --- a/agb/examples/test_logo.rs +++ b/agb/examples/test_logo.rs @@ -10,19 +10,7 @@ pub fn main() -> ! { let mut gba = agb::Gba::new(); let mut gfx = gba.display.video.tiled0(); - gfx.set_background_palettes(example_logo::test.palettes); - gfx.set_background_tilemap(0, example_logo::test.tiles); - - let mut back = gfx.get_background().unwrap(); - - let mut entries: [u16; 30 * 20] = [0; 30 * 20]; - for tile_id in 0..(30 * 20) { - let palette_entry = example_logo::test.palette_assignments[tile_id as usize] as u16; - entries[tile_id as usize] = tile_id | (palette_entry << 12); - } - - back.draw_full_map(&entries, (30_u32, 20_u32).into(), 0); - back.show(); + example_logo::display_logo(&mut gfx); loop {} } diff --git a/agb/gfx/agb_logo.toml b/agb/gfx/agb_logo.toml new file mode 100644 index 00000000..eb1f3304 --- /dev/null +++ b/agb/gfx/agb_logo.toml @@ -0,0 +1,9 @@ +version = "1.0" + +# Only needed for within the agb crate +crate_prefix = "crate" + +[image.test_logo] +filename = "test_logo.png" +transparent_colour = "010101" +tile_size = "8x8" diff --git a/agb/src/display/example_logo.rs b/agb/src/display/example_logo.rs index 2d5379f0..6de11ae5 100644 --- a/agb/src/display/example_logo.rs +++ b/agb/src/display/example_logo.rs @@ -1,22 +1,29 @@ -include!(concat!(env!("OUT_DIR"), "/test_logo.rs")); +use agb_image_converter::include_gfx; +use crate::display::tiled0::Tiled0; -#[test_case] -fn logo_display(gba: &mut crate::Gba) { - let mut gfx = gba.display.video.tiled0(); +include_gfx!("gfx/agb_logo.toml"); - gfx.set_background_palettes(test.palettes); - gfx.set_background_tilemap(0, test.tiles); +pub fn display_logo(gfx: &mut Tiled0) { + gfx.set_background_palettes(agb_logo::test_logo.palettes); + gfx.set_background_tilemap(0, agb_logo::test_logo.tiles); let mut back = gfx.get_background().unwrap(); let mut entries: [u16; 30 * 20] = [0; 30 * 20]; for tile_id in 0..(30 * 20) { - let palette_entry = test.palette_assignments[tile_id as usize] as u16; + let palette_entry = agb_logo::test_logo.palette_assignments[tile_id as usize] as u16; entries[tile_id as usize] = tile_id | (palette_entry << 12); } back.draw_full_map(&entries, (30_u32, 20_u32).into(), 0); back.show(); +} + +#[test_case] +fn logo_display(gba: &mut crate::Gba) { + let mut gfx = gba.display.video.tiled0(); + + display_logo(&mut gfx); crate::assert_image_output("gfx/test_logo.png"); } From 8466e9eaa41b3d1e798761f390cefd6b2616e3ae Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Wed, 21 Jul 2021 22:15:46 +0100 Subject: [PATCH 06/16] Remove usage of `typed_builder` --- agb-image-converter/Cargo.lock | 12 ------------ agb-image-converter/Cargo.toml | 1 - agb/Cargo.lock | 12 ------------ 3 files changed, 25 deletions(-) diff --git a/agb-image-converter/Cargo.lock b/agb-image-converter/Cargo.lock index e2070f5c..a90c496a 100644 --- a/agb-image-converter/Cargo.lock +++ b/agb-image-converter/Cargo.lock @@ -16,7 +16,6 @@ dependencies = [ "litrs", "serde", "toml", - "typed-builder", ] [[package]] @@ -218,17 +217,6 @@ dependencies = [ "serde", ] -[[package]] -name = "typed-builder" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "345426c7406aa355b60c5007c79a2d1f5b605540072795222f17f6443e6a9c6f" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "unicode-xid" version = "0.2.2" diff --git a/agb-image-converter/Cargo.toml b/agb-image-converter/Cargo.toml index 982334d8..c8e15621 100644 --- a/agb-image-converter/Cargo.toml +++ b/agb-image-converter/Cargo.toml @@ -11,7 +11,6 @@ proc-macro = true [dependencies] image = { version = "0.23.14", default-features = false, features = [ "png", "bmp" ] } -typed-builder = "0.9.0" toml = "0.5.8" serde = { version = "1.0", features = ["derive"] } litrs = "0.2.3" \ No newline at end of file diff --git a/agb/Cargo.lock b/agb/Cargo.lock index a7002fe4..bd2fd9a1 100644 --- a/agb/Cargo.lock +++ b/agb/Cargo.lock @@ -24,7 +24,6 @@ dependencies = [ "litrs", "serde", "toml", - "typed-builder", ] [[package]] @@ -226,17 +225,6 @@ dependencies = [ "serde", ] -[[package]] -name = "typed-builder" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "345426c7406aa355b60c5007c79a2d1f5b605540072795222f17f6443e6a9c6f" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "unicode-xid" version = "0.2.2" From bc46764e2f8ee2c58588d76b96edcd353a92139a Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Wed, 21 Jul 2021 22:19:28 +0100 Subject: [PATCH 07/16] Fix clippy linter errors --- agb-image-converter/src/config.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/agb-image-converter/src/config.rs b/agb-image-converter/src/config.rs index c2570f39..4d6b3bf4 100644 --- a/agb-image-converter/src/config.rs +++ b/agb-image-converter/src/config.rs @@ -5,7 +5,7 @@ use std::fs; use crate::{TileSize, Colour}; pub(crate) fn parse(filename: &str) -> Box { - let config_toml = fs::read_to_string(filename).expect(&format!("Failed to read file {}", filename)); + let config_toml = fs::read_to_string(filename).unwrap_or_else(|_| panic!("Failed to read file {}", filename)); let config: ConfigV1 = toml::from_str(&config_toml).expect("Failed to parse file"); @@ -37,7 +37,7 @@ pub struct ConfigV1 { impl Config for ConfigV1 { fn crate_prefix(&self) -> String { - self.crate_prefix.clone().unwrap_or("agb".to_owned()) + self.crate_prefix.clone().unwrap_or_else(|| "agb".to_owned()) } fn images(&self) -> HashMap { @@ -89,11 +89,11 @@ pub enum TileSizeV1 { Tile16, } -impl Into for TileSizeV1 { - fn into(self) -> TileSize { - match self { +impl From for TileSize { + fn from(item: TileSizeV1) -> Self { + match item { TileSizeV1::Tile8 => TileSize::Tile8, TileSizeV1::Tile16 => TileSize::Tile16, } } -} \ No newline at end of file +} From 061b8be3689ce7a34f8aa13f0cc9c7eadeb883ab Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Wed, 21 Jul 2021 22:46:22 +0100 Subject: [PATCH 08/16] Run rustfmt --- agb-image-converter/src/config.rs | 24 +++++++++++------- agb-image-converter/src/lib.rs | 31 ++++++++++++++++++----- agb-image-converter/src/rust_generator.rs | 30 +++++++++++++++++----- 3 files changed, 63 insertions(+), 22 deletions(-) diff --git a/agb-image-converter/src/config.rs b/agb-image-converter/src/config.rs index 4d6b3bf4..1c2c62da 100644 --- a/agb-image-converter/src/config.rs +++ b/agb-image-converter/src/config.rs @@ -2,15 +2,19 @@ use serde::Deserialize; use std::collections::HashMap; use std::fs; -use crate::{TileSize, Colour}; +use crate::{Colour, TileSize}; pub(crate) fn parse(filename: &str) -> Box { - let config_toml = fs::read_to_string(filename).unwrap_or_else(|_| panic!("Failed to read file {}", filename)); - + let config_toml = + fs::read_to_string(filename).unwrap_or_else(|_| panic!("Failed to read file {}", filename)); + let config: ConfigV1 = toml::from_str(&config_toml).expect("Failed to parse file"); if config.version != "1.0" { - panic!("Expected version of {} to be 1.0, got {}", filename, config.version); + panic!( + "Expected version of {} to be 1.0, got {}", + filename, config.version + ); } Box::new(config) @@ -37,14 +41,16 @@ pub struct ConfigV1 { impl Config for ConfigV1 { fn crate_prefix(&self) -> String { - self.crate_prefix.clone().unwrap_or_else(|| "agb".to_owned()) + self.crate_prefix + .clone() + .unwrap_or_else(|| "agb".to_owned()) } fn images(&self) -> HashMap { - self.image.iter() - .map(|(filename, image)| ( - filename.clone(), image as &dyn Image - )).collect() + self.image + .iter() + .map(|(filename, image)| (filename.clone(), image as &dyn Image)) + .collect() } } diff --git a/agb-image-converter/src/lib.rs b/agb-image-converter/src/lib.rs index 97e74990..797df419 100644 --- a/agb-image-converter/src/lib.rs +++ b/agb-image-converter/src/lib.rs @@ -1,15 +1,15 @@ -use proc_macro::TokenStream; use litrs::StringLit; +use proc_macro::TokenStream; -use std::path::Path; use std::convert::TryFrom; use std::fmt::Write; +use std::path::Path; mod colour; +mod config; mod image_loader; mod palette16; mod rust_generator; -mod config; use image_loader::Image; @@ -41,7 +41,9 @@ pub fn include_gfx(input: TokenStream) -> TokenStream { let root = std::env::var("CARGO_MANIFEST_DIR").expect("Failed to get cargo manifest dir"); let path = Path::new(&root).join(&*filename); - let parent = path.parent().expect("Expected a parent directory for the path"); + let parent = path + .parent() + .expect("Expected a parent directory for the path"); let config = config::parse(&path.to_string_lossy()); @@ -50,10 +52,20 @@ pub fn include_gfx(input: TokenStream) -> TokenStream { let mut output = String::new(); writeln!(&mut output, "mod {} {{", module_name.to_string_lossy()).unwrap(); - writeln!(&mut output, "const _: &[u8] = include_bytes!(\"{}\");", path.to_string_lossy()).unwrap(); + writeln!( + &mut output, + "const _: &[u8] = include_bytes!(\"{}\");", + path.to_string_lossy() + ) + .unwrap(); for (image_name, image) in config.images() { - writeln!(&mut output, "{}", convert_image(image, parent, &image_name, &config.crate_prefix())).unwrap(); + writeln!( + &mut output, + "{}", + convert_image(image, parent, &image_name, &config.crate_prefix()) + ) + .unwrap(); } writeln!(&mut output, "}}").unwrap(); @@ -61,7 +73,12 @@ pub fn include_gfx(input: TokenStream) -> TokenStream { output.parse().expect("Failed to generate valid rust code") } -fn convert_image(settings: &dyn config::Image, parent: &Path, variable_name: &str, crate_prefix: &str) -> String { +fn convert_image( + settings: &dyn config::Image, + parent: &Path, + variable_name: &str, + crate_prefix: &str, +) -> String { let image_filename = &parent.join(&settings.filename()); let image = Image::load_from_file(image_filename); diff --git a/agb-image-converter/src/rust_generator.rs b/agb-image-converter/src/rust_generator.rs index 34ab8f1b..d8f71100 100644 --- a/agb-image-converter/src/rust_generator.rs +++ b/agb-image-converter/src/rust_generator.rs @@ -14,22 +14,34 @@ pub(crate) fn generate_code( crate_prefix: String, ) { writeln!(output, "#[allow(non_upper_case_globals)]").unwrap(); - writeln!(output, "pub const {}: {}::display::tile_data::TileData = {{", output_variable_name, crate_prefix).unwrap(); + writeln!( + output, + "pub const {}: {}::display::tile_data::TileData = {{", + output_variable_name, crate_prefix + ) + .unwrap(); - writeln!(output, "const _: &[u8] = include_bytes!(\"{}\");", image_filename).unwrap(); + writeln!( + output, + "const _: &[u8] = include_bytes!(\"{}\");", + image_filename + ) + .unwrap(); writeln!( output, "const PALETTE_DATA: &[{}::display::palette16::Palette16] = &[", crate_prefix, - ).unwrap(); + ) + .unwrap(); for palette in &results.optimised_palettes { write!( output, " {}::display::palette16::Palette16::new([", crate_prefix - ).unwrap(); + ) + .unwrap(); for colour in palette.clone() { write!(output, "0x{:08x}, ", colour.to_rgb15()).unwrap(); @@ -60,7 +72,8 @@ pub(crate) fn generate_code( output, " /* {}, {} (palette index {}) */", x, y, palette_index - ).unwrap(); + ) + .unwrap(); for inner_y in 0..tile_size / 8 { write!(output, " ").unwrap(); @@ -99,5 +112,10 @@ pub(crate) fn generate_code( writeln!(output, "\n];").unwrap(); - writeln!(output, "{}::display::tile_data::TileData::new(PALETTE_DATA, TILE_DATA, PALETTE_ASSIGNMENT)\n}};", crate_prefix).unwrap(); + writeln!( + output, + "{}::display::tile_data::TileData::new(PALETTE_DATA, TILE_DATA, PALETTE_ASSIGNMENT)\n}};", + crate_prefix + ) + .unwrap(); } From 21a11a4e411512f1f22655042ea8970e2f18b76c Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Wed, 21 Jul 2021 22:47:10 +0100 Subject: [PATCH 09/16] Run rustfmt on modified files in agb --- agb/src/display/example_logo.rs | 2 +- agb/src/display/mod.rs | 4 ++-- agb/src/display/tile_data.rs | 8 ++++++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/agb/src/display/example_logo.rs b/agb/src/display/example_logo.rs index 6de11ae5..c35f81f1 100644 --- a/agb/src/display/example_logo.rs +++ b/agb/src/display/example_logo.rs @@ -1,5 +1,5 @@ -use agb_image_converter::include_gfx; use crate::display::tiled0::Tiled0; +use agb_image_converter::include_gfx; include_gfx!("gfx/agb_logo.toml"); diff --git a/agb/src/display/mod.rs b/agb/src/display/mod.rs index 9b00c4e2..b3d68bad 100644 --- a/agb/src/display/mod.rs +++ b/agb/src/display/mod.rs @@ -16,14 +16,14 @@ pub mod example_logo; pub mod object; /// Palette type. pub mod palette16; +/// Data produced by agb-image-converter +pub mod tile_data; /// Graphics mode 0. Four regular backgrounds. pub mod tiled0; /// Syscall for waiting for vblank. pub mod vblank; /// Giving out graphics mode. pub mod video; -/// Data produced by agb-image-converter -pub mod tile_data; const DISPLAY_CONTROL: MemoryMapped = unsafe { MemoryMapped::new(0x0400_0000) }; const DISPLAY_STATUS: MemoryMapped = unsafe { MemoryMapped::new(0x0400_0004) }; diff --git a/agb/src/display/tile_data.rs b/agb/src/display/tile_data.rs index aa6630ef..a8698e82 100644 --- a/agb/src/display/tile_data.rs +++ b/agb/src/display/tile_data.rs @@ -7,11 +7,15 @@ pub struct TileData { } impl TileData { - pub const fn new(palettes: &'static [Palette16], tiles: &'static [u32], palette_assignments: &'static [u8]) -> Self { + pub const fn new( + palettes: &'static [Palette16], + tiles: &'static [u32], + palette_assignments: &'static [u8], + ) -> Self { TileData { palettes, tiles, palette_assignments, } } -} \ No newline at end of file +} From 19e0a0527b1ccb103398df25fe52a12b0714d30d Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Thu, 22 Jul 2021 18:43:27 +0100 Subject: [PATCH 10/16] Use syn crate rather than litrs --- agb-image-converter/Cargo.lock | 11 +---------- agb-image-converter/Cargo.toml | 2 +- agb-image-converter/src/lib.rs | 10 +++------- agb/Cargo.lock | 11 +---------- 4 files changed, 6 insertions(+), 28 deletions(-) diff --git a/agb-image-converter/Cargo.lock b/agb-image-converter/Cargo.lock index a90c496a..fb990437 100644 --- a/agb-image-converter/Cargo.lock +++ b/agb-image-converter/Cargo.lock @@ -13,8 +13,8 @@ name = "agb_image_converter" version = "0.4.0" dependencies = [ "image", - "litrs", "serde", + "syn", "toml", ] @@ -88,15 +88,6 @@ dependencies = [ "png", ] -[[package]] -name = "litrs" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9275e0933cf8bb20f008924c0cb07a0692fe54d8064996520bf998de9eb79aa" -dependencies = [ - "proc-macro2", -] - [[package]] name = "miniz_oxide" version = "0.3.7" diff --git a/agb-image-converter/Cargo.toml b/agb-image-converter/Cargo.toml index c8e15621..95326b63 100644 --- a/agb-image-converter/Cargo.toml +++ b/agb-image-converter/Cargo.toml @@ -13,4 +13,4 @@ proc-macro = true image = { version = "0.23.14", default-features = false, features = [ "png", "bmp" ] } toml = "0.5.8" serde = { version = "1.0", features = ["derive"] } -litrs = "0.2.3" \ No newline at end of file +syn = "1.0.73" \ No newline at end of file diff --git a/agb-image-converter/src/lib.rs b/agb-image-converter/src/lib.rs index 797df419..cad79bae 100644 --- a/agb-image-converter/src/lib.rs +++ b/agb-image-converter/src/lib.rs @@ -1,7 +1,6 @@ -use litrs::StringLit; use proc_macro::TokenStream; +use syn::parse_macro_input; -use std::convert::TryFrom; use std::fmt::Write; use std::path::Path; @@ -32,12 +31,9 @@ impl TileSize { #[proc_macro] pub fn include_gfx(input: TokenStream) -> TokenStream { - let first_token = input.into_iter().next().expect("no input"); + let input = parse_macro_input!(input as syn::LitStr); - let filename = match StringLit::try_from(first_token) { - Err(e) => return e.to_compile_error(), - Ok(filename) => filename.into_value(), - }; + let filename = input.value(); let root = std::env::var("CARGO_MANIFEST_DIR").expect("Failed to get cargo manifest dir"); let path = Path::new(&root).join(&*filename); diff --git a/agb/Cargo.lock b/agb/Cargo.lock index bd2fd9a1..aa2dd606 100644 --- a/agb/Cargo.lock +++ b/agb/Cargo.lock @@ -21,8 +21,8 @@ name = "agb_image_converter" version = "0.4.0" dependencies = [ "image", - "litrs", "serde", + "syn", "toml", ] @@ -96,15 +96,6 @@ dependencies = [ "png", ] -[[package]] -name = "litrs" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9275e0933cf8bb20f008924c0cb07a0692fe54d8064996520bf998de9eb79aa" -dependencies = [ - "proc-macro2", -] - [[package]] name = "miniz_oxide" version = "0.3.7" From d7a24d965956e72b776ae5b41a61b56632d32775 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Thu, 22 Jul 2021 19:03:49 +0100 Subject: [PATCH 11/16] Start switching to the quote crate for generating the code --- agb-image-converter/Cargo.lock | 2 ++ agb-image-converter/Cargo.toml | 4 +++- agb-image-converter/src/lib.rs | 36 ++++++++++++++-------------------- agb/Cargo.lock | 2 ++ 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/agb-image-converter/Cargo.lock b/agb-image-converter/Cargo.lock index fb990437..a0dfd7c4 100644 --- a/agb-image-converter/Cargo.lock +++ b/agb-image-converter/Cargo.lock @@ -13,6 +13,8 @@ name = "agb_image_converter" version = "0.4.0" dependencies = [ "image", + "proc-macro2", + "quote", "serde", "syn", "toml", diff --git a/agb-image-converter/Cargo.toml b/agb-image-converter/Cargo.toml index 95326b63..8add75bb 100644 --- a/agb-image-converter/Cargo.toml +++ b/agb-image-converter/Cargo.toml @@ -13,4 +13,6 @@ proc-macro = true image = { version = "0.23.14", default-features = false, features = [ "png", "bmp" ] } toml = "0.5.8" serde = { version = "1.0", features = ["derive"] } -syn = "1.0.73" \ No newline at end of file +syn = "1.0.73" +proc-macro2 = "1.0.27" +quote = "1.0.9" \ No newline at end of file diff --git a/agb-image-converter/src/lib.rs b/agb-image-converter/src/lib.rs index cad79bae..f7697b31 100644 --- a/agb-image-converter/src/lib.rs +++ b/agb-image-converter/src/lib.rs @@ -1,9 +1,10 @@ use proc_macro::TokenStream; use syn::parse_macro_input; -use std::fmt::Write; use std::path::Path; +use quote::{quote, format_ident}; + mod colour; mod config; mod image_loader; @@ -43,30 +44,23 @@ pub fn include_gfx(input: TokenStream) -> TokenStream { let config = config::parse(&path.to_string_lossy()); - let module_name = path.file_stem().expect("Expected a file stem"); + let module_name = format_ident!("{}", path.file_stem().expect("Expected a file stem").to_string_lossy()); + let include_path = path.to_string_lossy(); - let mut output = String::new(); + let images = config.images(); + let image_code = images + .iter() + .map(|(image_name, &image)| convert_image(image, parent, &image_name, &config.crate_prefix()).parse::().unwrap()); - writeln!(&mut output, "mod {} {{", module_name.to_string_lossy()).unwrap(); - writeln!( - &mut output, - "const _: &[u8] = include_bytes!(\"{}\");", - path.to_string_lossy() - ) - .unwrap(); + let module = quote! { + pub mod #module_name { + const _: &[u8] = include_bytes!(#include_path); - for (image_name, image) in config.images() { - writeln!( - &mut output, - "{}", - convert_image(image, parent, &image_name, &config.crate_prefix()) - ) - .unwrap(); - } + #(#image_code)* + } + }; - writeln!(&mut output, "}}").unwrap(); - - output.parse().expect("Failed to generate valid rust code") + TokenStream::from(module) } fn convert_image( diff --git a/agb/Cargo.lock b/agb/Cargo.lock index aa2dd606..67d021c9 100644 --- a/agb/Cargo.lock +++ b/agb/Cargo.lock @@ -21,6 +21,8 @@ name = "agb_image_converter" version = "0.4.0" dependencies = [ "image", + "proc-macro2", + "quote", "serde", "syn", "toml", From aa32be1a27b991c7042c98007b728adcf012f854 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Thu, 22 Jul 2021 19:04:30 +0100 Subject: [PATCH 12/16] Move parsing 1 level up --- agb-image-converter/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/agb-image-converter/src/lib.rs b/agb-image-converter/src/lib.rs index f7697b31..ec4d6d33 100644 --- a/agb-image-converter/src/lib.rs +++ b/agb-image-converter/src/lib.rs @@ -50,7 +50,7 @@ pub fn include_gfx(input: TokenStream) -> TokenStream { let images = config.images(); let image_code = images .iter() - .map(|(image_name, &image)| convert_image(image, parent, &image_name, &config.crate_prefix()).parse::().unwrap()); + .map(|(image_name, &image)| convert_image(image, parent, &image_name, &config.crate_prefix())); let module = quote! { pub mod #module_name { @@ -68,7 +68,7 @@ fn convert_image( parent: &Path, variable_name: &str, crate_prefix: &str, -) -> String { +) -> proc_macro2::TokenStream { let image_filename = &parent.join(&settings.filename()); let image = Image::load_from_file(image_filename); @@ -92,7 +92,7 @@ fn convert_image( crate_prefix.to_owned(), ); - writer + writer.parse().unwrap() } fn optimiser_for_image(image: &Image, tile_size: usize) -> palette16::Palette16Optimiser { From fdff081c32cb4c61a20304a10b7a69b67568ba12 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Mon, 26 Jul 2021 23:27:16 +0100 Subject: [PATCH 13/16] Switch entirely to using `quote` to generate the code --- agb-image-converter/src/lib.rs | 22 ++-- agb-image-converter/src/rust_generator.rs | 152 +++++++++------------- 2 files changed, 70 insertions(+), 104 deletions(-) diff --git a/agb-image-converter/src/lib.rs b/agb-image-converter/src/lib.rs index ec4d6d33..02eddcf4 100644 --- a/agb-image-converter/src/lib.rs +++ b/agb-image-converter/src/lib.rs @@ -3,7 +3,7 @@ use syn::parse_macro_input; use std::path::Path; -use quote::{quote, format_ident}; +use quote::{format_ident, quote}; mod colour; mod config; @@ -44,13 +44,18 @@ pub fn include_gfx(input: TokenStream) -> TokenStream { let config = config::parse(&path.to_string_lossy()); - let module_name = format_ident!("{}", path.file_stem().expect("Expected a file stem").to_string_lossy()); + let module_name = format_ident!( + "{}", + path.file_stem() + .expect("Expected a file stem") + .to_string_lossy() + ); let include_path = path.to_string_lossy(); let images = config.images(); - let image_code = images - .iter() - .map(|(image_name, &image)| convert_image(image, parent, &image_name, &config.crate_prefix())); + let image_code = images.iter().map(|(image_name, &image)| { + convert_image(image, parent, &image_name, &config.crate_prefix()) + }); let module = quote! { pub mod #module_name { @@ -80,19 +85,14 @@ fn convert_image( let optimiser = optimiser_for_image(&image, tile_size); let optimisation_results = optimiser.optimise_palettes(settings.transparent_colour()); - let mut writer = String::new(); - rust_generator::generate_code( - &mut writer, variable_name, &optimisation_results, &image, &image_filename.to_string_lossy(), settings.tilesize(), crate_prefix.to_owned(), - ); - - writer.parse().unwrap() + ) } fn optimiser_for_image(image: &Image, tile_size: usize) -> palette16::Palette16Optimiser { diff --git a/agb-image-converter/src/rust_generator.rs b/agb-image-converter/src/rust_generator.rs index d8f71100..b8514085 100644 --- a/agb-image-converter/src/rust_generator.rs +++ b/agb-image-converter/src/rust_generator.rs @@ -1,121 +1,87 @@ -use std::fmt::Write; - use crate::image_loader::Image; use crate::palette16::Palette16OptimisationResults; use crate::TileSize; +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; + +use std::iter; + pub(crate) fn generate_code( - output: &mut dyn Write, output_variable_name: &str, results: &Palette16OptimisationResults, image: &Image, image_filename: &str, tile_size: TileSize, crate_prefix: String, -) { - writeln!(output, "#[allow(non_upper_case_globals)]").unwrap(); - writeln!( - output, - "pub const {}: {}::display::tile_data::TileData = {{", - output_variable_name, crate_prefix - ) - .unwrap(); +) -> TokenStream { + let crate_prefix = format_ident!("{}", crate_prefix); + let output_variable_name = format_ident!("{}", output_variable_name); - writeln!( - output, - "const _: &[u8] = include_bytes!(\"{}\");", - image_filename - ) - .unwrap(); + let palette_data = results.optimised_palettes.iter().map(|palette| { + let colours = palette + .clone() + .into_iter() + .map(|colour| colour.to_rgb15()) + .chain(iter::repeat(0)) + .take(16); - writeln!( - output, - "const PALETTE_DATA: &[{}::display::palette16::Palette16] = &[", - crate_prefix, - ) - .unwrap(); - - for palette in &results.optimised_palettes { - write!( - output, - " {}::display::palette16::Palette16::new([", - crate_prefix - ) - .unwrap(); - - for colour in palette.clone() { - write!(output, "0x{:08x}, ", colour.to_rgb15()).unwrap(); + quote! { + #crate_prefix::display::palette16::Palette16::new([ + #(#colours),* + ]) } - - for _ in palette.clone().into_iter().len()..16 { - write!(output, "0x00000000, ").unwrap(); - } - - writeln!(output, "]),").unwrap(); - } - - writeln!(output, "];").unwrap(); - writeln!(output).unwrap(); - - writeln!(output, "const TILE_DATA: &[u32] = &[",).unwrap(); + }); let tile_size = tile_size.to_size(); let tiles_x = image.width / tile_size; let tiles_y = image.height / tile_size; - for y in 0..tiles_y { - for x in 0..tiles_x { - let palette_index = results.assignments[y * tiles_x + x]; - let palette = &results.optimised_palettes[palette_index]; - writeln!( - output, - " /* {}, {} (palette index {}) */", - x, y, palette_index - ) - .unwrap(); + let tile_data = (0..tiles_y) + .map(|y| { + (0..tiles_x).map(move |x| { + let palette_index = results.assignments[y * tiles_x + x]; + let palette = &results.optimised_palettes[palette_index]; - for inner_y in 0..tile_size / 8 { - write!(output, " ").unwrap(); + (0..tile_size / 8).map(move |inner_y| { + (0..tile_size / 8).map(move |inner_x| { + (inner_y * 8..inner_y * 8 + 8).map(move |j| { + (inner_x * 8..inner_x * 8 + 8).rev().map(move |i| { + let colour = image.colour(x * tile_size + i, y * tile_size + j); + palette.colour_index(colour) + }) + }) + }) + }) + }) + }) + .flatten() + .flatten() + .flatten() + .flatten() + .flatten(); - for inner_x in 0..tile_size / 8 { - for j in inner_y * 8..inner_y * 8 + 8 { - write!(output, "0x").unwrap(); + let assignments = &results.assignments; - for i in (inner_x * 8..inner_x * 8 + 8).rev() { - let colour = image.colour(x * tile_size + i, y * tile_size + j); - let colour_index = palette.colour_index(colour); + quote! { + #[allow(non_upper_case_globals)] + pub const #output_variable_name: #crate_prefix::display::tile_data::TileData = { + const _: &[u8] = include_bytes!(#image_filename); - write!(output, "{:x}", colour_index).unwrap(); - } + const PALETTE_DATA: &[#crate_prefix::display::palette16::Palette16] = &[ + #(#palette_data),* + ]; - write!(output, ", ").unwrap(); - } - } - } + const TILE_DATA: &[u32] = &[ + #(#tile_data),* + ]; - writeln!(output).unwrap(); - } + const PALETTE_ASSIGNMENT: &[u8] = &[ + #(#assignments),* + ]; + + #crate_prefix::display::tile_data::TileData::new(PALETTE_DATA, TILE_DATA, PALETTE_ASSIGNMENT) + }; } - - writeln!(output, "];").unwrap(); - writeln!(output).unwrap(); - - write!(output, "const PALETTE_ASSIGNMENT: &[u8] = &[").unwrap(); - - for (i, assignment) in results.assignments.iter().enumerate() { - if i % 16 == 0 { - write!(output, "\n ").unwrap(); - } - write!(output, "{}, ", assignment).unwrap(); - } - - writeln!(output, "\n];").unwrap(); - - writeln!( - output, - "{}::display::tile_data::TileData::new(PALETTE_DATA, TILE_DATA, PALETTE_ASSIGNMENT)\n}};", - crate_prefix - ) - .unwrap(); } From 3626e2cb18f730a26a7c1fb583bed56006ff6fb9 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Mon, 26 Jul 2021 23:30:46 +0100 Subject: [PATCH 14/16] Feels cleaner with a bunch of for loops --- agb-image-converter/src/rust_generator.rs | 40 ++++++++++------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/agb-image-converter/src/rust_generator.rs b/agb-image-converter/src/rust_generator.rs index b8514085..a36f82d5 100644 --- a/agb-image-converter/src/rust_generator.rs +++ b/agb-image-converter/src/rust_generator.rs @@ -38,29 +38,25 @@ pub(crate) fn generate_code( let tiles_x = image.width / tile_size; let tiles_y = image.height / tile_size; - let tile_data = (0..tiles_y) - .map(|y| { - (0..tiles_x).map(move |x| { - let palette_index = results.assignments[y * tiles_x + x]; - let palette = &results.optimised_palettes[palette_index]; + let mut tile_data = vec![]; - (0..tile_size / 8).map(move |inner_y| { - (0..tile_size / 8).map(move |inner_x| { - (inner_y * 8..inner_y * 8 + 8).map(move |j| { - (inner_x * 8..inner_x * 8 + 8).rev().map(move |i| { - let colour = image.colour(x * tile_size + i, y * tile_size + j); - palette.colour_index(colour) - }) - }) - }) - }) - }) - }) - .flatten() - .flatten() - .flatten() - .flatten() - .flatten(); + for y in 0..tiles_y { + for x in 0..tiles_x { + let palette_index = results.assignments[y * tiles_x + x]; + let palette = &results.optimised_palettes[palette_index]; + + for inner_y in 0..tile_size / 8 { + for inner_x in 0..tile_size / 8 { + for j in inner_y * 8..inner_y * 8 + 8 { + for i in inner_x * 8..inner_x * 8 + 8 { + let colour = image.colour(x * tile_size + i, y * tile_size + j); + tile_data.push(palette.colour_index(colour)); + } + } + } + } + } + } let assignments = &results.assignments; From 0796247a2efb79eba277046da97e385df7c0f21e Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Mon, 26 Jul 2021 23:38:22 +0100 Subject: [PATCH 15/16] Finalize the code generation --- agb-image-converter/src/rust_generator.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/agb-image-converter/src/rust_generator.rs b/agb-image-converter/src/rust_generator.rs index a36f82d5..f3fafa83 100644 --- a/agb-image-converter/src/rust_generator.rs +++ b/agb-image-converter/src/rust_generator.rs @@ -24,7 +24,8 @@ pub(crate) fn generate_code( .into_iter() .map(|colour| colour.to_rgb15()) .chain(iter::repeat(0)) - .take(16); + .take(16) + .map(|colour| colour as u16); quote! { #crate_prefix::display::palette16::Palette16::new([ @@ -48,7 +49,7 @@ pub(crate) fn generate_code( for inner_y in 0..tile_size / 8 { for inner_x in 0..tile_size / 8 { for j in inner_y * 8..inner_y * 8 + 8 { - for i in inner_x * 8..inner_x * 8 + 8 { + for i in (inner_x * 8..inner_x * 8 + 8).rev() { let colour = image.colour(x * tile_size + i, y * tile_size + j); tile_data.push(palette.colour_index(colour)); } @@ -58,7 +59,10 @@ pub(crate) fn generate_code( } } - let assignments = &results.assignments; + let tile_data = tile_data.chunks(8) + .map(|chunk| chunk.iter().fold(0u32, |acc, &x| (acc << 4) | (x as u32))); + + let assignments = results.assignments.iter().map(|&x| x as u8); quote! { #[allow(non_upper_case_globals)] From e3510a73f7f8c03664e4cc7e5be3bdd113d8966a Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Mon, 26 Jul 2021 23:42:28 +0100 Subject: [PATCH 16/16] Re-export the image converter macro --- agb/src/display/example_logo.rs | 3 +-- agb/src/lib.rs | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/agb/src/display/example_logo.rs b/agb/src/display/example_logo.rs index c35f81f1..7b9c520d 100644 --- a/agb/src/display/example_logo.rs +++ b/agb/src/display/example_logo.rs @@ -1,7 +1,6 @@ use crate::display::tiled0::Tiled0; -use agb_image_converter::include_gfx; -include_gfx!("gfx/agb_logo.toml"); +crate::include_gfx!("gfx/agb_logo.toml"); pub fn display_logo(gfx: &mut Tiled0) { gfx.set_background_palettes(agb_logo::test_logo.palettes); diff --git a/agb/src/lib.rs b/agb/src/lib.rs index 286b0076..dc7a73ab 100644 --- a/agb/src/lib.rs +++ b/agb/src/lib.rs @@ -20,6 +20,8 @@ pub mod input; /// Implements sound output. pub mod sound; +pub use agb_image_converter::include_gfx; + mod bitarray; mod interrupt; mod memory_mapped;