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
CanFlushContext(pp::Instance * instance,pp::Graphics2D * context)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
CanFlushContextC(pp::Instance * instance,PP_Resource graphics_2d,const PPB_Graphics2D_1_1 * graphics_2d_if)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
TestGraphics2D(TestingInstance * instance)44 TestGraphics2D::TestGraphics2D(TestingInstance* instance)
45 : TestCase(instance),
46 is_view_changed_(false),
47 post_quit_on_view_changed_(false) {
48 }
49
Init()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
RunTests(const std::string & filter)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
QuitMessageLoop()76 void TestGraphics2D::QuitMessageLoop() {
77 testing_interface_->QuitMessageLoop(instance_->pp_instance());
78 }
79
ReadImageData(const pp::Graphics2D & dc,pp::ImageData * image,const pp::Point & top_left) const80 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
IsDCUniformColor(const pp::Graphics2D & dc,uint32_t color) const89 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
FlushAndWaitForDone(pp::Graphics2D * context)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
FillRectInImage(pp::ImageData * image,const pp::Rect & rect,uint32_t color) const108 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
FillImageWithGradient(pp::ImageData * image) const118 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
CompareImages(const pp::ImageData & image1,const pp::ImageData & image2)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
CompareImageRect(const pp::ImageData & image1,const pp::Rect & rc1,const pp::ImageData & image2,const pp::Rect & rc2) const137 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
IsSquareInImage(const pp::ImageData & image_data,uint32_t background_color,const pp::Rect & square,uint32_t square_color) const154 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
IsSquareInDC(const pp::Graphics2D & dc,uint32_t background_color,const pp::Rect & square,uint32_t square_color) const173 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
ReplaceContentsAndReturnID(pp::Graphics2D * dc,const pp::Size & size)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.
TestInvalidResource()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
TestInvalidSize()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
TestHumongous()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
TestInitToZero()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
TestDescribe()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
TestScale()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
TestPaint()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
TestScroll()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
TestReplace()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 int32_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
TestFlush()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
DidChangeView(const pp::View & view)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
ResetViewChangedState()651 void TestGraphics2D::ResetViewChangedState() {
652 is_view_changed_ = false;
653 }
654
WaitUntilViewChanged()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
TestFlushOffscreenUpdate()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
TestDev()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.
TestReplaceContentsCaching()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
TestBindNull()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