使用 SFML 等图形库时如何嵌入字体和其他图像文件

Kar*_*ski 4 c++ fonts file embedded-resource sfml

你们中的许多人可能想知道 - 在使用像 SFML 这样需要从文件路径加载字体的库时,如何从驱动器中删除字体或任何其他单个文件。

那么,如何将该数据嵌入到可执行文件中,从而使生成的可执行文件不再依赖于这些资源文件呢?

Kar*_*ski 6

首先,我们必须获得我们的资源。我已经从谷歌字体下载了“BalooBhaijaan-Regular.ttf”字体。
然后,应该得到给定字体的二进制数据。在我看来,实现这一目标的最简单方法是使用带有 -i 参数的 linux“xxd”命令,该命令以 C 样式数组的形式输出。
让我们将输出重定向到一个文件,因为如果我们谈论真正的字体或更大的图像,它通常会很长:

xxd -i BalooBhaijaan-Regular.ttf > font_data.txt
Run Code Online (Sandbox Code Playgroud)

创建一个空的 C/C++ 标头或将字体数据放入已存在的文件中。我更喜欢使用新的头文件,因为输出会很长。
当然,在粘贴到您的 IDE 后,您可以将数组类型更改为 const,因为字体的内容通常不会改变。
这是它在我的 IDE 中的外观: 在此处输入图片说明 您当然可能想知道为什么这是一个 char 数组 - 仅仅是因为在 char 数组中每个“字段”代表一个字节。

您可能已经注意到,xxd 还为我们创建了另一个变量 - font_data.txt 中的最后一个变量是一个 unsigned int,它告诉我们数组的长度。我们稍后会需要这个。“长度通知”整数的名称与带有“_len”后缀的数组名称相同

现在,有两种方法可以进行:
1. 使用内置方法从内存中加载字体(一些库支持,SFML 支持)
2. 创建一个“假”文件并加载它
让我们谈谈这两种情况


1.
这个是相当简单,sfml 支持从内存加载文件,因为它的地址和大小,所以我们可以这样做:

#include "BalooBhaijaanFont.hpp"
#include <SFML/Graphics.hpp>

int main(int argc, char** argv) {
    sf::RenderWindow mainWindow(sf::VideoMode(200,100), L"TEST");
    sf::Font fromMem;
    fromMem.loadFromMemory(&BalooBhaijaan_Regular_ttf, BalooBhaijaan_Regular_ttf_len);
    sf::Text text("WORKS!", fromMem);
    while(mainWindow.isOpen()){
        mainWindow.draw(text);
        mainWindow.display();
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

如您所见,使用内置函数加载它真的很容易。



2.
现在是时候使用我真的不推荐的临时文件方法了——大多数库都支持从内存加载,如果你正在制作自己的库,无论如何你都会以内存加载功能结束。
虽然仍然可以创建一个文件只是为了将其读取到字体类然后将其删除,但我认为使用这种方法没有任何意义,除非您对文件夹中的其他文件感到非常恼火。
仅供参考:

#include "BalooBhaijaanFont.hpp"
#include <SFML/Graphics.hpp>

int main(int argc, char** argv) {
    sf::RenderWindow mainWindow(sf::VideoMode(200,100), L"TEST");
    sf::Font fromFile;
    {
        FILE * tempFile = fopen("tmpfont.ttf", "wb");
        fwrite( BalooBhaijaan_Regular_ttf, sizeof(char), BalooBhaijaan_Regular_ttf_len, tempFile );
        fclose(tempFile);
        fromFile.loadFromFile("tmpfont.ttf");
        std::remove("tmpfont.ttf");
    }
    sf::Text text("WORKS!", fromFile);
    while(mainWindow.isOpen()){
        mainWindow.draw(text);
        mainWindow.display();
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)