前面介绍了如何截图,如果把这个过程反过来,先是读取BMP文件到内存,然后重新排列RGB,再用Blt输出到Video上就是显示BMP的过程。
根据这个原理编写程序如下:
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/ShellCEntryLib.h>
#include <stdio.h>
#include <stdlib.h>
#include <wchar.h>
#include <time.h>
#include <Protocol/EfiShell.h>
#include <Library/ShellLib.h>
#include <Protocol/SimpleFileSystem.h>
#include <Protocol/BlockIo.h>
#include <Library/DevicePathLib.h>
#include <Library/HandleParsingLib.h>
#include <Library/SortLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/BaseMemoryLib.h>
#include <Protocol/LoadedImage.h>
#define MAX_FILE_SIZE (1024*1024*1024)
extern EFI_BOOT_SERVICES *gBS;
extern EFI_SYSTEM_TABLE *gST;
extern EFI_RUNTIME_SERVICES *gRT;
extern EFI_SHELL_ENVIRONMENT2 *mEfiShellEnvironment2;
extern EFI_HANDLE gImageHandle;
static EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
static EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput = NULL;
#pragma pack(1)
//Copied from C\MdePkg\Include\Protocol\UgaDraw.h
typedef struct {
UINT8 Blue;
UINT8 Green;
UINT8 Red;
UINT8 Reserved;
} EFI_UGA_PIXEL;
/* This should be compatible with EFI_UGA_PIXEL */
typedef struct {
UINT8 b, g, r, a;
} EG_PIXEL;
typedef struct {
CHAR8 CharB;
CHAR8 CharM;
UINT32 Size;
UINT16 Reserved[2];
UINT32 ImageOffset;
UINT32 HeaderSize;
UINT32 PixelWidth;
UINT32 PixelHeight;
UINT16 Planes; // Must be 1
UINT16 BitPerPixel; // 1, 4, 8, or 24
UINT32 CompressionType;
UINT32 ImageSize; // Compressed image size in bytes
UINT32 XPixelsPerMeter;
UINT32 YPixelsPerMeter;
UINT32 NumberOfColors;
UINT32 ImportantColors;
} BMP_IMAGE_HEADER;
typedef struct {
UINTN Width;
UINTN Height;
BOOLEAN HasAlpha;
EG_PIXEL *PixelData;
} EG_IMAGE;
#pragma pack()
VOID egFreeImage(IN EG_IMAGE *Image)
{
if (Image != NULL) {
if (Image->PixelData != NULL)
FreePool(Image->PixelData);
FreePool(Image);
}
}
static EFI_GUID ESPGuid = { 0xc12a7328, 0xf81f, 0x11d2,
{ 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b } };
static EFI_STATUS egFindESP(OUT EFI_FILE_PROTOCOL *RootDir)
{
EFI_STATUS Status;
return Status;
}
//
// Basic image handling
//
EG_IMAGE * egCreateImage(IN UINTN Width, IN UINTN Height, IN BOOLEAN HasAlpha)
{
EG_IMAGE *NewImage;
NewImage = (EG_IMAGE *) AllocatePool(sizeof(EG_IMAGE));
if (NewImage == NULL)
return NULL;
NewImage->PixelData = (EG_PIXEL *) AllocatePool(
Width * Height * sizeof(EG_PIXEL));
if (NewImage->PixelData == NULL) {
FreePool(NewImage);
return NULL;
}
NewImage->Width = Width;
NewImage->Height = Height;
NewImage->HasAlpha = HasAlpha;
return NewImage;
}
EFI_STATUS LoadFile( IN CHAR16 *FileName,
IN UINT8 **FileData,
IN UINTN *FileDataLength)
{
EFI_STATUS Status;
EFI_FILE_HANDLE FileHandle;
EFI_FILE_PROTOCOL *Root;
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFileSystem;
EFI_FILE_INFO *FileInfo = NULL;
Status = gBS->LocateProtocol(
&gEfiSimpleFileSystemProtocolGuid,
NULL,
(VOID **)&SimpleFileSystem);
if (EFI_ERROR(Status)) {
Print(L"Cannot find EFI_SIMPLE_FILE_SYSTEM_PROTOCOL \r\n");
return Status;
}
Status = SimpleFileSystem->OpenVolume(SimpleFileSystem, &Root);
if (EFI_ERROR(Status)) {
Print(L"OpenVolume error \r\n");
return Status;
}
Status = Root->Open(
Root,
&FileHandle,
FileName,
EFI_FILE_MODE_READ ,
0);
if (EFI_ERROR(Status))
{
Print(L"Error Open NULL\n");
return Status;
}
FileInfo = ShellGetFileInfo( (SHELL_FILE_HANDLE)FileHandle);
Print(L"Filesize [%ld] bytes\n",FileInfo-> FileSize);
*FileDataLength=(UINTN) FileInfo->FileSize;
*FileData = AllocatePool((UINTN) FileInfo->FileSize);
Status = FileHandle->Read(FileHandle, FileDataLength, *FileData );
if (EFI_ERROR(Status)) {
Print(L"Loading file error! \n");
}
Print(L"Get file size [%d]!\n",*FileDataLength);
FileHandle->Close(FileHandle);
return Status;
}
//
// Load BMP image
//
EG_IMAGE * egDecodeBMP
(
IN UINT8 *FileData,
IN UINTN FileDataLength,
IN BOOLEAN WantAlpha)
{
EG_IMAGE *NewImage;
BMP_IMAGE_HEADER *BmpHeader;
EFI_UGA_PIXEL *BmpColorMap;
UINTN x, y;
UINT8 *ImagePtr;
UINT8 *ImagePtrBase;
UINTN ImageLineOffset;
UINT8 ImageValue=0, AlphaValue;
EG_PIXEL *PixelPtr;
UINTN Index, BitIndex;
// read and check header
if (FileDataLength < sizeof(BMP_IMAGE_HEADER) || FileData == NULL)
return NULL;
BmpHeader = (BMP_IMAGE_HEADER *) FileData;
if (BmpHeader->CharB != 'B' || BmpHeader->CharM != 'M')
return NULL;
if (BmpHeader->CompressionType != 0)
return NULL;
if (BmpHeader->BitPerPixel != 1 && BmpHeader->BitPerPixel != 4 &&
BmpHeader->BitPerPixel != 8 && BmpHeader->BitPerPixel != 24)
return NULL;
// calculate parameters
ImageLineOffset = BmpHeader->PixelWidth;
if (BmpHeader->BitPerPixel == 24)
ImageLineOffset *= 3;
else if (BmpHeader->BitPerPixel == 1)
ImageLineOffset = (ImageLineOffset + 7) >> 3;
else if (BmpHeader->BitPerPixel == 4)
ImageLineOffset = (ImageLineOffset + 1) >> 1;
if ((ImageLineOffset % 4) != 0)
ImageLineOffset = ImageLineOffset + (4 - (ImageLineOffset % 4));
// check bounds
if (BmpHeader->ImageOffset +
ImageLineOffset * BmpHeader->PixelHeight > FileDataLength)
return NULL;
// allocate image structure and buffer
NewImage = egCreateImage(BmpHeader->PixelWidth,
BmpHeader->PixelHeight, WantAlpha);
if (NewImage == NULL)
return NULL;
AlphaValue = WantAlpha ? 255 : 0;
// convert image
BmpColorMap = (EFI_UGA_PIXEL *)(FileData + sizeof(BMP_IMAGE_HEADER));
ImagePtrBase = FileData + BmpHeader->ImageOffset;
for (y = 0; y < BmpHeader->PixelHeight; y++) {
ImagePtr = ImagePtrBase;
ImagePtrBase += ImageLineOffset;
PixelPtr = NewImage->PixelData +
(BmpHeader->PixelHeight - 1 - y) * BmpHeader->PixelWidth;
switch (BmpHeader->BitPerPixel) {
case 1:
for (x = 0; x < BmpHeader->PixelWidth; x++) {
BitIndex = x & 0x07;
if (BitIndex == 0)
ImageValue = *ImagePtr++;
Index = (ImageValue >> (7 - BitIndex)) & 0x01;
PixelPtr->b = BmpColorMap[Index].Blue;
PixelPtr->g = BmpColorMap[Index].Green;
PixelPtr->r = BmpColorMap[Index].Red;
PixelPtr->a = AlphaValue;
PixelPtr++;
}
break;
case 4:
for (x = 0; x <= BmpHeader->PixelWidth - 2; x += 2) {
ImageValue = *ImagePtr++;
Index = ImageValue >> 4;
PixelPtr->b = BmpColorMap[Index].Blue;
PixelPtr->g = BmpColorMap[Index].Green;
PixelPtr->r = BmpColorMap[Index].Red;
PixelPtr->a = AlphaValue;
PixelPtr++;
Index = ImageValue & 0x0f;
PixelPtr->b = BmpColorMap[Index].Blue;
PixelPtr->g = BmpColorMap[Index].Green;
PixelPtr->r = BmpColorMap[Index].Red;
PixelPtr->a = AlphaValue;
PixelPtr++;
}
if (x < BmpHeader->PixelWidth) {
ImageValue = *ImagePtr++;
Index = ImageValue >> 4;
PixelPtr->b = BmpColorMap[Index].Blue;
PixelPtr->g = BmpColorMap[Index].Green;
PixelPtr->r = BmpColorMap[Index].Red;
PixelPtr->a = AlphaValue;
PixelPtr++;
}
break;
case 8:
for (x = 0; x < BmpHeader->PixelWidth; x++) {
Index = *ImagePtr++;
PixelPtr->b = BmpColorMap[Index].Blue;
PixelPtr->g = BmpColorMap[Index].Green;
PixelPtr->r = BmpColorMap[Index].Red;
PixelPtr->a = AlphaValue;
PixelPtr++;
}
break;
case 24:
for (x = 0; x < BmpHeader->PixelWidth; x++) {
PixelPtr->b = *ImagePtr++;
PixelPtr->g = *ImagePtr++;
PixelPtr->r = *ImagePtr++;
PixelPtr->a = AlphaValue;
PixelPtr++;
}
break;
}
}
return NewImage;
}
int
EFIAPI
main (
IN int Argc,
IN char **Argv
)
{
EFI_STATUS Status;
EG_IMAGE *Image;
UINT8 *FileData=NULL;
UINTN FileDataLength;
Status = gBS->LocateProtocol(&GraphicsOutputProtocolGuid,
NULL, (VOID **) &GraphicsOutput);
if (EFI_ERROR(Status)) {
GraphicsOutput = NULL;
Print(L"Loading Graphics_Output_Protocol error!\n");
return EFI_SUCCESS;
}
Status=LoadFile(L"test.bmp",&FileData,&FileDataLength);
Print(L"Get file size [%d]!\n",FileDataLength);
Image=egDecodeBMP(FileData, FileDataLength, FALSE);
Print(L"Image height [%d]:width[%d]",
Image->Height,
Image->Width);
GraphicsOutput->Blt(
GraphicsOutput,
(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) Image->PixelData,
EfiBltBufferToVideo,
0,
0 ,
0x20,
0x0,
Image->Width,
Image->Height, 0);
return EFI_SUCCESS;
}
运行结果:
完整代码例子:
本文同样参考了之前提到的 https://github.com/chengs/UEFI 的代码,再次感谢!














