Load indexed png

This commit is contained in:
Jaby 2025-02-15 17:07:41 +01:00
parent 6fa574c921
commit 4f0103e8fa
4 changed files with 59 additions and 9 deletions

View File

@ -7,6 +7,7 @@ edition = "2021"
panic = "abort" panic = "abort"
[dependencies] [dependencies]
png = "0.17.16"
rfd = "0.15.2" rfd = "0.15.2"
slint = "1.9.2" slint = "1.9.2"
tiny-skia = "0.11.4" tiny-skia = "0.11.4"

View File

@ -40,6 +40,6 @@ impl UnaddedTIM {
} }
fn empty() -> UnaddedTIM { fn empty() -> UnaddedTIM {
UnaddedTIM{info: TIMInfo{}, image: Image::default()} UnaddedTIM{info: TIMInfo::default(), image: Image::default()}
} }
} }

View File

@ -1,14 +1,63 @@
use std::path::PathBuf; use std::{default, fmt::format, fs::File, path::PathBuf};
use slint::{Rgba8Pixel, SharedPixelBuffer};
use tool_helper::Error; use tool_helper::Error;
pub struct TIMInfo {} pub struct TIMInfo {
image_data: SharedPixelBuffer<Rgba8Pixel>,
palette: Option<Vec<Rgba8Pixel>>,
}
impl std::default::Default for TIMInfo { impl std::default::Default for TIMInfo {
fn default() -> Self { fn default() -> Self {
TIMInfo{} TIMInfo{image_data: SharedPixelBuffer::new(1, 1), palette: None}
} }
} }
pub fn load_image(path: PathBuf) -> Result<(slint::Image, TIMInfo), Error> { pub fn load_image(path: &PathBuf) -> Result<(slint::Image, TIMInfo), Error> {
Ok((slint::Image::load_from_path(&path).or_else(|_| {Err(Error::from_str("Failed loading image"))})?, TIMInfo{})) fn make_color(iter: &mut dyn std::iter::Iterator<Item = &u8>) -> Option<Rgba8Pixel> {
Some(Rgba8Pixel::new(
*iter.next()?, *iter.next()?, *iter.next()?,
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 bit_depth = frame_info.bit_depth;
let mut image_data = SharedPixelBuffer::new(frame_info.width, frame_info.height);
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) {
palette_colors.push(color);
}
let mut dst_pixels = image_data.make_mut_slice();
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"));}
}
}
let image = slint::Image::from_rgba8_premultiplied(image_data.clone());
Ok((image, TIMInfo{image_data, palette: Some(palette_colors)}))
}
else {
Err(Error::not_implemented("Support for non indexed images"))
}
} }

View File

@ -47,8 +47,8 @@ fn setup_file_tab(gui_elements_ref: Rc<RefCell<GUIElements>>, logic_ref: Rc<RefC
} }
let file = FileDialog::new() let file = FileDialog::new()
.add_filter("Images (.png; .bmp; .jpeg)", &["png", "bmp", "jpeg"]) .add_filter("PNG image (.png)", &["png"])
.set_title("Select image file") .set_title("PNG image file")
.pick_file(); .pick_file();
if let Some(file) = file { if let Some(file) = file {
@ -59,7 +59,7 @@ fn setup_file_tab(gui_elements_ref: Rc<RefCell<GUIElements>>, logic_ref: Rc<RefC
let file_tab = &gui_elements.file_tab; let file_tab = &gui_elements.file_tab;
let file_name = if let Some(name) = file.file_name() {Some(name.to_string_lossy().to_string())} else {None}; let file_name = if let Some(name) = file.file_name() {Some(name.to_string_lossy().to_string())} else {None};
let (image, info) = match load_image(file) { let (image, info) = match load_image(&file) {
Ok((image, info)) => (image, info), Ok((image, info)) => (image, info),
Err(error) => { Err(error) => {
file_tab.clear_load(); file_tab.clear_load();