diff options
author | fsamuel <fsamuel@chromium.org> | 2015-10-05 17:57:05 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-10-06 00:57:53 +0000 |
commit | bd4d66ea87a1fa3b41cbf5b73661e5b74928a3a4 (patch) | |
tree | a96ee00dc29145d4331e536c597a5acc96253e8b /cc/surfaces | |
parent | 981b371bf42e3f5d53f3c06b272d974cb11c7f1f (diff) | |
download | chromium_src-bd4d66ea87a1fa3b41cbf5b73661e5b74928a3a4.zip chromium_src-bd4d66ea87a1fa3b41cbf5b73661e5b74928a3a4.tar.gz chromium_src-bd4d66ea87a1fa3b41cbf5b73661e5b74928a3a4.tar.bz2 |
Surface hit testing: Introduce GetTransformToTargetSurface
In order to support pointer capture, we need to be able to
transform points to a target surface's space even if the
point no longer falls within the bounds of the target
surface.
BUG=533161
CQ_INCLUDE_TRYBOTS=tryserver.blink:linux_blink_rel
TBR=jbauman@chromium.org
Review URL: https://codereview.chromium.org/1383283002
Cr-Commit-Position: refs/heads/master@{#352496}
Diffstat (limited to 'cc/surfaces')
-rw-r--r-- | cc/surfaces/surface_hittest.cc | 104 | ||||
-rw-r--r-- | cc/surfaces/surface_hittest.h | 22 | ||||
-rw-r--r-- | cc/surfaces/surface_hittest_unittest.cc | 240 |
3 files changed, 275 insertions, 91 deletions
diff --git a/cc/surfaces/surface_hittest.cc b/cc/surfaces/surface_hittest.cc index 3a3864e..752a708 100644 --- a/cc/surfaces/surface_hittest.cc +++ b/cc/surfaces/surface_hittest.cc @@ -22,20 +22,34 @@ SurfaceHittest::SurfaceHittest(SurfaceManager* manager) : manager_(manager) {} SurfaceHittest::~SurfaceHittest() {} -SurfaceId SurfaceHittest::GetTargetSurfaceAtPoint(SurfaceId surface_id, +SurfaceId SurfaceHittest::GetTargetSurfaceAtPoint(SurfaceId root_surface_id, const gfx::Point& point, gfx::Transform* transform) { - SurfaceId hittest_surface_id = surface_id; + SurfaceId out_surface_id = root_surface_id; + // Reset the output transform to identity. if (transform) *transform = gfx::Transform(); std::set<const RenderPass*> referenced_passes; - GetTargetSurfaceAtPointInternal(surface_id, RenderPassId(), point, - &referenced_passes, &hittest_surface_id, + GetTargetSurfaceAtPointInternal(root_surface_id, RenderPassId(), point, + &referenced_passes, &out_surface_id, transform); - return hittest_surface_id; + return out_surface_id; +} + +bool SurfaceHittest::GetTransformToTargetSurface(SurfaceId root_surface_id, + SurfaceId target_surface_id, + gfx::Transform* transform) { + // Reset the output transform to identity. + if (transform) + *transform = gfx::Transform(); + + std::set<const RenderPass*> referenced_passes; + return GetTransformToTargetSurfaceInternal(root_surface_id, target_surface_id, + RenderPassId(), &referenced_passes, + transform); } bool SurfaceHittest::GetTargetSurfaceAtPointInternal( @@ -120,6 +134,86 @@ bool SurfaceHittest::GetTargetSurfaceAtPointInternal( return false; } +bool SurfaceHittest::GetTransformToTargetSurfaceInternal( + SurfaceId root_surface_id, + SurfaceId target_surface_id, + const RenderPassId& render_pass_id, + std::set<const RenderPass*>* referenced_passes, + gfx::Transform* out_transform) { + if (root_surface_id == target_surface_id) { + *out_transform = gfx::Transform(); + return true; + } + + const RenderPass* render_pass = + GetRenderPassForSurfaceById(root_surface_id, render_pass_id); + if (!render_pass) + return false; + + // To avoid an infinite recursion, we need to skip the RenderPass if it's + // already been referenced. + if (referenced_passes->find(render_pass) != referenced_passes->end()) + return false; + + referenced_passes->insert(render_pass); + + // The |transform_to_root_target| matrix cannot be inverted if it has a + // z-scale of 0 or due to floating point errors. + gfx::Transform transform_from_root_target; + if (!render_pass->transform_to_root_target.GetInverse( + &transform_from_root_target)) { + return false; + } + + for (const DrawQuad* quad : render_pass->quad_list) { + if (quad->material == DrawQuad::SURFACE_CONTENT) { + gfx::Transform target_to_quad_transform; + if (!quad->shared_quad_state->quad_to_target_transform.GetInverse( + &target_to_quad_transform)) { + return false; + } + + const SurfaceDrawQuad* surface_quad = SurfaceDrawQuad::MaterialCast(quad); + if (surface_quad->surface_id == target_surface_id) { + *out_transform = target_to_quad_transform * transform_from_root_target; + return true; + } + + // This isn't the target surface. Let's recurse deeper to see if we can + // find the |target_surface_id| there. + gfx::Transform transform_to_child_space; + if (GetTransformToTargetSurfaceInternal( + surface_quad->surface_id, target_surface_id, RenderPassId(), + referenced_passes, &transform_to_child_space)) { + *out_transform = transform_to_child_space * target_to_quad_transform * + transform_from_root_target; + return true; + } + continue; + } + + if (quad->material == DrawQuad::RENDER_PASS) { + // We've hit a RenderPassDrawQuad, we need to recurse into this + // RenderPass. + const RenderPassDrawQuad* render_quad = + RenderPassDrawQuad::MaterialCast(quad); + + gfx::Transform transform_to_child_space; + if (GetTransformToTargetSurfaceInternal( + root_surface_id, target_surface_id, render_quad->render_pass_id, + referenced_passes, &transform_to_child_space)) { + *out_transform = transform_to_child_space; + return true; + } + + continue; + } + } + + // The target surface was not found. + return false; +} + const RenderPass* SurfaceHittest::GetRenderPassForSurfaceById( SurfaceId surface_id, const RenderPassId& render_pass_id) { diff --git a/cc/surfaces/surface_hittest.h b/cc/surfaces/surface_hittest.h index b5e1a67..60fa893 100644 --- a/cc/surfaces/surface_hittest.h +++ b/cc/surfaces/surface_hittest.h @@ -28,13 +28,20 @@ class CC_SURFACES_EXPORT SurfaceHittest { explicit SurfaceHittest(SurfaceManager* manager); ~SurfaceHittest(); - // Hittests against Surface with SurfaceId |surface_id|, return the contained - // surface that the point is hitting and the |transformed_point| in the - // surface space. - SurfaceId GetTargetSurfaceAtPoint(SurfaceId surface_id, + // Returns the target surface that falls underneath the provided |point|. + // Also returns the |transform| to convert the |point| to the target surface's + // space. + SurfaceId GetTargetSurfaceAtPoint(SurfaceId root_surface_id, const gfx::Point& point, gfx::Transform* transform); + // Returns whether the target surface falls inside the provide root surface. + // Returns the |transform| to convert points from the root surface coordinate + // space to the target surface coordinate space. + bool GetTransformToTargetSurface(SurfaceId root_surface_id, + SurfaceId target_surface_id, + gfx::Transform* transform); + private: bool GetTargetSurfaceAtPointInternal( SurfaceId surface_id, @@ -44,6 +51,13 @@ class CC_SURFACES_EXPORT SurfaceHittest { SurfaceId* out_surface_id, gfx::Transform* out_transform); + bool GetTransformToTargetSurfaceInternal( + SurfaceId root_surface_id, + SurfaceId target_surface_id, + const RenderPassId& render_pass_id, + std::set<const RenderPass*>* referenced_passes, + gfx::Transform* out_transform); + const RenderPass* GetRenderPassForSurfaceById( SurfaceId surface_id, const RenderPassId& render_pass_id); diff --git a/cc/surfaces/surface_hittest_unittest.cc b/cc/surfaces/surface_hittest_unittest.cc index a7f373f..ac9e3bc 100644 --- a/cc/surfaces/surface_hittest_unittest.cc +++ b/cc/surfaces/surface_hittest_unittest.cc @@ -23,6 +23,35 @@ namespace cc { namespace { +struct TestCase { + SurfaceId input_surface_id; + gfx::Point input_point; + SurfaceId expected_output_surface_id; + gfx::Point expected_output_point; +}; + +void RunTests(SurfaceManager* manager, TestCase* tests, size_t test_count) { + SurfaceHittest hittest(manager); + for (size_t i = 0; i < test_count; ++i) { + const TestCase& test = tests[i]; + gfx::Point point(test.input_point); + gfx::Transform transform; + EXPECT_EQ(test.expected_output_surface_id, + hittest.GetTargetSurfaceAtPoint(test.input_surface_id, point, + &transform)); + transform.TransformPoint(&point); + EXPECT_EQ(test.expected_output_point, point); + + // Verify that GetTransformToTargetSurface returns true and returns the same + // transform as returned by GetTargetSurfaceAtPoint. + gfx::Transform target_transform; + EXPECT_TRUE(hittest.GetTransformToTargetSurface( + test.input_surface_id, test.expected_output_surface_id, + &target_transform)); + EXPECT_EQ(transform, target_transform); + } +} + class EmptySurfaceFactoryClient : public SurfaceFactoryClient { public: void ReturnResources(const ReturnedResourceArray& resources) override {} @@ -178,16 +207,16 @@ TEST(SurfaceHittestTest, Hittest_SingleSurface) { factory.Create(root_surface_id); factory.SubmitCompositorFrame(root_surface_id, root_frame.Pass(), SurfaceFactory::DrawCallback()); + TestCase tests[] = { + { + root_surface_id, + gfx::Point(100, 100), + root_surface_id, + gfx::Point(100, 100) + }, + }; - { - SurfaceHittest hittest(&manager); - gfx::Point point(100, 100); - gfx::Transform transform; - EXPECT_EQ(root_surface_id, hittest.GetTargetSurfaceAtPoint( - root_surface_id, point, &transform)); - transform.TransformPoint(&point); - EXPECT_EQ(gfx::Point(100, 100), point); - } + RunTests(&manager, tests, sizeof(tests) / sizeof(*tests)); factory.Destroy(root_surface_id); } @@ -209,9 +238,9 @@ TEST(SurfaceHittestTest, Hittest_ChildSurface) { gfx::Rect child_rect(200, 200); CreateSurfaceDrawQuad(root_pass, gfx::Transform(1.0f, 0.0f, 0.0f, 50.0f, - 0.0f, 1.0f, 0.0f, 50.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f), + 0.0f, 1.0f, 0.0f, 50.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f), root_rect, child_rect, child_surface_id); @@ -232,8 +261,10 @@ TEST(SurfaceHittestTest, Hittest_ChildSurface) { gfx::Rect child_solid_quad_rect(100, 100); CreateSolidColorDrawQuad( child_pass, - gfx::Transform(1.0f, 0.0f, 0.0f, 50.0f, 0.0f, 1.0f, 0.0f, 50.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f), + gfx::Transform(1.0f, 0.0f, 0.0f, 50.0f, + 0.0f, 1.0f, 0.0f, 50.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f), root_rect, child_solid_quad_rect); // Submit the frame. @@ -241,33 +272,80 @@ TEST(SurfaceHittestTest, Hittest_ChildSurface) { factory.SubmitCompositorFrame(child_surface_id, child_frame.Pass(), SurfaceFactory::DrawCallback()); - struct Test { - SurfaceId input_surface_id; - gfx::Point input_point; - SurfaceId expected_output_surface_id; - gfx::Point expected_output_point; - } tests[] = {{root_surface_id, gfx::Point(10, 10), root_surface_id, - gfx::Point(10, 10)}, - {root_surface_id, gfx::Point(99, 99), root_surface_id, - gfx::Point(99, 99)}, - {root_surface_id, gfx::Point(100, 100), child_surface_id, - gfx::Point(50, 50)}, - {root_surface_id, gfx::Point(199, 199), child_surface_id, - gfx::Point(149, 149)}, - {root_surface_id, gfx::Point(200, 200), root_surface_id, - gfx::Point(200, 200)}, - {root_surface_id, gfx::Point(290, 290), root_surface_id, - gfx::Point(290, 290)}}; - - SurfaceHittest hittest(&manager); - for (const auto& test : tests) { - gfx::Point point(test.input_point); + TestCase tests[] = { + { + root_surface_id, + gfx::Point(10, 10), + root_surface_id, + gfx::Point(10, 10) + }, + { + root_surface_id, + gfx::Point(99, 99), + root_surface_id, + gfx::Point(99, 99) + }, + { + root_surface_id, + gfx::Point(100, 100), + child_surface_id, + gfx::Point(50, 50) + }, + { + root_surface_id, + gfx::Point(199, 199), + child_surface_id, + gfx::Point(149, 149) + }, + { + root_surface_id, + gfx::Point(200, 200), + root_surface_id, + gfx::Point(200, 200) + }, + { + root_surface_id, + gfx::Point(290, 290), + root_surface_id, + gfx::Point(290, 290) + } + }; + + RunTests(&manager, tests, sizeof(tests) / sizeof(*tests)); + + // Submit another root frame, with a slightly perturbed child Surface. + root_frame = CreateCompositorFrame(root_rect, &root_pass); + CreateSurfaceDrawQuad(root_pass, + gfx::Transform(1.0f, 0.0f, 0.0f, 75.0f, + 0.0f, 1.0f, 0.0f, 75.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f), + root_rect, + child_rect, + child_surface_id); + factory.SubmitCompositorFrame(root_surface_id, root_frame.Pass(), + SurfaceFactory::DrawCallback()); + + // Verify that point (100, 100) no longer falls on the child surface. + // Verify that the transform to the child surface's space has also shifted. + { + SurfaceHittest hittest(&manager); + + gfx::Point point(100, 100); gfx::Transform transform; - EXPECT_EQ(test.expected_output_surface_id, - hittest.GetTargetSurfaceAtPoint(test.input_surface_id, point, + EXPECT_EQ(root_surface_id, + hittest.GetTargetSurfaceAtPoint(root_surface_id, point, &transform)); transform.TransformPoint(&point); - EXPECT_EQ(test.expected_output_point, point); + EXPECT_EQ(gfx::Point(100, 100), point); + + gfx::Point point_in_target_space(100, 100); + gfx::Transform target_transform; + EXPECT_TRUE(hittest.GetTransformToTargetSurface( + root_surface_id, child_surface_id, &target_transform)); + target_transform.TransformPoint(&point_in_target_space); + EXPECT_NE(transform, target_transform); + EXPECT_EQ(gfx::Point(25, 25), point_in_target_space); } factory.Destroy(root_surface_id); @@ -334,34 +412,46 @@ TEST(SurfaceHittestTest, Hittest_InvalidRenderPassDrawQuad) { factory.SubmitCompositorFrame(child_surface_id, child_frame.Pass(), SurfaceFactory::DrawCallback()); - struct Test { - SurfaceId input_surface_id; - gfx::Point input_point; - SurfaceId expected_output_surface_id; - gfx::Point expected_output_point; - } tests[] = {{root_surface_id, gfx::Point(10, 10), root_surface_id, - gfx::Point(10, 10)}, - {root_surface_id, gfx::Point(99, 99), root_surface_id, - gfx::Point(99, 99)}, - {root_surface_id, gfx::Point(100, 100), child_surface_id, - gfx::Point(50, 50)}, - {root_surface_id, gfx::Point(199, 199), child_surface_id, - gfx::Point(149, 149)}, - {root_surface_id, gfx::Point(200, 200), root_surface_id, - gfx::Point(200, 200)}, - {root_surface_id, gfx::Point(290, 290), root_surface_id, - gfx::Point(290, 290)}}; - - SurfaceHittest hittest(&manager); - for (const auto& test : tests) { - gfx::Point point(test.input_point); - gfx::Transform transform; - EXPECT_EQ(test.expected_output_surface_id, - hittest.GetTargetSurfaceAtPoint(test.input_surface_id, point, - &transform)); - transform.TransformPoint(&point); - EXPECT_EQ(test.expected_output_point, point); - } + TestCase tests[] = { + { + root_surface_id, + gfx::Point(10, 10), + root_surface_id, + gfx::Point(10, 10) + }, + { + root_surface_id, + gfx::Point(99, 99), + root_surface_id, + gfx::Point(99, 99) + }, + { + root_surface_id, + gfx::Point(100, 100), + child_surface_id, + gfx::Point(50, 50) + }, + { + root_surface_id, + gfx::Point(199, 199), + child_surface_id, + gfx::Point(149, 149) + }, + { + root_surface_id, + gfx::Point(200, 200), + root_surface_id, + gfx::Point(200, 200) + }, + { + root_surface_id, + gfx::Point(290, 290), + root_surface_id, + gfx::Point(290, 290) + } + }; + + RunTests(&manager, tests, sizeof(tests) / sizeof(*tests)); factory.Destroy(root_surface_id); factory.Destroy(child_surface_id); @@ -421,12 +511,7 @@ TEST(SurfaceHittestTest, Hittest_RenderPassDrawQuad) { factory.SubmitCompositorFrame(root_surface_id, root_frame.Pass(), SurfaceFactory::DrawCallback()); - struct Test { - SurfaceId input_surface_id; - gfx::Point input_point; - SurfaceId expected_output_surface_id; - gfx::Point expected_output_point; - } tests[] = { + TestCase tests[] = { // These tests just miss the RenderPassDrawQuad. { root_surface_id, @@ -470,16 +555,7 @@ TEST(SurfaceHittestTest, Hittest_RenderPassDrawQuad) { } }; - SurfaceHittest hittest(&manager); - for (const auto& test : tests) { - gfx::Point point(test.input_point); - gfx::Transform transform; - EXPECT_EQ(test.expected_output_surface_id, - hittest.GetTargetSurfaceAtPoint(test.input_surface_id, point, - &transform)); - transform.TransformPoint(&point); - EXPECT_EQ(test.expected_output_point, point); - } + RunTests(&manager, tests, sizeof(tests) / sizeof(*tests)); factory.Destroy(root_surface_id); } |