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]];