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"
19 const RenderPass
* GetRootRenderPass(SurfaceManager
* manager
,
20 SurfaceId surface_id
) {
21 Surface
* surface
= manager
->GetSurfaceForId(surface_id
);
23 const CompositorFrame
* surface_frame
= surface
->GetEligibleFrame();
27 const DelegatedFrameData
* frame_data
=
28 surface_frame
->delegated_frame_data
.get();
29 return frame_data
->render_pass_list
.empty()
31 : frame_data
->render_pass_list
.back();
35 SurfaceHittest::SurfaceHittest(SurfaceManager
* manager
) : manager_(manager
) {}
37 SurfaceHittest::~SurfaceHittest() {}
39 SurfaceId
SurfaceHittest::Hittest(SurfaceId surface_id
,
40 const gfx::Point
& point
,
41 gfx::Point
* transformed_point
) {
42 SurfaceId hittest_surface_id
= surface_id
;
44 if (transformed_point
)
45 *transformed_point
= point
;
47 HittestInternal(surface_id
, GetRootRenderPass(manager_
, surface_id
), point
,
48 &hittest_surface_id
, transformed_point
);
50 referenced_passes_
.clear();
52 return hittest_surface_id
;
55 bool SurfaceHittest::HittestInternal(SurfaceId surface_id
,
56 const RenderPass
* render_pass
,
57 const gfx::Point
& point
,
58 SurfaceId
* out_surface_id
,
59 gfx::Point
* out_transformed_point
) {
60 // To avoid an infinite recursion, we need to skip the RenderPass if it's
61 // already been referenced.
62 if (referenced_passes_
.find(render_pass
) != referenced_passes_
.end())
64 referenced_passes_
.insert(render_pass
);
66 gfx::Transform transform_from_root_target
;
68 !render_pass
->transform_to_root_target
.GetInverse(
69 &transform_from_root_target
)) {
73 gfx::Point
point_in_target_space(point
);
74 transform_from_root_target
.TransformPoint(&point_in_target_space
);
76 for (const auto* quad
: render_pass
->quad_list
) {
77 // First we test against the clip_rect. The clip_rect is in target space, so
78 // we can test the point directly.
79 if (!quad
->shared_quad_state
->is_clipped
||
80 quad
->shared_quad_state
->clip_rect
.Contains(point_in_target_space
)) {
81 // We now transform the point to content space and test if it hits the
83 gfx::Transform target_to_quad_transform
;
84 if (quad
->shared_quad_state
->quad_to_target_transform
.GetInverse(
85 &target_to_quad_transform
)) {
86 gfx::Point
transformed_point(point_in_target_space
);
87 target_to_quad_transform
.TransformPoint(&transformed_point
);
89 if (quad
->rect
.Contains(transformed_point
)) {
90 if (quad
->material
== DrawQuad::SURFACE_CONTENT
) {
91 // We've hit a SurfaceDrawQuad, we need to recurse into this
93 const SurfaceDrawQuad
* surface_quad
=
94 SurfaceDrawQuad::MaterialCast(quad
);
96 gfx::Point point_in_current_surface
;
97 if (out_transformed_point
) {
98 point_in_current_surface
= *out_transformed_point
;
99 *out_transformed_point
= transformed_point
;
103 surface_quad
->surface_id
,
104 GetRootRenderPass(manager_
, surface_quad
->surface_id
),
105 transformed_point
, out_surface_id
, out_transformed_point
)) {
108 if (out_transformed_point
)
109 *out_transformed_point
= point_in_current_surface
;
111 } else if (quad
->material
== DrawQuad::RENDER_PASS
) {
112 // We've hit a RenderPassDrawQuad, we need to recurse into this
114 const RenderPassDrawQuad
* render_quad
=
115 RenderPassDrawQuad::MaterialCast(quad
);
117 Surface
* surface
= manager_
->GetSurfaceForId(surface_id
);
118 const CompositorFrame
* surface_frame
= surface
->GetEligibleFrame();
119 DCHECK(surface_frame
);
120 const DelegatedFrameData
* frame_data
=
121 surface_frame
->delegated_frame_data
.get();
123 const RenderPass
* quad_render_pass
= nullptr;
124 for (const auto* render_pass
: frame_data
->render_pass_list
) {
125 if (render_pass
->id
== render_quad
->render_pass_id
) {
126 quad_render_pass
= render_pass
;
131 if (quad_render_pass
&&
132 HittestInternal(surface_id
, quad_render_pass
,
133 point_in_target_space
, out_surface_id
,
134 out_transformed_point
)) {
138 // We've hit a different type of quad in the current Surface,
139 // there's no need to iterate anymore, this is the quad that
140 // receives the event;
141 *out_surface_id
= surface_id
;