diff --git a/CHANGELOG.md b/CHANGELOG.md index ec65322f..7097bdd0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/README.md b/README.md index 2b1d4d89..5c59ee24 100644 --- a/README.md +++ b/README.md @@ -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(); diff --git a/benches/buffer_mut.rs b/benches/buffer_mut.rs index a121a852..b9b8e137 100644 --- a/benches/buffer_mut.rs +++ b/benches/buffer_mut.rs @@ -40,7 +40,7 @@ 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; @@ -48,7 +48,7 @@ fn buffer_mut(c: &mut criterion::Criterion) { }); }); - 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); @@ -56,19 +56,15 @@ fn buffer_mut(c: &mut criterion::Criterion) { }); }); - 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); } }); }); diff --git a/examples/raytracing/game.rs b/examples/raytracing/game.rs index 6629c261..554477ad 100644 --- a/examples/raytracing/game.rs +++ b/examples/raytracing/game.rs @@ -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; } }); @@ -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; diff --git a/examples/rectangle.rs b/examples/rectangle.rs index 5fde1d5b..1124ea0c 100644 --- a/examples/rectangle.rs +++ b/examples/rectangle.rs @@ -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) + }; } } diff --git a/examples/winit.rs b/examples/winit.rs index 330860d9..240a04fc 100644 --- a/examples/winit.rs +++ b/examples/winit.rs @@ -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(); diff --git a/examples/winit_multithread.rs b/examples/winit_multithread.rs index 369eeaa6..52791f14 100644 --- a/examples/winit_multithread.rs +++ b/examples/winit_multithread.rs @@ -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..."); diff --git a/examples/winit_wrong_sized_buffer.rs b/examples/winit_wrong_sized_buffer.rs index 22b25475..0160f771 100644 --- a/examples/winit_wrong_sized_buffer.rs +++ b/examples/winit_wrong_sized_buffer.rs @@ -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(); } diff --git a/src/lib.rs b/src/lib.rs index 82e0f9d8..34110a2b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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 + 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) + } + + /// 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 { + 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];