Skip to content

Commit 1e942ee

Browse files
committed
Support chunky graphics.
Handles 1, 2, 4 and 8 bpp. Performance is terrible.
1 parent 3b5c52b commit 1e942ee

File tree

1 file changed

+103
-53
lines changed

1 file changed

+103
-53
lines changed

src/main.rs

Lines changed: 103 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,14 @@ extern "C" fn video_is_valid_mode(mode: common::video::Mode) -> bool {
438438
0 => true,
439439
// 640x480 80x60 text mode
440440
1 => true,
441+
// 640x480, 8-bpp bitmap mode
442+
4 => true,
443+
// 640x480, 4-bpp bitmap mode
444+
5 => true,
445+
// 640x480, 2-bpp bitmap mode
446+
6 => true,
447+
// 640x480, 1-bpp bitmap mode
448+
7 => true,
441449
// nothing else will work
442450
_ => false,
443451
};
@@ -450,29 +458,9 @@ extern "C" fn video_is_valid_mode(mode: common::video::Mode) -> bool {
450458
/// The contents of the screen are undefined after a call to this function.
451459
extern "C" fn video_set_mode(mode: common::video::Mode, fb: *mut u32) -> common::ApiResult<()> {
452460
info!("video_set_mode({:?})", mode);
453-
match mode.timing() {
454-
common::video::Timing::T640x480 => {
455-
// OK
456-
}
457-
common::video::Timing::T640x400 => {
458-
// OK
459-
}
460-
_ => {
461-
return common::ApiResult::Err(common::Error::UnsupportedConfiguration);
462-
}
463-
}
464-
match mode.format() {
465-
common::video::Format::Text8x16 => {
466-
// OK
467-
}
468-
common::video::Format::Text8x8 => {
469-
// OK
470-
}
471-
_ => {
472-
return common::ApiResult::Err(common::Error::UnsupportedConfiguration);
473-
}
461+
if !video_is_valid_mode(mode) {
462+
return common::ApiResult::Err(common::Error::UnsupportedConfiguration);
474463
}
475-
476464
// We know this is a valid video mode because it was set with `video_set_mode`.
477465
let mode_value = mode.as_u8();
478466
VIDEO_MODE.store(mode_value, Ordering::Relaxed);
@@ -499,7 +487,7 @@ extern "C" fn video_get_mode() -> common::video::Mode {
499487
/// allowed to write to, is a function of the current video mode (see
500488
/// `video_get_mode`).
501489
extern "C" fn video_get_framebuffer() -> *mut u32 {
502-
let p = FRAMEBUFFER.get_pointer() as *mut u32;
490+
let p = FRAMEBUFFER.get_pointer();
503491
debug!("video_get_framebuffer() -> {:p}", p);
504492
p
505493
}
@@ -535,10 +523,10 @@ extern "C" fn memory_get_region(region: u8) -> common::FfiOption<common::MemoryR
535523
match region {
536524
0 => {
537525
if unsafe { MEMORY_BLOCK.0.is_null() } {
538-
// Allocate 256 KiB of storage space for the OS to use
539-
let mut data = Box::new([0u8; 256 * 1024]);
526+
// Allocate 1 MiB of storage space for the OS to use
527+
let mut data = Box::new([0u8; 1024 * 1024]);
540528
unsafe {
541-
MEMORY_BLOCK.0 = data.as_mut_ptr() as *mut u8;
529+
MEMORY_BLOCK.0 = data.as_mut_ptr();
542530
MEMORY_BLOCK.1 = std::mem::size_of_val(&*data);
543531
}
544532
std::mem::forget(data);
@@ -1101,6 +1089,86 @@ impl MyApp {
11011089
Self::render_font(&font::font8::FONT, &mut self.font8x8, s)?;
11021090
Ok(())
11031091
}
1092+
1093+
fn render_text(
1094+
&self,
1095+
font: &[pix_engine::texture::TextureId],
1096+
font_height: u16,
1097+
s: &mut PixState,
1098+
) -> PixResult<()> {
1099+
let num_cols = self.mode.text_width().unwrap();
1100+
let num_rows = self.mode.text_height().unwrap();
1101+
let mut bg_idx = 0;
1102+
let mut bg_rgb = {
1103+
let bg = RGBColour::from_packed(PALETTE[usize::from(bg_idx)].load(Ordering::Relaxed));
1104+
rgb!(bg.red(), bg.green(), bg.blue())
1105+
};
1106+
s.stroke(None);
1107+
// FRAMEBUFFER is an num_cols x num_rows size array of (u8_glyph, u8_attr).
1108+
for row in 0..num_rows {
1109+
let y = row * font_height;
1110+
for col in 0..num_cols {
1111+
let cell_no = (row * num_cols) + col;
1112+
let byte_offset = usize::from(cell_no) * 2;
1113+
let x = col * 8;
1114+
let glyph = FRAMEBUFFER.get_at(byte_offset);
1115+
let attr = common::video::Attr(FRAMEBUFFER.get_at(byte_offset + 1));
1116+
let fg_idx = attr.fg().make_ffi_safe().0;
1117+
let new_bg_idx = attr.bg().make_ffi_safe().0;
1118+
if new_bg_idx != bg_idx {
1119+
bg_idx = new_bg_idx;
1120+
let bg = RGBColour::from_packed(
1121+
PALETTE[usize::from(bg_idx)].load(Ordering::Relaxed),
1122+
);
1123+
bg_rgb = rgb!(bg.red(), bg.green(), bg.blue());
1124+
}
1125+
let glyph_box = rect!(i32::from(x), i32::from(y), 8i32, font_height as i32,);
1126+
s.fill(bg_rgb);
1127+
s.rect(glyph_box)?;
1128+
let slot = (usize::from(glyph) * Self::NUM_FG) + usize::from(fg_idx);
1129+
s.texture(font[slot], None, Some(glyph_box))?;
1130+
}
1131+
}
1132+
Ok(())
1133+
}
1134+
1135+
fn render_chunky<const BPP: usize>(&self, s: &mut PixState) -> PixResult<()> {
1136+
let shift = 8 - BPP;
1137+
let num_colours = 1 << BPP;
1138+
let pixels_per_byte = 8 / BPP;
1139+
let num_col_bytes = self.mode.line_size_bytes();
1140+
let num_rows = self.mode.vertical_lines() as usize;
1141+
let colours = Self::make_colours(num_colours);
1142+
for y in 0..num_rows {
1143+
let y_bytes = y * num_col_bytes;
1144+
for x_byte in 0..num_col_bytes {
1145+
let byte_offset = y_bytes + x_byte;
1146+
let mut data = FRAMEBUFFER.get_at(byte_offset);
1147+
let x_start = x_byte * pixels_per_byte;
1148+
for x in 0..pixels_per_byte {
1149+
let bit = (data >> shift) as usize;
1150+
s.stroke(colours[bit]);
1151+
let p = point!((x_start + x) as i32, y as i32);
1152+
s.point(p)?;
1153+
data <<= BPP;
1154+
}
1155+
}
1156+
}
1157+
Ok(())
1158+
}
1159+
1160+
fn make_colours(count: usize) -> Vec<pix_engine::color::Color> {
1161+
let mut result = vec![];
1162+
for palette_entry in PALETTE.iter().take(count) {
1163+
let rgb = RGBColour::from_packed(palette_entry.load(Ordering::Relaxed));
1164+
result.push(rgb!(rgb.red(), rgb.green(), rgb.blue()));
1165+
}
1166+
if count == 2 {
1167+
// special case - use black/white for 2 colour mode, not black/blue
1168+
result[1] = rgb!(0xFF, 0xFF, 0xFF);
1169+
}
1170+
result
1171+
}
11041172
}
11051173

11061174
impl PixEngine for MyApp {
@@ -1168,39 +1236,21 @@ impl PixEngine for MyApp {
11681236
info!("Window set to {} x {}", width, height);
11691237
s.set_window_dimensions((width as u32, height as u32))?;
11701238
s.scale(SCALE_FACTOR, SCALE_FACTOR)?;
1239+
s.background(rgb!(0, 0, 0));
1240+
s.clear()?;
11711241
}
11721242

11731243
s.blend_mode(BlendMode::Blend);
11741244

1175-
let (font, font_height) = match self.mode.format() {
1176-
common::video::Format::Text8x16 => (&self.font8x16, 16),
1177-
common::video::Format::Text8x8 => (&self.font8x8, 8),
1245+
match self.mode.format() {
1246+
common::video::Format::Text8x16 => self.render_text(&self.font8x16, 16, s)?,
1247+
common::video::Format::Text8x8 => self.render_text(&self.font8x8, 8, s)?,
1248+
common::video::Format::Chunky1 => self.render_chunky::<1>(s)?,
1249+
common::video::Format::Chunky2 => self.render_chunky::<2>(s)?,
1250+
common::video::Format::Chunky4 => self.render_chunky::<4>(s)?,
1251+
common::video::Format::Chunky8 => self.render_chunky::<8>(s)?,
11781252
_ => {
11791253
// Unknown mode - do nothing
1180-
return Ok(());
1181-
}
1182-
};
1183-
1184-
let num_cols = self.mode.text_width().unwrap();
1185-
let num_rows = self.mode.text_height().unwrap();
1186-
// FRAMEBUFFER is an num_cols x num_rows size array of (u8_glyph, u8_attr).
1187-
for row in 0..num_rows {
1188-
let y = row * font_height;
1189-
for col in 0..num_cols {
1190-
let cell_no = (row * num_cols) + col;
1191-
let byte_offset = usize::from(cell_no) * 2;
1192-
let x = col * 8;
1193-
let glyph = FRAMEBUFFER.get_at(byte_offset);
1194-
let attr = common::video::Attr(FRAMEBUFFER.get_at(byte_offset + 1));
1195-
let fg_idx = attr.fg().make_ffi_safe().0;
1196-
let bg_idx = attr.bg().make_ffi_safe().0;
1197-
let bg =
1198-
RGBColour::from_packed(PALETTE[usize::from(bg_idx)].load(Ordering::Relaxed));
1199-
let glyph_box = rect!(i32::from(x), i32::from(y), 8i32, font_height as i32,);
1200-
s.fill(rgb!(bg.red(), bg.green(), bg.blue()));
1201-
s.rect(glyph_box)?;
1202-
let slot = (usize::from(glyph) * Self::NUM_FG) + usize::from(fg_idx);
1203-
s.texture(font[slot], None, Some(glyph_box))?;
12041254
}
12051255
}
12061256

0 commit comments

Comments
 (0)