Various improvements; Logic is now being reported about added and removed elements to be in sync with GUI
This commit is contained in:
144
src/Tools/tim_tool/src/logic/tim/mod.rs
Normal file
144
src/Tools/tim_tool/src/logic/tim/mod.rs
Normal file
@@ -0,0 +1,144 @@
|
||||
pub mod types;
|
||||
|
||||
use std::{fs::File, path::PathBuf};
|
||||
use slint::{Rgba8Pixel, SharedPixelBuffer};
|
||||
use tool_helper::Error;
|
||||
use types::Encoding;
|
||||
|
||||
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)?).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: u32, height: u32) -> 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
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct PaletteInfo {
|
||||
data: Vec<Rgba8Pixel>,
|
||||
width: u32,
|
||||
height: u32,
|
||||
}
|
||||
|
||||
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, self.height);
|
||||
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())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user