【UEFI实战】UEFI图形显示(字符输出)

这篇具有很好参考价值的文章主要介绍了【UEFI实战】UEFI图形显示(字符输出)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

HII Font

接下来介绍EFI_HII_FONT_PROTOCOL,它在UEFI代码中完成了字符到像素的转换,本节主要介绍这个转换关系,它的实现代码在edk2\MdeModulePkg\Universal\HiiDatabaseDxe\HiiDatabaseDxe.inf中,除了EFI_HII_FONT_PROTOCOL,这个模块还实现了很多其它的Protocol,后面用到的时候也会介绍,所以HiiDatabaseDxe.inf这个模块并不仅仅是字体的基础,而是UEFI的人机接口(Human Interface Infrastructure)的基础模块。

结构体组织

下图是HII数据库基础中字体相关的结构体:

【UEFI实战】UEFI图形显示(字符输出),uefi

从图中可以看出,字体分为两种,一种是普通版本另一种是简化版本,本节将分别介绍。

最左边的结构体HII_DATABASE_PRIVATE_DATA是HII数据库模块的基础结构体,对应字体部分:

HII_DATABASE_PRIVATE_DATA  mPrivate = {
  // 前略
  LIST_ENTRY                             DatabaseList;	// 所有HII资源都放在这个列表中,其中就包括字体和简单字体
  // 前略
  {
    HiiStringToImage,
    HiiStringIdToImage,
    HiiGetGlyph,
    HiiGetFontInfo
  },
  // 中略
  LIST_ENTRY                             FontInfoList; // global font info list
  // 后略
};

其中包含有一个Protocol,对应的是EFI_HII_FONT_PROTOCOL,其接口:

///
/// The protocol provides the service to retrieve the font informations.
///
struct _EFI_HII_FONT_PROTOCOL {
  EFI_HII_STRING_TO_IMAGE       StringToImage;
  EFI_HII_STRING_ID_TO_IMAGE    StringIdToImage;
  EFI_HII_GET_GLYPH             GetGlyph;
  EFI_HII_GET_FONT_INFO         GetFontInfo;
};

这个将在后面介绍。

还包含一个数据库列表DatabaseList,它包含了所有HII的资源,其中就包括注册了的字体部分内容。以及一个字体信息列表FontInfoList,指向额外的结构体HII_GLOBAL_FONT_INFO,它包含了普通字体需要使用到的数据,而简单字体并不需要这部分。因为是一个列表,所以表示UEFI可以支持多种字体信息。

HII_GLOBAL_FONT_INFO结构体如下:

typedef struct _HII_GLOBAL_FONT_INFO {
  UINTN                        Signature;
  LIST_ENTRY                   Entry;
  HII_FONT_PACKAGE_INSTANCE    *FontPackage;
  UINTN                        FontInfoSize;
  EFI_FONT_INFO                *FontInfo;
} HII_GLOBAL_FONT_INFO;

重要的是后面的三个成员,表示两类信息,FontPackageFontInfo。其中包含的字体和字形的所有信息。关于字体(Font)和字形(Glyph)需要特别说明:

  • 字体是所有字符的整体表现形式,比如楷体、宋体等,表示的是一个整体的概念。
  • 字形是每个字符的样子,会真正涉及到UEFI下如何绘制一个字符。
  • 所有的字形组成了一种字体样式。

下面对字体信息相关的结构体做简单的介绍。首先是描述字体本身的结构体:

typedef struct {
  EFI_HII_FONT_STYLE    FontStyle;  ///< 一个枚举值,表示的是粗体、斜体等
  UINT16                FontSize;   ///< character cell height in pixels
  CHAR16                FontName[1];///< 字体名,这个在文本编辑器里面更容易看到
} EFI_FONT_INFO;

当前支持的字体类型(对应FontStyle):

//
// Value for font style
//
#define EFI_HII_FONT_STYLE_NORMAL     0x00000000
#define EFI_HII_FONT_STYLE_BOLD       0x00000001
#define EFI_HII_FONT_STYLE_ITALIC     0x00000002
#define EFI_HII_FONT_STYLE_EMBOSS     0x00010000
#define EFI_HII_FONT_STYLE_OUTLINE    0x00020000
#define EFI_HII_FONT_STYLE_SHADOW     0x00040000
#define EFI_HII_FONT_STYLE_UNDERLINE  0x00080000
#define EFI_HII_FONT_STYLE_DBL_UNDER  0x00100000

然后是单个字形的结构体:

typedef struct _HII_GLYPH_INFO {
  UINTN                 Signature;
  LIST_ENTRY            Entry;
  CHAR16                CharId;
  EFI_HII_GLYPH_INFO    Cell;
} HII_GLYPH_INFO;

这里有一个列表Entry是因为字形本来就需要有很多个,CharId表示的是字符编码值,通过一个CHAR16理论上能够覆盖所有常用语言的字符,最后的Cell结构体如下:

typedef struct _EFI_HII_GLYPH_INFO {
  UINT16    Width;
  UINT16    Height;
  INT16     OffsetX;
  INT16     OffsetY;
  INT16     AdvanceX;
} EFI_HII_GLYPH_INFO;

这些值与字符的对应关系图如下所示:

【UEFI实战】UEFI图形显示(字符输出),uefi

剩下的结构体EFI_HII_FONT_PACKAGE_HDRHII_FONT_PACKAGE_INSTANCE主要也是上述字体和字形结构体的组织形式的描述。

需要注意,到这里为止仅仅是描述了字体和字形相关的结构,并没有涉及到真实地描述从字符到EFI_GRAPHICS_OUTPUT_PROTOCOL能够显示的图形之间的数据转换模型,这部分内容隐藏在前面的某个结构体成员中,主要是GlyphBlock,作为结构体成员它只是一个指针,通过它我们可以找到某个字符对应的图像显示数据,所以对于它的设计是比较重要的,因为我们需要快速地找到字符对应的图像。

实际上由于使用的限制,字体和字形的应用在UEFI下并不是很重要,UEFI只要能够清楚地表示字符就可以了,为此就有了简化版本的字体,它的结构相当简单,重要的是下面的结构体:

///
/// A simplified font package consists of a font header
/// followed by a series of glyph structures.
///
typedef struct _EFI_HII_SIMPLE_FONT_PACKAGE_HDR {
  EFI_HII_PACKAGE_HEADER    Header;
  UINT16                    NumberOfNarrowGlyphs;
  UINT16                    NumberOfWideGlyphs;
  // EFI_NARROW_GLYPH       NarrowGlyphs[];
  // EFI_WIDE_GLYPH         WideGlyphs[];
} EFI_HII_SIMPLE_FONT_PACKAGE_HDR;

虽然结构体中包含了HDR的字样,但是从上面的代码中也可以看出来,它之后直接就接了所有的数据,通过这些数据可以直接将字符转换成可输出的图形。后续的数据包含两个部分,分别对应窄体字符和宽体字符,实际就是对应8x19和16x19两种形式,以窄体为例,一个字符对应到的数据如下:

typedef struct {
  CHAR16                 UnicodeWeight;
  UINT8                  Attributes;
  UINT8                  GlyphCol1[EFI_GLYPH_HEIGHT];	// EFI_GLYPH_HEIGHT = 19
} EFI_NARROW_GLYPH;

UnicodeWeight对应到字符的计算机表示,Attributes表示字符的属性,目前支持的值:

///
/// Contents of EFI_NARROW_GLYPH.Attributes.
///@{
#define EFI_GLYPH_NON_SPACING  0x01
#define EFI_GLYPH_WIDE         0x02
#define EFI_GLYPH_HEIGHT       19
#define EFI_GLYPH_WIDTH        8
///@}

GlyphCol1实际上是一个位图,每一个比特位表示了是否需要绘制该像素。在【UEFI实战】UEFI图形显示(从像素到字符)的代码示例中,使用了一个二维数组来表示是否需要绘制对应像素:

  UINT8 BltIndex[NARROW_HEIGHT * NARROW_WIDTH] = {
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 1, 0, 0, 0, 0,
    0, 0, 1, 1, 1, 0, 0, 0,
    0, 1, 1, 0, 1, 1, 0, 0,
    1, 1, 0, 0, 0, 1, 1, 0,
    1, 1, 0, 0, 0, 1, 1, 0,
    1, 1, 0, 0, 0, 1, 1, 0,
    1, 1, 0, 0, 0, 1, 1, 0,
    1, 1, 1, 1, 1, 1, 1, 0,
    1, 1, 0, 0, 0, 1, 1, 0,
    1, 1, 0, 0, 0, 1, 1, 0,
    1, 1, 0, 0, 0, 1, 1, 0,
    1, 1, 0, 0, 0, 1, 1, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0
  };

显然这里的方式比上面的代码更加的简单和紧凑。

开源的EDK代码就是使用了这种简化的字体形式来表示字符。

字符到字形的表示

注册字符到字形的描述数据,涉及到HiiDataBase的一个接口:

///
/// Database manager for HII-related data structures.
///
struct _EFI_HII_DATABASE_PROTOCOL {
  EFI_HII_DATABASE_NEW_PACK             NewPackageList;
  // 后略
};

这里不写它的函数原型,而是直接关注对于字体来说其实现中最重要的的部分,其调用流程如下:

字体注册数据的位置在前面介绍过的GraphicsConsoleDxe模块中:

  //
  // Add 4 bytes to the header for entire length for HiiAddPackages use only.
  //
  //    +--------------------------------+ <-- Package
  //    |                                |
  //    |    PackageLength(4 bytes)      |
  //    |                                |
  //    |--------------------------------| <-- SimplifiedFont
  //    |                                |
  //    |EFI_HII_SIMPLE_FONT_PACKAGE_HDR |
  //    |                                |
  //    |--------------------------------| <-- Location
  //    |                                |
  //    |     gUsStdNarrowGlyphData      |
  //    |                                |
  //    +--------------------------------+

  PackageLength = sizeof (EFI_HII_SIMPLE_FONT_PACKAGE_HDR) + mNarrowFontSize + 4;
  Package       = AllocateZeroPool (PackageLength);
  ASSERT (Package != NULL);

  WriteUnaligned32 ((UINT32 *)Package, PackageLength);
  SimplifiedFont                       = (EFI_HII_SIMPLE_FONT_PACKAGE_HDR *)(Package + 4);
  SimplifiedFont->Header.Length        = (UINT32)(PackageLength - 4);
  SimplifiedFont->Header.Type          = EFI_HII_PACKAGE_SIMPLE_FONTS;
  SimplifiedFont->NumberOfNarrowGlyphs = (UINT16)(mNarrowFontSize / sizeof (EFI_NARROW_GLYPH));

  Location = (UINT8 *)(&SimplifiedFont->NumberOfWideGlyphs + 1);
  CopyMem (Location, gUsStdNarrowGlyphData, mNarrowFontSize);

  //
  // Add this simplified font package to a package list then install it.
  //
  mHiiHandle = HiiAddPackages (
                 &mFontPackageListGuid,
                 NULL,
                 Package,
                 NULL
                 );
  ASSERT (mHiiHandle != NULL);
  FreePool (Package);

具体的数据全部存放在数组gUsStdNarrowGlyphData中,它位于一个独立的文件edk2\MdeModulePkg\Universal\Console\GraphicsConsoleDxe\LaffStd.c中:

EFI_NARROW_GLYPH  gUsStdNarrowGlyphData[] = {
  //
  // Unicode glyphs from 0x20 to 0x7e are the same as ASCII characters 0x20 to 0x7e
  //
  { 0x0020,                                     0x00, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
  },
  { 0x0021,                                     0x00, { 0x00, 0x00, 0x00, 0x18, 0x3C, 0x3C, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00 }
  },
  // 后面还有很多的数据

实际上因为英文只需要支持ASCII就可以了,再加上一些特定的图形,所以最终的数据也不会太多。

EFI_HII_FONT_PROTOCOL

下面介绍字体的操作函数,对应的就是EFI_HII_FONT_PROTOCOL,其形式如下:

///
/// The protocol provides the service to retrieve the font informations.
///
struct _EFI_HII_FONT_PROTOCOL {
  EFI_HII_STRING_TO_IMAGE       StringToImage;
  EFI_HII_STRING_ID_TO_IMAGE    StringIdToImage;
  EFI_HII_GET_GLYPH             GetGlyph;
  EFI_HII_GET_FONT_INFO         GetFontInfo;
};

前面两个都是直接的显示函数,不同的是StringToImage直接输出字符串,而StringIdToImage根据uni文件创建的数据,通过StringId来找到字符串并输出。后两个函数则获取字体和字形的信息。【UEFI实战】UEFI图形显示(从像素到字符)的例子中,通过手动构建字形的方式写出了一个A,而这里就可以通过GetGlyph()来得到字形并输出,不再需要手动构建,下面是一个示例:

VOID
ShowB (
  IN  EFI_HII_FONT_PROTOCOL         *HiiFont,
  IN  EFI_GRAPHICS_OUTPUT_PROTOCOL  *Gop
  )
{
  EFI_STATUS        Status = EFI_ABORTED;
  EFI_IMAGE_OUTPUT  *Blt = NULL;

  Status = HiiFont->GetGlyph (
                      HiiFont,
                      L'B',
                      NULL,
                      &Blt,
                      NULL
                      );
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "[%a][%d] Failed. - %r\n", __FUNCTION__, __LINE__, Status));
  } else {
    Gop->Blt (
          Gop,
          Blt->Image.Bitmap,
          EfiBltBufferToVideo,
          0,
          0,
          0,
          0,
          Blt->Width,
          Blt->Height,
          0
          );
  }
}

输出的结果:

【UEFI实战】UEFI图形显示(字符输出),uefi

汉字输出

前面介绍了如何输出英文,这里简单说明如何在UEFI下使用汉字。对于该问题,其实原理本身是比较简单的,输出英文使用的是窄体字,即一个8x19的像素,而对于汉字显然是不够的,所以UEFI规范中还定义了宽体字:

///
/// The EFI_WIDE_GLYPH has a preferred dimension (w x h) of 16 x 19 pixels, which is large enough
/// to accommodate logographic characters.
///
typedef struct {
  ///
  /// The Unicode representation of the glyph. The term weight is the
  /// technical term for a character code.
  ///
  CHAR16    UnicodeWeight;
  ///
  /// The data element containing the glyph definitions.
  ///
  UINT8     Attributes;
  ///
  /// The column major glyph representation of the character. Bits
  /// with values of one indicate that the corresponding pixel is to be
  /// on when normally displayed; those with zero are off.
  ///
  UINT8     GlyphCol1[EFI_GLYPH_HEIGHT];
  ///
  /// The column major glyph representation of the character. Bits
  /// with values of one indicate that the corresponding pixel is to be
  /// on when normally displayed; those with zero are off.
  ///
  UINT8     GlyphCol2[EFI_GLYPH_HEIGHT];
  ///
  /// Ensures that sizeof (EFI_WIDE_GLYPH) is twice the
  /// sizeof (EFI_NARROW_GLYPH). The contents of Pad must
  /// be zero.
  ///
  UINT8     Pad[3];
} EFI_WIDE_GLYPH;

相比于窄体字,重点在于通过两个8x19的像素,每一个输出半个汉字,最终就组成了完整的汉字。而之后的重点就是一个汉字的像素该如何构建,这个在uefi-programming/book/GUIbasics/font/SimpleFont/createdata.html at master · zhenghuadai/uefi-programming · GitHub已经给出了工具,可以创建完整的EFI_WIDE_GLYPH数组来表示所有的汉字(由于很多字体是有版权的,所以使用的时候需要注意,最好自己参照免费的字体构造),这里直接使用该工具得到数组,然后再使用跟注册窄体字一样的方式来注册汉字,对应的代码:

  //
  // Reference:
  // edk2\MdeModulePkg\Universal\Console\GraphicsConsoleDxe\GraphicsConsole.c
  //
  PackageLength = sizeof (EFI_HII_SIMPLE_FONT_PACKAGE_HDR) + mWideFontSize + 4;
  Package       = AllocateZeroPool (PackageLength);
  if (NULL == Package) {
    DEBUG ((EFI_D_ERROR, "[%a][%d] Out of memory\n", __FUNCTION__, __LINE__));
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // Without this code, system will hang.
  //
  WriteUnaligned32 ((UINT32 *)Package, PackageLength);

  SimplifiedFont                       = (EFI_HII_SIMPLE_FONT_PACKAGE_HDR *)(Package + 4);
  SimplifiedFont->Header.Length        = (UINT32)(PackageLength - 4);
  SimplifiedFont->Header.Type          = EFI_HII_PACKAGE_SIMPLE_FONTS;
  SimplifiedFont->NumberOfNarrowGlyphs = 0;
  SimplifiedFont->NumberOfWideGlyphs = (UINT16) (mWideFontSize / sizeof (EFI_WIDE_GLYPH));

  Location = (UINT8 *)(&SimplifiedFont->NumberOfWideGlyphs + 1);
  CopyMem (Location, gWideGlyphData, mWideFontSize);

  mHiiHandle = HiiAddPackages (
                &mFontPackageListGuid,
                NULL,
                Package,
                NULL
                );
  if (NULL == mHiiHandle) {
    DEBUG ((EFI_D_ERROR, "[%a][%d] NULL == mHiiHandle\n", __FUNCTION__, __LINE__));
    Status = EFI_NOT_READY;
  } else {
    DEBUG ((EFI_D_ERROR, "HanFont added\n"));
  }

  FreePool (Package);

gWideGlyphData中存放了所有的汉字字形表示,由于数据太多这里不再列出,只要注册了之后就可以直接使用了,下面是一个代码示例:

Print (L"*********************** 图形和字体测试 ***********************\r\n");

得到的结果:

【UEFI实战】UEFI图形显示(字符输出),uefi

不过需要注意编译的时候要保证对应的c文件使用GBK(或者其它汉字的编码格式)的编码格式,否则会编译失败:文章来源地址https://www.toymoban.com/news/detail-544151.html

SearchString: Error while processing file

到了这里,关于【UEFI实战】UEFI图形显示(字符输出)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 中柏 EZpad 8 Windows 平板电脑 |驱动备份|系统优化|不能调节显示器亮度|进入Bios/uefi 快捷键 |蓝牙扫描不到设备|CPU占用过高解决方案

              之前一直使用的这个平板,但是有个很严重的问题就是性能实在太差了,每次都是CPU百分百,Mem 百分之50% 这个结果真的很意外, 但是又无可奈何,后面花时间做了一些优化包括 进程限制  启动优化  内存页面优化  但结果都不尽人意,一启动程序就卡顿 ..........  中间又

    2024年02月09日
    浏览(125)
  • 高通 UEFI:ABL(一)

    高通平台下的UEFI由XBL+ABL组成,主要完成各种客制化的需求实现,例如通过拉特定的gpio进入fastboot/recovery模式,读取ufs寿命,LCD兼容框架的实现等,想要实现客制化首先要搞明白源码种的框架组成,这篇文章先剖析一下abl阶段主要做了什么事情。 要分析abl框架,首先我们需要

    2024年02月09日
    浏览(41)
  • uefi安全启动

    参考博客:UEFI安全启动 - 知乎 (zhihu.com) UEFI安全引导(Secure Boot)的核心职能就是利用数字签名来确认EFI驱动程序或者应用程序是否是受信任的。在简要地介绍了数字签名的概念(这是安全引导的基础)之后,我们重点介绍UEFI 安全引导是如何利用数字签名以及其他的加密方式

    2024年02月12日
    浏览(33)
  • legacy启动和UEFI启动

    legacy启动是指传统的BIOS启动,和MBR分区模式相互依存,可以进行MBR分区系统的安装,支持所有Windows系统的安装,兼容性较强。Legacy作为传统的引导模式,Legacy模式支持磁盘分区为MBR结构,它能够引导32位系统也可以引导64位系统。 legacy启动意思是传统的BIOS启动,和MBR分区模

    2024年02月07日
    浏览(46)
  • UEFI统一可扩展固件接口

    统一可扩展固件接口(英语:Unified Extensible Firmware Interface,缩写UEFI)是一种个人电脑系统规格,用来定义操作系统与系统固件之间的软件界面,作为BIOS的替代方案。可扩展固件接口负责加电自检(POST)、联系操作系统以及提供连接操作系统与硬件的接口。 UEFI的前身是Int

    2024年02月04日
    浏览(31)
  • VM虚拟机 运行UEFI程序

    需要自行安装一个VM虚拟机,准备一个FAT32的U盘(U盘转格式时,最好用空U盘),U盘里面放你自己编译后生成的.efi文件。 1.新建虚拟机,点击“文件-》新建虚拟机” 大部分地方直接默认就行,这里只关注两个地方: 操作系统选Win10: 固件类型选择UEFI: 2.选中新建好的虚拟机

    2023年04月08日
    浏览(39)
  • 开宗明义—UEFI介绍 (二)

    上一篇介绍了UEFI的发展历史,以及对UEFI在ARM嵌入式领域的生态状况做了简单的调研。本篇旨在对UEFI规范和PI规范的内容以及二者之间的关系做一个简单的梳理。 本篇参考内容主要来源于以下3方面: (1) 微信公众号“ Wolf UEFI社区 ”系列文章。 (2) 《UEFI原理与编程》—戴正华

    2024年02月05日
    浏览(38)
  • BIOS MBR UEFI GPT详解

    名词解释 1、启动方式: BIOS:Basic Input Output System,中文名称\\\"基本输入输出系统\\\",也叫 Legacy BIOS。 UEFI:Unified Extensible Firmware Interface,中文名称\\\"统一的可扩展固件接口\\\"。 2、硬盘分区: MBR分区:Master Boot Record,中文名称\\\"主引导记录\\\"。 GPT分区:GUID Partition Table,中文名称

    2024年02月08日
    浏览(39)
  • 【UEFI基础】EDK网络框架(ARP)

    从这里开始涉及到的网络协议都是比较通用的了,在一般的TCP/IP四层模型中都能够看到这些内容,不过这里主要介绍的还是其在BIOS下的实现,但是在此之前还是需要先说明ARP的作用。 ARP的全称是 A ddress R esolution P rotocol,它是一种解决地址问题的协议。以目标IP为线索,用来

    2024年01月20日
    浏览(42)
  • 电脑磁盘布局不受UEFI固件支持

    我们在安装Windows10系统的过程中,难免会出现一些无法安装的情况,但其实都是有一定的原因,找到原因之后,才可以对症下药。近期有用户称自己在安装Win10系统的时候,提示了“无法安装Windows10,因为这台电脑磁盘布局不受UEFI固件”的问题,那么这是什么原因呢?下面

    2024年02月12日
    浏览(51)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包