| OLD | NEW |
| (Empty) |
| 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 "webrtc/base/flags.h" | |
| 36 #include "webrtc/base/gunit.h" | |
| 37 #include "webrtc/base/scoped_ptr.h" | |
| 38 #include "webrtc/media/base/testutils.h" | |
| 39 #include "webrtc/media/base/videocommon.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: | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| OLD | NEW |