diff --git a/src/main.rs b/src/main.rs index 41969ec..9bfb53a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,9 +2,11 @@ extern crate justerror; mod tga; - use tga::*; +mod point; +use point::*; + const WHITE: TGAColor = TGAColor { bgra: [255, 255, 255, 255], }; @@ -37,13 +39,31 @@ fn main() { let cx = 62; let cy = 53; - framebuffer.set(ax, ay, &WHITE); - framebuffer.set(bx, by, &WHITE); - framebuffer.set(cx, cy, &WHITE); + framebuffer.set(ax, ay, WHITE); + framebuffer.set(bx, by, WHITE); + framebuffer.set(cx, cy, WHITE); + + let a = Point::new(ax, ay); + let b = Point::new(bx, by); + let c = Point::new(cx, cy); + + line(a, b, &mut framebuffer, BLUE); + line(c, b, &mut framebuffer, GREEN); + line(c, a, &mut framebuffer, YELLOW); + line(a, c, &mut framebuffer, RED); framebuffer .write_file("framebuffer.tga", true, true) .unwrap(); - - let new = TGAImage::from_file("framebuffer.tga").unwrap(); +} + +fn line(a: Point, b: Point, fb: &mut TGAImage, color: TGAColor) { + let mut t = 0.0; + let step = 0.02; + while t < 1.0 { + let x = (a.x as f32 + (b.x - a.x) as f32 * t).round_ties_even() as u32; + let y = (a.y as f32 + (b.y - a.y) as f32 * t).round_ties_even() as u32; + fb.set(x, y, color); + t += step; + } } diff --git a/src/point.rs b/src/point.rs new file mode 100644 index 0000000..7c51252 --- /dev/null +++ b/src/point.rs @@ -0,0 +1,49 @@ +use std::ops::{Add, Mul, Sub}; + +#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct Point { + pub x: i32, + pub y: i32, +} + +impl Point { + pub fn new(x: u32, y: u32) -> Self { + Self { + x: x as i32, + y: y as i32, + } + } +} + +impl Add for Point { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + Self { + x: self.x + rhs.x, + y: self.y + rhs.y, + } + } +} + +impl Sub for Point { + type Output = Self; + + fn sub(self, rhs: Self) -> Self::Output { + Self { + x: self.x.saturating_sub(rhs.x), + y: self.y.saturating_sub(rhs.y), + } + } +} + +impl Mul for Point { + type Output = Self; + + fn mul(self, rhs: Self) -> Self::Output { + Self { + x: self.x * rhs.x, + y: self.y * rhs.y, + } + } +} diff --git a/src/tga.rs b/src/tga.rs index 09f78c7..b73ea36 100644 --- a/src/tga.rs +++ b/src/tga.rs @@ -6,6 +6,8 @@ use std::{ const HEADER_SIZE: usize = 18; +pub type ColorBuf = [u8; 4]; + #[derive(Debug, Default, Clone, PartialEq, Eq)] #[repr(C, packed)] pub struct TGAHeader { @@ -48,11 +50,11 @@ impl TryFrom for TGAFormat { #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] #[repr(transparent)] pub struct TGAColor { - pub bgra: [u8; 4], + pub bgra: ColorBuf, } impl Deref for TGAColor { - type Target = [u8; 4]; + type Target = ColorBuf; fn deref(&self) -> &Self::Target { &self.bgra @@ -65,15 +67,17 @@ impl DerefMut for TGAColor { } } -impl From<[u8; 4]> for TGAColor { - fn from(value: [u8; 4]) -> Self { +impl From for TGAColor { + fn from(value: ColorBuf) -> Self { Self { bgra: value } } } impl TGAColor { pub fn new() -> Self { - Self { bgra: [0; 4] } + Self { + bgra: ColorBuf::default(), + } } pub fn b(&mut self) -> &mut u8 { @@ -96,8 +100,8 @@ impl TGAColor { #[derive(Debug, Default, Clone, PartialEq, Eq)] pub struct TGAImage { pub format: TGAFormat, - pub width: u16, - pub height: u16, + pub width: u32, + pub height: u32, data: Vec, } @@ -128,7 +132,7 @@ impl From for TGAError { } impl TGAImage { - pub fn new(width: u16, height: u16, format: TGAFormat) -> Self { + pub fn new(width: u32, height: u32, format: TGAFormat) -> Self { let size = format as usize * (width * height) as usize; Self { format, @@ -155,8 +159,8 @@ impl TGAImage { } self.format = (header.bits_per_pixel >> 3).try_into()?; - self.width = header.width; - self.height = header.height; + self.width = header.width as u32; + self.height = header.height as u32; let nbytes = self.format as usize * (self.width * self.height) as usize; let mut image_data = vec![0u8; nbytes]; @@ -180,8 +184,8 @@ impl TGAImage { let mut header = TGAHeader::default(); header.bits_per_pixel = self.bytes_per_pixel() << 3; - header.width = self.width; - header.height = self.height; + header.width = self.width as u16; + header.height = self.height as u16; header.datatype_code = match self.format { TGAFormat::Grayscale if do_rle => 11, @@ -204,8 +208,8 @@ impl TGAImage { file.flush()?; - let mut developer_area = [0u8; 4]; - let mut extension_area = [0u8; 4]; + let mut developer_area = ColorBuf::default(); + let mut extension_area = ColorBuf::default(); let mut footer: Vec = vec![ 'T', 'R', 'U', 'E', 'V', 'I', 'S', 'I', 'O', 'N', '-', 'X', 'F', 'I', 'L', 'E', '.', '\0', @@ -231,7 +235,7 @@ impl TGAImage { todo!() } - pub fn set(&mut self, x: u16, y: u16, color: &TGAColor) { + pub fn set(&mut self, x: u32, y: u32, color: TGAColor) { if self.data.is_empty() || x > self.width || y > self.height { return; } @@ -239,12 +243,10 @@ impl TGAImage { let bpp = self.bytes_per_pixel() as usize; let start = (x + y * self.width) as usize * bpp; let end = start + bpp; - for (i, b) in &mut self.data[start..end].iter_mut().enumerate() { - *b = color[i]; - } + self.data[start..end].copy_from_slice(&color[0..bpp]); } - pub fn get(&self, x: u16, y: u16) -> Option { + pub fn get(&self, x: u32, y: u32) -> Option { if self.data.is_empty() || x >= self.width || y >= self.height { return None; } @@ -252,12 +254,11 @@ impl TGAImage { let bpp = self.bytes_per_pixel() as usize; let mut color = TGAColor::new(); - let start = (x + y * self.width) as usize; + let start = bpp * (x + y * self.width) as usize; let end = start + bpp; let data = &self.data[start..end]; - for i in 0..bpp { - color[i] = data[i]; - } + color[0..bpp].copy_from_slice(data); + Some(color) }