From ce6636e78e0f6ec9a54774405faf147635552923 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Mon, 30 Jan 2023 19:13:39 +0100 Subject: [PATCH] LibGfx: Make glyph ID lookup faster with a cache This patch adds a "GlyphPage" cache which stores the mapping between code points and glyph IDs in a segmented table of "pages". This makes Font::glyph_id_for_code_point() significantly faster by not reparsing the font tables every time you call it. In the future, we can add more information to GlyphPage (such as horizontal metrics for each glyph) to further reduce time spent in text layout and painting. --- .../Libraries/LibGfx/Font/OpenType/Font.cpp | 34 +++++++++++++++++++ .../Libraries/LibGfx/Font/OpenType/Font.h | 20 ++++++++++- 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/Userland/Libraries/LibGfx/Font/OpenType/Font.cpp b/Userland/Libraries/LibGfx/Font/OpenType/Font.cpp index b203c94ae4..64f39fdc2f 100644 --- a/Userland/Libraries/LibGfx/Font/OpenType/Font.cpp +++ b/Userland/Libraries/LibGfx/Font/OpenType/Font.cpp @@ -726,4 +726,38 @@ Optional Font::glyph_program(u32 glyph_id) const return glyph.program(); } +u32 Font::glyph_id_for_code_point(u32 code_point) const +{ + return glyph_page(code_point / GlyphPage::glyphs_per_page).glyph_ids[code_point % GlyphPage::glyphs_per_page]; +} + +Font::GlyphPage const& Font::glyph_page(size_t page_index) const +{ + if (page_index == 0) { + if (!m_glyph_page_zero) { + m_glyph_page_zero = make(); + populate_glyph_page(*m_glyph_page_zero, 0); + } + return *m_glyph_page_zero; + } + if (auto it = m_glyph_pages.find(page_index); it != m_glyph_pages.end()) { + return *it->value; + } + + auto glyph_page = make(); + populate_glyph_page(*glyph_page, page_index); + auto const* glyph_page_ptr = glyph_page.ptr(); + m_glyph_pages.set(page_index, move(glyph_page)); + return *glyph_page_ptr; +} + +void Font::populate_glyph_page(GlyphPage& glyph_page, size_t page_index) const +{ + u32 first_code_point = page_index * GlyphPage::glyphs_per_page; + for (size_t i = 0; i < GlyphPage::glyphs_per_page; ++i) { + u32 code_point = first_code_point + i; + glyph_page.glyph_ids[i] = m_cmap.glyph_id_for_code_point(code_point); + } +} + } diff --git a/Userland/Libraries/LibGfx/Font/OpenType/Font.h b/Userland/Libraries/LibGfx/Font/OpenType/Font.h index a1e27a7789..2061decc34 100644 --- a/Userland/Libraries/LibGfx/Font/OpenType/Font.h +++ b/Userland/Libraries/LibGfx/Font/OpenType/Font.h @@ -7,6 +7,8 @@ #pragma once #include +#include +#include #include #include #include @@ -31,7 +33,7 @@ public: virtual RefPtr rasterize_glyph(u32 glyph_id, float x_scale, float y_scale, Gfx::GlyphSubpixelOffset) const override; virtual u32 glyph_count() const override; virtual u16 units_per_em() const override; - virtual u32 glyph_id_for_code_point(u32 code_point) const override { return m_cmap.glyph_id_for_code_point(code_point); } + virtual u32 glyph_id_for_code_point(u32 code_point) const override; virtual DeprecatedString family() const override; virtual DeprecatedString variant() const override; virtual u16 weight() const override; @@ -90,6 +92,22 @@ private: Optional m_kern; Optional m_fpgm; Optional m_prep; + + // This cache stores information per code point. + // It's segmented into pages with data about 256 code points each. + struct GlyphPage { + static constexpr size_t glyphs_per_page = 256; + + u32 glyph_ids[glyphs_per_page]; + }; + + // Fast cache for GlyphPage #0 (code points 0-255) to avoid hash lookups for all of ASCII and Latin-1. + mutable OwnPtr m_glyph_page_zero; + + mutable HashMap> m_glyph_pages; + + GlyphPage const& glyph_page(size_t page_index) const; + void populate_glyph_page(GlyphPage&, size_t page_index) const; }; }