mirror of
https://github.com/italicsjenga/agb.git
synced 2024-12-24 08:41:34 +11:00
Merge pull request #92 from gwilymk/proc-macro-for-agb-image-converter
Proc macro for agb image converter
This commit is contained in:
commit
5bc3844a14
36
agb-image-converter/Cargo.lock
generated
36
agb-image-converter/Cargo.lock
generated
|
@ -13,7 +13,11 @@ name = "agb_image_converter"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"image",
|
"image",
|
||||||
"typed-builder",
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"serde",
|
||||||
|
"syn",
|
||||||
|
"toml",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -166,6 +170,26 @@ dependencies = [
|
||||||
"proc-macro2",
|
"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]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.73"
|
version = "1.0.73"
|
||||||
|
@ -178,14 +202,12 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typed-builder"
|
name = "toml"
|
||||||
version = "0.9.0"
|
version = "0.5.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "345426c7406aa355b60c5007c79a2d1f5b605540072795222f17f6443e6a9c6f"
|
checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"serde",
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -6,6 +6,13 @@ edition = "2018"
|
||||||
license = "MPL-2.0"
|
license = "MPL-2.0"
|
||||||
description = "Library for converting graphics for use on the Game Boy Advance"
|
description = "Library for converting graphics for use on the Game Boy Advance"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
proc-macro = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
image = { version = "0.23.14", default-features = false, features = [ "png", "bmp" ] }
|
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"] }
|
||||||
|
syn = "1.0.73"
|
||||||
|
proc-macro2 = "1.0.27"
|
||||||
|
quote = "1.0.9"
|
|
@ -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(),
|
|
||||||
);
|
|
||||||
}
|
|
105
agb-image-converter/src/config.rs
Normal file
105
agb-image-converter/src/config.rs
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
use serde::Deserialize;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
|
use crate::{Colour, TileSize};
|
||||||
|
|
||||||
|
pub(crate) fn parse(filename: &str) -> Box<dyn Config> {
|
||||||
|
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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Box::new(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) trait Config {
|
||||||
|
fn crate_prefix(&self) -> String;
|
||||||
|
fn images(&self) -> HashMap<String, &dyn Image>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) trait Image {
|
||||||
|
fn filename(&self) -> String;
|
||||||
|
fn transparent_colour(&self) -> Option<Colour>;
|
||||||
|
fn tilesize(&self) -> TileSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct ConfigV1 {
|
||||||
|
version: String,
|
||||||
|
crate_prefix: Option<String>,
|
||||||
|
|
||||||
|
image: HashMap<String, ImageV1>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config for ConfigV1 {
|
||||||
|
fn crate_prefix(&self) -> String {
|
||||||
|
self.crate_prefix
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_else(|| "agb".to_owned())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn images(&self) -> HashMap<String, &dyn Image> {
|
||||||
|
self.image
|
||||||
|
.iter()
|
||||||
|
.map(|(filename, image)| (filename.clone(), image as &dyn Image))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct ImageV1 {
|
||||||
|
filename: String,
|
||||||
|
transparent_colour: Option<String>,
|
||||||
|
tile_size: TileSizeV1,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Image for ImageV1 {
|
||||||
|
fn filename(&self) -> String {
|
||||||
|
self.filename.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transparent_colour(&self) -> Option<Colour> {
|
||||||
|
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,
|
||||||
|
#[serde(rename = "16x16")]
|
||||||
|
Tile16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<TileSizeV1> for TileSize {
|
||||||
|
fn from(item: TileSizeV1) -> Self {
|
||||||
|
match item {
|
||||||
|
TileSizeV1::Tile8 => TileSize::Tile8,
|
||||||
|
TileSizeV1::Tile16 => TileSize::Tile16,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,20 +1,22 @@
|
||||||
use std::fs::File;
|
use proc_macro::TokenStream;
|
||||||
use std::io::BufWriter;
|
use syn::parse_macro_input;
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use typed_builder::TypedBuilder;
|
use std::path::Path;
|
||||||
|
|
||||||
|
use quote::{format_ident, quote};
|
||||||
|
|
||||||
mod colour;
|
mod colour;
|
||||||
|
mod config;
|
||||||
mod image_loader;
|
mod image_loader;
|
||||||
mod palette16;
|
mod palette16;
|
||||||
mod rust_generator;
|
mod rust_generator;
|
||||||
|
|
||||||
use image_loader::Image;
|
use image_loader::Image;
|
||||||
|
|
||||||
pub use colour::Colour;
|
use colour::Colour;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum TileSize {
|
pub(crate) enum TileSize {
|
||||||
Tile8,
|
Tile8,
|
||||||
Tile16,
|
Tile16,
|
||||||
}
|
}
|
||||||
|
@ -28,40 +30,69 @@ impl TileSize {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(TypedBuilder)]
|
#[proc_macro]
|
||||||
pub struct ImageConverterConfig {
|
pub fn include_gfx(input: TokenStream) -> TokenStream {
|
||||||
#[builder(default, setter(strip_option))]
|
let input = parse_macro_input!(input as syn::LitStr);
|
||||||
transparent_colour: Option<Colour>,
|
|
||||||
tile_size: TileSize,
|
|
||||||
input_image: PathBuf,
|
|
||||||
output_file: PathBuf,
|
|
||||||
|
|
||||||
#[builder(default, setter(strip_option))]
|
let filename = input.value();
|
||||||
crate_prefix: Option<String>,
|
|
||||||
|
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 = 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 module = quote! {
|
||||||
|
pub mod #module_name {
|
||||||
|
const _: &[u8] = include_bytes!(#include_path);
|
||||||
|
|
||||||
|
#(#image_code)*
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TokenStream::from(module)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn convert_image(settings: ImageConverterConfig) {
|
fn convert_image(
|
||||||
let image = Image::load_from_file(&settings.input_image);
|
settings: &dyn config::Image,
|
||||||
|
parent: &Path,
|
||||||
|
variable_name: &str,
|
||||||
|
crate_prefix: &str,
|
||||||
|
) -> proc_macro2::TokenStream {
|
||||||
|
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 {
|
if image.width % tile_size != 0 || image.height % tile_size != 0 {
|
||||||
panic!("Image size not a multiple of tile size");
|
panic!("Image size not a multiple of tile size");
|
||||||
}
|
}
|
||||||
|
|
||||||
let optimiser = optimiser_for_image(&image, 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);
|
|
||||||
|
|
||||||
rust_generator::generate_code(
|
rust_generator::generate_code(
|
||||||
&mut writer,
|
variable_name,
|
||||||
&optimisation_results,
|
&optimisation_results,
|
||||||
&image,
|
&image,
|
||||||
settings.tile_size,
|
&image_filename.to_string_lossy(),
|
||||||
settings.crate_prefix.unwrap_or("agb".to_owned()),
|
settings.tilesize(),
|
||||||
|
crate_prefix.to_owned(),
|
||||||
)
|
)
|
||||||
.expect("Failed to write data");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn optimiser_for_image(image: &Image, tile_size: usize) -> palette16::Palette16Optimiser {
|
fn optimiser_for_image(image: &Image, tile_size: usize) -> palette16::Palette16Optimiser {
|
||||||
|
|
|
@ -1,97 +1,87 @@
|
||||||
use std::io;
|
|
||||||
use std::io::Write;
|
|
||||||
|
|
||||||
use crate::image_loader::Image;
|
use crate::image_loader::Image;
|
||||||
use crate::palette16::Palette16OptimisationResults;
|
use crate::palette16::Palette16OptimisationResults;
|
||||||
use crate::TileSize;
|
use crate::TileSize;
|
||||||
|
|
||||||
|
use proc_macro2::TokenStream;
|
||||||
|
use quote::{format_ident, quote};
|
||||||
|
|
||||||
|
use std::iter;
|
||||||
|
|
||||||
pub(crate) fn generate_code(
|
pub(crate) fn generate_code(
|
||||||
output: &mut dyn Write,
|
output_variable_name: &str,
|
||||||
results: &Palette16OptimisationResults,
|
results: &Palette16OptimisationResults,
|
||||||
image: &Image,
|
image: &Image,
|
||||||
|
image_filename: &str,
|
||||||
tile_size: TileSize,
|
tile_size: TileSize,
|
||||||
crate_prefix: String,
|
crate_prefix: String,
|
||||||
) -> io::Result<()> {
|
) -> TokenStream {
|
||||||
writeln!(
|
let crate_prefix = format_ident!("{}", crate_prefix);
|
||||||
output,
|
let output_variable_name = format_ident!("{}", output_variable_name);
|
||||||
"pub const PALETTE_DATA: &[{}::display::palette16::Palette16] = &[",
|
|
||||||
crate_prefix,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
for palette in &results.optimised_palettes {
|
let palette_data = results.optimised_palettes.iter().map(|palette| {
|
||||||
write!(
|
let colours = palette
|
||||||
output,
|
.clone()
|
||||||
" {}::display::palette16::Palette16::new([",
|
.into_iter()
|
||||||
crate_prefix
|
.map(|colour| colour.to_rgb15())
|
||||||
)?;
|
.chain(iter::repeat(0))
|
||||||
|
.take(16)
|
||||||
|
.map(|colour| colour as u16);
|
||||||
|
|
||||||
for colour in palette.clone() {
|
quote! {
|
||||||
write!(output, "0x{:08x}, ", colour.to_rgb15())?;
|
#crate_prefix::display::palette16::Palette16::new([
|
||||||
|
#(#colours),*
|
||||||
|
])
|
||||||
}
|
}
|
||||||
|
});
|
||||||
for _ in palette.clone().into_iter().len()..16 {
|
|
||||||
write!(output, "0x00000000, ")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
writeln!(output, "]),")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
writeln!(output, "];")?;
|
|
||||||
writeln!(output)?;
|
|
||||||
|
|
||||||
writeln!(output, "pub const TILE_DATA: &[u32] = &[",)?;
|
|
||||||
|
|
||||||
let tile_size = tile_size.to_size();
|
let tile_size = tile_size.to_size();
|
||||||
|
|
||||||
let tiles_x = image.width / tile_size;
|
let tiles_x = image.width / tile_size;
|
||||||
let tiles_y = image.height / tile_size;
|
let tiles_y = image.height / tile_size;
|
||||||
|
|
||||||
|
let mut tile_data = vec![];
|
||||||
|
|
||||||
for y in 0..tiles_y {
|
for y in 0..tiles_y {
|
||||||
for x in 0..tiles_x {
|
for x in 0..tiles_x {
|
||||||
let palette_index = results.assignments[y * tiles_x + x];
|
let palette_index = results.assignments[y * tiles_x + x];
|
||||||
let palette = &results.optimised_palettes[palette_index];
|
let palette = &results.optimised_palettes[palette_index];
|
||||||
writeln!(
|
|
||||||
output,
|
|
||||||
" /* {}, {} (palette index {}) */",
|
|
||||||
x, y, palette_index
|
|
||||||
)?;
|
|
||||||
|
|
||||||
for inner_y in 0..tile_size / 8 {
|
for inner_y in 0..tile_size / 8 {
|
||||||
write!(output, " ")?;
|
|
||||||
|
|
||||||
for inner_x in 0..tile_size / 8 {
|
for inner_x in 0..tile_size / 8 {
|
||||||
for j in inner_y * 8..inner_y * 8 + 8 {
|
for j in inner_y * 8..inner_y * 8 + 8 {
|
||||||
write!(output, "0x")?;
|
|
||||||
|
|
||||||
for i in (inner_x * 8..inner_x * 8 + 8).rev() {
|
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 = image.colour(x * tile_size + i, y * tile_size + j);
|
||||||
let colour_index = palette.colour_index(colour);
|
tile_data.push(palette.colour_index(colour));
|
||||||
|
}
|
||||||
write!(output, "{:x}", colour_index)?;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
write!(output, ", ")?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
writeln!(output)?;
|
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)]
|
||||||
|
pub const #output_variable_name: #crate_prefix::display::tile_data::TileData = {
|
||||||
|
const _: &[u8] = include_bytes!(#image_filename);
|
||||||
|
|
||||||
|
const PALETTE_DATA: &[#crate_prefix::display::palette16::Palette16] = &[
|
||||||
|
#(#palette_data),*
|
||||||
|
];
|
||||||
|
|
||||||
|
const TILE_DATA: &[u32] = &[
|
||||||
|
#(#tile_data),*
|
||||||
|
];
|
||||||
|
|
||||||
|
const PALETTE_ASSIGNMENT: &[u8] = &[
|
||||||
|
#(#assignments),*
|
||||||
|
];
|
||||||
|
|
||||||
|
#crate_prefix::display::tile_data::TileData::new(PALETTE_DATA, TILE_DATA, PALETTE_ASSIGNMENT)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
writeln!(output, "];")?;
|
|
||||||
writeln!(output)?;
|
|
||||||
|
|
||||||
write!(output, "pub const PALETTE_ASSIGNMENT: &[u8] = &[")?;
|
|
||||||
|
|
||||||
for (i, assignment) in results.assignments.iter().enumerate() {
|
|
||||||
if i % 16 == 0 {
|
|
||||||
write!(output, "\n ")?;
|
|
||||||
}
|
|
||||||
write!(output, "{}, ", assignment)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
writeln!(output, "\n];")?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
38
agb/Cargo.lock
generated
38
agb/Cargo.lock
generated
|
@ -19,11 +19,13 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "agb_image_converter"
|
name = "agb_image_converter"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "63c48ea2b7f5bb3c4605234f6355218401a840055ce703602b8266076a1e6f30"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"image",
|
"image",
|
||||||
"typed-builder",
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"serde",
|
||||||
|
"syn",
|
||||||
|
"toml",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -176,6 +178,26 @@ dependencies = [
|
||||||
"proc-macro2",
|
"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]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.73"
|
version = "1.0.73"
|
||||||
|
@ -188,14 +210,12 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typed-builder"
|
name = "toml"
|
||||||
version = "0.9.0"
|
version = "0.5.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "345426c7406aa355b60c5007c79a2d1f5b605540072795222f17f6443e6a9c6f"
|
checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"serde",
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -18,9 +18,7 @@ lto = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bitflags = "1.2"
|
bitflags = "1.2"
|
||||||
|
agb_image_converter = { version = "0.4.0", path = "../agb-image-converter" }
|
||||||
[build-dependencies]
|
|
||||||
agb_image_converter = "0.4.0"
|
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
default-target = "thumbv6m-none-eabi"
|
default-target = "thumbv6m-none-eabi"
|
||||||
|
|
12
agb/build.rs
12
agb/build.rs
|
@ -1,5 +1,3 @@
|
||||||
use agb_image_converter::{convert_image, Colour, ImageConverterConfig, TileSize};
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("cargo:rerun-if-changed=crt0.s");
|
println!("cargo:rerun-if-changed=crt0.s");
|
||||||
println!("cargo:rerun-if-changed=gba_mb.ld");
|
println!("cargo:rerun-if-changed=gba_mb.ld");
|
||||||
|
@ -24,14 +22,4 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("cargo:rustc-link-search={}", out_dir);
|
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(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,19 +10,7 @@ pub fn main() -> ! {
|
||||||
let mut gba = agb::Gba::new();
|
let mut gba = agb::Gba::new();
|
||||||
let mut gfx = gba.display.video.tiled0();
|
let mut gfx = gba.display.video.tiled0();
|
||||||
|
|
||||||
gfx.set_background_palettes(example_logo::PALETTE_DATA);
|
example_logo::display_logo(&mut gfx);
|
||||||
gfx.set_background_tilemap(0, example_logo::TILE_DATA);
|
|
||||||
|
|
||||||
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;
|
|
||||||
entries[tile_id as usize] = tile_id | (palette_entry << 12);
|
|
||||||
}
|
|
||||||
|
|
||||||
back.draw_full_map(&entries, (30_u32, 20_u32).into(), 0);
|
|
||||||
back.show();
|
|
||||||
|
|
||||||
loop {}
|
loop {}
|
||||||
}
|
}
|
||||||
|
|
9
agb/gfx/agb_logo.toml
Normal file
9
agb/gfx/agb_logo.toml
Normal file
|
@ -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"
|
|
@ -1,22 +1,28 @@
|
||||||
include!(concat!(env!("OUT_DIR"), "/test_logo.rs"));
|
use crate::display::tiled0::Tiled0;
|
||||||
|
|
||||||
#[test_case]
|
crate::include_gfx!("gfx/agb_logo.toml");
|
||||||
fn logo_display(gba: &mut crate::Gba) {
|
|
||||||
let mut gfx = gba.display.video.tiled0();
|
|
||||||
|
|
||||||
gfx.set_background_palettes(PALETTE_DATA);
|
pub fn display_logo(gfx: &mut Tiled0) {
|
||||||
gfx.set_background_tilemap(0, TILE_DATA);
|
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 back = gfx.get_background().unwrap();
|
||||||
|
|
||||||
let mut entries: [u16; 30 * 20] = [0; 30 * 20];
|
let mut entries: [u16; 30 * 20] = [0; 30 * 20];
|
||||||
for tile_id in 0..(30 * 20) {
|
for tile_id in 0..(30 * 20) {
|
||||||
let palette_entry = PALETTE_ASSIGNMENT[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);
|
entries[tile_id as usize] = tile_id | (palette_entry << 12);
|
||||||
}
|
}
|
||||||
|
|
||||||
back.draw_full_map(&entries, (30_u32, 20_u32).into(), 0);
|
back.draw_full_map(&entries, (30_u32, 20_u32).into(), 0);
|
||||||
back.show();
|
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");
|
crate::assert_image_output("gfx/test_logo.png");
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,8 @@ pub mod example_logo;
|
||||||
pub mod object;
|
pub mod object;
|
||||||
/// Palette type.
|
/// Palette type.
|
||||||
pub mod palette16;
|
pub mod palette16;
|
||||||
|
/// Data produced by agb-image-converter
|
||||||
|
pub mod tile_data;
|
||||||
/// Graphics mode 0. Four regular backgrounds.
|
/// Graphics mode 0. Four regular backgrounds.
|
||||||
pub mod tiled0;
|
pub mod tiled0;
|
||||||
/// Syscall for waiting for vblank.
|
/// Syscall for waiting for vblank.
|
||||||
|
|
21
agb/src/display/tile_data.rs
Normal file
21
agb/src/display/tile_data.rs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,6 +20,8 @@ pub mod input;
|
||||||
/// Implements sound output.
|
/// Implements sound output.
|
||||||
pub mod sound;
|
pub mod sound;
|
||||||
|
|
||||||
|
pub use agb_image_converter::include_gfx;
|
||||||
|
|
||||||
mod bitarray;
|
mod bitarray;
|
||||||
mod interrupt;
|
mod interrupt;
|
||||||
mod memory_mapped;
|
mod memory_mapped;
|
||||||
|
|
Loading…
Reference in a new issue