LibGfx: Join ScaledFont into Font

Since ScaledFont is the only class inherited from Font we could simply
merge them.
This commit is contained in:
Aliaksandr Kalenik 2025-04-19 19:10:01 +02:00 committed by Andreas Kling
commit 8e2d1559ec
Notes: github-actions[bot] 2025-04-21 07:52:41 +00:00
18 changed files with 166 additions and 234 deletions

View file

@ -13,9 +13,8 @@ set(SOURCES
Font/Font.cpp
Font/FontData.cpp
Font/FontDatabase.cpp
Font/FontSkia.cpp
Font/PathFontProvider.cpp
Font/ScaledFont.cpp
Font/ScaledFontSkia.cpp
Font/Typeface.cpp
Font/TypefaceSkia.cpp
Font/WOFF/Loader.cpp

View file

@ -1,15 +1,108 @@
/*
* Copyright (c) 2023, MacDue <macdue@dueutil.tech>
* Copyright (c) 2025, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/TypeCasts.h>
#include <AK/Utf8View.h>
#include <LibGfx/Font/Font.h>
#include <LibGfx/Font/FontDatabase.h>
#include <LibGfx/Font/TypefaceSkia.h>
#include <LibGfx/TextLayout.h>
#include <core/SkFont.h>
#include <core/SkFontMetrics.h>
#include <core/SkFontTypes.h>
#include <harfbuzz/hb.h>
namespace Gfx {
Font::Font(NonnullRefPtr<Typeface const> typeface, float point_width, float point_height, unsigned dpi_x, unsigned dpi_y)
: m_typeface(move(typeface))
, m_point_width(point_width)
, m_point_height(point_height)
{
float const units_per_em = m_typeface->units_per_em();
m_x_scale = (point_width * dpi_x) / (POINTS_PER_INCH * units_per_em);
m_y_scale = (point_height * dpi_y) / (POINTS_PER_INCH * units_per_em);
m_pixel_size = m_point_height * (DEFAULT_DPI / POINTS_PER_INCH);
m_pixel_size_rounded_up = static_cast<int>(ceilf(m_pixel_size));
auto const* sk_typeface = as<TypefaceSkia>(*m_typeface).sk_typeface();
SkFont const font { sk_ref_sp(sk_typeface), m_pixel_size };
SkFontMetrics skMetrics;
font.getMetrics(&skMetrics);
FontPixelMetrics metrics;
metrics.size = font.getSize();
metrics.x_height = skMetrics.fXHeight;
metrics.advance_of_ascii_zero = font.measureText("0", 1, SkTextEncoding::kUTF8);
metrics.ascent = -skMetrics.fAscent;
metrics.descent = skMetrics.fDescent;
metrics.line_gap = skMetrics.fLeading;
m_pixel_metrics = metrics;
}
ScaledFontMetrics Font::metrics() const
{
SkFontMetrics sk_metrics;
skia_font(1).getMetrics(&sk_metrics);
ScaledFontMetrics metrics;
metrics.ascender = -sk_metrics.fAscent;
metrics.descender = sk_metrics.fDescent;
metrics.line_gap = sk_metrics.fLeading;
metrics.x_height = sk_metrics.fXHeight;
return metrics;
}
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::glyph_width(u32 code_point) const
{
auto string = String::from_code_point(code_point);
return measure_text_width(Utf8View(string), *this, {});
}
NonnullRefPtr<Font> Font::scaled_with_size(float point_size) const
{
if (point_size == m_point_height && point_size == m_point_width)
return *const_cast<Font*>(this);
return m_typeface->font(point_size);
}
NonnullRefPtr<Font> Font::with_size(float point_size) const
{
return scaled_with_size(point_size);
}
Gfx::FontPixelMetrics Font::pixel_metrics() const
{
return m_pixel_metrics;
}
float Font::pixel_size() const
{
return m_pixel_size;
}
int Font::pixel_size_rounded_up() const
{
return m_pixel_size_rounded_up;
}
float Font::point_size() const
{
return m_point_height;
}
Font::~Font()
{
if (m_harfbuzz_font)

View file

@ -1,17 +1,19 @@
/*
* Copyright (c) 2020, Stephan Unverwerth <s.unverwerth@serenityos.org>
* Copyright (c) 2023, MacDue <macdue@dueutil.tech>
* Copyright (c) 2023, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2025, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/RefCounted.h>
#include <AK/RefPtr.h>
#include <AK/String.h>
#include <AK/Types.h>
#include <AK/FlyString.h>
#include <LibGfx/Font/Font.h>
#include <LibGfx/Font/Typeface.h>
class SkFont;
struct hb_font_t;
namespace Gfx {
@ -48,50 +50,51 @@ enum FontWidth {
constexpr float text_shaping_resolution = 64;
class Typeface;
class Font : public RefCounted<Font> {
public:
virtual ~Font();
Font(NonnullRefPtr<Typeface const>, float point_width, float point_height, unsigned dpi_x = DEFAULT_DPI, unsigned dpi_y = DEFAULT_DPI);
ScaledFontMetrics metrics() const;
~Font();
virtual FontPixelMetrics pixel_metrics() const = 0;
float point_size() const;
float pixel_size() const;
int pixel_size_rounded_up() const;
Gfx::FontPixelMetrics pixel_metrics() const;
u8 slope() const { return m_typeface->slope(); }
u16 weight() const { return m_typeface->weight(); }
bool contains_glyph(u32 code_point) const { return m_typeface->glyph_id_for_code_point(code_point) > 0; }
float glyph_width(u32 code_point) const;
u32 glyph_id_for_code_point(u32 code_point) const { return m_typeface->glyph_id_for_code_point(code_point); }
float preferred_line_height() const { return metrics().height() + metrics().line_gap; }
int x_height() const { return m_point_height; } // FIXME: Read from font
u8 baseline() const { return m_point_height; } // FIXME: Read from font
float width(StringView) const;
float width(Utf8View const&) const;
FlyString const& family() const { return m_typeface->family(); }
virtual u8 slope() const = 0;
NonnullRefPtr<Font> scaled_with_size(float point_size) const;
NonnullRefPtr<Font> with_size(float point_size) const;
// Font point size (distance between ascender and descender).
virtual float point_size() const = 0;
Typeface const& typeface() const { return m_typeface; }
// Font pixel size (distance between ascender and descender).
virtual float pixel_size() const = 0;
// Font pixel size, rounded up to the nearest integer.
virtual int pixel_size_rounded_up() const = 0;
virtual u16 weight() const = 0;
virtual bool contains_glyph(u32 code_point) const = 0;
virtual u32 glyph_id_for_code_point(u32 code_point) const = 0;
virtual float glyph_width(u32 code_point) const = 0;
virtual int x_height() const = 0;
virtual float preferred_line_height() const = 0;
virtual u8 baseline() const = 0;
virtual float width(StringView) const = 0;
virtual float width(Utf8View const&) const = 0;
virtual FlyString const& family() const = 0;
virtual NonnullRefPtr<Font> with_size(float point_size) const = 0;
SkFont skia_font(float scale) const;
Font const& bold_variant() const;
hb_font_t* harfbuzz_font() const;
virtual Typeface const& typeface() const = 0;
private:
mutable RefPtr<Gfx::Font const> m_bold_variant;
mutable RefPtr<Font const> m_bold_variant;
mutable hb_font_t* m_harfbuzz_font { nullptr };
NonnullRefPtr<Typeface const> m_typeface;
float m_x_scale { 0.0f };
float m_y_scale { 0.0f };
float m_point_width { 0.0f };
float m_point_height { 0.0f };
FontPixelMetrics m_pixel_metrics;
float m_pixel_size { 0.0f };
int m_pixel_size_rounded_up { 0 };
};
}

View file

@ -5,14 +5,14 @@
*/
#include <AK/TypeCasts.h>
#include <LibGfx/Font/ScaledFont.h>
#include <LibGfx/Font/Font.h>
#include <LibGfx/Font/TypefaceSkia.h>
#include <core/SkFont.h>
namespace Gfx {
SkFont ScaledFont::skia_font(float scale) const
SkFont Font::skia_font(float scale) const
{
auto const& sk_typeface = as<TypefaceSkia>(*m_typeface).sk_typeface();
auto sk_font = SkFont { sk_ref_sp(sk_typeface), pixel_size() * scale };

View file

@ -7,8 +7,8 @@
#include <AK/Format.h>
#include <AK/LexicalPath.h>
#include <LibCore/Resource.h>
#include <LibGfx/Font/Font.h>
#include <LibGfx/Font/PathFontProvider.h>
#include <LibGfx/Font/ScaledFont.h>
#include <LibGfx/Font/WOFF/Loader.h>
namespace Gfx {
@ -60,7 +60,7 @@ RefPtr<Gfx::Font> PathFontProvider::get_font(FlyString const& family, float poin
return nullptr;
for (auto const& typeface : it->value) {
if (typeface->weight() == weight && typeface->width() == width && typeface->slope() == slope)
return typeface->scaled_font(point_size);
return typeface->font(point_size);
}
return nullptr;
}

View file

@ -1,102 +0,0 @@
/*
* Copyright (c) 2022, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/TypeCasts.h>
#include <AK/Utf8View.h>
#include <LibGfx/Font/ScaledFont.h>
#include <LibGfx/Font/TypefaceSkia.h>
#include <LibGfx/TextLayout.h>
#include <core/SkFont.h>
#include <core/SkFontMetrics.h>
#include <core/SkFontTypes.h>
namespace Gfx {
ScaledFont::ScaledFont(NonnullRefPtr<Typeface const> typeface, float point_width, float point_height, unsigned dpi_x, unsigned dpi_y)
: m_typeface(move(typeface))
, m_point_width(point_width)
, m_point_height(point_height)
{
float const units_per_em = m_typeface->units_per_em();
m_x_scale = (point_width * dpi_x) / (POINTS_PER_INCH * units_per_em);
m_y_scale = (point_height * dpi_y) / (POINTS_PER_INCH * units_per_em);
m_pixel_size = m_point_height * (DEFAULT_DPI / POINTS_PER_INCH);
m_pixel_size_rounded_up = static_cast<int>(ceilf(m_pixel_size));
auto const* sk_typeface = as<TypefaceSkia>(*m_typeface).sk_typeface();
SkFont const font { sk_ref_sp(sk_typeface), m_pixel_size };
SkFontMetrics skMetrics;
font.getMetrics(&skMetrics);
FontPixelMetrics metrics;
metrics.size = font.getSize();
metrics.x_height = skMetrics.fXHeight;
metrics.advance_of_ascii_zero = font.measureText("0", 1, SkTextEncoding::kUTF8);
metrics.ascent = -skMetrics.fAscent;
metrics.descent = skMetrics.fDescent;
metrics.line_gap = skMetrics.fLeading;
m_pixel_metrics = metrics;
}
ScaledFontMetrics ScaledFont::metrics() const
{
SkFontMetrics sk_metrics;
skia_font(1).getMetrics(&sk_metrics);
ScaledFontMetrics metrics;
metrics.ascender = -sk_metrics.fAscent;
metrics.descender = sk_metrics.fDescent;
metrics.line_gap = sk_metrics.fLeading;
metrics.x_height = sk_metrics.fXHeight;
return metrics;
}
float ScaledFont::width(StringView view) const { return measure_text_width(Utf8View(view), *this, {}); }
float ScaledFont::width(Utf8View const& view) const { return measure_text_width(view, *this, {}); }
float ScaledFont::glyph_width(u32 code_point) const
{
auto string = String::from_code_point(code_point);
return measure_text_width(Utf8View(string), *this, {});
}
NonnullRefPtr<ScaledFont> ScaledFont::scaled_with_size(float point_size) const
{
if (point_size == m_point_height && point_size == m_point_width)
return *const_cast<ScaledFont*>(this);
return m_typeface->scaled_font(point_size);
}
NonnullRefPtr<Font> ScaledFont::with_size(float point_size) const
{
return scaled_with_size(point_size);
}
Gfx::FontPixelMetrics ScaledFont::pixel_metrics() const
{
return m_pixel_metrics;
}
float ScaledFont::pixel_size() const
{
return m_pixel_size;
}
int ScaledFont::pixel_size_rounded_up() const
{
return m_pixel_size_rounded_up;
}
float ScaledFont::point_size() const
{
return m_point_height;
}
}

View file

@ -1,59 +0,0 @@
/*
* Copyright (c) 2022, the SerenityOS developers.
* Copyright (c) 2023, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/FlyString.h>
#include <LibGfx/Font/Font.h>
#include <LibGfx/Font/Typeface.h>
class SkFont;
namespace Gfx {
class ScaledFont final : public Gfx::Font {
public:
ScaledFont(NonnullRefPtr<Typeface const>, float point_width, float point_height, unsigned dpi_x = DEFAULT_DPI, unsigned dpi_y = DEFAULT_DPI);
ScaledFontMetrics metrics() const;
// ^Gfx::Font
virtual float point_size() const override;
virtual float pixel_size() const override;
virtual int pixel_size_rounded_up() const override;
virtual Gfx::FontPixelMetrics pixel_metrics() const override;
virtual u8 slope() const override { return m_typeface->slope(); }
virtual u16 weight() const override { return m_typeface->weight(); }
virtual bool contains_glyph(u32 code_point) const override { return m_typeface->glyph_id_for_code_point(code_point) > 0; }
virtual float glyph_width(u32 code_point) const override;
virtual u32 glyph_id_for_code_point(u32 code_point) const override { return m_typeface->glyph_id_for_code_point(code_point); }
virtual float preferred_line_height() const override { return metrics().height() + metrics().line_gap; }
virtual int x_height() const override { return m_point_height; } // FIXME: Read from font
virtual u8 baseline() const override { return m_point_height; } // FIXME: Read from font
virtual float width(StringView) const override;
virtual float width(Utf8View const&) const override;
virtual FlyString const& family() const override { return m_typeface->family(); }
virtual NonnullRefPtr<ScaledFont> scaled_with_size(float point_size) const;
virtual NonnullRefPtr<Font> with_size(float point_size) const override;
virtual Typeface const& typeface() const override { return m_typeface; }
SkFont skia_font(float scale) const;
private:
NonnullRefPtr<Typeface const> m_typeface;
float m_x_scale { 0.0f };
float m_y_scale { 0.0f };
float m_point_width { 0.0f };
float m_point_height { 0.0f };
Gfx::FontPixelMetrics m_pixel_metrics;
float m_pixel_size { 0.0f };
int m_pixel_size_rounded_up { 0 };
};
}

View file

@ -6,7 +6,7 @@
#include <harfbuzz/hb.h>
#include <LibGfx/Font/ScaledFont.h>
#include <LibGfx/Font/Font.h>
#include <LibGfx/Font/Typeface.h>
#include <LibGfx/Font/TypefaceSkia.h>
@ -40,21 +40,21 @@ Typeface::~Typeface()
hb_blob_destroy(m_harfbuzz_blob);
}
NonnullRefPtr<ScaledFont> Typeface::scaled_font(float point_size) const
NonnullRefPtr<Font> Typeface::font(float point_size) const
{
auto it = m_scaled_fonts.find(point_size);
if (it != m_scaled_fonts.end())
auto it = m_fonts.find(point_size);
if (it != m_fonts.end())
return *it->value;
// FIXME: It might be nice to have a global cap on the number of fonts we cache
// instead of doing it at the per-Typeface level like this.
constexpr size_t max_cached_font_size_count = 128;
if (m_scaled_fonts.size() > max_cached_font_size_count)
m_scaled_fonts.remove(m_scaled_fonts.begin());
if (m_fonts.size() > max_cached_font_size_count)
m_fonts.remove(m_fonts.begin());
auto scaled_font = adopt_ref(*new ScaledFont(*this, point_size, point_size));
m_scaled_fonts.set(point_size, scaled_font);
return scaled_font;
auto font = adopt_ref(*new Font(*this, point_size, point_size));
m_fonts.set(point_size, font);
return font;
}
hb_face_t* Typeface::harfbuzz_typeface() const

View file

@ -20,7 +20,7 @@ struct hb_face_t;
namespace Gfx {
class ScaledFont;
class Font;
struct ScaledFontMetrics {
float ascender { 0 };
@ -50,7 +50,7 @@ public:
virtual u16 width() const = 0;
virtual u8 slope() const = 0;
[[nodiscard]] NonnullRefPtr<ScaledFont> scaled_font(float point_size) const;
[[nodiscard]] NonnullRefPtr<Font> font(float point_size) const;
hb_face_t* harfbuzz_typeface() const;
@ -61,9 +61,9 @@ protected:
virtual unsigned ttc_index() const = 0;
private:
OwnPtr<Gfx::FontData> m_font_data;
OwnPtr<FontData> m_font_data;
mutable HashMap<float, NonnullRefPtr<ScaledFont>> m_scaled_fonts;
mutable HashMap<float, NonnullRefPtr<Font>> m_fonts;
mutable hb_blob_t* m_harfbuzz_blob { nullptr };
mutable hb_face_t* m_harfbuzz_face { nullptr };
};

View file

@ -17,7 +17,6 @@ class Emoji;
class Font;
class ImageDecoder;
struct FontPixelMetrics;
class ScaledFont;
template<typename T>
class Line;

View file

@ -7,7 +7,7 @@
#define AK_DONT_REPLACE_STD
#include <AK/TypeCasts.h>
#include <LibGfx/Font/ScaledFont.h>
#include <LibGfx/Font/Font.h>
#include <LibGfx/PathSkia.h>
#include <LibGfx/Rect.h>
#include <LibGfx/SkiaUtils.h>
@ -137,11 +137,11 @@ void PathImplSkia::cubic_bezier_curve_to(FloatPoint c1, FloatPoint c2, FloatPoin
void PathImplSkia::text(Utf8View string, Font const& font)
{
SkTextUtils::GetPath(string.as_string().characters_without_null_termination(), string.as_string().length(), SkTextEncoding::kUTF8, last_point().x(), last_point().y(), as<ScaledFont>(font).skia_font(1), m_path.ptr());
SkTextUtils::GetPath(string.as_string().characters_without_null_termination(), string.as_string().length(), SkTextEncoding::kUTF8, last_point().x(), last_point().y(), font.skia_font(1), m_path.ptr());
}
NonnullOwnPtr<PathImpl> PathImplSkia::place_text_along(Utf8View text, Font const& font) const
{
auto sk_font = as<ScaledFont>(font).skia_font(1);
auto sk_font = font.skia_font(1);
size_t const text_length = text.length();
SkScalar x = 0;
SkScalar y = 0;

View file

@ -21,7 +21,6 @@
#include <LibGfx/Font/FontDatabase.h>
#include <LibGfx/Font/FontStyleMapping.h>
#include <LibGfx/Font/FontWeight.h>
#include <LibGfx/Font/ScaledFont.h>
#include <LibGfx/Font/Typeface.h>
#include <LibGfx/Font/WOFF/Loader.h>
#include <LibGfx/Font/WOFF2/Loader.h>
@ -236,7 +235,7 @@ RefPtr<Gfx::Font const> FontLoader::font_with_point_size(float point_size)
start_loading_next_url();
return nullptr;
}
return m_vector_font->scaled_font(point_size);
return m_vector_font->font(point_size);
}
void FontLoader::start_loading_next_url()
@ -304,7 +303,7 @@ struct StyleComputer::MatchingFontCandidate {
return font_list;
}
font_list->add(loader_or_typeface.get<Gfx::Typeface const*>()->scaled_font(point_size));
font_list->add(loader_or_typeface.get<Gfx::Typeface const*>()->font(point_size));
return font_list;
}
};

View file

@ -150,8 +150,8 @@ public:
bool can_contain_boxes_with_position_absolute() const;
Gfx::Font const& first_available_font() const;
Gfx::Font const& scaled_font(PaintContext&) const;
Gfx::Font const& scaled_font(float scale_factor) const;
Gfx::Font const& font(PaintContext&) const;
Gfx::Font const& font(float scale_factor) const;
CSS::ImmutableComputedValues const& computed_values() const;
@ -315,12 +315,12 @@ inline Gfx::Font const& Node::first_available_font() const
return parent()->first_available_font();
}
inline Gfx::Font const& Node::scaled_font(PaintContext& context) const
inline Gfx::Font const& Node::font(PaintContext& context) const
{
return scaled_font(context.device_pixels_per_css_pixel());
return font(context.device_pixels_per_css_pixel());
}
inline Gfx::Font const& Node::scaled_font(float scale_factor) const
inline Gfx::Font const& Node::font(float scale_factor) const
{
auto const& font = first_available_font();
return font.with_size(font.point_size() * scale_factor);

View file

@ -9,7 +9,7 @@
#include <AK/Debug.h>
#include <LibGfx/BoundingBox.h>
#include <LibGfx/Font/ScaledFont.h>
#include <LibGfx/Font/Font.h>
#include <LibGfx/Path.h>
#include <LibGfx/TextLayout.h>
#include <LibWeb/Layout/BlockFormattingContext.h>

View file

@ -22,7 +22,7 @@
#include <gpu/ganesh/SkSurfaceGanesh.h>
#include <pathops/SkPathOps.h>
#include <LibGfx/Font/ScaledFont.h>
#include <LibGfx/Font/Font.h>
#include <LibGfx/PainterSkia.h>
#include <LibGfx/PathSkia.h>
#include <LibGfx/SkiaUtils.h>
@ -77,7 +77,7 @@ void DisplayListPlayerSkia::flush()
void DisplayListPlayerSkia::draw_glyph_run(DrawGlyphRun const& command)
{
auto const& gfx_font = static_cast<Gfx::ScaledFont const&>(command.glyph_run->font());
auto const& gfx_font = command.glyph_run->font();
auto sk_font = gfx_font.skia_font(command.scale);
auto glyph_count = command.glyph_run->glyphs().size();

View file

@ -70,7 +70,7 @@ void MarkerPaintable::paint(PaintContext& context, PaintPhase phase) const
if (auto text = layout_box().text(); text.has_value()) {
// FIXME: This should use proper text layout logic!
// This does not line up with the text in the <li> element which looks very sad :(
context.display_list_recorder().draw_text(device_enclosing.to_type<int>(), *text, layout_box().scaled_font(context), Gfx::TextAlignment::Center, color);
context.display_list_recorder().draw_text(device_enclosing.to_type<int>(), *text, layout_box().font(context), Gfx::TextAlignment::Center, color);
} else if (auto const* counter_style = layout_box().list_style_type().get_pointer<CSS::CounterStyleNameKeyword>()) {
switch (*counter_style) {
case CSS::CounterStyleNameKeyword::Square:

View file

@ -118,7 +118,7 @@ MediaPaintable::Components MediaPaintable::compute_control_bar_components(PaintC
auto display_time = human_readable_digital_time(round(media_element.layout_display_time({})));
auto duration = human_readable_digital_time(isnan(media_element.duration()) ? 0 : round(media_element.duration()));
components.timestamp = String::formatted("{} / {}", display_time, duration).release_value_but_fixme_should_propagate_errors();
components.timestamp_font = layout_node().scaled_font(context);
components.timestamp_font = layout_node().font(context);
auto timestamp_size = DevicePixels { static_cast<DevicePixels::Type>(ceilf(components.timestamp_font->width(components.timestamp))) };
if (timestamp_size <= remaining_rect.width()) {

View file

@ -8,7 +8,7 @@
*/
#include <AK/GenericShorthands.h>
#include <LibGfx/Font/ScaledFont.h>
#include <LibGfx/Font/Font.h>
#include <LibUnicode/CharacterTypes.h>
#include <LibWeb/CSS/SystemColor.h>
#include <LibWeb/DOM/Document.h>