use std::fs::File;
use std::io::BufWriter;
use std::io::prelude::*;
use std::path::Path;
use png;
#[path = "../char_offset.rs"]
mod char_offset;
use char_offset::{CHARS_PER_ROW, char_offset_impl};
fn get_bit(data: &[u8], bit_index: usize) -> u8 {
let byte = data[bit_index / 8];
let shift = 7 - (bit_index % 8);
(byte >> shift) & 1
}
fn set_bit(data: &mut [u8], bit_index: usize, value: u8) {
let byte = &mut data[bit_index / 8];
let shift = 7 - (bit_index % 8);
if value == 1 {
*byte |= 1 << shift;
} else {
*byte &= !(1 << shift);
}
}
fn save_png(filename: &str, image: &[u8], width: usize, height: usize) -> std::io::Result<()> {
let path = Path::new(filename);
let file = File::create(path)?;
let ref mut w = BufWriter::new(file);
let mut encoder = png::Encoder::new(w, width as u32, height as u32);
encoder.set_color(png::ColorType::Grayscale);
encoder.set_depth(png::BitDepth::One);
let mut writer = encoder.write_header()?;
writer.write_image_data(&image)?;
Ok(())
}
fn save_raw(filename: &str, font_input: &[u8]) -> std::io::Result<()> {
let path = Path::new(filename);
let mut file = File::create(path)?;
file.write_all(font_input)?;
Ok(())
}
fn glyph_copy(
output: &mut [u8],
input: &[u8],
input_index: usize,
input_width_len: usize,
input_height_len: usize,
) {
let (x, y) = (input_index % CHARS_PER_ROW, input_index / CHARS_PER_ROW);
for j in 0..input_height_len {
let line_len = CHARS_PER_ROW * input_width_len;
let out_index = y * line_len * input_height_len + j * line_len + x * input_width_len;
let in_index = j * input_width_len;
for i in 0..input_width_len {
output[out_index + i] = input[in_index + i];
}
}
}
fn extract_9x14(
characters: &Vec<char>,
ibm437_src: &[u8; 8192],
base_dir: &str,
) -> std::io::Result<()> {
const INPUT_WIDTH_LEN: usize = 2;
const INPUT_HEIGHT_LEN: usize = 14;
let mut font_output: [u8; INPUT_WIDTH_LEN
* CHARS_PER_ROW
* INPUT_HEIGHT_LEN
* (256 / CHARS_PER_ROW)] =
[0; INPUT_WIDTH_LEN * CHARS_PER_ROW * INPUT_HEIGHT_LEN * (256 / CHARS_PER_ROW)];
for (i, chr) in characters.iter().enumerate() {
let top = &ibm437_src[8 * i..8 * (i + 1)];
let bottom = &ibm437_src[0x0800 + 8 * i..0x0800 + 8 * (i + 1) - 2];
let mut glyph: [u8; INPUT_WIDTH_LEN * INPUT_HEIGHT_LEN] =
[0; INPUT_WIDTH_LEN * INPUT_HEIGHT_LEN];
for (j, e) in top.iter().chain(bottom.iter()).enumerate() {
let c = match i & 0b1110_0000 {
0b1100_0000 => (*e & 0b0000_0001) << 7,
_ => 0b0000_0000,
};
glyph[2 * j] = *e;
glyph[2 * j + 1] = c;
}
glyph_copy(
&mut font_output,
&glyph,
char_offset_impl(*chr),
INPUT_WIDTH_LEN,
INPUT_HEIGHT_LEN,
);
}
let mut packed = [0u8; 9 * 14 * 256 / 8];
for y in 0..(256 / CHARS_PER_ROW) {
for x in 0..CHARS_PER_ROW {
let seek_src_bits_index = y * 16 * CHARS_PER_ROW * 14 + x * 16;
let seek_dst_bits_index = y * 9 * CHARS_PER_ROW * 14 + x * 9;
for gy in 0..14 {
for gx in 0..9 {
let src = get_bit(
&font_output,
seek_src_bits_index + gx + gy * CHARS_PER_ROW * 16,
);
let iout = seek_dst_bits_index + gx + gy * CHARS_PER_ROW * 9;
set_bit(&mut packed, iout, src);
}
}
}
}
save_raw(
&format!("{}/src/fonts/ibm437_font_9_14_regular.raw", base_dir),
&packed,
)?;
save_png(
&format!("{}/doc/ibm437_font_9_14_regular.png", base_dir),
&packed,
9 * CHARS_PER_ROW,
14 * (256 / CHARS_PER_ROW),
)?;
Ok(())
}
fn extract_8x8_regular(
characters: &Vec<char>,
ibm437_src: &[u8; 8192],
base_dir: &str,
) -> std::io::Result<()> {
const INPUT_WIDTH_LEN: usize = 1;
const INPUT_HEIGHT_LEN: usize = 8;
let mut font_output: [u8; INPUT_WIDTH_LEN
* CHARS_PER_ROW
* INPUT_HEIGHT_LEN
* (256 / CHARS_PER_ROW)] =
[0; INPUT_WIDTH_LEN * CHARS_PER_ROW * INPUT_HEIGHT_LEN * (256 / CHARS_PER_ROW)];
for (i, chr) in characters.iter().enumerate() {
let glyph = &ibm437_src[0x1000 + 8 * i..0x1000 + 8 * (i + 1)];
glyph_copy(
&mut font_output,
&glyph,
char_offset_impl(*chr),
INPUT_WIDTH_LEN,
INPUT_HEIGHT_LEN,
);
}
save_raw(
&format!("{}/src/fonts/ibm437_font_8_8_regular.raw", base_dir),
&font_output,
)?;
save_png(
&format!("{}/doc/ibm437_font_8_8_regular.png", base_dir),
&font_output,
8 * CHARS_PER_ROW,
8 * (256 / CHARS_PER_ROW),
)?;
Ok(())
}
fn extract_8x8_bold(
characters: &Vec<char>,
ibm437_src: &[u8; 8192],
base_dir: &str,
) -> std::io::Result<()> {
const INPUT_WIDTH_LEN: usize = 1;
const INPUT_HEIGHT_LEN: usize = 8;
let mut font_output: [u8; INPUT_WIDTH_LEN
* CHARS_PER_ROW
* INPUT_HEIGHT_LEN
* (256 / CHARS_PER_ROW)] =
[0; INPUT_WIDTH_LEN * CHARS_PER_ROW * INPUT_HEIGHT_LEN * (256 / CHARS_PER_ROW)];
for (i, chr) in characters.iter().enumerate() {
let glyph = &ibm437_src[0x1800 + 8 * i..0x1800 + 8 * (i + 1)];
glyph_copy(
&mut font_output,
&glyph,
char_offset_impl(*chr),
INPUT_WIDTH_LEN,
INPUT_HEIGHT_LEN,
);
}
save_raw(
&format!("{}/src/fonts/ibm437_font_8_8_bold.raw", base_dir),
&font_output,
)?;
save_png(
&format!("{}/doc/ibm437_font_8_8_bold.png", base_dir),
&font_output,
8 * CHARS_PER_ROW,
8 * (256 / CHARS_PER_ROW),
)?;
Ok(())
}
fn characters_mapping(characters: &Vec<char>, base_dir: &str) -> std::io::Result<()> {
let mut mapping: [char; 256] = [' '; 256];
for chr in characters.iter() {
let chr_index = char_offset_impl(*chr);
mapping[chr_index] = *chr;
}
let filename = format!("{}/doc/Characters.txt", base_dir);
let path = Path::new(&filename);
let mut file = File::create(path)?;
for (i, c) in mapping.iter().enumerate() {
write!(file, "{}", c)?;
if (i + 1) % CHARS_PER_ROW == 0 {
writeln!(file)?;
}
}
Ok(())
}
fn main() -> std::io::Result<()> {
let base_dir = env!("CARGO_MANIFEST_DIR").to_string();
const IBM437_SRC: &[u8; 8192] = include_bytes!("../IBM_5788005_AM9264_1981_CGA_MDA_CARD.BIN");
let characters: Vec<char> = include_str!("../Characters_src.txt")
.lines()
.map(|l| l.chars())
.flatten()
.collect::<Vec<char>>();
println!("Generating fonts into {}/src/fonts/ ...", base_dir);
extract_9x14(&characters, IBM437_SRC, &base_dir)?;
println!("- 9x14 regular");
extract_8x8_regular(&characters, IBM437_SRC, &base_dir)?;
println!("- 8x8 regular");
extract_8x8_bold(&characters, IBM437_SRC, &base_dir)?;
println!("- 8x8 bold");
characters_mapping(&characters, &base_dir)?;
println!("- Characters.txt");
println!("Done. PNG files updated in {}/doc/", base_dir);
Ok(())
}