From afc7b4620c44c52e3712e27c9d62dd8ae7c4febe Mon Sep 17 00:00:00 2001 From: nashaofu Date: Fri, 26 Jan 2024 10:03:25 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BF=AE=E6=94=B9=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E9=A3=8E=E6=A0=BC=EF=BC=8C=E4=BC=98=E5=8C=96=E6=88=AA=E5=9B=BE?= =?UTF-8?q?=E6=96=B9=E6=B3=95=20(#94)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 修改代码风格,优化截图方法 * fix: 修复hdc释放错误问题 * chore: update version --- Cargo.toml | 2 +- examples/monitor_capture.rs | 2 +- src/utils/image.rs | 4 +- src/windows/boxed.rs | 84 ++++++++++++------------------------- src/windows/capture.rs | 49 ++++++++++------------ src/windows/impl_monitor.rs | 56 +++++++++---------------- src/windows/impl_window.rs | 2 +- 7 files changed, 74 insertions(+), 125 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9991c09..3dd8cb3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xcap" -version = "0.0.1" +version = "0.0.2" edition = "2021" description = "A cross-platform screen capture library" license = "Apache-2.0" diff --git a/examples/monitor_capture.rs b/examples/monitor_capture.rs index 6545b54..248f20f 100644 --- a/examples/monitor_capture.rs +++ b/examples/monitor_capture.rs @@ -17,7 +17,7 @@ fn main() { let image = monitor.capture_image().unwrap(); image - .save(format!("monitor-{}.png", normalized(monitor.name()))) + .save(format!("target/monitor-{}.png", normalized(monitor.name()))) .unwrap(); } diff --git a/src/utils/image.rs b/src/utils/image.rs index c1b9e48..10193d1 100644 --- a/src/utils/image.rs +++ b/src/utils/image.rs @@ -46,10 +46,10 @@ mod tests { #[test] fn bgra() { - let image = bgra_to_rgba_image(2, 1, vec![1, 2, 3, 4, 255, 254, 253, 252]).unwrap(); + let image = bgra_to_rgba_image(2, 1, vec![1, 2, 3, 255, 255, 254, 253, 255]).unwrap(); assert_eq!( image, - RgbaImage::from_vec(2, 1, vec![3, 2, 1, 4, 253, 254, 255, 252]).unwrap() + RgbaImage::from_vec(2, 1, vec![3, 2, 1, 255, 253, 254, 255, 255]).unwrap() ); } diff --git a/src/windows/boxed.rs b/src/windows/boxed.rs index 1f5523b..45190c0 100644 --- a/src/windows/boxed.rs +++ b/src/windows/boxed.rs @@ -1,35 +1,41 @@ use std::{ops::Deref, ptr}; use windows::{ core::PCWSTR, - Win32::Graphics::Gdi::{CreateDCW, DeleteDC, DeleteObject, GetWindowDC, HBITMAP, HDC}, + Win32::{ + Foundation::HWND, + Graphics::Gdi::{CreateDCW, DeleteDC, DeleteObject, GetWindowDC, ReleaseDC, HBITMAP, HDC}, + }, }; -use windows::Win32::Foundation::HWND; -use windows::Win32::Graphics::Gdi::{GetDC, ReleaseDC}; -use windows::Win32::UI::WindowsAndMessaging::GetDesktopWindow; -use crate::{XCapError, XCapResult}; -use super::{impl_monitor::ImplMonitor, impl_window::ImplWindow}; - -pub(crate) struct BoxHDC(HDC); +pub(super) struct BoxHDC { + hdc: HDC, + hwnd: Option, +} impl Deref for BoxHDC { type Target = HDC; fn deref(&self) -> &Self::Target { - &self.0 + &self.hdc } } impl Drop for BoxHDC { fn drop(&mut self) { + // ReleaseDC 与 DeleteDC 的区别 + // https://learn.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-releasedc unsafe { - DeleteDC(self.0); + if let Some(hwnd) = self.hwnd { + ReleaseDC(hwnd, self.hdc); + } else { + DeleteDC(self.hdc); + } }; } } impl BoxHDC { - pub fn new(hdc: HDC) -> Self { - BoxHDC(hdc) + pub fn new(hdc: HDC, hwnd: Option) -> Self { + BoxHDC { hdc, hwnd } } } @@ -46,25 +52,21 @@ impl From<&[u16; 32]> for BoxHDC { ) }; - BoxHDC::new(hdc) - } -} - -impl From<&ImplMonitor> for BoxHDC { - fn from(impl_monitor: &ImplMonitor) -> Self { - BoxHDC::from(&impl_monitor.monitor_info_ex_w.szDevice) + BoxHDC::new(hdc, None) } } -impl From<&ImplWindow> for BoxHDC { - fn from(impl_window: &ImplWindow) -> Self { - let hdc = unsafe { GetWindowDC(impl_window.hwnd) }; +impl From for BoxHDC { + fn from(hwnd: HWND) -> Self { + // GetWindowDC vs GetDC, GetDC 不会绘制窗口边框 + // https://learn.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-getwindowdc + let hdc = unsafe { GetWindowDC(hwnd) }; - BoxHDC::new(hdc) + BoxHDC::new(hdc, Some(hwnd)) } } -pub(crate) struct BoxHBITMAP(HBITMAP); +pub(super) struct BoxHBITMAP(HBITMAP); impl Deref for BoxHBITMAP { type Target = HBITMAP; @@ -75,6 +77,7 @@ impl Deref for BoxHBITMAP { impl Drop for BoxHBITMAP { fn drop(&mut self) { + // https://learn.microsoft.com/zh-cn/windows/win32/api/wingdi/nf-wingdi-createcompatiblebitmap unsafe { DeleteObject(self.0); }; @@ -86,36 +89,3 @@ impl BoxHBITMAP { BoxHBITMAP(h_bitmap) } } - -pub(crate) struct BoxMonitorHDC { - hwnd: HWND, - hdc: HDC, -} -impl Deref for BoxMonitorHDC { - type Target = HDC; - fn deref(&self) -> &Self::Target { - &self.hdc - } -} - -impl Drop for BoxMonitorHDC { - fn drop(&mut self) { - unsafe { - ReleaseDC(self.hwnd, self.hdc); - }; - } -} - -impl BoxMonitorHDC { - pub unsafe fn new() -> XCapResult { - let hwnd = GetDesktopWindow(); - let hdc = GetDC(hwnd); - if hdc.is_invalid() { - return Err(XCapError::new("GetDC is failed")) - } - Ok(BoxMonitorHDC{ - hwnd, - hdc, - }) - } -} \ No newline at end of file diff --git a/src/windows/capture.rs b/src/windows/capture.rs index b7c1180..991f431 100644 --- a/src/windows/capture.rs +++ b/src/windows/capture.rs @@ -1,25 +1,21 @@ use image::RgbaImage; use std::mem; use windows::Win32::{ + Foundation::HWND, Graphics::Gdi::{ BitBlt, CreateCompatibleBitmap, CreateCompatibleDC, GetDIBits, SelectObject, BITMAPINFO, BITMAPINFOHEADER, DIB_RGB_COLORS, RGBQUAD, SRCCOPY, }, Storage::Xps::{PrintWindow, PRINT_WINDOW_FLAGS, PW_CLIENTONLY}, - UI::WindowsAndMessaging::PW_RENDERFULLCONTENT, + UI::WindowsAndMessaging::{GetDesktopWindow, PW_RENDERFULLCONTENT}, }; use crate::{ error::{XCapError, XCapResult}, utils::image::bgra_to_rgba_image, }; -use crate::platform::boxed::BoxMonitorHDC; -use super::{ - boxed::{BoxHBITMAP, BoxHDC}, - impl_monitor::ImplMonitor, - impl_window::ImplWindow, -}; +use super::boxed::{BoxHBITMAP, BoxHDC}; fn to_rgba_image( box_hdc_mem: BoxHDC, @@ -68,25 +64,25 @@ fn to_rgba_image( } #[allow(unused)] -pub fn capture_monitor( - impl_monitor: &ImplMonitor, - x: i32, - y: i32, - width: i32, - height: i32, -) -> XCapResult { +pub fn capture_monitor(x: i32, y: i32, width: i32, height: i32) -> XCapResult { unsafe { - let box_hdc_monitor = BoxMonitorHDC::new()?; - - // 内存中的HDC - let box_hdc_mem = BoxHDC::new(CreateCompatibleDC(*box_hdc_monitor)); - let box_h_bitmap = BoxHBITMAP::new(CreateCompatibleBitmap(*box_hdc_monitor, width, height)); + let hwnd = GetDesktopWindow(); + let box_hdc_desktop_window = BoxHDC::from(hwnd); + + // 内存中的HDC,使用 DeleteDC 函数释放 + // https://learn.microsoft.com/zh-cn/windows/win32/api/wingdi/nf-wingdi-createcompatibledc + let box_hdc_mem = BoxHDC::new(CreateCompatibleDC(*box_hdc_desktop_window), None); + let box_h_bitmap = BoxHBITMAP::new(CreateCompatibleBitmap( + *box_hdc_desktop_window, + width, + height, + )); // 使用SelectObject函数将这个位图选择到DC中 SelectObject(*box_hdc_mem, *box_h_bitmap); // 拷贝原始图像到内存 - // 咋合理不需要i缩放图片,所以直接使用BitBlt + // 这里不需要缩放图片,所以直接使用BitBlt // 如需要缩放,则使用 StretchBlt BitBlt( *box_hdc_mem, @@ -94,7 +90,7 @@ pub fn capture_monitor( 0, width, height, - *box_hdc_monitor, + *box_hdc_desktop_window, x, y, SRCCOPY, @@ -105,11 +101,12 @@ pub fn capture_monitor( } #[allow(unused)] -pub fn capture_window(impl_window: &ImplWindow, width: i32, height: i32) -> XCapResult { +pub fn capture_window(hwnd: HWND, width: i32, height: i32) -> XCapResult { unsafe { - let box_hdc_window: BoxHDC = BoxHDC::from(impl_window); - // 内存中的HDC - let box_hdc_mem = BoxHDC::new(CreateCompatibleDC(*box_hdc_window)); + let box_hdc_window: BoxHDC = BoxHDC::from(hwnd); + // 内存中的HDC,使用 DeleteDC 函数释放 + // https://learn.microsoft.com/zh-cn/windows/win32/api/wingdi/nf-wingdi-createcompatibledc + let box_hdc_mem = BoxHDC::new(CreateCompatibleDC(*box_hdc_window), None); let box_h_bitmap = BoxHBITMAP::new(CreateCompatibleBitmap(*box_hdc_window, width, height)); // 使用SelectObject函数将这个位图选择到DC中 @@ -121,7 +118,7 @@ pub fn capture_window(impl_window: &ImplWindow, width: i32, height: i32) -> XCap // the window that are drawn using DirectComposition. // https://github.com/chromium/chromium/blob/main/ui/snapshot/snapshot_win.cc#L39-L45 let flags = PW_CLIENTONLY.0 | PW_RENDERFULLCONTENT; - PrintWindow(impl_window.hwnd, *box_hdc_mem, PRINT_WINDOW_FLAGS(flags)); + PrintWindow(hwnd, *box_hdc_mem, PRINT_WINDOW_FLAGS(flags)); to_rgba_image(box_hdc_mem, box_h_bitmap, width, height) } diff --git a/src/windows/impl_monitor.rs b/src/windows/impl_monitor.rs index c47e62c..ec9d25b 100644 --- a/src/windows/impl_monitor.rs +++ b/src/windows/impl_monitor.rs @@ -5,27 +5,27 @@ use windows::{ Win32::{ Foundation::{BOOL, LPARAM, POINT, RECT, TRUE}, Graphics::Gdi::{ - EnumDisplayMonitors, GetDeviceCaps, GetMonitorInfoW, - MonitorFromPoint, DESKTOPHORZRES, DEVMODEW, DEVMODE_DISPLAY_ORIENTATION, + EnumDisplayMonitors, EnumDisplaySettingsW, GetDeviceCaps, GetMonitorInfoW, + MonitorFromPoint, DESKTOPHORZRES, DEVMODEW, DMDO_180, DMDO_270, DMDO_90, DMDO_DEFAULT, ENUM_CURRENT_SETTINGS, HDC, HMONITOR, HORZRES, MONITORINFO, MONITORINFOEXW, MONITOR_DEFAULTTONULL, }, UI::WindowsAndMessaging::MONITORINFOF_PRIMARY, }, }; -use windows::Win32::Graphics::Gdi::EnumDisplaySettingsW; use crate::error::{XCapError, XCapResult}; use super::{boxed::BoxHDC, capture::capture_monitor, utils::wide_string_to_string}; -// A函数与W函数区别 +// A 函数与 W 函数区别 // https://learn.microsoft.com/zh-cn/windows/win32/learnwin32/working-with-strings #[derive(Debug, Clone)] pub(crate) struct ImplMonitor { #[allow(unused)] pub hmonitor: HMONITOR, + #[allow(unused)] pub monitor_info_ex_w: MONITORINFOEXW, pub id: u32, pub name: String, @@ -59,13 +59,7 @@ fn get_dev_mode_w(monitor_info_exw: &MONITORINFOEXW) -> XCapResult { dev_mode_w.dmSize = mem::size_of::() as u16; unsafe { - EnumDisplaySettingsW( - PCWSTR(sz_device), - ENUM_CURRENT_SETTINGS, - &mut dev_mode_w, - // EDS_RAWMODE, - ) - .ok()?; + EnumDisplaySettingsW(PCWSTR(sz_device), ENUM_CURRENT_SETTINGS, &mut dev_mode_w).ok()?; }; Ok(dev_mode_w) @@ -80,23 +74,23 @@ impl ImplMonitor { // https://learn.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-getmonitorinfoa unsafe { GetMonitorInfoW(hmonitor, monitor_info_ex_w_ptr).ok()? }; - let mut rc_monitor = monitor_info_ex_w.monitorInfo.rcMonitor; let dev_mode_w = get_dev_mode_w(&monitor_info_ex_w)?; + let dm_position = unsafe { dev_mode_w.Anonymous1.Anonymous2.dmPosition }; + let dm_pels_width = dev_mode_w.dmPelsWidth; + let dm_pels_height = dev_mode_w.dmPelsHeight; + let dm_display_orientation = unsafe { dev_mode_w.Anonymous1.Anonymous2.dmDisplayOrientation }; - let rotation = match dm_display_orientation { - DEVMODE_DISPLAY_ORIENTATION(0) => 0.0, - DEVMODE_DISPLAY_ORIENTATION(1) => 90.0, - DEVMODE_DISPLAY_ORIENTATION(2) => 180.0, - DEVMODE_DISPLAY_ORIENTATION(3) => 270.0, - _ => dm_display_orientation.0 as f32, + DMDO_90 => 90.0, + DMDO_180 => 180.0, + DMDO_270 => 270.0, + DMDO_DEFAULT => 0.0, + _ => 0.0, }; - let dev_mode_w = get_dev_mode_w(&monitor_info_ex_w)?; - let box_hdc_monitor = BoxHDC::from(&monitor_info_ex_w.szDevice); let scale_factor = unsafe { @@ -106,23 +100,15 @@ impl ImplMonitor { physical_width as f32 / logical_width as f32 }; - unsafe { - rc_monitor.left = dev_mode_w.Anonymous1.Anonymous2.dmPosition.x; - rc_monitor.right = dev_mode_w.Anonymous1.Anonymous2.dmPosition.x + dev_mode_w.dmPelsWidth as i32; - rc_monitor.top = dev_mode_w.Anonymous1.Anonymous2.dmPosition.y; - rc_monitor.bottom = dev_mode_w.Anonymous1.Anonymous2.dmPosition.y + dev_mode_w.dmPelsHeight as i32; - - } - Ok(ImplMonitor { hmonitor, monitor_info_ex_w, id: hmonitor.0 as u32, name: wide_string_to_string(&monitor_info_ex_w.szDevice)?, - x: rc_monitor.left, - y: rc_monitor.top, - width: (rc_monitor.right - rc_monitor.left) as u32, - height: (rc_monitor.bottom - rc_monitor.top) as u32, + x: dm_position.x, + y: dm_position.y, + width: dm_pels_width, + height: dm_pels_height, rotation, scale_factor, frequency: dev_mode_w.dmDisplayFrequency as f32, @@ -167,10 +153,6 @@ impl ImplMonitor { impl ImplMonitor { pub fn capture_image(&self) -> XCapResult { - // let width = ((self.width as f32) * self.scale_factor) as i32; - // let height = ((self.height as f32) * self.scale_factor) as i32; - // let x = ((self.x as f32) * self.scale_factor) as i32; - // let y = ((self.y as f32) * self.scale_factor) as i32; - capture_monitor(self,self.x,self.y, self.width as i32, self.height as i32) + capture_monitor(self.x, self.y, self.width as i32, self.height as i32) } } diff --git a/src/windows/impl_window.rs b/src/windows/impl_window.rs index f77177f..e052a4d 100644 --- a/src/windows/impl_window.rs +++ b/src/windows/impl_window.rs @@ -163,6 +163,6 @@ impl ImplWindow { let width = ((self.width as f32) * self.current_monitor.scale_factor) as i32; let height = ((self.height as f32) * self.current_monitor.scale_factor) as i32; - capture_window(self, width, height) + capture_window(self.hwnd, width, height) } }