1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "content/browser/renderer_host/compositing_iosurface_transformer_mac.h" 6 7 #include <OpenGL/CGLCurrent.h> 8 #include <OpenGL/CGLRenderers.h> 9 #include <OpenGL/CGLTypes.h> 10 #include <OpenGL/OpenGL.h> 11 #include <OpenGL/gl.h> 12 #include <OpenGL/glu.h> 13 14 #include <algorithm> 15 #include <cstdlib> 16 #include <sstream> 17 #include <vector> 18 19 #include "base/logging.h" 20 #include "base/memory/scoped_ptr.h" 21 #include "content/browser/renderer_host/compositing_iosurface_shader_programs_mac.h" 22 #include "media/base/yuv_convert.h" 23 #include "testing/gtest/include/gtest/gtest.h" 24 #include "third_party/skia/include/core/SkBitmap.h" 25 #include "third_party/skia/include/core/SkCanvas.h" 26 #include "third_party/skia/include/core/SkColor.h" 27 #include "third_party/skia/include/core/SkRect.h" 28 #include "ui/gfx/rect.h" 29 30 namespace content { 31 32 #define EXPECT_NO_GL_ERROR(stmt) \ 33 do { \ 34 stmt; \ 35 const GLenum error_code = glGetError(); \ 36 EXPECT_TRUE(GL_NO_ERROR == error_code) \ 37 << "for error code " << error_code \ 38 << ": " << gluErrorString(error_code); \ 39 } while(0) 40 41 namespace { 42 43 const GLenum kGLTextureTarget = GL_TEXTURE_RECTANGLE_ARB; 44 45 enum RendererRestriction { 46 RESTRICTION_NONE, 47 RESTRICTION_SOFTWARE_ONLY, 48 RESTRICTION_HARDWARE_ONLY 49 }; 50 51 bool InitializeGLContext(CGLContextObj* context, 52 RendererRestriction restriction) { 53 std::vector<CGLPixelFormatAttribute> attribs; 54 // Select off-screen renderers only. 55 attribs.push_back(kCGLPFAOffScreen); 56 // By default, the library will prefer hardware-accelerated renderers, but 57 // falls back on the software ones if necessary. However, there are use cases 58 // where we want to force a restriction (e.g., benchmarking performance). 59 if (restriction == RESTRICTION_SOFTWARE_ONLY) { 60 attribs.push_back(kCGLPFARendererID); 61 attribs.push_back(static_cast<CGLPixelFormatAttribute>( 62 kCGLRendererGenericFloatID)); 63 } else if (restriction == RESTRICTION_HARDWARE_ONLY) { 64 attribs.push_back(kCGLPFAAccelerated); 65 } 66 attribs.push_back(static_cast<CGLPixelFormatAttribute>(0)); 67 68 CGLPixelFormatObj format; 69 GLint num_pixel_formats = 0; 70 bool success = true; 71 if (CGLChoosePixelFormat(&attribs.front(), &format, &num_pixel_formats) != 72 kCGLNoError) { 73 LOG(ERROR) << "Error choosing pixel format."; 74 success = false; 75 } 76 if (success && num_pixel_formats <= 0) { 77 LOG(ERROR) << "num_pixel_formats <= 0; actual value is " 78 << num_pixel_formats; 79 success = false; 80 } 81 if (success && CGLCreateContext(format, NULL, context) != kCGLNoError) { 82 LOG(ERROR) << "Error creating context."; 83 success = false; 84 } 85 CGLDestroyPixelFormat(format); 86 return success; 87 } 88 89 // Returns a decent test pattern for testing all of: 1) orientation, 2) scaling, 90 // 3) color space conversion (e.g., 4 pixels --> one U or V pixel), and 4) 91 // texture alignment/processing. Example 32x32 bitmap: 92 // 93 // GGGGGGGGGGGGGGGGRRBBRRBBRRBBRRBB 94 // GGGGGGGGGGGGGGGGRRBBRRBBRRBBRRBB 95 // GGGGGGGGGGGGGGGGYYCCYYCCYYCCYYCC 96 // GGGGGGGGGGGGGGGGYYCCYYCCYYCCYYCC 97 // GGGGGGGGGGGGGGGGRRBBRRBBRRBBRRBB 98 // GGGGGGGGGGGGGGGGRRBBRRBBRRBBRRBB 99 // GGGGGGGGGGGGGGGGYYCCYYCCYYCCYYCC 100 // GGGGGGGGGGGGGGGGYYCCYYCCYYCCYYCC 101 // RRBBRRBBRRBBRRBBRRBBRRBBRRBBRRBB 102 // RRBBRRBBRRBBRRBBRRBBRRBBRRBBRRBB 103 // YYCCYYCCYYCCYYCCYYCCYYCCYYCCYYCC 104 // YYCCYYCCYYCCYYCCYYCCYYCCYYCCYYCC 105 // RRBBRRBBRRBBRRBBRRBBRRBBRRBBRRBB 106 // RRBBRRBBRRBBRRBBRRBBRRBBRRBBRRBB 107 // YYCCYYCCYYCCYYCCYYCCYYCCYYCCYYCC 108 // YYCCYYCCYYCCYYCCYYCCYYCCYYCCYYCC 109 // 110 // Key: G = Gray, R = Red, B = Blue, Y = Yellow, C = Cyan 111 SkBitmap GenerateTestPatternBitmap(const gfx::Size& size) { 112 SkBitmap bitmap; 113 bitmap.setConfig(SkBitmap::kARGB_8888_Config, size.width(), size.height()); 114 CHECK(bitmap.allocPixels()); 115 SkAutoLockPixels lock_bitmap(bitmap); 116 bitmap.eraseColor(SK_ColorGRAY); 117 for (int y = 0; y < size.height(); ++y) { 118 uint32_t* p = bitmap.getAddr32(0, y); 119 for (int x = 0; x < size.width(); ++x, ++p) { 120 if ((x < (size.width() / 2)) && (y < (size.height() / 2))) 121 continue; // Leave upper-left quadrant gray. 122 *p = SkColorSetARGB(255, 123 x % 4 < 2 ? 255 : 0, 124 y % 4 < 2 ? 255 : 0, 125 x % 4 < 2 ? 0 : 255); 126 } 127 } 128 return bitmap; 129 } 130 131 // Creates a new texture consisting of the given |bitmap|. 132 GLuint CreateTextureWithImage(const SkBitmap& bitmap) { 133 GLuint texture; 134 EXPECT_NO_GL_ERROR(glGenTextures(1, &texture)); 135 EXPECT_NO_GL_ERROR(glBindTexture(kGLTextureTarget, texture)); 136 { 137 SkAutoLockPixels lock_bitmap(bitmap); 138 EXPECT_NO_GL_ERROR(glTexImage2D( 139 kGLTextureTarget, 0, GL_RGBA, bitmap.width(), bitmap.height(), 0, 140 GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, bitmap.getPixels())); 141 } 142 glBindTexture(kGLTextureTarget, 0); 143 return texture; 144 } 145 146 // Read back a texture from the GPU, returning the image data as an SkBitmap. 147 SkBitmap ReadBackTexture(GLuint texture, const gfx::Size& size, GLenum format) { 148 SkBitmap result; 149 result.setConfig(SkBitmap::kARGB_8888_Config, size.width(), size.height()); 150 CHECK(result.allocPixels()); 151 152 GLuint frame_buffer; 153 EXPECT_NO_GL_ERROR(glGenFramebuffersEXT(1, &frame_buffer)); 154 EXPECT_NO_GL_ERROR( 155 glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, frame_buffer)); 156 EXPECT_NO_GL_ERROR(glFramebufferTexture2DEXT( 157 GL_READ_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, kGLTextureTarget, 158 texture, 0)); 159 DCHECK(glCheckFramebufferStatusEXT(GL_READ_FRAMEBUFFER_EXT) == 160 GL_FRAMEBUFFER_COMPLETE_EXT); 161 162 { 163 SkAutoLockPixels lock_result(result); 164 EXPECT_NO_GL_ERROR(glReadPixels( 165 0, 0, size.width(), size.height(), format, GL_UNSIGNED_INT_8_8_8_8_REV, 166 result.getPixels())); 167 } 168 169 EXPECT_NO_GL_ERROR(glDeleteFramebuffersEXT(1, &frame_buffer)); 170 171 return result; 172 } 173 174 // Returns the |src_rect| region of |src| scaled to |to_size| by drawing on a 175 // Skia canvas, and using bilinear filtering (just like a GPU would). 176 SkBitmap ScaleBitmapWithSkia(const SkBitmap& src, 177 const gfx::Rect& src_rect, 178 const gfx::Size& to_size) { 179 SkBitmap cropped_src; 180 if (src_rect == gfx::Rect(0, 0, src.width(), src.height())) { 181 cropped_src = src; 182 } else { 183 CHECK(src.extractSubset( 184 &cropped_src, 185 SkIRect::MakeXYWH(src_rect.x(), src_rect.y(), 186 src_rect.width(), src_rect.height()))); 187 } 188 189 SkBitmap result; 190 result.setConfig(cropped_src.config(), to_size.width(), to_size.height()); 191 CHECK(result.allocPixels()); 192 193 SkCanvas canvas(result); 194 canvas.scale(static_cast<double>(result.width()) / cropped_src.width(), 195 static_cast<double>(result.height()) / cropped_src.height()); 196 SkPaint paint; 197 paint.setFilterBitmap(true); // Use bilinear filtering. 198 canvas.drawBitmap(cropped_src, 0, 0, &paint); 199 200 return result; 201 } 202 203 // The maximum value by which a pixel value may deviate from the expected value 204 // before considering it "significantly different." This is meant to account 205 // for the slight differences in filtering techniques used between the various 206 // GPUs and software implementations. 207 const int kDifferenceThreshold = 16; 208 209 // Returns the number of pixels significantly different between |expected| and 210 // |actual|. 211 int ImageDifference(const SkBitmap& expected, const SkBitmap& actual) { 212 SkAutoLockPixels lock_expected(expected); 213 SkAutoLockPixels lock_actual(actual); 214 215 // Sanity-check assumed image properties. 216 DCHECK_EQ(expected.width(), actual.width()); 217 DCHECK_EQ(expected.height(), actual.height()); 218 DCHECK_EQ(SkBitmap::kARGB_8888_Config, expected.config()); 219 DCHECK_EQ(SkBitmap::kARGB_8888_Config, actual.config()); 220 221 // Compare both images. 222 int num_pixels_different = 0; 223 for (int y = 0; y < expected.height(); ++y) { 224 const uint32_t* p = expected.getAddr32(0, y); 225 const uint32_t* q = actual.getAddr32(0, y); 226 for (int x = 0; x < expected.width(); ++x, ++p, ++q) { 227 if (abs(static_cast<int>(SkColorGetR(*p)) - 228 static_cast<int>(SkColorGetR(*q))) > kDifferenceThreshold || 229 abs(static_cast<int>(SkColorGetG(*p)) - 230 static_cast<int>(SkColorGetG(*q))) > kDifferenceThreshold || 231 abs(static_cast<int>(SkColorGetB(*p)) - 232 static_cast<int>(SkColorGetB(*q))) > kDifferenceThreshold) { 233 ++num_pixels_different; 234 } 235 } 236 } 237 238 return num_pixels_different; 239 } 240 241 // Returns the number of pixels significantly different between |expected| and 242 // |actual|. It is understood that |actual| contains 4-byte quads, and so we 243 // may need to be ignoring a mod-4 number of pixels at the end of each of its 244 // rows. 245 int ImagePlaneDifference(const uint8* expected, const SkBitmap& actual, 246 const gfx::Size& dst_size) { 247 SkAutoLockPixels actual_lock(actual); 248 249 int num_pixels_different = 0; 250 for (int y = 0; y < dst_size.height(); ++y) { 251 const uint8* p = expected + y * dst_size.width(); 252 const uint8* const p_end = p + dst_size.width(); 253 const uint8* q = 254 reinterpret_cast<uint8*>(actual.getPixels()) + y * actual.rowBytes(); 255 for (; p < p_end; ++p, ++q) { 256 if (abs(static_cast<int>(*p) - static_cast<int>(*q)) > 257 kDifferenceThreshold) { 258 ++num_pixels_different; 259 } 260 } 261 } 262 263 return num_pixels_different; 264 } 265 266 } // namespace 267 268 // Note: All tests fixtures operate within an off-screen OpenGL context. 269 class CompositingIOSurfaceTransformerTest : public testing::Test { 270 public: 271 CompositingIOSurfaceTransformerTest() { 272 // TODO(miu): Try to use RESTRICTION_NONE to speed up the execution time of 273 // unit tests, once it's established that the trybots and buildbots behave 274 // well when using the GPU. 275 CHECK(InitializeGLContext(&context_, RESTRICTION_SOFTWARE_ONLY)); 276 CGLSetCurrentContext(context_); 277 shader_program_cache_.reset(new CompositingIOSurfaceShaderPrograms()); 278 transformer_.reset(new CompositingIOSurfaceTransformer( 279 kGLTextureTarget, false, shader_program_cache_.get())); 280 } 281 282 virtual ~CompositingIOSurfaceTransformerTest() { 283 transformer_->ReleaseCachedGLObjects(); 284 shader_program_cache_->Reset(); 285 CGLSetCurrentContext(NULL); 286 CGLDestroyContext(context_); 287 } 288 289 protected: 290 void RunResizeTest(const SkBitmap& src_bitmap, const gfx::Rect& src_rect, 291 const gfx::Size& dst_size) { 292 SCOPED_TRACE(::testing::Message() 293 << "src_rect=(" << src_rect.x() << ',' << src_rect.y() 294 << ")x[" << src_rect.width() << 'x' << src_rect.height() 295 << "]; dst_size=[" << dst_size.width() << 'x' 296 << dst_size.height() << ']'); 297 298 // Do the scale operation on the GPU. 299 const GLuint original_texture = CreateTextureWithImage(src_bitmap); 300 ASSERT_NE(0u, original_texture); 301 GLuint scaled_texture = 0u; 302 ASSERT_TRUE(transformer_->ResizeBilinear( 303 original_texture, src_rect, dst_size, &scaled_texture)); 304 EXPECT_NE(0u, scaled_texture); 305 CGLFlushDrawable(context_); // Account for some buggy driver impls. 306 const SkBitmap result_bitmap = 307 ReadBackTexture(scaled_texture, dst_size, GL_BGRA); 308 EXPECT_NO_GL_ERROR(glDeleteTextures(1, &original_texture)); 309 310 // Compare the image read back to the version produced by a known-working 311 // software implementation. Allow up to 2 lines of mismatch due to how 312 // implementations disagree on resolving the processing of edges. 313 const SkBitmap expected_bitmap = 314 ScaleBitmapWithSkia(src_bitmap, src_rect, dst_size); 315 EXPECT_GE(std::max(expected_bitmap.width(), expected_bitmap.height()) * 2, 316 ImageDifference(expected_bitmap, result_bitmap)); 317 } 318 319 void RunTransformRGBToYV12Test( 320 const SkBitmap& src_bitmap, const gfx::Rect& src_rect, 321 const gfx::Size& dst_size) { 322 SCOPED_TRACE(::testing::Message() 323 << "src_rect=(" << src_rect.x() << ',' << src_rect.y() 324 << ")x[" << src_rect.width() << 'x' << src_rect.height() 325 << "]; dst_size=[" << dst_size.width() << 'x' 326 << dst_size.height() << ']'); 327 328 // Perform the RGB to YV12 conversion. 329 const GLuint original_texture = CreateTextureWithImage(src_bitmap); 330 ASSERT_NE(0u, original_texture); 331 GLuint texture_y = 0u; 332 GLuint texture_u = 0u; 333 GLuint texture_v = 0u; 334 gfx::Size packed_y_size; 335 gfx::Size packed_uv_size; 336 ASSERT_TRUE(transformer_->TransformRGBToYV12( 337 original_texture, src_rect, dst_size, 338 &texture_y, &texture_u, &texture_v, &packed_y_size, &packed_uv_size)); 339 EXPECT_NE(0u, texture_y); 340 EXPECT_NE(0u, texture_u); 341 EXPECT_NE(0u, texture_v); 342 EXPECT_FALSE(packed_y_size.IsEmpty()); 343 EXPECT_FALSE(packed_uv_size.IsEmpty()); 344 EXPECT_NO_GL_ERROR(glDeleteTextures(1, &original_texture)); 345 346 // Read-back the texture for each plane. 347 CGLFlushDrawable(context_); // Account for some buggy driver impls. 348 const GLenum format = shader_program_cache_->rgb_to_yv12_output_format(); 349 const SkBitmap result_y_bitmap = 350 ReadBackTexture(texture_y, packed_y_size, format); 351 const SkBitmap result_u_bitmap = 352 ReadBackTexture(texture_u, packed_uv_size, format); 353 const SkBitmap result_v_bitmap = 354 ReadBackTexture(texture_v, packed_uv_size, format); 355 356 // Compare the Y, U, and V planes read-back to the version produced by a 357 // known-working software implementation. Allow up to 2 lines of mismatch 358 // due to how implementations disagree on resolving the processing of edges. 359 const SkBitmap expected_bitmap = 360 ScaleBitmapWithSkia(src_bitmap, src_rect, dst_size); 361 const gfx::Size dst_uv_size( 362 (dst_size.width() + 1) / 2, (dst_size.height() + 1) / 2); 363 scoped_ptr<uint8[]> expected_y_plane( 364 new uint8[dst_size.width() * dst_size.height()]); 365 scoped_ptr<uint8[]> expected_u_plane( 366 new uint8[dst_uv_size.width() * dst_uv_size.height()]); 367 scoped_ptr<uint8[]> expected_v_plane( 368 new uint8[dst_uv_size.width() * dst_uv_size.height()]); 369 { 370 SkAutoLockPixels src_bitmap_lock(expected_bitmap); 371 media::ConvertRGB32ToYUV( 372 reinterpret_cast<const uint8*>(expected_bitmap.getPixels()), 373 expected_y_plane.get(), expected_u_plane.get(), 374 expected_v_plane.get(), 375 expected_bitmap.width(), expected_bitmap.height(), 376 expected_bitmap.rowBytes(), 377 dst_size.width(), (dst_size.width() + 1) / 2); 378 } 379 EXPECT_GE( 380 std::max(expected_bitmap.width(), expected_bitmap.height()) * 2, 381 ImagePlaneDifference(expected_y_plane.get(), result_y_bitmap, dst_size)) 382 << " for RGB --> Y Plane"; 383 EXPECT_GE( 384 std::max(expected_bitmap.width(), expected_bitmap.height()), 385 ImagePlaneDifference(expected_u_plane.get(), result_u_bitmap, 386 dst_uv_size)) 387 << " for RGB --> U Plane"; 388 EXPECT_GE( 389 std::max(expected_bitmap.width(), expected_bitmap.height()), 390 ImagePlaneDifference(expected_v_plane.get(), result_v_bitmap, 391 dst_uv_size)) 392 << " for RGB --> V Plane"; 393 } 394 395 CompositingIOSurfaceShaderPrograms* shader_program_cache() const { 396 return shader_program_cache_.get(); 397 } 398 399 private: 400 CGLContextObj context_; 401 scoped_ptr<CompositingIOSurfaceShaderPrograms> shader_program_cache_; 402 scoped_ptr<CompositingIOSurfaceTransformer> transformer_; 403 404 private: 405 DISALLOW_COPY_AND_ASSIGN(CompositingIOSurfaceTransformerTest); 406 }; 407 408 TEST_F(CompositingIOSurfaceTransformerTest, ShaderProgramsCompileAndLink) { 409 // Attempt to use each program, binding its required uniform variables. 410 EXPECT_NO_GL_ERROR(shader_program_cache()->UseBlitProgram()); 411 EXPECT_NO_GL_ERROR(shader_program_cache()->UseSolidWhiteProgram()); 412 EXPECT_NO_GL_ERROR(shader_program_cache()->UseRGBToYV12Program(1, 1.0f)); 413 EXPECT_NO_GL_ERROR(shader_program_cache()->UseRGBToYV12Program(2, 1.0f)); 414 415 EXPECT_NO_GL_ERROR(glUseProgram(0)); 416 } 417 418 namespace { 419 420 const struct TestParameters { 421 int src_width; 422 int src_height; 423 int scaled_width; 424 int scaled_height; 425 } kTestParameters[] = { 426 // Test 1:1 copies, but exposing varying pixel packing configurations. 427 { 64, 64, 64, 64 }, 428 { 63, 63, 63, 63 }, 429 { 62, 62, 62, 62 }, 430 { 61, 61, 61, 61 }, 431 { 60, 60, 60, 60 }, 432 { 59, 59, 59, 59 }, 433 { 58, 58, 58, 58 }, 434 { 57, 57, 57, 57 }, 435 { 56, 56, 56, 56 }, 436 437 // Even-size, one or both dimensions upscaled. 438 { 32, 32, 64, 32 }, { 32, 32, 32, 64 }, { 32, 32, 64, 64 }, 439 // Even-size, one or both dimensions downscaled by 2X. 440 { 32, 32, 16, 32 }, { 32, 32, 32, 16 }, { 32, 32, 16, 16 }, 441 // Even-size, one or both dimensions downscaled by 1 pixel. 442 { 32, 32, 31, 32 }, { 32, 32, 32, 31 }, { 32, 32, 31, 31 }, 443 // Even-size, one or both dimensions downscaled by 2 pixels. 444 { 32, 32, 30, 32 }, { 32, 32, 32, 30 }, { 32, 32, 30, 30 }, 445 // Even-size, one or both dimensions downscaled by 3 pixels. 446 { 32, 32, 29, 32 }, { 32, 32, 32, 29 }, { 32, 32, 29, 29 }, 447 448 // Odd-size, one or both dimensions upscaled. 449 { 33, 33, 66, 33 }, { 33, 33, 33, 66 }, { 33, 33, 66, 66 }, 450 // Odd-size, one or both dimensions downscaled by 2X. 451 { 33, 33, 16, 33 }, { 33, 33, 33, 16 }, { 33, 33, 16, 16 }, 452 // Odd-size, one or both dimensions downscaled by 1 pixel. 453 { 33, 33, 32, 33 }, { 33, 33, 33, 32 }, { 33, 33, 32, 32 }, 454 // Odd-size, one or both dimensions downscaled by 2 pixels. 455 { 33, 33, 31, 33 }, { 33, 33, 33, 31 }, { 33, 33, 31, 31 }, 456 // Odd-size, one or both dimensions downscaled by 3 pixels. 457 { 33, 33, 30, 33 }, { 33, 33, 33, 30 }, { 33, 33, 30, 30 }, 458 }; 459 460 } // namespace 461 462 TEST_F(CompositingIOSurfaceTransformerTest, ResizesTexturesCorrectly) { 463 for (size_t i = 0; i < arraysize(kTestParameters); ++i) { 464 SCOPED_TRACE(::testing::Message() << "kTestParameters[" << i << ']'); 465 466 const TestParameters& params = kTestParameters[i]; 467 const gfx::Size src_size(params.src_width, params.src_height); 468 const gfx::Size dst_size(params.scaled_width, params.scaled_height); 469 const SkBitmap src_bitmap = GenerateTestPatternBitmap(src_size); 470 471 // Full texture resize test. 472 RunResizeTest(src_bitmap, gfx::Rect(src_size), dst_size); 473 // Subrect resize test: missing top row in source. 474 RunResizeTest(src_bitmap, 475 gfx::Rect(0, 1, params.src_width, params.src_height - 1), 476 dst_size); 477 // Subrect resize test: missing left column in source. 478 RunResizeTest(src_bitmap, 479 gfx::Rect(1, 0, params.src_width - 1, params.src_height), 480 dst_size); 481 // Subrect resize test: missing top+bottom rows, and left column in source. 482 RunResizeTest(src_bitmap, 483 gfx::Rect(1, 1, params.src_width - 1, params.src_height - 2), 484 dst_size); 485 // Subrect resize test: missing top row, and left+right columns in source. 486 RunResizeTest(src_bitmap, 487 gfx::Rect(1, 1, params.src_width - 2, params.src_height - 1), 488 dst_size); 489 } 490 } 491 492 TEST_F(CompositingIOSurfaceTransformerTest, TransformsRGBToYV12) { 493 static const GLenum kOutputFormats[] = { GL_BGRA, GL_RGBA }; 494 495 for (size_t i = 0; i < arraysize(kOutputFormats); ++i) { 496 SCOPED_TRACE(::testing::Message() << "kOutputFormats[" << i << ']'); 497 498 shader_program_cache()->SetOutputFormatForTesting(kOutputFormats[i]); 499 500 for (size_t j = 0; j < arraysize(kTestParameters); ++j) { 501 SCOPED_TRACE(::testing::Message() << "kTestParameters[" << j << ']'); 502 503 const TestParameters& params = kTestParameters[j]; 504 const gfx::Size src_size(params.src_width, params.src_height); 505 const gfx::Size dst_size(params.scaled_width, params.scaled_height); 506 const SkBitmap src_bitmap = GenerateTestPatternBitmap(src_size); 507 508 // Full texture resize test. 509 RunTransformRGBToYV12Test(src_bitmap, gfx::Rect(src_size), dst_size); 510 // Subrect resize test: missing top row in source. 511 RunTransformRGBToYV12Test( 512 src_bitmap, gfx::Rect(0, 1, params.src_width, params.src_height - 1), 513 dst_size); 514 // Subrect resize test: missing left column in source. 515 RunTransformRGBToYV12Test( 516 src_bitmap, gfx::Rect(1, 0, params.src_width - 1, params.src_height), 517 dst_size); 518 // Subrect resize test: missing top+bottom rows, and left column in 519 // source. 520 RunTransformRGBToYV12Test( 521 src_bitmap, 522 gfx::Rect(1, 1, params.src_width - 1, params.src_height - 2), 523 dst_size); 524 // Subrect resize test: missing top row, and left+right columns in source. 525 RunTransformRGBToYV12Test( 526 src_bitmap, 527 gfx::Rect(1, 1, params.src_width - 2, params.src_height - 1), 528 dst_size); 529 } 530 } 531 } 532 533 } // namespace content 534