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 
45 using cricket::DumpPlanarYuvTestImage;
46 
47 DEFINE_bool(planarfunctions_dump, false,
48     "whether to write out scaled images for inspection");
49 DEFINE_int(planarfunctions_repeat, 1,
50     "how many times to perform each scaling operation (for perf testing)");
51 
52 namespace cricket {
53 
54 // Number of testing colors in each color channel.
55 static const int kTestingColorChannelResolution = 6;
56 
57 // The total number of testing colors
58 // kTestingColorNum = kTestingColorChannelResolution^3;
59 static const int kTestingColorNum = kTestingColorChannelResolution *
60     kTestingColorChannelResolution * kTestingColorChannelResolution;
61 
62 static const int kWidth = 1280;
63 static const int kHeight = 720;
64 static const int kAlignment = 16;
65 
66 class PlanarFunctionsTest : public testing::TestWithParam<int> {
67  protected:
PlanarFunctionsTest()68   PlanarFunctionsTest() : dump_(false), repeat_(1) {
69     InitializeColorBand();
70   }
71 
SetUp()72   virtual void SetUp() {
73     dump_ = FLAG_planarfunctions_dump;
74     repeat_ = FLAG_planarfunctions_repeat;
75   }
76 
77   // Initialize the color band for testing.
InitializeColorBand()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)
ConvertRgbPixel(uint8_t r,uint8_t g,uint8_t b,uint8_t * y,uint8_t * u,uint8_t * v)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 
ClampUint8(double value)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).
CreateFakeYuvTestingImage(int height,int width,int block_size,libyuv::JpegSubsamplingType subsample_type,uint8_t * & y_pointer,uint8_t * & u_pointer,uint8_t * & v_pointer)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).
CreateFakeInterleaveYuvTestingImage(int height,int width,int block_size,uint8_t * & yuv_pointer,FourCC fourcc_type)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).
CreateFakeNV12TestingImage(int height,int width,int block_size,uint8_t * & y_pointer,uint8_t * & uv_pointer)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).
CreateFakeM420TestingImage(int height,int width,int block_size,uint8_t * & m420_pointer)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).
CreateFakeArgbTestingImage(int height,int width,int block_size,uint8_t * & argb_pointer,FourCC fourcc_type)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).
IsMemoryEqual(const uint8_t * ibuf,const uint8_t * obuf,int osize,double average_error)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.
FindDiff(const uint8_t * buf1,const uint8_t * buf2,int len)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).
DumpArgbImage(const uint8_t * obuf,int width,int height)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).
DumpYuvImage(const uint8_t * obuf,int width,int height)474   void DumpYuvImage(const uint8_t* obuf, int width, int height) {
475     DumpPlanarYuvTestImage(GetTestName(), obuf, width, height);
476   }
477 
GetTestName()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 
TEST_F(PlanarFunctionsTest,I420Copy)497 TEST_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 
TEST_F(PlanarFunctionsTest,I422ToI420)535 TEST_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 
TEST_P(PlanarFunctionsTest,M420ToI420)582 TEST_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 
TEST_P(PlanarFunctionsTest,NV12ToI420)627 TEST_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)
720 TEST_YUVTOI420(YUY2, 1.e-6, 2);
721 // TEST_P(PlanarFunctionsTest, UYVYToI420)
722 TEST_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)
759 TEST_YUVTORGB(I420, ARGB, libyuv::kJpegYuv420, 3., 2);
760 // TEST_F(PlanarFunctionsTest, I420ToABGR)
761 TEST_YUVTORGB(I420, ABGR, libyuv::kJpegYuv420, 3., 2);
762 // TEST_F(PlanarFunctionsTest, I420ToBGRA)
763 TEST_YUVTORGB(I420, BGRA, libyuv::kJpegYuv420, 3., 2);
764 // TEST_F(PlanarFunctionsTest, I422ToARGB)
765 TEST_YUVTORGB(I422, ARGB, libyuv::kJpegYuv422, 3., 2);
766 // TEST_F(PlanarFunctionsTest, I444ToARGB)
767 TEST_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 
TEST_F(PlanarFunctionsTest,I400ToARGB_Reference)771 TEST_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 
TEST_P(PlanarFunctionsTest,I400ToARGB)816 TEST_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 
TEST_P(PlanarFunctionsTest,ARGBToI400)868 TEST_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 
928 TEST_ARGB(RAW, RAW, 3, 3);    // TEST_P(PlanarFunctionsTest, RAWToARGB)
929 TEST_ARGB(BG24, 24BG, 3, 3);  // TEST_P(PlanarFunctionsTest, BG24ToARGB)
930 TEST_ARGB(ABGR, ABGR, 4, 3);  // TEST_P(PlanarFunctionsTest, ABGRToARGB)
931 TEST_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.
935 INSTANTIATE_TEST_CASE_P(PlanarFunctionsAligned, PlanarFunctionsTest,
936     ::testing::Values(0));
937 
938 // Purposely unalign the output argb pointer to test slow path (C version).
939 INSTANTIATE_TEST_CASE_P(PlanarFunctionsMisaligned, PlanarFunctionsTest,
940     ::testing::Values(1));
941 
942 }  // namespace cricket
943