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