Skip to content

Commit 8ea991e

Browse files
committed
Add Buffer::pixel_rows and Buffer::pixels_iter iterators
1 parent ec10941 commit 8ea991e

File tree

9 files changed

+154
-63
lines changed

9 files changed

+154
-63
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# Unreleased
22

3-
- **Breaking:** Remove generic type parameters `D` and `W` from `Buffer<'_>` struct.
3+
- Added `Buffer::pixel_rows()` for iterating over rows of the buffer data.
4+
- Added `Buffer::pixels_iter()` for iterating over each pixel with its associated `x`/`y` coordinate.
5+
- **Breaking:** Removed generic type parameters `D` and `W` from `Buffer<'_>` struct.
46

57
# 0.4.7
68

README.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,14 +110,12 @@ fn main() {
110110
.unwrap();
111111
112112
let mut buffer = surface.buffer_mut().unwrap();
113-
for index in 0..(buffer.width().get() * buffer.height().get()) {
114-
let y = index / buffer.width().get();
115-
let x = index % buffer.width().get();
113+
for (x, y, pixel) in buffer.pixels_iter() {
116114
let red = x % 255;
117115
let green = y % 255;
118116
let blue = (x * y) % 255;
119117
120-
buffer[index as usize] = blue | (green << 8) | (red << 16);
118+
*pixel = blue | (green << 8) | (red << 16);
121119
}
122120
123121
buffer.present().unwrap();

benches/buffer_mut.rs

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,35 +40,31 @@ fn buffer_mut(c: &mut criterion::Criterion) {
4040
});
4141
});
4242

43-
c.bench_function("pixels_mut()", |b| {
43+
c.bench_function("data()", |b| {
4444
let mut buffer = surface.buffer_mut().unwrap();
4545
b.iter(|| {
4646
let pixels: &mut [u32] = &mut buffer;
4747
black_box(pixels);
4848
});
4949
});
5050

51-
c.bench_function("fill", |b| {
51+
c.bench_function("fill u32", |b| {
5252
let mut buffer = surface.buffer_mut().unwrap();
5353
b.iter(|| {
5454
let buffer = black_box(&mut buffer);
5555
buffer.fill(0x00000000);
5656
});
5757
});
5858

59-
c.bench_function("render", |b| {
59+
c.bench_function("render pixels_iter", |b| {
6060
let mut buffer = surface.buffer_mut().unwrap();
6161
b.iter(|| {
6262
let buffer = black_box(&mut buffer);
63-
let width = buffer.width().get();
64-
for y in 0..buffer.height().get() {
65-
for x in 0..buffer.width().get() {
66-
let red = (x & 0xff) ^ (y & 0xff);
67-
let green = (x & 0x7f) ^ (y & 0x7f);
68-
let blue = (x & 0x3f) ^ (y & 0x3f);
69-
let value = blue | (green << 8) | (red << 16);
70-
buffer[(y * width + x) as usize] = value;
71-
}
63+
for (x, y, pixel) in buffer.pixels_iter() {
64+
let red = (x & 0xff) ^ (y & 0xff);
65+
let green = (x & 0x7f) ^ (y & 0x7f);
66+
let blue = (x & 0x3f) ^ (y & 0x3f);
67+
*pixel = blue | (green << 8) | (red << 16);
7268
}
7369
});
7470
});

examples/raytracing/game.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -108,13 +108,11 @@ impl Game {
108108
}
109109

110110
// Upscale by `scale_factor`.
111-
let width = buffer.width().get() as usize;
112-
buffer.iter_mut().enumerate().for_each(|(i, pixel)| {
113-
let y = i % width;
114-
let x = i / width;
115-
let y = (y as f32 / scale_factor) as usize;
111+
let width = (buffer.width().get() as f32 / scale_factor) as usize;
112+
buffer.pixels_iter().for_each(|(x, y, pixel)| {
116113
let x = (x as f32 / scale_factor) as usize;
117-
if let Some(x) = pixels.get(x * (width as f32 / scale_factor) as usize + y) {
114+
let y = (y as f32 / scale_factor) as usize;
115+
if let Some(x) = pixels.get(x * width + y) {
118116
*pixel = *x;
119117
}
120118
});
@@ -149,8 +147,7 @@ impl Game {
149147
},
150148
];
151149

152-
let width = buffer.width().get();
153-
for (y, row) in buffer.chunks_exact_mut(width as usize).enumerate() {
150+
for (y, row) in buffer.pixel_rows().enumerate() {
154151
for rect in rects {
155152
let rect_vertical =
156153
(rect.top * scale_factor) as usize..(rect.bottom * scale_factor) as usize;

examples/rectangle.rs

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,15 @@ mod util;
99
fn redraw(buffer: &mut Buffer<'_>, flag: bool) {
1010
let width = buffer.width().get();
1111
let height = buffer.height().get();
12-
for y in 0..height {
13-
for x in 0..width {
14-
let value = if flag && x >= 100 && x < width - 100 && y >= 100 && y < height - 100 {
15-
0x00ffffff
16-
} else {
17-
let red = (x & 0xff) ^ (y & 0xff);
18-
let green = (x & 0x7f) ^ (y & 0x7f);
19-
let blue = (x & 0x3f) ^ (y & 0x3f);
20-
blue | (green << 8) | (red << 16)
21-
};
22-
buffer[(y * width + x) as usize] = value;
23-
}
12+
for (x, y, pixel) in buffer.pixels_iter() {
13+
*pixel = if flag && x >= 100 && x < width - 100 && y >= 100 && y < height - 100 {
14+
0x00ffffff
15+
} else {
16+
let red = (x & 0xff) ^ (y & 0xff);
17+
let green = (x & 0x7f) ^ (y & 0x7f);
18+
let blue = (x & 0x3f) ^ (y & 0x3f);
19+
blue | (green << 8) | (red << 16)
20+
};
2421
}
2522
}
2623

examples/winit.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,11 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
4646
};
4747

4848
let mut buffer = surface.buffer_mut().unwrap();
49-
for y in 0..buffer.height().get() {
50-
for x in 0..buffer.width().get() {
51-
let red = x % 255;
52-
let green = y % 255;
53-
let blue = (x * y) % 255;
54-
let index = y * buffer.width().get() + x;
55-
buffer[index as usize] = blue | (green << 8) | (red << 16);
56-
}
49+
for (x, y, pixel) in buffer.pixels_iter() {
50+
let red = x % 255;
51+
let green = y % 255;
52+
let blue = (x * y) % 255;
53+
*pixel = blue | (green << 8) | (red << 16);
5754
}
5855

5956
buffer.present().unwrap();

examples/winit_multithread.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,11 @@ pub mod ex {
3232
surface.resize(width, height).unwrap();
3333

3434
let mut buffer = surface.buffer_mut().unwrap();
35-
for y in 0..buffer.height().get() {
36-
for x in 0..buffer.width().get() {
37-
let red = x % 255;
38-
let green = y % 255;
39-
let blue = (x * y) % 255;
40-
let index = y * buffer.width().get() + x;
41-
buffer[index as usize] = blue | (green << 8) | (red << 16);
42-
}
35+
for (x, y, pixel) in buffer.pixels_iter() {
36+
let red = x % 255;
37+
let green = y % 255;
38+
let blue = (x * y) % 255;
39+
*pixel = blue | (green << 8) | (red << 16);
4340
}
4441

4542
tracing::info!("presenting...");

examples/winit_wrong_sized_buffer.rs

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,11 @@ fn main() {
3737
};
3838

3939
let mut buffer = surface.buffer_mut().unwrap();
40-
let width = buffer.width().get();
41-
for y in 0..buffer.height().get() {
42-
for x in 0..width {
43-
let red = x % 255;
44-
let green = y % 255;
45-
let blue = (x * y) % 255;
46-
47-
let color = blue | (green << 8) | (red << 16);
48-
buffer[(y * width + x) as usize] = color;
49-
}
40+
for (x, y, pixel) in buffer.pixels_iter() {
41+
let red = x % 255;
42+
let green = y % 255;
43+
let blue = (x * y) % 255;
44+
*pixel = blue | (green << 8) | (red << 16);
5045
}
5146
buffer.present().unwrap();
5247
}

src/lib.rs

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,118 @@ impl Buffer<'_> {
272272
}
273273
}
274274

275+
/// Pixel helper accessors.
276+
impl Buffer<'_> {
277+
/// Iterate over each row of pixels.
278+
///
279+
/// Each slice returned from the iterator has a length of `buffer.width()`.
280+
///
281+
/// # Examples
282+
///
283+
/// Fill each row with alternating black and white.
284+
///
285+
/// ```no_run
286+
/// # let buffer: softbuffer::Buffer<'_> = unimplemented!();
287+
/// for (y, row) in buffer.pixel_rows().enumerate() {
288+
/// if y % 2 == 0 {
289+
/// row.fill(0x00ffffff);
290+
/// } else {
291+
/// row.fill(0x00000000);
292+
/// }
293+
/// }
294+
/// ```
295+
///
296+
/// Fill a red rectangle while skipping over regions that don't need to be modified.
297+
///
298+
/// ```no_run
299+
/// # let buffer: softbuffer::Buffer<'_> = unimplemented!();
300+
/// let x = 100;
301+
/// let y = 200;
302+
/// let width = 10;
303+
/// let height = 20;
304+
///
305+
/// for row in buffer.pixel_rows().skip(y).take(height) {
306+
/// for pixel in row.iter_mut().skip(x).take(width) {
307+
/// *pixel = 0x00ff0000;
308+
/// }
309+
/// }
310+
/// ```
311+
///
312+
/// Iterate over each pixel (similar to what the [`pixels_iter`] method does).
313+
///
314+
/// [`pixels_iter`]: Self::pixels_iter
315+
///
316+
/// ```no_run
317+
/// # let buffer: softbuffer::Buffer<'_> = unimplemented!();
318+
/// # let pixel_value = |x, y| 0x00000000;
319+
/// for (y, row) in buffer.pixel_rows().enumerate() {
320+
/// for (x, pixel) in row.iter_mut().enumerate() {
321+
/// *pixel = pixel_value(x, y);
322+
/// }
323+
/// }
324+
/// ```
325+
#[inline]
326+
pub fn pixel_rows(
327+
&mut self,
328+
) -> impl DoubleEndedIterator<Item = &mut [u32]> + ExactSizeIterator {
329+
let width = self.width().get() as usize;
330+
let pixels = self.buffer_impl.pixels_mut();
331+
assert_eq!(pixels.len() % width, 0, "buffer must be multiple of width");
332+
// NOTE: This won't panic because `width` is `NonZeroU32`
333+
pixels.chunks_mut(width)
334+
}
335+
336+
/// Iterate over each pixel in the data.
337+
///
338+
/// The returned iterator contains the `x` and `y` coordinates and a mutable reference to the
339+
/// pixel at that position.
340+
///
341+
/// # Examples
342+
///
343+
/// Draw a red rectangle with a margin of 10 pixels, and fill the background with blue.
344+
///
345+
/// ```no_run
346+
/// # let buffer: softbuffer::Buffer<'_> = unimplemented!();
347+
/// let width = buffer.width().get();
348+
/// let height = buffer.height().get();
349+
/// let left = 10;
350+
/// let top = 10;
351+
/// let right = width.saturating_sub(10);
352+
/// let bottom = height.saturating_sub(10);
353+
///
354+
/// for (x, y, pixel) in buffer.pixels_iter() {
355+
/// if (left..=right).contains(&x) && (top..=bottom).contains(&y) {
356+
/// // Inside rectangle.
357+
/// *pixel = 0x00ff0000;
358+
/// } else {
359+
/// // Outside rectangle.
360+
/// *pixel = 0x000000ff;
361+
/// };
362+
/// }
363+
/// ```
364+
///
365+
/// Iterate over the pixel data in reverse, and draw a red rectangle in the top-left corner.
366+
///
367+
/// ```no_run
368+
/// # let buffer: softbuffer::Buffer<'_> = unimplemented!();
369+
/// // Only reverses iteration order, x and y are still relative to the top-left corner.
370+
/// for (x, y, pixel) in buffer.pixels_iter().rev() {
371+
/// if x <= 100 && y <= 100 {
372+
/// *pixel = 0x00ff0000;
373+
/// }
374+
/// }
375+
/// ```
376+
#[inline]
377+
pub fn pixels_iter(&mut self) -> impl DoubleEndedIterator<Item = (u32, u32, &mut u32)> {
378+
self.pixel_rows().enumerate().flat_map(|(y, pixels)| {
379+
pixels
380+
.iter_mut()
381+
.enumerate()
382+
.map(move |(x, pixel)| (x as u32, y as u32, pixel))
383+
})
384+
}
385+
}
386+
275387
impl ops::Deref for Buffer<'_> {
276388
type Target = [u32];
277389

0 commit comments

Comments
 (0)