diff --git a/webrender/src/clip_scroll_node.rs b/webrender/src/clip_scroll_node.rs index 4052a056ff..5d1064d0a4 100644 --- a/webrender/src/clip_scroll_node.rs +++ b/webrender/src/clip_scroll_node.rs @@ -5,12 +5,10 @@ use api::{DevicePixelScale, ExternalScrollId, LayoutPixel, LayoutPoint, LayoutRect, LayoutSize}; use api::{LayoutVector2D, LayoutTransform, PipelineId, PropertyBinding}; use api::{ScrollClamping, ScrollLocation, ScrollSensitivity, StickyOffsetBounds}; -use api::WorldPoint; use clip::{ClipChain, ClipChainNode, ClipSourcesHandle, ClipStore, ClipWorkItem}; use clip_scroll_tree::{ClipChainIndex, ClipScrollNodeIndex, CoordinateSystemId}; use clip_scroll_tree::TransformUpdateState; use euclid::SideOffsets2D; -use geometry::ray_intersects_rect; use gpu_cache::GpuCache; use gpu_types::{ClipScrollNodeIndex as GPUClipScrollNodeIndex, ClipScrollNodeData}; use resource_cache::ResourceCache; @@ -712,29 +710,6 @@ impl ClipScrollNode { scrolling.offset != original_layer_scroll_offset } - pub fn ray_intersects_node(&self, cursor: &WorldPoint) -> bool { - let inv = match self.world_viewport_transform.inverse() { - Some(inv) => inv, - None => return false, - }; - - let z0 = -10000.0; - let z1 = 10000.0; - - let p0 = inv.transform_point3d(&cursor.extend(z0)); - let p1 = inv.transform_point3d(&cursor.extend(z1)); - - if self.scrollable_size() == LayoutSize::zero() { - return false; - } - - ray_intersects_rect( - p0.to_untyped(), - p1.to_untyped(), - self.local_viewport_rect.to_untyped(), - ) - } - pub fn scroll_offset(&self) -> LayoutVector2D { match self.node_type { NodeType::ScrollFrame(ref scrolling) => scrolling.offset, diff --git a/webrender/src/clip_scroll_tree.rs b/webrender/src/clip_scroll_tree.rs index ae5a9ac544..067cc8fb8e 100644 --- a/webrender/src/clip_scroll_tree.rs +++ b/webrender/src/clip_scroll_tree.rs @@ -133,36 +133,6 @@ impl ClipScrollTree { TOPMOST_SCROLL_NODE_INDEX } - fn find_scrolling_node_at_point_in_node( - &self, - cursor: &WorldPoint, - index: ClipScrollNodeIndex, - ) -> Option { - let node = &self.nodes[index.0]; - for child_index in node.children.iter().rev() { - let found_index = self.find_scrolling_node_at_point_in_node(cursor, *child_index); - if found_index.is_some() { - return found_index; - } - } - - match node.node_type { - NodeType::ScrollFrame(state) if state.sensitive_to_input_events() => {} - _ => return None, - } - - if node.ray_intersects_node(cursor) { - Some(index) - } else { - None - } - } - - pub fn find_scrolling_node_at_point(&self, cursor: &WorldPoint) -> ClipScrollNodeIndex { - self.find_scrolling_node_at_point_in_node(cursor, self.root_reference_frame_index()) - .unwrap_or(self.topmost_scroll_node_index()) - } - pub fn get_scroll_node_state(&self) -> Vec { let mut result = vec![]; for node in &self.nodes { @@ -214,16 +184,31 @@ impl ClipScrollTree { false } - pub fn scroll( + fn find_nearest_scrolling_ancestor( + &self, + index: Option + ) -> ClipScrollNodeIndex { + let index = match index { + Some(index) => index, + None => return self.topmost_scroll_node_index(), + }; + + let node = &self.nodes[index.0]; + match node.node_type { + NodeType::ScrollFrame(state) if state.sensitive_to_input_events() => index, + _ => self.find_nearest_scrolling_ancestor(node.parent) + } + } + + pub fn scroll_nearest_scrolling_ancestor( &mut self, scroll_location: ScrollLocation, - cursor: WorldPoint, + node_index: Option, ) -> bool { if self.nodes.is_empty() { return false; } - - let node_index = self.find_scrolling_node_at_point(&cursor); + let node_index = self.find_nearest_scrolling_ancestor(node_index); self.nodes[node_index.0].scroll(scroll_location) } diff --git a/webrender/src/geometry.rs b/webrender/src/geometry.rs deleted file mode 100644 index b8ca5b343f..0000000000 --- a/webrender/src/geometry.rs +++ /dev/null @@ -1,113 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -use euclid::{Point3D, Rect}; - -/* - A naive port of "An Efficient and Robust Ray–Box Intersection Algorithm" - from https://www.cs.utah.edu/~awilliam/box/box.pdf - - This should be cleaned up and extracted into more useful types! - */ - -// Assumes rect is in the z=0 plane! -pub fn ray_intersects_rect( - ray_origin: Point3D, - ray_end: Point3D, - rect: Rect, -) -> bool { - let mut dir = ray_end - ray_origin; - let len = ((dir.x * dir.x) + (dir.y * dir.y) + (dir.z * dir.z)).sqrt(); - dir.x = dir.x / len; - dir.y = dir.y / len; - dir.z = dir.z / len; - let inv_direction = Point3D::new(1.0 / dir.x, 1.0 / dir.y, 1.0 / dir.z); - - let sign = [ - if inv_direction.x < 0.0 { 1 } else { 0 }, - if inv_direction.y < 0.0 { 1 } else { 0 }, - if inv_direction.z < 0.0 { 1 } else { 0 }, - ]; - - let parameters = [rect.origin.to_3d(), rect.bottom_right().to_3d()]; - - let mut tmin = (parameters[sign[0]].x - ray_origin.x) * inv_direction.x; - let mut tmax = (parameters[1 - sign[0]].x - ray_origin.x) * inv_direction.x; - let tymin = (parameters[sign[1]].y - ray_origin.y) * inv_direction.y; - let tymax = (parameters[1 - sign[1]].y - ray_origin.y) * inv_direction.y; - if (tmin > tymax) || (tymin > tmax) { - return false; - } - if tymin > tmin { - tmin = tymin; - } - if tymax < tmax { - tmax = tymax; - } - let tzmin = (parameters[sign[2]].z - ray_origin.z) * inv_direction.z; - let tzmax = (parameters[1 - sign[2]].z - ray_origin.z) * inv_direction.z; - if (tmin > tzmax) || (tzmin > tmax) { - return false; - } - - // Don't care about where on the ray it hits... - true - - /* - if tzmin > tmin { - tmin = tzmin; - } - if tzmax < tmax { - tmax = tzmax; - } - - let t0 = 0.0; - let t1 = len; - - (tmin < t1) && (tmax > t0) - */ -} - -/* -pub fn circle_contains_rect(circle_center: &Point2D, - radius: f32, - rect: &Rect) -> bool { - let dx = (circle_center.x - rect.origin.x).max( - rect.origin.x + rect.size.width - circle_center.x - ); - let dy = (circle_center.y - rect.origin.y).max( - rect.origin.y + rect.size.height - circle_center.y - ); - radius * radius >= dx * dx + dy * dy -} - -pub fn rect_intersects_circle(circle_center: &Point2D, - radius: f32, - rect: &Rect) -> bool { - let circle_distance_x = (circle_center.x - (rect.origin.x + rect.size.width * 0.5)).abs(); - let circle_distance_y = (circle_center.y - (rect.origin.y + rect.size.height * 0.5)).abs(); - - if circle_distance_x > rect.size.width * 0.5 + radius { - return false - } - if circle_distance_y > rect.size.height * 0.5 + radius { - return false - } - - if circle_distance_x <= rect.size.width * 0.5 { - return true; - } - if circle_distance_y <= rect.size.height * 0.5 { - return true; - } - - let corner_distance_sq = - (circle_distance_x - rect.size.width * 0.5) * - (circle_distance_x - rect.size.width * 0.5) + - (circle_distance_y - rect.size.height * 0.5) * - (circle_distance_y - rect.size.height * 0.5); - - corner_distance_sq <= radius * radius -} -*/ diff --git a/webrender/src/hit_test.rs b/webrender/src/hit_test.rs index f469849f7a..c5928ea67d 100644 --- a/webrender/src/hit_test.rs +++ b/webrender/src/hit_test.rs @@ -223,6 +223,39 @@ impl HitTester { true } + pub fn find_node_under_point(&self, mut test: HitTest) -> Option { + let point = test.get_absolute_point(self); + + for &HitTestingRun(ref items, ref clip_and_scroll) in self.runs.iter().rev() { + let scroll_node_id = clip_and_scroll.scroll_node_id; + let scroll_node = &self.nodes[scroll_node_id.0]; + let transform = scroll_node.world_content_transform; + let point_in_layer = match transform.inverse() { + Some(inverted) => inverted.transform_point2d(&point), + None => continue, + }; + + let mut clipped_in = false; + for item in items.iter().rev() { + if !item.rect.contains(&point_in_layer) || + !item.clip_rect.contains(&point_in_layer) { + continue; + } + + let clip_chain_index = clip_and_scroll.clip_chain_index; + clipped_in |= + self.is_point_clipped_in_for_clip_chain(point, clip_chain_index, &mut test); + if !clipped_in { + break; + } + + return Some(scroll_node_id); + } + } + + None + } + pub fn hit_test(&self, mut test: HitTest) -> HitTestResult { let point = test.get_absolute_point(self); diff --git a/webrender/src/lib.rs b/webrender/src/lib.rs index 482dd2cea0..6413f8688b 100644 --- a/webrender/src/lib.rs +++ b/webrender/src/lib.rs @@ -76,7 +76,6 @@ mod frame_builder; mod freelist; #[cfg(any(target_os = "macos", target_os = "windows"))] mod gamma_lut; -mod geometry; mod glyph_cache; mod glyph_rasterizer; mod gpu_cache; diff --git a/webrender/src/render_backend.rs b/webrender/src/render_backend.rs index c94d87cdee..3c00f1e0ee 100644 --- a/webrender/src/render_backend.rs +++ b/webrender/src/render_backend.rs @@ -6,15 +6,15 @@ use api::{ApiMsg, BuiltDisplayList, ClearCache, DebugCommand}; #[cfg(feature = "debugger")] use api::{BuiltDisplayListIter, SpecificDisplayItem}; use api::{DeviceIntPoint, DevicePixelScale, DeviceUintPoint, DeviceUintRect, DeviceUintSize}; -use api::{DocumentId, DocumentLayer, ExternalScrollId, FrameMsg, HitTestResult}; +use api::{DocumentId, DocumentLayer, ExternalScrollId, FrameMsg, HitTestFlags, HitTestResult}; use api::{IdNamespace, LayoutPoint, PipelineId, RenderNotifier, SceneMsg, ScrollClamping}; -use api::{ScrollLocation, ScrollNodeState, TransactionMsg, WorldPoint}; +use api::{ScrollLocation, ScrollNodeState, TransactionMsg}; use api::channel::{MsgReceiver, Payload}; #[cfg(feature = "capture")] use api::CaptureBits; #[cfg(feature = "replay")] use api::CapturedDocument; -use clip_scroll_tree::ClipScrollTree; +use clip_scroll_tree::{ClipScrollNodeIndex, ClipScrollTree}; #[cfg(feature = "debugger")] use debug_server; use display_list_flattener::DisplayListFlattener; @@ -315,12 +315,12 @@ impl Document { } /// Returns true if any nodes actually changed position or false otherwise. - pub fn scroll( + pub fn scroll_nearest_scrolling_ancestor( &mut self, scroll_location: ScrollLocation, - cursor: WorldPoint, + scroll_node_index: Option, ) -> bool { - self.clip_scroll_tree.scroll(scroll_location, cursor) + self.clip_scroll_tree.scroll_nearest_scrolling_ancestor(scroll_location, scroll_node_index) } /// Returns true if the node actually changed position or false otherwise. @@ -620,9 +620,24 @@ impl RenderBackend { FrameMsg::Scroll(delta, cursor) => { profile_scope!("Scroll"); - let should_render = doc.scroll(delta, cursor) - && doc.render_on_scroll == Some(true); + let mut should_render = true; + let node_index = match doc.hit_tester { + Some(ref hit_tester) => { + // Ideally we would call doc.scroll_nearest_scrolling_ancestor here, but + // we need have to avoid a double-borrow. + let test = HitTest::new(None, cursor, HitTestFlags::empty()); + hit_tester.find_node_under_point(test) + } + None => { + should_render = false; + None + } + }; + let should_render = + should_render && + doc.scroll_nearest_scrolling_ancestor(delta, node_index) && + doc.render_on_scroll == Some(true); DocumentOps { scroll: true, render: should_render, diff --git a/webrender/src/util.rs b/webrender/src/util.rs index b54b2fa685..282c4b9ed2 100644 --- a/webrender/src/util.rs +++ b/webrender/src/util.rs @@ -5,8 +5,8 @@ use api::{BorderRadius, DeviceIntPoint, DeviceIntRect, DeviceIntSize, DevicePixelScale}; use api::{DevicePoint, DeviceRect, DeviceSize, LayoutPixel, LayoutPoint, LayoutRect, LayoutSize}; use api::{WorldPixel, WorldRect}; -use euclid::{Point2D, Rect, Size2D, TypedPoint2D, TypedPoint3D, TypedRect, TypedSize2D}; -use euclid::{TypedTransform2D, TypedTransform3D, TypedVector2D}; +use euclid::{Point2D, Rect, Size2D, TypedPoint2D, TypedRect, TypedSize2D, TypedTransform2D}; +use euclid::{TypedTransform3D, TypedVector2D}; use num_traits::Zero; use std::{i32, f32}; @@ -456,15 +456,6 @@ impl FastTransform { } } - #[inline(always)] - pub fn transform_point3d(&self, point: &TypedPoint3D) -> TypedPoint3D { - match *self { - FastTransform::Offset(offset) => - TypedPoint3D::new(point.x + offset.x, point.y + offset.y, point.z), - FastTransform::Transform { ref transform, .. } => transform.transform_point3d(point), - } - } - #[inline(always)] pub fn transform_rect(&self, rect: &TypedRect) -> TypedRect { match *self { @@ -557,4 +548,4 @@ impl From> for FastTransform { pub type LayoutFastTransform = FastTransform; pub type LayoutToWorldFastTransform = FastTransform; -pub type WorldToLayoutFastTransform = FastTransform; \ No newline at end of file +pub type WorldToLayoutFastTransform = FastTransform; diff --git a/webrender_api/src/api.rs b/webrender_api/src/api.rs index 68829437a4..7738ba7db3 100644 --- a/webrender_api/src/api.rs +++ b/webrender_api/src/api.rs @@ -280,11 +280,7 @@ impl Transaction { /// /// WebRender looks for the layer closest to the user /// which has `ScrollPolicy::Scrollable` set. - pub fn scroll( - &mut self, - scroll_location: ScrollLocation, - cursor: WorldPoint, - ) { + pub fn scroll(&mut self, scroll_location: ScrollLocation, cursor: WorldPoint) { self.frame_ops.push(FrameMsg::Scroll(scroll_location, cursor)); }