Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(147)

Unified Diff: cc/trees/layer_tree_impl.cc

Issue 266913021: Hit test on the layer tree rather than the RSLL (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Helps if you upload all your files. Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « cc/trees/layer_tree_impl.h ('k') | cc/trees/layer_tree_impl_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: cc/trees/layer_tree_impl.cc
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index 97e9c1754c9a18eb8f9740101b72ad3849675808..a4d5cab9f94bf866f23e8d35e6fcfa9b107eb0ee 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -4,6 +4,9 @@
#include "cc/trees/layer_tree_impl.h"
+#include <limits>
+#include <set>
+
#include "base/debug/trace_event.h"
#include "cc/animation/keyframed_animation_curve.h"
#include "cc/animation/scrollbar_animation_controller.h"
@@ -20,6 +23,7 @@
#include "cc/resources/ui_resource_request.h"
#include "cc/trees/layer_tree_host_common.h"
#include "cc/trees/layer_tree_host_impl.h"
+#include "ui/gfx/point_conversions.h"
#include "ui/gfx/size_conversions.h"
#include "ui/gfx/vector2d_conversions.h"
@@ -1022,4 +1026,262 @@ void LayerTreeImpl::ReleaseResourcesRecursive(LayerImpl* current) {
ReleaseResourcesRecursive(current->children()[i]);
}
+template <typename LayerType>
+static inline bool LayerClipsSubtree(LayerType* layer) {
+ return layer->masks_to_bounds() || layer->mask_layer();
+}
+
+static bool PointHitsRect(
+ const gfx::PointF& screen_space_point,
+ const gfx::Transform& local_space_to_screen_space_transform,
+ const gfx::RectF& local_space_rect,
+ float* distance_to_camera) {
+ // If the transform is not invertible, then assume that this point doesn't hit
+ // this rect.
+ gfx::Transform inverse_local_space_to_screen_space(
+ gfx::Transform::kSkipInitialization);
+ if (!local_space_to_screen_space_transform.GetInverse(
+ &inverse_local_space_to_screen_space))
+ return false;
+
+ // Transform the hit test point from screen space to the local space of the
+ // given rect.
+ bool clipped = false;
+ gfx::Point3F planar_point = MathUtil::ProjectPoint3D(
+ inverse_local_space_to_screen_space, screen_space_point, &clipped);
+ gfx::PointF hit_test_point_in_local_space =
+ gfx::PointF(planar_point.x(), planar_point.y());
+
+ // If ProjectPoint could not project to a valid value, then we assume that
+ // this point doesn't hit this rect.
+ if (clipped)
+ return false;
+
+ if (!local_space_rect.Contains(hit_test_point_in_local_space))
+ return false;
+
+ if (distance_to_camera) {
+ // To compute the distance to the camera, we have to take the planar point
+ // and pull it back to world space and compute the displacement along the
+ // z-axis.
+ gfx::Point3F planar_point_in_screen_space(planar_point);
+ local_space_to_screen_space_transform.TransformPoint(
+ &planar_point_in_screen_space);
+ *distance_to_camera = planar_point_in_screen_space.z();
+ }
+
+ return true;
+}
+
+static bool PointHitsRegion(const gfx::PointF& screen_space_point,
+ const gfx::Transform& screen_space_transform,
+ const Region& layer_space_region,
+ float layer_content_scale_x,
+ float layer_content_scale_y) {
+ // If the transform is not invertible, then assume that this point doesn't hit
+ // this region.
+ gfx::Transform inverse_screen_space_transform(
+ gfx::Transform::kSkipInitialization);
+ if (!screen_space_transform.GetInverse(&inverse_screen_space_transform))
+ return false;
+
+ // Transform the hit test point from screen space to the local space of the
+ // given region.
+ bool clipped = false;
+ gfx::PointF hit_test_point_in_content_space = MathUtil::ProjectPoint(
+ inverse_screen_space_transform, screen_space_point, &clipped);
+ gfx::PointF hit_test_point_in_layer_space =
+ gfx::ScalePoint(hit_test_point_in_content_space,
+ 1.f / layer_content_scale_x,
+ 1.f / layer_content_scale_y);
+
+ // If ProjectPoint could not project to a valid value, then we assume that
+ // this point doesn't hit this region.
+ if (clipped)
+ return false;
+
+ return layer_space_region.Contains(
+ gfx::ToRoundedPoint(hit_test_point_in_layer_space));
+}
+
+static bool PointIsClippedBySurfaceOrClipRect(
+ const gfx::PointF& screen_space_point,
+ LayerImpl* layer) {
+ LayerImpl* current_layer = layer;
+
+ // Walk up the layer tree and hit-test any render_surfaces and any layer
+ // clip rects that are active.
+ while (current_layer) {
+ if (current_layer->render_surface() &&
+ !PointHitsRect(
+ screen_space_point,
+ current_layer->render_surface()->screen_space_transform(),
+ current_layer->render_surface()->content_rect(),
+ NULL))
+ return true;
+
+ // Note that drawable content rects are actually in target surface space, so
+ // the transform we have to provide is the target surface's
+ // screen_space_transform.
+ LayerImpl* render_target = current_layer->render_target();
+ if (LayerClipsSubtree(current_layer) &&
+ !PointHitsRect(
+ screen_space_point,
+ render_target->render_surface()->screen_space_transform(),
+ current_layer->drawable_content_rect(),
+ NULL))
+ return true;
+
+ current_layer = current_layer->parent();
+ }
+
+ // If we have finished walking all ancestors without having already exited,
+ // then the point is not clipped by any ancestors.
+ return false;
+}
+
+static bool PointHitsLayer(LayerImpl* layer,
+ const gfx::PointF& screen_space_point,
+ float* distance_to_intersection) {
+ gfx::RectF content_rect(layer->content_bounds());
+ if (!PointHitsRect(screen_space_point,
+ layer->screen_space_transform(),
+ content_rect,
+ distance_to_intersection))
+ return false;
+
+ // At this point, we think the point does hit the layer, but we need to walk
+ // up the parents to ensure that the layer was not clipped in such a way
+ // that the hit point actually should not hit the layer.
+ if (PointIsClippedBySurfaceOrClipRect(screen_space_point, layer))
+ return false;
+
+ // Skip the HUD layer.
+ if (layer == layer->layer_tree_impl()->hud_layer())
+ return false;
+
+ return true;
+}
+
+struct FindClosestMatchingLayerDataForRecursion {
+ FindClosestMatchingLayerDataForRecursion()
+ : closest_match(NULL),
+ closest_distance(-std::numeric_limits<float>::infinity()) {}
+ LayerImpl* closest_match;
+ // Note that the positive z-axis points towards the camera, so bigger means
+ // closer in this case, counterintuitively.
+ float closest_distance;
+};
+
+template <typename Functor>
+static void FindClosestMatchingLayer(
+ const gfx::PointF& screen_space_point,
+ LayerImpl* layer,
+ const Functor& func,
+ FindClosestMatchingLayerDataForRecursion* data_for_recursion) {
+ for (int i = layer->children().size() - 1; i >= 0; --i) {
+ FindClosestMatchingLayer(
+ screen_space_point, layer->children()[i], func, data_for_recursion);
+ }
+
+ float distance_to_intersection = 0.f;
+ if (func(layer) &&
+ PointHitsLayer(layer, screen_space_point, &distance_to_intersection) &&
+ ((!data_for_recursion->closest_match ||
+ distance_to_intersection > data_for_recursion->closest_distance))) {
+ data_for_recursion->closest_distance = distance_to_intersection;
+ data_for_recursion->closest_match = layer;
+ }
+}
+
+static bool ScrollsAnyDrawnRenderSurfaceLayerListMember(LayerImpl* layer) {
+ if (!layer->scrollable())
+ return false;
+ if (layer->IsDrawnRenderSurfaceLayerListMember())
+ return true;
+ if (!layer->scroll_children())
+ return false;
+ for (std::set<LayerImpl*>::const_iterator it =
+ layer->scroll_children()->begin();
+ it != layer->scroll_children()->end();
+ ++it) {
+ if ((*it)->IsDrawnRenderSurfaceLayerListMember())
+ return true;
+ }
+ return false;
+}
+
+struct FindScrollingLayerFunctor {
+ bool operator()(LayerImpl* layer) const {
+ return ScrollsAnyDrawnRenderSurfaceLayerListMember(layer);
+ }
+};
+
+LayerImpl* LayerTreeImpl::FindFirstScrollingLayerThatIsHitByPoint(
+ const gfx::PointF& screen_space_point) {
+ FindClosestMatchingLayerDataForRecursion data_for_recursion;
+ FindClosestMatchingLayer(screen_space_point,
+ root_layer(),
+ FindScrollingLayerFunctor(),
+ &data_for_recursion);
+ return data_for_recursion.closest_match;
+}
+
+struct HitTestVisibleScrollableOrTouchableFunctor {
+ bool operator()(LayerImpl* layer) const {
+ return layer->IsDrawnRenderSurfaceLayerListMember() ||
+ ScrollsAnyDrawnRenderSurfaceLayerListMember(layer) ||
+ !layer->touch_event_handler_region().IsEmpty() ||
+ layer->have_wheel_event_handlers();
+ }
+};
+
+LayerImpl* LayerTreeImpl::FindLayerThatIsHitByPoint(
+ const gfx::PointF& screen_space_point) {
+ FindClosestMatchingLayerDataForRecursion data_for_recursion;
+ FindClosestMatchingLayer(screen_space_point,
+ root_layer(),
+ HitTestVisibleScrollableOrTouchableFunctor(),
+ &data_for_recursion);
+ return data_for_recursion.closest_match;
+}
+
+static bool LayerHasTouchEventHandlersAt(const gfx::PointF& screen_space_point,
+ LayerImpl* layer_impl) {
+ if (layer_impl->touch_event_handler_region().IsEmpty())
+ return false;
+
+ if (!PointHitsRegion(screen_space_point,
+ layer_impl->screen_space_transform(),
+ layer_impl->touch_event_handler_region(),
+ layer_impl->contents_scale_x(),
+ layer_impl->contents_scale_y()))
+ return false;
+
+ // At this point, we think the point does hit the touch event handler region
+ // on the layer, but we need to walk up the parents to ensure that the layer
+ // was not clipped in such a way that the hit point actually should not hit
+ // the layer.
+ if (PointIsClippedBySurfaceOrClipRect(screen_space_point, layer_impl))
+ return false;
+
+ return true;
+}
+
+struct FindTouchEventLayerFunctor {
+ bool operator()(LayerImpl* layer) const {
+ return LayerHasTouchEventHandlersAt(screen_space_point, layer);
+ }
+ const gfx::PointF screen_space_point;
+};
+
+LayerImpl* LayerTreeImpl::FindLayerThatIsHitByPointInTouchHandlerRegion(
+ const gfx::PointF& screen_space_point) {
+ FindTouchEventLayerFunctor func = {screen_space_point};
+ FindClosestMatchingLayerDataForRecursion data_for_recursion;
+ FindClosestMatchingLayer(
+ screen_space_point, root_layer(), func, &data_for_recursion);
+ return data_for_recursion.closest_match;
+}
+
} // namespace cc
« no previous file with comments | « cc/trees/layer_tree_impl.h ('k') | cc/trees/layer_tree_impl_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698