mirror of
https://github.com/storytold/deckcraft.git
synced 2026-10-24 10:37:03 -06:00
Render: skip laying out table cell text that lands wholly off the target
This commit is contained in:
@@ -297,6 +297,25 @@ pub fn cell_rects(t: &Table, heights: &[f64]) -> Vec<(usize, usize, Rect)> {
|
||||
out
|
||||
}
|
||||
|
||||
/// Can a cell's text be left undrawn because it lands wholly outside the `target` (pixels)? Only
|
||||
/// for cells whose text stays inside the cell: its row grew to fit it (one row, horizontal text,
|
||||
/// not at the height cap), so nothing overflows. A margin covers italic overhang and the like.
|
||||
/// A table of thousands of rows otherwise lays out every cell's text off the slide's bottom.
|
||||
fn text_off_target(cell: &Cell, rect: Rect, m: Affine, target: (u16, u16)) -> bool {
|
||||
if cell.row_span > 1 || !matches!(cell.vertical, None | Some(TextDir::Horizontal)) || rect.height() >= 20_000.0 {
|
||||
return false;
|
||||
}
|
||||
let b = m.transform_rect_bbox(rect).inflate(64.0, 64.0);
|
||||
let view = Rect::new(0.0, 0.0, f64::from(target.0), f64::from(target.1));
|
||||
b.is_finite() && (b.x1 < view.x0 || b.y1 < view.y0 || b.x0 > view.x1 || b.y0 > view.y1)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
thread_local! {
|
||||
/// Cells whose text `draw` laid out on this thread (tests check off-slide cells are skipped).
|
||||
pub(crate) static TEXT_CELLS_DRAWN: std::cell::Cell<usize> = const { std::cell::Cell::new(0) };
|
||||
}
|
||||
|
||||
/// Draws the table; returns the box its cells cover (table space), which grows with their text.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn draw(
|
||||
@@ -399,11 +418,13 @@ pub fn draw(
|
||||
// Text.
|
||||
for (r, c, rect) in &rects {
|
||||
let Some(cell) = t.cell(*r, *c) else { continue };
|
||||
if cell.text.is_empty() {
|
||||
if cell.text.is_empty() || text_off_target(cell, *rect, m, (ctx.width(), ctx.height())) {
|
||||
continue;
|
||||
}
|
||||
let body = cell_body(rctx, t, *r, *c, cell);
|
||||
let tmp = Shape { text: Some(body.clone()), ..Default::default() };
|
||||
#[cfg(test)]
|
||||
TEXT_CELLS_DRAWN.with(|n| n.set(n.get() + 1));
|
||||
crate::draw_text(ctx, rctx, &tmp, &body, *rect, m, fields, None);
|
||||
let _ = peniko::Fill::NonZero;
|
||||
}
|
||||
|
||||
@@ -1711,3 +1711,27 @@ fn east_asian_vertical_text_stands_ideographs_upright() {
|
||||
let (w, h) = ink(deckcraft_model::text::TextDir::Vertical);
|
||||
assert!(h > w * 3, "vert: stroke on its side {w}x{h}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn table_rows_below_the_slide_are_not_laid_out_and_the_slide_looks_the_same() {
|
||||
// A fuzzed deck's 200 x 200 table of long text took 70 s to draw, most of it off the slide.
|
||||
let table = |rows: usize| {
|
||||
let mut t = Table::new(rows, 2, 400.0, 30.0);
|
||||
for r in 0..rows {
|
||||
for c in 0..2 {
|
||||
t.cell_mut(r, c).unwrap().text = TextBody::from_text(&format!("Row {r} col {c}"));
|
||||
}
|
||||
}
|
||||
Shape { id: ShapeId(611), xfrm: Some(Xfrm::new(50.0, 50.0, 400.0, 30.0 * rows as f64)), kind: ShapeKind::Table(t), ..Default::default() }
|
||||
};
|
||||
let opts = RenderOpts { scale: 0.5, ..Default::default() };
|
||||
crate::table::TEXT_CELLS_DRAWN.with(|n| n.set(0));
|
||||
let big = render_slide(&deck_with(vec![table(500)]), 0, &opts);
|
||||
let drawn = crate::table::TEXT_CELLS_DRAWN.with(|n| n.get());
|
||||
assert!(drawn > 2 * 10 && drawn < 2 * 40, "{drawn} cells drawn");
|
||||
// 30 rows already run past the slide's bottom: the same picture.
|
||||
let small = render_slide(&deck_with(vec![table(30)]), 0, &opts);
|
||||
assert_eq!((big.width, big.height), (small.width, small.height));
|
||||
let diff = (0..big.height).flat_map(|y| (0..big.width).map(move |x| (x, y))).filter(|&(x, y)| big.pixel(x, y) != small.pixel(x, y)).count();
|
||||
assert_eq!(diff, 0);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user