UEFI News and Commentary

Thursday, September 06, 2012

HOW TO: Display Your Own Image

In this article, I'll talk about how to get your own image to display on the screen.  As much as we love the Tiano logo, sometimes it just doesn't serve our purposes.  This article assumes that you have already followed the directions described in the previous post to display an image.

The first step is to get your image file into a format we can work with.  My previous article only supports .BMP files, so be sure the image you want is saved as such.  If it's a different file type, just open it in Paint or a similar program and save it in the proper format.  To make things simpler, save the image in the same directory as the application.

Next is to create a GUID for our image.  You can use uuidgen from the command line if you like, or in Visual Studio, you can go to Tools > Create GUID.  Now, we copy the GUID and go to Main.c (our entry point).  Change the declaration for gImageFileGuid to the new value.  If you are not sure how to convert a GUID into the format used in the EFI_GUID declaration, take a look at this article.

Open up Nt32Pkg.fdf [1].  Search for "Logo.bmp".  Below that statement, add a similar statement, in the following format:

FILE FREEFORM = 0E1FAAC7-82A4-41DA-90A4-EE1F6F3E81DC {
SECTION RAW = AppPkg/Applications/Main/6.bmp
}

Be sure to replace the GUID and filepath with your own.  


And there it is!  Build it and run Main like you would normally, and your own beautiful image will be displayed in place of the Tiano logo.

Next time, I'll talk about getting an image to moving an image around the screen using keyboard input.

11 comments:

lrwr said...

I have a question on this one.

After getting the previous and this example to work yesterday, I focused on getting visual studio to compile my programm for the "real" uefi shell in X64, not using the NT32.
The main question now is where to put the GUID definition now?
The MdeModulePkg does not use a .fdf file to put this code in.
And even if it did, would this program be "portable", meaning I could put it on my USB-drive and execute it from any uefi-shell (any machine) I want?

Tim Lewis said...

Good question. The real problem you have is that the function GetSectionFromAnyFv() only works on Firmware Volumes, which are store file system for the logical flash device. If you want to run an EFI shell application, the easiest way would be to replace calls to GetSectionFromAnyFv with the C library calls like fopen and fread. These are found in StdLib. Then you would just provide the file name of the BMP file.

UEFI 2.1 and above provides another method, called HII, but EDK2 doesn't provide the necessary build support to integrate the images directly into the .efi file.

lrwr said...

Thanks for your information.

I tried to get fopen to work for a while now but it won't find my file.
First I tried using NT32 again, stepping through the librarys in debug mode to track down the reason why it wouldn't work. Then I realised that I hat no file system selected and couldn't do so in the emulated shell. Testing in a real shell didn't make it any better.
My program and my file are both located in the same folder. The library seemes to support relative paths, but wouldn't give me anything except for a null-pointer.
To test the fopen function I tried fopen("test.txt","r"); in manny variants, but neither "/test.txt" nor "./test.txt" nor "\test.txt" nor anything else I tested worked.

Tim Lewis said...

I have used fopen, both in the real shell and in the NT32 environment numerous times, so I'm not quite sure what is going on. If you type 'map -r' what do you see?

lrwr said...

I've sent you an e-mail containing screenshots.

Tim Lewis said...

Thanks. After looking at your screen shots, you need to select a file system, or include the file system alias in the file path. (i.e. "fs0:test.txt" or fs0: and then run your program.) I sent you this program, and it worked.

FILE *f;
char inputstring[100];

f = fopen ("test.txt", "r");
if (f == NULL) {
printf ("unable to open test.txt");
return 1;
}

if (fgets (inputstring, 100, f) == NULL) {
printf ("unable to read test.txt");
return 1;
}

printf (inputstring);
return 0;

lrwr said...

thanks tim!
I got it working now.

As I got your program I tried to get it working, but even your simple sample wouldn't do it's job.
I used exactly your code and the same commands to open, so the problem had to be somewhere else.

With some testing I got it working after replacing some files.
Analyzing the problem came up with the following result:

my .inf file was missing
DevShell
in [LibraryClasses]

It's surprising me that it didn't cause any errors while compiling

Tim Lewis said...

You have found a very interesting part of UEFI's C library. The reason that it does not complain is that the library DevShell actually only exports two functions: __ctor_DevShell and __dtor_DevShell. And these only get pulled into the build by the CONSTRUCTOR and DESTRUCTOR keywords in daShell.inf. So, if you never include the library, these functions don't get pulled in and don't get called. These two functions, then register the standard devices with the shell's internal API. It seems a bit error prone since, as you have seen, you can succeed with the build but not get what you want. I suspect that the reason that it is designed this way is to permit the C library to work in other non-shell environments (even though it does not today) by substituting another library to install various devices.

Hank said...

Hi, would you please send me your code?

Unknown said...

We too faced the issue of fopen() fails. After adding DevShell it works.

Thanks a lot Tim

Unknown said...

Can you also forward me the code?