From 2d34ba431812a0d0d6a87a8949b55e213eaa52e9 Mon Sep 17 00:00:00 2001 From: Aliaksandr Kalenik Date: Wed, 20 May 2026 21:19:03 +0200 Subject: [PATCH] LibIPC+LibGfx: Add IPC serialization for typefaces Display-list resource transactions need to carry fonts across an IPC boundary without making LibWeb know how each typeface stores its bytes. Add a LibGfx-owned Typeface IPC representation so callers can encode and decode typefaces directly. Anonymous-buffer and Core::Resource-backed typefaces serialize through their retained backing. System typefaces serialize as family and style data, with macOS system UI typefaces carrying their SystemUIFontKind from creation so the receiver can rematch them through CoreText. This is preparatory work required to add IPC between the main and compositor threads. --- Libraries/LibGfx/Font/Typeface.cpp | 83 +++++++++++++++++++++---- Libraries/LibGfx/Font/Typeface.h | 28 ++++++++- Libraries/LibGfx/Font/TypefaceSkia.cpp | 85 ++++++++++++++++++++------ Libraries/LibGfx/Font/TypefaceSkia.h | 6 +- Libraries/LibIPC/Encoder.h | 2 +- 5 files changed, 170 insertions(+), 34 deletions(-) diff --git a/Libraries/LibGfx/Font/Typeface.cpp b/Libraries/LibGfx/Font/Typeface.cpp index 13abae202b..6cfe7e8031 100644 --- a/Libraries/LibGfx/Font/Typeface.cpp +++ b/Libraries/LibGfx/Font/Typeface.cpp @@ -10,6 +10,8 @@ #include #include #include +#include +#include namespace Gfx { @@ -84,20 +86,21 @@ hb_face_t* Typeface::harfbuzz_typeface() const return m_harfbuzz_face; } -ReadonlyBytes Typeface::font_data_bytes() const +void Typeface::encode_font_data_for_ipc(IPC::Encoder& encoder) const { - if (!m_font_data.has_value()) - return buffer(); - return m_font_data->visit( - [](Core::AnonymousBuffer const& anonymous_buffer) { return anonymous_buffer.bytes(); }, - [](NonnullRefPtr const& resource) { return resource->data(); }); -} + VERIFY(m_font_data.has_value()); -Optional Typeface::anonymous_font_data() const -{ - if (!m_font_data.has_value() || !m_font_data->has()) - return {}; - return m_font_data->get(); + m_font_data->visit( + [&](Core::AnonymousBuffer const& anonymous_buffer) { + MUST(encoder.encode(FontDataFormat::RawFontData)); + MUST(encoder.encode(anonymous_buffer)); + MUST(encoder.encode(ttc_index())); + }, + [&](NonnullRefPtr const& resource) { + MUST(encoder.encode(FontDataFormat::ResourceFontData)); + MUST(encoder.encode(resource->uri())); + MUST(encoder.encode(ttc_index())); + }); } void Typeface::set_anonymous_font_data(Core::AnonymousBuffer anonymous_buffer) @@ -116,3 +119,59 @@ void Typeface::copy_font_data_from(Typeface const& other) } } + +namespace IPC { + +static NonnullRefPtr match_system_typeface(Optional system_ui_font_kind, String family_name, u16 weight, u16 width, u8 slope) +{ + if (system_ui_font_kind.has_value()) { + auto typeface = MUST(Gfx::TypefaceSkia::match_system_ui(system_ui_font_kind.value(), 0, weight, width, slope)); + if (typeface) + return typeface.release_nonnull(); + } + + auto typeface = MUST(Gfx::TypefaceSkia::match_family_style(family_name.bytes_as_string_view(), weight, width, slope)); + VERIFY(typeface); + return typeface.release_nonnull(); +} + +template<> +ErrorOr encode(Encoder& encoder, Gfx::Typeface const& typeface) +{ + typeface.encode_font_data_for_ipc(encoder); + return {}; +} + +template<> +ErrorOr> decode(Decoder& decoder) +{ + auto format = TRY(decoder.decode()); + + switch (format) { + case Gfx::Typeface::FontDataFormat::RawFontData: { + auto font_data = TRY(decoder.decode()); + auto ttc_index = TRY(decoder.decode()); + if (!font_data.is_valid()) + return Error::from_string_literal("Typeface IPC data contained invalid font data"); + return TRY(Gfx::Typeface::try_load_from_anonymous_buffer(move(font_data), ttc_index)); + } + case Gfx::Typeface::FontDataFormat::ResourceFontData: { + auto resource_uri = TRY(decoder.decode()); + auto ttc_index = TRY(decoder.decode()); + auto resource = TRY(Core::Resource::load_from_uri(resource_uri.bytes_as_string_view())); + return TRY(Gfx::Typeface::try_load_from_resource(*resource, ttc_index)); + } + case Gfx::Typeface::FontDataFormat::SystemFont: { + auto system_ui_font_kind = TRY(decoder.decode>()); + auto family_name = TRY(decoder.decode()); + auto weight = TRY(decoder.decode()); + auto width = TRY(decoder.decode()); + auto slope = TRY(decoder.decode()); + return match_system_typeface(system_ui_font_kind, move(family_name), weight, width, slope); + } + } + + return Error::from_string_literal("Typeface IPC data contained invalid font data format"); +} + +} diff --git a/Libraries/LibGfx/Font/Typeface.h b/Libraries/LibGfx/Font/Typeface.h index e6bef19da2..242873a9a1 100644 --- a/Libraries/LibGfx/Font/Typeface.h +++ b/Libraries/LibGfx/Font/Typeface.h @@ -17,6 +17,7 @@ #include #include #include +#include #define POINTS_PER_INCH 72.0f #define DEFAULT_DPI 96 @@ -69,9 +70,6 @@ public: [[nodiscard]] NonnullRefPtr font(float point_size, FontVariationSettings const& variations = {}, Gfx::ShapeFeatures const& shape_features = {}) const; hb_face_t* harfbuzz_typeface() const; - ReadonlyBytes font_data_bytes() const; - Optional anonymous_font_data() const; - u32 font_data_ttc_index() const { return ttc_index(); } template bool fast_is() const = delete; @@ -79,16 +77,30 @@ public: virtual bool is_skia() const { return false; } protected: + enum class FontDataFormat : u8 { + RawFontData, + ResourceFontData, + SystemFont, + }; + Typeface(); virtual ReadonlyBytes buffer() const = 0; virtual u32 ttc_index() const = 0; + virtual void encode_font_data_for_ipc(IPC::Encoder&) const; void set_anonymous_font_data(Core::AnonymousBuffer); void set_resource_font_data(Core::Resource const&); void copy_font_data_from(Typeface const&); + bool has_font_data_backing() const { return m_font_data.has_value(); } private: + template + friend ErrorOr IPC::encode(IPC::Encoder&, T const&); + + template + friend ErrorOr IPC::decode(IPC::Decoder&); + using FontDataBacking = Variant>; Optional m_font_data; @@ -106,3 +118,13 @@ struct AK::Traits : public AK::DefaultTraits +ErrorOr encode(Encoder&, Gfx::Typeface const&); + +template<> +ErrorOr> decode(Decoder&); + +} diff --git a/Libraries/LibGfx/Font/TypefaceSkia.cpp b/Libraries/LibGfx/Font/TypefaceSkia.cpp index fdad0bef7c..5fadbdd4c0 100644 --- a/Libraries/LibGfx/Font/TypefaceSkia.cpp +++ b/Libraries/LibGfx/Font/TypefaceSkia.cpp @@ -5,14 +5,16 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include -#include #include #include +#include #include #include #include +#include #include #if defined(AK_OS_ANDROID) # include @@ -34,14 +36,16 @@ namespace Gfx { static sk_sp s_font_manager; struct TypefaceSkia::Impl { - Impl(sk_sp skia_typeface, std::unique_ptr stream = {}) + Impl(sk_sp skia_typeface, std::unique_ptr stream = {}, Optional system_ui_font_kind = {}) : skia_typeface(move(skia_typeface)) , stream(move(stream)) + , system_ui_font_kind(move(system_ui_font_kind)) { } sk_sp skia_typeface; std::unique_ptr stream; + Optional system_ui_font_kind; }; static SkFontMgr& font_manager() @@ -66,6 +70,18 @@ static SkFontMgr& font_manager() return *s_font_manager; } +static std::unique_ptr copy_stream_to_memory_stream(SkStreamAsset& stream) +{ + auto stream_copy = stream.duplicate(); + VERIFY(stream_copy); + + auto data = SkData::MakeFromStream(stream_copy.get(), stream_copy->getLength()); + VERIFY(data); + VERIFY(data->size() == stream.getLength()); + + return std::make_unique(move(data)); +} + static SkFontStyle::Slant slope_to_skia_slant(u8 slope) { switch (slope) { @@ -78,7 +94,11 @@ static SkFontStyle::Slant slope_to_skia_slant(u8 slope) } } -ErrorOr> TypefaceSkia::typeface_from_skia_typeface(sk_sp skia_typeface) +#ifdef AK_OS_MACOS +static CTFontRef create_system_ui_font(SystemUIFontKind, float point_size, u8 slope); +#endif + +ErrorOr> TypefaceSkia::typeface_from_skia_typeface(sk_sp skia_typeface, Optional system_ui_font_kind) { if (!skia_typeface) return RefPtr {}; @@ -91,22 +111,20 @@ ErrorOr> TypefaceSkia::typeface_from_skia_typeface(sk_sp(stream->getMemoryBase()), stream->getLength() }; return adopt_ref(*new TypefaceSkia { - make(skia_typeface, std::move(stream)), + make(skia_typeface, std::move(stream), system_ui_font_kind), bytes, ttc_index }); } - auto data = skia_typeface->serialize(SkTypeface::SerializeBehavior::kDoIncludeData); - if (!data) + if (!stream) return Error::from_string_literal("Failed to get font data from typeface"); - auto anonymous_buffer = TRY(Core::AnonymousBuffer::create_with_size(data->size())); - if (data->size() > 0) - memcpy(anonymous_buffer.data(), data->data(), data->size()); - - auto result = TRY(TypefaceSkia::load_from_buffer(anonymous_buffer.bytes(), ttc_index)); - result->set_anonymous_font_data(move(anonymous_buffer)); - return result; + auto memory_stream = copy_stream_to_memory_stream(*stream); + auto bytes = ReadonlyBytes { static_cast(memory_stream->getMemoryBase()), memory_stream->getLength() }; + return adopt_ref(*new TypefaceSkia { + make(skia_typeface, move(memory_stream), system_ui_font_kind), + bytes, + ttc_index }); } ErrorOr> TypefaceSkia::load_from_buffer(AK::ReadonlyBytes buffer, u32 ttc_index) @@ -129,6 +147,23 @@ ErrorOr> TypefaceSkia::load_from_buffer(AK::Readonly return adopt_ref(*new TypefaceSkia { make(skia_typeface), buffer, ttc_index }); } +void TypefaceSkia::encode_font_data_for_ipc(IPC::Encoder& encoder) const +{ + if (has_font_data_backing()) { + Typeface::encode_font_data_for_ipc(encoder); + return; + } + + auto family_name = family().to_string(); + + MUST(encoder.encode(FontDataFormat::SystemFont)); + MUST(encoder.encode(impl().system_ui_font_kind)); + MUST(encoder.encode(family_name)); + MUST(encoder.encode(weight())); + MUST(encoder.encode(width())); + MUST(encoder.encode(slope())); +} + #ifdef AK_OS_MACOS // NB: These are the CoreText string values behind the public AppKit NSFontDescriptorSystemDesign constants. // Keeping them here avoids pulling Objective-C headers into this C++ file. @@ -204,7 +239,7 @@ ErrorOr> TypefaceSkia::match_system_ui(SystemUIFontKind kin return RefPtr {}; auto skia_typeface = SkMakeTypefaceFromCTFont(ct_font); - auto typeface = typeface_from_skia_typeface(move(skia_typeface)); + auto typeface = typeface_from_skia_typeface(move(skia_typeface), kind); CFRelease(ct_font); return typeface; #else @@ -217,6 +252,12 @@ ErrorOr> TypefaceSkia::match_system_ui(SystemUIFontKind kin #endif } +ErrorOr> TypefaceSkia::match_family_style(StringView family_name, u16 weight, u16 width, u8 slope) +{ + auto skia_typeface = font_manager().matchFamilyStyle(ByteString(family_name).characters(), SkFontStyle { weight, width, slope_to_skia_slant(slope) }); + return typeface_from_skia_typeface(move(skia_typeface)); +} + ErrorOr> TypefaceSkia::find_typeface_for_code_point(u32 code_point, u16 weight, u16 width, u8 slope) { SkFontStyle style(weight, width, slope_to_skia_slant(slope)); @@ -273,9 +314,19 @@ RefPtr TypefaceSkia::clone_with_variations(Vector(skia_typeface), m_buffer, m_ttc_index }); - typeface->copy_font_data_from(*this); - return typeface; + if (has_font_data_backing()) { + auto typeface = adopt_ref(*new TypefaceSkia { + make(skia_typeface, std::unique_ptr {}, impl().system_ui_font_kind), + m_buffer, + m_ttc_index }); + typeface->copy_font_data_from(*this); + return typeface; + } + + auto typeface_or_error = typeface_from_skia_typeface(move(skia_typeface), impl().system_ui_font_kind); + if (typeface_or_error.is_error()) + return {}; + return typeface_or_error.release_value(); } SkTypeface const* TypefaceSkia::sk_typeface() const diff --git a/Libraries/LibGfx/Font/TypefaceSkia.h b/Libraries/LibGfx/Font/TypefaceSkia.h index 54be7695d8..234eaaa1e1 100644 --- a/Libraries/LibGfx/Font/TypefaceSkia.h +++ b/Libraries/LibGfx/Font/TypefaceSkia.h @@ -26,6 +26,7 @@ class TypefaceSkia : public Gfx::Typeface { public: static ErrorOr> load_from_buffer(ReadonlyBytes, u32 ttc_index = 0); static ErrorOr> match_system_ui(SystemUIFontKind, float point_size, u16 weight, double width, u8 slope); + static ErrorOr> match_family_style(StringView family_name, u16 weight, u16 width, u8 slope); static ErrorOr> find_typeface_for_code_point(u32 code_point, u16 weight, u16 width, u8 slope); static Optional resolve_generic_family(StringView family_name, u16 weight, u8 slope); @@ -44,12 +45,15 @@ public: SkTypeface const* sk_typeface() const; +protected: + virtual void encode_font_data_for_ipc(IPC::Encoder&) const override; + private: struct Impl; Impl& impl() const { return *m_impl; } NonnullOwnPtr m_impl; - static ErrorOr> typeface_from_skia_typeface(sk_sp); + static ErrorOr> typeface_from_skia_typeface(sk_sp, Optional = {}); TypefaceSkia(NonnullOwnPtr, ReadonlyBytes, u32 ttc_index = 0); diff --git a/Libraries/LibIPC/Encoder.h b/Libraries/LibIPC/Encoder.h index 07274ddd34..815d8343cc 100644 --- a/Libraries/LibIPC/Encoder.h +++ b/Libraries/LibIPC/Encoder.h @@ -18,7 +18,7 @@ #include #include #include -#include +#include namespace IPC {