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