fix(windows): mask resized window captures

This commit is contained in:
webadderall
2026-09-05 14:11:27 +10:00
parent 3a43d288d7
commit 2ca5c46b47
5 changed files with 24 additions and 8 deletions
+5
View File
@@ -122,4 +122,9 @@ describe("native Windows window capture", () => {
);
expect(windowsCaptureSource).not.toContain("nextWidth != captureWidth_");
});
it("does not expose desktop pixels when the selected window shrinks", () => {
expect(windowsCaptureSource).toContain("(std::min)(mappedWidth");
expect(windowsCaptureSource).toContain("ClearRenderTargetView");
});
});
@@ -5,10 +5,10 @@
"helpers": {
"wgc-capture": {
"binaryName": "wgc-capture.exe",
"binarySha256": "c0dca95216a46474f1c71e2274228cbc78dd09c49e1539333a76183b115b1c5a",
"binarySha256": "a5a9c0c417144a834af3206b60bae32d0c21d55deb8a3f5446831621cfdfa7b3",
"sourceDir": "electron/native/wgc-capture",
"sourceFingerprint": "9d8ba313407468ddff200317b210e74953e683c9ccd2e27104dd9e94b8efa7cb",
"updatedAt": "2026-09-04T21:09:08.014Z"
"sourceFingerprint": "c65b6eb2230be7db9b49aac48eba52a9eac469028ebb242e10c16c51c86b3220",
"updatedAt": "2026-09-05T02:09:18.163Z"
},
"cursor-monitor": {
"binaryName": "cursor-monitor.exe",
Binary file not shown.
@@ -256,20 +256,27 @@ bool WgcSession::updateWindowCropRect(bool initializeSize) {
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
desc.SampleDesc.Count = 1;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = D3D11_BIND_RENDER_TARGET;
ComPtr<ID3D11Texture2D> resizedTexture;
if (FAILED(d3dDevice_->CreateTexture2D(&desc, nullptr, &resizedTexture))) return false;
ComPtr<ID3D11RenderTargetView> renderTargetView;
if (FAILED(d3dDevice_->CreateRenderTargetView(resizedTexture.Get(), nullptr, &renderTargetView))) return false;
cropTexture_ = resizedTexture;
cropRenderTargetView_ = renderTargetView;
}
if (!cropTexture_) return false;
if (!cropTexture_ || !cropRenderTargetView_) return false;
// The encoder's dimensions are fixed for the lifetime of the MP4. Keep the
// crop texture fixed too: reallocating it after a resize made the encoder
// pad the changed frame with black bars.
left = std::clamp(left, 0L, static_cast<LONG>(framePoolWidth_ - captureWidth_));
top = std::clamp(top, 0L, static_cast<LONG>(framePoolHeight_ - captureHeight_));
cropRect_ = {left, top, left + captureWidth_, top + captureHeight_};
// pad the changed frame with black bars. If the selected window shrinks,
// copy only its current area so the crop never exposes nearby desktop pixels.
const LONG copyWidth = (std::min)(mappedWidth, static_cast<LONG>(captureWidth_));
const LONG copyHeight = (std::min)(mappedHeight, static_cast<LONG>(captureHeight_));
left = std::clamp(left, 0L, static_cast<LONG>(framePoolWidth_) - copyWidth);
top = std::clamp(top, 0L, static_cast<LONG>(framePoolHeight_) - copyHeight);
cropRect_ = {left, top, left + copyWidth, top + copyHeight};
return true;
}
@@ -308,6 +315,7 @@ void WgcSession::stopCapture() {
framePool_ = nullptr;
}
cropTexture_.Reset();
cropRenderTargetView_.Reset();
}
void WgcSession::onFrameArrived(
@@ -349,6 +357,8 @@ void WgcSession::onFrameArrived(
if (SUCCEEDED(hr) && texture && frameCallback_) {
if (windowHandle_ && cropTexture_ && updateWindowCropRect()) {
constexpr float clearColor[4] = {0.0f, 0.0f, 0.0f, 1.0f};
d3dContext_->ClearRenderTargetView(cropRenderTargetView_.Get(), clearColor);
D3D11_BOX sourceBox{
static_cast<UINT>(cropRect_.left), static_cast<UINT>(cropRect_.top), 0,
static_cast<UINT>(cropRect_.right), static_cast<UINT>(cropRect_.bottom), 1,
@@ -39,6 +39,7 @@ private:
ComPtr<ID3D11Device> d3dDevice_;
ComPtr<ID3D11DeviceContext> d3dContext_;
ComPtr<ID3D11Texture2D> cropTexture_;
ComPtr<ID3D11RenderTargetView> cropRenderTargetView_;
winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice winrtDevice_{nullptr};
winrt::Windows::Graphics::Capture::GraphicsCaptureItem captureItem_{nullptr};
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool framePool_{nullptr};