Font* Font::LoadFromFile(std::string font_file_path,unsigned short font_size){
Font* font=GetFont(font_file_path);
if(font!= nullptr){
return font;
}
//读取 ttf 字体文件
//这段代码创建了一个名为 input_file_stream 的 ifstream 对象,并打开了一个二进制文件进行读取操作
//ios::in 是一个枚举值,它表示打开文件流时使用的文件访问模式。其中,ios 是 C++ 标准库中的一个命名空间,in 是 ios 命名空间中的一个常量,表示输入模式(input mode)。
ifstream input_file_stream(Application::data_path()+ font_file_path,ios::in | ios::binary);
//ifstream 是 C++ 标准库中的一个输入流类,用于从文件中读取数据。它是 fstream 类的派生类之一,表示文件输入流。 ifstream 对象可以打开一个文件,读取其中的数据,并将其存储在内存中以供后续操作使用。常见的操作包括从文件中读取数据、检查文件是否打开成功、检查文件是否结束等等。可以通过调用 open() 方法打开一个文件,close() 方法关闭文件,eof() 方法检查文件是否结束,以及 getline() 方法读取文件中的一行数据等方法进行文件的操作。
//ofstream 输出文件流,文件流(file stream)是指将文件视为输入或输出源的流。文件流是C++中标准库的一部分,它允许将文件作为输入或输出对象,并支持与其他流对象一样的操作,例如读取和写入数据、移动文件指针等
//C++中,流(stream)是指数据在程序中的流动方式,它是一种数据处理的抽象概念。流的基本思想是将输入和输出数据都看作是一个数据流,程序通过读写流中的数据来完成输入和输出的操作。
// 获取文件长度 seekg 函数用于移动输入文件流的读指针。
// input_file_stream 是一个 ifstream 类型的文件输入流对象,seekg() 成员函数用于将文件读取位置定位到文件末尾。
// 具体来说,第一个参数 0 表示从文件末尾的偏移量为 0(即指向文件末尾),
// 而第二个参数 std::ios::end 表示定位的起始位置为文件流的末尾。
input_file_stream.seekg(0,std::ios::end);
//tellg() 成员函数用于获取当前文件读取位置相对于文件起始位置的偏移量,
// 也就是当前读取位置(文件流末尾)距离文件开头的字节数。
int len = input_file_stream.tellg();
//在读取文件时,通常会先将文件读取位置定位到文件流的起始位置,然后读取文件内容。
input_file_stream.seekg(0,std::ios::beg);
//new char[len] 表示在堆区中分配了一块大小为 len 个字节的内存空间,并将该内存块的首地址赋值给指针变量 font_file_buffer。
char *font_file_buffer = new char[len];
//read() 成员函数用于从文件流中读取 len 个字节的数据块,并将其存储到 font_file_buffer 指向的字符类型的内存空间中。
input_file_stream.read(font_file_buffer , len);
//将ttf 传入FreeType解析
//ft_library 对象用于存储 FreeType 库的全局状态和内部数据,包括字体文件的加载、字形的生成等操作。
FT_Library ft_library= nullptr;//struct object
//ft_face 对象用于存储字体文件的字形数据,包括字形的轮廓、字形的宽度、字形的坐标等信息
FT_Face ft_face= nullptr;//struct object
//FT_Init_FreeType(&ft_library) 函数用于初始化 FreeType 库,返回一个 FT_Library 类型的结构体对象指针
FT_Init_FreeType(&ft_library);//FreeType初始化;
//通过调用 FT_New_Memory_Face() 函数来将字体文件数据读入到 ft_face 结构体对象中。
FT_Error error = FT_New_Memory_Face(ft_library, (const FT_Byte*)font_file_buffer, len, 0, &ft_face);
if (error != 0){
DEBUG_LOG_ERROR("FT_New_Memory_Face return error {}!",error);
return nullptr;
}
//FT_Select_Charmap 是 FreeType 库中的一个函数,用于选择指定编码的字符映射表(charmap)
//字符映射表则用于将字符编码(Unicode、ASCII 等)和对应的编号之间进行转换。
FT_Select_Charmap(ft_face, FT_ENCODING_UNICODE);
//font_size 表示期望的字号大小(单位为点),将其乘以 $2^6$,然后将结果强制转换为 FT_F26Dot6 类型,就得到了 FreeType 库中使用的字号大小。
// 这个操作实际上是将点数转换为 FreeType 库中使用的固定点数格式,以便在生成字形时使用。
FT_F26Dot6 ft_size = (FT_F26Dot6)(font_size*(1 << 6));
//将 ft_face 指向的字体对象的字符大小设置为指定的大小。
FT_Set_Char_Size(ft_face, ft_size, 0, 72, 72);
if (ft_face == nullptr){
DEBUG_LOG_ERROR("FT_Set_Char_Size error!");
return nullptr;
}
//创建Font实例,保存Freetype解析字体结果。
font=new Font();
//-> 是一个成员访问运算符,它用于通过指针访问一个对象的成员。
// 例如,如果 obj_ptr 是一个指向对象的指针,那么可以使用 obj_ptr->member 来访问对象的成员 member。
// 这样做相当于先通过指针解引用得到对象,然后再访问对象的成员。
// 因此,obj_ptr->member 等价于 (*obj_ptr).member。
font->font_size_=font_size;
font->font_file_buffer_=font_file_buffer;
font->ft_library_=ft_library;
font->ft_face_=ft_face;
font_map_[font_file_path]=font;
//创建空白的、仅Alpha通道纹理,用于生成文字。
//font->font_texture_size_ * font->font_texture_size_ 字节大小的内存,并将该内存的起始地址赋给 pixels 指针
unsigned char * pixels = (unsigned char *)malloc(font->font_texture_size_ * font->font_texture_size_);
//这行代码的作用是将 pixels 指针所指向的内存空间全部初始化为 0
//fill a byte string with byte value
memset(pixels, 0,font->font_texture_size_*font->font_texture_size_);
font->font_texture_=Texture2D::Create(font->font_texture_size_,font->font_texture_size_,GL_RED,GL_RED,GL_UNSIGNED_BYTE,pixels);
//这其实就是一张图集,和普通的小图打包成大图唯一的不同之处就是,这里的小图是freetype动态生成的bitmap。
delete pixels;
return font;
}