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