mirror of
https://github.com/italicsjenga/agb.git
synced 2024-12-24 00:31:34 +11:00
Merge pull request #228 from gwilymk/support-transparent-sprites
Support transparent sprites
This commit is contained in:
commit
7187d89bdd
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in a new issue