【问题标题】:How to use text in EFI graphic mode?如何在 EFI 图形模式下使用文本?
【发布时间】:2019-09-25 11:18:24
【问题描述】:

我对创建 efi 应用程序非常陌生。我的目标是在 efi 中创建一个小型应用程序,在背景上显示一些文本。但我坚持尝试在显示器上显示文本(最好有一个自定义字体,但在这个阶段没有必要)。我希望应用程序(也)在苹果系统上运行(从 USB 启动)

  1. 如何找到有关 EFI 功能的良好文档?似乎很难找到好的例子等等。

  2. 如何使用 EFI 在背景上显示文本?

这是我到目前为止所得到的。我使用图形协议将背景更改为颜色。如何在其上显示文本。输出字符串似乎不起作用。

#include "efibind.h"
#include "efidef.h"
#include "efidevp.h"
#include "eficon.h"
#include "efiapi.h"
#include "efierr.h"
#include "efiprot.h"

static EFI_GUID GraphicsOutputProtocolGUID = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;

/**
 * efi_main - The entry point for the EFI application
 * @image: firmware-allocated handle that identifies the image
 * @SystemTable: EFI system table
 */
EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systemTable) {
        EFI_BOOT_SERVICES *bs = systemTable->BootServices;
        EFI_STATUS status;
        EFI_GRAPHICS_OUTPUT_PROTOCOL *graphicsProtocol;
        SIMPLE_TEXT_OUTPUT_INTERFACE *conOut = systemTable->ConOut;
        EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
        UINTN SizeOfInfo, sWidth, sHeight;

        status = bs->LocateProtocol(&GraphicsOutputProtocolGUID, NULL, 
                (void**)&graphicsProtocol);

        if (EFI_ERROR(status) || graphicsProtocol == NULL) {
                conOut->OutputString(conOut, L"Failed to init gfx!\r\n");
                return status;
        }

        conOut->ClearScreen(conOut);

        //Switch to current mode so gfx is started.
        status = graphicsProtocol->SetMode(graphicsProtocol, graphicsProtocol->Mode->Mode);
        if (EFI_ERROR(status)) {
                conOut->OutputString(conOut, L"Failed to set default mode!\r\n");
                return status;
        }

        EFI_GRAPHICS_OUTPUT_BLT_PIXEL p;
        p.Red = 200;
        p.Green = 77;
        p.Blue = 13;
        graphicsProtocol->QueryMode(graphicsProtocol, graphicsProtocol->Mode->Mode, &SizeOfInfo, &info);
        sWidth = info->HorizontalResolution;
        sHeight = info->VerticalResolution;
        status = graphicsProtocol->Blt(graphicsProtocol, &p, EfiBltVideoFill, 0, 0, 0, 0, sWidth, sHeight, 0);


while (1) {
conOut->OutputString(conOut, L"Some text that I want to display\r\n");
bs->Stall(500000);
}



        return EFI_SUCCESS;
}

【问题讨论】:

  • 您说的是 EFI (v1.x) 还是 UEFI (v2.x)?使用 EDK2 (UEFI) 构建时,您的代码可以正常工作。
  • @MiSimon 我都试过了,它可以在模拟器中运行,但不能从 Mac 上启动:/ 这就是问题所在!
  • 您的系统上可能有多个 SIMPLE_TEXT_OUTPUT_INTERFACE 实例可用,使用 bs->LocateHandleBuffer 获取安装了 SIMPLE_TEXT_OUTPUT_INTERFACE 的所有可用句柄,并尝试在所有实例上调用 OutputString(使用 bs->OpenProtocol 获取句柄中的实例)。
  • 所以我发现如果我只在 mac 上显示文本,我会看到它,但只是很快 - 就像一个快速闪烁然后我继续启动到 macos ......在 pc 上工作正常

标签: c macos uefi


【解决方案1】:

UEFI 支持图形输出。它还支持文本输出(这可能意味着输出到串行控制台,或呈现到图形控制台的文本,或两者兼而有之)。但是没有明确的方式以受控方式在它们之间进行交互。

提供带有文本元素的图形环境(BIOS 配置菜单,GRUB)的应用程序通常使用自己的框架使用 GRAPHICS_OUTPUT_PROTOCOL 在图形控制台上绘制文本。

【讨论】:

  • 在我见过的大多数系统上,SIMPLE_TEXT_OUTPUT_PROTOCOL 如果是图形控制台,则安装在与 GRAPHICS_OUTPUT_PROTOCOL 相同的句柄上;如果是串行、网络等控制台,则安装在其他句柄上跨度>
  • @MiSimon:当然,文本控制台可能会使用图形控制台进行渲染,但这使得交互更加复杂,而不是更少。
【解决方案2】:

如果您专门针对 MacEFI,则需要额外的协议调用来强制控制台进入文本模式,like this

【讨论】:

  • 谢谢@CodeRush,我试过了,但我不确定如何正确实现它(对不起,我对此很陌生),你知道如何在上面的例子中实现它吗?跨度>
  • 尤其是有彩色背景的,我不知道怎么做
  • 目前尚不清楚您最终要实现什么目标,即为什么需要在背景上呈现文本? EFI 中的图形并不容易做到,但有一些项目在 PC 和 MacOS 上都做得很好,比如rEFItClover
  • 感谢您的回答!我想做的是:彩色背景上的一些文字
【解决方案3】:

这是一个使用来自LVGL 的字体模块的文本渲染器的简短示例(可以独立使用,替换 #include "../../lv_conf.h" lv_font.h 文件,带有 #define USE_LV_FONT_DEJAVU_20 8)和 GRAPHICS_OUTPUT_PROTOCOL 中的 Blt 方法

#include <Uefi.h>
#include <Library\UefiLib.h>
#include <Protocol\GraphicsOutput.h>
#include "lv_font.h"

#define LETTER_SPACE 2
#define WAIT_SECONDS 10
#define FONT &lv_font_dejavu_20

static EFI_BOOT_SERVICES *gBS;
static EFI_RUNTIME_SERVICES *gRT;
static EFI_GRAPHICS_OUTPUT_PROTOCOL *gGOP = (EFI_GRAPHICS_OUTPUT_PROTOCOL *)NULL;
static EFI_GRAPHICS_OUTPUT_BLT_PIXEL gWhite = { 255,255,255,0 };

static void _util_render_glyph(UINT32 x, UINT32 y, CHAR8 letter)
{
    UINT32        height;
    UINT32        width;
    UINT32        pm_x;
    UINT32        pm_y;
    UINT32        index;
    const UINT8*  bitmap;
    EFI_GRAPHICS_OUTPUT_BLT_PIXEL *pixelmap;

    if (gGOP == NULL) {
        return;
    }

    height = lv_font_get_height(FONT);
    width = lv_font_get_width(FONT, letter);

    // glyph is not defined in this font
    if (width == 0) {
        return;
    }

    bitmap = lv_font_get_bitmap(FONT, letter);

    // using 8 bpp for simplicity
    if (EFI_ERROR(gBS->AllocatePool(EfiLoaderData, height * width * sizeof(*pixelmap), (VOID**)&pixelmap))) {
        return;
    }

    gBS->SetMem((VOID*)pixelmap, height * width * sizeof(*pixelmap), 0);

    // get the current content of the framebuffer to allow 'transparent' blt operations
    gGOP->Blt(gGOP, pixelmap, EfiBltVideoToBltBuffer, x, y, 0, 0, width, height, 0);

    for (pm_y = 0; pm_y < height; pm_y++) {
        for (pm_x = 0; pm_x < width; pm_x++) {
            index = width * pm_y + pm_x;

            if (bitmap[index] > 200) {
                pixelmap[index].Red = 0;
                pixelmap[index].Blue = 0;
                pixelmap[index].Green = 0;
                pixelmap[index].Reserved = 0;
            }
            else if (bitmap[index] > 100) {
                pixelmap[index].Red = 105;
                pixelmap[index].Blue = 105;
                pixelmap[index].Green = 105;
                pixelmap[index].Reserved = 0;
            }
        }
    }

    gGOP->Blt(gGOP, pixelmap, EfiBltBufferToVideo, 0, 0, x, y, width, height, 0);
    gBS->FreePool(pixelmap);
}

static void _util_render_text(UINT32 x, UINT32 y, const CHAR8 *string)
{
    UINT32 index;
    UINTN length;
    UINT32 scr_w;
    UINT32 scr_h;

    UINT32 str_x;

    UINT32 gly_w;
    UINT32 gly_h;

    if (string == NULL) {
        return;
    }

    if (gGOP == NULL) {
        return;
    }

    scr_w = gGOP->Mode->Info->HorizontalResolution;
    scr_h = gGOP->Mode->Info->VerticalResolution;

    length = AsciiStrnLenS(string, 32);

    gly_h = lv_font_get_height(FONT);

    // check if the string can be printed 
    if ((y + gly_h) > scr_h) {
        return;
    }

    if (x > scr_w) {
        return;
    }

    // print the string glyph by glyph
    str_x = x;
    for (index = 0; index < length; index++) {
        // check if the glyph can be printed
        gly_w = lv_font_get_width(FONT, string[index]);
        if ((str_x + gly_w) > scr_w) {
            break;
        }

        // print the glyph
        _util_render_glyph(str_x, y, string[index]);

        // calculate the position of the next glyph
        str_x += gly_w + LETTER_SPACE;
    }
}

static void _util_fill_screen(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *color)
{
    if (gGOP == NULL) {
        return;
    }

    gGOP->Blt(gGOP, color, EfiBltVideoFill, 0, 0, 0, 0, gGOP->Mode->Info->HorizontalResolution, gGOP->Mode->Info->VerticalResolution, 0);
}


static void _util_wait(UINT32 seconds)
{
    EFI_TIME    time;
    UINT8       current_second = 255;
    UINT32      elapsed_seconds = 0;

    //wait for some seconds
    while (elapsed_seconds <= WAIT_SECONDS) {
        if (!EFI_ERROR(gRT->GetTime(&time, (EFI_TIME_CAPABILITIES*)NULL))) {
            if (current_second != time.Second) {
                elapsed_seconds++;
                current_second = time.Second;
            }
        }
        else {
            break;
        }
        CpuPause();
    }
}

EFI_STATUS
EFIAPI
UefiMain(
    IN EFI_HANDLE        ImageHandle,
    IN EFI_SYSTEM_TABLE  *SystemTable)
{
    EFI_STATUS  eRc;

    gBS = SystemTable->BootServices;
    gRT = SystemTable->RuntimeServices;

    eRc = gBS->LocateProtocol(
        &gEfiGraphicsOutputProtocolGuid,
        NULL,
        (VOID**)&gGOP);

    if (EFI_ERROR(eRc) || gGOP == NULL) {
        return EFI_SUCCESS;
    }

    _util_fill_screen(&gWhite);
    _util_render_text(0, 0, "HELLO WORLD!");

    _util_wait(WAIT_SECONDS);

    return EFI_SUCCESS;
}

我在 pc 和 mac 上测试了它,它在两者上都运行。使用 LVGL 在其网站上提供的工具,您可以使用任何您想要的字体。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-08
    • 1970-01-01
    • 2021-09-18
    • 2020-08-12
    • 1970-01-01
    • 2017-08-24
    相关资源
    最近更新 更多