Rust winapi-rs调用StretchBlt绘制可变尺寸GDI位图出现不收缩黑边
问题根因
你遇到的黑边问题由2个核心参数错误和1个逻辑遗漏共同导致:
BitBlt源坐标与DC类型不匹配:GetDC(self.target_window)获取的是目标窗口的客户区DC,坐标原点为窗口客户区左上角,但你传入的源坐标rect.left、rect.top是GetWindowRect返回的屏幕坐标,偏移量完全错误,导致实际拷贝的内容只覆盖了out_dc中位图的部分区域,剩余未覆盖的初始区域为默认黑色- 尺寸匹配错误:
GetWindowRect返回的是包含标题栏、边框的完整窗口尺寸,但客户区DC实际可拷贝的区域小于该尺寸,进一步放大了未覆盖的黑色区域 - 新位图未初始化:你每次创建的新兼容位图初始内容为未定义的黑色,仅填充了自有窗口的DC,但是后续
StretchBlt会将out_dc的黑色区域直接覆盖到自有窗口上,导致你提前填充的白色透明色被覆盖,黑色区域直接显示
修复方案
1. 修正窗口内容采集逻辑
根据你要采集的内容选择对应API,二选一即可:
- 仅采集目标窗口客户区:将
GetWindowRect替换为GetClientRect获取客户区尺寸,BitBlt的源坐标改为0, 0 - 采集完整窗口(含边框、标题栏):将
GetDC(self.target_window)替换为GetWindowDC(self.target_window),BitBlt的源坐标改为0, 0
2. 新增新位图初始化逻辑
每次创建新位图并选入out_dc后,用你的透明色键(白色)填充整个out_dc,确保未被拷贝覆盖的区域为透明色,不会显示黑边。
3. 可选优化
如果不需要缩放,StretchBlt可以替换为性能更高的BitBlt;如果需要适配自有窗口大小缩放,将StretchBlt的目标宽高改为自有窗口的实际客户区尺寸即可。
核心代码修改示例
pub fn snap(&mut self) { if unsafe { IsWindow(self.target_window) }.is_positive() { // 改为获取客户区尺寸(如果抓整窗则保留GetWindowRect,把下面的GetDC改成GetWindowDC) let mut rect: RECT = RECT::default(); unsafe { assert!(GetClientRect(self.target_window, &mut rect).is_positive()) }; let width = rect.right - rect.left; let height = rect.bottom - rect.top; if self.height != height || self.width != width { self.height = height; self.width = width; unsafe { let screen_dc = GetDC(null_mut()); assert!(!screen_dc.is_null(), format!("Error getting screen dc: {}",Error::last_os_error())); let new_size_bmp = CreateCompatibleBitmap(screen_dc, self.width,self.height); assert!(!new_size_bmp.is_null(), format!("Error creating new bitmap: {}",Error::last_os_error())); assert!(ReleaseDC(null_mut(), screen_dc) == 1, format!("Error releasing screen_dc: {}", Error::last_os_error())); let ejected_hgdi = SelectObject(self.out_dc, new_size_bmp as HGDIOBJ); assert!(!ejected_hgdi.is_null(),format!("Error selecting new size bmp into out_dc: {}",Error::last_os_error())); assert!(DeleteObject(ejected_hgdi) != 0, format!("Error deleting old contents of out_dc: {}\nwidth => {}\nheight => {}", Error::last_os_error(), self.width, self.height)); // 新增:用透明色键白色填充新位图 let white_brush = CreateSolidBrush(0x00FFFFFF); // RGB白色 let mut fill_rect = RECT { left: 0, top: 0, right: self.width, bottom: self.height }; FillRect(self.out_dc, &fill_rect, white_brush); DeleteObject(white_brush as HGDIOBJ); }; } let target_dc = unsafe { GetDC(self.target_window) }; assert!(!target_dc.is_null(), format!("Error getting dc, (it`s a null): {}",Error::last_os_error())); // 源坐标改为0,0 unsafe {assert!( BitBlt(self.out_dc, 0, 0, self.width, self.height, target_dc, 0, 0, SRCCOPY).is_positive() );} unsafe { assert!( ReleaseDC(self.target_window, target_dc) == 1, format!("Error releasing dc: {}",Error::last_os_error()) ) }; } else { panic!() } }
内容的提问来源于stack exchange,提问作者Nikolai Savulkin
相关产品推荐
相关产品推荐

