Start tracking the colours for a 256 colour image

This commit is contained in:
Gwilym Kuiper 2022-08-11 22:47:52 +01:00
parent 702286979d
commit 916a3d4b20
4 changed files with 125 additions and 24 deletions

View file

@ -23,20 +23,19 @@ pub(crate) fn parse(filename: &str) -> Box<dyn Config> {
pub(crate) trait Config { pub(crate) trait Config {
fn crate_prefix(&self) -> String; fn crate_prefix(&self) -> String;
fn images(&self) -> HashMap<String, &dyn Image>; fn images(&self) -> HashMap<String, &dyn Image>;
fn colours(&self) -> Colours;
} }
pub(crate) trait Image { pub(crate) trait Image {
fn filename(&self) -> String; fn filename(&self) -> String;
fn transparent_colour(&self) -> Option<Colour>; fn transparent_colour(&self) -> Option<Colour>;
fn tilesize(&self) -> TileSize; fn tilesize(&self) -> TileSize;
fn colours(&self) -> Colours;
} }
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct ConfigV1 { pub struct ConfigV1 {
version: String, version: String,
crate_prefix: Option<String>, crate_prefix: Option<String>,
colours: Option<u32>,
image: HashMap<String, ImageV1>, image: HashMap<String, ImageV1>,
} }
@ -54,14 +53,6 @@ impl Config for ConfigV1 {
.map(|(filename, image)| (filename.clone(), image as &dyn Image)) .map(|(filename, image)| (filename.clone(), image as &dyn Image))
.collect() .collect()
} }
fn colours(&self) -> Colours {
match self.colours {
None | Some(16) => Colours::Colours16,
Some(256) => Colours::Colours256,
_ => panic!("colours must either not be set or 16 or 256"),
}
}
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@ -69,6 +60,7 @@ pub struct ImageV1 {
filename: String, filename: String,
transparent_colour: Option<String>, transparent_colour: Option<String>,
tile_size: TileSizeV1, tile_size: TileSizeV1,
colours: Option<u32>,
} }
impl Image for ImageV1 { impl Image for ImageV1 {
@ -95,6 +87,14 @@ impl Image for ImageV1 {
fn tilesize(&self) -> TileSize { fn tilesize(&self) -> TileSize {
self.tile_size.into() self.tile_size.into()
} }
fn colours(&self) -> Colours {
match self.colours {
None | Some(16) => Colours::Colours16,
Some(256) => Colours::Colours256,
_ => panic!("colours must either not be set or 16 or 256"),
}
}
} }
#[derive(Deserialize, Clone, Copy)] #[derive(Deserialize, Clone, Copy)]

View file

@ -1,4 +1,5 @@
use palette16::{Palette16OptimisationResults, Palette16Optimiser}; use palette16::{Palette16OptimisationResults, Palette16Optimiser};
use palette256::Palette256;
use proc_macro::TokenStream; use proc_macro::TokenStream;
use proc_macro2::Literal; use proc_macro2::Literal;
use syn::parse::Parser; use syn::parse::Parser;
@ -17,6 +18,7 @@ mod config;
mod font_loader; mod font_loader;
mod image_loader; mod image_loader;
mod palette16; mod palette16;
mod palette256;
mod rust_generator; mod rust_generator;
use image::GenericImageView; use image::GenericImageView;
@ -74,28 +76,38 @@ pub fn include_gfx(input: TokenStream) -> TokenStream {
let mut assignment_offsets = HashMap::new(); let mut assignment_offsets = HashMap::new();
let mut assignment_offset = 0; let mut assignment_offset = 0;
let mut palette256 = Palette256::new();
for (name, settings) in images.iter() { for (name, settings) in images.iter() {
let image_filename = &parent.join(&settings.filename()); let image_filename = &parent.join(&settings.filename());
let image = Image::load_from_file(image_filename); let image = Image::load_from_file(image_filename);
let tile_size = settings.tilesize().to_size(); match settings.colours() {
if image.width % tile_size != 0 || image.height % tile_size != 0 { Colours::Colours16 => {
panic!("Image size not a multiple of tile size"); let tile_size = settings.tilesize().to_size();
if image.width % tile_size != 0 || image.height % tile_size != 0 {
panic!("Image size not a multiple of tile size");
}
add_to_optimiser(
&mut optimiser,
&image,
tile_size,
settings.transparent_colour(),
);
let num_tiles = image.width * image.height / settings.tilesize().to_size().pow(2);
assignment_offsets.insert(name, assignment_offset);
assignment_offset += num_tiles;
}
Colours::Colours256 => {
palette256.add_image(&image);
}
} }
add_to_optimiser(
&mut optimiser,
&image,
tile_size,
settings.transparent_colour(),
);
let num_tiles = image.width * image.height / settings.tilesize().to_size().pow(2);
assignment_offsets.insert(name, assignment_offset);
assignment_offset += num_tiles;
} }
let optimisation_results = optimiser.optimise_palettes(); let optimisation_results = optimiser.optimise_palettes();
let optimisation_results = palette256.extend_results(&optimisation_results);
let mut image_code = vec![]; let mut image_code = vec![];

View file

@ -28,6 +28,19 @@ impl Palette16 {
true true
} }
pub fn try_add_colour(&mut self, colour: Colour) -> bool {
if self.colours.contains(&colour) {
return true;
}
if self.colours.len() == MAX_COLOURS_PER_PALETTE {
return false;
}
self.colours.push(colour);
true
}
pub fn colour_index(&self, colour: Colour, transparent_colour: Option<Colour>) -> u8 { pub fn colour_index(&self, colour: Colour, transparent_colour: Option<Colour>) -> u8 {
let colour_to_search = match (transparent_colour, colour.is_transparent()) { let colour_to_search = match (transparent_colour, colour.is_transparent()) {
(Some(transparent_colour), true) => transparent_colour, (Some(transparent_colour), true) => transparent_colour,
@ -45,6 +58,10 @@ impl Palette16 {
}) as u8 }) as u8
} }
pub fn colours<'a>(&'a self) -> impl Iterator<Item = &Colour> + 'a {
self.colours.iter()
}
fn union_length(&self, other: &Palette16) -> usize { fn union_length(&self, other: &Palette16) -> usize {
self.colours self.colours
.iter() .iter()

View file

@ -0,0 +1,72 @@
use std::{collections::HashSet, iter::FromIterator};
use crate::{
colour::Colour,
image_loader::Image,
palette16::{Palette16, Palette16OptimisationResults},
};
pub struct Palette256 {
colours: HashSet<Colour>,
}
impl Palette256 {
pub fn new() -> Self {
Self {
colours: HashSet::new(),
}
}
pub(crate) fn add_image(&mut self, image: &Image) {
for y in 0..image.height {
for x in 0..image.width {
self.colours.insert(image.colour(x, y));
}
}
assert!(
self.colours.len() <= 256,
"Must have at most 256 colours in the palette"
);
}
pub(crate) fn extend_results(
&self,
palette16: &Palette16OptimisationResults,
) -> Palette16OptimisationResults {
let optimised_palette_colours: Vec<_> = palette16
.optimised_palettes
.iter()
.flat_map(|p| p.colours())
.cloned()
.collect();
let current_colours_set = HashSet::from_iter(optimised_palette_colours.iter().cloned());
let new_colours: HashSet<_> = self
.colours
.symmetric_difference(&current_colours_set)
.collect();
assert!(
new_colours.len() + optimised_palette_colours.len() <= 256,
"Cannot optimise 16 colour and 256 colour palettes together, produces too many colours"
);
let mut new_palettes = palette16.optimised_palettes.clone();
new_palettes.resize_with(16, Palette16::new);
for colour in new_colours {
for palette in new_palettes.iter_mut() {
if palette.try_add_colour(*colour) {
break;
}
}
}
Palette16OptimisationResults {
optimised_palettes: new_palettes,
assignments: palette16.assignments.clone(),
transparent_colour: palette16.transparent_colour,
}
}
}