c++ - 为自定义内核解析 ZAP 组的控制台字体
问题描述
在我用C编写的EFI 引导加载程序中,我正在由ZAP Group加载PSF1 字体文件。然后我将我的字体数据结构传递给我用C++编写的内核。
这些字体有 2 个不同的版本。我的引导加载程序中的字体加载功能已经支持版本 1,并且它在我的内核的帧缓冲区渲染中工作。我成功地能够使用该zap-light16.psf
文件呈现基本字体。此字体的字形大小为 8x16。
在解析和加载字体文件时,有两种结构与此字体的文件格式相关联。
这些是main.c引导加载程序中的原始结构
typedef struct {
unsigned char magic[2];
unsigned char mode;
unsigned charsize;
} PSF1_HEADER;
typedef struct {
PSF1_HEADER* psf1_Header;
void* glyphBuffer;
} PSF1_FONT;
这是加载字体的函数:
PSF1_FONT* LoadPSF1Font(EFI_FILE* Directory, CHAR16* Path, EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE* SystemTable ) {
EFI_FILE* font = LoadFile(Directory, Path, ImageHandle, SystemTable);
if( font == NULL) return NULL;
PSF1_HEADER* fontHeader;
SystemTable->BootServices->AllocatePool(EfiLoaderData, sizeof(PSF1_HEADER), (void**)&fontHeader);
UINTN size = sizeof(PSF1_HEADER);
font->Read(font, &size, fontHeader);
if (fontHeader->magic[0] != PSF1_MAGIC0 || fontHeader->magic[1] != PSF1_MAGIC1) return NULL;
UINTN glyphBufferSize = fontHeader->charsize * 256;
if (fontHeader->mode == 1) { // 512 glyphs
glyphBufferSize *= 2;
}
void* glyphBuffer;
font->SetPosition(font, sizeof(PSF1_HEADER));
SystemTable->BootServices->AllocatePool(EfiLoaderData, glyphBufferSize, (void**)&glyphBuffer);
font->Read(font, &glyphBufferSize, glyphBuffer);
PSF1_FONT* finishedFont;
SystemTable->BootServices->AllocatePool(EfiLoaderData, sizeof(PSF1_FONT), (void**)&finishedFont);
finishedFont->psf1_Header = fontHeader;
finishedFont->glyphBuffer = glyphBuffer;
return finishedFont;
}
这是LoadFile()
您需要查看的功能:
EFI_FILE* LoadFile(EFI_FILE* Directory, CHAR16* Path, EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE* SystemTable) {
EFI_FILE* LoadedFile;
EFI_LOADED_IMAGE_PROTOCOL* LoadedImage;
SystemTable->BootServices->HandleProtocol(ImageHandle, &gEfiLoadedImageProtocolGuid, (void**)&LoadedImage);
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL* FileSystem;
SystemTable->BootServices->HandleProtocol(LoadedImage->DeviceHandle, &gEfiSimpleFileSystemProtocolGuid, (void**)&FileSystem);
if (Directory == NULL) {
FileSystem->OpenVolume(FileSystem, &Directory);
}
EFI_STATUS s = Directory->Open(Directory, &LoadedFile, Path, EFI_FILE_MODE_READ, EFI_FILE_READ_ONLY);
if (s != EFI_SUCCESS) {
return NULL;
}
return LoadedFile;
}
在我的efi-main
函数中,这就是我的调用方式。
{
// ... code
PSF1_FONT* newFont = LoadPSF1Font(NULL, L"zap-light16.psf", ImageHandle, SystemTable);
if (newFont == NULL) {
Print(L"Font is not valid or is not found\n\r");
} else {
Print(L"Font found, char size = %d\n\r", newFont->psf1_Header->charsize);
}
// ... more code
}
这就是我从引导加载程序将它传递给我的内核的方式。它与其他对象(例如Framebuffer*
, EFI_MEMORY_DESCRIPTOR* Map
, theMapSize
和MapDescriptorSize
... )一起存储在结构中
typedef struct {
Framebuffer* framebuffer;
PSF1_FONT* font;
EFI_MEMORY_DESCRIPTOR* Map;
UINTN MapSize;
UINTN MapDescriptorSize;
} BootInfo;
回到efi_main
它正在通过函数指针传递给内核......
{
// ...code
void (*KernelStart)(BootInfo*) = ((__attribute____(sysv_abi)) void(*)(BootInfo*)) header.e_entry);
BootInfo bootInfo;
bootInfo.framebuffer = newBuffer; // defined elsewhere in this function
bootInfo.font = font;
bootInfo.Map = Map;
bootInfo.MapSize = MapSize;
bootInfo.MapDescriptorSize = DescriptorSize;
SystemTable->BootServices->ExitBootServices(ImageHandle, MapKey);
KernelStart(&bootInfo);
return EFI_SUCCESS;
}
上面的代码在我的内核中运行,该内核在我的Windows 7 x64机器上的Virtual Box最新版本中的Ubuntu最新版本中的QEMU中运行。我正在使用交叉编译器,该项目中有几个 make 文件可帮助编译、链接和构建项目,以及帮助加载此处未显示的字体文件...gnu-cpp
我能够扩展它以支持具有相同版本和大小的第二个字体文件......
我只是将BootInfo
结构更改PSF1_FONT*
为 aPSF1FONT**
并将其命名为字体,在efi_main
我加载字体的函数中,我使用不同的指针变量名称重复了上述过程,然后创建了一个数组并使用两个创建的字体初始化该数组并更改我的文件中的类似或相关c++
结构kernel
以匹配此引导加载程序的结构。
这也有效。
现在我正在扩展它以支持版本 2 和Zap.org提供的具有不同字体大小的其余字体文件。
这是我从 ZAP 网站总结的不同字体及其属性的表格...
light16
glyphs 0x00-0xff
psf version 1
size 8x16 pixels
count 256
unicode Y
light16ext
glyphs 0x000-0x0ff
psf version 1
size 8x16 pixels
count 512
unicode Y
light18
glyphs 0x00-0xff
psf version 1
size 8x18 pixels
count 256
unicode Y
light18ext
glyphs 0x000-0x0ff
psf version 1
size 8x18 pixels
count 512
unicode Y
light20
glyphs 0x00-0xff
psf version 2
size 10x20 pixels
count 256
unicode Y
light20ext
glyphs 0x000-0x0ff
psf version 2
size 10x20 pixels
count 512
unicode Y
light24
glyphs 0x00-0x0ff
psf version 2
size 10x24 pixels
count 256
unicode Y
light24ext
glyphs 0x00-0x0ff
psf version 2
size 10x24 pixels
count 512
unicode Y
vga09
glyphs 0x00-0xff
psf version 1
size 8x9 pixels
count 256
unicode Y
vga09ext
glyphs 0x000-0x0ff
psf version 1
size 8x9 pixels
count 512
unicode Y
vga16
glyphs 0x00-0xff
psf version 1
size 8x16 pixels
count 256
unicode Y
vga16ext
glyphs 0x000-0x0ff
psf version 1
size 8x16 pixels
count 256
unicode Y
现在,在我的渲染器内核中,我硬编码了 和 的值,8
以便16
在渲染字体时控制光标位置。我想将这些更改为当前加载字体的变量。我需要两者width
,height
所以我正在考虑向我的引导加载程序添加一个结构,我的内核Point
中已经有一个结构......Point
typedef struct {
unsigned int x;
unsigned int y;
} Point;
然后在我的PSF1_FONT
结构中添加一个 this 的实例,version number
现在看起来像这样:
typedef struct {
PSF1_HEADER* psf1_Header;
void* glyphBuffer;
Point fontSize;
unsigned int version;
} PSF1_FONT;
这样,当我将它传递给我的内核时,BasicRenderer
类对象将可以直接访问加载字体的width
and height
。
现在我的问题......我已经搜索了他们的文档,他们的存储库等......但我似乎找不到任何关于获得这两个glyphs
维度的不相关信息。我唯一看到的是,size
但我相信这是由它决定的,height
因为大多数字体的8
宽度都是像素,并且它们使用1
像素空间缓冲区......当我调用我的LoadPSF1Font()
函数时,我如何或可以同时提取字体的字体标题或字形缓冲区的宽度和高度?或者在加载字体文件时,我是否必须在引导中硬编码或预定义这些值?
编辑
在我的内核BasicRenderer
类中,这就是我char
从字体渲染 a 的方式,因为它可以访问PSF1_FONT
结构:
void BasicRenderer::RenderChar(char c) {
if (c == '\n' ) {
Newline();
} else {
unsigned int* pixPtr = (unsigned int*)framebuffer_->BaseAddress;
char* fontPtr = (char*)selected_font_->glyphBuffer + (c * selected_font_->psf1_Header->charsize);
for (unsigned long y = cursor_position_.y; y < cursor_position_.y + 16; y++) {
for (unsigned long x = cursor_position_.x; x < cursor_position_.x + 8; x++) {
if ((*fontPtr & (0b10000000 >> (x - cursor_position_.x))) > 0) {
*(unsigned int*)(pixPtr + x + (y * framebuffer_->PixelsPerScanLine)) = font_color_;
}
}
fontPtr++;
}
}
}
如您所见,它正在引用psf1_Header
'charsize
成员。8
然后它使用和的硬编码值移动绘制调用16
。我想将这些更改为Point fontSize
并分别使用fontSize.x
和fontSize.y
...
解决方案
推荐阅读
- typescript - 如何在打字稿中编写错误处理装饰器
- ajax - React AJAX 发布请求不会转到成功/错误方法
- r - 如何将“chr”格式的“日期”转换为“日期”格式?
- javascript - 如何处理多个文件?
- c - 如何避免使用 ctrl-c 终止父进程?
- mysql - 如何以引用表中的另一个字段为条件对数据库字段强制执行参照完整性?
- c# - 在存储过程中组合两个视图
- python - 在 matplotlib 中设置图像的位置
- tensorflow - 尝试获取中间层输出时出现keras错误:无法创建cudnn句柄
- visual-studio - Xamarin.Android:System.Runtime.Caching 无法解析引用