Compare commits
No commits in common. "ba91baa30255f1fefc3783bf927240851a30f598" and "5492f3d4eb80d119013f378f30cd5aa812773197" have entirely different histories.
ba91baa302
...
5492f3d4eb
4 changed files with 40 additions and 181 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,2 +1 @@
|
||||||
/target
|
/target
|
||||||
*.tga
|
|
||||||
|
|
65
src/main.rs
65
src/main.rs
|
@ -2,68 +2,5 @@
|
||||||
extern crate justerror;
|
extern crate justerror;
|
||||||
|
|
||||||
mod tga;
|
mod tga;
|
||||||
use tga::*;
|
|
||||||
|
|
||||||
mod point;
|
fn main() {}
|
||||||
use point::*;
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
49
src/point.rs
49
src/point.rs
|
@ -1,49 +0,0 @@
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
106
src/tga.rs
106
src/tga.rs
|
@ -6,8 +6,6 @@ use std::{
|
||||||
|
|
||||||
const HEADER_SIZE: usize = 18;
|
const HEADER_SIZE: usize = 18;
|
||||||
|
|
||||||
pub type ColorBuf = [u8; 4];
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||||
#[repr(C, packed)]
|
#[repr(C, packed)]
|
||||||
pub struct TGAHeader {
|
pub struct TGAHeader {
|
||||||
|
@ -27,14 +25,14 @@ pub struct TGAHeader {
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum TGAFormat {
|
pub enum Format {
|
||||||
Grayscale = 1,
|
Grayscale = 1,
|
||||||
RGB = 3,
|
RGB = 3,
|
||||||
#[default]
|
#[default]
|
||||||
RGBA = 4,
|
RGBA = 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<u8> for TGAFormat {
|
impl TryFrom<u8> for Format {
|
||||||
type Error = String;
|
type Error = String;
|
||||||
|
|
||||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||||
|
@ -50,11 +48,11 @@ impl TryFrom<u8> for TGAFormat {
|
||||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct TGAColor {
|
pub struct TGAColor {
|
||||||
pub bgra: ColorBuf,
|
pub bgra: [u8; 4],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for TGAColor {
|
impl Deref for TGAColor {
|
||||||
type Target = ColorBuf;
|
type Target = [u8; 4];
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.bgra
|
&self.bgra
|
||||||
|
@ -67,17 +65,15 @@ impl DerefMut for TGAColor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ColorBuf> for TGAColor {
|
impl From<[u8; 4]> for TGAColor {
|
||||||
fn from(value: ColorBuf) -> Self {
|
fn from(value: [u8; 4]) -> Self {
|
||||||
Self { bgra: value }
|
Self { bgra: value }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TGAColor {
|
impl TGAColor {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self { bgra: [0; 4] }
|
||||||
bgra: ColorBuf::default(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn b(&mut self) -> &mut u8 {
|
pub fn b(&mut self) -> &mut u8 {
|
||||||
|
@ -98,10 +94,10 @@ impl TGAColor {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||||
pub struct TGAImage {
|
struct TGAImage {
|
||||||
pub format: TGAFormat,
|
pub format: Format,
|
||||||
pub width: u32,
|
pub width: u16,
|
||||||
pub height: u32,
|
pub height: u16,
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,13 +128,12 @@ impl From<std::io::Error> for TGAError {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TGAImage {
|
impl TGAImage {
|
||||||
pub fn new(width: u32, height: u32, format: TGAFormat) -> Self {
|
pub fn new(width: u16, height: u16, format: Format) -> Self {
|
||||||
let size = format as usize * (width * height) as usize;
|
|
||||||
Self {
|
Self {
|
||||||
format,
|
format,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
data: vec![0u8; size],
|
data: Vec::with_capacity((format as usize) * (width * height) as usize),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,11 +154,11 @@ impl TGAImage {
|
||||||
}
|
}
|
||||||
self.format = (header.bits_per_pixel >> 3).try_into()?;
|
self.format = (header.bits_per_pixel >> 3).try_into()?;
|
||||||
|
|
||||||
self.width = header.width as u32;
|
self.width = header.width;
|
||||||
self.height = header.height as u32;
|
self.height = header.height;
|
||||||
|
|
||||||
let nbytes = self.format as usize * (self.width * self.height) as usize;
|
let nbytes = self.format as usize * (self.width * self.height) as usize;
|
||||||
let mut image_data = vec![0u8; nbytes];
|
let mut image_data = Vec::with_capacity(nbytes);
|
||||||
match header.datatype_code {
|
match header.datatype_code {
|
||||||
2 | 3 => {
|
2 | 3 => {
|
||||||
file.read_to_end(&mut image_data)?;
|
file.read_to_end(&mut image_data)?;
|
||||||
|
@ -180,16 +175,26 @@ impl TGAImage {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_file(&mut self, file: &str, do_vflip: bool, do_rle: bool) -> IoResult {
|
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 file = std::fs::File::create(file)?;
|
||||||
|
|
||||||
let mut header = TGAHeader::default();
|
let mut header = TGAHeader::default();
|
||||||
header.bits_per_pixel = self.bytes_per_pixel() << 3;
|
header.bits_per_pixel = self.bytes_per_pixel() << 3;
|
||||||
header.width = self.width as u16;
|
header.width = self.width;
|
||||||
header.height = self.height as u16;
|
header.height = self.height;
|
||||||
|
|
||||||
header.datatype_code = match self.format {
|
header.datatype_code = match self.format {
|
||||||
TGAFormat::Grayscale if do_rle => 11,
|
Format::Grayscale if do_rle => 11,
|
||||||
TGAFormat::Grayscale => 3,
|
Format::Grayscale => 3,
|
||||||
_ if do_rle => 10,
|
_ if do_rle => 10,
|
||||||
_ => 2,
|
_ => 2,
|
||||||
};
|
};
|
||||||
|
@ -206,18 +211,6 @@ impl TGAImage {
|
||||||
file.write_all(&mut self.data)?;
|
file.write_all(&mut self.data)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
file.flush()?;
|
|
||||||
|
|
||||||
let mut developer_area = ColorBuf::default();
|
|
||||||
let mut extension_area = ColorBuf::default();
|
|
||||||
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 developer_area)?;
|
||||||
file.write_all(&mut extension_area)?;
|
file.write_all(&mut extension_area)?;
|
||||||
file.write_all(&mut footer)?;
|
file.write_all(&mut footer)?;
|
||||||
|
@ -235,31 +228,12 @@ impl TGAImage {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set(&mut self, x: u32, y: u32, color: TGAColor) {
|
pub fn set(&mut self, x: u32, y: u32, color: &TGAColor) {
|
||||||
if self.data.is_empty() || x > self.width || y > self.height {
|
todo!()
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let bpp = self.bytes_per_pixel() as usize;
|
|
||||||
let start = (x + y * self.width) as usize * bpp;
|
|
||||||
let end = start + bpp;
|
|
||||||
self.data[start..end].copy_from_slice(&color[0..bpp]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&self, x: u32, y: u32) -> Option<TGAColor> {
|
pub fn get(&self, x: u32, y: u32) -> Option<TGAColor> {
|
||||||
if self.data.is_empty() || x >= self.width || y >= self.height {
|
todo!()
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let bpp = self.bytes_per_pixel() as usize;
|
|
||||||
|
|
||||||
let mut color = TGAColor::new();
|
|
||||||
let start = bpp * (x + y * self.width) as usize;
|
|
||||||
let end = start + bpp;
|
|
||||||
let data = &self.data[start..end];
|
|
||||||
color[0..bpp].copy_from_slice(data);
|
|
||||||
|
|
||||||
Some(color)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bytes_per_pixel(&self) -> u8 {
|
pub fn bytes_per_pixel(&self) -> u8 {
|
||||||
|
@ -322,7 +296,7 @@ impl TGAImage {
|
||||||
let mut current_byte = current_pixel * bpp;
|
let mut current_byte = current_pixel * bpp;
|
||||||
|
|
||||||
let mut run_length = 1;
|
let mut run_length = 1;
|
||||||
let mut is_raw = true;
|
let mut raw = true;
|
||||||
|
|
||||||
while (current_pixel + run_length) < num_pixels && run_length < max_chunk_len {
|
while (current_pixel + run_length) < num_pixels && run_length < max_chunk_len {
|
||||||
let mut succ_eq = true;
|
let mut succ_eq = true;
|
||||||
|
@ -333,31 +307,29 @@ impl TGAImage {
|
||||||
}
|
}
|
||||||
current_byte += bpp;
|
current_byte += bpp;
|
||||||
if 1 == run_length {
|
if 1 == run_length {
|
||||||
is_raw = !succ_eq;
|
raw = !succ_eq;
|
||||||
}
|
}
|
||||||
if is_raw && succ_eq {
|
if raw && succ_eq {
|
||||||
run_length -= 1;
|
run_length -= 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if !(is_raw || succ_eq) {
|
if !(raw || succ_eq) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
run_length += 1;
|
run_length += 1;
|
||||||
}
|
}
|
||||||
current_pixel += run_length;
|
current_pixel += run_length;
|
||||||
let mut out = if is_raw {
|
let mut out = if raw {
|
||||||
[run_length as u8 - 1]
|
[run_length as u8 - 1]
|
||||||
} else {
|
} else {
|
||||||
[run_length as u8 + 127]
|
[run_length as u8 + 127]
|
||||||
};
|
};
|
||||||
|
|
||||||
file.write_all(&mut out)?;
|
file.write_all(&mut out)?;
|
||||||
let chunk_end = chunk_start + if is_raw { run_length * bpp } else { bpp };
|
let chunk_end = if raw { run_length * bpp } else { bpp };
|
||||||
file.write_all(&mut self.data[chunk_start..chunk_end])?;
|
file.write_all(&mut self.data[chunk_start..chunk_end])?;
|
||||||
}
|
}
|
||||||
|
|
||||||
file.flush()?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue