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