The next project that I'm working on involves displaying an image. This means that I have to rely a lot more on UEFI functions rather than simply the basic C functions. Because I'm still learning about how UEFI works and am not completely familiar it yet, I decided to go looking for something that already did something similar and pull out the bits that I need.
I knew that NT32 displayed the Tiano logo, so I decided to go find the function that did that and base my own image-displaying function off of theirs. The function is EnableQuietBoot() located in BdsConsole.c
[1]. There's a lot of unnecessary stuff in that function that we don't need to display an image, so we can definitely cut it out when we piece together our own function. I cut out all the stuff that dealt with UGA, since I only need the Graphics Output Protocol to display the image. I also got rid of anything to do with badging and deciding where on the screen to display the image. All I really needed was something to get the protocol, get the image from the file, convert it to a format that can be displayed by the GOP, and display it. So after determining what I did and didn't need, I went ahead and pieced together my own function.

The next step was to make sure all the proper files are included so that all the functions and global variables used to display this image can actually be accessed. One of the functions, ConvertBmpToGopBlt(), was already in BdsConsole.c, so I simply copied and pasted the function into my new file. The only thing that needs to be changed in that function, is that instead of calling AllocatePool(), call malloc(), and instead of calling FreePool(), call free(). For the most part, it was simple enough to find where everything was defined by searching through the EDK2 folder, and I went ahead and included them at the top of the file. My programming background is mainly in Java, so I'm still figuring out how #include statements work. It took me a while to realize that there were no forward references in C, so I had to switch the order of the statements before it would work.
It also was difficult to figure out how INF files worked. I had included DxeServicesLib.h in my file, but it still was unable to recognize the function whose prototype was located in that .H file. In order for my file to be able to access the code for that function and actually call it, I had to make sure the code for that function is also pulled into the project. The way to do that is through the INF files. In my project's .INF file, there's a list of "Library Classes," which lists other INFs that have to be acknowledged when the project is built. To make sure the code that defines the functions in DxeServicesLib.h was built, I put DxeServicesLib into that list. Below is the body for the .INF file. Click the picture to enlarge it.

Finally, we have to put together main(). This is our entry point for the application, so it's fairly simple. All it does is call to the function we put together to display the image, but the function has an EFI_GUID as a parameter (the GUID for the image). We could use uuidgen to produce one for us, but since NT32 already displays an image, we simply have to find the GUID for that image. It is located in Logo.inf
[2]. So, since we have to pass an EFI_GUID into the function, we take that long GUID and put it properly into a struct. The declaration goes outside the function so we have a global variable.
STATIC
EFI_GUID
gImageFileGuid = {0x7bb28b99, 0x61bb, 0x11d5, {0x9a, 0x5d, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d}};
Then the only line inside main() is as follows:
DisplayImage(&gImageFileGuid);
So let's walk through the code that I have that actually displays the image:
First, clear the screen. Without it, the image will simply be drawn over whatever has already been displayed (which, in most cases, is text).
gST->ConOut-> Reset(gST->ConOut, FALSE);
Check to see if the Graphics Output Protocol is supported. If not, return an error code.
Status = gBS->HandleProtocol (gST->ConsoleOutHandle, &gEfiGraphicsOutputProtocolGuid, (VOID **) &GraphicsOutput);
if (EFI_ERROR (Status)) {
return EFI_UNSUPPORTED;
}
Get the image information from the image file. If an error occurs, return error code.
Status = GetSectionFromAnyFv (LogoFile, EFI_SECTION_RAW, 0, (VOID **) &ImageData, &ImageSize);
if (EFI_ERROR (Status)) {
return EFI_UNSUPPORTED;
}
Convert the image to a format that GOP can work with (GOP Blt). Right now, .BMP files are the only supported image files.
Status = ConvertBmpToGopBlt (
ImageData,
ImageSize,
(VOID **) &Blt,
&BltSize,
&Height,
&Width
);
if (EFI_ERROR (Status)) {
free (ImageData);
}
Set the coordinates for where the image is going to be displayed. The coordinates you give will be the location of the upper left-hand corner of the image. The upper left-hand corner of the window is (0, 0).
CoordinateX = (GraphicsOutput->Mode->Info->HorizontalResolution / 2) - (Width / 2);
CoordinateY = (GraphicsOutput->Mode->Info->VerticalResolution / 2) - (Height / 2);
Finally, call Blt() to actually display the image. We've got quite a few parameters here, so let's go through them. First is the instance of the GRAPHICS_OUTPUT_PROTOCOL that's calling the function. Next is the image data that we have to transfer to the screen. We filled this buffer when we called ConvertBmpToGopBlt(). Now, the third parameter specifies which operation to perform in order to copy the image to the screen. The operation selected in this parameter affects the values of other parameters as well. Details of the different functions are in the UEFI spec. The next two parameters are the X and Y coordinates for the source for the operation specified in the previous parameter. After that come the coordinates for where on the screen the image will be displayed. The last three are data about the image itself. Width and Height are the width and height, respectively, of the image in pixels. Finally, the last parameter specifies the number of bytes in a row of the image, by taking the number of pixels in a row (the width) and multiplying it by the size of a pixel.
GraphicsOutput->Blt (
GraphicsOutput,
Blt,
EfiBltBufferToVideo,
0,
0,
(UINTN) CoordinateX,
(UINTN) CoordinateY,
Width,
Height,
Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)
);
return EFI_SUCCESS;
The next step in this project is to get the image to move around according to keyboard instructions.
And, of course, if anyone is interested in seeing the code for this project, let me know, and I'll get it sent to you.
[1] IntelFrameworkModulePkg\Library\GenericBdsLib\BdsConsole.c
[2] MdeModulePkg\Logo\Logo.inf