2022-12-21 17:26:09 -08:00
|
|
|
//! Implementation of software buffering for Windows.
|
2022-12-22 10:09:47 -08:00
|
|
|
//!
|
2022-12-21 17:26:09 -08:00
|
|
|
//! This module converts the input buffer into a bitmap and then stretches it to the window.
|
|
|
|
|
|
2023-04-21 10:37:48 -07:00
|
|
|
use crate::{Rect, SoftBufferError};
|
2022-12-20 08:43:26 -07:00
|
|
|
use raw_window_handle::Win32WindowHandle;
|
2022-12-21 17:26:09 -08:00
|
|
|
|
|
|
|
|
use std::io;
|
2022-12-22 10:09:47 -08:00
|
|
|
use std::mem;
|
2023-04-06 00:30:59 -07:00
|
|
|
use std::num::{NonZeroI32, NonZeroU32};
|
|
|
|
|
use std::ptr::{self, NonNull};
|
|
|
|
|
use std::slice;
|
2022-12-20 08:08:46 -08:00
|
|
|
|
|
|
|
|
use windows_sys::Win32::Foundation::HWND;
|
2023-04-06 00:30:59 -07:00
|
|
|
use windows_sys::Win32::Graphics::Gdi;
|
|
|
|
|
|
|
|
|
|
const ZERO_QUAD: Gdi::RGBQUAD = Gdi::RGBQUAD {
|
|
|
|
|
rgbBlue: 0,
|
|
|
|
|
rgbGreen: 0,
|
|
|
|
|
rgbRed: 0,
|
|
|
|
|
rgbReserved: 0,
|
2022-12-20 08:08:46 -08:00
|
|
|
};
|
2022-01-16 08:03:20 -06:00
|
|
|
|
2023-04-06 00:30:59 -07:00
|
|
|
struct Buffer {
|
|
|
|
|
dc: Gdi::HDC,
|
|
|
|
|
bitmap: Gdi::HBITMAP,
|
|
|
|
|
pixels: NonNull<u32>,
|
|
|
|
|
width: NonZeroI32,
|
|
|
|
|
height: NonZeroI32,
|
2023-04-20 18:52:34 -07:00
|
|
|
presented: bool,
|
2023-04-06 00:30:59 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Drop for Buffer {
|
|
|
|
|
fn drop(&mut self) {
|
|
|
|
|
unsafe {
|
|
|
|
|
Gdi::DeleteDC(self.dc);
|
|
|
|
|
Gdi::DeleteObject(self.bitmap);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Buffer {
|
|
|
|
|
fn new(window_dc: Gdi::HDC, width: NonZeroI32, height: NonZeroI32) -> Self {
|
|
|
|
|
let dc = unsafe { Gdi::CreateCompatibleDC(window_dc) };
|
|
|
|
|
assert!(dc != 0);
|
|
|
|
|
|
|
|
|
|
// Create a new bitmap info struct.
|
|
|
|
|
let bitmap_info = BitmapInfo {
|
|
|
|
|
bmi_header: Gdi::BITMAPINFOHEADER {
|
|
|
|
|
biSize: mem::size_of::<Gdi::BITMAPINFOHEADER>() as u32,
|
|
|
|
|
biWidth: width.get(),
|
|
|
|
|
biHeight: -height.get(),
|
|
|
|
|
biPlanes: 1,
|
|
|
|
|
biBitCount: 32,
|
2023-04-06 08:05:18 -07:00
|
|
|
biCompression: Gdi::BI_BITFIELDS as u32,
|
2023-04-06 00:30:59 -07:00
|
|
|
biSizeImage: 0,
|
|
|
|
|
biXPelsPerMeter: 0,
|
|
|
|
|
biYPelsPerMeter: 0,
|
|
|
|
|
biClrUsed: 0,
|
|
|
|
|
biClrImportant: 0,
|
|
|
|
|
},
|
|
|
|
|
bmi_colors: [
|
|
|
|
|
Gdi::RGBQUAD {
|
|
|
|
|
rgbRed: 0xff,
|
|
|
|
|
..ZERO_QUAD
|
|
|
|
|
},
|
|
|
|
|
Gdi::RGBQUAD {
|
|
|
|
|
rgbGreen: 0xff,
|
|
|
|
|
..ZERO_QUAD
|
|
|
|
|
},
|
|
|
|
|
Gdi::RGBQUAD {
|
|
|
|
|
rgbBlue: 0xff,
|
|
|
|
|
..ZERO_QUAD
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// XXX alignment?
|
|
|
|
|
// XXX better to use CreateFileMapping, and pass hSection?
|
|
|
|
|
// XXX test return value?
|
|
|
|
|
let mut pixels: *mut u32 = ptr::null_mut();
|
|
|
|
|
let bitmap = unsafe {
|
|
|
|
|
Gdi::CreateDIBSection(
|
|
|
|
|
dc,
|
|
|
|
|
&bitmap_info as *const BitmapInfo as *const _,
|
|
|
|
|
Gdi::DIB_RGB_COLORS,
|
|
|
|
|
&mut pixels as *mut *mut u32 as _,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
)
|
|
|
|
|
};
|
|
|
|
|
assert!(bitmap != 0);
|
|
|
|
|
let pixels = NonNull::new(pixels).unwrap();
|
|
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
Gdi::SelectObject(dc, bitmap);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Self {
|
|
|
|
|
dc,
|
|
|
|
|
bitmap,
|
|
|
|
|
width,
|
|
|
|
|
height,
|
|
|
|
|
pixels,
|
2023-04-20 18:52:34 -07:00
|
|
|
presented: false,
|
2023-04-06 00:30:59 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-07 17:31:21 -07:00
|
|
|
#[inline]
|
|
|
|
|
fn pixels(&self) -> &[u32] {
|
|
|
|
|
unsafe {
|
|
|
|
|
slice::from_raw_parts(
|
|
|
|
|
self.pixels.as_ptr(),
|
|
|
|
|
i32::from(self.width) as usize * i32::from(self.height) as usize,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
2023-04-06 00:30:59 -07:00
|
|
|
fn pixels_mut(&mut self) -> &mut [u32] {
|
|
|
|
|
unsafe {
|
|
|
|
|
slice::from_raw_parts_mut(
|
|
|
|
|
self.pixels.as_ptr(),
|
|
|
|
|
i32::from(self.width) as usize * i32::from(self.height) as usize,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-21 17:26:09 -08:00
|
|
|
/// The handle to a window for software buffering.
|
2022-01-16 08:59:29 -06:00
|
|
|
pub struct Win32Impl {
|
2022-12-21 17:26:09 -08:00
|
|
|
/// The window handle.
|
2022-01-16 08:03:20 -06:00
|
|
|
window: HWND,
|
2022-12-21 17:26:09 -08:00
|
|
|
|
|
|
|
|
/// The device context for the window.
|
2023-04-06 00:30:59 -07:00
|
|
|
dc: Gdi::HDC,
|
|
|
|
|
|
2023-04-07 17:31:21 -07:00
|
|
|
/// The buffer used to hold the image.
|
2023-04-06 00:30:59 -07:00
|
|
|
buffer: Option<Buffer>,
|
2022-01-16 08:03:20 -06:00
|
|
|
}
|
|
|
|
|
|
2022-12-21 17:26:09 -08:00
|
|
|
/// The Win32-compatible bitmap information.
|
2022-01-16 08:03:20 -06:00
|
|
|
#[repr(C)]
|
|
|
|
|
struct BitmapInfo {
|
2023-04-06 00:30:59 -07:00
|
|
|
bmi_header: Gdi::BITMAPINFOHEADER,
|
|
|
|
|
bmi_colors: [Gdi::RGBQUAD; 3],
|
2022-01-16 08:03:20 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Win32Impl {
|
2022-12-21 17:26:09 -08:00
|
|
|
/// Create a new `Win32Impl` from a `Win32WindowHandle`.
|
2022-12-22 10:09:47 -08:00
|
|
|
///
|
2022-12-21 17:26:09 -08:00
|
|
|
/// # Safety
|
2022-12-22 10:09:47 -08:00
|
|
|
///
|
2022-12-21 17:26:09 -08:00
|
|
|
/// The `Win32WindowHandle` must be a valid window handle.
|
2022-12-27 12:23:27 -08:00
|
|
|
pub unsafe fn new(handle: &Win32WindowHandle) -> Result<Self, crate::SoftBufferError> {
|
2022-12-21 17:26:09 -08:00
|
|
|
// It is valid for the window handle to be null here. Error out if it is.
|
|
|
|
|
if handle.hwnd.is_null() {
|
2022-12-27 12:23:27 -08:00
|
|
|
return Err(SoftBufferError::IncompleteWindowHandle);
|
2022-12-21 17:26:09 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get the handle to the device context.
|
|
|
|
|
// SAFETY: We have confirmed that the window handle is valid.
|
|
|
|
|
let hwnd = handle.hwnd as HWND;
|
2023-04-06 00:30:59 -07:00
|
|
|
let dc = unsafe { Gdi::GetDC(hwnd) };
|
2022-01-16 08:59:29 -06:00
|
|
|
|
2022-12-21 17:26:09 -08:00
|
|
|
// GetDC returns null if there is a platform error.
|
2022-12-20 08:08:46 -08:00
|
|
|
if dc == 0 {
|
2022-12-27 12:23:27 -08:00
|
|
|
return Err(SoftBufferError::PlatformError(
|
2022-12-21 17:26:09 -08:00
|
|
|
Some("Device Context is null".into()),
|
|
|
|
|
Some(Box::new(io::Error::last_os_error())),
|
|
|
|
|
));
|
2022-01-16 08:03:20 -06:00
|
|
|
}
|
|
|
|
|
|
2023-04-06 00:30:59 -07:00
|
|
|
Ok(Self {
|
|
|
|
|
dc,
|
|
|
|
|
window: hwnd,
|
|
|
|
|
buffer: None,
|
|
|
|
|
})
|
2022-01-16 08:59:29 -06:00
|
|
|
}
|
2022-01-16 08:03:20 -06:00
|
|
|
|
2023-04-06 00:30:59 -07:00
|
|
|
pub fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) -> Result<(), SoftBufferError> {
|
|
|
|
|
let (width, height) = (|| {
|
|
|
|
|
let width = NonZeroI32::new(i32::try_from(width.get()).ok()?)?;
|
|
|
|
|
let height = NonZeroI32::new(i32::try_from(height.get()).ok()?)?;
|
|
|
|
|
Some((width, height))
|
|
|
|
|
})()
|
|
|
|
|
.ok_or(SoftBufferError::SizeOutOfRange { width, height })?;
|
2022-01-16 08:03:20 -06:00
|
|
|
|
2023-04-06 00:30:59 -07:00
|
|
|
if let Some(buffer) = self.buffer.as_ref() {
|
|
|
|
|
if buffer.width == width && buffer.height == height {
|
|
|
|
|
return Ok(());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.buffer = Some(Buffer::new(self.dc, width, height));
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn buffer_mut(&mut self) -> Result<BufferImpl, SoftBufferError> {
|
2023-04-07 17:31:21 -07:00
|
|
|
if self.buffer.is_none() {
|
|
|
|
|
panic!("Must set size of surface before calling `buffer_mut()`");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(BufferImpl(self))
|
2023-04-06 00:30:59 -07:00
|
|
|
}
|
2023-04-21 10:37:48 -07:00
|
|
|
|
2023-04-20 18:52:34 -07:00
|
|
|
fn present_with_damage(&mut self, damage: &[Rect]) -> Result<(), SoftBufferError> {
|
|
|
|
|
let buffer = self.buffer.as_mut().unwrap();
|
2023-04-21 10:37:48 -07:00
|
|
|
unsafe {
|
2023-05-30 15:17:10 -07:00
|
|
|
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 })?;
|
|
|
|
|
Gdi::BitBlt(self.dc, x, y, width, height, buffer.dc, x, y, Gdi::SRCCOPY);
|
2023-04-21 10:37:48 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Validate the window.
|
|
|
|
|
Gdi::ValidateRect(self.window, ptr::null_mut());
|
|
|
|
|
}
|
2023-04-20 18:52:34 -07:00
|
|
|
buffer.presented = true;
|
2023-04-21 10:37:48 -07:00
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
2023-04-06 00:30:59 -07:00
|
|
|
}
|
|
|
|
|
|
2023-04-07 17:31:21 -07:00
|
|
|
pub struct BufferImpl<'a>(&'a mut Win32Impl);
|
2023-04-06 00:30:59 -07:00
|
|
|
|
|
|
|
|
impl<'a> BufferImpl<'a> {
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn pixels(&self) -> &[u32] {
|
2023-04-07 17:31:21 -07:00
|
|
|
self.0.buffer.as_ref().unwrap().pixels()
|
2023-04-06 00:30:59 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn pixels_mut(&mut self) -> &mut [u32] {
|
2023-04-07 17:31:21 -07:00
|
|
|
self.0.buffer.as_mut().unwrap().pixels_mut()
|
2023-04-06 00:30:59 -07:00
|
|
|
}
|
|
|
|
|
|
2023-04-20 18:52:34 -07:00
|
|
|
pub fn age(&self) -> u8 {
|
|
|
|
|
match self.0.buffer.as_ref() {
|
|
|
|
|
Some(buffer) if buffer.presented => 1,
|
|
|
|
|
_ => 0,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-06 00:30:59 -07:00
|
|
|
pub fn present(self) -> Result<(), SoftBufferError> {
|
2023-04-07 17:31:21 -07:00
|
|
|
let imp = self.0;
|
2023-04-06 00:30:59 -07:00
|
|
|
let buffer = imp.buffer.as_ref().unwrap();
|
2023-04-21 10:37:48 -07:00
|
|
|
imp.present_with_damage(&[Rect {
|
|
|
|
|
x: 0,
|
|
|
|
|
y: 0,
|
2023-05-30 15:17:10 -07:00
|
|
|
// We know width/height will be non-negative
|
|
|
|
|
width: buffer.width.try_into().unwrap(),
|
|
|
|
|
height: buffer.height.try_into().unwrap(),
|
2023-04-21 10:37:48 -07:00
|
|
|
}])
|
|
|
|
|
}
|
2022-01-16 08:03:20 -06:00
|
|
|
|
2023-04-21 10:37:48 -07:00
|
|
|
pub fn present_with_damage(self, damage: &[Rect]) -> Result<(), SoftBufferError> {
|
|
|
|
|
let imp = self.0;
|
|
|
|
|
imp.present_with_damage(damage)
|
2022-01-16 08:03:20 -06:00
|
|
|
}
|
2022-01-16 08:59:29 -06:00
|
|
|
}
|