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.
This commit is contained in:
Aliaksandr Kalenik 2026-05-20 21:19:03 +02:00 committed by Alexander Kalenik
parent b5bace2391
commit 2d34ba4318
5 changed files with 170 additions and 34 deletions

View file

@ -10,6 +10,8 @@
#include <LibGfx/Font/FontVariationSettings.h>
#include <LibGfx/Font/Typeface.h>
#include <LibGfx/Font/TypefaceSkia.h>
#include <LibIPC/Decoder.h>
#include <LibIPC/Encoder.h>
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<Core::Resource const> const& resource) { return resource->data(); });
}
VERIFY(m_font_data.has_value());
Optional<Core::AnonymousBuffer> Typeface::anonymous_font_data() const
{
if (!m_font_data.has_value() || !m_font_data->has<Core::AnonymousBuffer>())
return {};
return m_font_data->get<Core::AnonymousBuffer>();
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<Core::Resource const> 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<Gfx::Typeface const> match_system_typeface(Optional<Gfx::SystemUIFontKind> 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<void> encode(Encoder& encoder, Gfx::Typeface const& typeface)
{
typeface.encode_font_data_for_ipc(encoder);
return {};
}
template<>
ErrorOr<NonnullRefPtr<Gfx::Typeface const>> decode(Decoder& decoder)
{
auto format = TRY(decoder.decode<Gfx::Typeface::FontDataFormat>());
switch (format) {
case Gfx::Typeface::FontDataFormat::RawFontData: {
auto font_data = TRY(decoder.decode<Core::AnonymousBuffer>());
auto ttc_index = TRY(decoder.decode<u32>());
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<String>());
auto ttc_index = TRY(decoder.decode<u32>());
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<Optional<Gfx::SystemUIFontKind>>());
auto family_name = TRY(decoder.decode<String>());
auto weight = TRY(decoder.decode<u16>());
auto width = TRY(decoder.decode<u16>());
auto slope = TRY(decoder.decode<u8>());
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");
}
}

View file

@ -17,6 +17,7 @@
#include <LibGfx/Font/FontVariationSettings.h>
#include <LibGfx/Forward.h>
#include <LibGfx/ShapeFeature.h>
#include <LibIPC/Forward.h>
#define POINTS_PER_INCH 72.0f
#define DEFAULT_DPI 96
@ -69,9 +70,6 @@ public:
[[nodiscard]] NonnullRefPtr<Font> 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<Core::AnonymousBuffer> anonymous_font_data() const;
u32 font_data_ttc_index() const { return ttc_index(); }
template<typename T>
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<typename T>
friend ErrorOr<void> IPC::encode(IPC::Encoder&, T const&);
template<typename T>
friend ErrorOr<T> IPC::decode(IPC::Decoder&);
using FontDataBacking = Variant<Core::AnonymousBuffer, NonnullRefPtr<Core::Resource const>>;
Optional<FontDataBacking> m_font_data;
@ -106,3 +118,13 @@ struct AK::Traits<Gfx::FontCacheKey> : public AK::DefaultTraits<Gfx::FontCacheKe
return key.hash();
}
};
namespace IPC {
template<>
ErrorOr<void> encode(Encoder&, Gfx::Typeface const&);
template<>
ErrorOr<NonnullRefPtr<Gfx::Typeface const>> decode(Decoder&);
}

View file

@ -5,14 +5,16 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/ByteString.h>
#include <AK/LsanSuppressions.h>
#include <LibCore/AnonymousBuffer.h>
#include <LibGfx/Font/FontDatabase.h>
#include <LibGfx/Font/TypefaceSkia.h>
#include <LibIPC/Encoder.h>
#include <core/SkData.h>
#include <core/SkFontMgr.h>
#include <core/SkStream.h>
#include <core/SkString.h>
#include <core/SkTypeface.h>
#if defined(AK_OS_ANDROID)
# include <ports/SkFontMgr_android.h>
@ -34,14 +36,16 @@ namespace Gfx {
static sk_sp<SkFontMgr> s_font_manager;
struct TypefaceSkia::Impl {
Impl(sk_sp<SkTypeface> skia_typeface, std::unique_ptr<SkStreamAsset> stream = {})
Impl(sk_sp<SkTypeface> skia_typeface, std::unique_ptr<SkStreamAsset> stream = {}, Optional<SystemUIFontKind> system_ui_font_kind = {})
: skia_typeface(move(skia_typeface))
, stream(move(stream))
, system_ui_font_kind(move(system_ui_font_kind))
{
}
sk_sp<SkTypeface> skia_typeface;
std::unique_ptr<SkStreamAsset> stream;
Optional<SystemUIFontKind> system_ui_font_kind;
};
static SkFontMgr& font_manager()
@ -66,6 +70,18 @@ static SkFontMgr& font_manager()
return *s_font_manager;
}
static std::unique_ptr<SkMemoryStream> 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<SkMemoryStream>(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<RefPtr<TypefaceSkia>> TypefaceSkia::typeface_from_skia_typeface(sk_sp<SkTypeface> skia_typeface)
#ifdef AK_OS_MACOS
static CTFontRef create_system_ui_font(SystemUIFontKind, float point_size, u8 slope);
#endif
ErrorOr<RefPtr<TypefaceSkia>> TypefaceSkia::typeface_from_skia_typeface(sk_sp<SkTypeface> skia_typeface, Optional<SystemUIFontKind> system_ui_font_kind)
{
if (!skia_typeface)
return RefPtr<TypefaceSkia> {};
@ -91,22 +111,20 @@ ErrorOr<RefPtr<TypefaceSkia>> TypefaceSkia::typeface_from_skia_typeface(sk_sp<Sk
// NB: Safe to reference without copying because we hold on to the stream.
ReadonlyBytes bytes { static_cast<u8 const*>(stream->getMemoryBase()), stream->getLength() };
return adopt_ref(*new TypefaceSkia {
make<TypefaceSkia::Impl>(skia_typeface, std::move(stream)),
make<TypefaceSkia::Impl>(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<void>(), 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<u8 const*>(memory_stream->getMemoryBase()), memory_stream->getLength() };
return adopt_ref(*new TypefaceSkia {
make<TypefaceSkia::Impl>(skia_typeface, move(memory_stream), system_ui_font_kind),
bytes,
ttc_index });
}
ErrorOr<NonnullRefPtr<TypefaceSkia>> TypefaceSkia::load_from_buffer(AK::ReadonlyBytes buffer, u32 ttc_index)
@ -129,6 +147,23 @@ ErrorOr<NonnullRefPtr<TypefaceSkia>> TypefaceSkia::load_from_buffer(AK::Readonly
return adopt_ref(*new TypefaceSkia { make<TypefaceSkia::Impl>(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<RefPtr<TypefaceSkia>> TypefaceSkia::match_system_ui(SystemUIFontKind kin
return RefPtr<TypefaceSkia> {};
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<RefPtr<TypefaceSkia>> TypefaceSkia::match_system_ui(SystemUIFontKind kin
#endif
}
ErrorOr<RefPtr<TypefaceSkia>> 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<RefPtr<TypefaceSkia>> 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 const> TypefaceSkia::clone_with_variations(Vector<FontVariat
if (!skia_typeface)
return {};
auto typeface = adopt_ref(*new TypefaceSkia { make<TypefaceSkia::Impl>(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<TypefaceSkia::Impl>(skia_typeface, std::unique_ptr<SkStreamAsset> {}, 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

View file

@ -26,6 +26,7 @@ class TypefaceSkia : public Gfx::Typeface {
public:
static ErrorOr<NonnullRefPtr<TypefaceSkia>> load_from_buffer(ReadonlyBytes, u32 ttc_index = 0);
static ErrorOr<RefPtr<TypefaceSkia>> match_system_ui(SystemUIFontKind, float point_size, u16 weight, double width, u8 slope);
static ErrorOr<RefPtr<TypefaceSkia>> match_family_style(StringView family_name, u16 weight, u16 width, u8 slope);
static ErrorOr<RefPtr<TypefaceSkia>> find_typeface_for_code_point(u32 code_point, u16 weight, u16 width, u8 slope);
static Optional<FlyString> 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<Impl> m_impl;
static ErrorOr<RefPtr<TypefaceSkia>> typeface_from_skia_typeface(sk_sp<SkTypeface>);
static ErrorOr<RefPtr<TypefaceSkia>> typeface_from_skia_typeface(sk_sp<SkTypeface>, Optional<SystemUIFontKind> = {});
TypefaceSkia(NonnullOwnPtr<Impl>, ReadonlyBytes, u32 ttc_index = 0);

View file

@ -18,7 +18,7 @@
#include <LibIPC/File.h>
#include <LibIPC/Forward.h>
#include <LibIPC/Message.h>
#include <LibURL/Forward.h>
#include <LibURL/URL.h>
namespace IPC {