首页 > 解决方案 > 为自定义内核解析 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, theMapSizeMapDescriptorSize... )一起存储在结构中

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在渲染字体时控制光标位置。我想将这些更改为当前加载字体的变量。我需要两者widthheight所以我正在考虑向我的引导加载程序添加一个结构,我的内核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类对象将可以直接访问加载字体的widthand 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.xfontSize.y...

标签: c++cparsingfontskernel

解决方案


推荐阅读