Files
jabyengine/src/Tools/tim_tool/src/logic/tim/mod.rs

170 lines
6.5 KiB
Rust

pub mod types;
use pathdiff::diff_paths;
use std::{fs::File, path::PathBuf};
use slint::{Rgba8Pixel, SharedPixelBuffer};
use tool_helper::Error;
use types::Encoding;
#[derive(Clone)]
pub struct TIMInfo {
path: PathBuf,
image_data: SharedPixelBuffer<Rgba8Pixel>,
palette: Option<PaletteInfo>,
}
impl TIMInfo {
pub fn from_image(path: &PathBuf) -> Result<TIMInfo, Error> {
fn make_color(iter: &mut dyn std::iter::Iterator<Item = &u8>, load_alpha: bool) -> Option<Rgba8Pixel> {
Some(Rgba8Pixel::new(
*iter.next()?, *iter.next()?, *iter.next()?,
if load_alpha {*iter.next()?} else {0xFF}))
}
let mut reader = png::Decoder::new(File::open(path).map_err(|error| Error::from_text(format!("Failed loading \"{}\" with {}", path.to_string_lossy().as_ref(), error)))?).read_info().or_else(|error| {Err(Error::from_error(error))})?;
let info = reader.info().clone();
let mut buffer = vec![0; reader.output_buffer_size()];
let frame_info = reader.next_frame(&mut buffer).or_else(|error| {Err(Error::from_error(error))})?;
let bytes_per_pixel = info.bytes_per_pixel();
let bit_depth = frame_info.bit_depth;
let mut image_data = SharedPixelBuffer::new(frame_info.width, frame_info.height);
let mut dst_pixels = image_data.make_mut_slice();
if info.color_type == png::ColorType::Indexed {
let palette = info.palette.ok_or(Error::from_str("Found indexed PNG without palette"))?;
let mut palette_colors = Vec::new();
let mut iter = palette.iter();
while let Some(color) = make_color(&mut iter, false) {
palette_colors.push(color);
}
for byte in buffer.into_iter() {
match bit_depth {
png::BitDepth::Four => {
dst_pixels[0] = palette_colors[(byte >> 4) as usize];
dst_pixels[1] = palette_colors[(byte & 0xF) as usize];
dst_pixels = &mut dst_pixels[2..];
},
png::BitDepth::Eight => {
dst_pixels[0] = palette_colors[byte as usize];
dst_pixels = &mut dst_pixels[1..];
},
_ => {return Err(Error::from_str("Only 4 and 8bit color depth are supported for indexed color images"));}
}
}
Ok(TIMInfo{path: path.clone(), image_data, palette: Some(PaletteInfo::new(palette_colors))})
}
else {
if bytes_per_pixel != 3 && bytes_per_pixel != 4 {
return Err(Error::from_text(format!("Image has {} bytes per pixel, but only 3 and 4 bytes per pixel are supported", bytes_per_pixel)));
}
let mut byte_iter = buffer.iter();
while let Some(color) = make_color(&mut byte_iter, bytes_per_pixel == 4) {
match bit_depth {
png::BitDepth::Eight => {
dst_pixels[0] = color;
dst_pixels = &mut dst_pixels[1..];
}
_ => {return Err(Error::from_str("Only 8bit color depth are supported for direct color images"));}
}
}
Ok(TIMInfo{path: path.clone(), image_data, palette: None})
}
}
pub fn change_palette_size(&mut self, width: u16, height: u16) -> Result<Option<slint::Image>, Error> {
if let Some(palette) = &mut self.palette {
if width == 0 || height == 0 {
return Err(Error::from_text(format!("{}px x {}px is not a valid size for palette", width, height)));
}
palette.width = width;
palette.height = height;
Ok(Some(palette.get_image()))
}
else {
Ok(None)
}
}
pub fn get_slint_images(&self, encoding: Encoding) -> (slint::Image, Option<slint::Image>) {
fn scaled_copy(src: &SharedPixelBuffer<Rgba8Pixel>, factor: u32) -> SharedPixelBuffer<Rgba8Pixel> {
let mut image_data = SharedPixelBuffer::<Rgba8Pixel>::new(src.width()/factor, src.height());
let mut dst_pixel = image_data.make_mut_slice();
let src_pixel = src.as_slice();
for (idx, pixel) in src_pixel.into_iter().enumerate() {
if idx%factor as usize == 0 {
dst_pixel[0] = *pixel;
dst_pixel = &mut dst_pixel[1..];
}
}
image_data
}
let image_data = match encoding {
Encoding::FourBit => scaled_copy(&self.image_data, 4),
Encoding::EightBit => scaled_copy(&self.image_data, 2),
Encoding::FullColor => self.image_data.clone()
};
(slint::Image::from_rgba8_premultiplied(image_data), if let Some(palette) = &self.palette {
Some(palette.get_image())
} else {
None
})
}
pub fn get_palette_size(&self) -> Option<(u16, u16)> {
if let Some(palette_info) = &self.palette {
Some((palette_info.width, palette_info.height))
}
else {
None
}
}
pub fn get_path(&self) -> std::path::PathBuf {
self.path.clone()
}
pub fn get_path_rel_to(&self, mut reference_path: std::path::PathBuf) -> Result<std::path::PathBuf, Error> {
let file_path = self.path.clone();
reference_path.pop();
diff_paths(&file_path, &reference_path).ok_or_else(|| {
Error::from_text(format!("Can not create relative path from {} to {}", file_path.to_string_lossy().as_ref(), reference_path.to_string_lossy().as_ref()))
})
}
}
#[derive(Clone)]
struct PaletteInfo {
data: Vec<Rgba8Pixel>,
width: u16,
height: u16,
}
impl PaletteInfo {
pub fn new(data: Vec<Rgba8Pixel>) -> PaletteInfo {
let width = if data.len() <= 16 {16} else {256};
PaletteInfo{data, width, height: 1}
}
pub fn get_image(&self) -> slint::Image {
let mut image_data = SharedPixelBuffer::new(self.width as u32, self.height as u32);
let dst_pixels = image_data.make_mut_slice();
for (idx, byte) in dst_pixels.iter_mut().enumerate() {
*byte = if idx < self.data.len() {self.data[idx]} else {Rgba8Pixel::new(0, 0, 0, 0xFF)};
}
slint::Image::from_rgba8_premultiplied(image_data.clone())
}
}