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