Image loader (#46)
* imageLoader branch created imageLoader file created imageLoader function created (placeholder) included imageLoader into lib.rs added a new folder with 2 test img * added the decoder and began to configure it created new variables and initiated them with calues from the .info added some questions * added Debug to the the derive at the image mod.rs added a few more test pngs added functionality to the function imageLoader added four functions which decode the IDAT chunk into the correspondig rgba values added test functions added documentation --------- Co-authored-by: Schwingel0206 <Elias.alexander@unity-mail.de> Co-authored-by: Schwingel0206 <132705455+Schwingel0206@users.noreply.github.com>
|
@ -40,7 +40,7 @@ pub trait Sample: Into<f32> + PartialEq + Default + Copy + From<u8> + PartialOrd
|
||||||
impl<T: Into<f32> + PartialEq + Default + Copy + From<u8> + PartialOrd> Sample for T {}
|
impl<T: Into<f32> + PartialEq + Default + Copy + From<u8> + PartialOrd> Sample for T {}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
#[derive(Default)]
|
#[derive(Default, Debug)]
|
||||||
pub struct Image<T>
|
pub struct Image<T>
|
||||||
where
|
where
|
||||||
T: Sample,
|
T: Sample,
|
||||||
|
@ -91,6 +91,9 @@ where
|
||||||
pub fn path(&self) -> &PathBuf {
|
pub fn path(&self) -> &PathBuf {
|
||||||
&self.path
|
&self.path
|
||||||
}
|
}
|
||||||
|
pub fn pixels(&self) -> &Vec<(T, T, T, T)> {
|
||||||
|
&self.pixels
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the iterator of the pixels vector
|
/// Returns the iterator of the pixels vector
|
||||||
pub fn iter(&self) -> Iter<'_, (T, T, T, T)> {
|
pub fn iter(&self) -> Iter<'_, (T, T, T, T)> {
|
||||||
|
|
|
@ -0,0 +1,343 @@
|
||||||
|
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.<br>
|
||||||
|
///It can also retrieve the rgba values of every pixel from the image and the path.<br>
|
||||||
|
///<br>
|
||||||
|
///# IMPORTANT<br>
|
||||||
|
/// Doesn't support pictures with the color type indexed!<br>
|
||||||
|
///<br>
|
||||||
|
///# Parameter
|
||||||
|
///The function has the parameter path with the type &Path.<br>
|
||||||
|
///<br>
|
||||||
|
///# Return variables
|
||||||
|
///The return value is a struct which includes four variables.<br>
|
||||||
|
///1. width [u32]: width contains the number of pixels in a row of the given png.<br>
|
||||||
|
///2. height [u32]: height contains the height of the picture in measured pixels.<br>
|
||||||
|
///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.<br>
|
||||||
|
///4. path [path.to_path_buf()]: is the path from the parameter.<br>
|
||||||
|
pub fn image_loader(path: &Path) -> Result<Image<f32>, &'static str> {
|
||||||
|
let decoder = match File::open(path) {
|
||||||
|
Ok(file) => png::Decoder::new(file),
|
||||||
|
Err(_) => return Err("failed the decoder"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut reader = match decoder.read_info() {
|
||||||
|
Ok(reader) => reader,
|
||||||
|
Err(_) => return Err("Failed to read PNG info"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut buf = vec![0; reader.output_buffer_size()];
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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<f32> = Image::new(
|
||||||
|
width as usize,
|
||||||
|
height as usize,
|
||||||
|
pixel_vec,
|
||||||
|
path.to_path_buf(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(image)
|
||||||
|
}
|
||||||
|
|
||||||
|
///# 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.<br>
|
||||||
|
/// <br>
|
||||||
|
///# Parameter
|
||||||
|
///The function has the following two parameters:<br>
|
||||||
|
///1. IDAT [&[u8]]: This array cointans the IDAT chunk of the given image.<br>
|
||||||
|
///2. bit_depth [BitDepth]: This variable contains the bit depth of the given image.<br>
|
||||||
|
///<br>
|
||||||
|
///# Return variables
|
||||||
|
///This function returns a vector filled with vectors filled with four [f32] variables, which are the RGBA values.<br>
|
||||||
|
///Also this function sets the alpha channel of the RGBA value to 255.
|
||||||
|
fn grayscale_to_rgba(
|
||||||
|
idat: &[u8],
|
||||||
|
bit_depth: BitDepth,
|
||||||
|
) -> Result<Vec<(f32, f32, f32, f32)>, &'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.<br>
|
||||||
|
/// <br>
|
||||||
|
///# Parameter
|
||||||
|
///The function has the following two parameters:<br>
|
||||||
|
///1. IDAT [&[u8]]: This array contains the IDAT chunk of the given image.<br>
|
||||||
|
///2. bit_depth [BitDepth]: This variable contains the bit depth of the given image.<br>
|
||||||
|
///<br>
|
||||||
|
///# 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<Vec<(f32, f32, f32, f32)>, &'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.<br>
|
||||||
|
/// <br>
|
||||||
|
///# Parameter
|
||||||
|
///The function has the following two parameters:<br>
|
||||||
|
///1. IDAT [&[u8]]: This array contains the IDAT chunk of the given image.<br>
|
||||||
|
///2. bit_depth [BitDepth]: This variable contains the bit depth of the given image.<br>
|
||||||
|
///<br>
|
||||||
|
///# 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<Vec<(f32, f32, f32, f32)>, &'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.<br>
|
||||||
|
/// <br>
|
||||||
|
///# Parameter
|
||||||
|
///The function has the following two parameters:<br>
|
||||||
|
///1. IDAT [&[u8]]: This array contains the IDAT chunk of the given image.<br>
|
||||||
|
///2. bit_depth [BitDepth]: This variable contains the bit depth of the given image.<br>
|
||||||
|
///<br>
|
||||||
|
///# 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<Vec<(f32, f32, f32, f32)>, &'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())
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
extern crate core;
|
extern crate core;
|
||||||
|
|
||||||
pub mod image;
|
pub mod image;
|
||||||
|
pub mod image_loader;
|
||||||
pub mod multithreading;
|
pub mod multithreading;
|
||||||
|
|
||||||
pub fn add(left: usize, right: usize) -> usize {
|
pub fn add(left: usize, right: usize) -> usize {
|
||||||
|
|
After Width: | Height: | Size: 258 B |
After Width: | Height: | Size: 1.6 MiB |
After Width: | Height: | Size: 711 B |
After Width: | Height: | Size: 539 B |
After Width: | Height: | Size: 540 B |
After Width: | Height: | Size: 1.2 MiB |
After Width: | Height: | Size: 1.5 MiB |
After Width: | Height: | Size: 1.5 MiB |