From 17f4ed6ea1808ad74274300fb2d6384aa5773403 Mon Sep 17 00:00:00 2001 From: Aliaksandr Kalenik Date: Sat, 14 Sep 2024 19:54:41 +0200 Subject: [PATCH] LibGfx+LibWeb: Simplify text shaping API by removing per glyph callback All places where text shaping happens, the callback is used to simply append a glyph into the end of glyphs vector. This change removes the callback parameter and makes the text shaping function return a glyph run. --- Userland/Libraries/LibGfx/TextLayout.cpp | 18 ++++++------------ Userland/Libraries/LibGfx/TextLayout.h | 7 +++++-- .../LibWeb/HTML/CanvasRenderingContext2D.cpp | 10 ++-------- .../LibWeb/Layout/InlineLevelIterator.cpp | 13 +++---------- .../LibWeb/Painting/DisplayListRecorder.cpp | 13 +++---------- 5 files changed, 19 insertions(+), 42 deletions(-) diff --git a/Userland/Libraries/LibGfx/TextLayout.cpp b/Userland/Libraries/LibGfx/TextLayout.cpp index f1f95bd4e76..623a0905a4d 100644 --- a/Userland/Libraries/LibGfx/TextLayout.cpp +++ b/Userland/Libraries/LibGfx/TextLayout.cpp @@ -12,7 +12,7 @@ namespace Gfx { -void for_each_glyph_position(FloatPoint baseline_start, Utf8View string, Gfx::Font const& font, Function callback, Optional width) +RefPtr shape_text(FloatPoint baseline_start, Utf8View string, Gfx::Font const& font, GlyphRun::TextType text_type) { hb_buffer_t* buffer = hb_buffer_create(); ScopeGuard destroy_buffer = [&]() { hb_buffer_destroy(buffer); }; @@ -22,8 +22,6 @@ void for_each_glyph_position(FloatPoint baseline_start, Utf8View string, Gfx::Fo u32 glyph_count; auto* glyph_info = hb_buffer_get_glyph_infos(buffer, &glyph_count); Vector const input_glyph_info({ glyph_info, glyph_count }); - if (input_glyph_info.is_empty()) - return; auto* hb_font = font.harfbuzz_font(); hb_shape(hb_font, buffer, nullptr, 0); @@ -31,27 +29,23 @@ void for_each_glyph_position(FloatPoint baseline_start, Utf8View string, Gfx::Fo glyph_info = hb_buffer_get_glyph_infos(buffer, &glyph_count); auto* positions = hb_buffer_get_glyph_positions(buffer, &glyph_count); + Vector glyph_run; FloatPoint point = baseline_start; for (size_t i = 0; i < glyph_count; ++i) { auto position = point - FloatPoint { 0, font.pixel_metrics().ascent } + FloatPoint { positions[i].x_offset, positions[i].y_offset } / text_shaping_resolution; - callback(DrawGlyph { - .position = position, - .glyph_id = glyph_info[i].codepoint, - }); + glyph_run.append({ position, glyph_info[i].codepoint }); point += FloatPoint { positions[i].x_advance, positions[i].y_advance } / text_shaping_resolution; } - if (width.has_value()) - *width = point.x(); + return adopt_ref(*new Gfx::GlyphRun(move(glyph_run), font, text_type, point.x())); } float measure_text_width(Utf8View const& string, Gfx::Font const& font) { - float width = 0; - for_each_glyph_position({}, string, font, [&](DrawGlyph const&) {}, width); - return width; + auto glyph_run = shape_text({}, string, font, GlyphRun::TextType::Common); + return glyph_run->width(); } } diff --git a/Userland/Libraries/LibGfx/TextLayout.h b/Userland/Libraries/LibGfx/TextLayout.h index 29725f1b5b4..4928206039c 100644 --- a/Userland/Libraries/LibGfx/TextLayout.h +++ b/Userland/Libraries/LibGfx/TextLayout.h @@ -40,10 +40,11 @@ public: Rtl, }; - GlyphRun(Vector&& glyphs, NonnullRefPtr font, TextType text_type) + GlyphRun(Vector&& glyphs, NonnullRefPtr font, TextType text_type, float width) : m_glyphs(move(glyphs)) , m_font(move(font)) , m_text_type(text_type) + , m_width(width) { } @@ -52,6 +53,7 @@ public: [[nodiscard]] Vector const& glyphs() const { return m_glyphs; } [[nodiscard]] Vector& glyphs() { return m_glyphs; } [[nodiscard]] bool is_empty() const { return m_glyphs.is_empty(); } + [[nodiscard]] float width() const { return m_width; } void append(DrawGlyph glyph) { m_glyphs.append(glyph); } @@ -59,9 +61,10 @@ private: Vector m_glyphs; NonnullRefPtr m_font; TextType m_text_type; + float m_width { 0 }; }; -void for_each_glyph_position(FloatPoint baseline_start, Utf8View string, Gfx::Font const& font, Function callback, Optional width = {}); +RefPtr shape_text(FloatPoint baseline_start, Utf8View string, Gfx::Font const& font, GlyphRun::TextType); float measure_text_width(Utf8View const& string, Gfx::Font const& font); } diff --git a/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp b/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp index 2254a6cd6ab..282bec46f37 100644 --- a/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp +++ b/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp @@ -513,16 +513,10 @@ CanvasRenderingContext2D::PreparedText CanvasRenderingContext2D::prepare_text(By Gfx::FloatPoint anchor { 0, 0 }; auto physical_alignment = Gfx::TextAlignment::CenterLeft; - auto glyph_run = adopt_ref(*new Gfx::GlyphRun({}, *font, Gfx::GlyphRun::TextType::Ltr)); - float glyph_run_width = 0; - Gfx::for_each_glyph_position( - anchor, replaced_text.code_points(), *font, [&](Gfx::DrawGlyph const& glyph) { - glyph_run->append(glyph); - }, - glyph_run_width); + auto glyph_run = Gfx::shape_text(anchor, replaced_text.code_points(), *font, Gfx::GlyphRun::TextType::Ltr); // 8. Let result be an array constructed by iterating over each glyph in the inline box from left to right (if any), adding to the array, for each glyph, the shape of the glyph as it is in the inline box, positioned on a coordinate space using CSS pixels with its origin is at the anchor point. - PreparedText prepared_text { glyph_run, physical_alignment, { 0, 0, static_cast(glyph_run_width), static_cast(height) } }; + PreparedText prepared_text { glyph_run, physical_alignment, { 0, 0, static_cast(glyph_run->width()), static_cast(height) } }; // 9. Return result, physical alignment, and the inline box. return prepared_text; diff --git a/Userland/Libraries/LibWeb/Layout/InlineLevelIterator.cpp b/Userland/Libraries/LibWeb/Layout/InlineLevelIterator.cpp index 71488343df0..3c8f4738d03 100644 --- a/Userland/Libraries/LibWeb/Layout/InlineLevelIterator.cpp +++ b/Userland/Libraries/LibWeb/Layout/InlineLevelIterator.cpp @@ -239,15 +239,8 @@ Optional InlineLevelIterator::next_without_lookahead( }; } - Vector glyph_run; - float glyph_run_width = 0; - Gfx::for_each_glyph_position( - { 0, 0 }, chunk.view, chunk.font, [&](Gfx::DrawGlyph const& glyph) { - glyph_run.append(glyph); - }, - glyph_run_width); - - CSSPixels chunk_width = CSSPixels::nearest_value_for(glyph_run_width); + auto glyph_run = Gfx::shape_text({ 0, 0 }, chunk.view, chunk.font, text_type); + CSSPixels chunk_width = CSSPixels::nearest_value_for(glyph_run->width()); // NOTE: We never consider `content: ""` to be collapsible whitespace. bool is_generated_empty_string = text_node.is_generated() && chunk.length == 0; @@ -255,7 +248,7 @@ Optional InlineLevelIterator::next_without_lookahead( Item item { .type = Item::Type::Text, .node = &text_node, - .glyph_run = adopt_ref(*new Gfx::GlyphRun(move(glyph_run), chunk.font, text_type)), + .glyph_run = move(glyph_run), .offset_in_node = chunk.start, .length_in_node = chunk.length, .width = chunk_width, diff --git a/Userland/Libraries/LibWeb/Painting/DisplayListRecorder.cpp b/Userland/Libraries/LibWeb/Painting/DisplayListRecorder.cpp index 381e6f6320e..231260b444b 100644 --- a/Userland/Libraries/LibWeb/Painting/DisplayListRecorder.cpp +++ b/Userland/Libraries/LibWeb/Painting/DisplayListRecorder.cpp @@ -233,21 +233,14 @@ void DisplayListRecorder::draw_text(Gfx::IntRect const& rect, String raw_text, G if (rect.is_empty()) return; - auto glyph_run = adopt_ref(*new Gfx::GlyphRun({}, font, Gfx::GlyphRun::TextType::Ltr)); - float glyph_run_width = 0; - Gfx::for_each_glyph_position( - { 0, 0 }, raw_text.code_points(), font, [&](Gfx::DrawGlyph const& glyph) { - glyph_run->append(glyph); - }, - glyph_run_width); - + auto glyph_run = Gfx::shape_text({}, raw_text.code_points(), font, Gfx::GlyphRun::TextType::Ltr); float baseline_x = 0; if (alignment == Gfx::TextAlignment::CenterLeft) { baseline_x = rect.x(); } else if (alignment == Gfx::TextAlignment::Center) { - baseline_x = static_cast(rect.x()) + (static_cast(rect.width()) - glyph_run_width) / 2.0f; + baseline_x = static_cast(rect.x()) + (static_cast(rect.width()) - glyph_run->width()) / 2.0f; } else if (alignment == Gfx::TextAlignment::CenterRight) { - baseline_x = static_cast(rect.right()) - glyph_run_width; + baseline_x = static_cast(rect.right()) - glyph_run->width(); } else { // Unimplemented alignment. TODO();