Fix build break
[chromium-blink-merge.git] / ppapi / tests / test_graphics_2d.cc
blob7377736b47fdea9069a53e1c0d6e3727f6785587
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.
5 #include "ppapi/tests/test_graphics_2d.h"
7 #include <stdlib.h>
8 #include <string.h>
10 #include <set>
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"
27 REGISTER_TEST_CASE(Graphics2D);
29 namespace {
31 // A NOP flush callback for use in various tests.
32 void FlushCallbackNOP(void* data, int32_t result) {
35 void FlushCallbackQuitMessageLoop(void* data, int32_t result) {
36 static_cast<TestGraphics2D*>(data)->QuitMessageLoop();
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);
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);
53 } // namespace
55 TestGraphics2D::TestGraphics2D(TestingInstance* instance)
56 : TestCase(instance),
57 is_view_changed_(false),
58 post_quit_on_view_changed_(false) {
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();
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);
87 void TestGraphics2D::QuitMessageLoop() {
88 testing_interface_->QuitMessageLoop(instance_->pp_instance());
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()));
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);
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();
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;
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);
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()));
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;
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;
162 return true;
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;
181 return true;
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);
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);
203 PP_Resource id = image.pp_resource();
205 dc->ReplaceContents(&image);
206 std::string result = FlushAndWaitForDone(dc);
207 if (!result.empty())
208 return 0;
210 return id;
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);
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);
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);
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);
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());
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());
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));
271 PASS();
274 std::string TestGraphics2D::TestInvalidSize() {
275 pp::Graphics2D a(instance_, pp::Size(16, 0), false);
276 ASSERT_FALSE(CanFlushContext(instance_, &a));
278 pp::Graphics2D b(instance_, pp::Size(0, 16), false);
279 ASSERT_FALSE(CanFlushContext(instance_, &b));
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);
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);
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);
305 PASS();
308 std::string TestGraphics2D::TestHumongous() {
309 pp::Graphics2D a(instance_, pp::Size(100000, 100000), false);
310 ASSERT_FALSE(CanFlushContext(instance_, &a));
311 PASS();
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());
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 memset(image.data(), 0xFF, image.stride() * image.size().height() * 4);
326 // Read out the initial data from the device & check.
327 ASSERT_TRUE(ReadImageData(dc, &image, pp::Point(0, 0)));
328 ASSERT_TRUE(IsSquareInImage(image, 0, pp::Rect(0, 0, w, h), 0));
330 PASS();
333 std::string TestGraphics2D::TestDescribe() {
334 const int w = 15, h = 17;
335 const bool always_opaque = (::rand() % 2 == 1);
336 pp::Graphics2D dc(instance_, pp::Size(w, h), always_opaque);
337 ASSERT_FALSE(dc.is_null());
339 PP_Size size;
340 size.width = -1;
341 size.height = -1;
342 PP_Bool is_always_opaque = PP_FALSE;
343 ASSERT_TRUE(graphics_2d_interface_->Describe(dc.pp_resource(), &size,
344 &is_always_opaque));
345 ASSERT_EQ(w, size.width);
346 ASSERT_EQ(h, size.height);
347 ASSERT_EQ(PP_FromBool(always_opaque), is_always_opaque);
349 PASS();
352 std::string TestGraphics2D::TestScale() {
353 // Tests GetScale/SetScale
354 const int w = 20, h = 16;
355 const float scale = 1.0f/2.0f;
356 pp::Graphics2D dc(instance_, pp::Size(w, h), false);
357 ASSERT_FALSE(dc.is_null());
358 ASSERT_EQ(1.0, dc.GetScale());
359 ASSERT_TRUE(dc.SetScale(scale));
360 ASSERT_EQ(scale, dc.GetScale());
361 // Try setting a few invalid scale factors. Ensure that we catch these errors
362 // and don't change the actual scale
363 ASSERT_FALSE(dc.SetScale(-1.0f));
364 ASSERT_FALSE(dc.SetScale(0.0f));
365 ASSERT_EQ(scale, dc.GetScale());
367 // Verify that the context has the specified number of pixels, despite the
368 // non-identity scale
369 PP_Size size;
370 size.width = -1;
371 size.height = -1;
372 PP_Bool is_always_opaque = PP_FALSE;
373 ASSERT_TRUE(graphics_2d_interface_->Describe(dc.pp_resource(), &size,
374 &is_always_opaque));
375 ASSERT_EQ(w, size.width);
376 ASSERT_EQ(h, size.height);
377 ASSERT_EQ(PP_FALSE, is_always_opaque);
379 PASS();
382 std::string TestGraphics2D::TestPaint() {
383 const int w = 15, h = 17;
384 pp::Graphics2D dc(instance_, pp::Size(w, h), false);
385 ASSERT_FALSE(dc.is_null());
387 // Make sure the device background is 0.
388 ASSERT_TRUE(IsDCUniformColor(dc, 0));
390 // Fill the backing store with white.
391 const uint32_t background_color = 0xFFFFFFFF;
392 pp::ImageData background(instance_, PP_IMAGEDATAFORMAT_BGRA_PREMUL,
393 pp::Size(w, h), false);
394 FillRectInImage(&background, pp::Rect(0, 0, w, h), background_color);
395 dc.PaintImageData(background, pp::Point(0, 0));
396 ASSERT_SUBTEST_SUCCESS(FlushAndWaitForDone(&dc));
398 // Make an image to paint with that's opaque white and enqueue a paint.
399 const int fill_w = 2, fill_h = 3;
400 pp::ImageData fill(instance_, PP_IMAGEDATAFORMAT_BGRA_PREMUL,
401 pp::Size(fill_w, fill_h), true);
402 ASSERT_FALSE(fill.is_null());
403 FillRectInImage(&fill, pp::Rect(fill.size()), background_color);
404 const int paint_x = 4, paint_y = 5;
405 dc.PaintImageData(fill, pp::Point(paint_x, paint_y));
407 // Validate that nothing has been actually painted.
408 ASSERT_TRUE(IsDCUniformColor(dc, background_color));
410 // The paint hasn't been flushed so we can still change the bitmap. Fill with
411 // 50% blue. This will also verify that the backing store is replaced
412 // with the contents rather than blended.
413 const uint32_t fill_color = 0x80000080;
414 FillRectInImage(&fill, pp::Rect(fill.size()), fill_color);
415 ASSERT_SUBTEST_SUCCESS(FlushAndWaitForDone(&dc));
417 ASSERT_TRUE(IsSquareInDC(dc, background_color,
418 pp::Rect(paint_x, paint_y, fill_w, fill_h),
419 fill_color));
421 // Reset the DC to blank white & paint our image slightly off the buffer.
422 // This should succeed. We also try painting the same thing where the
423 // dirty rect falls outeside of the device, which should fail.
424 dc.PaintImageData(background, pp::Point(0, 0));
425 const int second_paint_x = -1, second_paint_y = -2;
426 dc.PaintImageData(fill, pp::Point(second_paint_x, second_paint_y));
427 dc.PaintImageData(fill, pp::Point(second_paint_x, second_paint_y),
428 pp::Rect(-second_paint_x, -second_paint_y, 1, 1));
429 ASSERT_SUBTEST_SUCCESS(FlushAndWaitForDone(&dc));
431 // Now we should have a little bit of the image peeking out the top left.
432 ASSERT_TRUE(IsSquareInDC(dc, background_color, pp::Rect(0, 0, 1, 1),
433 fill_color));
435 // Now repaint that top left pixel by doing a subset of the source image.
436 pp::ImageData subset(instance_, PP_IMAGEDATAFORMAT_BGRA_PREMUL,
437 pp::Size(w, h), false);
438 uint32_t subset_color = 0x80808080;
439 const int subset_x = 2, subset_y = 1;
440 *subset.GetAddr32(pp::Point(subset_x, subset_y)) = subset_color;
441 dc.PaintImageData(subset, pp::Point(-subset_x, -subset_y),
442 pp::Rect(subset_x, subset_y, 1, 1));
443 ASSERT_SUBTEST_SUCCESS(FlushAndWaitForDone(&dc));
444 ASSERT_TRUE(IsSquareInDC(dc, background_color, pp::Rect(0, 0, 1, 1),
445 subset_color));
447 PASS();
450 std::string TestGraphics2D::TestScroll() {
451 const int w = 115, h = 117;
452 pp::Graphics2D dc(instance_, pp::Size(w, h), false);
453 ASSERT_FALSE(dc.is_null());
454 ASSERT_TRUE(instance_->BindGraphics(dc));
456 // Make sure the device background is 0.
457 ASSERT_TRUE(IsDCUniformColor(dc, 0));
459 const int image_width = 15, image_height = 23;
460 pp::ImageData test_image(instance_, PP_IMAGEDATAFORMAT_BGRA_PREMUL,
461 pp::Size(image_width, image_height), false);
462 FillImageWithGradient(&test_image);
463 pp::ImageData no_image(instance_, PP_IMAGEDATAFORMAT_BGRA_PREMUL,
464 pp::Size(image_width, image_height), false);
465 FillRectInImage(&no_image, pp::Rect(0, 0, image_width, image_height), 0);
466 pp::ImageData readback_image(instance_, PP_IMAGEDATAFORMAT_BGRA_PREMUL,
467 pp::Size(image_width, image_height), false);
468 pp::ImageData readback_scroll(instance_, PP_IMAGEDATAFORMAT_BGRA_PREMUL,
469 pp::Size(image_width, image_height), false);
471 ASSERT_EQ(pp::Size(image_width, image_height), test_image.size());
473 int image_x = 51, image_y = 72;
474 dc.PaintImageData(test_image, pp::Point(image_x, image_y));
475 ASSERT_SUBTEST_SUCCESS(FlushAndWaitForDone(&dc));
477 // Test Case 1. Incorrect usage when scrolling image to a free space.
478 // The clip area is *not* the area to shift around within the graphics device
479 // by specified amount. It's the area to which the scroll is limited. So if
480 // the clip area is the size of the image and the amount points to free space,
481 // the scroll won't result in additional images.
482 int dx = -40, dy = -48;
483 int scroll_x = image_x + dx, scroll_y = image_y + dy;
484 pp::Rect clip(image_x, image_y, image_width, image_height);
485 dc.Scroll(clip, pp::Point(dx, dy));
486 ASSERT_SUBTEST_SUCCESS(FlushAndWaitForDone(&dc));
487 ASSERT_TRUE(
488 ReadImageData(dc, &readback_scroll, pp::Point(scroll_x, scroll_y)));
489 ASSERT_TRUE(CompareImages(no_image, readback_scroll));
491 // Test Case 2.
492 // The amount is intended to place the image in the free space outside
493 // of the original, but the clip area extends beyond the graphics device area.
494 // This scroll is invalid and will be a noop.
495 scroll_x = 11, scroll_y = 24;
496 clip = pp::Rect(0, 0, w, h + 1);
497 dc.Scroll(clip, pp::Point(scroll_x - image_x, scroll_y - image_y));
498 ASSERT_SUBTEST_SUCCESS(FlushAndWaitForDone(&dc));
499 ASSERT_TRUE(
500 ReadImageData(dc, &readback_scroll, pp::Point(scroll_x, scroll_y)));
501 ASSERT_TRUE(CompareImages(no_image, readback_scroll));
503 // Test Case 3.
504 // The amount is intended to place the image in the free space outside
505 // of the original, but the clip area does not cover the image,
506 // so there is nothing to scroll.
507 scroll_x = 11, scroll_y = 24;
508 clip = pp::Rect(0, 0, image_x, image_y);
509 dc.Scroll(clip, pp::Point(scroll_x - image_x, scroll_y - image_y));
510 ASSERT_SUBTEST_SUCCESS(FlushAndWaitForDone(&dc));
511 ASSERT_TRUE(
512 ReadImageData(dc, &readback_scroll, pp::Point(scroll_x, scroll_y)));
513 ASSERT_TRUE(CompareImages(no_image, readback_scroll));
515 // Test Case 4.
516 // Same as TC3, but the clip covers part of the image.
517 // This part will be scrolled to the intended origin.
518 int part_w = image_width / 2, part_h = image_height / 2;
519 clip = pp::Rect(0, 0, image_x + part_w, image_y + part_h);
520 dc.Scroll(clip, pp::Point(scroll_x - image_x, scroll_y - image_y));
521 ASSERT_SUBTEST_SUCCESS(FlushAndWaitForDone(&dc));
522 ASSERT_TRUE(
523 ReadImageData(dc, &readback_scroll, pp::Point(scroll_x, scroll_y)));
524 ASSERT_FALSE(CompareImages(test_image, readback_scroll));
525 pp::Rect part_rect(part_w, part_h);
526 ASSERT_TRUE(
527 CompareImageRect(test_image, part_rect, readback_scroll, part_rect));
529 // Test Case 5
530 // Same as TC3, but the clip area covers the entire image.
531 // It will be scrolled to the intended origin.
532 clip = pp::Rect(0, 0, image_x + image_width, image_y + image_height);
533 dc.Scroll(clip, pp::Point(scroll_x - image_x, scroll_y - image_y));
534 ASSERT_SUBTEST_SUCCESS(FlushAndWaitForDone(&dc));
535 ASSERT_TRUE(
536 ReadImageData(dc, &readback_scroll, pp::Point(scroll_x, scroll_y)));
537 ASSERT_TRUE(CompareImages(test_image, readback_scroll));
539 // Note that the undefined area left by the scroll does not actually get
540 // cleared, so the original image is still there. This is not guaranteed and
541 // is not something for users to rely on, but we can test for this here, so
542 // we know when the underlying behavior changes.
543 ASSERT_TRUE(ReadImageData(dc, &readback_image, pp::Point(image_x, image_y)));
544 ASSERT_TRUE(CompareImages(test_image, readback_image));
546 // Test Case 6.
547 // Scroll image to an overlapping space. The clip area is limited
548 // to the image, so this will just modify its area.
549 dx = 6;
550 dy = 9;
551 scroll_x = image_x + dx;
552 scroll_y = image_y + dy;
553 clip = pp::Rect(image_x, image_y, image_width, image_height);
554 dc.Scroll(clip, pp::Point(dx, dy));
555 ASSERT_SUBTEST_SUCCESS(FlushAndWaitForDone(&dc));
556 ASSERT_TRUE(ReadImageData(dc, &readback_image, pp::Point(image_x, image_y)));
557 ASSERT_FALSE(CompareImages(test_image, readback_image));
558 pp::Rect scroll_rect(image_width - dx, image_height - dy);
559 ASSERT_TRUE(
560 ReadImageData(dc, &readback_scroll, pp::Point(scroll_x, scroll_y)));
561 ASSERT_TRUE(
562 CompareImageRect(test_image, scroll_rect, readback_scroll, scroll_rect));
564 PASS();
567 std::string TestGraphics2D::TestReplace() {
568 const int w = 15, h = 17;
569 pp::Graphics2D dc(instance_, pp::Size(w, h), false);
570 ASSERT_FALSE(dc.is_null());
572 // Replacing with a different size image should fail.
573 pp::ImageData weird_size(instance_, PP_IMAGEDATAFORMAT_BGRA_PREMUL,
574 pp::Size(w - 1, h), true);
575 ASSERT_FALSE(weird_size.is_null());
576 dc.ReplaceContents(&weird_size);
578 // Fill the background with blue but don't flush yet.
579 const int32_t background_color = 0xFF0000FF;
580 pp::ImageData background(instance_, PP_IMAGEDATAFORMAT_BGRA_PREMUL,
581 pp::Size(w, h), true);
582 ASSERT_FALSE(background.is_null());
583 FillRectInImage(&background, pp::Rect(0, 0, w, h), background_color);
584 dc.PaintImageData(background, pp::Point(0, 0));
586 // Replace with a green background but don't flush yet.
587 const int32_t swapped_color = 0x00FF00FF;
588 pp::ImageData swapped(instance_, PP_IMAGEDATAFORMAT_BGRA_PREMUL,
589 pp::Size(w, h), true);
590 ASSERT_FALSE(swapped.is_null());
591 FillRectInImage(&swapped, pp::Rect(0, 0, w, h), swapped_color);
592 dc.ReplaceContents(&swapped);
594 // The background should be unchanged since we didn't flush yet.
595 ASSERT_TRUE(IsDCUniformColor(dc, 0));
597 // Test the C++ wrapper. The size of the swapped image should be reset.
598 ASSERT_TRUE(!swapped.pp_resource() && !swapped.size().width() &&
599 !swapped.size().height() && !swapped.data());
601 // Painting with the swapped image should fail.
602 dc.PaintImageData(swapped, pp::Point(0, 0));
604 // Flush and make sure the result is correct.
605 ASSERT_SUBTEST_SUCCESS(FlushAndWaitForDone(&dc));
607 // The background should be green from the swapped image.
608 ASSERT_TRUE(IsDCUniformColor(dc, swapped_color));
610 PASS();
613 std::string TestGraphics2D::TestFlush() {
614 // Tests that synchronous flushes (NULL callback) fail on the main thread
615 // (which is the current one).
616 const int w = 15, h = 17;
617 pp::Graphics2D dc(instance_, pp::Size(w, h), false);
618 ASSERT_FALSE(dc.is_null());
620 // Fill the background with blue but don't flush yet.
621 pp::ImageData background(instance_, PP_IMAGEDATAFORMAT_BGRA_PREMUL,
622 pp::Size(w, h), true);
623 ASSERT_FALSE(background.is_null());
624 dc.PaintImageData(background, pp::Point(0, 0));
626 int32_t rv = dc.Flush(pp::BlockUntilComplete());
627 ASSERT_EQ(PP_ERROR_BLOCKS_MAIN_THREAD, rv);
629 // Test flushing with no operations still issues a callback.
630 // (This may also hang if the browser never issues the callback).
631 pp::Graphics2D dc_nopaints(instance_, pp::Size(w, h), false);
632 ASSERT_FALSE(dc.is_null());
633 ASSERT_SUBTEST_SUCCESS(FlushAndWaitForDone(&dc_nopaints));
635 TestCompletionCallback callback_1(instance_->pp_instance(), callback_type());
637 // Test that multiple flushes fail if we don't get a callback in between.
638 rv = dc_nopaints.Flush(callback_1.GetCallback());
639 if (rv == PP_OK_COMPLETIONPENDING) {
640 // If the first flush completes asynchronously, then a second should fail.
641 TestCompletionCallback callback_2(instance_->pp_instance(),
642 callback_type());
643 callback_2.WaitForResult(dc_nopaints.Flush(callback_2.GetCallback()));
644 CHECK_CALLBACK_BEHAVIOR(callback_2);
645 ASSERT_EQ(PP_ERROR_INPROGRESS, callback_2.result());
647 callback_1.WaitForResult(rv);
648 ASSERT_EQ(PP_OK, callback_1.result());
650 PASS();
653 void TestGraphics2D::DidChangeView(const pp::View& view) {
654 if (post_quit_on_view_changed_) {
655 post_quit_on_view_changed_ = false;
656 is_view_changed_ = true;
657 testing_interface_->QuitMessageLoop(instance_->pp_instance());
661 void TestGraphics2D::ResetViewChangedState() {
662 is_view_changed_ = false;
665 bool TestGraphics2D::WaitUntilViewChanged() {
666 // Run a nested message loop. It will exit either on ViewChanged or if the
667 // timeout happens.
669 // If view changed before we have chance to run message loop, return directly.
670 if (is_view_changed_)
671 return true;
673 post_quit_on_view_changed_ = true;
674 testing_interface_->RunMessageLoop(instance_->pp_instance());
675 post_quit_on_view_changed_ = false;
677 return is_view_changed_;
680 std::string TestGraphics2D::TestFlushOffscreenUpdate() {
681 // Tests that callback of offscreen updates should be delayed.
682 const PP_Time kFlushDelaySec = 1. / 30; // 30 fps
683 const int w = 80, h = 80;
684 pp::Graphics2D dc(instance_, pp::Size(w, h), true);
685 ASSERT_FALSE(dc.is_null());
686 ASSERT_TRUE(instance_->BindGraphics(dc));
688 // Squeeze from top until bottom half of plugin is out of screen.
689 ResetViewChangedState();
690 instance_->EvalScript(
691 "var big = document.createElement('div');"
692 "var offset = "
693 " window.innerHeight - plugin.offsetTop - plugin.offsetHeight / 2;"
694 "big.setAttribute('id', 'big-div');"
695 "big.setAttribute('style', 'height: ' + offset + '; width: 100%;');"
696 "document.body.insertBefore(big, document.body.firstChild);");
697 ASSERT_TRUE(WaitUntilViewChanged());
699 // Allocate a red image chunk
700 pp::ImageData chunk(instance_, PP_IMAGEDATAFORMAT_RGBA_PREMUL,
701 pp::Size(w/8, h/8), true);
702 ASSERT_FALSE(chunk.is_null());
703 const uint32_t kRed = 0xff0000ff;
704 FillRectInImage(&chunk, pp::Rect(chunk.size()), kRed);
706 // Paint a invisable chunk, expecting Flush to invoke callback slowly.
707 dc.PaintImageData(chunk, pp::Point(0, h*0.75));
709 PP_Time begin = pp::Module::Get()->core()->GetTime();
710 ASSERT_SUBTEST_SUCCESS(FlushAndWaitForDone(&dc));
711 PP_Time actual_time_elapsed = pp::Module::Get()->core()->GetTime() - begin;
712 // Expect actual_time_elapsed >= kFlushDelaySec, but loose a bit to avoid
713 // precision issue.
714 ASSERT_GE(actual_time_elapsed, kFlushDelaySec * 0.9);
716 // Remove the padding on the top since test cases here isn't independent.
717 instance_->EvalScript(
718 "var big = document.getElementById('big-div');"
719 "big.parentNode.removeChild(big);");
720 ResetViewChangedState();
721 ASSERT_TRUE(WaitUntilViewChanged());
723 PASS();
726 std::string TestGraphics2D::TestDev() {
727 // Tests GetScale/SetScale via the Graphics2D_Dev C++ wrapper
728 const int w = 20, h = 16;
729 const float scale = 1.0f/2.0f;
730 pp::Graphics2D dc(instance_, pp::Size(w, h), false);
731 ASSERT_FALSE(dc.is_null());
732 pp::Graphics2D_Dev dc_dev(dc);
733 ASSERT_EQ(1.0f, dc_dev.GetScale());
734 ASSERT_TRUE(dc_dev.SetScale(scale));
735 ASSERT_EQ(scale, dc_dev.GetScale());
736 // Try setting a few invalid scale factors. Ensure that we catch these errors
737 // and don't change the actual scale
738 ASSERT_FALSE(dc_dev.SetScale(-1.0f));
739 ASSERT_FALSE(dc_dev.SetScale(0.0f));
740 ASSERT_EQ(scale, dc_dev.GetScale());
742 // Verify that the context has the specified number of pixels, despite the
743 // non-identity scale
744 PP_Size size;
745 size.width = -1;
746 size.height = -1;
747 PP_Bool is_always_opaque = PP_FALSE;
748 ASSERT_TRUE(graphics_2d_interface_->Describe(dc_dev.pp_resource(), &size,
749 &is_always_opaque));
750 ASSERT_EQ(w, size.width);
751 ASSERT_EQ(h, size.height);
752 ASSERT_EQ(PP_FALSE, is_always_opaque);
754 PASS();
757 // This test makes sure that the out-of-process image data caching works as
758 // expected. Doing ReplaceContents quickly should re-use the image data from
759 // older ones.
760 std::string TestGraphics2D::TestReplaceContentsCaching() {
761 // The cache is only active when running in the proxy, so skip it otherwise.
762 if (!testing_interface_->IsOutOfProcess())
763 PASS();
765 // Here we test resource IDs as a way to determine if the resource is being
766 // cached and re-used. This is non-optimal since it's entirely possible
767 // (and maybe better) for the proxy to return new resource IDs for the
768 // re-used objects. Howevever, our current implementation does this so it is
769 // an easy thing to check for.
771 // You could check for the shared memory pointers getting re-used, but the
772 // OS is very likely to use the same memory location for a newly-mapped image
773 // if one was deleted, meaning that it could pass even if the cache is broken.
774 // This would then require that we add some address-re-use-preventing code
775 // which would be tricky.
776 std::set<PP_Resource> resources;
778 pp::Size size(16, 16);
779 pp::Graphics2D dc(instance_, size, false);
781 // Do two replace contentses, adding the old resource IDs to our map.
782 PP_Resource imageres = ReplaceContentsAndReturnID(&dc, size);
783 ASSERT_TRUE(imageres);
784 resources.insert(imageres);
785 imageres = ReplaceContentsAndReturnID(&dc, size);
786 ASSERT_TRUE(imageres);
787 resources.insert(imageres);
789 // Now doing more replace contents should re-use older IDs if the cache is
790 // working.
791 imageres = ReplaceContentsAndReturnID(&dc, size);
792 ASSERT_TRUE(resources.find(imageres) != resources.end());
793 imageres = ReplaceContentsAndReturnID(&dc, size);
794 ASSERT_TRUE(resources.find(imageres) != resources.end());
796 PASS();
799 std::string TestGraphics2D::TestBindNull() {
800 // Binding a null resource is not an error, it should clear all bound
801 // resources. We can't easily test what resource is bound, but we can test
802 // that this doesn't throw an error.
803 ASSERT_TRUE(instance_->BindGraphics(pp::Graphics2D()));
804 ASSERT_TRUE(instance_->BindGraphics(pp::Graphics3D()));
806 const int w = 115, h = 117;
807 pp::Graphics2D dc(instance_, pp::Size(w, h), false);
808 ASSERT_FALSE(dc.is_null());
809 ASSERT_TRUE(instance_->BindGraphics(dc));
811 ASSERT_TRUE(instance_->BindGraphics(pp::Graphics2D()));
812 ASSERT_TRUE(instance_->BindGraphics(pp::Graphics3D()));
814 PASS();