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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
- 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.
- **Breaking:** Removed `Deref[Mut]` implementation on `Buffer<'_>`. Use `Buffer::pixels()` instead.
- **Breaking:** Removed `DamageOutOfRange` error case. If the damage value is greater than the backend supports, it is instead clamped to an appropriate value.
- Fixed `present_with_damage` with bounds out of range on Windows, Web and X11.

# 0.4.7

Expand Down
9 changes: 0 additions & 9 deletions src/backend_dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,15 +185,6 @@ macro_rules! make_dispatch {
}
}

fn present(self) -> Result<(), SoftBufferError> {
match self {
$(
$(#[$attr])*
Self::$name(inner) => inner.present(),
)*
}
}

fn present_with_damage(self, damage: &[Rect]) -> Result<(), SoftBufferError> {
match self {
$(
Expand Down
1 change: 0 additions & 1 deletion src/backend_interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,4 @@ pub(crate) trait BufferInterface {
fn pixels_mut(&mut self) -> &mut [u32];
fn age(&self) -> u8;
fn present_with_damage(self, damage: &[Rect]) -> Result<(), SoftBufferError>;
fn present(self) -> Result<(), SoftBufferError>;
}
19 changes: 11 additions & 8 deletions src/backends/android.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,17 @@ impl BufferInterface for BufferImpl<'_> {
}

// TODO: This function is pretty slow this way
fn present(mut self) -> Result<(), SoftBufferError> {
fn present_with_damage(mut self, damage: &[Rect]) -> Result<(), SoftBufferError> {
// TODO: Android requires the damage rect _at lock time_
// Since we're faking the backing buffer _anyway_, we could even fake the surface lock
// and lock it here (if it doesn't influence timings).
//
// Android seems to do this because the region can be expanded by the
// system, requesting the user to actually redraw a larger region.
// It's unclear if/when this is used, or if corruption/artifacts occur
// when the enlarged damage region is not re-rendered?
let _ = damage;

let input_lines = self.buffer.chunks(self.native_window_buffer.width());
for (output, input) in self
.native_window_buffer
Expand All @@ -165,11 +175,4 @@ impl BufferInterface for BufferImpl<'_> {
}
Ok(())
}

fn present_with_damage(self, _damage: &[Rect]) -> Result<(), SoftBufferError> {
// TODO: Android requires the damage rect _at lock time_
// Since we're faking the backing buffer _anyway_, we could even fake the surface lock
// and lock it here (if it doesn't influence timings).
self.present()
}
}
6 changes: 1 addition & 5 deletions src/backends/cg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ impl BufferInterface for BufferImpl<'_> {
0
}

fn present(self) -> Result<(), SoftBufferError> {
fn present_with_damage(self, _damage: &[Rect]) -> Result<(), SoftBufferError> {
unsafe extern "C-unwind" fn release(
_info: *mut c_void,
data: NonNull<c_void>,
Expand Down Expand Up @@ -361,10 +361,6 @@ impl BufferInterface for BufferImpl<'_> {
CATransaction::commit();
Ok(())
}

fn present_with_damage(self, _damage: &[Rect]) -> Result<(), SoftBufferError> {
self.present()
}
}

#[derive(Debug)]
Expand Down
32 changes: 10 additions & 22 deletions src/backends/kms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -325,24 +325,23 @@ impl BufferInterface for BufferImpl<'_> {

#[inline]
fn present_with_damage(self, damage: &[crate::Rect]) -> Result<(), SoftBufferError> {
let rectangles = damage
let rectangles: Vec<_> = damage
.iter()
.map(|&rect| {
let err = || SoftBufferError::DamageOutOfRange { rect };
Ok::<_, SoftBufferError>(ClipRect::new(
rect.x.try_into().map_err(|_| err())?,
rect.y.try_into().map_err(|_| err())?,
.map(|rect| {
ClipRect::new(
rect.x.try_into().unwrap_or(u16::MAX),
rect.y.try_into().unwrap_or(u16::MAX),
Comment on lines +332 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.

Same here, it seems that not having x or y means the rect should be infinitely large, starting at 0 rather than MAX (and ending at MAX, making it 0×0)?

rect.x
.checked_add(rect.width.get())
.and_then(|x| x.try_into().ok())
.ok_or_else(err)?,
.unwrap_or(u16::MAX),
rect.y
.checked_add(rect.height.get())
.and_then(|y| y.try_into().ok())
.ok_or_else(err)?,
))
.and_then(|x| x.try_into().ok())
.unwrap_or(u16::MAX),
)
})
.collect::<Result<Vec<_>, _>>()?;
.collect();

// Dirty the framebuffer with out damage rectangles.
//
Expand Down Expand Up @@ -378,17 +377,6 @@ impl BufferInterface for BufferImpl<'_> {

Ok(())
}

#[inline]
fn present(self) -> Result<(), SoftBufferError> {
let (width, height) = self.size;
self.present_with_damage(&[crate::Rect {
x: 0,
y: 0,
width,
height,
}])
}
}

impl SharedBuffer {
Expand Down
6 changes: 1 addition & 5 deletions src/backends/orbital.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ impl BufferInterface for BufferImpl<'_> {
}
}

fn present(self) -> Result<(), SoftBufferError> {
fn present_with_damage(self, _damage: &[Rect]) -> Result<(), SoftBufferError> {
match self.pixels {
Pixels::Mapping(mapping) => {
drop(mapping);
Expand All @@ -190,10 +190,6 @@ impl BufferInterface for BufferImpl<'_> {

Ok(())
}

fn present_with_damage(self, _damage: &[Rect]) -> Result<(), SoftBufferError> {
self.present()
}
}

// Read the current width and size
Expand Down
27 changes: 7 additions & 20 deletions src/backends/wayland/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,16 +260,14 @@ impl BufferInterface for BufferImpl<'_> {
self.surface.damage(0, 0, i32::MAX, i32::MAX);
} else {
for rect in damage {
// Damage that falls outside the surface is ignored.
// https://wayland.freedesktop.org/docs/html/apa.html#protocol-spec-wl_surface
let x = rect.x.try_into().unwrap_or(i32::MAX);
let y = rect.y.try_into().unwrap_or(i32::MAX);
Comment on lines +265 to +266
Copy link
Member

Choose a reason for hiding this comment

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

MIN or 0?

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm, no, rect.x is a u32, the problem we're protecting against is if it's a large value.

let width = rect.width.get().try_into().unwrap_or(i32::MAX);
let height = rect.height.get().try_into().unwrap_or(i32::MAX);

// Introduced in version 4, it is an error to use this request in version 3 or lower.
let (x, y, width, height) = (|| {
Some((
i32::try_from(rect.x).ok()?,
i32::try_from(rect.y).ok()?,
i32::try_from(rect.width.get()).ok()?,
i32::try_from(rect.height.get()).ok()?,
))
})()
.ok_or(SoftBufferError::DamageOutOfRange { rect: *rect })?;
self.surface.damage_buffer(x, y, width, height);
}
}
Expand All @@ -284,17 +282,6 @@ impl BufferInterface for BufferImpl<'_> {

Ok(())
}

fn present(self) -> Result<(), SoftBufferError> {
let (width, height) = (self.width, self.height);
self.present_with_damage(&[Rect {
x: 0,
y: 0,
// We know width/height will be non-negative and non-zero.
width: (width as u32).try_into().unwrap(),
height: (height as u32).try_into().unwrap(),
}])
}
}

impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for State {
Expand Down
32 changes: 11 additions & 21 deletions src/backends/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -331,20 +331,8 @@ impl BufferInterface for BufferImpl<'_> {
}

/// Push the buffer to the canvas.
fn present(self) -> Result<(), SoftBufferError> {
let (width, height) = self
.size
.expect("Must set size of surface before calling `present()`");
self.present_with_damage(&[Rect {
x: 0,
y: 0,
width,
height,
}])
}

fn present_with_damage(self, damage: &[Rect]) -> Result<(), SoftBufferError> {
let (buffer_width, _buffer_height) = self
let (buffer_width, buffer_height) = self
.size
.expect("Must set size of surface before calling `present_with_damage()`");

Expand All @@ -370,8 +358,10 @@ impl BufferInterface for BufferImpl<'_> {
.collect();

debug_assert_eq!(
bitmap.len() as u32,
union_damage.width.get() * union_damage.height.get() * 4
bitmap.len(),
union_damage.width.get().min(buffer_width.get()) as usize
* union_damage.height.get().min(buffer_height.get()) as usize
* 4
);

#[cfg(target_feature = "atomics")]
Expand All @@ -394,14 +384,14 @@ impl BufferInterface for BufferImpl<'_> {
let array = Uint8Array::new_with_length(bitmap.len() as u32);
array.copy_from(&bitmap);
let array = Uint8ClampedArray::new(&array);
ImageDataExt::new(array, union_damage.width.get())
ImageDataExt::new(array, union_damage.width.get().min(buffer_width.get()))
.map(JsValue::from)
.map(ImageData::unchecked_from_js)
};
#[cfg(not(target_feature = "atomics"))]
let result = ImageData::new_with_u8_clamped_array(
wasm_bindgen::Clamped(&bitmap),
union_damage.width.get(),
union_damage.width.get().min(buffer_width.get()),
);
// This should only throw an error if the buffer we pass's size is incorrect.
let image_data = result.unwrap();
Expand All @@ -411,12 +401,12 @@ impl BufferInterface for BufferImpl<'_> {
self.canvas
.put_image_data(
&image_data,
union_damage.x.into(),
union_damage.y.into(),
union_damage.x.min(buffer_width.get()).into(),
union_damage.y.min(buffer_height.get()).into(),
(rect.x - union_damage.x).into(),
(rect.y - union_damage.y).into(),
rect.width.get().into(),
rect.height.get().into(),
rect.width.get().min(buffer_width.get()).into(),
rect.height.get().min(buffer_height.get()).into(),
Comment on lines +408 to +409
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't the width/height take into account the x/y offset before calling .min()?

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not entirely sure, but I think no? See the docs for dirtyWidth:
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/putImageData#dirtywidth

I'd assume it's correct then to pass the size of the surface, though to be honest, I haven't really tested the Web part that much, only that it doesn't crash with width/height = u32::MAX.

)
.unwrap();
}
Expand Down
37 changes: 11 additions & 26 deletions src/backends/win32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,38 +271,23 @@ impl BufferInterface for BufferImpl<'_> {
}
}

fn present(self) -> Result<(), SoftBufferError> {
let (width, height) = (self.buffer.width, self.buffer.height);
self.present_with_damage(&[Rect {
x: 0,
y: 0,
// We know width/height will be non-negative
width: width.try_into().unwrap(),
height: height.try_into().unwrap(),
}])
}

fn present_with_damage(self, damage: &[Rect]) -> Result<(), SoftBufferError> {
unsafe {
for rect in damage.iter().copied() {
let (x, y, width, height) = (|| {
Some((
i32::try_from(rect.x).ok()?,
i32::try_from(rect.y).ok()?,
i32::try_from(rect.width.get()).ok()?,
i32::try_from(rect.height.get()).ok()?,
))
})()
.ok_or(SoftBufferError::DamageOutOfRange { rect })?;
let x = rect.x.try_into().unwrap_or(i32::MAX);
let y = rect.y.try_into().unwrap_or(i32::MAX);
let width = rect.width.get().try_into().unwrap_or(i32::MAX);
let height = rect.height.get().try_into().unwrap_or(i32::MAX);

Gdi::BitBlt(
self.dc.0,
x,
y,
width,
height,
x.min(self.buffer.width.get()),
y.min(self.buffer.height.get()),
width.min(self.buffer.width.get()),
height.min(self.buffer.height.get()),
self.buffer.dc,
x,
y,
x.min(self.buffer.width.get()),
y.min(self.buffer.height.get()),
Gdi::SRCCOPY,
);
}
Expand Down
43 changes: 13 additions & 30 deletions src/backends/x11.rs
Original file line number Diff line number Diff line change
Expand Up @@ -469,30 +469,25 @@ impl BufferInterface for BufferImpl<'_> {
damage
.iter()
.try_for_each(|rect| {
let (src_x, src_y, dst_x, dst_y, width, height) = (|| {
Some((
u16::try_from(rect.x).ok()?,
u16::try_from(rect.y).ok()?,
i16::try_from(rect.x).ok()?,
i16::try_from(rect.y).ok()?,
u16::try_from(rect.width.get()).ok()?,
u16::try_from(rect.height.get()).ok()?,
))
})(
)
.ok_or(SoftBufferError::DamageOutOfRange { rect: *rect })?;
let src_x = rect.x.try_into().unwrap_or(u16::MAX);
let src_y = rect.y.try_into().unwrap_or(u16::MAX);
let dst_x = rect.x.try_into().unwrap_or(i16::MAX);
let dst_y = rect.y.try_into().unwrap_or(i16::MAX);
let width = rect.width.get().try_into().unwrap_or(u16::MAX);
let height = rect.height.get().try_into().unwrap_or(u16::MAX);

self.connection
.shm_put_image(
self.window,
self.gc,
surface_width.get(),
surface_height.get(),
src_x,
src_y,
width,
height,
dst_x,
dst_y,
src_x.min(surface_width.get()),
src_y.min(surface_height.get()),
width.min(surface_width.get()),
height.min(surface_height.get()),
dst_x.min(surface_width.get() as i16),
dst_y.min(surface_height.get() as i16),
self.depth,
xproto::ImageFormat::Z_PIXMAP.into(),
false,
Expand All @@ -516,18 +511,6 @@ impl BufferInterface for BufferImpl<'_> {

Ok(())
}

fn present(self) -> Result<(), SoftBufferError> {
let (width, height) = self
.size
.expect("Must set size of surface before calling `present()`");
self.present_with_damage(&[Rect {
x: 0,
y: 0,
width: width.into(),
height: height.into(),
}])
}
}

impl Buffer {
Expand Down
Loading