2021-04-21 06:07:57 +10:00
|
|
|
use std::fs;
|
2021-04-21 05:51:49 +10:00
|
|
|
use std::fs::File;
|
2021-04-21 06:07:57 +10:00
|
|
|
use std::io;
|
2021-04-21 05:51:49 +10:00
|
|
|
use std::io::BufWriter;
|
2021-04-21 06:07:57 +10:00
|
|
|
use std::path::{Path, PathBuf};
|
|
|
|
use std::time::SystemTime;
|
2021-04-20 08:01:28 +10:00
|
|
|
|
2021-04-20 08:15:03 +10:00
|
|
|
mod colour;
|
2021-04-20 09:40:07 +10:00
|
|
|
mod image_loader;
|
|
|
|
mod palette16;
|
2021-04-21 05:41:04 +10:00
|
|
|
mod rust_generator;
|
2021-04-20 09:40:07 +10:00
|
|
|
|
|
|
|
use image_loader::Image;
|
2021-04-20 08:15:03 +10:00
|
|
|
|
|
|
|
pub use colour::Colour;
|
2021-04-20 08:01:28 +10:00
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
|
|
pub enum TileSize {
|
|
|
|
Tile8,
|
|
|
|
Tile16,
|
|
|
|
}
|
|
|
|
|
2021-04-20 09:40:07 +10:00
|
|
|
impl TileSize {
|
|
|
|
fn to_size(&self) -> usize {
|
|
|
|
match &self {
|
|
|
|
TileSize::Tile8 => 8,
|
|
|
|
TileSize::Tile16 => 16,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-20 08:15:03 +10:00
|
|
|
pub struct ImageConverterConfig {
|
|
|
|
pub transparent_colour: Option<Colour>,
|
|
|
|
pub tile_size: TileSize,
|
2021-04-20 09:40:07 +10:00
|
|
|
pub input_image: PathBuf,
|
|
|
|
pub output_file: PathBuf,
|
2021-04-21 05:51:49 +10:00
|
|
|
pub output_name: String,
|
2021-04-20 09:40:07 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn convert_image(settings: &ImageConverterConfig) {
|
2021-04-21 06:07:57 +10:00
|
|
|
let source_modified_time =
|
|
|
|
get_modified_time(&settings.input_image).unwrap_or(SystemTime::now());
|
|
|
|
let target_modified_time =
|
|
|
|
get_modified_time(&settings.output_file).unwrap_or(SystemTime::UNIX_EPOCH);
|
|
|
|
|
|
|
|
if source_modified_time < target_modified_time {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-04-20 09:40:07 +10:00
|
|
|
let image = Image::load_from_file(&settings.input_image);
|
|
|
|
|
|
|
|
let tile_size = settings.tile_size.to_size();
|
|
|
|
if image.width % tile_size != 0 || image.height % tile_size != 0 {
|
|
|
|
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);
|
|
|
|
|
2021-04-21 05:51:49 +10:00
|
|
|
let output_file = File::create(&settings.output_file).expect("Failed to create file");
|
|
|
|
let mut writer = BufWriter::new(output_file);
|
2021-04-21 05:41:04 +10:00
|
|
|
|
|
|
|
rust_generator::generate_code(
|
|
|
|
&mut writer,
|
|
|
|
&optimisation_results,
|
|
|
|
&image,
|
|
|
|
settings.tile_size,
|
2021-04-21 05:51:49 +10:00
|
|
|
&settings.output_name,
|
2021-04-21 05:41:04 +10:00
|
|
|
)
|
|
|
|
.expect("Failed to write data");
|
2021-04-20 08:01:28 +10:00
|
|
|
}
|
|
|
|
|
2021-04-20 09:40:07 +10:00
|
|
|
fn optimiser_for_image(image: &Image, tile_size: usize) -> palette16::Palette16Optimiser {
|
|
|
|
let tiles_x = image.width / tile_size;
|
|
|
|
let tiles_y = image.height / tile_size;
|
|
|
|
|
|
|
|
let mut palette_optimiser = palette16::Palette16Optimiser::new();
|
|
|
|
|
|
|
|
for y in 0..tiles_y {
|
|
|
|
for x in 0..tiles_x {
|
|
|
|
let mut palette = palette16::Palette16::new();
|
|
|
|
|
|
|
|
for j in 0..tile_size {
|
|
|
|
for i in 0..tile_size {
|
|
|
|
let colour = image.colour(x * tile_size + i, y * tile_size + j);
|
|
|
|
|
2021-04-20 09:57:47 +10:00
|
|
|
palette.add_colour(colour);
|
2021-04-20 09:40:07 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
palette_optimiser.add_palette(palette);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
palette_optimiser
|
|
|
|
}
|
2021-04-21 06:07:57 +10:00
|
|
|
|
|
|
|
fn get_modified_time(path: impl AsRef<Path>) -> io::Result<SystemTime> {
|
|
|
|
let metadata = fs::metadata(path)?;
|
|
|
|
metadata.modified()
|
|
|
|
}
|