/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{BorderRadius, ClipMode, HitTestResultItem, HitTestResult, ItemTag, PrimitiveFlags}; use api::{PipelineId, ApiHitTester}; use api::units::*; usecrate::clip::{rounded_rectangle_contains_point, ClipNodeId, ClipTreeBuilder}; usecrate::clip::{polygon_contains_point, ClipItemKey, ClipItemKeyKind}; usecrate::prim_store::PolygonKey; usecrate::scene_builder_thread::Interners; usecrate::spatial_tree::{SpatialNodeIndex, SpatialTree}; usecrate::internal_types::{FastHashMap, LayoutPrimitiveInfo}; use std::sync::{Arc, Mutex}; usecrate::util::LayoutToWorldFastTransform;
pubstruct SharedHitTester { // We don't really need a mutex here. We could do with some sort of // atomic-atomic-ref-counted pointer (an Arc which would let the pointer // be swapped atomically like an AtomicPtr). // In practive this shouldn't cause performance issues, though.
hit_tester: Mutex<Arc<HitTester>>,
}
/// A copy of important spatial node data to use during hit testing. This a copy of /// data from the SpatialTree that will persist as a new frame is under construction, /// allowing hit tests consistent with the currently rendered frame. #[derive(MallocSizeOf)] struct HitTestSpatialNode { /// The pipeline id of this node.
pipeline_id: PipelineId,
/// World transform for content transformed by this node.
world_content_transform: LayoutToWorldFastTransform,
/// World viewport transform for content transformed by this node.
world_viewport_transform: LayoutToWorldFastTransform,
}
#[derive(MallocSizeOf)] struct HitTestClipNode { /// A particular point must be inside all of these regions to be considered clipped in /// for the purposes of a hit test.
region: HitTestRegion, /// The positioning node for this clip
spatial_node_index: SpatialNodeIndex, /// Parent clip node
parent: ClipNodeId,
}
impl HitTestClipNode { fn new(
item: &ClipItemKey,
clip_rect: LayoutRect,
interners: &Interners,
parent: ClipNodeId,
spatial_node_index: SpatialNodeIndex,
) -> Self { let region = match item.kind {
ClipItemKeyKind::Rectangle(mode) => {
HitTestRegion::Rectangle(clip_rect, mode)
}
ClipItemKeyKind::RoundedRectangle(radius, mode) => {
HitTestRegion::RoundedRectangle(clip_rect, radius.into(), mode)
}
ClipItemKeyKind::ImageMask(_, polygon_handle) => { iflet Some(handle) = polygon_handle { // Retrieve the polygon data from the interner. let polygon = &interners.polygon[handle];
HitTestRegion::Polygon(clip_rect, *polygon)
} else {
HitTestRegion::Rectangle(clip_rect, ClipMode::Clip)
}
}
};
/// Statistics about allocation sizes of current hit tester, /// used to pre-allocate size of the next hit tester. pubstruct HitTestingSceneStats { pub clip_nodes_count: usize, pub items_count: usize,
}
/// Defines the immutable part of a hit tester for a given scene. /// The hit tester is recreated each time a frame is built, since /// it relies on the current values of the spatial tree. /// However, the clip chain and item definitions don't change, /// so they are created once per scene, and shared between /// hit tester instances via Arc. #[derive(MallocSizeOf)] pubstruct HitTestingScene {
clip_nodes: FastHashMap<ClipNodeId, HitTestClipNode>,
/// List of hit testing primitives.
items: Vec<HitTestingItem>,
}
impl HitTestingScene { /// Construct a new hit testing scene, pre-allocating to size /// provided by previous scene stats. pubfn new(stats: &HitTestingSceneStats) -> Self {
HitTestingScene {
clip_nodes: FastHashMap::default(),
items: Vec::with_capacity(stats.items_count),
}
}
/// Get stats about the current scene allocation sizes. pubfn get_stats(&self) -> HitTestingSceneStats {
HitTestingSceneStats {
clip_nodes_count: 0,
items_count: self.items.len(),
}
}
if !self.clip_nodes.contains_key(&clip_node_id) { let src_clip_node = clip_tree_builder.get_node(clip_node_id); let clip_item = &interners.clip[src_clip_node.handle];
// SNAPTODO: Scene-build hit-test scene captures the unsnapped // clip rect. Snapping happens against frame-time spatial state // which isn't available here; audit hit-test consumers to // confirm using the unsnapped value is correct for hit // semantics, or apply a frame-time snap before testing. let clip_node = HitTestClipNode::new(
&clip_item.key,
src_clip_node.unsnapped_clip_rect,
interners,
src_clip_node.parent,
src_clip_node.spatial_node_index,
);
spatial_tree.visit_nodes(|index, node| { //TODO: avoid inverting more than necessary: // - if the coordinate system is non-invertible, no need to try any of these concrete transforms // - if there are other places where inversion is needed, let's not repeat the step
// For each hit test primitive for item inself.scene.items.iter().rev() { let scroll_node = &self.spatial_nodes[&item.spatial_node_index]; let pipeline_id = scroll_node.pipeline_id;
// Update the cached point in layer space, if the spatial node // changed since last primitive. if item.spatial_node_index != current_spatial_node_index {
point_in_layer = scroll_node
.world_content_transform
.inverse()
.and_then(|inverted| inverted.project_point2d(test.point));
current_spatial_node_index = item.spatial_node_index;
}
// Only consider hit tests on transformable layers. let point_in_layer = match point_in_layer {
Some(p) => p,
None => continue,
};
// If the item's rect or clip rect don't contain this point, it's // not a valid hit. if !item.rect.contains(point_in_layer) { continue;
}
// See if any of the clips for this primitive cull out the item. letmut current_clip_node_id = item.clip_node_id; letmut is_valid = true;
while current_clip_node_id != ClipNodeId::NONE { let clip_node = &self.scene.clip_nodes[¤t_clip_node_id];
// Don't hit items with backface-visibility:hidden if they are facing the back. if !item.is_backface_visible && scroll_node.world_content_transform.is_backface_visible() { continue;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.