1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "cc/surfaces/surface_hittest.h"
7 #include "cc/output/compositor_frame.h"
8 #include "cc/output/delegated_frame_data.h"
9 #include "cc/quads/draw_quad.h"
10 #include "cc/quads/render_pass_draw_quad.h"
11 #include "cc/quads/surface_draw_quad.h"
12 #include "cc/surfaces/surface.h"
13 #include "cc/surfaces/surface_manager.h"
14 #include "ui/gfx/geometry/point.h"
15 #include "ui/gfx/transform.h"
21 SurfaceHittest::SurfaceHittest(SurfaceManager
* manager
) : manager_(manager
) {}
23 SurfaceHittest::~SurfaceHittest() {}
25 SurfaceId
SurfaceHittest::GetTargetSurfaceAtPoint(
27 const gfx::Point
& point
,
28 gfx::Point
* transformed_point
) {
29 SurfaceId hittest_surface_id
= surface_id
;
31 if (transformed_point
)
32 *transformed_point
= point
;
34 std::set
<const RenderPass
*> referenced_passes
;
35 GetTargetSurfaceAtPointInternal(surface_id
, RenderPassId(), point
,
36 &referenced_passes
, &hittest_surface_id
,
39 return hittest_surface_id
;
42 bool SurfaceHittest::GetTargetSurfaceAtPointInternal(
44 const RenderPassId
& render_pass_id
,
45 const gfx::Point
& point_in_root_target
,
46 std::set
<const RenderPass
*>* referenced_passes
,
47 SurfaceId
* out_surface_id
,
48 gfx::Point
* out_transformed_point
) {
49 const RenderPass
* render_pass
=
50 GetRenderPassForSurfaceById(surface_id
, render_pass_id
);
54 // To avoid an infinite recursion, we need to skip the RenderPass if it's
55 // already been referenced.
56 if (referenced_passes
->find(render_pass
) != referenced_passes
->end())
59 referenced_passes
->insert(render_pass
);
61 // The |transform_to_root_target| matrix cannot be inverted if it has a
62 // z-scale of 0 or due to floating point errors.
63 gfx::Transform transform_from_root_target
;
64 if (!render_pass
->transform_to_root_target
.GetInverse(
65 &transform_from_root_target
)) {
69 gfx::Point
point_in_render_pass_space(point_in_root_target
);
70 transform_from_root_target
.TransformPoint(&point_in_render_pass_space
);
72 for (const DrawQuad
* quad
: render_pass
->quad_list
) {
73 gfx::Point point_in_quad_space
;
74 if (!PointInQuad(quad
, point_in_render_pass_space
, &point_in_quad_space
))
77 if (quad
->material
== DrawQuad::SURFACE_CONTENT
) {
78 // We've hit a SurfaceDrawQuad, we need to recurse into this
80 const SurfaceDrawQuad
* surface_quad
= SurfaceDrawQuad::MaterialCast(quad
);
82 gfx::Point
point_in_child_space(point_in_quad_space
);
83 if (GetTargetSurfaceAtPointInternal(
84 surface_quad
->surface_id
, RenderPassId(), point_in_quad_space
,
85 referenced_passes
, out_surface_id
, &point_in_child_space
)) {
86 *out_transformed_point
= point_in_child_space
;
93 if (quad
->material
== DrawQuad::RENDER_PASS
) {
94 // We've hit a RenderPassDrawQuad, we need to recurse into this
96 const RenderPassDrawQuad
* render_quad
=
97 RenderPassDrawQuad::MaterialCast(quad
);
99 gfx::Point
point_in_child_space(point_in_root_target
);
100 if (GetTargetSurfaceAtPointInternal(
101 surface_id
, render_quad
->render_pass_id
, point_in_root_target
,
102 referenced_passes
, out_surface_id
, &point_in_child_space
)) {
103 *out_transformed_point
= point_in_child_space
;
110 // We've hit a different type of quad in the current Surface,
111 // there's no need to iterate anymore, this is the quad that
112 // receives the event;
113 *out_surface_id
= surface_id
;
117 // No quads were found beneath the provided |point|.
121 const RenderPass
* SurfaceHittest::GetRenderPassForSurfaceById(
122 SurfaceId surface_id
,
123 const RenderPassId
& render_pass_id
) {
124 Surface
* surface
= manager_
->GetSurfaceForId(surface_id
);
128 const CompositorFrame
* surface_frame
= surface
->GetEligibleFrame();
132 const DelegatedFrameData
* frame_data
=
133 surface_frame
->delegated_frame_data
.get();
134 if (frame_data
->render_pass_list
.empty())
137 if (!render_pass_id
.IsValid())
138 return frame_data
->render_pass_list
.back();
140 for (const auto* render_pass
: frame_data
->render_pass_list
) {
141 if (render_pass
->id
== render_pass_id
)
148 bool SurfaceHittest::PointInQuad(const DrawQuad
* quad
,
149 const gfx::Point
& point_in_render_pass_space
,
150 gfx::Point
* point_in_quad_space
) {
151 // First we test against the clip_rect. The clip_rect is in target space, so
152 // we can test the point directly.
153 if (quad
->shared_quad_state
->is_clipped
&&
154 !quad
->shared_quad_state
->clip_rect
.Contains(
155 point_in_render_pass_space
)) {
159 // We now transform the point to content space and test if it hits the
161 gfx::Transform target_to_quad_transform
;
162 if (!quad
->shared_quad_state
->quad_to_target_transform
.GetInverse(
163 &target_to_quad_transform
)) {
167 *point_in_quad_space
= point_in_render_pass_space
;
168 target_to_quad_transform
.TransformPoint(point_in_quad_space
);
170 return quad
->rect
.Contains(*point_in_quad_space
);