Paint only glyphs that intersect the visible bounds in `Text`

Antonio Scandurra and Nathan Sobo created

Co-Authored-By: Nathan Sobo <nathan@zed.dev>

Change summary

gpui/src/elements/text.rs | 21 ++++++++++++++++-----
gpui/src/text_layout.rs   | 22 +++++++++++++++-------
2 files changed, 31 insertions(+), 12 deletions(-)

Detailed changes

gpui/src/elements/text.rs 🔗

@@ -82,13 +82,24 @@ impl Element for Text {
     ) -> Self::PaintState {
         let mut origin = bounds.origin();
         for (line, wrap_boundaries) in &layout.lines {
-            line.paint_wrapped(
+            let wrapped_line_boundaries = RectF::new(
                 origin,
-                layout.line_height,
-                wrap_boundaries.iter().copied(),
-                cx,
+                vec2f(
+                    bounds.width(),
+                    (wrap_boundaries.len() + 1) as f32 * layout.line_height,
+                ),
             );
-            origin.set_y(origin.y() + (wrap_boundaries.len() + 1) as f32 * layout.line_height);
+
+            if wrapped_line_boundaries.intersects(visible_bounds) {
+                line.paint_wrapped(
+                    origin,
+                    visible_bounds,
+                    layout.line_height,
+                    wrap_boundaries.iter().copied(),
+                    cx,
+                );
+            }
+            origin.set_y(wrapped_line_boundaries.max_y());
         }
     }
 

gpui/src/text_layout.rs 🔗

@@ -253,6 +253,7 @@ impl Line {
     pub fn paint_wrapped(
         &self,
         origin: Vector2F,
+        visible_bounds: RectF,
         line_height: f32,
         boundaries: impl IntoIterator<Item = ShapedBoundary>,
         cx: &mut PaintContext,
@@ -287,13 +288,20 @@ impl Line {
                     }
                 }
 
-                cx.scene.push_glyph(scene::Glyph {
-                    font_id: run.font_id,
-                    font_size: self.layout.font_size,
-                    id: glyph.id,
-                    origin: origin + glyph_origin,
-                    color,
-                });
+                let glyph_bounds = RectF::new(
+                    origin + glyph_origin,
+                    cx.font_cache
+                        .bounding_box(run.font_id, self.layout.font_size),
+                );
+                if glyph_bounds.intersects(visible_bounds) {
+                    cx.scene.push_glyph(scene::Glyph {
+                        font_id: run.font_id,
+                        font_size: self.layout.font_size,
+                        id: glyph.id,
+                        origin: origin + glyph_origin,
+                        color,
+                    });
+                }
             }
         }
     }