Coverage Report

Created: 2025-11-16 07:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibGfx/Font/ScaledFont.cpp
Line
Count
Source
1
/*
2
 * Copyright (c) 2022, the SerenityOS developers.
3
 *
4
 * SPDX-License-Identifier: BSD-2-Clause
5
 */
6
7
#include <AK/Utf32View.h>
8
#include <AK/Utf8View.h>
9
#include <LibGfx/Font/Emoji.h>
10
#include <LibGfx/Font/ScaledFont.h>
11
12
namespace Gfx {
13
14
ScaledFont::ScaledFont(NonnullRefPtr<VectorFont> font, float point_width, float point_height, unsigned dpi_x, unsigned dpi_y)
15
0
    : m_font(move(font))
16
0
    , m_point_width(point_width)
17
0
    , m_point_height(point_height)
18
0
{
19
0
    float units_per_em = m_font->units_per_em();
20
0
    m_x_scale = (point_width * dpi_x) / (POINTS_PER_INCH * units_per_em);
21
0
    m_y_scale = (point_height * dpi_y) / (POINTS_PER_INCH * units_per_em);
22
23
0
    auto metrics = m_font->metrics(m_x_scale, m_y_scale);
24
25
0
    m_pixel_size = m_point_height * (DEFAULT_DPI / POINTS_PER_INCH);
26
0
    m_pixel_size_rounded_up = static_cast<int>(ceilf(m_pixel_size));
27
28
0
    m_pixel_metrics = Gfx::FontPixelMetrics {
29
0
        .size = (float)pixel_size(),
30
0
        .x_height = metrics.x_height,
31
0
        .advance_of_ascii_zero = (float)glyph_width('0'),
32
0
        .glyph_spacing = (float)glyph_spacing(),
33
0
        .ascent = metrics.ascender,
34
0
        .descent = metrics.descender,
35
0
        .line_gap = metrics.line_gap,
36
0
    };
37
0
}
38
39
int ScaledFont::width_rounded_up(StringView view) const
40
0
{
41
0
    return static_cast<int>(ceilf(width(view)));
42
0
}
43
44
0
float ScaledFont::width(StringView view) const { return unicode_view_width(Utf8View(view)); }
45
0
float ScaledFont::width(Utf8View const& view) const { return unicode_view_width(view); }
46
0
float ScaledFont::width(Utf32View const& view) const { return unicode_view_width(view); }
47
48
template<typename T>
49
ALWAYS_INLINE float ScaledFont::unicode_view_width(T const& view) const
50
0
{
51
0
    if (view.is_empty())
52
0
        return 0;
53
0
    float width = 0;
54
0
    float longest_width = 0;
55
0
    u32 last_code_point = 0;
56
57
0
    for (auto it = view.begin(); it != view.end(); last_code_point = *it, ++it) {
58
0
        auto code_point = *it;
59
60
0
        if (code_point == '\n' || code_point == '\r') {
61
0
            longest_width = max(width, longest_width);
62
0
            width = 0;
63
0
            continue;
64
0
        }
65
66
0
        auto kerning = glyphs_horizontal_kerning(last_code_point, code_point);
67
0
        width += kerning + glyph_or_emoji_width(it);
68
0
    }
69
70
0
    longest_width = max(width, longest_width);
71
0
    return longest_width;
72
0
}
Unexecuted instantiation: float Gfx::ScaledFont::unicode_view_width<AK::Utf8View>(AK::Utf8View const&) const
Unexecuted instantiation: float Gfx::ScaledFont::unicode_view_width<AK::Utf32View>(AK::Utf32View const&) const
73
74
RefPtr<Gfx::Bitmap> ScaledFont::rasterize_glyph(u32 glyph_id, GlyphSubpixelOffset subpixel_offset) const
75
0
{
76
0
    GlyphIndexWithSubpixelOffset index { glyph_id, subpixel_offset };
77
0
    auto glyph_iterator = m_cached_glyph_bitmaps.find(index);
78
0
    if (glyph_iterator != m_cached_glyph_bitmaps.end())
79
0
        return glyph_iterator->value;
80
81
0
    auto glyph_bitmap = m_font->rasterize_glyph(glyph_id, m_x_scale, m_y_scale, subpixel_offset);
82
0
    m_cached_glyph_bitmaps.set(index, glyph_bitmap);
83
0
    return glyph_bitmap;
84
0
}
85
86
bool ScaledFont::append_glyph_path_to(Gfx::Path& path, u32 glyph_id) const
87
0
{
88
0
    auto glyph_iterator = m_glyph_cache.find(glyph_id);
89
0
    if (glyph_iterator != m_glyph_cache.end()) {
90
0
        path.append_path(glyph_iterator->value, Path::AppendRelativeToLastPoint::Yes);
91
0
        return true;
92
0
    }
93
0
    Gfx::Path glyph_path;
94
0
    bool success = m_font->append_glyph_path_to(glyph_path, glyph_id, m_x_scale, m_y_scale);
95
0
    if (success) {
96
0
        path.append_path(glyph_path, Path::AppendRelativeToLastPoint::Yes);
97
0
        m_glyph_cache.set(glyph_id, move(glyph_path));
98
0
    }
99
0
    return success;
100
0
}
101
102
Optional<Gfx::Glyph> ScaledFont::glyph(u32 code_point) const
103
0
{
104
0
    return glyph(code_point, GlyphSubpixelOffset { 0, 0 });
105
0
}
106
107
Optional<Glyph> ScaledFont::glyph_for_id(u32 id, GlyphSubpixelOffset subpixel_offset) const
108
0
{
109
0
    auto bitmap = rasterize_glyph(id, subpixel_offset);
110
0
    if (!bitmap)
111
0
        return {};
112
0
    auto metrics = glyph_metrics(id);
113
0
    return Gfx::Glyph(bitmap, metrics.left_side_bearing, metrics.advance_width, metrics.ascender, m_font->has_color_bitmaps());
114
0
}
115
116
Optional<Gfx::Glyph> ScaledFont::glyph(u32 code_point, GlyphSubpixelOffset subpixel_offset) const
117
0
{
118
0
    auto id = glyph_id_for_code_point(code_point);
119
0
    return glyph_for_id(id, subpixel_offset);
120
0
}
121
122
Optional<Glyph> ScaledFont::glyph_for_postscript_name(StringView name, GlyphSubpixelOffset subpixel_offset) const
123
0
{
124
0
    auto id = glyph_id_for_postscript_name(name);
125
0
    if (!id.has_value())
126
0
        return {};
127
0
    return glyph_for_id(id.value(), subpixel_offset);
128
0
}
129
130
float ScaledFont::glyph_left_bearing(u32 code_point) const
131
0
{
132
0
    auto id = glyph_id_for_code_point(code_point);
133
0
    return glyph_metrics(id).left_side_bearing;
134
0
}
135
136
Optional<float> ScaledFont::glyph_left_bearing_for_postscript_name(StringView name) const
137
0
{
138
0
    auto id = glyph_id_for_postscript_name(name);
139
0
    if (!id.has_value())
140
0
        return {};
141
0
    return glyph_metrics(id.value()).left_side_bearing;
142
0
}
143
144
float ScaledFont::glyph_width(u32 code_point) const
145
0
{
146
0
    auto id = glyph_id_for_code_point(code_point);
147
0
    return m_font->glyph_advance(id, m_x_scale, m_y_scale, m_point_width, m_point_height);
148
0
}
149
150
Optional<float> ScaledFont::glyph_width_for_postscript_name(StringView name) const
151
0
{
152
0
    auto id = glyph_id_for_postscript_name(name);
153
0
    if (!id.has_value())
154
0
        return {};
155
0
    return m_font->glyph_advance(id.value(), m_x_scale, m_y_scale, m_point_width, m_point_height);
156
0
}
157
158
template<typename CodePointIterator>
159
static float glyph_or_emoji_width_impl(ScaledFont const& font, CodePointIterator& it)
160
0
{
161
0
    if (!font.has_color_bitmaps()) {
162
0
        if (auto const* emoji = Emoji::emoji_for_code_point_iterator(it))
163
0
            return font.pixel_size() * emoji->width() / emoji->height();
164
0
    }
165
166
0
    return font.glyph_width(*it);
167
0
}
Unexecuted instantiation: ScaledFont.cpp:float Gfx::glyph_or_emoji_width_impl<AK::Utf8CodePointIterator>(Gfx::ScaledFont const&, AK::Utf8CodePointIterator&)
Unexecuted instantiation: ScaledFont.cpp:float Gfx::glyph_or_emoji_width_impl<AK::Utf32CodePointIterator>(Gfx::ScaledFont const&, AK::Utf32CodePointIterator&)
168
169
float ScaledFont::glyph_or_emoji_width(Utf8CodePointIterator& it) const
170
0
{
171
0
    return glyph_or_emoji_width_impl(*this, it);
172
0
}
173
174
float ScaledFont::glyph_or_emoji_width(Utf32CodePointIterator& it) const
175
0
{
176
0
    return glyph_or_emoji_width_impl(*this, it);
177
0
}
178
179
float ScaledFont::glyphs_horizontal_kerning(u32 left_code_point, u32 right_code_point) const
180
0
{
181
0
    if (left_code_point == 0 || right_code_point == 0)
182
0
        return 0.f;
183
184
0
    auto left_glyph_id = glyph_id_for_code_point(left_code_point);
185
0
    auto right_glyph_id = glyph_id_for_code_point(right_code_point);
186
0
    if (left_glyph_id == 0 || right_glyph_id == 0)
187
0
        return 0.f;
188
189
0
    return m_font->glyphs_horizontal_kerning(left_glyph_id, right_glyph_id, m_x_scale);
190
0
}
191
192
u8 ScaledFont::glyph_fixed_width() const
193
0
{
194
0
    return glyph_metrics(glyph_id_for_code_point(' ')).advance_width;
195
0
}
196
197
NonnullRefPtr<ScaledFont> ScaledFont::scaled_with_size(float point_size) const
198
0
{
199
0
    if (point_size == m_point_height && point_size == m_point_width)
200
0
        return *const_cast<ScaledFont*>(this);
201
0
    return m_font->scaled_font(point_size);
202
0
}
203
204
NonnullRefPtr<Font> ScaledFont::with_size(float point_size) const
205
0
{
206
0
    return scaled_with_size(point_size);
207
0
}
208
209
Gfx::FontPixelMetrics ScaledFont::pixel_metrics() const
210
0
{
211
0
    return m_pixel_metrics;
212
0
}
213
214
float ScaledFont::pixel_size() const
215
0
{
216
0
    return m_pixel_size;
217
0
}
218
219
int ScaledFont::pixel_size_rounded_up() const
220
0
{
221
0
    return m_pixel_size_rounded_up;
222
0
}
223
224
float ScaledFont::point_size() const
225
0
{
226
0
    return m_point_height;
227
0
}
228
229
}