OLD | NEW |
| (Empty) |
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 "media/base/yuv_convert.h" | |
6 | |
7 #include <stddef.h> | |
8 #include <stdint.h> | |
9 | |
10 #include <memory> | |
11 | |
12 #include "base/base_paths.h" | |
13 #include "base/cpu.h" | |
14 #include "base/files/file_util.h" | |
15 #include "base/logging.h" | |
16 #include "base/path_service.h" | |
17 #include "build/build_config.h" | |
18 #include "media/base/djb2.h" | |
19 #include "media/base/simd/convert_rgb_to_yuv.h" | |
20 #include "media/base/simd/convert_yuv_to_rgb.h" | |
21 #include "media/base/simd/filter_yuv.h" | |
22 #include "testing/gtest/include/gtest/gtest.h" | |
23 #include "ui/gfx/geometry/rect.h" | |
24 | |
25 // Size of raw image. | |
26 static const int kSourceWidth = 640; | |
27 static const int kSourceHeight = 360; | |
28 static const int kSourceYSize = kSourceWidth * kSourceHeight; | |
29 static const int kSourceUOffset = kSourceYSize; | |
30 static const int kSourceVOffset = kSourceYSize * 5 / 4; | |
31 static const int kScaledWidth = 1024; | |
32 static const int kScaledHeight = 768; | |
33 static const int kDownScaledWidth = 512; | |
34 static const int kDownScaledHeight = 320; | |
35 static const int kBpp = 4; | |
36 | |
37 // Surface sizes for various test files. | |
38 static const int kYUV12Size = kSourceYSize * 12 / 8; | |
39 static const int kYUV16Size = kSourceYSize * 16 / 8; | |
40 static const int kRGBSize = kSourceYSize * kBpp; | |
41 static const int kRGBSizeScaled = kScaledWidth * kScaledHeight * kBpp; | |
42 static const int kRGB24Size = kSourceYSize * 3; | |
43 static const int kRGBSizeConverted = kSourceYSize * kBpp; | |
44 | |
45 #if !defined(ARCH_CPU_ARM_FAMILY) && !defined(ARCH_CPU_MIPS_FAMILY) && \ | |
46 !defined(OS_ANDROID) | |
47 static const int kSourceAOffset = kSourceYSize * 12 / 8; | |
48 static const int kYUVA12Size = kSourceYSize * 20 / 8; | |
49 #endif | |
50 | |
51 // Helper for reading test data into a std::unique_ptr<uint8_t[]>. | |
52 static void ReadData(const base::FilePath::CharType* filename, | |
53 int expected_size, | |
54 std::unique_ptr<uint8_t[]>* data) { | |
55 data->reset(new uint8_t[expected_size]); | |
56 | |
57 base::FilePath path; | |
58 CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &path)); | |
59 path = path.Append(FILE_PATH_LITERAL("media")) | |
60 .Append(FILE_PATH_LITERAL("test")) | |
61 .Append(FILE_PATH_LITERAL("data")) | |
62 .Append(filename); | |
63 | |
64 // Verify file size is correct. | |
65 int64_t actual_size = 0; | |
66 base::GetFileSize(path, &actual_size); | |
67 CHECK_EQ(actual_size, expected_size); | |
68 | |
69 // Verify bytes read are correct. | |
70 int bytes_read = base::ReadFile( | |
71 path, reinterpret_cast<char*>(data->get()), expected_size); | |
72 CHECK_EQ(bytes_read, expected_size); | |
73 } | |
74 | |
75 static void ReadYV12Data(std::unique_ptr<uint8_t[]>* data) { | |
76 ReadData(FILE_PATH_LITERAL("bali_640x360_P420.yuv"), kYUV12Size, data); | |
77 } | |
78 | |
79 static void ReadYV16Data(std::unique_ptr<uint8_t[]>* data) { | |
80 ReadData(FILE_PATH_LITERAL("bali_640x360_P422.yuv"), kYUV16Size, data); | |
81 } | |
82 | |
83 #if !defined(ARCH_CPU_ARM_FAMILY) && !defined(ARCH_CPU_MIPS_FAMILY) && \ | |
84 !defined(OS_ANDROID) | |
85 static void ReadYV12AData(std::unique_ptr<uint8_t[]>* data) { | |
86 ReadData(FILE_PATH_LITERAL("bali_640x360_P420_alpha.yuv"), kYUVA12Size, data); | |
87 } | |
88 #endif | |
89 | |
90 static void ReadRGB24Data(std::unique_ptr<uint8_t[]>* data) { | |
91 ReadData(FILE_PATH_LITERAL("bali_640x360_RGB24.rgb"), kRGB24Size, data); | |
92 } | |
93 | |
94 #if defined(OS_ANDROID) | |
95 // Helper for swapping red and blue channels of RGBA or BGRA. | |
96 static void SwapRedAndBlueChannels(unsigned char* pixels, size_t buffer_size) { | |
97 for (size_t i = 0; i < buffer_size; i += 4) { | |
98 std::swap(pixels[i], pixels[i + 2]); | |
99 } | |
100 } | |
101 #endif | |
102 | |
103 namespace media { | |
104 | |
105 TEST(YUVConvertTest, YV12) { | |
106 // Allocate all surfaces. | |
107 std::unique_ptr<uint8_t[]> yuv_bytes; | |
108 std::unique_ptr<uint8_t[]> rgb_bytes(new uint8_t[kRGBSize]); | |
109 std::unique_ptr<uint8_t[]> rgb_converted_bytes( | |
110 new uint8_t[kRGBSizeConverted]); | |
111 | |
112 // Read YUV reference data from file. | |
113 ReadYV12Data(&yuv_bytes); | |
114 | |
115 // Convert a frame of YUV to 32 bit ARGB. | |
116 media::ConvertYUVToRGB32(yuv_bytes.get(), | |
117 yuv_bytes.get() + kSourceUOffset, | |
118 yuv_bytes.get() + kSourceVOffset, | |
119 rgb_converted_bytes.get(), // RGB output | |
120 kSourceWidth, kSourceHeight, // Dimensions | |
121 kSourceWidth, // YStride | |
122 kSourceWidth / 2, // UVStride | |
123 kSourceWidth * kBpp, // RGBStride | |
124 media::YV12); | |
125 | |
126 #if defined(OS_ANDROID) | |
127 SwapRedAndBlueChannels(rgb_converted_bytes.get(), kRGBSizeConverted); | |
128 #endif | |
129 | |
130 uint32_t rgb_hash = | |
131 DJB2Hash(rgb_converted_bytes.get(), kRGBSizeConverted, kDJB2HashSeed); | |
132 EXPECT_EQ(2413171226u, rgb_hash); | |
133 } | |
134 | |
135 TEST(YUVConvertTest, YV16) { | |
136 // Allocate all surfaces. | |
137 std::unique_ptr<uint8_t[]> yuv_bytes; | |
138 std::unique_ptr<uint8_t[]> rgb_bytes(new uint8_t[kRGBSize]); | |
139 std::unique_ptr<uint8_t[]> rgb_converted_bytes( | |
140 new uint8_t[kRGBSizeConverted]); | |
141 | |
142 // Read YUV reference data from file. | |
143 ReadYV16Data(&yuv_bytes); | |
144 | |
145 // Convert a frame of YUV to 32 bit ARGB. | |
146 media::ConvertYUVToRGB32(yuv_bytes.get(), // Y | |
147 yuv_bytes.get() + kSourceUOffset, // U | |
148 yuv_bytes.get() + kSourceYSize * 3 / 2, // V | |
149 rgb_converted_bytes.get(), // RGB output | |
150 kSourceWidth, kSourceHeight, // Dimensions | |
151 kSourceWidth, // YStride | |
152 kSourceWidth / 2, // UVStride | |
153 kSourceWidth * kBpp, // RGBStride | |
154 media::YV16); | |
155 | |
156 #if defined(OS_ANDROID) | |
157 SwapRedAndBlueChannels(rgb_converted_bytes.get(), kRGBSizeConverted); | |
158 #endif | |
159 | |
160 uint32_t rgb_hash = | |
161 DJB2Hash(rgb_converted_bytes.get(), kRGBSizeConverted, kDJB2HashSeed); | |
162 EXPECT_EQ(4222342047u, rgb_hash); | |
163 } | |
164 | |
165 struct YUVScaleTestData { | |
166 YUVScaleTestData(media::YUVType y, media::ScaleFilter s, uint32_t r) | |
167 : yuv_type(y), scale_filter(s), rgb_hash(r) {} | |
168 | |
169 media::YUVType yuv_type; | |
170 media::ScaleFilter scale_filter; | |
171 uint32_t rgb_hash; | |
172 }; | |
173 | |
174 class YUVScaleTest : public ::testing::TestWithParam<YUVScaleTestData> { | |
175 public: | |
176 YUVScaleTest() { | |
177 switch (GetParam().yuv_type) { | |
178 case media::YV12: | |
179 case media::YV12J: | |
180 case media::YV12HD: | |
181 ReadYV12Data(&yuv_bytes_); | |
182 break; | |
183 case media::YV16: | |
184 ReadYV16Data(&yuv_bytes_); | |
185 break; | |
186 } | |
187 | |
188 rgb_bytes_.reset(new uint8_t[kRGBSizeScaled]); | |
189 } | |
190 | |
191 // Helpers for getting the proper Y, U and V plane offsets. | |
192 uint8_t* y_plane() { return yuv_bytes_.get(); } | |
193 uint8_t* u_plane() { return yuv_bytes_.get() + kSourceYSize; } | |
194 uint8_t* v_plane() { | |
195 switch (GetParam().yuv_type) { | |
196 case media::YV12: | |
197 case media::YV12J: | |
198 case media::YV12HD: | |
199 return yuv_bytes_.get() + kSourceVOffset; | |
200 case media::YV16: | |
201 return yuv_bytes_.get() + kSourceYSize * 3 / 2; | |
202 } | |
203 return NULL; | |
204 } | |
205 | |
206 std::unique_ptr<uint8_t[]> yuv_bytes_; | |
207 std::unique_ptr<uint8_t[]> rgb_bytes_; | |
208 }; | |
209 | |
210 TEST_P(YUVScaleTest, NoScale) { | |
211 media::ScaleYUVToRGB32(y_plane(), // Y | |
212 u_plane(), // U | |
213 v_plane(), // V | |
214 rgb_bytes_.get(), // RGB output | |
215 kSourceWidth, kSourceHeight, // Dimensions | |
216 kSourceWidth, kSourceHeight, // Dimensions | |
217 kSourceWidth, // YStride | |
218 kSourceWidth / 2, // UvStride | |
219 kSourceWidth * kBpp, // RgbStride | |
220 GetParam().yuv_type, | |
221 media::ROTATE_0, | |
222 GetParam().scale_filter); | |
223 | |
224 uint32_t yuv_hash = DJB2Hash(rgb_bytes_.get(), kRGBSize, kDJB2HashSeed); | |
225 | |
226 media::ConvertYUVToRGB32(y_plane(), // Y | |
227 u_plane(), // U | |
228 v_plane(), // V | |
229 rgb_bytes_.get(), // RGB output | |
230 kSourceWidth, kSourceHeight, // Dimensions | |
231 kSourceWidth, // YStride | |
232 kSourceWidth / 2, // UVStride | |
233 kSourceWidth * kBpp, // RGBStride | |
234 GetParam().yuv_type); | |
235 | |
236 uint32_t rgb_hash = DJB2Hash(rgb_bytes_.get(), kRGBSize, kDJB2HashSeed); | |
237 | |
238 EXPECT_EQ(yuv_hash, rgb_hash); | |
239 } | |
240 | |
241 TEST_P(YUVScaleTest, Normal) { | |
242 media::ScaleYUVToRGB32(y_plane(), // Y | |
243 u_plane(), // U | |
244 v_plane(), // V | |
245 rgb_bytes_.get(), // RGB output | |
246 kSourceWidth, kSourceHeight, // Dimensions | |
247 kScaledWidth, kScaledHeight, // Dimensions | |
248 kSourceWidth, // YStride | |
249 kSourceWidth / 2, // UvStride | |
250 kScaledWidth * kBpp, // RgbStride | |
251 GetParam().yuv_type, | |
252 media::ROTATE_0, | |
253 GetParam().scale_filter); | |
254 | |
255 #if defined(OS_ANDROID) | |
256 SwapRedAndBlueChannels(rgb_bytes_.get(), kRGBSizeScaled); | |
257 #endif | |
258 | |
259 uint32_t rgb_hash = DJB2Hash(rgb_bytes_.get(), kRGBSizeScaled, kDJB2HashSeed); | |
260 EXPECT_EQ(GetParam().rgb_hash, rgb_hash); | |
261 } | |
262 | |
263 TEST_P(YUVScaleTest, ZeroSourceSize) { | |
264 media::ScaleYUVToRGB32(y_plane(), // Y | |
265 u_plane(), // U | |
266 v_plane(), // V | |
267 rgb_bytes_.get(), // RGB output | |
268 0, 0, // Dimensions | |
269 kScaledWidth, kScaledHeight, // Dimensions | |
270 kSourceWidth, // YStride | |
271 kSourceWidth / 2, // UvStride | |
272 kScaledWidth * kBpp, // RgbStride | |
273 GetParam().yuv_type, | |
274 media::ROTATE_0, | |
275 GetParam().scale_filter); | |
276 | |
277 // Testing for out-of-bound read/writes with AddressSanitizer. | |
278 } | |
279 | |
280 TEST_P(YUVScaleTest, ZeroDestinationSize) { | |
281 media::ScaleYUVToRGB32(y_plane(), // Y | |
282 u_plane(), // U | |
283 v_plane(), // V | |
284 rgb_bytes_.get(), // RGB output | |
285 kSourceWidth, kSourceHeight, // Dimensions | |
286 0, 0, // Dimensions | |
287 kSourceWidth, // YStride | |
288 kSourceWidth / 2, // UvStride | |
289 kScaledWidth * kBpp, // RgbStride | |
290 GetParam().yuv_type, | |
291 media::ROTATE_0, | |
292 GetParam().scale_filter); | |
293 | |
294 // Testing for out-of-bound read/writes with AddressSanitizer. | |
295 } | |
296 | |
297 TEST_P(YUVScaleTest, OddWidthAndHeightNotCrash) { | |
298 media::ScaleYUVToRGB32(y_plane(), // Y | |
299 u_plane(), // U | |
300 v_plane(), // V | |
301 rgb_bytes_.get(), // RGB output | |
302 kSourceWidth, kSourceHeight, // Dimensions | |
303 3, 3, // Dimensions | |
304 kSourceWidth, // YStride | |
305 kSourceWidth / 2, // UvStride | |
306 kScaledWidth * kBpp, // RgbStride | |
307 GetParam().yuv_type, | |
308 media::ROTATE_0, | |
309 GetParam().scale_filter); | |
310 } | |
311 | |
312 INSTANTIATE_TEST_CASE_P( | |
313 YUVScaleFormats, YUVScaleTest, | |
314 ::testing::Values( | |
315 YUVScaleTestData(media::YV12, media::FILTER_NONE, 4136904952u), | |
316 YUVScaleTestData(media::YV16, media::FILTER_NONE, 1501777547u), | |
317 YUVScaleTestData(media::YV12, media::FILTER_BILINEAR, 3164274689u), | |
318 YUVScaleTestData(media::YV16, media::FILTER_BILINEAR, 3095878046u))); | |
319 | |
320 // This tests a known worst case YUV value, and for overflow. | |
321 TEST(YUVConvertTest, Clamp) { | |
322 // Allocate all surfaces. | |
323 std::unique_ptr<uint8_t[]> yuv_bytes(new uint8_t[1]); | |
324 std::unique_ptr<uint8_t[]> rgb_bytes(new uint8_t[1]); | |
325 std::unique_ptr<uint8_t[]> rgb_converted_bytes(new uint8_t[1]); | |
326 | |
327 // Values that failed previously in bug report. | |
328 unsigned char y = 255u; | |
329 unsigned char u = 255u; | |
330 unsigned char v = 19u; | |
331 | |
332 // Prefill extra large destination buffer to test for overflow. | |
333 unsigned char rgb[8] = { 0, 1, 2, 3, 4, 5, 6, 7 }; | |
334 unsigned char expected[8] = { 255, 255, 104, 255, 4, 5, 6, 7 }; | |
335 // Convert a frame of YUV to 32 bit ARGB. | |
336 media::ConvertYUVToRGB32(&y, // Y | |
337 &u, // U | |
338 &v, // V | |
339 &rgb[0], // RGB output | |
340 1, 1, // Dimensions | |
341 0, // YStride | |
342 0, // UVStride | |
343 0, // RGBStride | |
344 media::YV12); | |
345 | |
346 #if defined(OS_ANDROID) | |
347 SwapRedAndBlueChannels(rgb, kBpp); | |
348 #endif | |
349 | |
350 int expected_test = memcmp(rgb, expected, sizeof(expected)); | |
351 EXPECT_EQ(0, expected_test); | |
352 } | |
353 | |
354 TEST(YUVConvertTest, RGB24ToYUV) { | |
355 // Allocate all surfaces. | |
356 std::unique_ptr<uint8_t[]> rgb_bytes; | |
357 std::unique_ptr<uint8_t[]> yuv_converted_bytes(new uint8_t[kYUV12Size]); | |
358 | |
359 // Read RGB24 reference data from file. | |
360 ReadRGB24Data(&rgb_bytes); | |
361 | |
362 // Convert to I420. | |
363 media::ConvertRGB24ToYUV(rgb_bytes.get(), | |
364 yuv_converted_bytes.get(), | |
365 yuv_converted_bytes.get() + kSourceUOffset, | |
366 yuv_converted_bytes.get() + kSourceVOffset, | |
367 kSourceWidth, kSourceHeight, // Dimensions | |
368 kSourceWidth * 3, // RGBStride | |
369 kSourceWidth, // YStride | |
370 kSourceWidth / 2); // UVStride | |
371 | |
372 uint32_t rgb_hash = | |
373 DJB2Hash(yuv_converted_bytes.get(), kYUV12Size, kDJB2HashSeed); | |
374 EXPECT_EQ(320824432u, rgb_hash); | |
375 } | |
376 | |
377 TEST(YUVConvertTest, RGB32ToYUV) { | |
378 // Allocate all surfaces. | |
379 std::unique_ptr<uint8_t[]> yuv_bytes(new uint8_t[kYUV12Size]); | |
380 std::unique_ptr<uint8_t[]> rgb_bytes(new uint8_t[kRGBSize]); | |
381 std::unique_ptr<uint8_t[]> yuv_converted_bytes(new uint8_t[kYUV12Size]); | |
382 std::unique_ptr<uint8_t[]> rgb_converted_bytes(new uint8_t[kRGBSize]); | |
383 | |
384 // Read YUV reference data from file. | |
385 base::FilePath yuv_url; | |
386 EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &yuv_url)); | |
387 yuv_url = yuv_url.Append(FILE_PATH_LITERAL("media")) | |
388 .Append(FILE_PATH_LITERAL("test")) | |
389 .Append(FILE_PATH_LITERAL("data")) | |
390 .Append(FILE_PATH_LITERAL("bali_640x360_P420.yuv")); | |
391 EXPECT_EQ(static_cast<int>(kYUV12Size), | |
392 base::ReadFile(yuv_url, | |
393 reinterpret_cast<char*>(yuv_bytes.get()), | |
394 static_cast<int>(kYUV12Size))); | |
395 | |
396 // Convert a frame of YUV to 32 bit ARGB. | |
397 media::ConvertYUVToRGB32(yuv_bytes.get(), | |
398 yuv_bytes.get() + kSourceUOffset, | |
399 yuv_bytes.get() + kSourceVOffset, | |
400 rgb_bytes.get(), // RGB output | |
401 kSourceWidth, kSourceHeight, // Dimensions | |
402 kSourceWidth, // YStride | |
403 kSourceWidth / 2, // UVStride | |
404 kSourceWidth * kBpp, // RGBStride | |
405 media::YV12); | |
406 | |
407 // Convert RGB32 to YV12. | |
408 media::ConvertRGB32ToYUV(rgb_bytes.get(), | |
409 yuv_converted_bytes.get(), | |
410 yuv_converted_bytes.get() + kSourceUOffset, | |
411 yuv_converted_bytes.get() + kSourceVOffset, | |
412 kSourceWidth, kSourceHeight, // Dimensions | |
413 kSourceWidth * 4, // RGBStride | |
414 kSourceWidth, // YStride | |
415 kSourceWidth / 2); // UVStride | |
416 | |
417 // Convert YV12 back to RGB32. | |
418 media::ConvertYUVToRGB32(yuv_converted_bytes.get(), | |
419 yuv_converted_bytes.get() + kSourceUOffset, | |
420 yuv_converted_bytes.get() + kSourceVOffset, | |
421 rgb_converted_bytes.get(), // RGB output | |
422 kSourceWidth, kSourceHeight, // Dimensions | |
423 kSourceWidth, // YStride | |
424 kSourceWidth / 2, // UVStride | |
425 kSourceWidth * kBpp, // RGBStride | |
426 media::YV12); | |
427 | |
428 int error = 0; | |
429 for (int i = 0; i < kRGBSize; ++i) { | |
430 int diff = rgb_converted_bytes[i] - rgb_bytes[i]; | |
431 if (diff < 0) | |
432 diff = -diff; | |
433 error += diff; | |
434 } | |
435 | |
436 // Make sure error is within bound. | |
437 DVLOG(1) << "Average error per channel: " << error / kRGBSize; | |
438 EXPECT_GT(5, error / kRGBSize); | |
439 } | |
440 | |
441 TEST(YUVConvertTest, DownScaleYUVToRGB32WithRect) { | |
442 // Read YUV reference data from file. | |
443 base::FilePath yuv_url; | |
444 EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &yuv_url)); | |
445 yuv_url = yuv_url.Append(FILE_PATH_LITERAL("media")) | |
446 .Append(FILE_PATH_LITERAL("test")) | |
447 .Append(FILE_PATH_LITERAL("data")) | |
448 .Append(FILE_PATH_LITERAL("bali_640x360_P420.yuv")); | |
449 const size_t size_of_yuv = kSourceYSize * 12 / 8; // 12 bpp. | |
450 std::unique_ptr<uint8_t[]> yuv_bytes(new uint8_t[size_of_yuv]); | |
451 EXPECT_EQ(static_cast<int>(size_of_yuv), | |
452 base::ReadFile(yuv_url, | |
453 reinterpret_cast<char*>(yuv_bytes.get()), | |
454 static_cast<int>(size_of_yuv))); | |
455 | |
456 // Scale the full frame of YUV to 32 bit ARGB. | |
457 // The API currently only supports down-scaling, so we don't test up-scaling. | |
458 const size_t size_of_rgb_scaled = kDownScaledWidth * kDownScaledHeight * kBpp; | |
459 std::unique_ptr<uint8_t[]> rgb_scaled_bytes(new uint8_t[size_of_rgb_scaled]); | |
460 gfx::Rect sub_rect(0, 0, kDownScaledWidth, kDownScaledHeight); | |
461 | |
462 // We can't compare with the full-frame scaler because it uses slightly | |
463 // different sampling coordinates. | |
464 media::ScaleYUVToRGB32WithRect( | |
465 yuv_bytes.get(), // Y | |
466 yuv_bytes.get() + kSourceUOffset, // U | |
467 yuv_bytes.get() + kSourceVOffset, // V | |
468 rgb_scaled_bytes.get(), // Rgb output | |
469 kSourceWidth, kSourceHeight, // Dimensions | |
470 kDownScaledWidth, kDownScaledHeight, // Dimensions | |
471 sub_rect.x(), sub_rect.y(), // Dest rect | |
472 sub_rect.right(), sub_rect.bottom(), // Dest rect | |
473 kSourceWidth, // YStride | |
474 kSourceWidth / 2, // UvStride | |
475 kDownScaledWidth * kBpp); // RgbStride | |
476 | |
477 uint32_t rgb_hash_full_rect = | |
478 DJB2Hash(rgb_scaled_bytes.get(), size_of_rgb_scaled, kDJB2HashSeed); | |
479 | |
480 // Re-scale sub-rectangles and verify the results are the same. | |
481 int next_sub_rect = 0; | |
482 while (!sub_rect.IsEmpty()) { | |
483 // Scale a partial rectangle. | |
484 media::ScaleYUVToRGB32WithRect( | |
485 yuv_bytes.get(), // Y | |
486 yuv_bytes.get() + kSourceUOffset, // U | |
487 yuv_bytes.get() + kSourceVOffset, // V | |
488 rgb_scaled_bytes.get(), // Rgb output | |
489 kSourceWidth, kSourceHeight, // Dimensions | |
490 kDownScaledWidth, kDownScaledHeight, // Dimensions | |
491 sub_rect.x(), sub_rect.y(), // Dest rect | |
492 sub_rect.right(), sub_rect.bottom(), // Dest rect | |
493 kSourceWidth, // YStride | |
494 kSourceWidth / 2, // UvStride | |
495 kDownScaledWidth * kBpp); // RgbStride | |
496 uint32_t rgb_hash_sub_rect = | |
497 DJB2Hash(rgb_scaled_bytes.get(), size_of_rgb_scaled, kDJB2HashSeed); | |
498 | |
499 EXPECT_EQ(rgb_hash_full_rect, rgb_hash_sub_rect); | |
500 | |
501 // Now pick choose a quarter rect of this sub-rect. | |
502 if (next_sub_rect & 1) | |
503 sub_rect.set_x(sub_rect.x() + sub_rect.width() / 2); | |
504 if (next_sub_rect & 2) | |
505 sub_rect.set_y(sub_rect.y() + sub_rect.height() / 2); | |
506 sub_rect.set_width(sub_rect.width() / 2); | |
507 sub_rect.set_height(sub_rect.height() / 2); | |
508 next_sub_rect++; | |
509 } | |
510 } | |
511 | |
512 #if !defined(ARCH_CPU_ARM_FAMILY) && !defined(ARCH_CPU_MIPS_FAMILY) | |
513 #if !defined(OS_ANDROID) | |
514 TEST(YUVConvertTest, YUVAtoARGB_MMX_MatchReference) { | |
515 // Allocate all surfaces. | |
516 std::unique_ptr<uint8_t[]> yuv_bytes; | |
517 std::unique_ptr<uint8_t[]> rgb_bytes(new uint8_t[kRGBSize]); | |
518 std::unique_ptr<uint8_t[]> rgb_converted_bytes( | |
519 new uint8_t[kRGBSizeConverted]); | |
520 std::unique_ptr<uint8_t[]> rgb_converted_bytes_ref( | |
521 new uint8_t[kRGBSizeConverted]); | |
522 | |
523 // Read YUV reference data from file. | |
524 ReadYV12AData(&yuv_bytes); | |
525 | |
526 // Convert a frame of YUV to 32 bit ARGB using both C and MMX versions. | |
527 media::ConvertYUVAToARGB_C(yuv_bytes.get(), | |
528 yuv_bytes.get() + kSourceUOffset, | |
529 yuv_bytes.get() + kSourceVOffset, | |
530 yuv_bytes.get() + kSourceAOffset, | |
531 rgb_converted_bytes_ref.get(), | |
532 kSourceWidth, | |
533 kSourceHeight, | |
534 kSourceWidth, | |
535 kSourceWidth / 2, | |
536 kSourceWidth, | |
537 kSourceWidth * kBpp, | |
538 media::YV12); | |
539 media::ConvertYUVAToARGB_MMX(yuv_bytes.get(), | |
540 yuv_bytes.get() + kSourceUOffset, | |
541 yuv_bytes.get() + kSourceVOffset, | |
542 yuv_bytes.get() + kSourceAOffset, | |
543 rgb_converted_bytes.get(), | |
544 kSourceWidth, | |
545 kSourceHeight, | |
546 kSourceWidth, | |
547 kSourceWidth / 2, | |
548 kSourceWidth, | |
549 kSourceWidth * kBpp, | |
550 media::YV12); | |
551 | |
552 EXPECT_EQ(0, | |
553 memcmp(rgb_converted_bytes.get(), | |
554 rgb_converted_bytes_ref.get(), | |
555 kRGBSizeConverted)); | |
556 } | |
557 #endif // !defined(OS_ANDROID) | |
558 | |
559 TEST(YUVConvertTest, RGB32ToYUV_SSE2_MatchReference) { | |
560 base::CPU cpu; | |
561 if (!cpu.has_sse2()) { | |
562 LOG(WARNING) << "System doesn't support SSE2, test not executed."; | |
563 return; | |
564 } | |
565 | |
566 // Allocate all surfaces. | |
567 std::unique_ptr<uint8_t[]> yuv_bytes(new uint8_t[kYUV12Size]); | |
568 std::unique_ptr<uint8_t[]> rgb_bytes(new uint8_t[kRGBSize]); | |
569 std::unique_ptr<uint8_t[]> yuv_converted_bytes(new uint8_t[kYUV12Size]); | |
570 std::unique_ptr<uint8_t[]> yuv_reference_bytes(new uint8_t[kYUV12Size]); | |
571 | |
572 ReadYV12Data(&yuv_bytes); | |
573 | |
574 // Convert a frame of YUV to 32 bit ARGB. | |
575 media::ConvertYUVToRGB32( | |
576 yuv_bytes.get(), | |
577 yuv_bytes.get() + kSourceUOffset, | |
578 yuv_bytes.get() + kSourceVOffset, | |
579 rgb_bytes.get(), // RGB output | |
580 kSourceWidth, kSourceHeight, // Dimensions | |
581 kSourceWidth, // YStride | |
582 kSourceWidth / 2, // UVStride | |
583 kSourceWidth * kBpp, // RGBStride | |
584 media::YV12); | |
585 | |
586 // Convert RGB32 to YV12 with SSE2 version. | |
587 media::ConvertRGB32ToYUV_SSE2( | |
588 rgb_bytes.get(), | |
589 yuv_converted_bytes.get(), | |
590 yuv_converted_bytes.get() + kSourceUOffset, | |
591 yuv_converted_bytes.get() + kSourceVOffset, | |
592 kSourceWidth, kSourceHeight, // Dimensions | |
593 kSourceWidth * 4, // RGBStride | |
594 kSourceWidth, // YStride | |
595 kSourceWidth / 2); // UVStride | |
596 | |
597 // Convert RGB32 to YV12 with reference version. | |
598 media::ConvertRGB32ToYUV_SSE2_Reference( | |
599 rgb_bytes.get(), | |
600 yuv_reference_bytes.get(), | |
601 yuv_reference_bytes.get() + kSourceUOffset, | |
602 yuv_reference_bytes.get() + kSourceVOffset, | |
603 kSourceWidth, kSourceHeight, // Dimensions | |
604 kSourceWidth * 4, // RGBStride | |
605 kSourceWidth, // YStride | |
606 kSourceWidth / 2); // UVStride | |
607 | |
608 // Now convert a odd width and height, this overrides part of the buffer | |
609 // generated above but that is fine because the point of this test is to | |
610 // match the result with the reference code. | |
611 | |
612 // Convert RGB32 to YV12 with SSE2 version. | |
613 media::ConvertRGB32ToYUV_SSE2( | |
614 rgb_bytes.get(), | |
615 yuv_converted_bytes.get(), | |
616 yuv_converted_bytes.get() + kSourceUOffset, | |
617 yuv_converted_bytes.get() + kSourceVOffset, | |
618 7, 7, // Dimensions | |
619 kSourceWidth * 4, // RGBStride | |
620 kSourceWidth, // YStride | |
621 kSourceWidth / 2); // UVStride | |
622 | |
623 // Convert RGB32 to YV12 with reference version. | |
624 media::ConvertRGB32ToYUV_SSE2_Reference( | |
625 rgb_bytes.get(), | |
626 yuv_reference_bytes.get(), | |
627 yuv_reference_bytes.get() + kSourceUOffset, | |
628 yuv_reference_bytes.get() + kSourceVOffset, | |
629 7, 7, // Dimensions | |
630 kSourceWidth * 4, // RGBStride | |
631 kSourceWidth, // YStride | |
632 kSourceWidth / 2); // UVStride | |
633 | |
634 int error = 0; | |
635 for (int i = 0; i < kYUV12Size; ++i) { | |
636 int diff = yuv_reference_bytes[i] - yuv_converted_bytes[i]; | |
637 if (diff < 0) | |
638 diff = -diff; | |
639 error += diff; | |
640 } | |
641 | |
642 // Make sure there's no difference from the reference. | |
643 EXPECT_EQ(0, error); | |
644 } | |
645 | |
646 TEST(YUVConvertTest, ConvertYUVToRGB32Row_SSE) { | |
647 base::CPU cpu; | |
648 if (!cpu.has_sse()) { | |
649 LOG(WARNING) << "System not supported. Test skipped."; | |
650 return; | |
651 } | |
652 | |
653 std::unique_ptr<uint8_t[]> yuv_bytes(new uint8_t[kYUV12Size]); | |
654 std::unique_ptr<uint8_t[]> rgb_bytes_reference(new uint8_t[kRGBSize]); | |
655 std::unique_ptr<uint8_t[]> rgb_bytes_converted(new uint8_t[kRGBSize]); | |
656 ReadYV12Data(&yuv_bytes); | |
657 | |
658 const int kWidth = 167; | |
659 ConvertYUVToRGB32Row_C(yuv_bytes.get(), | |
660 yuv_bytes.get() + kSourceUOffset, | |
661 yuv_bytes.get() + kSourceVOffset, | |
662 rgb_bytes_reference.get(), | |
663 kWidth, | |
664 GetLookupTable(YV12)); | |
665 ConvertYUVToRGB32Row_SSE(yuv_bytes.get(), | |
666 yuv_bytes.get() + kSourceUOffset, | |
667 yuv_bytes.get() + kSourceVOffset, | |
668 rgb_bytes_converted.get(), | |
669 kWidth, | |
670 GetLookupTable(YV12)); | |
671 media::EmptyRegisterState(); | |
672 EXPECT_EQ(0, memcmp(rgb_bytes_reference.get(), | |
673 rgb_bytes_converted.get(), | |
674 kWidth * kBpp)); | |
675 } | |
676 | |
677 // 64-bit release + component builds on Windows are too smart and optimizes | |
678 // away the function being tested. | |
679 #if defined(OS_WIN) && (defined(ARCH_CPU_X86) || !defined(COMPONENT_BUILD)) | |
680 TEST(YUVConvertTest, ScaleYUVToRGB32Row_SSE) { | |
681 base::CPU cpu; | |
682 if (!cpu.has_sse()) { | |
683 LOG(WARNING) << "System not supported. Test skipped."; | |
684 return; | |
685 } | |
686 | |
687 std::unique_ptr<uint8_t[]> yuv_bytes(new uint8_t[kYUV12Size]); | |
688 std::unique_ptr<uint8_t[]> rgb_bytes_reference(new uint8_t[kRGBSize]); | |
689 std::unique_ptr<uint8_t[]> rgb_bytes_converted(new uint8_t[kRGBSize]); | |
690 ReadYV12Data(&yuv_bytes); | |
691 | |
692 const int kWidth = 167; | |
693 const int kSourceDx = 80000; // This value means a scale down. | |
694 ScaleYUVToRGB32Row_C(yuv_bytes.get(), | |
695 yuv_bytes.get() + kSourceUOffset, | |
696 yuv_bytes.get() + kSourceVOffset, | |
697 rgb_bytes_reference.get(), | |
698 kWidth, | |
699 kSourceDx, | |
700 GetLookupTable(YV12)); | |
701 ScaleYUVToRGB32Row_SSE(yuv_bytes.get(), | |
702 yuv_bytes.get() + kSourceUOffset, | |
703 yuv_bytes.get() + kSourceVOffset, | |
704 rgb_bytes_converted.get(), | |
705 kWidth, | |
706 kSourceDx, | |
707 GetLookupTable(YV12)); | |
708 media::EmptyRegisterState(); | |
709 EXPECT_EQ(0, memcmp(rgb_bytes_reference.get(), | |
710 rgb_bytes_converted.get(), | |
711 kWidth * kBpp)); | |
712 } | |
713 | |
714 TEST(YUVConvertTest, LinearScaleYUVToRGB32Row_SSE) { | |
715 base::CPU cpu; | |
716 if (!cpu.has_sse()) { | |
717 LOG(WARNING) << "System not supported. Test skipped."; | |
718 return; | |
719 } | |
720 | |
721 std::unique_ptr<uint8_t[]> yuv_bytes(new uint8_t[kYUV12Size]); | |
722 std::unique_ptr<uint8_t[]> rgb_bytes_reference(new uint8_t[kRGBSize]); | |
723 std::unique_ptr<uint8_t[]> rgb_bytes_converted(new uint8_t[kRGBSize]); | |
724 ReadYV12Data(&yuv_bytes); | |
725 | |
726 const int kWidth = 167; | |
727 const int kSourceDx = 80000; // This value means a scale down. | |
728 LinearScaleYUVToRGB32Row_C(yuv_bytes.get(), | |
729 yuv_bytes.get() + kSourceUOffset, | |
730 yuv_bytes.get() + kSourceVOffset, | |
731 rgb_bytes_reference.get(), | |
732 kWidth, | |
733 kSourceDx, | |
734 GetLookupTable(YV12)); | |
735 LinearScaleYUVToRGB32Row_SSE(yuv_bytes.get(), | |
736 yuv_bytes.get() + kSourceUOffset, | |
737 yuv_bytes.get() + kSourceVOffset, | |
738 rgb_bytes_converted.get(), | |
739 kWidth, | |
740 kSourceDx, | |
741 GetLookupTable(YV12)); | |
742 media::EmptyRegisterState(); | |
743 EXPECT_EQ(0, memcmp(rgb_bytes_reference.get(), | |
744 rgb_bytes_converted.get(), | |
745 kWidth * kBpp)); | |
746 } | |
747 #endif // defined(OS_WIN) && (ARCH_CPU_X86 || COMPONENT_BUILD) | |
748 | |
749 TEST(YUVConvertTest, FilterYUVRows_C_OutOfBounds) { | |
750 std::unique_ptr<uint8_t[]> src(new uint8_t[16]); | |
751 std::unique_ptr<uint8_t[]> dst(new uint8_t[16]); | |
752 | |
753 memset(src.get(), 0xff, 16); | |
754 memset(dst.get(), 0, 16); | |
755 | |
756 media::FilterYUVRows_C(dst.get(), src.get(), src.get(), 1, 255); | |
757 | |
758 EXPECT_EQ(255u, dst[0]); | |
759 for (int i = 1; i < 16; ++i) { | |
760 EXPECT_EQ(0u, dst[i]) << " not equal at " << i; | |
761 } | |
762 } | |
763 | |
764 TEST(YUVConvertTest, FilterYUVRows_SSE2_OutOfBounds) { | |
765 base::CPU cpu; | |
766 if (!cpu.has_sse2()) { | |
767 LOG(WARNING) << "System not supported. Test skipped."; | |
768 return; | |
769 } | |
770 | |
771 std::unique_ptr<uint8_t[]> src(new uint8_t[16]); | |
772 std::unique_ptr<uint8_t[]> dst(new uint8_t[16]); | |
773 | |
774 memset(src.get(), 0xff, 16); | |
775 memset(dst.get(), 0, 16); | |
776 | |
777 media::FilterYUVRows_SSE2(dst.get(), src.get(), src.get(), 1, 255); | |
778 | |
779 EXPECT_EQ(255u, dst[0]); | |
780 for (int i = 1; i < 16; ++i) { | |
781 EXPECT_EQ(0u, dst[i]); | |
782 } | |
783 } | |
784 | |
785 TEST(YUVConvertTest, FilterYUVRows_SSE2_UnalignedDestination) { | |
786 base::CPU cpu; | |
787 if (!cpu.has_sse2()) { | |
788 LOG(WARNING) << "System not supported. Test skipped."; | |
789 return; | |
790 } | |
791 | |
792 const int kSize = 64; | |
793 std::unique_ptr<uint8_t[]> src(new uint8_t[kSize]); | |
794 std::unique_ptr<uint8_t[]> dst_sample(new uint8_t[kSize]); | |
795 std::unique_ptr<uint8_t[]> dst(new uint8_t[kSize]); | |
796 | |
797 memset(dst_sample.get(), 0, kSize); | |
798 memset(dst.get(), 0, kSize); | |
799 for (int i = 0; i < kSize; ++i) | |
800 src[i] = 100 + i; | |
801 | |
802 media::FilterYUVRows_C(dst_sample.get(), | |
803 src.get(), src.get(), 37, 128); | |
804 | |
805 // Generate an unaligned output address. | |
806 uint8_t* dst_ptr = reinterpret_cast<uint8_t*>( | |
807 (reinterpret_cast<uintptr_t>(dst.get() + 16) & ~15) + 1); | |
808 media::FilterYUVRows_SSE2(dst_ptr, src.get(), src.get(), 37, 128); | |
809 media::EmptyRegisterState(); | |
810 | |
811 EXPECT_EQ(0, memcmp(dst_sample.get(), dst_ptr, 37)); | |
812 } | |
813 | |
814 #if defined(ARCH_CPU_X86_64) | |
815 | |
816 TEST(YUVConvertTest, ScaleYUVToRGB32Row_SSE2_X64) { | |
817 std::unique_ptr<uint8_t[]> yuv_bytes(new uint8_t[kYUV12Size]); | |
818 std::unique_ptr<uint8_t[]> rgb_bytes_reference(new uint8_t[kRGBSize]); | |
819 std::unique_ptr<uint8_t[]> rgb_bytes_converted(new uint8_t[kRGBSize]); | |
820 ReadYV12Data(&yuv_bytes); | |
821 | |
822 const int kWidth = 167; | |
823 const int kSourceDx = 80000; // This value means a scale down. | |
824 ScaleYUVToRGB32Row_C(yuv_bytes.get(), | |
825 yuv_bytes.get() + kSourceUOffset, | |
826 yuv_bytes.get() + kSourceVOffset, | |
827 rgb_bytes_reference.get(), | |
828 kWidth, | |
829 kSourceDx, | |
830 GetLookupTable(YV12)); | |
831 ScaleYUVToRGB32Row_SSE2_X64(yuv_bytes.get(), | |
832 yuv_bytes.get() + kSourceUOffset, | |
833 yuv_bytes.get() + kSourceVOffset, | |
834 rgb_bytes_converted.get(), | |
835 kWidth, | |
836 kSourceDx, | |
837 GetLookupTable(YV12)); | |
838 media::EmptyRegisterState(); | |
839 EXPECT_EQ(0, memcmp(rgb_bytes_reference.get(), | |
840 rgb_bytes_converted.get(), | |
841 kWidth * kBpp)); | |
842 } | |
843 | |
844 TEST(YUVConvertTest, LinearScaleYUVToRGB32Row_MMX_X64) { | |
845 std::unique_ptr<uint8_t[]> yuv_bytes(new uint8_t[kYUV12Size]); | |
846 std::unique_ptr<uint8_t[]> rgb_bytes_reference(new uint8_t[kRGBSize]); | |
847 std::unique_ptr<uint8_t[]> rgb_bytes_converted(new uint8_t[kRGBSize]); | |
848 ReadYV12Data(&yuv_bytes); | |
849 | |
850 const int kWidth = 167; | |
851 const int kSourceDx = 80000; // This value means a scale down. | |
852 LinearScaleYUVToRGB32Row_C(yuv_bytes.get(), | |
853 yuv_bytes.get() + kSourceUOffset, | |
854 yuv_bytes.get() + kSourceVOffset, | |
855 rgb_bytes_reference.get(), | |
856 kWidth, | |
857 kSourceDx, | |
858 GetLookupTable(YV12)); | |
859 LinearScaleYUVToRGB32Row_MMX_X64(yuv_bytes.get(), | |
860 yuv_bytes.get() + kSourceUOffset, | |
861 yuv_bytes.get() + kSourceVOffset, | |
862 rgb_bytes_converted.get(), | |
863 kWidth, | |
864 kSourceDx, | |
865 GetLookupTable(YV12)); | |
866 media::EmptyRegisterState(); | |
867 EXPECT_EQ(0, memcmp(rgb_bytes_reference.get(), | |
868 rgb_bytes_converted.get(), | |
869 kWidth * kBpp)); | |
870 } | |
871 | |
872 #endif // defined(ARCH_CPU_X86_64) | |
873 | |
874 #endif // defined(ARCH_CPU_X86_FAMILY) | |
875 | |
876 } // namespace media | |
OLD | NEW |