can write and read RLE TGA files
This commit is contained in:
parent
5492f3d4eb
commit
ea3c4e89e0
2 changed files with 95 additions and 25 deletions
45
src/main.rs
45
src/main.rs
|
@ -3,4 +3,47 @@ extern crate justerror;
|
|||
|
||||
mod tga;
|
||||
|
||||
fn main() {}
|
||||
use tga::*;
|
||||
|
||||
const WHITE: TGAColor = TGAColor {
|
||||
bgra: [255, 255, 255, 255],
|
||||
};
|
||||
|
||||
const GREEN: TGAColor = TGAColor {
|
||||
bgra: [0, 255, 0, 255],
|
||||
};
|
||||
|
||||
const RED: TGAColor = TGAColor {
|
||||
bgra: [0, 0, 255, 255],
|
||||
};
|
||||
|
||||
const BLUE: TGAColor = TGAColor {
|
||||
bgra: [255, 128, 64, 255],
|
||||
};
|
||||
|
||||
const YELLOW: TGAColor = TGAColor {
|
||||
bgra: [0, 200, 255, 255],
|
||||
};
|
||||
|
||||
fn main() {
|
||||
let w = 64;
|
||||
let h = 64;
|
||||
let mut framebuffer = TGAImage::new(w, h, TGAFormat::RGB);
|
||||
|
||||
let ax = 7;
|
||||
let ay = 3;
|
||||
let bx = 12;
|
||||
let by = 37;
|
||||
let cx = 62;
|
||||
let cy = 53;
|
||||
|
||||
framebuffer.set(ax, ay, &WHITE);
|
||||
framebuffer.set(bx, by, &WHITE);
|
||||
framebuffer.set(cx, cy, &WHITE);
|
||||
|
||||
framebuffer
|
||||
.write_file("framebuffer.tga", true, true)
|
||||
.unwrap();
|
||||
|
||||
let new = TGAImage::from_file("framebuffer.tga").unwrap();
|
||||
}
|
||||
|
|
75
src/tga.rs
75
src/tga.rs
|
@ -25,14 +25,14 @@ pub struct TGAHeader {
|
|||
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
||||
#[repr(u8)]
|
||||
pub enum Format {
|
||||
pub enum TGAFormat {
|
||||
Grayscale = 1,
|
||||
RGB = 3,
|
||||
#[default]
|
||||
RGBA = 4,
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for Format {
|
||||
impl TryFrom<u8> for TGAFormat {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
|
@ -94,8 +94,8 @@ impl TGAColor {
|
|||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||
struct TGAImage {
|
||||
pub format: Format,
|
||||
pub struct TGAImage {
|
||||
pub format: TGAFormat,
|
||||
pub width: u16,
|
||||
pub height: u16,
|
||||
data: Vec<u8>,
|
||||
|
@ -128,12 +128,13 @@ impl From<std::io::Error> for TGAError {
|
|||
}
|
||||
|
||||
impl TGAImage {
|
||||
pub fn new(width: u16, height: u16, format: Format) -> Self {
|
||||
pub fn new(width: u16, height: u16, format: TGAFormat) -> Self {
|
||||
let size = format as usize * (width * height) as usize;
|
||||
Self {
|
||||
format,
|
||||
width,
|
||||
height,
|
||||
data: Vec::with_capacity((format as usize) * (width * height) as usize),
|
||||
data: vec![0u8; size],
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -158,7 +159,7 @@ impl TGAImage {
|
|||
self.height = header.height;
|
||||
|
||||
let nbytes = self.format as usize * (self.width * self.height) as usize;
|
||||
let mut image_data = Vec::with_capacity(nbytes);
|
||||
let mut image_data = vec![0u8; nbytes];
|
||||
match header.datatype_code {
|
||||
2 | 3 => {
|
||||
file.read_to_end(&mut image_data)?;
|
||||
|
@ -175,16 +176,6 @@ impl TGAImage {
|
|||
}
|
||||
|
||||
pub fn write_file(&mut self, file: &str, do_vflip: bool, do_rle: bool) -> IoResult {
|
||||
let mut developer_area = [0u8; 4];
|
||||
let mut extension_area = [0u8; 4];
|
||||
let mut footer: Vec<u8> = vec![
|
||||
'T', 'R', 'U', 'E', 'V', 'I', 'S', 'I', 'O', 'N', '-', 'X', 'F', 'I', 'L', 'E', '.',
|
||||
'\0',
|
||||
]
|
||||
.into_iter()
|
||||
.map(|b| b as u8)
|
||||
.collect();
|
||||
|
||||
let mut file = std::fs::File::create(file)?;
|
||||
|
||||
let mut header = TGAHeader::default();
|
||||
|
@ -193,8 +184,8 @@ impl TGAImage {
|
|||
header.height = self.height;
|
||||
|
||||
header.datatype_code = match self.format {
|
||||
Format::Grayscale if do_rle => 11,
|
||||
Format::Grayscale => 3,
|
||||
TGAFormat::Grayscale if do_rle => 11,
|
||||
TGAFormat::Grayscale => 3,
|
||||
_ if do_rle => 10,
|
||||
_ => 2,
|
||||
};
|
||||
|
@ -211,6 +202,18 @@ impl TGAImage {
|
|||
file.write_all(&mut self.data)?;
|
||||
}
|
||||
|
||||
file.flush()?;
|
||||
|
||||
let mut developer_area = [0u8; 4];
|
||||
let mut extension_area = [0u8; 4];
|
||||
let mut footer: Vec<u8> = vec![
|
||||
'T', 'R', 'U', 'E', 'V', 'I', 'S', 'I', 'O', 'N', '-', 'X', 'F', 'I', 'L', 'E', '.',
|
||||
'\0',
|
||||
]
|
||||
.into_iter()
|
||||
.map(|b| b as u8)
|
||||
.collect();
|
||||
|
||||
file.write_all(&mut developer_area)?;
|
||||
file.write_all(&mut extension_area)?;
|
||||
file.write_all(&mut footer)?;
|
||||
|
@ -228,12 +231,34 @@ impl TGAImage {
|
|||
todo!()
|
||||
}
|
||||
|
||||
pub fn set(&mut self, x: u32, y: u32, color: &TGAColor) {
|
||||
todo!()
|
||||
pub fn set(&mut self, x: u16, y: u16, color: &TGAColor) {
|
||||
if self.data.is_empty() || x > self.width || y > self.height {
|
||||
return;
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, x: u32, y: u32) -> Option<TGAColor> {
|
||||
todo!()
|
||||
pub fn get(&self, x: u16, y: u16) -> Option<TGAColor> {
|
||||
if self.data.is_empty() || x >= self.width || y >= self.height {
|
||||
return None;
|
||||
}
|
||||
|
||||
let bpp = self.bytes_per_pixel() as usize;
|
||||
|
||||
let mut color = TGAColor::new();
|
||||
let start = (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];
|
||||
}
|
||||
Some(color)
|
||||
}
|
||||
|
||||
pub fn bytes_per_pixel(&self) -> u8 {
|
||||
|
@ -326,10 +351,12 @@ impl TGAImage {
|
|||
};
|
||||
|
||||
file.write_all(&mut out)?;
|
||||
let chunk_end = if raw { run_length * bpp } else { bpp };
|
||||
let chunk_end = chunk_start + if raw { run_length * bpp } else { bpp };
|
||||
file.write_all(&mut self.data[chunk_start..chunk_end])?;
|
||||
}
|
||||
|
||||
file.flush()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue