2 Star 1 Fork 1

ssda/LVGLBuilder

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
LVGLFontData.cpp 11.83 KB
一键复制 编辑 原始数据 按行查看 历史
#include "LVGLFontData.h"
#include <QDebug>
#include <QFile>
#include <QtMath>
#include "LVGLCore.h"
#include <ft2build.h>
#include FT_FREETYPE_H
LVGLFontData::LVGLFontData(const QString &name, const QString &codeName, uint8_t size, const lv_font_t *font)
: m_font(const_cast<lv_font_t*>(font))
, m_name(name)
, m_codeName(codeName)
, m_size(size)
, m_customFont(false)
{
}
LVGLFontData::~LVGLFontData()
{
if (m_customFont) {
lv_font_fmt_txt_dsc_t *fdsc = reinterpret_cast<lv_font_fmt_txt_dsc_t *>(m_font->dsc);
delete [] fdsc->glyph_bitmap;
delete [] fdsc->glyph_dsc;
delete [] fdsc->cmaps;
delete fdsc;
delete m_font;
m_font = nullptr;
}
}
LVGLFontData *LVGLFontData::parse(const QString &fileName, uint8_t size, uint8_t bpp, uint32_t unicodeFirst, uint32_t unicodeLast)
{
FT_Face face;
int error = FT_New_Face(lvgl.m_ft, qPrintable(fileName), 0, &face);
if (error == FT_Err_Unknown_File_Format) {
qCritical() << "LVGLFontData: Unkown file format";
return nullptr;
} else if (error) {
qCritical() << "LVGLFontData: Error loading font:" << FT_Error_String(error);
return nullptr;
}
//QString unicode_start_str = "U+" + QString::number(unicodeFirst, 16).rightJustified(4, '0');
//QString unicode_last_str = "U+" + QString::number(unicodeLast, 16).rightJustified(4, '0');
FT_Set_Char_Size(face, 0, size * 64, 300, 300);
FT_Set_Pixel_Sizes(face, 0, size);
const int mask = (~(((1 << bpp) - 1) & 0xff)) & 0xff;
std::vector<uint8_t> gylph_bitmap;
const uint32_t glyphs = unicodeLast - unicodeFirst + 1;
lv_font_fmt_txt_glyph_dsc_t *glyph_dsc = new lv_font_fmt_txt_glyph_dsc_t[glyphs + 1];
memset(glyph_dsc, 0, sizeof(lv_font_fmt_txt_glyph_dsc_t));
int ascent = 0;
int descent = 0;
uint32_t counter = 0;
for(uint32_t unicodeAct = unicodeFirst; unicodeAct <= unicodeLast; ++unicodeAct) {
int error = FT_Load_Char(face, unicodeAct, FT_LOAD_RENDER);
if (error) {
qCritical() << "Error" << error << FT_Error_String(error);
continue;
}
const size_t curr_buf_size = gylph_bitmap.size();
uint8_t w = 0, h = 0;
if (face->glyph->bitmap.buffer != nullptr) {
FT_Bitmap bitmap = face->glyph->bitmap;
w = bitmap.width & 0xff;
h = bitmap.rows & 0xff;
const uint size_bitmap = bitmap.rows * bitmap.width;
const size_t buf_size = (size_bitmap + 1) * bpp / 8;
gylph_bitmap.resize(curr_buf_size + buf_size);
//assert(size_bitmap % (8 / bpp) == 0);
uint8_t *buf_in = bitmap.buffer;
uint8_t *buf_out = gylph_bitmap.data() + curr_buf_size;
// convert to bpp format
for (uint j = 0; j < buf_size; ++j) {
*buf_out = 0;
for (int i = 0; i < 8; i += bpp) {
const bool ok = ((buf_in - bitmap.buffer) < size_bitmap);
const uint8_t val = (ok?*buf_in:0);
*buf_out |= ((val & mask) >> i);
if (ok)
++buf_in; // avoid pointing to not allocated memory
}
++buf_out; // avoid pointing to not allocated memory
}
}
lv_font_fmt_txt_glyph_dsc_t &desc = glyph_dsc[unicodeAct - unicodeFirst + 1];
desc.bitmap_index = curr_buf_size & 0xffff;
const double advance = face->glyph->metrics.horiAdvance / 64.0;
desc.adv_w = uint32_t(std::ceil(advance * 16));
desc.box_w = w;
desc.box_h = h;
desc.ofs_x = static_cast<int8_t>(face->glyph->bitmap_left);
desc.ofs_y = static_cast<int8_t>(face->glyph->bitmap_top) - h;
ascent = std::max(ascent, face->glyph->bitmap_top);
descent = std::min(descent, face->glyph->bitmap_top - h);
++counter;
//qDebug() << QString::number(unicodeAct, 16).rightJustified(4, '0') << QChar(unicodeAct);
}
Q_ASSERT(counter == glyphs);
uint8_t *gylph_bitmap_buf = new uint8_t[gylph_bitmap.size()];
memcpy(gylph_bitmap_buf, gylph_bitmap.data(), gylph_bitmap.size());
// allocate cmap
lv_font_fmt_txt_cmap_t *cmap = new lv_font_fmt_txt_cmap_t[1];
cmap[0].range_start = unicodeFirst;
cmap[0].range_length = static_cast<uint16_t>(glyphs & 0xffff);
cmap[0].glyph_id_start = 1;
cmap[0].unicode_list = nullptr;
cmap[0].glyph_id_ofs_list = nullptr;
cmap[0].list_length = 0;
cmap[0].type = LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY;
// allocate txt description
lv_font_fmt_txt_dsc_t *font_dsc = new lv_font_fmt_txt_dsc_t;
font_dsc->glyph_bitmap = gylph_bitmap_buf;
font_dsc->glyph_dsc = glyph_dsc;
font_dsc->cmaps = cmap;
font_dsc->kern_dsc = nullptr;
font_dsc->kern_scale = 0;
font_dsc->cmap_num = 1;
font_dsc->bpp = bpp;
font_dsc->kern_classes = 0;
font_dsc->bitmap_format = LV_FONT_FMT_TXT_PLAIN;
// allocate font in with default callbacks
lv_font_t *font = new lv_font_t;
font->get_glyph_dsc = lv_font_get_glyph_dsc_fmt_txt;
font->get_glyph_bitmap = lv_font_get_bitmap_fmt_txt;
//font->line_height = uint8_t(std::round((face->ascender - face->descender) / 64.0));
//font->base_line = uint8_t(-std::round(face->descender / 64.0));
font->line_height = uint8_t(ascent - descent);
font->base_line = uint8_t(-descent);
font->subpx = LV_FONT_SUBPX_NONE;
font->dsc = font_dsc;
// generate font name
QString baseName(face->family_name);
if (strcmp(face->style_name, "Regular") != 0)
baseName += QString(" %1").arg(face->style_name);
QString name = QString("%1 %2").arg(baseName).arg(size);
LVGLFontData *ret = new LVGLFontData(name, name.toLower().replace(" ", "_").replace("-", "_"), size, font);
ret->m_customFont = true;
ret->m_fileName = fileName;
return ret;
}
LVGLFontData *LVGLFontData::parse(QJsonObject object)
{
if (!object.contains("fileName") || !object.contains("bpp") ||
!object.contains("start") || !object.contains("end"))
return nullptr;
const uint8_t size = static_cast<uint8_t>(object["size"].toInt());
const uint8_t bpp = static_cast<uint8_t>(object["bpp"].toInt());
const uint32_t start = static_cast<uint32_t>(object["start"].toInt());
const uint32_t end = static_cast<uint32_t>(object["end"].toInt());
return parse(object["fileName"].toString(), size, bpp, start, end);
}
const lv_font_t *LVGLFontData::font() const
{
return m_font;
}
QString LVGLFontData::name() const
{
return m_name;
}
QString LVGLFontData::codeName() const
{
return m_codeName;
}
bool LVGLFontData::saveAsCode(const QString &fileName) const
{
QFile file(fileName);
if (!file.open(QIODevice::WriteOnly))
return false;
lv_font_fmt_txt_dsc_t *fdsc = reinterpret_cast<lv_font_fmt_txt_dsc_t *>(m_font->dsc);
if (fdsc->cmap_num != 1) {
qCritical() << "LVGLFontData: Char map count <==> 1!";
return false;
}
const uint32_t unicodeFirst = fdsc->cmaps[0].range_start;
const uint32_t unicodeSec = unicodeFirst + fdsc->cmaps[0].range_length - 1;
QTextStream stream(&file);
stream << "#include \"lvgl.h\"\n\n";
const QString defName = m_codeName.toUpper();
const QString output_name = m_codeName;
stream << "/*******************************************************************************\n";
stream << " * Size: " << m_size << " px\n";
stream << " * Bpp: " << fdsc->bpp << "\n";
stream << " * Opts: \n";
stream << " ******************************************************************************/\n\n";
stream << "#ifndef " << defName << "\n";
stream << "#define " << defName << " 1\n";
stream << "#endif\n\n";
stream << "#if " << defName << "\n\n";
stream << "/*-----------------\n";
stream << " * BITMAPS\n";
stream << " *----------------*/\n\n";
stream << "/* Store the image of the letters (glyph) */\n";
stream << "static const uint8_t " << output_name << "_glyph_bitmap[] =\n{\n";
for (uint32_t unicodeAct = unicodeFirst; unicodeAct <= unicodeSec; ++unicodeAct) {
stream << "\t/* U+" << QString::number(unicodeAct, 16).rightJustified(4, '0') << " \"" << QChar(unicodeAct) << "\" */";
const lv_font_fmt_txt_glyph_dsc_t &gdsc = fdsc->glyph_dsc[unicodeAct - unicodeFirst + 1];
const uint32_t size = (gdsc.box_w * gdsc.box_h + 1) * fdsc->bpp / 8;
const uint8_t *buf = fdsc->glyph_bitmap + gdsc.bitmap_index;
for (uint32_t i = 0; i < size; ++i, ++buf) {
if (i % 8 == 0) stream << "\n\t";
stream << "0x" << QString::number(*buf, 16).rightJustified(2, '0') << ", ";
}
stream << "\n";
if (unicodeAct != unicodeSec) stream << "\n";
}
stream << "};\n\n";
stream << "/*---------------------\n";
stream << " * GLYPH DESCRIPTION\n";
stream << " *--------------------*/\n\n";
stream << "static const lv_font_fmt_txt_glyph_dsc_t " << output_name << "_glyph_dsc[] =\n{\n";
stream << "\t{.bitmap_index = 0, .adv_w = 0, .box_w = 0, .box_h = 0, .ofs_x = 0, .ofs_y = 0} /* id = 0 reserved */,\n";
const lv_font_fmt_txt_glyph_dsc_t *gdsc = fdsc->glyph_dsc + 1;
for (uint32_t unicodeAct = unicodeFirst; unicodeAct <= unicodeSec; ++unicodeAct) {
stream << "\t{.bitmap_index = " << gdsc->bitmap_index << ", .adv_w = "
<< gdsc->adv_w << ", .box_w = " << gdsc->box_w << ", .box_h = "
<< gdsc->box_h << ", .ofs_x = " << gdsc->ofs_x << ", .ofs_y = "
<< gdsc->ofs_y << "}";
stream << " /* U+" << QString::number(unicodeAct, 16).rightJustified(4, '0') << " \"" << QChar(unicodeAct) << "\" */";
if (unicodeAct != unicodeSec) stream << ",";
stream << "\n";
++gdsc;
}
stream << "};\n\n";
stream << "/*---------------------\n";
stream << " * CHARACTER MAPPING\n";
stream << " *--------------------*/\n\n";
stream << "/* Collect the unicode lists and glyph_id offsets */\n";
stream << "static const lv_font_fmt_txt_cmap_t " << output_name << "_cmaps[] =\n{\n";
stream << "\t{\n";
stream << "\t\t.range_start = " << fdsc->cmaps[0].range_start
<< ", .range_length = " << fdsc->cmaps[0].range_length
<< ", .glyph_id_start = " << fdsc->cmaps[0].glyph_id_start << ",\n";
stream << "\t\t.unicode_list = NULL, .glyph_id_ofs_list = NULL, .list_length = 0, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY\n";
stream << "\t}\n";
stream << "};\n\n";
stream << "/*--------------------\n";
stream << " * ALL CUSTOM DATA\n";
stream << " *--------------------*/\n\n";
stream << "/* Store all the custom data of the font */\n";
stream << "static lv_font_fmt_txt_dsc_t " << output_name << "_font_dsc = \n{\n";
stream << "\t.glyph_bitmap = " << output_name << "_glyph_bitmap,\n";
stream << "\t.glyph_dsc = " << output_name << "_glyph_dsc,\n";
stream << "\t.cmaps = " << output_name << "_cmaps,\n";
stream << "\t.kern_dsc = NULL,\n\t.kern_scale = 0,\n";
stream << "\t.cmap_num = " << fdsc->cmap_num << ",\n";
stream << "\t.bpp = " << fdsc->bpp << ",\n";
stream << "\t.kern_classes = 0,\n";
stream << "\t.bitmap_format = LV_FONT_FMT_TXT_PLAIN\n};\n\n";
stream << "/*-----------------\n";
stream << " * PUBLIC FONT\n";
stream << " *----------------*/\n\n";
stream << "/* Initialize a public general font descriptor */\n";
stream << "lv_font_t " << output_name << " = \n{\n";
stream << "\t.get_glyph_dsc = lv_font_get_glyph_dsc_fmt_txt, /* Function pointer to get glyph's data */\n";
stream << "\t.get_glyph_bitmap = lv_font_get_bitmap_fmt_txt, /* Function pointer to get glyph's bitmap */\n";
stream << "\t.line_height = " << m_font->line_height << ", /* The maximum line height required by the font */\n";
stream << "\t.base_line = " << m_font->base_line << ", /* Baseline measured from the bottom of the line */\n";
stream << "\t.subpx = LV_FONT_SUBPX_NONE,\n";
stream << "\t.dsc = &" << output_name << "_font_dsc /* The custom font data. Will be accessed by `get_glyph_bitmap/dsc` */\n";
stream << "};\n\n";
stream << "#endif /*#if " << defName << "*/\n";
file.close();
return true;
}
bool LVGLFontData::isCustomFont() const
{
return m_customFont;
}
QString LVGLFontData::fileName() const
{
return m_fileName;
}
QJsonObject LVGLFontData::toJson() const
{
QJsonObject object({{"name", m_name},
{"code", m_codeName},
{"size", m_size}
});
if (m_customFont) {
object.insert("fileName", m_fileName);
lv_font_fmt_txt_dsc_t *fdsc = reinterpret_cast<lv_font_fmt_txt_dsc_t *>(m_font->dsc);
object.insert("bpp", fdsc->bpp);
object.insert("start", int(fdsc->cmaps[0].range_start));
object.insert("end", int(fdsc->cmaps[0].range_start + fdsc->cmaps[0].range_length - 1));
}
return object;
}
uint8_t LVGLFontData::size() const
{
return m_size;
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/shaoshunda/LVGLBuilder.git
git@gitee.com:shaoshunda/LVGLBuilder.git
shaoshunda
LVGLBuilder
LVGLBuilder
master

搜索帮助