OLD | NEW |
(Empty) | |
| 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_ma
c.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 CHECK(bitmap.allocN32Pixels(size.width(), size.height())); |
| 114 bitmap.eraseColor(SK_ColorGRAY); |
| 115 for (int y = 0; y < size.height(); ++y) { |
| 116 uint32_t* p = bitmap.getAddr32(0, y); |
| 117 for (int x = 0; x < size.width(); ++x, ++p) { |
| 118 if ((x < (size.width() / 2)) && (y < (size.height() / 2))) |
| 119 continue; // Leave upper-left quadrant gray. |
| 120 *p = SkColorSetARGB(255, |
| 121 x % 4 < 2 ? 255 : 0, |
| 122 y % 4 < 2 ? 255 : 0, |
| 123 x % 4 < 2 ? 0 : 255); |
| 124 } |
| 125 } |
| 126 return bitmap; |
| 127 } |
| 128 |
| 129 // Creates a new texture consisting of the given |bitmap|. |
| 130 GLuint CreateTextureWithImage(const SkBitmap& bitmap) { |
| 131 GLuint texture; |
| 132 EXPECT_NO_GL_ERROR(glGenTextures(1, &texture)); |
| 133 EXPECT_NO_GL_ERROR(glBindTexture(kGLTextureTarget, texture)); |
| 134 { |
| 135 SkAutoLockPixels lock_bitmap(bitmap); |
| 136 EXPECT_NO_GL_ERROR(glTexImage2D( |
| 137 kGLTextureTarget, 0, GL_RGBA, bitmap.width(), bitmap.height(), 0, |
| 138 GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, bitmap.getPixels())); |
| 139 } |
| 140 glBindTexture(kGLTextureTarget, 0); |
| 141 return texture; |
| 142 } |
| 143 |
| 144 // Read back a texture from the GPU, returning the image data as an SkBitmap. |
| 145 SkBitmap ReadBackTexture(GLuint texture, const gfx::Size& size, GLenum format) { |
| 146 SkBitmap result; |
| 147 CHECK(result.allocN32Pixels(size.width(), size.height())); |
| 148 |
| 149 GLuint frame_buffer; |
| 150 EXPECT_NO_GL_ERROR(glGenFramebuffersEXT(1, &frame_buffer)); |
| 151 EXPECT_NO_GL_ERROR( |
| 152 glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, frame_buffer)); |
| 153 EXPECT_NO_GL_ERROR(glFramebufferTexture2DEXT( |
| 154 GL_READ_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, kGLTextureTarget, |
| 155 texture, 0)); |
| 156 DCHECK(glCheckFramebufferStatusEXT(GL_READ_FRAMEBUFFER_EXT) == |
| 157 GL_FRAMEBUFFER_COMPLETE_EXT); |
| 158 |
| 159 { |
| 160 SkAutoLockPixels lock_result(result); |
| 161 EXPECT_NO_GL_ERROR(glReadPixels( |
| 162 0, 0, size.width(), size.height(), format, GL_UNSIGNED_INT_8_8_8_8_REV, |
| 163 result.getPixels())); |
| 164 } |
| 165 |
| 166 EXPECT_NO_GL_ERROR(glDeleteFramebuffersEXT(1, &frame_buffer)); |
| 167 |
| 168 return result; |
| 169 } |
| 170 |
| 171 // Returns the |src_rect| region of |src| scaled to |to_size| by drawing on a |
| 172 // Skia canvas, and using bilinear filtering (just like a GPU would). |
| 173 SkBitmap ScaleBitmapWithSkia(const SkBitmap& src, |
| 174 const gfx::Rect& src_rect, |
| 175 const gfx::Size& to_size) { |
| 176 SkBitmap cropped_src; |
| 177 if (src_rect == gfx::Rect(0, 0, src.width(), src.height())) { |
| 178 cropped_src = src; |
| 179 } else { |
| 180 CHECK(src.extractSubset( |
| 181 &cropped_src, |
| 182 SkIRect::MakeXYWH(src_rect.x(), src_rect.y(), |
| 183 src_rect.width(), src_rect.height()))); |
| 184 } |
| 185 |
| 186 SkBitmap result; |
| 187 CHECK(result.allocPixels( |
| 188 cropped_src.info().makeWH(to_size.width(), to_size.height()))); |
| 189 |
| 190 SkCanvas canvas(result); |
| 191 canvas.scale(static_cast<double>(result.width()) / cropped_src.width(), |
| 192 static_cast<double>(result.height()) / cropped_src.height()); |
| 193 SkPaint paint; |
| 194 paint.setFilterLevel(SkPaint::kLow_FilterLevel); // Use bilinear filtering. |
| 195 canvas.drawBitmap(cropped_src, 0, 0, &paint); |
| 196 |
| 197 return result; |
| 198 } |
| 199 |
| 200 // The maximum value by which a pixel value may deviate from the expected value |
| 201 // before considering it "significantly different." This is meant to account |
| 202 // for the slight differences in filtering techniques used between the various |
| 203 // GPUs and software implementations. |
| 204 const int kDifferenceThreshold = 16; |
| 205 |
| 206 // Returns the number of pixels significantly different between |expected| and |
| 207 // |actual|. |
| 208 int ImageDifference(const SkBitmap& expected, const SkBitmap& actual) { |
| 209 SkAutoLockPixels lock_expected(expected); |
| 210 SkAutoLockPixels lock_actual(actual); |
| 211 |
| 212 // Sanity-check assumed image properties. |
| 213 DCHECK_EQ(expected.width(), actual.width()); |
| 214 DCHECK_EQ(expected.height(), actual.height()); |
| 215 DCHECK_EQ(kN32_SkColorType, expected.colorType()); |
| 216 DCHECK_EQ(kN32_SkColorType, actual.colorType()); |
| 217 |
| 218 // Compare both images. |
| 219 int num_pixels_different = 0; |
| 220 for (int y = 0; y < expected.height(); ++y) { |
| 221 const uint32_t* p = expected.getAddr32(0, y); |
| 222 const uint32_t* q = actual.getAddr32(0, y); |
| 223 for (int x = 0; x < expected.width(); ++x, ++p, ++q) { |
| 224 if (abs(static_cast<int>(SkColorGetR(*p)) - |
| 225 static_cast<int>(SkColorGetR(*q))) > kDifferenceThreshold || |
| 226 abs(static_cast<int>(SkColorGetG(*p)) - |
| 227 static_cast<int>(SkColorGetG(*q))) > kDifferenceThreshold || |
| 228 abs(static_cast<int>(SkColorGetB(*p)) - |
| 229 static_cast<int>(SkColorGetB(*q))) > kDifferenceThreshold) { |
| 230 ++num_pixels_different; |
| 231 } |
| 232 } |
| 233 } |
| 234 |
| 235 return num_pixels_different; |
| 236 } |
| 237 |
| 238 // Returns the number of pixels significantly different between |expected| and |
| 239 // |actual|. It is understood that |actual| contains 4-byte quads, and so we |
| 240 // may need to be ignoring a mod-4 number of pixels at the end of each of its |
| 241 // rows. |
| 242 int ImagePlaneDifference(const uint8* expected, const SkBitmap& actual, |
| 243 const gfx::Size& dst_size) { |
| 244 SkAutoLockPixels actual_lock(actual); |
| 245 |
| 246 int num_pixels_different = 0; |
| 247 for (int y = 0; y < dst_size.height(); ++y) { |
| 248 const uint8* p = expected + y * dst_size.width(); |
| 249 const uint8* const p_end = p + dst_size.width(); |
| 250 const uint8* q = |
| 251 reinterpret_cast<uint8*>(actual.getPixels()) + y * actual.rowBytes(); |
| 252 for (; p < p_end; ++p, ++q) { |
| 253 if (abs(static_cast<int>(*p) - static_cast<int>(*q)) > |
| 254 kDifferenceThreshold) { |
| 255 ++num_pixels_different; |
| 256 } |
| 257 } |
| 258 } |
| 259 |
| 260 return num_pixels_different; |
| 261 } |
| 262 |
| 263 } // namespace |
| 264 |
| 265 // Note: All tests fixtures operate within an off-screen OpenGL context. |
| 266 class CompositingIOSurfaceTransformerTest : public testing::Test { |
| 267 public: |
| 268 CompositingIOSurfaceTransformerTest() { |
| 269 // TODO(miu): Try to use RESTRICTION_NONE to speed up the execution time of |
| 270 // unit tests, once it's established that the trybots and buildbots behave |
| 271 // well when using the GPU. |
| 272 CHECK(InitializeGLContext(&context_, RESTRICTION_SOFTWARE_ONLY)); |
| 273 CGLSetCurrentContext(context_); |
| 274 shader_program_cache_.reset(new CompositingIOSurfaceShaderPrograms()); |
| 275 transformer_.reset(new CompositingIOSurfaceTransformer( |
| 276 kGLTextureTarget, false, shader_program_cache_.get())); |
| 277 } |
| 278 |
| 279 virtual ~CompositingIOSurfaceTransformerTest() { |
| 280 transformer_->ReleaseCachedGLObjects(); |
| 281 shader_program_cache_->Reset(); |
| 282 CGLSetCurrentContext(NULL); |
| 283 CGLDestroyContext(context_); |
| 284 } |
| 285 |
| 286 protected: |
| 287 void RunResizeTest(const SkBitmap& src_bitmap, const gfx::Rect& src_rect, |
| 288 const gfx::Size& dst_size) { |
| 289 SCOPED_TRACE(::testing::Message() |
| 290 << "src_rect=(" << src_rect.x() << ',' << src_rect.y() |
| 291 << ")x[" << src_rect.width() << 'x' << src_rect.height() |
| 292 << "]; dst_size=[" << dst_size.width() << 'x' |
| 293 << dst_size.height() << ']'); |
| 294 |
| 295 // Do the scale operation on the GPU. |
| 296 const GLuint original_texture = CreateTextureWithImage(src_bitmap); |
| 297 ASSERT_NE(0u, original_texture); |
| 298 GLuint scaled_texture = 0u; |
| 299 ASSERT_TRUE(transformer_->ResizeBilinear( |
| 300 original_texture, src_rect, dst_size, &scaled_texture)); |
| 301 EXPECT_NE(0u, scaled_texture); |
| 302 CGLFlushDrawable(context_); // Account for some buggy driver impls. |
| 303 const SkBitmap result_bitmap = |
| 304 ReadBackTexture(scaled_texture, dst_size, GL_BGRA); |
| 305 EXPECT_NO_GL_ERROR(glDeleteTextures(1, &original_texture)); |
| 306 |
| 307 // Compare the image read back to the version produced by a known-working |
| 308 // software implementation. Allow up to 2 lines of mismatch due to how |
| 309 // implementations disagree on resolving the processing of edges. |
| 310 const SkBitmap expected_bitmap = |
| 311 ScaleBitmapWithSkia(src_bitmap, src_rect, dst_size); |
| 312 EXPECT_GE(std::max(expected_bitmap.width(), expected_bitmap.height()) * 2, |
| 313 ImageDifference(expected_bitmap, result_bitmap)); |
| 314 } |
| 315 |
| 316 void RunTransformRGBToYV12Test( |
| 317 const SkBitmap& src_bitmap, const gfx::Rect& src_rect, |
| 318 const gfx::Size& dst_size) { |
| 319 SCOPED_TRACE(::testing::Message() |
| 320 << "src_rect=(" << src_rect.x() << ',' << src_rect.y() |
| 321 << ")x[" << src_rect.width() << 'x' << src_rect.height() |
| 322 << "]; dst_size=[" << dst_size.width() << 'x' |
| 323 << dst_size.height() << ']'); |
| 324 |
| 325 // Perform the RGB to YV12 conversion. |
| 326 const GLuint original_texture = CreateTextureWithImage(src_bitmap); |
| 327 ASSERT_NE(0u, original_texture); |
| 328 GLuint texture_y = 0u; |
| 329 GLuint texture_u = 0u; |
| 330 GLuint texture_v = 0u; |
| 331 gfx::Size packed_y_size; |
| 332 gfx::Size packed_uv_size; |
| 333 ASSERT_TRUE(transformer_->TransformRGBToYV12( |
| 334 original_texture, src_rect, dst_size, |
| 335 &texture_y, &texture_u, &texture_v, &packed_y_size, &packed_uv_size)); |
| 336 EXPECT_NE(0u, texture_y); |
| 337 EXPECT_NE(0u, texture_u); |
| 338 EXPECT_NE(0u, texture_v); |
| 339 EXPECT_FALSE(packed_y_size.IsEmpty()); |
| 340 EXPECT_FALSE(packed_uv_size.IsEmpty()); |
| 341 EXPECT_NO_GL_ERROR(glDeleteTextures(1, &original_texture)); |
| 342 |
| 343 // Read-back the texture for each plane. |
| 344 CGLFlushDrawable(context_); // Account for some buggy driver impls. |
| 345 const GLenum format = shader_program_cache_->rgb_to_yv12_output_format(); |
| 346 const SkBitmap result_y_bitmap = |
| 347 ReadBackTexture(texture_y, packed_y_size, format); |
| 348 const SkBitmap result_u_bitmap = |
| 349 ReadBackTexture(texture_u, packed_uv_size, format); |
| 350 const SkBitmap result_v_bitmap = |
| 351 ReadBackTexture(texture_v, packed_uv_size, format); |
| 352 |
| 353 // Compare the Y, U, and V planes read-back to the version produced by a |
| 354 // known-working software implementation. Allow up to 2 lines of mismatch |
| 355 // due to how implementations disagree on resolving the processing of edges. |
| 356 const SkBitmap expected_bitmap = |
| 357 ScaleBitmapWithSkia(src_bitmap, src_rect, dst_size); |
| 358 const gfx::Size dst_uv_size( |
| 359 (dst_size.width() + 1) / 2, (dst_size.height() + 1) / 2); |
| 360 scoped_ptr<uint8[]> expected_y_plane( |
| 361 new uint8[dst_size.width() * dst_size.height()]); |
| 362 scoped_ptr<uint8[]> expected_u_plane( |
| 363 new uint8[dst_uv_size.width() * dst_uv_size.height()]); |
| 364 scoped_ptr<uint8[]> expected_v_plane( |
| 365 new uint8[dst_uv_size.width() * dst_uv_size.height()]); |
| 366 { |
| 367 SkAutoLockPixels src_bitmap_lock(expected_bitmap); |
| 368 media::ConvertRGB32ToYUV( |
| 369 reinterpret_cast<const uint8*>(expected_bitmap.getPixels()), |
| 370 expected_y_plane.get(), expected_u_plane.get(), |
| 371 expected_v_plane.get(), |
| 372 expected_bitmap.width(), expected_bitmap.height(), |
| 373 expected_bitmap.rowBytes(), |
| 374 dst_size.width(), (dst_size.width() + 1) / 2); |
| 375 } |
| 376 EXPECT_GE( |
| 377 std::max(expected_bitmap.width(), expected_bitmap.height()) * 2, |
| 378 ImagePlaneDifference(expected_y_plane.get(), result_y_bitmap, dst_size)) |
| 379 << " for RGB --> Y Plane"; |
| 380 EXPECT_GE( |
| 381 std::max(expected_bitmap.width(), expected_bitmap.height()), |
| 382 ImagePlaneDifference(expected_u_plane.get(), result_u_bitmap, |
| 383 dst_uv_size)) |
| 384 << " for RGB --> U Plane"; |
| 385 EXPECT_GE( |
| 386 std::max(expected_bitmap.width(), expected_bitmap.height()), |
| 387 ImagePlaneDifference(expected_v_plane.get(), result_v_bitmap, |
| 388 dst_uv_size)) |
| 389 << " for RGB --> V Plane"; |
| 390 } |
| 391 |
| 392 CompositingIOSurfaceShaderPrograms* shader_program_cache() const { |
| 393 return shader_program_cache_.get(); |
| 394 } |
| 395 |
| 396 private: |
| 397 CGLContextObj context_; |
| 398 scoped_ptr<CompositingIOSurfaceShaderPrograms> shader_program_cache_; |
| 399 scoped_ptr<CompositingIOSurfaceTransformer> transformer_; |
| 400 |
| 401 private: |
| 402 DISALLOW_COPY_AND_ASSIGN(CompositingIOSurfaceTransformerTest); |
| 403 }; |
| 404 |
| 405 TEST_F(CompositingIOSurfaceTransformerTest, ShaderProgramsCompileAndLink) { |
| 406 // Attempt to use each program, binding its required uniform variables. |
| 407 EXPECT_NO_GL_ERROR(shader_program_cache()->UseBlitProgram()); |
| 408 EXPECT_NO_GL_ERROR(shader_program_cache()->UseSolidWhiteProgram()); |
| 409 EXPECT_NO_GL_ERROR(shader_program_cache()->UseRGBToYV12Program(1, 1.0f)); |
| 410 EXPECT_NO_GL_ERROR(shader_program_cache()->UseRGBToYV12Program(2, 1.0f)); |
| 411 |
| 412 EXPECT_NO_GL_ERROR(glUseProgram(0)); |
| 413 } |
| 414 |
| 415 namespace { |
| 416 |
| 417 const struct TestParameters { |
| 418 int src_width; |
| 419 int src_height; |
| 420 int scaled_width; |
| 421 int scaled_height; |
| 422 } kTestParameters[] = { |
| 423 // Test 1:1 copies, but exposing varying pixel packing configurations. |
| 424 { 64, 64, 64, 64 }, |
| 425 { 63, 63, 63, 63 }, |
| 426 { 62, 62, 62, 62 }, |
| 427 { 61, 61, 61, 61 }, |
| 428 { 60, 60, 60, 60 }, |
| 429 { 59, 59, 59, 59 }, |
| 430 { 58, 58, 58, 58 }, |
| 431 { 57, 57, 57, 57 }, |
| 432 { 56, 56, 56, 56 }, |
| 433 |
| 434 // Even-size, one or both dimensions upscaled. |
| 435 { 32, 32, 64, 32 }, { 32, 32, 32, 64 }, { 32, 32, 64, 64 }, |
| 436 // Even-size, one or both dimensions downscaled by 2X. |
| 437 { 32, 32, 16, 32 }, { 32, 32, 32, 16 }, { 32, 32, 16, 16 }, |
| 438 // Even-size, one or both dimensions downscaled by 1 pixel. |
| 439 { 32, 32, 31, 32 }, { 32, 32, 32, 31 }, { 32, 32, 31, 31 }, |
| 440 // Even-size, one or both dimensions downscaled by 2 pixels. |
| 441 { 32, 32, 30, 32 }, { 32, 32, 32, 30 }, { 32, 32, 30, 30 }, |
| 442 // Even-size, one or both dimensions downscaled by 3 pixels. |
| 443 { 32, 32, 29, 32 }, { 32, 32, 32, 29 }, { 32, 32, 29, 29 }, |
| 444 |
| 445 // Odd-size, one or both dimensions upscaled. |
| 446 { 33, 33, 66, 33 }, { 33, 33, 33, 66 }, { 33, 33, 66, 66 }, |
| 447 // Odd-size, one or both dimensions downscaled by 2X. |
| 448 { 33, 33, 16, 33 }, { 33, 33, 33, 16 }, { 33, 33, 16, 16 }, |
| 449 // Odd-size, one or both dimensions downscaled by 1 pixel. |
| 450 { 33, 33, 32, 33 }, { 33, 33, 33, 32 }, { 33, 33, 32, 32 }, |
| 451 // Odd-size, one or both dimensions downscaled by 2 pixels. |
| 452 { 33, 33, 31, 33 }, { 33, 33, 33, 31 }, { 33, 33, 31, 31 }, |
| 453 // Odd-size, one or both dimensions downscaled by 3 pixels. |
| 454 { 33, 33, 30, 33 }, { 33, 33, 33, 30 }, { 33, 33, 30, 30 }, |
| 455 }; |
| 456 |
| 457 } // namespace |
| 458 |
| 459 TEST_F(CompositingIOSurfaceTransformerTest, ResizesTexturesCorrectly) { |
| 460 for (size_t i = 0; i < arraysize(kTestParameters); ++i) { |
| 461 SCOPED_TRACE(::testing::Message() << "kTestParameters[" << i << ']'); |
| 462 |
| 463 const TestParameters& params = kTestParameters[i]; |
| 464 const gfx::Size src_size(params.src_width, params.src_height); |
| 465 const gfx::Size dst_size(params.scaled_width, params.scaled_height); |
| 466 const SkBitmap src_bitmap = GenerateTestPatternBitmap(src_size); |
| 467 |
| 468 // Full texture resize test. |
| 469 RunResizeTest(src_bitmap, gfx::Rect(src_size), dst_size); |
| 470 // Subrect resize test: missing top row in source. |
| 471 RunResizeTest(src_bitmap, |
| 472 gfx::Rect(0, 1, params.src_width, params.src_height - 1), |
| 473 dst_size); |
| 474 // Subrect resize test: missing left column in source. |
| 475 RunResizeTest(src_bitmap, |
| 476 gfx::Rect(1, 0, params.src_width - 1, params.src_height), |
| 477 dst_size); |
| 478 // Subrect resize test: missing top+bottom rows, and left column in source. |
| 479 RunResizeTest(src_bitmap, |
| 480 gfx::Rect(1, 1, params.src_width - 1, params.src_height - 2), |
| 481 dst_size); |
| 482 // Subrect resize test: missing top row, and left+right columns in source. |
| 483 RunResizeTest(src_bitmap, |
| 484 gfx::Rect(1, 1, params.src_width - 2, params.src_height - 1), |
| 485 dst_size); |
| 486 } |
| 487 } |
| 488 |
| 489 TEST_F(CompositingIOSurfaceTransformerTest, TransformsRGBToYV12) { |
| 490 static const GLenum kOutputFormats[] = { GL_BGRA, GL_RGBA }; |
| 491 |
| 492 for (size_t i = 0; i < arraysize(kOutputFormats); ++i) { |
| 493 SCOPED_TRACE(::testing::Message() << "kOutputFormats[" << i << ']'); |
| 494 |
| 495 shader_program_cache()->SetOutputFormatForTesting(kOutputFormats[i]); |
| 496 |
| 497 for (size_t j = 0; j < arraysize(kTestParameters); ++j) { |
| 498 SCOPED_TRACE(::testing::Message() << "kTestParameters[" << j << ']'); |
| 499 |
| 500 const TestParameters& params = kTestParameters[j]; |
| 501 const gfx::Size src_size(params.src_width, params.src_height); |
| 502 const gfx::Size dst_size(params.scaled_width, params.scaled_height); |
| 503 const SkBitmap src_bitmap = GenerateTestPatternBitmap(src_size); |
| 504 |
| 505 // Full texture resize test. |
| 506 RunTransformRGBToYV12Test(src_bitmap, gfx::Rect(src_size), dst_size); |
| 507 // Subrect resize test: missing top row in source. |
| 508 RunTransformRGBToYV12Test( |
| 509 src_bitmap, gfx::Rect(0, 1, params.src_width, params.src_height - 1), |
| 510 dst_size); |
| 511 // Subrect resize test: missing left column in source. |
| 512 RunTransformRGBToYV12Test( |
| 513 src_bitmap, gfx::Rect(1, 0, params.src_width - 1, params.src_height), |
| 514 dst_size); |
| 515 // Subrect resize test: missing top+bottom rows, and left column in |
| 516 // source. |
| 517 RunTransformRGBToYV12Test( |
| 518 src_bitmap, |
| 519 gfx::Rect(1, 1, params.src_width - 1, params.src_height - 2), |
| 520 dst_size); |
| 521 // Subrect resize test: missing top row, and left+right columns in source. |
| 522 RunTransformRGBToYV12Test( |
| 523 src_bitmap, |
| 524 gfx::Rect(1, 1, params.src_width - 2, params.src_height - 1), |
| 525 dst_size); |
| 526 } |
| 527 } |
| 528 } |
| 529 |
| 530 } // namespace content |
OLD | NEW |