Home | History | Annotate | Download | only in renderer_host
      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