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 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
}
}

View file

@ -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

View file

@ -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 {

View file

@ -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<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
}
@ -235,6 +246,7 @@ fn add_to_optimiser(
palette_optimiser: &mut palette16::Palette16Optimiser,
image: &Image,
tile_size: usize,
transparent_colour: Option<Colour>,
) {
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),
);
}
}
}

View file

@ -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<Colour>) -> 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<Palette16>,
colours: Vec<Colour>,
transparent_colour: Option<Colour>,
}
#[derive(Debug)]
pub(crate) struct Palette16OptimisationResults {
pub optimised_palettes: Vec<Palette16>,
pub assignments: Vec<usize>,
pub transparent_colour: Option<Colour>,
}
impl Palette16Optimiser {
pub fn new() -> Self {
pub fn new(transparent_colour: Option<Colour>) -> Self {
Palette16Optimiser {
palettes: vec![],
colours: Vec::new(),
transparent_colour,
}
}
@ -94,10 +108,7 @@ impl Palette16Optimiser {
}
}
pub fn optimise_palettes(
&self,
transparent_colour: Option<Colour>,
) -> 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::<HashSet<Palette16>>();
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<Palette16>,
transparent_colour: Option<Colour>,
) -> Palette16 {
fn find_maximal_palette_for(&self, unsatisfied_palettes: &HashSet<Palette16>) -> 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);
}

View file

@ -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));
}
}
}

Binary file not shown.