120 lines
3.7 KiB
C++
120 lines
3.7 KiB
C++
#include "display/ui/text/Fonts.h"
|
|
#include <ft2build.h>
|
|
#include FT_FREETYPE_H
|
|
#include <stdexcept>
|
|
#include <iostream>
|
|
#include <filesystem>
|
|
#include "helpers/Paths.h"
|
|
|
|
namespace display::ui::text
|
|
{
|
|
struct Fonts::Library
|
|
{
|
|
FT_Library library = nullptr;
|
|
};
|
|
struct Fonts::Face
|
|
{
|
|
FT_Face face = nullptr;
|
|
};
|
|
|
|
Fonts::Fonts()
|
|
: library(std::make_unique<Library>())
|
|
{
|
|
if (FT_Init_FreeType(&library->library))
|
|
throw std::runtime_error("FT_Init_FreeType failed");
|
|
|
|
loadAllFonts();
|
|
}
|
|
|
|
Fonts::~Fonts()
|
|
{
|
|
for (auto &[k, v] : fonts)
|
|
if (v->face)
|
|
FT_Done_Face(v->face);
|
|
if (library->library)
|
|
FT_Done_FreeType(library->library);
|
|
}
|
|
|
|
void Fonts::loadAllFonts()
|
|
{
|
|
loadFonts("/usr/share/fonts/truetype");
|
|
loadFonts("/usr/share/esdashboard/fonts");
|
|
// loadFonts(helpers::initPaths().exeDir);
|
|
}
|
|
|
|
void Fonts::loadFonts(const std::string &path)
|
|
{
|
|
if (std::filesystem::exists(path))
|
|
{
|
|
for (auto &p : std::filesystem::recursive_directory_iterator(path))
|
|
{
|
|
if (p.path().extension() == ".ttf" || p.path().extension() == ".otf")
|
|
{
|
|
std::string path = p.path().string();
|
|
std::string filename = p.path().stem().string();
|
|
|
|
// Можно сразу создавать Fonts для стандартных размеров, например: 10,12,14,16,18,24
|
|
for (int sz : {5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 24, 36, 72})
|
|
{
|
|
Font key{filename, sz};
|
|
|
|
std::unique_ptr<Face> face = std::make_unique<Face>();
|
|
if (FT_New_Face(library->library, path.c_str(), 0, &face->face))
|
|
throw std::runtime_error("FT_New_Face failed");
|
|
FT_Set_Pixel_Sizes(face->face, 0, sz);
|
|
|
|
fonts.emplace(key, std::move(face));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const Glyph &Fonts::getGlyph(char ch, const Font &font)
|
|
{
|
|
|
|
GlyphKey key{ch, font};
|
|
|
|
auto gph = glyphs.find(key);
|
|
if (gph != glyphs.end())
|
|
return gph->second;
|
|
|
|
auto fnt = fonts.find(font);
|
|
if (fnt == fonts.end())
|
|
{
|
|
// Если нет, можно использовать какой-нибудь дефолтный шрифт
|
|
fnt = fonts.begin();
|
|
}
|
|
|
|
FT_Face face = fnt->second->face;
|
|
|
|
if (!face)
|
|
throw std::runtime_error("Fonts not initialized!");
|
|
if (FT_Load_Char(face, ch, FT_LOAD_RENDER | FT_LOAD_TARGET_MONO))
|
|
throw std::runtime_error("FT_Load_Char failed");
|
|
FT_GlyphSlot g = face->glyph;
|
|
|
|
Glyph glyph;
|
|
glyph.width = g->bitmap.width;
|
|
glyph.height = g->bitmap.rows;
|
|
glyph.bearingX = g->bitmap_left;
|
|
glyph.bearingY = g->bitmap_top;
|
|
glyph.advance = g->advance.x >> 6;
|
|
// glyph.buffer.assign(g->bitmap.buffer, g->bitmap.buffer + g->bitmap.width * g->bitmap.rows);
|
|
|
|
glyph.buffer.resize(glyph.width * glyph.height, 0);
|
|
|
|
const FT_Bitmap &bm = g->bitmap;
|
|
for (int y = 0; y < bm.rows; ++y)
|
|
{
|
|
for (int x = 0; x < bm.width; ++x)
|
|
{
|
|
int byteIndex = y * bm.pitch + x / 8;
|
|
int bitIndex = 7 - (x % 8);
|
|
uint8_t bit = (bm.buffer[byteIndex] >> bitIndex) & 1;
|
|
glyph.buffer[y * bm.width + x] = bit ? 255 : 0;
|
|
}
|
|
}
|
|
return glyphs.emplace(key, std::move(glyph)).first->second;
|
|
}
|
|
} // namespace display::ui::text
|