1 /*
2 * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #include "api/video/video_frame.h"
12
13 #include <math.h>
14 #include <string.h>
15
16 #include "api/video/i010_buffer.h"
17 #include "api/video/i210_buffer.h"
18 #include "api/video/i420_buffer.h"
19 #include "api/video/i422_buffer.h"
20 #include "api/video/i444_buffer.h"
21 #include "api/video/nv12_buffer.h"
22 #include "rtc_base/time_utils.h"
23 #include "test/fake_texture_frame.h"
24 #include "test/frame_utils.h"
25 #include "test/gtest.h"
26
27 namespace webrtc {
28
29 namespace {
30
31 struct SubSampling {
32 int x;
33 int y;
34 };
35
SubSamplingForType(VideoFrameBuffer::Type type)36 SubSampling SubSamplingForType(VideoFrameBuffer::Type type) {
37 switch (type) {
38 case VideoFrameBuffer::Type::kI420:
39 return {.x = 2, .y = 2};
40 case VideoFrameBuffer::Type::kI420A:
41 return {.x = 2, .y = 2};
42 case VideoFrameBuffer::Type::kI422:
43 return {.x = 2, .y = 1};
44 case VideoFrameBuffer::Type::kI444:
45 return {.x = 1, .y = 1};
46 case VideoFrameBuffer::Type::kI010:
47 return {.x = 2, .y = 2};
48 case VideoFrameBuffer::Type::kI210:
49 return {.x = 2, .y = 1};
50 default:
51 return {};
52 }
53 }
54
55 // Helper function to create a buffer and fill it with a gradient for
56 // PlanarYuvBuffer based buffers.
57 template <class T>
CreateGradient(int width,int height)58 rtc::scoped_refptr<T> CreateGradient(int width, int height) {
59 rtc::scoped_refptr<T> buffer(T::Create(width, height));
60 // Initialize with gradient, Y = 128(x/w + y/h), U = 256 x/w, V = 256 y/h
61 for (int x = 0; x < width; x++) {
62 for (int y = 0; y < height; y++) {
63 buffer->MutableDataY()[x + y * width] =
64 128 * (x * height + y * width) / (width * height);
65 }
66 }
67 int chroma_width = buffer->ChromaWidth();
68 int chroma_height = buffer->ChromaHeight();
69 for (int x = 0; x < chroma_width; x++) {
70 for (int y = 0; y < chroma_height; y++) {
71 buffer->MutableDataU()[x + y * chroma_width] =
72 255 * x / (chroma_width - 1);
73 buffer->MutableDataV()[x + y * chroma_width] =
74 255 * y / (chroma_height - 1);
75 }
76 }
77 return buffer;
78 }
79
80 // Helper function to create a buffer and fill it with a gradient.
CreateNV12Gradient(int width,int height)81 rtc::scoped_refptr<NV12BufferInterface> CreateNV12Gradient(int width,
82 int height) {
83 rtc::scoped_refptr<NV12Buffer> buffer(NV12Buffer::Create(width, height));
84 // Initialize with gradient, Y = 128(x/w + y/h), U = 256 x/w, V = 256 y/h
85 for (int x = 0; x < width; x++) {
86 for (int y = 0; y < height; y++) {
87 buffer->MutableDataY()[x + y * width] =
88 128 * (x * height + y * width) / (width * height);
89 }
90 }
91 int chroma_width = buffer->ChromaWidth();
92 int chroma_height = buffer->ChromaHeight();
93 for (int x = 0; x < chroma_width; x++) {
94 for (int y = 0; y < chroma_height; y++) {
95 buffer->MutableDataUV()[x * 2 + y * buffer->StrideUV()] =
96 255 * x / (chroma_width - 1);
97 buffer->MutableDataUV()[x * 2 + 1 + y * buffer->StrideUV()] =
98 255 * y / (chroma_height - 1);
99 }
100 }
101 return buffer;
102 }
103
104 // The offsets and sizes describe the rectangle extracted from the
105 // original (gradient) frame, in relative coordinates where the
106 // original frame correspond to the unit square, 0.0 <= x, y < 1.0.
107 template <class T>
CheckCrop(const T & frame,double offset_x,double offset_y,double rel_width,double rel_height)108 void CheckCrop(const T& frame,
109 double offset_x,
110 double offset_y,
111 double rel_width,
112 double rel_height) {
113 int width = frame.width();
114 int height = frame.height();
115
116 SubSampling plane_divider = SubSamplingForType(frame.type());
117
118 // Check that pixel values in the corners match the gradient used
119 // for initialization.
120 for (int i = 0; i < 2; i++) {
121 for (int j = 0; j < 2; j++) {
122 // Pixel coordinates of the corner.
123 int x = i * (width - 1);
124 int y = j * (height - 1);
125 // Relative coordinates, range 0.0 - 1.0 correspond to the
126 // size of the uncropped input frame.
127 double orig_x = offset_x + i * rel_width;
128 double orig_y = offset_y + j * rel_height;
129
130 EXPECT_NEAR(frame.DataY()[x + y * frame.StrideY()] / 256.0,
131 (orig_x + orig_y) / 2, 0.02);
132 EXPECT_NEAR(frame.DataU()[x / plane_divider.x +
133 (y / plane_divider.y) * frame.StrideU()] /
134 256.0,
135 orig_x, 0.02);
136 EXPECT_NEAR(frame.DataV()[x / plane_divider.x +
137 (y / plane_divider.y) * frame.StrideV()] /
138 256.0,
139 orig_y, 0.02);
140 }
141 }
142 }
143
144 template <class T>
CheckRotate(int width,int height,webrtc::VideoRotation rotation,const T & rotated)145 void CheckRotate(int width,
146 int height,
147 webrtc::VideoRotation rotation,
148 const T& rotated) {
149 int rotated_width = width;
150 int rotated_height = height;
151
152 if (rotation == kVideoRotation_90 || rotation == kVideoRotation_270) {
153 std::swap(rotated_width, rotated_height);
154 }
155 EXPECT_EQ(rotated_width, rotated.width());
156 EXPECT_EQ(rotated_height, rotated.height());
157
158 // Clock-wise order (with 0,0 at top-left)
159 const struct {
160 int x;
161 int y;
162 } corners[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
163 // Corresponding corner colors of the frame produced by CreateGradient.
164 const struct {
165 int y;
166 int u;
167 int v;
168 } colors[] = {{0, 0, 0}, {127, 255, 0}, {255, 255, 255}, {127, 0, 255}};
169 int corner_offset = static_cast<int>(rotation) / 90;
170
171 SubSampling plane_divider = SubSamplingForType(rotated.type());
172
173 for (int i = 0; i < 4; i++) {
174 int j = (i + corner_offset) % 4;
175 int x = corners[j].x * (rotated_width - 1);
176 int y = corners[j].y * (rotated_height - 1);
177 EXPECT_EQ(colors[i].y, rotated.DataY()[x + y * rotated.StrideY()]);
178 if (rotated.type() == VideoFrameBuffer::Type::kI422 ||
179 rotated.type() == VideoFrameBuffer::Type::kI210) {
180 EXPECT_NEAR(colors[i].u,
181 rotated.DataU()[(x / plane_divider.x) +
182 (y / plane_divider.y) * rotated.StrideU()],
183 1);
184 EXPECT_NEAR(colors[i].v,
185 rotated.DataV()[(x / plane_divider.x) +
186 (y / plane_divider.y) * rotated.StrideV()],
187 1);
188 } else {
189 EXPECT_EQ(colors[i].u,
190 rotated.DataU()[(x / plane_divider.x) +
191 (y / plane_divider.y) * rotated.StrideU()]);
192 EXPECT_EQ(colors[i].v,
193 rotated.DataV()[(x / plane_divider.x) +
194 (y / plane_divider.y) * rotated.StrideV()]);
195 }
196 }
197 }
198
199 } // namespace
200
TEST(TestVideoFrame,WidthHeightValues)201 TEST(TestVideoFrame, WidthHeightValues) {
202 VideoFrame frame =
203 VideoFrame::Builder()
204 .set_video_frame_buffer(I420Buffer::Create(10, 10, 10, 14, 90))
205 .set_rotation(webrtc::kVideoRotation_0)
206 .set_timestamp_ms(789)
207 .build();
208 const int valid_value = 10;
209 EXPECT_EQ(valid_value, frame.width());
210 EXPECT_EQ(valid_value, frame.height());
211 frame.set_timestamp(123u);
212 EXPECT_EQ(123u, frame.timestamp());
213 frame.set_ntp_time_ms(456);
214 EXPECT_EQ(456, frame.ntp_time_ms());
215 EXPECT_EQ(789, frame.render_time_ms());
216 }
217
TEST(TestVideoFrame,ShallowCopy)218 TEST(TestVideoFrame, ShallowCopy) {
219 uint32_t timestamp = 1;
220 int64_t ntp_time_ms = 2;
221 int64_t timestamp_us = 3;
222 int stride_y = 15;
223 int stride_u = 10;
224 int stride_v = 10;
225 int width = 15;
226 int height = 15;
227
228 const int kSizeY = 400;
229 const int kSizeU = 100;
230 const int kSizeV = 100;
231 const VideoRotation kRotation = kVideoRotation_270;
232 uint8_t buffer_y[kSizeY];
233 uint8_t buffer_u[kSizeU];
234 uint8_t buffer_v[kSizeV];
235 memset(buffer_y, 16, kSizeY);
236 memset(buffer_u, 8, kSizeU);
237 memset(buffer_v, 4, kSizeV);
238
239 VideoFrame frame1 = VideoFrame::Builder()
240 .set_video_frame_buffer(I420Buffer::Copy(
241 width, height, buffer_y, stride_y, buffer_u,
242 stride_u, buffer_v, stride_v))
243 .set_rotation(kRotation)
244 .set_timestamp_us(0)
245 .build();
246 frame1.set_timestamp(timestamp);
247 frame1.set_ntp_time_ms(ntp_time_ms);
248 frame1.set_timestamp_us(timestamp_us);
249 VideoFrame frame2(frame1);
250
251 EXPECT_EQ(frame1.video_frame_buffer(), frame2.video_frame_buffer());
252 const webrtc::I420BufferInterface* yuv1 =
253 frame1.video_frame_buffer()->GetI420();
254 const webrtc::I420BufferInterface* yuv2 =
255 frame2.video_frame_buffer()->GetI420();
256 EXPECT_EQ(yuv1->DataY(), yuv2->DataY());
257 EXPECT_EQ(yuv1->DataU(), yuv2->DataU());
258 EXPECT_EQ(yuv1->DataV(), yuv2->DataV());
259
260 EXPECT_EQ(frame2.timestamp(), frame1.timestamp());
261 EXPECT_EQ(frame2.ntp_time_ms(), frame1.ntp_time_ms());
262 EXPECT_EQ(frame2.timestamp_us(), frame1.timestamp_us());
263 EXPECT_EQ(frame2.rotation(), frame1.rotation());
264
265 frame2.set_timestamp(timestamp + 1);
266 frame2.set_ntp_time_ms(ntp_time_ms + 1);
267 frame2.set_timestamp_us(timestamp_us + 1);
268 frame2.set_rotation(kVideoRotation_90);
269
270 EXPECT_NE(frame2.timestamp(), frame1.timestamp());
271 EXPECT_NE(frame2.ntp_time_ms(), frame1.ntp_time_ms());
272 EXPECT_NE(frame2.timestamp_us(), frame1.timestamp_us());
273 EXPECT_NE(frame2.rotation(), frame1.rotation());
274 }
275
TEST(TestVideoFrame,TextureInitialValues)276 TEST(TestVideoFrame, TextureInitialValues) {
277 VideoFrame frame = test::FakeNativeBuffer::CreateFrame(
278 640, 480, 100, 10, webrtc::kVideoRotation_0);
279 EXPECT_EQ(640, frame.width());
280 EXPECT_EQ(480, frame.height());
281 EXPECT_EQ(100u, frame.timestamp());
282 EXPECT_EQ(10, frame.render_time_ms());
283 ASSERT_TRUE(frame.video_frame_buffer() != nullptr);
284 EXPECT_TRUE(frame.video_frame_buffer()->type() ==
285 VideoFrameBuffer::Type::kNative);
286
287 frame.set_timestamp(200);
288 EXPECT_EQ(200u, frame.timestamp());
289 frame.set_timestamp_us(20);
290 EXPECT_EQ(20, frame.timestamp_us());
291 }
292
293 template <typename T>
294 class TestPlanarYuvBuffer : public ::testing::Test {};
295 TYPED_TEST_SUITE_P(TestPlanarYuvBuffer);
296
297 template <class T>
CreateAndFillBuffer()298 rtc::scoped_refptr<T> CreateAndFillBuffer() {
299 auto buf = T::Create(20, 10);
300 memset(buf->MutableDataY(), 1, 200);
301
302 if (buf->type() == VideoFrameBuffer::Type::kI444) {
303 memset(buf->MutableDataU(), 2, 200);
304 memset(buf->MutableDataV(), 3, 200);
305 } else if (buf->type() == VideoFrameBuffer::Type::kI422 ||
306 buf->type() == VideoFrameBuffer::Type::kI210) {
307 memset(buf->MutableDataU(), 2, 100);
308 memset(buf->MutableDataV(), 3, 100);
309 } else {
310 memset(buf->MutableDataU(), 2, 50);
311 memset(buf->MutableDataV(), 3, 50);
312 }
313
314 return buf;
315 }
316
TYPED_TEST_P(TestPlanarYuvBuffer,Copy)317 TYPED_TEST_P(TestPlanarYuvBuffer, Copy) {
318 rtc::scoped_refptr<TypeParam> buf1 = CreateAndFillBuffer<TypeParam>();
319 rtc::scoped_refptr<TypeParam> buf2 = TypeParam::Copy(*buf1);
320 EXPECT_TRUE(test::FrameBufsEqual(buf1, buf2));
321 }
322
TYPED_TEST_P(TestPlanarYuvBuffer,CropXCenter)323 TYPED_TEST_P(TestPlanarYuvBuffer, CropXCenter) {
324 rtc::scoped_refptr<TypeParam> buf = CreateGradient<TypeParam>(200, 100);
325
326 // Pure center cropping, no scaling.
327 rtc::scoped_refptr<TypeParam> scaled_buffer = TypeParam::Create(100, 100);
328 scaled_buffer->CropAndScaleFrom(*buf, 50, 0, 100, 100);
329 CheckCrop<TypeParam>(*scaled_buffer, 0.25, 0.0, 0.5, 1.0);
330 }
331
TYPED_TEST_P(TestPlanarYuvBuffer,CropXNotCenter)332 TYPED_TEST_P(TestPlanarYuvBuffer, CropXNotCenter) {
333 rtc::scoped_refptr<TypeParam> buf = CreateGradient<TypeParam>(200, 100);
334
335 // Non-center cropping, no scaling.
336 rtc::scoped_refptr<TypeParam> scaled_buffer = TypeParam::Create(100, 100);
337 scaled_buffer->CropAndScaleFrom(*buf, 25, 0, 100, 100);
338 CheckCrop<TypeParam>(*scaled_buffer, 0.125, 0.0, 0.5, 1.0);
339 }
340
TYPED_TEST_P(TestPlanarYuvBuffer,CropYCenter)341 TYPED_TEST_P(TestPlanarYuvBuffer, CropYCenter) {
342 rtc::scoped_refptr<TypeParam> buf = CreateGradient<TypeParam>(100, 200);
343
344 // Pure center cropping, no scaling.
345 rtc::scoped_refptr<TypeParam> scaled_buffer = TypeParam::Create(100, 100);
346 scaled_buffer->CropAndScaleFrom(*buf, 0, 50, 100, 100);
347 CheckCrop<TypeParam>(*scaled_buffer, 0.0, 0.25, 1.0, 0.5);
348 }
349
TYPED_TEST_P(TestPlanarYuvBuffer,CropYNotCenter)350 TYPED_TEST_P(TestPlanarYuvBuffer, CropYNotCenter) {
351 rtc::scoped_refptr<TypeParam> buf = CreateGradient<TypeParam>(100, 200);
352
353 // Pure center cropping, no scaling.
354 rtc::scoped_refptr<TypeParam> scaled_buffer = TypeParam::Create(100, 100);
355 scaled_buffer->CropAndScaleFrom(*buf, 0, 25, 100, 100);
356 CheckCrop<TypeParam>(*scaled_buffer, 0.0, 0.125, 1.0, 0.5);
357 }
358
TYPED_TEST_P(TestPlanarYuvBuffer,CropAndScale16x9)359 TYPED_TEST_P(TestPlanarYuvBuffer, CropAndScale16x9) {
360 const int buffer_width = 640;
361 const int buffer_height = 480;
362 const int crop_width = 320;
363 const int crop_height = 180;
364 rtc::scoped_refptr<TypeParam> buf = CreateGradient<TypeParam>(640, 480);
365
366 // Pure center cropping, no scaling.
367 const int out_width =
368 std::min(buffer_width, crop_width * buffer_height / crop_height);
369 const int out_height =
370 std::min(buffer_height, crop_height * buffer_width / crop_width);
371 rtc::scoped_refptr<TypeParam> scaled_buffer =
372 TypeParam::Create(out_width, out_height);
373 scaled_buffer->CropAndScaleFrom(*buf, (buffer_width - out_width) / 2,
374 (buffer_height - out_height) / 2, out_width,
375 out_height);
376 CheckCrop<TypeParam>(*scaled_buffer, 0.0, 0.125, 1.0, 0.75);
377 }
378
379 REGISTER_TYPED_TEST_SUITE_P(TestPlanarYuvBuffer,
380 Copy,
381 CropXCenter,
382 CropXNotCenter,
383 CropYCenter,
384 CropYNotCenter,
385 CropAndScale16x9);
386
387 using TestTypesAll = ::testing::
388 Types<I420Buffer, I010Buffer, I444Buffer, I422Buffer, I210Buffer>;
389 INSTANTIATE_TYPED_TEST_SUITE_P(All, TestPlanarYuvBuffer, TestTypesAll);
390
391 template <class T>
392 class TestPlanarYuvBufferScale : public ::testing::Test {};
393 TYPED_TEST_SUITE_P(TestPlanarYuvBufferScale);
394
TYPED_TEST_P(TestPlanarYuvBufferScale,Scale)395 TYPED_TEST_P(TestPlanarYuvBufferScale, Scale) {
396 rtc::scoped_refptr<TypeParam> buf = CreateGradient<TypeParam>(200, 100);
397
398 // Pure scaling, no cropping.
399 rtc::scoped_refptr<TypeParam> scaled_buffer = TypeParam::Create(150, 75);
400 scaled_buffer->ScaleFrom(*buf);
401 CheckCrop<TypeParam>(*scaled_buffer, 0.0, 0.0, 1.0, 1.0);
402 }
403
404 REGISTER_TYPED_TEST_SUITE_P(TestPlanarYuvBufferScale, Scale);
405
406 using TestTypesScale = ::testing::Types<I420Buffer, I010Buffer, I210Buffer>;
407 INSTANTIATE_TYPED_TEST_SUITE_P(All, TestPlanarYuvBufferScale, TestTypesScale);
408
409 template <class T>
410 class TestPlanarYuvBufferRotate : public ::testing::Test {
411 public:
412 std::vector<webrtc::VideoRotation> RotationParams = {
413 kVideoRotation_0, kVideoRotation_90, kVideoRotation_180,
414 kVideoRotation_270};
415 };
416
417 TYPED_TEST_SUITE_P(TestPlanarYuvBufferRotate);
418
TYPED_TEST_P(TestPlanarYuvBufferRotate,Rotates)419 TYPED_TEST_P(TestPlanarYuvBufferRotate, Rotates) {
420 for (const webrtc::VideoRotation& rotation : this->RotationParams) {
421 rtc::scoped_refptr<TypeParam> buffer = CreateGradient<TypeParam>(640, 480);
422 rtc::scoped_refptr<TypeParam> rotated_buffer =
423 TypeParam::Rotate(*buffer, rotation);
424 CheckRotate(640, 480, rotation, *rotated_buffer);
425 }
426 }
427
428 REGISTER_TYPED_TEST_SUITE_P(TestPlanarYuvBufferRotate, Rotates);
429
430 using TestTypesRotate = ::testing::
431 Types<I420Buffer, I010Buffer, I444Buffer, I422Buffer, I210Buffer>;
432 INSTANTIATE_TYPED_TEST_SUITE_P(Rotate,
433 TestPlanarYuvBufferRotate,
434 TestTypesRotate);
435
TEST(TestNV12Buffer,CropAndScale)436 TEST(TestNV12Buffer, CropAndScale) {
437 const int kSourceWidth = 640;
438 const int kSourceHeight = 480;
439 const int kScaledWidth = 320;
440 const int kScaledHeight = 240;
441 const int kCropLeft = 40;
442 const int kCropTop = 30;
443 const int kCropRight = 0;
444 const int kCropBottom = 30;
445
446 rtc::scoped_refptr<VideoFrameBuffer> buf =
447 CreateNV12Gradient(kSourceWidth, kSourceHeight);
448
449 rtc::scoped_refptr<VideoFrameBuffer> scaled_buffer = buf->CropAndScale(
450 kCropLeft, kCropTop, kSourceWidth - kCropLeft - kCropRight,
451 kSourceHeight - kCropTop - kCropBottom, kScaledWidth, kScaledHeight);
452
453 // Parameters to CheckCrop indicate what part of the source frame is in the
454 // scaled frame.
455 const float kOffsetX = (kCropLeft + 0.0) / kSourceWidth;
456 const float kOffsetY = (kCropTop + 0.0) / kSourceHeight;
457 const float kRelativeWidth =
458 (kSourceWidth - kCropLeft - kCropRight + 0.0) / kSourceWidth;
459 const float kRelativeHeight =
460 (kSourceHeight - kCropTop - kCropBottom + 0.0) / kSourceHeight;
461 CheckCrop(*scaled_buffer->ToI420(), kOffsetX, kOffsetY, kRelativeWidth,
462 kRelativeHeight);
463 }
464
TEST(TestUpdateRect,CanCompare)465 TEST(TestUpdateRect, CanCompare) {
466 VideoFrame::UpdateRect a = {0, 0, 100, 200};
467 VideoFrame::UpdateRect b = {0, 0, 100, 200};
468 VideoFrame::UpdateRect c = {1, 0, 100, 200};
469 VideoFrame::UpdateRect d = {0, 1, 100, 200};
470 EXPECT_TRUE(a == b);
471 EXPECT_FALSE(a == c);
472 EXPECT_FALSE(a == d);
473 }
474
TEST(TestUpdateRect,ComputesIsEmpty)475 TEST(TestUpdateRect, ComputesIsEmpty) {
476 VideoFrame::UpdateRect a = {0, 0, 0, 0};
477 VideoFrame::UpdateRect b = {0, 0, 100, 200};
478 VideoFrame::UpdateRect c = {1, 100, 0, 0};
479 VideoFrame::UpdateRect d = {1, 100, 100, 200};
480 EXPECT_TRUE(a.IsEmpty());
481 EXPECT_FALSE(b.IsEmpty());
482 EXPECT_TRUE(c.IsEmpty());
483 EXPECT_FALSE(d.IsEmpty());
484 }
485
TEST(TestUpdateRectUnion,NonIntersecting)486 TEST(TestUpdateRectUnion, NonIntersecting) {
487 VideoFrame::UpdateRect a = {0, 0, 10, 20};
488 VideoFrame::UpdateRect b = {100, 200, 10, 20};
489 a.Union(b);
490 EXPECT_EQ(a, VideoFrame::UpdateRect({0, 0, 110, 220}));
491 }
492
TEST(TestUpdateRectUnion,Intersecting)493 TEST(TestUpdateRectUnion, Intersecting) {
494 VideoFrame::UpdateRect a = {0, 0, 10, 10};
495 VideoFrame::UpdateRect b = {5, 5, 30, 20};
496 a.Union(b);
497 EXPECT_EQ(a, VideoFrame::UpdateRect({0, 0, 35, 25}));
498 }
499
TEST(TestUpdateRectUnion,OneInsideAnother)500 TEST(TestUpdateRectUnion, OneInsideAnother) {
501 VideoFrame::UpdateRect a = {0, 0, 100, 100};
502 VideoFrame::UpdateRect b = {5, 5, 30, 20};
503 a.Union(b);
504 EXPECT_EQ(a, VideoFrame::UpdateRect({0, 0, 100, 100}));
505 }
506
TEST(TestUpdateRectIntersect,NonIntersecting)507 TEST(TestUpdateRectIntersect, NonIntersecting) {
508 VideoFrame::UpdateRect a = {0, 0, 10, 20};
509 VideoFrame::UpdateRect b = {100, 200, 10, 20};
510 a.Intersect(b);
511 EXPECT_EQ(a, VideoFrame::UpdateRect({0, 0, 0, 0}));
512 }
513
TEST(TestUpdateRectIntersect,Intersecting)514 TEST(TestUpdateRectIntersect, Intersecting) {
515 VideoFrame::UpdateRect a = {0, 0, 10, 10};
516 VideoFrame::UpdateRect b = {5, 5, 30, 20};
517 a.Intersect(b);
518 EXPECT_EQ(a, VideoFrame::UpdateRect({5, 5, 5, 5}));
519 }
520
TEST(TestUpdateRectIntersect,OneInsideAnother)521 TEST(TestUpdateRectIntersect, OneInsideAnother) {
522 VideoFrame::UpdateRect a = {0, 0, 100, 100};
523 VideoFrame::UpdateRect b = {5, 5, 30, 20};
524 a.Intersect(b);
525 EXPECT_EQ(a, VideoFrame::UpdateRect({5, 5, 30, 20}));
526 }
527
TEST(TestUpdateRectScale,NoScale)528 TEST(TestUpdateRectScale, NoScale) {
529 const int width = 640;
530 const int height = 480;
531 VideoFrame::UpdateRect a = {100, 50, 100, 200};
532 VideoFrame::UpdateRect scaled =
533 a.ScaleWithFrame(width, height, 0, 0, width, height, width, height);
534 EXPECT_EQ(scaled, VideoFrame::UpdateRect({100, 50, 100, 200}));
535 }
536
TEST(TestUpdateRectScale,CropOnly)537 TEST(TestUpdateRectScale, CropOnly) {
538 const int width = 640;
539 const int height = 480;
540 VideoFrame::UpdateRect a = {100, 50, 100, 200};
541 VideoFrame::UpdateRect scaled = a.ScaleWithFrame(
542 width, height, 10, 10, width - 20, height - 20, width - 20, height - 20);
543 EXPECT_EQ(scaled, VideoFrame::UpdateRect({90, 40, 100, 200}));
544 }
545
TEST(TestUpdateRectScale,CropOnlyToOddOffset)546 TEST(TestUpdateRectScale, CropOnlyToOddOffset) {
547 const int width = 640;
548 const int height = 480;
549 VideoFrame::UpdateRect a = {100, 50, 100, 200};
550 VideoFrame::UpdateRect scaled = a.ScaleWithFrame(
551 width, height, 5, 5, width - 10, height - 10, width - 10, height - 10);
552 EXPECT_EQ(scaled, VideoFrame::UpdateRect({94, 44, 102, 202}));
553 }
554
TEST(TestUpdateRectScale,ScaleByHalf)555 TEST(TestUpdateRectScale, ScaleByHalf) {
556 const int width = 640;
557 const int height = 480;
558 VideoFrame::UpdateRect a = {100, 60, 100, 200};
559 VideoFrame::UpdateRect scaled = a.ScaleWithFrame(
560 width, height, 0, 0, width, height, width / 2, height / 2);
561 // Scaled by half and +2 pixels in all directions.
562 EXPECT_EQ(scaled, VideoFrame::UpdateRect({48, 28, 54, 104}));
563 }
564
TEST(TestUpdateRectScale,CropToUnchangedRegionBelowUpdateRect)565 TEST(TestUpdateRectScale, CropToUnchangedRegionBelowUpdateRect) {
566 const int width = 640;
567 const int height = 480;
568 VideoFrame::UpdateRect a = {100, 60, 100, 200};
569 VideoFrame::UpdateRect scaled = a.ScaleWithFrame(
570 width, height, (width - 10) / 2, (height - 10) / 2, 10, 10, 10, 10);
571 // Update is out of the cropped frame.
572 EXPECT_EQ(scaled, VideoFrame::UpdateRect({0, 0, 0, 0}));
573 }
574
TEST(TestUpdateRectScale,CropToUnchangedRegionAboveUpdateRect)575 TEST(TestUpdateRectScale, CropToUnchangedRegionAboveUpdateRect) {
576 const int width = 640;
577 const int height = 480;
578 VideoFrame::UpdateRect a = {600, 400, 10, 10};
579 VideoFrame::UpdateRect scaled = a.ScaleWithFrame(
580 width, height, (width - 10) / 2, (height - 10) / 2, 10, 10, 10, 10);
581 // Update is out of the cropped frame.
582 EXPECT_EQ(scaled, VideoFrame::UpdateRect({0, 0, 0, 0}));
583 }
584
TEST(TestUpdateRectScale,CropInsideUpdate)585 TEST(TestUpdateRectScale, CropInsideUpdate) {
586 const int width = 640;
587 const int height = 480;
588 VideoFrame::UpdateRect a = {300, 200, 100, 100};
589 VideoFrame::UpdateRect scaled = a.ScaleWithFrame(
590 width, height, (width - 10) / 2, (height - 10) / 2, 10, 10, 10, 10);
591 // Cropped frame is inside the update rect.
592 EXPECT_EQ(scaled, VideoFrame::UpdateRect({0, 0, 10, 10}));
593 }
594
TEST(TestUpdateRectScale,CropAndScaleByHalf)595 TEST(TestUpdateRectScale, CropAndScaleByHalf) {
596 const int width = 640;
597 const int height = 480;
598 VideoFrame::UpdateRect a = {100, 60, 100, 200};
599 VideoFrame::UpdateRect scaled =
600 a.ScaleWithFrame(width, height, 10, 10, width - 20, height - 20,
601 (width - 20) / 2, (height - 20) / 2);
602 // Scaled by half and +3 pixels in all directions, because of odd offset after
603 // crop and scale.
604 EXPECT_EQ(scaled, VideoFrame::UpdateRect({42, 22, 56, 106}));
605 }
606
607 } // namespace webrtc
608