mod sprites;
use core::cmp;
use bitmap32::{BitBlock, BitMap};
use calc_common::{
util::{SliceExt, TransmutableInto},
DrawableCharacter, Point,
};
pub type ScreenBuffer = BitMap<u32, 256>;
#[inline(always)]
pub fn draw_text_at<C: TransmutableInto<DrawableCharacter>>(
screen_buffer: &mut ScreenBuffer,
text: &[C],
x: u16,
y: u16,
) {
let text = C::transmute_slice_into(text);
_draw_text_at(screen_buffer, text, x, y)
}
fn _draw_text_at(screen_buffer: &mut ScreenBuffer, text: &[DrawableCharacter], x: u16, y: u16) {
let mut offset = 0;
for char in text {
draw_sprite_at(screen_buffer, *char, x + offset, y);
offset += 5;
}
}
pub fn draw_hline(screen_buffer: &mut ScreenBuffer, len: u16, x: u16, y: u16) {
let screen_blocks: &mut [u32] = screen_buffer
.get_blocks_mut((x as usize * 2)..)
.unwrap_or(&mut []);
let screen_blocks = if let Some(blocks) = screen_blocks.get_mut(0..(len as usize * 2)) {
blocks
} else {
screen_blocks
};
let block_offset = (y / 32) % 2;
let block_shift_amnt = y % 32;
let mask = u32::UPSTREAM_ONE.wrapping_shift_downstream(block_shift_amnt as usize);
for column in screen_blocks.chunks_exact_mut(2) {
column[block_offset as usize] |= mask;
}
}
pub const fn get_text_width<C: TransmutableInto<DrawableCharacter>>(text: &[C]) -> usize {
text.len() * 5
}
#[inline(always)]
pub fn draw_sprite_at<T: Into<DrawableCharacter>>(
screen_buffer: &mut ScreenBuffer,
item: T,
x: u16,
y: u16,
) {
let map = sprites::get_sprite(item.into());
draw_map_at(screen_buffer, map, x, y);
}
pub fn draw_xor_rectangle(screen_buffer: &mut ScreenBuffer, p1: Point, p2: Point) {
let top_left = Point {
x: cmp::min(p1.x, p2.x).min(127),
y: cmp::min(p1.y, p2.y).min(63),
};
let bottom_right = Point {
x: cmp::max(p1.x, p2.x.min(128)),
y: cmp::max(p1.y, p2.y).min(64),
};
let screen_blocks: &mut [u32] = screen_buffer.as_mut();
let screen_blocks = unsafe {
screen_blocks.get_unchecked_mut((top_left.x as usize * 2)..(bottom_right.x as usize * 2))
};
let top_mask = u32::MAX
.checked_shl(u32::from(
top_left.y + (32u8.saturating_sub(bottom_right.y)),
))
.unwrap_or(0)
.checked_shr(u32::from(top_left.y))
.unwrap_or(0);
let bottom_mask = u32::MAX
.checked_shl(u32::from(
top_left.y.saturating_sub(32) + 64u8.saturating_sub(bottom_right.y),
))
.unwrap_or(0)
>> (top_left.y.saturating_sub(32));
for [top, bottom] in screen_blocks.uarray_chunks_mut() {
*top ^= top_mask;
*bottom ^= bottom_mask;
}
}
fn draw_map_at(screen_buffer: &mut ScreenBuffer, map: &BitMap<u8, 4>, x: u16, y: u16) -> bool {
let screen_blocks: &mut [u32] = screen_buffer
.get_blocks_mut((x as usize * 2)..)
.unwrap_or(&mut []);
let top_shift_amount = u32::from(y);
let bottom_shift_amount = if let Some(s) = (64 - 8u16).checked_sub(y) {
u32::from(s)
} else {
return false;
};
for ([top, bottom], map_column) in screen_blocks
.uarray_chunks_mut::<2>()
.zip(map.0.iter().copied())
{
let top_mask = (u32::from(map_column) << (32 - 8))
.checked_shr(top_shift_amount)
.unwrap_or(0);
let bottom_mask = u32::from(map_column)
.checked_shl(bottom_shift_amount)
.unwrap_or(0);
*top |= top_mask;
*bottom |= bottom_mask;
}
true
}