use crate::error::Error;
use crate::import::palette::Palette;
use crate::import::png::save_png;
use image::{DynamicImage, Rgb, RgbImage};
use serde_json::json;
use std::path::{Path, PathBuf};
#[derive(Debug, Clone)]
pub struct PaletteExportPaths {
pub png_path: PathBuf,
pub json_path: PathBuf,
}
const CELL_SIZE: u32 = 16;
const GRID_DIM: u32 = 16;
fn ensure_parent_dir(path: &Path) -> Result<(), Error> {
if let Some(parent) = path.parent()
&& !parent.as_os_str().is_empty()
{
std::fs::create_dir_all(parent).map_err(|e| Error::File {
path: parent.to_path_buf(),
message: format!("failed to create directory: {e}"),
})?;
}
Ok(())
}
fn paired_output_paths(output: &Path) -> (PathBuf, PathBuf) {
let ext = output
.extension()
.and_then(|e| e.to_str())
.map(str::to_ascii_lowercase);
match ext.as_deref() {
Some("png") => (output.to_path_buf(), output.with_extension("json")),
Some("json") => (output.with_extension("png"), output.to_path_buf()),
Some(_) => (output.with_extension("png"), output.with_extension("json")),
None => {
let mut png = output.to_path_buf();
png.set_extension("png");
let mut json_path = output.to_path_buf();
json_path.set_extension("json");
(png, json_path)
}
}
}
pub fn export_col_palette(
palette: &Palette,
output_path: &Path,
compress: bool,
) -> Result<PaletteExportPaths, Error> {
let img_size = GRID_DIM * CELL_SIZE;
let mut image = RgbImage::new(img_size, img_size);
for (idx, color) in palette.colors.iter().enumerate() {
let idx_u32 = u32::try_from(idx).map_err(|_| {
Error::Conversion("palette index overflow while exporting swatches".to_string())
})?;
let col = idx_u32 % GRID_DIM;
let row = idx_u32 / GRID_DIM;
let origin_x = col * CELL_SIZE;
let origin_y = row * CELL_SIZE;
let pixel = Rgb([color[0], color[1], color[2]]);
for y in 0..CELL_SIZE {
for x in 0..CELL_SIZE {
image.put_pixel(origin_x + x, origin_y + y, pixel);
}
}
}
let (png_path, json_path) = paired_output_paths(output_path);
ensure_parent_dir(&png_path)?;
ensure_parent_dir(&json_path)?;
save_png(&DynamicImage::ImageRgb8(image), &png_path, compress)
.map_err(|e| Error::Conversion(e.to_string()))?;
let colors: Vec<serde_json::Value> = palette
.colors
.iter()
.enumerate()
.map(|(i, c)| {
json!({
"index": i,
"r": c[0],
"g": c[1],
"b": c[2],
"hex": format!("#{:02X}{:02X}{:02X}", c[0], c[1], c[2])
})
})
.collect();
let metadata = json!({
"format": "redguard-col-v1",
"entry_count": palette.colors.len(),
"colors": colors
});
let json_text = serde_json::to_string_pretty(&metadata)?;
std::fs::write(&json_path, json_text)?;
Ok(PaletteExportPaths {
png_path,
json_path,
})
}