| 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
|
|
|