// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <algorithm>
#include "remoting/base/util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
static const int kWidth = 32 ;
static const int kHeight = 24 ;
static const int kBytesPerPixel = 4;
static const int kYStride = kWidth;
static const int kUvStride = kWidth / 2;
static const int kRgbStride = kWidth * kBytesPerPixel;
static const uint32 kFillColor = 0xffffff;
namespace remoting {
class YuvToRgbTester {
public:
YuvToRgbTester() {
yuv_buffer_size_ = (kYStride + kUvStride) * kHeight;
yuv_buffer_.reset(new uint8[yuv_buffer_size_]);
yplane_ = yuv_buffer_.get();
uplane_ = yplane_ + (kYStride * kHeight);
vplane_ = uplane_ + (kUvStride * kHeight / 2);
rgb_buffer_size_ = kWidth * kHeight * kBytesPerPixel;
rgb_buffer_.reset(new uint8[rgb_buffer_size_]);
ResetYuvBuffer();
ResetRgbBuffer();
}
~YuvToRgbTester() {}
void ResetYuvBuffer() {
memset(yuv_buffer_.get(), 0, yuv_buffer_size_);
}
void ResetRgbBuffer() {
memset(rgb_buffer_.get(), 0, rgb_buffer_size_);
}
void FillRgbBuffer(const webrtc::DesktopRect& rect) {
uint32* ptr = reinterpret_cast<uint32*>(
rgb_buffer_.get() + (rect.top() * kRgbStride) +
(rect.left() * kBytesPerPixel));
int width = rect.width();
for (int height = rect.height(); height > 0; --height) {
std::fill(ptr, ptr + width, kFillColor);
ptr += kRgbStride / kBytesPerPixel;
}
}
// Check the the desination buffer is filled within expected bounds.
void CheckRgbBuffer(const webrtc::DesktopRect& rect) {
uint32* ptr = reinterpret_cast<uint32*>(rgb_buffer_.get());
for (int y = 0; y < kHeight; ++y) {
if (y < rect.top() || rect.bottom() <= y) {
// The whole line should be intact.
EXPECT_EQ((ptrdiff_t)kWidth,
std::count(ptr, ptr + kWidth, 0u));
} else {
// The space before the painted rectangle should be intact.
EXPECT_EQ((ptrdiff_t)rect.left(),
std::count(ptr, ptr + rect.left(), 0u));
// All pixels of the target rectangle should be touched.
EXPECT_EQ(ptr + rect.right(),
std::find(ptr + rect.left(), ptr + rect.right(), 0u));
// The space after the painted rectangle should be intact.
EXPECT_EQ((ptrdiff_t)kWidth - rect.right(),
std::count(ptr + rect.right(), ptr + kWidth, 0u));
}
ptr += kRgbStride / kBytesPerPixel;
}
}
void RunTest(const webrtc::DesktopSize dest_size,
const webrtc::DesktopRect& rect) {
ASSERT_TRUE(
DoesRectContain(webrtc::DesktopRect::MakeSize(dest_size), rect));
// Reset buffers.
ResetYuvBuffer();
ResetRgbBuffer();
FillRgbBuffer(rect);
// RGB -> YUV
ConvertRGB32ToYUVWithRect(rgb_buffer_.get(),
yplane_,
uplane_,
vplane_,
0,
0,
kWidth,
kHeight,
kRgbStride,
kYStride,
kUvStride);
// Reset RGB buffer and do opposite conversion.
ResetRgbBuffer();
ConvertAndScaleYUVToRGB32Rect(yplane_,
uplane_,
vplane_,
kYStride,
kUvStride,
webrtc::DesktopSize(kWidth, kHeight),
webrtc::DesktopRect::MakeWH(kWidth, kHeight),
rgb_buffer_.get(),
kRgbStride,
dest_size,
webrtc::DesktopRect::MakeSize(dest_size),
rect);
// Check if it worked out.
CheckRgbBuffer(rect);
}
void TestBasicConversion() {
// Whole buffer.
RunTest(webrtc::DesktopSize(kWidth, kHeight),
webrtc::DesktopRect::MakeWH(kWidth, kHeight));
}
private:
size_t yuv_buffer_size_;
scoped_ptr<uint8[]> yuv_buffer_;
uint8* yplane_;
uint8* uplane_;
uint8* vplane_;
size_t rgb_buffer_size_;
scoped_ptr<uint8[]> rgb_buffer_;
DISALLOW_COPY_AND_ASSIGN(YuvToRgbTester);
};
TEST(YuvToRgbTest, BasicConversion) {
YuvToRgbTester tester;
tester.TestBasicConversion();
}
TEST(YuvToRgbTest, Clipping) {
YuvToRgbTester tester;
webrtc::DesktopSize dest_size = webrtc::DesktopSize(kWidth, kHeight);
webrtc::DesktopRect rect =
webrtc::DesktopRect::MakeLTRB(0, 0, kWidth - 1, kHeight - 1);
// TODO(fbarchard): Allow top/left clipping to odd boundary.
for (int i = 0; i < 16; ++i) {
webrtc::DesktopRect dest_rect = webrtc::DesktopRect::MakeLTRB(
rect.left() + ((i & 1) ? 2 : 0),
rect.top() + ((i & 2) ? 2 : 0),
rect.right() - ((i & 4) ? 1 : 0),
rect.bottom() - ((i & 8) ? 1 : 0));
tester.RunTest(dest_size, dest_rect);
}
}
TEST(YuvToRgbTest, ClippingAndScaling) {
YuvToRgbTester tester;
webrtc::DesktopSize dest_size =
webrtc::DesktopSize(kWidth - 10, kHeight - 10);
webrtc::DesktopRect rect =
webrtc::DesktopRect::MakeLTRB(6, 6, kWidth - 11, kHeight - 11);
for (int i = 0; i < 16; ++i) {
webrtc::DesktopRect dest_rect = webrtc::DesktopRect::MakeLTRB(
rect.left() + ((i & 1) ? 2 : 0),
rect.top() + ((i & 2) ? 2 : 0),
rect.right() - ((i & 4) ? 1 : 0),
rect.bottom() - ((i & 8) ? 1 : 0));
tester.RunTest(dest_size, dest_rect);
}
}
TEST(ReplaceLfByCrLfTest, Basic) {
EXPECT_EQ("ab", ReplaceLfByCrLf("ab"));
EXPECT_EQ("\r\nab", ReplaceLfByCrLf("\nab"));
EXPECT_EQ("\r\nab\r\n", ReplaceLfByCrLf("\nab\n"));
EXPECT_EQ("\r\nab\r\ncd", ReplaceLfByCrLf("\nab\ncd"));
EXPECT_EQ("\r\nab\r\ncd\r\n", ReplaceLfByCrLf("\nab\ncd\n"));
EXPECT_EQ("\r\n\r\nab\r\n\r\ncd\r\n\r\n",
ReplaceLfByCrLf("\n\nab\n\ncd\n\n"));
}
TEST(ReplaceLfByCrLfTest, Speed) {
int kLineSize = 128;
std::string line(kLineSize, 'a');
line[kLineSize - 1] = '\n';
// Make a 10M string.
int kLineNum = 10 * 1024 * 1024 / kLineSize;
std::string buffer;
buffer.resize(kLineNum * kLineSize);
for (int i = 0; i < kLineNum; ++i) {
memcpy(&buffer[i * kLineSize], &line[0], kLineSize);
}
// Convert the string.
buffer = ReplaceLfByCrLf(buffer);
// Check the converted string.
EXPECT_EQ(static_cast<size_t>((kLineSize + 1) * kLineNum), buffer.size());
const char* p = &buffer[0];
for (int i = 0; i < kLineNum; ++i) {
EXPECT_EQ(0, memcmp(&line[0], p, kLineSize - 1));
p += kLineSize - 1;
EXPECT_EQ('\r', *p++);
EXPECT_EQ('\n', *p++);
}
}
TEST(ReplaceCrLfByLfTest, Basic) {
EXPECT_EQ("ab", ReplaceCrLfByLf("ab"));
EXPECT_EQ("\nab", ReplaceCrLfByLf("\r\nab"));
EXPECT_EQ("\nab\n", ReplaceCrLfByLf("\r\nab\r\n"));
EXPECT_EQ("\nab\ncd", ReplaceCrLfByLf("\r\nab\r\ncd"));
EXPECT_EQ("\nab\ncd\n", ReplaceCrLfByLf("\r\nab\r\ncd\n"));
EXPECT_EQ("\n\nab\n\ncd\n\n",
ReplaceCrLfByLf("\r\n\r\nab\r\n\r\ncd\r\n\r\n"));
EXPECT_EQ("\rab\rcd\r", ReplaceCrLfByLf("\rab\rcd\r"));
}
TEST(ReplaceCrLfByLfTest, Speed) {
int kLineSize = 128;
std::string line(kLineSize, 'a');
line[kLineSize - 2] = '\r';
line[kLineSize - 1] = '\n';
// Make a 10M string.
int kLineNum = 10 * 1024 * 1024 / kLineSize;
std::string buffer;
buffer.resize(kLineNum * kLineSize);
for (int i = 0; i < kLineNum; ++i) {
memcpy(&buffer[i * kLineSize], &line[0], kLineSize);
}
// Convert the string.
buffer = ReplaceCrLfByLf(buffer);
// Check the converted string.
EXPECT_EQ(static_cast<size_t>((kLineSize - 1) * kLineNum), buffer.size());
const char* p = &buffer[0];
for (int i = 0; i < kLineNum; ++i) {
EXPECT_EQ(0, memcmp(&line[0], p, kLineSize - 2));
p += kLineSize - 2;
EXPECT_EQ('\n', *p++);
}
}
TEST(StringIsUtf8Test, Basic) {
EXPECT_TRUE(StringIsUtf8("", 0));
EXPECT_TRUE(StringIsUtf8("\0", 1));
EXPECT_TRUE(StringIsUtf8("abc", 3));
EXPECT_TRUE(StringIsUtf8("\xc0\x80", 2));
EXPECT_TRUE(StringIsUtf8("\xe0\x80\x80", 3));
EXPECT_TRUE(StringIsUtf8("\xf0\x80\x80\x80", 4));
EXPECT_TRUE(StringIsUtf8("\xf8\x80\x80\x80\x80", 5));
EXPECT_TRUE(StringIsUtf8("\xfc\x80\x80\x80\x80\x80", 6));
// Not enough continuation characters
EXPECT_FALSE(StringIsUtf8("\xc0", 1));
EXPECT_FALSE(StringIsUtf8("\xe0\x80", 2));
EXPECT_FALSE(StringIsUtf8("\xf0\x80\x80", 3));
EXPECT_FALSE(StringIsUtf8("\xf8\x80\x80\x80", 4));
EXPECT_FALSE(StringIsUtf8("\xfc\x80\x80\x80\x80", 5));
// One more continuation character than needed
EXPECT_FALSE(StringIsUtf8("\xc0\x80\x80", 3));
EXPECT_FALSE(StringIsUtf8("\xe0\x80\x80\x80", 4));
EXPECT_FALSE(StringIsUtf8("\xf0\x80\x80\x80\x80", 5));
EXPECT_FALSE(StringIsUtf8("\xf8\x80\x80\x80\x80\x80", 6));
EXPECT_FALSE(StringIsUtf8("\xfc\x80\x80\x80\x80\x80\x80", 7));
// Invalid first byte
EXPECT_FALSE(StringIsUtf8("\xfe\x80\x80\x80\x80\x80\x80", 7));
EXPECT_FALSE(StringIsUtf8("\xff\x80\x80\x80\x80\x80\x80", 7));
// Invalid continuation byte
EXPECT_FALSE(StringIsUtf8("\xc0\x00", 2));
EXPECT_FALSE(StringIsUtf8("\xc0\x40", 2));
EXPECT_FALSE(StringIsUtf8("\xc0\xc0", 2));
}
} // namespace remoting