From af5686a229eb16a68771d7bad204e7aea8d89958 Mon Sep 17 00:00:00 2001 From: Zijie He Date: Fri, 25 Aug 2017 11:36:52 -0700 Subject: [PATCH] Implement scaling detection in WindowCapturerWin WindowCapturerWin wrongly calculate the image size if the application it target does not support high DPI. It causes part of the output frame black. See bug for details. Bug: webrtc:8112 Change-Id: I33c66dfa977ec08a29c56ff86ae37320b1459c87 Reviewed-on: https://chromium-review.googlesource.com/634383 Commit-Queue: Zijie He Reviewed-by: Jamie Walch Cr-Commit-Position: refs/heads/master@{#19531} --- .../desktop_capture/desktop_geometry.cc | 5 ++ .../desktop_capture/desktop_geometry.h | 4 ++ .../desktop_geometry_unittest.cc | 37 +++++++++++++++ .../win/window_capture_utils.cc | 12 +++++ .../win/window_capture_utils.h | 4 ++ .../desktop_capture/window_capturer_win.cc | 46 +++++++++++++++---- 6 files changed, 99 insertions(+), 9 deletions(-) diff --git a/webrtc/modules/desktop_capture/desktop_geometry.cc b/webrtc/modules/desktop_capture/desktop_geometry.cc index 33d6070cf2..5fadea2984 100644 --- a/webrtc/modules/desktop_capture/desktop_geometry.cc +++ b/webrtc/modules/desktop_capture/desktop_geometry.cc @@ -70,5 +70,10 @@ void DesktopRect::Extend(int32_t left_offset, bottom_ += bottom_offset; } +void DesktopRect::Scale(double horizontal, double vertical) { + right_ += width() * (horizontal - 1); + bottom_ += height() * (vertical - 1); +} + } // namespace webrtc diff --git a/webrtc/modules/desktop_capture/desktop_geometry.h b/webrtc/modules/desktop_capture/desktop_geometry.h index e14ab7085f..71d4173a8b 100644 --- a/webrtc/modules/desktop_capture/desktop_geometry.h +++ b/webrtc/modules/desktop_capture/desktop_geometry.h @@ -142,6 +142,10 @@ class DesktopRect { int32_t right_offset, int32_t bottom_offset); + // Scales current DesktopRect. This function does not impact the |top_| and + // |left_|. + void Scale(double horizontal, double vertical); + private: DesktopRect(int32_t left, int32_t top, int32_t right, int32_t bottom) : left_(left), top_(top), right_(right), bottom_(bottom) { diff --git a/webrtc/modules/desktop_capture/desktop_geometry_unittest.cc b/webrtc/modules/desktop_capture/desktop_geometry_unittest.cc index 0628d9aeb2..520b1f9723 100644 --- a/webrtc/modules/desktop_capture/desktop_geometry_unittest.cc +++ b/webrtc/modules/desktop_capture/desktop_geometry_unittest.cc @@ -66,4 +66,41 @@ TEST(DesktopRectTest, EmptyRectUnionWithEmptyOne) { ASSERT_TRUE(rect.is_empty()); } +TEST(DesktopRectTest, Scale) { + DesktopRect rect = DesktopRect::MakeXYWH(100, 100, 100, 100); + rect.Scale(1.1, 1.1); + ASSERT_EQ(rect.top(), 100); + ASSERT_EQ(rect.left(), 100); + ASSERT_EQ(rect.width(), 110); + ASSERT_EQ(rect.height(), 110); + + rect = DesktopRect::MakeXYWH(100, 100, 100, 100); + rect.Scale(0.01, 0.01); + ASSERT_EQ(rect.top(), 100); + ASSERT_EQ(rect.left(), 100); + ASSERT_EQ(rect.width(), 1); + ASSERT_EQ(rect.height(), 1); + + rect = DesktopRect::MakeXYWH(100, 100, 100, 100); + rect.Scale(1.1, 0.9); + ASSERT_EQ(rect.top(), 100); + ASSERT_EQ(rect.left(), 100); + ASSERT_EQ(rect.width(), 110); + ASSERT_EQ(rect.height(), 90); + + rect = DesktopRect::MakeXYWH(0, 0, 100, 100); + rect.Scale(1.1, 1.1); + ASSERT_EQ(rect.top(), 0); + ASSERT_EQ(rect.left(), 0); + ASSERT_EQ(rect.width(), 110); + ASSERT_EQ(rect.height(), 110); + + rect = DesktopRect::MakeXYWH(0, 100, 100, 100); + rect.Scale(1.1, 1.1); + ASSERT_EQ(rect.top(), 100); + ASSERT_EQ(rect.left(), 0); + ASSERT_EQ(rect.width(), 110); + ASSERT_EQ(rect.height(), 110); +} + } // namespace webrtc diff --git a/webrtc/modules/desktop_capture/win/window_capture_utils.cc b/webrtc/modules/desktop_capture/win/window_capture_utils.cc index 07da6ee6af..4241b81b2c 100644 --- a/webrtc/modules/desktop_capture/win/window_capture_utils.cc +++ b/webrtc/modules/desktop_capture/win/window_capture_utils.cc @@ -113,6 +113,18 @@ int GetWindowRegionTypeWithBoundary(HWND window, DesktopRect* result) { return region_type; } +bool GetDcSize(HDC hdc, DesktopSize* size) { + win::ScopedGDIObject> + scoped_hgdi(GetCurrentObject(hdc, OBJ_BITMAP)); + BITMAP bitmap; + memset(&bitmap, 0, sizeof(BITMAP)); + if (GetObject(scoped_hgdi.Get(), sizeof(BITMAP), &bitmap) == 0) { + return false; + } + size->set(bitmap.bmWidth, bitmap.bmHeight); + return true; +} + AeroChecker::AeroChecker() : dwmapi_library_(nullptr), func_(nullptr) { // Try to load dwmapi.dll dynamically since it is not available on XP. dwmapi_library_ = LoadLibrary(L"dwmapi.dll"); diff --git a/webrtc/modules/desktop_capture/win/window_capture_utils.h b/webrtc/modules/desktop_capture/win/window_capture_utils.h index c889c16aed..4c19d74cea 100644 --- a/webrtc/modules/desktop_capture/win/window_capture_utils.h +++ b/webrtc/modules/desktop_capture/win/window_capture_utils.h @@ -41,6 +41,10 @@ bool GetWindowContentRect(HWND window, DesktopRect* result); // |window| if region type is SIMPLEREGION. int GetWindowRegionTypeWithBoundary(HWND window, DesktopRect* result); +// Retrieves the size of the |hdc|. This function returns false if native APIs +// fail. +bool GetDcSize(HDC hdc, DesktopSize* size); + typedef HRESULT (WINAPI *DwmIsCompositionEnabledFunc)(BOOL* enabled); class AeroChecker { public: diff --git a/webrtc/modules/desktop_capture/window_capturer_win.cc b/webrtc/modules/desktop_capture/window_capturer_win.cc index 29fbebe2db..dde91aa5b0 100644 --- a/webrtc/modules/desktop_capture/window_capturer_win.cc +++ b/webrtc/modules/desktop_capture/window_capturer_win.cc @@ -172,10 +172,23 @@ void WindowCapturerWin::CaptureFrame() { return; } + DesktopRect original_rect; + DesktopRect cropped_rect; + // TODO(zijiehe): GetCroppedWindowRect() is not accurate, Windows won't draw + // the content below the |window_| with PrintWindow() or BitBlt(). See bug + // https://bugs.chromium.org/p/webrtc/issues/detail?id=8157. + if (!GetCroppedWindowRect(window_, &cropped_rect, &original_rect)) { + LOG(LS_WARNING) << "Failed to get window info: " << GetLastError(); + callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr); + return; + } + // Return a 1x1 black frame if the window is minimized or invisible, to match // behavior on mace. Window can be temporarily invisible during the // transition of full screen mode on/off. - if (IsIconic(window_) || !IsWindowVisible(window_)) { + if (original_rect.is_empty() || + IsIconic(window_) || + !IsWindowVisible(window_)) { std::unique_ptr frame( new BasicDesktopFrame(DesktopSize(1, 1))); memset(frame->data(), 0, frame->stride() * frame->size().height()); @@ -186,14 +199,6 @@ void WindowCapturerWin::CaptureFrame() { return; } - DesktopRect original_rect; - DesktopRect cropped_rect; - if (!GetCroppedWindowRect(window_, &cropped_rect, &original_rect)) { - LOG(LS_WARNING) << "Failed to get window info: " << GetLastError(); - callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr); - return; - } - HDC window_dc = GetWindowDC(window_); if (!window_dc) { LOG(LS_WARNING) << "Failed to get window DC: " << GetLastError(); @@ -201,6 +206,29 @@ void WindowCapturerWin::CaptureFrame() { return; } + DesktopSize window_dc_size; + if (GetDcSize(window_dc, &window_dc_size)) { + // The |window_dc_size| is used to detect the scaling of the original + // window. If the application does not support high-DPI settings, it will + // be scaled by Windows according to the scaling setting. + // https://www.google.com/search?q=windows+scaling+settings&ie=UTF-8 + // So the size of the |window_dc|, i.e. the bitmap we can retrieve from + // PrintWindow() or BitBlt() function, will be smaller than + // |original_rect| and |cropped_rect|. Part of the captured desktop frame + // will be black. See + // bug https://bugs.chromium.org/p/webrtc/issues/detail?id=8112 for + // details. + + // If |window_dc_size| is smaller than |window_rect|, let's resize both + // |original_rect| and |cropped_rect| according to the scaling factor. + const double vertical_scale = + static_cast(window_dc_size.width()) / original_rect.width(); + const double horizontal_scale = + static_cast(window_dc_size.height()) / original_rect.height(); + original_rect.Scale(vertical_scale, horizontal_scale); + cropped_rect.Scale(vertical_scale, horizontal_scale); + } + std::unique_ptr frame( DesktopFrameWin::Create(cropped_rect.size(), nullptr, window_dc)); if (!frame.get()) {