1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <algorithm>
6
7#include "remoting/base/util.h"
8#include "testing/gtest/include/gtest/gtest.h"
9#include "third_party/libyuv/include/libyuv/convert_from_argb.h"
10#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
11
12static const int kWidth = 32 ;
13static const int kHeight = 24 ;
14static const int kBytesPerPixel = 4;
15static const int kYStride = kWidth;
16static const int kUvStride = kWidth / 2;
17static const int kRgbStride = kWidth * kBytesPerPixel;
18static const uint32 kFillColor = 0xffffff;
19
20namespace remoting {
21
22class YuvToRgbTester {
23 public:
24  YuvToRgbTester() {
25    yuv_buffer_size_ = (kYStride + kUvStride) * kHeight;
26    yuv_buffer_.reset(new uint8[yuv_buffer_size_]);
27    yplane_ = yuv_buffer_.get();
28    uplane_ = yplane_ + (kYStride * kHeight);
29    vplane_ = uplane_ + (kUvStride * kHeight / 2);
30
31    rgb_buffer_size_ = kWidth * kHeight * kBytesPerPixel;
32    rgb_buffer_.reset(new uint8[rgb_buffer_size_]);
33
34    ResetYuvBuffer();
35    ResetRgbBuffer();
36  }
37
38  ~YuvToRgbTester() {}
39
40  void ResetYuvBuffer() {
41    memset(yuv_buffer_.get(), 0, yuv_buffer_size_);
42  }
43
44  void ResetRgbBuffer() {
45    memset(rgb_buffer_.get(), 0, rgb_buffer_size_);
46  }
47
48  void FillRgbBuffer(const webrtc::DesktopRect& rect) {
49    uint32* ptr = reinterpret_cast<uint32*>(
50        rgb_buffer_.get() + (rect.top() * kRgbStride) +
51        (rect.left() * kBytesPerPixel));
52    int width = rect.width();
53    for (int height = rect.height(); height > 0; --height) {
54      std::fill(ptr, ptr + width, kFillColor);
55      ptr += kRgbStride / kBytesPerPixel;
56    }
57  }
58
59  // Check the the desination buffer is filled within expected bounds.
60  void  CheckRgbBuffer(const webrtc::DesktopRect& rect) {
61    uint32* ptr = reinterpret_cast<uint32*>(rgb_buffer_.get());
62    for (int y = 0; y < kHeight; ++y) {
63      if (y < rect.top() || rect.bottom() <= y) {
64        // The whole line should be intact.
65        EXPECT_EQ((ptrdiff_t)kWidth,
66                  std::count(ptr, ptr + kWidth, 0u));
67      } else {
68        // The space before the painted rectangle should be intact.
69        EXPECT_EQ((ptrdiff_t)rect.left(),
70                  std::count(ptr, ptr + rect.left(), 0u));
71
72        // All pixels of the target rectangle should be touched.
73        EXPECT_EQ(ptr + rect.right(),
74                  std::find(ptr + rect.left(), ptr + rect.right(), 0u));
75
76        // The space after the painted rectangle should be intact.
77        EXPECT_EQ((ptrdiff_t)kWidth - rect.right(),
78                  std::count(ptr + rect.right(), ptr + kWidth, 0u));
79      }
80      ptr += kRgbStride / kBytesPerPixel;
81    }
82  }
83
84  void RunTest(const webrtc::DesktopSize dest_size,
85               const webrtc::DesktopRect& rect) {
86    ASSERT_TRUE(
87        DoesRectContain(webrtc::DesktopRect::MakeSize(dest_size), rect));
88
89    // Reset buffers.
90    ResetYuvBuffer();
91    ResetRgbBuffer();
92    FillRgbBuffer(rect);
93
94    // RGB -> YUV
95    libyuv::ARGBToI420(rgb_buffer_.get(),
96                       kRgbStride,
97                       yplane_,
98                       kYStride,
99                       uplane_,
100                       kUvStride,
101                       vplane_,
102                       kUvStride,
103                       kWidth,
104                       kHeight);
105
106    // Reset RGB buffer and do opposite conversion.
107    ResetRgbBuffer();
108    ConvertAndScaleYUVToRGB32Rect(yplane_,
109                                  uplane_,
110                                  vplane_,
111                                  kYStride,
112                                  kUvStride,
113                                  webrtc::DesktopSize(kWidth, kHeight),
114                                  webrtc::DesktopRect::MakeWH(kWidth, kHeight),
115                                  rgb_buffer_.get(),
116                                  kRgbStride,
117                                  dest_size,
118                                  webrtc::DesktopRect::MakeSize(dest_size),
119                                  rect);
120
121    // Check if it worked out.
122    CheckRgbBuffer(rect);
123  }
124
125  void TestBasicConversion() {
126    // Whole buffer.
127    RunTest(webrtc::DesktopSize(kWidth, kHeight),
128            webrtc::DesktopRect::MakeWH(kWidth, kHeight));
129  }
130
131 private:
132  size_t yuv_buffer_size_;
133  scoped_ptr<uint8[]> yuv_buffer_;
134  uint8* yplane_;
135  uint8* uplane_;
136  uint8* vplane_;
137
138  size_t rgb_buffer_size_;
139  scoped_ptr<uint8[]> rgb_buffer_;
140
141  DISALLOW_COPY_AND_ASSIGN(YuvToRgbTester);
142};
143
144TEST(YuvToRgbTest, BasicConversion) {
145  YuvToRgbTester tester;
146  tester.TestBasicConversion();
147}
148
149TEST(YuvToRgbTest, Clipping) {
150  YuvToRgbTester tester;
151
152  webrtc::DesktopSize dest_size = webrtc::DesktopSize(kWidth, kHeight);
153  webrtc::DesktopRect rect =
154      webrtc::DesktopRect::MakeLTRB(0, 0, kWidth - 1, kHeight - 1);
155  // TODO(fbarchard): Allow top/left clipping to odd boundary.
156  for (int i = 0; i < 16; ++i) {
157    webrtc::DesktopRect dest_rect = webrtc::DesktopRect::MakeLTRB(
158        rect.left() + ((i & 1) ? 2 : 0),
159        rect.top() + ((i & 2) ? 2 : 0),
160        rect.right() - ((i & 4) ? 1 : 0),
161        rect.bottom() - ((i & 8) ? 1 : 0));
162
163    tester.RunTest(dest_size, dest_rect);
164  }
165}
166
167TEST(YuvToRgbTest, ClippingAndScaling) {
168  YuvToRgbTester tester;
169
170  webrtc::DesktopSize dest_size =
171      webrtc::DesktopSize(kWidth - 10, kHeight - 10);
172  webrtc::DesktopRect rect =
173      webrtc::DesktopRect::MakeLTRB(6, 6, kWidth - 11, kHeight - 11);
174  for (int i = 0; i < 16; ++i) {
175    webrtc::DesktopRect dest_rect = webrtc::DesktopRect::MakeLTRB(
176        rect.left() + ((i & 1) ? 2 : 0),
177        rect.top() + ((i & 2) ? 2 : 0),
178        rect.right() - ((i & 4) ? 1 : 0),
179        rect.bottom() - ((i & 8) ? 1 : 0));
180
181    tester.RunTest(dest_size, dest_rect);
182  }
183}
184
185TEST(ReplaceLfByCrLfTest, Basic) {
186  EXPECT_EQ("ab", ReplaceLfByCrLf("ab"));
187  EXPECT_EQ("\r\nab", ReplaceLfByCrLf("\nab"));
188  EXPECT_EQ("\r\nab\r\n", ReplaceLfByCrLf("\nab\n"));
189  EXPECT_EQ("\r\nab\r\ncd", ReplaceLfByCrLf("\nab\ncd"));
190  EXPECT_EQ("\r\nab\r\ncd\r\n", ReplaceLfByCrLf("\nab\ncd\n"));
191  EXPECT_EQ("\r\n\r\nab\r\n\r\ncd\r\n\r\n",
192      ReplaceLfByCrLf("\n\nab\n\ncd\n\n"));
193}
194
195TEST(ReplaceLfByCrLfTest, Speed) {
196  int kLineSize = 128;
197  std::string line(kLineSize, 'a');
198  line[kLineSize - 1] = '\n';
199  // Make a 10M string.
200  int kLineNum = 10 * 1024 * 1024 / kLineSize;
201  std::string buffer;
202  buffer.resize(kLineNum * kLineSize);
203  for (int i = 0; i < kLineNum; ++i) {
204    memcpy(&buffer[i * kLineSize], &line[0], kLineSize);
205  }
206  // Convert the string.
207  buffer = ReplaceLfByCrLf(buffer);
208  // Check the converted string.
209  EXPECT_EQ(static_cast<size_t>((kLineSize + 1) * kLineNum), buffer.size());
210  const char* p = &buffer[0];
211  for (int i = 0; i < kLineNum; ++i) {
212    EXPECT_EQ(0, memcmp(&line[0], p, kLineSize - 1));
213    p += kLineSize - 1;
214    EXPECT_EQ('\r', *p++);
215    EXPECT_EQ('\n', *p++);
216  }
217}
218
219TEST(ReplaceCrLfByLfTest, Basic) {
220  EXPECT_EQ("ab", ReplaceCrLfByLf("ab"));
221  EXPECT_EQ("\nab", ReplaceCrLfByLf("\r\nab"));
222  EXPECT_EQ("\nab\n", ReplaceCrLfByLf("\r\nab\r\n"));
223  EXPECT_EQ("\nab\ncd", ReplaceCrLfByLf("\r\nab\r\ncd"));
224  EXPECT_EQ("\nab\ncd\n", ReplaceCrLfByLf("\r\nab\r\ncd\n"));
225  EXPECT_EQ("\n\nab\n\ncd\n\n",
226      ReplaceCrLfByLf("\r\n\r\nab\r\n\r\ncd\r\n\r\n"));
227  EXPECT_EQ("\rab\rcd\r", ReplaceCrLfByLf("\rab\rcd\r"));
228}
229
230TEST(ReplaceCrLfByLfTest, Speed) {
231  int kLineSize = 128;
232  std::string line(kLineSize, 'a');
233  line[kLineSize - 2] = '\r';
234  line[kLineSize - 1] = '\n';
235  // Make a 10M string.
236  int kLineNum = 10 * 1024 * 1024 / kLineSize;
237  std::string buffer;
238  buffer.resize(kLineNum * kLineSize);
239  for (int i = 0; i < kLineNum; ++i) {
240    memcpy(&buffer[i * kLineSize], &line[0], kLineSize);
241  }
242  // Convert the string.
243  buffer = ReplaceCrLfByLf(buffer);
244  // Check the converted string.
245  EXPECT_EQ(static_cast<size_t>((kLineSize - 1) * kLineNum), buffer.size());
246  const char* p = &buffer[0];
247  for (int i = 0; i < kLineNum; ++i) {
248    EXPECT_EQ(0, memcmp(&line[0], p, kLineSize - 2));
249    p += kLineSize - 2;
250    EXPECT_EQ('\n', *p++);
251  }
252}
253
254TEST(StringIsUtf8Test, Basic) {
255  EXPECT_TRUE(StringIsUtf8("", 0));
256  EXPECT_TRUE(StringIsUtf8("\0", 1));
257  EXPECT_TRUE(StringIsUtf8("abc", 3));
258  EXPECT_TRUE(StringIsUtf8("\xc0\x80", 2));
259  EXPECT_TRUE(StringIsUtf8("\xe0\x80\x80", 3));
260  EXPECT_TRUE(StringIsUtf8("\xf0\x80\x80\x80", 4));
261  EXPECT_TRUE(StringIsUtf8("\xf8\x80\x80\x80\x80", 5));
262  EXPECT_TRUE(StringIsUtf8("\xfc\x80\x80\x80\x80\x80", 6));
263
264  // Not enough continuation characters
265  EXPECT_FALSE(StringIsUtf8("\xc0", 1));
266  EXPECT_FALSE(StringIsUtf8("\xe0\x80", 2));
267  EXPECT_FALSE(StringIsUtf8("\xf0\x80\x80", 3));
268  EXPECT_FALSE(StringIsUtf8("\xf8\x80\x80\x80", 4));
269  EXPECT_FALSE(StringIsUtf8("\xfc\x80\x80\x80\x80", 5));
270
271  // One more continuation character than needed
272  EXPECT_FALSE(StringIsUtf8("\xc0\x80\x80", 3));
273  EXPECT_FALSE(StringIsUtf8("\xe0\x80\x80\x80", 4));
274  EXPECT_FALSE(StringIsUtf8("\xf0\x80\x80\x80\x80", 5));
275  EXPECT_FALSE(StringIsUtf8("\xf8\x80\x80\x80\x80\x80", 6));
276  EXPECT_FALSE(StringIsUtf8("\xfc\x80\x80\x80\x80\x80\x80", 7));
277
278  // Invalid first byte
279  EXPECT_FALSE(StringIsUtf8("\xfe\x80\x80\x80\x80\x80\x80", 7));
280  EXPECT_FALSE(StringIsUtf8("\xff\x80\x80\x80\x80\x80\x80", 7));
281
282  // Invalid continuation byte
283  EXPECT_FALSE(StringIsUtf8("\xc0\x00", 2));
284  EXPECT_FALSE(StringIsUtf8("\xc0\x40", 2));
285  EXPECT_FALSE(StringIsUtf8("\xc0\xc0", 2));
286}
287
288}  // namespace remoting
289