UEFI开发学习10 – 中文显示(Ⅰ)

前言

UDK的源码中默认是不支持中文字体显示的,有需要的话需要自己添加。显示中文字库的方法有两种,分别适用于不同的场景。第一种是将中文字库转换成数组,然后使用

EFI_GRAPHICS_OUTPUT_PROTOCOL->Blt()将字体“画”出来,第二种是按照UEFI标准生成中文字库,然后将字体添加到数据库中。两种方法中,前者适用于开发EFI应用使用,后者则适用于汉化UEFI Setup界面。

点阵原理

以上提到的两种方法中都需要生成中文字库,怎么生成呢?原理是什么?

生成的话较简单,使用我之前发布的工具即可(https://ay123.net/share/865/),这里主要是认识一下字库生成的原理。以生成“十”字的字库来说,首先确定生成字体的大小,如宽10,高10,单位是像素,如下图:

图中为1的部分表示在这个10x10的阵列中存在一个像素点,用一个10x10的C语言数组可表示为

array[10][10] = {
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
{0, 1, 1, 1, 1, 1, 1, 1, 1, 0},
{0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
};

这样我们便可以用EFI_GRAPHICS_OUTPUT_PROTOCOL->Blt()这个函数将“十”一点一点画出来了。下面我们用字库生成工具生成一个,发现是这样的(选择不同的字体,字库会有不同的差异):

static const unsigned char bits[][20]=
{
//U+5341(十)
0x00,0x00,0x04,0x00,0x08,0x00,0x08,0x00,0x7F,0x80,0x08,0x00,0x08,0x00,0x08,0x00,
0x08,0x00,0x00,0x00,
};

整理一下:

static const unsigned char bits[][20]=
{
//U+5341(十)
0x00,0x00,
0x04,0x00,
0x08,0x00,
0x08,0x00,
0x7F,0x80,
0x08,0x00,
0x08,0x00,
0x08,0x00,
0x08,0x00,
0x00,0x00,
};

这是使用一个byte表示8个点,所以是这种写法,将16进制转换成2进制的话是一样的。一个byte不足表示一行10个点怎么办呢?那就再加一个byte,所以看到的数组每行有两个byte表示。

再看一下,将数组的第二行0x04,0x00转换为二进制后:0000 0100, 0000 0000,程序该怎么读取这个点呢?是从左边逐个取值(高位在前)还是右边逐个取值(低位在前)呢?顺序不一样,画出来的结果也是不同的。这个在生成工具是可配置的,如下图:

这里设置时高位在前,所以按从左到右逐个取值画图就可以了。工具还有一个扫描方式,水平扫描和垂直扫描,水平扫描是一行阵列组成n个byte,画点的时候是从左到右,而垂直扫描则是一列组成n个byte,按从上到下的顺序画点,我们按照默认设置即可。

实例1,画点阵字库

1). 使用字库生成工具,在编码表输入“上善若水”,配置如下图,然后保存字库,文件名为"FontLib.c"

2). 在AppPkg新建一个Font模块,并将FontLib.c加入该文件夹,如图:

3). 打开FontLib.c,将数组前的static去掉,否则在Font.c将无法访问,编译出错,因为静态变量是无法跨文件访问的

4). Font.c程序如下:

#include  <Uefi.h>
#include  <Library/UefiLib.h>
#include  <Library/UefiBootServicesTableLib.h>
#include  <Protocol/GraphicsOutput.h>

#define FONT_HEIGHT 55
#define FONT_WIDTH 60
#define FONT_ARRAY_SIZE 440
#define FONT_LINE_COUNT (FONT_ARRAY_SIZE / FONT_HEIGHT)
#define FONT_COUNT 4

extern unsigned char bits[][440];

VOID DrawByte(
    EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput,
    UINT8 Byte,
    UINTN X,
    UINTN Y,
    EFI_GRAPHICS_OUTPUT_BLT_PIXEL Color
    )
{
    UINT8 i;

    for (i = 0; i < 8; i ++) {
        if (Byte & 0x80) {
            GraphicsOutput->Blt(GraphicsOutput, &Color, EfiBltVideoFill, 0, 0, X, Y, 1, 1, 0);
        }
        /* 从高位到低位逐个取值 */
        Byte <<= 1; 
        X ++;
    }
}

VOID DrawFont(
    EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput,
    UINT8 *FontBitMap, 
    UINT8 Height, 
    UINT8 LineCout,
    UINTN X,
    UINTN Y,
    EFI_GRAPHICS_OUTPUT_BLT_PIXEL Color
    )
{
    UINT8 i, j;
    UINTN BackupX;

    BackupX = X;

    for(i = 0; i < Height; i ++) {
        X = BackupX;
        /* 画一行,每行8个byte */
        for(j = 0; j < LineCout; j ++) {
            DrawByte(GraphicsOutput, FontBitMap[i * LineCout + j], X, Y, Color);
            X += LineCout;
        }
        Y ++;
    }
}


int main (
  IN int Argc,
  IN char **Argv
  )
{
    EFI_STATUS Status;
    UINTN ScreenWidth, ScreenHeight, i;
    EFI_GRAPHICS_OUTPUT_PROTOCOL  *GraphicsOutput;
    EFI_GRAPHICS_OUTPUT_BLT_PIXEL ColorWhite = { 0xFF, 0xFF, 0xFF, 0 };
    EFI_GRAPHICS_OUTPUT_BLT_PIXEL ColorBlue  = { 0xFF, 0x00, 0x00, 0 };

    Status = gBS->LocateProtocol(&gEfiGraphicsOutputProtocolGuid, NULL, (VOID **) &GraphicsOutput);
        
    if (EFI_ERROR (Status)) {
        return EFI_UNSUPPORTED;
    }

    ScreenWidth  = GraphicsOutput->Mode->Info->HorizontalResolution;
    ScreenHeight = GraphicsOutput->Mode->Info->VerticalResolution;
    
    /* 将屏幕填充为白色 */
    Status = GraphicsOutput->Blt(
                    GraphicsOutput,
                    &ColorWhite,
                    EfiBltVideoFill,
                    0, 0,
                    0, 0, 
                    ScreenWidth, ScreenHeight,   
                    0
                    );

    for (i = 0; i < FONT_COUNT; i ++) {
        DrawFont(GraphicsOutput, bits[i], FONT_HEIGHT, FONT_LINE_COUNT, 100 + FONT_WIDTH * i, 100, ColorBlue);
    }

    return(0);
}

Font.inf

## @file
#  A simple, basic, EDK II native, "hello" application.
#
#   Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>
#   This program and the accompanying materials
#   are licensed and made available under the terms and conditions of the BSD License
#   which accompanies this distribution. The full text of the license may be found at
#   http://opensource.org/licenses/bsd-license.
#
#   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
#   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#
##

[Defines]
  INF_VERSION                    = 0x00010006
  BASE_NAME                      = Font
  FILE_GUID                      = a912fa98-7f0e-4a03-b908-b757b806ec83
  MODULE_TYPE                    = UEFI_APPLICATION
  VERSION_STRING                 = 0.1
  ENTRY_POINT                    = ShellCEntryLib

#
#  VALID_ARCHITECTURES           = IA32 X64 IPF
#

[Sources]
  Font.c
  FontLib.c

[Packages]
  MdePkg/MdePkg.dec

[LibraryClasses]
  LibC

[Protocols]
  gEfiGraphicsOutputProtocolGuid

最后结果如下:

上面的演示中居然还能看到一点点动画效果,这是由于多个画点的原因导致的,效率较低。下面再按照我这篇文章所讲的->UEFI开发学习9 - 高效画图,修改下代码,如下:

#include  <Uefi.h>
#include  <Library/UefiLib.h>
#include  <Library/UefiBootServicesTableLib.h>
#include  <Protocol/GraphicsOutput.h>
#include  <Library/MemoryAllocationLib.h>

#define FONT_HEIGHT 55
#define FONT_WIDTH 60
#define FONT_ARRAY_SIZE 440
#define FONT_LINE_COUNT (FONT_ARRAY_SIZE / FONT_HEIGHT)
#define FONT_COUNT 4

extern unsigned char bits[][440];

VOID DrawByte(
    EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput,
    UINT8 Byte,
    UINTN X,
    UINTN Y,
    EFI_GRAPHICS_OUTPUT_BLT_PIXEL Color
    )
{
    UINT8 i;

    for (i = 0; i < 8; i ++) {
        if (Byte & 0x80) {
            GraphicsOutput->Blt(GraphicsOutput, &Color, EfiBltVideoFill, 0, 0, X, Y, 1, 1, 0);
        }
        /* 从高位到低位逐个取值 */
        Byte <<= 1; 
        X ++;
    }
}

VOID DrawFont(
    EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput,
    UINT8 *FontBitMap, 
    UINT8 Height, 
    UINT8 LineCout,
    UINTN X,
    UINTN Y,
    EFI_GRAPHICS_OUTPUT_BLT_PIXEL Color
    )
{
    UINT8 i, j;
    UINTN BackupX;

    BackupX = X;

    for(i = 0; i < Height; i ++) {
        X = BackupX;
        /* 画一行,每行8个byte */
        for(j = 0; j < LineCout; j ++) {
            DrawByte(GraphicsOutput, FontBitMap[i * LineCout + j], X, Y, Color);
            X += LineCout;
        }
        Y ++;
    }
}

VOID DrawFontBuffer(
    EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput,
    UINT8 *FontBitMap, 
    UINT8 Height, 
    UINT8 Width,
    UINTN X,
    UINTN Y,
    EFI_GRAPHICS_OUTPUT_BLT_PIXEL Color    
)
{
    EFI_GRAPHICS_OUTPUT_BLT_PIXEL *FontBuf;
    UINTN i, k;
    UINTN BitIndex, LineBit;

    BitIndex = 0;
    LineBit  = 0;

    FontBuf = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) AllocateZeroPool (Height * Width * sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
    
    for (i = 0; i < FONT_ARRAY_SIZE; i ++) {
        for (k = 0; k < 8; k ++) {
            if ((FontBitMap[i] << k) & 0x80) {
                FontBuf[BitIndex] = Color;
            }
            BitIndex ++;

            if (++ LineBit == Width) {
                LineBit = 0;
                break;
            }
        }
    }
    GraphicsOutput->Blt(GraphicsOutput, FontBuf, EfiBltBufferToVideo, 0, 0, X, Y, Width, Height, 0);
}

int main (
  IN int Argc,
  IN char **Argv
  )
{
    EFI_STATUS Status;
    UINTN ScreenWidth, ScreenHeight, i;
    EFI_GRAPHICS_OUTPUT_PROTOCOL  *GraphicsOutput;
    EFI_GRAPHICS_OUTPUT_BLT_PIXEL ColorWhite = { 0xFF, 0xFF, 0xFF, 0 };
    EFI_GRAPHICS_OUTPUT_BLT_PIXEL ColorBlue  = { 0xFF, 0x00, 0x00, 0 };

    Status = gBS->LocateProtocol(&gEfiGraphicsOutputProtocolGuid, NULL, (VOID **) &GraphicsOutput);
        
    if (EFI_ERROR (Status)) {
        return EFI_UNSUPPORTED;
    }

    ScreenWidth  = GraphicsOutput->Mode->Info->HorizontalResolution;
    ScreenHeight = GraphicsOutput->Mode->Info->VerticalResolution;

    /* 将屏幕填充为白色 */
    Status = GraphicsOutput->Blt(
                    GraphicsOutput,
                    &ColorWhite,
                    EfiBltVideoFill,
                    0, 0,
                    0, 0, 
                    ScreenWidth, ScreenHeight,   
                    0
                    );

    for (i = 0; i < FONT_COUNT; i ++) {
        //DrawFont(GraphicsOutput, bits[i], FONT_HEIGHT, FONT_LINE_COUNT, 100 + FONT_WIDTH * i, 100, ColorBlue);
        DrawFontBuffer(GraphicsOutput, bits[i], FONT_HEIGHT, FONT_WIDTH, 100 + FONT_WIDTH * i, 100, ColorBlue);
    }

    return(0);
}

运行结果看不到任何延迟了,如下:

思考:结合上方的程序,如何将文字的背景色黑色改为其它颜色?

实例代码下载:https://wwa.lanzous.com/b0dkco43c 密码:epqm

 

版权声明:
作者:bin
链接:https://ay123.net/mystudy/uefi/869/
来源:爱影博客
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
< <上一篇
下一篇>>