From 017875c469b040fa94b60207969fd8241b3c4a34 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Tue, 10 Jan 2023 18:09:39 -0800 Subject: [PATCH] win32: Use owned buffer and `Gdi:BitBlt` --- src/win32.rs | 217 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 146 insertions(+), 71 deletions(-) diff --git a/src/win32.rs b/src/win32.rs index 3390bf7..f8a82a4 100644 --- a/src/win32.rs +++ b/src/win32.rs @@ -7,28 +7,124 @@ use raw_window_handle::Win32WindowHandle; use std::io; use std::mem; -use std::os::raw::c_int; +use std::ptr; +use std::slice; use windows_sys::Win32::Foundation::HWND; -use windows_sys::Win32::Graphics::Gdi::{ - GetDC, StretchDIBits, ValidateRect, BITMAPINFOHEADER, BI_BITFIELDS, DIB_RGB_COLORS, HDC, - RGBQUAD, SRCCOPY, +use windows_sys::Win32::Graphics::Gdi; + +const ZERO_QUAD: Gdi::RGBQUAD = Gdi::RGBQUAD { + rgbBlue: 0, + rgbGreen: 0, + rgbRed: 0, + rgbReserved: 0, }; +struct Buffer { + dc: Gdi::HDC, + bitmap: Gdi::HBITMAP, + pixels: *mut u32, + width: i32, + height: i32, +} + +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: i32, height: i32) -> 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::() as u32, + biWidth: width, + biHeight: -height, + biPlanes: 1, + biBitCount: 32, + biCompression: Gdi::BI_BITFIELDS, + 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); + + unsafe { + Gdi::SelectObject(dc, bitmap); + } + + Self { + dc, + bitmap, + width, + height, + pixels, + } + } + + fn pixels_mut(&mut self) -> &mut [u32] { + unsafe { + slice::from_raw_parts_mut(self.pixels, self.width as usize * self.height as usize * 4) + } + } +} + /// The handle to a window for software buffering. pub struct Win32Impl { /// The window handle. window: HWND, /// The device context for the window. - dc: HDC, + dc: Gdi::HDC, + + buffer: Option, } /// The Win32-compatible bitmap information. #[repr(C)] struct BitmapInfo { - pub bmi_header: BITMAPINFOHEADER, - pub bmi_colors: [RGBQUAD; 3], + bmi_header: Gdi::BITMAPINFOHEADER, + bmi_colors: [Gdi::RGBQUAD; 3], } impl Win32Impl { @@ -46,7 +142,7 @@ impl Win32Impl { // Get the handle to the device context. // SAFETY: We have confirmed that the window handle is valid. let hwnd = handle.hwnd as HWND; - let dc = unsafe { GetDC(hwnd) }; + let dc = unsafe { Gdi::GetDC(hwnd) }; // GetDC returns null if there is a platform error. if dc == 0 { @@ -56,72 +152,51 @@ impl Win32Impl { )); } - Ok(Self { dc, window: hwnd }) + Ok(Self { + dc, + window: hwnd, + buffer: None, + }) } - pub(crate) unsafe fn set_buffer(&mut self, buffer: &[u32], width: u16, height: u16) { - // Create a new bitmap info struct. - let bmi_header = BITMAPINFOHEADER { - biSize: mem::size_of::() as u32, - biWidth: width as i32, - biHeight: -(height as i32), - biPlanes: 1, - biBitCount: 32, - biCompression: BI_BITFIELDS, - biSizeImage: 0, - biXPelsPerMeter: 0, - biYPelsPerMeter: 0, - biClrUsed: 0, - biClrImportant: 0, - }; - let zero_quad = RGBQUAD { - rgbBlue: 0, - rgbGreen: 0, - rgbRed: 0, - rgbReserved: 0, - }; - let bmi_colors = [ - RGBQUAD { - rgbRed: 0xff, - ..zero_quad - }, - RGBQUAD { - rgbGreen: 0xff, - ..zero_quad - }, - RGBQUAD { - rgbBlue: 0xff, - ..zero_quad - }, - ]; - let bitmap_info = BitmapInfo { - bmi_header, - bmi_colors, - }; + pub fn resize(&mut self, width: u32, height: u32) { + if let Some(buffer) = self.buffer.as_ref() { + if buffer.width == width as i32 && buffer.height == height as i32 { + return; + } + } - // Stretch the bitmap onto the window. - // SAFETY: - // - The bitmap information is valid. - // - The buffer is a valid pointer to image data. - unsafe { - StretchDIBits( - self.dc, - 0, - 0, - width as c_int, - height as c_int, - 0, - 0, - width as c_int, - height as c_int, - buffer.as_ptr().cast(), - &bitmap_info as *const BitmapInfo as *const _, - DIB_RGB_COLORS, - SRCCOPY, - ) - }; + self.buffer = Some(Buffer::new(self.dc, width as i32, height as i32)); + } + + pub fn buffer_mut(&mut self) -> &mut [u32] { + self.buffer.as_mut().map_or(&mut [], Buffer::pixels_mut) + } + + pub fn present(&mut self) { + if let Some(buffer) = &self.buffer { + unsafe { + Gdi::BitBlt( + self.dc, + 0, + 0, + buffer.width, + buffer.height, + buffer.dc, + 0, + 0, + Gdi::SRCCOPY, + ); + + // Validate the window. + Gdi::ValidateRect(self.window, ptr::null_mut()); + } + } + } - // Validate the window. - unsafe { ValidateRect(self.window, std::ptr::null_mut()) }; + pub unsafe fn set_buffer(&mut self, buffer: &[u32], width: u16, height: u16) { + self.resize(width.into(), height.into()); + self.buffer_mut().copy_from_slice(buffer); + self.present(); } }