1/*
2 * libjingle
3 * Copyright 2014 Google Inc.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 *  1. Redistributions of source code must retain the above copyright notice,
9 *     this list of conditions and the following disclaimer.
10 *  2. Redistributions in binary form must reproduce the above copyright notice,
11 *     this list of conditions and the following disclaimer in the documentation
12 *     and/or other materials provided with the distribution.
13 *  3. The name of the author may not be used to endorse or promote products
14 *     derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include <string>
29
30#include "libyuv/convert.h"
31#include "libyuv/convert_from.h"
32#include "libyuv/convert_from_argb.h"
33#include "libyuv/mjpeg_decoder.h"
34#include "libyuv/planar_functions.h"
35#include "talk/media/base/testutils.h"
36#include "talk/media/base/videocommon.h"
37#include "webrtc/base/flags.h"
38#include "webrtc/base/gunit.h"
39#include "webrtc/base/scoped_ptr.h"
40
41// Undefine macros for the windows build.
42#undef max
43#undef min
44
45using cricket::DumpPlanarYuvTestImage;
46
47DEFINE_bool(planarfunctions_dump, false,
48    "whether to write out scaled images for inspection");
49DEFINE_int(planarfunctions_repeat, 1,
50    "how many times to perform each scaling operation (for perf testing)");
51
52namespace cricket {
53
54// Number of testing colors in each color channel.
55static const int kTestingColorChannelResolution = 6;
56
57// The total number of testing colors
58// kTestingColorNum = kTestingColorChannelResolution^3;
59static const int kTestingColorNum = kTestingColorChannelResolution *
60    kTestingColorChannelResolution * kTestingColorChannelResolution;
61
62static const int kWidth = 1280;
63static const int kHeight = 720;
64static const int kAlignment = 16;
65
66class PlanarFunctionsTest : public testing::TestWithParam<int> {
67 protected:
68  PlanarFunctionsTest() : dump_(false), repeat_(1) {
69    InitializeColorBand();
70  }
71
72  virtual void SetUp() {
73    dump_ = FLAG_planarfunctions_dump;
74    repeat_ = FLAG_planarfunctions_repeat;
75  }
76
77  // Initialize the color band for testing.
78  void InitializeColorBand() {
79    testing_color_y_.reset(new uint8_t[kTestingColorNum]);
80    testing_color_u_.reset(new uint8_t[kTestingColorNum]);
81    testing_color_v_.reset(new uint8_t[kTestingColorNum]);
82    testing_color_r_.reset(new uint8_t[kTestingColorNum]);
83    testing_color_g_.reset(new uint8_t[kTestingColorNum]);
84    testing_color_b_.reset(new uint8_t[kTestingColorNum]);
85    int color_counter = 0;
86    for (int i = 0; i < kTestingColorChannelResolution; ++i) {
87      uint8_t color_r =
88          static_cast<uint8_t>(i * 255 / (kTestingColorChannelResolution - 1));
89      for (int j = 0; j < kTestingColorChannelResolution; ++j) {
90        uint8_t color_g = static_cast<uint8_t>(
91            j * 255 / (kTestingColorChannelResolution - 1));
92        for (int k = 0; k < kTestingColorChannelResolution; ++k) {
93          uint8_t color_b = static_cast<uint8_t>(
94              k * 255 / (kTestingColorChannelResolution - 1));
95          testing_color_r_[color_counter] = color_r;
96          testing_color_g_[color_counter] = color_g;
97          testing_color_b_[color_counter] = color_b;
98           // Converting the testing RGB colors to YUV colors.
99          ConvertRgbPixel(color_r, color_g, color_b,
100                          &(testing_color_y_[color_counter]),
101                          &(testing_color_u_[color_counter]),
102                          &(testing_color_v_[color_counter]));
103          ++color_counter;
104        }
105      }
106    }
107  }
108  // Simple and slow RGB->YUV conversion. From NTSC standard, c/o Wikipedia.
109  // (from lmivideoframe_unittest.cc)
110  void ConvertRgbPixel(uint8_t r,
111                       uint8_t g,
112                       uint8_t b,
113                       uint8_t* y,
114                       uint8_t* u,
115                       uint8_t* v) {
116    *y = ClampUint8(.257 * r + .504 * g + .098 * b + 16);
117    *u = ClampUint8(-.148 * r - .291 * g + .439 * b + 128);
118    *v = ClampUint8(.439 * r - .368 * g - .071 * b + 128);
119  }
120
121  uint8_t ClampUint8(double value) {
122    value = std::max(0., std::min(255., value));
123    uint8_t uint8_value = static_cast<uint8_t>(value);
124    return uint8_value;
125  }
126
127  // Generate a Red-Green-Blue inter-weaving chessboard-like
128  // YUV testing image (I420/I422/I444).
129  // The pattern looks like c0 c1 c2 c3 ...
130  //                        c1 c2 c3 c4 ...
131  //                        c2 c3 c4 c5 ...
132  //                        ...............
133  // The size of each chrome block is (block_size) x (block_size).
134  uint8_t* CreateFakeYuvTestingImage(int height,
135                                     int width,
136                                     int block_size,
137                                     libyuv::JpegSubsamplingType subsample_type,
138                                     uint8_t*& y_pointer,
139                                     uint8_t*& u_pointer,
140                                     uint8_t*& v_pointer) {
141    if (height <= 0 || width <= 0 || block_size <= 0) { return NULL; }
142    int y_size = height * width;
143    int u_size, v_size;
144    int vertical_sample_ratio = 1, horizontal_sample_ratio = 1;
145    switch (subsample_type) {
146      case libyuv::kJpegYuv420:
147        u_size = ((height + 1) >> 1) * ((width + 1) >> 1);
148        v_size = u_size;
149        vertical_sample_ratio = 2, horizontal_sample_ratio = 2;
150        break;
151      case libyuv::kJpegYuv422:
152        u_size = height * ((width + 1) >> 1);
153        v_size = u_size;
154        vertical_sample_ratio = 1, horizontal_sample_ratio = 2;
155        break;
156      case libyuv::kJpegYuv444:
157        v_size = u_size = y_size;
158        vertical_sample_ratio = 1, horizontal_sample_ratio = 1;
159        break;
160      case libyuv::kJpegUnknown:
161      default:
162        return NULL;
163        break;
164    }
165    uint8_t* image_pointer = new uint8_t[y_size + u_size + v_size + kAlignment];
166    y_pointer = ALIGNP(image_pointer, kAlignment);
167    u_pointer = ALIGNP(&image_pointer[y_size], kAlignment);
168    v_pointer = ALIGNP(&image_pointer[y_size + u_size], kAlignment);
169    uint8_t* current_y_pointer = y_pointer;
170    uint8_t* current_u_pointer = u_pointer;
171    uint8_t* current_v_pointer = v_pointer;
172    for (int j = 0; j < height; ++j) {
173      for (int i = 0; i < width; ++i) {
174        int color = ((i / block_size) + (j / block_size)) % kTestingColorNum;
175        *(current_y_pointer++) = testing_color_y_[color];
176        if (i % horizontal_sample_ratio == 0 &&
177            j % vertical_sample_ratio == 0) {
178          *(current_u_pointer++) = testing_color_u_[color];
179          *(current_v_pointer++) = testing_color_v_[color];
180        }
181      }
182    }
183    return image_pointer;
184  }
185
186  // Generate a Red-Green-Blue inter-weaving chessboard-like
187  // YUY2/UYVY testing image.
188  // The pattern looks like c0 c1 c2 c3 ...
189  //                        c1 c2 c3 c4 ...
190  //                        c2 c3 c4 c5 ...
191  //                        ...............
192  // The size of each chrome block is (block_size) x (block_size).
193  uint8_t* CreateFakeInterleaveYuvTestingImage(int height,
194                                               int width,
195                                               int block_size,
196                                               uint8_t*& yuv_pointer,
197                                               FourCC fourcc_type) {
198    if (height <= 0 || width <= 0 || block_size <= 0) { return NULL; }
199    if (fourcc_type != FOURCC_YUY2 && fourcc_type != FOURCC_UYVY) {
200      LOG(LS_ERROR) << "Format " << static_cast<int>(fourcc_type)
201                    << " is not supported.";
202      return NULL;
203    }
204    // Regularize the width of the output to be even.
205    int awidth = (width + 1) & ~1;
206
207    uint8_t* image_pointer = new uint8_t[2 * height * awidth + kAlignment];
208    yuv_pointer = ALIGNP(image_pointer, kAlignment);
209    uint8_t* current_yuv_pointer = yuv_pointer;
210    switch (fourcc_type) {
211      case FOURCC_YUY2: {
212        for (int j = 0; j < height; ++j) {
213          for (int i = 0; i < awidth; i += 2, current_yuv_pointer += 4) {
214            int color1 = ((i / block_size) + (j / block_size)) %
215                kTestingColorNum;
216            int color2 = (((i + 1) / block_size) + (j / block_size)) %
217                kTestingColorNum;
218            current_yuv_pointer[0] = testing_color_y_[color1];
219            if (i < width) {
220              current_yuv_pointer[1] = static_cast<uint8_t>(
221                  (static_cast<uint32_t>(testing_color_u_[color1]) +
222                   static_cast<uint32_t>(testing_color_u_[color2])) /
223                  2);
224              current_yuv_pointer[2] = testing_color_y_[color2];
225              current_yuv_pointer[3] = static_cast<uint8_t>(
226                  (static_cast<uint32_t>(testing_color_v_[color1]) +
227                   static_cast<uint32_t>(testing_color_v_[color2])) /
228                  2);
229            } else {
230              current_yuv_pointer[1] = testing_color_u_[color1];
231              current_yuv_pointer[2] = 0;
232              current_yuv_pointer[3] = testing_color_v_[color1];
233            }
234          }
235        }
236        break;
237      }
238      case FOURCC_UYVY: {
239        for (int j = 0; j < height; ++j) {
240          for (int i = 0; i < awidth; i += 2, current_yuv_pointer += 4) {
241            int color1 = ((i / block_size) + (j / block_size)) %
242                kTestingColorNum;
243            int color2 = (((i + 1) / block_size) + (j / block_size)) %
244                kTestingColorNum;
245            if (i < width) {
246              current_yuv_pointer[0] = static_cast<uint8_t>(
247                  (static_cast<uint32_t>(testing_color_u_[color1]) +
248                   static_cast<uint32_t>(testing_color_u_[color2])) /
249                  2);
250              current_yuv_pointer[1] = testing_color_y_[color1];
251              current_yuv_pointer[2] = static_cast<uint8_t>(
252                  (static_cast<uint32_t>(testing_color_v_[color1]) +
253                   static_cast<uint32_t>(testing_color_v_[color2])) /
254                  2);
255              current_yuv_pointer[3] = testing_color_y_[color2];
256            } else {
257              current_yuv_pointer[0] = testing_color_u_[color1];
258              current_yuv_pointer[1] = testing_color_y_[color1];
259              current_yuv_pointer[2] = testing_color_v_[color1];
260              current_yuv_pointer[3] = 0;
261            }
262          }
263        }
264        break;
265      }
266    }
267    return image_pointer;
268  }
269
270  // Generate a Red-Green-Blue inter-weaving chessboard-like
271  // NV12 testing image.
272  // (Note: No interpolation is used.)
273  // The pattern looks like c0 c1 c2 c3 ...
274  //                        c1 c2 c3 c4 ...
275  //                        c2 c3 c4 c5 ...
276  //                        ...............
277  // The size of each chrome block is (block_size) x (block_size).
278  uint8_t* CreateFakeNV12TestingImage(int height,
279                                      int width,
280                                      int block_size,
281                                      uint8_t*& y_pointer,
282                                      uint8_t*& uv_pointer) {
283    if (height <= 0 || width <= 0 || block_size <= 0) { return NULL; }
284
285    uint8_t* image_pointer =
286        new uint8_t[height * width +
287                    ((height + 1) / 2) * ((width + 1) / 2) * 2 + kAlignment];
288    y_pointer = ALIGNP(image_pointer, kAlignment);
289    uv_pointer = y_pointer + height * width;
290    uint8_t* current_uv_pointer = uv_pointer;
291    uint8_t* current_y_pointer = y_pointer;
292    for (int j = 0; j < height; ++j) {
293      for (int i = 0; i < width; ++i) {
294        int color = ((i / block_size) + (j / block_size)) %
295            kTestingColorNum;
296        *(current_y_pointer++) = testing_color_y_[color];
297      }
298      if (j % 2 == 0) {
299        for (int i = 0; i < width; i += 2, current_uv_pointer += 2) {
300          int color = ((i / block_size) + (j / block_size)) %
301              kTestingColorNum;
302          current_uv_pointer[0] = testing_color_u_[color];
303          current_uv_pointer[1] = testing_color_v_[color];
304        }
305      }
306    }
307    return image_pointer;
308  }
309
310  // Generate a Red-Green-Blue inter-weaving chessboard-like
311  // M420 testing image.
312  // (Note: No interpolation is used.)
313  // The pattern looks like c0 c1 c2 c3 ...
314  //                        c1 c2 c3 c4 ...
315  //                        c2 c3 c4 c5 ...
316  //                        ...............
317  // The size of each chrome block is (block_size) x (block_size).
318  uint8_t* CreateFakeM420TestingImage(int height,
319                                      int width,
320                                      int block_size,
321                                      uint8_t*& m420_pointer) {
322    if (height <= 0 || width <= 0 || block_size <= 0) { return NULL; }
323
324    uint8_t* image_pointer =
325        new uint8_t[height * width +
326                    ((height + 1) / 2) * ((width + 1) / 2) * 2 + kAlignment];
327    m420_pointer = ALIGNP(image_pointer, kAlignment);
328    uint8_t* current_m420_pointer = m420_pointer;
329    for (int j = 0; j < height; ++j) {
330      for (int i = 0; i < width; ++i) {
331        int color = ((i / block_size) + (j / block_size)) %
332            kTestingColorNum;
333        *(current_m420_pointer++) = testing_color_y_[color];
334      }
335      if (j % 2 == 1) {
336        for (int i = 0; i < width; i += 2, current_m420_pointer += 2) {
337          int color = ((i / block_size) + ((j - 1) / block_size)) %
338              kTestingColorNum;
339          current_m420_pointer[0] = testing_color_u_[color];
340          current_m420_pointer[1] = testing_color_v_[color];
341        }
342      }
343    }
344    return image_pointer;
345  }
346
347  // Generate a Red-Green-Blue inter-weaving chessboard-like
348  // ARGB/ABGR/RAW/BG24 testing image.
349  // The pattern looks like c0 c1 c2 c3 ...
350  //                        c1 c2 c3 c4 ...
351  //                        c2 c3 c4 c5 ...
352  //                        ...............
353  // The size of each chrome block is (block_size) x (block_size).
354  uint8_t* CreateFakeArgbTestingImage(int height,
355                                      int width,
356                                      int block_size,
357                                      uint8_t*& argb_pointer,
358                                      FourCC fourcc_type) {
359    if (height <= 0 || width <= 0 || block_size <= 0) { return NULL; }
360    uint8_t* image_pointer = NULL;
361    if (fourcc_type == FOURCC_ABGR || fourcc_type == FOURCC_BGRA ||
362        fourcc_type == FOURCC_ARGB) {
363      image_pointer = new uint8_t[height * width * 4 + kAlignment];
364    } else if (fourcc_type == FOURCC_RAW || fourcc_type == FOURCC_24BG) {
365      image_pointer = new uint8_t[height * width * 3 + kAlignment];
366    } else {
367      LOG(LS_ERROR) << "Format " << static_cast<int>(fourcc_type)
368                    << " is not supported.";
369      return NULL;
370    }
371    argb_pointer = ALIGNP(image_pointer, kAlignment);
372    uint8_t* current_pointer = argb_pointer;
373    switch (fourcc_type) {
374      case FOURCC_ARGB: {
375        for (int j = 0; j < height; ++j) {
376          for (int i = 0; i < width; ++i) {
377            int color = ((i / block_size) + (j / block_size)) %
378                kTestingColorNum;
379            *(current_pointer++) = testing_color_b_[color];
380            *(current_pointer++) = testing_color_g_[color];
381            *(current_pointer++) = testing_color_r_[color];
382            *(current_pointer++) = 255;
383          }
384        }
385        break;
386      }
387      case FOURCC_ABGR: {
388        for (int j = 0; j < height; ++j) {
389          for (int i = 0; i < width; ++i) {
390            int color = ((i / block_size) + (j / block_size)) %
391                kTestingColorNum;
392            *(current_pointer++) = testing_color_r_[color];
393            *(current_pointer++) = testing_color_g_[color];
394            *(current_pointer++) = testing_color_b_[color];
395            *(current_pointer++) = 255;
396          }
397        }
398        break;
399      }
400      case FOURCC_BGRA: {
401        for (int j = 0; j < height; ++j) {
402          for (int i = 0; i < width; ++i) {
403            int color = ((i / block_size) + (j / block_size)) %
404                kTestingColorNum;
405            *(current_pointer++) = 255;
406            *(current_pointer++) = testing_color_r_[color];
407            *(current_pointer++) = testing_color_g_[color];
408            *(current_pointer++) = testing_color_b_[color];
409           }
410        }
411        break;
412      }
413      case FOURCC_24BG: {
414        for (int j = 0; j < height; ++j) {
415          for (int i = 0; i < width; ++i) {
416            int color = ((i / block_size) + (j / block_size)) %
417                kTestingColorNum;
418            *(current_pointer++) = testing_color_b_[color];
419            *(current_pointer++) = testing_color_g_[color];
420            *(current_pointer++) = testing_color_r_[color];
421          }
422        }
423        break;
424      }
425      case FOURCC_RAW: {
426        for (int j = 0; j < height; ++j) {
427          for (int i = 0; i < width; ++i) {
428            int color = ((i / block_size) + (j / block_size)) %
429                kTestingColorNum;
430            *(current_pointer++) = testing_color_r_[color];
431            *(current_pointer++) = testing_color_g_[color];
432            *(current_pointer++) = testing_color_b_[color];
433          }
434        }
435        break;
436      }
437      default: {
438        LOG(LS_ERROR) << "Format " << static_cast<int>(fourcc_type)
439                      << " is not supported.";
440      }
441    }
442    return image_pointer;
443  }
444
445  // Check if two memory chunks are equal.
446  // (tolerate MSE errors within a threshold).
447  static bool IsMemoryEqual(const uint8_t* ibuf,
448                            const uint8_t* obuf,
449                            int osize,
450                            double average_error) {
451    double sse = cricket::ComputeSumSquareError(ibuf, obuf, osize);
452    double error = sse / osize;  // Mean Squared Error.
453    double PSNR = cricket::ComputePSNR(sse, osize);
454    LOG(LS_INFO) << "Image MSE: "  << error << " Image PSNR: " << PSNR
455                 << " First Diff Byte: " << FindDiff(ibuf, obuf, osize);
456    return (error < average_error);
457  }
458
459  // Returns the index of the first differing byte. Easier to debug than memcmp.
460  static int FindDiff(const uint8_t* buf1, const uint8_t* buf2, int len) {
461    int i = 0;
462    while (i < len && buf1[i] == buf2[i]) {
463      i++;
464    }
465    return (i < len) ? i : -1;
466  }
467
468  // Dump the result image (ARGB format).
469  void DumpArgbImage(const uint8_t* obuf, int width, int height) {
470    DumpPlanarArgbTestImage(GetTestName(), obuf, width, height);
471  }
472
473  // Dump the result image (YUV420 format).
474  void DumpYuvImage(const uint8_t* obuf, int width, int height) {
475    DumpPlanarYuvTestImage(GetTestName(), obuf, width, height);
476  }
477
478  std::string GetTestName() {
479    const testing::TestInfo* const test_info =
480        testing::UnitTest::GetInstance()->current_test_info();
481    std::string test_name(test_info->name());
482    return test_name;
483  }
484
485  bool dump_;
486  int repeat_;
487
488  // Y, U, V and R, G, B channels of testing colors.
489  rtc::scoped_ptr<uint8_t[]> testing_color_y_;
490  rtc::scoped_ptr<uint8_t[]> testing_color_u_;
491  rtc::scoped_ptr<uint8_t[]> testing_color_v_;
492  rtc::scoped_ptr<uint8_t[]> testing_color_r_;
493  rtc::scoped_ptr<uint8_t[]> testing_color_g_;
494  rtc::scoped_ptr<uint8_t[]> testing_color_b_;
495};
496
497TEST_F(PlanarFunctionsTest, I420Copy) {
498  uint8_t* y_pointer = nullptr;
499  uint8_t* u_pointer = nullptr;
500  uint8_t* v_pointer = nullptr;
501  int y_pitch = kWidth;
502  int u_pitch = (kWidth + 1) >> 1;
503  int v_pitch = (kWidth + 1) >> 1;
504  int y_size = kHeight * kWidth;
505  int uv_size = ((kHeight + 1) >> 1) * ((kWidth + 1) >> 1);
506  int block_size = 3;
507  // Generate a fake input image.
508  rtc::scoped_ptr<uint8_t[]> yuv_input(CreateFakeYuvTestingImage(
509      kHeight, kWidth, block_size, libyuv::kJpegYuv420, y_pointer, u_pointer,
510      v_pointer));
511  // Allocate space for the output image.
512  rtc::scoped_ptr<uint8_t[]> yuv_output(
513      new uint8_t[I420_SIZE(kHeight, kWidth) + kAlignment]);
514  uint8_t* y_output_pointer = ALIGNP(yuv_output.get(), kAlignment);
515  uint8_t* u_output_pointer = y_output_pointer + y_size;
516  uint8_t* v_output_pointer = u_output_pointer + uv_size;
517
518  for (int i = 0; i < repeat_; ++i) {
519  libyuv::I420Copy(y_pointer, y_pitch,
520                   u_pointer, u_pitch,
521                   v_pointer, v_pitch,
522                   y_output_pointer, y_pitch,
523                   u_output_pointer, u_pitch,
524                   v_output_pointer, v_pitch,
525                   kWidth, kHeight);
526  }
527
528  // Expect the copied frame to be exactly the same.
529  EXPECT_TRUE(IsMemoryEqual(y_output_pointer, y_pointer,
530      I420_SIZE(kHeight, kWidth), 1.e-6));
531
532  if (dump_) { DumpYuvImage(y_output_pointer, kWidth, kHeight); }
533}
534
535TEST_F(PlanarFunctionsTest, I422ToI420) {
536  uint8_t* y_pointer = nullptr;
537  uint8_t* u_pointer = nullptr;
538  uint8_t* v_pointer = nullptr;
539  int y_pitch = kWidth;
540  int u_pitch = (kWidth + 1) >> 1;
541  int v_pitch = (kWidth + 1) >> 1;
542  int y_size = kHeight * kWidth;
543  int uv_size = ((kHeight + 1) >> 1) * ((kWidth + 1) >> 1);
544  int block_size = 2;
545  // Generate a fake input image.
546  rtc::scoped_ptr<uint8_t[]> yuv_input(CreateFakeYuvTestingImage(
547      kHeight, kWidth, block_size, libyuv::kJpegYuv422, y_pointer, u_pointer,
548      v_pointer));
549  // Allocate space for the output image.
550  rtc::scoped_ptr<uint8_t[]> yuv_output(
551      new uint8_t[I420_SIZE(kHeight, kWidth) + kAlignment]);
552  uint8_t* y_output_pointer = ALIGNP(yuv_output.get(), kAlignment);
553  uint8_t* u_output_pointer = y_output_pointer + y_size;
554  uint8_t* v_output_pointer = u_output_pointer + uv_size;
555  // Generate the expected output.
556  uint8_t* y_expected_pointer = nullptr;
557  uint8_t* u_expected_pointer = nullptr;
558  uint8_t* v_expected_pointer = nullptr;
559  rtc::scoped_ptr<uint8_t[]> yuv_output_expected(CreateFakeYuvTestingImage(
560      kHeight, kWidth, block_size, libyuv::kJpegYuv420, y_expected_pointer,
561      u_expected_pointer, v_expected_pointer));
562
563  for (int i = 0; i < repeat_; ++i) {
564  libyuv::I422ToI420(y_pointer, y_pitch,
565                     u_pointer, u_pitch,
566                     v_pointer, v_pitch,
567                     y_output_pointer, y_pitch,
568                     u_output_pointer, u_pitch,
569                     v_output_pointer, v_pitch,
570                     kWidth, kHeight);
571  }
572
573  // Compare the output frame with what is expected; expect exactly the same.
574  // Note: MSE should be set to a larger threshold if an odd block width
575  // is used, since the conversion will be lossy.
576  EXPECT_TRUE(IsMemoryEqual(y_output_pointer, y_expected_pointer,
577      I420_SIZE(kHeight, kWidth), 1.e-6));
578
579  if (dump_) { DumpYuvImage(y_output_pointer, kWidth, kHeight); }
580}
581
582TEST_P(PlanarFunctionsTest, M420ToI420) {
583  // Get the unalignment offset
584  int unalignment = GetParam();
585  uint8_t* m420_pointer = NULL;
586  int y_pitch = kWidth;
587  int m420_pitch = kWidth;
588  int u_pitch = (kWidth + 1) >> 1;
589  int v_pitch = (kWidth + 1) >> 1;
590  int y_size = kHeight * kWidth;
591  int uv_size = ((kHeight + 1) >> 1) * ((kWidth + 1) >> 1);
592  int block_size = 2;
593  // Generate a fake input image.
594  rtc::scoped_ptr<uint8_t[]> yuv_input(
595      CreateFakeM420TestingImage(kHeight, kWidth, block_size, m420_pointer));
596  // Allocate space for the output image.
597  rtc::scoped_ptr<uint8_t[]> yuv_output(
598      new uint8_t[I420_SIZE(kHeight, kWidth) + kAlignment + unalignment]);
599  uint8_t* y_output_pointer =
600      ALIGNP(yuv_output.get(), kAlignment) + unalignment;
601  uint8_t* u_output_pointer = y_output_pointer + y_size;
602  uint8_t* v_output_pointer = u_output_pointer + uv_size;
603  // Generate the expected output.
604  uint8_t* y_expected_pointer = nullptr;
605  uint8_t* u_expected_pointer = nullptr;
606  uint8_t* v_expected_pointer = nullptr;
607  rtc::scoped_ptr<uint8_t[]> yuv_output_expected(CreateFakeYuvTestingImage(
608      kHeight, kWidth, block_size, libyuv::kJpegYuv420, y_expected_pointer,
609      u_expected_pointer, v_expected_pointer));
610
611  for (int i = 0; i < repeat_; ++i) {
612  libyuv::M420ToI420(m420_pointer, m420_pitch,
613                     y_output_pointer, y_pitch,
614                     u_output_pointer, u_pitch,
615                     v_output_pointer, v_pitch,
616                     kWidth, kHeight);
617  }
618  // Compare the output frame with what is expected; expect exactly the same.
619  // Note: MSE should be set to a larger threshold if an odd block width
620  // is used, since the conversion will be lossy.
621  EXPECT_TRUE(IsMemoryEqual(y_output_pointer, y_expected_pointer,
622      I420_SIZE(kHeight, kWidth), 1.e-6));
623
624  if (dump_) { DumpYuvImage(y_output_pointer, kWidth, kHeight); }
625}
626
627TEST_P(PlanarFunctionsTest, NV12ToI420) {
628  // Get the unalignment offset
629  int unalignment = GetParam();
630  uint8_t* y_pointer = nullptr;
631  uint8_t* uv_pointer = nullptr;
632  int y_pitch = kWidth;
633  int uv_pitch = 2 * ((kWidth + 1) >> 1);
634  int u_pitch = (kWidth + 1) >> 1;
635  int v_pitch = (kWidth + 1) >> 1;
636  int y_size = kHeight * kWidth;
637  int uv_size = ((kHeight + 1) >> 1) * ((kWidth + 1) >> 1);
638  int block_size = 2;
639  // Generate a fake input image.
640  rtc::scoped_ptr<uint8_t[]> yuv_input(CreateFakeNV12TestingImage(
641      kHeight, kWidth, block_size, y_pointer, uv_pointer));
642  // Allocate space for the output image.
643  rtc::scoped_ptr<uint8_t[]> yuv_output(
644      new uint8_t[I420_SIZE(kHeight, kWidth) + kAlignment + unalignment]);
645  uint8_t* y_output_pointer =
646      ALIGNP(yuv_output.get(), kAlignment) + unalignment;
647  uint8_t* u_output_pointer = y_output_pointer + y_size;
648  uint8_t* v_output_pointer = u_output_pointer + uv_size;
649  // Generate the expected output.
650  uint8_t* y_expected_pointer = nullptr;
651  uint8_t* u_expected_pointer = nullptr;
652  uint8_t* v_expected_pointer = nullptr;
653  rtc::scoped_ptr<uint8_t[]> yuv_output_expected(CreateFakeYuvTestingImage(
654      kHeight, kWidth, block_size, libyuv::kJpegYuv420, y_expected_pointer,
655      u_expected_pointer, v_expected_pointer));
656
657  for (int i = 0; i < repeat_; ++i) {
658  libyuv::NV12ToI420(y_pointer, y_pitch,
659                     uv_pointer, uv_pitch,
660                     y_output_pointer, y_pitch,
661                     u_output_pointer, u_pitch,
662                     v_output_pointer, v_pitch,
663                     kWidth, kHeight);
664  }
665  // Compare the output frame with what is expected; expect exactly the same.
666  // Note: MSE should be set to a larger threshold if an odd block width
667  // is used, since the conversion will be lossy.
668  EXPECT_TRUE(IsMemoryEqual(y_output_pointer, y_expected_pointer,
669      I420_SIZE(kHeight, kWidth), 1.e-6));
670
671  if (dump_) { DumpYuvImage(y_output_pointer, kWidth, kHeight); }
672}
673
674// A common macro for testing converting YUY2/UYVY to I420.
675#define TEST_YUVTOI420(SRC_NAME, MSE, BLOCK_SIZE)                             \
676  TEST_P(PlanarFunctionsTest, SRC_NAME##ToI420) {                             \
677    /* Get the unalignment offset.*/                                          \
678    int unalignment = GetParam();                                             \
679    uint8_t* yuv_pointer = nullptr;                                           \
680    int yuv_pitch = 2 * ((kWidth + 1) & ~1);                                  \
681    int y_pitch = kWidth;                                                     \
682    int u_pitch = (kWidth + 1) >> 1;                                          \
683    int v_pitch = (kWidth + 1) >> 1;                                          \
684    int y_size = kHeight * kWidth;                                            \
685    int uv_size = ((kHeight + 1) >> 1) * ((kWidth + 1) >> 1);                 \
686    int block_size = 2;                                                       \
687    /* Generate a fake input image.*/                                         \
688    rtc::scoped_ptr<uint8_t[]> yuv_input(CreateFakeInterleaveYuvTestingImage( \
689        kHeight, kWidth, BLOCK_SIZE, yuv_pointer, FOURCC_##SRC_NAME));        \
690    /* Allocate space for the output image.*/                                 \
691    rtc::scoped_ptr<uint8_t[]> yuv_output(                                    \
692        new uint8_t[I420_SIZE(kHeight, kWidth) + kAlignment + unalignment]);  \
693    uint8_t* y_output_pointer =                                               \
694        ALIGNP(yuv_output.get(), kAlignment) + unalignment;                   \
695    uint8_t* u_output_pointer = y_output_pointer + y_size;                    \
696    uint8_t* v_output_pointer = u_output_pointer + uv_size;                   \
697    /* Generate the expected output.*/                                        \
698    uint8_t* y_expected_pointer = nullptr;                                    \
699    uint8_t* u_expected_pointer = nullptr;                                    \
700    uint8_t* v_expected_pointer = nullptr;                                    \
701    rtc::scoped_ptr<uint8_t[]> yuv_output_expected(CreateFakeYuvTestingImage( \
702        kHeight, kWidth, block_size, libyuv::kJpegYuv420, y_expected_pointer, \
703        u_expected_pointer, v_expected_pointer));                             \
704    for (int i = 0; i < repeat_; ++i) {                                       \
705      libyuv::SRC_NAME##ToI420(yuv_pointer, yuv_pitch, y_output_pointer,      \
706                               y_pitch, u_output_pointer, u_pitch,            \
707                               v_output_pointer, v_pitch, kWidth, kHeight);   \
708    }                                                                         \
709    /* Compare the output frame with what is expected.*/                      \
710    /* Note: MSE should be set to a larger threshold if an odd block width*/  \
711    /* is used, since the conversion will be lossy.*/                         \
712    EXPECT_TRUE(IsMemoryEqual(y_output_pointer, y_expected_pointer,           \
713                              I420_SIZE(kHeight, kWidth), MSE));              \
714    if (dump_) {                                                              \
715      DumpYuvImage(y_output_pointer, kWidth, kHeight);                        \
716    }                                                                         \
717  }
718
719// TEST_P(PlanarFunctionsTest, YUV2ToI420)
720TEST_YUVTOI420(YUY2, 1.e-6, 2);
721// TEST_P(PlanarFunctionsTest, UYVYToI420)
722TEST_YUVTOI420(UYVY, 1.e-6, 2);
723
724// A common macro for testing converting I420 to ARGB, BGRA and ABGR.
725#define TEST_YUVTORGB(SRC_NAME, DST_NAME, JPG_TYPE, MSE, BLOCK_SIZE)           \
726  TEST_F(PlanarFunctionsTest, SRC_NAME##To##DST_NAME) {                        \
727    uint8_t* y_pointer = nullptr;                                              \
728    uint8_t* u_pointer = nullptr;                                              \
729    uint8_t* v_pointer = nullptr;                                              \
730    uint8_t* argb_expected_pointer = NULL;                                     \
731    int y_pitch = kWidth;                                                      \
732    int u_pitch = (kWidth + 1) >> 1;                                           \
733    int v_pitch = (kWidth + 1) >> 1;                                           \
734    /* Generate a fake input image.*/                                          \
735    rtc::scoped_ptr<uint8_t[]> yuv_input(                                      \
736        CreateFakeYuvTestingImage(kHeight, kWidth, BLOCK_SIZE, JPG_TYPE,       \
737                                  y_pointer, u_pointer, v_pointer));           \
738    /* Generate the expected output.*/                                         \
739    rtc::scoped_ptr<uint8_t[]> argb_expected(                                  \
740        CreateFakeArgbTestingImage(kHeight, kWidth, BLOCK_SIZE,                \
741                                   argb_expected_pointer, FOURCC_##DST_NAME)); \
742    /* Allocate space for the output.*/                                        \
743    rtc::scoped_ptr<uint8_t[]> argb_output(                                    \
744        new uint8_t[kHeight * kWidth * 4 + kAlignment]);                       \
745    uint8_t* argb_pointer = ALIGNP(argb_expected.get(), kAlignment);           \
746    for (int i = 0; i < repeat_; ++i) {                                        \
747      libyuv::SRC_NAME##To##DST_NAME(y_pointer, y_pitch, u_pointer, u_pitch,   \
748                                     v_pointer, v_pitch, argb_pointer,         \
749                                     kWidth * 4, kWidth, kHeight);             \
750    }                                                                          \
751    EXPECT_TRUE(IsMemoryEqual(argb_expected_pointer, argb_pointer,             \
752                              kHeight* kWidth * 4, MSE));                      \
753    if (dump_) {                                                               \
754      DumpArgbImage(argb_pointer, kWidth, kHeight);                            \
755    }                                                                          \
756  }
757
758// TEST_F(PlanarFunctionsTest, I420ToARGB)
759TEST_YUVTORGB(I420, ARGB, libyuv::kJpegYuv420, 3., 2);
760// TEST_F(PlanarFunctionsTest, I420ToABGR)
761TEST_YUVTORGB(I420, ABGR, libyuv::kJpegYuv420, 3., 2);
762// TEST_F(PlanarFunctionsTest, I420ToBGRA)
763TEST_YUVTORGB(I420, BGRA, libyuv::kJpegYuv420, 3., 2);
764// TEST_F(PlanarFunctionsTest, I422ToARGB)
765TEST_YUVTORGB(I422, ARGB, libyuv::kJpegYuv422, 3., 2);
766// TEST_F(PlanarFunctionsTest, I444ToARGB)
767TEST_YUVTORGB(I444, ARGB, libyuv::kJpegYuv444, 3., 3);
768// Note: an empirical MSE tolerance 3.0 is used here for the probable
769// error from float-to-uint8_t type conversion.
770
771TEST_F(PlanarFunctionsTest, I400ToARGB_Reference) {
772  uint8_t* y_pointer = nullptr;
773  uint8_t* u_pointer = nullptr;
774  uint8_t* v_pointer = nullptr;
775  int y_pitch = kWidth;
776  int u_pitch = (kWidth + 1) >> 1;
777  int v_pitch = (kWidth + 1) >> 1;
778  int block_size = 3;
779  // Generate a fake input image.
780  rtc::scoped_ptr<uint8_t[]> yuv_input(CreateFakeYuvTestingImage(
781      kHeight, kWidth, block_size, libyuv::kJpegYuv420, y_pointer, u_pointer,
782      v_pointer));
783  // As the comparison standard, we convert a grayscale image (by setting both
784  // U and V channels to be 128) using an I420 converter.
785  int uv_size = ((kHeight + 1) >> 1) * ((kWidth + 1) >> 1);
786
787  rtc::scoped_ptr<uint8_t[]> uv(new uint8_t[uv_size + kAlignment]);
788  u_pointer = v_pointer = ALIGNP(uv.get(), kAlignment);
789  memset(u_pointer, 128, uv_size);
790
791  // Allocate space for the output image and generate the expected output.
792  rtc::scoped_ptr<uint8_t[]> argb_expected(
793      new uint8_t[kHeight * kWidth * 4 + kAlignment]);
794  rtc::scoped_ptr<uint8_t[]> argb_output(
795      new uint8_t[kHeight * kWidth * 4 + kAlignment]);
796  uint8_t* argb_expected_pointer = ALIGNP(argb_expected.get(), kAlignment);
797  uint8_t* argb_pointer = ALIGNP(argb_output.get(), kAlignment);
798
799  libyuv::I420ToARGB(y_pointer, y_pitch,
800                     u_pointer, u_pitch,
801                     v_pointer, v_pitch,
802                     argb_expected_pointer, kWidth * 4,
803                     kWidth, kHeight);
804  for (int i = 0; i < repeat_; ++i) {
805    libyuv::I400ToARGB_Reference(y_pointer, y_pitch,
806                                 argb_pointer, kWidth * 4,
807                                 kWidth, kHeight);
808  }
809
810  // Note: I420ToARGB and I400ToARGB_Reference should produce identical results.
811  EXPECT_TRUE(IsMemoryEqual(argb_expected_pointer, argb_pointer,
812                            kHeight * kWidth * 4, 2.));
813  if (dump_) { DumpArgbImage(argb_pointer, kWidth, kHeight); }
814}
815
816TEST_P(PlanarFunctionsTest, I400ToARGB) {
817  // Get the unalignment offset
818  int unalignment = GetParam();
819  uint8_t* y_pointer = nullptr;
820  uint8_t* u_pointer = nullptr;
821  uint8_t* v_pointer = nullptr;
822  int y_pitch = kWidth;
823  int u_pitch = (kWidth + 1) >> 1;
824  int v_pitch = (kWidth + 1) >> 1;
825  int block_size = 3;
826  // Generate a fake input image.
827  rtc::scoped_ptr<uint8_t[]> yuv_input(CreateFakeYuvTestingImage(
828      kHeight, kWidth, block_size, libyuv::kJpegYuv420, y_pointer, u_pointer,
829      v_pointer));
830  // As the comparison standard, we convert a grayscale image (by setting both
831  // U and V channels to be 128) using an I420 converter.
832  int uv_size = ((kHeight + 1) >> 1) * ((kWidth + 1) >> 1);
833
834  // 1 byte extra if in the unaligned mode.
835  rtc::scoped_ptr<uint8_t[]> uv(new uint8_t[uv_size * 2 + kAlignment]);
836  u_pointer = ALIGNP(uv.get(), kAlignment);
837  v_pointer = u_pointer + uv_size;
838  memset(u_pointer, 128, uv_size);
839  memset(v_pointer, 128, uv_size);
840
841  // Allocate space for the output image and generate the expected output.
842  rtc::scoped_ptr<uint8_t[]> argb_expected(
843      new uint8_t[kHeight * kWidth * 4 + kAlignment]);
844  // 1 byte extra if in the misalinged mode.
845  rtc::scoped_ptr<uint8_t[]> argb_output(
846      new uint8_t[kHeight * kWidth * 4 + kAlignment + unalignment]);
847  uint8_t* argb_expected_pointer = ALIGNP(argb_expected.get(), kAlignment);
848  uint8_t* argb_pointer = ALIGNP(argb_output.get(), kAlignment) + unalignment;
849
850  libyuv::I420ToARGB(y_pointer, y_pitch,
851                     u_pointer, u_pitch,
852                     v_pointer, v_pitch,
853                     argb_expected_pointer, kWidth * 4,
854                     kWidth, kHeight);
855  for (int i = 0; i < repeat_; ++i) {
856    libyuv::I400ToARGB(y_pointer, y_pitch,
857                       argb_pointer, kWidth * 4,
858                       kWidth, kHeight);
859  }
860
861  // Note: current I400ToARGB uses an approximate method,
862  // so the error tolerance is larger here.
863  EXPECT_TRUE(IsMemoryEqual(argb_expected_pointer, argb_pointer,
864                            kHeight * kWidth * 4, 64.0));
865  if (dump_) { DumpArgbImage(argb_pointer, kWidth, kHeight); }
866}
867
868TEST_P(PlanarFunctionsTest, ARGBToI400) {
869  // Get the unalignment offset
870  int unalignment = GetParam();
871  // Create a fake ARGB input image.
872  uint8_t* y_pointer = NULL, * u_pointer = NULL, * v_pointer = NULL;
873  uint8_t* argb_pointer = NULL;
874  int block_size = 3;
875  // Generate a fake input image.
876  rtc::scoped_ptr<uint8_t[]> argb_input(CreateFakeArgbTestingImage(
877      kHeight, kWidth, block_size, argb_pointer, FOURCC_ARGB));
878  // Generate the expected output. Only Y channel is used
879  rtc::scoped_ptr<uint8_t[]> yuv_expected(CreateFakeYuvTestingImage(
880      kHeight, kWidth, block_size, libyuv::kJpegYuv420, y_pointer, u_pointer,
881      v_pointer));
882  // Allocate space for the Y output.
883  rtc::scoped_ptr<uint8_t[]> y_output(
884      new uint8_t[kHeight * kWidth + kAlignment + unalignment]);
885  uint8_t* y_output_pointer = ALIGNP(y_output.get(), kAlignment) + unalignment;
886
887  for (int i = 0; i < repeat_; ++i) {
888    libyuv::ARGBToI400(argb_pointer, kWidth * 4, y_output_pointer, kWidth,
889                       kWidth, kHeight);
890  }
891  // Check if the output matches the input Y channel.
892  // Note: an empirical MSE tolerance 2.0 is used here for the probable
893  // error from float-to-uint8_t type conversion.
894  EXPECT_TRUE(IsMemoryEqual(y_output_pointer, y_pointer,
895                            kHeight * kWidth, 2.));
896  if (dump_) { DumpArgbImage(argb_pointer, kWidth, kHeight); }
897}
898
899// A common macro for testing converting RAW, BG24, BGRA, and ABGR
900// to ARGB.
901#define TEST_ARGB(SRC_NAME, FC_ID, BPP, BLOCK_SIZE)                            \
902  TEST_P(PlanarFunctionsTest, SRC_NAME##ToARGB) {                              \
903    int unalignment = GetParam(); /* Get the unalignment offset.*/             \
904    uint8_t* argb_expected_pointer = NULL, * src_pointer = NULL;               \
905    /* Generate a fake input image.*/                                          \
906    rtc::scoped_ptr<uint8_t[]> src_input(CreateFakeArgbTestingImage(           \
907        kHeight, kWidth, BLOCK_SIZE, src_pointer, FOURCC_##FC_ID));            \
908    /* Generate the expected output.*/                                         \
909    rtc::scoped_ptr<uint8_t[]> argb_expected(CreateFakeArgbTestingImage(       \
910        kHeight, kWidth, BLOCK_SIZE, argb_expected_pointer, FOURCC_ARGB));     \
911    /* Allocate space for the output; 1 byte extra if in the unaligned mode.*/ \
912    rtc::scoped_ptr<uint8_t[]> argb_output(                                    \
913        new uint8_t[kHeight * kWidth * 4 + kAlignment + unalignment]);         \
914    uint8_t* argb_pointer =                                                    \
915        ALIGNP(argb_output.get(), kAlignment) + unalignment;                   \
916    for (int i = 0; i < repeat_; ++i) {                                        \
917      libyuv::SRC_NAME##ToARGB(src_pointer, kWidth*(BPP), argb_pointer,        \
918                               kWidth * 4, kWidth, kHeight);                   \
919    }                                                                          \
920    /* Compare the result; expect identical.*/                                 \
921    EXPECT_TRUE(IsMemoryEqual(argb_expected_pointer, argb_pointer,             \
922                              kHeight* kWidth * 4, 1.e-6));                    \
923    if (dump_) {                                                               \
924      DumpArgbImage(argb_pointer, kWidth, kHeight);                            \
925    }                                                                          \
926  }
927
928TEST_ARGB(RAW, RAW, 3, 3);    // TEST_P(PlanarFunctionsTest, RAWToARGB)
929TEST_ARGB(BG24, 24BG, 3, 3);  // TEST_P(PlanarFunctionsTest, BG24ToARGB)
930TEST_ARGB(ABGR, ABGR, 4, 3);  // TEST_P(PlanarFunctionsTest, ABGRToARGB)
931TEST_ARGB(BGRA, BGRA, 4, 3);  // TEST_P(PlanarFunctionsTest, BGRAToARGB)
932
933// Parameter Test: The parameter is the unalignment offset.
934// Aligned data for testing assembly versions.
935INSTANTIATE_TEST_CASE_P(PlanarFunctionsAligned, PlanarFunctionsTest,
936    ::testing::Values(0));
937
938// Purposely unalign the output argb pointer to test slow path (C version).
939INSTANTIATE_TEST_CASE_P(PlanarFunctionsMisaligned, PlanarFunctionsTest,
940    ::testing::Values(1));
941
942}  // namespace cricket
943