UEFI News and Commentary

Thursday, August 15, 2013

Composite Images and Transparency

In this project, I endeavored to create a visual thermometer, which would change the displayed temperature based on user input.  I did not want to have to store a dozen different sprites to be able to display different temperatures, however, because the images would be so similar, the only difference being the length of the red bar inside the thermometer.  To do this, I tried out a few different things.  First was the display of a smaller chunk of a larger image, so that rather than keeping track of many images of different thermometers, I could just display bigger or smaller sections of a single image.  Second is the composition of different images on top of one another to create one image.  Finally, I implemented transparency for the purpose of combining multiple images.

This article assumes you have read the previous article on displaying images in a cycle using sprites.

The code for this project can be found on sourceforge, here.

Because the only part of the thermometer that changes is the height of the red bar, and it is a very simple image, I figured I didn't need to save all the different sprites, and could just use one image of a red bar to display all the different heights.  Basically, what I decided to do was to store a single image of the largest the bar could be and display smaller sections of the image, depending on user input.  Rather than displaying the entire image, only a small section at a time is displayed.

Like with sprites, I created a loop that waited for user input of either an UP or DOWN keystroke, and would call another function from there.  However, instead of changing the pointer to the image, it increases or decreases the height of the displayed image.  This way, the actual image that is used stays the same, but a shorter or taller section is displayed.

Next is the combination of several images to make one single image that will be displayed.  Since only the red bar in the thermometer would be changing, I figured I could just stack the different parts on top of each other: the white background, the red bar, and the tick marks.  

Rather than calling Blt() several different times for each of the different layers, I merged the three layers into one bitmap and then called Blt() once to display that combined image.

First, I started by creating a buffer of type EFI_GRAPHICS_OUTPUT_BLT_PIXEL, set to be the size of the largest bitmap I would be displaying (the background).  I then filled each entry with a black pixel (each color set to 0).

I then copied each layer into the new buffer, starting with the background.  The copy function takes the information from the destination and the source buffers, including their heights and widths, as well as the X and Y coordinates to which the source buffer should be copied.  The function will locate the pixel specified, and copy the source buffer into the destination buffer, clipping the edges of the source buffer if they extend past the edges of the destination buffer.  

Transparency is also implemented in this function.  I wanted to put the tick marks on the thermometer, but couldn't do it with simple image merging because .BMP files (the only supported image file at the moment) do not have transparency.  So in this function, while merging all the image buffers together, pure black (the values for red, blue and green are all 0) is interpreted as transparent, and it does not copy the source pixel to the destination buffer.
These are the three layers used for the thermometer image.  I wanted black tick marks on the thermometer, but since black is interpreted as transparent, I instead used a very dark blue to draw the marks, and then colored the rest of the image black.

I did try to use the DrawImage() function for EFI_HII_IMAGE_PROTOCOL in the open source tianocore.org implementation because the spec describes the ability for it to implement transparency.  However, transparency had not been fully implemented  in that function, so I decided not to use it at all and implement it all manually.

For my next project, I plan on using this thermometer in a simple puzzle-based game.

No comments: