commit 7d190cec8888ba06eb2f39dad0d4c558e8d3ae0a Author: Joe Ardent Date: Thu Sep 4 16:27:13 2025 -0700 almost done with tga stuff diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100644 index 0000000..8feb93e --- /dev/null +++ b/.rustfmt.toml @@ -0,0 +1,4 @@ +imports_granularity = "Crate" +group_imports = "StdExternalCrate" +wrap_comments = true +edition = "2024" diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..e704976 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "tinyrender" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..38d6838 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "tinyrender" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..e7a11a9 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/src/tga.rs b/src/tga.rs new file mode 100644 index 0000000..0421018 --- /dev/null +++ b/src/tga.rs @@ -0,0 +1,151 @@ +use std::{ + fs::File, + io::Read, + ops::{Deref, DerefMut}, +}; + +#[derive(Debug, Default, Clone, PartialEq, Eq)] +#[repr(C, packed)] +pub struct TGAHeader { + pub id_length: u8, + pub colormap_type: u8, + pub datatype_code: u8, + pub colormap_origin: u16, + pub colormap_length: u16, + pub colormap_depth: u8, + pub x_origin: u16, + pub y_origin: u16, + pub width: u16, + pub height: u16, + pub bits_per_pixel: u8, + pub image_descriptor: u8, +} + +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +#[repr(u8)] +pub enum Format { + Grayscale = 1, + RGB = 3, + #[default] + RGBA = 4, +} + +impl TryFrom for Format { + type Error = String; + + fn try_from(value: u8) -> Result { + match value { + 1 => Ok(Self::Grayscale), + 3 => Ok(Self::RGB), + 4 => Ok(Self::RGBA), + _ => Err(format!("could not convert {value} into a Format")), + } + } +} + +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +#[repr(transparent)] +pub struct TGAColor { + pub bgra: [u8; 4], +} + +impl Deref for TGAColor { + type Target = [u8; 4]; + + fn deref(&self) -> &Self::Target { + &self.bgra + } +} + +impl DerefMut for TGAColor { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.bgra + } +} + +impl TGAColor { + pub fn new() -> Self { + Self { bgra: [0; 4] } + } +} + +#[derive(Debug, Default, Clone, PartialEq, Eq)] +struct TGAImage { + pub format: Format, + pub width: u32, + pub height: u32, + data: Vec, +} + +pub type IoResult = std::io::Result<()>; + +impl TGAImage { + pub fn new(width: u32, height: u32, format: Format) -> Self { + Self { + format, + width, + height, + data: Vec::with_capacity((format as usize) * (width * height) as usize), + } + } + + pub fn bytes_per_pixel(&self) -> u8 { + self.format as u8 + } + + pub fn read_file(&mut self, file: &str) -> IoResult { + let mut file = std::fs::File::open(file)?; + let header_size = size_of::(); + assert_eq!(header_size, 18); + let mut bytes = [0u8; header_size]; + file.read_exact(&mut bytes)?; + let header: TGAHeader = unsafe { std::mem::transmute(bytes) }; + + if header.width < 1 || header.height < 1 { + return Err("bad image metadata".into()); + } + self.format = (header.bits_per_pixel >> 3).try_into()?; + + self.width = header.width; + self.height = header.height; + + let nbytes = self.format as usize * (self.width * self.height) as usize; + let mut image_data = Vec::with_capacity(nbytes); + match header.datatype_code { + 2 | 3 => { + file.read_to_end(&mut image_data)?; + self.data = image_data; + } + 10 | 11 => { + self.read_rle_file(&mut file)?; + } + _ => { + return Err("invalid TGA file".into()); + } + } + } + + pub fn write_file(&self, file: &str) -> IoResult { + todo!() + } + + pub fn flip_horizontal(&mut self) { + todo!() + } + + pub fn flip_vertical(&mut self) { + todo!() + } + + pub fn set(&mut self, x: u32, y: u32, color: &TGAColor) { + todo!() + } + + pub fn get(&self, x: u32, y: u32) -> Option { + todo!() + } + + fn read_rle_file(&mut self, file: &mut File) -> IoResult { + todo!() + } +}