Use u32/NonZeroU32 for Rect, and return error if out of range

This commit is contained in:
Ian Douglas Scott 2023-05-30 15:17:10 -07:00
parent 199a016f44
commit a147a15d45
6 changed files with 129 additions and 136 deletions

View file

@ -36,6 +36,11 @@ pub enum SoftBufferError {
height: NonZeroU32, height: NonZeroU32,
}, },
#[error(
"Damage rect {}x{} at ({}, {}) out of range for backend.", .rect.width, .rect.height, .rect.x, .rect.y,
)]
DamageOutOfRange { rect: crate::Rect },
#[error("Platform error")] #[error("Platform error")]
PlatformError(Option<String>, Option<Box<dyn Error>>), PlatformError(Option<String>, Option<Box<dyn Error>>),
} }

View file

@ -233,13 +233,13 @@ impl Context {
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct Rect { pub struct Rect {
/// x coordinate of top left corner /// x coordinate of top left corner
pub x: i32, pub x: u32,
/// y coordinate of top left corner /// y coordinate of top left corner
pub y: i32, pub y: u32,
/// width /// width
pub width: i32, pub width: NonZeroU32,
/// height /// height
pub height: i32, pub height: NonZeroU32,
} }
/// A surface for drawing to a window with software buffers. /// A surface for drawing to a window with software buffers.

View file

@ -159,15 +159,18 @@ impl WaylandImpl {
if self.surface.version() < 4 { if self.surface.version() < 4 {
self.surface.damage(0, 0, i32::MAX, i32::MAX); self.surface.damage(0, 0, i32::MAX, i32::MAX);
} else { } else {
for Rect { for rect in damage {
x,
y,
width,
height,
} in damage
{
// Introduced in version 4, it is an error to use this request in version 3 or lower. // Introduced in version 4, it is an error to use this request in version 3 or lower.
self.surface.damage_buffer(*x, *y, *width, *height); 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);
} }
} }
@ -212,8 +215,9 @@ impl<'a> BufferImpl<'a> {
imp.present_with_damage(&[Rect { imp.present_with_damage(&[Rect {
x: 0, x: 0,
y: 0, y: 0,
width: width.get(), // We know width/height will be non-negative
height: height.get(), width: width.try_into().unwrap(),
height: height.try_into().unwrap(),
}]) }])
} }
} }

View file

@ -45,11 +45,8 @@ pub struct WebImpl {
/// Buffer has been presented. /// Buffer has been presented.
buffer_presented: bool, buffer_presented: bool,
/// The current width of the canvas. /// The current canvas width/height.
width: u32, size: Option<(NonZeroU32, NonZeroU32)>,
/// The current height of the canvas.
height: u32,
} }
impl WebImpl { impl WebImpl {
@ -82,8 +79,7 @@ impl WebImpl {
ctx, ctx,
buffer: Vec::new(), buffer: Vec::new(),
buffer_presented: false, buffer_presented: false,
width: 0, size: None,
height: 0,
}) })
} }
@ -93,16 +89,12 @@ impl WebImpl {
width: NonZeroU32, width: NonZeroU32,
height: NonZeroU32, height: NonZeroU32,
) -> Result<(), SoftBufferError> { ) -> Result<(), SoftBufferError> {
let width = width.get(); if self.size != Some((width, height)) {
let height = height.get();
if width != self.width || height != self.height {
self.buffer_presented = false; self.buffer_presented = false;
self.buffer.resize(total_len(width, height), 0); self.buffer.resize(total_len(width.get(), height.get()), 0);
self.canvas.set_width(width); self.canvas.set_width(width.get());
self.canvas.set_height(height); self.canvas.set_height(height.get());
self.width = width; self.size = Some((width, height));
self.height = height;
} }
Ok(()) Ok(())
@ -114,6 +106,9 @@ impl WebImpl {
} }
fn present_with_damage(&mut self, damage: &[Rect]) -> Result<(), SoftBufferError> { fn present_with_damage(&mut self, damage: &[Rect]) -> Result<(), SoftBufferError> {
let (width, _height) = self
.size
.expect("Must set size of surface before calling `present_with_damage()`");
// Create a bitmap from the buffer. // Create a bitmap from the buffer.
let bitmap: Vec<_> = self let bitmap: Vec<_> = self
.buffer .buffer
@ -140,33 +135,27 @@ impl WebImpl {
let array = Uint8Array::new_with_length(bitmap.len() as u32); let array = Uint8Array::new_with_length(bitmap.len() as u32);
array.copy_from(&bitmap); array.copy_from(&bitmap);
let array = Uint8ClampedArray::new(&array); let array = Uint8ClampedArray::new(&array);
ImageDataExt::new(array, self.width) ImageDataExt::new(array, width.get())
.map(JsValue::from) .map(JsValue::from)
.map(ImageData::unchecked_from_js) .map(ImageData::unchecked_from_js)
}; };
#[cfg(not(target_feature = "atomics"))] #[cfg(not(target_feature = "atomics"))]
let result = let result =
ImageData::new_with_u8_clamped_array(wasm_bindgen::Clamped(&bitmap), self.width); ImageData::new_with_u8_clamped_array(wasm_bindgen::Clamped(&bitmap), width.get());
// This should only throw an error if the buffer we pass's size is incorrect. // This should only throw an error if the buffer we pass's size is incorrect.
let image_data = result.unwrap(); let image_data = result.unwrap();
for Rect { for rect in damage {
x,
y,
width,
height,
} in damage
{
// This can only throw an error if `data` is detached, which is impossible. // This can only throw an error if `data` is detached, which is impossible.
self.ctx self.ctx
.put_image_data_with_dirty_x_and_dirty_y_and_dirty_width_and_dirty_height( .put_image_data_with_dirty_x_and_dirty_y_and_dirty_width_and_dirty_height(
&image_data, &image_data,
(*x).into(), rect.x.into(),
(*y).into(), rect.y.into(),
(*x).into(), rect.x.into(),
(*y).into(), rect.y.into(),
(*width).into(), rect.width.get().into(),
(*height).into(), rect.height.get().into(),
) )
.unwrap(); .unwrap();
} }
@ -217,16 +206,10 @@ impl<'a> BufferImpl<'a> {
/// Push the buffer to the canvas. /// Push the buffer to the canvas.
pub fn present(self) -> Result<(), SoftBufferError> { pub fn present(self) -> Result<(), SoftBufferError> {
let (width, height) = (|| { let (width, height) = self
let width = i32::try_from(self.imp.width).ok()?; .imp
let height = i32::try_from(self.imp.height).ok()?; .size
Some((width, height)) .expect("Must set size of surface before calling `present()`");
})()
.ok_or(SoftBufferError::SizeOutOfRange {
width: NonZeroU32::new(self.imp.width).unwrap(),
height: NonZeroU32::new(self.imp.height).unwrap(),
})?;
self.imp.present_with_damage(&[Rect { self.imp.present_with_damage(&[Rect {
x: 0, x: 0,
y: 0, y: 0,

View file

@ -208,24 +208,17 @@ impl Win32Impl {
fn present_with_damage(&mut self, damage: &[Rect]) -> Result<(), SoftBufferError> { fn present_with_damage(&mut self, damage: &[Rect]) -> Result<(), SoftBufferError> {
let buffer = self.buffer.as_mut().unwrap(); let buffer = self.buffer.as_mut().unwrap();
unsafe { unsafe {
for Rect { for rect in damage.iter().copied() {
x, let (x, y, width, height) = (|| {
y, Some((
width, i32::try_from(rect.x).ok()?,
height, i32::try_from(rect.y).ok()?,
} in damage i32::try_from(rect.width.get()).ok()?,
{ i32::try_from(rect.height.get()).ok()?,
Gdi::BitBlt( ))
self.dc, })()
*x, .ok_or(SoftBufferError::DamageOutOfRange { rect })?;
*y, Gdi::BitBlt(self.dc, x, y, width, height, buffer.dc, x, y, Gdi::SRCCOPY);
*width,
*height,
buffer.dc,
*x,
*y,
Gdi::SRCCOPY,
);
} }
// Validate the window. // Validate the window.
@ -263,8 +256,9 @@ impl<'a> BufferImpl<'a> {
imp.present_with_damage(&[Rect { imp.present_with_damage(&[Rect {
x: 0, x: 0,
y: 0, y: 0,
width: buffer.width.get(), // We know width/height will be non-negative
height: buffer.height.get(), width: buffer.width.try_into().unwrap(),
height: buffer.height.try_into().unwrap(),
}]) }])
} }

View file

@ -10,7 +10,12 @@ use crate::{Rect, SoftBufferError};
use nix::libc::{shmat, shmctl, shmdt, shmget, IPC_PRIVATE, IPC_RMID}; use nix::libc::{shmat, shmctl, shmdt, shmget, IPC_PRIVATE, IPC_RMID};
use raw_window_handle::{XcbDisplayHandle, XcbWindowHandle, XlibDisplayHandle, XlibWindowHandle}; use raw_window_handle::{XcbDisplayHandle, XcbWindowHandle, XlibDisplayHandle, XlibWindowHandle};
use std::ptr::{null_mut, NonNull}; use std::ptr::{null_mut, NonNull};
use std::{fmt, io, mem, num::NonZeroU32, rc::Rc, slice}; use std::{
fmt, io, mem,
num::{NonZeroU16, NonZeroU32},
rc::Rc,
slice,
};
use x11_dl::xlib::Display; use x11_dl::xlib::Display;
use x11_dl::xlib_xcb::Xlib_xcb; use x11_dl::xlib_xcb::Xlib_xcb;
@ -110,11 +115,8 @@ pub struct X11Impl {
/// Buffer has been presented. /// Buffer has been presented.
buffer_presented: bool, buffer_presented: bool,
/// The current buffer width. /// The current buffer width/height.
width: u16, size: Option<(NonZeroU16, NonZeroU16)>,
/// The current buffer height.
height: u16,
} }
/// The buffer that is being drawn to. /// The buffer that is being drawn to.
@ -227,8 +229,7 @@ impl X11Impl {
depth: geometry_reply.depth, depth: geometry_reply.depth,
buffer, buffer,
buffer_presented: false, buffer_presented: false,
width: 0, size: None,
height: 0,
}) })
} }
@ -246,27 +247,22 @@ impl X11Impl {
); );
// Width and height should fit in u16. // Width and height should fit in u16.
let width: u16 = width let width: NonZeroU16 = width
.get()
.try_into() .try_into()
.or(Err(SoftBufferError::SizeOutOfRange { width, height }))?; .or(Err(SoftBufferError::SizeOutOfRange { width, height }))?;
let height: u16 = height let height: NonZeroU16 = height.try_into().or(Err(SoftBufferError::SizeOutOfRange {
.get() width: width.into(),
.try_into() height,
.or(Err(SoftBufferError::SizeOutOfRange { }))?;
width: NonZeroU32::new(width.into()).unwrap(),
height,
}))?;
if width != self.width || height != self.height { if self.size != Some((width, height)) {
self.buffer_presented = false; self.buffer_presented = false;
self.buffer self.buffer
.resize(&self.display.connection, width, height) .resize(&self.display.connection, width.get(), height.get())
.swbuf_err("Failed to resize X11 buffer")?; .swbuf_err("Failed to resize X11 buffer")?;
// We successfully resized the buffer. // We successfully resized the buffer.
self.width = width; self.size = Some((width, height));
self.height = height;
} }
Ok(()) Ok(())
@ -311,6 +307,10 @@ impl<'a> BufferImpl<'a> {
pub fn present_with_damage(self, damage: &[Rect]) -> Result<(), SoftBufferError> { pub fn present_with_damage(self, damage: &[Rect]) -> Result<(), SoftBufferError> {
let imp = self.0; let imp = self.0;
let (surface_width, surface_height) = imp
.size
.expect("Must set size of surface before calling `present_with_damage()`");
log::trace!("present: window={:X}", imp.window); log::trace!("present: window={:X}", imp.window);
match imp.buffer { match imp.buffer {
@ -324,8 +324,8 @@ impl<'a> BufferImpl<'a> {
xproto::ImageFormat::Z_PIXMAP, xproto::ImageFormat::Z_PIXMAP,
imp.window, imp.window,
imp.gc, imp.gc,
imp.width, surface_width.get(),
imp.height, surface_height.get(),
0, 0,
0, 0,
0, 0,
@ -334,6 +334,7 @@ impl<'a> BufferImpl<'a> {
) )
.map(|c| c.ignore_error()) .map(|c| c.ignore_error())
.push_err() .push_err()
.swbuf_err("Failed to draw image to window")?;
} }
Buffer::Shm(ref mut shm) => { Buffer::Shm(ref mut shm) => {
@ -343,46 +344,50 @@ impl<'a> BufferImpl<'a> {
if let Some((_, segment_id)) = shm.seg { if let Some((_, segment_id)) = shm.seg {
damage damage
.iter() .iter()
.try_for_each( .try_for_each(|rect| {
|Rect { let (src_x, src_y, dst_x, dst_y, width, height) = (|| {
x, Some((
y, u16::try_from(rect.x).ok()?,
width, u16::try_from(rect.y).ok()?,
height, i16::try_from(rect.x).ok()?,
}| { i16::try_from(rect.y).ok()?,
imp.display u16::try_from(rect.width.get()).ok()?,
.connection u16::try_from(rect.height.get()).ok()?,
.shm_put_image( ))
imp.window, })(
imp.gc, )
imp.width, .ok_or(SoftBufferError::DamageOutOfRange { rect: *rect })?;
imp.height, imp.display
*x as u16, .connection
*y as u16, .shm_put_image(
*width as u16, imp.window,
*height as u16, imp.gc,
*x as i16, surface_width.get(),
*y as i16, surface_height.get(),
imp.depth, src_x,
xproto::ImageFormat::Z_PIXMAP.into(), src_y,
false, width,
segment_id, height,
0, dst_x,
) dst_y,
.push_err() imp.depth,
.map(|c| c.ignore_error()) xproto::ImageFormat::Z_PIXMAP.into(),
}, false,
) segment_id,
0,
)
.push_err()
.map(|c| c.ignore_error())
.swbuf_err("Failed to draw image to window")
})
.and_then(|()| { .and_then(|()| {
// Send a short request to act as a notification for when the X server is done processing the image. // Send a short request to act as a notification for when the X server is done processing the image.
shm.begin_wait(&imp.display.connection) shm.begin_wait(&imp.display.connection)
}) .swbuf_err("Failed to draw image to window")
} else { })?;
Ok(())
} }
} }
} }
.swbuf_err("Failed to draw image to window")?;
imp.buffer_presented = true; imp.buffer_presented = true;
@ -390,13 +395,15 @@ impl<'a> BufferImpl<'a> {
} }
pub fn present(self) -> Result<(), SoftBufferError> { pub fn present(self) -> Result<(), SoftBufferError> {
let width = self.0.width.into(); let (width, height) = self
let height = self.0.height.into(); .0
.size
.expect("Must set size of surface before calling `present()`");
self.present_with_damage(&[Rect { self.present_with_damage(&[Rect {
x: 0, x: 0,
y: 0, y: 0,
width, width: width.into(),
height, height: height.into(),
}]) }])
} }
} }