很早之前的文章介绍过如何在 Shell 下实实现 BMP 的显示【参考1】。从原理上来说就是读取 BMP 文件,然后进行解析,最后按照 BLT 要求的格式重新排列,最终用EfiBltBufferToVideo就可以显示出来。
最近在查看 EDK202008 的代码时,偶然发现了2个新增的函数在BmpSupportLib.h 文件中。从名称上看一个是将 BMP 转化为 BLT要求的GopBlt格式,另外一个是将 GopBlt 转为 BMP 的。
/**
Translate a *.BMP graphics image to a GOP blt buffer. If a NULL Blt buffer
is passed in a GopBlt buffer will be allocated by this routine using
EFI_BOOT_SERVICES.AllocatePool(). If a GopBlt buffer is passed in it will be
used if it is big enough.
@param [in] BmpImage Pointer to BMP file.
@param [in] BmpImageSize Number of bytes in BmpImage.
@param [in, out] GopBlt Buffer containing GOP version of BmpImage.
@param [in, out] GopBltSize Size of GopBlt in bytes.
@param [out] PixelHeight Height of GopBlt/BmpImage in pixels.
@param [out] PixelWidth Width of GopBlt/BmpImage in pixels.
@retval RETURN_SUCCESS GopBlt and GopBltSize are returned.
@retval RETURN_INVALID_PARAMETER BmpImage is NULL.
@retval RETURN_INVALID_PARAMETER GopBlt is NULL.
@retval RETURN_INVALID_PARAMETER GopBltSize is NULL.
@retval RETURN_INVALID_PARAMETER PixelHeight is NULL.
@retval RETURN_INVALID_PARAMETER PixelWidth is NULL.
@retval RETURN_UNSUPPORTED BmpImage is not a valid *.BMP image.
@retval RETURN_BUFFER_TOO_SMALL The passed in GopBlt buffer is not big
enough. The required size is returned in
GopBltSize.
@retval RETURN_OUT_OF_RESOURCES The GopBlt buffer could not be allocated.
**/
RETURN_STATUS
EFIAPI
TranslateBmpToGopBlt (
IN VOID *BmpImage,
IN UINTN BmpImageSize,
IN OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL **GopBlt,
IN OUT UINTN *GopBltSize,
OUT UINTN *PixelHeight,
OUT UINTN *PixelWidth
);
/**
Translate a GOP blt buffer to an uncompressed 24-bit per pixel BMP graphics
image. If a NULL BmpImage is passed in a BmpImage buffer will be allocated by
this routine using EFI_BOOT_SERVICES.AllocatePool(). If a BmpImage buffer is
passed in it will be used if it is big enough.
@param [in] GopBlt Pointer to GOP blt buffer.
@param [in] PixelHeight Height of GopBlt/BmpImage in pixels.
@param [in] PixelWidth Width of GopBlt/BmpImage in pixels.
@param [in, out] BmpImage Buffer containing BMP version of GopBlt.
@param [in, out] BmpImageSize Size of BmpImage in bytes.
@retval RETURN_SUCCESS BmpImage and BmpImageSize are returned.
@retval RETURN_INVALID_PARAMETER GopBlt is NULL.
@retval RETURN_INVALID_PARAMETER BmpImage is NULL.
@retval RETURN_INVALID_PARAMETER BmpImageSize is NULL.
@retval RETURN_UNSUPPORTED GopBlt cannot be converted to a *.BMP image.
@retval RETURN_BUFFER_TOO_SMALL The passed in BmpImage buffer is not big
enough. The required size is returned in
BmpImageSize.
@retval RETURN_OUT_OF_RESOURCES The BmpImage buffer could not be allocated.
**/
RETURN_STATUS
EFIAPI
TranslateGopBltToBmp (
IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *GopBlt,
IN UINT32 PixelHeight,
IN UINT32 PixelWidth,
IN OUT VOID **BmpImage,
IN OUT UINT32 *BmpImageSize
);
为此,编写一个测试程序进行测试,因为编译环境是 EDK201903 其中并没有对应的库,所以直接将上面提到的库从 EDK202008 中提取出来直接使用,同时还有一个SafeIntLib的库:
#include <Uefi.h>
#include <Library/BaseLib.h>
#include <Library/UefiLib.h>
#include <Library/ShellCEntryLib.h>
#include <stdio.h>
#include <stdlib.h>
#include "BmpSupportLib.h"
extern EFI_BOOT_SERVICES *gBS;
static EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
INTN
EFIAPI
main (
IN UINTN Argc,
IN CHAR8 **Argv
)
{
FILE *fp;
long BmpSize;
char *pBmpImage;
UINTN BltSize;
UINTN Height;
UINTN Width;
EFI_STATUS Status;
EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Blt;
EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput;
// Check parameter
if (Argc<2) {
printf("Please input a file name\n");
} else {
// Open file
fp=fopen(Argv[1],"rb");
if (fp==NULL) {
printf("Can't open file [%s]\n",Argv[1]);
return EFI_SUCCESS;
}
// Get file size
fseek(fp,0,SEEK_END);
BmpSize=ftell(fp);
printf("File size is [%d]\n",BmpSize);
// Create a memory for BMP file
pBmpImage=malloc(BmpSize);
if (pBmpImage==NULL) {
printf("Memory allocate error\n");
fclose(fp);
return EFI_SUCCESS;
}
// Load BMP to memory
fseek(fp,0,SEEK_SET);
fread(pBmpImage, sizeof(char), BmpSize, fp);
fclose(fp);
Status = gBS->LocateProtocol(
&GraphicsOutputProtocolGuid,
NULL,
(VOID **) &GraphicsOutput);
if (EFI_ERROR(Status)) {
GraphicsOutput = NULL;
Print(L"Loading Graphics_Output_Protocol error!\n");
return EFI_SUCCESS;
}
// Translate BMP to GopBlt
Blt = NULL;
Width = 0;
Height = 0;
Status = TranslateBmpToGopBlt (
pBmpImage,
BmpSize,
&Blt,
&BltSize,
&Height,
&Width
);
if (EFI_ERROR (Status)) {
Print(L"TranslateBmpToGopBlt error!\n");
return Status;
}
// Show the GopBlt to screen
Status = GraphicsOutput->Blt (
GraphicsOutput,
Blt,
EfiBltBufferToVideo,
0, // Source X
0, // Source Y
0x140, // Destination X
0, // Destination Y
Width,
Height,
Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)
);
if (EFI_ERROR (Status)) {
Print(L"GraphicsOutput->Blt error! [%r]\n",Status);
return Status;
}
free(pBmpImage);
}
return EFI_SUCCESS;
}
测试结果(NT32虚拟机)

源代码和编译后的 X64 代码可以在这里下载:
参考: