Переглянути джерело

LibGfx: Consider multi-code point glyphs when computing text width

Currently, we compute the width of text one code point at a time. This
ignores grapheme clusters (emoji in particular). One effect of this is
when highlighting a multi-code point emoji. We will errantly increase
the highlight rect to the sum of all code point widths, rather than
just the width of the resolved emoji bitmap.
Timothy Flynn 2 роки тому
батько
коміт
b823f3d29f

+ 6 - 2
Userland/Libraries/LibGfx/Font/BitmapFont.cpp

@@ -357,7 +357,9 @@ ALWAYS_INLINE int BitmapFont::unicode_view_width(T const& view) const
     int width = 0;
     int longest_width = 0;
 
-    for (u32 code_point : view) {
+    for (auto it = view.begin(); it != view.end(); ++it) {
+        auto code_point = *it;
+
         if (code_point == '\n' || code_point == '\r') {
             first = true;
             longest_width = max(width, longest_width);
@@ -367,8 +369,10 @@ ALWAYS_INLINE int BitmapFont::unicode_view_width(T const& view) const
         if (!first)
             width += glyph_spacing();
         first = false;
-        width += glyph_or_emoji_width(code_point);
+
+        width += glyph_or_emoji_width(it);
     }
+
     longest_width = max(width, longest_width);
     return longest_width;
 }

+ 6 - 4
Userland/Libraries/LibGfx/Painter.cpp

@@ -1518,7 +1518,9 @@ void draw_text_line(FloatRect const& a_rect, Utf8View const& text, Font const& f
         if (kerning != 0.0f)
             point.translate_by(direction == TextDirection::LTR ? kerning : -kerning, 0);
 
-        FloatSize glyph_size(font.glyph_or_emoji_width(code_point) + font.glyph_spacing(), font.pixel_size());
+        auto it_copy = it; // The callback function will advance the iterator, so create a copy for this lookup.
+        FloatSize glyph_size(font.glyph_or_emoji_width(it_copy) + font.glyph_spacing(), font.pixel_size());
+
         if (direction == TextDirection::RTL)
             point.translate_by(-glyph_size.width(), 0); // If we are drawing right to left, we have to move backwards before drawing the glyph
         draw_glyph({ point, glyph_size }, it);
@@ -2480,11 +2482,11 @@ void Gfx::Painter::draw_ui_text(Gfx::IntRect const& rect, StringView text, Gfx::
             if (utf8_view.byte_offset_of(it) >= underline_offset.value()) {
                 int y = text_rect.bottom() + 1;
                 int x1 = text_rect.left() + width;
-                int x2 = x1 + font.glyph_or_emoji_width(*it);
+                int x2 = x1 + font.glyph_or_emoji_width(it);
                 draw_line({ x1, y }, { x2, y }, color);
                 break;
             }
-            width += font.glyph_or_emoji_width(*it) + font.glyph_spacing();
+            width += font.glyph_or_emoji_width(it) + font.glyph_spacing();
         }
     }
 }
@@ -2514,7 +2516,7 @@ void Painter::draw_text_run(FloatPoint baseline_start, Utf8View const& string, F
         // FIXME: this is probably not the real space taken for complex emojis
         x += font.glyphs_horizontal_kerning(last_code_point, code_point);
         draw_glyph_or_emoji(FloatPoint { x, y }, code_point_iterator, font, color);
-        x += font.glyph_or_emoji_width(code_point) + font.glyph_spacing();
+        x += font.glyph_or_emoji_width(code_point_iterator) + font.glyph_spacing();
         last_code_point = code_point;
     }
 }

+ 1 - 2
Userland/Libraries/LibGfx/TextLayout.cpp

@@ -171,8 +171,7 @@ DeprecatedString TextLayout::elide_text_from_right(Utf8View text) const
         if (ellipsis_width < text_width) {
             size_t offset = 0;
             for (auto it = text.begin(); !it.done(); ++it) {
-                auto code_point = *it;
-                auto glyph_width = m_font.glyph_or_emoji_width(code_point);
+                auto glyph_width = m_font.glyph_or_emoji_width(it);
                 // NOTE: Glyph spacing should not be added after the last glyph on the line,
                 //       but since we are here because the last glyph does not actually fit on the line,
                 //       we don't have to worry about spacing.