Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions converter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,9 @@ edition = "2021"

[dependencies]
image = "0.24.7"
ffmpeg = { package = "ffmpeg-next", version = "6.0.0"}
rayon = "1.8.0"

[features]
simd_if_available = []
default = ["simd_if_available"]
64 changes: 59 additions & 5 deletions converter/src/char.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use std::collections::HashSet;
use std::time::Instant;

use image::{GenericImageView, Rgb, RgbImage};

use crate::constants::*;
use crate::image::Chunk;

#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct VGAChar {
char: u8,
foreground: u8,
Expand Down Expand Up @@ -32,23 +34,74 @@ impl VGAChar {
}
}

pub fn generate_lookup_table() -> [(Self, RgbImage); POSSIBLE_CHARS] {
pub fn generate_lookup_table() -> Box<[(Self, Chunk)]> {
let now = Instant::now();
let mut table: Vec<(Self, RgbImage)> = Vec::with_capacity(POSSIBLE_CHARS);
let mut table = Vec::with_capacity(POSSIBLE_CHARS);

for char in 0..=255 {
for foreground in 0..FOREGROUND.len() as u8 {
for background in 0..BACKGROUND.len() as u8 {
let char = Self::new(char, foreground, background);
let render = char.render();
let chunk = Chunk::new(render.view(0, 0, CHAR_WIDTH, CHAR_HEIGHT));

table.push((char, render))
table.push((char, chunk.clone()));
}
}
}

println!("Generating lookup table took {:?}", now.elapsed());
table.try_into().unwrap()
println!("Lookup Table Entries: {}", table.len());
table.into_boxed_slice()
}

// TODO dedup
pub fn generate_bitmaps() -> Box<
[(
u32,
u32,
[[bool; CHAR_WIDTH as usize]; CHAR_HEIGHT as usize],
); 236],
> {
let mut maps = HashSet::new();

for char in 0..=255u32 {
let x = char % 32; // there's 32 chars each row
let y = char / 32;

let bitmap = CODEPAGE_737
.view(x * CHAR_WIDTH, y * CHAR_HEIGHT, CHAR_WIDTH, CHAR_HEIGHT)
.to_image();

let mut map = [[false; CHAR_WIDTH as usize]; CHAR_HEIGHT as usize];

let mut fg_count = 0;
let mut bg_count = 0;

for x in 0..CHAR_WIDTH {
for y in 0..CHAR_HEIGHT {
let flipped = match bitmap.get_pixel(x, y).0 {
[170, 170, 170] => {
fg_count += 1;
true
}
[0, 0, 0] => {
bg_count += 1;
false
}
_ => panic!(),
};

map[y as usize][x as usize] = flipped;
}
}

maps.insert((fg_count, bg_count, map));
}

let maps = maps.into_iter().collect::<Vec<_>>();

maps.into_boxed_slice().try_into().unwrap()
}

#[allow(dead_code)] // TODO
Expand Down Expand Up @@ -81,6 +134,7 @@ impl VGAChar {
image
}

// TODO remove
fn get_bitmap(&self) -> RgbImage {
let x = self.char as u32 % 32; // there's 32 chars each row
let y = self.char as u32 / 32;
Expand Down
50 changes: 47 additions & 3 deletions converter/src/constants.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
use std::collections::HashMap;
use std::io::Cursor;
use std::sync::LazyLock;
use std::sync::{LazyLock, Mutex};

use image::ImageFormat::Png;
use image::io::Reader;
use image::ImageFormat::Png;
use image::RgbImage;

use crate::char::VGAChar;
use crate::image::Chunk;

pub const CHAR_WIDTH: u32 = 9;
pub const CHAR_HEIGHT: u32 = 16;
Expand Down Expand Up @@ -57,4 +59,46 @@ pub static CODEPAGE_737: LazyLock<RgbImage> = LazyLock::new(|| {
.into_rgb8()
});

pub static VGACHAR_LOOKUP: LazyLock<[(VGAChar, RgbImage); POSSIBLE_CHARS]> = LazyLock::new(VGAChar::generate_lookup_table);
pub static VGACHAR_LOOKUP: LazyLock<Box<[(VGAChar, Chunk)]>> =
LazyLock::new(VGAChar::generate_lookup_table);
pub static BITMAPS: LazyLock<
Box<
[(
u32,
u32,
[[bool; CHAR_WIDTH as usize]; CHAR_HEIGHT as usize],
); 236],
>,
> = LazyLock::new(VGAChar::generate_bitmaps);

pub static DYNAMIC_CACHE: LazyLock<Mutex<HashMap<Chunk, VGAChar>>> =
LazyLock::new(|| Mutex::new(HashMap::new()));

pub static FIXED_CACHE: LazyLock<HashMap<Chunk, VGAChar>> = LazyLock::new(|| {
let mut map = HashMap::with_capacity(VGACHAR_LOOKUP.len());
for (char, chunk) in VGACHAR_LOOKUP.iter() {
map.insert(chunk.clone(), *char);
}
map
});

#[cfg(test)]
mod tests {
use crate::constants::VGACHAR_LOOKUP;

#[test]
fn lookup_index() {
for (i, (v, _)) in VGACHAR_LOOKUP.iter().enumerate() {
assert_eq!(i, v.lookup_index());
}
}

#[test]
fn best_char_lookup() {
for (i, (v, x)) in VGACHAR_LOOKUP.iter().enumerate() {
let best = x.get_best_char();
let best = &VGACHAR_LOOKUP[best.lookup_index()].1;
assert_eq!(x, best);
}
}
}
Loading