Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- Added `Buffer::pixels_iter()` for iterating over each pixel with its associated `x`/`y` coordinate.
- Added `Buffer::byte_stride()` for pixel buffers whose rows are aligned and may contain padding bytes at the end. Prefer to use the above helpers instead of accessing pixel data directly.
- Renamed `Surface::buffer_mut()` to `Surface::next_buffer()`.
- Added `Buffer::data_u8()` and `Buffer::rows_u8()` for accessing the buffer's data as `u8` slices.
- **Breaking:** Add `Pixel` struct, and use that for pixels instead of `u32`.
- **Breaking:** The pixel format is now target-dependent. Access `PixelFormat::default()` to see which format is used on the current platform.
- **Breaking:** Removed generic type parameters `D` and `W` from `Buffer<'_>` struct.
Expand Down
87 changes: 75 additions & 12 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -397,20 +397,20 @@ impl Buffer<'_> {
}
}

/// Helper methods for writing to the buffer as RGBA pixel data.
/// Helper methods for writing to the buffer's "raw" data.
impl Buffer<'_> {
/// Get a mutable reference to the buffer's pixels.
/// Access the buffer's data as a `u8` slice.
///
/// The size of the returned slice is `buffer.byte_stride() * buffer.height() / 4`.
/// The size of the returned slice is `buffer.byte_stride() * buffer.height()`, and it is
/// guaranteed to have an alignment of at least 4.
///
/// # Examples
///
/// Clear the buffer with red.
/// Zero the buffer.
///
/// ```no_run
/// # use softbuffer::{Buffer, Pixel};
/// # let buffer: Buffer<'_> = unimplemented!();
/// buffer.pixels().fill(Pixel::new_rgb(0xff, 0x00, 0x00));
/// # let buffer: softbuffer::Buffer<'_> = unimplemented!();
/// buffer.data_u8().fill(0x00);
/// ```
///
/// Render to a slice of `[u8; 4]`s. This might be useful for library authors that want to
Expand Down Expand Up @@ -453,12 +453,12 @@ impl Buffer<'_> {
/// {
/// // Use zero-copy implementation when possible.
///
/// // SAFETY: `Pixel` can be reinterpreted as `[u8; 4]`.
/// let pixels = unsafe { std::mem::transmute::<&mut [Pixel], &mut [[u8; 4]]>(buffer.pixels()) };
/// // Reinterpret data from `&mut [u8]` to `&mut [[u8; 4]]`.
/// let pixels = buffer.data_u8().as_chunks_mut::<4>().0;
/// // CORRECTNESS: We just checked that:
/// // - The format is RGBA.
/// // - The `stride == width * 4`.
/// // - The alpha channel is ignored (A -> X).
/// // - Format is RGBA.
/// // - `stride == width * 4`.
/// // - Alpha channel is ignored (A -> X).
/// //
/// // So we can correctly render in the user's expected simplified RGBX format.
/// render(pixels, width, height);
Expand All @@ -483,6 +483,69 @@ impl Buffer<'_> {
///
/// buffer.present();
/// ```
#[inline]
pub fn data_u8(&mut self) -> &mut [u8] {
let data = self.buffer_impl.pixels_mut();
// SAFETY: `u32` can be reinterpreted as 4 `u8`s.
unsafe { std::slice::from_raw_parts_mut(data.as_mut_ptr().cast::<u8>(), data.len() * 4) }
}

/// Iterate over each row of the buffer's data as `u8` subslices.
///
/// Each slice returned from the iterator has a length of `buffer.byte_stride()` and alignment
/// of at least 4.
///
/// # Examples
///
/// Work with the data as individual `u8` components:
///
/// ```no_run
/// use softbuffer::PixelFormat;
///
/// # let buffer: softbuffer::Buffer<'_> = unimplemented!();
/// for row in buffer.rows_u8() {
/// // Ignore remaining pixels, each row should have at least `4` elements.
/// let (row, _remainder) = row.as_chunks_mut::<4>();
///
/// for [b0, b1, b2, b3] in row {
/// let [r, g, b, a] = match PixelFormat::default() {
/// PixelFormat::Bgra8 => [b2, b1, b0, b3],
/// PixelFormat::Rgba8 => [b0, b1, b2, b3],
/// format => unimplemented!("unknown default pixel format: {format:?}"),
/// };
/// // Write a red pixel.
/// *r = 0xff;
/// *b = 0x00;
/// *g = 0x00;
/// *a = 0x00;
/// }
/// }
/// ```
#[inline]
pub fn rows_u8(&mut self) -> impl DoubleEndedIterator<Item = &mut [u8]> + ExactSizeIterator {
let byte_stride = self.byte_stride().get() as usize;
let data = self.data_u8();
assert_eq!(data.len() % byte_stride, 0, "must be multiple of stride");
// NOTE: This won't panic because `byte_stride` came from `NonZeroU32`
data.chunks_mut(byte_stride)
}
}

/// Helper methods for writing to the buffer as RGBA pixel data.
impl Buffer<'_> {
/// Get a mutable reference to the buffer's pixels.
///
/// The size of the returned slice is `buffer.byte_stride() * buffer.height() / 4`.
///
/// # Examples
///
/// Clear the buffer with red.
///
/// ```no_run
/// # use softbuffer::{Buffer, Pixel};
/// # let buffer: Buffer<'_> = unimplemented!();
/// buffer.pixels().fill(Pixel::new_rgb(0xff, 0x00, 0x00));
/// ```
pub fn pixels(&mut self) -> &mut [Pixel] {
self.buffer_impl.pixels_mut()
}
Expand Down