diff --git a/src/image.rs b/src/image.rs index 2a9b058..996bb06 100644 --- a/src/image.rs +++ b/src/image.rs @@ -1,15 +1,6 @@ pub mod farbfeld; - pub use farbfeld::*; -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct SRGBA { - pub r: f32, - pub g: f32, - pub b: f32, - pub a: f32, -} - #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct Extent { pub x: usize, @@ -28,6 +19,28 @@ pub enum Error { Io(std::io::Error), } +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct SRGBA { + pub r: f32, + pub g: f32, + pub b: f32, + pub a: f32, +} + +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct SRGB { + pub r: f32, + pub g: f32, + pub b: f32, +} + +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct LinearRGB { + pub r: f32, + pub g: f32, + pub b: f32, +} + pub trait Format

{ fn read(input: &mut I) -> Result, Error> where @@ -38,18 +51,6 @@ pub trait Format

{ O: std::io::Write; } -impl Extent { - pub fn size(&self) -> usize { - self.x * self.y - } -} - -impl SRGBA { - pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self { - Self { r, g, b, a } - } -} - pub trait Pixel: std::ops::AddAssign + std::ops::MulAssign @@ -59,6 +60,8 @@ pub trait Pixel: + Clone + Copy + Default + + Send + + Sync + Sized { } @@ -72,10 +75,115 @@ impl

Pixel for P where + Clone + Copy + Default + + Send + + Sync + Sized { } +impl Extent { + pub fn size(&self) -> usize { + self.x * self.y + } +} + +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 with_extent(extent: Extent) -> Self { + let mut pixels = Vec::with_capacity(extent.size()); + pixels.resize(extent.size(), P::default()); + Self { extent, pixels } + } + + pub fn for_each(&mut self, f: F) + where + F: Fn(usize, usize, &mut P) + Send + Sync, + { + if let Ok(count) = std::thread::available_parallelism() { + self.for_each_parallel(f, count.get()) + } else { + Self::for_each_thread( + f, + self.pixels.as_mut_slice(), + 0, + self.extent.x, + ) + } + } + + pub fn map(&self, f: F) -> Image + where + F: Fn(P) -> Q + Send + Sync, + Q: Pixel, + { + let mut img = Image::with_extent(self.extent); + img.for_each(|x, y, p| { + *p = f(self.pixels[self.extent.x * y + x]); + }); + img + } + + fn for_each_parallel(&mut self, f: F, n: usize) + where + F: Fn(usize, usize, &mut P) + Send + Sync, + { + let row = self.extent.x; + let per_thread = self.extent.size() / n; + let rem = self.extent.size() % n; + + std::thread::scope(|scope| { + let mut rest = self.pixels.as_mut_slice(); + let mut buf: &mut [P]; + { + let count = per_thread + rem; + (buf, rest) = rest.split_at_mut(count); + let fref = &f; + scope.spawn(move || Self::for_each_thread(fref, buf, 0, row)); + } + for i in 1..n { + (buf, rest) = rest.split_at_mut(per_thread); + let start = i * per_thread + rem; + let fref = &f; + scope.spawn(move || { + Self::for_each_thread(fref, buf, start, row) + }); + } + }); + } + + fn for_each_thread(f: F, pixels: &mut [P], start: usize, row: usize) + where + F: Fn(usize, usize, &mut P), + { + for i in 0..pixels.len() { + let x = (start + i) % row; + let y = (start + i) / row; + f(x, y, &mut pixels[i]); + } + } +} + +impl From for Error { + fn from(error: std::io::Error) -> Self { + Error::Io(error) + } +} + +impl SRGBA { + pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self { + Self { r, g, b, a } + } +} + impl std::ops::AddAssign for SRGBA { fn add_assign(&mut self, other: SRGBA) { self.r += other.r; @@ -132,20 +240,156 @@ impl std::default::Default for SRGBA { } } -impl From for Error { - fn from(error: std::io::Error) -> Self { - Error::Io(error) +impl SRGB { + pub fn new(r: f32, g: f32, b: f32) -> Self { + Self { r, g, b } } } -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) +impl std::ops::AddAssign for SRGB { + fn add_assign(&mut self, other: SRGB) { + self.r += other.r; + self.g += other.g; + self.b += other.b; + } +} + +impl std::ops::MulAssign for SRGB { + fn mul_assign(&mut self, scalar: f32) { + self.r *= scalar; + self.g *= scalar; + self.b *= scalar; + } +} + +impl std::ops::DivAssign for SRGB { + fn div_assign(&mut self, scalar: f32) { + self.r /= scalar; + self.g /= scalar; + self.b /= scalar; + } +} + +impl std::ops::Mul for SRGB { + type Output = Self; + + fn mul(mut self, scalar: f32) -> Self { + self *= scalar; + self + } +} + +impl std::ops::Div for SRGB { + type Output = Self; + + fn div(mut self, scalar: f32) -> Self { + self /= scalar; + self + } +} + +impl std::default::Default for SRGB { + fn default() -> Self { + Self { + r: 0.0, + g: 0.0, + b: 0.0, + } + } +} + +impl LinearRGB { + pub fn new(r: f32, g: f32, b: f32) -> Self { + Self { r, g, b } + } +} + +impl std::ops::AddAssign for LinearRGB { + fn add_assign(&mut self, other: LinearRGB) { + self.r += other.r; + self.g += other.g; + self.b += other.b; + } +} + +impl std::ops::MulAssign for LinearRGB { + fn mul_assign(&mut self, scalar: f32) { + self.r *= scalar; + self.g *= scalar; + self.b *= scalar; + } +} + +impl std::ops::DivAssign for LinearRGB { + fn div_assign(&mut self, scalar: f32) { + self.r /= scalar; + self.g /= scalar; + self.b /= scalar; + } +} + +impl std::ops::Mul for LinearRGB { + type Output = Self; + + fn mul(mut self, scalar: f32) -> Self { + self *= scalar; + self + } +} + +impl std::ops::Div for LinearRGB { + type Output = Self; + + fn div(mut self, scalar: f32) -> Self { + self /= scalar; + self + } +} + +impl std::default::Default for LinearRGB { + fn default() -> Self { + Self { + r: 0.0, + g: 0.0, + b: 0.0, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn f32_image_created_with_extent_has_all_zeros() { + let img = Image::::with_extent(Extent { x: 10, y: 10 }); + assert_eq!(img.pixels.len(), 100); + for pixel in img.pixels { + assert_eq!(pixel, 0.0); + } + } + + #[test] + fn f32_image_for_each() { + let mut img = Image::::with_extent(Extent { x: 10, y: 10 }); + img.for_each(|x, y, p| *p = (x + y) as f32); + for x in 0..img.extent.x { + for y in 0..img.extent.y { + assert_eq!(img.pixels[y * img.extent.x + x], (x + y) as f32); + } + } + } + + #[test] + fn f32_image_map() { + let mut img = Image::::with_extent(Extent { x: 10, y: 10 }); + for i in 0..img.extent.size() { + img.pixels[i] = i as f32; + } + let res = img.map(|p| p * 2.0); + assert_eq!(res.extent, img.extent); + for i in 0..res.extent.size() { + assert_eq!(res.pixels[i], 2.0 * i as f32); + } } }