LibGfx: Support UTF-16 text shaping

We can achieve this with templating the string view type, and then just
piping the view into the correct `hb_buffer_add_utf*` API.
This commit is contained in:
Timothy Flynn 2025-07-25 09:05:18 -04:00 committed by Jelle Raaijmakers
parent a740bfd8ff
commit 0e9b694058
4 changed files with 43 additions and 11 deletions

View File

@ -63,6 +63,7 @@ ScaledFontMetrics Font::metrics() const
float Font::width(StringView view) const { return measure_text_width(Utf8View(view), *this, {}); }
float Font::width(Utf8View const& view) const { return measure_text_width(view, *this, {}); }
float Font::width(Utf16View const& view) const { return measure_text_width(view, *this, {}); }
float Font::glyph_width(u32 code_point) const
{

View File

@ -69,6 +69,7 @@ public:
u8 baseline() const { return m_point_height; } // FIXME: Read from font
float width(StringView) const;
float width(Utf8View const&) const;
float width(Utf16View const&) const;
FlyString const& family() const { return m_typeface->family(); }
NonnullRefPtr<Font> scaled_with_size(float point_size) const;

View File

@ -7,15 +7,18 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Utf16View.h>
#include <AK/Utf8View.h>
#include <LibGfx/Point.h>
#include <LibGfx/TextLayout.h>
#include <harfbuzz/hb.h>
namespace Gfx {
Vector<NonnullRefPtr<GlyphRun>> shape_text(FloatPoint baseline_start, Utf8View string, FontCascadeList const& font_cascade_list)
template<typename UnicodeView>
Vector<NonnullRefPtr<GlyphRun>> shape_text(FloatPoint baseline_start, UnicodeView const& string, FontCascadeList const& font_cascade_list)
{
if (string.length() == 0)
if (string.is_empty())
return {};
Vector<NonnullRefPtr<GlyphRun>> runs;
@ -25,7 +28,7 @@ Vector<NonnullRefPtr<GlyphRun>> shape_text(FloatPoint baseline_start, Utf8View s
Font const* last_font = &font_cascade_list.font_for_code_point(*it);
FloatPoint last_position = baseline_start;
auto add_run = [&runs, &last_position](Utf8View string, Font const& font) {
auto add_run = [&runs, &last_position](UnicodeView const& string, Font const& font) {
auto run = shape_text(last_position, 0, string, font, GlyphRun::TextType::Common, {});
last_position.translate_by(run->width(), 0);
runs.append(*run);
@ -52,11 +55,26 @@ Vector<NonnullRefPtr<GlyphRun>> shape_text(FloatPoint baseline_start, Utf8View s
return runs;
}
static hb_buffer_t* setup_text_shaping(Utf8View string, Font const& font, ShapeFeatures const& features)
template Vector<NonnullRefPtr<GlyphRun>> shape_text(FloatPoint, Utf8View const&, FontCascadeList const&);
template Vector<NonnullRefPtr<GlyphRun>> shape_text(FloatPoint, Utf16View const&, FontCascadeList const&);
template<typename UnicodeView>
static hb_buffer_t* setup_text_shaping(UnicodeView const& string, Font const& font, ShapeFeatures const& features)
{
static hb_buffer_t* buffer = hb_buffer_create();
hb_buffer_reset(buffer);
hb_buffer_add_utf8(buffer, reinterpret_cast<char const*>(string.bytes()), string.byte_length(), 0, -1);
if constexpr (IsSame<UnicodeView, Utf8View>) {
hb_buffer_add_utf8(buffer, reinterpret_cast<char const*>(string.bytes()), string.byte_length(), 0, -1);
} else if constexpr (IsSame<UnicodeView, Utf16View>) {
if (string.has_ascii_storage())
hb_buffer_add_utf8(buffer, string.ascii_span().data(), string.length_in_code_units(), 0, -1);
else
hb_buffer_add_utf16(buffer, reinterpret_cast<u16 const*>(string.utf16_span().data()), string.length_in_code_units(), 0, -1);
} else {
static_assert(DependentFalse<UnicodeView>);
}
hb_buffer_guess_segment_properties(buffer);
auto* hb_font = font.harfbuzz_font();
@ -80,7 +98,8 @@ static hb_buffer_t* setup_text_shaping(Utf8View string, Font const& font, ShapeF
return buffer;
}
NonnullRefPtr<GlyphRun> shape_text(FloatPoint baseline_start, float letter_spacing, Utf8View string, Font const& font, GlyphRun::TextType text_type, ShapeFeatures const& features)
template<typename UnicodeView>
NonnullRefPtr<GlyphRun> shape_text(FloatPoint baseline_start, float letter_spacing, UnicodeView const& string, Font const& font, GlyphRun::TextType text_type, ShapeFeatures const& features)
{
auto* buffer = setup_text_shaping(string, font, features);
@ -107,7 +126,11 @@ NonnullRefPtr<GlyphRun> shape_text(FloatPoint baseline_start, float letter_spaci
return adopt_ref(*new GlyphRun(move(glyph_run), font, text_type, point.x() - baseline_start.x()));
}
float measure_text_width(Utf8View const& string, Font const& font, ShapeFeatures const& features)
template NonnullRefPtr<GlyphRun> shape_text(FloatPoint, float, Utf8View const&, Font const&, GlyphRun::TextType, ShapeFeatures const&);
template NonnullRefPtr<GlyphRun> shape_text(FloatPoint, float, Utf16View const&, Font const&, GlyphRun::TextType, ShapeFeatures const&);
template<typename UnicodeView>
float measure_text_width(UnicodeView const& string, Font const& font, ShapeFeatures const& features)
{
auto* buffer = setup_text_shaping(string, font, features);
@ -121,4 +144,7 @@ float measure_text_width(Utf8View const& string, Font const& font, ShapeFeatures
return point_x / text_shaping_resolution;
}
template float measure_text_width(Utf8View const&, Font const&, ShapeFeatures const&);
template float measure_text_width(Utf16View const&, Font const&, ShapeFeatures const&);
}

View File

@ -10,7 +10,6 @@
#include <AK/AtomicRefCounted.h>
#include <AK/Forward.h>
#include <AK/Utf8View.h>
#include <AK/Vector.h>
#include <LibGfx/Font/Font.h>
#include <LibGfx/FontCascadeList.h>
@ -68,8 +67,13 @@ private:
float m_width { 0 };
};
NonnullRefPtr<GlyphRun> shape_text(FloatPoint baseline_start, float letter_spacing, Utf8View string, Gfx::Font const& font, GlyphRun::TextType, ShapeFeatures const& features);
Vector<NonnullRefPtr<GlyphRun>> shape_text(FloatPoint baseline_start, Utf8View string, FontCascadeList const&);
float measure_text_width(Utf8View const& string, Gfx::Font const& font, ShapeFeatures const& features);
template<typename UnicodeView>
NonnullRefPtr<GlyphRun> shape_text(FloatPoint baseline_start, float letter_spacing, UnicodeView const& string, Gfx::Font const& font, GlyphRun::TextType, ShapeFeatures const& features);
template<typename UnicodeView>
Vector<NonnullRefPtr<GlyphRun>> shape_text(FloatPoint baseline_start, UnicodeView const& string, FontCascadeList const&);
template<typename UnicodeView>
float measure_text_width(UnicodeView const& string, Gfx::Font const& font, ShapeFeatures const& features);
}