Refactor allowing multiple pixel formats
This commit is contained in:
1 parent
cef82a2e14
commit
6cd059f401
3 files changed
+130
-94
No files matched your search
+47
-21
@@ -3,7 +3,7 @@ pub mod farbfeld;
|
|||||||
pub use farbfeld::*;
|
pub use farbfeld::*;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub struct Pixel {
|
pub struct SRGBA {
|
||||||
pub r: f32,
|
pub r: f32,
|
||||||
pub g: f32,
|
pub g: f32,
|
||||||
pub b: f32,
|
pub b: f32,
|
||||||
@@ -12,14 +12,14 @@ pub struct Pixel {
|
|||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
pub struct Extent {
|
pub struct Extent {
|
||||||
pub x: u32,
|
pub x: usize,
|
||||||
pub y: u32,
|
pub y: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Image {
|
pub struct Image<P> {
|
||||||
pub extent: Extent,
|
pub extent: Extent,
|
||||||
pub pixels: Vec<Pixel>,
|
pub pixels: Vec<P>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -28,30 +28,56 @@ pub enum Error {
|
|||||||
Io(std::io::Error),
|
Io(std::io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Format {
|
pub trait Format<P> {
|
||||||
fn read<I>(input: &mut I) -> Result<Image, Error>
|
fn read<I>(input: &mut I) -> Result<Image<P>, Error>
|
||||||
where
|
where
|
||||||
I: std::io::Read;
|
I: std::io::Read;
|
||||||
|
|
||||||
fn write<O>(output: &mut O, img: &Image) -> std::io::Result<()>
|
fn write<O>(output: &mut O, img: &Image<P>) -> std::io::Result<()>
|
||||||
where
|
where
|
||||||
O: std::io::Write;
|
O: std::io::Write;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Extent {
|
impl Extent {
|
||||||
pub fn size(&self) -> u32 {
|
pub fn size(&self) -> usize {
|
||||||
self.x * self.y
|
self.x * self.y
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pixel {
|
impl SRGBA {
|
||||||
pub fn rgba(r: f32, g: f32, b: f32, a: f32) -> Self {
|
pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
|
||||||
Self { r, g, b, a }
|
Self { r, g, b, a }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::AddAssign for Pixel {
|
pub trait Pixel:
|
||||||
fn add_assign(&mut self, other: Pixel) {
|
std::ops::AddAssign
|
||||||
|
+ std::ops::MulAssign<f32>
|
||||||
|
+ std::ops::DivAssign<f32>
|
||||||
|
+ std::ops::Mul<f32, Output = Self>
|
||||||
|
+ std::ops::Div<f32, Output = Self>
|
||||||
|
+ Clone
|
||||||
|
+ Copy
|
||||||
|
+ Default
|
||||||
|
+ Sized
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P> Pixel for P where
|
||||||
|
P: std::ops::AddAssign
|
||||||
|
+ std::ops::MulAssign<f32>
|
||||||
|
+ std::ops::DivAssign<f32>
|
||||||
|
+ std::ops::Mul<f32, Output = Self>
|
||||||
|
+ std::ops::Div<f32, Output = Self>
|
||||||
|
+ Clone
|
||||||
|
+ Copy
|
||||||
|
+ Default
|
||||||
|
+ Sized
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::AddAssign for SRGBA {
|
||||||
|
fn add_assign(&mut self, other: SRGBA) {
|
||||||
self.r += other.r;
|
self.r += other.r;
|
||||||
self.g += other.g;
|
self.g += other.g;
|
||||||
self.b += other.b;
|
self.b += other.b;
|
||||||
@@ -59,7 +85,7 @@ impl std::ops::AddAssign for Pixel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::MulAssign<f32> for Pixel {
|
impl std::ops::MulAssign<f32> for SRGBA {
|
||||||
fn mul_assign(&mut self, scalar: f32) {
|
fn mul_assign(&mut self, scalar: f32) {
|
||||||
self.r *= scalar;
|
self.r *= scalar;
|
||||||
self.g *= scalar;
|
self.g *= scalar;
|
||||||
@@ -68,7 +94,7 @@ impl std::ops::MulAssign<f32> for Pixel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::DivAssign<f32> for Pixel {
|
impl std::ops::DivAssign<f32> for SRGBA {
|
||||||
fn div_assign(&mut self, scalar: f32) {
|
fn div_assign(&mut self, scalar: f32) {
|
||||||
self.r /= scalar;
|
self.r /= scalar;
|
||||||
self.g /= scalar;
|
self.g /= scalar;
|
||||||
@@ -77,7 +103,7 @@ impl std::ops::DivAssign<f32> for Pixel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Mul<f32> for Pixel {
|
impl std::ops::Mul<f32> for SRGBA {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
fn mul(mut self, scalar: f32) -> Self {
|
fn mul(mut self, scalar: f32) -> Self {
|
||||||
@@ -86,7 +112,7 @@ impl std::ops::Mul<f32> for Pixel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Div<f32> for Pixel {
|
impl std::ops::Div<f32> for SRGBA {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
fn div(mut self, scalar: f32) -> Self {
|
fn div(mut self, scalar: f32) -> Self {
|
||||||
@@ -95,7 +121,7 @@ impl std::ops::Div<f32> for Pixel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::default::Default for Pixel {
|
impl std::default::Default for SRGBA {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
r: 0.0,
|
r: 0.0,
|
||||||
@@ -112,13 +138,13 @@ impl From<std::io::Error> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Image {
|
impl<P> Image<P> {
|
||||||
pub fn load<F: Format>(path: &str) -> Result<Image, Error> {
|
pub fn load<F: Format<P>>(path: &str) -> Result<Image<P>, Error> {
|
||||||
let file = std::fs::File::open(path)?;
|
let file = std::fs::File::open(path)?;
|
||||||
F::read(&mut std::io::BufReader::new(file))
|
F::read(&mut std::io::BufReader::new(file))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn save<F: Format>(&self, path: &str) -> std::io::Result<()> {
|
pub fn save<F: Format<P>>(&self, path: &str) -> std::io::Result<()> {
|
||||||
let file = std::fs::File::create(path)?;
|
let file = std::fs::File::create(path)?;
|
||||||
F::write(&mut std::io::BufWriter::new(file), self)
|
F::write(&mut std::io::BufWriter::new(file), self)
|
||||||
}
|
}
|
||||||
|
|||||||
+64
-59
@@ -2,59 +2,8 @@ use super::*;
|
|||||||
|
|
||||||
pub struct Farbfeld;
|
pub struct Farbfeld;
|
||||||
|
|
||||||
fn read_header<I>(input: &mut I) -> Result<Extent, Error>
|
impl Format<SRGBA> for Farbfeld {
|
||||||
where
|
fn read<I>(input: &mut I) -> Result<Image<SRGBA>, Error>
|
||||||
I: std::io::Read,
|
|
||||||
{
|
|
||||||
let mut bytes: [u8; 16] = [0; 16];
|
|
||||||
if input.read(&mut bytes)? < 16 || &bytes[0..8] != b"farbfeld" {
|
|
||||||
Err(Error::Invalid)
|
|
||||||
} else {
|
|
||||||
Ok(Extent {
|
|
||||||
x: u32::from_be_bytes(bytes[8..12].try_into().unwrap()),
|
|
||||||
y: u32::from_be_bytes(bytes[12..16].try_into().unwrap()),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_pixels<I>(input: &mut I, extent: Extent) -> Result<Vec<Pixel>, Error>
|
|
||||||
where
|
|
||||||
I: std::io::Read,
|
|
||||||
{
|
|
||||||
let mut pixels = Vec::with_capacity(extent.size() as usize);
|
|
||||||
for _ in 0..extent.size() {
|
|
||||||
pixels.push(read_pixel(input)?);
|
|
||||||
}
|
|
||||||
Ok(pixels)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_pixel<I>(input: &mut I) -> Result<Pixel, Error>
|
|
||||||
where
|
|
||||||
I: std::io::Read,
|
|
||||||
{
|
|
||||||
let mut bytes: [u8; 8] = [0; 8];
|
|
||||||
if input.read(&mut bytes)? < 8 {
|
|
||||||
Err(Error::Invalid)
|
|
||||||
} else {
|
|
||||||
Ok(Pixel {
|
|
||||||
r: to_channel(bytes[0..2].try_into().unwrap()),
|
|
||||||
g: to_channel(bytes[2..4].try_into().unwrap()),
|
|
||||||
b: to_channel(bytes[4..6].try_into().unwrap()),
|
|
||||||
a: to_channel(bytes[6..8].try_into().unwrap()),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_channel(bytes: [u8; 2]) -> f32 {
|
|
||||||
u16::from_be_bytes(bytes) as f32 / u16::MAX as f32
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_channel(channel: f32) -> [u8; 2] {
|
|
||||||
((channel * u16::MAX as f32) as u16).to_be_bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Format for Farbfeld {
|
|
||||||
fn read<I>(input: &mut I) -> Result<Image, Error>
|
|
||||||
where
|
where
|
||||||
I: std::io::Read,
|
I: std::io::Read,
|
||||||
{
|
{
|
||||||
@@ -65,13 +14,13 @@ impl Format for Farbfeld {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write<O>(output: &mut O, img: &Image) -> std::io::Result<()>
|
fn write<O>(output: &mut O, img: &Image<SRGBA>) -> std::io::Result<()>
|
||||||
where
|
where
|
||||||
O: std::io::Write,
|
O: std::io::Write,
|
||||||
{
|
{
|
||||||
output.write_all(b"farbfeld")?;
|
output.write_all(b"farbfeld")?;
|
||||||
output.write_all(&img.extent.x.to_be_bytes())?;
|
output.write_all(&(img.extent.x as u32).to_be_bytes())?;
|
||||||
output.write_all(&img.extent.y.to_be_bytes())?;
|
output.write_all(&(img.extent.y as u32).to_be_bytes())?;
|
||||||
for pixel in &img.pixels {
|
for pixel in &img.pixels {
|
||||||
output.write_all(&from_channel(pixel.r))?;
|
output.write_all(&from_channel(pixel.r))?;
|
||||||
output.write_all(&from_channel(pixel.g))?;
|
output.write_all(&from_channel(pixel.g))?;
|
||||||
@@ -82,6 +31,62 @@ impl Format for Farbfeld {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn read_header<I>(input: &mut I) -> Result<Extent, Error>
|
||||||
|
where
|
||||||
|
I: std::io::Read,
|
||||||
|
{
|
||||||
|
let mut bytes: [u8; 16] = [0; 16];
|
||||||
|
if input.read(&mut bytes)? < 16 || &bytes[0..8] != b"farbfeld" {
|
||||||
|
Err(Error::Invalid)
|
||||||
|
} else {
|
||||||
|
Ok(Extent {
|
||||||
|
x: be_u32_at(&bytes, 8) as usize,
|
||||||
|
y: be_u32_at(&bytes, 12) as usize,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_pixels<I>(input: &mut I, extent: Extent) -> Result<Vec<SRGBA>, Error>
|
||||||
|
where
|
||||||
|
I: std::io::Read,
|
||||||
|
{
|
||||||
|
let mut pixels = Vec::with_capacity(extent.size() as usize);
|
||||||
|
for _ in 0..extent.size() {
|
||||||
|
pixels.push(read_pixel(input)?);
|
||||||
|
}
|
||||||
|
Ok(pixels)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_pixel<I>(input: &mut I) -> Result<SRGBA, Error>
|
||||||
|
where
|
||||||
|
I: std::io::Read,
|
||||||
|
{
|
||||||
|
let mut bytes: [u8; 8] = [0; 8];
|
||||||
|
if input.read(&mut bytes)? < 8 {
|
||||||
|
Err(Error::Invalid)
|
||||||
|
} else {
|
||||||
|
Ok(SRGBA {
|
||||||
|
r: to_channel(bytes[0..2].try_into().unwrap()),
|
||||||
|
g: to_channel(bytes[2..4].try_into().unwrap()),
|
||||||
|
b: to_channel(bytes[4..6].try_into().unwrap()),
|
||||||
|
a: to_channel(bytes[6..8].try_into().unwrap()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn be_u32_at(bytes: &[u8], start: usize) -> u32 {
|
||||||
|
let end = start + std::mem::size_of::<u32>();
|
||||||
|
u32::from_be_bytes(bytes[start..end].try_into().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_channel(bytes: [u8; 2]) -> f32 {
|
||||||
|
u16::from_be_bytes(bytes) as f32 / u16::MAX as f32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_channel(channel: f32) -> [u8; 2] {
|
||||||
|
((channel * u16::MAX as f32) as u16).to_be_bytes()
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -177,7 +182,7 @@ mod tests {
|
|||||||
fn write_magic() {
|
fn write_magic() {
|
||||||
let img = Image {
|
let img = Image {
|
||||||
extent: Extent { x: 1, y: 1 },
|
extent: Extent { x: 1, y: 1 },
|
||||||
pixels: vec![Pixel {
|
pixels: vec![SRGBA {
|
||||||
r: 0.0,
|
r: 0.0,
|
||||||
g: 0.0,
|
g: 0.0,
|
||||||
b: 0.0,
|
b: 0.0,
|
||||||
@@ -193,7 +198,7 @@ mod tests {
|
|||||||
fn write_extent() {
|
fn write_extent() {
|
||||||
let img = Image {
|
let img = Image {
|
||||||
extent: Extent { x: 1, y: 2 },
|
extent: Extent { x: 1, y: 2 },
|
||||||
pixels: vec![Pixel {
|
pixels: vec![SRGBA {
|
||||||
r: 0.0,
|
r: 0.0,
|
||||||
g: 0.0,
|
g: 0.0,
|
||||||
b: 0.0,
|
b: 0.0,
|
||||||
@@ -210,7 +215,7 @@ mod tests {
|
|||||||
fn write_pixel() {
|
fn write_pixel() {
|
||||||
let img = Image {
|
let img = Image {
|
||||||
extent: Extent { x: 1, y: 1 },
|
extent: Extent { x: 1, y: 1 },
|
||||||
pixels: vec![Pixel {
|
pixels: vec![SRGBA {
|
||||||
r: 0.0,
|
r: 0.0,
|
||||||
g: 0.25,
|
g: 0.25,
|
||||||
b: 0.5,
|
b: 0.5,
|
||||||
|
|||||||
+19
-14
@@ -1,7 +1,9 @@
|
|||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
pub trait Kernel {
|
pub trait Kernel {
|
||||||
fn convolve(&self, img: &image::Image) -> image::Image;
|
fn convolve<P>(&self, img: &image::Image<P>) -> image::Image<P>
|
||||||
|
where
|
||||||
|
P: image::Pixel;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sum<const N: usize>(kernel: &[[f32; N]; N]) -> f32 {
|
fn sum<const N: usize>(kernel: &[[f32; N]; N]) -> f32 {
|
||||||
@@ -15,8 +17,11 @@ fn sum<const N: usize>(kernel: &[[f32; N]; N]) -> f32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<const N: usize> Kernel for [[f32; N]; N] {
|
impl<const N: usize> Kernel for [[f32; N]; N] {
|
||||||
fn convolve(&self, img: &image::Image) -> image::Image {
|
fn convolve<P>(&self, img: &image::Image<P>) -> image::Image<P>
|
||||||
let l = N as u32 / 2;
|
where
|
||||||
|
P: image::Pixel,
|
||||||
|
{
|
||||||
|
let l = N / 2;
|
||||||
let mag = sum(self);
|
let mag = sum(self);
|
||||||
|
|
||||||
let extent = image::Extent {
|
let extent = image::Extent {
|
||||||
@@ -27,7 +32,7 @@ impl<const N: usize> Kernel for [[f32; N]; N] {
|
|||||||
let mut pixels = Vec::with_capacity(extent.size() as usize);
|
let mut pixels = Vec::with_capacity(extent.size() as usize);
|
||||||
for y in 0..extent.y {
|
for y in 0..extent.y {
|
||||||
for x in 0..extent.x {
|
for x in 0..extent.x {
|
||||||
let mut pixel = image::Pixel::default();
|
let mut pixel = P::default();
|
||||||
for i in 0..N {
|
for i in 0..N {
|
||||||
for j in 0..N {
|
for j in 0..N {
|
||||||
let img_x = x as usize + i;
|
let img_x = x as usize + i;
|
||||||
@@ -52,7 +57,7 @@ mod tests {
|
|||||||
fn size_of_result_img() {
|
fn size_of_result_img() {
|
||||||
let input = image::Image {
|
let input = image::Image {
|
||||||
extent: image::Extent { x: 6, y: 10 },
|
extent: image::Extent { x: 6, y: 10 },
|
||||||
pixels: vec![image::Pixel::default(); 60],
|
pixels: vec![0.0; 60],
|
||||||
};
|
};
|
||||||
let kernel = [[1.0, 1.0, 1.0], [1.0, 1.0, 1.0], [1.0, 1.0, 1.0]];
|
let kernel = [[1.0, 1.0, 1.0], [1.0, 1.0, 1.0], [1.0, 1.0, 1.0]];
|
||||||
let output = kernel.convolve(&input);
|
let output = kernel.convolve(&input);
|
||||||
@@ -65,15 +70,15 @@ mod tests {
|
|||||||
let input = image::Image {
|
let input = image::Image {
|
||||||
extent: image::Extent { x: 3, y: 3 },
|
extent: image::Extent { x: 3, y: 3 },
|
||||||
pixels: vec![
|
pixels: vec![
|
||||||
image::Pixel::rgba(0.1, 0.0, 0.0, 1.0),
|
image::SRGBA::new(0.1, 0.0, 0.0, 1.0),
|
||||||
image::Pixel::rgba(0.0, 0.2, 0.0, 1.0),
|
image::SRGBA::new(0.0, 0.2, 0.0, 1.0),
|
||||||
image::Pixel::rgba(0.0, 0.0, 0.3, 1.0),
|
image::SRGBA::new(0.0, 0.0, 0.3, 1.0),
|
||||||
image::Pixel::rgba(0.2, 0.0, 0.0, 1.0),
|
image::SRGBA::new(0.2, 0.0, 0.0, 1.0),
|
||||||
image::Pixel::rgba(0.0, 0.3, 0.0, 1.0),
|
image::SRGBA::new(0.0, 0.3, 0.0, 1.0),
|
||||||
image::Pixel::rgba(0.0, 0.0, 0.1, 1.0),
|
image::SRGBA::new(0.0, 0.0, 0.1, 1.0),
|
||||||
image::Pixel::rgba(0.3, 0.0, 0.0, 1.0),
|
image::SRGBA::new(0.3, 0.0, 0.0, 1.0),
|
||||||
image::Pixel::rgba(0.0, 0.1, 0.0, 1.0),
|
image::SRGBA::new(0.0, 0.1, 0.0, 1.0),
|
||||||
image::Pixel::rgba(0.0, 0.0, 0.2, 1.0),
|
image::SRGBA::new(0.0, 0.0, 0.2, 1.0),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
let kernel = [[0.1, 0.2, 0.3], [0.4, 0.5, 0.6], [0.7, 0.8, 0.9]];
|
let kernel = [[0.1, 0.2, 0.3], [0.4, 0.5, 0.6], [0.7, 0.8, 0.9]];
|
||||||
|
|||||||
Reference in new issue
Block a user