Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add get_pixel & toggle_pixel & extract shared code #194

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
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