17网站一起做网店app,全国网站开发公司,装修公司名字 有创意,房产网址Raylib 绘制自定义字体是真的难搞。我的需求是程序可以加载多种自定义字体#xff0c;英文中文的都有。
我调试了很久成功了#xff01;
很有用的参考#xff0c;建议先看一遍#xff1a;
瞿华#xff1a;raylib绘制中文内容 个人笔记#xff5c;Raylib 的字体使用 - …Raylib 绘制自定义字体是真的难搞。我的需求是程序可以加载多种自定义字体英文中文的都有。
我调试了很久成功了
很有用的参考建议先看一遍
瞿华raylib绘制中文内容 个人笔记Raylib 的字体使用 - bilibili
再放一下第一篇文章的可用示例代码
#include raylib.hint main() {InitWindow(800,600,世界你好);Image imgGenImageColor(800,600,WHITE);//读取字体文件unsigned int fileSize;unsigned char *fontFileData LoadFileData(c:\\windows\\fonts\\simhei.ttf, fileSize);//ImageDrawCircleEx(img, 400,300,200,10,BLACK);SetTraceLogLevel(LOG_WARNING);SetTargetFPS(120);while (!WindowShouldClose()) {//将要输出的内容放到字符串中必须是utf8编码char text[]世界你好;// 将字符串中的字符逐一转换成Unicode码点得到码点表int codepointsCount;int *codepointsLoadCodepoints(text,codepointsCount);// 读取仅码点表中各字符的字体Font font LoadFontFromMemory(.ttf,fontFileData,fileSize,32,codepoints,codepointsCount);// 释放码点表UnloadCodepoints(codepoints);BeginDrawing();ClearBackground(WHITE);DrawTextEx(font,text,(Vector2){50,50},32,5,RED);EndDrawing();//释放字体UnloadFont(font);}UnloadImage(img);//释放字体文件内容UnloadFileData(fontFileData);return 0;
}是的图片img好像没有用
关键步骤概括为
LoadFileData 读取字体文件while 主循环准备好要输出的文本LoadCodepoints 用准备的文本加载码点LoadFontFromMemory 得到含需要输出的文本的字符的字体UnloadCodepoints 卸载码点BeginDrawing 开始绘制 使用刚刚的字体绘制文本EndDrawing 结束绘制UnloadFont循环结束UnloadFileData 卸载字体文件
注意每一轮循环都用指定文本的码点加载了新的字体绘制好后才卸载该字体。 我试图将这一系列操作封装成函数DrawTextPlus发现UnloadFont必须要在EndDrawing后面执行不然会输出失败。
下面这张图更离谱了大错特错
但是如果在一帧内调用多次BeginDrawing和EndDrawing还是会出事。。出事代码如下
还是错误的代码别复制
void DrawTextPlus(const string s, int x, int y, int fs 32)
{BeginDrawing();// 将字符串中的字符逐一转换成Unicode码点得到码点表int codepointsCount;int *codepointsLoadCodepoints(s.c_str(),codepointsCount);// 读取仅码点表中各字符的字体Font font LoadFontFromMemory(.ttf, fontFileData, fileSize, 32, codepoints, codepointsCount);// 释放码点表UnloadCodepoints(codepoints);DrawTextEx(font,s.c_str(),(Vector2){x,y},fs,0,RED);EndDrawing();//释放字体UnloadFont(font);
}出现了闪烁现象
所以一帧还只能调用一次BeginDrawing,EndDrawing。那只能采取其他措施了。 如果真的不封装需要在同一帧输出不同文本的话以下代码可以正常运行
#include raylib.h
#include stringint main() {InitWindow(800,600,世界你好);//读取字体文件unsigned int fileSize;unsigned char *fontFileData LoadFileData(c:\\windows\\fonts\\simhei.ttf, fileSize);SetTraceLogLevel(LOG_WARNING);SetTargetFPS(120);//将要输出的内容放到字符串中必须是utf8编码ssize_t scnt 4;const std::string strings[] {DarkVoxel, Battle of Phantom, Poemaze, TerraSurvivor};while (!WindowShouldClose()) {std::string total_texts{};for (size_t i {0}; i scnt; i)total_texts strings[i];// 将字符串中的字符逐一转换成Unicode码点得到码点表int codepointsCount;int *codepointsLoadCodepoints(total_texts.c_str(),codepointsCount);// 读取仅码点表中各字符的字体Font font LoadFontFromMemory(.ttf,fontFileData,fileSize,32,codepoints,codepointsCount);// 释放码点表UnloadCodepoints(codepoints);BeginDrawing();ClearBackground(WHITE);//可以按需要输出了只要total_texts中有该字符就可以正常输出for (size_t i {0}; i scnt; i)DrawTextEx(font,strings[i].c_str(),Vector2{50.0f, 50.0f * i}, 32.0f, 5.0f, RED);EndDrawing();//释放字体UnloadFont(font);}//释放字体文件内容UnloadFileData(fontFileData);return 0;
}可以发现有好几个地方值得注意以及一点想法
1.字体整个文件的读取还是在循环前也就是在程序的载入阶段可以一口气把所有的字体文件读完放进一个容器中 2.需要输出的文本得提前准备好如果真的在项目中这样未免太难受了 3.在准备码点的时候可以把需要输出的文本合并在一起当然可以进行一个字符去重以提高效率 4.绘制文本的时候只要字符在合并好的文本之中就可以正常输出 5.每帧都进行了加载和卸载字体的操作还是变慢了 6.最后程序退出前卸载时要释放所有的字体文件内容。释放容器
小项目就上面这样的写法应该可以接受。但是中大项目就不一样了动不动就要输出一大堆文本不可能搞一堆string存在那里看的都烦而且每帧都要重新准备字体效率低下。
经过进一步思考我形成了另一种思路。我在上面的代码中添加了一些【伪代码】
#include raylib.h
#include string
#include map
#include vector【
容器存储所有词汇std::string LSTR(const std::string输出内容ID)
{//在我的项目中支持多语言我弄一个CSV专门存储每种语言的词汇//那么这个输出内容ID就可以是中文方便我阅读代码。返回真实的输出内容
}
】int main()
{InitWindow(800,600,世界你不好);//读取读取你的CSV文件并存储到一个容器中以供上面的LSTR函数使用mapstd::string, pair std::string, unsigned char* 所有需要用到的字体名称以及路径、数据;{{..., {..., nullptr}},{..., {..., nullptr}},};】【for (const auto fdt : ...){unsigned int fileSize;unsigned char *fontFileData LoadFileData(字体文件路径, fileSize);把fontFileData存进去string 整合串 去重后的把CSV文件所有内容拼接在一起的字符串;// 将字符串中的字符逐一转换成Unicode码点得到码点表int codepointsCount;int *codepointsLoadCodepoints(整合串.c_str(), codepointsCount);// 读取仅码点表中各字符的字体Font font LoadFontFromMemory(取字体路径扩展名, 字体文件内容fontFileData,fileSize, 200, codepoints, codepointsCount);把字体装进去// 释放码点表UnloadCodepoints(codepoints);}】SetTraceLogLevel(LOG_WARNING);SetTargetFPS(120);//将要输出的内容放到字符串中必须是utf8编码while (!WindowShouldClose()) {BeginDrawing();ClearBackground(WHITE);//可以按需要输出了只要total_texts中有该字符就可以正常输出
//CUR_FONT 是一个宏获取当前字体DrawTextEx(CUR_FONT,LSTR(CSV中),Vector2{50.0f, 50.0f}, 80.0f, 5.0f, BLACK);DrawTextEx(CUR_FONT,LSTR(包含的内容),Vector2{50.0f, 130.0f}, 80.0f, 5.0f, BLACK);DrawTextEx(CUR_FONT,LSTR(都可以写),Vector2{50.0f, 210.0f}, 80.0f, 5.0f, BLACK);EndDrawing();}【for (auto fdt : ...){UnloadFileData(字体文件内容指针);}】return 0;
}注意你需要准备一个文件例如CSV格式的每行存储一个你需要的字符串然后LSTR函数的参数就是你访问任意一个字符串的索引可以是数字【我觉得挺烦的还要查】可以是字符串【本身】。正如我注释中写的我的程序支持多语言因此可以每行一个中文逗号一个英文然后用中文索引特别方便。
这样的结构虽然很难搞但是大大简化了中间绘制文本的代码只需要加个LSTR这样的函数即可无需手动准备一堆string来搞临时的字体再输出。 如果你不想撰写新的文件存储所要用的字符串还有几种偷懒的方法仅供参考 1写一个辅助的程序在要编译前运行它提取你的源文件中的字符串然后整合在一起再把字符串写进去然后编译雾。 2把所有字符例如汉字加载进去日常试试可以实际运用肯定不现实内存都要爆了 上面的伪代码可能看的不是很明白我也不可能全部帮你补全只能提供一些我跑成功的项目的代码或是截图希望对你有帮助
语言、词汇处理
enum LangID
{Chinese 0,English 1,
};
#define LANG_CNT 2//下标宏
#define LID_LANG 0 //各语言名称
#define LID_GAME_NAME 1vectorvectorstring lang_words;bool ReadLanguage();constexpr const char* PunctAndNumberString(void)
{return 0123456789,.?/()~[]{}\\|\\:;!#$%^*-_ ;
}
constexpr const char* PunctAndNumberStringIncludingChinese(void)
{return 0123456789,.?/()~[]{}\\|\\:;!#$%^*-_。、【】“”‘’《》·…—;
}
string ObtainNormalEnglish(const string s)
{string res;bool wordbeg{ true };for (char ch : s){if (wordbeg isalpha(ch)){res islower(ch) ? toupper(ch) : ch;wordbeg false;}else if (isalpha(ch)){res ch;}else if (ch _ || ch ){res ;wordbeg true;}}return res;
}
string AssembleTotalChineseString(void);
string AssembleTotalEnglishString(void)
{string res;for (char ch A; ch Z; ch)res str(ch);for (char ch a; ch z; ch)res str(ch);res PunctAndNumberString();return res;
}string UniqueChinese(const string s) {string result;unordered_setint chineseChars;for (size_t i 0; i s.length(); i) {// 检查当前字符是否是中文字符if ((s[i] 0xE0) 0xE0) {int codePoint ((s[i] 0x0F) 12) | ((s[i 1] 0x3F) 6) | (s[i 2] 0x3F);// 如果当前中文字符不在哈希集合中则将其添加到结果字符串和哈希集合中if (chineseChars.find(codePoint) chineseChars.end()) {result s.substr(i, 3);chineseChars.insert(codePoint);}// 由于中文字符占用3个字节因此增加索引i的值i 2;}else {result s[i];}}return result;
}bool ReadLanguage()
{string path g.data_dir Language.csv;if (!ExistFile(path)){ErrorLogTip(nullptr, Cannot Find the Language File :(\n path, ReadLanguage);return false;}DebugLog(读取语言...);vectorstring lines ReadFileLines(path);int i{ 1 };while (i lines.size()){string line lines.at(i);if (line.empty()){i;continue;}line strrpc(line, , $);line strrpc(line, ,, );stringstream ss;string tmp;ss line;vectorstring langs;for (int i 0; i LANG_CNT; i){ss tmp;tmp strrpc(tmp, $, );tmp strrpc(tmp, ^, ,);langs.push_back(tmp);}// DebugLog(str(langs));lang_words.push_back(langs);i;}for (const auto idt : itemdata)lang_words.push_back(vector{ idt.cn_name, ObtainNormalEnglish(idt.en_name) });for (const auto edt : entitydata)lang_words.push_back(vector{ edt.cn_name, ObtainNormalEnglish(edt.en_name) });for (const auto bdt : buffdata)lang_words.push_back(vector{ bdt.cn_name, ObtainNormalEnglish(bdt.en_name) });for (const auto pdt : placeabledata)lang_words.push_back(vector{ pdt.cn_name, ObtainNormalEnglish(pdt.en_name) });for (const auto rdt : random_tips)lang_words.push_back(rdt.versions);DebugLog(共计, lang_words.size(), 个词汇支持, LANG_CNT, 门语言);return true;
}
string AssembleTotalChineseString(void)
{string res;//英文也要for (char ch A; ch Z; ch)res str(ch);for (char ch a; ch z; ch)res str(ch);//然后是中文for (const auto pr : lang_words)res pr.at(Chinese);res PunctAndNumberStringIncludingChinese();res UniqueChinese(res);return res;
}#define CHN_FONTNAME Sthginkra ItalicmapLangID, string lang_font_names
{{Chinese, CHN_FONTNAME},{English, Andy Bold},
};#define CUR_FONTNAME (g.lang_font_names[g.lang].c_str())
#define CENTER_TITLE_CHN_FONTNAME 钉钉进步体mapstring, pairstring, LangID used_fonts
{{Andy Bold, {ANDYB.TTF, English}},{CHN_FONTNAME, {ZhouFangRiMingTiXieTi-2.otf, Chinese}}, //我不是舟批{CENTER_TITLE_CHN_FONTNAME, {DingTalk JinBuTi.ttf, Chinese}},
};DebugLog(安装, used_fonts.size() - 1, 个字体...);unsigned char* pFileData{ nullptr };auto iter used_fonts.begin();
for (;iter ! used_fonts.end(); iter)
{if (iter-second.second English)continue;auto pr make_pair(iter-first,make_pair(ProduceMemoryFont(iter-second.first, iter-second.second, pFileData),pFileData)); //见下文cout iter-second.first iter-second.second pr.first pr.second.first.glyphCount \n;g.fonts.insert(pr);
}DebugLog(加载 str(g.fonts.size()) 个字体完毕);Font ProduceMemoryFont(const string filename, LangID lid, unsigned char** pFileData)
{string s;switch (lid){case Chinese:s AssembleTotalChineseString();break;case English:s AssembleTotalEnglishString();break;default:return GetFontDefault();}Font font;unsigned int fileSize{ 0U };unsigned char* fontFileData LoadFileData((g.font_dir filename).c_str(), fileSize);*pFileData fontFileData;if (fontFileData nullptr){DebugLog(ERROR: fontFileData is empty);}int codepointsCount;cout LoadCodepoints...\n;cout s s \n;int* codepoints LoadCodepoints(s.c_str(), codepointsCount);if (!codepoints){cout ERROR: LoadCodePoints failed\n;}cout CodepointsCount codepointsCount \n;cout FileSize fileSize \n;string ext GetFileExtension(filename.c_str());cout Ext ext \n;// 读取仅码点表中各字符的字体cout LoadFontFromMemory...\n;font LoadFontFromMemory(ext.c_str(), fontFileData,fileSize, 200, codepoints, codepointsCount); //200挺合适的// 释放码点表cout UnloadCodepoints...\n;UnloadCodepoints(codepoints);return font;
}DebugLog(卸载, used_fonts.size(), 个字体...);
for (const auto fn : used_fonts)
{UnloadFont(g.fonts[fn.first].first);UnloadFileData(g.fonts[fn.first].second);
}控制台输出截图非中文字符去重我好像没做
怎么样有思路了吗 大概就是把要输出的字符串提前收集好然后装载字体一次就行后面就随心所欲输出就行了。
还有几点 1.装载字体时的字号选 200 是挺合适的值如果太低就马赛克了太高会出问题 2.CSV文件可能是这样的 文章转载自: http://www.morning.qdrhf.cn.gov.cn.qdrhf.cn http://www.morning.mbhdl.cn.gov.cn.mbhdl.cn http://www.morning.mhmdx.cn.gov.cn.mhmdx.cn http://www.morning.wmlby.cn.gov.cn.wmlby.cn http://www.morning.mqlsf.cn.gov.cn.mqlsf.cn http://www.morning.dwrbn.cn.gov.cn.dwrbn.cn http://www.morning.ktpzb.cn.gov.cn.ktpzb.cn http://www.morning.fxxmj.cn.gov.cn.fxxmj.cn http://www.morning.yrddl.cn.gov.cn.yrddl.cn http://www.morning.ftwlay.cn.gov.cn.ftwlay.cn http://www.morning.4r5w91.cn.gov.cn.4r5w91.cn http://www.morning.qnzgr.cn.gov.cn.qnzgr.cn http://www.morning.prkdl.cn.gov.cn.prkdl.cn http://www.morning.kqhlm.cn.gov.cn.kqhlm.cn http://www.morning.hbfqm.cn.gov.cn.hbfqm.cn http://www.morning.bylzr.cn.gov.cn.bylzr.cn http://www.morning.lbrwm.cn.gov.cn.lbrwm.cn http://www.morning.4q9h.cn.gov.cn.4q9h.cn http://www.morning.llxqj.cn.gov.cn.llxqj.cn http://www.morning.rcjqgy.com.gov.cn.rcjqgy.com http://www.morning.nsrlb.cn.gov.cn.nsrlb.cn http://www.morning.kgnnc.cn.gov.cn.kgnnc.cn http://www.morning.lqws.cn.gov.cn.lqws.cn http://www.morning.hrpjx.cn.gov.cn.hrpjx.cn http://www.morning.yjknk.cn.gov.cn.yjknk.cn http://www.morning.jcxqc.cn.gov.cn.jcxqc.cn http://www.morning.gbrdx.cn.gov.cn.gbrdx.cn http://www.morning.xhlpn.cn.gov.cn.xhlpn.cn http://www.morning.mwwnz.cn.gov.cn.mwwnz.cn http://www.morning.wtxdp.cn.gov.cn.wtxdp.cn http://www.morning.slfkt.cn.gov.cn.slfkt.cn http://www.morning.lczxm.cn.gov.cn.lczxm.cn http://www.morning.fbxlj.cn.gov.cn.fbxlj.cn http://www.morning.nkhdt.cn.gov.cn.nkhdt.cn http://www.morning.synkr.cn.gov.cn.synkr.cn http://www.morning.hmktd.cn.gov.cn.hmktd.cn http://www.morning.mqdr.cn.gov.cn.mqdr.cn http://www.morning.qqfcf.cn.gov.cn.qqfcf.cn http://www.morning.swimstaracademy.cn.gov.cn.swimstaracademy.cn http://www.morning.wjpsn.cn.gov.cn.wjpsn.cn http://www.morning.fcqlt.cn.gov.cn.fcqlt.cn http://www.morning.rfpq.cn.gov.cn.rfpq.cn http://www.morning.pzrpz.cn.gov.cn.pzrpz.cn http://www.morning.nrfrd.cn.gov.cn.nrfrd.cn http://www.morning.tdldh.cn.gov.cn.tdldh.cn http://www.morning.fmznd.cn.gov.cn.fmznd.cn http://www.morning.kzrbd.cn.gov.cn.kzrbd.cn http://www.morning.gyfhk.cn.gov.cn.gyfhk.cn http://www.morning.jbpdk.cn.gov.cn.jbpdk.cn http://www.morning.yrqb.cn.gov.cn.yrqb.cn http://www.morning.trtdg.cn.gov.cn.trtdg.cn http://www.morning.mfmrg.cn.gov.cn.mfmrg.cn http://www.morning.xflzm.cn.gov.cn.xflzm.cn http://www.morning.wmdqc.com.gov.cn.wmdqc.com http://www.morning.yggdq.cn.gov.cn.yggdq.cn http://www.morning.qbwtb.cn.gov.cn.qbwtb.cn http://www.morning.dwztj.cn.gov.cn.dwztj.cn http://www.morning.bdwqy.cn.gov.cn.bdwqy.cn http://www.morning.ndcf.cn.gov.cn.ndcf.cn http://www.morning.hnrqn.cn.gov.cn.hnrqn.cn http://www.morning.8yitong.com.gov.cn.8yitong.com http://www.morning.dmfdl.cn.gov.cn.dmfdl.cn http://www.morning.rhqr.cn.gov.cn.rhqr.cn http://www.morning.nwczt.cn.gov.cn.nwczt.cn http://www.morning.ldsgm.cn.gov.cn.ldsgm.cn http://www.morning.drrt.cn.gov.cn.drrt.cn http://www.morning.txysr.cn.gov.cn.txysr.cn http://www.morning.ktnt.cn.gov.cn.ktnt.cn http://www.morning.qnzk.cn.gov.cn.qnzk.cn http://www.morning.fwkq.cn.gov.cn.fwkq.cn http://www.morning.qwwhs.cn.gov.cn.qwwhs.cn http://www.morning.jcxzq.cn.gov.cn.jcxzq.cn http://www.morning.dnycx.cn.gov.cn.dnycx.cn http://www.morning.nshhf.cn.gov.cn.nshhf.cn http://www.morning.mghgl.cn.gov.cn.mghgl.cn http://www.morning.c7625.cn.gov.cn.c7625.cn http://www.morning.pymff.cn.gov.cn.pymff.cn http://www.morning.dmxzd.cn.gov.cn.dmxzd.cn http://www.morning.lbcfj.cn.gov.cn.lbcfj.cn http://www.morning.mcwrg.cn.gov.cn.mcwrg.cn