207 lines
5.1 KiB
Rust
207 lines
5.1 KiB
Rust
pub mod linear_rgb;
|
|
pub mod srgb;
|
|
pub use linear_rgb::*;
|
|
pub use srgb::*;
|
|
|
|
pub mod farbfeld;
|
|
pub use farbfeld::*;
|
|
|
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
pub struct Extent {
|
|
pub x: usize,
|
|
pub y: usize,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct Image<P> {
|
|
pub extent: Extent,
|
|
pub pixels: Vec<P>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum Error {
|
|
Invalid,
|
|
Io(std::io::Error),
|
|
}
|
|
|
|
pub trait Format<P> {
|
|
fn read<I>(input: &mut I) -> Result<Image<P>, Error>
|
|
where
|
|
I: std::io::Read;
|
|
|
|
fn write<O>(output: &mut O, img: &Image<P>) -> std::io::Result<()>
|
|
where
|
|
O: std::io::Write;
|
|
}
|
|
|
|
pub trait Pixel:
|
|
std::ops::AddAssign
|
|
+ std::ops::SubAssign
|
|
+ std::ops::MulAssign<f32>
|
|
+ std::ops::DivAssign<f32>
|
|
+ std::ops::Add<Output = Self>
|
|
+ std::ops::Sub<Output = Self>
|
|
+ std::ops::Mul<f32, Output = Self>
|
|
+ std::ops::Div<f32, Output = Self>
|
|
+ Clone
|
|
+ Copy
|
|
+ Default
|
|
+ Send
|
|
+ Sync
|
|
+ Sized
|
|
{
|
|
}
|
|
|
|
impl<P> Pixel for P where
|
|
P: std::ops::AddAssign
|
|
+ std::ops::SubAssign
|
|
+ std::ops::MulAssign<f32>
|
|
+ std::ops::DivAssign<f32>
|
|
+ std::ops::Add<Output = Self>
|
|
+ std::ops::Sub<Output = Self>
|
|
+ std::ops::Mul<f32, Output = Self>
|
|
+ std::ops::Div<f32, Output = Self>
|
|
+ Clone
|
|
+ Copy
|
|
+ Default
|
|
+ Send
|
|
+ Sync
|
|
+ Sized
|
|
{
|
|
}
|
|
|
|
impl Extent {
|
|
pub fn size(&self) -> usize {
|
|
self.x * self.y
|
|
}
|
|
}
|
|
|
|
impl<P: Pixel> Image<P> {
|
|
pub fn load<F: Format<P>>(path: &str) -> Result<Image<P>, Error> {
|
|
let file = std::fs::File::open(path)?;
|
|
F::read(&mut std::io::BufReader::new(file))
|
|
}
|
|
|
|
pub fn save<F: Format<P>>(&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<F>(&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<F, Q>(&self, f: F) -> Image<Q>
|
|
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<F>(&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: 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<std::io::Error> for Error {
|
|
fn from(error: std::io::Error) -> Self {
|
|
Error::Io(error)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn f32_image_created_with_extent_has_all_zeros() {
|
|
let img = Image::<f32>::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::<f32>::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::<f32>::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);
|
|
}
|
|
}
|
|
}
|