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, } 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

(&self, path: P) -> Result<(), CanvasError> where P: AsRef, { 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 = 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); } }