Skip to content

Commit

Permalink
Implement safer pixel data functions
Browse files Browse the repository at this point in the history
I believe that it is unnecessary for the user to use unsafe functions
when it can easily be implemented with safe functionality. This is an
example of doing it safely with a small abstraction. Rust is all about
safe code, not unsafe code. Such a simple abstraction should be hidden
from the user
  • Loading branch information
dogunbound committed Feb 19, 2024
1 parent 22ebe6c commit 2b5f38f
Showing 1 changed file with 65 additions and 2 deletions.
67 changes: 65 additions & 2 deletions src/graphics/image.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
use core::fmt;
use std::error::Error;

use {
crate::{
ffi::graphics as ffi,
Expand Down Expand Up @@ -186,10 +189,30 @@ impl Image {
/// This function doesn't check the validity of the pixel
/// coordinates, using out-of-range values will result in
/// an undefined behaviour.
pub unsafe fn set_pixel(&mut self, x: u32, y: u32, color: Color) {
pub unsafe fn set_pixel_unchecked(&mut self, x: u32, y: u32, color: Color) {
ffi::sfImage_setPixel(self.image, x, y, color)
}

/// Change the color of a pixel in an image
///
/// # Arguments
/// * x - X coordinate of pixel to change
/// * y - Y coordinate of pixel to change
/// * color - New color of the pixel
pub fn set_pixel(&mut self, x: u32, y: u32, color: Color) -> Result<(), SetPixelError> {
let image_size = self.size();
if x >= image_size.x {
return Err(SetPixelError::XTooLarge(x, image_size.x - 1));
}
if y >= image_size.y {
return Err(SetPixelError::YTooLarge(y, image_size.y - 1));
}

// Since we check for index validity before setting the pixel, it is safe unless the
// image texture has been unloaded, but I doubt you can even do that.
unsafe { Ok(ffi::sfImage_setPixel(self.image, x, y, color)) }
}

/// Get the color of a pixel in an image
///
/// # Arguments
Expand All @@ -204,10 +227,29 @@ impl Image {
/// coordinates, using out-of-range values will result in
/// an undefined behaviour.
#[must_use]
pub unsafe fn pixel_at(&self, x: u32, y: u32) -> Color {
pub unsafe fn pixel_at_unchecked(&self, x: u32, y: u32) -> Color {
ffi::sfImage_getPixel(self.image, x, y)
}

/// Get the color of a pixel in an image
///
/// # Arguments
/// * x - X coordinate of pixel to get
/// * y - Y coordinate of pixel to get
///
/// Return the Color of the pixel at coordinates (x, y)
#[must_use]
pub fn pixel_at(&self, x: u32, y: u32) -> Option<Color> {
let image_size = self.size();
if image_size.x <= x || image_size.y <= y {
return None;
}

// Since we check for index validity before setting the pixel, it is safe unless the
// image texture has been unloaded, but I doubt you can even do that.
unsafe { Some(ffi::sfImage_getPixel(self.image, x, y)) }
}

/// Return the memory buffer of this image.
#[must_use]
pub fn pixel_data(&self) -> &[u8] {
Expand Down Expand Up @@ -293,3 +335,24 @@ impl Drop for Image {
unsafe { ffi::sfImage_destroy(self.image) }
}
}

#[derive(Debug, Copy, Clone)]
pub enum SetPixelError {
XTooLarge(u32, u32),
YTooLarge(u32, u32),
}

impl Error for SetPixelError {}

impl fmt::Display for SetPixelError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::XTooLarge(passed_in, max) => {
write!(f, "x index out of bounds. x:{} max_x:{}", passed_in, max)
}
Self::YTooLarge(passed_in, max) => {
write!(f, "y index out of bounds. y:{} max_y:{}", passed_in, max)
}
}
}
}

0 comments on commit 2b5f38f

Please sign in to comment.