Skip to content
Merged
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
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Unreleased

- **Breaking:** Remove generic type parameters `D` and `W` from `Buffer<'_>` struct.
- 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.
- **Breaking:** Removed generic type parameters `D` and `W` from `Buffer<'_>` struct.

# 0.4.7

Expand Down
6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,12 @@ fn main() {
.unwrap();

let mut buffer = surface.buffer_mut().unwrap();
for index in 0..(buffer.width().get() * buffer.height().get()) {
let y = index / buffer.width().get();
let x = index % buffer.width().get();
for (x, y, pixel) in buffer.pixels_iter() {
let red = x % 255;
let green = y % 255;
let blue = (x * y) % 255;

buffer[index as usize] = blue | (green << 8) | (red << 16);
*pixel = blue | (green << 8) | (red << 16);
}

buffer.present().unwrap();
Expand Down
20 changes: 8 additions & 12 deletions benches/buffer_mut.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,35 +40,31 @@ fn buffer_mut(c: &mut criterion::Criterion) {
});
});

c.bench_function("pixels_mut()", |b| {
c.bench_function("data()", |b| {
let mut buffer = surface.buffer_mut().unwrap();
b.iter(|| {
let pixels: &mut [u32] = &mut buffer;
black_box(pixels);
});
});

c.bench_function("fill", |b| {
c.bench_function("fill u32", |b| {
let mut buffer = surface.buffer_mut().unwrap();
b.iter(|| {
let buffer = black_box(&mut buffer);
buffer.fill(0x00000000);
});
});

c.bench_function("render", |b| {
c.bench_function("render pixels_iter", |b| {
let mut buffer = surface.buffer_mut().unwrap();
b.iter(|| {
let buffer = black_box(&mut buffer);
let width = buffer.width().get();
for y in 0..buffer.height().get() {
for x in 0..buffer.width().get() {
let red = (x & 0xff) ^ (y & 0xff);
let green = (x & 0x7f) ^ (y & 0x7f);
let blue = (x & 0x3f) ^ (y & 0x3f);
let value = blue | (green << 8) | (red << 16);
buffer[(y * width + x) as usize] = value;
}
for (x, y, pixel) in buffer.pixels_iter() {
let red = (x & 0xff) ^ (y & 0xff);
let green = (x & 0x7f) ^ (y & 0x7f);
let blue = (x & 0x3f) ^ (y & 0x3f);
*pixel = blue | (green << 8) | (red << 16);
}
});
});
Expand Down
13 changes: 5 additions & 8 deletions examples/raytracing/game.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,13 +108,11 @@ impl Game {
}

// Upscale by `scale_factor`.
let width = buffer.width().get() as usize;
buffer.iter_mut().enumerate().for_each(|(i, pixel)| {
let y = i % width;
let x = i / width;
let y = (y as f32 / scale_factor) as usize;
let width = (buffer.width().get() as f32 / scale_factor) as usize;
buffer.pixels_iter().for_each(|(x, y, pixel)| {
let x = (x as f32 / scale_factor) as usize;
if let Some(x) = pixels.get(x * (width as f32 / scale_factor) as usize + y) {
let y = (y as f32 / scale_factor) as usize;
if let Some(x) = pixels.get(x * width + y) {
*pixel = *x;
}
});
Expand Down Expand Up @@ -149,8 +147,7 @@ impl Game {
},
];

let width = buffer.width().get();
for (y, row) in buffer.chunks_exact_mut(width as usize).enumerate() {
for (y, row) in buffer.pixel_rows().enumerate() {
for rect in rects {
let rect_vertical =
(rect.top * scale_factor) as usize..(rect.bottom * scale_factor) as usize;
Expand Down
21 changes: 9 additions & 12 deletions examples/rectangle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,15 @@ mod util;
fn redraw(buffer: &mut Buffer<'_>, flag: bool) {
let width = buffer.width().get();
let height = buffer.height().get();
for y in 0..height {
for x in 0..width {
let value = if flag && x >= 100 && x < width - 100 && y >= 100 && y < height - 100 {
0x00ffffff
} else {
let red = (x & 0xff) ^ (y & 0xff);
let green = (x & 0x7f) ^ (y & 0x7f);
let blue = (x & 0x3f) ^ (y & 0x3f);
blue | (green << 8) | (red << 16)
};
buffer[(y * width + x) as usize] = value;
}
for (x, y, pixel) in buffer.pixels_iter() {
*pixel = if flag && x >= 100 && x < width - 100 && y >= 100 && y < height - 100 {
0x00ffffff
} else {
let red = (x & 0xff) ^ (y & 0xff);
let green = (x & 0x7f) ^ (y & 0x7f);
let blue = (x & 0x3f) ^ (y & 0x3f);
blue | (green << 8) | (red << 16)
};
}
}

Expand Down
13 changes: 5 additions & 8 deletions examples/winit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,11 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
};

let mut buffer = surface.buffer_mut().unwrap();
for y in 0..buffer.height().get() {
for x in 0..buffer.width().get() {
let red = x % 255;
let green = y % 255;
let blue = (x * y) % 255;
let index = y * buffer.width().get() + x;
buffer[index as usize] = blue | (green << 8) | (red << 16);
}
for (x, y, pixel) in buffer.pixels_iter() {
let red = x % 255;
let green = y % 255;
let blue = (x * y) % 255;
*pixel = blue | (green << 8) | (red << 16);
}

buffer.present().unwrap();
Expand Down
13 changes: 5 additions & 8 deletions examples/winit_multithread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,11 @@ pub mod ex {
surface.resize(width, height).unwrap();

let mut buffer = surface.buffer_mut().unwrap();
for y in 0..buffer.height().get() {
for x in 0..buffer.width().get() {
let red = x % 255;
let green = y % 255;
let blue = (x * y) % 255;
let index = y * buffer.width().get() + x;
buffer[index as usize] = blue | (green << 8) | (red << 16);
}
for (x, y, pixel) in buffer.pixels_iter() {
let red = x % 255;
let green = y % 255;
let blue = (x * y) % 255;
*pixel = blue | (green << 8) | (red << 16);
}

tracing::info!("presenting...");
Expand Down
15 changes: 5 additions & 10 deletions examples/winit_wrong_sized_buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,11 @@ fn main() {
};

let mut buffer = surface.buffer_mut().unwrap();
let width = buffer.width().get();
for y in 0..buffer.height().get() {
for x in 0..width {
let red = x % 255;
let green = y % 255;
let blue = (x * y) % 255;

let color = blue | (green << 8) | (red << 16);
buffer[(y * width + x) as usize] = color;
}
for (x, y, pixel) in buffer.pixels_iter() {
let red = x % 255;
let green = y % 255;
let blue = (x * y) % 255;
*pixel = blue | (green << 8) | (red << 16);
}
buffer.present().unwrap();
}
Expand Down
112 changes: 112 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,118 @@ impl Buffer<'_> {
}
}

/// Pixel helper accessors.
impl Buffer<'_> {
/// Iterate over each row of pixels.
///
/// Each slice returned from the iterator has a length of `buffer.width()`.
///
/// # Examples
///
/// Fill each row with alternating black and white.
///
/// ```no_run
/// # let buffer: softbuffer::Buffer<'_> = unimplemented!();
/// for (y, row) in buffer.pixel_rows().enumerate() {
/// if y % 2 == 0 {
/// row.fill(0x00ffffff);
/// } else {
/// row.fill(0x00000000);
/// }
/// }
/// ```
///
/// Fill a red rectangle while skipping over regions that don't need to be modified.
///
/// ```no_run
/// # let buffer: softbuffer::Buffer<'_> = unimplemented!();
/// let x = 100;
/// let y = 200;
/// let width = 10;
/// let height = 20;
///
/// for row in buffer.pixel_rows().skip(y).take(height) {
/// for pixel in row.iter_mut().skip(x).take(width) {
/// *pixel = 0x00ff0000;
/// }
/// }
/// ```
///
/// Iterate over each pixel (similar to what the [`pixels_iter`] method does).
///
/// [`pixels_iter`]: Self::pixels_iter
///
/// ```no_run
/// # let buffer: softbuffer::Buffer<'_> = unimplemented!();
/// # let pixel_value = |x, y| 0x00000000;
/// for (y, row) in buffer.pixel_rows().enumerate() {
/// for (x, pixel) in row.iter_mut().enumerate() {
/// *pixel = pixel_value(x, y);
/// }
/// }
/// ```
#[inline]
pub fn pixel_rows(
&mut self,
) -> impl DoubleEndedIterator<Item = &mut [u32]> + ExactSizeIterator {
let width = self.width().get() as usize;
let pixels = self.buffer_impl.pixels_mut();
assert_eq!(pixels.len() % width, 0, "buffer must be multiple of width");
// NOTE: This won't panic because `width` is `NonZeroU32`
pixels.chunks_mut(width)
Comment on lines +329 to +333
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hope that we can soon drive this from the buffer instead, taking a stride into account.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Working on it ;)

}

/// Iterate over each pixel in the data.
///
/// The returned iterator contains the `x` and `y` coordinates and a mutable reference to the
/// pixel at that position.
///
/// # Examples
///
/// Draw a red rectangle with a margin of 10 pixels, and fill the background with blue.
///
/// ```no_run
/// # let buffer: softbuffer::Buffer<'_> = unimplemented!();
/// let width = buffer.width().get();
/// let height = buffer.height().get();
/// let left = 10;
/// let top = 10;
/// let right = width.saturating_sub(10);
/// let bottom = height.saturating_sub(10);
///
/// for (x, y, pixel) in buffer.pixels_iter() {
/// if (left..=right).contains(&x) && (top..=bottom).contains(&y) {
/// // Inside rectangle.
/// *pixel = 0x00ff0000;
/// } else {
/// // Outside rectangle.
/// *pixel = 0x000000ff;
/// };
/// }
/// ```
///
/// Iterate over the pixel data in reverse, and draw a red rectangle in the top-left corner.
///
/// ```no_run
/// # let buffer: softbuffer::Buffer<'_> = unimplemented!();
/// // Only reverses iteration order, x and y are still relative to the top-left corner.
/// for (x, y, pixel) in buffer.pixels_iter().rev() {
/// if x <= 100 && y <= 100 {
/// *pixel = 0x00ff0000;
/// }
/// }
/// ```
#[inline]
pub fn pixels_iter(&mut self) -> impl DoubleEndedIterator<Item = (u32, u32, &mut u32)> {
self.pixel_rows().enumerate().flat_map(|(y, pixels)| {
pixels
.iter_mut()
.enumerate()
.map(move |(x, pixel)| (x as u32, y as u32, pixel))
})
}
}

impl ops::Deref for Buffer<'_> {
type Target = [u32];

Expand Down