diff options
-rw-r--r-- | src/devices/screen.rs | 219 |
1 files changed, 100 insertions, 119 deletions
diff --git a/src/devices/screen.rs b/src/devices/screen.rs index ee11f52..c96d1c4 100644 --- a/src/devices/screen.rs +++ b/src/devices/screen.rs @@ -257,28 +257,28 @@ impl ScreenDevice { } fn draw_line(&mut self, colour: u8, layer: ScreenLayer) { - let points = self.vector.get_pair(); - match (points[0].x == points[1].x, points[0].y == points[1].y) { - (false, false) => self.draw_diagonal_line(colour, layer, points), - (false, true) => self.draw_horizontal_line(colour, layer, points), - ( true, false) => self.draw_vertical_line(colour, layer, points), - ( true, true) => self.draw_pixel(colour, layer, points[0]), + let [p0, p1] = self.vector.get_pair(); + 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), }; } - fn draw_diagonal_line(&mut self, colour: u8, layer: ScreenLayer, points: [ScreenPosition; 2]) { + 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] = points; + let [p0, p1] = self.vector.get_pair(); // 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 so that y0 is always smaller than y1. - let (x0, y0, x1, y1) = match points[0].y > points[1].y { - true => (points[1].x, points[1].y, points[0].x, points[0].y), - false => (points[0].x, points[0].y, points[1].x, points[1].y), + 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; @@ -303,9 +303,9 @@ impl ScreenDevice { // 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 points[0].x > points[1].x { - true => (points[1].x, points[1].y, points[0].x, points[0].y), - false => (points[0].x, points[0].y, points[1].x, points[1].y), + 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; @@ -330,125 +330,82 @@ impl ScreenDevice { } } - fn draw_horizontal_line(&mut self, colour: u8, layer: ScreenLayer, points: [ScreenPosition; 2]) { - let [start, end] = points; - let dim = self.dimensions; - let x0 = min(start.x, end.x); - let x1 = max(start.x, end.x); - if (x0 >= dim.width && x1 >= dim.width) || start.y >= dim.height { return } - let x0 = min(x0, dim.width.saturating_sub(1)); - let x1 = min(x1, dim.width.saturating_sub(1)); - let row_i = (dim.width as usize) * (start.y as usize); - let start_i = row_i + x0 as usize; - let end_i = row_i + x1 as usize; - let layer = match layer { - ScreenLayer::Background => &mut self.background, - ScreenLayer::Foreground => &mut self.foreground, - }; - layer[start_i..=end_i].fill(colour); - return + 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 layer = match layer { + ScreenLayer::Background => &mut self.background, + ScreenLayer::Foreground => &mut self.foreground, + }; + layer[i+x0..=i+x1].fill(colour); + } } - fn draw_vertical_line(&mut self, colour: u8, layer: ScreenLayer, points: [ScreenPosition; 2]) { - let [start, end] = points; - let dim = self.dimensions; - let y0 = min(start.y, end.y); - let y1 = max(start.y, end.y); - if (y0 >= dim.height && y1 >= dim.height) || start.x >= dim.width { return } - let y0 = min(y0, dim.height.saturating_sub(1)); - let y1 = min(y1, dim.height.saturating_sub(1)); - let mut i = (start.x as usize) + (dim.width as usize * (y0 as usize)); - let pixels = match layer { - ScreenLayer::Background => &mut self.background, - ScreenLayer::Foreground => &mut self.foreground, - }; - for _ in y0..=y1 { - pixels[i] = colour; - i += dim.width as usize; + 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 layer = match layer { + ScreenLayer::Background => &mut self.background, + ScreenLayer::Foreground => &mut self.foreground, + }; + for _ in y0..=y1 { + layer[i] = colour; + i += screen_width; + } + } - return } fn draw_rect(&mut self, colour: u8, layer: ScreenLayer) { - let [x0, y0, x1, y1] = { - macro_rules! raise {($v:expr) => {$v.wrapping_add(0x8000)};} - macro_rules! lower {($v:expr) => {$v.wrapping_sub(0x8000)};} - let [p0, p1] = self.vector.get_pair(); - let [p0x, p0y] = [ raise!(p0.x), raise!(p0.y) ]; - let [p1x, p1y] = [ raise!(p1.x), raise!(p1.y) ]; - let [x0, y0] = [ min(p0x, p1x), min(p0y, p1y) ]; - let [x1, y1] = [ max(p0x, p1x), max(p0y, p1y) ]; - let right = self.dimensions.width.saturating_sub(1); - let bottom = self.dimensions.height.saturating_sub(1); - if x0 > raise!(right) || y0 > raise!(bottom) || x1 < 0x8000 || y1 < 0x8000 { return } - [ - if x0 < 0x8000 { 0 } else { min(lower!(x0), right) } as usize, - if y0 < 0x8000 { 0 } else { min(lower!(y0), bottom) } as usize, - if x1 < 0x8000 { 0 } else { min(lower!(x1), right) } as usize, - if y1 < 0x8000 { 0 } else { min(lower!(y1), bottom) } as usize, - ] - }; - let screen_width = self.dimensions.width as usize; - let rect_width = x1 - x0 + 1; - let mut i = x0 + (screen_width * y0); - let pixels = match layer { - ScreenLayer::Background => &mut self.background, - ScreenLayer::Foreground => &mut self.foreground, - }; - for _ in y0..=y1 { - pixels[i..i+rect_width].fill(colour); - i += screen_width; + 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 pixels = match layer { + ScreenLayer::Background => &mut self.background, + ScreenLayer::Foreground => &mut self.foreground, + }; + for _ in y0..=y1 { + pixels[i..i+rect_width].fill(colour); + i += screen_width; + } } } fn draw_rect_1bit(&mut self, params: u8, layer: ScreenLayer) { - let [x0, y0, x1, y1] = { - macro_rules! raise {($v:expr) => {$v.wrapping_add(0x8000)};} - macro_rules! lower {($v:expr) => {$v.wrapping_sub(0x8000)};} - let [p0, p1] = self.vector.get_pair(); - let [p0x, p0y] = [ raise!(p0.x), raise!(p0.y) ]; - let [p1x, p1y] = [ raise!(p1.x), raise!(p1.y) ]; - let [x0, y0] = [ min(p0x, p1x), min(p0y, p1y) ]; - let [x1, y1] = [ max(p0x, p1x), max(p0y, p1y) ]; - let right = self.dimensions.width.saturating_sub(1); - let bottom = self.dimensions.height.saturating_sub(1); - if x0 > raise!(right) || y0 > raise!(bottom) || x1 < 0x8000 || y1 < 0x8000 { return } - [ - if x0 < 0x8000 { 0 } else { min(lower!(x0), right) } as usize, - if y0 < 0x8000 { 0 } else { min(lower!(y0), bottom) } as usize, - if x1 < 0x8000 { 0 } else { min(lower!(x1), right) } as usize, - if y1 < 0x8000 { 0 } else { min(lower!(y1), bottom) } as usize, - ] - }; - let screen_width = self.dimensions.width as usize; - let rect_width = x1 - x0 + 1; - let mut i = x0 + (screen_width * y0); - let pixels = match layer { - ScreenLayer::Background => &mut self.background, - ScreenLayer::Foreground => &mut self.foreground, - }; + 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 pixels = match layer { + ScreenLayer::Background => &mut self.background, + ScreenLayer::Foreground => &mut self.foreground, + }; - let sprite_data = self.sprite_data.get_1bit_sprite(); - let mut sprite_i = y0 % 8; - let sprite_x_off = (x0 % 8) as u32; - let transparent = params & 0x08 != 0; - if params & 0x07 != 0 { - todo!("Pre-treat sprite, with rotation/translation"); - } + let sprite_data = self.sprite_data.get_1bit_sprite(); + let mut sprite_i = y0 % 8; + let sprite_x_off = (x0 % 8) as u32; + let transparent = params & 0x08 != 0; + if params & 0x07 != 0 { + todo!("Pre-treat sprite, with rotation/translation"); + } - for _ in y0..=y1 { - let mut row = sprite_data[sprite_i].rotate_left(sprite_x_off); - for _ in x0..=x1 { - let colour = (row >> 7) as usize; - if !(transparent && colour == 0) { - pixels[i] = self.sprite_colours[colour]; + for _ in y0..=y1 { + let mut row = sprite_data[sprite_i].rotate_left(sprite_x_off); + for _ in x0..=x1 { + let colour = (row >> 7) as usize; + if !(transparent && colour == 0) { + pixels[i] = self.sprite_colours[colour]; + } + row = row.rotate_left(1); + i += 1; } - row = row.rotate_left(1); - i += 1; + sprite_i = (sprite_i + 1) % 8; + i += screen_width - rect_width; } - sprite_i = (sprite_i + 1) % 8; - i += screen_width - rect_width; - } + }; } fn draw_sprite(&mut self, params: u8, layer: ScreenLayer, sprite: [u8; 64]) { @@ -484,4 +441,28 @@ impl ScreenDevice { _ => unreachable!(), } } + + /// Returns [x0, y0, x1, y1] + fn find_vector_bounding_box(&self) -> Option<[usize; 4]> { + macro_rules! raise {($v:expr) => {$v.wrapping_add(0x8000)};} + macro_rules! lower {($v:expr) => {$v.wrapping_sub(0x8000)};} + + let [p0, p1] = self.vector.get_pair(); + let [p0x, p0y] = [ raise!(p0.x), raise!(p0.y) ]; + let [p1x, p1y] = [ raise!(p1.x), raise!(p1.y) ]; + let [x0, y0] = [ min(p0x, p1x), min(p0y, p1y) ]; + let [x1, y1] = [ max(p0x, p1x), max(p0y, p1y) ]; + let right = self.dimensions.width.saturating_sub(1); + let bottom = self.dimensions.height.saturating_sub(1); + if x0 > raise!(right) || y0 > raise!(bottom) || x1 < 0x8000 || y1 < 0x8000 { + None + } else { + Some([ + if x0 < 0x8000 { 0 } else { min(lower!(x0), right) } as usize, + if y0 < 0x8000 { 0 } else { min(lower!(y0), bottom) } as usize, + if x1 < 0x8000 { 0 } else { min(lower!(x1), right) } as usize, + if y1 < 0x8000 { 0 } else { min(lower!(y1), bottom) } as usize, + ]) + } + } } |