Home | History | Annotate | Download | only in tests
      1 // Copyright (c) 2012 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 "ppapi/tests/test_graphics_2d.h"
      6 
      7 #include <stdlib.h>
      8 #include <string.h>
      9 
     10 #include <set>
     11 
     12 #include "ppapi/c/pp_errors.h"
     13 #include "ppapi/c/ppb_graphics_2d.h"
     14 #include "ppapi/cpp/completion_callback.h"
     15 #include "ppapi/cpp/graphics_2d.h"
     16 #include "ppapi/cpp/graphics_3d.h"
     17 #include "ppapi/cpp/image_data.h"
     18 #include "ppapi/cpp/instance.h"
     19 #include "ppapi/cpp/module.h"
     20 #include "ppapi/cpp/rect.h"
     21 #include "ppapi/tests/test_utils.h"
     22 #include "ppapi/tests/testing_instance.h"
     23 
     24 REGISTER_TEST_CASE(Graphics2D);
     25 
     26 namespace {
     27 
     28 bool CanFlushContext(pp::Instance* instance, pp::Graphics2D* context) {
     29   TestCompletionCallback callback(instance->pp_instance());
     30   callback.WaitForResult(context->Flush(callback.GetCallback()));
     31   return (callback.result() == PP_OK);
     32 }
     33 
     34 bool CanFlushContextC(pp::Instance* instance, PP_Resource graphics_2d,
     35                       const PPB_Graphics2D_1_1* graphics_2d_if) {
     36   TestCompletionCallback callback(instance->pp_instance());
     37   callback.WaitForResult(graphics_2d_if->Flush(
     38       graphics_2d, callback.GetCallback().pp_completion_callback()));
     39   return (callback.result() == PP_OK);
     40 }
     41 
     42 }  // namespace
     43 
     44 TestGraphics2D::TestGraphics2D(TestingInstance* instance)
     45   : TestCase(instance),
     46     is_view_changed_(false),
     47     post_quit_on_view_changed_(false) {
     48 }
     49 
     50 bool TestGraphics2D::Init() {
     51   graphics_2d_interface_ = static_cast<const PPB_Graphics2D*>(
     52       pp::Module::Get()->GetBrowserInterface(PPB_GRAPHICS_2D_INTERFACE_1_1));
     53   image_data_interface_ = static_cast<const PPB_ImageData*>(
     54       pp::Module::Get()->GetBrowserInterface(PPB_IMAGEDATA_INTERFACE_1_0));
     55   return graphics_2d_interface_ && image_data_interface_ &&
     56          CheckTestingInterface();
     57 }
     58 
     59 void TestGraphics2D::RunTests(const std::string& filter) {
     60   RUN_TEST(InvalidResource, filter);
     61   RUN_TEST(InvalidSize, filter);
     62   RUN_TEST(Humongous, filter);
     63   RUN_TEST(InitToZero, filter);
     64   RUN_TEST(Describe, filter);
     65   RUN_TEST(Scale, filter);
     66   RUN_TEST_FORCEASYNC_AND_NOT(Paint, filter);
     67   RUN_TEST_FORCEASYNC_AND_NOT(Scroll, filter);
     68   RUN_TEST_FORCEASYNC_AND_NOT(Replace, filter);
     69   RUN_TEST_FORCEASYNC_AND_NOT(Flush, filter);
     70   RUN_TEST_FORCEASYNC_AND_NOT(FlushOffscreenUpdate, filter);
     71   RUN_TEST(Dev, filter);
     72   RUN_TEST(ReplaceContentsCaching, filter);
     73   RUN_TEST(BindNull, filter);
     74 }
     75 
     76 void TestGraphics2D::QuitMessageLoop() {
     77   testing_interface_->QuitMessageLoop(instance_->pp_instance());
     78 }
     79 
     80 bool TestGraphics2D::ReadImageData(const pp::Graphics2D& dc,
     81                                    pp::ImageData* image,
     82                                    const pp::Point& top_left) const {
     83   return PP_ToBool(testing_interface_->ReadImageData(
     84       dc.pp_resource(),
     85       image->pp_resource(),
     86       &top_left.pp_point()));
     87 }
     88 
     89 bool TestGraphics2D::IsDCUniformColor(const pp::Graphics2D& dc,
     90                                       uint32_t color) const {
     91   pp::ImageData readback(instance_, PP_IMAGEDATAFORMAT_BGRA_PREMUL,
     92                          dc.size(), false);
     93   if (readback.is_null())
     94     return false;
     95   if (!ReadImageData(dc, &readback, pp::Point(0, 0)))
     96     return false;
     97   return IsSquareInImage(readback, 0, pp::Rect(dc.size()), color);
     98 }
     99 
    100 std::string TestGraphics2D::FlushAndWaitForDone(pp::Graphics2D* context) {
    101   TestCompletionCallback callback(instance_->pp_instance(), callback_type());
    102   callback.WaitForResult(context->Flush(callback.GetCallback()));
    103   CHECK_CALLBACK_BEHAVIOR(callback);
    104   ASSERT_EQ(PP_OK, callback.result());
    105   PASS();
    106 }
    107 
    108 void TestGraphics2D::FillRectInImage(pp::ImageData* image,
    109                                      const pp::Rect& rect,
    110                                      uint32_t color) const {
    111   for (int y = rect.y(); y < rect.bottom(); y++) {
    112     uint32_t* row = image->GetAddr32(pp::Point(rect.x(), y));
    113     for (int pixel = 0; pixel < rect.width(); pixel++)
    114       row[pixel] = color;
    115   }
    116 }
    117 
    118 void TestGraphics2D::FillImageWithGradient(pp::ImageData* image) const {
    119   for (int y = 0; y < image->size().height(); y++) {
    120     uint32_t red = ((y * 256) / image->size().height()) & 0xFF;
    121     for (int x = 0; x < image->size().width(); x++) {
    122       uint32_t green = ((x * 256) / image->size().width()) & 0xFF;
    123       uint32_t blue = ((red + green) / 2) & 0xFF;
    124       uint32_t* pixel = image->GetAddr32(pp::Point(x, y));
    125       *pixel = (blue << 24) | (green << 16) | (red << 8);
    126     }
    127   }
    128 }
    129 
    130 bool TestGraphics2D::CompareImages(const pp::ImageData& image1,
    131                                    const pp::ImageData& image2) {
    132   return CompareImageRect(
    133       image1, pp::Rect(0, 0, image1.size().width(), image1.size().height()),
    134       image2, pp::Rect(0, 0, image2.size().width(), image2.size().height()));
    135 }
    136 
    137 bool TestGraphics2D::CompareImageRect(const pp::ImageData& image1,
    138                                       const pp::Rect& rc1,
    139                                       const pp::ImageData& image2,
    140                                       const pp::Rect& rc2) const {
    141   if (rc1.width() != rc2.width() || rc1.height() != rc2.height())
    142     return false;
    143 
    144   for (int y = 0; y < rc1.height(); y++) {
    145     for (int x = 0; x < rc1.width(); x++) {
    146       if (*(image1.GetAddr32(pp::Point(rc1.x() + x, rc1.y() + y))) !=
    147           *(image2.GetAddr32(pp::Point(rc2.x() + x, rc2.y() + y))))
    148         return false;
    149     }
    150   }
    151   return true;
    152 }
    153 
    154 bool TestGraphics2D::IsSquareInImage(const pp::ImageData& image_data,
    155                                      uint32_t background_color,
    156                                      const pp::Rect& square,
    157                                      uint32_t square_color) const {
    158   for (int y = 0; y < image_data.size().height(); y++) {
    159     for (int x = 0; x < image_data.size().width(); x++) {
    160       uint32_t pixel = *image_data.GetAddr32(pp::Point(x, y));
    161       uint32_t desired_color;
    162       if (square.Contains(x, y))
    163         desired_color = square_color;
    164       else
    165         desired_color = background_color;
    166       if (pixel != desired_color)
    167         return false;
    168     }
    169   }
    170   return true;
    171 }
    172 
    173 bool TestGraphics2D::IsSquareInDC(const pp::Graphics2D& dc,
    174                                   uint32_t background_color,
    175                                   const pp::Rect& square,
    176                                   uint32_t square_color) const {
    177   pp::ImageData readback(instance_, PP_IMAGEDATAFORMAT_BGRA_PREMUL,
    178                          dc.size(), false);
    179   if (readback.is_null())
    180     return false;
    181   if (!ReadImageData(dc, &readback, pp::Point(0, 0)))
    182     return false;
    183   return IsSquareInImage(readback, background_color, square, square_color);
    184 }
    185 
    186 
    187 PP_Resource TestGraphics2D::ReplaceContentsAndReturnID(
    188     pp::Graphics2D* dc,
    189     const pp::Size& size) {
    190   pp::ImageData image(instance_, PP_IMAGEDATAFORMAT_BGRA_PREMUL, size, true);
    191 
    192   PP_Resource id = image.pp_resource();
    193 
    194   dc->ReplaceContents(&image);
    195   std::string result = FlushAndWaitForDone(dc);
    196   if (!result.empty())
    197     return 0;
    198 
    199   return id;
    200 }
    201 
    202 // Test all the functions with an invalid handle. Most of these just check for
    203 // a crash since the browser don't return a value.
    204 std::string TestGraphics2D::TestInvalidResource() {
    205   pp::Graphics2D null_context;
    206   pp::ImageData image(instance_, PP_IMAGEDATAFORMAT_BGRA_PREMUL,
    207                       pp::Size(16, 16), true);
    208 
    209   // Describe.
    210   PP_Size size;
    211   PP_Bool opaque;
    212   graphics_2d_interface_->Describe(image.pp_resource(), &size, &opaque);
    213   graphics_2d_interface_->Describe(null_context.pp_resource(),
    214                                    &size, &opaque);
    215 
    216   // PaintImageData.
    217   PP_Point zero_zero;
    218   zero_zero.x = 0;
    219   zero_zero.y = 0;
    220   graphics_2d_interface_->PaintImageData(image.pp_resource(),
    221                                          image.pp_resource(),
    222                                          &zero_zero, NULL);
    223   graphics_2d_interface_->PaintImageData(null_context.pp_resource(),
    224                                          image.pp_resource(),
    225                                          &zero_zero, NULL);
    226 
    227   // Scroll.
    228   PP_Point zero_ten;
    229   zero_ten.x = 0;
    230   zero_ten.y = 10;
    231   graphics_2d_interface_->Scroll(image.pp_resource(), NULL, &zero_ten);
    232   graphics_2d_interface_->Scroll(null_context.pp_resource(),
    233                                  NULL, &zero_ten);
    234 
    235   // ReplaceContents.
    236   graphics_2d_interface_->ReplaceContents(image.pp_resource(),
    237                                           image.pp_resource());
    238   graphics_2d_interface_->ReplaceContents(null_context.pp_resource(),
    239                                           image.pp_resource());
    240 
    241   // Flush.
    242   TestCompletionCallback cb(instance_->pp_instance(), PP_OPTIONAL);
    243   cb.WaitForResult(
    244       graphics_2d_interface_->Flush(image.pp_resource(),
    245                                     cb.GetCallback().pp_completion_callback()));
    246   ASSERT_EQ(PP_ERROR_BADRESOURCE, cb.result());
    247   cb.WaitForResult(
    248       graphics_2d_interface_->Flush(null_context.pp_resource(),
    249                                     cb.GetCallback().pp_completion_callback()));
    250   ASSERT_EQ(PP_ERROR_BADRESOURCE, cb.result());
    251 
    252   // ReadImageData.
    253   ASSERT_FALSE(testing_interface_->ReadImageData(image.pp_resource(),
    254                                                  image.pp_resource(),
    255                                                  &zero_zero));
    256   ASSERT_FALSE(testing_interface_->ReadImageData(null_context.pp_resource(),
    257                                                  image.pp_resource(),
    258                                                  &zero_zero));
    259 
    260   PASS();
    261 }
    262 
    263 std::string TestGraphics2D::TestInvalidSize() {
    264   pp::Graphics2D a(instance_, pp::Size(16, 0), false);
    265   ASSERT_FALSE(CanFlushContext(instance_, &a));
    266 
    267   pp::Graphics2D b(instance_, pp::Size(0, 16), false);
    268   ASSERT_FALSE(CanFlushContext(instance_, &b));
    269 
    270   // Need to use the C API since pp::Size prevents negative sizes.
    271   PP_Size size;
    272   size.width = 16;
    273   size.height = -16;
    274   PP_Resource graphics = graphics_2d_interface_->Create(
    275       instance_->pp_instance(), &size, PP_FALSE);
    276   ASSERT_FALSE(CanFlushContextC(instance_, graphics, graphics_2d_interface_));
    277   pp::Module::Get()->core()->ReleaseResource(graphics);
    278 
    279   size.width = -16;
    280   size.height = 16;
    281   graphics = graphics_2d_interface_->Create(
    282       instance_->pp_instance(), &size, PP_FALSE);
    283   ASSERT_FALSE(CanFlushContextC(instance_, graphics, graphics_2d_interface_));
    284   pp::Module::Get()->core()->ReleaseResource(graphics);
    285 
    286   // Overflow to negative size
    287   size.width = std::numeric_limits<int32_t>::max();
    288   size.height = std::numeric_limits<int32_t>::max();
    289   graphics = graphics_2d_interface_->Create(
    290       instance_->pp_instance(), &size, PP_FALSE);
    291   ASSERT_FALSE(CanFlushContextC(instance_, graphics, graphics_2d_interface_));
    292   pp::Module::Get()->core()->ReleaseResource(graphics);
    293 
    294   PASS();
    295 }
    296 
    297 std::string TestGraphics2D::TestHumongous() {
    298   pp::Graphics2D a(instance_, pp::Size(100000, 100000), false);
    299   ASSERT_FALSE(CanFlushContext(instance_, &a));
    300   PASS();
    301 }
    302 
    303 std::string TestGraphics2D::TestInitToZero() {
    304   const int w = 15, h = 17;
    305   pp::Graphics2D dc(instance_, pp::Size(w, h), false);
    306   ASSERT_FALSE(dc.is_null());
    307 
    308   // Make an image with nonzero data in it (so we can test that zeros were
    309   // actually read versus ReadImageData being a NOP).
    310   pp::ImageData image(instance_, PP_IMAGEDATAFORMAT_BGRA_PREMUL,
    311                       pp::Size(w, h), true);
    312   ASSERT_FALSE(image.is_null());
    313   ASSERT_FALSE(image.size().IsEmpty());
    314   memset(image.data(), 0xFF, image.stride() * image.size().height() * 4);
    315 
    316   // Read out the initial data from the device & check.
    317   ASSERT_TRUE(ReadImageData(dc, &image, pp::Point(0, 0)));
    318   ASSERT_TRUE(IsSquareInImage(image, 0, pp::Rect(0, 0, w, h), 0));
    319 
    320   PASS();
    321 }
    322 
    323 std::string TestGraphics2D::TestDescribe() {
    324   const int w = 15, h = 17;
    325   const bool always_opaque = (::rand() % 2 == 1);
    326   pp::Graphics2D dc(instance_, pp::Size(w, h), always_opaque);
    327   ASSERT_FALSE(dc.is_null());
    328 
    329   PP_Size size;
    330   size.width = -1;
    331   size.height = -1;
    332   PP_Bool is_always_opaque = PP_FALSE;
    333   ASSERT_TRUE(graphics_2d_interface_->Describe(dc.pp_resource(), &size,
    334                                                &is_always_opaque));
    335   ASSERT_EQ(w, size.width);
    336   ASSERT_EQ(h, size.height);
    337   ASSERT_EQ(PP_FromBool(always_opaque), is_always_opaque);
    338 
    339   PASS();
    340 }
    341 
    342 std::string TestGraphics2D::TestScale() {
    343   // Tests GetScale/SetScale
    344   const int w = 20, h = 16;
    345   const float scale = 1.0f/2.0f;
    346   pp::Graphics2D dc(instance_, pp::Size(w, h), false);
    347   ASSERT_FALSE(dc.is_null());
    348   ASSERT_EQ(1.0,  dc.GetScale());
    349   ASSERT_TRUE(dc.SetScale(scale));
    350   ASSERT_EQ(scale, dc.GetScale());
    351   // Try setting a few invalid scale factors. Ensure that we catch these errors
    352   // and don't change the actual scale
    353   ASSERT_FALSE(dc.SetScale(-1.0f));
    354   ASSERT_FALSE(dc.SetScale(0.0f));
    355   ASSERT_EQ(scale, dc.GetScale());
    356 
    357   // Verify that the context has the specified number of pixels, despite the
    358   // non-identity scale
    359   PP_Size size;
    360   size.width = -1;
    361   size.height = -1;
    362   PP_Bool is_always_opaque = PP_FALSE;
    363   ASSERT_TRUE(graphics_2d_interface_->Describe(dc.pp_resource(), &size,
    364                                                &is_always_opaque));
    365   ASSERT_EQ(w, size.width);
    366   ASSERT_EQ(h, size.height);
    367   ASSERT_EQ(PP_FALSE, is_always_opaque);
    368 
    369   PASS();
    370 }
    371 
    372 std::string TestGraphics2D::TestPaint() {
    373   const int w = 15, h = 17;
    374   pp::Graphics2D dc(instance_, pp::Size(w, h), false);
    375   ASSERT_FALSE(dc.is_null());
    376 
    377   // Make sure the device background is 0.
    378   ASSERT_TRUE(IsDCUniformColor(dc, 0));
    379 
    380   // Fill the backing store with white.
    381   const uint32_t background_color = 0xFFFFFFFF;
    382   pp::ImageData background(instance_, PP_IMAGEDATAFORMAT_BGRA_PREMUL,
    383                            pp::Size(w, h), false);
    384   FillRectInImage(&background, pp::Rect(0, 0, w, h), background_color);
    385   dc.PaintImageData(background, pp::Point(0, 0));
    386   ASSERT_SUBTEST_SUCCESS(FlushAndWaitForDone(&dc));
    387 
    388   // Make an image to paint with that's opaque white and enqueue a paint.
    389   const int fill_w = 2, fill_h = 3;
    390   pp::ImageData fill(instance_, PP_IMAGEDATAFORMAT_BGRA_PREMUL,
    391                      pp::Size(fill_w, fill_h), true);
    392   ASSERT_FALSE(fill.is_null());
    393   FillRectInImage(&fill, pp::Rect(fill.size()), background_color);
    394   const int paint_x = 4, paint_y = 5;
    395   dc.PaintImageData(fill, pp::Point(paint_x, paint_y));
    396 
    397   // Validate that nothing has been actually painted.
    398   ASSERT_TRUE(IsDCUniformColor(dc, background_color));
    399 
    400   // The paint hasn't been flushed so we can still change the bitmap. Fill with
    401   // 50% blue. This will also verify that the backing store is replaced
    402   // with the contents rather than blended.
    403   const uint32_t fill_color = 0x80000080;
    404   FillRectInImage(&fill, pp::Rect(fill.size()), fill_color);
    405   ASSERT_SUBTEST_SUCCESS(FlushAndWaitForDone(&dc));
    406 
    407   ASSERT_TRUE(IsSquareInDC(dc, background_color,
    408                            pp::Rect(paint_x, paint_y, fill_w, fill_h),
    409                            fill_color));
    410 
    411   // Reset the DC to blank white & paint our image slightly off the buffer.
    412   // This should succeed. We also try painting the same thing where the
    413   // dirty rect falls outeside of the device, which should fail.
    414   dc.PaintImageData(background, pp::Point(0, 0));
    415   const int second_paint_x = -1, second_paint_y = -2;
    416   dc.PaintImageData(fill, pp::Point(second_paint_x, second_paint_y));
    417   dc.PaintImageData(fill, pp::Point(second_paint_x, second_paint_y),
    418                     pp::Rect(-second_paint_x, -second_paint_y, 1, 1));
    419   ASSERT_SUBTEST_SUCCESS(FlushAndWaitForDone(&dc));
    420 
    421   // Now we should have a little bit of the image peeking out the top left.
    422   ASSERT_TRUE(IsSquareInDC(dc, background_color, pp::Rect(0, 0, 1, 1),
    423                            fill_color));
    424 
    425   // Now repaint that top left pixel by doing a subset of the source image.
    426   pp::ImageData subset(instance_, PP_IMAGEDATAFORMAT_BGRA_PREMUL,
    427                        pp::Size(w, h), false);
    428   uint32_t subset_color = 0x80808080;
    429   const int subset_x = 2, subset_y = 1;
    430   *subset.GetAddr32(pp::Point(subset_x, subset_y)) = subset_color;
    431   dc.PaintImageData(subset, pp::Point(-subset_x, -subset_y),
    432                     pp::Rect(subset_x, subset_y, 1, 1));
    433   ASSERT_SUBTEST_SUCCESS(FlushAndWaitForDone(&dc));
    434   ASSERT_TRUE(IsSquareInDC(dc, background_color, pp::Rect(0, 0, 1, 1),
    435                            subset_color));
    436 
    437   PASS();
    438 }
    439 
    440 std::string TestGraphics2D::TestScroll() {
    441   const int w = 115, h = 117;
    442   pp::Graphics2D dc(instance_, pp::Size(w, h), false);
    443   ASSERT_FALSE(dc.is_null());
    444   ASSERT_TRUE(instance_->BindGraphics(dc));
    445 
    446   // Make sure the device background is 0.
    447   ASSERT_TRUE(IsDCUniformColor(dc, 0));
    448 
    449   const int image_width = 15, image_height = 23;
    450   pp::ImageData test_image(instance_, PP_IMAGEDATAFORMAT_BGRA_PREMUL,
    451                            pp::Size(image_width, image_height), false);
    452   FillImageWithGradient(&test_image);
    453   pp::ImageData no_image(instance_, PP_IMAGEDATAFORMAT_BGRA_PREMUL,
    454                          pp::Size(image_width, image_height), false);
    455   FillRectInImage(&no_image, pp::Rect(0, 0, image_width, image_height), 0);
    456   pp::ImageData readback_image(instance_, PP_IMAGEDATAFORMAT_BGRA_PREMUL,
    457                                pp::Size(image_width, image_height), false);
    458   pp::ImageData readback_scroll(instance_, PP_IMAGEDATAFORMAT_BGRA_PREMUL,
    459                                 pp::Size(image_width, image_height), false);
    460 
    461   ASSERT_EQ(pp::Size(image_width, image_height), test_image.size());
    462 
    463   int image_x = 51, image_y = 72;
    464   dc.PaintImageData(test_image, pp::Point(image_x, image_y));
    465   ASSERT_SUBTEST_SUCCESS(FlushAndWaitForDone(&dc));
    466 
    467   // Test Case 1. Incorrect usage when scrolling image to a free space.
    468   // The clip area is *not* the area to shift around within the graphics device
    469   // by specified amount. It's the area to which the scroll is limited. So if
    470   // the clip area is the size of the image and the amount points to free space,
    471   // the scroll won't result in additional images.
    472   int dx = -40, dy = -48;
    473   int scroll_x = image_x + dx, scroll_y = image_y + dy;
    474   pp::Rect clip(image_x, image_y, image_width, image_height);
    475   dc.Scroll(clip, pp::Point(dx, dy));
    476   ASSERT_SUBTEST_SUCCESS(FlushAndWaitForDone(&dc));
    477   ASSERT_TRUE(
    478       ReadImageData(dc, &readback_scroll, pp::Point(scroll_x, scroll_y)));
    479   ASSERT_TRUE(CompareImages(no_image, readback_scroll));
    480 
    481   // Test Case 2.
    482   // The amount is intended to place the image in the free space outside
    483   // of the original, but the clip area extends beyond the graphics device area.
    484   // This scroll is invalid and will be a noop.
    485   scroll_x = 11, scroll_y = 24;
    486   clip = pp::Rect(0, 0, w, h + 1);
    487   dc.Scroll(clip, pp::Point(scroll_x - image_x, scroll_y - image_y));
    488   ASSERT_SUBTEST_SUCCESS(FlushAndWaitForDone(&dc));
    489   ASSERT_TRUE(
    490       ReadImageData(dc, &readback_scroll, pp::Point(scroll_x, scroll_y)));
    491   ASSERT_TRUE(CompareImages(no_image, readback_scroll));
    492 
    493   // Test Case 3.
    494   // The amount is intended to place the image in the free space outside
    495   // of the original, but the clip area does not cover the image,
    496   // so there is nothing to scroll.
    497   scroll_x = 11, scroll_y = 24;
    498   clip = pp::Rect(0, 0, image_x, image_y);
    499   dc.Scroll(clip, pp::Point(scroll_x - image_x, scroll_y - image_y));
    500   ASSERT_SUBTEST_SUCCESS(FlushAndWaitForDone(&dc));
    501   ASSERT_TRUE(
    502       ReadImageData(dc, &readback_scroll, pp::Point(scroll_x, scroll_y)));
    503   ASSERT_TRUE(CompareImages(no_image, readback_scroll));
    504 
    505   // Test Case 4.
    506   // Same as TC3, but the clip covers part of the image.
    507   // This part will be scrolled to the intended origin.
    508   int part_w = image_width / 2, part_h = image_height / 2;
    509   clip = pp::Rect(0, 0, image_x + part_w, image_y + part_h);
    510   dc.Scroll(clip, pp::Point(scroll_x - image_x, scroll_y - image_y));
    511   ASSERT_SUBTEST_SUCCESS(FlushAndWaitForDone(&dc));
    512   ASSERT_TRUE(
    513       ReadImageData(dc, &readback_scroll, pp::Point(scroll_x, scroll_y)));
    514   ASSERT_FALSE(CompareImages(test_image, readback_scroll));
    515   pp::Rect part_rect(part_w, part_h);
    516   ASSERT_TRUE(
    517       CompareImageRect(test_image, part_rect, readback_scroll, part_rect));
    518 
    519   // Test Case 5
    520   // Same as TC3, but the clip area covers the entire image.
    521   // It will be scrolled to the intended origin.
    522   clip = pp::Rect(0, 0, image_x + image_width, image_y + image_height);
    523   dc.Scroll(clip, pp::Point(scroll_x - image_x, scroll_y - image_y));
    524   ASSERT_SUBTEST_SUCCESS(FlushAndWaitForDone(&dc));
    525   ASSERT_TRUE(
    526       ReadImageData(dc, &readback_scroll, pp::Point(scroll_x, scroll_y)));
    527   ASSERT_TRUE(CompareImages(test_image, readback_scroll));
    528 
    529   // Note that the undefined area left by the scroll does not actually get
    530   // cleared, so the original image is still there. This is not guaranteed and
    531   // is not something for users to rely on, but we can test for this here, so
    532   // we know when the underlying behavior changes.
    533   ASSERT_TRUE(ReadImageData(dc, &readback_image, pp::Point(image_x, image_y)));
    534   ASSERT_TRUE(CompareImages(test_image, readback_image));
    535 
    536   // Test Case 6.
    537   // Scroll image to an overlapping space. The clip area is limited
    538   // to the image, so this will just modify its area.
    539   dx = 6;
    540   dy = 9;
    541   scroll_x = image_x + dx;
    542   scroll_y = image_y + dy;
    543   clip = pp::Rect(image_x, image_y, image_width, image_height);
    544   dc.Scroll(clip, pp::Point(dx, dy));
    545   ASSERT_SUBTEST_SUCCESS(FlushAndWaitForDone(&dc));
    546   ASSERT_TRUE(ReadImageData(dc, &readback_image, pp::Point(image_x, image_y)));
    547   ASSERT_FALSE(CompareImages(test_image, readback_image));
    548   pp::Rect scroll_rect(image_width - dx, image_height - dy);
    549   ASSERT_TRUE(
    550       ReadImageData(dc, &readback_scroll, pp::Point(scroll_x, scroll_y)));
    551   ASSERT_TRUE(
    552       CompareImageRect(test_image, scroll_rect, readback_scroll, scroll_rect));
    553 
    554   PASS();
    555 }
    556 
    557 std::string TestGraphics2D::TestReplace() {
    558   const int w = 15, h = 17;
    559   pp::Graphics2D dc(instance_, pp::Size(w, h), false);
    560   ASSERT_FALSE(dc.is_null());
    561 
    562   // Replacing with a different size image should fail.
    563   pp::ImageData weird_size(instance_, PP_IMAGEDATAFORMAT_BGRA_PREMUL,
    564                            pp::Size(w - 1, h), true);
    565   ASSERT_FALSE(weird_size.is_null());
    566   dc.ReplaceContents(&weird_size);
    567 
    568   // Fill the background with blue but don't flush yet.
    569   const uint32_t background_color = 0xFF0000FF;
    570   pp::ImageData background(instance_, PP_IMAGEDATAFORMAT_BGRA_PREMUL,
    571                            pp::Size(w, h), true);
    572   ASSERT_FALSE(background.is_null());
    573   FillRectInImage(&background, pp::Rect(0, 0, w, h), background_color);
    574   dc.PaintImageData(background, pp::Point(0, 0));
    575 
    576   // Replace with a green background but don't flush yet.
    577   const int32_t swapped_color = 0x00FF00FF;
    578   pp::ImageData swapped(instance_, PP_IMAGEDATAFORMAT_BGRA_PREMUL,
    579                         pp::Size(w, h), true);
    580   ASSERT_FALSE(swapped.is_null());
    581   FillRectInImage(&swapped, pp::Rect(0, 0, w, h), swapped_color);
    582   dc.ReplaceContents(&swapped);
    583 
    584   // The background should be unchanged since we didn't flush yet.
    585   ASSERT_TRUE(IsDCUniformColor(dc, 0));
    586 
    587   // Test the C++ wrapper. The size of the swapped image should be reset.
    588   ASSERT_TRUE(!swapped.pp_resource() && !swapped.size().width() &&
    589               !swapped.size().height() && !swapped.data());
    590 
    591   // Painting with the swapped image should fail.
    592   dc.PaintImageData(swapped, pp::Point(0, 0));
    593 
    594   // Flush and make sure the result is correct.
    595   ASSERT_SUBTEST_SUCCESS(FlushAndWaitForDone(&dc));
    596 
    597   // The background should be green from the swapped image.
    598   ASSERT_TRUE(IsDCUniformColor(dc, swapped_color));
    599 
    600   PASS();
    601 }
    602 
    603 std::string TestGraphics2D::TestFlush() {
    604   // Tests that synchronous flushes (NULL callback) fail on the main thread
    605   // (which is the current one).
    606   const int w = 15, h = 17;
    607   pp::Graphics2D dc(instance_, pp::Size(w, h), false);
    608   ASSERT_FALSE(dc.is_null());
    609 
    610   // Fill the background with blue but don't flush yet.
    611   pp::ImageData background(instance_, PP_IMAGEDATAFORMAT_BGRA_PREMUL,
    612                            pp::Size(w, h), true);
    613   ASSERT_FALSE(background.is_null());
    614   dc.PaintImageData(background, pp::Point(0, 0));
    615 
    616   int32_t rv = dc.Flush(pp::BlockUntilComplete());
    617   ASSERT_EQ(PP_ERROR_BLOCKS_MAIN_THREAD, rv);
    618 
    619   // Test flushing with no operations still issues a callback.
    620   // (This may also hang if the browser never issues the callback).
    621   pp::Graphics2D dc_nopaints(instance_, pp::Size(w, h), false);
    622   ASSERT_FALSE(dc.is_null());
    623   ASSERT_SUBTEST_SUCCESS(FlushAndWaitForDone(&dc_nopaints));
    624 
    625   TestCompletionCallback callback_1(instance_->pp_instance(), callback_type());
    626 
    627   // Test that multiple flushes fail if we don't get a callback in between.
    628   rv = dc_nopaints.Flush(callback_1.GetCallback());
    629   if (rv == PP_OK_COMPLETIONPENDING) {
    630     // If the first flush completes asynchronously, then a second should fail.
    631     TestCompletionCallback callback_2(instance_->pp_instance(),
    632                                       callback_type());
    633     callback_2.WaitForResult(dc_nopaints.Flush(callback_2.GetCallback()));
    634     CHECK_CALLBACK_BEHAVIOR(callback_2);
    635     ASSERT_EQ(PP_ERROR_INPROGRESS, callback_2.result());
    636   }
    637   callback_1.WaitForResult(rv);
    638   ASSERT_EQ(PP_OK, callback_1.result());
    639 
    640   PASS();
    641 }
    642 
    643 void TestGraphics2D::DidChangeView(const pp::View& view) {
    644   if (post_quit_on_view_changed_) {
    645     post_quit_on_view_changed_ = false;
    646     is_view_changed_ = true;
    647     testing_interface_->QuitMessageLoop(instance_->pp_instance());
    648   }
    649 }
    650 
    651 void TestGraphics2D::ResetViewChangedState() {
    652   is_view_changed_ = false;
    653 }
    654 
    655 bool TestGraphics2D::WaitUntilViewChanged() {
    656   // Run a nested message loop. It will exit either on ViewChanged or if the
    657   // timeout happens.
    658 
    659   // If view changed before we have chance to run message loop, return directly.
    660   if (is_view_changed_)
    661     return true;
    662 
    663   post_quit_on_view_changed_ = true;
    664   testing_interface_->RunMessageLoop(instance_->pp_instance());
    665   post_quit_on_view_changed_ = false;
    666 
    667   return is_view_changed_;
    668 }
    669 
    670 std::string TestGraphics2D::TestFlushOffscreenUpdate() {
    671   // Tests that callback of offscreen updates should be delayed.
    672   const PP_Time kFlushDelaySec = 1. / 30;  // 30 fps
    673   const int w = 80, h = 80;
    674   pp::Graphics2D dc(instance_, pp::Size(w, h), true);
    675   ASSERT_FALSE(dc.is_null());
    676   ASSERT_TRUE(instance_->BindGraphics(dc));
    677 
    678   // Squeeze from top until bottom half of plugin is out of screen.
    679   ResetViewChangedState();
    680   instance_->EvalScript(
    681       "var big = document.createElement('div');"
    682       "var offset = "
    683       "    window.innerHeight - plugin.offsetTop - plugin.offsetHeight / 2;"
    684       "big.setAttribute('id', 'big-div');"
    685       "big.setAttribute('style', 'height: ' + offset + '; width: 100%;');"
    686       "document.body.insertBefore(big, document.body.firstChild);");
    687   ASSERT_TRUE(WaitUntilViewChanged());
    688 
    689   // Allocate a red image chunk
    690   pp::ImageData chunk(instance_, PP_IMAGEDATAFORMAT_RGBA_PREMUL,
    691                       pp::Size(w/8, h/8), true);
    692   ASSERT_FALSE(chunk.is_null());
    693   const uint32_t kRed = 0xff0000ff;
    694   FillRectInImage(&chunk, pp::Rect(chunk.size()), kRed);
    695 
    696   // Paint a invisable chunk, expecting Flush to invoke callback slowly.
    697   dc.PaintImageData(chunk, pp::Point(0, h*0.75));
    698 
    699   PP_Time begin = pp::Module::Get()->core()->GetTime();
    700   ASSERT_SUBTEST_SUCCESS(FlushAndWaitForDone(&dc));
    701   PP_Time actual_time_elapsed = pp::Module::Get()->core()->GetTime() - begin;
    702   // Expect actual_time_elapsed >= kFlushDelaySec, but loose a bit to avoid
    703   // precision issue.
    704   ASSERT_GE(actual_time_elapsed, kFlushDelaySec * 0.9);
    705 
    706   // Remove the padding on the top since test cases here isn't independent.
    707   instance_->EvalScript(
    708       "var big = document.getElementById('big-div');"
    709       "big.parentNode.removeChild(big);");
    710   ResetViewChangedState();
    711   ASSERT_TRUE(WaitUntilViewChanged());
    712 
    713   PASS();
    714 }
    715 
    716 std::string TestGraphics2D::TestDev() {
    717   // Tests GetScale/SetScale via the Graphics2D_Dev C++ wrapper
    718   const int w = 20, h = 16;
    719   const float scale = 1.0f/2.0f;
    720   pp::Graphics2D dc(instance_, pp::Size(w, h), false);
    721   ASSERT_FALSE(dc.is_null());
    722   ASSERT_EQ(1.0f, dc.GetScale());
    723   ASSERT_TRUE(dc.SetScale(scale));
    724   ASSERT_EQ(scale, dc.GetScale());
    725   // Try setting a few invalid scale factors. Ensure that we catch these errors
    726   // and don't change the actual scale
    727   ASSERT_FALSE(dc.SetScale(-1.0f));
    728   ASSERT_FALSE(dc.SetScale(0.0f));
    729   ASSERT_EQ(scale, dc.GetScale());
    730 
    731   // Verify that the context has the specified number of pixels, despite the
    732   // non-identity scale
    733   PP_Size size;
    734   size.width = -1;
    735   size.height = -1;
    736   PP_Bool is_always_opaque = PP_FALSE;
    737   ASSERT_TRUE(graphics_2d_interface_->Describe(dc.pp_resource(), &size,
    738                                                &is_always_opaque));
    739   ASSERT_EQ(w, size.width);
    740   ASSERT_EQ(h, size.height);
    741   ASSERT_EQ(PP_FALSE, is_always_opaque);
    742 
    743   PASS();
    744 }
    745 
    746 // This test makes sure that the out-of-process image data caching works as
    747 // expected. Doing ReplaceContents quickly should re-use the image data from
    748 // older ones.
    749 std::string TestGraphics2D::TestReplaceContentsCaching() {
    750   // The cache is only active when running in the proxy, so skip it otherwise.
    751   if (!testing_interface_->IsOutOfProcess())
    752     PASS();
    753 
    754   // Here we test resource IDs as a way to determine if the resource is being
    755   // cached and re-used. This is non-optimal since it's entirely possible
    756   // (and maybe better) for the proxy to return new resource IDs for the
    757   // re-used objects. Howevever, our current implementation does this so it is
    758   // an easy thing to check for.
    759   //
    760   // You could check for the shared memory pointers getting re-used, but the
    761   // OS is very likely to use the same memory location for a newly-mapped image
    762   // if one was deleted, meaning that it could pass even if the cache is broken.
    763   // This would then require that we add some address-re-use-preventing code
    764   // which would be tricky.
    765   std::set<PP_Resource> resources;
    766 
    767   pp::Size size(16, 16);
    768   pp::Graphics2D dc(instance_, size, false);
    769 
    770   // Do two replace contentses, adding the old resource IDs to our map.
    771   PP_Resource imageres = ReplaceContentsAndReturnID(&dc, size);
    772   ASSERT_TRUE(imageres);
    773   resources.insert(imageres);
    774   imageres = ReplaceContentsAndReturnID(&dc, size);
    775   ASSERT_TRUE(imageres);
    776   resources.insert(imageres);
    777 
    778   // Now doing more replace contents should re-use older IDs if the cache is
    779   // working.
    780   imageres = ReplaceContentsAndReturnID(&dc, size);
    781   ASSERT_TRUE(resources.find(imageres) != resources.end());
    782   imageres = ReplaceContentsAndReturnID(&dc, size);
    783   ASSERT_TRUE(resources.find(imageres) != resources.end());
    784 
    785   PASS();
    786 }
    787 
    788 std::string TestGraphics2D::TestBindNull() {
    789   // Binding a null resource is not an error, it should clear all bound
    790   // resources. We can't easily test what resource is bound, but we can test
    791   // that this doesn't throw an error.
    792   ASSERT_TRUE(instance_->BindGraphics(pp::Graphics2D()));
    793   ASSERT_TRUE(instance_->BindGraphics(pp::Graphics3D()));
    794 
    795   const int w = 115, h = 117;
    796   pp::Graphics2D dc(instance_, pp::Size(w, h), false);
    797   ASSERT_FALSE(dc.is_null());
    798   ASSERT_TRUE(instance_->BindGraphics(dc));
    799 
    800   ASSERT_TRUE(instance_->BindGraphics(pp::Graphics2D()));
    801   ASSERT_TRUE(instance_->BindGraphics(pp::Graphics3D()));
    802 
    803   PASS();
    804 }
    805 
    806