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 @@ -3,6 +3,7 @@
- Added `Buffer::pixels()` for accessing the buffer's pixel data.
- Added `Buffer::pixel_rows()` for iterating over rows of the buffer data.
- 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.
- **Breaking:** Removed generic type parameters `D` and `W` from `Buffer<'_>` struct.
- **Breaking:** Removed `Deref[Mut]` implementation on `Buffer<'_>`. Use `Buffer::pixels()` instead.

Expand Down
8 changes: 4 additions & 4 deletions examples/animation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ fn main() {
let window = util::make_window(event_loop, |w| w);

let old_size = (0, 0);
let frames = pre_render_frames(0, 0);
let frames = pre_render_frames(0, 0, 0);

(window, old_size, frames)
},
Expand Down Expand Up @@ -65,7 +65,7 @@ fn main() {
let size = (buffer.width().get(), buffer.height().get());
if size != *old_size {
*old_size = size;
*frames = pre_render_frames(size.0, size.1);
*frames = pre_render_frames(buffer.byte_stride().get() / 4, size.0, size.1);
}

let frame = &frames[((elapsed * 60.0).round() as usize).clamp(0, 59)];
Expand Down Expand Up @@ -95,11 +95,11 @@ fn main() {
util::run_app(event_loop, app);
}

fn pre_render_frames(width: u32, height: u32) -> Vec<Vec<u32>> {
fn pre_render_frames(stride: u32, width: u32, height: u32) -> Vec<Vec<u32>> {
let render = |frame_id| {
let elapsed = ((frame_id as f64) / (60.0)) * 2.0 * PI;

let coords = (0..height).flat_map(|x| (0..width).map(move |y| (x, y)));
let coords = (0..height).flat_map(|x| (0..stride).map(move |y| (x, y)));
coords
.map(|(x, y)| {
let y = (y as f64) / (height as f64);
Expand Down
4 changes: 2 additions & 2 deletions examples/fruit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,14 @@ fn main() {
};

let mut buffer = surface.buffer_mut().unwrap();
let width = fruit.width();
let stride = buffer.byte_stride().get() / 4;
for (x, y, pixel) in fruit.pixels() {
let red = pixel.0[0] as u32;
let green = pixel.0[1] as u32;
let blue = pixel.0[2] as u32;

let color = blue | (green << 8) | (red << 16);
buffer.pixels()[(y * width + x) as usize] = color;
buffer.pixels()[(y * stride + x) as usize] = color;
}

buffer.present().unwrap();
Expand Down
10 changes: 10 additions & 0 deletions src/backend_dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,16 @@ macro_rules! make_dispatch {
}

impl BufferInterface for BufferDispatch<'_> {
#[inline]
fn byte_stride(&self) -> NonZeroU32 {
match self {
$(
$(#[$attr])*
Self::$name(inner) => inner.byte_stride(),
)*
}
}

#[inline]
fn width(&self) -> NonZeroU32 {
match self {
Expand Down
1 change: 1 addition & 0 deletions src/backend_interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ pub(crate) trait SurfaceInterface<D: HasDisplayHandle + ?Sized, W: HasWindowHand
}

pub(crate) trait BufferInterface {
fn byte_stride(&self) -> NonZeroU32;
fn width(&self) -> NonZeroU32;
fn height(&self) -> NonZeroU32;
fn pixels_mut(&mut self) -> &mut [u32];
Expand Down
36 changes: 16 additions & 20 deletions src/backends/android.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ impl<D: HasDisplayHandle, W: HasWindowHandle> SurfaceInterface<D, W> for Android
));
}

let buffer = vec![0; native_window_buffer.width() * native_window_buffer.height()];
let buffer = vec![0; native_window_buffer.stride() * native_window_buffer.height()];

Ok(BufferImpl {
native_window_buffer,
Expand All @@ -123,6 +123,10 @@ pub struct BufferImpl<'a> {
unsafe impl Send for BufferImpl<'_> {}

impl BufferInterface for BufferImpl<'_> {
fn byte_stride(&self) -> NonZeroU32 {
NonZeroU32::new(self.native_window_buffer.stride() as u32 * 4).unwrap()
}

fn width(&self) -> NonZeroU32 {
NonZeroU32::new(self.native_window_buffer.width() as u32).unwrap()
}
Expand All @@ -143,26 +147,18 @@ impl BufferInterface for BufferImpl<'_> {

// TODO: This function is pretty slow this way
fn present(mut self) -> Result<(), SoftBufferError> {
let input_lines = self.buffer.chunks(self.native_window_buffer.width());
for (output, input) in self
.native_window_buffer
.lines()
// Unreachable as we checked before that this is a valid, mappable format
.unwrap()
.zip(input_lines)
{
// .lines() removed the stride
assert_eq!(output.len(), input.len() * 4);

for (i, pixel) in input.iter().enumerate() {
// Swizzle colors from BGR(A) to RGB(A)
let [b, g, r, a] = pixel.to_le_bytes();
output[i * 4].write(r);
output[i * 4 + 1].write(g);
output[i * 4 + 2].write(b);
output[i * 4 + 3].write(a);
}
// Unreachable as we checked before that this is a valid, mappable format
let native_buffer = self.native_window_buffer.bytes().unwrap();

for (pixel, output) in self.buffer.iter().zip(native_buffer.chunks_mut(4)) {
// Swizzle colors from BGR(A) to RGB(A)
let [b, g, r, a] = pixel.to_le_bytes();
output[0].write(r);
output[1].write(g);
output[2].write(b);
output[3].write(a);
}

Ok(())
}

Expand Down
9 changes: 7 additions & 2 deletions src/backends/cg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,8 +259,9 @@ impl<D: HasDisplayHandle, W: HasWindowHandle> SurfaceInterface<D, W> for CGImpl<
}

fn buffer_mut(&mut self) -> Result<BufferImpl<'_>, SoftBufferError> {
let buffer_size = util::byte_stride(self.width as u32) as usize * self.height / 4;
Ok(BufferImpl {
buffer: util::PixelBuffer(vec![0; self.width * self.height]),
buffer: util::PixelBuffer(vec![0; buffer_size]),
width: self.width,
height: self.height,
color_space: &self.color_space,
Expand All @@ -279,6 +280,10 @@ pub struct BufferImpl<'a> {
}

impl BufferInterface for BufferImpl<'_> {
fn byte_stride(&self) -> NonZeroU32 {
NonZeroU32::new(util::byte_stride(self.width as u32)).unwrap()
}

fn width(&self) -> NonZeroU32 {
NonZeroU32::new(self.width as u32).unwrap()
}
Expand Down Expand Up @@ -338,7 +343,7 @@ impl BufferInterface for BufferImpl<'_> {
self.height,
8,
32,
self.width * 4,
util::byte_stride(self.width as u32) as usize,
Some(self.color_space),
bitmap_info,
Some(&data_provider),
Expand Down
4 changes: 4 additions & 0 deletions src/backends/kms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,10 @@ impl<D: ?Sized, W: ?Sized> Drop for KmsImpl<D, W> {
}

impl BufferInterface for BufferImpl<'_> {
fn byte_stride(&self) -> NonZeroU32 {
NonZeroU32::new(self.width().get() * 4).unwrap()
}

fn width(&self) -> NonZeroU32 {
self.size.0
}
Expand Down
4 changes: 4 additions & 0 deletions src/backends/orbital.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@ pub struct BufferImpl<'a> {
}

impl BufferInterface for BufferImpl<'_> {
fn byte_stride(&self) -> NonZeroU32 {
NonZeroU32::new(self.width().get() * 4).unwrap()
}

fn width(&self) -> NonZeroU32 {
NonZeroU32::new(self.width as u32).unwrap()
}
Expand Down
7 changes: 4 additions & 3 deletions src/backends/wayland/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use wayland_client::{
};

use super::State;
use crate::util;

#[cfg(any(target_os = "linux", target_os = "freebsd"))]
fn create_memfile() -> File {
Expand Down Expand Up @@ -98,7 +99,7 @@ impl WaylandBuffer {
0,
width,
height,
width * 4,
util::byte_stride(width as u32) as i32,
wl_shm::Format::Xrgb8888,
qh,
released.clone(),
Expand Down Expand Up @@ -138,7 +139,7 @@ impl WaylandBuffer {
0,
width,
height,
width * 4,
util::byte_stride(width as u32) as i32,
wl_shm::Format::Xrgb8888,
&self.qh,
self.released.clone(),
Expand All @@ -158,7 +159,7 @@ impl WaylandBuffer {
}

fn len(&self) -> usize {
self.width as usize * self.height as usize
util::byte_stride(self.width as u32) as usize * self.height as usize / 4
}

#[inline]
Expand Down
6 changes: 5 additions & 1 deletion src/backends/wayland/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
backend_interface::*,
error::{InitError, SwResultExt},
Rect, SoftBufferError,
util, Rect, SoftBufferError,
};
use raw_window_handle::{HasDisplayHandle, HasWindowHandle, RawDisplayHandle, RawWindowHandle};
use std::{
Expand Down Expand Up @@ -219,6 +219,10 @@ pub struct BufferImpl<'a> {
}

impl BufferInterface for BufferImpl<'_> {
fn byte_stride(&self) -> NonZeroU32 {
NonZeroU32::new(util::byte_stride(self.width as u32)).unwrap()
}

fn width(&self) -> NonZeroU32 {
NonZeroU32::new(self.width as u32).unwrap()
}
Expand Down
4 changes: 4 additions & 0 deletions src/backends/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,10 @@ pub struct BufferImpl<'a> {
}

impl BufferInterface for BufferImpl<'_> {
fn byte_stride(&self) -> NonZeroU32 {
NonZeroU32::new(self.width().get() * 4).unwrap()
}

fn width(&self) -> NonZeroU32 {
self.size
.expect("must set size of surface before calling `width()` on the buffer")
Expand Down
21 changes: 15 additions & 6 deletions src/backends/win32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,9 @@ impl Buffer {

#[inline]
fn pixels_mut(&mut self) -> &mut [u32] {
unsafe {
slice::from_raw_parts_mut(
self.pixels.as_ptr(),
i32::from(self.width) as usize * i32::from(self.height) as usize,
)
}
let num_bytes =
byte_stride(self.width.get() as u32, 32) as usize * i32::from(self.height) as usize;
unsafe { slice::from_raw_parts_mut(self.pixels.as_ptr(), num_bytes / 4) }
}
}

Expand Down Expand Up @@ -250,6 +247,11 @@ pub struct BufferImpl<'a> {
}

impl BufferInterface for BufferImpl<'_> {
fn byte_stride(&self) -> NonZeroU32 {
let width = self.buffer.width.get() as u32;
NonZeroU32::new(byte_stride(width, 32)).unwrap()
}

fn width(&self) -> NonZeroU32 {
self.buffer.width.try_into().unwrap()
}
Expand Down Expand Up @@ -467,3 +469,10 @@ impl<T> From<T> for OnlyUsedFromOrigin<T> {
Self(t)
}
}

#[inline]
fn byte_stride(width: u32, bit_count: u32) -> u32 {
// <https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader#calculating-surface-stride>
// When `bit_count == 32`, this is always just equal to `width * 4`.
((width * bit_count + 31) & !31) >> 3
}
34 changes: 12 additions & 22 deletions src/backends/x11.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,10 +327,13 @@ impl<D: HasDisplayHandle + ?Sized, W: HasWindowHandle> SurfaceInterface<D, W> fo
height,
}))?;

let byte_stride = width.get() * 4;
let num_bytes = byte_stride as usize * height.get() as usize;

if self.size != Some((width, height)) {
self.buffer_presented = false;
self.buffer
.resize(self.display.connection(), width.get(), height.get())
.resize(self.display.connection(), num_bytes)
.swbuf_err("Failed to resize X11 buffer")?;

// We successfully resized the buffer.
Expand Down Expand Up @@ -408,6 +411,10 @@ pub struct BufferImpl<'a> {
}

impl BufferInterface for BufferImpl<'_> {
fn byte_stride(&self) -> NonZeroU32 {
NonZeroU32::new(self.width().get() * 4).unwrap()
}

fn width(&self) -> NonZeroU32 {
self.size.unwrap().0.into()
}
Expand Down Expand Up @@ -531,17 +538,12 @@ impl BufferInterface for BufferImpl<'_> {
}

impl Buffer {
/// Resize the buffer to the given size.
fn resize(
&mut self,
conn: &impl Connection,
width: u16,
height: u16,
) -> Result<(), PushBufferError> {
/// Resize the buffer to the given number of bytes.
fn resize(&mut self, conn: &impl Connection, num_bytes: usize) -> Result<(), PushBufferError> {
match self {
Buffer::Shm(ref mut shm) => shm.alloc_segment(conn, total_len(width, height)),
Buffer::Shm(ref mut shm) => shm.alloc_segment(conn, num_bytes),
Buffer::Wire(wire) => {
wire.resize(total_len(width, height) / 4, 0);
wire.resize(num_bytes / 4, 0);
Ok(())
}
}
Expand Down Expand Up @@ -970,15 +972,3 @@ impl<T, E: Into<PushBufferError>> PushResultExt<T, E> for Result<T, E> {
self.map_err(Into::into)
}
}

/// Get the length that a slice needs to be to hold a buffer of the given dimensions.
#[inline(always)]
fn total_len(width: u16, height: u16) -> usize {
let width: usize = width.into();
let height: usize = height.into();

width
.checked_mul(height)
.and_then(|len| len.checked_mul(4))
.unwrap_or_else(|| panic!("Dimensions are too large: ({} x {})", width, height))
}
Loading