Skip to content

Commit

Permalink
feat: 修改代码风格,优化截图方法 (#94)
Browse files Browse the repository at this point in the history
* feat: 修改代码风格,优化截图方法

* fix: 修复hdc释放错误问题

* chore: update version
  • Loading branch information
nashaofu authored Jan 26, 2024
1 parent 5757ce5 commit afc7b46
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 125 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
2 changes: 1 addition & 1 deletion examples/monitor_capture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand Down
4 changes: 2 additions & 2 deletions src/utils/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
);
}

Expand Down
84 changes: 27 additions & 57 deletions src/windows/boxed.rs
Original file line number Diff line number Diff line change
@@ -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<HWND>,
}

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<HWND>) -> Self {
BoxHDC { hdc, hwnd }
}
}

Expand All @@ -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<HWND> 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;
Expand All @@ -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);
};
Expand All @@ -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<BoxMonitorHDC> {
let hwnd = GetDesktopWindow();
let hdc = GetDC(hwnd);
if hdc.is_invalid() {
return Err(XCapError::new("GetDC is failed"))
}
Ok(BoxMonitorHDC{
hwnd,
hdc,
})
}
}
49 changes: 23 additions & 26 deletions src/windows/capture.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -68,33 +64,33 @@ fn to_rgba_image(
}

#[allow(unused)]
pub fn capture_monitor(
impl_monitor: &ImplMonitor,
x: i32,
y: i32,
width: i32,
height: i32,
) -> XCapResult<RgbaImage> {
pub fn capture_monitor(x: i32, y: i32, width: i32, height: i32) -> XCapResult<RgbaImage> {
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,
0,
0,
width,
height,
*box_hdc_monitor,
*box_hdc_desktop_window,
x,
y,
SRCCOPY,
Expand All @@ -105,11 +101,12 @@ pub fn capture_monitor(
}

#[allow(unused)]
pub fn capture_window(impl_window: &ImplWindow, width: i32, height: i32) -> XCapResult<RgbaImage> {
pub fn capture_window(hwnd: HWND, width: i32, height: i32) -> XCapResult<RgbaImage> {
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中
Expand All @@ -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)
}
Expand Down
56 changes: 19 additions & 37 deletions src/windows/impl_monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -59,13 +59,7 @@ fn get_dev_mode_w(monitor_info_exw: &MONITORINFOEXW) -> XCapResult<DEVMODEW> {
dev_mode_w.dmSize = mem::size_of::<DEVMODEW>() 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)
Expand All @@ -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 {
Expand All @@ -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,
Expand Down Expand Up @@ -167,10 +153,6 @@ impl ImplMonitor {

impl ImplMonitor {
pub fn capture_image(&self) -> XCapResult<RgbaImage> {
// 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)
}
}
2 changes: 1 addition & 1 deletion src/windows/impl_window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

0 comments on commit afc7b46

Please sign in to comment.