Add more pixel formats and implement for_each and map for image
This commit is contained in:
1 parent
6cd059f401
commit
1abf71abc5
1 file changed
+277
-33
+277
-33
@@ -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<P> {
|
||||
fn read<I>(input: &mut I) -> Result<Image<P>, Error>
|
||||
where
|
||||
@@ -38,18 +51,6 @@ pub trait Format<P> {
|
||||
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<f32>
|
||||
@@ -59,6 +60,8 @@ pub trait Pixel:
|
||||
+ Clone
|
||||
+ Copy
|
||||
+ Default
|
||||
+ Send
|
||||
+ Sync
|
||||
+ Sized
|
||||
{
|
||||
}
|
||||
@@ -72,10 +75,115 @@ impl<P> Pixel for P where
|
||||
+ 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)
|
||||
}
|
||||
}
|
||||
|
||||
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<std::io::Error> 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<P> 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)
|
||||
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<f32> for SRGB {
|
||||
fn mul_assign(&mut self, scalar: f32) {
|
||||
self.r *= scalar;
|
||||
self.g *= scalar;
|
||||
self.b *= scalar;
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::DivAssign<f32> for SRGB {
|
||||
fn div_assign(&mut self, scalar: f32) {
|
||||
self.r /= scalar;
|
||||
self.g /= scalar;
|
||||
self.b /= scalar;
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Mul<f32> for SRGB {
|
||||
type Output = Self;
|
||||
|
||||
fn mul(mut self, scalar: f32) -> Self {
|
||||
self *= scalar;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Div<f32> 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<f32> for LinearRGB {
|
||||
fn mul_assign(&mut self, scalar: f32) {
|
||||
self.r *= scalar;
|
||||
self.g *= scalar;
|
||||
self.b *= scalar;
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::DivAssign<f32> for LinearRGB {
|
||||
fn div_assign(&mut self, scalar: f32) {
|
||||
self.r /= scalar;
|
||||
self.g /= scalar;
|
||||
self.b /= scalar;
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Mul<f32> for LinearRGB {
|
||||
type Output = Self;
|
||||
|
||||
fn mul(mut self, scalar: f32) -> Self {
|
||||
self *= scalar;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Div<f32> 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::<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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user