mirror of
https://github.com/typst/typst
synced 2025-08-04 02:07:56 +08:00
perf: optimize bbox computation
This commit is contained in:
parent
35f44f79de
commit
6d0c4c620d
@ -42,6 +42,7 @@ impl TextItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The bounding box of the text run.
|
/// The bounding box of the text run.
|
||||||
|
#[comemo::memoize]
|
||||||
pub fn bbox(&self) -> Rect {
|
pub fn bbox(&self) -> Rect {
|
||||||
let mut min = Point::splat(Abs::inf());
|
let mut min = Point::splat(Abs::inf());
|
||||||
let mut max = Point::splat(-Abs::inf());
|
let mut max = Point::splat(-Abs::inf());
|
||||||
|
@ -295,6 +295,9 @@ pub(crate) fn handle_end(
|
|||||||
// Assign a new link id, so a new link annotation will be created.
|
// Assign a new link id, so a new link annotation will be created.
|
||||||
*id = gc.tags.next_link_id();
|
*id = gc.tags.next_link_id();
|
||||||
}
|
}
|
||||||
|
if let Some(bbox) = kind.bbox_mut() {
|
||||||
|
bbox.reset();
|
||||||
|
}
|
||||||
|
|
||||||
broken_entries.push(StackEntry {
|
broken_entries.push(StackEntry {
|
||||||
loc: entry.loc,
|
loc: entry.loc,
|
||||||
@ -454,8 +457,8 @@ pub(crate) fn update_bbox(
|
|||||||
fc: &FrameContext,
|
fc: &FrameContext,
|
||||||
compute_bbox: impl FnOnce() -> Rect,
|
compute_bbox: impl FnOnce() -> Rect,
|
||||||
) {
|
) {
|
||||||
if gc.options.standards.config.validator() == Validator::UA1
|
if let Some(bbox) = gc.tags.stack.find_parent_bbox()
|
||||||
&& let Some(bbox) = gc.tags.stack.find_parent_bbox()
|
&& gc.options.standards.config.validator() == Validator::UA1
|
||||||
{
|
{
|
||||||
bbox.expand_frame(fc, compute_bbox());
|
bbox.expand_frame(fc, compute_bbox());
|
||||||
}
|
}
|
||||||
@ -485,7 +488,7 @@ pub(crate) struct Tags {
|
|||||||
impl Tags {
|
impl Tags {
|
||||||
pub(crate) fn new() -> Self {
|
pub(crate) fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
stack: TagStack(Vec::new()),
|
stack: TagStack::new(),
|
||||||
placeholders: Placeholders(Vec::new()),
|
placeholders: Placeholders(Vec::new()),
|
||||||
footnotes: HashMap::new(),
|
footnotes: HashMap::new(),
|
||||||
in_artifact: None,
|
in_artifact: None,
|
||||||
@ -557,47 +560,65 @@ impl Tags {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct TagStack(Vec<StackEntry>);
|
pub(crate) struct TagStack {
|
||||||
|
items: Vec<StackEntry>,
|
||||||
|
/// The index of the topmost stack entry that has a bbox.
|
||||||
|
bbox_idx: Option<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
impl<I: SliceIndex<[StackEntry]>> std::ops::Index<I> for TagStack {
|
impl<I: SliceIndex<[StackEntry]>> std::ops::Index<I> for TagStack {
|
||||||
type Output = I::Output;
|
type Output = I::Output;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn index(&self, index: I) -> &Self::Output {
|
fn index(&self, index: I) -> &Self::Output {
|
||||||
std::ops::Index::index(&self.0, index)
|
std::ops::Index::index(&self.items, index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: SliceIndex<[StackEntry]>> std::ops::IndexMut<I> for TagStack {
|
impl<I: SliceIndex<[StackEntry]>> std::ops::IndexMut<I> for TagStack {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn index_mut(&mut self, index: I) -> &mut Self::Output {
|
fn index_mut(&mut self, index: I) -> &mut Self::Output {
|
||||||
std::ops::IndexMut::index_mut(&mut self.0, index)
|
std::ops::IndexMut::index_mut(&mut self.items, index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TagStack {
|
impl TagStack {
|
||||||
|
pub(crate) fn new() -> Self {
|
||||||
|
Self { items: Vec::new(), bbox_idx: None }
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn len(&self) -> usize {
|
pub(crate) fn len(&self) -> usize {
|
||||||
self.0.len()
|
self.items.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn last(&self) -> Option<&StackEntry> {
|
pub(crate) fn last(&self) -> Option<&StackEntry> {
|
||||||
self.0.last()
|
self.items.last()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn last_mut(&mut self) -> Option<&mut StackEntry> {
|
pub(crate) fn last_mut(&mut self) -> Option<&mut StackEntry> {
|
||||||
self.0.last_mut()
|
self.items.last_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn iter(&self) -> std::slice::Iter<StackEntry> {
|
pub(crate) fn iter(&self) -> std::slice::Iter<StackEntry> {
|
||||||
self.0.iter()
|
self.items.iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn push(&mut self, entry: StackEntry) {
|
pub(crate) fn push(&mut self, entry: StackEntry) {
|
||||||
self.0.push(entry);
|
if entry.kind.bbox().is_some() {
|
||||||
|
self.bbox_idx = Some(self.len());
|
||||||
|
}
|
||||||
|
self.items.push(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn extend(&mut self, iter: impl IntoIterator<Item = StackEntry>) {
|
pub(crate) fn extend(&mut self, iter: impl IntoIterator<Item = StackEntry>) {
|
||||||
self.0.extend(iter);
|
let start = self.len();
|
||||||
|
self.items.extend(iter);
|
||||||
|
let last_bbox_offset = self.items[start..]
|
||||||
|
.iter()
|
||||||
|
.rposition(|entry| entry.kind.bbox().is_some());
|
||||||
|
if let Some(offset) = last_bbox_offset {
|
||||||
|
self.bbox_idx = Some(start + offset);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove the last stack entry if the predicate returns true.
|
/// Remove the last stack entry if the predicate returns true.
|
||||||
@ -606,24 +627,30 @@ impl TagStack {
|
|||||||
&mut self,
|
&mut self,
|
||||||
mut predicate: impl FnMut(&mut StackEntry) -> bool,
|
mut predicate: impl FnMut(&mut StackEntry) -> bool,
|
||||||
) -> Option<StackEntry> {
|
) -> Option<StackEntry> {
|
||||||
let last = self.0.last_mut()?;
|
let last = self.items.last_mut()?;
|
||||||
if predicate(last) { self.pop() } else { None }
|
if predicate(last) { self.pop() } else { None }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove the last stack entry.
|
/// Remove the last stack entry.
|
||||||
/// This takes care of updating the parent bboxes.
|
/// This takes care of updating the parent bboxes.
|
||||||
pub(crate) fn pop(&mut self) -> Option<StackEntry> {
|
pub(crate) fn pop(&mut self) -> Option<StackEntry> {
|
||||||
let entry = self.0.pop()?;
|
let removed = self.items.pop()?;
|
||||||
if let Some((page_idx, rect)) = entry.kind.bbox().and_then(|b| b.rect)
|
|
||||||
&& let Some(bbox) = self.find_parent_bbox()
|
let Some(inner_bbox) = removed.kind.bbox() else { return Some(removed) };
|
||||||
{
|
|
||||||
bbox.expand_page(page_idx, rect);
|
self.bbox_idx = self.items.iter_mut().enumerate().rev().find_map(|(i, entry)| {
|
||||||
|
let outer_bbox = entry.kind.bbox_mut()?;
|
||||||
|
if let Some((page_idx, rect)) = inner_bbox.rect {
|
||||||
|
outer_bbox.expand_page(page_idx, rect);
|
||||||
}
|
}
|
||||||
Some(entry)
|
Some(i)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(removed)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn parent(&mut self) -> Option<&mut StackEntryKind> {
|
pub(crate) fn parent(&mut self) -> Option<&mut StackEntryKind> {
|
||||||
self.0.last_mut().map(|e| &mut e.kind)
|
self.items.last_mut().map(|e| &mut e.kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn parent_table(&mut self) -> Option<&mut TableCtx> {
|
pub(crate) fn parent_table(&mut self) -> Option<&mut TableCtx> {
|
||||||
@ -641,7 +668,7 @@ impl TagStack {
|
|||||||
pub(crate) fn parent_outline(
|
pub(crate) fn parent_outline(
|
||||||
&mut self,
|
&mut self,
|
||||||
) -> Option<(&mut OutlineCtx, &mut Vec<TagNode>)> {
|
) -> Option<(&mut OutlineCtx, &mut Vec<TagNode>)> {
|
||||||
self.0.last_mut().and_then(|e| {
|
self.items.last_mut().and_then(|e| {
|
||||||
let ctx = e.kind.as_outline_mut()?;
|
let ctx = e.kind.as_outline_mut()?;
|
||||||
Some((ctx, &mut e.nodes))
|
Some((ctx, &mut e.nodes))
|
||||||
})
|
})
|
||||||
@ -650,7 +677,7 @@ impl TagStack {
|
|||||||
pub(crate) fn find_parent_link(
|
pub(crate) fn find_parent_link(
|
||||||
&mut self,
|
&mut self,
|
||||||
) -> Option<(LinkId, &Packed<LinkMarker>, &mut Vec<TagNode>)> {
|
) -> Option<(LinkId, &Packed<LinkMarker>, &mut Vec<TagNode>)> {
|
||||||
self.0.iter_mut().rev().find_map(|e| {
|
self.items.iter_mut().rev().find_map(|e| {
|
||||||
let (link_id, link) = e.kind.as_link()?;
|
let (link_id, link) = e.kind.as_link()?;
|
||||||
Some((link_id, link, &mut e.nodes))
|
Some((link_id, link, &mut e.nodes))
|
||||||
})
|
})
|
||||||
@ -658,7 +685,7 @@ impl TagStack {
|
|||||||
|
|
||||||
/// Finds the first parent that has a bounding box.
|
/// Finds the first parent that has a bounding box.
|
||||||
pub(crate) fn find_parent_bbox(&mut self) -> Option<&mut BBoxCtx> {
|
pub(crate) fn find_parent_bbox(&mut self) -> Option<&mut BBoxCtx> {
|
||||||
self.0.iter_mut().rev().find_map(|e| e.kind.bbox_mut())
|
self.items[self.bbox_idx?].kind.bbox_mut()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -836,6 +863,10 @@ impl BBoxCtx {
|
|||||||
Self { rect: None, multi_page: false }
|
Self { rect: None, multi_page: false }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn reset(&mut self) {
|
||||||
|
*self = Self::new();
|
||||||
|
}
|
||||||
|
|
||||||
/// Expand the bounding box with a `rect` relative to the current frame
|
/// Expand the bounding box with a `rect` relative to the current frame
|
||||||
/// context transform.
|
/// context transform.
|
||||||
pub(crate) fn expand_frame(&mut self, fc: &FrameContext, rect: Rect) {
|
pub(crate) fn expand_frame(&mut self, fc: &FrameContext, rect: Rect) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user