From 2cf10981afe409f4563b090c511efef9fb0257da Mon Sep 17 00:00:00 2001 From: Aliaksandr Kalenik Date: Tue, 23 Sep 2025 04:56:53 +0200 Subject: [PATCH] LibGfx+LibWeb: Implement `GlyphRun::bounding_rect()` Allows us to skip painting of glyph runs lying outside of viewport. --- Libraries/LibGfx/TextLayout.cpp | 18 ++++++++++++++++-- Libraries/LibGfx/TextLayout.h | 6 +++++- .../LibWeb/Painting/DisplayListCommand.cpp | 1 + Libraries/LibWeb/Painting/DisplayListCommand.h | 2 ++ .../LibWeb/Painting/DisplayListPlayerSkia.cpp | 5 ++--- .../LibWeb/Painting/DisplayListRecorder.cpp | 1 + 6 files changed, 27 insertions(+), 6 deletions(-) diff --git a/Libraries/LibGfx/TextLayout.cpp b/Libraries/LibGfx/TextLayout.cpp index c6b448bb30..8ca5f58767 100644 --- a/Libraries/LibGfx/TextLayout.cpp +++ b/Libraries/LibGfx/TextLayout.cpp @@ -15,6 +15,19 @@ namespace Gfx { +FloatRect GlyphRun::bounding_rect() const +{ + if (glyphs().is_empty()) + return {}; + auto const& first = glyphs().first(); + FloatRect result { first.position, { first.glyph_width, m_line_height } }; + for (auto const& glyph : glyphs()) { + FloatRect glyph_rect { glyph.position, { glyph.glyph_width, m_line_height } }; + result.unite(glyph_rect); + } + return result; +} + Vector> shape_text(FloatPoint baseline_start, Utf16View const& string, FontCascadeList const& font_cascade_list) { if (string.is_empty()) @@ -88,6 +101,7 @@ static hb_buffer_t* setup_text_shaping(Utf16View const& string, Font const& font NonnullRefPtr shape_text(FloatPoint baseline_start, float letter_spacing, Utf16View const& string, Font const& font, GlyphRun::TextType text_type, ShapeFeatures const& features) { + auto const& metrics = font.pixel_metrics(); auto& shaping_cache = font.shaping_cache(); // NOTE: We only cache shaping results for a specific set of features. If the features change, we clear the cache. @@ -146,7 +160,7 @@ NonnullRefPtr shape_text(FloatPoint baseline_start, float letter_spaci for (size_t i = 0; i < glyph_count; ++i) { auto position = point - - FloatPoint { 0, font.pixel_metrics().ascent } + - FloatPoint { 0, metrics.ascent } + FloatPoint { positions[i].x_offset, positions[i].y_offset } / text_shaping_resolution; glyph_run.unchecked_append({ @@ -163,7 +177,7 @@ NonnullRefPtr shape_text(FloatPoint baseline_start, float letter_spaci point.translate_by(letter_spacing, 0); } - return adopt_ref(*new GlyphRun(move(glyph_run), font, text_type, point.x() - baseline_start.x())); + return adopt_ref(*new GlyphRun(move(glyph_run), font, text_type, point.x() - baseline_start.x(), metrics.ascent + metrics.descent)); } float measure_text_width(Utf16View const& string, Font const& font, ShapeFeatures const& features) diff --git a/Libraries/LibGfx/TextLayout.h b/Libraries/LibGfx/TextLayout.h index 1677034083..29aec4f12a 100644 --- a/Libraries/LibGfx/TextLayout.h +++ b/Libraries/LibGfx/TextLayout.h @@ -15,6 +15,7 @@ #include #include #include +#include #include namespace Gfx { @@ -36,11 +37,12 @@ public: Rtl, }; - GlyphRun(Vector&& glyphs, NonnullRefPtr font, TextType text_type, float width) + GlyphRun(Vector&& glyphs, NonnullRefPtr font, TextType text_type, float width, float line_height) : m_glyphs(move(glyphs)) , m_font(move(font)) , m_text_type(text_type) , m_width(width) + , m_line_height(line_height) { } @@ -50,12 +52,14 @@ public: [[nodiscard]] Vector& glyphs() { return m_glyphs; } [[nodiscard]] bool is_empty() const { return m_glyphs.is_empty(); } [[nodiscard]] float width() const { return m_width; } + [[nodiscard]] FloatRect bounding_rect() const; private: Vector m_glyphs; NonnullRefPtr m_font; TextType m_text_type; float m_width { 0 }; + float m_line_height { 0 }; }; NonnullRefPtr shape_text(FloatPoint baseline_start, float letter_spacing, Utf16View const&, Gfx::Font const& font, GlyphRun::TextType, ShapeFeatures const& features); diff --git a/Libraries/LibWeb/Painting/DisplayListCommand.cpp b/Libraries/LibWeb/Painting/DisplayListCommand.cpp index 14bdd9b94f..2cbd5aac2e 100644 --- a/Libraries/LibWeb/Painting/DisplayListCommand.cpp +++ b/Libraries/LibWeb/Painting/DisplayListCommand.cpp @@ -13,6 +13,7 @@ void DrawGlyphRun::translate_by(Gfx::IntPoint const& offset) { rect.translate_by(offset); translation.translate_by(offset.to_type()); + bounding_rectangle.translate_by(offset); } Gfx::IntRect PaintOuterBoxShadow::bounding_rect() const diff --git a/Libraries/LibWeb/Painting/DisplayListCommand.h b/Libraries/LibWeb/Painting/DisplayListCommand.h index 6c849644f0..3d4455afc2 100644 --- a/Libraries/LibWeb/Painting/DisplayListCommand.h +++ b/Libraries/LibWeb/Painting/DisplayListCommand.h @@ -43,7 +43,9 @@ struct DrawGlyphRun { Gfx::FloatPoint translation; Color color; Gfx::Orientation orientation { Gfx::Orientation::Horizontal }; + Gfx::IntRect bounding_rectangle; + [[nodiscard]] Gfx::IntRect bounding_rect() const { return bounding_rectangle; } void translate_by(Gfx::IntPoint const& offset); void dump(StringBuilder&) const; }; diff --git a/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp b/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp index d40758cfeb..f8af418754 100644 --- a/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp +++ b/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp @@ -553,13 +553,12 @@ void DisplayListPlayerSkia::paint_text_shadow(PaintTextShadow const& command) SkPaint blur_paint; blur_paint.setImageFilter(blur_image_filter); canvas.saveLayer(SkCanvas::SaveLayerRec(nullptr, &blur_paint, nullptr, 0)); - draw_glyph_run({ - .glyph_run = command.glyph_run, + draw_glyph_run({ .glyph_run = command.glyph_run, .scale = command.glyph_run_scale, .rect = command.text_rect, .translation = command.draw_location + command.text_rect.location().to_type(), .color = command.color, - }); + .bounding_rectangle = command.bounding_rect() }); canvas.restore(); } diff --git a/Libraries/LibWeb/Painting/DisplayListRecorder.cpp b/Libraries/LibWeb/Painting/DisplayListRecorder.cpp index 38e5d332d5..cb245449c9 100644 --- a/Libraries/LibWeb/Painting/DisplayListRecorder.cpp +++ b/Libraries/LibWeb/Painting/DisplayListRecorder.cpp @@ -255,6 +255,7 @@ void DisplayListRecorder::draw_glyph_run(Gfx::FloatPoint baseline_start, Gfx::Gl .translation = baseline_start, .color = color, .orientation = orientation, + .bounding_rectangle = glyph_run.bounding_rect().scaled(scale).translated(baseline_start).to_type(), }); }