feat: rendering pages on demand

This commit is contained in:
MihailRis 2026-02-16 01:42:48 +03:00
parent 381cb77d08
commit 47809b4024
9 changed files with 106 additions and 39 deletions

View file

@ -208,6 +208,7 @@ assetload::postfunc assetload::font(
logger.info() << "loading vector font " << util::quote(filename); logger.info() << "loading vector font " << util::quote(filename);
return [=](auto assets) { return [=](auto assets) {
auto font = vector_fonts::load_font(paths.find(filename).string()); auto font = vector_fonts::load_font(paths.find(filename).string());
assets->store(font, filename);
assets->store(font->createInstance(cfg ? cfg->size : 16), name); assets->store(font->createInstance(cfg ? cfg->size : 16), name);
}; };
} }

View file

@ -25,7 +25,8 @@ static const char* get_ft_error_message(FT_Error error) {
} }
namespace { namespace {
class FTFontFile : public vector_fonts::FontFile { class FTFontFile : public vector_fonts::FontFile,
public std::enable_shared_from_this<FTFontFile> {
public: public:
FTFontFile(FT_Face face, util::Buffer<ubyte> buffer) FTFontFile(FT_Face face, util::Buffer<ubyte> buffer)
: face(std::move(face)), buffer(std::move(buffer)) { : face(std::move(face)), buffer(std::move(buffer)) {
@ -47,20 +48,42 @@ namespace {
} }
std::vector<std::unique_ptr<Texture>> pages; std::vector<std::unique_ptr<Texture>> pages;
std::vector<Font::Glyph> glyphs; std::vector<Glyph> glyphs;
int dstSize = size * 16; int dstSize = size * 16;
ImageData canvas(ImageFormat::RGBA8888, dstSize, dstSize); ImageData canvas(ImageFormat::RGBA8888, dstSize, dstSize);
ImageData bitmapDst(ImageFormat::RGBA8888, size, size); ImageData bitmapDst(ImageFormat::RGBA8888, size, size);
for (int pageid = 0; pageid < FONT_PAGES; pageid++) { for (int pageid = 0; pageid < DEFAULT_PRE_RENDER_FONT_PAGES; pageid++) {
renderPage(pageid, canvas, bitmapDst, glyphs); renderPage(pageid, canvas, bitmapDst, glyphs);
pages.push_back(Texture::from(&canvas)); pages.push_back(Texture::from(&canvas));
} }
return std::make_unique<Font>( return std::make_unique<Font>(
std::move(pages), std::move(glyphs), size, size / 2 std::move(pages),
std::move(glyphs),
size,
size / 2,
shared_from_this()
); );
} }
std::unique_ptr<Texture> renderPage(
int pageid, std::vector<Glyph>& glyphs, int size
) override {
if (FT_Error error = FT_Set_Pixel_Sizes(face, 0, size)) {
throw std::runtime_error(
"FT_Set_Pixel_Sizes error: " +
std::string(get_ft_error_message(error))
);
}
int dstSize = size * 16;
ImageData canvas(ImageFormat::RGBA8888, dstSize, dstSize);
ImageData bitmapDst(ImageFormat::RGBA8888, size, size);
renderPage(pageid, canvas, bitmapDst, glyphs);
return Texture::from(&canvas);
}
private: private:
FT_Face face; FT_Face face;
util::Buffer<ubyte> buffer; util::Buffer<ubyte> buffer;
@ -95,19 +118,19 @@ namespace {
int pageid, int pageid,
ImageData& canvas, ImageData& canvas,
ImageData& bitmapDst, ImageData& bitmapDst,
std::vector<Font::Glyph>& glyphs std::vector<Glyph>& glyphs
) { ) {
int size = canvas.getWidth() / 16; int size = canvas.getWidth() / 16;
for (int c = 0; c < 256; c++) { for (int c = 0; c < 256; c++) {
int codepoint = pageid << 8 | c; int codepoint = pageid << 8 | c;
if (!renderGlyph(codepoint, bitmapDst)) { if (!renderGlyph(codepoint, bitmapDst)) {
glyphs.push_back(Font::Glyph {0, size / 2}); glyphs.push_back(Glyph {0, size / 2});
continue; continue;
} }
canvas.blit(bitmapDst, (c % 16) * size, (c / 16) * size); canvas.blit(bitmapDst, (c % 16) * size, (c / 16) * size);
Font::Glyph glyph { Glyph glyph {
face->glyph->bitmap_top - size, face->glyph->bitmap_top - size,
static_cast<int>(face->glyph->advance.x >> 6) static_cast<int>(face->glyph->advance.x >> 6)
}; };
@ -133,11 +156,14 @@ void vector_fonts::initialize() {
void vector_fonts::finalize() { void vector_fonts::finalize() {
if (FT_Error error = FT_Done_FreeType(library)) { if (FT_Error error = FT_Done_FreeType(library)) {
logger.error() << "error on FT_Done_FreeType: " << get_ft_error_message(error); logger.error() << "error on FT_Done_FreeType: "
<< get_ft_error_message(error);
} }
} }
std::unique_ptr<vector_fonts::FontFile> vector_fonts::load_font(const std::string& filename) { std::shared_ptr<vector_fonts::FontFile> vector_fonts::load_font(
const std::string& filename
) {
auto bytes = io::read_bytes_buffer(io::path(filename)); auto bytes = io::read_bytes_buffer(io::path(filename));
FT_Face face; FT_Face face;
@ -150,5 +176,5 @@ std::unique_ptr<vector_fonts::FontFile> vector_fonts::load_font(const std::strin
); );
} }
return std::make_unique<FTFontFile>(std::move(face), std::move(bytes)); return std::make_shared<FTFontFile>(std::move(face), std::move(bytes));
} }

View file

@ -2,8 +2,12 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include <vector>
class Font; class Font;
struct Glyph;
class Texture;
namespace vector_fonts { namespace vector_fonts {
class FontFile { class FontFile {
@ -11,10 +15,14 @@ namespace vector_fonts {
virtual ~FontFile() {} virtual ~FontFile() {}
virtual std::unique_ptr<Font> createInstance(int size) = 0; virtual std::unique_ptr<Font> createInstance(int size) = 0;
virtual std::unique_ptr<Texture> renderPage(
int pageid, std::vector<Glyph>& glyphs, int size
) = 0;
}; };
void initialize(); void initialize();
void finalize(); void finalize();
std::unique_ptr<vector_fonts::FontFile> load_font(const std::string& filename); std::shared_ptr<vector_fonts::FontFile> load_font(const std::string& filename);
} }

View file

@ -65,7 +65,7 @@ inline constexpr int ATLAS_EXTRUSION = 2;
inline constexpr int DEFAULT_FONT_SIZE = 16; inline constexpr int DEFAULT_FONT_SIZE = 16;
inline constexpr int FONT_PAGES = 5; inline constexpr int DEFAULT_PRE_RENDER_FONT_PAGES = 1; // must be at least 1
inline const std::string SHADERS_FOLDER = "shaders"; inline const std::string SHADERS_FOLDER = "shaders";
inline const std::string TEXTURES_FOLDER = "textures"; inline const std::string TEXTURES_FOLDER = "textures";

View file

@ -1,11 +1,13 @@
#include "Font.hpp" #include "Font.hpp"
#include "Batch2D.hpp"
#include "Batch3D.hpp"
#include "coders/vector_fonts.hpp"
#include "Texture.hpp"
#include "window/Camera.hpp"
#include <limits> #include <limits>
#include <utility> #include <utility>
#include "Texture.hpp"
#include "Batch2D.hpp"
#include "Batch3D.hpp"
#include "window/Camera.hpp"
inline constexpr uint GLYPH_SIZE = 16; inline constexpr uint GLYPH_SIZE = 16;
inline constexpr uint MAX_CODEPAGES = 10000; // idk ho many codepages unicode has inline constexpr uint MAX_CODEPAGES = 10000; // idk ho many codepages unicode has
@ -15,13 +17,15 @@ Font::Font(
std::vector<std::unique_ptr<Texture>> pages, std::vector<std::unique_ptr<Texture>> pages,
std::vector<Glyph> glyphs, std::vector<Glyph> glyphs,
int lineHeight, int lineHeight,
int yoffset int yoffset,
std::optional<std::weak_ptr<vector_fonts::FontFile>> fontFile
) )
: lineHeight(lineHeight), : lineHeight(lineHeight),
yoffset(yoffset), yoffset(yoffset),
glyphInterval(lineHeight / 2), glyphInterval(lineHeight / 2),
pages(std::move(pages)), pages(std::move(pages)),
glyphs(std::move(glyphs)) { glyphs(std::move(glyphs)),
fontFile(std::move(fontFile)) {
} }
Font::~Font() = default; Font::~Font() = default;
@ -139,7 +143,7 @@ static inline void draw_glyph(
template <class Batch> template <class Batch>
static inline void draw_text( static inline void draw_text(
const Font& font, Font& font,
Batch& batch, Batch& batch,
std::wstring_view text, std::wstring_view text,
const glm::vec3& pos, const glm::vec3& pos,
@ -244,7 +248,7 @@ void Font::draw(
const FontStylesScheme* styles, const FontStylesScheme* styles,
size_t styleMapOffset, size_t styleMapOffset,
float scale float scale
) const { ) {
draw_text( draw_text(
*this, batch, text, *this, batch, text,
glm::vec3(x, y, 0), glm::vec3(x, y, 0),
@ -264,7 +268,7 @@ void Font::draw(
const glm::vec3& pos, const glm::vec3& pos,
const glm::vec3& right, const glm::vec3& right,
const glm::vec3& up const glm::vec3& up
) const { ) {
draw_text( draw_text(
*this, batch, text, pos, *this, batch, text, pos,
right * static_cast<float>(glyphInterval), right * static_cast<float>(glyphInterval),
@ -296,3 +300,28 @@ std::unique_ptr<Font> Font::createBitmapFont(
} }
return std::make_unique<Font>(std::move(textures), std::move(glyphs), res, 4); return std::make_unique<Font>(std::move(textures), std::move(glyphs), res, 4);
} }
const Glyph* Font::getGlyph(int codepoint) {
if (codepoint < 0) {
return nullptr;
}
if (codepoint < glyphs.size()) {
return &glyphs.at(codepoint);
}
if (!this->fontFile.has_value() || this->fontFile->expired()) {
return nullptr;
}
int codepage = codepoint >> 8;
if (codepage >= 1024) {
return nullptr;
}
if (glyphs.size() < (codepage << 8)) {
glyphs.resize(codepage << 8);
}
auto fontFile = this->fontFile->lock();
if (pages.size() <= codepage) {
pages.resize(codepage);
}
pages.push_back(fontFile->renderPage(codepage, glyphs, lineHeight));
return &glyphs.at(codepoint);
}

View file

@ -3,6 +3,7 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include <vector> #include <vector>
#include <optional>
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include "typedefs.hpp" #include "typedefs.hpp"
@ -18,18 +19,23 @@ class ImageData;
class Font; class Font;
namespace vector_fonts {
class FontFile;
}
struct Glyph {
int yOffset;
int xAdvance;
};
class Font { class Font {
public: public:
struct Glyph {
int yOffset;
int xAdvance;
};
Font( Font(
std::vector<std::unique_ptr<Texture>> pages, std::vector<std::unique_ptr<Texture>> pages,
std::vector<Glyph> glyphs, std::vector<Glyph> glyphs,
int lineHeight, int lineHeight,
int yoffset int yoffset,
std::optional<std::weak_ptr<vector_fonts::FontFile>> fontFile = std::nullopt
); );
~Font(); ~Font();
@ -61,7 +67,7 @@ public:
const FontStylesScheme* styles, const FontStylesScheme* styles,
size_t styleMapOffset, size_t styleMapOffset,
float scale = 1 float scale = 1
) const; );
void draw( void draw(
Batch3D& batch, Batch3D& batch,
@ -71,7 +77,7 @@ public:
const glm::vec3& pos, const glm::vec3& pos,
const glm::vec3& right={1, 0, 0}, const glm::vec3& right={1, 0, 0},
const glm::vec3& up={0, 1, 0} const glm::vec3& up={0, 1, 0}
) const; );
const Texture* getPage(int page) const; const Texture* getPage(int page) const;
@ -79,11 +85,7 @@ public:
return {std::nullopt, lineHeight, yoffset, glyphInterval}; return {std::nullopt, lineHeight, yoffset, glyphInterval};
} }
const Glyph* getGlyph(int codepoint) const { const Glyph* getGlyph(int codepoint);
return (codepoint < 0 || codepoint >= glyphs.size())
? nullptr
: &glyphs.at(codepoint);
}
static std::unique_ptr<Font> createBitmapFont( static std::unique_ptr<Font> createBitmapFont(
std::vector<std::unique_ptr<ImageData>> pages std::vector<std::unique_ptr<ImageData>> pages
@ -94,4 +96,5 @@ private:
int glyphInterval; int glyphInterval;
std::vector<std::unique_ptr<Texture>> pages; std::vector<std::unique_ptr<Texture>> pages;
std::vector<Glyph> glyphs; std::vector<Glyph> glyphs;
std::optional<std::weak_ptr<vector_fonts::FontFile>> fontFile;
}; };

View file

@ -45,7 +45,7 @@ void TextsRenderer::renderNote(
} }
opacity = preset.xrayOpacity; opacity = preset.xrayOpacity;
} }
const auto& font = assets.require<Font>(FONT_DEFAULT); auto& font = assets.require<Font>(FONT_DEFAULT);
glm::vec3 xvec = note.getAxisX(); glm::vec3 xvec = note.getAxisX();
glm::vec3 yvec = note.getAxisY(); glm::vec3 yvec = note.getAxisY();

View file

@ -276,13 +276,13 @@ void SlotView::draw(const DrawContext& pctx, const Assets& assets) {
drawItemIcon(batch, stack, item, assets, tint, pos); drawItemIcon(batch, stack, item, assets, tint, pos);
if (stack.getCount() > 1 || stack.getFields() != nullptr) { if (stack.getCount() > 1 || stack.getFields() != nullptr) {
const auto& font = assets.require<Font>(FONT_DEFAULT); auto& font = assets.require<Font>(FONT_DEFAULT);
drawItemInfo(batch, stack, item, font, pos); drawItemInfo(batch, stack, item, font, pos);
} }
} }
static void draw_shaded_text( static void draw_shaded_text(
Batch2D& batch, const Font& font, const std::wstring& text, int x, int y Batch2D& batch, Font& font, const std::wstring& text, int x, int y
) { ) {
batch.setColor({0, 0, 0, 1.0f}); batch.setColor({0, 0, 0, 1.0f});
font.draw(batch, text, x + 1, y + 1, nullptr, 0); font.draw(batch, text, x + 1, y + 1, nullptr, 0);
@ -294,7 +294,7 @@ void SlotView::drawItemInfo(
Batch2D& batch, Batch2D& batch,
const ItemStack& stack, const ItemStack& stack,
const ItemDef& item, const ItemDef& item,
const Font& font, Font& font,
const glm::vec2& pos const glm::vec2& pos
) { ) {
const int SLOT_SIZE = InventoryView::SLOT_SIZE; const int SLOT_SIZE = InventoryView::SLOT_SIZE;

View file

@ -81,7 +81,7 @@ namespace gui {
Batch2D& batch, Batch2D& batch,
const ItemStack& stack, const ItemStack& stack,
const ItemDef& item, const ItemDef& item,
const Font& font, Font& font,
const glm::vec2& pos const glm::vec2& pos
); );