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"
[dependencies]
png = "0.17.16"
rfd = "0.15.2"
slint = "1.9.2"
tiny-skia = "0.11.4"

View File

@ -40,6 +40,6 @@ impl 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;
pub struct TIMInfo {}
pub struct TIMInfo {
image_data: SharedPixelBuffer<Rgba8Pixel>,
palette: Option<Vec<Rgba8Pixel>>,
}
impl std::default::Default for TIMInfo {
fn default() -> Self {
TIMInfo{}
TIMInfo{image_data: SharedPixelBuffer::new(1, 1), palette: None}
}
}
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{}))
pub fn load_image(path: &PathBuf) -> Result<(slint::Image, TIMInfo), Error> {
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()
.add_filter("Images (.png; .bmp; .jpeg)", &["png", "bmp", "jpeg"])
.set_title("Select image file")
.add_filter("PNG image (.png)", &["png"])
.set_title("PNG image file")
.pick_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_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),
Err(error) => {
file_tab.clear_load();