diff --git a/src/mode/buffered_graphics.rs b/src/mode/buffered_graphics.rs index 6a9cfb66..f7d70bce 100644 --- a/src/mode/buffered_graphics.rs +++ b/src/mode/buffered_graphics.rs @@ -156,39 +156,56 @@ where } } - /// Turn a pixel on or off. A non-zero `value` is treated as on, `0` as off. If the X and Y - /// coordinates are out of the bounds of the display, this method call is a noop. - pub fn set_pixel(&mut self, x: u32, y: u32, value: bool) { - let value = value as u8; - let rotation = self.rotation; + fn pixel_location(&self, x: u32, y: u32) -> Option<(usize, u32)> { + let (x_rotated, y_rotated) = match self.rotation { + DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => (x, y), + DisplayRotation::Rotate90 | DisplayRotation::Rotate270 => (y, x), + }; + if x_rotated >= SIZE::WIDTH as u32 || y_rotated >= SIZE::HEIGHT as u32 { + None + } else { + let idx = ((y_rotated as usize) / 8 * SIZE::WIDTH as usize) + (x_rotated as usize); + let bit = y_rotated % 8; + Some((idx, bit)) + } + } - let (idx, bit) = match rotation { - DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => { - let idx = ((y as usize) / 8 * SIZE::WIDTH as usize) + (x as usize); - let bit = y % 8; + fn track_change(&mut self, x: u32, y: u32) { + self.mode.min_x = self.mode.min_x.min(x as u8); + self.mode.max_x = self.mode.max_x.max(x as u8); + self.mode.min_y = self.mode.min_y.min(y as u8); + self.mode.max_y = self.mode.max_y.max(y as u8); + } - (idx, bit) - } - DisplayRotation::Rotate90 | DisplayRotation::Rotate270 => { - let idx = ((x as usize) / 8 * SIZE::WIDTH as usize) + (y as usize); - let bit = x % 8; + pub fn get_pixel(&self, x: u32, y: u32) -> Option { + self.pixel_location(x, y).and_then(|(idx, bit)| { + self.mode + .buffer + .as_ref() + .get(idx) + .map(|byte| byte & (1 << bit) != 0) + }) + } - (idx, bit) + /// Turn a pixel on or off. If the coordinates are out of bounds for the display, this method call is a noop + pub fn set_pixel(&mut self, x: u32, y: u32, value: bool) { + self.pixel_location(x, y).map(|(idx, bit)| { + self.track_change(x, y); + if let Some(byte) = self.mode.buffer.as_mut().get_mut(idx) { + // Ref this comment https://stackoverflow.com/questions/47981/how-do-you-set-clear-and-toggle-a-single-bit#comment46654671_47990 + *byte = *byte & !(1 << bit) | ((value as u8) << bit) // Set pixel value in byte } - }; - - if let Some(byte) = self.mode.buffer.as_mut().get_mut(idx) { - // Keep track of max and min values - self.mode.min_x = self.mode.min_x.min(x as u8); - self.mode.max_x = self.mode.max_x.max(x as u8); - - self.mode.min_y = self.mode.min_y.min(y as u8); - self.mode.max_y = self.mode.max_y.max(y as u8); + }); + } - // Set pixel value in byte - // Ref this comment https://stackoverflow.com/questions/47981/how-do-you-set-clear-and-toggle-a-single-bit#comment46654671_47990 - *byte = *byte & !(1 << bit) | (value << bit) - } + /// Toggle a pixel to the opposite of it's current state. If the coordinates are out of bounds for the display, this method call is a noop + pub fn toggle_pixel(&mut self, x: u32, y: u32) { + self.pixel_location(x, y).map(|(idx, bit)| { + self.track_change(x, y); + if let Some(byte) = self.mode.buffer.as_mut().get_mut(idx) { + *byte = *byte ^ (1 << bit) // toggle pixel in byte + } + }); } } diff --git a/src/size.rs b/src/size.rs index b177300e..a503042a 100644 --- a/src/size.rs +++ b/src/size.rs @@ -40,7 +40,7 @@ pub trait DisplaySize { /// Size of framebuffer. Because the display is monochrome, this is /// width * height / 8 - type Buffer: AsMut<[u8]> + NewZeroed; + type Buffer: AsRef<[u8]> + AsMut<[u8]> + NewZeroed; /// Send resolution and model-dependent configuration to the display ///