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
This commit is contained in:
Schwingel0206 2023-06-17 22:00:03 +02:00
parent 65e422e547
commit af5097bdfd
9 changed files with 334 additions and 45 deletions

View File

@ -36,7 +36,7 @@ use std::slice::{Iter, IterMut};
use std::vec::IntoIter; use std::vec::IntoIter;
#[allow(unused)] #[allow(unused)]
#[derive(Default)] #[derive(Default, Debug)]
pub struct Image<T> pub struct Image<T>
where where
T: Into<f32> + PartialEq + Default + Copy + From<u8> + PartialOrd, T: Into<f32> + PartialEq + Default + Copy + From<u8> + PartialOrd,
@ -87,6 +87,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)> {

View File

@ -1,57 +1,343 @@
use std::path::Path;
use crate::image::Image; use crate::image::Image;
use png::BitDepth;
use std::fs::File; 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"),
};
/* !!!Fragen!!! let mut reader = match decoder.read_info() {
- was ist .unwrap() Ok(reader) => reader,
- was ist ein IDAT chunk (bei read.info() in der doku) Err(_) => return Err("Failed to read PNG info"),
- 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
*/
pub fn image_loader(path: &Path) -> Image<f32>
{
//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()]; 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 bit_depth = reader.info().bit_depth;
let color_type = reader.info().color_type; let color_type = reader.info().color_type;
let width = reader.info().width; let width = reader.info().width;
let height = reader.info().height; let height = reader.info().height;
let palette = &reader.info().palette;
// Grab the bytes of the image. let idat = &buf[..info.buffer_size()];
let bytes = &buf[..info.buffer_size()];
// Inspect more details of the last read frame.
let in_animation = reader.info().frame_control.is_some();
//marker1 println!("idat {:?} idat ", idat);
let vec: Vec<(f32, f32, f32, f32)> = vec! println!("palette {:?} palette", palette);
[ println!("depth {:?} depth", bit_depth);
(-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<f32> = Image::new(1, 3, vec, path.to_path_buf());
image 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)
} }
//höhe(in px[usize: u8]) ///# Grayscale to RGBA
//breite(in px[usize: u8]) ///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>
//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!!! /// <br>
//absoluter pfad ///# 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())
}
}

BIN
test_img/gray_image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 B

BIN
test_img/indexed_image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 711 B

BIN
test_img/red_image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 539 B

BIN
test_img/rot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 540 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

BIN
test_img/wrong size.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB