diff --git a/webrtc/modules/BUILD.gn b/webrtc/modules/BUILD.gn index 63e328dbd1..55378290eb 100644 --- a/webrtc/modules/BUILD.gn +++ b/webrtc/modules/BUILD.gn @@ -561,12 +561,16 @@ if (rtc_include_tests) { sources += [ "desktop_capture/desktop_and_cursor_composer_unittest.cc", "desktop_capture/desktop_capturer_differ_wrapper_unittest.cc", + "desktop_capture/desktop_frame_rotation_unittest.cc", "desktop_capture/mouse_cursor_monitor_unittest.cc", "desktop_capture/rgba_color_unittest.cc", "desktop_capture/screen_capturer_helper_unittest.cc", "desktop_capture/screen_capturer_mac_unittest.cc", "desktop_capture/screen_capturer_mock_objects.h", "desktop_capture/screen_capturer_unittest.cc", + "desktop_capture/test_utils.cc", + "desktop_capture/test_utils.h", + "desktop_capture/test_utils_unittest.cc", "desktop_capture/win/cursor_unittest.cc", "desktop_capture/win/cursor_unittest_resources.h", "desktop_capture/win/cursor_unittest_resources.rc", diff --git a/webrtc/modules/desktop_capture/BUILD.gn b/webrtc/modules/desktop_capture/BUILD.gn index 34184516ef..5d27994a2f 100644 --- a/webrtc/modules/desktop_capture/BUILD.gn +++ b/webrtc/modules/desktop_capture/BUILD.gn @@ -95,6 +95,8 @@ rtc_static_library("desktop_capture") { "desktop_capturer.h", "desktop_capturer_differ_wrapper.cc", "desktop_capturer_differ_wrapper.h", + "desktop_frame_rotation.cc", + "desktop_frame_rotation.h", "desktop_frame_win.cc", "desktop_frame_win.h", "differ_block.cc", @@ -198,6 +200,7 @@ rtc_static_library("desktop_capture") { ":primitives", "../../base:rtc_base", # TODO(kjellander): Cleanup in bugs.webrtc.org/3806. "../../system_wrappers", + "//third_party/libyuv", ] if (use_desktop_capture_differ_sse2) { diff --git a/webrtc/modules/desktop_capture/DEPS b/webrtc/modules/desktop_capture/DEPS index 2805958070..9229e7f19b 100644 --- a/webrtc/modules/desktop_capture/DEPS +++ b/webrtc/modules/desktop_capture/DEPS @@ -1,4 +1,5 @@ include_rules = [ "+webrtc/base", "+webrtc/system_wrappers", + "+third_party/libyuv", ] diff --git a/webrtc/modules/desktop_capture/desktop_frame_rotation.cc b/webrtc/modules/desktop_capture/desktop_frame_rotation.cc new file mode 100644 index 0000000000..7a3a8acc4f --- /dev/null +++ b/webrtc/modules/desktop_capture/desktop_frame_rotation.cc @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/desktop_capture/desktop_frame_rotation.h" + +#include + +#include "webrtc/base/checks.h" +#include "third_party/libyuv/include/libyuv/rotate_argb.h" + +namespace webrtc { + +namespace { + +libyuv::RotationMode ToLibyuvRotationMode(Rotation rotation) { + switch (rotation) { + case Rotation::CLOCK_WISE_0: + return libyuv::kRotate0; + case Rotation::CLOCK_WISE_90: + return libyuv::kRotate90; + case Rotation::CLOCK_WISE_180: + return libyuv::kRotate180; + case Rotation::CLOCK_WISE_270: + return libyuv::kRotate270; + } + RTC_NOTREACHED(); + return libyuv::kRotate0; +} + +DesktopRect RotateAndOffsetRect(DesktopRect rect, + DesktopSize size, + Rotation rotation, + DesktopVector offset) { + DesktopRect result = RotateRect(rect, size, rotation); + result.Translate(offset); + return result; +} + +} // namespace + +Rotation ReverseRotation(Rotation rotation) { + switch (rotation) { + case Rotation::CLOCK_WISE_0: + return rotation; + case Rotation::CLOCK_WISE_90: + return Rotation::CLOCK_WISE_270; + case Rotation::CLOCK_WISE_180: + return Rotation::CLOCK_WISE_180; + case Rotation::CLOCK_WISE_270: + return Rotation::CLOCK_WISE_90; + } + RTC_NOTREACHED(); + return Rotation::CLOCK_WISE_0; +} + +DesktopSize RotateSize(DesktopSize size, Rotation rotation) { + switch (rotation) { + case Rotation::CLOCK_WISE_0: + case Rotation::CLOCK_WISE_180: + return size; + case Rotation::CLOCK_WISE_90: + case Rotation::CLOCK_WISE_270: + return DesktopSize(size.height(), size.width()); + } + RTC_NOTREACHED(); + return DesktopSize(); +} + +DesktopRect RotateRect(DesktopRect rect, DesktopSize size, Rotation rotation) { + switch (rotation) { + case Rotation::CLOCK_WISE_0: + return rect; + case Rotation::CLOCK_WISE_90: + return DesktopRect::MakeXYWH(size.height() - rect.bottom(), rect.left(), + rect.height(), rect.width()); + case Rotation::CLOCK_WISE_180: + return DesktopRect::MakeXYWH(size.width() - rect.right(), + size.height() - rect.bottom(), rect.width(), + rect.height()); + case Rotation::CLOCK_WISE_270: + return DesktopRect::MakeXYWH(rect.top(), size.width() - rect.right(), + rect.height(), rect.width()); + } + RTC_NOTREACHED(); + return DesktopRect(); +} + +void RotateDesktopFrame(const DesktopFrame& source, + const DesktopRect& source_rect, + const Rotation& rotation, + const DesktopVector& target_offset, + DesktopFrame* target) { + RTC_DCHECK(target); + RTC_DCHECK(DesktopRect::MakeSize(source.size()).ContainsRect(source_rect)); + // The rectangle in |target|. + const DesktopRect target_rect = + RotateAndOffsetRect(source_rect, source.size(), rotation, target_offset); + RTC_DCHECK(DesktopRect::MakeSize(target->size()).ContainsRect(target_rect)); + + if (target_rect.is_empty()) { + return; + } + + target->mutable_updated_region()->AddRect(target_rect); + int result = libyuv::ARGBRotate( + source.GetFrameDataAtPos(source_rect.top_left()), source.stride(), + target->GetFrameDataAtPos(target_rect.top_left()), target->stride(), + source_rect.width(), source_rect.height(), + ToLibyuvRotationMode(rotation)); + RTC_DCHECK_EQ(result, 0); +} + +} // namespace webrtc diff --git a/webrtc/modules/desktop_capture/desktop_frame_rotation.h b/webrtc/modules/desktop_capture/desktop_frame_rotation.h new file mode 100644 index 0000000000..e78ea83f67 --- /dev/null +++ b/webrtc/modules/desktop_capture/desktop_frame_rotation.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_DESKTOP_FRAME_ROTATION_H_ +#define WEBRTC_MODULES_DESKTOP_CAPTURE_DESKTOP_FRAME_ROTATION_H_ + +#include "webrtc/modules/desktop_capture/desktop_frame.h" +#include "webrtc/modules/desktop_capture/desktop_geometry.h" + +namespace webrtc { + +// Represents the rotation of a DesktopFrame. +enum class Rotation { + CLOCK_WISE_0, + CLOCK_WISE_90, + CLOCK_WISE_180, + CLOCK_WISE_270, +}; + +// Rotates input DesktopFrame |source|, copies pixel in an unrotated rectangle +// |source_rect| into the target rectangle of another DesktopFrame |target|, and +// adds target rectangle into |target|->mutable_updated_region(). +// Target rectangle here is the rotated |source_rect| plus |target_offset|. +// |rotation| specifies |source| to |target| rotation. |source_rect| is in +// |source| coordinate. |target_offset| is in |target| coordinate. +// This function triggers check failure if |source| does not cover the +// |source_rect|, or |target| does not cover the rotated |rect|. +void RotateDesktopFrame(const DesktopFrame& source, + const DesktopRect& source_rect, + const Rotation& rotation, + const DesktopVector& target_offset, + DesktopFrame* target); + +// Returns a reverse rotation of |rotation|. +Rotation ReverseRotation(Rotation rotation); + +// Returns a rotated DesktopSize of |size|. +DesktopSize RotateSize(DesktopSize size, Rotation rotation); + +// Returns a rotated DesktopRect of |rect|. The |size| represents the size of +// the DesktopFrame which |rect| belongs in. +DesktopRect RotateRect(DesktopRect rect, DesktopSize size, Rotation rotation); + +} // namespace webrtc + +#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_DESKTOP_FRAME_ROTATION_H_ diff --git a/webrtc/modules/desktop_capture/desktop_frame_rotation_unittest.cc b/webrtc/modules/desktop_capture/desktop_frame_rotation_unittest.cc new file mode 100644 index 0000000000..864559e516 --- /dev/null +++ b/webrtc/modules/desktop_capture/desktop_frame_rotation_unittest.cc @@ -0,0 +1,447 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/desktop_capture/desktop_frame_rotation.h" + +#include "webrtc/modules/desktop_capture/desktop_frame.h" +#include "webrtc/modules/desktop_capture/desktop_region.h" +#include "webrtc/modules/desktop_capture/test_utils.h" +#include "webrtc/test/gtest.h" + +namespace webrtc { + +namespace { + +// A DesktopFrame implementation which stores data in an external int array. +class ArrayDesktopFrame : public DesktopFrame { + public: + ArrayDesktopFrame(DesktopSize size, uint32_t* data); + ~ArrayDesktopFrame() override; +}; + +ArrayDesktopFrame::ArrayDesktopFrame(DesktopSize size, uint32_t* data) + : DesktopFrame(size, + size.width() * kBytesPerPixel, + reinterpret_cast(data), + nullptr) {} + +ArrayDesktopFrame::~ArrayDesktopFrame() = default; + +} // namespace + +TEST(DesktopFrameRotationTest, CopyRect3x4) { + // A DesktopFrame of 4-pixel width by 3-pixel height. + static uint32_t frame_pixels[] = { + 0, 1, 2, 3, // + 4, 5, 6, 7, // + 8, 9, 10, 11, // + }; + ArrayDesktopFrame frame(DesktopSize(4, 3), frame_pixels); + + { + BasicDesktopFrame target(DesktopSize(4, 3)); + RotateDesktopFrame(frame, DesktopRect::MakeSize(frame.size()), + Rotation::CLOCK_WISE_0, DesktopVector(), &target); + ASSERT_TRUE(DesktopFrameDataEquals(frame, target)); + } + + // After Rotating clock-wise 90 degree + { + static uint32_t expected_pixels[] = { + 8, 4, 0, // + 9, 5, 1, // + 10, 6, 2, // + 11, 7, 3, // + }; + ArrayDesktopFrame expected(DesktopSize(3, 4), expected_pixels); + + BasicDesktopFrame target(DesktopSize(3, 4)); + RotateDesktopFrame(frame, DesktopRect::MakeSize(frame.size()), + Rotation::CLOCK_WISE_90, DesktopVector(), &target); + ASSERT_TRUE(DesktopFrameDataEquals(target, expected)); + } + + // After Rotating clock-wise 180 degree + { + static uint32_t expected_pixels[] = { + 11, 10, 9, 8, // + 7, 6, 5, 4, // + 3, 2, 1, 0, // + }; + ArrayDesktopFrame expected(DesktopSize(4, 3), expected_pixels); + + BasicDesktopFrame target(DesktopSize(4, 3)); + RotateDesktopFrame(frame, DesktopRect::MakeSize(frame.size()), + Rotation::CLOCK_WISE_180, DesktopVector(), &target); + ASSERT_TRUE(DesktopFrameDataEquals(target, expected)); + } + + // After Rotating clock-wise 270 degree + { + static uint32_t expected_pixels[] = { + 3, 7, 11, // + 2, 6, 10, // + 1, 5, 9, // + 0, 4, 8, // + }; + ArrayDesktopFrame expected(DesktopSize(3, 4), expected_pixels); + + BasicDesktopFrame target(DesktopSize(3, 4)); + RotateDesktopFrame(frame, DesktopRect::MakeSize(frame.size()), + Rotation::CLOCK_WISE_270, DesktopVector(), &target); + ASSERT_TRUE(DesktopFrameDataEquals(target, expected)); + } +} + +TEST(DesktopFrameRotationTest, CopyRect3x5) { + // A DesktopFrame of 5-pixel width by 3-pixel height. + static uint32_t frame_pixels[] = { + 0, 1, 2, 3, 4, // + 5, 6, 7, 8, 9, // + 10, 11, 12, 13, 14, // + }; + ArrayDesktopFrame frame(DesktopSize(5, 3), frame_pixels); + + { + BasicDesktopFrame target(DesktopSize(5, 3)); + RotateDesktopFrame(frame, DesktopRect::MakeSize(frame.size()), + Rotation::CLOCK_WISE_0, DesktopVector(), &target); + ASSERT_TRUE(DesktopFrameDataEquals(target, frame)); + } + + // After Rotating clock-wise 90 degree + { + static uint32_t expected_pixels[] = { + 10, 5, 0, // + 11, 6, 1, // + 12, 7, 2, // + 13, 8, 3, // + 14, 9, 4, // + }; + ArrayDesktopFrame expected(DesktopSize(3, 5), expected_pixels); + + BasicDesktopFrame target(DesktopSize(3, 5)); + RotateDesktopFrame(frame, DesktopRect::MakeSize(frame.size()), + Rotation::CLOCK_WISE_90, DesktopVector(), &target); + ASSERT_TRUE(DesktopFrameDataEquals(target, expected)); + } + + // After Rotating clock-wise 180 degree + { + static uint32_t expected_pixels[] { + 14, 13, 12, 11, 10, // + 9, 8, 7, 6, 5, // + 4, 3, 2, 1, 0, // + }; + ArrayDesktopFrame expected(DesktopSize(5, 3), expected_pixels); + + BasicDesktopFrame target(DesktopSize(5, 3)); + RotateDesktopFrame(frame, DesktopRect::MakeSize(frame.size()), + Rotation::CLOCK_WISE_180, DesktopVector(), &target); + ASSERT_TRUE(DesktopFrameDataEquals(target, expected)); + } + + // After Rotating clock-wise 270 degree + { + static uint32_t expected_pixels[] = { + 4, 9, 14, // + 3, 8, 13, // + 2, 7, 12, // + 1, 6, 11, // + 0, 5, 10, // + }; + ArrayDesktopFrame expected(DesktopSize(3, 5), expected_pixels); + + BasicDesktopFrame target(DesktopSize(3, 5)); + RotateDesktopFrame(frame, DesktopRect::MakeSize(frame.size()), + Rotation::CLOCK_WISE_270, DesktopVector(), &target); + ASSERT_TRUE(DesktopFrameDataEquals(target, expected)); + } +} + +TEST(DesktopFrameRotationTest, PartialCopyRect3x5) { + // A DesktopFrame of 5-pixel width by 3-pixel height. + static uint32_t frame_pixels[] = { + 0, 1, 2, 3, 4, // + 5, 6, 7, 8, 9, // + 10, 11, 12, 13, 14, // + }; + ArrayDesktopFrame frame(DesktopSize(5, 3), frame_pixels); + + { + static uint32_t expected_pixels[] = { + 0, 0, 0, 0, 0, // + 0, 6, 7, 8, 0, // + 0, 0, 0, 0, 0, // + }; + ArrayDesktopFrame expected(DesktopSize(5, 3), expected_pixels); + + BasicDesktopFrame target(DesktopSize(5, 3)); + ClearDesktopFrame(&target); + RotateDesktopFrame(frame, DesktopRect::MakeXYWH(1, 1, 3, 1), + Rotation::CLOCK_WISE_0, DesktopVector(), &target); + ASSERT_TRUE(DesktopFrameDataEquals(target, expected)); + } + + { + static uint32_t expected_pixels[] = { + 0, 1, 2, 3, 0, // + 0, 6, 7, 8, 0, // + 0, 0, 0, 0, 0, // + }; + ArrayDesktopFrame expected(DesktopSize(5, 3), expected_pixels); + + BasicDesktopFrame target(DesktopSize(5, 3)); + ClearDesktopFrame(&target); + RotateDesktopFrame(frame, DesktopRect::MakeXYWH(1, 0, 3, 2), + Rotation::CLOCK_WISE_0, DesktopVector(), &target); + ASSERT_TRUE(DesktopFrameDataEquals(target, expected)); + } + + // After Rotating clock-wise 90 degree + { + static uint32_t expected_pixels[] = { + 0, 0, 0, // + 0, 6, 0, // + 0, 7, 0, // + 0, 8, 0, // + 0, 0, 0, // + }; + ArrayDesktopFrame expected(DesktopSize(3, 5), expected_pixels); + + BasicDesktopFrame target(DesktopSize(3, 5)); + ClearDesktopFrame(&target); + RotateDesktopFrame(frame, DesktopRect::MakeXYWH(1, 1, 3, 1), + Rotation::CLOCK_WISE_90, DesktopVector(), &target); + ASSERT_TRUE(DesktopFrameDataEquals(target, expected)); + } + + { + static uint32_t expected_pixels[] = { + 0, 0, 0, // + 11, 6, 0, // + 12, 7, 0, // + 13, 8, 0, // + 0, 0, 0, // + }; + ArrayDesktopFrame expected(DesktopSize(3, 5), expected_pixels); + + BasicDesktopFrame target(DesktopSize(3, 5)); + ClearDesktopFrame(&target); + RotateDesktopFrame(frame, DesktopRect::MakeXYWH(1, 1, 3, 2), + Rotation::CLOCK_WISE_90, DesktopVector(), &target); + ASSERT_TRUE(DesktopFrameDataEquals(target, expected)); + } + + // After Rotating clock-wise 180 degree + { + static uint32_t expected_pixels[] = { + 0, 0, 0, 0, 0, // + 0, 8, 7, 6, 0, // + 0, 0, 0, 0, 0, // + }; + ArrayDesktopFrame expected(DesktopSize(5, 3), expected_pixels); + + BasicDesktopFrame target(DesktopSize(5, 3)); + ClearDesktopFrame(&target); + RotateDesktopFrame(frame, DesktopRect::MakeXYWH(1, 1, 3, 1), + Rotation::CLOCK_WISE_180, DesktopVector(), &target); + ASSERT_TRUE(DesktopFrameDataEquals(target, expected)); + } + + { + static uint32_t expected_pixels[] = { + 0, 13, 12, 11, 0, // + 0, 8, 7, 6, 0, // + 0, 0, 0, 0, 0, // + }; + ArrayDesktopFrame expected(DesktopSize(5, 3), expected_pixels); + + BasicDesktopFrame target(DesktopSize(5, 3)); + ClearDesktopFrame(&target); + RotateDesktopFrame(frame, DesktopRect::MakeXYWH(1, 1, 3, 2), + Rotation::CLOCK_WISE_180, DesktopVector(), &target); + ASSERT_TRUE(DesktopFrameDataEquals(target, expected)); + } + + // After Rotating clock-wise 270 degree + { + static uint32_t expected_pixels[] = { + 0, 0, 0, // + 0, 8, 0, // + 0, 7, 0, // + 0, 6, 0, // + 0, 0, 0, // + }; + ArrayDesktopFrame expected(DesktopSize(3, 5), expected_pixels); + + BasicDesktopFrame target(DesktopSize(3, 5)); + ClearDesktopFrame(&target); + RotateDesktopFrame(frame, DesktopRect::MakeXYWH(1, 1, 3, 1), + Rotation::CLOCK_WISE_270, DesktopVector(), &target); + ASSERT_TRUE(DesktopFrameDataEquals(target, expected)); + } + + { + static uint32_t expected_pixels[] = { + 0, 0, 0, // + 3, 8, 0, // + 2, 7, 0, // + 1, 6, 0, // + 0, 0, 0, // + }; + ArrayDesktopFrame expected(DesktopSize(3, 5), expected_pixels); + + BasicDesktopFrame target(DesktopSize(3, 5)); + ClearDesktopFrame(&target); + RotateDesktopFrame(frame, DesktopRect::MakeXYWH(1, 0, 3, 2), + Rotation::CLOCK_WISE_270, DesktopVector(), &target); + ASSERT_TRUE(DesktopFrameDataEquals(target, expected)); + } +} + +TEST(DesktopFrameRotationTest, WithOffset) { + // A DesktopFrame of 4-pixel width by 3-pixel height. + static uint32_t frame_pixels[] = { + 0, 1, 2, 3, // + 4, 5, 6, 7, // + 8, 9, 10, 11, // + }; + ArrayDesktopFrame frame(DesktopSize(4, 3), frame_pixels); + + { + static uint32_t expected_pixels[] = { + 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 1, 2, 3, 0, 0, 0, // + 0, 4, 5, 6, 7, 0, 0, 0, // + 0, 8, 9, 10, 11, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, // + }; + ArrayDesktopFrame expected(DesktopSize(8, 6), expected_pixels); + + BasicDesktopFrame target(DesktopSize(8, 6)); + ClearDesktopFrame(&target); + RotateDesktopFrame(frame, DesktopRect::MakeSize(frame.size()), + Rotation::CLOCK_WISE_0, DesktopVector(1, 1), &target); + ASSERT_TRUE(DesktopFrameDataEquals(target, expected)); + target.mutable_updated_region()->Subtract( + DesktopRect::MakeOriginSize(DesktopVector(1, 1), frame.size())); + ASSERT_TRUE(target.updated_region().is_empty()); + } + + { + static uint32_t expected_pixels[] = { + 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 11, 10, 9, 8, 0, 0, 0, // + 0, 7, 6, 5, 4, 0, 0, 0, // + 0, 3, 2, 1, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, // + }; + ArrayDesktopFrame expected(DesktopSize(8, 6), expected_pixels); + + BasicDesktopFrame target(DesktopSize(8, 6)); + ClearDesktopFrame(&target); + RotateDesktopFrame(frame, DesktopRect::MakeSize(frame.size()), + Rotation::CLOCK_WISE_180, DesktopVector(1, 1), &target); + ASSERT_TRUE(DesktopFrameDataEquals(target, expected)); + target.mutable_updated_region()->Subtract( + DesktopRect::MakeOriginSize(DesktopVector(1, 1), frame.size())); + ASSERT_TRUE(target.updated_region().is_empty()); + } + + { + static uint32_t expected_pixels[] = { + 0, 0, 0, 0, 0, 0, // + 0, 8, 4, 0, 0, 0, // + 0, 9, 5, 1, 0, 0, // + 0, 10, 6, 2, 0, 0, // + 0, 11, 7, 3, 0, 0, // + 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, // + }; + ArrayDesktopFrame expected(DesktopSize(6, 8), expected_pixels); + + BasicDesktopFrame target(DesktopSize(6, 8)); + ClearDesktopFrame(&target); + RotateDesktopFrame(frame, DesktopRect::MakeSize(frame.size()), + Rotation::CLOCK_WISE_90, DesktopVector(1, 1), &target); + ASSERT_TRUE(DesktopFrameDataEquals(target, expected)); + target.mutable_updated_region()->Subtract( + DesktopRect::MakeXYWH(1, 1, 3, 4)); + ASSERT_TRUE(target.updated_region().is_empty()); + } + + { + static uint32_t expected_pixels[] = { + 0, 0, 0, 0, 0, 0, // + 0, 3, 7, 11, 0, 0, // + 0, 2, 6, 10, 0, 0, // + 0, 1, 5, 9, 0, 0, // + 0, 0, 4, 8, 0, 0, // + 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, // + }; + ArrayDesktopFrame expected(DesktopSize(6, 8), expected_pixels); + + BasicDesktopFrame target(DesktopSize(6, 8)); + ClearDesktopFrame(&target); + RotateDesktopFrame(frame, DesktopRect::MakeSize(frame.size()), + Rotation::CLOCK_WISE_270, DesktopVector(1, 1), &target); + ASSERT_TRUE(DesktopFrameDataEquals(target, expected)); + target.mutable_updated_region()->Subtract( + DesktopRect::MakeXYWH(1, 1, 3, 4)); + ASSERT_TRUE(target.updated_region().is_empty()); + } +} + +// On a typical machine (Intel(R) Xeon(R) E5-1650 v3 @ 3.50GHz, with O2 +// optimization, the following case uses ~1.4s to finish. It means entirely +// rotating one 2048 x 1536 frame, which is a large enough number to cover most +// of desktop computer users, uses around 14ms. +TEST(DesktopFrameRotationTest, DISABLED_PerformanceTest) { + BasicDesktopFrame frame(DesktopSize(2048, 1536)); + BasicDesktopFrame target(DesktopSize(1536, 2048)); + BasicDesktopFrame target2(DesktopSize(2048, 1536)); + for (int i = 0; i < 100; i++) { + RotateDesktopFrame(frame, DesktopRect::MakeSize(frame.size()), + Rotation::CLOCK_WISE_90, DesktopVector(), &target); + RotateDesktopFrame(frame, DesktopRect::MakeSize(frame.size()), + Rotation::CLOCK_WISE_270, DesktopVector(), &target); + RotateDesktopFrame(frame, DesktopRect::MakeSize(frame.size()), + Rotation::CLOCK_WISE_0, DesktopVector(), &target2); + RotateDesktopFrame(frame, DesktopRect::MakeSize(frame.size()), + Rotation::CLOCK_WISE_180, DesktopVector(), &target2); + } +} + +// On a typical machine (Intel(R) Xeon(R) E5-1650 v3 @ 3.50GHz, with O2 +// optimization, the following case uses ~6.7s to finish. It means entirely +// rotating one 4096 x 3072 frame uses around 67ms. +TEST(DesktopFrameRotationTest, DISABLED_PerformanceTestOnLargeScreen) { + BasicDesktopFrame frame(DesktopSize(4096, 3072)); + BasicDesktopFrame target(DesktopSize(3072, 4096)); + BasicDesktopFrame target2(DesktopSize(4096, 3072)); + for (int i = 0; i < 100; i++) { + RotateDesktopFrame(frame, DesktopRect::MakeSize(frame.size()), + Rotation::CLOCK_WISE_90, DesktopVector(), &target); + RotateDesktopFrame(frame, DesktopRect::MakeSize(frame.size()), + Rotation::CLOCK_WISE_270, DesktopVector(), &target); + RotateDesktopFrame(frame, DesktopRect::MakeSize(frame.size()), + Rotation::CLOCK_WISE_0, DesktopVector(), &target2); + RotateDesktopFrame(frame, DesktopRect::MakeSize(frame.size()), + Rotation::CLOCK_WISE_180, DesktopVector(), &target2); + } +} + +} // namespace webrtc diff --git a/webrtc/modules/desktop_capture/test_utils.cc b/webrtc/modules/desktop_capture/test_utils.cc new file mode 100644 index 0000000000..a312a3feac --- /dev/null +++ b/webrtc/modules/desktop_capture/test_utils.cc @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/desktop_capture/test_utils.h" + +#include + +#include "webrtc/base/checks.h" + +namespace webrtc { + +void ClearDesktopFrame(DesktopFrame* frame) { + RTC_DCHECK(frame); + uint8_t* data = frame->data(); + for (int i = 0; i < frame->size().height(); i++) { + memset(data, 0, frame->size().width() * DesktopFrame::kBytesPerPixel); + data += frame->stride(); + } +} + +bool DesktopFrameDataEquals(const DesktopFrame& left, + const DesktopFrame& right) { + if (!left.size().equals(right.size())) { + return false; + } + + const uint8_t* left_array = left.data(); + const uint8_t* right_array = right.data(); + for (int i = 0; i < left.size().height(); i++) { + if (memcmp(left_array, + right_array, + DesktopFrame::kBytesPerPixel * left.size().width()) != 0) { + return false; + } + left_array += left.stride(); + right_array += right.stride(); + } + + return true; +} + +} // namespace webrtc diff --git a/webrtc/modules/desktop_capture/test_utils.h b/webrtc/modules/desktop_capture/test_utils.h new file mode 100644 index 0000000000..ca272986a0 --- /dev/null +++ b/webrtc/modules/desktop_capture/test_utils.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_TEST_UTILS_H_ +#define WEBRTC_MODULES_DESKTOP_CAPTURE_TEST_UTILS_H_ + +#include "webrtc/modules/desktop_capture/desktop_frame.h" + +namespace webrtc { + +// Clears a DesktopFrame |frame| by setting its data() into 0. +void ClearDesktopFrame(DesktopFrame* frame); + +// Compares size() and data() of two DesktopFrames |left| and |right|. +bool DesktopFrameDataEquals(const DesktopFrame& left, + const DesktopFrame& right); + +} // namespace webrtc + +#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_TEST_UTILS_H_ diff --git a/webrtc/modules/desktop_capture/test_utils_unittest.cc b/webrtc/modules/desktop_capture/test_utils_unittest.cc new file mode 100644 index 0000000000..8d3389f715 --- /dev/null +++ b/webrtc/modules/desktop_capture/test_utils_unittest.cc @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/desktop_capture/test_utils.h" + +#include "webrtc/base/checks.h" +#include "webrtc/modules/desktop_capture/rgba_color.h" +#include "webrtc/test/gtest.h" + +namespace webrtc { + +namespace { + +void PaintDesktopFrame(DesktopFrame* frame, + DesktopVector pos, + RgbaColor color) { + RTC_DCHECK(frame); + RTC_DCHECK(DesktopRect::MakeSize(frame->size()).Contains(pos)); + *reinterpret_cast(frame->GetFrameDataAtPos(pos)) = + color.ToUInt32(); +} + +// A DesktopFrame implementation to store data in heap, but the stide is +// doubled. +class DoubleSizeDesktopFrame : public DesktopFrame { + public: + explicit DoubleSizeDesktopFrame(DesktopSize size); + ~DoubleSizeDesktopFrame() override; +}; + +DoubleSizeDesktopFrame::DoubleSizeDesktopFrame(DesktopSize size) + : DesktopFrame( + size, + kBytesPerPixel * size.width() * 2, + new uint8_t[kBytesPerPixel * size.width() * size.height() * 2], + nullptr) {} + +DoubleSizeDesktopFrame::~DoubleSizeDesktopFrame() { + delete[] data_; +} + +} // namespace + +TEST(TestUtilsTest, BasicDataEqualsCases) { + BasicDesktopFrame frame(DesktopSize(4, 4)); + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + PaintDesktopFrame(&frame, DesktopVector(i, j), RgbaColor(4U * j + i)); + } + } + + ASSERT_TRUE(DesktopFrameDataEquals(frame, frame)); + BasicDesktopFrame other(DesktopSize(4, 4)); + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + PaintDesktopFrame(&other, DesktopVector(i, j), RgbaColor(4U * j + i)); + } + } + ASSERT_TRUE(DesktopFrameDataEquals(frame, other)); + PaintDesktopFrame(&other, DesktopVector(2, 2), RgbaColor(0U)); + ASSERT_FALSE(DesktopFrameDataEquals(frame, other)); +} + +TEST(TestUtilsTest, DifferentSizeShouldNotEqual) { + BasicDesktopFrame frame(DesktopSize(4, 4)); + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + PaintDesktopFrame(&frame, DesktopVector(i, j), RgbaColor(4U * j + i)); + } + } + + BasicDesktopFrame other(DesktopSize(2, 8)); + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 8; j++) { + PaintDesktopFrame(&other, DesktopVector(i, j), RgbaColor(2U * j + i)); + } + } + + ASSERT_FALSE(DesktopFrameDataEquals(frame, other)); +} + +TEST(TestUtilsTest, DifferentStrideShouldBeComparable) { + BasicDesktopFrame frame(DesktopSize(4, 4)); + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + PaintDesktopFrame(&frame, DesktopVector(i, j), RgbaColor(4U * j + i)); + } + } + + ASSERT_TRUE(DesktopFrameDataEquals(frame, frame)); + DoubleSizeDesktopFrame other(DesktopSize(4, 4)); + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + PaintDesktopFrame(&other, DesktopVector(i, j), RgbaColor(4U * j + i)); + } + } + ASSERT_TRUE(DesktopFrameDataEquals(frame, other)); +} + +} // namespace webrtc