Add DesktopFrame rotation functions
This change adds RotateDesktopFrame(), RotateRect(), RotateSize(), ReverseRotate() functions, so an implementation can use these free functions to rotate and copy pixels from one DesktopFrame to another at the same time. This is the first part of the change to support rotation in DirectX capturer. In a coming change, these functions will be used in DxgiOutputDuplicator to do the rotation and copying. Background, DirectX APIs always return unrotated data buffer, so we need to rotate it to match the user-selected rotation. What worse is except for the data buffer, other variables return by these APIs are all rotated, e.g. output size, monitor position. So we will eventually not be able to capture the rotated monitors, because we cannot set their position and size correctly. Though DXGI_OUTDUPL_DESC provides a DXGI_MODE_ROTATION enumeration to indicate the output rotation, it does not provide a simple way to rotate an IDXGIResource, which is the only thing we can get from duplication APIs. A typical user case here is to use a matrix to transform the IDXGIResource and render it to a surface. But since we do not render the IDXGIResource at all, we need to manually rotate it. BUG=314516 Review-Url: https://codereview.webrtc.org/2500883004 Cr-Commit-Position: refs/heads/master@{#15205}
This commit is contained in:
@ -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",
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
include_rules = [
|
||||
"+webrtc/base",
|
||||
"+webrtc/system_wrappers",
|
||||
"+third_party/libyuv",
|
||||
]
|
||||
|
||||
120
webrtc/modules/desktop_capture/desktop_frame_rotation.cc
Normal file
120
webrtc/modules/desktop_capture/desktop_frame_rotation.cc
Normal file
@ -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 <string.h>
|
||||
|
||||
#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
|
||||
53
webrtc/modules/desktop_capture/desktop_frame_rotation.h
Normal file
53
webrtc/modules/desktop_capture/desktop_frame_rotation.h
Normal file
@ -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_
|
||||
@ -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<uint8_t*>(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
|
||||
49
webrtc/modules/desktop_capture/test_utils.cc
Normal file
49
webrtc/modules/desktop_capture/test_utils.cc
Normal file
@ -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 <string.h>
|
||||
|
||||
#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
|
||||
27
webrtc/modules/desktop_capture/test_utils.h
Normal file
27
webrtc/modules/desktop_capture/test_utils.h
Normal file
@ -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_
|
||||
107
webrtc/modules/desktop_capture/test_utils_unittest.cc
Normal file
107
webrtc/modules/desktop_capture/test_utils_unittest.cc
Normal file
@ -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<uint32_t*>(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
|
||||
Reference in New Issue
Block a user