Skip to content

Commit

Permalink
fix: 修复windows窗口截图存在阴影边框问题 (#145)
Browse files Browse the repository at this point in the history
* fix: 修复windows窗口截图存在阴影边框问题

* chore: 修改版本号
  • Loading branch information
nashaofu authored Aug 20, 2024
1 parent ce670d1 commit daff24c
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 42 deletions.
1 change: 0 additions & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
"recommendations": [
"streetsidesoftware.code-spell-checker",
"wengerk.highlight-bad-chars",
"serayuzgur.crates",
"editorconfig.editorconfig",
"tamasfe.even-better-toml",
"rust-lang.rust-analyzer"
Expand Down
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.11"
version = "0.0.12"
edition = "2021"
description = "XCap is a cross-platform screen capture library written in Rust. It supports Linux (X11, Wayland), MacOS, and Windows. XCap supports screenshot and video recording (to be implemented)."
license = "Apache-2.0"
Expand Down
39 changes: 26 additions & 13 deletions src/windows/capture.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use image::RgbaImage;
use image::{DynamicImage, RgbaImage};
use std::{ffi::c_void, mem};
use windows::Win32::{
Foundation::HWND,
Expand All @@ -11,13 +11,10 @@ use windows::Win32::{
},
},
Storage::Xps::{PrintWindow, PRINT_WINDOW_FLAGS},
UI::WindowsAndMessaging::GetDesktopWindow,
UI::WindowsAndMessaging::{GetDesktopWindow, WINDOWINFO},
};

use crate::{
error::{XCapError, XCapResult},
platform::utils::get_window_rect,
};
use crate::error::{XCapError, XCapResult};

use super::{
boxed::{BoxHBITMAP, BoxHDC},
Expand Down Expand Up @@ -115,12 +112,17 @@ pub fn capture_monitor(x: i32, y: i32, width: i32, height: i32) -> XCapResult<Rg
}

#[allow(unused)]
pub fn capture_window(hwnd: HWND, scale_factor: f32) -> XCapResult<RgbaImage> {
pub fn capture_window(
hwnd: HWND,
scale_factor: f32,
window_info: &WINDOWINFO,
) -> XCapResult<RgbaImage> {
unsafe {
let box_hdc_window: BoxHDC = BoxHDC::from(hwnd);
let rect = get_window_rect(hwnd)?;
let mut width = rect.right - rect.left;
let mut height = rect.bottom - rect.top;
let rc_window = window_info.rcWindow;

let mut width = rc_window.right - rc_window.left;
let mut height = rc_window.bottom - rc_window.top;

let hgdi_obj = GetCurrentObject(*box_hdc_window, OBJ_BITMAP);
let mut bitmap = BITMAP::default();
Expand All @@ -138,8 +140,8 @@ pub fn capture_window(hwnd: HWND, scale_factor: f32) -> XCapResult<RgbaImage> {
height = bitmap.bmHeight;
}

width = (width as f32 * scale_factor) as i32;
height = (height as f32 * scale_factor) as i32;
width = (width as f32 * scale_factor).ceil() as i32;
height = (height as f32 * scale_factor).ceil() as i32;

// 内存中的HDC,使用 DeleteDC 函数释放
// https://learn.microsoft.com/zh-cn/windows/win32/api/wingdi/nf-wingdi-createcompatibledc
Expand Down Expand Up @@ -180,6 +182,17 @@ pub fn capture_window(hwnd: HWND, scale_factor: f32) -> XCapResult<RgbaImage> {

SelectObject(*box_hdc_mem, previous_object);

to_rgba_image(box_hdc_mem, box_h_bitmap, width, height)
let image = to_rgba_image(box_hdc_mem, box_h_bitmap, width, height)?;

let mut rc_client = window_info.rcClient;

let x = ((rc_client.left - rc_window.left) as f32 * scale_factor).ceil();
let y = ((rc_client.top - rc_window.top) as f32 * scale_factor).ceil();
let w = ((rc_client.right - rc_client.left) as f32 * scale_factor).floor();
let h = ((rc_client.bottom - rc_client.top) as f32 * scale_factor).floor();

Ok(DynamicImage::ImageRgba8(image)
.crop(x as u32, y as u32, w as u32, h as u32)
.to_rgba8())
}
}
42 changes: 27 additions & 15 deletions src/windows/impl_window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ use std::{cmp::Ordering, ffi::c_void, mem, ptr};
use windows::{
core::{HSTRING, PCWSTR},
Win32::{
Foundation::{BOOL, HWND, LPARAM, MAX_PATH, TRUE},
Foundation::{BOOL, HWND, LPARAM, MAX_PATH, RECT, TRUE},
Graphics::{
Dwm::{DwmGetWindowAttribute, DWMWA_CLOAKED},
Dwm::{DwmGetWindowAttribute, DWMWA_CLOAKED, DWMWA_EXTENDED_FRAME_BOUNDS},
Gdi::{IsRectEmpty, MonitorFromWindow, MONITOR_DEFAULTTONEAREST},
},
Storage::FileSystem::{GetFileVersionInfoSizeW, GetFileVersionInfoW, VerQueryValueW},
Expand All @@ -27,11 +27,7 @@ use crate::{
platform::{boxed::BoxProcessHandle, utils::log_last_error},
};

use super::{
capture::capture_window,
impl_monitor::ImplMonitor,
utils::{get_window_rect, wide_string_to_string},
};
use super::{capture::capture_window, impl_monitor::ImplMonitor, utils::wide_string_to_string};

#[derive(Debug, Clone)]
pub(crate) struct ImplWindow {
Expand Down Expand Up @@ -140,9 +136,21 @@ fn is_valid_window(hwnd: HWND) -> bool {
return false;
}

let is_rect_empty = get_window_rect(hwnd).is_ok_and(|rect| IsRectEmpty(&rect).as_bool());
let mut rect = RECT::default();

let get_rect_is_err = DwmGetWindowAttribute(
hwnd,
DWMWA_EXTENDED_FRAME_BOUNDS,
&mut rect as *mut RECT as *mut c_void,
mem::size_of::<RECT>() as u32,
)
.is_err();

if is_rect_empty {
if get_rect_is_err {
return false;
}

if IsRectEmpty(&rect).as_bool() {
return false;
}
}
Expand Down Expand Up @@ -308,7 +316,7 @@ impl ImplWindow {
let app_name = get_app_name(hwnd)?;

let hmonitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
let rc_window = window_info.rcWindow;
let rc_client = window_info.rcClient;
let is_minimized = IsIconic(hwnd).as_bool();
let is_maximized = IsZoomed(hwnd).as_bool();

Expand All @@ -320,10 +328,10 @@ impl ImplWindow {
app_name,
process_id: get_process_id(hwnd),
current_monitor: ImplMonitor::new(hmonitor)?,
x: rc_window.left,
y: rc_window.top,
width: (rc_window.right - rc_window.left) as u32,
height: (rc_window.bottom - rc_window.top) as u32,
x: rc_client.left,
y: rc_client.top,
width: (rc_client.right - rc_client.left) as u32,
height: (rc_client.bottom - rc_client.top) as u32,
is_minimized,
is_maximized,
})
Expand Down Expand Up @@ -355,6 +363,10 @@ impl ImplWindow {
impl ImplWindow {
pub fn capture_image(&self) -> XCapResult<RgbaImage> {
// TODO: 在win10之后,不同窗口有不同的dpi,所以可能存在截图不全或者截图有较大空白,实际窗口没有填充满图片
capture_window(self.hwnd, self.current_monitor.scale_factor)
capture_window(
self.hwnd,
self.current_monitor.scale_factor,
&self.window_info,
)
}
}
13 changes: 1 addition & 12 deletions src/windows/utils.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
use sysinfo::System;
use windows::Win32::{
Foundation::{GetLastError, HWND, RECT},
UI::WindowsAndMessaging::GetWindowRect,
};
use windows::Win32::Foundation::GetLastError;

use crate::error::XCapResult;

Expand All @@ -25,14 +22,6 @@ pub(super) fn get_os_major_version() -> u8 {
.unwrap_or(0)
}

pub(super) fn get_window_rect(hwnd: HWND) -> XCapResult<RECT> {
unsafe {
let mut rect = RECT::default();
GetWindowRect(hwnd, &mut rect)?;
Ok(rect)
}
}

pub(super) fn log_last_error<T: ToString>(label: T) {
unsafe {
let err = GetLastError();
Expand Down

0 comments on commit daff24c

Please sign in to comment.