98 lines
2.4 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) -> Canvas {
let pixels = vec![Color::new(0., 0., 0.); 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(&mut 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;
#[test]
fn create_canvas() {
let c = Canvas::new(10, 20);
assert_eq!(c.width, 10);
assert_eq!(c.height, 20);
let black = Color::new(0., 0., 0.);
for (i, p) in c.pixels.iter().enumerate() {
assert_eq!(p, &black, "pixel {} not {:?}: {:?}", i, &black, p);
}
}
#[test]
fn write_to_canvas() {
let mut c = Canvas::new(10, 20);
let red = Color::new(1., 0., 0.);
c.set(2, 3, red);
assert_eq!(c.get(2, 3), red);
}
}