Skip to content

Commit 17cb3a1

Browse files
authored
Add Buffer data_u8 and rows_u8 accessors (#363)
And move RGBX rendering example to `data_u8`
1 parent fdc0d52 commit 17cb3a1

2 files changed

Lines changed: 76 additions & 12 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
- Added `Buffer::pixels_iter()` for iterating over each pixel with its associated `x`/`y` coordinate.
99
- 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.
1010
- Renamed `Surface::buffer_mut()` to `Surface::next_buffer()`.
11+
- Added `Buffer::data_u8()` and `Buffer::rows_u8()` for accessing the buffer's data as `u8` slices.
1112
- **Breaking:** Add `Pixel` struct, and use that for pixels instead of `u32`.
1213
- **Breaking:** The pixel format is now target-dependent. Access `PixelFormat::default()` to see which format is used on the current platform.
1314
- **Breaking:** Removed generic type parameters `D` and `W` from `Buffer<'_>` struct.

src/lib.rs

Lines changed: 75 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -397,20 +397,20 @@ impl Buffer<'_> {
397397
}
398398
}
399399

400-
/// Helper methods for writing to the buffer as RGBA pixel data.
400+
/// Helper methods for writing to the buffer's "raw" data.
401401
impl Buffer<'_> {
402-
/// Get a mutable reference to the buffer's pixels.
402+
/// Access the buffer's data as a `u8` slice.
403403
///
404-
/// The size of the returned slice is `buffer.byte_stride() * buffer.height() / 4`.
404+
/// The size of the returned slice is `buffer.byte_stride() * buffer.height()`, and it is
405+
/// guaranteed to have an alignment of at least 4.
405406
///
406407
/// # Examples
407408
///
408-
/// Clear the buffer with red.
409+
/// Zero the buffer.
409410
///
410411
/// ```no_run
411-
/// # use softbuffer::{Buffer, Pixel};
412-
/// # let buffer: Buffer<'_> = unimplemented!();
413-
/// buffer.pixels().fill(Pixel::new_rgb(0xff, 0x00, 0x00));
412+
/// # let buffer: softbuffer::Buffer<'_> = unimplemented!();
413+
/// buffer.data_u8().fill(0x00);
414414
/// ```
415415
///
416416
/// Render to a slice of `[u8; 4]`s. This might be useful for library authors that want to
@@ -453,12 +453,12 @@ impl Buffer<'_> {
453453
/// {
454454
/// // Use zero-copy implementation when possible.
455455
///
456-
/// // SAFETY: `Pixel` can be reinterpreted as `[u8; 4]`.
457-
/// let pixels = unsafe { std::mem::transmute::<&mut [Pixel], &mut [[u8; 4]]>(buffer.pixels()) };
456+
/// // Reinterpret data from `&mut [u8]` to `&mut [[u8; 4]]`.
457+
/// let pixels = buffer.data_u8().as_chunks_mut::<4>().0;
458458
/// // CORRECTNESS: We just checked that:
459-
/// // - The format is RGBA.
460-
/// // - The `stride == width * 4`.
461-
/// // - The alpha channel is ignored (A -> X).
459+
/// // - Format is RGBA.
460+
/// // - `stride == width * 4`.
461+
/// // - Alpha channel is ignored (A -> X).
462462
/// //
463463
/// // So we can correctly render in the user's expected simplified RGBX format.
464464
/// render(pixels, width, height);
@@ -483,6 +483,69 @@ impl Buffer<'_> {
483483
///
484484
/// buffer.present();
485485
/// ```
486+
#[inline]
487+
pub fn data_u8(&mut self) -> &mut [u8] {
488+
let data = self.buffer_impl.pixels_mut();
489+
// SAFETY: `u32` can be reinterpreted as 4 `u8`s.
490+
unsafe { std::slice::from_raw_parts_mut(data.as_mut_ptr().cast::<u8>(), data.len() * 4) }
491+
}
492+
493+
/// Iterate over each row of the buffer's data as `u8` subslices.
494+
///
495+
/// Each slice returned from the iterator has a length of `buffer.byte_stride()` and alignment
496+
/// of at least 4.
497+
///
498+
/// # Examples
499+
///
500+
/// Work with the data as individual `u8` components:
501+
///
502+
/// ```no_run
503+
/// use softbuffer::PixelFormat;
504+
///
505+
/// # let buffer: softbuffer::Buffer<'_> = unimplemented!();
506+
/// for row in buffer.rows_u8() {
507+
/// // Ignore remaining pixels, each row should have at least `4` elements.
508+
/// let (row, _remainder) = row.as_chunks_mut::<4>();
509+
///
510+
/// for [b0, b1, b2, b3] in row {
511+
/// let [r, g, b, a] = match PixelFormat::default() {
512+
/// PixelFormat::Bgra8 => [b2, b1, b0, b3],
513+
/// PixelFormat::Rgba8 => [b0, b1, b2, b3],
514+
/// format => unimplemented!("unknown default pixel format: {format:?}"),
515+
/// };
516+
/// // Write a red pixel.
517+
/// *r = 0xff;
518+
/// *b = 0x00;
519+
/// *g = 0x00;
520+
/// *a = 0x00;
521+
/// }
522+
/// }
523+
/// ```
524+
#[inline]
525+
pub fn rows_u8(&mut self) -> impl DoubleEndedIterator<Item = &mut [u8]> + ExactSizeIterator {
526+
let byte_stride = self.byte_stride().get() as usize;
527+
let data = self.data_u8();
528+
assert_eq!(data.len() % byte_stride, 0, "must be multiple of stride");
529+
// NOTE: This won't panic because `byte_stride` came from `NonZeroU32`
530+
data.chunks_mut(byte_stride)
531+
}
532+
}
533+
534+
/// Helper methods for writing to the buffer as RGBA pixel data.
535+
impl Buffer<'_> {
536+
/// Get a mutable reference to the buffer's pixels.
537+
///
538+
/// The size of the returned slice is `buffer.byte_stride() * buffer.height() / 4`.
539+
///
540+
/// # Examples
541+
///
542+
/// Clear the buffer with red.
543+
///
544+
/// ```no_run
545+
/// # use softbuffer::{Buffer, Pixel};
546+
/// # let buffer: Buffer<'_> = unimplemented!();
547+
/// buffer.pixels().fill(Pixel::new_rgb(0xff, 0x00, 0x00));
548+
/// ```
486549
pub fn pixels(&mut self) -> &mut [Pixel] {
487550
self.buffer_impl.pixels_mut()
488551
}

0 commit comments

Comments
 (0)