diff --git a/agb-image-converter/src/colour.rs b/agb-image-converter/src/colour.rs index 31151d87..917b3b99 100644 --- a/agb-image-converter/src/colour.rs +++ b/agb-image-converter/src/colour.rs @@ -3,15 +3,20 @@ pub struct Colour { pub r: u8, pub g: u8, pub b: u8, + pub a: u8, } impl Colour { - pub fn from_rgb(r: u8, g: u8, b: u8) -> Self { - Colour { r, g, b } + pub fn from_rgb(r: u8, g: u8, b: u8, a: u8) -> Self { + Colour { r, g, b, a } } pub fn to_rgb15(self) -> u16 { let (r, g, b) = (self.r as u16, self.g as u16, self.b as u16); ((r >> 3) & 31) | (((g >> 3) & 31) << 5) | (((b >> 3) & 31) << 10) } + + pub fn is_transparent(self) -> bool { + self.a != 255 + } } diff --git a/agb-image-converter/src/config.rs b/agb-image-converter/src/config.rs index 559f3e09..5e0ce482 100644 --- a/agb-image-converter/src/config.rs +++ b/agb-image-converter/src/config.rs @@ -76,7 +76,7 @@ impl Image for ImageV1 { 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)); + return Some(Colour::from_rgb(r, g, b, 255)); } None diff --git a/agb-image-converter/src/image_loader.rs b/agb-image-converter/src/image_loader.rs index 52a3b04f..9126a637 100644 --- a/agb-image-converter/src/image_loader.rs +++ b/agb-image-converter/src/image_loader.rs @@ -25,7 +25,7 @@ impl Image { let mut colour_data = Vec::with_capacity(width * height); for (_, _, pixel) in img.pixels() { - colour_data.push(Colour::from_rgb(pixel[0], pixel[1], pixel[2])); + colour_data.push(Colour::from_rgb(pixel[0], pixel[1], pixel[2], pixel[3])); } Image { diff --git a/agb-image-converter/src/lib.rs b/agb-image-converter/src/lib.rs index c82cbd09..6077dc1c 100644 --- a/agb-image-converter/src/lib.rs +++ b/agb-image-converter/src/lib.rs @@ -94,7 +94,9 @@ pub fn include_aseprite_inner(input: TokenStream) -> TokenStream { Err(e) => return e.to_compile_error().into(), }; - let mut optimiser = palette16::Palette16Optimiser::new(); + let transparent_colour = Colour::from_rgb(255, 0, 255, 0); + + let mut optimiser = palette16::Palette16Optimiser::new(Some(transparent_colour)); let mut images = Vec::new(); let mut tags = Vec::new(); @@ -116,12 +118,17 @@ pub fn include_aseprite_inner(input: TokenStream) -> TokenStream { assert!(width == frame.height() && width.is_power_of_two() && width <= 32); let image = Image::load_from_dyn_image(frame); - add_to_optimiser(&mut optimiser, &image, width as usize); + add_to_optimiser( + &mut optimiser, + &image, + width as usize, + Some(transparent_colour), + ); images.push(image); } } - let optimised_results = optimiser.optimise_palettes(None); + let optimised_results = optimiser.optimise_palettes(); let (palette_data, tile_data, assignments) = palete_tile_data(&optimised_results, &images); @@ -212,8 +219,8 @@ fn convert_image( 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 optimiser = optimiser_for_image(&image, tile_size, settings.transparent_colour()); + let optimisation_results = optimiser.optimise_palettes(); rust_generator::generate_code( variable_name, @@ -225,9 +232,13 @@ fn convert_image( ) } -fn optimiser_for_image(image: &Image, tile_size: usize) -> palette16::Palette16Optimiser { - let mut palette_optimiser = palette16::Palette16Optimiser::new(); - add_to_optimiser(&mut palette_optimiser, image, tile_size); +fn optimiser_for_image( + image: &Image, + tile_size: usize, + transparent_colour: Option, +) -> palette16::Palette16Optimiser { + let mut palette_optimiser = palette16::Palette16Optimiser::new(transparent_colour); + add_to_optimiser(&mut palette_optimiser, image, tile_size, transparent_colour); palette_optimiser } @@ -235,6 +246,7 @@ fn add_to_optimiser( palette_optimiser: &mut palette16::Palette16Optimiser, image: &Image, tile_size: usize, + transparent_colour: Option, ) { let tiles_x = image.width / tile_size; let tiles_y = image.height / tile_size; @@ -247,7 +259,10 @@ fn add_to_optimiser( for i in 0..tile_size { let colour = image.colour(x * tile_size + i, y * tile_size + j); - palette.add_colour(colour); + palette.add_colour(match (colour.is_transparent(), transparent_colour) { + (true, Some(transparent_colour)) => transparent_colour, + _ => colour, + }); } } @@ -292,7 +307,9 @@ fn palete_tile_data( 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)); + tile_data.push( + palette.colour_index(colour, optimiser.transparent_colour), + ); } } } diff --git a/agb-image-converter/src/palette16.rs b/agb-image-converter/src/palette16.rs index 18adc83c..01d0af0f 100644 --- a/agb-image-converter/src/palette16.rs +++ b/agb-image-converter/src/palette16.rs @@ -27,11 +27,22 @@ impl Palette16 { self.colours.push(colour); true } - pub fn colour_index(&self, colour: Colour) -> u8 { + + pub fn colour_index(&self, colour: Colour, transparent_colour: Option) -> u8 { + let colour_to_search = match (transparent_colour, colour.is_transparent()) { + (Some(transparent_colour), true) => transparent_colour, + _ => colour, + }; + self.colours .iter() - .position(|c| *c == colour) - .expect("Can't get a colour index without it existing") as u8 + .position(|c| *c == colour_to_search) + .unwrap_or_else(|| { + panic!( + "Can't get a colour index without it existing, looking for {:?}, got {:?}", + colour, self.colours + ) + }) as u8 } fn union_length(&self, other: &Palette16) -> usize { @@ -62,19 +73,22 @@ impl IntoIterator for Palette16 { pub(crate) struct Palette16Optimiser { palettes: Vec, colours: Vec, + transparent_colour: Option, } #[derive(Debug)] pub(crate) struct Palette16OptimisationResults { pub optimised_palettes: Vec, pub assignments: Vec, + pub transparent_colour: Option, } impl Palette16Optimiser { - pub fn new() -> Self { + pub fn new(transparent_colour: Option) -> Self { Palette16Optimiser { palettes: vec![], colours: Vec::new(), + transparent_colour, } } @@ -94,10 +108,7 @@ impl Palette16Optimiser { } } - pub fn optimise_palettes( - &self, - transparent_colour: Option, - ) -> Palette16OptimisationResults { + pub fn optimise_palettes(&self) -> Palette16OptimisationResults { let mut assignments = vec![0; self.palettes.len()]; let mut optimised_palettes = vec![]; @@ -108,7 +119,7 @@ impl Palette16Optimiser { .collect::>(); while !unsatisfied_palettes.is_empty() { - let palette = self.find_maximal_palette_for(&unsatisfied_palettes, transparent_colour); + let palette = self.find_maximal_palette_for(&unsatisfied_palettes); for test_palette in unsatisfied_palettes.clone() { if test_palette.is_satisfied_by(&palette) { @@ -132,17 +143,14 @@ impl Palette16Optimiser { Palette16OptimisationResults { optimised_palettes, assignments, + transparent_colour: self.transparent_colour, } } - fn find_maximal_palette_for( - &self, - unsatisfied_palettes: &HashSet, - transparent_colour: Option, - ) -> Palette16 { + fn find_maximal_palette_for(&self, unsatisfied_palettes: &HashSet) -> Palette16 { let mut palette = Palette16::new(); - if let Some(transparent_colour) = transparent_colour { + if let Some(transparent_colour) = self.transparent_colour { palette.add_colour(transparent_colour); } diff --git a/agb-image-converter/src/rust_generator.rs b/agb-image-converter/src/rust_generator.rs index c223d3a9..f289ffeb 100644 --- a/agb-image-converter/src/rust_generator.rs +++ b/agb-image-converter/src/rust_generator.rs @@ -51,7 +51,8 @@ pub(crate) fn generate_code( 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)); + tile_data + .push(palette.colour_index(colour, results.transparent_colour)); } } } diff --git a/book/games/pong/gfx/sprites.aseprite b/book/games/pong/gfx/sprites.aseprite index 5363cd3d..098e2551 100644 Binary files a/book/games/pong/gfx/sprites.aseprite and b/book/games/pong/gfx/sprites.aseprite differ