前面介绍了 EFI_GRAPHICS_OUTPUT_PROTOCOL 的各种用法,这里介绍一下如何使用这个 Protocol 进行截屏,将屏幕信息保存为 BMP 文件。
原理是:用 GraphicsOutput->Blt 将屏幕信息保存在指定的内存位置,然后加一个 BMP 的文件头,再重新排列一下颜色(BMP是直接颜色BGR排列下来),最后将这块内存保存下来就得到需要的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> 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() // // 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; } // // Save BMP image // VOID egEncodeBMP(IN EG_IMAGE *Image, OUT UINT8 **FileDataReturn, OUT UINTN *FileDataLengthReturn) { BMP_IMAGE_HEADER *BmpHeader; UINT8 *FileData; UINTN FileDataLength; UINT8 *ImagePtr; UINT8 *ImagePtrBase; UINTN ImageLineOffset; EG_PIXEL *PixelPtr; UINTN x, y; ImageLineOffset = Image->Width * 3; if ((ImageLineOffset % 4) != 0) ImageLineOffset = ImageLineOffset + (4 - (ImageLineOffset % 4)); // allocate buffer for file data FileDataLength = sizeof(BMP_IMAGE_HEADER) + Image->Height * ImageLineOffset; FileData = AllocateZeroPool(FileDataLength); if (FileData == NULL) { Print(L"Error allocate %d bytes\n", FileDataLength); *FileDataReturn = NULL; *FileDataLengthReturn = 0; return; } // fill header BmpHeader = (BMP_IMAGE_HEADER *)FileData; BmpHeader->CharB = 'B'; BmpHeader->CharM = 'M'; BmpHeader->Size = FileDataLength; BmpHeader->ImageOffset = sizeof(BMP_IMAGE_HEADER); BmpHeader->HeaderSize = 40; BmpHeader->PixelWidth = Image->Width; BmpHeader->PixelHeight = Image->Height; BmpHeader->Planes = 1; BmpHeader->BitPerPixel = 24; BmpHeader->CompressionType = 0; BmpHeader->XPixelsPerMeter = 0xb13; BmpHeader->YPixelsPerMeter = 0xb13; // fill pixel buffer ImagePtrBase = FileData + BmpHeader->ImageOffset; for (y = 0; y < Image->Height; y++) { ImagePtr = ImagePtrBase; ImagePtrBase += ImageLineOffset; PixelPtr = Image->PixelData + (Image->Height - 1 - y) * Image->Width; for (x = 0; x < Image->Width; x++) { *ImagePtr++ = PixelPtr->b; *ImagePtr++ = PixelPtr->g; *ImagePtr++ = PixelPtr->r; PixelPtr++; } } *FileDataReturn = FileData; *FileDataLengthReturn = FileDataLength; } VOID egFreeImage(IN EG_IMAGE *Image) { if (Image != NULL) { if (Image->PixelData != NULL) FreePool(Image->PixelData); FreePool(Image); } } /** Function opens and returns a file handle to the root directory of a volume. @param DeviceHandle A handle for a device @return A valid file handle or NULL is returned **/ EFI_FILE_HANDLE EfiLibOpenRoot ( IN EFI_HANDLE DeviceHandle ) { EFI_STATUS Status; EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Volume; EFI_FILE_HANDLE File; File = NULL; // // File the file system interface to the device // Status = gBS->HandleProtocol ( DeviceHandle, &gEfiSimpleFileSystemProtocolGuid, (VOID *) &Volume ); // // Open the root directory of the volume // if (!EFI_ERROR (Status)) { Status = Volume->OpenVolume ( Volume, &File ); } // // Done // return EFI_ERROR (Status) ? NULL : File; } 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; } EFI_STATUS egSaveFile(IN CHAR16 *FileName, IN UINT8 *FileData, IN UINTN FileDataLength) { EFI_STATUS Status; EFI_FILE_HANDLE FileHandle; UINTN BufferSize; EFI_FILE_PROTOCOL *Root; EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFileSystem; 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 | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, 0); if (EFI_ERROR(Status)) { Print(L"Error Open NULL\n"); return Status; } BufferSize = FileDataLength; Status = FileHandle->Write(FileHandle, &BufferSize, FileData); FileHandle->Close(FileHandle); return Status; } int EFIAPI main ( IN int Argc, IN char **Argv ) { EFI_STATUS Status; EG_IMAGE *Image; UINT8 *FileData; 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; } // allocate a buffer for the whole screen Image = egCreateImage(GraphicsOutput->Mode->Info->HorizontalResolution, GraphicsOutput->Mode->Info->VerticalResolution, FALSE); if (Image == NULL) { Print(L"Error egCreateImage returned NULL\n"); return EFI_SUCCESS; } // get full screen image if (GraphicsOutput != NULL) { GraphicsOutput->Blt(GraphicsOutput, (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) Image->PixelData, EfiBltVideoToBltBuffer, 0, 0, 0, 0, Image->Width, Image->Height, 0); } // encode as BMP egEncodeBMP(Image, &FileData, &FileDataLength); egFreeImage(Image); if (FileData == NULL) { Print(L"Error egEncodeBMP returned NULL\n"); return EFI_SUCCESS; } // save to file on the ESP Status = egSaveFile(L"screenshot.bmp", FileData, FileDataLength); FreePool(FileData); if (EFI_ERROR(Status)) { Print(L"Error egSaveFile: %x\n", Status); return EFI_SUCCESS; } return EFI_SUCCESS; }
运行结果:
完整代码下载:
特别注意,本文使用的代码来自 https://github.com/chengs/UEFI 再次表示感谢。
参考:
1.http://biosengineer.blogspot.com/2011/09/uefi-screenshot-capture-screen.html UEFI Screenshot (Capture screen)
2.http://kurtqiao.blogspot.com/2013/03/how-to-take-screen-shot-under-uefi-shell.htmlHow to take screen shot under UEFI shell