UEFI News and Commentary

Tuesday, September 04, 2012

HOW TO: Display an Image Using UEFI

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



24 comments:

pdplaza said...

present you are supporting and displaying .bmp images. In future are you going to support all image formats?

Shannon Lewis said...

I have no plans currently to support any other image formats.

lrwr said...

I've been trying to make this work for several hours now, but I can't get it to work.
Probably I'm missing someting.

The problem is a warning telling me 'AllocatePool' is undefined.
I searched through the EDK2 folder and found it defined in EfiApi.h
When including efiapi.h I'm getting errors concerning the #include EFI_PROTOCOL_DEFINITION lines.

lrwr said...

Sorry für doubble posting but I finally got it to work by replaceing

*GopBlt = AllocatePool (*GopBltSize);

with

(gBS->AllocatePool) (EfiRuntimeServicesData, *GopBltSize, GopBlt);

as also done in the EdkIIGlueLib.
Simply including the lib just threw more errors about unresolved externals and I didn't want to mess with all my include paths.

I also had to put

BltSize=0;
Blt=NULL;

before

Status = ConvertBmpToGopBlt (

to make use of the automatic allocation inside the function.

Please note I'm still interested in how you got arround this problem :)

Tim Lewis said...

AllocatePool() is a part of the MemoryAllocationLib. So you would have to make sure that MdePkg/MdePkg.dec is in your INF's [Packages] section, MemoryAllocationLib is in your INF's [LibraryClasses] section and add #include in your C file.

Shannon Lewis said...

lrwr, thank you for pointing this out! I forgot to mention it in the blog post.

To avoid the problem that you were having with AllocatePool(), replace the line
*GopBlt = AllocatePool(*GopBltSize);
with
*GopBlt = malloc (*GopBltSize);
You will also want to replace
FreePool (*GopBlt);
with
free (*GopBlt);

That should fix your problem. I will edit the article so that other people don't encounter the same issue.

Hank said...

Hi, I would like to see your code of this article. Can you send this code to me via email (hmaisd15@gmail.com)? Thanks for everything. Hank

Tim Lewis said...

Hank -- If Shannon hasn't already sent it, she'll get it to you in the next day or so.

Hank said...

Unfortunately, not yet. Please send:)

Sharif said...

Hi, would you please send me your code? I am getting lots of error to implement it. I am beginner in UEFI programming.

Help would be appreciated!

My email : srabbis@hotmail

Thanks.

Roger said...

Hi May I have your code

My mail:tzerulin@gmail.com

Thanks

sheldoor said...

Hi Tim,may I have your codes,I'm just a newcomer in UEFI, your articals and codes will help me a lots.tks~,
my e-mail:1102878561@qq.com

Chelsea FC Fan said...

hey. I'm also trying to learn UEFI and was wondering if you can kindly forward the code. Thanks!
ramdass.kevin@gmail.com

Tim Lewis said...

We have posted all of the code now on SourceForge at http://sourceforge.net/projects/syslibforuefi/

Chelsea FC Fan said...

Thanks. Is there anyway to shrink or stretch the bmp image?

Tim Lewis said...

No, UEFI doesn't support stretching directly through its APIs. Don't have a plan to implement it in our immediate blog plans, sorry.

Michał Ciesielski said...

Hi,

I,m just begining UEFI programming and i found this article. It's very helpful for me, but can You send me source code?
My mail: ciesielski.michal85@gmail.com

Thanks a lot

Tim Lewis said...

All of the source code is now found on SourceForge. http://sourceforge.net/p/syslibforuefi/wiki/Home/

Michał Ciesielski said...

Thanks a lot for your help!

baky said...

Hi! I am begining UEFI programming.
so, may i have your code?
thanks.

study.yoyo@gmail.com

Tim Lewis said...

All of the source code is now available on http://syslibforuefi.sourceforge.net/

Akshay Paranjape said...

When I call
gBS->HandleProtocol (gST->ConsoleOutHandle, &gEfiGraphicsOutputProtocolGuid, (VOID **) &GraphicsOutput);

if (EFI_ERROR (Status)) {
return EFI_UNSUPPORTED;
}

it enters if condition always i dont understand. do we need to configure something?

游昭霖 said...

Akshay, I encountered the same problem.
Tim, could you help?

小高 said...


Hello

would you please send me your code?
I am interest in showing picture using GOP,

My email : namechi.kao@gmail.com

Thanks.