blob: af7e628cde349a9ccbb28132b8e287d719a83c56 [file] [log] [blame]
jlmiller@webrtc.org5f93d0a2015-01-20 21:36:13 +00001/*
kjellander65c7f672016-02-12 00:05:01 -08002 * Copyright 2014 The WebRTC project authors. All Rights Reserved.
jlmiller@webrtc.org5f93d0a2015-01-20 21:36:13 +00003 *
kjellander65c7f672016-02-12 00:05:01 -08004 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
jlmiller@webrtc.org5f93d0a2015-01-20 21:36:13 +00009 */
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +000010
kwiberg31022942016-03-11 14:18:21 -080011#include <memory>
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +000012#include <string>
13
14#include "libyuv/convert.h"
15#include "libyuv/convert_from.h"
16#include "libyuv/convert_from_argb.h"
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +000017#include "libyuv/mjpeg_decoder.h"
18#include "libyuv/planar_functions.h"
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000019#include "webrtc/base/flags.h"
20#include "webrtc/base/gunit.h"
kjellandera96e2d72016-02-04 23:52:28 -080021#include "webrtc/media/base/testutils.h"
22#include "webrtc/media/base/videocommon.h"
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +000023
24// Undefine macros for the windows build.
25#undef max
26#undef min
27
28using cricket::DumpPlanarYuvTestImage;
29
30DEFINE_bool(planarfunctions_dump, false,
31 "whether to write out scaled images for inspection");
32DEFINE_int(planarfunctions_repeat, 1,
33 "how many times to perform each scaling operation (for perf testing)");
34
35namespace cricket {
36
37// Number of testing colors in each color channel.
38static const int kTestingColorChannelResolution = 6;
39
40// The total number of testing colors
41// kTestingColorNum = kTestingColorChannelResolution^3;
42static const int kTestingColorNum = kTestingColorChannelResolution *
43 kTestingColorChannelResolution * kTestingColorChannelResolution;
44
45static const int kWidth = 1280;
46static const int kHeight = 720;
47static const int kAlignment = 16;
48
49class PlanarFunctionsTest : public testing::TestWithParam<int> {
50 protected:
51 PlanarFunctionsTest() : dump_(false), repeat_(1) {
52 InitializeColorBand();
53 }
54
55 virtual void SetUp() {
56 dump_ = FLAG_planarfunctions_dump;
57 repeat_ = FLAG_planarfunctions_repeat;
58 }
59
60 // Initialize the color band for testing.
61 void InitializeColorBand() {
Peter Boström0c4e06b2015-10-07 12:23:21 +020062 testing_color_y_.reset(new uint8_t[kTestingColorNum]);
63 testing_color_u_.reset(new uint8_t[kTestingColorNum]);
64 testing_color_v_.reset(new uint8_t[kTestingColorNum]);
65 testing_color_r_.reset(new uint8_t[kTestingColorNum]);
66 testing_color_g_.reset(new uint8_t[kTestingColorNum]);
67 testing_color_b_.reset(new uint8_t[kTestingColorNum]);
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +000068 int color_counter = 0;
69 for (int i = 0; i < kTestingColorChannelResolution; ++i) {
Peter Boström0c4e06b2015-10-07 12:23:21 +020070 uint8_t color_r =
71 static_cast<uint8_t>(i * 255 / (kTestingColorChannelResolution - 1));
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +000072 for (int j = 0; j < kTestingColorChannelResolution; ++j) {
Peter Boström0c4e06b2015-10-07 12:23:21 +020073 uint8_t color_g = static_cast<uint8_t>(
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +000074 j * 255 / (kTestingColorChannelResolution - 1));
75 for (int k = 0; k < kTestingColorChannelResolution; ++k) {
Peter Boström0c4e06b2015-10-07 12:23:21 +020076 uint8_t color_b = static_cast<uint8_t>(
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +000077 k * 255 / (kTestingColorChannelResolution - 1));
78 testing_color_r_[color_counter] = color_r;
79 testing_color_g_[color_counter] = color_g;
80 testing_color_b_[color_counter] = color_b;
81 // Converting the testing RGB colors to YUV colors.
82 ConvertRgbPixel(color_r, color_g, color_b,
83 &(testing_color_y_[color_counter]),
84 &(testing_color_u_[color_counter]),
85 &(testing_color_v_[color_counter]));
86 ++color_counter;
87 }
88 }
89 }
90 }
91 // Simple and slow RGB->YUV conversion. From NTSC standard, c/o Wikipedia.
92 // (from lmivideoframe_unittest.cc)
Peter Boström0c4e06b2015-10-07 12:23:21 +020093 void ConvertRgbPixel(uint8_t r,
94 uint8_t g,
95 uint8_t b,
96 uint8_t* y,
97 uint8_t* u,
98 uint8_t* v) {
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +000099 *y = ClampUint8(.257 * r + .504 * g + .098 * b + 16);
100 *u = ClampUint8(-.148 * r - .291 * g + .439 * b + 128);
101 *v = ClampUint8(.439 * r - .368 * g - .071 * b + 128);
102 }
103
Peter Boström0c4e06b2015-10-07 12:23:21 +0200104 uint8_t ClampUint8(double value) {
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000105 value = std::max(0., std::min(255., value));
Peter Boström0c4e06b2015-10-07 12:23:21 +0200106 uint8_t uint8_value = static_cast<uint8_t>(value);
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000107 return uint8_value;
108 }
109
110 // Generate a Red-Green-Blue inter-weaving chessboard-like
111 // YUV testing image (I420/I422/I444).
112 // The pattern looks like c0 c1 c2 c3 ...
113 // c1 c2 c3 c4 ...
114 // c2 c3 c4 c5 ...
115 // ...............
116 // The size of each chrome block is (block_size) x (block_size).
Peter Boström0c4e06b2015-10-07 12:23:21 +0200117 uint8_t* CreateFakeYuvTestingImage(int height,
118 int width,
119 int block_size,
120 libyuv::JpegSubsamplingType subsample_type,
121 uint8_t*& y_pointer,
122 uint8_t*& u_pointer,
123 uint8_t*& v_pointer) {
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000124 if (height <= 0 || width <= 0 || block_size <= 0) { return NULL; }
125 int y_size = height * width;
126 int u_size, v_size;
127 int vertical_sample_ratio = 1, horizontal_sample_ratio = 1;
128 switch (subsample_type) {
129 case libyuv::kJpegYuv420:
130 u_size = ((height + 1) >> 1) * ((width + 1) >> 1);
131 v_size = u_size;
132 vertical_sample_ratio = 2, horizontal_sample_ratio = 2;
133 break;
134 case libyuv::kJpegYuv422:
135 u_size = height * ((width + 1) >> 1);
136 v_size = u_size;
137 vertical_sample_ratio = 1, horizontal_sample_ratio = 2;
138 break;
139 case libyuv::kJpegYuv444:
140 v_size = u_size = y_size;
141 vertical_sample_ratio = 1, horizontal_sample_ratio = 1;
142 break;
143 case libyuv::kJpegUnknown:
144 default:
145 return NULL;
146 break;
147 }
Peter Boström0c4e06b2015-10-07 12:23:21 +0200148 uint8_t* image_pointer = new uint8_t[y_size + u_size + v_size + kAlignment];
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000149 y_pointer = ALIGNP(image_pointer, kAlignment);
150 u_pointer = ALIGNP(&image_pointer[y_size], kAlignment);
151 v_pointer = ALIGNP(&image_pointer[y_size + u_size], kAlignment);
Peter Boström0c4e06b2015-10-07 12:23:21 +0200152 uint8_t* current_y_pointer = y_pointer;
153 uint8_t* current_u_pointer = u_pointer;
154 uint8_t* current_v_pointer = v_pointer;
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000155 for (int j = 0; j < height; ++j) {
156 for (int i = 0; i < width; ++i) {
157 int color = ((i / block_size) + (j / block_size)) % kTestingColorNum;
158 *(current_y_pointer++) = testing_color_y_[color];
159 if (i % horizontal_sample_ratio == 0 &&
160 j % vertical_sample_ratio == 0) {
161 *(current_u_pointer++) = testing_color_u_[color];
162 *(current_v_pointer++) = testing_color_v_[color];
163 }
164 }
165 }
166 return image_pointer;
167 }
168
169 // Generate a Red-Green-Blue inter-weaving chessboard-like
170 // YUY2/UYVY testing image.
171 // The pattern looks like c0 c1 c2 c3 ...
172 // c1 c2 c3 c4 ...
173 // c2 c3 c4 c5 ...
174 // ...............
175 // The size of each chrome block is (block_size) x (block_size).
Peter Boström0c4e06b2015-10-07 12:23:21 +0200176 uint8_t* CreateFakeInterleaveYuvTestingImage(int height,
177 int width,
178 int block_size,
179 uint8_t*& yuv_pointer,
180 FourCC fourcc_type) {
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000181 if (height <= 0 || width <= 0 || block_size <= 0) { return NULL; }
182 if (fourcc_type != FOURCC_YUY2 && fourcc_type != FOURCC_UYVY) {
183 LOG(LS_ERROR) << "Format " << static_cast<int>(fourcc_type)
184 << " is not supported.";
185 return NULL;
186 }
187 // Regularize the width of the output to be even.
188 int awidth = (width + 1) & ~1;
189
Peter Boström0c4e06b2015-10-07 12:23:21 +0200190 uint8_t* image_pointer = new uint8_t[2 * height * awidth + kAlignment];
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000191 yuv_pointer = ALIGNP(image_pointer, kAlignment);
Peter Boström0c4e06b2015-10-07 12:23:21 +0200192 uint8_t* current_yuv_pointer = yuv_pointer;
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000193 switch (fourcc_type) {
194 case FOURCC_YUY2: {
195 for (int j = 0; j < height; ++j) {
196 for (int i = 0; i < awidth; i += 2, current_yuv_pointer += 4) {
197 int color1 = ((i / block_size) + (j / block_size)) %
198 kTestingColorNum;
199 int color2 = (((i + 1) / block_size) + (j / block_size)) %
200 kTestingColorNum;
201 current_yuv_pointer[0] = testing_color_y_[color1];
202 if (i < width) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200203 current_yuv_pointer[1] = static_cast<uint8_t>(
204 (static_cast<uint32_t>(testing_color_u_[color1]) +
205 static_cast<uint32_t>(testing_color_u_[color2])) /
206 2);
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000207 current_yuv_pointer[2] = testing_color_y_[color2];
Peter Boström0c4e06b2015-10-07 12:23:21 +0200208 current_yuv_pointer[3] = static_cast<uint8_t>(
209 (static_cast<uint32_t>(testing_color_v_[color1]) +
210 static_cast<uint32_t>(testing_color_v_[color2])) /
211 2);
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000212 } else {
213 current_yuv_pointer[1] = testing_color_u_[color1];
214 current_yuv_pointer[2] = 0;
215 current_yuv_pointer[3] = testing_color_v_[color1];
216 }
217 }
218 }
219 break;
220 }
221 case FOURCC_UYVY: {
222 for (int j = 0; j < height; ++j) {
223 for (int i = 0; i < awidth; i += 2, current_yuv_pointer += 4) {
224 int color1 = ((i / block_size) + (j / block_size)) %
225 kTestingColorNum;
226 int color2 = (((i + 1) / block_size) + (j / block_size)) %
227 kTestingColorNum;
228 if (i < width) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200229 current_yuv_pointer[0] = static_cast<uint8_t>(
230 (static_cast<uint32_t>(testing_color_u_[color1]) +
231 static_cast<uint32_t>(testing_color_u_[color2])) /
232 2);
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000233 current_yuv_pointer[1] = testing_color_y_[color1];
Peter Boström0c4e06b2015-10-07 12:23:21 +0200234 current_yuv_pointer[2] = static_cast<uint8_t>(
235 (static_cast<uint32_t>(testing_color_v_[color1]) +
236 static_cast<uint32_t>(testing_color_v_[color2])) /
237 2);
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000238 current_yuv_pointer[3] = testing_color_y_[color2];
239 } else {
240 current_yuv_pointer[0] = testing_color_u_[color1];
241 current_yuv_pointer[1] = testing_color_y_[color1];
242 current_yuv_pointer[2] = testing_color_v_[color1];
243 current_yuv_pointer[3] = 0;
244 }
245 }
246 }
247 break;
248 }
249 }
250 return image_pointer;
251 }
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000252
253 // Generate a Red-Green-Blue inter-weaving chessboard-like
254 // NV12 testing image.
255 // (Note: No interpolation is used.)
256 // The pattern looks like c0 c1 c2 c3 ...
257 // c1 c2 c3 c4 ...
258 // c2 c3 c4 c5 ...
259 // ...............
260 // The size of each chrome block is (block_size) x (block_size).
Peter Boström0c4e06b2015-10-07 12:23:21 +0200261 uint8_t* CreateFakeNV12TestingImage(int height,
262 int width,
263 int block_size,
264 uint8_t*& y_pointer,
265 uint8_t*& uv_pointer) {
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000266 if (height <= 0 || width <= 0 || block_size <= 0) { return NULL; }
267
Peter Boström0c4e06b2015-10-07 12:23:21 +0200268 uint8_t* image_pointer =
269 new uint8_t[height * width +
270 ((height + 1) / 2) * ((width + 1) / 2) * 2 + kAlignment];
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000271 y_pointer = ALIGNP(image_pointer, kAlignment);
272 uv_pointer = y_pointer + height * width;
Peter Boström0c4e06b2015-10-07 12:23:21 +0200273 uint8_t* current_uv_pointer = uv_pointer;
274 uint8_t* current_y_pointer = y_pointer;
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000275 for (int j = 0; j < height; ++j) {
276 for (int i = 0; i < width; ++i) {
277 int color = ((i / block_size) + (j / block_size)) %
278 kTestingColorNum;
279 *(current_y_pointer++) = testing_color_y_[color];
280 }
281 if (j % 2 == 0) {
282 for (int i = 0; i < width; i += 2, current_uv_pointer += 2) {
283 int color = ((i / block_size) + (j / block_size)) %
284 kTestingColorNum;
285 current_uv_pointer[0] = testing_color_u_[color];
286 current_uv_pointer[1] = testing_color_v_[color];
287 }
288 }
289 }
290 return image_pointer;
291 }
292
293 // Generate a Red-Green-Blue inter-weaving chessboard-like
294 // M420 testing image.
295 // (Note: No interpolation is used.)
296 // The pattern looks like c0 c1 c2 c3 ...
297 // c1 c2 c3 c4 ...
298 // c2 c3 c4 c5 ...
299 // ...............
300 // The size of each chrome block is (block_size) x (block_size).
Peter Boström0c4e06b2015-10-07 12:23:21 +0200301 uint8_t* CreateFakeM420TestingImage(int height,
302 int width,
303 int block_size,
304 uint8_t*& m420_pointer) {
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000305 if (height <= 0 || width <= 0 || block_size <= 0) { return NULL; }
306
Peter Boström0c4e06b2015-10-07 12:23:21 +0200307 uint8_t* image_pointer =
308 new uint8_t[height * width +
309 ((height + 1) / 2) * ((width + 1) / 2) * 2 + kAlignment];
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000310 m420_pointer = ALIGNP(image_pointer, kAlignment);
Peter Boström0c4e06b2015-10-07 12:23:21 +0200311 uint8_t* current_m420_pointer = m420_pointer;
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000312 for (int j = 0; j < height; ++j) {
313 for (int i = 0; i < width; ++i) {
314 int color = ((i / block_size) + (j / block_size)) %
315 kTestingColorNum;
316 *(current_m420_pointer++) = testing_color_y_[color];
317 }
318 if (j % 2 == 1) {
319 for (int i = 0; i < width; i += 2, current_m420_pointer += 2) {
320 int color = ((i / block_size) + ((j - 1) / block_size)) %
321 kTestingColorNum;
322 current_m420_pointer[0] = testing_color_u_[color];
323 current_m420_pointer[1] = testing_color_v_[color];
324 }
325 }
326 }
327 return image_pointer;
328 }
329
330 // Generate a Red-Green-Blue inter-weaving chessboard-like
331 // ARGB/ABGR/RAW/BG24 testing image.
332 // The pattern looks like c0 c1 c2 c3 ...
333 // c1 c2 c3 c4 ...
334 // c2 c3 c4 c5 ...
335 // ...............
336 // The size of each chrome block is (block_size) x (block_size).
Peter Boström0c4e06b2015-10-07 12:23:21 +0200337 uint8_t* CreateFakeArgbTestingImage(int height,
338 int width,
339 int block_size,
340 uint8_t*& argb_pointer,
341 FourCC fourcc_type) {
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000342 if (height <= 0 || width <= 0 || block_size <= 0) { return NULL; }
Peter Boström0c4e06b2015-10-07 12:23:21 +0200343 uint8_t* image_pointer = NULL;
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000344 if (fourcc_type == FOURCC_ABGR || fourcc_type == FOURCC_BGRA ||
345 fourcc_type == FOURCC_ARGB) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200346 image_pointer = new uint8_t[height * width * 4 + kAlignment];
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000347 } else if (fourcc_type == FOURCC_RAW || fourcc_type == FOURCC_24BG) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200348 image_pointer = new uint8_t[height * width * 3 + kAlignment];
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000349 } else {
350 LOG(LS_ERROR) << "Format " << static_cast<int>(fourcc_type)
351 << " is not supported.";
352 return NULL;
353 }
354 argb_pointer = ALIGNP(image_pointer, kAlignment);
Peter Boström0c4e06b2015-10-07 12:23:21 +0200355 uint8_t* current_pointer = argb_pointer;
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000356 switch (fourcc_type) {
357 case FOURCC_ARGB: {
358 for (int j = 0; j < height; ++j) {
359 for (int i = 0; i < width; ++i) {
360 int color = ((i / block_size) + (j / block_size)) %
361 kTestingColorNum;
362 *(current_pointer++) = testing_color_b_[color];
363 *(current_pointer++) = testing_color_g_[color];
364 *(current_pointer++) = testing_color_r_[color];
365 *(current_pointer++) = 255;
366 }
367 }
368 break;
369 }
370 case FOURCC_ABGR: {
371 for (int j = 0; j < height; ++j) {
372 for (int i = 0; i < width; ++i) {
373 int color = ((i / block_size) + (j / block_size)) %
374 kTestingColorNum;
375 *(current_pointer++) = testing_color_r_[color];
376 *(current_pointer++) = testing_color_g_[color];
377 *(current_pointer++) = testing_color_b_[color];
378 *(current_pointer++) = 255;
379 }
380 }
381 break;
382 }
383 case FOURCC_BGRA: {
384 for (int j = 0; j < height; ++j) {
385 for (int i = 0; i < width; ++i) {
386 int color = ((i / block_size) + (j / block_size)) %
387 kTestingColorNum;
388 *(current_pointer++) = 255;
389 *(current_pointer++) = testing_color_r_[color];
390 *(current_pointer++) = testing_color_g_[color];
391 *(current_pointer++) = testing_color_b_[color];
392 }
393 }
394 break;
395 }
396 case FOURCC_24BG: {
397 for (int j = 0; j < height; ++j) {
398 for (int i = 0; i < width; ++i) {
399 int color = ((i / block_size) + (j / block_size)) %
400 kTestingColorNum;
401 *(current_pointer++) = testing_color_b_[color];
402 *(current_pointer++) = testing_color_g_[color];
403 *(current_pointer++) = testing_color_r_[color];
404 }
405 }
406 break;
407 }
408 case FOURCC_RAW: {
409 for (int j = 0; j < height; ++j) {
410 for (int i = 0; i < width; ++i) {
411 int color = ((i / block_size) + (j / block_size)) %
412 kTestingColorNum;
413 *(current_pointer++) = testing_color_r_[color];
414 *(current_pointer++) = testing_color_g_[color];
415 *(current_pointer++) = testing_color_b_[color];
416 }
417 }
418 break;
419 }
420 default: {
421 LOG(LS_ERROR) << "Format " << static_cast<int>(fourcc_type)
422 << " is not supported.";
423 }
424 }
425 return image_pointer;
426 }
427
428 // Check if two memory chunks are equal.
429 // (tolerate MSE errors within a threshold).
Peter Boström0c4e06b2015-10-07 12:23:21 +0200430 static bool IsMemoryEqual(const uint8_t* ibuf,
431 const uint8_t* obuf,
432 int osize,
433 double average_error) {
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000434 double sse = cricket::ComputeSumSquareError(ibuf, obuf, osize);
435 double error = sse / osize; // Mean Squared Error.
436 double PSNR = cricket::ComputePSNR(sse, osize);
437 LOG(LS_INFO) << "Image MSE: " << error << " Image PSNR: " << PSNR
438 << " First Diff Byte: " << FindDiff(ibuf, obuf, osize);
439 return (error < average_error);
440 }
441
442 // Returns the index of the first differing byte. Easier to debug than memcmp.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200443 static int FindDiff(const uint8_t* buf1, const uint8_t* buf2, int len) {
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000444 int i = 0;
445 while (i < len && buf1[i] == buf2[i]) {
446 i++;
447 }
448 return (i < len) ? i : -1;
449 }
450
451 // Dump the result image (ARGB format).
Peter Boström0c4e06b2015-10-07 12:23:21 +0200452 void DumpArgbImage(const uint8_t* obuf, int width, int height) {
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000453 DumpPlanarArgbTestImage(GetTestName(), obuf, width, height);
454 }
455
456 // Dump the result image (YUV420 format).
Peter Boström0c4e06b2015-10-07 12:23:21 +0200457 void DumpYuvImage(const uint8_t* obuf, int width, int height) {
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000458 DumpPlanarYuvTestImage(GetTestName(), obuf, width, height);
459 }
460
461 std::string GetTestName() {
462 const testing::TestInfo* const test_info =
463 testing::UnitTest::GetInstance()->current_test_info();
464 std::string test_name(test_info->name());
465 return test_name;
466 }
467
468 bool dump_;
469 int repeat_;
470
471 // Y, U, V and R, G, B channels of testing colors.
kwiberg31022942016-03-11 14:18:21 -0800472 std::unique_ptr<uint8_t[]> testing_color_y_;
473 std::unique_ptr<uint8_t[]> testing_color_u_;
474 std::unique_ptr<uint8_t[]> testing_color_v_;
475 std::unique_ptr<uint8_t[]> testing_color_r_;
476 std::unique_ptr<uint8_t[]> testing_color_g_;
477 std::unique_ptr<uint8_t[]> testing_color_b_;
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000478};
479
480TEST_F(PlanarFunctionsTest, I420Copy) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200481 uint8_t* y_pointer = nullptr;
482 uint8_t* u_pointer = nullptr;
483 uint8_t* v_pointer = nullptr;
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000484 int y_pitch = kWidth;
485 int u_pitch = (kWidth + 1) >> 1;
486 int v_pitch = (kWidth + 1) >> 1;
487 int y_size = kHeight * kWidth;
488 int uv_size = ((kHeight + 1) >> 1) * ((kWidth + 1) >> 1);
489 int block_size = 3;
490 // Generate a fake input image.
kwiberg31022942016-03-11 14:18:21 -0800491 std::unique_ptr<uint8_t[]> yuv_input(CreateFakeYuvTestingImage(
Peter Boström0c4e06b2015-10-07 12:23:21 +0200492 kHeight, kWidth, block_size, libyuv::kJpegYuv420, y_pointer, u_pointer,
493 v_pointer));
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000494 // Allocate space for the output image.
kwiberg31022942016-03-11 14:18:21 -0800495 std::unique_ptr<uint8_t[]> yuv_output(
Peter Boström0c4e06b2015-10-07 12:23:21 +0200496 new uint8_t[I420_SIZE(kHeight, kWidth) + kAlignment]);
497 uint8_t* y_output_pointer = ALIGNP(yuv_output.get(), kAlignment);
498 uint8_t* u_output_pointer = y_output_pointer + y_size;
499 uint8_t* v_output_pointer = u_output_pointer + uv_size;
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000500
501 for (int i = 0; i < repeat_; ++i) {
502 libyuv::I420Copy(y_pointer, y_pitch,
503 u_pointer, u_pitch,
504 v_pointer, v_pitch,
505 y_output_pointer, y_pitch,
506 u_output_pointer, u_pitch,
507 v_output_pointer, v_pitch,
508 kWidth, kHeight);
509 }
510
511 // Expect the copied frame to be exactly the same.
512 EXPECT_TRUE(IsMemoryEqual(y_output_pointer, y_pointer,
513 I420_SIZE(kHeight, kWidth), 1.e-6));
514
515 if (dump_) { DumpYuvImage(y_output_pointer, kWidth, kHeight); }
516}
517
518TEST_F(PlanarFunctionsTest, I422ToI420) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200519 uint8_t* y_pointer = nullptr;
520 uint8_t* u_pointer = nullptr;
521 uint8_t* v_pointer = nullptr;
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000522 int y_pitch = kWidth;
523 int u_pitch = (kWidth + 1) >> 1;
524 int v_pitch = (kWidth + 1) >> 1;
525 int y_size = kHeight * kWidth;
526 int uv_size = ((kHeight + 1) >> 1) * ((kWidth + 1) >> 1);
527 int block_size = 2;
528 // Generate a fake input image.
kwiberg31022942016-03-11 14:18:21 -0800529 std::unique_ptr<uint8_t[]> yuv_input(CreateFakeYuvTestingImage(
Peter Boström0c4e06b2015-10-07 12:23:21 +0200530 kHeight, kWidth, block_size, libyuv::kJpegYuv422, y_pointer, u_pointer,
531 v_pointer));
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000532 // Allocate space for the output image.
kwiberg31022942016-03-11 14:18:21 -0800533 std::unique_ptr<uint8_t[]> yuv_output(
Peter Boström0c4e06b2015-10-07 12:23:21 +0200534 new uint8_t[I420_SIZE(kHeight, kWidth) + kAlignment]);
535 uint8_t* y_output_pointer = ALIGNP(yuv_output.get(), kAlignment);
536 uint8_t* u_output_pointer = y_output_pointer + y_size;
537 uint8_t* v_output_pointer = u_output_pointer + uv_size;
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000538 // Generate the expected output.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200539 uint8_t* y_expected_pointer = nullptr;
540 uint8_t* u_expected_pointer = nullptr;
541 uint8_t* v_expected_pointer = nullptr;
kwiberg31022942016-03-11 14:18:21 -0800542 std::unique_ptr<uint8_t[]> yuv_output_expected(CreateFakeYuvTestingImage(
Peter Boström0c4e06b2015-10-07 12:23:21 +0200543 kHeight, kWidth, block_size, libyuv::kJpegYuv420, y_expected_pointer,
544 u_expected_pointer, v_expected_pointer));
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000545
546 for (int i = 0; i < repeat_; ++i) {
547 libyuv::I422ToI420(y_pointer, y_pitch,
548 u_pointer, u_pitch,
549 v_pointer, v_pitch,
550 y_output_pointer, y_pitch,
551 u_output_pointer, u_pitch,
552 v_output_pointer, v_pitch,
553 kWidth, kHeight);
554 }
555
556 // Compare the output frame with what is expected; expect exactly the same.
557 // Note: MSE should be set to a larger threshold if an odd block width
558 // is used, since the conversion will be lossy.
559 EXPECT_TRUE(IsMemoryEqual(y_output_pointer, y_expected_pointer,
560 I420_SIZE(kHeight, kWidth), 1.e-6));
561
562 if (dump_) { DumpYuvImage(y_output_pointer, kWidth, kHeight); }
563}
564
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000565TEST_P(PlanarFunctionsTest, M420ToI420) {
566 // Get the unalignment offset
567 int unalignment = GetParam();
Peter Boström0c4e06b2015-10-07 12:23:21 +0200568 uint8_t* m420_pointer = NULL;
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000569 int y_pitch = kWidth;
570 int m420_pitch = kWidth;
571 int u_pitch = (kWidth + 1) >> 1;
572 int v_pitch = (kWidth + 1) >> 1;
573 int y_size = kHeight * kWidth;
574 int uv_size = ((kHeight + 1) >> 1) * ((kWidth + 1) >> 1);
575 int block_size = 2;
576 // Generate a fake input image.
kwiberg31022942016-03-11 14:18:21 -0800577 std::unique_ptr<uint8_t[]> yuv_input(
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000578 CreateFakeM420TestingImage(kHeight, kWidth, block_size, m420_pointer));
579 // Allocate space for the output image.
kwiberg31022942016-03-11 14:18:21 -0800580 std::unique_ptr<uint8_t[]> yuv_output(
Peter Boström0c4e06b2015-10-07 12:23:21 +0200581 new uint8_t[I420_SIZE(kHeight, kWidth) + kAlignment + unalignment]);
582 uint8_t* y_output_pointer =
583 ALIGNP(yuv_output.get(), kAlignment) + unalignment;
584 uint8_t* u_output_pointer = y_output_pointer + y_size;
585 uint8_t* v_output_pointer = u_output_pointer + uv_size;
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000586 // Generate the expected output.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200587 uint8_t* y_expected_pointer = nullptr;
588 uint8_t* u_expected_pointer = nullptr;
589 uint8_t* v_expected_pointer = nullptr;
kwiberg31022942016-03-11 14:18:21 -0800590 std::unique_ptr<uint8_t[]> yuv_output_expected(CreateFakeYuvTestingImage(
Peter Boström0c4e06b2015-10-07 12:23:21 +0200591 kHeight, kWidth, block_size, libyuv::kJpegYuv420, y_expected_pointer,
592 u_expected_pointer, v_expected_pointer));
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000593
594 for (int i = 0; i < repeat_; ++i) {
595 libyuv::M420ToI420(m420_pointer, m420_pitch,
596 y_output_pointer, y_pitch,
597 u_output_pointer, u_pitch,
598 v_output_pointer, v_pitch,
599 kWidth, kHeight);
600 }
601 // Compare the output frame with what is expected; expect exactly the same.
602 // Note: MSE should be set to a larger threshold if an odd block width
603 // is used, since the conversion will be lossy.
604 EXPECT_TRUE(IsMemoryEqual(y_output_pointer, y_expected_pointer,
605 I420_SIZE(kHeight, kWidth), 1.e-6));
606
607 if (dump_) { DumpYuvImage(y_output_pointer, kWidth, kHeight); }
608}
609
610TEST_P(PlanarFunctionsTest, NV12ToI420) {
611 // Get the unalignment offset
612 int unalignment = GetParam();
Peter Boström0c4e06b2015-10-07 12:23:21 +0200613 uint8_t* y_pointer = nullptr;
614 uint8_t* uv_pointer = nullptr;
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000615 int y_pitch = kWidth;
616 int uv_pitch = 2 * ((kWidth + 1) >> 1);
617 int u_pitch = (kWidth + 1) >> 1;
618 int v_pitch = (kWidth + 1) >> 1;
619 int y_size = kHeight * kWidth;
620 int uv_size = ((kHeight + 1) >> 1) * ((kWidth + 1) >> 1);
621 int block_size = 2;
622 // Generate a fake input image.
kwiberg31022942016-03-11 14:18:21 -0800623 std::unique_ptr<uint8_t[]> yuv_input(CreateFakeNV12TestingImage(
Peter Boström0c4e06b2015-10-07 12:23:21 +0200624 kHeight, kWidth, block_size, y_pointer, uv_pointer));
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000625 // Allocate space for the output image.
kwiberg31022942016-03-11 14:18:21 -0800626 std::unique_ptr<uint8_t[]> yuv_output(
Peter Boström0c4e06b2015-10-07 12:23:21 +0200627 new uint8_t[I420_SIZE(kHeight, kWidth) + kAlignment + unalignment]);
628 uint8_t* y_output_pointer =
629 ALIGNP(yuv_output.get(), kAlignment) + unalignment;
630 uint8_t* u_output_pointer = y_output_pointer + y_size;
631 uint8_t* v_output_pointer = u_output_pointer + uv_size;
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000632 // Generate the expected output.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200633 uint8_t* y_expected_pointer = nullptr;
634 uint8_t* u_expected_pointer = nullptr;
635 uint8_t* v_expected_pointer = nullptr;
kwiberg31022942016-03-11 14:18:21 -0800636 std::unique_ptr<uint8_t[]> yuv_output_expected(CreateFakeYuvTestingImage(
Peter Boström0c4e06b2015-10-07 12:23:21 +0200637 kHeight, kWidth, block_size, libyuv::kJpegYuv420, y_expected_pointer,
638 u_expected_pointer, v_expected_pointer));
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000639
640 for (int i = 0; i < repeat_; ++i) {
641 libyuv::NV12ToI420(y_pointer, y_pitch,
642 uv_pointer, uv_pitch,
643 y_output_pointer, y_pitch,
644 u_output_pointer, u_pitch,
645 v_output_pointer, v_pitch,
646 kWidth, kHeight);
647 }
648 // Compare the output frame with what is expected; expect exactly the same.
649 // Note: MSE should be set to a larger threshold if an odd block width
650 // is used, since the conversion will be lossy.
651 EXPECT_TRUE(IsMemoryEqual(y_output_pointer, y_expected_pointer,
652 I420_SIZE(kHeight, kWidth), 1.e-6));
653
654 if (dump_) { DumpYuvImage(y_output_pointer, kWidth, kHeight); }
655}
656
657// A common macro for testing converting YUY2/UYVY to I420.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200658#define TEST_YUVTOI420(SRC_NAME, MSE, BLOCK_SIZE) \
659 TEST_P(PlanarFunctionsTest, SRC_NAME##ToI420) { \
660 /* Get the unalignment offset.*/ \
661 int unalignment = GetParam(); \
662 uint8_t* yuv_pointer = nullptr; \
663 int yuv_pitch = 2 * ((kWidth + 1) & ~1); \
664 int y_pitch = kWidth; \
665 int u_pitch = (kWidth + 1) >> 1; \
666 int v_pitch = (kWidth + 1) >> 1; \
667 int y_size = kHeight * kWidth; \
668 int uv_size = ((kHeight + 1) >> 1) * ((kWidth + 1) >> 1); \
669 int block_size = 2; \
670 /* Generate a fake input image.*/ \
kwiberg31022942016-03-11 14:18:21 -0800671 std::unique_ptr<uint8_t[]> yuv_input(CreateFakeInterleaveYuvTestingImage( \
Peter Boström0c4e06b2015-10-07 12:23:21 +0200672 kHeight, kWidth, BLOCK_SIZE, yuv_pointer, FOURCC_##SRC_NAME)); \
673 /* Allocate space for the output image.*/ \
kwiberg31022942016-03-11 14:18:21 -0800674 std::unique_ptr<uint8_t[]> yuv_output( \
Peter Boström0c4e06b2015-10-07 12:23:21 +0200675 new uint8_t[I420_SIZE(kHeight, kWidth) + kAlignment + unalignment]); \
676 uint8_t* y_output_pointer = \
677 ALIGNP(yuv_output.get(), kAlignment) + unalignment; \
678 uint8_t* u_output_pointer = y_output_pointer + y_size; \
679 uint8_t* v_output_pointer = u_output_pointer + uv_size; \
680 /* Generate the expected output.*/ \
681 uint8_t* y_expected_pointer = nullptr; \
682 uint8_t* u_expected_pointer = nullptr; \
683 uint8_t* v_expected_pointer = nullptr; \
kwiberg31022942016-03-11 14:18:21 -0800684 std::unique_ptr<uint8_t[]> yuv_output_expected(CreateFakeYuvTestingImage( \
Peter Boström0c4e06b2015-10-07 12:23:21 +0200685 kHeight, kWidth, block_size, libyuv::kJpegYuv420, y_expected_pointer, \
686 u_expected_pointer, v_expected_pointer)); \
687 for (int i = 0; i < repeat_; ++i) { \
688 libyuv::SRC_NAME##ToI420(yuv_pointer, yuv_pitch, y_output_pointer, \
689 y_pitch, u_output_pointer, u_pitch, \
690 v_output_pointer, v_pitch, kWidth, kHeight); \
691 } \
692 /* Compare the output frame with what is expected.*/ \
693 /* Note: MSE should be set to a larger threshold if an odd block width*/ \
694 /* is used, since the conversion will be lossy.*/ \
695 EXPECT_TRUE(IsMemoryEqual(y_output_pointer, y_expected_pointer, \
696 I420_SIZE(kHeight, kWidth), MSE)); \
697 if (dump_) { \
698 DumpYuvImage(y_output_pointer, kWidth, kHeight); \
699 } \
700 }
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000701
702// TEST_P(PlanarFunctionsTest, YUV2ToI420)
703TEST_YUVTOI420(YUY2, 1.e-6, 2);
704// TEST_P(PlanarFunctionsTest, UYVYToI420)
705TEST_YUVTOI420(UYVY, 1.e-6, 2);
706
707// A common macro for testing converting I420 to ARGB, BGRA and ABGR.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200708#define TEST_YUVTORGB(SRC_NAME, DST_NAME, JPG_TYPE, MSE, BLOCK_SIZE) \
709 TEST_F(PlanarFunctionsTest, SRC_NAME##To##DST_NAME) { \
710 uint8_t* y_pointer = nullptr; \
711 uint8_t* u_pointer = nullptr; \
712 uint8_t* v_pointer = nullptr; \
713 uint8_t* argb_expected_pointer = NULL; \
714 int y_pitch = kWidth; \
715 int u_pitch = (kWidth + 1) >> 1; \
716 int v_pitch = (kWidth + 1) >> 1; \
717 /* Generate a fake input image.*/ \
kwiberg31022942016-03-11 14:18:21 -0800718 std::unique_ptr<uint8_t[]> yuv_input( \
Peter Boström0c4e06b2015-10-07 12:23:21 +0200719 CreateFakeYuvTestingImage(kHeight, kWidth, BLOCK_SIZE, JPG_TYPE, \
720 y_pointer, u_pointer, v_pointer)); \
721 /* Generate the expected output.*/ \
kwiberg31022942016-03-11 14:18:21 -0800722 std::unique_ptr<uint8_t[]> argb_expected( \
Peter Boström0c4e06b2015-10-07 12:23:21 +0200723 CreateFakeArgbTestingImage(kHeight, kWidth, BLOCK_SIZE, \
724 argb_expected_pointer, FOURCC_##DST_NAME)); \
725 /* Allocate space for the output.*/ \
kwiberg31022942016-03-11 14:18:21 -0800726 std::unique_ptr<uint8_t[]> argb_output( \
Peter Boström0c4e06b2015-10-07 12:23:21 +0200727 new uint8_t[kHeight * kWidth * 4 + kAlignment]); \
728 uint8_t* argb_pointer = ALIGNP(argb_expected.get(), kAlignment); \
729 for (int i = 0; i < repeat_; ++i) { \
730 libyuv::SRC_NAME##To##DST_NAME(y_pointer, y_pitch, u_pointer, u_pitch, \
731 v_pointer, v_pitch, argb_pointer, \
732 kWidth * 4, kWidth, kHeight); \
733 } \
734 EXPECT_TRUE(IsMemoryEqual(argb_expected_pointer, argb_pointer, \
735 kHeight* kWidth * 4, MSE)); \
736 if (dump_) { \
737 DumpArgbImage(argb_pointer, kWidth, kHeight); \
738 } \
739 }
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000740
741// TEST_F(PlanarFunctionsTest, I420ToARGB)
742TEST_YUVTORGB(I420, ARGB, libyuv::kJpegYuv420, 3., 2);
743// TEST_F(PlanarFunctionsTest, I420ToABGR)
744TEST_YUVTORGB(I420, ABGR, libyuv::kJpegYuv420, 3., 2);
745// TEST_F(PlanarFunctionsTest, I420ToBGRA)
746TEST_YUVTORGB(I420, BGRA, libyuv::kJpegYuv420, 3., 2);
747// TEST_F(PlanarFunctionsTest, I422ToARGB)
748TEST_YUVTORGB(I422, ARGB, libyuv::kJpegYuv422, 3., 2);
749// TEST_F(PlanarFunctionsTest, I444ToARGB)
750TEST_YUVTORGB(I444, ARGB, libyuv::kJpegYuv444, 3., 3);
751// Note: an empirical MSE tolerance 3.0 is used here for the probable
Peter Boström0c4e06b2015-10-07 12:23:21 +0200752// error from float-to-uint8_t type conversion.
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000753
754TEST_F(PlanarFunctionsTest, I400ToARGB_Reference) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200755 uint8_t* y_pointer = nullptr;
756 uint8_t* u_pointer = nullptr;
757 uint8_t* v_pointer = nullptr;
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000758 int y_pitch = kWidth;
759 int u_pitch = (kWidth + 1) >> 1;
760 int v_pitch = (kWidth + 1) >> 1;
761 int block_size = 3;
762 // Generate a fake input image.
kwiberg31022942016-03-11 14:18:21 -0800763 std::unique_ptr<uint8_t[]> yuv_input(CreateFakeYuvTestingImage(
Peter Boström0c4e06b2015-10-07 12:23:21 +0200764 kHeight, kWidth, block_size, libyuv::kJpegYuv420, y_pointer, u_pointer,
765 v_pointer));
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000766 // As the comparison standard, we convert a grayscale image (by setting both
767 // U and V channels to be 128) using an I420 converter.
768 int uv_size = ((kHeight + 1) >> 1) * ((kWidth + 1) >> 1);
769
kwiberg31022942016-03-11 14:18:21 -0800770 std::unique_ptr<uint8_t[]> uv(new uint8_t[uv_size + kAlignment]);
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000771 u_pointer = v_pointer = ALIGNP(uv.get(), kAlignment);
772 memset(u_pointer, 128, uv_size);
773
774 // Allocate space for the output image and generate the expected output.
kwiberg31022942016-03-11 14:18:21 -0800775 std::unique_ptr<uint8_t[]> argb_expected(
Peter Boström0c4e06b2015-10-07 12:23:21 +0200776 new uint8_t[kHeight * kWidth * 4 + kAlignment]);
kwiberg31022942016-03-11 14:18:21 -0800777 std::unique_ptr<uint8_t[]> argb_output(
Peter Boström0c4e06b2015-10-07 12:23:21 +0200778 new uint8_t[kHeight * kWidth * 4 + kAlignment]);
779 uint8_t* argb_expected_pointer = ALIGNP(argb_expected.get(), kAlignment);
780 uint8_t* argb_pointer = ALIGNP(argb_output.get(), kAlignment);
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000781
782 libyuv::I420ToARGB(y_pointer, y_pitch,
783 u_pointer, u_pitch,
784 v_pointer, v_pitch,
785 argb_expected_pointer, kWidth * 4,
786 kWidth, kHeight);
787 for (int i = 0; i < repeat_; ++i) {
788 libyuv::I400ToARGB_Reference(y_pointer, y_pitch,
789 argb_pointer, kWidth * 4,
790 kWidth, kHeight);
791 }
792
793 // Note: I420ToARGB and I400ToARGB_Reference should produce identical results.
794 EXPECT_TRUE(IsMemoryEqual(argb_expected_pointer, argb_pointer,
795 kHeight * kWidth * 4, 2.));
796 if (dump_) { DumpArgbImage(argb_pointer, kWidth, kHeight); }
797}
798
799TEST_P(PlanarFunctionsTest, I400ToARGB) {
800 // Get the unalignment offset
801 int unalignment = GetParam();
Peter Boström0c4e06b2015-10-07 12:23:21 +0200802 uint8_t* y_pointer = nullptr;
803 uint8_t* u_pointer = nullptr;
804 uint8_t* v_pointer = nullptr;
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000805 int y_pitch = kWidth;
806 int u_pitch = (kWidth + 1) >> 1;
807 int v_pitch = (kWidth + 1) >> 1;
808 int block_size = 3;
809 // Generate a fake input image.
kwiberg31022942016-03-11 14:18:21 -0800810 std::unique_ptr<uint8_t[]> yuv_input(CreateFakeYuvTestingImage(
Peter Boström0c4e06b2015-10-07 12:23:21 +0200811 kHeight, kWidth, block_size, libyuv::kJpegYuv420, y_pointer, u_pointer,
812 v_pointer));
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000813 // As the comparison standard, we convert a grayscale image (by setting both
814 // U and V channels to be 128) using an I420 converter.
815 int uv_size = ((kHeight + 1) >> 1) * ((kWidth + 1) >> 1);
816
817 // 1 byte extra if in the unaligned mode.
kwiberg31022942016-03-11 14:18:21 -0800818 std::unique_ptr<uint8_t[]> uv(new uint8_t[uv_size * 2 + kAlignment]);
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000819 u_pointer = ALIGNP(uv.get(), kAlignment);
820 v_pointer = u_pointer + uv_size;
821 memset(u_pointer, 128, uv_size);
822 memset(v_pointer, 128, uv_size);
823
824 // Allocate space for the output image and generate the expected output.
kwiberg31022942016-03-11 14:18:21 -0800825 std::unique_ptr<uint8_t[]> argb_expected(
Peter Boström0c4e06b2015-10-07 12:23:21 +0200826 new uint8_t[kHeight * kWidth * 4 + kAlignment]);
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000827 // 1 byte extra if in the misalinged mode.
kwiberg31022942016-03-11 14:18:21 -0800828 std::unique_ptr<uint8_t[]> argb_output(
Peter Boström0c4e06b2015-10-07 12:23:21 +0200829 new uint8_t[kHeight * kWidth * 4 + kAlignment + unalignment]);
830 uint8_t* argb_expected_pointer = ALIGNP(argb_expected.get(), kAlignment);
831 uint8_t* argb_pointer = ALIGNP(argb_output.get(), kAlignment) + unalignment;
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000832
833 libyuv::I420ToARGB(y_pointer, y_pitch,
834 u_pointer, u_pitch,
835 v_pointer, v_pitch,
836 argb_expected_pointer, kWidth * 4,
837 kWidth, kHeight);
838 for (int i = 0; i < repeat_; ++i) {
839 libyuv::I400ToARGB(y_pointer, y_pitch,
840 argb_pointer, kWidth * 4,
841 kWidth, kHeight);
842 }
843
844 // Note: current I400ToARGB uses an approximate method,
845 // so the error tolerance is larger here.
846 EXPECT_TRUE(IsMemoryEqual(argb_expected_pointer, argb_pointer,
847 kHeight * kWidth * 4, 64.0));
848 if (dump_) { DumpArgbImage(argb_pointer, kWidth, kHeight); }
849}
850
851TEST_P(PlanarFunctionsTest, ARGBToI400) {
852 // Get the unalignment offset
853 int unalignment = GetParam();
854 // Create a fake ARGB input image.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200855 uint8_t* y_pointer = NULL, * u_pointer = NULL, * v_pointer = NULL;
856 uint8_t* argb_pointer = NULL;
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000857 int block_size = 3;
858 // Generate a fake input image.
kwiberg31022942016-03-11 14:18:21 -0800859 std::unique_ptr<uint8_t[]> argb_input(CreateFakeArgbTestingImage(
Peter Boström0c4e06b2015-10-07 12:23:21 +0200860 kHeight, kWidth, block_size, argb_pointer, FOURCC_ARGB));
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000861 // Generate the expected output. Only Y channel is used
kwiberg31022942016-03-11 14:18:21 -0800862 std::unique_ptr<uint8_t[]> yuv_expected(CreateFakeYuvTestingImage(
Peter Boström0c4e06b2015-10-07 12:23:21 +0200863 kHeight, kWidth, block_size, libyuv::kJpegYuv420, y_pointer, u_pointer,
864 v_pointer));
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000865 // Allocate space for the Y output.
kwiberg31022942016-03-11 14:18:21 -0800866 std::unique_ptr<uint8_t[]> y_output(
Peter Boström0c4e06b2015-10-07 12:23:21 +0200867 new uint8_t[kHeight * kWidth + kAlignment + unalignment]);
868 uint8_t* y_output_pointer = ALIGNP(y_output.get(), kAlignment) + unalignment;
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000869
870 for (int i = 0; i < repeat_; ++i) {
871 libyuv::ARGBToI400(argb_pointer, kWidth * 4, y_output_pointer, kWidth,
872 kWidth, kHeight);
873 }
874 // Check if the output matches the input Y channel.
875 // Note: an empirical MSE tolerance 2.0 is used here for the probable
Peter Boström0c4e06b2015-10-07 12:23:21 +0200876 // error from float-to-uint8_t type conversion.
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000877 EXPECT_TRUE(IsMemoryEqual(y_output_pointer, y_pointer,
878 kHeight * kWidth, 2.));
879 if (dump_) { DumpArgbImage(argb_pointer, kWidth, kHeight); }
880}
881
882// A common macro for testing converting RAW, BG24, BGRA, and ABGR
883// to ARGB.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200884#define TEST_ARGB(SRC_NAME, FC_ID, BPP, BLOCK_SIZE) \
885 TEST_P(PlanarFunctionsTest, SRC_NAME##ToARGB) { \
886 int unalignment = GetParam(); /* Get the unalignment offset.*/ \
kwiberg31022942016-03-11 14:18:21 -0800887 uint8_t *argb_expected_pointer = NULL, *src_pointer = NULL; \
Peter Boström0c4e06b2015-10-07 12:23:21 +0200888 /* Generate a fake input image.*/ \
kwiberg31022942016-03-11 14:18:21 -0800889 std::unique_ptr<uint8_t[]> src_input(CreateFakeArgbTestingImage( \
Peter Boström0c4e06b2015-10-07 12:23:21 +0200890 kHeight, kWidth, BLOCK_SIZE, src_pointer, FOURCC_##FC_ID)); \
891 /* Generate the expected output.*/ \
kwiberg31022942016-03-11 14:18:21 -0800892 std::unique_ptr<uint8_t[]> argb_expected(CreateFakeArgbTestingImage( \
Peter Boström0c4e06b2015-10-07 12:23:21 +0200893 kHeight, kWidth, BLOCK_SIZE, argb_expected_pointer, FOURCC_ARGB)); \
894 /* Allocate space for the output; 1 byte extra if in the unaligned mode.*/ \
kwiberg31022942016-03-11 14:18:21 -0800895 std::unique_ptr<uint8_t[]> argb_output( \
Peter Boström0c4e06b2015-10-07 12:23:21 +0200896 new uint8_t[kHeight * kWidth * 4 + kAlignment + unalignment]); \
897 uint8_t* argb_pointer = \
898 ALIGNP(argb_output.get(), kAlignment) + unalignment; \
899 for (int i = 0; i < repeat_; ++i) { \
900 libyuv::SRC_NAME##ToARGB(src_pointer, kWidth*(BPP), argb_pointer, \
901 kWidth * 4, kWidth, kHeight); \
902 } \
903 /* Compare the result; expect identical.*/ \
904 EXPECT_TRUE(IsMemoryEqual(argb_expected_pointer, argb_pointer, \
905 kHeight* kWidth * 4, 1.e-6)); \
906 if (dump_) { \
907 DumpArgbImage(argb_pointer, kWidth, kHeight); \
908 } \
909 }
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000910
911TEST_ARGB(RAW, RAW, 3, 3); // TEST_P(PlanarFunctionsTest, RAWToARGB)
912TEST_ARGB(BG24, 24BG, 3, 3); // TEST_P(PlanarFunctionsTest, BG24ToARGB)
913TEST_ARGB(ABGR, ABGR, 4, 3); // TEST_P(PlanarFunctionsTest, ABGRToARGB)
914TEST_ARGB(BGRA, BGRA, 4, 3); // TEST_P(PlanarFunctionsTest, BGRAToARGB)
915
916// Parameter Test: The parameter is the unalignment offset.
917// Aligned data for testing assembly versions.
918INSTANTIATE_TEST_CASE_P(PlanarFunctionsAligned, PlanarFunctionsTest,
919 ::testing::Values(0));
920
921// Purposely unalign the output argb pointer to test slow path (C version).
922INSTANTIATE_TEST_CASE_P(PlanarFunctionsMisaligned, PlanarFunctionsTest,
923 ::testing::Values(1));
924
925} // namespace cricket