Add support for transparent sprites

This commit is contained in:
Gwilym Kuiper 2022-05-22 19:23:29 +01:00
parent ece73dd975
commit 8b063f8440
7 changed files with 61 additions and 30 deletions

View file

@ -3,15 +3,20 @@ pub struct Colour {
pub r: u8, pub r: u8,
pub g: u8, pub g: u8,
pub b: u8, pub b: u8,
pub a: u8,
} }
impl Colour { impl Colour {
pub fn from_rgb(r: u8, g: u8, b: u8) -> Self { pub fn from_rgb(r: u8, g: u8, b: u8, a: u8) -> Self {
Colour { r, g, b } Colour { r, g, b, a }
} }
pub fn to_rgb15(self) -> u16 { pub fn to_rgb15(self) -> u16 {
let (r, g, b) = (self.r as u16, self.g as u16, self.b as 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) ((r >> 3) & 31) | (((g >> 3) & 31) << 5) | (((b >> 3) & 31) << 10)
} }
pub fn is_transparent(self) -> bool {
self.a != 255
}
} }

View file

@ -76,7 +76,7 @@ impl Image for ImageV1 {
let g = u8::from_str_radix(&colour[2..4], 16).unwrap(); let g = u8::from_str_radix(&colour[2..4], 16).unwrap();
let b = u8::from_str_radix(&colour[4..6], 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 None

View file

@ -25,7 +25,7 @@ impl Image {
let mut colour_data = Vec::with_capacity(width * height); let mut colour_data = Vec::with_capacity(width * height);
for (_, _, pixel) in img.pixels() { 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 { Image {

View file

@ -94,7 +94,9 @@ pub fn include_aseprite_inner(input: TokenStream) -> TokenStream {
Err(e) => return e.to_compile_error().into(), 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 images = Vec::new();
let mut tags = 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); assert!(width == frame.height() && width.is_power_of_two() && width <= 32);
let image = Image::load_from_dyn_image(frame); 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); 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); 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"); 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, settings.transparent_colour());
let optimisation_results = optimiser.optimise_palettes(settings.transparent_colour()); let optimisation_results = optimiser.optimise_palettes();
rust_generator::generate_code( rust_generator::generate_code(
variable_name, variable_name,
@ -225,9 +232,13 @@ fn convert_image(
) )
} }
fn optimiser_for_image(image: &Image, tile_size: usize) -> palette16::Palette16Optimiser { fn optimiser_for_image(
let mut palette_optimiser = palette16::Palette16Optimiser::new(); image: &Image,
add_to_optimiser(&mut palette_optimiser, image, tile_size); tile_size: usize,
transparent_colour: Option<Colour>,
) -> palette16::Palette16Optimiser {
let mut palette_optimiser = palette16::Palette16Optimiser::new(transparent_colour);
add_to_optimiser(&mut palette_optimiser, image, tile_size, transparent_colour);
palette_optimiser palette_optimiser
} }
@ -235,6 +246,7 @@ fn add_to_optimiser(
palette_optimiser: &mut palette16::Palette16Optimiser, palette_optimiser: &mut palette16::Palette16Optimiser,
image: &Image, image: &Image,
tile_size: usize, tile_size: usize,
transparent_colour: Option<Colour>,
) { ) {
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;
@ -247,7 +259,10 @@ fn add_to_optimiser(
for i in 0..tile_size { for i in 0..tile_size {
let colour = image.colour(x * tile_size + i, y * tile_size + j); 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 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 {
let colour = image.colour(x * tile_size + i, y * tile_size + j); 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),
);
} }
} }
} }

View file

@ -27,11 +27,22 @@ impl Palette16 {
self.colours.push(colour); self.colours.push(colour);
true true
} }
pub fn colour_index(&self, colour: Colour) -> u8 {
pub fn colour_index(&self, colour: Colour, transparent_colour: Option<Colour>) -> u8 {
let colour_to_search = match (transparent_colour, colour.is_transparent()) {
(Some(transparent_colour), true) => transparent_colour,
_ => colour,
};
self.colours self.colours
.iter() .iter()
.position(|c| *c == colour) .position(|c| *c == colour_to_search)
.expect("Can't get a colour index without it existing") as u8 .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 { fn union_length(&self, other: &Palette16) -> usize {
@ -62,19 +73,22 @@ impl IntoIterator for Palette16 {
pub(crate) struct Palette16Optimiser { pub(crate) struct Palette16Optimiser {
palettes: Vec<Palette16>, palettes: Vec<Palette16>,
colours: Vec<Colour>, colours: Vec<Colour>,
transparent_colour: Option<Colour>,
} }
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct Palette16OptimisationResults { pub(crate) struct Palette16OptimisationResults {
pub optimised_palettes: Vec<Palette16>, pub optimised_palettes: Vec<Palette16>,
pub assignments: Vec<usize>, pub assignments: Vec<usize>,
pub transparent_colour: Option<Colour>,
} }
impl Palette16Optimiser { impl Palette16Optimiser {
pub fn new() -> Self { pub fn new(transparent_colour: Option<Colour>) -> Self {
Palette16Optimiser { Palette16Optimiser {
palettes: vec![], palettes: vec![],
colours: Vec::new(), colours: Vec::new(),
transparent_colour,
} }
} }
@ -94,10 +108,7 @@ impl Palette16Optimiser {
} }
} }
pub fn optimise_palettes( pub fn optimise_palettes(&self) -> Palette16OptimisationResults {
&self,
transparent_colour: Option<Colour>,
) -> Palette16OptimisationResults {
let mut assignments = vec![0; self.palettes.len()]; let mut assignments = vec![0; self.palettes.len()];
let mut optimised_palettes = vec![]; let mut optimised_palettes = vec![];
@ -108,7 +119,7 @@ impl Palette16Optimiser {
.collect::<HashSet<Palette16>>(); .collect::<HashSet<Palette16>>();
while !unsatisfied_palettes.is_empty() { 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() { for test_palette in unsatisfied_palettes.clone() {
if test_palette.is_satisfied_by(&palette) { if test_palette.is_satisfied_by(&palette) {
@ -132,17 +143,14 @@ impl Palette16Optimiser {
Palette16OptimisationResults { Palette16OptimisationResults {
optimised_palettes, optimised_palettes,
assignments, assignments,
transparent_colour: self.transparent_colour,
} }
} }
fn find_maximal_palette_for( fn find_maximal_palette_for(&self, unsatisfied_palettes: &HashSet<Palette16>) -> Palette16 {
&self,
unsatisfied_palettes: &HashSet<Palette16>,
transparent_colour: Option<Colour>,
) -> Palette16 {
let mut palette = Palette16::new(); 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); palette.add_colour(transparent_colour);
} }

View file

@ -51,7 +51,8 @@ pub(crate) fn generate_code(
for j in inner_y * 8..inner_y * 8 + 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 {
let colour = image.colour(x * tile_size + i, y * tile_size + j); 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));
} }
} }
} }

Binary file not shown.