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 mod farbfeld;
|
||||||
|
|
||||||
pub use 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)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
pub struct Extent {
|
pub struct Extent {
|
||||||
pub x: usize,
|
pub x: usize,
|
||||||
@@ -28,6 +19,28 @@ pub enum Error {
|
|||||||
Io(std::io::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> {
|
pub trait Format<P> {
|
||||||
fn read<I>(input: &mut I) -> Result<Image<P>, Error>
|
fn read<I>(input: &mut I) -> Result<Image<P>, Error>
|
||||||
where
|
where
|
||||||
@@ -38,18 +51,6 @@ pub trait Format<P> {
|
|||||||
O: std::io::Write;
|
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:
|
pub trait Pixel:
|
||||||
std::ops::AddAssign
|
std::ops::AddAssign
|
||||||
+ std::ops::MulAssign<f32>
|
+ std::ops::MulAssign<f32>
|
||||||
@@ -59,6 +60,8 @@ pub trait Pixel:
|
|||||||
+ Clone
|
+ Clone
|
||||||
+ Copy
|
+ Copy
|
||||||
+ Default
|
+ Default
|
||||||
|
+ Send
|
||||||
|
+ Sync
|
||||||
+ Sized
|
+ Sized
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -72,10 +75,115 @@ impl<P> Pixel for P where
|
|||||||
+ Clone
|
+ Clone
|
||||||
+ Copy
|
+ Copy
|
||||||
+ Default
|
+ Default
|
||||||
|
+ Send
|
||||||
|
+ Sync
|
||||||
+ Sized
|
+ 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 {
|
impl std::ops::AddAssign for SRGBA {
|
||||||
fn add_assign(&mut self, other: SRGBA) {
|
fn add_assign(&mut self, other: SRGBA) {
|
||||||
self.r += other.r;
|
self.r += other.r;
|
||||||
@@ -132,20 +240,156 @@ impl std::default::Default for SRGBA {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<std::io::Error> for Error {
|
impl SRGB {
|
||||||
fn from(error: std::io::Error) -> Self {
|
pub fn new(r: f32, g: f32, b: f32) -> Self {
|
||||||
Error::Io(error)
|
Self { r, g, b }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P> Image<P> {
|
impl std::ops::AddAssign for SRGB {
|
||||||
pub fn load<F: Format<P>>(path: &str) -> Result<Image<P>, Error> {
|
fn add_assign(&mut self, other: SRGB) {
|
||||||
let file = std::fs::File::open(path)?;
|
self.r += other.r;
|
||||||
F::read(&mut std::io::BufReader::new(file))
|
self.g += other.g;
|
||||||
}
|
self.b += other.b;
|
||||||
|
}
|
||||||
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::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