diff --git a/src/image/mod.rs b/src/image/mod.rs index 685524b..11b36e9 100644 --- a/src/image/mod.rs +++ b/src/image/mod.rs @@ -36,7 +36,7 @@ use std::slice::{Iter, IterMut}; use std::vec::IntoIter; #[allow(unused)] -#[derive(Default)] +#[derive(Default, Debug)] pub struct Image where T: Into + PartialEq + Default + Copy + From + PartialOrd, @@ -87,6 +87,9 @@ where pub fn path(&self) -> &PathBuf { &self.path } + pub fn pixels(&self) -> &Vec<(T, T, T, T)> { + &self.pixels + } /// Returns the iterator of the pixels vector pub fn iter(&self) -> Iter<'_, (T, T, T, T)> { diff --git a/src/image_loader/mod.rs b/src/image_loader/mod.rs index 4250a60..c3625bd 100644 --- a/src/image_loader/mod.rs +++ b/src/image_loader/mod.rs @@ -1,57 +1,343 @@ -use std::path::Path; use crate::image::Image; +use png::BitDepth; use std::fs::File; +use std::path::Path; +///# Image Loader +/// The image_loader function is a function which can use a path of an image to return some Metadata from the image.
+///It can also retrieve the rgba values of every pixel from the image and the path.
+///
+///# IMPORTANT
+/// Doesn't support pictures with the color type indexed!
+///
+///# Parameter +///The function has the parameter path with the type &Path.
+///
+///# Return variables +///The return value is a struct which includes four variables.
+///1. width [u32]: width contains the number of pixels in a row of the given png.
+///2. height [u32]: height contains the height of the picture in measured pixels.
+///3. pixel_vec [Vec<(f32, f32, f32, f32)>]: pixel_vec contains the rgba values of every pixel in the picture, saved in a vec themselves.
+///4. path [path.to_path_buf()]: is the path from the parameter.
+pub fn image_loader(path: &Path) -> Result, &'static str> { + let decoder = match File::open(path) { + Ok(file) => png::Decoder::new(file), + Err(_) => return Err("failed the decoder"), + }; -/* !!!Fragen!!! - - was ist .unwrap() - - was ist ein IDAT chunk (bei read.info() in der doku) - - was ist .output_buffer_size() - - was ist .next_frame - - kann ich srgb nutzen für dir rgba values bzw für den rgb teil - - woher ziehe ich den alpha channel - - ist makierter teil kommplet nötig - */ + let mut reader = match decoder.read_info() { + Ok(reader) => reader, + Err(_) => return Err("Failed to read PNG info"), + }; -pub fn image_loader(path: &Path) -> Image -{ -//marker1 - - // The decoder is a build for reader and can be used to set various decoding options - // via `Transformations`. The default output transformation is `Transformations::IDENTITY`. - - //open a path in the decoder to look into the img - let decoder = png::Decoder::new(File::open(path).unwrap()); - - let mut reader = decoder.read_info().unwrap(); - // Allocate the output buffer. let mut buf = vec![0; reader.output_buffer_size()]; - // Read the next frame. An APNG might contain multiple frames. - let info = reader.next_frame(&mut buf).unwrap(); - + + let info = match reader.next_frame(&mut buf) { + Ok(info) => info, + Err(_) => return Err("Failed to read PNG frame"), + }; + let bit_depth = reader.info().bit_depth; let color_type = reader.info().color_type; let width = reader.info().width; let height = reader.info().height; + let palette = &reader.info().palette; - // Grab the bytes of the image. - let bytes = &buf[..info.buffer_size()]; - // Inspect more details of the last read frame. - let in_animation = reader.info().frame_control.is_some(); - -//marker1 - let vec: Vec<(f32, f32, f32, f32)> = vec! - [ - (-33.0, 7732.0, 2564355.0, -79.0), - (1.0, 79.0, 255.0, 1.05), - (300.0, 300.0, 300.0, 300.0), - ]; - let image: Image = Image::new(1, 3, vec, path.to_path_buf()); - - image + let idat = &buf[..info.buffer_size()]; + + println!("idat {:?} idat ", idat); + println!("palette {:?} palette", palette); + println!("depth {:?} depth", bit_depth); + + let pixel_vec = match color_type { + png::ColorType::Grayscale => grayscale_to_rgba(idat, bit_depth), + png::ColorType::GrayscaleAlpha => grayscale_alpha_to_rgba(idat, bit_depth), + png::ColorType::Rgb => rgb_to_rgba(idat, bit_depth), + png::ColorType::Rgba => decode_rgba(idat, bit_depth), + _ => panic!("Unsupported color type or bit depth"), + }?; + + let image: Image = Image::new( + width as usize, + height as usize, + pixel_vec, + path.to_path_buf(), + ); + + Ok(image) } -//höhe(in px[usize: u8]) -//breite(in px[usize: u8]) -//tupel pro pixel mit (f32, f32, f32, f32) alle pixel in einen vector und der verctor in den image struct !!!ALLES IN 0-255 PIXEL!!! -//absoluter pfad \ No newline at end of file +///# Grayscale to RGBA +///The grayscale_to_rgba function converts a IDAT chunk from an picture with the color type grayscale and the bit depth into an RGBA value.
+///
+///# Parameter +///The function has the following two parameters:
+///1. IDAT [&[u8]]: This array cointans the IDAT chunk of the given image.
+///2. bit_depth [BitDepth]: This variable contains the bit depth of the given image.
+///
+///# Return variables +///This function returns a vector filled with vectors filled with four [f32] variables, which are the RGBA values.
+///Also this function sets the alpha channel of the RGBA value to 255. +fn grayscale_to_rgba( + idat: &[u8], + bit_depth: BitDepth, +) -> Result, &'static str> { + let mut rgba_values = Vec::new(); + + let max_value: u32 = (1 << bit_depth as u32) - 1; + + for byte in idat { + let grayscale = match bit_depth { + BitDepth::One => ((*byte & 0x01) as u32 * max_value) as f32, + BitDepth::Two => ((*byte & 0x03) as u32 * max_value / 3) as f32, + BitDepth::Four => ((*byte & 0x0F) as u32 * max_value / 15) as f32, + BitDepth::Eight => *byte as f32, + BitDepth::Sixteen => ((*byte as u32 * 255) / max_value) as f32, + }; + + let rgba = (grayscale, grayscale, grayscale, 255_f32); + rgba_values.push(rgba); + } + + Ok(rgba_values) +} + +///# Grayscale Alpha to RGBA +///The grayscale_alpha_to_rgba function converts a IDAT chunk from a picture with the color type grayscale alpha and the bit depth into an RGBA value.
+///
+///# Parameter +///The function has the following two parameters:
+///1. IDAT [&[u8]]: This array contains the IDAT chunk of the given image.
+///2. bit_depth [BitDepth]: This variable contains the bit depth of the given image.
+///
+///# Return variables +///This function returns a vector filled with vectors filled with four [f32] variables, which are the RGBA values. +fn grayscale_alpha_to_rgba( + idat: &[u8], + bit_depth: BitDepth, +) -> Result, &'static str> { + let mut rgba_values = Vec::new(); + + let chunk_size; + + if bit_depth == BitDepth::Eight { + chunk_size = 2; + } else if bit_depth == BitDepth::Sixteen { + chunk_size = 4; + } else { + return Err("Invalid Bit Depth"); + } + + for pair in idat.chunks(chunk_size) { + if pair.len() < 2 { + return Err("Insufficient data"); + } + + let grayscale = match bit_depth { + BitDepth::Eight => pair[0] as f32, + BitDepth::Sixteen => ((pair[0] as u16) << 8 | pair[1] as u16) as f32, + _ => return Err("Unsupported bit depth"), + }; + + let alpha = match bit_depth { + BitDepth::Eight => pair[1] as f32, + BitDepth::Sixteen => ((pair[2] as u16) << 8 | pair[3] as u16) as f32, + _ => return Err("Unsupported bit depth"), + }; + + let rgba = (grayscale, grayscale, grayscale, alpha); + rgba_values.push(rgba); + } + + Ok(rgba_values) +} + +///# RGB to RGBA +///The tgb_to_rgba function converts a IDAT chunk from a picture with the color type rgb and the bit depth into an RGBA value.
+///
+///# Parameter +///The function has the following two parameters:
+///1. IDAT [&[u8]]: This array contains the IDAT chunk of the given image.
+///2. bit_depth [BitDepth]: This variable contains the bit depth of the given image.
+///
+///# Return variables +///This function returns a vector filled with vectors filled with four [f32] variables, which are the RGBA values. +///Also this function sets the alpha channel of the RGBA value to 255. +fn rgb_to_rgba( + idat: &[u8], + bit_depth: BitDepth, +) -> Result, &'static str> { + let mut rgba_values = Vec::new(); + + let chunk_size; + if bit_depth == BitDepth::Eight { + chunk_size = 3; + } else if bit_depth == BitDepth::Sixteen { + chunk_size = 6; + } else { + return Err("Invalid Bit Depth"); + } + + for group in idat.chunks(chunk_size) { + if group.len() < 3 { + return Err("Insufficient data"); + } + + let red = match bit_depth { + BitDepth::Eight => group[0] as f32, + BitDepth::Sixteen => ((group[0] as u16) << 8 | group[1] as u16) as f32, + _ => return Err("Unsupported bit depth"), + }; + + let green = match bit_depth { + BitDepth::Eight => group[1] as f32, + BitDepth::Sixteen => ((group[2] as u16) << 8 | group[3] as u16) as f32, + _ => return Err("Unsupported bit depth"), + }; + + let blue = match bit_depth { + BitDepth::Eight => group[2] as f32, + BitDepth::Sixteen => ((group[4] as u16) << 8 | group[5] as u16) as f32, + _ => return Err("Unsupported bit depth"), + }; + + let rgba = (red, green, blue, 255_f32); + rgba_values.push(rgba); + } + + Ok(rgba_values) +} + +///# Decode RGBA +///The decode_rgba function converts a IDAT chunk from a picture with the color type rgba and the bit depth into an RGBA value.
+///
+///# Parameter +///The function has the following two parameters:
+///1. IDAT [&[u8]]: This array contains the IDAT chunk of the given image.
+///2. bit_depth [BitDepth]: This variable contains the bit depth of the given image.
+///
+///# Return variables +///This function returns a vector filled with vectors filled with four [f32] variables, which are the RGBA values. +fn decode_rgba( + idat: &[u8], + bit_depth: BitDepth, +) -> Result, &'static str> { + let mut rgba_values = Vec::new(); + + let chunk_size; + if bit_depth == BitDepth::Eight { + chunk_size = 4; + } else if bit_depth == BitDepth::Sixteen { + chunk_size = 8; + } else { + return Err("Invalid Bit Depth"); + } + + for group in idat.chunks(chunk_size) { + if group.len() < 4 { + return Err("Insufficient data"); + } + + let red = match bit_depth { + BitDepth::Eight => group[0] as f32, + BitDepth::Sixteen => ((group[0] as u16) << 8 | group[1] as u16) as f32, + _ => return Err("Unsupported bit depth"), + }; + + let green = match bit_depth { + BitDepth::Eight => group[1] as f32, + BitDepth::Sixteen => ((group[2] as u16) << 8 | group[3] as u16) as f32, + _ => return Err("Unsupported bit depth"), + }; + + let blue = match bit_depth { + BitDepth::Eight => group[2] as f32, + BitDepth::Sixteen => ((group[4] as u16) << 8 | group[5] as u16) as f32, + _ => return Err("Unsupported bit depth"), + }; + + let alpha = match bit_depth { + BitDepth::Eight => group[3] as f32, + BitDepth::Sixteen => ((group[6] as u16) << 8 | group[7] as u16) as f32, + _ => return Err("Unsupported bit depth"), + }; + + let rgba = (red, green, blue, alpha); + rgba_values.push(rgba); + } + + Ok(rgba_values) +} + +#[cfg(test)] +mod test { + use std::path::PathBuf; + + use super::*; + + #[test] + fn test_image_loader() { + let path = Path::new("test_img/red_image.png"); + let test = image_loader(path); + + let image = Image::new( + 4, + 4, + vec![ + (255., 0., 0., 255.), + (0., 255., 0., 255.), + (0., 0., 255., 255.), + (255., 255., 255., 255.), + (127., 127., 127., 255.), + (0., 255., 255., 255.), + (255., 255., 0., 255.), + (255., 0., 0., 255.), + (127., 127., 127., 255.), + (255., 0., 255., 255.), + (255., 0., 0., 255.), + (255., 0., 0., 255.), + (255., 0., 0., 255.), + (255., 0., 0., 255.), + (255., 0., 0., 255.), + (0., 0., 0., 255.), + ], + PathBuf::default(), + ); + + assert_eq!(test.unwrap().pixels(), image.pixels()) + } + + #[test] + #[should_panic] + fn test_wrong_img() { + let path = Path::new("test_img/wrong pixel count.png"); + let test = image_loader(path); + + let image = Image::new( + 4, + 4, + vec![ + (255., 0., 0., 255.), + (255., 0., 0., 255.), + (255., 0., 0., 255.), + (255., 0., 0., 255.), + (255., 0., 0., 255.), + (255., 0., 0., 255.), + (255., 0., 0., 255.), + (255., 0., 0., 255.), + (255., 0., 0., 255.), + (255., 0., 0., 255.), + (255., 0., 0., 255.), + (255., 0., 0., 255.), + (255., 0., 0., 255.), + (255., 0., 0., 255.), + (255., 0., 0., 255.), + (255., 0., 0., 255.), + ], + PathBuf::default(), + ); + + //should panic because we are looking at a corrupt picture + assert_eq!(test.unwrap().pixels(), image.pixels()) + } +} diff --git a/test_img/gray_image.png b/test_img/gray_image.png new file mode 100644 index 0000000..fb26f09 Binary files /dev/null and b/test_img/gray_image.png differ diff --git a/test_img/indexed_image.png b/test_img/indexed_image.png new file mode 100644 index 0000000..6e98605 Binary files /dev/null and b/test_img/indexed_image.png differ diff --git a/test_img/red_image.png b/test_img/red_image.png new file mode 100644 index 0000000..896198b Binary files /dev/null and b/test_img/red_image.png differ diff --git a/test_img/rot.png b/test_img/rot.png new file mode 100644 index 0000000..a40594e Binary files /dev/null and b/test_img/rot.png differ diff --git a/test_img/wrong colorspace_bitdepth.png b/test_img/wrong colorspace_bitdepth.png new file mode 100644 index 0000000..cdf1b48 Binary files /dev/null and b/test_img/wrong colorspace_bitdepth.png differ diff --git a/test_img/wrong pixel count.png b/test_img/wrong pixel count.png new file mode 100644 index 0000000..5c0e1cb Binary files /dev/null and b/test_img/wrong pixel count.png differ diff --git a/test_img/wrong size.png b/test_img/wrong size.png new file mode 100644 index 0000000..146a603 Binary files /dev/null and b/test_img/wrong size.png differ