You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 11:24:03