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_2 * graphics_2d_if)34 bool CanFlushContextC(pp::Instance* instance, PP_Resource graphics_2d,
35                       const PPB_Graphics2D_1_2* 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_2));
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 uint32_t background_color = 0xFF0000FF;
570   pp::ImageData background(instance_, PP_IMAGEDATAFORMAT_BGRA_PREMUL,
571                            pp::Size(w, h), true);
572   ASSERT_FALSE(background.is_null());
573   FillRectInImage(&background, pp::Rect(0, 0, w, h), background_color);
574   dc.PaintImageData(background, pp::Point(0, 0));
575 
576   // Replace with a green background but don't flush yet.
577   const int32_t swapped_color = 0x00FF00FF;
578   pp::ImageData swapped(instance_, PP_IMAGEDATAFORMAT_BGRA_PREMUL,
579                         pp::Size(w, h), true);
580   ASSERT_FALSE(swapped.is_null());
581   FillRectInImage(&swapped, pp::Rect(0, 0, w, h), swapped_color);
582   dc.ReplaceContents(&swapped);
583 
584   // The background should be unchanged since we didn't flush yet.
585   ASSERT_TRUE(IsDCUniformColor(dc, 0));
586 
587   // Test the C++ wrapper. The size of the swapped image should be reset.
588   ASSERT_TRUE(!swapped.pp_resource() && !swapped.size().width() &&
589               !swapped.size().height() && !swapped.data());
590 
591   // Painting with the swapped image should fail.
592   dc.PaintImageData(swapped, pp::Point(0, 0));
593 
594   // Flush and make sure the result is correct.
595   ASSERT_SUBTEST_SUCCESS(FlushAndWaitForDone(&dc));
596 
597   // The background should be green from the swapped image.
598   ASSERT_TRUE(IsDCUniformColor(dc, swapped_color));
599 
600   PASS();
601 }
602 
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 run 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   ASSERT_SUBTEST_SUCCESS(FlushAndWaitForDone(&dc));
689 
690   // Allocate a red image chunk
691   pp::ImageData chunk(instance_, PP_IMAGEDATAFORMAT_RGBA_PREMUL,
692                       pp::Size(w/8, h/8), true);
693   ASSERT_FALSE(chunk.is_null());
694   const uint32_t kRed = 0xff0000ff;
695   FillRectInImage(&chunk, pp::Rect(chunk.size()), kRed);
696 
697   // Paint a invisable chunk, expecting Flush to invoke callback slowly.
698   dc.PaintImageData(chunk, pp::Point(0, h*0.75));
699 
700   PP_Time begin = pp::Module::Get()->core()->GetTime();
701   ASSERT_SUBTEST_SUCCESS(FlushAndWaitForDone(&dc));
702   PP_Time actual_time_elapsed = pp::Module::Get()->core()->GetTime() - begin;
703   // Expect actual_time_elapsed >= kFlushDelaySec, but loose a bit to avoid
704   // precision issue.
705   ASSERT_GE(actual_time_elapsed, kFlushDelaySec * 0.9);
706 
707   // Remove the padding on the top since test cases here isn't independent.
708   instance_->EvalScript(
709       "var big = document.getElementById('big-div');"
710       "big.parentNode.removeChild(big);");
711   ResetViewChangedState();
712   ASSERT_TRUE(WaitUntilViewChanged());
713 
714   PASS();
715 }
716 
TestDev()717 std::string TestGraphics2D::TestDev() {
718   // Tests GetScale/SetScale via the Graphics2D_Dev C++ wrapper
719   const int w = 20, h = 16;
720   const float scale = 1.0f/2.0f;
721   pp::Graphics2D dc(instance_, pp::Size(w, h), false);
722   ASSERT_FALSE(dc.is_null());
723   ASSERT_EQ(1.0f, dc.GetScale());
724   ASSERT_TRUE(dc.SetScale(scale));
725   ASSERT_EQ(scale, dc.GetScale());
726   // Try setting a few invalid scale factors. Ensure that we catch these errors
727   // and don't change the actual scale
728   ASSERT_FALSE(dc.SetScale(-1.0f));
729   ASSERT_FALSE(dc.SetScale(0.0f));
730   ASSERT_EQ(scale, dc.GetScale());
731 
732   // Verify that the context has the specified number of pixels, despite the
733   // non-identity scale
734   PP_Size size;
735   size.width = -1;
736   size.height = -1;
737   PP_Bool is_always_opaque = PP_FALSE;
738   ASSERT_TRUE(graphics_2d_interface_->Describe(dc.pp_resource(), &size,
739                                                &is_always_opaque));
740   ASSERT_EQ(w, size.width);
741   ASSERT_EQ(h, size.height);
742   ASSERT_EQ(PP_FALSE, is_always_opaque);
743 
744   PASS();
745 }
746 
747 // This test makes sure that the out-of-process image data caching works as
748 // expected. Doing ReplaceContents quickly should re-use the image data from
749 // older ones.
TestReplaceContentsCaching()750 std::string TestGraphics2D::TestReplaceContentsCaching() {
751   // The cache is only active when running in the proxy, so skip it otherwise.
752   if (!testing_interface_->IsOutOfProcess())
753     PASS();
754 
755   // Here we test resource IDs as a way to determine if the resource is being
756   // cached and re-used. This is non-optimal since it's entirely possible
757   // (and maybe better) for the proxy to return new resource IDs for the
758   // re-used objects. Howevever, our current implementation does this so it is
759   // an easy thing to check for.
760   //
761   // You could check for the shared memory pointers getting re-used, but the
762   // OS is very likely to use the same memory location for a newly-mapped image
763   // if one was deleted, meaning that it could pass even if the cache is broken.
764   // This would then require that we add some address-re-use-preventing code
765   // which would be tricky.
766   std::set<PP_Resource> resources;
767 
768   pp::Size size(16, 16);
769   pp::Graphics2D dc(instance_, size, false);
770 
771   // Do two replace contentses, adding the old resource IDs to our map.
772   PP_Resource imageres = ReplaceContentsAndReturnID(&dc, size);
773   ASSERT_TRUE(imageres);
774   resources.insert(imageres);
775   imageres = ReplaceContentsAndReturnID(&dc, size);
776   ASSERT_TRUE(imageres);
777   resources.insert(imageres);
778 
779   // Now doing more replace contents should re-use older IDs if the cache is
780   // working.
781   imageres = ReplaceContentsAndReturnID(&dc, size);
782   ASSERT_TRUE(resources.find(imageres) != resources.end());
783   imageres = ReplaceContentsAndReturnID(&dc, size);
784   ASSERT_TRUE(resources.find(imageres) != resources.end());
785 
786   PASS();
787 }
788 
TestBindNull()789 std::string TestGraphics2D::TestBindNull() {
790   // Binding a null resource is not an error, it should clear all bound
791   // resources. We can't easily test what resource is bound, but we can test
792   // that this doesn't throw an error.
793   ASSERT_TRUE(instance_->BindGraphics(pp::Graphics2D()));
794   ASSERT_TRUE(instance_->BindGraphics(pp::Graphics3D()));
795 
796   const int w = 115, h = 117;
797   pp::Graphics2D dc(instance_, pp::Size(w, h), false);
798   ASSERT_FALSE(dc.is_null());
799   ASSERT_TRUE(instance_->BindGraphics(dc));
800 
801   ASSERT_TRUE(instance_->BindGraphics(pp::Graphics2D()));
802   ASSERT_TRUE(instance_->BindGraphics(pp::Graphics3D()));
803 
804   PASS();
805 }
806 
807