Skip to content
73 changes: 45 additions & 28 deletions src/mode/buffered_graphics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<bool> {
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
}
});
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/size.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
///
Expand Down