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