diff --git a/src/image.rs b/src/image.rs index dcbacfe..2a9b058 100644 --- a/src/image.rs +++ b/src/image.rs @@ -3,7 +3,7 @@ pub mod farbfeld; pub use farbfeld::*; #[derive(Clone, Copy, Debug, PartialEq)] -pub struct Pixel { +pub struct SRGBA { pub r: f32, pub g: f32, pub b: f32, @@ -12,14 +12,14 @@ pub struct Pixel { #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct Extent { - pub x: u32, - pub y: u32, + pub x: usize, + pub y: usize, } #[derive(Debug)] -pub struct Image { +pub struct Image

{ pub extent: Extent, - pub pixels: Vec, + pub pixels: Vec

, } #[derive(Debug)] @@ -28,30 +28,56 @@ pub enum Error { Io(std::io::Error), } -pub trait Format { - fn read(input: &mut I) -> Result +pub trait Format

{ + fn read(input: &mut I) -> Result, Error> where I: std::io::Read; - fn write(output: &mut O, img: &Image) -> std::io::Result<()> + fn write(output: &mut O, img: &Image

) -> std::io::Result<()> where O: std::io::Write; } impl Extent { - pub fn size(&self) -> u32 { + pub fn size(&self) -> usize { self.x * self.y } } -impl Pixel { - pub fn rgba(r: f32, g: f32, b: f32, a: f32) -> Self { +impl SRGBA { + pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self { Self { r, g, b, a } } } -impl std::ops::AddAssign for Pixel { - fn add_assign(&mut self, other: Pixel) { +pub trait Pixel: + std::ops::AddAssign + + std::ops::MulAssign + + std::ops::DivAssign + + std::ops::Mul + + std::ops::Div + + Clone + + Copy + + Default + + Sized +{ +} + +impl

Pixel for P where + P: std::ops::AddAssign + + std::ops::MulAssign + + std::ops::DivAssign + + std::ops::Mul + + std::ops::Div + + Clone + + Copy + + Default + + Sized +{ +} + +impl std::ops::AddAssign for SRGBA { + fn add_assign(&mut self, other: SRGBA) { self.r += other.r; self.g += other.g; self.b += other.b; @@ -59,7 +85,7 @@ impl std::ops::AddAssign for Pixel { } } -impl std::ops::MulAssign for Pixel { +impl std::ops::MulAssign for SRGBA { fn mul_assign(&mut self, scalar: f32) { self.r *= scalar; self.g *= scalar; @@ -68,7 +94,7 @@ impl std::ops::MulAssign for Pixel { } } -impl std::ops::DivAssign for Pixel { +impl std::ops::DivAssign for SRGBA { fn div_assign(&mut self, scalar: f32) { self.r /= scalar; self.g /= scalar; @@ -77,7 +103,7 @@ impl std::ops::DivAssign for Pixel { } } -impl std::ops::Mul for Pixel { +impl std::ops::Mul for SRGBA { type Output = Self; fn mul(mut self, scalar: f32) -> Self { @@ -86,7 +112,7 @@ impl std::ops::Mul for Pixel { } } -impl std::ops::Div for Pixel { +impl std::ops::Div for SRGBA { type Output = Self; fn div(mut self, scalar: f32) -> Self { @@ -95,7 +121,7 @@ impl std::ops::Div for Pixel { } } -impl std::default::Default for Pixel { +impl std::default::Default for SRGBA { fn default() -> Self { Self { r: 0.0, @@ -112,14 +138,14 @@ impl From for Error { } } -impl Image { - pub fn load(path: &str) -> Result { - let file = std::fs::File::open(path)?; - F::read(&mut std::io::BufReader::new(file)) - } +impl

Image

{ + pub fn load>(path: &str) -> Result, Error> { + let file = std::fs::File::open(path)?; + F::read(&mut std::io::BufReader::new(file)) + } - pub fn save(&self, path: &str) -> std::io::Result<()> { - let file = std::fs::File::create(path)?; - F::write(&mut std::io::BufWriter::new(file), self) - } + pub fn save>(&self, path: &str) -> std::io::Result<()> { + let file = std::fs::File::create(path)?; + F::write(&mut std::io::BufWriter::new(file), self) + } } diff --git a/src/image/farbfeld.rs b/src/image/farbfeld.rs index 2d3c872..eac22a7 100644 --- a/src/image/farbfeld.rs +++ b/src/image/farbfeld.rs @@ -2,59 +2,8 @@ use super::*; pub struct Farbfeld; -fn read_header(input: &mut I) -> Result -where - I: std::io::Read, -{ - let mut bytes: [u8; 16] = [0; 16]; - if input.read(&mut bytes)? < 16 || &bytes[0..8] != b"farbfeld" { - Err(Error::Invalid) - } else { - Ok(Extent { - x: u32::from_be_bytes(bytes[8..12].try_into().unwrap()), - y: u32::from_be_bytes(bytes[12..16].try_into().unwrap()), - }) - } -} - -fn read_pixels(input: &mut I, extent: Extent) -> Result, Error> -where - I: std::io::Read, -{ - let mut pixels = Vec::with_capacity(extent.size() as usize); - for _ in 0..extent.size() { - pixels.push(read_pixel(input)?); - } - Ok(pixels) -} - -fn read_pixel(input: &mut I) -> Result -where - I: std::io::Read, -{ - let mut bytes: [u8; 8] = [0; 8]; - if input.read(&mut bytes)? < 8 { - Err(Error::Invalid) - } else { - Ok(Pixel { - r: to_channel(bytes[0..2].try_into().unwrap()), - g: to_channel(bytes[2..4].try_into().unwrap()), - b: to_channel(bytes[4..6].try_into().unwrap()), - a: to_channel(bytes[6..8].try_into().unwrap()), - }) - } -} - -fn to_channel(bytes: [u8; 2]) -> f32 { - u16::from_be_bytes(bytes) as f32 / u16::MAX as f32 -} - -fn from_channel(channel: f32) -> [u8; 2] { - ((channel * u16::MAX as f32) as u16).to_be_bytes() -} - -impl Format for Farbfeld { - fn read(input: &mut I) -> Result +impl Format for Farbfeld { + fn read(input: &mut I) -> Result, Error> where I: std::io::Read, { @@ -65,13 +14,13 @@ impl Format for Farbfeld { }) } - fn write(output: &mut O, img: &Image) -> std::io::Result<()> + fn write(output: &mut O, img: &Image) -> std::io::Result<()> where O: std::io::Write, { output.write_all(b"farbfeld")?; - output.write_all(&img.extent.x.to_be_bytes())?; - output.write_all(&img.extent.y.to_be_bytes())?; + output.write_all(&(img.extent.x as u32).to_be_bytes())?; + output.write_all(&(img.extent.y as u32).to_be_bytes())?; for pixel in &img.pixels { output.write_all(&from_channel(pixel.r))?; output.write_all(&from_channel(pixel.g))?; @@ -82,6 +31,62 @@ impl Format for Farbfeld { } } +fn read_header(input: &mut I) -> Result +where + I: std::io::Read, +{ + let mut bytes: [u8; 16] = [0; 16]; + if input.read(&mut bytes)? < 16 || &bytes[0..8] != b"farbfeld" { + Err(Error::Invalid) + } else { + Ok(Extent { + x: be_u32_at(&bytes, 8) as usize, + y: be_u32_at(&bytes, 12) as usize, + }) + } +} + +fn read_pixels(input: &mut I, extent: Extent) -> Result, Error> +where + I: std::io::Read, +{ + let mut pixels = Vec::with_capacity(extent.size() as usize); + for _ in 0..extent.size() { + pixels.push(read_pixel(input)?); + } + Ok(pixels) +} + +fn read_pixel(input: &mut I) -> Result +where + I: std::io::Read, +{ + let mut bytes: [u8; 8] = [0; 8]; + if input.read(&mut bytes)? < 8 { + Err(Error::Invalid) + } else { + Ok(SRGBA { + r: to_channel(bytes[0..2].try_into().unwrap()), + g: to_channel(bytes[2..4].try_into().unwrap()), + b: to_channel(bytes[4..6].try_into().unwrap()), + a: to_channel(bytes[6..8].try_into().unwrap()), + }) + } +} + +fn be_u32_at(bytes: &[u8], start: usize) -> u32 { + let end = start + std::mem::size_of::(); + u32::from_be_bytes(bytes[start..end].try_into().unwrap()) +} + +fn to_channel(bytes: [u8; 2]) -> f32 { + u16::from_be_bytes(bytes) as f32 / u16::MAX as f32 +} + +fn from_channel(channel: f32) -> [u8; 2] { + ((channel * u16::MAX as f32) as u16).to_be_bytes() +} + #[cfg(test)] mod tests { use super::*; @@ -177,7 +182,7 @@ mod tests { fn write_magic() { let img = Image { extent: Extent { x: 1, y: 1 }, - pixels: vec![Pixel { + pixels: vec![SRGBA { r: 0.0, g: 0.0, b: 0.0, @@ -193,7 +198,7 @@ mod tests { fn write_extent() { let img = Image { extent: Extent { x: 1, y: 2 }, - pixels: vec![Pixel { + pixels: vec![SRGBA { r: 0.0, g: 0.0, b: 0.0, @@ -210,7 +215,7 @@ mod tests { fn write_pixel() { let img = Image { extent: Extent { x: 1, y: 1 }, - pixels: vec![Pixel { + pixels: vec![SRGBA { r: 0.0, g: 0.25, b: 0.5, diff --git a/src/kernel.rs b/src/kernel.rs index 642b8e6..2cc88a4 100644 --- a/src/kernel.rs +++ b/src/kernel.rs @@ -1,7 +1,9 @@ use crate::*; pub trait Kernel { - fn convolve(&self, img: &image::Image) -> image::Image; + fn convolve

(&self, img: &image::Image

) -> image::Image

+ where + P: image::Pixel; } fn sum(kernel: &[[f32; N]; N]) -> f32 { @@ -15,9 +17,12 @@ fn sum(kernel: &[[f32; N]; N]) -> f32 { } impl Kernel for [[f32; N]; N] { - fn convolve(&self, img: &image::Image) -> image::Image { - let l = N as u32 / 2; - let mag = sum(self); + fn convolve

(&self, img: &image::Image

) -> image::Image

+ where + P: image::Pixel, + { + let l = N / 2; + let mag = sum(self); let extent = image::Extent { x: img.extent.x - 2 * l, @@ -26,8 +31,8 @@ impl Kernel for [[f32; N]; N] { let mut pixels = Vec::with_capacity(extent.size() as usize); for y in 0..extent.y { - for x in 0..extent.x { - let mut pixel = image::Pixel::default(); + for x in 0..extent.x { + let mut pixel = P::default(); for i in 0..N { for j in 0..N { let img_x = x as usize + i; @@ -52,7 +57,7 @@ mod tests { fn size_of_result_img() { let input = image::Image { extent: image::Extent { x: 6, y: 10 }, - pixels: vec![image::Pixel::default(); 60], + pixels: vec![0.0; 60], }; let kernel = [[1.0, 1.0, 1.0], [1.0, 1.0, 1.0], [1.0, 1.0, 1.0]]; let output = kernel.convolve(&input); @@ -65,15 +70,15 @@ mod tests { let input = image::Image { extent: image::Extent { x: 3, y: 3 }, pixels: vec![ - image::Pixel::rgba(0.1, 0.0, 0.0, 1.0), - image::Pixel::rgba(0.0, 0.2, 0.0, 1.0), - image::Pixel::rgba(0.0, 0.0, 0.3, 1.0), - image::Pixel::rgba(0.2, 0.0, 0.0, 1.0), - image::Pixel::rgba(0.0, 0.3, 0.0, 1.0), - image::Pixel::rgba(0.0, 0.0, 0.1, 1.0), - image::Pixel::rgba(0.3, 0.0, 0.0, 1.0), - image::Pixel::rgba(0.0, 0.1, 0.0, 1.0), - image::Pixel::rgba(0.0, 0.0, 0.2, 1.0), + image::SRGBA::new(0.1, 0.0, 0.0, 1.0), + image::SRGBA::new(0.0, 0.2, 0.0, 1.0), + image::SRGBA::new(0.0, 0.0, 0.3, 1.0), + image::SRGBA::new(0.2, 0.0, 0.0, 1.0), + image::SRGBA::new(0.0, 0.3, 0.0, 1.0), + image::SRGBA::new(0.0, 0.0, 0.1, 1.0), + image::SRGBA::new(0.3, 0.0, 0.0, 1.0), + image::SRGBA::new(0.0, 0.1, 0.0, 1.0), + image::SRGBA::new(0.0, 0.0, 0.2, 1.0), ], }; let kernel = [[0.1, 0.2, 0.3], [0.4, 0.5, 0.6], [0.7, 0.8, 0.9]];