In my EFI bootloader that is written in C, I'm loading in PSF1 Font files by the ZAP Group. Then I'm passing off my data structure of fonts to my kernel that is written in C++.
These fonts have 2 different versions. Version 1 is already supported by my font loading function in my bootloader and it is working within my kernel's frame buffer render. I am successfully able to render a basic font using the zap-light16.psf file. This font's glyph size is 8x16.
There are two structures associated to this font's file format when parsing and loading in the font file.
These are the original structures found in main.c bootloader
typedef struct { unsigned char magic[2]; unsigned char mode; unsigned charsize;} PSF1_HEADER;typedef struct { PSF1_HEADER* psf1_Header; void* glyphBuffer;} PSF1_FONT;Here is the function to load the 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;}Here is the LoadFile() function in case you need to see it:
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;}In my efi-main function this is how I was calling it.
{ // ... 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}This is how I was passing it to my kernel from the bootloader. It is being stored in a struct with other objects such as the Framebuffer*, EFI_MEMORY_DESCRIPTOR* Map, the MapSize and the MapDescriptorSize...
typedef struct { Framebuffer* framebuffer; PSF1_FONT* font; EFI_MEMORY_DESCRIPTOR* Map; UINTN MapSize; UINTN MapDescriptorSize;} BootInfo;And back in efi_main It is being passed to the kernel via function pointer...
{ // ...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;}The above code works within my kernel that is running QEMU on Ubuntu latest version within Virtual Box latest version on my Windows 7 x64 machine. I'm using gnu-cpp cross compiler, and there are several make files within this project to help compile, link and build the project as well as aiding in loading the font files not shown here...
I was able to extend this to support a second font file that had the same version and size...
I just changed the BootInfo struct's PSF1_FONT* to a PSF1FONT** and named it fonts, and in my efi_main function where I loaded in my font, I repeated the process above with a different pointer variable name, then created an array and initialized that array with the two created fonts and changed the similar or related c++ struct within my kernel files to match that of this bootloader.
This was also working.
Now I'm in the process of extending this to also support version 2 and the rest of the font files that are available from Zap.org with differing font sizes.
Here's a table of the different fonts and their properties that I summarized from ZAP's website...
light16 glyphs 0x00-0xff psf version 1 size 8x16 pixels count 256 unicode Ylight16ext glyphs 0x000-0x0ff psf version 1 size 8x16 pixels count 512 unicode Ylight18 glyphs 0x00-0xff psf version 1 size 8x18 pixels count 256 unicode Ylight18ext glyphs 0x000-0x0ff psf version 1 size 8x18 pixels count 512 unicode Ylight20 glyphs 0x00-0xff psf version 2 size 10x20 pixels count 256 unicode Ylight20ext glyphs 0x000-0x0ff psf version 2 size 10x20 pixels count 512 unicode Ylight24 glyphs 0x00-0x0ff psf version 2 size 10x24 pixels count 256 unicode Ylight24ext glyphs 0x00-0x0ff psf version 2 size 10x24 pixels count 512 unicode Yvga09 glyphs 0x00-0xff psf version 1 size 8x9 pixels count 256 unicode Yvga09ext glyphs 0x000-0x0ff psf version 1 size 8x9 pixels count 512 unicode Yvga16 glyphs 0x00-0xff psf version 1 size 8x16 pixels count 256 unicode Yvga16ext glyphs 0x000-0x0ff psf version 1 size 8x16 pixels count 256 unicode YNow, within my kernel for the renderer, I have hardcoded values of 8 and 16 to control the cursor position when rendering the fonts. I want to change these to be variables for the currently loaded font. I need both width and height so I'm thinking about adding a Point struct to my bootloader, there is already a Point structure in my kernel...
typedef struct { unsigned int x; unsigned int y;} Point;And then adding an instance of this within my PSF1_FONT struct as well as the version number which would now look like this:
typedef struct { PSF1_HEADER* psf1_Header; void* glyphBuffer; Point fontSize; unsigned int version;} PSF1_FONT;This way when I pass it to my kernel, BasicRenderer class object will have direct access to the width and height of the loaded font.
Now to my question... I've searched through their documentations, their repositories etc... but I can not seem to find any irrelevant information about obtaining both of the glyphs dimensions. The only thing I have seen is the size but I believe that is determined by its height as most fonts are 8pixels in with and they use a 1 pixel space buffer... When I'm calling my LoadPSF1Font() function how do I or can I extract both the font's width and height from the font's header or glyph buffer? Or would I have to hard-code or pre-define these values within the boot when loading the font file?
Edit
Within in my kernel's BasicRenderer class, this is how I'm rendering a char from the font as it has access to the PSF1_FONT structure:
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++; } }}As you can see, it is referencing the psf1_Header's charsize member. Then it is moving the draw calls with hard coded values of 8 and 16. I would like to change these to Point fontSize and use fontSize.x and fontSize.y respectively...