99 lines
2.5 KiB
Rust
99 lines
2.5 KiB
Rust
use std::fs::File;
|
|
use std::io::BufWriter;
|
|
use std::path::Path;
|
|
|
|
use png;
|
|
use thiserror::Error;
|
|
|
|
use crate::tuples::Color;
|
|
|
|
#[derive(Error, Debug)]
|
|
pub enum CanvasError {
|
|
#[error("faile to write canvas")]
|
|
IOError(#[from] std::io::Error),
|
|
#[error("faile to encode canvas to png")]
|
|
EncodingError(#[from] png::EncodingError),
|
|
}
|
|
pub struct Canvas {
|
|
pub height: usize,
|
|
pub width: usize,
|
|
pub pixels: Vec<Color>,
|
|
}
|
|
|
|
impl Canvas {
|
|
pub fn new(width: usize, height: usize, background: Color) -> Canvas {
|
|
let pixels = vec![background; width * height];
|
|
Canvas {
|
|
width,
|
|
height,
|
|
pixels,
|
|
}
|
|
}
|
|
|
|
pub fn set(&mut self, x: usize, y: usize, c: Color) {
|
|
if x > self.width {
|
|
return;
|
|
}
|
|
if y > self.height {
|
|
return;
|
|
}
|
|
self.pixels[x + y * self.width] = c;
|
|
}
|
|
pub fn get(&self, x: usize, y: usize) -> Color {
|
|
self.pixels[x + y * self.width]
|
|
}
|
|
pub fn write_to_file<P>(&self, path: P) -> Result<(), CanvasError>
|
|
where
|
|
P: AsRef<Path>,
|
|
{
|
|
let path = Path::new(path.as_ref());
|
|
let file = File::create(path)?;
|
|
let ref mut w = BufWriter::new(file);
|
|
|
|
let mut encoder = png::Encoder::new(w, self.width as u32, self.height as u32);
|
|
encoder.set_color(png::ColorType::RGB);
|
|
encoder.set_depth(png::BitDepth::Eight);
|
|
let mut writer = encoder.write_header()?;
|
|
|
|
let data: Vec<u8> = self
|
|
.pixels
|
|
.iter()
|
|
.flat_map(|p| {
|
|
vec![
|
|
(p.red.clamp(0., 1.) * 255.) as u8,
|
|
(p.green.clamp(0., 1.) * 255.) as u8,
|
|
(p.blue.clamp(0., 1.) * 255.) as u8,
|
|
]
|
|
})
|
|
.collect();
|
|
writer.write_image_data(&data)?;
|
|
Ok(())
|
|
}
|
|
}
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::Canvas;
|
|
|
|
use crate::{tuples::Color, BLACK};
|
|
|
|
#[test]
|
|
fn create_canvas() {
|
|
let bg = BLACK;
|
|
let c = Canvas::new(10, 20, bg);
|
|
assert_eq!(c.width, 10);
|
|
assert_eq!(c.height, 20);
|
|
for (i, p) in c.pixels.iter().enumerate() {
|
|
assert_eq!(p, &BLACK, "pixel {} not {:?}: {:?}", i, &BLACK, p);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn write_to_canvas() {
|
|
let bg = Color::new(0.2, 0.2, 0.2);
|
|
let mut c = Canvas::new(10, 20, bg);
|
|
let red = Color::new(1., 0., 0.);
|
|
c.set(2, 3, red);
|
|
assert_eq!(c.get(2, 3), red);
|
|
}
|
|
}
|