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);
+ }
}
}