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
);
25 const CompositorFrame
* surface_frame
= surface
->GetEligibleFrame();
29 const DelegatedFrameData
* frame_data
=
30 surface_frame
->delegated_frame_data
.get();
31 return frame_data
->render_pass_list
.empty()
33 : frame_data
->render_pass_list
.back();
37 SurfaceHittest::SurfaceHittest(SurfaceManager
* manager
) : manager_(manager
) {}
39 SurfaceHittest::~SurfaceHittest() {}
41 SurfaceId
SurfaceHittest::Hittest(SurfaceId surface_id
,
42 const gfx::Point
& point
,
43 gfx::Point
* transformed_point
) {
44 SurfaceId hittest_surface_id
= surface_id
;
46 if (transformed_point
)
47 *transformed_point
= point
;
49 HittestInternal(surface_id
, GetRootRenderPass(manager_
, surface_id
), point
,
50 &hittest_surface_id
, transformed_point
);
52 referenced_passes_
.clear();
54 return hittest_surface_id
;
57 bool SurfaceHittest::HittestInternal(SurfaceId surface_id
,
58 const RenderPass
* render_pass
,
59 const gfx::Point
& point
,
60 SurfaceId
* out_surface_id
,
61 gfx::Point
* out_transformed_point
) {
62 // To avoid an infinite recursion, we need to skip the RenderPass if it's
63 // already been referenced.
64 if (referenced_passes_
.find(render_pass
) != referenced_passes_
.end())
66 referenced_passes_
.insert(render_pass
);
68 gfx::Transform transform_from_root_target
;
70 !render_pass
->transform_to_root_target
.GetInverse(
71 &transform_from_root_target
)) {
75 gfx::Point
point_in_target_space(point
);
76 transform_from_root_target
.TransformPoint(&point_in_target_space
);
78 for (const auto* quad
: render_pass
->quad_list
) {
79 // First we test against the clip_rect. The clip_rect is in target space, so
80 // we can test the point directly.
81 if (!quad
->shared_quad_state
->is_clipped
||
82 quad
->shared_quad_state
->clip_rect
.Contains(point_in_target_space
)) {
83 // We now transform the point to content space and test if it hits the
85 gfx::Transform target_to_quad_transform
;
86 if (quad
->shared_quad_state
->quad_to_target_transform
.GetInverse(
87 &target_to_quad_transform
)) {
88 gfx::Point
transformed_point(point_in_target_space
);
89 target_to_quad_transform
.TransformPoint(&transformed_point
);
91 if (quad
->rect
.Contains(transformed_point
)) {
92 if (quad
->material
== DrawQuad::SURFACE_CONTENT
) {
93 // We've hit a SurfaceDrawQuad, we need to recurse into this
95 const SurfaceDrawQuad
* surface_quad
=
96 SurfaceDrawQuad::MaterialCast(quad
);
98 gfx::Point point_in_current_surface
;
99 if (out_transformed_point
) {
100 point_in_current_surface
= *out_transformed_point
;
101 *out_transformed_point
= transformed_point
;
105 surface_quad
->surface_id
,
106 GetRootRenderPass(manager_
, surface_quad
->surface_id
),
107 transformed_point
, out_surface_id
, out_transformed_point
)) {
110 if (out_transformed_point
)
111 *out_transformed_point
= point_in_current_surface
;
113 } else if (quad
->material
== DrawQuad::RENDER_PASS
) {
114 // We've hit a RenderPassDrawQuad, we need to recurse into this
116 const RenderPassDrawQuad
* render_quad
=
117 RenderPassDrawQuad::MaterialCast(quad
);
119 Surface
* surface
= manager_
->GetSurfaceForId(surface_id
);
120 const CompositorFrame
* surface_frame
= surface
->GetEligibleFrame();
121 DCHECK(surface_frame
);
122 const DelegatedFrameData
* frame_data
=
123 surface_frame
->delegated_frame_data
.get();
125 const RenderPass
* quad_render_pass
= nullptr;
126 for (const auto* render_pass
: frame_data
->render_pass_list
) {
127 if (render_pass
->id
== render_quad
->render_pass_id
) {
128 quad_render_pass
= render_pass
;
133 if (quad_render_pass
&&
134 HittestInternal(surface_id
, quad_render_pass
,
135 point_in_target_space
, out_surface_id
,
136 out_transformed_point
)) {
140 // We've hit a different type of quad in the current Surface,
141 // there's no need to iterate anymore, this is the quad that
142 // receives the event;
143 *out_surface_id
= surface_id
;