// 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