test_graphics_2d.cc 31 KB

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