| Index: cc/trees/layer_tree_impl.cc
|
| diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
|
| index 962ce7de13c18d3ffa4c573380fe097e628203fa..afd6cfa052149e127aafae504fc4172be9e72650 100644
|
| --- a/cc/trees/layer_tree_impl.cc
|
| +++ b/cc/trees/layer_tree_impl.cc
|
| @@ -20,6 +20,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"
|
|
|
| @@ -995,4 +996,243 @@ const std::vector<LayerImpl*>& LayerTreeImpl::LayersWithCopyOutputRequest()
|
| return layers_with_copy_output_request_;
|
| }
|
|
|
| +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(0.f), found_match(false) {}
|
| + LayerImpl* closest_match;
|
| + // Note that the positive z-axis points towards the camera, so bigger means
|
| + // closer in this case, counterintuitively.
|
| + float closest_distance;
|
| + bool found_match;
|
| +};
|
| +
|
| +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->found_match ||
|
| + distance_to_intersection > data_for_recursion->closest_distance))) {
|
| + data_for_recursion->found_match = true;
|
| + data_for_recursion->closest_distance = distance_to_intersection;
|
| + data_for_recursion->closest_match = layer;
|
| + }
|
| +}
|
| +
|
| +struct FindScrollingLayerFunctor {
|
| + bool operator()(LayerImpl* layer) const { return layer->scrollable(); }
|
| +};
|
| +
|
| +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 HitTestVisibleFunctor {
|
| + bool operator()(LayerImpl* layer) const {
|
| + return layer->IsDrawnRenderSurfaceLayerListMember() ||
|
| + !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(),
|
| + HitTestVisibleFunctor(),
|
| + &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
|
|
|