Refactored Glyph Textures into Atlases.
This commit is contained in:
parent
cac8c85fcb
commit
c1515b2bb9
2 changed files with 124 additions and 101 deletions
25
src/ega.rs
25
src/ega.rs
|
@ -142,4 +142,29 @@ impl EgaColors {
|
||||||
};
|
};
|
||||||
col
|
col
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn u8_to_textattr(num: u8) -> (bool, u8, u8) {
|
||||||
|
// In EGA/VGA text mode, each screen character is represented by two
|
||||||
|
// bytes. The low byte is the character, and the high byte is for
|
||||||
|
// the attributes of the character:
|
||||||
|
|
||||||
|
// +-------+-------+-------+-------+-------+-------+-------+-------+
|
||||||
|
// | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
||||||
|
// +-------+-------+-------+-------+-------+-------+-------+-------+
|
||||||
|
// | Blink | Background Color | Foreground Color |
|
||||||
|
// +-------+-----------------------+-------------------------------+
|
||||||
|
|
||||||
|
let blink = (num >> 7) != 0;
|
||||||
|
let back = (num >> 4) & 7;
|
||||||
|
let fore = num & 15;
|
||||||
|
(blink, back, fore)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn textattr_to_u8(blink: bool, background: u8, foreground: u8) -> u8 {
|
||||||
|
let add_blink: u8 = match blink {
|
||||||
|
false => 0,
|
||||||
|
true => 128,
|
||||||
|
};
|
||||||
|
foreground + background * 16 + add_blink
|
||||||
|
}
|
||||||
}
|
}
|
182
src/main.rs
182
src/main.rs
|
@ -4,17 +4,20 @@ use std::thread::sleep;
|
||||||
use std::time::{Duration, SystemTime, SystemTimeError, UNIX_EPOCH};
|
use std::time::{Duration, SystemTime, SystemTimeError, UNIX_EPOCH};
|
||||||
|
|
||||||
use bitvec::prelude::*;
|
use bitvec::prelude::*;
|
||||||
|
use sdl2::Sdl;
|
||||||
use sdl2::event::Event;
|
use sdl2::event::Event;
|
||||||
use sdl2::keyboard::Keycode;
|
use sdl2::keyboard::Keycode;
|
||||||
use sdl2::pixels::{Color, PixelFormatEnum};
|
use sdl2::pixels::{Color, PixelFormatEnum};
|
||||||
use sdl2::rect::Rect;
|
use sdl2::rect::Rect;
|
||||||
use sdl2::render::{WindowCanvas, Texture};
|
use sdl2::render::{WindowCanvas, Texture, TextureCreator};
|
||||||
|
use sdl2::video::{Window, WindowContext};
|
||||||
|
|
||||||
pub mod cp437;
|
pub mod cp437;
|
||||||
pub mod ega;
|
pub mod ega;
|
||||||
use cp437::GLYPHS;
|
use cp437::GLYPHS;
|
||||||
use ega::EgaColors;
|
use ega::EgaColors;
|
||||||
|
|
||||||
|
|
||||||
const SCREEN_HEIGHT: u32 = 350;
|
const SCREEN_HEIGHT: u32 = 350;
|
||||||
const SCREEN_WIDTH: u32 = 640;
|
const SCREEN_WIDTH: u32 = 640;
|
||||||
|
|
||||||
|
@ -24,8 +27,11 @@ const GLYPH_ROWS: u8 = 25;
|
||||||
const GLYPH_HEIGHT: u8 = 14;
|
const GLYPH_HEIGHT: u8 = 14;
|
||||||
const GLYPH_WIDTH: u8 = 8;
|
const GLYPH_WIDTH: u8 = 8;
|
||||||
|
|
||||||
|
const GLYPH_ATLAS_HEIGHT: usize = GLYPH_HEIGHT as usize * 8;
|
||||||
|
const GLYPH_ATLAS_WIDTH: usize = GLYPH_WIDTH as usize * 32;
|
||||||
|
|
||||||
// Total glyph textures = 256 glyphs * 16 foreground colors * 8 background colors
|
// Total glyph textures = 256 glyphs * 16 foreground colors * 8 background colors
|
||||||
const TOTAL_GLYPH_TEXTURES: usize = 256 * 16 * 8;
|
const TOTAL_GLYPH_ATLASES: u8 = 16 * 8;
|
||||||
|
|
||||||
// Total screen blocks = GLYPH_COLUMNS * GLYPH_ROWS
|
// Total screen blocks = GLYPH_COLUMNS * GLYPH_ROWS
|
||||||
const TOTAL_SCREEN_BLOCKS: usize = 2000;
|
const TOTAL_SCREEN_BLOCKS: usize = 2000;
|
||||||
|
@ -46,73 +52,47 @@ impl ScreenBlock {
|
||||||
ScreenBlock { blink, background, foreground, glyph }
|
ScreenBlock { blink, background, foreground, glyph }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn attrs_to_index(blink: bool, background: u8, foreground: u8, glyph: u8) -> usize {
|
pub fn as_index(&self) -> usize {
|
||||||
let num = (usize::from(blink) << 15) +
|
EgaColors::textattr_to_u8(false, self.background, self.foreground) as usize
|
||||||
(usize::from(background) << 12) +
|
|
||||||
(usize::from(foreground) << 8) +
|
|
||||||
usize::from(glyph);
|
|
||||||
num
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn index_to_attrs(num: usize) -> (bool, u8, u8, u8) {
|
|
||||||
let blink = (num >> 15) != 0;
|
|
||||||
let back = (num >> 12) as u8;
|
|
||||||
let fore = ((num >> 8) & 15) as u8;
|
|
||||||
let glyph = (num & 255) as u8;
|
|
||||||
(blink, back, fore, glyph)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_texture_index(&self) -> usize {
|
|
||||||
let num = ScreenBlock::attrs_to_index(
|
|
||||||
false,
|
|
||||||
self.background,
|
|
||||||
self.foreground,
|
|
||||||
self.glyph
|
|
||||||
);
|
|
||||||
num
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_texture_index(num: usize) -> Self {
|
|
||||||
let (blink, background, foreground, glyph) = ScreenBlock::index_to_attrs(num);
|
|
||||||
ScreenBlock { blink, background, foreground, glyph }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_canvas(
|
pub fn to_canvas(
|
||||||
&self,
|
&self,
|
||||||
canvas: &mut WindowCanvas,
|
canvas: &mut WindowCanvas,
|
||||||
textures: &Vec<Texture>,
|
textures: &Vec<Texture>,
|
||||||
x: u8,
|
x: i32,
|
||||||
y: u8,
|
y: i32,
|
||||||
blink: &bool
|
blink_state: &bool
|
||||||
) {
|
) {
|
||||||
|
|
||||||
let tex_idx = match blink {
|
let tex_idx = match blink_state {
|
||||||
false => self.as_texture_index(),
|
false => self.as_index(),
|
||||||
true => match self.blink {
|
true => match self.blink {
|
||||||
false => self.as_texture_index(),
|
false => self.as_index(),
|
||||||
true => ScreenBlock::new(self.blink, self.background, self.background, self.glyph).as_texture_index(),
|
true => EgaColors::textattr_to_u8(false, self.background, self.background) as usize,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let src_x = (self.glyph % 32 * GLYPH_WIDTH) as i32;
|
||||||
|
let src_y = (self.glyph / 32 * GLYPH_HEIGHT) as i32;
|
||||||
|
|
||||||
// These next two assignments look odd mainly because of Rust's current
|
// These next two assignments look odd mainly because of Rust's current
|
||||||
// issue with exclusive range patterns. See here:
|
// issue with exclusive range patterns. See here:
|
||||||
// https://github.com/rust-lang/rust/issues/37854
|
// https://github.com/rust-lang/rust/issues/37854
|
||||||
|
|
||||||
let dest_x = match x {
|
let dest_x = match x {
|
||||||
n if (0..=GLYPH_COLUMNS - 1).contains(&n) => GLYPH_WIDTH as i32 * x as i32,
|
n if (0..=GLYPH_COLUMNS as i32 - 1).contains(&n) => GLYPH_WIDTH as i32 * x,
|
||||||
//0..=79 => GLYPH_WIDTH as i32 * x as i32,
|
|
||||||
_ => panic!("x is out of range! Must be 0..79 inclusive."),
|
_ => panic!("x is out of range! Must be 0..79 inclusive."),
|
||||||
};
|
};
|
||||||
|
|
||||||
let dest_y = match y {
|
let dest_y = match y {
|
||||||
n if (0..=GLYPH_ROWS - 1).contains(&n) => GLYPH_HEIGHT as i32 * y as i32,
|
n if (0..=GLYPH_ROWS as i32 - 1).contains(&n) => GLYPH_HEIGHT as i32 * y,
|
||||||
//0..=24 => GLYPH_HEIGHT as i32 * y as i32,
|
|
||||||
_ => panic!("y is out of range! Must be 0..24 inclusive."),
|
_ => panic!("y is out of range! Must be 0..24 inclusive."),
|
||||||
};
|
};
|
||||||
|
|
||||||
match canvas.copy(
|
match canvas.copy(
|
||||||
&textures[tex_idx],
|
&textures[tex_idx],
|
||||||
Rect::new(0, 0, u32::from(GLYPH_WIDTH), u32::from(GLYPH_HEIGHT)),
|
Rect::new(src_x, src_y, u32::from(GLYPH_WIDTH), u32::from(GLYPH_HEIGHT)),
|
||||||
Rect::new(dest_x, dest_y, u32::from(GLYPH_WIDTH), u32::from(GLYPH_HEIGHT)),
|
Rect::new(dest_x, dest_y, u32::from(GLYPH_WIDTH), u32::from(GLYPH_HEIGHT)),
|
||||||
) {
|
) {
|
||||||
Ok(()) => (),
|
Ok(()) => (),
|
||||||
|
@ -121,31 +101,7 @@ impl ScreenBlock {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(elapsed: u128) {
|
pub fn init_sdl() -> (Sdl, Window) {
|
||||||
//println!("Updated after {} nanoseconds.", elapsed);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render(canvas: &mut WindowCanvas, textures: &Vec<Texture>, screen: &[ScreenBlock], blink: &bool) {
|
|
||||||
canvas.set_draw_color::<Color>(EgaColors::Black.into());
|
|
||||||
canvas.clear();
|
|
||||||
|
|
||||||
for (idx, block) in screen.iter().enumerate() {
|
|
||||||
let x = (idx % GLYPH_COLUMNS as usize) as u8;
|
|
||||||
let y = (idx / GLYPH_COLUMNS as usize) as u8;
|
|
||||||
block.to_canvas(canvas, &textures, x, y, blink);
|
|
||||||
}
|
|
||||||
canvas.present();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_timestamp_in_nanos() -> Result<u128, SystemTimeError> {
|
|
||||||
let current_systime = SystemTime::now();
|
|
||||||
let since_epoch = current_systime.duration_since(UNIX_EPOCH)?;
|
|
||||||
let nanos = since_epoch.as_nanos();
|
|
||||||
|
|
||||||
Ok(nanos)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn main() {
|
|
||||||
let sdl_context = match sdl2::init() {
|
let sdl_context = match sdl2::init() {
|
||||||
Ok(sdl_context) => sdl_context,
|
Ok(sdl_context) => sdl_context,
|
||||||
Err(err) => panic!("SDL could not initialize! SDL_Error: {}", err),
|
Err(err) => panic!("SDL could not initialize! SDL_Error: {}", err),
|
||||||
|
@ -163,26 +119,25 @@ pub fn main() {
|
||||||
Ok(window) => window,
|
Ok(window) => window,
|
||||||
Err(err) => panic!("SDL could not create a window! SDL_Error: {}", err),
|
Err(err) => panic!("SDL could not create a window! SDL_Error: {}", err),
|
||||||
};
|
};
|
||||||
|
(sdl_context, window)
|
||||||
|
}
|
||||||
|
|
||||||
let mut canvas = match window.into_canvas().build() {
|
pub fn init_glyph_atlases(creator: &TextureCreator<WindowContext>) -> Vec<Texture<'_>> {
|
||||||
Ok(canvas) => canvas,
|
let mut atlases: Vec<Texture> = Vec::new();
|
||||||
Err(err) => panic!("SDL could not create a canvas! SDL_Error: {}", err),
|
for atlas_idx in 0..TOTAL_GLYPH_ATLASES {
|
||||||
};
|
let (_, back, fore) = EgaColors::u8_to_textattr(atlas_idx.try_into().unwrap());
|
||||||
|
let mut texture = match creator.create_texture_streaming(PixelFormatEnum::RGB24, GLYPH_ATLAS_WIDTH as u32, GLYPH_ATLAS_HEIGHT as u32) {
|
||||||
let texture_creator = canvas.texture_creator();
|
|
||||||
let mut glyph_textures: Vec<Texture> = Vec::new();
|
|
||||||
|
|
||||||
for tex_idx in 0..TOTAL_GLYPH_TEXTURES {
|
|
||||||
let (_, back, fore, cp) = ScreenBlock::index_to_attrs(tex_idx);
|
|
||||||
let mut texture = match texture_creator.create_texture_streaming(PixelFormatEnum::RGB24, u32::from(GLYPH_WIDTH), u32::from(GLYPH_HEIGHT)) {
|
|
||||||
Ok(texture) => texture,
|
Ok(texture) => texture,
|
||||||
Err(err) => panic!("SDL could not create texture. SDL Error: {}", err),
|
Err(err) => panic!("SDL could not create texture. SDL Error: {}", err),
|
||||||
};
|
};
|
||||||
match texture.with_lock(None, |buffer: &mut [u8], pitch: usize| {
|
match texture.with_lock(None, |buffer: &mut [u8], pitch: usize| {
|
||||||
for (y, line) in GLYPHS[cp as usize].iter().enumerate() {
|
for (glyph_num, glyph_data) in GLYPHS.iter().enumerate() {
|
||||||
|
let glyph_x = glyph_num % 32;
|
||||||
|
let glyph_y = glyph_num / 32;
|
||||||
|
for (local_y, line) in glyph_data.iter().enumerate() {
|
||||||
let bits = line.view_bits::<Msb0>();
|
let bits = line.view_bits::<Msb0>();
|
||||||
for (x, pixel) in bits.iter().enumerate() {
|
for (local_x, pixel) in bits.iter().enumerate() {
|
||||||
let offset = y * pitch + x * 3;
|
let offset = ((glyph_y * GLYPH_HEIGHT as usize * pitch) + (local_y * pitch)) + ((glyph_x * GLYPH_WIDTH as usize) + local_x) * 3;
|
||||||
if pixel == false {
|
if pixel == false {
|
||||||
buffer[offset] = EgaColors::u8_to_rgb(back).r;
|
buffer[offset] = EgaColors::u8_to_rgb(back).r;
|
||||||
buffer[offset + 1] = EgaColors::u8_to_rgb(back).g;
|
buffer[offset + 1] = EgaColors::u8_to_rgb(back).g;
|
||||||
|
@ -194,34 +149,77 @@ pub fn main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}) {
|
}) {
|
||||||
Ok(()) => (),
|
Ok(()) => (),
|
||||||
Err(err) => panic!("Unable to modify texture. SDL Error: {}", err),
|
Err(err) => panic!("Unable to modify texture. SDL Error: {}", err),
|
||||||
};
|
};
|
||||||
glyph_textures.push(texture);
|
atlases.push(texture);
|
||||||
}
|
}
|
||||||
|
atlases
|
||||||
|
}
|
||||||
|
|
||||||
let mut dos_screen: [ScreenBlock; TOTAL_SCREEN_BLOCKS] = [ScreenBlock::new(false, EgaColors::Black.into(), EgaColors::Black.into(), 0); TOTAL_SCREEN_BLOCKS];
|
pub fn create_test_screen(screen: &mut [ScreenBlock]) {
|
||||||
|
|
||||||
for col in 0..60 {
|
for col in 0..60 {
|
||||||
dos_screen[col as usize] = ScreenBlock::new(false, EgaColors::Black.into(), EgaColors::BrightYellow.into(), 178);
|
screen[col as usize] = ScreenBlock::new(false, EgaColors::Black.into(), EgaColors::BrightYellow.into(), 178);
|
||||||
}
|
}
|
||||||
for row in 1..GLYPH_ROWS-1 {
|
for row in 1..GLYPH_ROWS-1 {
|
||||||
let idx = row as usize * GLYPH_COLUMNS as usize;
|
let idx = row as usize * GLYPH_COLUMNS as usize;
|
||||||
dos_screen[idx] = ScreenBlock::new(false, EgaColors::Black.into(), EgaColors::BrightYellow.into(), 178);
|
screen[idx] = ScreenBlock::new(false, EgaColors::Black.into(), EgaColors::BrightYellow.into(), 178);
|
||||||
dos_screen[idx + 59] = ScreenBlock::new(false, EgaColors::Black.into(), EgaColors::BrightYellow.into(), 178);
|
screen[idx + 59] = ScreenBlock::new(false, EgaColors::Black.into(), EgaColors::BrightYellow.into(), 178);
|
||||||
}
|
}
|
||||||
for col in 0..60 {
|
for col in 0..60 {
|
||||||
let idx = (GLYPH_ROWS - 1) as usize * GLYPH_COLUMNS as usize + col as usize;
|
let idx = (GLYPH_ROWS - 1) as usize * GLYPH_COLUMNS as usize + col as usize;
|
||||||
dos_screen[idx] = ScreenBlock::new(false, EgaColors::Black.into(), EgaColors::BrightYellow.into(), 178);
|
screen[idx] = ScreenBlock::new(false, EgaColors::Black.into(), EgaColors::BrightYellow.into(), 178);
|
||||||
}
|
}
|
||||||
|
|
||||||
dos_screen[1030] = ScreenBlock::new(true, EgaColors::Blue.into(), EgaColors::LightGray.into(), 178);
|
screen[1030] = ScreenBlock::new(true, EgaColors::Blue.into(), EgaColors::LightGray.into(), 178);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(elapsed: u128) {
|
||||||
|
//println!("Updated after {} nanoseconds.", elapsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render(canvas: &mut WindowCanvas, atlases: &Vec<Texture>, screen: &[ScreenBlock], blink: &bool) {
|
||||||
|
canvas.set_draw_color::<Color>(EgaColors::Black.into());
|
||||||
|
canvas.clear();
|
||||||
|
|
||||||
|
for (idx, block) in screen.iter().enumerate() {
|
||||||
|
let x = (idx % GLYPH_COLUMNS as usize) as i32;
|
||||||
|
let y = (idx / GLYPH_COLUMNS as usize) as i32;
|
||||||
|
block.to_canvas(canvas, &atlases, x, y, blink);
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.present();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_timestamp_in_nanos() -> Result<u128, SystemTimeError> {
|
||||||
|
let current_systime = SystemTime::now();
|
||||||
|
let since_epoch = current_systime.duration_since(UNIX_EPOCH)?;
|
||||||
|
let nanos = since_epoch.as_nanos();
|
||||||
|
|
||||||
|
Ok(nanos)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
let (sdl_context, window) = init_sdl();
|
||||||
|
|
||||||
|
let mut canvas = match window.into_canvas().build() {
|
||||||
|
Ok(canvas) => canvas,
|
||||||
|
Err(err) => panic!("SDL could not create a canvas! SDL_Error: {}", err),
|
||||||
|
};
|
||||||
|
|
||||||
|
let texture_creator = canvas.texture_creator();
|
||||||
|
let atlases: Vec<Texture> = init_glyph_atlases(&texture_creator);
|
||||||
|
|
||||||
|
let mut dos_screen: [ScreenBlock; TOTAL_SCREEN_BLOCKS] = [ScreenBlock::new(false, EgaColors::Black.into(), EgaColors::Black.into(), 0); TOTAL_SCREEN_BLOCKS];
|
||||||
|
|
||||||
|
create_test_screen(&mut dos_screen);
|
||||||
|
|
||||||
let mut blink_tick: u128 = 0;
|
let mut blink_tick: u128 = 0;
|
||||||
let mut blink_state: bool = false;
|
let mut blink_state: bool = false;
|
||||||
|
|
||||||
render(&mut canvas, &glyph_textures, &dos_screen, &blink_state);
|
render(&mut canvas, &atlases, &dos_screen, &blink_state);
|
||||||
|
|
||||||
let mut event_pump = match sdl_context.event_pump() {
|
let mut event_pump = match sdl_context.event_pump() {
|
||||||
Ok(event_pump) => event_pump,
|
Ok(event_pump) => event_pump,
|
||||||
|
@ -259,7 +257,7 @@ pub fn main() {
|
||||||
blink_tick += elapsed;
|
blink_tick += elapsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
render(&mut canvas, &glyph_textures, &dos_screen, &blink_state);
|
render(&mut canvas, &atlases, &dos_screen, &blink_state);
|
||||||
//sleep(Duration::new(0, FPS_60_HERTZ));
|
//sleep(Duration::new(0, FPS_60_HERTZ));
|
||||||
|
|
||||||
prev_tick = curr_tick;
|
prev_tick = curr_tick;
|
||||||
|
|
Loading…
Reference in a new issue