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