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()) {