mirror of
https://github.com/italicsjenga/agb.git
synced 2024-12-23 16:21:33 +11:00
Start tracking the colours for a 256 colour image
This commit is contained in:
parent
702286979d
commit
916a3d4b20
|
@ -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)]
|
||||||
|
|
|
@ -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![];
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
72
agb-image-converter/src/palette256.rs
Normal file
72
agb-image-converter/src/palette256.rs
Normal 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(¤t_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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue