diff options
Diffstat (limited to 'src/devices/screen')
-rw-r--r-- | src/devices/screen/draw_line.rs | 223 | ||||
-rw-r--r-- | src/devices/screen/draw_rect.rs | 42 | ||||
-rw-r--r-- | src/devices/screen/draw_sprite.rs | 25 | ||||
-rw-r--r-- | src/devices/screen/sprite_data.rs | 98 | ||||
-rw-r--r-- | src/devices/screen/vector_points.rs | 21 |
5 files changed, 377 insertions, 32 deletions
diff --git a/src/devices/screen/draw_line.rs b/src/devices/screen/draw_line.rs new file mode 100644 index 0000000..94066f4 --- /dev/null +++ b/src/devices/screen/draw_line.rs @@ -0,0 +1,223 @@ +use super::*; + +impl ScreenDevice { + pub fn draw_line(&mut self, colour: u8, layer: ScreenLayer) { + let [p0, p1] = [self.cursor, self.vector]; + match (p0.x == p1.x, p0.y == p1.y) { + (false, false) => self.draw_diagonal_line(colour, layer), + (false, true) => self.draw_horizontal_line(colour, layer), + ( true, false) => self.draw_vertical_line(colour, layer), + ( true, true) => self.draw_pixel(colour, layer, p0), + }; + } + + pub fn draw_line_1bit(&mut self, params: u8, layer: ScreenLayer) { + let [p0, p1] = [self.cursor, self.vector]; + match (p0.x == p1.x, p0.y == p1.y) { + (false, false) => self.draw_diagonal_line_1bit(params, layer), + (false, true) => self.draw_horizontal_line_1bit(params, layer), + ( true, false) => self.draw_vertical_line_1bit(params, layer), + ( true, true) => self.draw_pixel_1bit(params, layer, p0), + }; + } + + pub fn draw_pixel_1bit(&mut self, params: u8, layer: ScreenLayer, point: ScreenPosition) { + let dim = self.dimensions; + let sprite = self.sprite_buffer.get_1bit_sprite(params); + let colour = sprite[point.y as usize % 8][point.x as usize % 8]; + if !dim.contains_point(point) || colour == 0xff { return } + let index = point.x as usize + ((dim.width as usize) * (point.y as usize)); + match layer { + ScreenLayer::Background => self.background[index] = colour, + ScreenLayer::Foreground => self.foreground[index] = colour, + }; + } + + fn draw_horizontal_line(&mut self, colour: u8, layer: ScreenLayer) { + if let Some([x0, y, x1, _]) = self.find_vector_bounding_box() { + let screen_width = self.dimensions.width as usize; + let i = screen_width * y; + let buffer = match layer { + ScreenLayer::Background => &mut self.background, + ScreenLayer::Foreground => &mut self.foreground, + }; + buffer[i+x0..=i+x1].fill(colour); + } + } + + fn draw_horizontal_line_1bit(&mut self, params: u8, layer: ScreenLayer) { + if let Some([x0, y, x1, _]) = self.find_vector_bounding_box() { + let screen_width = self.dimensions.width as usize; + let i = screen_width * y; + let buffer = match layer { + ScreenLayer::Background => &mut self.background, + ScreenLayer::Foreground => &mut self.foreground, + }; + let sprite = self.sprite_buffer.get_1bit_sprite(params); + let row = sprite[y % 8]; + for x in x0..=x1 { + let colour = row[x % 8]; + if colour != 0xff { buffer[i+x] = colour }; + } + } + } + + fn draw_vertical_line(&mut self, colour: u8, layer: ScreenLayer) { + if let Some([x, y0, _, y1]) = self.find_vector_bounding_box() { + let screen_width = self.dimensions.width as usize; + let mut i = (screen_width * y0) + x; + let buffer = match layer { + ScreenLayer::Background => &mut self.background, + ScreenLayer::Foreground => &mut self.foreground, + }; + for _ in y0..=y1 { + buffer[i] = colour; + i += screen_width; + } + } + } + + fn draw_vertical_line_1bit(&mut self, params: u8, layer: ScreenLayer) { + if let Some([x, y0, _, y1]) = self.find_vector_bounding_box() { + let screen_width = self.dimensions.width as usize; + let mut i = (screen_width * y0) + x; + let buffer = match layer { + ScreenLayer::Background => &mut self.background, + ScreenLayer::Foreground => &mut self.foreground, + }; + let sprite = self.sprite_buffer.get_1bit_sprite(params); + let mut column = [0u8; 8]; + for y in 0..8 { column[y] = sprite[y][x % 8] } + for y in y0..=y1 { + let colour = column[y % 8]; + if colour != 0xff { buffer[i] = colour }; + i += screen_width; + } + } + } + + fn draw_diagonal_line(&mut self, colour: u8, layer: ScreenLayer) { + fn abs_diff(v0: u16, v1: u16) -> u16 { + let v = v1.wrapping_sub(v0); + if v > 0x8000 { !v + 1 } else { v } + } + let [p0, p1] = [self.cursor, self.vector]; + + // If the slope of the line is greater than 1. + if abs_diff(p0.y, p1.y) > abs_diff(p0.x, p1.x) { + // Swap points 0 and 1 such that y0 is always smaller than y1. + let (x0, y0, x1, y1) = match p0.y > p1.y { + true => (p1.x, p1.y, p0.x, p0.y), + false => (p0.x, p0.y, p1.x, p1.y), + }; + let dy = y1 - y0; + let (dx, xi) = match x0 > x1 { + true => (x0 - x1, 0xffff), + false => (x1 - x0, 0x0001), + }; + let dxdy2 = (dx.wrapping_sub(dy)).wrapping_mul(2); + let dx2 = dx * 2; + let mut d = dx2.wrapping_sub(dy); + let mut x = x0; + + for y in y0..=y1 { + self.draw_pixel(colour, layer, ScreenPosition::new(x, y)); + if d < 0x8000 { + x = x.wrapping_add(xi); d = d.wrapping_add(dxdy2); + } else { + d = d.wrapping_add(dx2); + } + } + // If the slope of the line is less than or equal to 1. + } else { + // Swap points 0 and 1 so that x0 is always smaller than x1. + let (x0, y0, x1, y1) = match p0.x > p1.x { + true => (p1.x, p1.y, p0.x, p0.y), + false => (p0.x, p0.y, p1.x, p1.y), + }; + let dx = x1 - x0; + let (dy, yi) = match y0 > y1 { + true => (y0 - y1, 0xffff), + false => (y1 - y0, 0x0001), + }; + let dydx2 = (dy.wrapping_sub(dx)).wrapping_mul(2); + let dy2 = dy * 2; + let mut d = dy2.wrapping_sub(dx); + let mut y = y0; + + for x in x0..=x1 { + self.draw_pixel(colour, layer, ScreenPosition::new(x, y)); + if d < 0x8000 { + y = y.wrapping_add(yi); + d = d.wrapping_add(dydx2); + } else { + d = d.wrapping_add(dy2); + } + } + } + } + + fn draw_diagonal_line_1bit(&mut self, params: u8, layer: ScreenLayer) { + fn abs_diff(v0: u16, v1: u16) -> u16 { + let v = v1.wrapping_sub(v0); + if v > 0x8000 { !v + 1 } else { v } + } + let [p0, p1] = [self.cursor, self.vector]; + + // If the slope of the line is greater than 1. + if abs_diff(p0.y, p1.y) > abs_diff(p0.x, p1.x) { + // Swap points 0 and 1 such that y0 is always smaller than y1. + let (x0, y0, x1, y1) = match p0.y > p1.y { + true => (p1.x, p1.y, p0.x, p0.y), + false => (p0.x, p0.y, p1.x, p1.y), + }; + let dy = y1 - y0; + let (dx, xi) = match x0 > x1 { + true => (x0 - x1, 0xffff), + false => (x1 - x0, 0x0001), + }; + let dxdy2 = (dx.wrapping_sub(dy)).wrapping_mul(2); + let dx2 = dx * 2; + let mut d = dx2.wrapping_sub(dy); + let mut x = x0; + + for y in y0..=y1 { + self.draw_pixel_1bit(params, layer, ScreenPosition::new(x, y)); + if d < 0x8000 { + x = x.wrapping_add(xi); d = d.wrapping_add(dxdy2); + } else { + d = d.wrapping_add(dx2); + } + } + // If the slope of the line is less than or equal to 1. + } else { + // Swap points 0 and 1 so that x0 is always smaller than x1. + let (x0, y0, x1, y1) = match p0.x > p1.x { + true => (p1.x, p1.y, p0.x, p0.y), + false => (p0.x, p0.y, p1.x, p1.y), + }; + let dx = x1 - x0; + let (dy, yi) = match y0 > y1 { + true => (y0 - y1, 0xffff), + false => (y1 - y0, 0x0001), + }; + let dydx2 = (dy.wrapping_sub(dx)).wrapping_mul(2); + let dy2 = dy * 2; + let mut d = dy2.wrapping_sub(dx); + let mut y = y0; + + for x in x0..=x1 { + self.draw_pixel_1bit(params, layer, ScreenPosition::new(x, y)); + if d < 0x8000 { + y = y.wrapping_add(yi); + d = d.wrapping_add(dydx2); + } else { + d = d.wrapping_add(dy2); + } + } + } + } + + + +} diff --git a/src/devices/screen/draw_rect.rs b/src/devices/screen/draw_rect.rs new file mode 100644 index 0000000..265a87f --- /dev/null +++ b/src/devices/screen/draw_rect.rs @@ -0,0 +1,42 @@ +use super::*; + +impl ScreenDevice { + pub fn draw_rect(&mut self, colour: u8, layer: ScreenLayer) { + if let Some([x0, y0, x1, y1]) = self.find_vector_bounding_box() { + let screen_width = self.dimensions.width as usize; + let rect_width = x1 - x0 + 1; + let mut i = x0 + (screen_width * y0); + let buffer = match layer { + ScreenLayer::Background => &mut self.background, + ScreenLayer::Foreground => &mut self.foreground, + }; + for _ in y0..=y1 { + buffer[i..i+rect_width].fill(colour); + i += screen_width; + } + } + } + + pub fn draw_rect_1bit(&mut self, params: u8, layer: ScreenLayer) { + if let Some([x0, y0, x1, y1]) = self.find_vector_bounding_box() { + let screen_width = self.dimensions.width as usize; + let rect_width = x1 - x0 + 1; + let mut i = x0 + (screen_width * y0); + let sprite = self.sprite_buffer.get_1bit_sprite(params); + let buffer = match layer { + ScreenLayer::Background => &mut self.background, + ScreenLayer::Foreground => &mut self.foreground, + }; + + for y in y0..=y1 { + let row = sprite[y % 8]; + for x in x0..=x1 { + let colour = row[x % 8]; + if colour != 0xff { buffer[i] = colour } + i += 1; + } + i += screen_width - rect_width; + } + }; + } +} diff --git a/src/devices/screen/draw_sprite.rs b/src/devices/screen/draw_sprite.rs new file mode 100644 index 0000000..9b0658c --- /dev/null +++ b/src/devices/screen/draw_sprite.rs @@ -0,0 +1,25 @@ +use super::*; + +impl ScreenDevice { + pub fn draw_sprite_1bit(&mut self, params: u8, layer: ScreenLayer) { + let sprite = self.sprite_buffer.get_1bit_sprite(params); + self.draw_sprite(sprite, layer); + } + + pub fn draw_sprite_2bit(&mut self, params: u8, layer: ScreenLayer) { + let sprite = self.sprite_buffer.get_2bit_sprite(params); + self.draw_sprite(sprite, layer); + } + + fn draw_sprite(&mut self, sprite: Sprite, layer: ScreenLayer) { + let mut pos = self.cursor; + for row in sprite { + for colour in row { + self.draw_pixel(colour, layer, pos); + pos.x = pos.x.wrapping_add(1); + } + pos.x = pos.x.wrapping_sub(8); + pos.y = pos.x.wrapping_add(1); + } + } +} diff --git a/src/devices/screen/sprite_data.rs b/src/devices/screen/sprite_data.rs index aade2c6..0a1d3c2 100644 --- a/src/devices/screen/sprite_data.rs +++ b/src/devices/screen/sprite_data.rs @@ -1,11 +1,17 @@ -pub struct SpriteData { +use super::*; + +macro_rules! test { ($value:expr, $mask:expr) => { $value & $mask != 0 }; } + + +pub struct SpriteBuffer { data: [u8; 16], pointer: usize, + colours: [u8; 4], } -impl SpriteData { +impl SpriteBuffer { pub fn new() -> Self { - Self { data: [0; 16], pointer: 0 } + Self { data: [0; 16], pointer: 0, colours: [0; 4] } } pub fn push(&mut self, val: u8) { @@ -13,21 +19,91 @@ impl SpriteData { self.pointer = (self.pointer + 1) % 16; } - pub fn get_1bit_sprite(&self) -> [u8; 8] { - let mut sprite = [0u8; 8]; - for (i, r) in sprite.iter_mut().enumerate() { - *r = self.data[(self.pointer + i + 8) % 16] + pub fn set_colour_high(&mut self, val: u8) { + self.colours[0] = val >> 4; + self.colours[1] = val & 0x0f; + } + + pub fn set_colour_low(&mut self, val: u8) { + self.colours[2] = val >> 4; + self.colours[3] = val & 0x0f; + } + + // Return the 64 transformed pixels of the current 1-bit sprite. + // Each pixel is the palette index of that pixel, or 0xff if transparent. + pub fn get_1bit_sprite(&self, params: u8) -> Sprite { + let mut sprite = [[0u8; 8]; 8]; + let plane = self.get_low_plane(params); + let colours = self.get_colours(params); + for (y, row) in plane.into_iter().enumerate() { + for x in (0..8).rev() { + sprite[y][7-x] = colours[(row >> x & 0x1) as usize]; + } } return sprite; } - pub fn get_2bit_sprite(&self) -> [u8; 16] { - let mut sprite = [0u8; 16]; - for (i, r) in sprite.iter_mut().enumerate() { - *r = self.data[(self.pointer + i) % 16] + // Return the 64 transformed pixels of the current 2-bit sprite. + // Each pixel is the palette index of that pixel, or 0xff if transparent. + pub fn get_2bit_sprite(&self, params: u8) -> Sprite { + let mut sprite = [[0u8; 8]; 8]; + let high_plane = self.get_high_plane(params); + let low_plane = self.get_low_plane(params); + let colours = self.get_colours(params); + for (y, (row_h, row_l)) in zip(high_plane, low_plane).enumerate() { + for x in (0..8).rev() { + let bit_h = (row_h >> x) & 0x1; + let bit_l = (row_l >> x) & 0x1; + sprite[y][7-x] = colours[(bit_h << 1 | bit_l) as usize]; + } } return sprite; } + + fn get_high_plane(&self, params: u8) -> Plane { + let mut plane = [0u8; 8]; + for (i, row) in plane.iter_mut().enumerate() { + *row = self.data[(self.pointer + i) % 16] + } + transform_plane(plane, params) + } + + fn get_low_plane(&self, params: u8) -> Plane { + let mut plane = [0u8; 8]; + for (i, row) in plane.iter_mut().enumerate() { + *row = self.data[(self.pointer + i + 8) % 16] + } + transform_plane(plane, params) + } + + fn get_colours(&self, params: u8) -> [u8; 4] { + let mut colours = self.colours; + if test!(params, TRANSPARENT) { + colours[0] = 0xff; + } + return colours; + } } +fn transform_plane(mut plane: Plane, params: u8) -> Plane { + if test!(params, FLIP_DIAGONAL) { + let mut flipped = [0u8; 8]; + for mut row in plane { + for y in 0..8 { + flipped[y] = flipped[y] << 1 | row >> 7; + row <<= 1; + } + } + plane = flipped; + } + if test!(params, FLIP_VERTICAL) { + plane.reverse(); + } + if test!(params, FLIP_HORIZONTAL) { + for row in plane.iter_mut() { + *row = row.reverse_bits(); + } + } + return plane; +} diff --git a/src/devices/screen/vector_points.rs b/src/devices/screen/vector_points.rs deleted file mode 100644 index e6c28ef..0000000 --- a/src/devices/screen/vector_points.rs +++ /dev/null @@ -1,21 +0,0 @@ -use crate::*; - -pub struct VectorPoints { - points: [ScreenPosition; 2], - pointer: usize, -} - -impl VectorPoints { - pub fn new() -> Self { - Self { points: [ScreenPosition::ZERO; 2], pointer: 0 } - } - - pub fn push(&mut self, point: ScreenPosition) { - self.points[self.pointer] = point; - self.pointer = (self.pointer + 1) % 2; - } - - pub fn get_pair(&self) -> [ScreenPosition; 2] { - self.points - } -} |